00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 if (!class_exists('Object')) {
00034 uses('object');
00035 }
00036 if (!class_exists('CakeLog')) {
00037 uses('cake_log');
00038 }
00039
00040
00041
00042
00043
00044
00045
00046
00047 class Debugger extends Object {
00048
00049
00050
00051
00052
00053
00054
00055 var $errors = array();
00056
00057
00058
00059
00060
00061
00062 var $helpPath = null;
00063
00064
00065
00066
00067
00068
00069 var $__outputFormat = 'js';
00070
00071
00072
00073
00074
00075
00076 var $__data = array();
00077
00078
00079
00080
00081 function __construct() {
00082 $docRef = ini_get('docref_root');
00083 if (empty($docRef)) {
00084 ini_set('docref_root', 'http:
00085 }
00086 if (!defined('E_RECOVERABLE_ERROR')) {
00087 define('E_RECOVERABLE_ERROR', 4096);
00088 }
00089 }
00090
00091
00092
00093
00094
00095
00096 function &getInstance() {
00097 static $instance = array();
00098
00099 if (!isset($instance[0]) || !$instance[0]) {
00100 $instance[0] =& new Debugger();
00101 if (Configure::read() > 0) {
00102 Configure::version();
00103 $instance[0]->helpPath = Configure::read('Cake.Debugger.HelpPath');
00104 }
00105 }
00106 return $instance[0];
00107 }
00108
00109
00110
00111 function dump($var) {
00112 $_this = Debugger::getInstance();
00113 pr($_this->exportVar($var));
00114 }
00115
00116
00117
00118 function log($var, $level = LOG_DEBUG) {
00119 $_this = Debugger::getInstance();
00120 $trace = $_this->trace(array('start' => 1, 'depth' => 2, 'format' => 'array'));
00121 $source = null;
00122
00123 if (is_object($trace[0]['object']) && isset($trace[0]['object']->_reporter->_test_stack)) {
00124 $stack = $trace[0]['object']->_reporter->_test_stack;
00125 $source = "[". $stack[0].", ". $stack[2] ."::" . $stack[3] ."()]\n";
00126 }
00127
00128 CakeLog::write($level, $source . $_this->exportVar($var));
00129 }
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142 function handleError($code, $description, $file = null, $line = null, $context = null) {
00143 if (error_reporting() == 0 || $code === 2048) {
00144 return;
00145 }
00146
00147 $_this = Debugger::getInstance();
00148
00149 if (empty($file)) {
00150 $file = '[internal]';
00151 }
00152 if (empty($line)) {
00153 $line = '??';
00154 }
00155 $file = $_this->trimPath($file);
00156
00157 $info = compact('code', 'description', 'file', 'line');
00158 if (!in_array($info, $_this->errors)) {
00159 $_this->errors[] = $info;
00160 } else {
00161 return;
00162 }
00163
00164 $level = LOG_DEBUG;
00165 switch ($code) {
00166 case E_PARSE:
00167 case E_ERROR:
00168 case E_CORE_ERROR:
00169 case E_COMPILE_ERROR:
00170 case E_USER_ERROR:
00171 $error = 'Fatal Error';
00172 $level = LOG_ERROR;
00173 break;
00174 case E_WARNING:
00175 case E_USER_WARNING:
00176 case E_COMPILE_WARNING:
00177 case E_RECOVERABLE_ERROR:
00178 $error = 'Warning';
00179 $level = LOG_WARNING;
00180 break;
00181 case E_NOTICE:
00182 case E_USER_NOTICE:
00183 $error = 'Notice';
00184 $level = LOG_NOTICE;
00185 break;
00186 default:
00187 return false;
00188 break;
00189 }
00190
00191 $helpCode = null;
00192 if (!empty($_this->helpPath) && preg_match('/.*\[([0-9]+)\]$/', $description, $codes)) {
00193 if (isset($codes[1])) {
00194 $helpCode = $codes[1];
00195 $description = trim(preg_replace('/\[[0-9]+\]$/', '', $description));
00196 }
00197 }
00198
00199 echo $_this->__output($level, $error, $code, $helpCode, $description, $file, $line, $context);
00200
00201 if (Configure::read('log')) {
00202 CakeLog::write($level, "{$error} ({$code}): {$description} in [{$file}, line {$line}]");
00203 }
00204
00205 if ($error == 'Fatal Error') {
00206 die();
00207 }
00208 return true;
00209 }
00210
00211
00212
00213
00214
00215
00216
00217 function trace($options = array()) {
00218 $options = array_merge(array(
00219 'depth' => 999,
00220 'format' => '',
00221 'args' => false,
00222 'start' => 0,
00223 'scope' => null,
00224 'exclude' => null
00225 ),
00226 $options
00227 );
00228
00229 $backtrace = debug_backtrace();
00230 $back = array();
00231
00232 for ($i = $options['start']; $i < count($backtrace) && $i < $options['depth']; $i++) {
00233 $trace = array_merge(
00234 array(
00235 'file' => '[internal]',
00236 'line' => '??'
00237 ),
00238 $backtrace[$i]
00239 );
00240
00241 if (isset($backtrace[$i + 1])) {
00242 $next = array_merge(
00243 array(
00244 'line' => '??',
00245 'file' => '[internal]',
00246 'class' => null,
00247 'function' => '[main]'
00248 ),
00249 $backtrace[$i + 1]
00250 );
00251 $function = $next['function'];
00252
00253 if (!empty($next['class'])) {
00254 $function = $next['class'] . '::' . $function . '(';
00255 if ($options['args'] && isset($next['args'])) {
00256 $args = array();
00257 foreach ($next['args'] as $arg) {
00258 $args[] = Debugger::exportVar($arg);
00259 }
00260 $function .= join(', ', $args);
00261 }
00262 $function .= ')';
00263 }
00264 } else {
00265 $function = '[main]';
00266 }
00267 if (in_array($function, array('call_user_func_array', 'trigger_error'))) {
00268 continue;
00269 }
00270 if ($options['format'] == 'points' && $trace['file'] != '[internal]') {
00271 $back[] = array('file' => $trace['file'], 'line' => $trace['line']);
00272 } elseif (empty($options['format'])) {
00273 $back[] = $function . ' - ' . Debugger::trimPath($trace['file']) . ', line ' . $trace['line'];
00274 } else {
00275 $back[] = $trace;
00276 }
00277 }
00278
00279 if ($options['format'] == 'array' || $options['format'] == 'points') {
00280 return $back;
00281 }
00282 return join("\n", $back);
00283 }
00284
00285
00286
00287
00288
00289
00290
00291
00292 function trimPath($path) {
00293 if (!defined('CAKE_CORE_INCLUDE_PATH') || !defined('APP')) {
00294 return $path;
00295 }
00296
00297 if (strpos($path, APP) === 0) {
00298 return str_replace(APP, 'APP' . DS, $path);
00299 } elseif (strpos($path, CAKE_CORE_INCLUDE_PATH) === 0) {
00300 return str_replace(CAKE_CORE_INCLUDE_PATH, 'CORE', $path);
00301 } elseif (strpos($path, ROOT) === 0) {
00302 return str_replace(ROOT, 'ROOT', $path);
00303 }
00304 $corePaths = Configure::corePaths('cake');
00305 foreach ($corePaths as $corePath) {
00306 if (strpos($path, $corePath) === 0) {
00307 return str_replace($corePath, 'CORE' .DS . 'cake' .DS, $path);
00308 }
00309 }
00310 return $path;
00311 }
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321 function excerpt($file, $line, $context = 2) {
00322 $data = $lines = array();
00323 $data = @explode("\n", file_get_contents($file));
00324
00325 if (empty($data) || !isset($data[$line])) {
00326 return;
00327 }
00328 for ($i = $line - ($context + 1); $i < $line + $context; $i++) {
00329 if (!isset($data[$i])) {
00330 continue;
00331 }
00332 $string = str_replace(array("\r\n", "\n"), "", highlight_string($data[$i], true));
00333 if ($i == $line) {
00334 $lines[] = '<span class="code-highlight">' . $string . '</span>';
00335 } else {
00336 $lines[] = $string;
00337 }
00338 }
00339 return $lines;
00340 }
00341
00342
00343
00344
00345
00346
00347
00348 function exportVar($var, $recursion = 0) {
00349 $_this = Debugger::getInstance();
00350 switch(strtolower(gettype($var))) {
00351 case 'boolean':
00352 return ife($var, 'true', 'false');
00353 break;
00354 case 'integer':
00355 case 'double':
00356 return $var;
00357 break;
00358 case 'string':
00359 if (trim($var) == "") {
00360 return '""';
00361 }
00362 return '"' . h($var) . '"';
00363 break;
00364 case 'object':
00365 return get_class($var) . "\n" . $_this->__object($var);
00366 case 'array':
00367 $out = "array(";
00368 $vars = array();
00369 foreach ($var as $key => $val) {
00370 if ($recursion >= 0) {
00371 if (is_numeric($key)) {
00372 $vars[] = "\n\t" . $_this->exportVar($val, $recursion - 1);
00373 } else {
00374 $vars[] = "\n\t" .$_this->exportVar($key, $recursion - 1)
00375 . ' => ' . $_this->exportVar($val, $recursion - 1);
00376 }
00377 }
00378 }
00379 $n = null;
00380 if (count($vars) > 0) {
00381 $n = "\n";
00382 }
00383 return $out . join(",", $vars) . "{$n})";
00384 break;
00385 case 'resource':
00386 return strtolower(gettype($var));
00387 break;
00388 case 'null':
00389 return 'null';
00390 break;
00391 }
00392 }
00393
00394
00395
00396
00397
00398
00399 function __object($var) {
00400 $out = array();
00401
00402 if(is_object($var)) {
00403 $className = get_class($var);
00404 $objectVars = get_object_vars($var);
00405
00406 foreach($objectVars as $key => $value) {
00407 if(is_object($value)) {
00408 $value = get_class($value) . ' object';
00409 } elseif (is_array($value)) {
00410 $value = 'array';
00411 } elseif ($value === null) {
00412 $value = 'NULL';
00413 } elseif (in_array(gettype($value), array('boolean', 'integer', 'double', 'string', 'array', 'resource'))) {
00414 $value = Debugger::exportVar($value);
00415 }
00416 $out[] = "$className::$$key = " . $value;
00417 }
00418 }
00419 return join("\n", $out);
00420 }
00421
00422
00423
00424
00425
00426
00427 function output($format = 'js') {
00428 $_this = Debugger::getInstance();
00429 $data = null;
00430
00431 if ($format === true && !empty($_this->__data)) {
00432 $data = $_this->__data;
00433 $_this->__data = array();
00434 $format = false;
00435 }
00436 $_this->__outputFormat = $format;
00437
00438 return $data;
00439 }
00440
00441
00442
00443
00444
00445
00446 function __output($level, $error, $code, $helpCode, $description, $file, $line, $kontext) {
00447 $_this = Debugger::getInstance();
00448
00449 $files = $_this->trace(array('start' => 2, 'format' => 'points'));
00450 $listing = $_this->excerpt($files[0]['file'], $files[0]['line'] - 1, 1);
00451 $trace = $_this->trace(array('start' => 2, 'depth' => '20'));
00452 $context = array();
00453
00454 foreach ((array)$kontext as $var => $value) {
00455 $context[] = "\${$var}\t=\t" . $_this->exportVar($value, 1);
00456 }
00457
00458 switch ($_this->__outputFormat) {
00459 default:
00460 case 'js':
00461 $link = "document.getElementById(\"CakeStackTrace" . count($_this->errors) . "\").style.display = (document.getElementById(\"CakeStackTrace" . count($_this->errors) . "\").style.display == \"none\" ? \"\" : \"none\")";
00462 $out = "<a href='javascript:void(0);' onclick='{$link}'><b>{$error}</b> ({$code})</a>: {$description} [<b>{$file}</b>, line <b>{$line}</b>]";
00463 if (Configure::read() > 0) {
00464 debug($out, false, false);
00465 e('<div id="CakeStackTrace' . count($_this->errors) . '" class="cake-stack-trace" style="display: none;">');
00466 $link = "document.getElementById(\"CakeErrorCode" . count($_this->errors) . "\").style.display = (document.getElementById(\"CakeErrorCode" . count($_this->errors) . "\").style.display == \"none\" ? \"\" : \"none\")";
00467 e("<a href='javascript:void(0);' onclick='{$link}'>Code</a>");
00468
00469 if (!empty($context)) {
00470 $link = "document.getElementById(\"CakeErrorContext" . count($_this->errors) . "\").style.display = (document.getElementById(\"CakeErrorContext" . count($_this->errors) . "\").style.display == \"none\" ? \"\" : \"none\")";
00471 e(" | <a href='javascript:void(0);' onclick='{$link}'>Context</a>");
00472
00473 if (!empty($helpCode)) {
00474 e(" | <a href='{$_this->helpPath}{$helpCode}' target='blank'>Help</a>");
00475 }
00476
00477 e("<pre id=\"CakeErrorContext" . count($_this->errors) . "\" class=\"cake-context\" style=\"display: none;\">");
00478 e(implode("\n", $context));
00479 e("</pre>");
00480 }
00481
00482 if (!empty($listing)) {
00483 e("<div id=\"CakeErrorCode" . count($_this->errors) . "\" class=\"cake-code-dump\" style=\"display: none;\">");
00484 pr(implode("\n", $listing) . "\n", false);
00485 e('</div>');
00486 }
00487 pr($trace, false);
00488 e('</div>');
00489 }
00490 break;
00491 case 'html':
00492 echo "<pre class=\"cake-debug\"><b>{$error}</b> ({$code}) : {$description} [<b>{$file}</b>, line <b>{$line}]</b></pre>";
00493 if (!empty($context)) {
00494 echo "Context:\n" .implode("\n", $context) . "\n";
00495 }
00496 echo "<pre class=\"cake-debug context\"><b>Context</b> <p>" . implode("\n", $context) . "</p></pre>";
00497 echo "<pre class=\"cake-debug trace\"><b>Trace</b> <p>" . $trace. "</p></pre>";
00498 break;
00499 case 'text':
00500 case 'txt':
00501 echo "{$error}: {$code} :: {$description} on line {$line} of {$file}\n";
00502 if (!empty($context)) {
00503 echo "Context:\n" .implode("\n", $context) . "\n";
00504 }
00505 echo "Trace:\n" . $trace;
00506 break;
00507 case 'log':
00508 $_this->log(compact('error', 'code', 'description', 'line', 'file', 'context', 'trace'));
00509 break;
00510 case false:
00511 $this->__data[] = compact('error', 'code', 'description', 'line', 'file', 'context', 'trace');
00512 break;
00513 }
00514 }
00515
00516
00517
00518
00519
00520 function checkSessionKey() {
00521 if (Configure::read('Security.salt') == 'DYhG93b0qyJfIxfs2guVoUubWwvniR2G0FgaC9mi') {
00522 trigger_error(__('Please change the value of \'Security.salt\' in app/config/core.php to a salt value specific to your application', true), E_USER_NOTICE);
00523 }
00524 }
00525
00526
00527
00528
00529
00530
00531
00532 function invoke(&$debugger) {
00533 set_error_handler(array(&$debugger, 'handleError'));
00534 }
00535 }
00536
00537 if (!defined('DISABLE_DEFAULT_ERROR_HANDLING')) {
00538 Debugger::invoke(Debugger::getInstance());
00539 }
00540
00541 ?>