WhoopsExceptionHandler.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of Hyperf.
  5. *
  6. * @link https://www.hyperf.io
  7. * @document https://hyperf.wiki
  8. * @contact group@hyperf.io
  9. * @license https://github.com/hyperf/hyperf/blob/master/LICENSE
  10. */
  11. namespace Hyperf\ExceptionHandler\Handler;
  12. use Hyperf\Context\Context;
  13. use Hyperf\Context\RequestContext;
  14. use Hyperf\Contract\SessionInterface;
  15. use Hyperf\ExceptionHandler\ExceptionHandler;
  16. use Hyperf\HttpMessage\Stream\SwooleStream;
  17. use Hyperf\Stringable\Str;
  18. use Swow\Psr7\Message\ResponsePlusInterface;
  19. use Throwable;
  20. use Whoops\Handler\JsonResponseHandler;
  21. use Whoops\Handler\PlainTextHandler;
  22. use Whoops\Handler\PrettyPageHandler;
  23. use Whoops\Handler\XmlResponseHandler;
  24. use Whoops\Run;
  25. use Whoops\RunInterface;
  26. use function Hyperf\Support\env;
  27. class WhoopsExceptionHandler extends ExceptionHandler
  28. {
  29. protected static array $preference = [
  30. 'text/html' => PrettyPageHandler::class,
  31. 'application/json' => JsonResponseHandler::class,
  32. 'application/xml' => XmlResponseHandler::class,
  33. ];
  34. public function handle(Throwable $throwable, ResponsePlusInterface $response)
  35. {
  36. $whoops = new Run();
  37. [$handler, $contentType] = $this->negotiateHandler();
  38. $whoops->pushHandler($handler);
  39. $whoops->allowQuit(false);
  40. ob_start();
  41. $whoops->{RunInterface::EXCEPTION_HANDLER}($throwable);
  42. $content = ob_get_clean();
  43. return $response
  44. ->setStatus(500)
  45. ->addHeader('Content-Type', $contentType)
  46. ->setBody(new SwooleStream($content));
  47. }
  48. public function isValid(Throwable $throwable): bool
  49. {
  50. return env('APP_ENV') !== 'prod' && class_exists(Run::class);
  51. }
  52. protected function negotiateHandler()
  53. {
  54. $request = RequestContext::get();
  55. $accepts = $request->getHeaderLine('accept');
  56. foreach (self::$preference as $contentType => $handler) {
  57. if (Str::contains($accepts, $contentType)) {
  58. return [$this->setupHandler(new $handler()), $contentType];
  59. }
  60. }
  61. return [new PlainTextHandler(), 'text/plain'];
  62. }
  63. protected function setupHandler($handler)
  64. {
  65. if ($handler instanceof PrettyPageHandler) {
  66. $handler->handleUnconditionally(true);
  67. if (defined('BASE_PATH')) {
  68. $handler->setApplicationRootPath(BASE_PATH);
  69. }
  70. $request = RequestContext::get();
  71. $handler->addDataTableCallback('PSR7 Query', [$request, 'getQueryParams']);
  72. $handler->addDataTableCallback('PSR7 Post', [$request, 'getParsedBody']);
  73. $handler->addDataTableCallback('PSR7 Server', [$request, 'getServerParams']);
  74. $handler->addDataTableCallback('PSR7 Cookie', [$request, 'getCookieParams']);
  75. $handler->addDataTableCallback('PSR7 File', [$request, 'getUploadedFiles']);
  76. $handler->addDataTableCallback('PSR7 Attribute', [$request, 'getAttributes']);
  77. $session = Context::get(SessionInterface::class);
  78. if ($session) {
  79. $handler->addDataTableCallback('Hyperf Session', [$session, 'all']);
  80. }
  81. } elseif ($handler instanceof JsonResponseHandler) {
  82. $handler->addTraceToOutput(true);
  83. }
  84. return $handler;
  85. }
  86. }