5

I'm trying to issue a 404 from a KernelEvents::REQUEST event, but it's resulting in the following error:

Fatal error: Maximum function nesting level of '256' reached, aborting! in /var/www/vendor/symfony/http-foundation/HeaderBag.php on line 162

Here's my class

<?php

namespace Drupal\my_module\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;

class MyClass implements EventSubscriberInterface {

  /**
   * {@inheritdoc}
   */
  static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = ['my404Check'];

    return $events;
  }

  public function my404Check(GetResponseEvent $event) {
    // Do some logic here to check if it should be a 404.
    throw new NotFoundHttpException();
  }

}

I'm guessing I can't throw exceptions here, but can't see any other documentation on how to issue the 404.

3 Answers 3

7

You can set a response early in a request event:

 public function my404Check(RequestEvent $event) {

    // only return a response for a master request
    if (!$event->isMainRequest()) {
      return;
    }

    // Do some logic here to check if it should be a 404.

    $response = new Response('', Response::HTTP_NOT_FOUND);
    $event->setResponse($response);
  }

The event dispatcher checks if a response is already set and returns this response without executing any more code to build a Drupal response.

(Code updated for D9/10)

3
  • It might be worth noting that this approach won't invoke the theme handlers. i.e. Symfony (and Drupal)'s default 404 page handling will be bypassed, you'll literally get a fast 404 style blank white page. The "right" way to do it seems to involve only throwing an exception when if ($event->isMasterRequest()) { gives true per stackoverflow.com/questions/30617347/…
    – Clive
    Mar 30, 2017 at 12:38
  • @Clive, good point, I'll add the check for the master request.
    – 4uk4
    Mar 30, 2017 at 12:48
  • 1
    Great, $event->isMasterRequest() was the trick I needed. Still seems to work with throw new NotFoundHttpException();
    – leon.nk
    Mar 30, 2017 at 13:04
1

I would like to post another solution, that I have found in the Rabbit Hole module:

public function my404Check(GetResponseEvent $event) {
  // Don't process events with HTTP exceptions - those have either been thrown
  // by us or have nothing to do with this module.
  if ($event->getRequest()->get('exception') != NULL) {
    return;
  }

  // Do some logic here to check if it should be a 404.

  $response = new Response('', Response::HTTP_NOT_FOUND);
  $event->setResponse($response);
}

The event dispatcher skips processing if request has an exception already. In addition you can check the exception type.

0

I know this is old, but in case someone else finds this... setting the response was not enough for me, I still ended up with the same 'fast 404' error page, even when checking for the master request (I cleared cache). I also would prefer to simply throw the NotFoundHttpException like anywhere else in my code rather than create the response manually.

With this approach, I simply 'transform' the exception I'm catching from the event into a the NotFoundHttpException. Note that I use a priority higher than core's ExceptionLoggingSubscriber to avoid double logging of the exception.

  /**
   * {@inheritdoc}
   *
   * Use priority > 50 to run before core's ExceptionLoggingSubscriber.
   */
  public static function getSubscribedEvents() {
    $events[KernelEvents::EXCEPTION][] = ['onException', 60];
    return $events;
  }

  /**
   * Return a 404.
   *
   * @param GetResponseForExceptionEvent $event
   */
  public function onException(GetResponseForExceptionEvent $event) {
    $e = $event->getException();
    if ({{CUSTOM LOGIC HERE}}) {
      $new_exception = new NotFoundHttpException($e->getMessage());
      $event->setException($new_exception);
    }
  }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.