2008-05-30 11:40:08 +00:00
< ? php
/**
* Framework debugging and PHP error - handling class
*
* Provides enhanced logging , stack traces , and rendering debug views
*
2009-05-01 21:05:46 +00:00
* CakePHP ( tm ) : Rapid Development Framework ( http :// cakephp . org )
2013-02-08 11:59:49 +00:00
* Copyright ( c ) Cake Software Foundation , Inc . ( http :// cakefoundation . org )
2008-05-30 11:40:08 +00:00
*
* Licensed under The MIT License
2013-02-08 12:22:51 +00:00
* For full copyright and license information , please see the LICENSE . txt
2008-05-30 11:40:08 +00:00
* Redistributions of files must retain the above copyright notice .
*
2013-02-08 11:59:49 +00:00
* @ copyright Copyright ( c ) Cake Software Foundation , Inc . ( http :// cakefoundation . org )
2010-01-26 22:03:03 +00:00
* @ link http :// cakephp . org CakePHP ( tm ) Project
2011-07-26 06:16:14 +00:00
* @ package Cake . Utility
2008-10-30 17:30:26 +00:00
* @ since CakePHP ( tm ) v 1.2 . 4560
2013-05-30 22:11:14 +00:00
* @ license http :// www . opensource . org / licenses / mit - license . php MIT License
2008-05-30 11:40:08 +00:00
*/
2009-05-01 21:05:46 +00:00
2010-12-04 07:04:30 +00:00
App :: uses ( 'CakeLog' , 'Log' );
App :: uses ( 'String' , 'Utility' );
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
* Provide custom logging and error handling .
*
* Debugger overrides PHP ' s default error handling to provide stack traces and enhanced logging
*
2011-07-26 06:16:14 +00:00
* @ package Cake . Utility
2011-10-15 17:12:23 +00:00
* @ link http :// book . cakephp . org / 2.0 / en / development / debugging . html #debugger-class
2008-05-30 11:40:08 +00:00
*/
2010-04-19 04:10:22 +00:00
class Debugger {
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2008-10-31 20:17:26 +00:00
* A list of errors generated by the application .
2008-05-30 11:40:08 +00:00
*
* @ var array
*/
2010-04-04 07:14:00 +00:00
public $errors = array ();
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2008-10-31 20:17:26 +00:00
* The current output format .
2008-05-30 11:40:08 +00:00
*
* @ var string
*/
2010-04-04 06:36:12 +00:00
protected $_outputFormat = 'js' ;
2009-05-01 21:05:46 +00:00
/**
2012-12-22 22:48:15 +00:00
* Templates used when generating trace or error strings . Can be global or indexed by the format
2009-05-01 21:05:46 +00:00
* value used in $_outputFormat .
*
* @ var string
*/
2010-04-04 06:36:12 +00:00
protected $_templates = array (
2009-05-01 21:05:46 +00:00
'log' => array (
'trace' => '{:reference} - {:path}, line {:line}' ,
'error' => " { :error} ( { :code}): { :description} in [ { :file}, line { :line}] "
),
'js' => array (
'error' => '' ,
'info' => '' ,
'trace' => '<pre class="stack-trace">{:trace}</pre>' ,
'code' => '' ,
'context' => '' ,
2012-05-17 01:07:45 +00:00
'links' => array (),
'escapeContext' => true ,
2009-05-01 21:05:46 +00:00
),
'html' => array (
2011-09-02 13:18:33 +00:00
'trace' => '<pre class="cake-error trace"><b>Trace</b> <p>{:trace}</p></pre>' ,
2012-05-17 01:07:45 +00:00
'context' => '<pre class="cake-error context"><b>Context</b> <p>{:context}</p></pre>' ,
'escapeContext' => true ,
2009-05-01 21:05:46 +00:00
),
'txt' => array (
'error' => " { :error}: { :code} :: { :description} on line { :line} of { :path} \n { :info} " ,
'code' => '' ,
'info' => ''
),
'base' => array (
2011-09-29 01:11:35 +00:00
'traceLine' => '{:reference} - {:path}, line {:line}' ,
'trace' => " Trace: \n { :trace} \n " ,
'context' => " Context: \n { :context} \n " ,
2014-01-15 07:14:00 +00:00
)
2009-05-01 21:05:46 +00:00
);
2008-05-30 11:40:08 +00:00
/**
2008-10-31 20:17:26 +00:00
* Holds current output data when outputFormat is false .
2008-05-30 11:40:08 +00:00
*
* @ var string
*/
2010-04-04 06:36:12 +00:00
protected $_data = array ();
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2008-10-31 20:17:26 +00:00
* Constructor .
2008-05-30 11:40:08 +00:00
*
*/
2011-05-28 20:38:46 +00:00
public function __construct () {
2008-05-30 11:40:08 +00:00
$docRef = ini_get ( 'docref_root' );
2009-05-01 21:05:46 +00:00
2010-10-19 20:46:17 +00:00
if ( empty ( $docRef ) && function_exists ( 'ini_set' )) {
2008-05-30 11:40:08 +00:00
ini_set ( 'docref_root' , 'http://php.net/' );
}
if ( ! defined ( 'E_RECOVERABLE_ERROR' )) {
define ( 'E_RECOVERABLE_ERROR' , 4096 );
}
2009-05-01 21:05:46 +00:00
2011-09-02 13:18:33 +00:00
$e = '<pre class="cake-error">' ;
2009-07-25 17:27:17 +00:00
$e .= '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-trace\')' ;
2009-05-01 21:05:46 +00:00
$e .= '.style.display = (document.getElementById(\'{:id}-trace\').style.display == ' ;
$e .= '\'none\' ? \'\' : \'none\');"><b>{:error}</b> ({:code})</a>: {:description} ' ;
$e .= '[<b>{:path}</b>, line <b>{:line}</b>]' ;
$e .= '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">' ;
$e .= '{:links}{:info}</div>' ;
2009-07-25 17:27:17 +00:00
$e .= '</pre>' ;
2009-05-01 21:05:46 +00:00
$this -> _templates [ 'js' ][ 'error' ] = $e ;
$t = '<div id="{:id}-trace" class="cake-stack-trace" style="display: none;">' ;
$t .= '{:context}{:code}{:trace}</div>' ;
$this -> _templates [ 'js' ][ 'info' ] = $t ;
$links = array ();
$link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-code\')' ;
$link .= '.style.display = (document.getElementById(\'{:id}-code\').style.display == ' ;
$link .= '\'none\' ? \'\' : \'none\')">Code</a>' ;
$links [ 'code' ] = $link ;
$link = '<a href="javascript:void(0);" onclick="document.getElementById(\'{:id}-context\')' ;
$link .= '.style.display = (document.getElementById(\'{:id}-context\').style.display == ' ;
$link .= '\'none\' ? \'\' : \'none\')">Context</a>' ;
$links [ 'context' ] = $link ;
$this -> _templates [ 'js' ][ 'links' ] = $links ;
$this -> _templates [ 'js' ][ 'context' ] = '<pre id="{:id}-context" class="cake-context" ' ;
$this -> _templates [ 'js' ][ 'context' ] .= 'style="display: none;">{:context}</pre>' ;
2011-09-02 08:23:46 +00:00
$this -> _templates [ 'js' ][ 'code' ] = '<pre id="{:id}-code" class="cake-code-dump" ' ;
$this -> _templates [ 'js' ][ 'code' ] .= 'style="display: none;">{:code}</pre>' ;
2009-05-01 21:05:46 +00:00
2011-09-02 13:18:33 +00:00
$e = '<pre class="cake-error"><b>{:error}</b> ({:code}) : {:description} ' ;
2009-05-01 21:05:46 +00:00
$e .= '[<b>{:path}</b>, line <b>{:line}]</b></pre>' ;
$this -> _templates [ 'html' ][ 'error' ] = $e ;
2011-09-02 13:18:33 +00:00
$this -> _templates [ 'html' ][ 'context' ] = '<pre class="cake-context"><b>Context</b> ' ;
2009-05-01 21:05:46 +00:00
$this -> _templates [ 'html' ][ 'context' ] .= '<p>{:context}</p></pre>' ;
2008-05-30 11:40:08 +00:00
}
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2008-10-31 20:17:26 +00:00
* Returns a reference to the Debugger singleton object instance .
2008-05-30 11:40:08 +00:00
*
2014-05-28 16:39:54 +00:00
* @ param string $class Debugger class name .
2008-05-30 11:40:08 +00:00
* @ return object
*/
2013-04-16 01:23:48 +00:00
public static function getInstance ( $class = null ) {
2008-05-30 11:40:08 +00:00
static $instance = array ();
2008-12-07 20:44:00 +00:00
if ( ! empty ( $class )) {
if ( ! $instance || strtolower ( $class ) != strtolower ( get_class ( $instance [ 0 ]))) {
2010-11-09 01:51:45 +00:00
$instance [ 0 ] = new $class ();
2008-12-07 20:44:00 +00:00
}
}
2008-09-12 05:11:34 +00:00
if ( ! $instance ) {
2010-11-09 01:51:45 +00:00
$instance [ 0 ] = new Debugger ();
2008-05-30 11:40:08 +00:00
}
return $instance [ 0 ];
}
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2011-08-07 18:31:14 +00:00
* Recursively formats and outputs the contents of the supplied variable .
*
2011-07-29 03:24:31 +00:00
* @ param mixed $var the variable to dump
2013-10-30 17:31:48 +00:00
* @ param int $depth The depth to output to . Defaults to 3.
2008-09-25 16:49:56 +00:00
* @ return void
2010-03-28 16:31:52 +00:00
* @ see Debugger :: exportVar ()
2011-10-15 17:12:23 +00:00
* @ link http :// book . cakephp . org / 2.0 / en / development / debugging . html #Debugger::dump
2011-08-07 18:31:14 +00:00
*/
2013-10-30 17:31:48 +00:00
public static function dump ( $var , $depth = 3 ) {
pr ( self :: exportVar ( $var , $depth ));
2008-05-30 11:40:08 +00:00
}
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2012-12-22 22:48:15 +00:00
* Creates an entry in the log file . The log entry will contain a stack trace from where it was called .
2010-03-28 16:31:52 +00:00
* as well as export the variable using exportVar . By default the log is written to the debug log .
2008-10-12 03:52:24 +00:00
*
2011-07-29 03:24:31 +00:00
* @ param mixed $var Variable or content to log
* @ param integer $level type of log to use . Defaults to LOG_DEBUG
2014-02-16 19:35:00 +00:00
* @ param int $depth The depth to output to . Defaults to 3.
2008-09-25 16:49:56 +00:00
* @ return void
2011-10-15 17:12:23 +00:00
* @ link http :// book . cakephp . org / 2.0 / en / development / debugging . html #Debugger::log
2008-09-25 16:49:56 +00:00
*/
2014-02-16 19:35:00 +00:00
public static function log ( $var , $level = LOG_DEBUG , $depth = 3 ) {
2010-04-19 04:15:56 +00:00
$source = self :: trace ( array ( 'start' => 1 )) . " \n " ;
2014-02-16 19:35:00 +00:00
CakeLog :: write ( $level , " \n " . $source . self :: exportVar ( $var , $depth ));
2008-05-30 11:40:08 +00:00
}
/**
2008-10-31 20:17:26 +00:00
* Overrides PHP ' s default error handling .
2008-05-30 11:40:08 +00:00
*
* @ param integer $code Code of error
* @ param string $description Error description
* @ param string $file File on which error occurred
* @ param integer $line Line that triggered the error
* @ param array $context Context
* @ return boolean true if error was handled
2013-08-12 10:51:12 +00:00
* @ deprecated Will be removed in 3.0 . This function is superseded by Debugger :: outputError () .
2008-05-30 11:40:08 +00:00
*/
2010-12-13 02:09:56 +00:00
public static function showError ( $code , $description , $file = null , $line = null , $context = null ) {
2012-03-03 22:31:47 +00:00
$self = Debugger :: getInstance ();
2008-05-30 11:40:08 +00:00
if ( empty ( $file )) {
$file = '[internal]' ;
}
if ( empty ( $line )) {
$line = '??' ;
}
$info = compact ( 'code' , 'description' , 'file' , 'line' );
2012-03-03 22:31:47 +00:00
if ( ! in_array ( $info , $self -> errors )) {
$self -> errors [] = $info ;
2008-05-30 11:40:08 +00:00
} else {
return ;
}
switch ( $code ) {
case E_PARSE :
case E_ERROR :
case E_CORE_ERROR :
case E_COMPILE_ERROR :
case E_USER_ERROR :
$error = 'Fatal Error' ;
2012-05-11 13:42:54 +00:00
$level = LOG_ERR ;
2013-07-02 22:52:48 +00:00
break ;
2008-05-30 11:40:08 +00:00
case E_WARNING :
case E_USER_WARNING :
case E_COMPILE_WARNING :
case E_RECOVERABLE_ERROR :
$error = 'Warning' ;
$level = LOG_WARNING ;
2013-07-02 22:52:48 +00:00
break ;
2008-05-30 11:40:08 +00:00
case E_NOTICE :
case E_USER_NOTICE :
$error = 'Notice' ;
$level = LOG_NOTICE ;
2013-07-02 22:52:48 +00:00
break ;
2012-02-04 18:27:09 +00:00
case E_DEPRECATED :
case E_USER_DEPRECATED :
$error = 'Deprecated' ;
$level = LOG_NOTICE ;
2013-07-02 22:52:48 +00:00
break ;
2008-05-30 11:40:08 +00:00
default :
2009-09-10 15:32:21 +00:00
return ;
2008-05-30 11:40:08 +00:00
}
2009-05-01 21:05:46 +00:00
$data = compact (
2011-09-03 11:18:03 +00:00
'level' , 'error' , 'code' , 'description' , 'file' , 'path' , 'line' , 'context'
2009-05-01 21:05:46 +00:00
);
2012-03-03 22:31:47 +00:00
echo $self -> outputError ( $data );
2008-05-30 11:40:08 +00:00
2013-02-12 02:38:08 +00:00
if ( $error === 'Fatal Error' ) {
2009-11-19 22:05:59 +00:00
exit ();
2008-05-30 11:40:08 +00:00
}
return true ;
}
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2008-10-31 20:17:26 +00:00
* Outputs a stack trace based on the supplied options .
2008-05-30 11:40:08 +00:00
*
2010-03-28 16:31:52 +00:00
* ### Options
*
* - `depth` - The number of stack frames to return . Defaults to 999
2012-12-22 22:48:15 +00:00
* - `format` - The format you want the return . Defaults to the currently selected format . If
2010-03-28 16:31:52 +00:00
* format is 'array' or 'points' the return will be an array .
* - `args` - Should arguments for functions be shown ? If true , the arguments for each method call
* will be displayed .
2012-12-22 22:48:15 +00:00
* - `start` - The stack frame to start generating a trace from . Defaults to 0
2010-03-28 16:31:52 +00:00
*
2008-05-30 11:40:08 +00:00
* @ param array $options Format for outputting stack trace
2010-03-28 16:31:52 +00:00
* @ return mixed Formatted stack trace
2011-10-15 17:12:23 +00:00
* @ link http :// book . cakephp . org / 2.0 / en / development / debugging . html #Debugger::trace
2008-05-30 11:40:08 +00:00
*/
2010-04-19 04:10:22 +00:00
public static function trace ( $options = array ()) {
2012-03-03 22:31:47 +00:00
$self = Debugger :: getInstance ();
2009-05-01 21:05:46 +00:00
$defaults = array (
2011-12-16 07:00:07 +00:00
'depth' => 999 ,
2012-03-03 22:31:47 +00:00
'format' => $self -> _outputFormat ,
2011-12-16 07:00:07 +00:00
'args' => false ,
'start' => 0 ,
'scope' => null ,
'exclude' => array ( 'call_user_func_array' , 'trigger_error' )
2008-05-30 11:40:08 +00:00
);
2012-03-11 01:57:18 +00:00
$options = Hash :: merge ( $defaults , $options );
2008-05-30 11:40:08 +00:00
$backtrace = debug_backtrace ();
2008-09-18 03:09:19 +00:00
$count = count ( $backtrace );
2009-05-01 21:05:46 +00:00
$back = array ();
$_trace = array (
2012-10-26 22:46:12 +00:00
'line' => '??' ,
'file' => '[internal]' ,
'class' => null ,
2009-05-01 21:05:46 +00:00
'function' => '[main]'
);
2008-05-30 11:40:08 +00:00
2008-09-18 03:09:19 +00:00
for ( $i = $options [ 'start' ]; $i < $count && $i < $options [ 'depth' ]; $i ++ ) {
2009-05-01 21:05:46 +00:00
$trace = array_merge ( array ( 'file' => '[internal]' , 'line' => '??' ), $backtrace [ $i ]);
2011-10-27 14:01:36 +00:00
$signature = $reference = '[main]' ;
2008-05-30 11:40:08 +00:00
if ( isset ( $backtrace [ $i + 1 ])) {
2009-05-01 21:05:46 +00:00
$next = array_merge ( $_trace , $backtrace [ $i + 1 ]);
2011-10-27 14:01:36 +00:00
$signature = $reference = $next [ 'function' ];
2008-05-30 11:40:08 +00:00
if ( ! empty ( $next [ 'class' ])) {
2011-10-27 14:01:36 +00:00
$signature = $next [ 'class' ] . '::' . $next [ 'function' ];
$reference = $signature . '(' ;
2008-05-30 11:40:08 +00:00
if ( $options [ 'args' ] && isset ( $next [ 'args' ])) {
$args = array ();
foreach ( $next [ 'args' ] as $arg ) {
$args [] = Debugger :: exportVar ( $arg );
}
2012-09-15 10:06:02 +00:00
$reference .= implode ( ', ' , $args );
2008-05-30 11:40:08 +00:00
}
2009-05-01 21:05:46 +00:00
$reference .= ')' ;
2008-05-30 11:40:08 +00:00
}
}
2011-10-27 14:01:36 +00:00
if ( in_array ( $signature , $options [ 'exclude' ])) {
2008-05-30 11:40:08 +00:00
continue ;
}
2013-02-12 02:38:08 +00:00
if ( $options [ 'format' ] === 'points' && $trace [ 'file' ] !== '[internal]' ) {
2008-05-30 11:40:08 +00:00
$back [] = array ( 'file' => $trace [ 'file' ], 'line' => $trace [ 'line' ]);
2013-02-12 02:38:08 +00:00
} elseif ( $options [ 'format' ] === 'array' ) {
2008-05-30 11:40:08 +00:00
$back [] = $trace ;
2009-05-01 21:05:46 +00:00
} else {
2012-03-03 22:31:47 +00:00
if ( isset ( $self -> _templates [ $options [ 'format' ]][ 'traceLine' ])) {
$tpl = $self -> _templates [ $options [ 'format' ]][ 'traceLine' ];
2009-05-01 21:05:46 +00:00
} else {
2012-03-03 22:31:47 +00:00
$tpl = $self -> _templates [ 'base' ][ 'traceLine' ];
2009-05-01 21:05:46 +00:00
}
2010-04-19 04:15:56 +00:00
$trace [ 'path' ] = self :: trimPath ( $trace [ 'file' ]);
2009-05-01 21:05:46 +00:00
$trace [ 'reference' ] = $reference ;
unset ( $trace [ 'object' ], $trace [ 'args' ]);
$back [] = String :: insert ( $tpl , $trace , array ( 'before' => '{:' , 'after' => '}' ));
2008-05-30 11:40:08 +00:00
}
}
2013-02-12 02:38:08 +00:00
if ( $options [ 'format' ] === 'array' || $options [ 'format' ] === 'points' ) {
2008-05-30 11:40:08 +00:00
return $back ;
}
2009-11-19 22:13:35 +00:00
return implode ( " \n " , $back );
2008-05-30 11:40:08 +00:00
}
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
* Shortens file paths by replacing the application base path with 'APP' , and the CakePHP core
2008-10-31 20:17:26 +00:00
* path with 'CORE' .
2008-05-30 11:40:08 +00:00
*
* @ param string $path Path to shorten
* @ return string Normalized path
*/
2010-04-19 04:10:22 +00:00
public static function trimPath ( $path ) {
2008-05-30 11:40:08 +00:00
if ( ! defined ( 'CAKE_CORE_INCLUDE_PATH' ) || ! defined ( 'APP' )) {
return $path ;
}
if ( strpos ( $path , APP ) === 0 ) {
return str_replace ( APP , 'APP' . DS , $path );
} elseif ( strpos ( $path , CAKE_CORE_INCLUDE_PATH ) === 0 ) {
return str_replace ( CAKE_CORE_INCLUDE_PATH , 'CORE' , $path );
} elseif ( strpos ( $path , ROOT ) === 0 ) {
return str_replace ( ROOT , 'ROOT' , $path );
}
2011-04-17 10:35:21 +00:00
2008-05-30 11:40:08 +00:00
return $path ;
}
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2011-08-07 18:31:14 +00:00
* Grabs an excerpt from a file and highlights a given line of code .
*
* Usage :
*
* `Debugger::excerpt('/path/to/file', 100, 4);`
*
* The above would return an array of 8 items . The 4 th item would be the provided line ,
2012-12-22 22:48:15 +00:00
* and would be wrapped in `<span class="code-highlight"></span>` . All of the lines
2011-08-07 18:31:14 +00:00
* are processed with highlight_string () as well , so they have basic PHP syntax highlighting
* applied .
2008-05-30 11:40:08 +00:00
*
* @ param string $file Absolute path to a PHP file
* @ param integer $line Line number to highlight
* @ param integer $context Number of lines of context to extract above and below $line
* @ return array Set of lines highlighted
2011-08-07 18:31:14 +00:00
* @ see http :// php . net / highlight_string
2011-10-15 17:12:23 +00:00
* @ link http :// book . cakephp . org / 2.0 / en / development / debugging . html #Debugger::excerpt
2008-05-30 11:40:08 +00:00
*/
2010-04-19 04:10:22 +00:00
public static function excerpt ( $file , $line , $context = 2 ) {
2011-04-22 20:00:08 +00:00
$lines = array ();
2008-10-18 01:23:33 +00:00
if ( ! file_exists ( $file )) {
return array ();
}
2012-11-14 09:00:15 +00:00
$data = file_get_contents ( $file );
2012-12-09 01:48:13 +00:00
if ( empty ( $data )) {
return $lines ;
}
if ( strpos ( $data , " \n " ) !== false ) {
2012-11-14 09:00:15 +00:00
$data = explode ( " \n " , $data );
}
2012-12-09 01:48:13 +00:00
if ( ! isset ( $data [ $line ])) {
return $lines ;
2008-05-30 11:40:08 +00:00
}
for ( $i = $line - ( $context + 1 ); $i < $line + $context ; $i ++ ) {
if ( ! isset ( $data [ $i ])) {
continue ;
}
2012-01-07 23:34:14 +00:00
$string = str_replace ( array ( " \r \n " , " \n " ), " " , self :: _highlight ( $data [ $i ]));
2008-05-30 11:40:08 +00:00
if ( $i == $line ) {
$lines [] = '<span class="code-highlight">' . $string . '</span>' ;
} else {
$lines [] = $string ;
}
}
return $lines ;
}
2009-05-01 21:05:46 +00:00
2012-01-07 23:34:14 +00:00
/**
2013-04-23 10:34:59 +00:00
* Wraps the highlight_string function in case the server API does not
2012-01-07 23:34:14 +00:00
* implement the function as it is the case of the HipHop interpreter
*
* @ param string $str the string to convert
* @ return string
*/
protected static function _highlight ( $str ) {
2012-12-27 13:38:43 +00:00
if ( function_exists ( 'hphp_log' ) || function_exists ( 'hphp_gettid' )) {
2012-01-07 23:34:14 +00:00
return htmlentities ( $str );
}
2012-12-09 01:48:13 +00:00
$added = false ;
if ( strpos ( $str , '<?php' ) === false ) {
$added = true ;
$str = " <?php \n " . $str ;
}
$highlight = highlight_string ( $str , true );
if ( $added ) {
$highlight = str_replace (
'<?php <br />' ,
'' ,
$highlight
);
}
return $highlight ;
2012-01-07 23:34:14 +00:00
}
2008-05-30 11:40:08 +00:00
/**
2008-10-31 20:17:26 +00:00
* Converts a variable to a string for debug output .
2008-05-30 11:40:08 +00:00
*
2011-12-21 21:45:04 +00:00
* * Note :* The following keys will have their contents
2011-10-15 15:42:09 +00:00
* replaced with `*****` :
2011-08-07 18:31:14 +00:00
*
* - password
* - login
* - host
* - database
* - port
* - prefix
* - schema
*
* This is done to protect database credentials , which could be accidentally
* shown in an error message if CakePHP is deployed in development mode .
*
2008-05-30 11:40:08 +00:00
* @ param string $var Variable to convert
2011-10-15 20:40:11 +00:00
* @ param integer $depth The depth to output to . Defaults to 3.
2008-05-30 11:40:08 +00:00
* @ return string Variable as a formatted string
2011-10-15 17:12:23 +00:00
* @ link http :// book . cakephp . org / 2.0 / en / development / debugging . html #Debugger::exportVar
2008-05-30 11:40:08 +00:00
*/
2011-10-15 15:42:09 +00:00
public static function exportVar ( $var , $depth = 3 ) {
return self :: _export ( $var , $depth , 0 );
}
2011-10-15 20:40:11 +00:00
/**
* Protected export function used to keep track of indentation and recursion .
*
* @ param mixed $var The variable to dump .
* @ param integer $depth The remaining depth .
* @ param integer $indent The current indentation level .
* @ return string The dumped variable .
*/
2011-10-15 15:42:09 +00:00
protected static function _export ( $var , $depth , $indent ) {
switch ( self :: getType ( $var )) {
2008-05-30 11:40:08 +00:00
case 'boolean' :
2008-07-30 20:34:01 +00:00
return ( $var ) ? 'true' : 'false' ;
2008-05-30 11:40:08 +00:00
case 'integer' :
2011-10-15 15:42:09 +00:00
return '(int) ' . $var ;
case 'float' :
return '(float) ' . $var ;
2008-05-30 11:40:08 +00:00
case 'string' :
2013-01-10 04:04:58 +00:00
if ( trim ( $var ) === '' ) {
2011-10-15 15:42:09 +00:00
return " '' " ;
2008-05-30 11:40:08 +00:00
}
2011-10-21 01:38:31 +00:00
return " ' " . $var . " ' " ;
2008-05-30 11:40:08 +00:00
case 'array' :
2011-10-15 15:42:09 +00:00
return self :: _array ( $var , $depth - 1 , $indent + 1 );
2008-05-30 11:40:08 +00:00
case 'resource' :
return strtolower ( gettype ( $var ));
case 'null' :
return 'null' ;
2013-08-06 21:14:41 +00:00
case 'unknown' :
return 'unknown' ;
2011-10-15 15:42:09 +00:00
default :
return self :: _object ( $var , $depth - 1 , $indent + 1 );
2008-05-30 11:40:08 +00:00
}
}
2009-05-01 21:05:46 +00:00
2011-10-15 15:42:09 +00:00
/**
2012-12-22 22:48:15 +00:00
* Export an array type object . Filters out keys used in datasource configuration .
2011-10-15 15:42:09 +00:00
*
2012-01-25 01:52:43 +00:00
* The following keys are replaced with *** ' s
*
* - password
* - login
* - host
* - database
* - port
* - prefix
* - schema
*
2011-10-15 15:42:09 +00:00
* @ param array $var The array to export .
* @ param integer $depth The current depth , used for recursion tracking .
2011-10-15 20:40:11 +00:00
* @ param integer $indent The current indentation level .
2011-10-15 15:42:09 +00:00
* @ return string Exported array .
*/
protected static function _array ( array $var , $depth , $indent ) {
2012-01-25 01:52:43 +00:00
$secrets = array (
2011-10-15 15:42:09 +00:00
'password' => '*****' ,
2012-10-26 22:46:12 +00:00
'login' => '*****' ,
2011-10-15 15:42:09 +00:00
'host' => '*****' ,
'database' => '*****' ,
'port' => '*****' ,
'prefix' => '*****' ,
'schema' => '*****'
2012-01-25 01:52:43 +00:00
);
$replace = array_intersect_key ( $secrets , $var );
$var = $replace + $var ;
2011-10-15 15:42:09 +00:00
$out = " array( " ;
2014-02-11 21:38:24 +00:00
$break = $end = null ;
2011-10-15 15:42:09 +00:00
if ( ! empty ( $var )) {
$break = " \n " . str_repeat ( " \t " , $indent );
$end = " \n " . str_repeat ( " \t " , $indent - 1 );
}
$vars = array ();
if ( $depth >= 0 ) {
foreach ( $var as $key => $val ) {
2012-08-09 03:09:44 +00:00
// Sniff for globals as !== explodes in < 5.4
if ( $key === 'GLOBALS' && is_array ( $val ) && isset ( $val [ 'GLOBALS' ])) {
2012-08-09 02:30:27 +00:00
$val = '[recursion]' ;
2013-06-09 15:12:46 +00:00
} elseif ( $val !== $var ) {
2012-08-09 03:09:44 +00:00
$val = self :: _export ( $val , $depth , $indent );
2012-08-09 02:30:27 +00:00
}
2011-10-15 15:42:09 +00:00
$vars [] = $break . self :: exportVar ( $key ) .
' => ' .
2012-08-09 02:30:27 +00:00
$val ;
2011-10-15 15:42:09 +00:00
}
2012-06-11 00:00:34 +00:00
} else {
$vars [] = $break . '[maximum depth reached]' ;
2011-10-15 15:42:09 +00:00
}
return $out . implode ( ',' , $vars ) . $end . ')' ;
}
2008-05-30 11:40:08 +00:00
/**
2008-10-31 20:17:26 +00:00
* Handles object to string conversion .
2008-05-30 11:40:08 +00:00
*
* @ param string $var Object to convert
2011-10-15 15:42:09 +00:00
* @ param integer $depth The current depth , used for tracking recursion .
2011-10-15 20:40:11 +00:00
* @ param integer $indent The current indentation level .
2008-09-25 16:49:56 +00:00
* @ return string
2010-03-28 16:31:52 +00:00
* @ see Debugger :: exportVar ()
2008-05-30 11:40:08 +00:00
*/
2011-10-15 15:42:09 +00:00
protected static function _object ( $var , $depth , $indent ) {
$out = '' ;
$props = array ();
$className = get_class ( $var );
$out .= 'object(' . $className . ') {' ;
2011-10-15 20:40:11 +00:00
2011-10-15 15:42:09 +00:00
if ( $depth > 0 ) {
$end = " \n " . str_repeat ( " \t " , $indent - 1 );
$break = " \n " . str_repeat ( " \t " , $indent );
2008-05-30 11:40:08 +00:00
$objectVars = get_object_vars ( $var );
2008-10-23 00:10:44 +00:00
foreach ( $objectVars as $key => $value ) {
2011-10-15 15:42:09 +00:00
$value = self :: _export ( $value , $depth - 1 , $indent );
$props [] = " $key => " . $value ;
2008-05-30 11:40:08 +00:00
}
2012-08-06 17:31:35 +00:00
if ( version_compare ( PHP_VERSION , '5.3.0' ) >= 0 ) {
$ref = new ReflectionObject ( $var );
2013-08-06 22:37:20 +00:00
$filters = array (
ReflectionProperty :: IS_PROTECTED => 'protected' ,
ReflectionProperty :: IS_PRIVATE => 'private' ,
);
foreach ( $filters as $filter => $visibility ) {
$reflectionProperties = $ref -> getProperties ( $filter );
foreach ( $reflectionProperties as $reflectionProperty ) {
$reflectionProperty -> setAccessible ( true );
$property = $reflectionProperty -> getValue ( $var );
$value = self :: _export ( $property , $depth - 1 , $indent );
$key = $reflectionProperty -> name ;
$props [] = sprintf ( '[%s] %s => %s' , $visibility , $key , $value );
}
2012-08-06 17:31:35 +00:00
}
}
2011-10-15 15:42:09 +00:00
$out .= $break . implode ( $break , $props ) . $end ;
2008-05-30 11:40:08 +00:00
}
2011-10-15 15:42:09 +00:00
$out .= '}' ;
return $out ;
2008-05-30 11:40:08 +00:00
}
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2011-08-21 13:56:23 +00:00
* Get / Set the output format for Debugger error rendering .
2011-08-21 13:18:15 +00:00
*
2011-08-21 13:56:23 +00:00
* @ param string $format The format you want errors to be output as .
* Leave null to get the current format .
2012-12-22 22:48:15 +00:00
* @ return mixed Returns null when setting . Returns the current format when getting .
2011-08-21 14:19:43 +00:00
* @ throws CakeException when choosing a format that doesn ' t exist .
2011-08-21 13:56:23 +00:00
*/
public static function outputAs ( $format = null ) {
$self = Debugger :: getInstance ();
if ( $format === null ) {
return $self -> _outputFormat ;
}
2011-08-21 14:19:43 +00:00
if ( $format !== false && ! isset ( $self -> _templates [ $format ])) {
throw new CakeException ( __d ( 'cake_dev' , 'Invalid Debugger output format.' ));
}
2011-08-21 13:56:23 +00:00
$self -> _outputFormat = $format ;
}
/**
* Add an output format or update a format in Debugger .
2011-08-21 13:18:15 +00:00
*
2011-08-21 13:56:23 +00:00
* `Debugger::addFormat('custom', $data);`
2011-08-21 13:18:15 +00:00
*
2011-10-28 05:01:17 +00:00
* Where $data is an array of strings that use String :: insert () variable
2012-12-22 22:48:15 +00:00
* replacement . The template vars should be in a `{:id}` style .
2011-08-21 13:18:15 +00:00
* An error formatter can have the following keys :
*
* - 'error' - Used for the container for the error message . Gets the following template
* variables : `id` , `error` , `code` , `description` , `path` , `line` , `links` , `info`
2011-08-21 14:47:52 +00:00
* - 'info' - A combination of `code` , `context` and `trace` . Will be set with
* the contents of the other template keys .
2011-08-21 13:18:15 +00:00
* - 'trace' - The container for a stack trace . Gets the following template
* variables : `trace`
2011-10-28 05:01:17 +00:00
* - 'context' - The container element for the context variables .
2011-08-21 13:18:15 +00:00
* Gets the following templates : `id` , `context`
* - 'links' - An array of HTML links that are used for creating links to other resources .
* Typically this is used to create javascript links to open other sections .
2012-12-22 22:48:15 +00:00
* Link keys , are : `code` , `context` , `help` . See the js output format for an
2011-08-21 13:18:15 +00:00
* example .
* - 'traceLine' - Used for creating lines in the stacktrace . Gets the following
* template variables : `reference` , `path` , `line`
2008-05-30 11:40:08 +00:00
*
2011-08-21 14:47:52 +00:00
* Alternatively if you want to use a custom callback to do all the formatting , you can use
* the callback key , and provide a callable :
*
* `Debugger::addFormat('custom', array('callback' => array($foo, 'outputError'));`
*
2012-12-22 22:48:15 +00:00
* The callback can expect two parameters . The first is an array of all
2011-08-21 14:47:52 +00:00
* the error data . The second contains the formatted strings generated using
2012-12-22 22:48:15 +00:00
* the other template strings . Keys like `info` , `links` , `code` , `context` and `trace`
2011-08-21 14:47:52 +00:00
* will be present depending on the other templates in the format type .
*
2009-05-01 21:05:46 +00:00
* @ param string $format Format to use , including 'js' for JavaScript - enhanced HTML , 'html' for
2010-03-28 16:31:52 +00:00
* straight HTML output , or 'txt' for unformatted text .
2011-08-21 14:47:52 +00:00
* @ param array $strings Template strings , or a callback to be used for the output format .
2011-08-21 13:56:23 +00:00
* @ return The resulting format string set .
*/
public static function addFormat ( $format , array $strings ) {
$self = Debugger :: getInstance ();
if ( isset ( $self -> _templates [ $format ])) {
if ( isset ( $strings [ 'links' ])) {
$self -> _templates [ $format ][ 'links' ] = array_merge (
$self -> _templates [ $format ][ 'links' ],
$strings [ 'links' ]
);
unset ( $strings [ 'links' ]);
}
$self -> _templates [ $format ] = array_merge ( $self -> _templates [ $format ], $strings );
} else {
$self -> _templates [ $format ] = $strings ;
}
return $self -> _templates [ $format ];
}
/**
2011-10-28 05:01:17 +00:00
* Switches output format , updates format strings .
2011-08-21 13:56:23 +00:00
* Can be used to switch the active output format :
*
* @ param string $format Format to use , including 'js' for JavaScript - enhanced HTML , 'html' for
* straight HTML output , or 'txt' for unformatted text .
* @ param array $strings Template strings to be used for the output format .
2011-07-29 03:24:31 +00:00
* @ return string
2013-07-05 12:36:40 +00:00
* @ deprecated Use Debugger :: outputAs () and Debugger :: addFormat () . Will be removed
2011-08-21 13:56:23 +00:00
* in 3.0
2008-05-30 11:40:08 +00:00
*/
2014-05-12 18:29:10 +00:00
public static function output ( $format = null , $strings = array ()) {
2012-03-03 22:31:47 +00:00
$self = Debugger :: getInstance ();
2008-05-30 11:40:08 +00:00
$data = null ;
2013-08-16 18:12:49 +00:00
if ( $format === null ) {
2011-08-21 13:56:23 +00:00
return Debugger :: outputAs ();
2009-05-01 21:05:46 +00:00
}
if ( ! empty ( $strings )) {
2011-08-21 13:56:23 +00:00
return Debugger :: addFormat ( $format , $strings );
2009-05-01 21:05:46 +00:00
}
2012-03-03 22:31:47 +00:00
if ( $format === true && ! empty ( $self -> _data )) {
$data = $self -> _data ;
$self -> _data = array ();
2008-05-30 11:40:08 +00:00
$format = false ;
}
2011-08-21 13:56:23 +00:00
Debugger :: outputAs ( $format );
2008-05-30 11:40:08 +00:00
return $data ;
}
2009-05-01 21:05:46 +00:00
2008-05-30 11:40:08 +00:00
/**
2010-11-15 03:06:18 +00:00
* Takes a processed array of data from an error and displays it in the chosen format .
2008-05-30 11:40:08 +00:00
*
2014-05-28 16:39:54 +00:00
* @ param string $data Data to output .
2010-11-15 03:06:18 +00:00
* @ return void
2008-05-30 11:40:08 +00:00
*/
2010-11-15 03:06:18 +00:00
public function outputError ( $data ) {
2009-05-01 21:05:46 +00:00
$defaults = array (
'level' => 0 ,
'error' => 0 ,
'code' => 0 ,
'description' => '' ,
'file' => '' ,
'line' => 0 ,
2010-11-15 03:06:18 +00:00
'context' => array (),
2011-09-03 11:18:03 +00:00
'start' => 2 ,
2009-05-01 21:05:46 +00:00
);
$data += $defaults ;
2010-11-15 03:06:18 +00:00
$files = $this -> trace ( array ( 'start' => $data [ 'start' ], 'format' => 'points' ));
2011-12-30 03:01:46 +00:00
$code = '' ;
2012-07-25 03:37:37 +00:00
$file = null ;
if ( isset ( $files [ 0 ][ 'file' ])) {
$file = $files [ 0 ];
} elseif ( isset ( $files [ 1 ][ 'file' ])) {
$file = $files [ 1 ];
}
if ( $file ) {
$code = $this -> excerpt ( $file [ 'file' ], $file [ 'line' ] - 1 , 1 );
2011-12-30 03:01:46 +00:00
}
2010-11-15 03:06:18 +00:00
$trace = $this -> trace ( array ( 'start' => $data [ 'start' ], 'depth' => '20' ));
2009-05-01 21:05:46 +00:00
$insertOpts = array ( 'before' => '{:' , 'after' => '}' );
2008-05-30 11:40:08 +00:00
$context = array ();
2009-05-01 21:05:46 +00:00
$links = array ();
$info = '' ;
2008-05-30 11:40:08 +00:00
2009-05-01 21:05:46 +00:00
foreach (( array ) $data [ 'context' ] as $var => $value ) {
2012-07-25 03:39:48 +00:00
$context [] = " \$ { $var } = " . $this -> exportVar ( $value , 3 );
2008-05-30 11:40:08 +00:00
}
2008-12-07 20:44:00 +00:00
switch ( $this -> _outputFormat ) {
2009-05-01 21:05:46 +00:00
case false :
$this -> _data [] = compact ( 'context' , 'trace' ) + $data ;
return ;
case 'log' :
$this -> log ( compact ( 'context' , 'trace' ) + $data );
return ;
}
2008-05-30 11:40:08 +00:00
2011-09-29 03:26:38 +00:00
$data [ 'trace' ] = $trace ;
2011-05-05 03:30:24 +00:00
$data [ 'id' ] = 'cakeErr' . uniqid ();
2009-05-01 21:05:46 +00:00
$tpl = array_merge ( $this -> _templates [ 'base' ], $this -> _templates [ $this -> _outputFormat ]);
if ( isset ( $tpl [ 'links' ])) {
foreach ( $tpl [ 'links' ] as $key => $val ) {
2012-05-17 01:07:45 +00:00
$links [ $key ] = String :: insert ( $val , $data , $insertOpts );
2009-05-01 21:05:46 +00:00
}
2008-05-30 11:40:08 +00:00
}
2009-05-01 21:05:46 +00:00
2012-05-17 01:07:45 +00:00
if ( ! empty ( $tpl [ 'escapeContext' ])) {
$context = h ( $context );
}
$infoData = compact ( 'code' , 'context' , 'trace' );
foreach ( $infoData as $key => $value ) {
if ( empty ( $value ) || ! isset ( $tpl [ $key ])) {
2009-05-01 21:05:46 +00:00
continue ;
}
2012-05-17 01:07:45 +00:00
if ( is_array ( $value )) {
2012-09-15 10:06:02 +00:00
$value = implode ( " \n " , $value );
2009-05-01 21:05:46 +00:00
}
2012-05-17 01:07:45 +00:00
$info .= String :: insert ( $tpl [ $key ], array ( $key => $value ) + $data , $insertOpts );
2009-05-01 21:05:46 +00:00
}
2012-09-15 10:06:02 +00:00
$links = implode ( ' ' , $links );
2012-05-17 01:07:45 +00:00
2011-08-21 14:47:52 +00:00
if ( isset ( $tpl [ 'callback' ]) && is_callable ( $tpl [ 'callback' ])) {
return call_user_func ( $tpl [ 'callback' ], $data , compact ( 'links' , 'info' ));
}
2009-05-01 21:05:46 +00:00
echo String :: insert ( $tpl [ 'error' ], compact ( 'links' , 'info' ) + $data , $insertOpts );
2008-05-30 11:40:08 +00:00
}
2009-05-01 21:05:46 +00:00
2011-10-09 03:27:05 +00:00
/**
2013-10-23 02:59:50 +00:00
* Get the type of the given variable . Will return the class name
2011-10-09 03:27:05 +00:00
* for objects .
*
* @ param mixed $var The variable to get the type of
* @ return string The type of variable .
*/
public static function getType ( $var ) {
if ( is_object ( $var )) {
2011-12-21 21:45:04 +00:00
return get_class ( $var );
2011-10-09 03:27:05 +00:00
}
2013-08-16 18:12:49 +00:00
if ( $var === null ) {
2011-10-09 03:27:05 +00:00
return 'null' ;
}
if ( is_string ( $var )) {
2011-10-15 20:40:11 +00:00
return 'string' ;
2011-10-09 03:27:05 +00:00
}
if ( is_array ( $var )) {
return 'array' ;
}
if ( is_int ( $var )) {
2011-10-15 20:40:11 +00:00
return 'integer' ;
2011-10-09 03:27:05 +00:00
}
if ( is_bool ( $var )) {
2011-10-15 20:40:11 +00:00
return 'boolean' ;
2011-10-09 03:27:05 +00:00
}
if ( is_float ( $var )) {
2011-10-15 20:40:11 +00:00
return 'float' ;
2011-10-09 03:27:05 +00:00
}
if ( is_resource ( $var )) {
2011-10-15 20:40:11 +00:00
return 'resource' ;
2011-10-09 03:27:05 +00:00
}
2011-10-15 20:40:11 +00:00
return 'unknown' ;
2011-10-09 03:27:05 +00:00
}
2008-05-30 11:40:08 +00:00
/**
2010-01-15 21:56:26 +00:00
* Verifies that the application ' s salt and cipher seed value has been changed from the default value .
2008-05-30 11:40:08 +00:00
*
2011-07-29 03:24:31 +00:00
* @ return void
2008-05-30 11:40:08 +00:00
*/
2010-04-19 04:10:22 +00:00
public static function checkSecurityKeys () {
2013-02-12 02:38:08 +00:00
if ( Configure :: read ( 'Security.salt' ) === 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi' ) {
2013-08-20 22:05:53 +00:00
trigger_error ( __d ( 'cake_dev' , 'Please change the value of %s in %s to a salt value specific to your application.' , '\'Security.salt\'' , 'APP/Config/core.php' ), E_USER_NOTICE );
2008-05-30 11:40:08 +00:00
}
2010-01-15 21:56:26 +00:00
2010-02-03 10:09:52 +00:00
if ( Configure :: read ( 'Security.cipherSeed' ) === '76859309657453542496749683645' ) {
2013-08-20 22:05:53 +00:00
trigger_error ( __d ( 'cake_dev' , 'Please change the value of %s in %s to a numeric (digits only) seed value specific to your application.' , '\'Security.cipherSeed\'' , 'APP/Config/core.php' ), E_USER_NOTICE );
2010-01-15 21:56:26 +00:00
}
2008-05-30 11:40:08 +00:00
}
2012-03-03 22:31:47 +00:00
2008-05-30 11:40:08 +00:00
}