CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Team
    • Issues (Github)
    • YouTube Channel
    • Get Involved
    • Bakery
    • Featured Resources
    • Newsletter
    • Certification
    • My CakePHP
    • CakeFest
    • Facebook
    • Twitter
    • Help & Support
    • Forum
    • Stack Overflow
    • IRC
    • Slack
    • Paid Support
CakePHP

C CakePHP 3.7 Red Velvet API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 3.7
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Namespaces

  • Cake
    • Auth
      • Storage
    • Cache
      • Engine
    • Collection
      • Iterator
    • Command
    • Console
      • Exception
    • Controller
      • Component
      • Exception
    • Core
      • Configure
        • Engine
      • Exception
      • Retry
    • Database
      • Driver
      • Exception
      • Expression
      • Schema
      • Statement
      • Type
    • Datasource
      • Exception
    • Error
      • Middleware
    • Event
      • Decorator
    • Filesystem
    • Form
    • Http
      • Client
        • Adapter
        • Auth
      • Cookie
      • Exception
      • Middleware
      • Session
    • I18n
      • Formatter
      • Middleware
      • Parser
    • Log
      • Engine
    • Mailer
      • Exception
      • Transport
    • Network
      • Exception
    • ORM
      • Association
      • Behavior
        • Translate
      • Exception
      • Locator
      • Rule
    • Routing
      • Exception
      • Filter
      • Middleware
      • Route
    • Shell
      • Helper
      • Task
    • TestSuite
      • Fixture
      • Stub
    • Utility
      • Exception
    • Validation
    • View
      • Exception
      • Form
      • Helper
      • Widget
  • None

Classes

  • BaseErrorHandler
  • Debugger
  • ErrorHandler
  • ExceptionRenderer

Interfaces

  • ExceptionRendererInterface

Exceptions

  • FatalErrorException
  • PHP7ErrorException
  1: <?php
  2: /**
  3:  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4:  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5:  *
  6:  * Licensed under The MIT License
  7:  * For full copyright and license information, please see the LICENSE.txt
  8:  * Redistributions of files must retain the above copyright notice.
  9:  *
 10:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 11:  * @link          https://cakephp.org CakePHP(tm) Project
 12:  * @since         1.2.0
 13:  * @license       https://opensource.org/licenses/mit-license.php MIT License
 14:  */
 15: namespace Cake\Error;
 16: 
 17: use Cake\Core\InstanceConfigTrait;
 18: use Cake\Log\Log;
 19: use Cake\Utility\Hash;
 20: use Cake\Utility\Security;
 21: use Cake\Utility\Text;
 22: use Exception;
 23: use InvalidArgumentException;
 24: use ReflectionObject;
 25: use ReflectionProperty;
 26: 
 27: /**
 28:  * Provide custom logging and error handling.
 29:  *
 30:  * Debugger overrides PHP's default error handling to provide stack traces and enhanced logging
 31:  *
 32:  * @link https://book.cakephp.org/3.0/en/development/debugging.html#namespace-Cake\Error
 33:  */
 34: class Debugger
 35: {
 36:     use InstanceConfigTrait;
 37: 
 38:     /**
 39:      * Default configuration
 40:      *
 41:      * @var array
 42:      */
 43:     protected $_defaultConfig = [
 44:         'outputMask' => []
 45:     ];
 46: 
 47:     /**
 48:      * A list of errors generated by the application.
 49:      *
 50:      * @var array
 51:      */
 52:     public $errors = [];
 53: 
 54:     /**
 55:      * The current output format.
 56:      *
 57:      * @var string
 58:      */
 59:     protected $_outputFormat = 'js';
 60: 
 61:     /**
 62:      * Templates used when generating trace or error strings. Can be global or indexed by the format
 63:      * value used in $_outputFormat.
 64:      *
 65:      * @var array
 66:      */
 67:     protected $_templates = [
 68:         'log' => [
 69:             'trace' => '{:reference} - {:path}, line {:line}',
 70:             'error' => '{:error} ({:code}): {:description} in [{:file}, line {:line}]'
 71:         ],
 72:         'js' => [
 73:             'error' => '',
 74:             'info' => '',
 75:             'trace' => '<pre class="stack-trace">{:trace}</pre>',
 76:             'code' => '',
 77:             'context' => '',
 78:             'links' => [],
 79:             'escapeContext' => true,
 80:         ],
 81:         'html' => [
 82:             'trace' => '<pre class="cake-error trace"><b>Trace</b> <p>{:trace}</p></pre>',
 83:             'context' => '<pre class="cake-error context"><b>Context</b> <p>{:context}</p></pre>',
 84:             'escapeContext' => true,
 85:         ],
 86:         'txt' => [
 87:             'error' => "{:error}: {:code} :: {:description} on line {:line} of {:path}\n{:info}",
 88:             'code' => '',
 89:             'info' => ''
 90:         ],
 91:         'base' => [
 92:             'traceLine' => '{:reference} - {:path}, line {:line}',
 93:             'trace' => "Trace:\n{:trace}\n",
 94:             'context' => "Context:\n{:context}\n",
 95:         ]
 96:     ];
 97: 
 98:     /**
 99:      * Holds current output data when outputFormat is false.
100:      *
101:      * @var array
102:      */
103:     protected $_data = [];
104: 
105:     /**
106:      * Constructor.
107:      *
108:      */
109:     public function __construct()
110:     {
111:         $docRef = ini_get('docref_root');
112: 
113:         if (empty($docRef) && function_exists('ini_set')) {
114:             ini_set('docref_root', 'https://secure.php.net/');
115:         }
116:         if (!defined('E_RECOVERABLE_ERROR')) {
117:             define('E_RECOVERABLE_ERROR', 4096);
118:         }
119: 
120:         $e = '<pre class="cake-error">';
121:         $e .= '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-trace\')';
122:         $e .= '.style.display = (document.getElementById(\'{:id}-trace\').style.display == ';
123:         $e .= '\'none\' ? \'\' : \'none\');"><b>{:error}</b> ({:code})</a>: {:description} ';
124:         $e .= '[<b>{:path}</b>, line <b>{:line}</b>]';
125: 
126:         $e .= '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">';
127:         $e .= '{:links}{:info}</div>';
128:         $e .= '</pre>';
129:         $this->_templates['js']['error'] = $e;
130: 
131:         $t = '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">';
132:         $t .= '{:context}{:code}{:trace}</div>';
133:         $this->_templates['js']['info'] = $t;
134: 
135:         $links = [];
136:         $link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-code\')';
137:         $link .= '.style.display = (document.getElementById(\'{:id}-code\').style.display == ';
138:         $link .= '\'none\' ? \'\' : \'none\')">Code</a>';
139:         $links['code'] = $link;
140: 
141:         $link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-context\')';
142:         $link .= '.style.display = (document.getElementById(\'{:id}-context\').style.display == ';
143:         $link .= '\'none\' ? \'\' : \'none\')">Context</a>';
144:         $links['context'] = $link;
145: 
146:         $this->_templates['js']['links'] = $links;
147: 
148:         $this->_templates['js']['context'] = '<pre id="{:id}-context" class="cake-context" ';
149:         $this->_templates['js']['context'] .= 'style="display: none;">{:context}</pre>';
150: 
151:         $this->_templates['js']['code'] = '<pre id="{:id}-code" class="cake-code-dump" ';
152:         $this->_templates['js']['code'] .= 'style="display: none;">{:code}</pre>';
153: 
154:         $e = '<pre class="cake-error"><b>{:error}</b> ({:code}) : {:description} ';
155:         $e .= '[<b>{:path}</b>, line <b>{:line}]</b></pre>';
156:         $this->_templates['html']['error'] = $e;
157: 
158:         $this->_templates['html']['context'] = '<pre class="cake-context"><b>Context</b> ';
159:         $this->_templates['html']['context'] .= '<p>{:context}</p></pre>';
160:     }
161: 
162:     /**
163:      * Returns a reference to the Debugger singleton object instance.
164:      *
165:      * @param string|null $class Class name.
166:      * @return \Cake\Error\Debugger
167:      */
168:     public static function getInstance($class = null)
169:     {
170:         static $instance = [];
171:         if (!empty($class)) {
172:             if (!$instance || strtolower($class) !== strtolower(get_class($instance[0]))) {
173:                 $instance[0] = new $class();
174:             }
175:         }
176:         if (!$instance) {
177:             $instance[0] = new Debugger();
178:         }
179: 
180:         return $instance[0];
181:     }
182: 
183:     /**
184:      * Read or write configuration options for the Debugger instance.
185:      *
186:      * @param string|array|null $key The key to get/set, or a complete array of configs.
187:      * @param mixed|null $value The value to set.
188:      * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
189:      * @return mixed Config value being read, or the object itself on write operations.
190:      * @throws \Cake\Core\Exception\Exception When trying to set a key that is invalid.
191:      */
192:     public static function configInstance($key = null, $value = null, $merge = true)
193:     {
194:         if (is_array($key) || func_num_args() >= 2) {
195:             return static::getInstance()->setConfig($key, $value, $merge);
196:         }
197: 
198:         return static::getInstance()->getConfig($key);
199:     }
200: 
201:     /**
202:      * Reads the current output masking.
203:      *
204:      * @return array
205:      */
206:     public static function outputMask()
207:     {
208:         return static::configInstance('outputMask');
209:     }
210: 
211:     /**
212:      * Sets configurable masking of debugger output by property name and array key names.
213:      *
214:      * ### Example
215:      *
216:      * Debugger::setOutputMask(['password' => '[*************]');
217:      *
218:      * @param array $value An array where keys are replaced by their values in output.
219:      * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
220:      * @return void
221:      */
222:     public static function setOutputMask(array $value, $merge = true)
223:     {
224:         static::configInstance('outputMask', $value, $merge);
225:     }
226: 
227:     /**
228:      * Recursively formats and outputs the contents of the supplied variable.
229:      *
230:      * @param mixed $var The variable to dump.
231:      * @param int $depth The depth to output to. Defaults to 3.
232:      * @return void
233:      * @see \Cake\Error\Debugger::exportVar()
234:      * @link https://book.cakephp.org/3.0/en/development/debugging.html#outputting-values
235:      */
236:     public static function dump($var, $depth = 3)
237:     {
238:         pr(static::exportVar($var, $depth));
239:     }
240: 
241:     /**
242:      * Creates an entry in the log file. The log entry will contain a stack trace from where it was called.
243:      * as well as export the variable using exportVar. By default the log is written to the debug log.
244:      *
245:      * @param mixed $var Variable or content to log.
246:      * @param int|string $level Type of log to use. Defaults to 'debug'.
247:      * @param int $depth The depth to output to. Defaults to 3.
248:      * @return void
249:      */
250:     public static function log($var, $level = 'debug', $depth = 3)
251:     {
252:         $source = static::trace(['start' => 1]) . "\n";
253:         Log::write($level, "\n" . $source . static::exportVar($var, $depth));
254:     }
255: 
256:     /**
257:      * Outputs a stack trace based on the supplied options.
258:      *
259:      * ### Options
260:      *
261:      * - `depth` - The number of stack frames to return. Defaults to 999
262:      * - `format` - The format you want the return. Defaults to the currently selected format. If
263:      *    format is 'array' or 'points' the return will be an array.
264:      * - `args` - Should arguments for functions be shown? If true, the arguments for each method call
265:      *   will be displayed.
266:      * - `start` - The stack frame to start generating a trace from. Defaults to 0
267:      *
268:      * @param array $options Format for outputting stack trace.
269:      * @return mixed Formatted stack trace.
270:      * @link https://book.cakephp.org/3.0/en/development/debugging.html#generating-stack-traces
271:      */
272:     public static function trace(array $options = [])
273:     {
274:         return Debugger::formatTrace(debug_backtrace(), $options);
275:     }
276: 
277:     /**
278:      * Formats a stack trace based on the supplied options.
279:      *
280:      * ### Options
281:      *
282:      * - `depth` - The number of stack frames to return. Defaults to 999
283:      * - `format` - The format you want the return. Defaults to the currently selected format. If
284:      *    format is 'array' or 'points' the return will be an array.
285:      * - `args` - Should arguments for functions be shown? If true, the arguments for each method call
286:      *   will be displayed.
287:      * - `start` - The stack frame to start generating a trace from. Defaults to 0
288:      *
289:      * @param array|\Exception $backtrace Trace as array or an exception object.
290:      * @param array $options Format for outputting stack trace.
291:      * @return mixed Formatted stack trace.
292:      * @link https://book.cakephp.org/3.0/en/development/debugging.html#generating-stack-traces
293:      */
294:     public static function formatTrace($backtrace, $options = [])
295:     {
296:         if ($backtrace instanceof Exception) {
297:             $backtrace = $backtrace->getTrace();
298:         }
299:         $self = Debugger::getInstance();
300:         $defaults = [
301:             'depth' => 999,
302:             'format' => $self->_outputFormat,
303:             'args' => false,
304:             'start' => 0,
305:             'scope' => null,
306:             'exclude' => ['call_user_func_array', 'trigger_error']
307:         ];
308:         $options = Hash::merge($defaults, $options);
309: 
310:         $count = count($backtrace);
311:         $back = [];
312: 
313:         $_trace = [
314:             'line' => '??',
315:             'file' => '[internal]',
316:             'class' => null,
317:             'function' => '[main]'
318:         ];
319: 
320:         for ($i = $options['start']; $i < $count && $i < $options['depth']; $i++) {
321:             $trace = $backtrace[$i] + ['file' => '[internal]', 'line' => '??'];
322:             $signature = $reference = '[main]';
323: 
324:             if (isset($backtrace[$i + 1])) {
325:                 $next = $backtrace[$i + 1] + $_trace;
326:                 $signature = $reference = $next['function'];
327: 
328:                 if (!empty($next['class'])) {
329:                     $signature = $next['class'] . '::' . $next['function'];
330:                     $reference = $signature . '(';
331:                     if ($options['args'] && isset($next['args'])) {
332:                         $args = [];
333:                         foreach ($next['args'] as $arg) {
334:                             $args[] = Debugger::exportVar($arg);
335:                         }
336:                         $reference .= implode(', ', $args);
337:                     }
338:                     $reference .= ')';
339:                 }
340:             }
341:             if (in_array($signature, $options['exclude'])) {
342:                 continue;
343:             }
344:             if ($options['format'] === 'points' && $trace['file'] !== '[internal]') {
345:                 $back[] = ['file' => $trace['file'], 'line' => $trace['line']];
346:             } elseif ($options['format'] === 'array') {
347:                 $back[] = $trace;
348:             } else {
349:                 if (isset($self->_templates[$options['format']]['traceLine'])) {
350:                     $tpl = $self->_templates[$options['format']]['traceLine'];
351:                 } else {
352:                     $tpl = $self->_templates['base']['traceLine'];
353:                 }
354:                 $trace['path'] = static::trimPath($trace['file']);
355:                 $trace['reference'] = $reference;
356:                 unset($trace['object'], $trace['args']);
357:                 $back[] = Text::insert($tpl, $trace, ['before' => '{:', 'after' => '}']);
358:             }
359:         }
360: 
361:         if ($options['format'] === 'array' || $options['format'] === 'points') {
362:             return $back;
363:         }
364: 
365:         return implode("\n", $back);
366:     }
367: 
368:     /**
369:      * Shortens file paths by replacing the application base path with 'APP', and the CakePHP core
370:      * path with 'CORE'.
371:      *
372:      * @param string $path Path to shorten.
373:      * @return string Normalized path
374:      */
375:     public static function trimPath($path)
376:     {
377:         if (defined('APP') && strpos($path, APP) === 0) {
378:             return str_replace(APP, 'APP/', $path);
379:         }
380:         if (defined('CAKE_CORE_INCLUDE_PATH') && strpos($path, CAKE_CORE_INCLUDE_PATH) === 0) {
381:             return str_replace(CAKE_CORE_INCLUDE_PATH, 'CORE', $path);
382:         }
383:         if (defined('ROOT') && strpos($path, ROOT) === 0) {
384:             return str_replace(ROOT, 'ROOT', $path);
385:         }
386: 
387:         return $path;
388:     }
389: 
390:     /**
391:      * Grabs an excerpt from a file and highlights a given line of code.
392:      *
393:      * Usage:
394:      *
395:      * ```
396:      * Debugger::excerpt('/path/to/file', 100, 4);
397:      * ```
398:      *
399:      * The above would return an array of 8 items. The 4th item would be the provided line,
400:      * and would be wrapped in `<span class="code-highlight"></span>`. All of the lines
401:      * are processed with highlight_string() as well, so they have basic PHP syntax highlighting
402:      * applied.
403:      *
404:      * @param string $file Absolute path to a PHP file.
405:      * @param int $line Line number to highlight.
406:      * @param int $context Number of lines of context to extract above and below $line.
407:      * @return array Set of lines highlighted
408:      * @see https://secure.php.net/highlight_string
409:      * @link https://book.cakephp.org/3.0/en/development/debugging.html#getting-an-excerpt-from-a-file
410:      */
411:     public static function excerpt($file, $line, $context = 2)
412:     {
413:         $lines = [];
414:         if (!file_exists($file)) {
415:             return [];
416:         }
417:         $data = file_get_contents($file);
418:         if (empty($data)) {
419:             return $lines;
420:         }
421:         if (strpos($data, "\n") !== false) {
422:             $data = explode("\n", $data);
423:         }
424:         $line--;
425:         if (!isset($data[$line])) {
426:             return $lines;
427:         }
428:         for ($i = $line - $context; $i < $line + $context + 1; $i++) {
429:             if (!isset($data[$i])) {
430:                 continue;
431:             }
432:             $string = str_replace(["\r\n", "\n"], '', static::_highlight($data[$i]));
433:             if ($i == $line) {
434:                 $lines[] = '<span class="code-highlight">' . $string . '</span>';
435:             } else {
436:                 $lines[] = $string;
437:             }
438:         }
439: 
440:         return $lines;
441:     }
442: 
443:     /**
444:      * Wraps the highlight_string function in case the server API does not
445:      * implement the function as it is the case of the HipHop interpreter
446:      *
447:      * @param string $str The string to convert.
448:      * @return string
449:      */
450:     protected static function _highlight($str)
451:     {
452:         if (function_exists('hphp_log') || function_exists('hphp_gettid')) {
453:             return htmlentities($str);
454:         }
455:         $added = false;
456:         if (strpos($str, '<?php') === false) {
457:             $added = true;
458:             $str = "<?php \n" . $str;
459:         }
460:         $highlight = highlight_string($str, true);
461:         if ($added) {
462:             $highlight = str_replace(
463:                 ['&lt;?php&nbsp;<br/>', '&lt;?php&nbsp;<br />'],
464:                 '',
465:                 $highlight
466:             );
467:         }
468: 
469:         return $highlight;
470:     }
471: 
472:     /**
473:      * Converts a variable to a string for debug output.
474:      *
475:      * *Note:* The following keys will have their contents
476:      * replaced with `*****`:
477:      *
478:      *  - password
479:      *  - login
480:      *  - host
481:      *  - database
482:      *  - port
483:      *  - prefix
484:      *  - schema
485:      *
486:      * This is done to protect database credentials, which could be accidentally
487:      * shown in an error message if CakePHP is deployed in development mode.
488:      *
489:      * @param mixed $var Variable to convert.
490:      * @param int $depth The depth to output to. Defaults to 3.
491:      * @return string Variable as a formatted string
492:      */
493:     public static function exportVar($var, $depth = 3)
494:     {
495:         return static::_export($var, $depth, 0);
496:     }
497: 
498:     /**
499:      * Protected export function used to keep track of indentation and recursion.
500:      *
501:      * @param mixed $var The variable to dump.
502:      * @param int $depth The remaining depth.
503:      * @param int $indent The current indentation level.
504:      * @return string The dumped variable.
505:      */
506:     protected static function _export($var, $depth, $indent)
507:     {
508:         switch (static::getType($var)) {
509:             case 'boolean':
510:                 return $var ? 'true' : 'false';
511:             case 'integer':
512:                 return '(int) ' . $var;
513:             case 'float':
514:                 return '(float) ' . $var;
515:             case 'string':
516:                 if (trim($var) === '' && ctype_space($var) === false) {
517:                     return "''";
518:                 }
519: 
520:                 return "'" . $var . "'";
521:             case 'array':
522:                 return static::_array($var, $depth - 1, $indent + 1);
523:             case 'resource':
524:                 return strtolower(gettype($var));
525:             case 'null':
526:                 return 'null';
527:             case 'unknown':
528:                 return 'unknown';
529:             default:
530:                 return static::_object($var, $depth - 1, $indent + 1);
531:         }
532:     }
533: 
534:     /**
535:      * Export an array type object. Filters out keys used in datasource configuration.
536:      *
537:      * The following keys are replaced with ***'s
538:      *
539:      * - password
540:      * - login
541:      * - host
542:      * - database
543:      * - port
544:      * - prefix
545:      * - schema
546:      *
547:      * @param array $var The array to export.
548:      * @param int $depth The current depth, used for recursion tracking.
549:      * @param int $indent The current indentation level.
550:      * @return string Exported array.
551:      */
552:     protected static function _array(array $var, $depth, $indent)
553:     {
554:         $out = '[';
555:         $break = $end = null;
556:         if (!empty($var)) {
557:             $break = "\n" . str_repeat("\t", $indent);
558:             $end = "\n" . str_repeat("\t", $indent - 1);
559:         }
560:         $vars = [];
561: 
562:         if ($depth >= 0) {
563:             $outputMask = (array)static::outputMask();
564:             foreach ($var as $key => $val) {
565:                 // Sniff for globals as !== explodes in < 5.4
566:                 if ($key === 'GLOBALS' && is_array($val) && isset($val['GLOBALS'])) {
567:                     $val = '[recursion]';
568:                 } elseif (array_key_exists($key, $outputMask)) {
569:                     $val = (string)$outputMask[$key];
570:                 } elseif ($val !== $var) {
571:                     $val = static::_export($val, $depth, $indent);
572:                 }
573:                 $vars[] = $break . static::exportVar($key) .
574:                     ' => ' .
575:                     $val;
576:             }
577:         } else {
578:             $vars[] = $break . '[maximum depth reached]';
579:         }
580: 
581:         return $out . implode(',', $vars) . $end . ']';
582:     }
583: 
584:     /**
585:      * Handles object to string conversion.
586:      *
587:      * @param object $var Object to convert.
588:      * @param int $depth The current depth, used for tracking recursion.
589:      * @param int $indent The current indentation level.
590:      * @return string
591:      * @see \Cake\Error\Debugger::exportVar()
592:      */
593:     protected static function _object($var, $depth, $indent)
594:     {
595:         $out = '';
596:         $props = [];
597: 
598:         $className = get_class($var);
599:         $out .= 'object(' . $className . ') {';
600:         $break = "\n" . str_repeat("\t", $indent);
601:         $end = "\n" . str_repeat("\t", $indent - 1);
602: 
603:         if ($depth > 0 && method_exists($var, '__debugInfo')) {
604:             try {
605:                 return $out . "\n" .
606:                     substr(static::_array($var->__debugInfo(), $depth - 1, $indent), 1, -1) .
607:                     $end . '}';
608:             } catch (Exception $e) {
609:                 $message = $e->getMessage();
610: 
611:                 return $out . "\n(unable to export object: $message)\n }";
612:             }
613:         }
614: 
615:         if ($depth > 0) {
616:             $outputMask = (array)static::outputMask();
617:             $objectVars = get_object_vars($var);
618:             foreach ($objectVars as $key => $value) {
619:                 $value = array_key_exists($key, $outputMask) ? $outputMask[$key] : static::_export($value, $depth - 1, $indent);
620:                 $props[] = "$key => " . $value;
621:             }
622: 
623:             $ref = new ReflectionObject($var);
624: 
625:             $filters = [
626:                 ReflectionProperty::IS_PROTECTED => 'protected',
627:                 ReflectionProperty::IS_PRIVATE => 'private',
628:             ];
629:             foreach ($filters as $filter => $visibility) {
630:                 $reflectionProperties = $ref->getProperties($filter);
631:                 foreach ($reflectionProperties as $reflectionProperty) {
632:                     $reflectionProperty->setAccessible(true);
633:                     $property = $reflectionProperty->getValue($var);
634: 
635:                     $value = static::_export($property, $depth - 1, $indent);
636:                     $key = $reflectionProperty->name;
637:                     $props[] = sprintf(
638:                         '[%s] %s => %s',
639:                         $visibility,
640:                         $key,
641:                         array_key_exists($key, $outputMask) ? $outputMask[$key] : $value
642:                     );
643:                 }
644:             }
645: 
646:             $out .= $break . implode($break, $props) . $end;
647:         }
648:         $out .= '}';
649: 
650:         return $out;
651:     }
652: 
653:     /**
654:      * Get the output format for Debugger error rendering.
655:      *
656:      * @return string Returns the current format when getting.
657:      */
658:     public static function getOutputFormat()
659:     {
660:         return Debugger::getInstance()->_outputFormat;
661:     }
662: 
663:     /**
664:      * Set the output format for Debugger error rendering.
665:      *
666:      * @param string $format The format you want errors to be output as.
667:      * @return void
668:      * @throws \InvalidArgumentException When choosing a format that doesn't exist.
669:      */
670:     public static function setOutputFormat($format)
671:     {
672:         $self = Debugger::getInstance();
673: 
674:         if (!isset($self->_templates[$format])) {
675:             throw new InvalidArgumentException('Invalid Debugger output format.');
676:         }
677:         $self->_outputFormat = $format;
678:     }
679: 
680:     /**
681:      * Get/Set the output format for Debugger error rendering.
682:      *
683:      * @deprecated 3.5.0 Use getOutputFormat()/setOutputFormat() instead.
684:      * @param string|null $format The format you want errors to be output as.
685:      *   Leave null to get the current format.
686:      * @return string|null Returns null when setting. Returns the current format when getting.
687:      * @throws \InvalidArgumentException When choosing a format that doesn't exist.
688:      */
689:     public static function outputAs($format = null)
690:     {
691:         deprecationWarning(
692:             'Debugger::outputAs() is deprecated. Use Debugger::getOutputFormat()/setOutputFormat() instead.'
693:         );
694:         $self = Debugger::getInstance();
695:         if ($format === null) {
696:             return $self->_outputFormat;
697:         }
698: 
699:         if (!isset($self->_templates[$format])) {
700:             throw new InvalidArgumentException('Invalid Debugger output format.');
701:         }
702:         $self->_outputFormat = $format;
703: 
704:         return null;
705:     }
706: 
707:     /**
708:      * Add an output format or update a format in Debugger.
709:      *
710:      * ```
711:      * Debugger::addFormat('custom', $data);
712:      * ```
713:      *
714:      * Where $data is an array of strings that use Text::insert() variable
715:      * replacement. The template vars should be in a `{:id}` style.
716:      * An error formatter can have the following keys:
717:      *
718:      * - 'error' - Used for the container for the error message. Gets the following template
719:      *   variables: `id`, `error`, `code`, `description`, `path`, `line`, `links`, `info`
720:      * - 'info' - A combination of `code`, `context` and `trace`. Will be set with
721:      *   the contents of the other template keys.
722:      * - 'trace' - The container for a stack trace. Gets the following template
723:      *   variables: `trace`
724:      * - 'context' - The container element for the context variables.
725:      *   Gets the following templates: `id`, `context`
726:      * - 'links' - An array of HTML links that are used for creating links to other resources.
727:      *   Typically this is used to create javascript links to open other sections.
728:      *   Link keys, are: `code`, `context`, `help`. See the js output format for an
729:      *   example.
730:      * - 'traceLine' - Used for creating lines in the stacktrace. Gets the following
731:      *   template variables: `reference`, `path`, `line`
732:      *
733:      * Alternatively if you want to use a custom callback to do all the formatting, you can use
734:      * the callback key, and provide a callable:
735:      *
736:      * ```
737:      * Debugger::addFormat('custom', ['callback' => [$foo, 'outputError']];
738:      * ```
739:      *
740:      * The callback can expect two parameters. The first is an array of all
741:      * the error data. The second contains the formatted strings generated using
742:      * the other template strings. Keys like `info`, `links`, `code`, `context` and `trace`
743:      * will be present depending on the other templates in the format type.
744:      *
745:      * @param string $format Format to use, including 'js' for JavaScript-enhanced HTML, 'html' for
746:      *    straight HTML output, or 'txt' for unformatted text.
747:      * @param array $strings Template strings, or a callback to be used for the output format.
748:      * @return array The resulting format string set.
749:      */
750:     public static function addFormat($format, array $strings)
751:     {
752:         $self = Debugger::getInstance();
753:         if (isset($self->_templates[$format])) {
754:             if (isset($strings['links'])) {
755:                 $self->_templates[$format]['links'] = array_merge(
756:                     $self->_templates[$format]['links'],
757:                     $strings['links']
758:                 );
759:                 unset($strings['links']);
760:             }
761:             $self->_templates[$format] = $strings + $self->_templates[$format];
762:         } else {
763:             $self->_templates[$format] = $strings;
764:         }
765: 
766:         return $self->_templates[$format];
767:     }
768: 
769:     /**
770:      * Takes a processed array of data from an error and displays it in the chosen format.
771:      *
772:      * @param array $data Data to output.
773:      * @return void
774:      */
775:     public function outputError($data)
776:     {
777:         $defaults = [
778:             'level' => 0,
779:             'error' => 0,
780:             'code' => 0,
781:             'description' => '',
782:             'file' => '',
783:             'line' => 0,
784:             'context' => [],
785:             'start' => 2,
786:         ];
787:         $data += $defaults;
788: 
789:         $files = static::trace(['start' => $data['start'], 'format' => 'points']);
790:         $code = '';
791:         $file = null;
792:         if (isset($files[0]['file'])) {
793:             $file = $files[0];
794:         } elseif (isset($files[1]['file'])) {
795:             $file = $files[1];
796:         }
797:         if ($file) {
798:             $code = static::excerpt($file['file'], $file['line'], 1);
799:         }
800:         $trace = static::trace(['start' => $data['start'], 'depth' => '20']);
801:         $insertOpts = ['before' => '{:', 'after' => '}'];
802:         $context = [];
803:         $links = [];
804:         $info = '';
805: 
806:         foreach ((array)$data['context'] as $var => $value) {
807:             $context[] = "\${$var} = " . static::exportVar($value, 3);
808:         }
809: 
810:         switch ($this->_outputFormat) {
811:             case false:
812:                 $this->_data[] = compact('context', 'trace') + $data;
813: 
814:                 return;
815:             case 'log':
816:                 static::log(compact('context', 'trace') + $data);
817: 
818:                 return;
819:         }
820: 
821:         $data['trace'] = $trace;
822:         $data['id'] = 'cakeErr' . uniqid();
823:         $tpl = $this->_templates[$this->_outputFormat] + $this->_templates['base'];
824: 
825:         if (isset($tpl['links'])) {
826:             foreach ($tpl['links'] as $key => $val) {
827:                 $links[$key] = Text::insert($val, $data, $insertOpts);
828:             }
829:         }
830: 
831:         if (!empty($tpl['escapeContext'])) {
832:             $context = h($context);
833:             $data['description'] = h($data['description']);
834:         }
835: 
836:         $infoData = compact('code', 'context', 'trace');
837:         foreach ($infoData as $key => $value) {
838:             if (empty($value) || !isset($tpl[$key])) {
839:                 continue;
840:             }
841:             if (is_array($value)) {
842:                 $value = implode("\n", $value);
843:             }
844:             $info .= Text::insert($tpl[$key], [$key => $value] + $data, $insertOpts);
845:         }
846:         $links = implode(' ', $links);
847: 
848:         if (isset($tpl['callback']) && is_callable($tpl['callback'])) {
849:             call_user_func($tpl['callback'], $data, compact('links', 'info'));
850: 
851:             return;
852:         }
853:         echo Text::insert($tpl['error'], compact('links', 'info') + $data, $insertOpts);
854:     }
855: 
856:     /**
857:      * Get the type of the given variable. Will return the class name
858:      * for objects.
859:      *
860:      * @param mixed $var The variable to get the type of.
861:      * @return string The type of variable.
862:      */
863:     public static function getType($var)
864:     {
865:         if (is_object($var)) {
866:             return get_class($var);
867:         }
868:         if ($var === null) {
869:             return 'null';
870:         }
871:         if (is_string($var)) {
872:             return 'string';
873:         }
874:         if (is_array($var)) {
875:             return 'array';
876:         }
877:         if (is_int($var)) {
878:             return 'integer';
879:         }
880:         if (is_bool($var)) {
881:             return 'boolean';
882:         }
883:         if (is_float($var)) {
884:             return 'float';
885:         }
886:         if (is_resource($var)) {
887:             return 'resource';
888:         }
889: 
890:         return 'unknown';
891:     }
892: 
893:     /**
894:      * Prints out debug information about given variable.
895:      *
896:      * @param mixed $var Variable to show debug information for.
897:      * @param array $location If contains keys "file" and "line" their values will
898:      *    be used to show location info.
899:      * @param bool|null $showHtml If set to true, the method prints the debug
900:      *    data in a browser-friendly way.
901:      * @return void
902:      */
903:     public static function printVar($var, $location = [], $showHtml = null)
904:     {
905:         $location += ['file' => null, 'line' => null];
906:         $file = $location['file'];
907:         $line = $location['line'];
908:         $lineInfo = '';
909:         if ($file) {
910:             $search = [];
911:             if (defined('ROOT')) {
912:                 $search = [ROOT];
913:             }
914:             if (defined('CAKE_CORE_INCLUDE_PATH')) {
915:                 array_unshift($search, CAKE_CORE_INCLUDE_PATH);
916:             }
917:             $file = str_replace($search, '', $file);
918:         }
919:         $html = <<<HTML
920: <div class="cake-debug-output" style="direction:ltr">
921: %s
922: <pre class="cake-debug">
923: %s
924: </pre>
925: </div>
926: HTML;
927:         $text = <<<TEXT
928: %s
929: ########## DEBUG ##########
930: %s
931: ###########################
932: 
933: TEXT;
934:         $template = $html;
935:         if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') || $showHtml === false) {
936:             $template = $text;
937:             if ($file && $line) {
938:                 $lineInfo = sprintf('%s (line %s)', $file, $line);
939:             }
940:         }
941:         if ($showHtml === null && $template !== $text) {
942:             $showHtml = true;
943:         }
944:         $var = Debugger::exportVar($var, 25);
945:         if ($showHtml) {
946:             $template = $html;
947:             $var = h($var);
948:             if ($file && $line) {
949:                 $lineInfo = sprintf('<span><strong>%s</strong> (line <strong>%s</strong>)</span>', $file, $line);
950:             }
951:         }
952:         printf($template, $lineInfo, $var);
953:     }
954: 
955:     /**
956:      * Verifies that the application's salt and cipher seed value has been changed from the default value.
957:      *
958:      * @return void
959:      */
960:     public static function checkSecurityKeys()
961:     {
962:         if (Security::getSalt() === '__SALT__') {
963:             trigger_error(sprintf('Please change the value of %s in %s to a salt value specific to your application.', '\'Security.salt\'', 'ROOT/config/app.php'), E_USER_NOTICE);
964:         }
965:     }
966: }
967: 
Follow @CakePHP
#IRC
OpenHub
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Logos & Trademarks
  • Community
  • Team
  • Issues (Github)
  • YouTube Channel
  • Get Involved
  • Bakery
  • Featured Resources
  • Newsletter
  • Certification
  • My CakePHP
  • CakeFest
  • Facebook
  • Twitter
  • Help & Support
  • Forum
  • Stack Overflow
  • IRC
  • Slack
  • Paid Support

Generated using CakePHP API Docs