1 <?php
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
25 class Debug {
26
27 28 29
30 protected static $custom_smtp_server = '';
31
32 33 34
35 protected static $send_errors_to;
36
37 38 39
40 protected static $send_warnings_to;
41
42 43 44 45 46 47 48
49 protected static $log_errors_to = null;
50
51 52 53
54 public static = 'There has been an error';
55
56 57 58
59 public static $friendly_error_detail = 'The website server has not been able to respond to your request.';
60
61 62 63 64
65 static function show($val, $showHeader = true) {
66 if(!Director::isLive()) {
67 if($showHeader) {
68 $caller = Debug::caller();
69 if(Director::is_ajax() || Director::is_cli())
70 echo "Debug ($caller[class]$caller[type]$caller[function]() in line $caller[line] of " . basename($caller['file']) . ")\n";
71 else
72 echo "<div style=\"background-color: white; text-align: left;\">\n<hr>\n<h3>Debug <span style=\"font-size: 65%\">($caller[class]$caller[type]$caller[function]() \n<span style=\"font-weight:normal\">in line</span> $caller[line] \n<span style=\"font-weight:normal\">of</span> " . basename($caller['file']) . ")</span>\n</h3>\n";
73 }
74
75 echo Debug::text($val);
76
77 if(!Director::is_ajax() && !Director::is_cli()) echo "</div>";
78 else echo "\n\n";
79 }
80
81 }
82
83 84 85 86 87
88 static function endshow($val) {
89 if(!Director::isLive()) {
90 $caller = Debug::caller();
91 echo "<hr>\n<h3>Debug \n<span style=\"font-size: 65%\">($caller[class]$caller[type]$caller[function]() \n<span style=\"font-weight:normal\">in line</span> $caller[line] \n<span style=\"font-weight:normal\">of</span> " . basename($caller['file']) . ")</span>\n</h3>\n";
92 echo Debug::text($val);
93 die();
94 }
95 }
96
97 98 99 100 101
102 static function dump($val) {
103 echo '<pre style="background-color:#ccc;padding:5px;font-size:14px;line-height:18px;">';
104 $caller = Debug::caller();
105 echo "<span style=\"font-size: 12px;color:#666;\">Line $caller[line] of " . basename($caller['file']) . ":</span>\n";
106 if (is_string($val)) print_r(wordwrap($val, 100));
107 else print_r($val);
108 echo '</pre>';
109 }
110
111 112 113 114 115 116
117 static function text($val) {
118 if(is_object($val)) {
119 if(method_exists($val, 'hasMethod')) {
120 $hasDebugMethod = $val->hasMethod('debug');
121 } else {
122 $hasDebugMethod = method_exists($val, 'debug');
123 }
124
125 if($hasDebugMethod) {
126 return $val->debug();
127 }
128 }
129
130 if(is_array($val)) {
131 $result = "<ul>\n";
132 foreach($val as $k => $v) {
133 $result .= "<li>$k = " . Debug::text($v) . "</li>\n";
134 }
135 $val = $result . "</ul>\n";
136
137 } else if (is_object($val)) {
138 $val = var_export($val, true);
139 } else {
140 if(!Director::is_cli() && !Director::is_ajax()) {
141 $val = "<pre style=\"font-family: Courier new\">" . htmlentities($val) . "</pre>\n";
142 }
143 }
144
145 return $val;
146 }
147
148 149 150
151 static function message($message, $showHeader = true) {
152 if(!Director::isLive()) {
153 $caller = Debug::caller();
154 $file = basename($caller['file']);
155 if(Director::is_cli()) {
156 if($showHeader) echo "Debug (line $caller[line] of $file):\n ";
157 echo $message . "\n";
158 } else {
159 echo "<p style=\"background-color: white; color: black; width: 95%; margin: 0.5em; padding: 0.3em; border: 1px #CCC solid\">\n";
160 if($showHeader) echo "<b>Debug (line $caller[line] of $file):</b>\n ";
161 echo Convert::raw2xml($message) . "</p>\n";
162 }
163 }
164 }
165
166
167 static = 0;
168
169 170 171 172 173 174 175 176
177 static function header($msg, $prefix = null) {
178 if (Director::isDev() && !headers_sent()) {
179 self::$headerCount++;
180 header('SS-'.self::$headerCount.($prefix?'-'.$prefix:'').': '.$msg);
181 }
182 }
183
184 185 186 187 188
189 static function log($message) {
190 $file = Director::baseFolder().'/debug.log';
191 $now = date('r');
192 $oldcontent = (file_exists($file)) ? file_get_contents($file) : '';
193 $content = $oldcontent . "\n\n== $now ==\n$message\n";
194 file_put_contents($file, $content);
195 }
196
197 198 199 200
201 static function loadErrorHandlers() {
202 set_error_handler('errorHandler', error_reporting());
203 set_exception_handler('exceptionHandler');
204 }
205
206 static function noticeHandler($errno, $errstr, $errfile, $errline, $errcontext) {
207 if(error_reporting() == 0) return;
208
209
210 SS_Log::log(
211 array(
212 'errno' => $errno,
213 'errstr' => $errstr,
214 'errfile' => $errfile,
215 'errline' => $errline,
216 'errcontext' => $errcontext
217 ),
218 SS_Log::NOTICE
219 );
220
221 if(Director::isDev()) {
222 self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Notice");
223 }
224 }
225
226 227 228 229 230 231 232 233 234
235 static function warningHandler($errno, $errstr, $errfile, $errline, $errcontext) {
236 if(error_reporting() == 0) return;
237 if(self::$send_warnings_to) {
238 self::emailError(self::$send_warnings_to, $errno, $errstr, $errfile, $errline, $errcontext, "Warning");
239 }
240
241
242 SS_Log::log(
243 array(
244 'errno' => $errno,
245 'errstr' => $errstr,
246 'errfile' => $errfile,
247 'errline' => $errline,
248 'errcontext' => $errcontext
249 ),
250 SS_Log::WARN
251 );
252
253 if(self::$log_errors_to) {
254 self::log_error_if_necessary( $errno, $errstr, $errfile, $errline, $errcontext, "Warning");
255 }
256
257 if(Director::isDev()) {
258 self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Warning");
259 }
260 }
261
262 263 264 265 266 267 268 269 270 271 272
273 static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) {
274 if(self::$send_errors_to) {
275 self::emailError(self::$send_errors_to, $errno, $errstr, $errfile, $errline, $errcontext, "Error");
276 }
277
278
279 SS_Log::log(
280 array(
281 'errno' => $errno,
282 'errstr' => $errstr,
283 'errfile' => $errfile,
284 'errline' => $errline,
285 'errcontext' => $errcontext
286 ),
287 SS_Log::ERR
288 );
289
290 if(self::$log_errors_to) {
291 self::log_error_if_necessary( $errno, $errstr, $errfile, $errline, $errcontext, "Error");
292 }
293
294 if(Director::isDev() || Director::is_cli()) {
295 self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Error");
296 } else {
297 self::friendlyError();
298 }
299 exit(1);
300 }
301
302 303 304 305 306 307 308 309 310 311 312 313 314 315
316 static function friendlyError($statusCode = 500, $friendlyErrorMessage = null, $friendlyErrorDetail = null) {
317 if(!$friendlyErrorMessage) $friendlyErrorMessage = self::$friendly_error_header;
318 if(!$friendlyErrorDetail) $friendlyErrorDetail = self::$friendly_error_detail;
319
320 if(!headers_sent()) header($_SERVER['SERVER_PROTOCOL'] . " $statusCode $friendlyErrorMessage");
321
322 if(Director::is_ajax()) {
323 echo $friendlyErrorMessage;
324 } else {
325 $errorFilePath = ErrorPage::get_filepath_for_errorcode($statusCode, i18n::get_locale());
326 if(file_exists($errorFilePath)) {
327 $content = file_get_contents(ASSETS_PATH . "/error-$statusCode.html");
328
329 echo str_replace('$BaseURL', Director::absoluteBaseURL(), $content);
330
331 } else {
332 $renderer = new DebugView();
333 $renderer->writeHeader();
334 $renderer->writeInfo("Website Error", $friendlyErrorMessage, $friendlyErrorDetail);
335
336 if(Email::getAdminEmail()) {
337 $mailto = Email::obfuscate(Email::getAdminEmail());
338 $renderer->writeParagraph('Contact an administrator: ' . $mailto . '');
339 }
340
341 $renderer->writeFooter();
342 }
343 }
344 }
345
346 347 348
349 static function create_debug_view() {
350 if(Director::is_cli() || Director::is_ajax()) return new CliDebugView();
351 else return new DebugView();
352 }
353
354 355 356 357 358 359 360 361 362 363
364 static function showError($errno, $errstr, $errfile, $errline, $errcontext, $errtype) {
365 if(!headers_sent()) {
366 $errText = "$errtype: \"$errstr\" at line $errline of $errfile";
367 $errText = str_replace(array("\n","\r")," ",$errText);
368 if(!headers_sent()) header($_SERVER['SERVER_PROTOCOL'] . " 500 $errText");
369
370
371 if(Director::is_ajax()) header('Content-Type: text/plain');
372 }
373
374
375
376
377 $reporter = self::create_debug_view();
378
379
380 $httpRequest = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : @$_REQUEST['url'];
381 if(isset($_SERVER['REQUEST_METHOD'])) $httpRequest = $_SERVER['REQUEST_METHOD'] . ' ' . $httpRequest;
382
383 $reporter->writeHeader($httpRequest);
384 $reporter->writeError($httpRequest, $errno, $errstr, $errfile, $errline, $errcontext);
385
386 if(file_exists($errfile)) {
387 $lines = file($errfile);
388
389
390 array_unshift($lines,"");
391 unset($lines[0]);
392
393 $offset = $errline-10;
394 $lines = array_slice($lines, $offset, 16, true);
395 $reporter->writeSourceFragment($lines, $errline);
396 }
397 $reporter->writeTrace(($errcontext ? $errcontext : debug_backtrace()));
398 $reporter->writeFooter();
399 exit(1);
400 }
401
402 403 404 405 406 407 408
409 static function showLines($errfile, $errline) {
410 $lines = file($errfile);
411 $offset = $errline-10;
412 $lines = array_slice($lines, $offset, 16);
413 echo '<pre>';
414 $offset++;
415 foreach($lines as $line) {
416 $line = htmlentities($line);
417 if ($offset == $errline) {
418 echo "<span>$offset</span> <span class=\"error\">$line</span>";
419 } else {
420 echo "<span>$offset</span> $line";
421 }
422 $offset++;
423 }
424 echo '</pre>';
425 }
426
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
445 static function emailError($emailAddress, $errno, $errstr, $errfile, $errline, $errcontext, $errorType = "Error") {
446 user_error('Debug::send_errors_to() and Debug::emailError() is deprecated. Please use SS_Log instead.
447 See the class documentation in SS_Log.php for more information.', E_USER_NOTICE);
448 $priority = ($errorType == 'Error') ? SS_Log::ERR : SS_Log::WARN;
449 $writer = new SS_LogEmailWriter($emailAddress);
450 SS_Log::add_writer($writer, $priority);
451 SS_Log::log(
452 array(
453 'errno' => $errno,
454 'errstr' => $errstr,
455 'errfile' => $errfile,
456 'errline' => $errline,
457 'errcontext' => $errcontext
458 ),
459 $priority
460 );
461 SS_Log::remove_writer($writer);
462 }
463
464 465 466 467 468 469 470 471 472 473
474 protected static function log_error_if_necessary($errno, $errstr, $errfile, $errline, $errcontext, $errtype) {
475 user_error('Debug::log_error_if_necessary() and Debug::log_errors_to() are deprecated. Please use SS_Log instead.
476 See the class documentation in SS_Log.php for more information.', E_USER_NOTICE);
477 $priority = ($errtype == 'Error') ? SS_Log::ERR : SS_Log::WARN;
478 $writer = new SS_LogFileWriter(Director::baseFolder() .'/'. self::$log_errors_to);
479 SS_Log::add_writer($writer, $priority);
480 SS_Log::log(
481 array(
482 'errno' => $errno,
483 'errstr' => $errstr,
484 'errfile' => $errfile,
485 'errline' => $errline,
486 'errcontext' => $errcontext
487 ),
488 $priority
489 );
490 SS_Log::remove_writer($writer);
491 }
492
493 494 495 496
497 static function set_custom_smtp_server($server) {
498 self::$custom_smtp_server = $server;
499 }
500
501 502 503 504
505 static function get_custom_smtp_server() {
506 return self::$custom_smtp_server;
507 }
508
509 510 511 512 513 514 515 516 517 518
519 static function send_errors_to($emailAddress, $sendWarnings = false) {
520 self::$send_errors_to = $emailAddress;
521 self::$send_warnings_to = $sendWarnings ? $emailAddress : null;
522 }
523
524 525 526 527
528 static function get_send_errors_to() {
529 return self::$send_errors_to;
530 }
531
532 533 534 535
536 static function send_warnings_to($emailAddress) {
537 self::$send_warnings_to = $emailAddress;
538 }
539
540 541 542 543
544 static function get_send_warnings_to() {
545 return self::$send_warnings_to;
546 }
547
548 549 550 551
552 static function log_errors_to($logFile = ".sserrors") {
553 self::$log_errors_to = $logFile;
554 }
555
556 static function caller() {
557 $bt = debug_backtrace();
558 $caller = $bt[2];
559 $caller['line'] = $bt[1]['line'];
560 $caller['file'] = $bt[1]['file'];
561 if(!isset($caller['class'])) $caller['class'] = '';
562 if(!isset($caller['type'])) $caller['type'] = '';
563 return $caller;
564 }
565
566 567 568
569 static function backtrace($returnVal = false, $ignoreAjax = false) {
570 user_error('Debug::backtrace() is deprecated. Please use SS_Backtrace::backtrace() instead', E_USER_NOTICE);
571 return SS_Backtrace::backtrace($returnVal, $ignoreAjax);
572 }
573
574 575 576
577 static function get_rendered_backtrace($bt, $plainText = false) {
578 user_error('Debug::get_rendered_backtrace() is deprecated. Please use SS_Backtrace::get_rendered_backtrace() instead', E_USER_NOTICE);
579 return SS_Backtrace::get_rendered_backtrace($bt, $plainText);
580 }
581
582 583 584 585
586 static function require_developer_login() {
587 if(Director::isDev()) {
588 return;
589 }
590 if(isset($_SESSION['loggedInAs'])) {
591
592
593
594
595
596 $memberID = $_SESSION['loggedInAs'];
597
598 $groups = DB::query("SELECT \"GroupID\" from \"Group_Members\" WHERE \"MemberID\" = " . $memberID);
599 $groupCSV = implode($groups->column(), ',');
600
601 $permission = DB::query("
602 SELECT \"ID\"
603 FROM \"Permission\"
604 WHERE (
605 \"Code\" = 'ADMIN'
606 AND \"Type\" = " . Permission::GRANT_PERMISSION . "
607 AND \"GroupID\" IN ($groupCSV)
608 )
609 ")->value();
610
611 if($permission) {
612 return;
613 }
614 }
615
616
617
618
619 $_SESSION['Security']['Message']['message'] = "You need to login with developer access to make use of debugging tools.";
620 $_SESSION['Security']['Message']['type'] = 'warning';
621 $_SESSION['BackURL'] = $_SERVER['REQUEST_URI'];
622 header($_SERVER['SERVER_PROTOCOL'] . " 302 Found");
623 header("Location: " . Director::baseURL() . "Security/login");
624 die();
625 }
626 }
627
628
629
630
631
632
633
634
635
636
637 638 639 640 641 642
643 function exceptionHandler($exception) {
644 $errno = E_USER_ERROR;
645 $type = get_class($exception);
646 $message = "Uncaught " . $type . ": " . $exception->getMessage();
647 $file = $exception->getFile();
648 $line = $exception->getLine();
649 $context = $exception->getTrace();
650 Debug::fatalHandler($errno, $message, $file, $line, $context);
651 }
652
653 654 655 656 657 658 659 660 661 662 663
664 function errorHandler($errno, $errstr, $errfile, $errline) {
665 switch($errno) {
666 case E_ERROR:
667 case E_CORE_ERROR:
668 case E_USER_ERROR:
669 Debug::fatalHandler($errno, $errstr, $errfile, $errline, null);
670 break;
671
672 case E_WARNING:
673 case E_CORE_WARNING:
674 case E_USER_WARNING:
675 Debug::warningHandler($errno, $errstr, $errfile, $errline, null);
676 break;
677
678 case E_NOTICE:
679 case E_USER_NOTICE:
680 Debug::noticeHandler($errno, $errstr, $errfile, $errline, null);
681 break;
682 }
683 }
684
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the
Silverstripe Forums to ask development related questions.
-