Explorar el Código

[ZF-9192] Zend_Log
Add errorHandler support

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@21613 44c647ce-9c0f-0410-b52a-842ac1e357ba

karnaf hace 16 años
padre
commit
6c9209b8cc

+ 61 - 0
documentation/manual/en/module_specs/Zend_Log-Overview.xml

@@ -230,4 +230,65 @@ $logger->setEventItem('pid', getmypid());
             to learn more.
         </para>
     </sect2>
+
+    <sect2 id="zend.log.overview.as-errorHandler">
+        <title>Log PHP Errors</title>
+
+        <para>
+            Zend_Log can also be used to log php errors.  Calling <methodname>registerErrorHandler()</methodname> will
+            add Zend_Log before the current error handler, and will pass the error along as well.
+        </para>
+        
+        <table id="zend.log.overview.as-errorHandler.properties.table-1">
+            <title>Zend_Log events from php errors have the additional fields matching 
+            <methodname>handler  ( int $errno  , string $errstr  [, string $errfile  [, int $errline  [, array $errcontext  ]]] )</methodname>
+            from <ulink url="http://us3.php.net/manual/en/function.set-error-handler.php">set_error_handler</ulink></title>
+
+            <tgroup cols="3">
+                <thead>
+                    <row>
+                        <entry>Name</entry>
+                        <entry>Error Handler Paramater</entry>
+                        <entry>Description</entry>
+                    </row>
+                </thead>
+
+                <tbody>
+                    <row>
+                        <entry>message</entry>
+                        <entry>errstr</entry>
+                        <entry>Contains the error message, as a string.</entry>
+                    </row>
+
+                    <row>
+                        <entry>errno</entry>
+                        <entry>errno</entry>
+                        <entry>Contains the level of the error raised, as an integer.</entry>
+                    </row>
+
+                    <row>
+                        <entry>file</entry>
+                        <entry>errfile</entry>
+                        <entry>Contains the filename that the error was raised in, as a string.</entry>
+                    </row>
+
+                    <row>
+                        <entry>line</entry>
+                        <entry>errline</entry>
+                        <entry>Contains the line number the error was raised at, as an integer. </entry>
+                    </row>
+
+                    <row>
+                        <entry>context</entry>
+                        <entry>errcontext</entry>
+                        <entry>(optional) An array that points to the active symbol table at the point the
+                        error occurred. In other words, errcontext  will contain an array of every variable
+                        that existed in the scope the error was triggered in. User error handler must not
+                        modify error context.</entry>
+                    </row>
+
+                </tbody>
+            </tgroup>
+        </table>
+    </sect2>
 </sect1>

+ 97 - 0
library/Zend/Log.php

@@ -70,6 +70,23 @@ class Zend_Log
      */
     protected $_defaultFilterNamespace = 'Zend_Log_Filter';
 
+    /**
+     *
+     * @var callback
+     */
+    protected $_origErrorHandler       = null;
+    
+    /**
+     *
+     * @var boolean
+     */
+    protected $_registeredErrorHandler = false;
+    
+    /**
+     *
+     * @var array
+     */
+    protected $_errorHandlerMap        = false;
 
     /**
      * Class constructor.  Create a new logger
@@ -437,4 +454,84 @@ class Zend_Log
     public function setEventItem($name, $value) {
         $this->_extras = array_merge($this->_extras, array($name => $value));
     }
+    
+    /**
+     * Register Logging system as an error handler to log php errors
+     * Note: it still calls the original error handler if set_error_handler is able to return it.
+     *
+     * Errors will be mapped as:
+     *   E_NOTICE, E_USER_NOTICE => NOTICE
+     *   E_WARNING, E_CORE_WARNING, E_USER_WARNING => WARN
+     *   E_ERROR, E_USER_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR => ERR
+     *   E_DEPRECATED, E_STRICT, E_USER_DEPRECATED => DEBUG
+     *   (unknown/other) => INFO
+     *
+     * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler
+     *
+     * @return Zend_Log
+     */
+    public function registerErrorHandler()
+    {
+        // Only register once.  Avoids loop issues if it gets registered twice.
+        if ($this->_registeredErrorHandler) { 
+        	return $this; 
+        }
+        
+        $this->_origErrorHandler = set_error_handler(array($this, 'errorHandler'));
+        
+        // Contruct a default map of phpErrors to Zend_Log priorities.
+        // Some of the errors are uncatchable, but are included for completeness
+        $this->_errorHandlerMap = array(
+            E_NOTICE            => Zend_Log::NOTICE,
+            E_USER_NOTICE       => Zend_Log::NOTICE,
+            E_WARNING           => Zend_Log::WARN,
+            E_CORE_WARNING      => Zend_Log::WARN,
+            E_USER_WARNING      => Zend_Log::WARN,
+            E_ERROR             => Zend_Log::ERR,
+            E_USER_ERROR        => Zend_Log::ERR,
+            E_CORE_ERROR        => Zend_Log::ERR,
+            E_RECOVERABLE_ERROR => Zend_Log::ERR,
+            E_STRICT            => Zend_Log::DEBUG,
+        );
+        // PHP 5.3.0+
+        if (defined('E_DEPRECATED')) {
+            $this->_errorHandlerMap['E_DEPRECATED'] = Zend_Log::DEBUG;
+        }
+        if (defined('E_USER_DEPRECATED')) {
+            $this->_errorHandlerMap['E_USER_DEPRECATED'] = Zend_Log::DEBUG;
+        }
+        
+        $this->_registeredErrorHandler = true;
+        return $this;
+    }
+    
+    /**
+     * Error Handler will convert error into log message, and then call the original error handler
+     *
+     * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler
+     * @param int $errno
+     * @param string $errstr
+     * @param string $errfile
+     * @param int $errline
+     * @param array $errcontext
+     * @return boolean
+     */
+    public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
+    {
+        $errorLevel = error_reporting();
+        
+        if ($errorLevel && $errno) {
+            if (isset($this->_errorHandlerMap[$errno])) {
+                $priority = $this->_errorHandlerMap[$errno];
+            } else {
+                $priority = Zend_Log::INFO;
+            }
+            $this->log($errstr, $priority, array('errno'=>$errno, 'file'=>$errfile, 'line'=>$errline, 'context'=>$errcontext));
+        }
+        
+        if ($this->_origErrorHandler !== null) {
+            return call_user_func($this->_origErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext);
+        }
+        return false;
+    }
 }

+ 70 - 14
tests/Zend/Log/LogTest.php

@@ -244,47 +244,103 @@ class Zend_Log_LogTest extends PHPUnit_Framework_TestCase
     
     // Factory
 
-    public function testLogConstructFromConfigStream() 
+    public function testLogConstructFromConfigStream()
     {
         $cfg = array('log' => array('memory' => array(
-            'writerName'      => "Stream", 
-            'writerNamespace' => "Zend_Log_Writer", 
+            'writerName'      => "Stream",
+            'writerNamespace' => "Zend_Log_Writer",
             'writerParams'    => array(
                 'stream'      => "php://memory"
-            )        
+            )
         )));
 
         $logger = Zend_Log::factory($cfg['log']);
         $this->assertTrue($logger instanceof Zend_Log);
     }
 
-    public function testLogConstructFromConfigStreamAndFilter() 
+    public function testLogConstructFromConfigStreamAndFilter()
     {
         $cfg = array('log' => array('memory' => array(
-            'writerName'      => "Stream", 
-            'writerNamespace' => "Zend_Log_Writer", 
+            'writerName'      => "Stream",
+            'writerNamespace' => "Zend_Log_Writer",
             'writerParams'    => array(
                 'stream'      => "php://memory"
-            ), 
-            'filterName'   => "Priority", 
+            ),
+            'filterName'   => "Priority",
             'filterParams' => array(
-                'priority' => "Zend_Log::CRIT", 
+                'priority' => "Zend_Log::CRIT",
                 'operator' => "<="
-             ),        
+             ),
         )));
 
         $logger = Zend_Log::factory($cfg['log']);
         $this->assertTrue($logger instanceof Zend_Log);
     }
 
-    public function testFactoryUsesNameAndNamespaceWithoutModifications() 
+    public function testFactoryUsesNameAndNamespaceWithoutModifications()
     {
         $cfg = array('log' => array('memory' => array(
-            'writerName'      => "ZendMonitor", 
-            'writerNamespace' => "Zend_Log_Writer", 
+            'writerName'      => "ZendMonitor",
+            'writerNamespace' => "Zend_Log_Writer",
         )));
 
         $logger = Zend_Log::factory($cfg['log']);
         $this->assertTrue($logger instanceof Zend_Log);
     }
+
+    /**
+     * @group ZF-9192
+     */
+    public function testUsingWithErrorHandler()
+    {
+        $writer = new Zend_Log_Writer_Mock();
+
+        $logger = new Zend_Log();
+        $logger->addWriter($writer);
+        $this->errWriter = $writer;
+        
+        
+        $oldErrorLevel = error_reporting();
+        
+        $this->expectingLogging = true;
+        error_reporting(E_ALL | E_STRICT);
+        
+        $oldHandler = set_error_handler(array($this, 'verifyHandlerData'));
+        $logger->registerErrorHandler();
+        
+        trigger_error("Testing notice shows up in logs", E_USER_NOTICE);
+        trigger_error("Testing warning shows up in logs", E_USER_WARNING);
+        trigger_error("Testing error shows up in logs", E_USER_ERROR);
+        
+        $this->expectingLogging = false;
+        error_reporting(0);
+        
+        trigger_error("Testing notice misses logs", E_USER_NOTICE);
+        trigger_error("Testing warning misses logs", E_USER_WARNING);
+        trigger_error("Testing error misses logs", E_USER_ERROR);
+        
+        restore_error_handler(); // Pop off the Logger
+        restore_error_handler(); // Pop off the verifyHandlerData
+        error_reporting($oldErrorLevel); // Restore original reporting level
+        unset($this->errWriter);
+    }
+    
+    /**
+     * @group ZF-9192
+     * Used by testUsingWithErrorHandler - 
+     * verifies that the data written to the original logger is the same as the data written in Zend_Log
+     */
+    public function verifyHandlerData($errno, $errstr, $errfile, $errline, $errcontext)
+    {
+        if ($this->expectingLogging) {
+            $this->assertFalse(empty($this->errWriter->events));
+            $event = array_shift($this->errWriter->events);
+            $this->assertEquals($errstr, $event['message']);
+            $this->assertEquals($errno, $event['errno']);
+            $this->assertEquals($errfile, $event['file']);
+            $this->assertEquals($errline, $event['line']);
+        } else {
+            $this->assertTrue(empty($this->errWriter->events));
+        }
+    }
 }