Bradezone

Reception. Observation. Perception. Emotion.

CakePHP, beforeFilter, and the Error Error

We all know CakePHP is the bee’s knees for developing web applications, but what’s not so hot is Cake’s handling of error pages. Most developers discover soon enough that they can customize their error pages, most commonly the 404 “not found” page, by creating an appropriately named file, e.g. error404.ctp, within the views/errors folder. Easy peasy, right? Sure, but lurking in the bowels of Cake’s code is a significantly more obscure problem that manifests itself in various ways. Simply put, CakePHP fails to load components and does not run the beforeFilter() function of the AppController, deviating from its normal page-loading process. This can lead to utterly maddening behavior if you do anything important in beforeFilter(), which would be almost always.

An application I’m developing at work runs critical code in beforeFilter() that checks if a user is logged in and uses the results to set the website title and which links appear in the main menu. So you could say that’s fairly important. But on my error pages, Cake would conveniently display no title or main menu whatsoever. Because no debugging messages were displayed, I thought the issue might have been a minor case of a different layout file being used for error pages. But after a thorough combing of Cake’s library code, I discovered the ugly truth within the cake/libs/error.php file: the CakeErrorController class, which is used by the accompanying ErrorHandler class, never calls the beforeFilter() function. This contrasts with the Dispatcher class in cake/dispatcher.php that is responsible for most normal pages—this code eventually runs beforeFilter() within the controller’s _invoke() function, but before that it also calls the controller’s constructClasses() function, which turned out to be a critical part of my solution to this now unwieldy problem.

After attempting several ways of making the error pages run my beforeFilter() code, I found that simply adding $this->beforeFilter(); as the last line of the CakeErrorController constructor seemed to work. The problem with doing this, however, is that I had to change a core file within Cake. As my fellow developers know, this is not an option, so I had to make it work from within my own code. But luckily I was on the right track: the constructor inherits from the AppController constructor, which I was free to modify. So within my application’s app_controller.php file, I added a constructor and copied the code from CakeErrorController, making sure to add my call to beforeFilter() at the end. Amazingly it seemed to work, so I then proceeded to see which lines I could safely comment out, until I was left with just a few lines of code. The constructClasses() function in particular seems to be Cake’s succinct way of loading the components used by the application. After testing several normal pages and error pages alike, I feel fairly confident that I have solved my initial problem. Here’s the code I added to app_controller.php:

function __construct() {
    parent::__construct();
    if ($this->name == 'CakeError') {
        $this->constructClasses();
        $this->beforeFilter();
    }
}

Since I was forced to put the code in the main AppController class, some side effects may exist: beforeFilter() is potentially called twice on standard pages and perhaps earlier than normal. From what I can tell, this doesn’t affect the application’s performance or behavior. So hopefully this solution will help other CakePHP developers who want their error pages to behave as expected. But surely I am not alone in hoping that future versions of CakePHP will make this workaround unnecessary.

UPDATE: I tweaked the code to include an explicit check for CakeErrorController before running the extra lines of code in the constructor. I found a fail case that had to do with adding new users via the Auth component—new passwords were being hashed twice. So this new snippet should successfully eliminate the possibility of code running twice needlessly.

UPDATE DEUX: After some discussion in the comments below, we may have found a slightly better solution. Create an app_error.php file in your app root (if it is not already there) and supply the following code.

class AppError extends ErrorHandler {
    function _outputMessage($template) {
        $this->controller->beforeFilter();
        parent::_outputMessage($template);
    }
}

26 Comments

  1. What about creating an app_error.ctp in /app with:
    controller->beforeFilter();
    parent::error404();
    }
    }
    ?>

  2. That didn’t come out well…one more try:

    <?php
    class AppError extends ErrorHandler {
    function error404() {
    $this->controller->beforeFilter();
    parent::error404();
    }
    }
    ?>

  3. Matt, thanks for the info. Your solution works fine in production mode for the 404 page specifically. I guess I wanted my solution to cover all the other error pages, including ones that are used in development mode, like “missing controller” and “missing action,” etc.

  4. Cheers for this! I was having this problem myself and your solution was heaps better than my own hack. :)

  5. very informative post for beforeFilter of cakphp..helped me lot..thanks..:)

  6. I was having the same problems, and couldn’t overcome the problems until I read your article. Helped a lot, thanks.

    I did have a small problem with it though. the code above loaded up the beforeFilter() fine, and I added a beforeRender() aswell, just because my layout needs it. The problem was that even though my components were loaded, they didn’t do anything. I added:

    $this->MyNiceComponent->startup($this);

    to the bottom of the _construct() and all was fine.

    Just thought I’d post it, incase others get a bit stuck.

    Andrew Lawson
  7. Hi, nice site, thanks for the infos.

    I have a query – I have an app with 20+ classes using AMFPHP. I am using the beforeFilter() to validate requests and it works fine, however I want to store the beforeFilter function centrally. It’s a pain to have it sitting at the top of EVERY class file. I tried having it in a PHP include to no avail. It doesn’t recognize it. Any tips? I’m a bit new to AMF…

    Thanks,
    S

    S.G,
  8. I haven’t worked with AMF, but if it’s anything like Cake, there should be a file like “app_controller.php” that runs before any individual class file. But for all I know, it might be totally different.

  9. wow… saves me a lot of time… I was working with language links

    ex.:
    example.com/eng/controllers/action
    example.com/fre/controllers/action

    and it solve my problem cause each time there was an error my language params was gone… THKS a lot

  10. Hah, it seems not only me got this problem, luckily you Brade solved it already and was kind enough to share the solution, thank you ;)

    Nicolae N
  11. Great article. I’ve been working on unearthing the same issue on my production API for a couple of hours now. Google has loads of links that describe how to create custom error handling within CakePHP. The issue of calling beforeFilter is critcal to my app and you have saved my sanity. Thanks :-)

  12. Your suggestion has solved my problem with (dynamically set) themes not being detected by CakeError as well. Great tip, thanks man!

  13. This was a great resource. Just wish I would have done this way before I started to parse through the entire Core directory looking for the correct function to import to app_controller.

  14. Awesome, thanks a ton for this! Worked like a charm. :)

    Jordan
  15. Hey there, thanks a lot. That was a great tip!

    Domnik Binz
  16. Hey, there’s a lot easier way to deal with that problem!
    Just call $this->controller->beforeFilter(); in your error method:

    controller->beforeFilter();
    $this->_outputMessage(‘my_error’);
    }
    }
    ?>

  17. Oh that displaying didnt work well, so again:

    class AppError extends ErrorHandler
    {
    function myError($params)
    {
    $this->controller->beforeFilter();
    $this->_outputMessage(‘my_error’);
    }
    }

  18. It’s hard to get easier than my five lines of code, but thanks for the feedback. Your solution is quite similar to Matt’s at the top–again, the problem is that you’d have to override a number of methods to catch all possible errors. My solution ensures that no matter the error (even the ones encountered in debug mode), your AppController variables and your template will remain intact.

  19. Brade, five lines of code or not, I think the reason many people are looking for a way to put this into the Error stack somewhere is that it’s far more idiomatic to the framework.

    Specifically, by calling beforeFilter (and beforeRender if it’s needed) in the AppController constructor means that it’s going to run earlier than it normally would. You’re taking a very conventional event schedule and flipping it.

    Now this isn’t horrible, of course. Sometimes hacks are part of life. But if you’ve got a superclass (AppController in this case) and you’ve got a conditional to execute code only when that class is being inherited by a specific subclass — that’s a pretty awful code smell. I mean, that rubs against the grain of OO design.

    Still, I for one appreciate your solution and it helped my find my own solution. Which is pretty simple, actually:

    Every one of the built-in error methods call the _outputMessage() function.

    So I overloaded that:

    function _outputMessage($template)
    {
    static $virgin = true;

    if ($virgin) {

    $virgin = false;
    $this->controller->constructClasses();
    $this->controller->beforeFilter();

    }
    parent::_outputMessage($template);

    }

    Done.

    Shane
  20. Aha, NOW we’re getting somewhere. I’m all for using the proper Cake conventions, but I’m even more in favor of economy (which after all is one of the main points of OOP). Luckily your method seems to get us on this track.

    Looking at the code again (in both the latest 1.2 and 1.3), I see that you’re correct that _outputMessage is always called. And what’s interesting is that _outputMessage includes a call to afterFilter but NOT beforeFilter. So now I’m more convinced than ever that this is simply an oversight by the CakePHP developers. By merely including a call to beforeFilter at the top of this function, our problems would be solved.

    Therefore we don’t even need to bother with constructClasses since that is called in the CakeErrorController object created by the ErrorHandler. And I also believe we don’t need the static variable check, since we’re merely adding a beforeFilter call to match the afterFilter call that’s already there, so I ended up with the following code in my app_error.php file:

    class AppError extends ErrorHandler {
        function _outputMessage($template) {
            $this->controller->beforeFilter();
            parent::_outputMessage($template);
        }
    }
    

    I see also that this is pretty close to what Georg had, only moving it into the private method that all the error methods use, so hooray for teamwork, everyone!

  21. Again, I’m glad I found other people with this same problem because I was annoyed and stumped when my 404′s were covered with error messages (things not being set I set in the beforeRender). Thanks for posting your original post. It’s hell stepping thru all that framework code line-by-line without even knowing really what you’re looking for.

    Before i saw your post I just assumed maybe the error controller was maybe clearing the output buffer before it was flushed and then including itself and never re-calling my controller methods. Nope. I was barking up the wrong tree.

    Shane
  22. Also, I had the $virgin check in there because I wasn’t sure if there would be a situation where more than one error is called on that same error object. I’m still not sure if that’s the case, so rather than risk calling beforeFilter et al more than once, I’m going to keep that check for now.

    The real reason I’m posting again tho is to share one more missing step in this cake parallel-dispatch farce.. In the CakeErrorController::_construct() they call constructClasses() (to build models) and ->Component->initialize() BUT in normal dispatching, ->Component->startup($controller) is called AFTER the beforeFilter call.

    Now, from what I saw, not having this on my app didn’t cause any ill effects. But that could just be a function of the Components i’m using not using that event.

    So my complete workaround now is this:
    // file /app/app_error.php

    /**
    * Overload this protected method because it’s called by all the built-in error methods (error404, missingAction, etc) and
    * we need to fix a sneaky little bug caused by the fact that the CakeErrorController — while a subclass of AppController and designed
    * to function like any other page controller — is not instantiated by the normal Dispatch process and instead has this parallel bootstrap
    * in the ErrorHandler::__construct class. Somehow this was bunged a little and some of the normal dispatch steps aren’t executed.
    * At a minimum, I know it’s not calling AppController::beforeFilter(). In our lil app here we use that HEAVILY so this became a big problem for us.
    *
    * @see A little discussion I participated in on the subject here: http://www.bradezone.com/2009/05/21/cakephp-beforefilter-and-the-error-error/
    * @see ErrorHandler::_outputMessage()
    */
    function _outputMessage($template)
    {
    static $virgin = true;

    if ($virgin) {

    // $controller->beforeFilter();

    $virgin = false;

    // The commented-out methods make-up the sequence of events that occurs during normal dispatch.
    // I know for certain that we need to call beforeFilter. But as of writing this, there’s no benefit of me calling the others.
    // And I have seen at least the constructClasses call done ele
    $this->controller->beforeFilter();
    $this->controller->Component->startup($this->controller);
    }
    parent::_outputMessage($template);

    }

    Shane
  23. This should look a lil cleaner:

    /**
    * Overload this protected method because it’s called by all the
    * built-in error methods (error404, missingAction, etc) and
    * we need to fix a sneaky little bug caused by the fact that the
    * CakeErrorController — while a subclass of AppController and designed
    * to function like any other page controller — is not instantiated by the
    * normal Dispatch process and instead has this parallel bootstrap
    * in the ErrorHandler::__construct class. Somehow this was bunged a
    * little and some of the normal dispatch steps aren’t executed.
    * At a minimum, I know it’s not calling AppController::beforeFilter().
    * In our lil app here we use that HEAVILY so this became a big problem for us.
    *
    * @see http://www.bradezone.com/2009/05/21/cakephp-beforefilter-and-the-error-error/
    * @see ErrorHandler::_outputMessage()
    */
    function _outputMessage($template)
    {
    static $virgin = true;

    if ($virgin) {

    $virgin = false;
    $this->controller->beforeFilter();
    }
    parent::_outputMessage($template);

    }

    Shane
  24. Thank you! I’ve been banging my head against the wall for several weeks now, trying to figure out how to supply a useful 404 page, while still showing my custom site settings from my beforeFilter method. This is such an easy fix.

  25. Excellent!

    I had a hard time figuring out that the beforeFilter() Method of the AppController was NOT called when a 404 error occurs, your solution works like a charm!

    Erik
  26. Thanks. Sorted my problem with the Theme being ignored for error pages.

    Cheers

Leave a Comment

Notify of follow-up comments via email, or subscribe without commenting.