1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Supports debugging and core error handling. |
4
|
|
|
* |
5
|
|
|
* Attaches custom methods to the default error handling hooks |
6
|
|
|
* in PHP. Currently, two levels of error are supported: |
7
|
|
|
* |
8
|
|
|
* - Notice |
9
|
|
|
* - Warning |
10
|
|
|
* - Error |
11
|
|
|
* |
12
|
|
|
* Uncaught exceptions are currently passed to the debug |
13
|
|
|
* reporter as standard PHP errors. |
14
|
|
|
* |
15
|
|
|
* Errors handled by this class are passed along to {@link SS_Log}. |
16
|
|
|
* For configuration information, see the {@link SS_Log} |
17
|
|
|
* class documentation. |
18
|
|
|
* |
19
|
|
|
* @todo add support for user defined config: Debug::die_on_notice(true | false) |
20
|
|
|
* @todo better way of figuring out the error context to display in highlighted source |
21
|
|
|
* |
22
|
|
|
* @package framework |
23
|
|
|
* @subpackage dev |
24
|
|
|
*/ |
25
|
|
|
class Debug { |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @config |
29
|
|
|
* @var String indicating the file where errors are logged. |
30
|
|
|
* Filename is relative to the site root. |
31
|
|
|
* The named file will have a terse log sent to it, and the full log (an |
32
|
|
|
* encoded file containing backtraces and things) will go to a file of a similar |
33
|
|
|
* name, but with the suffix ".full" added. |
34
|
|
|
*/ |
35
|
|
|
private static $log_errors_to = null; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @config |
39
|
|
|
* @var string The header of the message shown to users on the live site when a fatal error occurs. |
40
|
|
|
*/ |
41
|
|
|
private static $friendly_error_header = 'There has been an error'; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @config |
45
|
|
|
* @var string The body of the message shown to users on the live site when a fatal error occurs. |
46
|
|
|
*/ |
47
|
|
|
private static $friendly_error_detail = 'The website server has not been able to respond to your request.'; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Show the contents of val in a debug-friendly way. |
51
|
|
|
* Debug::show() is intended to be equivalent to dprintr() |
52
|
|
|
*/ |
53
|
|
|
public static function show($val, $showHeader = true) { |
54
|
|
|
if(!Director::isLive()) { |
55
|
|
|
if($showHeader) { |
56
|
|
|
$caller = Debug::caller(); |
57
|
|
|
if(Director::is_ajax() || Director::is_cli()) |
58
|
|
|
echo "Debug ($caller[class]$caller[type]$caller[function]() in " . basename($caller['file']) |
59
|
|
|
. ":$caller[line])\n"; |
60
|
|
|
else |
61
|
|
|
echo "<div style=\"background-color: white; text-align: left;\">\n<hr>\n" |
62
|
|
|
. "<h3>Debug <span style=\"font-size: 65%\">($caller[class]$caller[type]$caller[function]()" |
63
|
|
|
. " \nin " . basename($caller['file']) . ":$caller[line])</span>\n</h3>\n"; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
echo Debug::text($val); |
67
|
|
|
|
68
|
|
|
if(!Director::is_ajax() && !Director::is_cli()) echo "</div>"; |
69
|
|
|
else echo "\n\n"; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Returns the caller for a specific method |
76
|
|
|
* |
77
|
|
|
* @return array |
78
|
|
|
*/ |
79
|
|
|
public static function caller() { |
80
|
|
|
$bt = debug_backtrace(); |
81
|
|
|
$caller = $bt[2]; |
82
|
|
|
$caller['line'] = $bt[1]['line']; |
83
|
|
|
$caller['file'] = $bt[1]['file']; |
84
|
|
|
if(!isset($caller['class'])) $caller['class'] = ''; |
85
|
|
|
if(!isset($caller['type'])) $caller['type'] = ''; |
86
|
|
|
return $caller; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Close out the show dumper |
91
|
|
|
* |
92
|
|
|
* @param mixed $val |
93
|
|
|
*/ |
94
|
|
|
public static function endshow($val) { |
95
|
|
|
if(!Director::isLive()) { |
96
|
|
|
$caller = Debug::caller(); |
97
|
|
|
echo "<hr>\n<h3>Debug \n<span style=\"font-size: 65%\">($caller[class]$caller[type]$caller[function]()" |
98
|
|
|
. " \nin " . basename($caller['file']) . ":$caller[line])</span>\n</h3>\n"; |
99
|
|
|
echo Debug::text($val); |
100
|
|
|
die(); |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Quick dump of a variable. |
106
|
|
|
* |
107
|
|
|
* @param mixed $val |
108
|
|
|
*/ |
109
|
|
|
public static function dump($val) { |
110
|
|
|
self::create_debug_view()->writeVariable($val, self::caller()); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* ?? |
115
|
|
|
* |
116
|
|
|
* @param unknown_type $val |
117
|
|
|
* @return unknown |
118
|
|
|
*/ |
119
|
|
|
public static function text($val) { |
120
|
|
|
if(is_object($val)) { |
121
|
|
|
if(method_exists($val, 'hasMethod')) { |
122
|
|
|
$hasDebugMethod = $val->hasMethod('debug'); |
123
|
|
|
} else { |
124
|
|
|
$hasDebugMethod = method_exists($val, 'debug'); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
if($hasDebugMethod) { |
128
|
|
|
return $val->debug(); |
129
|
|
|
} |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
if(is_array($val)) { |
133
|
|
|
$result = "<ul>\n"; |
134
|
|
|
foreach($val as $k => $v) { |
135
|
|
|
$result .= "<li>$k = " . Debug::text($v) . "</li>\n"; |
136
|
|
|
} |
137
|
|
|
$val = $result . "</ul>\n"; |
138
|
|
|
|
139
|
|
|
} else if (is_object($val)) { |
140
|
|
|
$val = var_export($val, true); |
141
|
|
|
} else if (is_bool($val)) { |
142
|
|
|
$val = $val ? 'true' : 'false'; |
143
|
|
|
$val = '(bool) ' . $val; |
144
|
|
|
} else { |
145
|
|
|
if(!Director::is_cli() && !Director::is_ajax()) { |
146
|
|
|
$val = "<pre style=\"font-family: Courier new\">" . htmlentities($val, ENT_COMPAT, 'UTF-8') |
147
|
|
|
. "</pre>\n"; |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
return $val; |
|
|
|
|
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Show a debugging message |
156
|
|
|
*/ |
157
|
|
|
public static function message($message, $showHeader = true) { |
158
|
|
|
if(!Director::isLive()) { |
159
|
|
|
$caller = Debug::caller(); |
160
|
|
|
$file = basename($caller['file']); |
161
|
|
|
if(Director::is_cli()) { |
162
|
|
|
if($showHeader) echo "Debug (line $caller[line] of $file):\n "; |
163
|
|
|
echo $message . "\n"; |
164
|
|
|
} else { |
165
|
|
|
echo "<p class=\"message warning\">\n"; |
166
|
|
|
if($showHeader) echo "<b>Debug (line $caller[line] of $file):</b>\n "; |
167
|
|
|
echo Convert::raw2xml($message) . "</p>\n"; |
168
|
|
|
} |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
// Keep track of how many headers have been sent |
173
|
|
|
private static $headerCount = 0; |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Send a debug message in an HTTP header. Only works if you are |
177
|
|
|
* on Dev, and headers have not yet been sent. |
178
|
|
|
* |
179
|
|
|
* @param string $msg |
180
|
|
|
* @param string $prefix (optional) |
181
|
|
|
* @return void |
182
|
|
|
*/ |
183
|
|
|
public static function header($msg, $prefix = null) { |
184
|
|
|
if (Director::isDev() && !headers_sent()) { |
185
|
|
|
self::$headerCount++; |
186
|
|
|
header('SS-'.self::$headerCount.($prefix?'-'.$prefix:'').': '.$msg); |
187
|
|
|
} |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Log to a standard text file output. |
192
|
|
|
* |
193
|
|
|
* @param $message string to output |
194
|
|
|
*/ |
195
|
|
|
public static function log($message) { |
196
|
|
|
if (defined('BASE_PATH')) { |
197
|
|
|
$path = BASE_PATH; |
198
|
|
|
} |
199
|
|
|
else { |
200
|
|
|
$path = dirname(__FILE__) . '/../..'; |
201
|
|
|
} |
202
|
|
|
$file = $path . '/debug.log'; |
203
|
|
|
$now = date('r'); |
204
|
|
|
$content = "\n\n== $now ==\n$message\n"; |
205
|
|
|
file_put_contents($file, $content, FILE_APPEND); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Load error handlers into environment. |
210
|
|
|
* Caution: The error levels default to E_ALL is the site is in dev-mode (set in main.php). |
211
|
|
|
*/ |
212
|
|
|
public static function loadErrorHandlers() { |
213
|
|
|
set_error_handler('errorHandler', error_reporting()); |
214
|
|
|
set_exception_handler('exceptionHandler'); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
View Code Duplication |
public static function noticeHandler($errno, $errstr, $errfile, $errline, $errcontext) { |
|
|
|
|
218
|
|
|
if(error_reporting() == 0) return; |
219
|
|
|
ini_set('display_errors', 0); |
220
|
|
|
|
221
|
|
|
// Send out the error details to the logger for writing |
222
|
|
|
SS_Log::log( |
223
|
|
|
array( |
224
|
|
|
'errno' => $errno, |
225
|
|
|
'errstr' => $errstr, |
226
|
|
|
'errfile' => $errfile, |
227
|
|
|
'errline' => $errline, |
228
|
|
|
'errcontext' => $errcontext |
229
|
|
|
), |
230
|
|
|
SS_Log::NOTICE |
231
|
|
|
); |
232
|
|
|
|
233
|
|
|
if(Director::isDev()) { |
234
|
|
|
return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Notice"); |
235
|
|
|
} else { |
236
|
|
|
return false; |
237
|
|
|
} |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Handle a non-fatal warning error thrown by PHP interpreter. |
242
|
|
|
* |
243
|
|
|
* @param unknown_type $errno |
244
|
|
|
* @param unknown_type $errstr |
245
|
|
|
* @param unknown_type $errfile |
246
|
|
|
* @param unknown_type $errline |
247
|
|
|
* @param unknown_type $errcontext |
248
|
|
|
*/ |
249
|
|
View Code Duplication |
public static function warningHandler($errno, $errstr, $errfile, $errline, $errcontext) { |
|
|
|
|
250
|
|
|
if(error_reporting() == 0) return; |
251
|
|
|
ini_set('display_errors', 0); |
252
|
|
|
|
253
|
|
|
// Send out the error details to the logger for writing |
254
|
|
|
SS_Log::log( |
255
|
|
|
array( |
256
|
|
|
'errno' => $errno, |
257
|
|
|
'errstr' => $errstr, |
258
|
|
|
'errfile' => $errfile, |
259
|
|
|
'errline' => $errline, |
260
|
|
|
'errcontext' => $errcontext |
261
|
|
|
), |
262
|
|
|
SS_Log::WARN |
263
|
|
|
); |
264
|
|
|
|
265
|
|
|
if(Director::isDev()) { |
266
|
|
|
return self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Warning"); |
267
|
|
|
} else { |
268
|
|
|
return false; |
269
|
|
|
} |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* Handle a fatal error, depending on the mode of the site (ie: Dev, Test, or Live). |
274
|
|
|
* |
275
|
|
|
* Runtime execution dies immediately once the error is generated. |
276
|
|
|
* |
277
|
|
|
* @param unknown_type $errno |
278
|
|
|
* @param unknown_type $errstr |
279
|
|
|
* @param unknown_type $errfile |
280
|
|
|
* @param unknown_type $errline |
281
|
|
|
* @param unknown_type $errcontext |
282
|
|
|
*/ |
283
|
|
|
public static function fatalHandler($errno, $errstr, $errfile, $errline, $errcontext) { |
284
|
|
|
ini_set('display_errors', 0); |
285
|
|
|
|
286
|
|
|
// Send out the error details to the logger for writing |
287
|
|
|
SS_Log::log( |
288
|
|
|
array( |
289
|
|
|
'errno' => $errno, |
290
|
|
|
'errstr' => $errstr, |
291
|
|
|
'errfile' => $errfile, |
292
|
|
|
'errline' => $errline, |
293
|
|
|
'errcontext' => $errcontext |
294
|
|
|
), |
295
|
|
|
SS_Log::ERR |
296
|
|
|
); |
297
|
|
|
|
298
|
|
|
if(Director::isDev() || Director::is_cli()) { |
299
|
|
|
self::showError($errno, $errstr, $errfile, $errline, $errcontext, "Error"); |
300
|
|
|
} else { |
301
|
|
|
self::friendlyError(); |
302
|
|
|
} |
303
|
|
|
return false; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Render a user-facing error page, using the default HTML error template |
308
|
|
|
* rendered by {@link ErrorPage} if it exists. Doesn't use the standard {@link SS_HTTPResponse} class |
309
|
|
|
* the keep dependencies minimal. |
310
|
|
|
* |
311
|
|
|
* @uses ErrorPage |
312
|
|
|
* |
313
|
|
|
* @param int $statusCode HTTP Status Code (Default: 500) |
314
|
|
|
* @param string $friendlyErrorMessage User-focused error message. Should not contain code pointers |
315
|
|
|
* or "tech-speak". Used in the HTTP Header and ajax responses. |
316
|
|
|
* @param string $friendlyErrorDetail Detailed user-focused message. Is just used if no {@link ErrorPage} is found |
317
|
|
|
* for this specific status code. |
318
|
|
|
* @return string HTML error message for non-ajax requests, plaintext for ajax-request. |
319
|
|
|
*/ |
320
|
|
|
public static function friendlyError($statusCode=500, $friendlyErrorMessage=null, $friendlyErrorDetail=null) { |
321
|
|
|
if(!$friendlyErrorMessage) { |
|
|
|
|
322
|
|
|
$friendlyErrorMessage = Config::inst()->get('Debug', 'friendly_error_header'); |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
if(!$friendlyErrorDetail) { |
|
|
|
|
326
|
|
|
$friendlyErrorDetail = Config::inst()->get('Debug', 'friendly_error_detail'); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
if(!headers_sent()) { |
330
|
|
|
// Ensure the error message complies with the HTTP 1.1 spec |
331
|
|
|
$msg = strip_tags(str_replace(array("\n", "\r"), '', $friendlyErrorMessage)); |
332
|
|
|
if(Controller::has_curr()) { |
333
|
|
|
$response = Controller::curr()->getResponse(); |
334
|
|
|
$response->setStatusCode($statusCode, $msg); |
335
|
|
|
} else { |
336
|
|
|
header($_SERVER['SERVER_PROTOCOL'] . " $statusCode $msg"); |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
if(Director::is_ajax()) { |
341
|
|
|
echo $friendlyErrorMessage; |
342
|
|
|
} else { |
343
|
|
|
if(class_exists('ErrorPage')){ |
344
|
|
|
$errorFilePath = ErrorPage::get_filepath_for_errorcode( |
345
|
|
|
$statusCode, |
346
|
|
|
class_exists('Translatable') ? Translatable::get_current_locale() : null |
347
|
|
|
); |
348
|
|
|
if(file_exists($errorFilePath)) { |
349
|
|
|
$content = file_get_contents($errorFilePath); |
350
|
|
|
if(!headers_sent()) header('Content-Type: text/html'); |
351
|
|
|
// $BaseURL is left dynamic in error-###.html, so that multi-domain sites don't get broken |
352
|
|
|
echo str_replace('$BaseURL', Director::absoluteBaseURL(), $content); |
353
|
|
|
} |
354
|
|
|
} else { |
355
|
|
|
$renderer = new DebugView(); |
356
|
|
|
$renderer->writeHeader(); |
357
|
|
|
$renderer->writeInfo("Website Error", $friendlyErrorMessage, $friendlyErrorDetail); |
358
|
|
|
|
359
|
|
|
if(Email::config()->admin_email) { |
360
|
|
|
$mailto = Email::obfuscate(Email::config()->admin_email); |
361
|
|
|
$renderer->writeParagraph('Contact an administrator: ' . $mailto . ''); |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
$renderer->writeFooter(); |
365
|
|
|
} |
366
|
|
|
} |
367
|
|
|
return false; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
/** |
371
|
|
|
* Create an instance of an appropriate DebugView object. |
372
|
|
|
* |
373
|
|
|
* @return DebugView |
374
|
|
|
*/ |
375
|
|
|
public static function create_debug_view() { |
376
|
|
|
$service = Director::is_cli() || Director::is_ajax() |
377
|
|
|
? 'CliDebugView' |
378
|
|
|
: 'DebugView'; |
379
|
|
|
return Injector::inst()->get($service); |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* Render a developer facing error page, showing the stack trace and details |
384
|
|
|
* of the code where the error occured. |
385
|
|
|
* |
386
|
|
|
* @param unknown_type $errno |
387
|
|
|
* @param unknown_type $errstr |
388
|
|
|
* @param unknown_type $errfile |
389
|
|
|
* @param unknown_type $errline |
390
|
|
|
* @param unknown_type $errcontext |
391
|
|
|
*/ |
392
|
|
|
public static function showError($errno, $errstr, $errfile, $errline, $errcontext, $errtype) { |
393
|
|
|
if(!headers_sent()) { |
394
|
|
|
$errText = "$errtype at line $errline of $errfile"; |
395
|
|
|
$errText = str_replace(array("\n","\r")," ",$errText); |
396
|
|
|
|
397
|
|
|
if(!headers_sent()) header($_SERVER['SERVER_PROTOCOL'] . " 500 $errText"); |
398
|
|
|
|
399
|
|
|
// if error is displayed through ajax with CliDebugView, use plaintext output |
400
|
|
|
if(Director::is_ajax()) { |
401
|
|
|
header('Content-Type: text/plain'); |
402
|
|
|
} |
403
|
|
|
} |
404
|
|
|
|
405
|
|
|
$reporter = self::create_debug_view(); |
406
|
|
|
|
407
|
|
|
// Coupling alert: This relies on knowledge of how the director gets its URL, it could be improved. |
408
|
|
|
$httpRequest = null; |
409
|
|
|
if(isset($_SERVER['REQUEST_URI'])) { |
410
|
|
|
$httpRequest = $_SERVER['REQUEST_URI']; |
411
|
|
|
} elseif(isset($_REQUEST['url'])) { |
412
|
|
|
$httpRequest = $_REQUEST['url']; |
413
|
|
|
} |
414
|
|
|
if(isset($_SERVER['REQUEST_METHOD'])) $httpRequest = $_SERVER['REQUEST_METHOD'] . ' ' . $httpRequest; |
415
|
|
|
|
416
|
|
|
$reporter->writeHeader($httpRequest); |
|
|
|
|
417
|
|
|
$reporter->writeError($httpRequest, $errno, $errstr, $errfile, $errline, $errcontext); |
418
|
|
|
|
419
|
|
|
if(file_exists($errfile)) { |
420
|
|
|
$lines = file($errfile); |
421
|
|
|
|
422
|
|
|
// Make the array 1-based |
423
|
|
|
array_unshift($lines,""); |
424
|
|
|
unset($lines[0]); |
425
|
|
|
|
426
|
|
|
$offset = $errline-10; |
427
|
|
|
$lines = array_slice($lines, $offset, 16, true); |
428
|
|
|
$reporter->writeSourceFragment($lines, $errline); |
429
|
|
|
} |
430
|
|
|
$reporter->writeTrace(($errcontext ? $errcontext : debug_backtrace())); |
431
|
|
|
$reporter->writeFooter(); |
432
|
|
|
return false; |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
/** |
436
|
|
|
* Utility method to render a snippet of PHP source code, from selected file |
437
|
|
|
* and highlighting the given line number. |
438
|
|
|
* |
439
|
|
|
* @param string $errfile |
440
|
|
|
* @param int $errline |
441
|
|
|
*/ |
442
|
|
|
public static function showLines($errfile, $errline) { |
443
|
|
|
$lines = file($errfile); |
444
|
|
|
$offset = $errline-10; |
445
|
|
|
$lines = array_slice($lines, $offset, 16); |
446
|
|
|
echo '<pre>'; |
447
|
|
|
$offset++; |
448
|
|
View Code Duplication |
foreach($lines as $line) { |
|
|
|
|
449
|
|
|
$line = htmlentities($line, ENT_COMPAT, 'UTF-8'); |
450
|
|
|
if ($offset == $errline) { |
451
|
|
|
echo "<span>$offset</span> <span class=\"error\">$line</span>"; |
452
|
|
|
} else { |
453
|
|
|
echo "<span>$offset</span> $line"; |
454
|
|
|
} |
455
|
|
|
$offset++; |
456
|
|
|
} |
457
|
|
|
echo '</pre>'; |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
/** |
461
|
|
|
* Check if the user has permissions to run URL debug tools, |
462
|
|
|
* else redirect them to log in. |
463
|
|
|
*/ |
464
|
|
|
public static function require_developer_login() { |
465
|
|
|
if(Director::isDev()) { |
466
|
|
|
return; |
467
|
|
|
} |
468
|
|
|
if(isset($_SESSION['loggedInAs'])) { |
469
|
|
|
// We have to do some raw SQL here, because this method is called in Object::defineMethods(). |
470
|
|
|
// This means we have to be careful about what objects we create, as we don't want Object::defineMethods() |
471
|
|
|
// being called again. |
472
|
|
|
// This basically calls Permission::checkMember($_SESSION['loggedInAs'], 'ADMIN'); |
473
|
|
|
|
474
|
|
|
// @TODO - Rewrite safely using DataList::filter |
475
|
|
|
$memberID = $_SESSION['loggedInAs']; |
476
|
|
|
$permission = DB::prepared_query(' |
477
|
|
|
SELECT "ID" FROM "Permission" |
478
|
|
|
INNER JOIN "Group_Members" ON "Permission"."GroupID" = "Group_Members"."GroupID" |
479
|
|
|
WHERE "Permission"."Code" = ? |
480
|
|
|
AND "Permission"."Type" = ? |
481
|
|
|
AND "Group_Members"."MemberID" = ?', |
482
|
|
|
array( |
483
|
|
|
'ADMIN', // Code |
484
|
|
|
Permission::GRANT_PERMISSION, // Type |
485
|
|
|
$memberID // MemberID |
486
|
|
|
) |
487
|
|
|
)->value(); |
488
|
|
|
|
489
|
|
|
if($permission) return; |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
// This basically does the same as |
493
|
|
|
// Security::permissionFailure(null, "You need to login with developer access to make use of debugging tools.") |
494
|
|
|
// We have to do this because of how early this method is called in execution. |
495
|
|
|
$_SESSION['Security']['Message']['message'] |
496
|
|
|
= "You need to login with developer access to make use of debugging tools."; |
497
|
|
|
$_SESSION['Security']['Message']['type'] = 'warning'; |
498
|
|
|
$_SESSION['BackURL'] = $_SERVER['REQUEST_URI']; |
499
|
|
|
header($_SERVER['SERVER_PROTOCOL'] . " 302 Found"); |
500
|
|
|
header("Location: " . Director::baseURL() . Security::login_url()); |
501
|
|
|
die(); |
502
|
|
|
} |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
|
506
|
|
|
|
507
|
|
|
|
508
|
|
|
|
509
|
|
|
|
510
|
|
|
|
511
|
|
|
|
512
|
|
|
|
513
|
|
|
|
514
|
|
|
/** |
515
|
|
|
* Generic callback, to catch uncaught exceptions when they bubble up to the top of the call chain. |
516
|
|
|
* |
517
|
|
|
* @ignore |
518
|
|
|
* @param Exception $exception |
519
|
|
|
*/ |
520
|
|
|
function exceptionHandler($exception) { |
521
|
|
|
$errno = E_USER_ERROR; |
522
|
|
|
$type = get_class($exception); |
523
|
|
|
$message = "Uncaught " . $type . ": " . $exception->getMessage(); |
524
|
|
|
$file = $exception->getFile(); |
525
|
|
|
$line = $exception->getLine(); |
526
|
|
|
$context = $exception->getTrace(); |
527
|
|
|
Debug::fatalHandler($errno, $message, $file, $line, $context); |
|
|
|
|
528
|
|
|
exit(1); |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
/** |
532
|
|
|
* Generic callback to catch standard PHP runtime errors thrown by the interpreter |
533
|
|
|
* or manually triggered with the user_error function. Any unknown error codes are treated as |
534
|
|
|
* fatal errors. |
535
|
|
|
* Caution: The error levels default to E_ALL if the site is in dev-mode (set in main.php). |
536
|
|
|
* |
537
|
|
|
* @ignore |
538
|
|
|
* @param int $errno |
539
|
|
|
* @param string $errstr |
540
|
|
|
* @param string $errfile |
541
|
|
|
* @param int $errline |
542
|
|
|
*/ |
543
|
|
|
function errorHandler($errno, $errstr, $errfile, $errline) { |
544
|
|
|
switch($errno) { |
545
|
|
|
case E_NOTICE: |
546
|
|
|
case E_USER_NOTICE: |
547
|
|
|
case E_DEPRECATED: |
548
|
|
|
case E_USER_DEPRECATED: |
549
|
|
|
case E_STRICT: |
550
|
|
|
return Debug::noticeHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); |
551
|
|
|
|
552
|
|
|
case E_WARNING: |
553
|
|
|
case E_CORE_WARNING: |
554
|
|
|
case E_USER_WARNING: |
555
|
|
|
case E_RECOVERABLE_ERROR: |
556
|
|
|
return Debug::warningHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); |
|
|
|
|
557
|
|
|
|
558
|
|
|
case E_ERROR: |
559
|
|
|
case E_CORE_ERROR: |
560
|
|
|
case E_USER_ERROR: |
561
|
|
|
default: |
562
|
|
|
Debug::fatalHandler($errno, $errstr, $errfile, $errline, debug_backtrace()); |
|
|
|
|
563
|
|
|
exit(1); |
564
|
|
|
} |
565
|
|
|
} |
566
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.