Namespaces

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • ArrayTools
  • Callback
  • Component
  • ComponentContainer
  • Configurator
  • DateTime
  • Debug
  • Environment
  • Framework
  • FreezableObject
  • GenericRecursiveIterator
  • Image
  • ImageMagick
  • InstanceFilterIterator
  • Object
  • ObjectMixin
  • Paginator
  • RecursiveComponentIterator
  • ServiceLocator
  • SmartCachingIterator
  • String
  • Tools

Interfaces

  • IComponent
  • IComponentContainer
  • IDebuggable
  • IServiceLocator
  • ITranslator

Exceptions

  • AmbiguousServiceException
  • Overview
  • Namespace
  • Class
  • Tree
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  */
 11: 
 12: namespace Nette;
 13: 
 14: use Nette,
 15:     Nette\Environment;
 16: 
 17: 
 18: 
 19: /**
 20:  * Debug static class.
 21:  *
 22:  * @author     David Grudl
 23:  */
 24: final class Debug
 25: {
 26:     /** @var bool determines whether a server is running in production mode */
 27:     public static $productionMode;
 28: 
 29:     /** @var bool determines whether a server is running in console mode */
 30:     public static $consoleMode;
 31: 
 32:     /** @var int */
 33:     public static $time;
 34: 
 35:     /** @var bool is Firebug & FirePHP detected? */
 36:     private static $firebugDetected;
 37: 
 38:     /** @var bool is AJAX request detected? */
 39:     private static $ajaxDetected;
 40: 
 41:     /** @var array payload filled by {@link Debug::consoleDump()} */
 42:     private static $consoleData;
 43: 
 44:     /********************* Debug::dump() ****************d*g**/
 45: 
 46:     /** @var int  how many nested levels of array/object properties display {@link Debug::dump()} */
 47:     public static $maxDepth = 3;
 48: 
 49:     /** @var int  how long strings display {@link Debug::dump()} */
 50:     public static $maxLen = 150;
 51: 
 52:     /** @var int  display location? {@link Debug::dump()} */
 53:     public static $showLocation = FALSE;
 54: 
 55:     /********************* errors and exceptions reporing ****************d*g**/
 56: 
 57:     /**#@+ server modes {@link Debug::enable()} */
 58:     const DEVELOPMENT = FALSE;
 59:     const PRODUCTION = TRUE;
 60:     const DETECT = NULL;
 61:     /**#@-*/
 62: 
 63:     /** @var bool determines whether to consider all errors as fatal */
 64:     public static $strictMode = FALSE;
 65: 
 66:     /** @var array of callbacks specifies the functions that are automatically called after fatal error */
 67:     public static $onFatalError = array();
 68: 
 69:     /** @var callback */
 70:     public static $mailer = array(__CLASS__, 'defaultMailer');
 71: 
 72:     /** @var int interval for sending email is 2 days */
 73:     public static $emailSnooze = 172800;
 74: 
 75:     /** @var bool {@link Debug::enable()} */
 76:     private static $enabled = FALSE;
 77: 
 78:     /** @var string  name of the file where script errors should be logged */
 79:     private static $logFile;
 80: 
 81:     /** @var resource */
 82:     private static $logHandle;
 83: 
 84:     /** @var bool  send e-mail notifications of errors? */
 85:     private static $sendEmails;
 86: 
 87:     /** @var string  e-mail headers & body */
 88:     private static $emailHeaders = array(
 89:         'To' => '',
 90:         'From' => 'noreply@%host%',
 91:         'X-Mailer' => 'Nette Framework',
 92:         'Subject' => 'PHP: An error occurred on the server %host%',
 93:         'Body' => '[%date%] %message%',
 94:     );
 95: 
 96:     /** @var array  */
 97:     private static $colophons = array(array(__CLASS__, 'getDefaultColophons'));
 98: 
 99:     /********************* profiler ****************d*g**/
100: 
101:     /** @var bool {@link Debug::enableProfiler()} */
102:     private static $enabledProfiler = FALSE;
103: 
104:     /** @var array  free counters for your usage */
105:     public static $counters = array();
106: 
107:     /********************* Firebug extension ****************d*g**/
108: 
109:     /**#@+ FirePHP log priority */
110:     const LOG = 'LOG';
111:     const INFO = 'INFO';
112:     const WARN = 'WARN';
113:     const ERROR = 'ERROR';
114:     const TRACE = 'TRACE';
115:     const EXCEPTION = 'EXCEPTION';
116:     const GROUP_START = 'GROUP_START';
117:     const GROUP_END = 'GROUP_END';
118:     /**#@-*/
119: 
120: 
121: 
122:     /**
123:      * Static class - cannot be instantiated.
124:      */
125:     final public function __construct()
126:     {
127:         throw new \LogicException("Cannot instantiate static class " . get_class($this));
128:     }
129: 
130: 
131: 
132:     /**
133:      * Static class constructor.
134:      * @internal
135:      */
136:     public static function _init()
137:     {
138:         self::$time = microtime(TRUE);
139:         self::$consoleMode = PHP_SAPI === 'cli';
140:         self::$productionMode = self::DETECT;
141:         self::$firebugDetected = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/');
142:         self::$ajaxDetected = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
143:         register_shutdown_function(array(__CLASS__, '_shutdownHandler'));
144:     }
145: 
146: 
147: 
148:     /**
149:      * Shutdown handler to execute of the planned activities.
150:      * @return void
151:      * @internal
152:      */
153:     public static function _shutdownHandler()
154:     {
155:         // 1) fatal error handler
156:         static $types = array(
157:             E_ERROR => 1,
158:             E_CORE_ERROR => 1,
159:             E_COMPILE_ERROR => 1,
160:             E_PARSE => 1,
161:         );
162: 
163:         $error = error_get_last();
164:         if (self::$enabled && isset($types[$error['type']])) {
165:             if (!headers_sent()) { // for PHP < 5.2.4
166:                 header('HTTP/1.1 500 Internal Server Error');
167:             }
168: 
169:             if (ini_get('html_errors')) {
170:                 $error['message'] = html_entity_decode(strip_tags($error['message']), ENT_QUOTES, 'UTF-8');
171:             }
172: 
173:             self::processException(new \FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL), TRUE);
174:         }
175: 
176: 
177:         // other activities require HTML & development mode
178:         if (self::$productionMode) {
179:             return;
180:         }
181:         foreach (headers_list() as $header) {
182:             if (strncasecmp($header, 'Content-Type:', 13) === 0) {
183:                 if (substr($header, 14, 9) === 'text/html') {
184:                     break;
185:                 }
186:                 return;
187:             }
188:         }
189: 
190:         // 2) profiler
191:         if (self::$enabledProfiler) {
192:             if (self::$firebugDetected) {
193:                 self::fireLog('Nette profiler', self::GROUP_START);
194:                 foreach (self::$colophons as $callback) {
195:                     foreach ((array) call_user_func($callback, 'profiler') as $line) self::fireLog(strip_tags($line));
196:                 }
197:                 self::fireLog(NULL, self::GROUP_END);
198:             }
199: 
200:             if (!self::$ajaxDetected) {
201:                 $colophons = self::$colophons;
202:                 require __DIR__ . '/templates/profiler.phtml';
203:             }
204:         }
205: 
206: 
207:         // 3) console
208:         if (self::$consoleData) {
209:             $payload = self::$consoleData;
210:             require __DIR__ . '/templates/console.phtml';
211:         }
212:     }
213: 
214: 
215: 
216:     /********************* useful tools ****************d*g**/
217: 
218: 
219: 
220:     /**
221:      * Dumps information about a variable in readable format.
222:      * @param  mixed  variable to dump
223:      * @param  bool   return output instead of printing it? (bypasses $productionMode)
224:      * @return mixed  variable itself or dump
225:      */
226:     public static function dump($var, $return = FALSE)
227:     {
228:         if (!$return && self::$productionMode) {
229:             return $var;
230:         }
231: 
232:         $output = "<pre class=\"dump\">" . self::_dump($var, 0) . "</pre>\n";
233: 
234:         if (!$return && self::$showLocation) {
235:             $trace = debug_backtrace();
236:             $i = isset($trace[1]['class']) && $trace[1]['class'] === __CLASS__ ? 1 : 0;
237:             if (isset($trace[$i]['file'], $trace[$i]['line'])) {
238:                 $output = substr_replace($output, ' <small>' . htmlspecialchars("in file {$trace[$i]['file']} on line {$trace[$i]['line']}", ENT_NOQUOTES) . '</small>', -8, 0);
239:             }
240:         }
241: 
242:         if (self::$consoleMode) {
243:             $output = htmlspecialchars_decode(strip_tags($output), ENT_NOQUOTES);
244:         }
245: 
246:         if ($return) {
247:             return $output;
248: 
249:         } else {
250:             echo $output;
251:             return $var;
252:         }
253:     }
254: 
255: 
256: 
257:     /**
258:      * Dumps information about a variable in Nette Debug Console.
259:      * @param  mixed  variable to dump
260:      * @param  string optional title
261:      * @return mixed  variable itself
262:      */
263:     public static function consoleDump($var, $title = NULL)
264:     {
265:         if (!self::$productionMode) {
266:             $dump = array();
267:             foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) {
268:                 $dump[$key] = self::dump($val, TRUE);
269:             }
270:             self::$consoleData[] = array('title' => $title, 'dump' => $dump);
271:         }
272:         return $var;
273:     }
274: 
275: 
276: 
277:     /**
278:      * Internal dump() implementation.
279:      * @param  mixed  variable to dump
280:      * @param  int    current recursion level
281:      * @return string
282:      */
283:     private static function _dump(&$var, $level)
284:     {
285:         static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
286:         if ($tableUtf === NULL) {
287:             foreach (range("\x00", "\xFF") as $ch) {
288:                 if (ord($ch) < 32 && strpos("\r\n\t", $ch) === FALSE) $tableUtf[$ch] = $tableBin[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
289:                 elseif (ord($ch) < 127) $tableUtf[$ch] = $tableBin[$ch] = $ch;
290:                 else { $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch)); }
291:             }
292:             $tableBin["\\"] = '\\\\';
293:             $tableBin["\r"] = '\\r';
294:             $tableBin["\n"] = '\\n';
295:             $tableBin["\t"] = '\\t';
296:             $tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x';
297:         }
298: 
299:         if (is_bool($var)) {
300:             return ($var ? 'TRUE' : 'FALSE') . "\n";
301: 
302:         } elseif ($var === NULL) {
303:             return "NULL\n";
304: 
305:         } elseif (is_int($var)) {
306:             return "$var\n";
307: 
308:         } elseif (is_float($var)) {
309:             $var = (string) $var;
310:             if (strpos($var, '.') === FALSE) $var .= '.0';
311:             return "$var\n";
312: 
313:         } elseif (is_string($var)) {
314:             if (self::$maxLen && strlen($var) > self::$maxLen) {
315:                 $s = htmlSpecialChars(substr($var, 0, self::$maxLen), ENT_NOQUOTES) . ' ... ';
316:             } else {
317:                 $s = htmlSpecialChars($var, ENT_NOQUOTES);
318:             }
319:             $s = strtr($s, preg_match($reBinary, $s) || preg_last_error() ? $tableBin : $tableUtf);
320:             $len = strlen($var);
321:             return "\"$s\"" . ($len > 1 ? " ($len)" : "") . "\n";
322: 
323:         } elseif (is_array($var)) {
324:             $s = "<span>array</span>(" . count($var) . ") ";
325:             $space = str_repeat($space1 = '   ', $level);
326:             $brackets = range(0, count($var) - 1) === array_keys($var) ? "[]" : "{}";
327: 
328:             static $marker;
329:             if ($marker === NULL) $marker = uniqid("\x00", TRUE);
330:             if (empty($var)) {
331: 
332:             } elseif (isset($var[$marker])) {
333:                 $brackets = $var[$marker];
334:                 $s .= "$brackets[0] *RECURSION* $brackets[1]";
335: 
336:             } elseif ($level < self::$maxDepth || !self::$maxDepth) {
337:                 $s .= "<code>$brackets[0]\n";
338:                 $var[$marker] = $brackets;
339:                 foreach ($var as $k => &$v) {
340:                     if ($k === $marker) continue;
341:                     $k = is_int($k) ? $k : '"' . strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf) . '"';
342:                     $s .= "$space$space1$k => " . self::_dump($v, $level + 1);
343:                 }
344:                 unset($var[$marker]);
345:                 $s .= "$space$brackets[1]</code>";
346: 
347:             } else {
348:                 $s .= "$brackets[0] ... $brackets[1]";
349:             }
350:             return $s . "\n";
351: 
352:         } elseif (is_object($var)) {
353:             $arr = (array) $var;
354:             $s = "<span>" . get_class($var) . "</span>(" . count($arr) . ") ";
355:             $space = str_repeat($space1 = '   ', $level);
356: 
357:             static $list = array();
358:             if (empty($arr)) {
359: 
360:             } elseif (in_array($var, $list, TRUE)) {
361:                 $s .= "{ *RECURSION* }";
362: 
363:             } elseif ($level < self::$maxDepth || !self::$maxDepth) {
364:                 $s .= "<code>{\n";
365:                 $list[] = $var;
366:                 foreach ($arr as $k => &$v) {
367:                     $m = '';
368:                     if ($k[0] === "\x00") {
369:                         $m = $k[1] === '*' ? ' <span>protected</span>' : ' <span>private</span>';
370:                         $k = substr($k, strrpos($k, "\x00") + 1);
371:                     }
372:                     $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf);
373:                     $s .= "$space$space1\"$k\"$m => " . self::_dump($v, $level + 1);
374:                 }
375:                 array_pop($list);
376:                 $s .= "$space}</code>";
377: 
378:             } else {
379:                 $s .= "{ ... }";
380:             }
381:             return $s . "\n";
382: 
383:         } elseif (is_resource($var)) {
384:             return "<span>" . get_resource_type($var) . " resource</span>\n";
385: 
386:         } else {
387:             return "<span>unknown type</span>\n";
388:         }
389:     }
390: 
391: 
392: 
393:     /**
394:      * Starts/stops stopwatch.
395:      * @param  string  name
396:      * @return elapsed seconds
397:      */
398:     public static function timer($name = NULL)
399:     {
400:         static $time = array();
401:         $now = microtime(TRUE);
402:         $delta = isset($time[$name]) ? $now - $time[$name] : 0;
403:         $time[$name] = $now;
404:         return $delta;
405:     }
406: 
407: 
408: 
409:     /********************* errors and exceptions reporing ****************d*g**/
410: 
411: 
412: 
413:     /**
414:      * Enables displaying or logging errors and exceptions.
415:      * @param  mixed         production, development mode, autodetection or IP address(es).
416:      * @param  string        error log file (FALSE disables logging in production mode)
417:      * @param  array|string  administrator email or email headers; enables email sending in production mode
418:      * @return void
419:      */
420:     public static function enable($mode = NULL, $logFile = NULL, $email = NULL)
421:     {
422:         error_reporting(E_ALL | E_STRICT);
423: 
424:         // production/development mode detection
425:         if (is_bool($mode)) {
426:             self::$productionMode = $mode;
427: 
428:         } elseif (is_string($mode)) { // IP adresses
429:             $mode = preg_split('#[,\s]+#', $mode);
430:         }
431: 
432:         if (is_array($mode)) { // IP adresses
433:             self::$productionMode = !isset($_SERVER['REMOTE_ADDR']) || !in_array($_SERVER['REMOTE_ADDR'], $mode, TRUE);
434:         }
435: 
436:         if (self::$productionMode === self::DETECT) {
437:             if (class_exists('Nette\Environment')) {
438:                 self::$productionMode = Environment::isProduction();
439: 
440:             } elseif (isset($_SERVER['SERVER_ADDR']) || isset($_SERVER['LOCAL_ADDR'])) { // IP address based detection
441:                 $addr = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR'];
442:                 $oct = explode('.', $addr);
443:                 self::$productionMode = $addr !== '::1' && (count($oct) !== 4 || ($oct[0] !== '10' && $oct[0] !== '127' && ($oct[0] !== '172' || $oct[1] < 16 || $oct[1] > 31)
444:                     && ($oct[0] !== '169' || $oct[1] !== '254') && ($oct[0] !== '192' || $oct[1] !== '168')));
445: 
446:             } else {
447:                 self::$productionMode = !self::$consoleMode;
448:             }
449:         }
450: 
451:         // logging configuration
452:         if (self::$productionMode && $logFile !== FALSE) {
453:             self::$logFile = 'log/php_error.log';
454: 
455:             if (class_exists('Nette\Environment')) {
456:                 if (is_string($logFile)) {
457:                     self::$logFile = Environment::expand($logFile);
458: 
459:                 } else try {
460:                     self::$logFile = Environment::expand('%logDir%/php_error.log');
461: 
462:                 } catch (\InvalidStateException $e) {
463:                 }
464: 
465:             } elseif (is_string($logFile)) {
466:                 self::$logFile = $logFile;
467:             }
468: 
469:             ini_set('error_log', self::$logFile);
470:         }
471: 
472:         // php configuration
473:         if (function_exists('ini_set')) {
474:             ini_set('display_errors', !self::$productionMode); // or 'stderr'
475:             ini_set('html_errors', !self::$logFile && !self::$consoleMode);
476:             ini_set('log_errors', (bool) self::$logFile);
477: 
478:         } elseif (ini_get('log_errors') != (bool) self::$logFile || // intentionally ==
479:             (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout'))) {
480:             throw new \LogicException('Function ini_set() must be enabled.');
481:         }
482: 
483:         self::$sendEmails = self::$logFile && $email;
484:         if (self::$sendEmails) {
485:             if (is_string($email)) {
486:                 self::$emailHeaders['To'] = $email;
487: 
488:             } elseif (is_array($email)) {
489:                 self::$emailHeaders = $email + self::$emailHeaders;
490:             }
491:         }
492: 
493:         if (!defined('E_DEPRECATED')) {
494:             define('E_DEPRECATED', 8192);
495:         }
496: 
497:         if (!defined('E_USER_DEPRECATED')) {
498:             define('E_USER_DEPRECATED', 16384);
499:         }
500: 
501:         set_exception_handler(array(__CLASS__, '_exceptionHandler'));
502:         set_error_handler(array(__CLASS__, '_errorHandler'));
503:         self::$enabled = TRUE;
504:     }
505: 
506: 
507: 
508:     /**
509:      * Is Debug enabled?
510:      * @return bool
511:      */
512:     public static function isEnabled()
513:     {
514:         return self::$enabled;
515:     }
516: 
517: 
518: 
519:     /**
520:      * Debug exception handler.
521:      * @param  \Exception
522:      * @return void
523:      * @internal
524:      */
525:     public static function _exceptionHandler(\Exception $exception)
526:     {
527:         if (!headers_sent()) {
528:             header('HTTP/1.1 500 Internal Server Error');
529:         }
530:         self::processException($exception, TRUE);
531:         exit;
532:     }
533: 
534: 
535: 
536:     /**
537:      * Own error handler.
538:      * @param  int    level of the error raised
539:      * @param  string error message
540:      * @param  string file that the error was raised in
541:      * @param  int    line number the error was raised at
542:      * @param  array  an array of variables that existed in the scope the error was triggered in
543:      * @return bool   FALSE to call normal error handler, NULL otherwise
544:      * @throws \FatalErrorException
545:      * @internal
546:      */
547:     public static function _errorHandler($severity, $message, $file, $line, $context)
548:     {
549:         if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) {
550:             throw new \FatalErrorException($message, 0, $severity, $file, $line, $context);
551: 
552:         } elseif (($severity & error_reporting()) !== $severity) {
553:             return NULL; // nothing to do
554: 
555:         } elseif (self::$strictMode) {
556:             self::_exceptionHandler(new \FatalErrorException($message, 0, $severity, $file, $line, $context), TRUE);
557:         }
558: 
559:         static $types = array(
560:             E_WARNING => 'Warning',
561:             E_USER_WARNING => 'Warning',
562:             E_NOTICE => 'Notice',
563:             E_USER_NOTICE => 'Notice',
564:             E_STRICT => 'Strict standards',
565:             E_DEPRECATED => 'Deprecated',
566:             E_USER_DEPRECATED => 'Deprecated',
567:         );
568: 
569:         $type = isset($types[$severity]) ? $types[$severity] : 'Unknown error';
570: 
571:         if (self::$logFile) {
572:             if (self::$sendEmails) {
573:                 self::sendEmail("$type: $message in $file on line $line");
574:             }
575:             return FALSE; // call normal error handler
576: 
577:         } elseif (!self::$productionMode && self::$firebugDetected && !headers_sent()) {
578:             $message = strip_tags($message);
579:             self::fireLog("$type: $message in $file on line $line", self::ERROR);
580:             return NULL;
581:         }
582: 
583:         return FALSE; // call normal error handler
584:     }
585: 
586: 
587: 
588:     /**
589:      * Logs or displays exception.
590:      * @param  \Exception
591:      * @param  bool  is writing to standard output buffer allowed?
592:      * @return void
593:      */
594:     public static function processException(\Exception $exception, $outputAllowed = FALSE)
595:     {
596:         if (!self::$enabled) {
597:             return;
598: 
599:         } elseif (self::$logFile) {
600:             try {
601:                 $hash = md5($exception );
602:                 error_log("PHP Fatal error:  Uncaught $exception");
603:                 foreach (new \DirectoryIterator(dirname(self::$logFile)) as $entry) {
604:                     if (strpos($entry, $hash)) {
605:                         $skip = TRUE;
606:                         break;
607:                     }
608:                 }
609:                 $file = dirname(self::$logFile) . "/exception " . @date('Y-m-d H-i-s') . " $hash.html";
610:                 if (empty($skip) && self::$logHandle = @fopen($file, 'x')) {
611:                     ob_start(); // double buffer prevents sending HTTP headers in some PHP
612:                     ob_start(array(__CLASS__, '_writeFile'), 1);
613:                     self::_paintBlueScreen($exception);
614:                     ob_end_flush();
615:                     ob_end_clean();
616:                     fclose(self::$logHandle);
617:                 }
618:                 if (self::$sendEmails) {
619:                     self::sendEmail((string) $exception);
620:                 }
621:             } catch (\Exception $e) {
622:                 if (!headers_sent()) {
623:                     header('HTTP/1.1 500 Internal Server Error');
624:                 }
625:                 echo 'Nette\Debug fatal error: ', get_class($e), ': ', ($e->getCode() ? '#' . $e->getCode() . ' ' : '') . $e->getMessage(), "\n";
626:                 exit;
627:             }
628: 
629:         } elseif (self::$productionMode) {
630:             // be quiet
631: 
632:         } elseif (self::$consoleMode) { // dump to console
633:             if ($outputAllowed) {
634:                 echo "$exception\n";
635:                 foreach (self::$colophons as $callback) {
636:                     foreach ((array) call_user_func($callback, 'bluescreen') as $line) echo strip_tags($line) . "\n";
637:                 }
638:             }
639: 
640:         } elseif (self::$firebugDetected && self::$ajaxDetected && !headers_sent()) { // AJAX mode
641:             self::fireLog($exception, self::EXCEPTION);
642: 
643:         } elseif ($outputAllowed) { // dump to browser
644:             self::_paintBlueScreen($exception);
645: 
646:         } elseif (self::$firebugDetected && !headers_sent()) {
647:             self::fireLog($exception, self::EXCEPTION);
648:         }
649: 
650:         foreach (self::$onFatalError as $handler) {
651:             call_user_func($handler, $exception);
652:         }
653:     }
654: 
655: 
656: 
657:     /**
658:      * Handles exception throwed in __toString().
659:      * @param  \Exception
660:      * @return void
661:      */
662:     public static function toStringException(\Exception $exception)
663:     {
664:         if (self::$enabled) {
665:             self::_exceptionHandler($exception);
666:         } else {
667:             trigger_error($exception->getMessage(), E_USER_ERROR);
668:         }
669:     }
670: 
671: 
672: 
673:     /**
674:      * Paint blue screen.
675:      * @param  \Exception
676:      * @return void
677:      * @internal
678:      */
679:     public static function _paintBlueScreen(\Exception $exception)
680:     {
681:         if (class_exists('Nette\Environment', FALSE)) {
682:             $application = Environment::getServiceLocator()->hasService('Nette\\Application\\Application', TRUE) ? Environment::getServiceLocator()->getService('Nette\\Application\\Application') : NULL;
683:         }
684: 
685:         $colophons = self::$colophons;
686:         require __DIR__ . '/templates/bluescreen.phtml';
687:     }
688: 
689: 
690: 
691:     /**
692:      * Redirects output to file.
693:      * @param  string
694:      * @return string
695:      * @internal
696:      */
697:     public static function _writeFile($buffer)
698:     {
699:         fwrite(self::$logHandle, $buffer);
700:     }
701: 
702: 
703: 
704:     /**
705:      * Sends e-mail notification.
706:      * @param  string
707:      * @return void
708:      */
709:     private static function sendEmail($message)
710:     {
711:         $monitorFile = self::$logFile . '.monitor';
712:         if (@filemtime($monitorFile) + self::$emailSnooze < time() // @ - file may not exist
713:             && @file_put_contents($monitorFile, 'sent')) { // @ - file may not be writable
714:             call_user_func(self::$mailer, $message);
715:         }
716:     }
717: 
718: 
719: 
720:     /**
721:      * Default mailer.
722:      * @param  string
723:      * @return void
724:      */
725:     private static function defaultMailer($message)
726:     {
727:         $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] :
728:                 (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '');
729: 
730:         $headers = str_replace(
731:             array('%host%', '%date%', '%message%'),
732:             array($host, @date('Y-m-d H:i:s', self::$time), $message), // @ - timezone may not be set
733:             self::$emailHeaders
734:         );
735: 
736:         $subject = $headers['Subject'];
737:         $to = $headers['To'];
738:         $body = str_replace("\r\n", PHP_EOL, $headers['Body']);
739:         unset($headers['Subject'], $headers['To'], $headers['Body']);
740:         $header = '';
741:         foreach ($headers as $key => $value) {
742:             $header .= "$key: $value" . PHP_EOL;
743:         }
744: 
745:         mail($to, $subject, $body, $header);
746:     }
747: 
748: 
749: 
750:     /********************* profiler ****************d*g**/
751: 
752: 
753: 
754:     /**
755:      * Enables profiler.
756:      * @return void
757:      */
758:     public static function enableProfiler()
759:     {
760:         self::$enabledProfiler = TRUE;
761:     }
762: 
763: 
764: 
765:     /**
766:      * Disables profiler.
767:      * @return void
768:      */
769:     public static function disableProfiler()
770:     {
771:         self::$enabledProfiler = FALSE;
772:     }
773: 
774: 
775: 
776:     /********************* colophons ****************d*g**/
777: 
778: 
779: 
780:     /**
781:      * Add custom descriptions.
782:      * @param  callback
783:      * @return void
784:      */
785:     public static function addColophon($callback)
786:     {
787:         if (!is_callable($callback)) {
788:             $able = is_callable($callback, TRUE, $textual);
789:             throw new \InvalidArgumentException("Colophon handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.'));
790:         }
791: 
792:         if (!in_array($callback, self::$colophons, TRUE)) {
793:             self::$colophons[] = $callback;
794:         }
795:     }
796: 
797: 
798: 
799:     /**
800:      * Returns default colophons.
801:      * @param  string  profiler | bluescreen
802:      * @return array
803:      */
804:     private static function getDefaultColophons($sender)
805:     {
806:         if ($sender === 'profiler') {
807:             $arr[] = 'Elapsed time: <b>' . number_format((microtime(TRUE) - self::$time) * 1000, 1, '.', ' ') . '</b> ms | Allocated memory: <b>' . number_format(memory_get_peak_usage() / 1000, 1, '.', ' ') . '</b> kB';
808: 
809:             foreach ((array) self::$counters as $name => $value) {
810:                 if (is_array($value)) $value = implode(', ', $value);
811:                 $arr[] = htmlSpecialChars($name) . ' = <strong>' . htmlSpecialChars($value) . '</strong>';
812:             }
813: 
814:             $autoloaded = class_exists('Nette\Loaders\AutoLoader', FALSE) ? Nette\Loaders\AutoLoader::$count : 0;
815:             $s = '<span>' . count(get_included_files()) . '/' .  $autoloaded . ' files</span>, ';
816: 
817:             $exclude = array('stdClass', 'Exception', 'ErrorException', 'Traversable', 'IteratorAggregate', 'Iterator', 'ArrayAccess', 'Serializable', 'Closure');
818:             foreach (get_loaded_extensions() as $ext) {
819:                 $ref = new \ReflectionExtension($ext);
820:                 $exclude = array_merge($exclude, $ref->getClassNames());
821:             }
822:             $classes = array_diff(get_declared_classes(), $exclude);
823:             $intf = array_diff(get_declared_interfaces(), $exclude);
824:             $func = get_defined_functions();
825:             $func = (array) @$func['user'];
826:             $consts = get_defined_constants(TRUE);
827:             $consts = array_keys((array) @$consts['user']);
828:             foreach (array('classes', 'intf', 'func', 'consts') as $item) {
829:                 $s .= '<span ' . ($$item ? 'title="' . implode(", ", $$item) . '"' : '') . '>' . count($$item) . ' ' . $item . '</span>, ';
830:             }
831:             $arr[] = $s;
832:         }
833: 
834:         if ($sender === 'bluescreen') {
835:             $arr[] = 'Report generated at ' . @date('Y/m/d H:i:s', self::$time); // intentionally @
836:             if (isset($_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'])) {
837:                 $url = (isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://') . htmlSpecialChars($_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
838:                 $arr[] = '<a href="' . $url . '">' . $url . '</a>';
839:             }
840:             $arr[] = 'PHP ' . htmlSpecialChars(PHP_VERSION);
841:             if (isset($_SERVER['SERVER_SOFTWARE'])) $arr[] = htmlSpecialChars($_SERVER['SERVER_SOFTWARE']);
842:             if (class_exists('Nette\Framework')) $arr[] = htmlSpecialChars('Nette Framework ' . Framework::VERSION) . ' <i>(revision ' . htmlSpecialChars(Framework::REVISION) . ')</i>';
843:         }
844:         return $arr;
845:     }
846: 
847: 
848: 
849:     /********************* Firebug extension ****************d*g**/
850: 
851: 
852: 
853:     /**
854:      * Sends variable dump to Firebug tab request/server.
855:      * @param  mixed  variable to dump
856:      * @param  string unique key
857:      * @return mixed  variable itself
858:      */
859:     public static function fireDump($var, $key)
860:     {
861:         self::fireSend('Dump/0.1', array((string) $key => $var));
862:         return $var;
863:     }
864: 
865: 
866: 
867:     /**
868:      * Sends message to Firebug console.
869:      * @param  mixed   message to log
870:      * @param  string  priority of message (LOG, INFO, WARN, ERROR, GROUP_START, GROUP_END)
871:      * @param  string  optional label
872:      * @return bool    was successful?
873:      */
874:     public static function fireLog($message, $priority = self::LOG, $label = NULL)
875:     {
876:         if ($message instanceof \Exception) {
877:             if ($priority !== self::EXCEPTION && $priority !== self::TRACE) {
878:                 $priority = self::TRACE;
879:             }
880:             $message = array(
881:                 'Class' => get_class($message),
882:                 'Message' => $message->getMessage(),
883:                 'File' => $message->getFile(),
884:                 'Line' => $message->getLine(),
885:                 'Trace' => $message->getTrace(),
886:                 'Type' => '',
887:                 'Function' => '',
888:             );
889:             foreach ($message['Trace'] as & $row) {
890:                 if (empty($row['file'])) $row['file'] = '?';
891:                 if (empty($row['line'])) $row['line'] = '?';
892:             }
893:         } elseif ($priority === self::GROUP_START) {
894:             $label = $message;
895:             $message = NULL;
896:         }
897:         return self::fireSend('FirebugConsole/0.1', self::replaceObjects(array(array('Type' => $priority, 'Label' => $label), $message)));
898:     }
899: 
900: 
901: 
902:     /**
903:      * Performs Firebug output.
904:      * @see http://www.firephp.org
905:      * @param  string  structure
906:      * @param  array   payload
907:      * @return bool    was successful?
908:      */
909:     private static function fireSend($struct, $payload)
910:     {
911:         if (self::$productionMode) return NULL;
912: 
913:         if (headers_sent()) return FALSE; // or throw exception?
914: 
915:         header('X-Wf-Protocol-nette: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
916:         header('X-Wf-nette-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
917: 
918:         static $structures;
919:         $index = isset($structures[$struct]) ? $structures[$struct] : ($structures[$struct] = count($structures) + 1);
920:         header("X-Wf-nette-Structure-$index: http://meta.firephp.org/Wildfire/Structure/FirePHP/$struct");
921: 
922:         $payload = json_encode($payload);
923:         static $counter;
924:         foreach (str_split($payload, 4990) as $s) {
925:             $num = ++$counter;
926:             header("X-Wf-nette-$index-1-n$num: |$s|\\");
927:         }
928:         header("X-Wf-nette-$index-1-n$num: |$s|");
929: 
930:         return TRUE;
931:     }
932: 
933: 
934: 
935:     /**
936:      * fireLog helper.
937:      * @param  mixed
938:      * @return mixed
939:      */
940:     static private function replaceObjects($val)
941:     {
942:         if (is_object($val)) {
943:             return 'object ' . get_class($val) . '';
944: 
945:         } elseif (is_string($val)) {
946:             return @iconv('UTF-16', 'UTF-8//IGNORE', iconv('UTF-8', 'UTF-16//IGNORE', $val)); // intentionally @
947: 
948:         } elseif (is_array($val)) {
949:             foreach ($val as $k => $v) {
950:                 unset($val[$k]);
951:                 $k = @iconv('UTF-16', 'UTF-8//IGNORE', iconv('UTF-8', 'UTF-16//IGNORE', $k)); // intentionally @
952:                 $val[$k] = self::replaceObjects($v);
953:             }
954:         }
955: 
956:         return $val;
957:     }
958: 
959: }
960: 
961: 
962: 
963: Debug::_init();
964: 
Nette Framework 0.9.7 API documentation generated by ApiGen 2.3.0