ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
Run.php
Go to the documentation of this file.
1<?php
7namespace Whoops;
8
9use Exception;
10use InvalidArgumentException;
16
17class Run
18{
19 const EXCEPTION_HANDLER = "handleException";
20 const ERROR_HANDLER = "handleError";
21 const SHUTDOWN_HANDLER = "handleShutdown";
22
23 protected $isRegistered;
24 protected $allowQuit = true;
25 protected $sendOutput = true;
26
30 protected $sendHttpCode = 500;
31
35 protected $handlerStack = array();
36
37 protected $silencedPatterns = array();
38
46 public function pushHandler($handler)
47 {
48 if (is_callable($handler)) {
49 $handler = new CallbackHandler($handler);
50 }
51
52 if (!$handler instanceof HandlerInterface) {
53 throw new InvalidArgumentException(
54 "Argument to " . __METHOD__ . " must be a callable, or instance of"
55 . "Whoops\\Handler\\HandlerInterface"
56 );
57 }
58
59 $this->handlerStack[] = $handler;
60 return $this;
61 }
62
68 public function popHandler()
69 {
70 return array_pop($this->handlerStack);
71 }
72
78 public function getHandlers()
79 {
80 return $this->handlerStack;
81 }
82
88 public function clearHandlers()
89 {
90 $this->handlerStack = array();
91 return $this;
92 }
93
98 protected function getInspector(Exception $exception)
99 {
100 return new Inspector($exception);
101 }
102
107 public function register()
108 {
109 if (!$this->isRegistered) {
110 // Workaround PHP bug 42098
111 // https://bugs.php.net/bug.php?id=42098
112 class_exists("\\Whoops\\Exception\\ErrorException");
113 class_exists("\\Whoops\\Exception\\FrameCollection");
114 class_exists("\\Whoops\\Exception\\Frame");
115 class_exists("\\Whoops\\Exception\\Inspector");
116
117 set_error_handler(array($this, self::ERROR_HANDLER));
118 set_exception_handler(array($this, self::EXCEPTION_HANDLER));
119 register_shutdown_function(array($this, self::SHUTDOWN_HANDLER));
120
121 $this->isRegistered = true;
122 }
123
124 return $this;
125 }
126
131 public function unregister()
132 {
133 if ($this->isRegistered) {
134 restore_exception_handler();
135 restore_error_handler();
136
137 $this->isRegistered = false;
138 }
139
140 return $this;
141 }
142
148 public function allowQuit($exit = null)
149 {
150 if (func_num_args() == 0) {
151 return $this->allowQuit;
152 }
153
154 return $this->allowQuit = (bool) $exit;
155 }
156
163 public function silenceErrorsInPaths($patterns, $levels = 10240)
164 {
165 $this->silencedPatterns = array_merge(
166 $this->silencedPatterns,
167 array_map(
168 function ($pattern) use ($levels) {
169 return array(
170 "pattern" => $pattern,
171 "levels" => $levels,
172 );
173 },
174 (array) $patterns
175 )
176 );
177 return $this;
178 }
179
180 /*
181 * Should Whoops send HTTP error code to the browser if possible?
182 * Whoops will by default send HTTP code 500, but you may wish to
183 * use 502, 503, or another 5xx family code.
184 *
185 * @param bool|int $code
186 * @return int|false
187 */
188 public function sendHttpCode($code = null)
189 {
190 if (func_num_args() == 0) {
191 return $this->sendHttpCode;
192 }
193
194 if (!$code) {
195 return $this->sendHttpCode = false;
196 }
197
198 if ($code === true) {
199 $code = 500;
200 }
201
202 if ($code < 400 || 600 <= $code) {
203 throw new InvalidArgumentException(
204 "Invalid status code '$code', must be 4xx or 5xx"
205 );
206 }
207
208 return $this->sendHttpCode = $code;
209 }
210
217 public function writeToOutput($send = null)
218 {
219 if (func_num_args() == 0) {
220 return $this->sendOutput;
221 }
222
223 return $this->sendOutput = (bool) $send;
224 }
225
233 public function handleException(Exception $exception)
234 {
235 // Walk the registered handlers in the reverse order
236 // they were registered, and pass off the exception
237 $inspector = $this->getInspector($exception);
238
239 // Capture output produced while handling the exception,
240 // we might want to send it straight away to the client,
241 // or return it silently.
242 ob_start();
243
244 // Just in case there are no handlers:
245 $handlerResponse = null;
246
247 foreach (array_reverse($this->handlerStack) as $handler) {
248 $handler->setRun($this);
249 $handler->setInspector($inspector);
250 $handler->setException($exception);
251
252 // The HandlerInterface does not require an Exception passed to handle()
253 // and neither of our bundled handlers use it.
254 // However, 3rd party handlers may have already relied on this parameter,
255 // and removing it would be possibly breaking for users.
256 $handlerResponse = $handler->handle($exception);
257
258 if (in_array($handlerResponse, array(Handler::LAST_HANDLER, Handler::QUIT))) {
259 // The Handler has handled the exception in some way, and
260 // wishes to quit execution (Handler::QUIT), or skip any
261 // other handlers (Handler::LAST_HANDLER). If $this->allowQuit
262 // is false, Handler::QUIT behaves like Handler::LAST_HANDLER
263 break;
264 }
265 }
266
267 $willQuit = $handlerResponse == Handler::QUIT && $this->allowQuit();
268
269 $output = ob_get_clean();
270
271 // If we're allowed to, send output generated by handlers directly
272 // to the output, otherwise, and if the script doesn't quit, return
273 // it so that it may be used by the caller
274 if ($this->writeToOutput()) {
275 // @todo Might be able to clean this up a bit better
276 // If we're going to quit execution, cleanup all other output
277 // buffers before sending our own output:
278 if ($willQuit) {
279 while (ob_get_level() > 0) {
280 ob_end_clean();
281 }
282 }
283
284 $this->writeToOutputNow($output);
285 }
286
287 if ($willQuit) {
288 flush(); // HHVM fix for https://github.com/facebook/hhvm/issues/4055
289 exit(1);
290 }
291
292 return $output;
293 }
294
309 public function handleError($level, $message, $file = null, $line = null)
310 {
311 if ($level & error_reporting()) {
312 foreach ($this->silencedPatterns as $entry) {
313 $pathMatches = (bool) preg_match($entry["pattern"], $file);
314 $levelMatches = $level & $entry["levels"];
315 if ($pathMatches && $levelMatches) {
316 // Ignore the error, abort handling
317 return true;
318 }
319 }
320
321 $exception = new ErrorException($message, $level, 0, $file, $line);
322 if ($this->canThrowExceptions) {
323 throw $exception;
324 } else {
325 $this->handleException($exception);
326 }
327 // Do not propagate errors which were already handled by Whoops.
328 return true;
329 }
330
331 // Propagate error to the next handler, allows error_get_last() to
332 // work on silenced errors.
333 return false;
334 }
335
339 public function handleShutdown()
340 {
341 // If we reached this step, we are in shutdown handler.
342 // An exception thrown in a shutdown handler will not be propagated
343 // to the exception handler. Pass that information along.
344 $this->canThrowExceptions = false;
345
346 $error = error_get_last();
347 if ($error && $this->isLevelFatal($error['type'])) {
348 // If there was a fatal error,
349 // it was not handled in handleError yet.
350 $this->handleError(
351 $error['type'],
352 $error['message'],
353 $error['file'],
354 $error['line']
355 );
356 }
357 }
358
363 private $canThrowExceptions = true;
364
370 private function writeToOutputNow($output)
371 {
372 if ($this->sendHttpCode() && \Whoops\Util\Misc::canSendHeaders()) {
373 $httpCode = $this->sendHttpCode();
374
375 if (function_exists('http_response_code')) {
376 http_response_code($httpCode);
377 } else {
378 // http_response_code is added in 5.4.
379 // For compatibility with 5.3 we use the third argument in header call
380 // First argument must be a real header.
381 // If it is empty, PHP will ignore the third argument.
382 // If it is invalid, such as a single space, Apache will handle it well,
383 // but the PHP development server will hang.
384 // Setting a full status line would require us to hardcode
385 // string values for all different status code, and detect the protocol.
386 // which is an extra error-prone complexity.
387 header('X-Ignore-This: 1', true, $httpCode);
388 }
389 }
390
391 echo $output;
392
393 return $this;
394 }
395
396 private static function isLevelFatal($level)
397 {
398 $errors = E_ERROR;
399 $errors |= E_PARSE;
400 $errors |= E_CORE_ERROR;
401 $errors |= E_CORE_WARNING;
402 $errors |= E_COMPILE_ERROR;
403 $errors |= E_COMPILE_WARNING;
404 return ($level & $errors) > 0;
405 }
406}
print $file
Wraps ErrorException; mostly used for typing (at least now) to easily cleanup the stack trace of redu...
Wrapper for Closures passed as handlers.
Abstract implementation of a Handler.
Definition: Handler.php:17
writeToOutputNow($output)
Echo something to the browser.
Definition: Run.php:370
static isLevelFatal($level)
Definition: Run.php:396
writeToOutput($send=null)
Should Whoops push output directly to the client? If this is false, output will be returned by handle...
Definition: Run.php:217
unregister()
Unregisters all handlers registered by this Whoops\Run instance.
Definition: Run.php:131
clearHandlers()
Clears all handlers in the handlerStack, including the default PrettyPage handler.
Definition: Run.php:88
silenceErrorsInPaths($patterns, $levels=10240)
Silence particular errors in particular files.
Definition: Run.php:163
allowQuit($exit=null)
Should Whoops allow Handlers to force the script to quit?
Definition: Run.php:148
handleShutdown()
Special case to deal with Fatal errors and the like.
Definition: Run.php:339
handleException(Exception $exception)
Handles an exception, ultimately generating a Whoops error page.
Definition: Run.php:233
getHandlers()
Returns an array with all handlers, in the order they were added to the stack.
Definition: Run.php:78
$isRegistered
Definition: Run.php:23
pushHandler($handler)
Pushes a handler to the end of the stack.
Definition: Run.php:46
handleError($level, $message, $file=null, $line=null)
Converts generic PHP errors to \ErrorException instances, before passing them off to be handled.
Definition: Run.php:309
getInspector(Exception $exception)
Definition: Run.php:98
popHandler()
Removes the last handler in the stack and returns it.
Definition: Run.php:68
sendHttpCode($code=null)
Definition: Run.php:188
$code
Definition: example_050.php:99
exit
Definition: login.php:54
Whoops - php errors for cool kids.
$errors