HttpExceptionSubscriberBase.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <?php
  2. namespace Drupal\Core\EventSubscriber;
  3. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  4. use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
  5. use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
  6. use Symfony\Component\HttpKernel\KernelEvents;
  7. /**
  8. * Utility base class for exception subscribers.
  9. *
  10. * A subscriber may extend this class and implement getHandledFormats() to
  11. * indicate which request formats it will respond to. Then implement an on*()
  12. * method for any error code (HTTP response code) that should be handled. For
  13. * example, to handle a specific error code like 404 Not Found messages add the
  14. * method:
  15. *
  16. * @code
  17. * public function on404(GetResponseForExceptionEvent $event) {}
  18. * @endcode
  19. *
  20. * To implement a fallback for the entire 4xx class of codes, implement the
  21. * method:
  22. *
  23. * @code
  24. * public function on4xx(GetResponseForExceptionEvent $event) {}
  25. * @endcode
  26. *
  27. * That method should then call $event->setResponse() to set the response object
  28. * for the exception. Alternatively, it may opt not to do so and then other
  29. * listeners will have the opportunity to handle the exception.
  30. *
  31. * Note: Core provides several important exception listeners by default. In most
  32. * cases, setting the priority of a contrib listener to the default of 0 will
  33. * do what you expect and handle the exceptions you'd expect it to handle.
  34. * If a custom priority is set, be aware of the following core-registered
  35. * listeners.
  36. *
  37. * - Fast404ExceptionHtmlSubscriber: 200. This subscriber will return a
  38. * minimalist, high-performance 404 page for HTML requests. It is not
  39. * recommended to have a priority higher than this one as it will only slow
  40. * down that use case.
  41. * - ExceptionLoggingSubscriber: 50. This subscriber logs all exceptions but
  42. * does not handle them. Do not register a listener with a higher priority
  43. * unless you want exceptions to not get logged, which makes debugging more
  44. * difficult.
  45. * - DefaultExceptionSubscriber: -256. The subscriber of last resort, this will
  46. * provide generic handling for any exception. A listener with a lower
  47. * priority will never get called.
  48. *
  49. * All other core-provided exception handlers have negative priorities so most
  50. * module-provided listeners will naturally take precedence over them.
  51. */
  52. abstract class HttpExceptionSubscriberBase implements EventSubscriberInterface {
  53. /**
  54. * Specifies the request formats this subscriber will respond to.
  55. *
  56. * @return array
  57. * An indexed array of the format machine names that this subscriber will
  58. * attempt to process, such as "html" or "json". Returning an empty array
  59. * will apply to all formats.
  60. *
  61. * @see \Symfony\Component\HttpFoundation\Request
  62. */
  63. abstract protected function getHandledFormats();
  64. /**
  65. * Specifies the priority of all listeners in this class.
  66. *
  67. * The default priority is 1, which is very low. To have listeners that have
  68. * a "first attempt" at handling exceptions return a higher priority.
  69. *
  70. * @return int
  71. * The event priority of this subscriber.
  72. */
  73. protected static function getPriority() {
  74. return 0;
  75. }
  76. /**
  77. * Handles errors for this subscriber.
  78. *
  79. * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
  80. * The event to process.
  81. */
  82. public function onException(GetResponseForExceptionEvent $event) {
  83. $exception = $event->getException();
  84. // Make the exception available for example when rendering a block.
  85. $request = $event->getRequest();
  86. $request->attributes->set('exception', $exception);
  87. $handled_formats = $this->getHandledFormats();
  88. $format = $request->query->get(MainContentViewSubscriber::WRAPPER_FORMAT, $request->getRequestFormat());
  89. if ($exception instanceof HttpExceptionInterface && (empty($handled_formats) || in_array($format, $handled_formats))) {
  90. $method = 'on' . $exception->getStatusCode();
  91. // Keep just the leading number of the status code to produce either a
  92. // on400 or a 500 method callback.
  93. $method_fallback = 'on' . substr($exception->getStatusCode(), 0, 1) . 'xx';
  94. // We want to allow the method to be called and still not set a response
  95. // if it has additional filtering logic to determine when it will apply.
  96. // It is therefore the method's responsibility to set the response on the
  97. // event if appropriate.
  98. if (method_exists($this, $method)) {
  99. $this->$method($event);
  100. }
  101. elseif (method_exists($this, $method_fallback)) {
  102. $this->$method_fallback($event);
  103. }
  104. }
  105. }
  106. /**
  107. * Registers the methods in this class that should be listeners.
  108. *
  109. * @return array
  110. * An array of event listener definitions.
  111. */
  112. public static function getSubscribedEvents() {
  113. $events[KernelEvents::EXCEPTION][] = ['onException', static::getPriority()];
  114. return $events;
  115. }
  116. }