Overview

Namespaces

  • Jyxo
    • Beholder
      • TestCase
    • Gettext
      • Parser
    • Input
      • Chain
      • Filter
      • Validator
    • Mail
      • Email
        • Attachment
      • Parser
      • Sender
    • Rpc
      • Json
      • Xml
    • Shell
    • Spl
    • Svn
    • Time
    • Webdav
  • PHP

Classes

  • Charset
  • Color
  • Css
  • ErrorHandler
  • ErrorMail
  • FirePhp
  • Html
  • HtmlTag
  • SpamFilter
  • String
  • Timer
  • XmlReader

Exceptions

  • Exception
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * Jyxo PHP Library
  5:  *
  6:  * LICENSE
  7:  *
  8:  * This source file is subject to the new BSD license that is bundled
  9:  * with this package in the file license.txt.
 10:  * It is also available through the world-wide-web at this URL:
 11:  * https://github.com/jyxo/php/blob/master/license.txt
 12:  */
 13: 
 14: namespace Jyxo;
 15: 
 16: /**
 17:  * Error and exception handler.
 18:  *
 19:  * @category Jyxo
 20:  * @package Jyxo\ErrorHandling
 21:  * @copyright Copyright (c) 2005-2011 Jyxo, s.r.o.
 22:  * @license https://github.com/jyxo/php/blob/master/license.txt
 23:  * @author Jaroslav HanslĂ­k
 24:  */
 25: class ErrorHandler
 26: {
 27:     /**
 28:      * Notice.
 29:      *
 30:      * @var string
 31:      */
 32:     const NOTICE = 'notice';
 33: 
 34:     /**
 35:      * Warning.
 36:      *
 37:      * @var string
 38:      */
 39:     const WARNING = 'warning';
 40: 
 41:     /**
 42:      * Error.
 43:      *
 44:      * @var string
 45:      */
 46:     const ERROR = 'error';
 47: 
 48:     /**
 49:      * Fatal error.
 50:      *
 51:      * @var string
 52:      */
 53:     const FATAL = 'fatal';
 54: 
 55:     /**
 56:      * Exception.
 57:      *
 58:      * @var string
 59:      */
 60:     const EXCEPTION = 'exception';
 61: 
 62:     /**
 63:      * Strict rules warning.
 64:      *
 65:      * @var string
 66:      */
 67:     const STRICT = 'strict';
 68: 
 69:     /**
 70:      * Deprecated code usage warning.
 71:      *
 72:      * @var string
 73:      */
 74:     const DEPRECATED = 'deprecated';
 75: 
 76:     /**
 77:      * Is debug enabled?
 78:      *
 79:      * @var boolean
 80:      */
 81:     private static $debug = false;
 82: 
 83:     /**
 84:      * \Jyxo\ErrorMail instance for sending fatal error emails (used by the shutdown function and error handler).
 85:      *
 86:      * @var \Jyxo\ErrorMail
 87:      */
 88:     private static $errorMail;
 89: 
 90:     /**
 91:      * Constructor preventing from creating class instances.
 92:      *
 93:      * @throws \LogicException When trying to create an instance
 94:      */
 95:     public final function __construct()
 96:     {
 97:         throw new \LogicException(sprintf('It is forbidden to create instances of %s class.', get_class($this)));
 98:     }
 99: 
100:     /**
101:      * Initializes error handling.
102:      *
103:      * @param boolean $debug Turn debugging on?
104:      */
105:     public static function init($debug = false)
106:     {
107:         // Sets debugging
108:         self::$debug = (bool) $debug;
109: 
110:         // Registers handlers
111:         set_error_handler(array(__CLASS__, 'handleError'));
112:         set_exception_handler(array(__CLASS__, 'handleException'));
113:         register_shutdown_function(array(__CLASS__, 'handleFatalError'));
114:     }
115: 
116:     /**
117:      * Sets \Jyxo\ErrorMail instance for sending fatal error emails (used by the shutdown function and error handler).
118:      *
119:      * @param \Jyxo\ErrorMail $errorMail
120:      */
121:     public static function setErrorMail(\Jyxo\ErrorMail $errorMail)
122:     {
123:         self::$errorMail = $errorMail;
124:     }
125: 
126:     /**
127:      * Handles errors and logs them.
128:      *
129:      * @param integer $type Error type
130:      * @param string $message Error message
131:      * @param string $file File where the error occurred
132:      * @param integer $line Line on which the error occurred
133:      * @param array $context Error context variables
134:      * @return boolean Was the error processed?
135:      */
136:     public static function handleError($type, $message, $file, $line, $context)
137:     {
138:         // 0 means the error was blocked by prepending "@" to the command or by error_reporting settings
139:         if (0 === ($type & error_reporting())) {
140:             return true;
141:         }
142: 
143:         static $types = array(
144:             E_RECOVERABLE_ERROR => self::ERROR,
145:             E_USER_ERROR => self::ERROR,
146:             E_WARNING => self::WARNING,
147:             E_USER_WARNING => self::WARNING,
148:             E_NOTICE => self::NOTICE,
149:             E_USER_NOTICE => self::NOTICE,
150:             E_STRICT => self::STRICT,
151:             E_DEPRECATED => self::DEPRECATED,
152:             E_USER_DEPRECATED => self::DEPRECATED
153:         );
154: 
155:         // On false, the standard error handler will be used
156:         return self::log(
157:             array(
158:                 'type' => $types[$type],
159:                 'text' => $message,
160:                 'file' => $file,
161:                 'line' => $line,
162:                 'context' => $context,
163:                 'trace' => array_slice(debug_backtrace(), 1) // Removes the error handler call from trace
164:             )
165:         );
166:     }
167: 
168:     /**
169:      * Catches exceptions and logs them.
170:      *
171:      * @param \Exception $exception Uncaught exception
172:      */
173:     public static function handleException(\Exception $exception)
174:     {
175:         self::exception($exception);
176:         if (self::$errorMail) {
177:             self::$errorMail->send($exception);
178:         }
179:     }
180: 
181:     /**
182:      * Handles critical errors and logs them.
183:      */
184:     public static function handleFatalError()
185:     {
186:         // List of critical errors
187:         static $fatalErrors = array(
188:             E_ERROR => true,
189:             E_CORE_ERROR => true,
190:             E_COMPILE_ERROR => true,
191:             E_PARSE => true,
192:         );
193: 
194:         // If the last error was critical
195:         $error = error_get_last();
196:         if (isset($fatalErrors[$error['type']])) {
197:             self::log(
198:                 array(
199:                     'type' => self::FATAL,
200:                     'text' => $error['message'],
201:                     'file' => $error['file'],
202:                     'line' => $error['line']
203:                 )
204:             );
205:             if (self::$errorMail) {
206:                 $ex = new \ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
207:                 self::$errorMail->send($ex);
208:             }
209:         }
210:     }
211: 
212:     /**
213:      * Adds a caught exception.
214:      *
215:      * @param \Exception $exception Caught exception
216:      * @param boolean $fire Shall we use FirePHP?
217:      */
218:     public static function exception(\Exception $exception, $fire = true)
219:     {
220:         self::log(
221:             array(
222:                 'type' => self::EXCEPTION,
223:                 'text' => $exception->getMessage() . ' [' . $exception->getCode() . ']',
224:                 'file' => $exception->getFile(),
225:                 'line' => $exception->getLine(),
226:                 'trace' => $exception->getTrace(),
227:                 'previous' => self::getAllPreviousExceptions($exception)
228:             ),
229:             $fire
230:         );
231:     }
232: 
233:     /**
234:      * Logs a message.
235:      *
236:      * @param array $message Message definition
237:      * @param boolean $fire Shall we use FirePHP?
238:      * @return boolean Was logging successful?
239:      */
240:     public static function log(array $message, $fire = true)
241:     {
242:         // Adds default values if missing
243:         if (!isset($message['file'])) {
244:             $message['file'] = null;
245:         }
246:         if (!isset($message['line'])) {
247:             $message['line'] = null;
248:         }
249:         if (!isset($message['trace'])) {
250:             $message['trace'] = array();
251:         }
252:         if (!isset($message['previous'])) {
253:             $message['previous'] = array();
254:         }
255: 
256:         // We don't want HTML tags and entities in the log
257:         if (ini_get('html_errors')) {
258:             $message['text'] = html_entity_decode(strip_tags($message['text']));
259:         }
260: 
261:         // Request type
262:         if (!empty($_SERVER['argv'])) {
263:             // CLI
264:             $request = implode(' ', $_SERVER['argv']);
265:         } else {
266:             // Apache
267:             $request = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '';
268:             $request .= $_SERVER['REQUEST_URI'];
269:         }
270: 
271:         // Base text
272:         $text = sprintf('%s [Request: %s]', $message['text'], $request);
273:         if (isset($message['file'], $message['line'])) {
274:             $text .= sprintf(' [%s: %s]', $message['file'], $message['line']);
275:         }
276:         $log = sprintf("%s: %s\n", strtoupper($message['type']), $text);
277: 
278:         // Trace
279:         $log .= self::getTraceLog($message['trace']);
280: 
281:         // Previous exceptions
282:         $previousTrace = $message['trace'];
283:         foreach ($message['previous'] as $previous) {
284:             // Throw away trace parts that have already been processed
285:             $trace = array_reverse(array_diff_key(array_reverse($previous->getTrace()), array_reverse($previousTrace)));
286:             $previousTrace = $previous->getTrace();
287: 
288:             $log .= sprintf("Previous: %s [%s] [%s: %s]\n", $previous->getMessage(), $previous->getCode(), $previous->getFile(), $previous->getLine());
289:             $log .= self::getTraceLog($trace);
290:         }
291: 
292:         // FirePHP log for debugging
293:         if (self::$debug && $fire) {
294:             self::firephp($message);
295:         }
296: 
297:         // Logging
298:         return error_log(trim($log));
299:     }
300: 
301:     /**
302:      * Sends a message to FirePHP.
303:      *
304:      * @param array $message Message definition
305:      * @return boolean Was sending successful?
306:      */
307:     private static function firephp($message)
308:     {
309:         static $labels = array(
310:             self::EXCEPTION => 'Exception',
311:             self::FATAL => 'Fatal Error',
312:             self::ERROR => 'Error',
313:             self::WARNING => 'Warning',
314:             self::NOTICE => 'Notice',
315:             self::STRICT => 'Strict',
316:             self::DEPRECATED => 'Deprecated'
317:         );
318: 
319:         // Adds to FirePHP
320:         return FirePhp::trace(
321:             sprintf('%s: %s', $labels[$message['type']], $message['text']),
322:             $message['file'],
323:             $message['line'],
324:             $message['trace']
325:         );
326:     }
327: 
328:     /**
329:      * Returns trace log.
330:      *
331:      * @param array $trace Trace definition
332:      * @return string
333:      */
334:     private static function getTraceLog(array $trace)
335:     {
336:         $log = '';
337:         foreach ($trace as $levelNo => $level) {
338:             if (!isset($level['file'])) {
339:                 $level['file'] = 0;
340:             }
341:             if (!isset($level['line'])) {
342:                 $level['line'] = 0;
343:             }
344:             if (!isset($level['class'])) {
345:                 $level['class'] = '';
346:             }
347:             if (!isset($level['type'])) {
348:                 $level['type'] = '';
349:             }
350:             if (!isset($level['function'])) {
351:                 $level['function'] = '';
352:             }
353:             $log .= sprintf("\t%s\t%s\t%s\t%s\n", $levelNo, $level['file'], $level['line'], $level['class'] . $level['type'] . $level['function']);
354:         }
355:         return $log;
356:     }
357: 
358:     /**
359:      * Returns all exception's previous exceptions.
360:      *
361:      * @param \Exception $exception Exception to process
362:      * @return array
363:      */
364:     private static function getAllPreviousExceptions(\Exception $exception)
365:     {
366:         $stack = array();
367:         $previous = $exception->getPrevious();
368:         while (null !== $previous) {
369:             $stack[] = $previous;
370:             $previous = $previous->getPrevious();
371:         }
372:         return $stack;
373:     }
374: }
375: 
Jyxo PHP Library API documentation generated by ApiGen 2.3.0