Advertisement
valdeir2000

StackOverflow 266968 - Debugger.php

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