Christer’s blog o’ fun

February 25, 2009

Zend_Controller_Action_Helper_Redirector session issue fixed

Filed under: PHP, Technology — Tags: , , , , — christer @ 6:11 pm

I just commited a fix to the issue described in this post to Zend Framework’s trunk. The problem can now be solved by fetching the helper and setting a flag:

$redirectorHelper = Zend_Controller_Action_HelperBroker::getStaticHelper('Redirector');
$redirectorHelper->setCloseSessionOnExit(false);

And thats that. Now the redirector will NOT call session_write_close() or Zend_Session::writeClose() (if you use Zend_Session) upon exit. This way the session is still available in any function that is registered as a shutdown function. The default behavior is to close it, so if this is not an issue for you, you don’t need to think about it.

No need for that custom redirector helper anymore!

February 24, 2009

Custom Zend_Auth_Adapter and Zend_Auth_Storage classes

I’m working on a small application these days using Zend Framework’s MVC components. I use Zend_Auth for authentication and since I did not want to use Zend_Session I decided to implement a custom Zend_Auth_Storage adapter that uses the regular session handling in PHP. Currently the application does authentication towards a database table (users) and since I have made model classes for my tables and rows I wanted the adapter to provide me with an instance of the User class (which implements the Zend_Db_Table_Row_Abstract class and holds some custom logic). First, the storage:

class STM_Auth_Storage implements Zend_Auth_Storage_Interface {
    /**
     * See if the session is empty
     *
     * @return boolean
     */
    public function isEmpty() {
        return empty($_SESSION['stm']['user']);
    }

    /**
     * Read the contents from the session
     *
     * @return null|User
     */
    public function read() {
        $contents = null;

        if (!empty($_SESSION['stm']['user'])) {
            $contents = $_SESSION['stm']['user'];
        }

        return $contents;
    }

    /**
     * Store content in the session
     *
     * @param User $contents
     */
    public function write($contents) {
        if (!($contents instanceof User)) {
            $contents = null;
        }

        $_SESSION['stm']['user'] = $contents;
    }

    /**
     * Clear the data stored in the session
     *
     */
    public function clear() {
        unset($_SESSION['stm']['user']);
    }
}

As you can see we only need to implement a simple interface with 4 methods. The phpdoc comments should be enough to understand whats going on in the methods. Now over to the authentication adapter:

class STM_Auth_Adapter implements Zend_Auth_Adapter_Interface {
    /**
     * Username
     *
     * @var string
     */
    protected $username = null;

    /**
     * Password
     *
     * @var string
     */
    protected $password = null;

    /**
     * Class constructor
     *
     * The constructor sets the username and password
     *
     * @param string $username
     * @param string $password
     */
    public function __construct($username, $password) {
        $this->username = $username;
        $this->password = $password;
    }

    /**
     * Authenticate
     *
     * Authenticate the username and password
     *
     * @return Zend_Auth_Result
     */
    public function authenticate() {
        // Try to fetch the user from the database using the model
        $users = new Users();
        $select = $users->select()
                        ->where('username = ?', $this->username),
                        ->where('password = ?', md5($this->password));
        $user = $users->fetchRow($select);

        // Initialize return values
        $code = Zend_Auth_Result::FAILURE;
        $identity = null;
        $messages = array();

        // Do we have a valid user?
        if ($user instanceof User) {
            $code = Zend_Auth_Result::SUCCESS;
            $identity = $user;
        } else {
            $messages[] = 'Authentication error';
        }

        return new Zend_Auth_Result($code, $identity, $messages);
    }
}

This is also a small class implementing a simple interface. In the authenticate method I do new Users(); to fetch an instance of the gateway to the users table using an implementation of Zend_Db_Table_Abstract. I then make a select statement, that will try to find a valid user, and use that as an argument to the fetchRow() method. If the result is an instance of the User model which is an implementation of the Zend_Db_Table_Row_Abstract class I change two values that is used to create the Zend_Auth_Result return object. If the call to fetchRow() does not return a valid user, we add an error message to the $messages array for the Zend_Auth_Result return object. To log in I have made a form that sends a request to /user/login with some POST data. The login action in the user controller looks something like this:

class UserController extends Zend_Controller_Action {
    /**
     * Authentication object
     *
     * @var Zend_Auth
     */
    protected $auth = null;

    /**
     * Pre dispatch method
     *
     * This code will be executed before the the front controller tries to dispatch the current
     * request.
     */
    public function preDispatch() {
        $this->auth = Zend_Auth::getInstance();
    }

    /**
     * Login action
     *
     * Someone tries to login. Use the auth object to see if the input is valid.
     */
    public function loginAction() {
        $request = $this->getRequest();
        $authAdapter = new STM_Auth_Adapter($request->username, $request->password);
        $result = $this->auth->authenticate($authAdapter);

        if ($result->isValid()) {
            // Yay! User is authenticated and stored in the session (via the storage class)
            $this->_redirect('/user/index');
        } else {
            // Do some error handling...
        }
    }
}

My controller is a bit bigger than this bit I stripped away most of it to keep this example as simple as possible. First I have a preDistpatch() method that populates the protected $auth; attribute that is used in the login action. Before it is used we create the authentication adapter and provide it with the username and password that the user has typed in. If $result->isValid() evaluates to true we have fetched a valid user based on the given credentials. The User instance is also saved in the session under $_SESSION['stm']['user'] (as specified in the write() method in the STM_Auth_Storage class that can be found at the start of this post). And thats about that really. Feel free to play around with this code, and please let me know if there are any problems. If anyone can see some vulnerabilities with this please let me know.

February 23, 2009

AOTW 8, 2009: Stone Sour – Come What(ever) May

Filed under: AOTW, Music — Tags: , , — christer @ 9:36 am

The award for week 8 goes to Stone Sour, with their 2nd album: Come What(ever) May.

My three favorite songs are:

  • Come What(ever) May
  • Hell & Consequences
  • Socio

Updated numbers on the IE6 upgrade box on www.vg.no and www.nettby.no

Filed under: Technology, Work related — Tags: , , , , , , — christer @ 8:54 am

Number of clicks since the box was put up on www.vg.no and www.nettby.no:

www.vg.no

  • Internet Explorer: 15949
  • Firefox: 1789
  • Opera: 1189
  • Google Chrome: 1311

www.nettby.no

  • Internet Explorer: 2279
  • Firefox: 493
  • Opera: 101
  • Google Chrome: 111

Totals

  • Internet Explorer: 18228 (78%)
  • Firefox: 2282 (10%)
  • Opera: 1290 (6%)
  • Google Chrome: 1422 (6%)

In total 23222 clicks have been made in the upgrade box.

February 19, 2009

Some numbers on the IE6 upgrade box on www.vg.no and www.nettby.no

Filed under: Technology, Work related — Tags: , , , , , , — christer @ 2:33 pm

The IE6 upgrade box has been available on www.vg.no and www.nettby.no for about 24 hours. Here are some statistics on which links users click on:

www.vg.no

  • Internet Explorer: 6093
  • Firefox: 497
  • Opera: 316
  • Google Chrome: 357

www.nettby.no

  • Internet Explorer: 742
  • Firefox: 357
  • Opera: 37
  • Google Chrome: 45

Totals

  • Internet Explorer: 6835 (81%)
  • Firefox: 854 (10%)
  • Opera: 353 (4%)
  • Google Chrome: 402 (5%)

In total www.vg.no and www.nettby.no have generated 8444 clicks in the upgrade box.

February 18, 2009

The biggest sites in Norway urge users with IE6 to upgrade (or switch)

Filed under: Technology, Work related — Tags: , , , , , , , — christer @ 1:35 pm

If you view http://www.vg.no/ (the biggest site in Norway and my employer) using Internet Explorer 6 or older you will get a message urging you to upgrade to a newer version. We also provide links to Firefox, Opera and Google Chrome.

Screenshot from www.vg.no

Screenshot from www.vg.no

finn.no (among the top 5 sites in Norway) also does this. I got a request from Finn’s Erlend Schei about this on Twitter yesterday and got the “GO” from our CEO earlier today to make it happen.

More sites will follow this example later today.

February 17, 2009

Zend_Controller_Action_Helper_Redirector wants to kill my session

Filed under: PHP, Technology — Tags: , , , , , , , — christer @ 10:48 pm

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 corrent, 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!

February 16, 2009

Uploaded some photos to Flickr

Filed under: Photography — Tags: , , , , — christer @ 12:44 am

Just uploaded some photos to my flickr account from Saturday when Geir, Gøril and myself went for a walk up to a lavvo in the woods.

Snow Borredalsdammen Lights

AOTW 7, 2009: Lily Allen – Alright, Still

Filed under: AOTW, Music — Tags: , , — christer @ 12:35 am

The award for week 7 goes to an album I listened to a lot last year: Alright, Still by Lily Allen. I always get in a good mood when listening to this album!

My favorite three songs are:

  • Knock ‘em out
  • LDN
  • Alfie

February 9, 2009

Caramel pudding w/coffee on diggmat.com

Filed under: In the kitchen — Tags: , , , , — christer @ 1:27 pm

A couple of weeks ago I made a caramel pudding w/coffee. I wrote a post about the process (in Norwegian) over at diggmat.com. If anyone wants it here in English, comment this post and I might add it.

Older Posts »

Blog at WordPress.com.