/vendor/laravel/framework/src/Illuminate/Exception/Handler.php

https://gitlab.com/xolotsoft/pumasruiz · PHP · 385 lines · 156 code · 46 blank · 183 comment · 14 complexity · f3d1a905625d4e282de5e0baeff59111 MD5 · raw file

  1. <?php namespace Illuminate\Exception;
  2. use Closure;
  3. use ErrorException;
  4. use ReflectionFunction;
  5. use Illuminate\Support\Contracts\ResponsePreparerInterface;
  6. use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
  7. use Symfony\Component\Debug\Exception\FatalErrorException as FatalError;
  8. class Handler {
  9. /**
  10. * The response preparer implementation.
  11. *
  12. * @var \Illuminate\Support\Contracts\ResponsePreparerInterface
  13. */
  14. protected $responsePreparer;
  15. /**
  16. * The plain exception displayer.
  17. *
  18. * @var \Illuminate\Exception\ExceptionDisplayerInterface
  19. */
  20. protected $plainDisplayer;
  21. /**
  22. * The debug exception displayer.
  23. *
  24. * @var \Illuminate\Exception\ExceptionDisplayerInterface
  25. */
  26. protected $debugDisplayer;
  27. /**
  28. * Indicates if the application is in debug mode.
  29. *
  30. * @var bool
  31. */
  32. protected $debug;
  33. /**
  34. * All of the register exception handlers.
  35. *
  36. * @var array
  37. */
  38. protected $handlers = array();
  39. /**
  40. * All of the handled error messages.
  41. *
  42. * @var array
  43. */
  44. protected $handled = array();
  45. /**
  46. * Create a new error handler instance.
  47. *
  48. * @param \Illuminate\Support\Contracts\ResponsePreparerInterface $responsePreparer
  49. * @param \Illuminate\Exception\ExceptionDisplayerInterface $plainDisplayer
  50. * @param \Illuminate\Exception\ExceptionDisplayerInterface $debugDisplayer
  51. * @param bool $debug
  52. * @return void
  53. */
  54. public function __construct(ResponsePreparerInterface $responsePreparer,
  55. ExceptionDisplayerInterface $plainDisplayer,
  56. ExceptionDisplayerInterface $debugDisplayer,
  57. $debug = true)
  58. {
  59. $this->debug = $debug;
  60. $this->plainDisplayer = $plainDisplayer;
  61. $this->debugDisplayer = $debugDisplayer;
  62. $this->responsePreparer = $responsePreparer;
  63. }
  64. /**
  65. * Register the exception / error handlers for the application.
  66. *
  67. * @param string $environment
  68. * @return void
  69. */
  70. public function register($environment)
  71. {
  72. $this->registerErrorHandler();
  73. $this->registerExceptionHandler();
  74. if ($environment != 'testing') $this->registerShutdownHandler();
  75. }
  76. /**
  77. * Register the PHP error handler.
  78. *
  79. * @return void
  80. */
  81. protected function registerErrorHandler()
  82. {
  83. set_error_handler(array($this, 'handleError'));
  84. }
  85. /**
  86. * Register the PHP exception handler.
  87. *
  88. * @return void
  89. */
  90. protected function registerExceptionHandler()
  91. {
  92. set_exception_handler(array($this, 'handleUncaughtException'));
  93. }
  94. /**
  95. * Register the PHP shutdown handler.
  96. *
  97. * @return void
  98. */
  99. protected function registerShutdownHandler()
  100. {
  101. register_shutdown_function(array($this, 'handleShutdown'));
  102. }
  103. /**
  104. * Handle a PHP error for the application.
  105. *
  106. * @param int $level
  107. * @param string $message
  108. * @param string $file
  109. * @param int $line
  110. * @param array $context
  111. *
  112. * @throws \ErrorException
  113. */
  114. public function handleError($level, $message, $file = '', $line = 0, $context = array())
  115. {
  116. if (error_reporting() & $level)
  117. {
  118. throw new ErrorException($message, 0, $level, $file, $line);
  119. }
  120. }
  121. /**
  122. * Handle an exception for the application.
  123. *
  124. * @param \Exception $exception
  125. * @return \Symfony\Component\HttpFoundation\Response
  126. */
  127. public function handleException($exception)
  128. {
  129. $response = $this->callCustomHandlers($exception);
  130. // If one of the custom error handlers returned a response, we will send that
  131. // response back to the client after preparing it. This allows a specific
  132. // type of exceptions to handled by a Closure giving great flexibility.
  133. if ( ! is_null($response))
  134. {
  135. return $this->prepareResponse($response);
  136. }
  137. // If no response was sent by this custom exception handler, we will call the
  138. // default exception displayer for the current application context and let
  139. // it show the exception to the user / developer based on the situation.
  140. return $this->displayException($exception);
  141. }
  142. /**
  143. * Handle an uncaught exception.
  144. *
  145. * @param \Exception $exception
  146. * @return void
  147. */
  148. public function handleUncaughtException($exception)
  149. {
  150. $this->handleException($exception)->send();
  151. }
  152. /**
  153. * Handle the PHP shutdown event.
  154. *
  155. * @return void
  156. */
  157. public function handleShutdown()
  158. {
  159. $error = error_get_last();
  160. // If an error has occurred that has not been displayed, we will create a fatal
  161. // error exception instance and pass it into the regular exception handling
  162. // code so it can be displayed back out to the developer for information.
  163. if ( ! is_null($error))
  164. {
  165. extract($error);
  166. if ( ! $this->isFatal($type)) return;
  167. $this->handleException(new FatalError($message, $type, 0, $file, $line))->send();
  168. }
  169. }
  170. /**
  171. * Determine if the error type is fatal.
  172. *
  173. * @param int $type
  174. * @return bool
  175. */
  176. protected function isFatal($type)
  177. {
  178. return in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE));
  179. }
  180. /**
  181. * Handle a console exception.
  182. *
  183. * @param \Exception $exception
  184. * @return void
  185. */
  186. public function handleConsole($exception)
  187. {
  188. return $this->callCustomHandlers($exception, true);
  189. }
  190. /**
  191. * Handle the given exception.
  192. *
  193. * @param \Exception $exception
  194. * @param bool $fromConsole
  195. * @return void
  196. */
  197. protected function callCustomHandlers($exception, $fromConsole = false)
  198. {
  199. foreach ($this->handlers as $handler)
  200. {
  201. // If this exception handler does not handle the given exception, we will just
  202. // go the next one. A handler may type-hint an exception that it handles so
  203. // we can have more granularity on the error handling for the developer.
  204. if ( ! $this->handlesException($handler, $exception))
  205. {
  206. continue;
  207. }
  208. elseif ($exception instanceof HttpExceptionInterface)
  209. {
  210. $code = $exception->getStatusCode();
  211. }
  212. // If the exception doesn't implement the HttpExceptionInterface, we will just
  213. // use the generic 500 error code for a server side error. If it implements
  214. // the HttpException interfaces we'll grab the error code from the class.
  215. else
  216. {
  217. $code = 500;
  218. }
  219. // We will wrap this handler in a try / catch and avoid white screens of death
  220. // if any exceptions are thrown from a handler itself. This way we will get
  221. // at least some errors, and avoid errors with no data or not log writes.
  222. try
  223. {
  224. $response = $handler($exception, $code, $fromConsole);
  225. }
  226. catch (\Exception $e)
  227. {
  228. $response = $this->formatException($e);
  229. }
  230. // If this handler returns a "non-null" response, we will return it so it will
  231. // get sent back to the browsers. Once the handler returns a valid response
  232. // we will cease iterating through them and calling these other handlers.
  233. if (isset($response) && ! is_null($response))
  234. {
  235. return $response;
  236. }
  237. }
  238. }
  239. /**
  240. * Display the given exception to the user.
  241. *
  242. * @param \Exception $exception
  243. * @return void
  244. */
  245. protected function displayException($exception)
  246. {
  247. $displayer = $this->debug ? $this->debugDisplayer : $this->plainDisplayer;
  248. return $displayer->display($exception);
  249. }
  250. /**
  251. * Determine if the given handler handles this exception.
  252. *
  253. * @param \Closure $handler
  254. * @param \Exception $exception
  255. * @return bool
  256. */
  257. protected function handlesException(Closure $handler, $exception)
  258. {
  259. $reflection = new ReflectionFunction($handler);
  260. return $reflection->getNumberOfParameters() == 0 || $this->hints($reflection, $exception);
  261. }
  262. /**
  263. * Determine if the given handler type hints the exception.
  264. *
  265. * @param \ReflectionFunction $reflection
  266. * @param \Exception $exception
  267. * @return bool
  268. */
  269. protected function hints(ReflectionFunction $reflection, $exception)
  270. {
  271. $parameters = $reflection->getParameters();
  272. $expected = $parameters[0];
  273. return ! $expected->getClass() || $expected->getClass()->isInstance($exception);
  274. }
  275. /**
  276. * Format an exception thrown by a handler.
  277. *
  278. * @param \Exception $e
  279. * @return string
  280. */
  281. protected function formatException(\Exception $e)
  282. {
  283. if ($this->debug)
  284. {
  285. $location = $e->getMessage().' in '.$e->getFile().':'.$e->getLine();
  286. return 'Error in exception handler: '.$location;
  287. }
  288. return 'Error in exception handler.';
  289. }
  290. /**
  291. * Register an application error handler.
  292. *
  293. * @param \Closure $callback
  294. * @return void
  295. */
  296. public function error(Closure $callback)
  297. {
  298. array_unshift($this->handlers, $callback);
  299. }
  300. /**
  301. * Register an application error handler at the bottom of the stack.
  302. *
  303. * @param \Closure $callback
  304. * @return void
  305. */
  306. public function pushError(Closure $callback)
  307. {
  308. $this->handlers[] = $callback;
  309. }
  310. /**
  311. * Prepare the given response.
  312. *
  313. * @param mixed $response
  314. * @return \Illuminate\Http\Response
  315. */
  316. protected function prepareResponse($response)
  317. {
  318. return $this->responsePreparer->prepareResponse($response);
  319. }
  320. /**
  321. * Determine if we are running in the console.
  322. *
  323. * @return bool
  324. */
  325. public function runningInConsole()
  326. {
  327. return php_sapi_name() == 'cli';
  328. }
  329. /**
  330. * Set the debug level for the handler.
  331. *
  332. * @param bool $debug
  333. * @return void
  334. */
  335. public function setDebug($debug)
  336. {
  337. $this->debug = $debug;
  338. }
  339. }