Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • none

Classes

  • Bar
  • BlueScreen
  • Debugger
  • FireLogger
  • Helpers
  • Logger

Interfaces

  • IBarPanel
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Diagnostics;
  9: 
 10: use Nette;
 11: 
 12: 
 13: /**
 14:  * Debugger: displays and logs errors.
 15:  *
 16:  * Behavior is determined by two factors: mode & output
 17:  * - modes: production / development
 18:  * - output: HTML / AJAX / CLI / other (e.g. XML)
 19:  *
 20:  * @author     David Grudl
 21:  */
 22: class Debugger
 23: {
 24:     /** @var bool in production mode is suppressed any debugging output */
 25:     public static $productionMode;
 26: 
 27:     /** @var bool in console mode is omitted HTML output */
 28:     public static $consoleMode;
 29: 
 30:     /** @var int timestamp with microseconds of the start of the request */
 31:     public static $time;
 32: 
 33:     /** @var bool is AJAX request detected? */
 34:     private static $ajaxDetected;
 35: 
 36:     /** @var string  requested URI or command line */
 37:     public static $source;
 38: 
 39:     /** @var string URL pattern mask to open editor */
 40:     public static $editor = 'editor://open/?file=%file&line=%line';
 41: 
 42:     /** @var string command to open browser (use 'start ""' in Windows) */
 43:     public static $browser;
 44: 
 45:     /********************* Debugger::dump() ****************d*g**/
 46: 
 47:     /** @var int  how many nested levels of array/object properties display {@link Debugger::dump()} */
 48:     public static $maxDepth = 3;
 49: 
 50:     /** @var int  how long strings display {@link Debugger::dump()} */
 51:     public static $maxLen = 150;
 52: 
 53:     /** @var bool display location? {@link Debugger::dump()} */
 54:     public static $showLocation = FALSE;
 55: 
 56:     /** @var array */
 57:     public static $consoleColors = array(
 58:         'bool' => '1;33',
 59:         'null' => '1;33',
 60:         'int' => '1;36',
 61:         'float' => '1;36',
 62:         'string' => '1;32',
 63:         'array' => '1;31',
 64:         'key' => '1;37',
 65:         'object' => '1;31',
 66:         'visibility' => '1;30',
 67:         'resource' => '1;37',
 68:     );
 69: 
 70:     /********************* errors and exceptions reporting ****************d*g**/
 71: 
 72:     /** server modes {@link Debugger::enable()} */
 73:     const DEVELOPMENT = FALSE,
 74:         PRODUCTION = TRUE,
 75:         DETECT = NULL;
 76: 
 77:     /** @var BlueScreen */
 78:     public static $blueScreen;
 79: 
 80:     /** @var bool|int determines whether any error will cause immediate death; if integer that it's matched against error severity */
 81:     public static $strictMode = FALSE; // $immediateDeath
 82: 
 83:     /** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */
 84:     public static $scream = FALSE;
 85: 
 86:     /** @var array of callables specifies the functions that are automatically called after fatal error */
 87:     public static $onFatalError = array();
 88: 
 89:     /** @var bool {@link Debugger::enable()} */
 90:     private static $enabled = FALSE;
 91: 
 92:     /** @var mixed {@link Debugger::tryError()} FALSE means catching is disabled */
 93:     private static $lastError = FALSE;
 94: 
 95:     /********************* logging ****************d*g**/
 96: 
 97:     /** @var Logger */
 98:     public static $logger;
 99: 
100:     /** @var FireLogger */
101:     public static $fireLogger;
102: 
103:     /** @var string name of the directory where errors should be logged; FALSE means that logging is disabled */
104:     public static $logDirectory;
105: 
106:     /** @var string email to sent error notifications */
107:     public static $email;
108: 
109:     /** @deprecated */
110:     public static $mailer;
111: 
112:     /** @deprecated */
113:     public static $emailSnooze;
114: 
115:     /********************* debug bar ****************d*g**/
116: 
117:     /** @var Bar */
118:     public static $bar;
119: 
120:     /** @var DefaultBarPanel */
121:     private static $errorPanel;
122: 
123:     /** @var DefaultBarPanel */
124:     private static $dumpPanel;
125: 
126:     /********************* Firebug extension ****************d*g**/
127: 
128:     /** {@link Debugger::log()} and {@link Debugger::fireLog()} */
129:     const DEBUG = 'debug',
130:         INFO = 'info',
131:         WARNING = 'warning',
132:         ERROR = 'error',
133:         CRITICAL = 'critical';
134: 
135: 
136:     /**
137:      * Static class - cannot be instantiated.
138:      */
139:     final public function __construct()
140:     {
141:         throw new Nette\StaticClassException;
142:     }
143: 
144: 
145:     /**
146:      * Static class constructor.
147:      * @internal
148:      */
149:     public static function _init()
150:     {
151:         self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(TRUE);
152:         self::$consoleMode = PHP_SAPI === 'cli';
153:         self::$productionMode = self::DETECT;
154:         if (self::$consoleMode) {
155:             self::$source = empty($_SERVER['argv']) ? 'cli' : 'cli: ' . implode(' ', $_SERVER['argv']);
156:         } else {
157:             self::$ajaxDetected = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
158:             if (isset($_SERVER['REQUEST_URI'])) {
159:                 self::$source = (!empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://')
160:                     . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : ''))
161:                     . $_SERVER['REQUEST_URI'];
162:             }
163:         }
164: 
165:         self::$logger = new Logger;
166:         self::$logDirectory = & self::$logger->directory;
167:         self::$email = & self::$logger->email;
168:         self::$mailer = & self::$logger->mailer;
169:         self::$emailSnooze = & Logger::$emailSnooze;
170: 
171:         self::$fireLogger = new FireLogger;
172: 
173:         self::$blueScreen = new BlueScreen;
174:         self::$blueScreen->addPanel(function($e) {
175:             if ($e instanceof Nette\Templating\FilterException) {
176:                 return array(
177:                     'tab' => 'Template',
178:                     'panel' => '<p><b>File:</b> ' . Helpers::editorLink($e->sourceFile, $e->sourceLine)
179:                     . '&nbsp; <b>Line:</b> ' . ($e->sourceLine ? $e->sourceLine : 'n/a') . '</p>'
180:                     . ($e->sourceLine ? BlueScreen::highlightFile($e->sourceFile, $e->sourceLine) : '')
181:                 );
182:             } elseif ($e instanceof Nette\Utils\NeonException && preg_match('#line (\d+)#', $e->getMessage(), $m)) {
183:                 if ($item = Helpers::findTrace($e->getTrace(), 'Nette\Config\Adapters\NeonAdapter::load')) {
184:                     return array(
185:                         'tab' => 'NEON',
186:                         'panel' => '<p><b>File:</b> ' . Helpers::editorLink($item['args'][0], $m[1]) . '&nbsp; <b>Line:</b> ' . $m[1] . '</p>'
187:                             . BlueScreen::highlightFile($item['args'][0], $m[1])
188:                     );
189:                 } elseif ($item = Helpers::findTrace($e->getTrace(), 'Nette\Utils\Neon::decode')) {
190:                     return array(
191:                         'tab' => 'NEON',
192:                         'panel' => BlueScreen::highlightPhp($item['args'][0], $m[1])
193:                     );
194:                 }
195:             }
196:         });
197: 
198:         self::$bar = new Bar;
199:         self::$bar->addPanel(new DefaultBarPanel('time'));
200:         self::$bar->addPanel(new DefaultBarPanel('memory'));
201:         self::$bar->addPanel(self::$errorPanel = new DefaultBarPanel('errors')); // filled by _errorHandler()
202:         self::$bar->addPanel(self::$dumpPanel = new DefaultBarPanel('dumps')); // filled by barDump()
203:     }
204: 
205: 
206:     /********************* errors and exceptions reporting ****************d*g**/
207: 
208: 
209:     /**
210:      * Enables displaying or logging errors and exceptions.
211:      * @param  mixed         production, development mode, autodetection or IP address(es) whitelist.
212:      * @param  string        error log directory; enables logging in production mode, FALSE means that logging is disabled
213:      * @param  string        administrator email; enables email sending in production mode
214:      * @return void
215:      */
216:     public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL)
217:     {
218:         error_reporting(E_ALL | E_STRICT);
219: 
220:         // production/development mode detection
221:         if (is_bool($mode)) {
222:             self::$productionMode = $mode;
223: 
224:         } elseif ($mode !== self::DETECT || self::$productionMode === NULL) { // IP addresses or computer names whitelist detection
225:             $list = is_string($mode) ? preg_split('#[,\s]+#', $mode) : (array) $mode;
226:             if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
227:                 $list[] = '127.0.0.1';
228:                 $list[] = '::1';
229:             }
230:             self::$productionMode = !in_array(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : php_uname('n'), $list, TRUE);
231:         }
232: 
233:         // logging configuration
234:         if (is_string($logDirectory)) {
235:             self::$logDirectory = realpath($logDirectory);
236:             if (self::$logDirectory === FALSE) {
237:                 echo __METHOD__ . "() error: Log directory is not found or is not directory.\n";
238:                 exit(254);
239:             }
240:         } elseif ($logDirectory === FALSE) {
241:             self::$logDirectory = FALSE;
242: 
243:         } elseif (self::$logDirectory === NULL) {
244:             self::$logDirectory = defined('APP_DIR') ? APP_DIR . '/../log' : getcwd() . '/log';
245:         }
246:         if (self::$logDirectory) {
247:             ini_set('error_log', self::$logDirectory . '/php_error.log');
248:         }
249: 
250:         // php configuration
251:         if (function_exists('ini_set')) {
252:             ini_set('display_errors', !self::$productionMode); // or 'stderr'
253:             ini_set('html_errors', FALSE);
254:             ini_set('log_errors', FALSE);
255: 
256:         } elseif (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout')) { // intentionally ==
257:             echo __METHOD__ . "() error: Unable to set 'display_errors' because function ini_set() is disabled.\n";
258:             exit(254);
259:         }
260: 
261:         if ($email) {
262:             if (!is_string($email)) {
263:                 echo __METHOD__ . "() error: Email address must be a string.\n";
264:                 exit(254);
265:             }
266:             self::$email = $email;
267:         }
268: 
269:         if (!defined('E_DEPRECATED')) {
270:             define('E_DEPRECATED', 8192);
271:         }
272: 
273:         if (!defined('E_USER_DEPRECATED')) {
274:             define('E_USER_DEPRECATED', 16384);
275:         }
276: 
277:         if (!self::$enabled) {
278:             register_shutdown_function(array(__CLASS__, '_shutdownHandler'));
279:             set_exception_handler(array(__CLASS__, '_exceptionHandler'));
280:             set_error_handler(array(__CLASS__, '_errorHandler'));
281:             self::$enabled = TRUE;
282:         }
283:     }
284: 
285: 
286:     /**
287:      * Is Debug enabled?
288:      * @return bool
289:      */
290:     public static function isEnabled()
291:     {
292:         return self::$enabled;
293:     }
294: 
295: 
296:     /**
297:      * Logs message or exception to file (if not disabled) and sends email notification (if enabled).
298:      * @param  string|Exception
299:      * @param  int  one of constant Debugger::INFO, WARNING, ERROR (sends email), CRITICAL (sends email)
300:      * @return string logged error filename
301:      */
302:     public static function log($message, $priority = self::INFO)
303:     {
304:         if (self::$logDirectory === FALSE) {
305:             return;
306: 
307:         } elseif (!self::$logDirectory) {
308:             throw new Nette\InvalidStateException('Logging directory is not specified in Nette\Diagnostics\Debugger::$logDirectory.');
309:         }
310: 
311:         if ($message instanceof \Exception) {
312:             $exception = $message;
313:             $message = ($message instanceof Nette\FatalErrorException
314:                 ? 'Fatal error: ' . $exception->getMessage()
315:                 : get_class($exception) . ": " . $exception->getMessage())
316:                 . " in " . $exception->getFile() . ":" . $exception->getLine();
317: 
318:             $hash = md5(preg_replace('~(Resource id #)\d+~', '$1', $exception ));
319:             $exceptionFilename = "exception-" . @date('Y-m-d-H-i-s') . "-$hash.html";
320:             foreach (new \DirectoryIterator(self::$logDirectory) as $entry) {
321:                 if (strpos($entry, $hash)) {
322:                     $exceptionFilename = $entry;
323:                     $saved = TRUE;
324:                     break;
325:                 }
326:             }
327:         }
328: 
329:         self::$logger->log(array(
330:             @date('[Y-m-d H-i-s]'),
331:             trim($message),
332:             self::$source ? ' @  ' . self::$source : NULL,
333:             !empty($exceptionFilename) ? ' @@  ' . $exceptionFilename : NULL
334:         ), $priority);
335: 
336:         if (!empty($exceptionFilename)) {
337:             $exceptionFilename = self::$logDirectory . '/' . $exceptionFilename;
338:             if (empty($saved) && $logHandle = @fopen($exceptionFilename, 'w')) {
339:                 ob_start(); // double buffer prevents sending HTTP headers in some PHP
340:                 ob_start(function($buffer) use ($logHandle) { fwrite($logHandle, $buffer); }, 4096);
341:                 self::$blueScreen->render($exception);
342:                 ob_end_flush();
343:                 ob_end_clean();
344:                 fclose($logHandle);
345:             }
346:             return strtr($exceptionFilename, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR);
347:         }
348:     }
349: 
350: 
351:     /**
352:      * Shutdown handler to catch fatal errors and execute of the planned activities.
353:      * @return void
354:      * @internal
355:      */
356:     public static function _shutdownHandler()
357:     {
358:         if (!self::$enabled) {
359:             return;
360:         }
361: 
362:         // fatal error handler
363:         static $types = array(
364:             E_ERROR => 1,
365:             E_CORE_ERROR => 1,
366:             E_COMPILE_ERROR => 1,
367:             E_PARSE => 1,
368:         );
369:         $error = error_get_last();
370:         if (isset($types[$error['type']])) {
371:             self::_exceptionHandler(new Nette\FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL));
372:         }
373: 
374:         // debug bar (require HTML & development mode)
375:         if (self::$bar && !self::$productionMode && self::isHtmlMode()) {
376:             self::$bar->render();
377:         }
378:     }
379: 
380: 
381:     /**
382:      * Handler to catch uncaught exception.
383:      * @param  \Exception
384:      * @return void
385:      * @internal
386:      */
387:     public static function _exceptionHandler(\Exception $exception)
388:     {
389:         if (!headers_sent()) { // for PHP < 5.2.4
390:             $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
391:             $code = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== FALSE ? 503 : 500;
392:             header("$protocol $code", TRUE, $code);
393:         }
394: 
395:         try {
396:             if (self::$productionMode) {
397:                 try {
398:                     self::log($exception, self::ERROR);
399:                 } catch (\Exception $e) {
400:                     echo 'FATAL ERROR: unable to log error';
401:                 }
402: 
403:                 if (self::$consoleMode) {
404:                     echo "ERROR: the server encountered an internal error and was unable to complete your request.\n";
405: 
406:                 } elseif (self::isHtmlMode()) {
407:                     require __DIR__ . '/templates/error.phtml';
408:                 }
409: 
410:             } else {
411:                 if (self::$consoleMode) { // dump to console
412:                     echo "$exception\n";
413:                     if ($file = self::log($exception)) {
414:                         echo "(stored in $file)\n";
415:                         if (self::$browser) {
416:                             exec(self::$browser . ' ' . escapeshellarg($file));
417:                         }
418:                     }
419: 
420:                 } elseif (self::isHtmlMode()) { // dump to browser
421:                     self::$blueScreen->render($exception);
422:                     if (self::$bar) {
423:                         self::$bar->render();
424:                     }
425: 
426:                 } elseif (!self::fireLog($exception)) { // AJAX or non-HTML mode
427:                     $file = self::log($exception, self::ERROR);
428:                     if (!headers_sent()) {
429:                         header("X-Nette-Error-Log: $file");
430:                     }
431:                 }
432:             }
433: 
434:             foreach (self::$onFatalError as $handler) {
435:                 call_user_func($handler, $exception);
436:             }
437: 
438:         } catch (\Exception $e) {
439:             if (self::$productionMode) {
440:                 echo self::isHtmlMode() ? '<meta name=robots content=noindex>FATAL ERROR' : 'FATAL ERROR';
441:             } else {
442:                 echo "FATAL ERROR: thrown ", get_class($e), ': ', $e->getMessage(),
443:                     "\nwhile processing ", get_class($exception), ': ', $exception->getMessage(), "\n";
444:             }
445:         }
446: 
447:         self::$enabled = FALSE; // un-register shutdown function
448:         exit(254);
449:     }
450: 
451: 
452:     /**
453:      * Handler to catch warnings and notices.
454:      * @param  int    level of the error raised
455:      * @param  string error message
456:      * @param  string file that the error was raised in
457:      * @param  int    line number the error was raised at
458:      * @param  array  an array of variables that existed in the scope the error was triggered in
459:      * @return bool   FALSE to call normal error handler, NULL otherwise
460:      * @throws Nette\FatalErrorException
461:      * @internal
462:      */
463:     public static function _errorHandler($severity, $message, $file, $line, $context)
464:     {
465:         if (self::$scream) {
466:             error_reporting(E_ALL | E_STRICT);
467:         }
468: 
469:         if (self::$lastError !== FALSE && ($severity & error_reporting()) === $severity) { // tryError mode
470:             self::$lastError = new \ErrorException($message, 0, $severity, $file, $line);
471:             return NULL;
472:         }
473: 
474:         if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) {
475:             if (Helpers::findTrace(debug_backtrace(FALSE), '*::__toString')) {
476:                 $previous = isset($context['e']) && $context['e'] instanceof \Exception ? $context['e'] : NULL;
477:                 self::_exceptionHandler(new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context, $previous));
478:             }
479:             throw new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context);
480: 
481:         } elseif (($severity & error_reporting()) !== $severity) {
482:             return FALSE; // calls normal error handler to fill-in error_get_last()
483: 
484:         } elseif (!self::$productionMode && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity))) {
485:             self::_exceptionHandler(new Nette\FatalErrorException($message, 0, $severity, $file, $line, $context));
486:         }
487: 
488:         static $types = array(
489:             E_WARNING => 'Warning',
490:             E_COMPILE_WARNING => 'Warning', // currently unable to handle
491:             E_USER_WARNING => 'Warning',
492:             E_NOTICE => 'Notice',
493:             E_USER_NOTICE => 'Notice',
494:             E_STRICT => 'Strict standards',
495:             E_DEPRECATED => 'Deprecated',
496:             E_USER_DEPRECATED => 'Deprecated',
497:         );
498: 
499:         $message = 'PHP ' . (isset($types[$severity]) ? $types[$severity] : 'Unknown error') . ": $message";
500:         $count = & self::$errorPanel->data["$file|$line|$message"];
501: 
502:         if ($count++) { // repeated error
503:             return NULL;
504: 
505:         } elseif (self::$productionMode) {
506:             self::log("$message in $file:$line", self::ERROR);
507:             return NULL;
508: 
509:         } else {
510:             $ok = self::fireLog(new \ErrorException($message, 0, $severity, $file, $line));
511:             return !self::isHtmlMode() || (!self::$bar && !$ok) ? FALSE : NULL;
512:         }
513:     }
514: 
515: 
516:     /**
517:      * Handles exception thrown in __toString().
518:      * @param  \Exception
519:      * @return void
520:      */
521:     public static function toStringException(\Exception $exception)
522:     {
523:         if (self::$enabled) {
524:             self::_exceptionHandler($exception);
525:         } else {
526:             trigger_error($exception->getMessage(), E_USER_ERROR);
527:         }
528:     }
529: 
530: 
531:     /**
532:      * Starts catching potential errors/warnings.
533:      * @return void
534:      */
535:     public static function tryError()
536:     {
537:         if (!self::$enabled && self::$lastError === FALSE) {
538:             set_error_handler(array(__CLASS__, '_errorHandler'));
539:         }
540:         self::$lastError = NULL;
541:     }
542: 
543: 
544:     /**
545:      * Returns catched error/warning message.
546:      * @param  \ErrorException  catched error
547:      * @return bool
548:      */
549:     public static function catchError(& $error)
550:     {
551:         if (!self::$enabled && self::$lastError !== FALSE) {
552:             restore_error_handler();
553:         }
554:         $error = self::$lastError;
555:         self::$lastError = FALSE;
556:         return (bool) $error;
557:     }
558: 
559: 
560:     /********************* useful tools ****************d*g**/
561: 
562: 
563:     /**
564:      * Dumps information about a variable in readable format.
565:      * @param  mixed  variable to dump
566:      * @param  bool   return output instead of printing it? (bypasses $productionMode)
567:      * @return mixed  variable itself or dump
568:      */
569:     public static function dump($var, $return = FALSE)
570:     {
571:         if (!$return && self::$productionMode) {
572:             return $var;
573:         }
574: 
575:         $output = "<pre class=\"nette-dump\">" . Helpers::htmlDump($var) . "</pre>\n";
576: 
577:         if (!$return) {
578:             $trace = debug_backtrace(FALSE);
579:             $item = Helpers::findTrace($trace, 'dump') ?: Helpers::findTrace($trace, __CLASS__ . '::dump');
580:             if (isset($item['file'], $item['line']) && is_file($item['file'])) {
581:                 $lines = file($item['file']);
582:                 preg_match('#dump\((.*)\)#', $lines[$item['line'] - 1], $m);
583:                 $output = substr_replace(
584:                     $output,
585:                     ' title="' . htmlspecialchars((isset($m[0]) ? "$m[0] \n" : '') . "in file {$item['file']} on line {$item['line']}") . '"',
586:                     4, 0);
587: 
588:                 if (self::$showLocation) {
589:                     $output = substr_replace(
590:                         $output,
591:                         ' <small>in ' . Helpers::editorLink($item['file'], $item['line']) . ":{$item['line']}</small>",
592:                         -8, 0);
593:                 }
594:             }
595:         }
596: 
597:         if (self::$consoleMode) {
598:             if (self::$consoleColors && substr(getenv('TERM'), 0, 5) === 'xterm') {
599:                 $output = preg_replace_callback('#<span class="php-(\w+)">|</span>#', function($m) {
600:                     return "\033[" . (isset($m[1], Debugger::$consoleColors[$m[1]]) ? Debugger::$consoleColors[$m[1]] : '0') . "m";
601:                 }, $output);
602:             }
603:             $output = htmlspecialchars_decode(strip_tags($output), ENT_QUOTES);
604:         }
605: 
606:         if ($return) {
607:             return $output;
608: 
609:         } else {
610:             echo $output;
611:             return $var;
612:         }
613:     }
614: 
615: 
616:     /**
617:      * Starts/stops stopwatch.
618:      * @param  string  name
619:      * @return float   elapsed seconds
620:      */
621:     public static function timer($name = NULL)
622:     {
623:         static $time = array();
624:         $now = microtime(TRUE);
625:         $delta = isset($time[$name]) ? $now - $time[$name] : 0;
626:         $time[$name] = $now;
627:         return $delta;
628:     }
629: 
630: 
631:     /**
632:      * Dumps information about a variable in Nette Debug Bar.
633:      * @param  mixed  variable to dump
634:      * @param  string optional title
635:      * @return mixed  variable itself
636:      */
637:     public static function barDump($var, $title = NULL)
638:     {
639:         if (!self::$productionMode) {
640:             $dump = array();
641:             foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) {
642:                 $dump[$key] = Helpers::clickableDump($val);
643:             }
644:             self::$dumpPanel->data[] = array('title' => $title, 'dump' => $dump);
645:         }
646:         return $var;
647:     }
648: 
649: 
650:     /**
651:      * Sends message to FireLogger console.
652:      * @param  mixed   message to log
653:      * @return bool    was successful?
654:      */
655:     public static function fireLog($message)
656:     {
657:         if (!self::$productionMode) {
658:             return self::$fireLogger->log($message);
659:         }
660:     }
661: 
662: 
663:     private static function isHtmlMode()
664:     {
665:         return !self::$ajaxDetected && !self::$consoleMode
666:             && !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list()));
667:     }
668: 
669: 
670:     /** @deprecated */
671:     public static function addPanel(IBarPanel $panel, $id = NULL)
672:     {
673:         return self::$bar->addPanel($panel, $id);
674:     }
675: 
676: }
677: 
Nette 2.0 API documentation generated by ApiGen 2.8.0