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.8 Red Velvet API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 3.8
      • 3.8
      • 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:         /** @var string $source */
253:         $source = static::trace(['start' => 1]);
254:         $source .= "\n";
255: 
256:         Log::write($level, "\n" . $source . static::exportVar($var, $depth));
257:     }
258: 
259:     /**
260:      * Outputs a stack trace based on the supplied options.
261:      *
262:      * ### Options
263:      *
264:      * - `depth` - The number of stack frames to return. Defaults to 999
265:      * - `format` - The format you want the return. Defaults to the currently selected format. If
266:      *    format is 'array' or 'points' the return will be an array.
267:      * - `args` - Should arguments for functions be shown? If true, the arguments for each method call
268:      *   will be displayed.
269:      * - `start` - The stack frame to start generating a trace from. Defaults to 0
270:      *
271:      * @param array $options Format for outputting stack trace.
272:      * @return string|array Formatted stack trace.
273:      * @link https://book.cakephp.org/3.0/en/development/debugging.html#generating-stack-traces
274:      */
275:     public static function trace(array $options = [])
276:     {
277:         return Debugger::formatTrace(debug_backtrace(), $options);
278:     }
279: 
280:     /**
281:      * Formats a stack trace based on the supplied options.
282:      *
283:      * ### Options
284:      *
285:      * - `depth` - The number of stack frames to return. Defaults to 999
286:      * - `format` - The format you want the return. Defaults to the currently selected format. If
287:      *    format is 'array' or 'points' the return will be an array.
288:      * - `args` - Should arguments for functions be shown? If true, the arguments for each method call
289:      *   will be displayed.
290:      * - `start` - The stack frame to start generating a trace from. Defaults to 0
291:      *
292:      * @param array|\Exception $backtrace Trace as array or an exception object.
293:      * @param array $options Format for outputting stack trace.
294:      * @return string|array Formatted stack trace.
295:      * @link https://book.cakephp.org/3.0/en/development/debugging.html#generating-stack-traces
296:      */
297:     public static function formatTrace($backtrace, $options = [])
298:     {
299:         if ($backtrace instanceof Exception) {
300:             $backtrace = $backtrace->getTrace();
301:         }
302:         $self = Debugger::getInstance();
303:         $defaults = [
304:             'depth' => 999,
305:             'format' => $self->_outputFormat,
306:             'args' => false,
307:             'start' => 0,
308:             'scope' => null,
309:             'exclude' => ['call_user_func_array', 'trigger_error']
310:         ];
311:         $options = Hash::merge($defaults, $options);
312: 
313:         $count = count($backtrace);
314:         $back = [];
315: 
316:         $_trace = [
317:             'line' => '??',
318:             'file' => '[internal]',
319:             'class' => null,
320:             'function' => '[main]'
321:         ];
322: 
323:         for ($i = $options['start']; $i < $count && $i < $options['depth']; $i++) {
324:             $trace = $backtrace[$i] + ['file' => '[internal]', 'line' => '??'];
325:             $signature = $reference = '[main]';
326: 
327:             if (isset($backtrace[$i + 1])) {
328:                 $next = $backtrace[$i + 1] + $_trace;
329:                 $signature = $reference = $next['function'];
330: 
331:                 if (!empty($next['class'])) {
332:                     $signature = $next['class'] . '::' . $next['function'];
333:                     $reference = $signature . '(';
334:                     if ($options['args'] && isset($next['args'])) {
335:                         $args = [];
336:                         foreach ($next['args'] as $arg) {
337:                             $args[] = Debugger::exportVar($arg);
338:                         }
339:                         $reference .= implode(', ', $args);
340:                     }
341:                     $reference .= ')';
342:                 }
343:             }
344:             if (in_array($signature, $options['exclude'])) {
345:                 continue;
346:             }
347:             if ($options['format'] === 'points' && $trace['file'] !== '[internal]') {
348:                 $back[] = ['file' => $trace['file'], 'line' => $trace['line']];
349:             } elseif ($options['format'] === 'array') {
350:                 $back[] = $trace;
351:             } else {
352:                 if (isset($self->_templates[$options['format']]['traceLine'])) {
353:                     $tpl = $self->_templates[$options['format']]['traceLine'];
354:                 } else {
355:                     $tpl = $self->_templates['base']['traceLine'];
356:                 }
357:                 $trace['path'] = static::trimPath($trace['file']);
358:                 $trace['reference'] = $reference;
359:                 unset($trace['object'], $trace['args']);
360:                 $back[] = Text::insert($tpl, $trace, ['before' => '{:', 'after' => '}']);
361:             }
362:         }
363: 
364:         if ($options['format'] === 'array' || $options['format'] === 'points') {
365:             return $back;
366:         }
367: 
368:         return implode("\n", $back);
369:     }
370: 
371:     /**
372:      * Shortens file paths by replacing the application base path with 'APP', and the CakePHP core
373:      * path with 'CORE'.
374:      *
375:      * @param string $path Path to shorten.
376:      * @return string Normalized path
377:      */
378:     public static function trimPath($path)
379:     {
380:         if (defined('APP') && strpos($path, APP) === 0) {
381:             return str_replace(APP, 'APP/', $path);
382:         }
383:         if (defined('CAKE_CORE_INCLUDE_PATH') && strpos($path, CAKE_CORE_INCLUDE_PATH) === 0) {
384:             return str_replace(CAKE_CORE_INCLUDE_PATH, 'CORE', $path);
385:         }
386:         if (defined('ROOT') && strpos($path, ROOT) === 0) {
387:             return str_replace(ROOT, 'ROOT', $path);
388:         }
389: 
390:         return $path;
391:     }
392: 
393:     /**
394:      * Grabs an excerpt from a file and highlights a given line of code.
395:      *
396:      * Usage:
397:      *
398:      * ```
399:      * Debugger::excerpt('/path/to/file', 100, 4);
400:      * ```
401:      *
402:      * The above would return an array of 8 items. The 4th item would be the provided line,
403:      * and would be wrapped in `<span class="code-highlight"></span>`. All of the lines
404:      * are processed with highlight_string() as well, so they have basic PHP syntax highlighting
405:      * applied.
406:      *
407:      * @param string $file Absolute path to a PHP file.
408:      * @param int $line Line number to highlight.
409:      * @param int $context Number of lines of context to extract above and below $line.
410:      * @return array Set of lines highlighted
411:      * @see https://secure.php.net/highlight_string
412:      * @link https://book.cakephp.org/3.0/en/development/debugging.html#getting-an-excerpt-from-a-file
413:      */
414:     public static function excerpt($file, $line, $context = 2)
415:     {
416:         $lines = [];
417:         if (!file_exists($file)) {
418:             return [];
419:         }
420:         $data = file_get_contents($file);
421:         if (empty($data)) {
422:             return $lines;
423:         }
424:         if (strpos($data, "\n") !== false) {
425:             $data = explode("\n", $data);
426:         }
427:         $line--;
428:         if (!isset($data[$line])) {
429:             return $lines;
430:         }
431:         for ($i = $line - $context; $i < $line + $context + 1; $i++) {
432:             if (!isset($data[$i])) {
433:                 continue;
434:             }
435:             $string = str_replace(["\r\n", "\n"], '', static::_highlight($data[$i]));
436:             if ($i == $line) {
437:                 $lines[] = '<span class="code-highlight">' . $string . '</span>';
438:             } else {
439:                 $lines[] = $string;
440:             }
441:         }
442: 
443:         return $lines;
444:     }
445: 
446:     /**
447:      * Wraps the highlight_string function in case the server API does not
448:      * implement the function as it is the case of the HipHop interpreter
449:      *
450:      * @param string $str The string to convert.
451:      * @return string
452:      */
453:     protected static function _highlight($str)
454:     {
455:         if (function_exists('hphp_log') || function_exists('hphp_gettid')) {
456:             return htmlentities($str);
457:         }
458:         $added = false;
459:         if (strpos($str, '<?php') === false) {
460:             $added = true;
461:             $str = "<?php \n" . $str;
462:         }
463:         $highlight = highlight_string($str, true);
464:         if ($added) {
465:             $highlight = str_replace(
466:                 ['&lt;?php&nbsp;<br/>', '&lt;?php&nbsp;<br />'],
467:                 '',
468:                 $highlight
469:             );
470:         }
471: 
472:         return $highlight;
473:     }
474: 
475:     /**
476:      * Converts a variable to a string for debug output.
477:      *
478:      * *Note:* The following keys will have their contents
479:      * replaced with `*****`:
480:      *
481:      *  - password
482:      *  - login
483:      *  - host
484:      *  - database
485:      *  - port
486:      *  - prefix
487:      *  - schema
488:      *
489:      * This is done to protect database credentials, which could be accidentally
490:      * shown in an error message if CakePHP is deployed in development mode.
491:      *
492:      * @param mixed $var Variable to convert.
493:      * @param int $depth The depth to output to. Defaults to 3.
494:      * @return string Variable as a formatted string
495:      */
496:     public static function exportVar($var, $depth = 3)
497:     {
498:         return static::_export($var, $depth, 0);
499:     }
500: 
501:     /**
502:      * Protected export function used to keep track of indentation and recursion.
503:      *
504:      * @param mixed $var The variable to dump.
505:      * @param int $depth The remaining depth.
506:      * @param int $indent The current indentation level.
507:      * @return string The dumped variable.
508:      */
509:     protected static function _export($var, $depth, $indent)
510:     {
511:         switch (static::getType($var)) {
512:             case 'boolean':
513:                 return $var ? 'true' : 'false';
514:             case 'integer':
515:                 return '(int) ' . $var;
516:             case 'float':
517:                 return '(float) ' . $var;
518:             case 'string':
519:                 if (trim($var) === '' && ctype_space($var) === false) {
520:                     return "''";
521:                 }
522: 
523:                 return "'" . $var . "'";
524:             case 'array':
525:                 return static::_array($var, $depth - 1, $indent + 1);
526:             case 'resource':
527:                 return strtolower(gettype($var));
528:             case 'null':
529:                 return 'null';
530:             case 'unknown':
531:                 return 'unknown';
532:             default:
533:                 return static::_object($var, $depth - 1, $indent + 1);
534:         }
535:     }
536: 
537:     /**
538:      * Export an array type object. Filters out keys used in datasource configuration.
539:      *
540:      * The following keys are replaced with ***'s
541:      *
542:      * - password
543:      * - login
544:      * - host
545:      * - database
546:      * - port
547:      * - prefix
548:      * - schema
549:      *
550:      * @param array $var The array to export.
551:      * @param int $depth The current depth, used for recursion tracking.
552:      * @param int $indent The current indentation level.
553:      * @return string Exported array.
554:      */
555:     protected static function _array(array $var, $depth, $indent)
556:     {
557:         $out = '[';
558:         $break = $end = null;
559:         if (!empty($var)) {
560:             $break = "\n" . str_repeat("\t", $indent);
561:             $end = "\n" . str_repeat("\t", $indent - 1);
562:         }
563:         $vars = [];
564: 
565:         if ($depth >= 0) {
566:             $outputMask = (array)static::outputMask();
567:             foreach ($var as $key => $val) {
568:                 // Sniff for globals as !== explodes in < 5.4
569:                 if ($key === 'GLOBALS' && is_array($val) && isset($val['GLOBALS'])) {
570:                     $val = '[recursion]';
571:                 } elseif (array_key_exists($key, $outputMask)) {
572:                     $val = (string)$outputMask[$key];
573:                 } elseif ($val !== $var) {
574:                     $val = static::_export($val, $depth, $indent);
575:                 }
576:                 $vars[] = $break . static::exportVar($key) .
577:                     ' => ' .
578:                     $val;
579:             }
580:         } else {
581:             $vars[] = $break . '[maximum depth reached]';
582:         }
583: 
584:         return $out . implode(',', $vars) . $end . ']';
585:     }
586: 
587:     /**
588:      * Handles object to string conversion.
589:      *
590:      * @param object $var Object to convert.
591:      * @param int $depth The current depth, used for tracking recursion.
592:      * @param int $indent The current indentation level.
593:      * @return string
594:      * @see \Cake\Error\Debugger::exportVar()
595:      */
596:     protected static function _object($var, $depth, $indent)
597:     {
598:         $out = '';
599:         $props = [];
600: 
601:         $className = get_class($var);
602:         $out .= 'object(' . $className . ') {';
603:         $break = "\n" . str_repeat("\t", $indent);
604:         $end = "\n" . str_repeat("\t", $indent - 1);
605: 
606:         if ($depth > 0 && method_exists($var, '__debugInfo')) {
607:             try {
608:                 return $out . "\n" .
609:                     substr(static::_array($var->__debugInfo(), $depth - 1, $indent), 1, -1) .
610:                     $end . '}';
611:             } catch (Exception $e) {
612:                 $message = $e->getMessage();
613: 
614:                 return $out . "\n(unable to export object: $message)\n }";
615:             }
616:         }
617: 
618:         if ($depth > 0) {
619:             $outputMask = (array)static::outputMask();
620:             $objectVars = get_object_vars($var);
621:             foreach ($objectVars as $key => $value) {
622:                 $value = array_key_exists($key, $outputMask) ? $outputMask[$key] : static::_export($value, $depth - 1, $indent);
623:                 $props[] = "$key => " . $value;
624:             }
625: 
626:             $ref = new ReflectionObject($var);
627: 
628:             $filters = [
629:                 ReflectionProperty::IS_PROTECTED => 'protected',
630:                 ReflectionProperty::IS_PRIVATE => 'private',
631:             ];
632:             foreach ($filters as $filter => $visibility) {
633:                 $reflectionProperties = $ref->getProperties($filter);
634:                 foreach ($reflectionProperties as $reflectionProperty) {
635:                     $reflectionProperty->setAccessible(true);
636:                     $property = $reflectionProperty->getValue($var);
637: 
638:                     $value = static::_export($property, $depth - 1, $indent);
639:                     $key = $reflectionProperty->name;
640:                     $props[] = sprintf(
641:                         '[%s] %s => %s',
642:                         $visibility,
643:                         $key,
644:                         array_key_exists($key, $outputMask) ? $outputMask[$key] : $value
645:                     );
646:                 }
647:             }
648: 
649:             $out .= $break . implode($break, $props) . $end;
650:         }
651:         $out .= '}';
652: 
653:         return $out;
654:     }
655: 
656:     /**
657:      * Get the output format for Debugger error rendering.
658:      *
659:      * @return string Returns the current format when getting.
660:      */
661:     public static function getOutputFormat()
662:     {
663:         return Debugger::getInstance()->_outputFormat;
664:     }
665: 
666:     /**
667:      * Set the output format for Debugger error rendering.
668:      *
669:      * @param string $format The format you want errors to be output as.
670:      * @return void
671:      * @throws \InvalidArgumentException When choosing a format that doesn't exist.
672:      */
673:     public static function setOutputFormat($format)
674:     {
675:         $self = Debugger::getInstance();
676: 
677:         if (!isset($self->_templates[$format])) {
678:             throw new InvalidArgumentException('Invalid Debugger output format.');
679:         }
680:         $self->_outputFormat = $format;
681:     }
682: 
683:     /**
684:      * Get/Set the output format for Debugger error rendering.
685:      *
686:      * @deprecated 3.5.0 Use getOutputFormat()/setOutputFormat() instead.
687:      * @param string|null $format The format you want errors to be output as.
688:      *   Leave null to get the current format.
689:      * @return string|null Returns null when setting. Returns the current format when getting.
690:      * @throws \InvalidArgumentException When choosing a format that doesn't exist.
691:      */
692:     public static function outputAs($format = null)
693:     {
694:         deprecationWarning(
695:             'Debugger::outputAs() is deprecated. Use Debugger::getOutputFormat()/setOutputFormat() instead.'
696:         );
697:         $self = Debugger::getInstance();
698:         if ($format === null) {
699:             return $self->_outputFormat;
700:         }
701: 
702:         if (!isset($self->_templates[$format])) {
703:             throw new InvalidArgumentException('Invalid Debugger output format.');
704:         }
705:         $self->_outputFormat = $format;
706: 
707:         return null;
708:     }
709: 
710:     /**
711:      * Add an output format or update a format in Debugger.
712:      *
713:      * ```
714:      * Debugger::addFormat('custom', $data);
715:      * ```
716:      *
717:      * Where $data is an array of strings that use Text::insert() variable
718:      * replacement. The template vars should be in a `{:id}` style.
719:      * An error formatter can have the following keys:
720:      *
721:      * - 'error' - Used for the container for the error message. Gets the following template
722:      *   variables: `id`, `error`, `code`, `description`, `path`, `line`, `links`, `info`
723:      * - 'info' - A combination of `code`, `context` and `trace`. Will be set with
724:      *   the contents of the other template keys.
725:      * - 'trace' - The container for a stack trace. Gets the following template
726:      *   variables: `trace`
727:      * - 'context' - The container element for the context variables.
728:      *   Gets the following templates: `id`, `context`
729:      * - 'links' - An array of HTML links that are used for creating links to other resources.
730:      *   Typically this is used to create javascript links to open other sections.
731:      *   Link keys, are: `code`, `context`, `help`. See the js output format for an
732:      *   example.
733:      * - 'traceLine' - Used for creating lines in the stacktrace. Gets the following
734:      *   template variables: `reference`, `path`, `line`
735:      *
736:      * Alternatively if you want to use a custom callback to do all the formatting, you can use
737:      * the callback key, and provide a callable:
738:      *
739:      * ```
740:      * Debugger::addFormat('custom', ['callback' => [$foo, 'outputError']];
741:      * ```
742:      *
743:      * The callback can expect two parameters. The first is an array of all
744:      * the error data. The second contains the formatted strings generated using
745:      * the other template strings. Keys like `info`, `links`, `code`, `context` and `trace`
746:      * will be present depending on the other templates in the format type.
747:      *
748:      * @param string $format Format to use, including 'js' for JavaScript-enhanced HTML, 'html' for
749:      *    straight HTML output, or 'txt' for unformatted text.
750:      * @param array $strings Template strings, or a callback to be used for the output format.
751:      * @return array The resulting format string set.
752:      */
753:     public static function addFormat($format, array $strings)
754:     {
755:         $self = Debugger::getInstance();
756:         if (isset($self->_templates[$format])) {
757:             if (isset($strings['links'])) {
758:                 $self->_templates[$format]['links'] = array_merge(
759:                     $self->_templates[$format]['links'],
760:                     $strings['links']
761:                 );
762:                 unset($strings['links']);
763:             }
764:             $self->_templates[$format] = $strings + $self->_templates[$format];
765:         } else {
766:             $self->_templates[$format] = $strings;
767:         }
768: 
769:         return $self->_templates[$format];
770:     }
771: 
772:     /**
773:      * Takes a processed array of data from an error and displays it in the chosen format.
774:      *
775:      * @param array $data Data to output.
776:      * @return void
777:      */
778:     public function outputError($data)
779:     {
780:         $defaults = [
781:             'level' => 0,
782:             'error' => 0,
783:             'code' => 0,
784:             'description' => '',
785:             'file' => '',
786:             'line' => 0,
787:             'context' => [],
788:             'start' => 2,
789:         ];
790:         $data += $defaults;
791: 
792:         $files = static::trace(['start' => $data['start'], 'format' => 'points']);
793:         $code = '';
794:         $file = null;
795:         if (isset($files[0]['file'])) {
796:             $file = $files[0];
797:         } elseif (isset($files[1]['file'])) {
798:             $file = $files[1];
799:         }
800:         if ($file) {
801:             $code = static::excerpt($file['file'], $file['line'], 1);
802:         }
803:         $trace = static::trace(['start' => $data['start'], 'depth' => '20']);
804:         $insertOpts = ['before' => '{:', 'after' => '}'];
805:         $context = [];
806:         $links = [];
807:         $info = '';
808: 
809:         foreach ((array)$data['context'] as $var => $value) {
810:             $context[] = "\${$var} = " . static::exportVar($value, 3);
811:         }
812: 
813:         switch ($this->_outputFormat) {
814:             case false:
815:                 $this->_data[] = compact('context', 'trace') + $data;
816: 
817:                 return;
818:             case 'log':
819:                 static::log(compact('context', 'trace') + $data);
820: 
821:                 return;
822:         }
823: 
824:         $data['trace'] = $trace;
825:         $data['id'] = 'cakeErr' . uniqid();
826:         $tpl = $this->_templates[$this->_outputFormat] + $this->_templates['base'];
827: 
828:         if (isset($tpl['links'])) {
829:             foreach ($tpl['links'] as $key => $val) {
830:                 $links[$key] = Text::insert($val, $data, $insertOpts);
831:             }
832:         }
833: 
834:         if (!empty($tpl['escapeContext'])) {
835:             $context = h($context);
836:             $data['description'] = h($data['description']);
837:         }
838: 
839:         $infoData = compact('code', 'context', 'trace');
840:         foreach ($infoData as $key => $value) {
841:             if (empty($value) || !isset($tpl[$key])) {
842:                 continue;
843:             }
844:             if (is_array($value)) {
845:                 $value = implode("\n", $value);
846:             }
847:             $info .= Text::insert($tpl[$key], [$key => $value] + $data, $insertOpts);
848:         }
849:         $links = implode(' ', $links);
850: 
851:         if (isset($tpl['callback']) && is_callable($tpl['callback'])) {
852:             call_user_func($tpl['callback'], $data, compact('links', 'info'));
853: 
854:             return;
855:         }
856:         echo Text::insert($tpl['error'], compact('links', 'info') + $data, $insertOpts);
857:     }
858: 
859:     /**
860:      * Get the type of the given variable. Will return the class name
861:      * for objects.
862:      *
863:      * @param mixed $var The variable to get the type of.
864:      * @return string The type of variable.
865:      */
866:     public static function getType($var)
867:     {
868:         if (is_object($var)) {
869:             return get_class($var);
870:         }
871:         if ($var === null) {
872:             return 'null';
873:         }
874:         if (is_string($var)) {
875:             return 'string';
876:         }
877:         if (is_array($var)) {
878:             return 'array';
879:         }
880:         if (is_int($var)) {
881:             return 'integer';
882:         }
883:         if (is_bool($var)) {
884:             return 'boolean';
885:         }
886:         if (is_float($var)) {
887:             return 'float';
888:         }
889:         if (is_resource($var)) {
890:             return 'resource';
891:         }
892: 
893:         return 'unknown';
894:     }
895: 
896:     /**
897:      * Prints out debug information about given variable.
898:      *
899:      * @param mixed $var Variable to show debug information for.
900:      * @param array $location If contains keys "file" and "line" their values will
901:      *    be used to show location info.
902:      * @param bool|null $showHtml If set to true, the method prints the debug
903:      *    data in a browser-friendly way.
904:      * @return void
905:      */
906:     public static function printVar($var, $location = [], $showHtml = null)
907:     {
908:         $location += ['file' => null, 'line' => null];
909:         $file = $location['file'];
910:         $line = $location['line'];
911:         $lineInfo = '';
912:         if ($file) {
913:             $search = [];
914:             if (defined('ROOT')) {
915:                 $search = [ROOT];
916:             }
917:             if (defined('CAKE_CORE_INCLUDE_PATH')) {
918:                 array_unshift($search, CAKE_CORE_INCLUDE_PATH);
919:             }
920:             $file = str_replace($search, '', $file);
921:         }
922:         $html = <<<HTML
923: <div class="cake-debug-output" style="direction:ltr">
924: %s
925: <pre class="cake-debug">
926: %s
927: </pre>
928: </div>
929: HTML;
930:         $text = <<<TEXT
931: %s
932: ########## DEBUG ##########
933: %s
934: ###########################
935: 
936: TEXT;
937:         $template = $html;
938:         if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') || $showHtml === false) {
939:             $template = $text;
940:             if ($file && $line) {
941:                 $lineInfo = sprintf('%s (line %s)', $file, $line);
942:             }
943:         }
944:         if ($showHtml === null && $template !== $text) {
945:             $showHtml = true;
946:         }
947:         $var = Debugger::exportVar($var, 25);
948:         if ($showHtml) {
949:             $template = $html;
950:             $var = h($var);
951:             if ($file && $line) {
952:                 $lineInfo = sprintf('<span><strong>%s</strong> (line <strong>%s</strong>)</span>', $file, $line);
953:             }
954:         }
955:         printf($template, $lineInfo, $var);
956:     }
957: 
958:     /**
959:      * Verifies that the application's salt and cipher seed value has been changed from the default value.
960:      *
961:      * @return void
962:      */
963:     public static function checkSecurityKeys()
964:     {
965:         if (Security::getSalt() === '__SALT__') {
966:             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);
967:         }
968:     }
969: }
970: 
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