Zend_Controller_Action_Helper_Redirector wants to kill my session

I’m writing a web application using Zend Framwork‘s MVC components these days and today I came over a weird problem regarding a custom php shutdown function and the Redirector action helper from the Zend Framework.

I have a Bootstrap class that registers a shutdown function that looks somewhat like this:

<?php
class Bootstrap {
    static public function run() {
        // ...
        self::prepareEnvironment();
        // ...
    }

    static public function prepareEnvironment() {
        // ...
        register_shutdown_function('Bootstrap::shutdown');
        // ...
    }

    static public function shutdown() {
        $messages = STM_FlashMessage::getMessages();

        if (count($messages)) {
            $_SESSION['stm']['flashMessages'] = $messages;
        }
    }
}

STM_FlashMessage is a class with a static $messages array that I add messages to. Messages that I haven’t handled when the script is done should be available in the next request. An example of when I want a message to travel to the next request is when I have successfully edited a user in the application. In the edit user action I do something like:

<?php
class AdminController extends Zend_Controller_Action {
    public function editUserAction() {
        // ...

        if ($this->editUser( /* some params */ )) {
            STM_FlashMessage::add('User has been edited.');
            $this->_redirect('/admin');
        } else {
            STM_FlashMessage::add('User was not edited', STM_FlashMessage::ERROR);
        }

        // ...
    }
}

As you can see I call $this->_redirect('/admin'); which redirects the client to the /admin page and kills the script. The message that I added on the line before the call should then be registered in the session by the shutdown function. Well, this is not the case. Why is that you say? Well, the call to $this->_redirect() proxies to the Redirector action helper, and that helper has a method called redirectAndExit(), which looks like this:

public function redirectAndExit()
{
     // Close session, if started
    if (class_exists('Zend_Session', false) && Zend_Session::isStarted()) {
        Zend_Session::writeClose();
    } elseif (isset($_SESSION)) {
        session_write_close();
    }

    $this->getResponse()->sendHeaders();
    exit();
}

What it actually does is to write to the session storage and close the current session before sending some headers to the client, regardless if you use Zend_Session or not. Therefore, when my shutdown function is executed I am not in the session anymore! I’m not really sure what happens to the $_SESSION variable after the session is closed but I’m guessing it loses its magic abilities and behaves like a regular PHP variable. If someone who reads this knows better, please let me know.

And now over to the fix!

Since the Zend Framework is easily extendable I created my own Redirector helper that simply overrides the redirectAndExit() method:

<?php
class STM_Controller_Action_Helper_Redirector extends Zend_Controller_Action_Helper_Redirector {
    public function redirectAndExit() {
        $this->getResponse()->sendHeaders();
        exit;
    }
}

Now I need to register this helper so it will be used instead of Zend Framework’s Redirector helper:

<?php
class Bootstrap {
    static public function run() {
        // ...
        self::prepareEnvironment();
        self::prepareFrontController();
        // ...
    }

    static public function prepareEnvironment() {
        // ...
        register_shutdown_function('Bootstrap::shutdown');
        // ...
    }

    static public function prepareFrontController() {
        $redirector = new STM_Controller_Action_Helper_Redirector();
        Zend_Controller_Action_HelperBroker::addHelper($redirector);

        // ...
    }

    static public function shutdown() {
        $messages = STM_FlashMessage::getMessages();

        if (count($messages)) {
            $_SESSION['stm']['flashMessages'] = $messages;
        }
    }
}

Now the session is still active when my shutdown function is executed, and the messages stored in the static $messages array in my STM_FlashMessage class will be available in the next request. I am aware that Zend Framework has a FlashMessenger action helper, but I don’t really want to use the Zend_Session stuff so I created my own component instead.

Personally I don’t think it’s correct that an action helper kills the session like the Redirector helper does. “But, the Zend Framework is so easy to extend! You can simply fix it by extending the Redirector.” … yes, that is correct, and that is what I did in this case, but I still don’t think it’s the way to do it! Anyways, got more coding to do! Hopefully this might be of help to some of you out there!

Advertisements
This entry was posted in PHP, Technology and tagged , , , , , , , . Bookmark the permalink.

One Response to Zend_Controller_Action_Helper_Redirector wants to kill my session

  1. Pingback: Zend_Controller_Action_Helper_Redirector session issue fixed « Christer’s blog o’ fun

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s