ILIAS  release_5-1 Revision 5.0.0-5477-g43f3e3fab5f
Run.php
Go to the documentation of this file.
1 <?php
7 namespace Whoops;
8 
9 use Exception;
16 
17 class 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 }
clearHandlers()
Clears all handlers in the handlerStack, including the default PrettyPage handler.
Definition: Run.php:88
print $file
exit
Definition: login.php:54
$isRegistered
Definition: Run.php:23
$code
Definition: example_050.php:99
handleException(Exception $exception)
Handles an exception, ultimately generating a Whoops error page.
Definition: Run.php:233
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
handleError($level, $message, $file=null, $line=null)
Converts generic PHP errors to instances, before passing them off to be handled. ...
Definition: Run.php:309
sendHttpCode($code=null)
Definition: Run.php:188
writeToOutputNow($output)
Echo something to the browser.
Definition: Run.php:370
getInspector(Exception $exception)
Definition: Run.php:98
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
getHandlers()
Returns an array with all handlers, in the order they were added to the stack.
Definition: Run.php:78
Whoops - php errors for cool kids.
unregister()
Unregisters all handlers registered by this Whoops instance.
Definition: Run.php:131
pushHandler($handler)
Pushes a handler to the end of the stack.
Definition: Run.php:46
handleShutdown()
Special case to deal with Fatal errors and the like.
Definition: Run.php:339
$errors
popHandler()
Removes the last handler in the stack and returns it.
Definition: Run.php:68
Wrapper for Closures passed as handlers.
Wraps ErrorException; mostly used for typing (at least now) to easily cleanup the stack trace of redu...
A Whoops error handler that delegates calls on it self to another handler that is created only in the...
static isLevelFatal($level)
Definition: Run.php:396