File indexing completed on 2024-04-28 06:00:02

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Log
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /**
0023  * @category   Zend
0024  * @package    Zend_Log
0025  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0026  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0027  * @version    $Id$
0028  *
0029  * Convenience methods for log [@see Zend_Log::__call()]:
0030  *
0031  * @method emerg(string $message, $extras = null)
0032  * @method alert(string $message, $extras = null)
0033  * @method crit(string $message, $extras = null)
0034  * @method err(string $message, $extras = null)
0035  * @method warn(string $message, $extras = null)
0036  * @method notice(string $message, $extras = null)
0037  * @method info(string $message, $extras = null)
0038  * @method debug(string $message, $extras = null)
0039  */
0040 class Zend_Log
0041 {
0042     const EMERG   = 0;  // Emergency: system is unusable
0043     const ALERT   = 1;  // Alert: action must be taken immediately
0044     const CRIT    = 2;  // Critical: critical conditions
0045     const ERR     = 3;  // Error: error conditions
0046     const WARN    = 4;  // Warning: warning conditions
0047     const NOTICE  = 5;  // Notice: normal but significant condition
0048     const INFO    = 6;  // Informational: informational messages
0049     const DEBUG   = 7;  // Debug: debug messages
0050 
0051     /**
0052      * @var array of priorities where the keys are the
0053      * priority numbers and the values are the priority names
0054      */
0055     protected $_priorities = array();
0056 
0057     /**
0058      * @var array of Zend_Log_Writer_Abstract
0059      */
0060     protected $_writers = array();
0061 
0062     /**
0063      * @var array of Zend_Log_Filter_Interface
0064      */
0065     protected $_filters = array();
0066 
0067     /**
0068      * @var array of extra log event
0069      */
0070     protected $_extras = array();
0071 
0072     /**
0073      *
0074      * @var string
0075      */
0076     protected $_defaultWriterNamespace = 'Zend_Log_Writer';
0077 
0078     /**
0079      *
0080      * @var string
0081      */
0082     protected $_defaultFilterNamespace = 'Zend_Log_Filter';
0083 
0084     /**
0085      *
0086      * @var string
0087      */
0088     protected $_defaultFormatterNamespace = 'Zend_Log_Formatter';
0089 
0090     /**
0091      *
0092      * @var callback
0093      */
0094     protected $_origErrorHandler       = null;
0095 
0096     /**
0097      *
0098      * @var boolean
0099      */
0100     protected $_registeredErrorHandler = false;
0101 
0102     /**
0103      *
0104      * @var array|boolean
0105      */
0106     protected $_errorHandlerMap        = false;
0107 
0108     /**
0109      *
0110      * @var string
0111      */
0112     protected $_timestampFormat        = 'c';
0113 
0114     /**
0115      * Class constructor.  Create a new logger
0116      *
0117      * @param Zend_Log_Writer_Abstract|null  $writer  default writer
0118      */
0119     public function __construct(Zend_Log_Writer_Abstract $writer = null)
0120     {
0121         $r = new ReflectionClass($this);
0122         $this->_priorities = array_flip($r->getConstants());
0123 
0124         if ($writer !== null) {
0125             $this->addWriter($writer);
0126         }
0127     }
0128 
0129     /**
0130      * Factory to construct the logger and one or more writers
0131      * based on the configuration array
0132      *
0133      * @param  array|Zend_Config Array or instance of Zend_Config
0134      * @return Zend_Log
0135      * @throws Zend_Log_Exception
0136      */
0137     static public function factory($config = array())
0138     {
0139         if ($config instanceof Zend_Config) {
0140             $config = $config->toArray();
0141         }
0142 
0143         if (!is_array($config) || empty($config)) {
0144             /** @see Zend_Log_Exception */
0145             // require_once 'Zend/Log/Exception.php';
0146             throw new Zend_Log_Exception('Configuration must be an array or instance of Zend_Config');
0147         }
0148 
0149         if (array_key_exists('className', $config)) {
0150             $class = $config['className'];
0151             unset($config['className']);
0152         } else {
0153             $class = __CLASS__;
0154         }
0155 
0156         $log = new $class;
0157 
0158         if (!$log instanceof Zend_Log) {
0159             /** @see Zend_Log_Exception */
0160             // require_once 'Zend/Log/Exception.php';
0161             throw new Zend_Log_Exception('Passed className does not belong to a descendant of Zend_Log');
0162         }
0163 
0164         if (array_key_exists('timestampFormat', $config)) {
0165             if (null != $config['timestampFormat'] && '' != $config['timestampFormat']) {
0166                 $log->setTimestampFormat($config['timestampFormat']);
0167             }
0168             unset($config['timestampFormat']);
0169         }
0170 
0171         if (!is_array(current($config))) {
0172             $log->addWriter(current($config));
0173         } else {
0174             foreach($config as $writer) {
0175                 $log->addWriter($writer);
0176             }
0177         }
0178 
0179         return $log;
0180     }
0181 
0182 
0183     /**
0184      * Construct a writer object based on a configuration array
0185      *
0186      * @param  array $config config array with writer spec
0187      * @return Zend_Log_Writer_Abstract
0188      * @throws Zend_Log_Exception
0189      */
0190     protected function _constructWriterFromConfig($config)
0191     {
0192         $writer = $this->_constructFromConfig('writer', $config, $this->_defaultWriterNamespace);
0193 
0194         if (!$writer instanceof Zend_Log_Writer_Abstract) {
0195             $writerName = is_object($writer)
0196                         ? get_class($writer)
0197                         : 'The specified writer';
0198             /** @see Zend_Log_Exception */
0199             // require_once 'Zend/Log/Exception.php';
0200             throw new Zend_Log_Exception("{$writerName} does not extend Zend_Log_Writer_Abstract!");
0201         }
0202 
0203         if (isset($config['filterName'])) {
0204             $filter = $this->_constructFilterFromConfig($config);
0205             $writer->addFilter($filter);
0206         }
0207 
0208         if (isset($config['formatterName'])) {
0209             $formatter = $this->_constructFormatterFromConfig($config);
0210             $writer->setFormatter($formatter);
0211         }
0212 
0213         return $writer;
0214     }
0215 
0216     /**
0217      * Construct filter object from configuration array or Zend_Config object
0218      *
0219      * @param  array|Zend_Config $config Zend_Config or Array
0220      * @return Zend_Log_Filter_Interface
0221      * @throws Zend_Log_Exception
0222      */
0223     protected function _constructFilterFromConfig($config)
0224     {
0225         $filter = $this->_constructFromConfig('filter', $config, $this->_defaultFilterNamespace);
0226 
0227         if (!$filter instanceof Zend_Log_Filter_Interface) {
0228              $filterName = is_object($filter)
0229                          ? get_class($filter)
0230                          : 'The specified filter';
0231             /** @see Zend_Log_Exception */
0232             // require_once 'Zend/Log/Exception.php';
0233             throw new Zend_Log_Exception("{$filterName} does not implement Zend_Log_Filter_Interface");
0234         }
0235 
0236         return $filter;
0237     }
0238 
0239    /**
0240     * Construct formatter object from configuration array or Zend_Config object
0241     *
0242     * @param  array|Zend_Config $config Zend_Config or Array
0243     * @return Zend_Log_Formatter_Interface
0244     * @throws Zend_Log_Exception
0245     */
0246     protected function _constructFormatterFromConfig($config)
0247     {
0248         $formatter = $this->_constructFromConfig('formatter', $config, $this->_defaultFormatterNamespace);
0249 
0250         if (!$formatter instanceof Zend_Log_Formatter_Interface) {
0251              $formatterName = is_object($formatter)
0252                          ? get_class($formatter)
0253                          : 'The specified formatter';
0254             /** @see Zend_Log_Exception */
0255             // require_once 'Zend/Log/Exception.php';
0256             throw new Zend_Log_Exception($formatterName . ' does not implement Zend_Log_Formatter_Interface');
0257         }
0258 
0259         return $formatter;
0260     }
0261 
0262     /**
0263      * Construct a filter or writer from config
0264      *
0265      * @param string $type 'writer' of 'filter'
0266      * @param mixed $config Zend_Config or Array
0267      * @param string $namespace
0268      * @return object
0269      * @throws Zend_Log_Exception
0270      */
0271     protected function _constructFromConfig($type, $config, $namespace)
0272     {
0273         if ($config instanceof Zend_Config) {
0274             $config = $config->toArray();
0275         }
0276 
0277         if (!is_array($config) || empty($config)) {
0278             // require_once 'Zend/Log/Exception.php';
0279             throw new Zend_Log_Exception(
0280                 'Configuration must be an array or instance of Zend_Config'
0281             );
0282         }
0283 
0284         $params    = isset($config[ $type .'Params' ]) ? $config[ $type .'Params' ] : array();
0285         $className = $this->getClassName($config, $type, $namespace);
0286         if (!class_exists($className)) {
0287             // require_once 'Zend/Loader.php';
0288             Zend_Loader::loadClass($className);
0289         }
0290 
0291         $reflection = new ReflectionClass($className);
0292         if (!$reflection->implementsInterface('Zend_Log_FactoryInterface')) {
0293             // require_once 'Zend/Log/Exception.php';
0294             throw new Zend_Log_Exception(
0295                 $className . ' does not implement Zend_Log_FactoryInterface and can not be constructed from config.'
0296             );
0297         }
0298 
0299         return call_user_func(array($className, 'factory'), $params);
0300     }
0301 
0302     /**
0303      * Get the writer or filter full classname
0304      *
0305      * @param array $config
0306      * @param string $type filter|writer
0307      * @param string $defaultNamespace
0308      * @return string full classname
0309      * @throws Zend_Log_Exception
0310      */
0311     protected function getClassName($config, $type, $defaultNamespace)
0312     {
0313         if (!isset($config[$type . 'Name'])) {
0314             // require_once 'Zend/Log/Exception.php';
0315             throw new Zend_Log_Exception("Specify {$type}Name in the configuration array");
0316         }
0317 
0318         $className = $config[$type . 'Name'];
0319         $namespace = $defaultNamespace;
0320 
0321         if (isset($config[$type . 'Namespace'])) {
0322             $namespace = $config[$type . 'Namespace'];
0323         }
0324 
0325         // PHP >= 5.3.0 namespace given?
0326         if (substr($namespace, -1) == '\\') {
0327             return $namespace . $className;
0328         }
0329 
0330         // empty namespace given?
0331         if (strlen($namespace) === 0) {
0332             return $className;
0333         }
0334 
0335         return $namespace . '_' . $className;
0336     }
0337 
0338     /**
0339      * Packs message and priority into Event array
0340      *
0341      * @param  string   $message   Message to log
0342      * @param  integer  $priority  Priority of message
0343      * @return array Event array
0344      */
0345     protected function _packEvent($message, $priority)
0346     {
0347         return array_merge(array(
0348             'timestamp'    => date($this->_timestampFormat),
0349             'message'      => $message,
0350             'priority'     => $priority,
0351             'priorityName' => $this->_priorities[$priority]
0352             ),
0353             $this->_extras
0354         );
0355     }
0356 
0357     /**
0358      * Class destructor.  Shutdown log writers
0359      *
0360      * @return void
0361      */
0362     public function __destruct()
0363     {
0364         /** @var Zend_Log_Writer_Abstract $writer */
0365         foreach($this->_writers as $writer) {
0366             $writer->shutdown();
0367         }
0368     }
0369 
0370     /**
0371      * Undefined method handler allows a shortcut:
0372      *   $log->priorityName('message')
0373      *     instead of
0374      *   $log->log('message', Zend_Log::PRIORITY_NAME)
0375      *
0376      * @param  string  $method  priority name
0377      * @param  string  $params  message to log
0378      * @return void
0379      * @throws Zend_Log_Exception
0380      */
0381     public function __call($method, $params)
0382     {
0383         $priority = strtoupper($method);
0384         if (($priority = array_search($priority, $this->_priorities)) !== false) {
0385             switch (count($params)) {
0386                 case 0:
0387                     /** @see Zend_Log_Exception */
0388                     // require_once 'Zend/Log/Exception.php';
0389                     throw new Zend_Log_Exception('Missing log message');
0390                 case 1:
0391                     $message = array_shift($params);
0392                     $extras = null;
0393                     break;
0394                 default:
0395                     $message = array_shift($params);
0396                     $extras  = array_shift($params);
0397                     break;
0398             }
0399             $this->log($message, $priority, $extras);
0400         } else {
0401             /** @see Zend_Log_Exception */
0402             // require_once 'Zend/Log/Exception.php';
0403             throw new Zend_Log_Exception('Bad log priority');
0404         }
0405     }
0406 
0407     /**
0408      * Log a message at a priority
0409      *
0410      * @param  string   $message   Message to log
0411      * @param  integer  $priority  Priority of message
0412      * @param  mixed    $extras    Extra information to log in event
0413      * @return void
0414      * @throws Zend_Log_Exception
0415      */
0416     public function log($message, $priority, $extras = null)
0417     {
0418         // sanity checks
0419         if (empty($this->_writers)) {
0420             /** @see Zend_Log_Exception */
0421             // require_once 'Zend/Log/Exception.php';
0422             throw new Zend_Log_Exception('No writers were added');
0423         }
0424 
0425         if (! isset($this->_priorities[$priority])) {
0426             /** @see Zend_Log_Exception */
0427             // require_once 'Zend/Log/Exception.php';
0428             throw new Zend_Log_Exception('Bad log priority');
0429         }
0430 
0431         // pack into event required by filters and writers
0432         $event = $this->_packEvent($message, $priority);
0433 
0434         // Check to see if any extra information was passed
0435         if (!empty($extras)) {
0436             $info = array();
0437             if (is_array($extras)) {
0438                 foreach ($extras as $key => $value) {
0439                     if (is_string($key)) {
0440                         $event[$key] = $value;
0441                     } else {
0442                         $info[] = $value;
0443                     }
0444                 }
0445             } else {
0446                 $info = $extras;
0447             }
0448             if (!empty($info)) {
0449                 $event['info'] = $info;
0450             }
0451         }
0452 
0453         // abort if rejected by the global filters
0454         /** @var Zend_Log_Filter_Interface $filter */
0455         foreach ($this->_filters as $filter) {
0456             if (! $filter->accept($event)) {
0457                 return;
0458             }
0459         }
0460 
0461         // send to each writer
0462         /** @var Zend_Log_Writer_Abstract $writer */
0463         foreach ($this->_writers as $writer) {
0464             $writer->write($event);
0465         }
0466     }
0467 
0468     /**
0469      * Add a custom priority
0470      *
0471      * @param  string  $name     Name of priority
0472      * @param  integer $priority Numeric priority
0473      * @return $this
0474      * @throws Zend_Log_Exception
0475      */
0476     public function addPriority($name, $priority)
0477     {
0478         // Priority names must be uppercase for predictability.
0479         $name = strtoupper($name);
0480 
0481         if (isset($this->_priorities[$priority])
0482             || false !== array_search($name, $this->_priorities)) {
0483             /** @see Zend_Log_Exception */
0484             // require_once 'Zend/Log/Exception.php';
0485             throw new Zend_Log_Exception('Existing priorities cannot be overwritten');
0486         }
0487 
0488         $this->_priorities[$priority] = $name;
0489         return $this;
0490     }
0491 
0492     /**
0493      * Add a filter that will be applied before all log writers.
0494      * Before a message will be received by any of the writers, it
0495      * must be accepted by all filters added with this method.
0496      *
0497      * @param  int|Zend_Config|array|Zend_Log_Filter_Interface $filter
0498      * @return $this
0499      * @throws Zend_Log_Exception
0500      */
0501     public function addFilter($filter)
0502     {
0503         if (is_int($filter)) {
0504             /** @see Zend_Log_Filter_Priority */
0505             // require_once 'Zend/Log/Filter/Priority.php';
0506             $filter = new Zend_Log_Filter_Priority($filter);
0507 
0508         } elseif ($filter instanceof Zend_Config || is_array($filter)) {
0509             $filter = $this->_constructFilterFromConfig($filter);
0510 
0511         } elseif(! $filter instanceof Zend_Log_Filter_Interface) {
0512             /** @see Zend_Log_Exception */
0513             // require_once 'Zend/Log/Exception.php';
0514             throw new Zend_Log_Exception('Invalid filter provided');
0515         }
0516 
0517         $this->_filters[] = $filter;
0518         return $this;
0519     }
0520 
0521     /**
0522      * Add a writer.  A writer is responsible for taking a log
0523      * message and writing it out to storage.
0524      *
0525      * @param  mixed $writer Zend_Log_Writer_Abstract or Config array
0526      * @return Zend_Log
0527      * @throws Zend_Log_Exception
0528      */
0529     public function addWriter($writer)
0530     {
0531         if (is_array($writer) || $writer instanceof  Zend_Config) {
0532             $writer = $this->_constructWriterFromConfig($writer);
0533         }
0534 
0535         if (!$writer instanceof Zend_Log_Writer_Abstract) {
0536             /** @see Zend_Log_Exception */
0537             // require_once 'Zend/Log/Exception.php';
0538             throw new Zend_Log_Exception(
0539                 'Writer must be an instance of Zend_Log_Writer_Abstract'
0540                 . ' or you should pass a configuration array'
0541             );
0542         }
0543 
0544         $this->_writers[] = $writer;
0545         return $this;
0546     }
0547 
0548     /**
0549      * Set an extra item to pass to the log writers.
0550      *
0551      * @param  string $name    Name of the field
0552      * @param  string $value   Value of the field
0553      * @return Zend_Log
0554      */
0555     public function setEventItem($name, $value)
0556     {
0557         $this->_extras = array_merge($this->_extras, array($name => $value));
0558         return $this;
0559     }
0560 
0561     /**
0562      * Register Logging system as an error handler to log php errors
0563      * Note: it still calls the original error handler if set_error_handler is able to return it.
0564      *
0565      * Errors will be mapped as:
0566      *   E_NOTICE, E_USER_NOTICE => NOTICE
0567      *   E_WARNING, E_CORE_WARNING, E_USER_WARNING => WARN
0568      *   E_ERROR, E_USER_ERROR, E_CORE_ERROR, E_RECOVERABLE_ERROR => ERR
0569      *   E_DEPRECATED, E_STRICT, E_USER_DEPRECATED => DEBUG
0570      *   (unknown/other) => INFO
0571      *
0572      * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler
0573      *
0574      * @return Zend_Log
0575      */
0576     public function registerErrorHandler()
0577     {
0578         // Only register once.  Avoids loop issues if it gets registered twice.
0579         if ($this->_registeredErrorHandler) {
0580             return $this;
0581         }
0582 
0583         $this->_origErrorHandler = set_error_handler(array($this, 'errorHandler'));
0584 
0585         // Contruct a default map of phpErrors to Zend_Log priorities.
0586         // Some of the errors are uncatchable, but are included for completeness
0587         $this->_errorHandlerMap = array(
0588             E_NOTICE            => Zend_Log::NOTICE,
0589             E_USER_NOTICE       => Zend_Log::NOTICE,
0590             E_WARNING           => Zend_Log::WARN,
0591             E_CORE_WARNING      => Zend_Log::WARN,
0592             E_USER_WARNING      => Zend_Log::WARN,
0593             E_ERROR             => Zend_Log::ERR,
0594             E_USER_ERROR        => Zend_Log::ERR,
0595             E_CORE_ERROR        => Zend_Log::ERR,
0596             E_RECOVERABLE_ERROR => Zend_Log::ERR,
0597             E_STRICT            => Zend_Log::DEBUG,
0598         );
0599         // PHP 5.3.0+
0600         if (defined('E_DEPRECATED')) {
0601             $this->_errorHandlerMap['E_DEPRECATED'] = Zend_Log::DEBUG;
0602         }
0603         if (defined('E_USER_DEPRECATED')) {
0604             $this->_errorHandlerMap['E_USER_DEPRECATED'] = Zend_Log::DEBUG;
0605         }
0606 
0607         $this->_registeredErrorHandler = true;
0608         return $this;
0609     }
0610 
0611     /**
0612      * Error Handler will convert error into log message, and then call the original error handler
0613      *
0614      * @link http://www.php.net/manual/en/function.set-error-handler.php Custom error handler
0615      * @param int $errno
0616      * @param string $errstr
0617      * @param string $errfile
0618      * @param int $errline
0619      * @param array $errcontext
0620      * @return boolean
0621      */
0622     public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext)
0623     {
0624         $errorLevel = error_reporting();
0625 
0626         if ($errorLevel & $errno) {
0627             if (isset($this->_errorHandlerMap[$errno])) {
0628                 $priority = $this->_errorHandlerMap[$errno];
0629             } else {
0630                 $priority = Zend_Log::INFO;
0631             }
0632             $this->log($errstr, $priority, array('errno'=>$errno, 'file'=>$errfile, 'line'=>$errline, 'context'=>$errcontext));
0633         }
0634 
0635         if ($this->_origErrorHandler !== null) {
0636             return call_user_func($this->_origErrorHandler, $errno, $errstr, $errfile, $errline, $errcontext);
0637         }
0638         return false;
0639     }
0640 
0641     /**
0642      * Set timestamp format for log entries.
0643      *
0644      * @param string $format
0645      * @return Zend_Log
0646      */
0647     public function setTimestampFormat($format)
0648     {
0649         $this->_timestampFormat = $format;
0650         return $this;
0651     }
0652 
0653     /**
0654      * Get timestamp format used for log entries.
0655      *
0656      * @return string
0657      */
0658     public function getTimestampFormat()
0659     {
0660         return $this->_timestampFormat;
0661     }
0662 }