Packages

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

Classes

  • NArrayTools
  • NCallback
  • NComponent
  • NComponentContainer
  • NConfigurator
  • NDateTime53
  • NDebug
  • NEnvironment
  • NFramework
  • NFreezableObject
  • NGenericRecursiveIterator
  • NImage
  • NImageMagick
  • NInstanceFilterIterator
  • NObject
  • NObjectMixin
  • NPaginator
  • NRecursiveComponentIterator
  • NServiceLocator
  • NSmartCachingIterator
  • NString
  • NTools

Interfaces

  • IComponent
  • IComponentContainer
  • IDebuggable
  • IServiceLocator
  • ITranslator

Exceptions

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