Evan Coury: Module-specific layouts in Zend Framework 2

11th May 2012

Evan Coury has posted Module-specific layouts in Zend Framework 2

First, I should point out that the title of this post is a bit of an intentional misnomer. There’s really no such thing as “module-specific” anything in ZF2, so what we’re really talking about is the topmost namespace of the controller being dispatched. So in the case of MyModule\Controller\SomeController, the topmost namespace would be MyModle. In most cases, this will be the name of a given module.

Here’s how you can easily switch the layout (or perform any other arbitrary logic) for a specific module in Zend Framework 2.0 (as of d0b1dbc92)

He shows how easy it is to register an event that is only triggered when an action belonging to the namespace of the current module is dispatched. If you need to change the layout for a given module, this is the best way to do it as of today.

His point that a Module is merely the namespace of the dispatch controller is very important to understand too!

Vagrant in Zend Framework 1

9th May 2012

I recently added support for vagrant to the Zend Framework codebase to enable easier testing. I was motivated by some work the joind.in folks have done to get a working development environment for joind.in development using Vagrant.

Vagrant is a fantastic tool that enables you to manage and run virtual machines from the command line, including automatic provisioning of them using puppet or chef. The really cool thing about it however from my point of view is that vagrant automatically sets up the VM with a folder called /vagrant that holds the code on your local hard drive from where you started the VM. This means that you can continue to edit your code in your local editor/IDE and test it within the VM easily.

I highly recommend checking it out.

ZF1's Vagrant set up

The Vagrant set up for ZF1 is designed for testing ZF1 against multiple PHP versions. As such it sets up a simple Ubuntu VM with the required toolchain for compiling PHP and provides a script called php-build.sh which will download and build any PHP 5.2, 5.3 or 5.4 version that you are interested in. I based this script on information in Derick Rethans' excellent Multiple PHP versions set-up article.

One thing that I discovered was that by default, the VM cannot create symlinks in /vagrant. The way to solve this is to add the following to the Vagrantfile:

  config.vm.customize [
    "setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root", "1"
  ]

I chose to use puppet to install the required packages and created a simple default.pp configuration. I'm sure this isn't optimal, but it works :)

Running ZF1 unit tests

The process to set up ZF1 to run unit tests in a VM using PHPUnit 3.4, follow these steps:

1. Install requirements for running the VM:

2. Checkout the ZF1 repository:

    $ svn checkout http://framework.zend.com/svn/framework/standard/trunk zf1-dev
    $ cd zf1-dev

3. Start the process by running Vagrant.

    $ vagrant up

This will take a long while as it has to download a VM image and then provision it. Once it has finished, it will exit and leave you back at the command prompt.

4. SSH into the VM

    $ vagrant ssh

Vagrant sets up key-less ssh connections to the VM that "just works" :)

5. Build a version of PHP.

    $ php-build.sh 5.3.11

This also takes a while as it compiles PHP for you! It also installs PHPUnit 3.4 as that's the version we need to unit test ZF1.

Each version of PHP that you compile is stored in /usr/local/php/{version number}. You can compile any version; I have 5.2.4, 5.2.12, 5.3.3 and 5.3.11 installed at the moment…

6. Select PHP to use:

   $ pe 5.3.11

pe is a handy shell function that Derick wrote that changes your PHP environment to whichever version your specify.

7. Run tests

   $ cd /vagrant/tests
   $ php runtests.php

Alternatively, you can run each component's tests individually:

   $ phpunit --stderr -d memory_limit=-1 Zend/Acl/AclTest.php
   $ phpunit --stderr -d memory_limit=-1 Zend/Amf/AllTests.php
   (etc...)

Obviously, you repeat steps 5 through 7 for each version of PHP you want to test on.

To stop your Vagrant VM, exit the SSH shell and type `vagrant halt` or `vagrant suspend`. For more details on controlling your Vagrant VM, I recommend Lorna's article

Running unit tests on Zend Framework 1 is now considerably easier!

Unit testing Zend Framework 1

30th April 2012

As part of our release process for Zend Framework 1.12, I've been working through the unit tests and running them on PHP 5.2.4 as it seems that recent changes weren't being tested with that version. This isn't totally surprising as Open Source contributors are, almost by definition, interested in new things and so are much more likely to be running PHP 5.4 rather than 5.2! This is, of course, a compelling reason for using continuous integration and I'm quite excited with Travis-CI and we are using it with ZF2.

Installing PHPUnit 3.4

The first challenge that I encountered was that ZF1's unit test are not compatible with PHPUnit 3.6. As there are over 14,000 ZF1 unit tests which have been written since 2006, there hasn't been much enthusiasm for rewriting them to be PHPUnit 3.6 compatible. (However, if someone wants to volunteer, please contact me!)

As I have PHPUnit 3.6 installed for testing other projects, I needed to install PHPUnit 3.4 side-by-side with version 3.6 This turns out to be relatively easy and has been documented by Christer Edvartsen in his article Running Multiple Versions of PHPUnit.

I ran into one problem with his instructions though and needed this code to be added to the top of phpunit:

set_include_path(implode(PATH_SEPARATOR, array(
    __DIR__ '/../share/php',
    '/usr/share/php',
    get_include_path()
)));

Having done this though, PHPUnit 3.4 works correctly and we can run ZF1 unit tests.

Running the ZF1 unit tests

To run ZF1's unit tests, you first need to check out the code from the Subversion repository.

svn co http://framework.zend.com/svn/framework/standard/trunk/

Due to the number of tests and the memory that they take up, you should run tests for each component individually.

The command to use is:

phpunit34 --stderr -d memory_limit=-1 Zend/{Component}/AllTests.php

Note:

  • --stderr pipes PHPUnit's output to stderr which means that any tests that rely on header() will work. (i,e. don't test Zend_Session without it!)
  • -d memory_limit=-1 will turn off PHP's memory_limit setting. The ZF1 unit tests use a lot of memory, so this is easiest.
  • The tests rely on being set up correctly. This is done using AllTests.php so don't forget this. Tests will fail if you forget!

All that needs to happen now is that any failing tests are fixed!

Evan Coury: Sharing a database connection across modules in Zend Framework 2

27th April 2012

Evan Coury has posted Sharing a database connection across modules in Zend Framework 2 » Evan's Blog

With the new modular infrastructure in Zend Framework 2, one of the most common questions will indoubitably be how to share a database connection across modules. Here’s a quick explanation of how to share your database connection across multiple modules in a way that can even allow you to use a single connection between Zend\Db, Doctrine2, and possibly even other database libraries / ORMs.

If you're using ZF2, this is a must-read.

An introduction to Zend\EventManager

23rd April 2012

Zend Framework 2's EventManager is a key component of the framework which is used for the core MVC system. The EventManager allows a class to publish events that other objects can listen for and then act when the event occurs. The convention within Zend Framework 2 is that any class that triggers events composing its own EventManager.

Terminology

For the purposes of this article, we will use these definitions:

  • An EventManager is an object that holds a collection of listeners for one or more named events, and which triggers events.
  • An event is an action that is triggered by an EventManager.
  • A listener is a callback that can react to an event.

When the EventManager's trigger() method is called, all the listeners attached to the event are called in turn and are passed an Event object which contains a target and additional parameters as required.

Triggering events

A typical use-case for EventManager is to trigger events within a mapper class:


use Zend\EventManager\EventManager;
use Zend\EventManager\Event;

class PhotoMapper
{
    public $events;

    public function events() 
    {
        if (!$this->events) {
            $this->events = new EventManager(__CLASS__); 
        }

        return $events;
    }

    public function findById($id)
    {
        $this->events()->trigger(__FUNCTION__ '.pre'$this,
            array('id' => $id));

        // retrieve from database and create a $photo entity object

        $this->events()->trigger(__FUNCTION__ '.post'$this, 
            array('photo' => $photo));

        return $photo;
    }

    //  class continues...

Firstly, note that, by convention, the EventManager instance is called $events and we use a method called events() and that will instantiate the EventManager for us when needed. This is a very common code pattern in ZF2 classes; so common in fact that the ZfcBase module contains a class called ZfcBase\EventManager\EventProvider that you can extend from to provide you with this functionality.

Within our mapper's findById()method, we trigger two events: before and after the actual work is done. When triggering an event, we supply the event's name, the target class and an array of data that the listeners may be interested in.

By convention, we use the name of the method as our event's name. This makes it predictable for users of the class. Also, by convention, if there two events, then we append '.pre' and '.post' to the event name. the target class is usually the class that triggered the event, and so we pass in $this. Finally, we can provide some additional data to the listeners. In this particular method, we supply the id before we retrieve (as that's all we have!) After retrieval, we have the entity object itself, so we supply that to the listener callbacks.

Listening for an event

A listener is just any PHP callback function which takes a single argument, $event, which is an instance of an Event. The listener is attached using event manager's attach() method. For example, we could attach a logger to our mapper's findById.pre event like this:


$photoMapper->events()->attach('findById.pre', function(Event $event) {
    $message "Trying to retrieve photo: " $event->getParam('id');
    MyLogger::log($message);
});

The $event parameter that is passed to the listener method has three user methods:

  • getName() - Useful when the same listener is attached to multiple events
  • getTarget() - Usually the class that trigger the event
  • getParams() - Retrieve the parameters sent when the event was triggered.

Priority
When attaching a listener to an event, you can specify the priority in the third parameter. The larger the number, the earlier the listener is called. i.e. for a listener that you want to be executed first, set the priority to say 1000. For a listener that you want to be executed last, set the priority the -1000. The default priority is 1 and listeners with the same priority are executed in order or priority.

Short circuiting

The return value from trigger() is a ResponseCollection which is a collection of all the returned results from every listener that has been called in reverse order. i.e. the result from the last listener is first in the collection.

As this result is passed back to trigger, we can use it to stop processing of the listeners if something interesting happens. This is known as short-circuiting and we do this by attaching a callback as the last parameter to trigger().

A good example of this is a caching solution on our findById() method. We change it so that it looks like this:


    public function findById($id)
    {
        $results $this->events()->trigger(__FUNCTION__ '.pre'$this, 
            array('id' => $id),
            function ($result) {
                return ($result instanceof Photo) ? true false;
            }
        );

        if ($results->stopped()) {
            // we ended early. Result returned from last listener was an entity
            $photo $results->last();
            return $photo;
        }        

        // retrieve from database and create a $photo entity object

        $this->events()->trigger(__FUNCTION__ '.post'$this,
            array('photo' => $photo));

        // save $photo to cache here

        return $photo;
    }

We have added a callback to our trigger() call that returns true if the result returned by a listener is an instance of Photo. This means that we can attach a listener that tries to read a cache for the id. If it finds one, then it returns the cached object. The trigger callback notices and returns true and the ResponseCollection's stopped flag is set to true.

We then test for stopped() and if it has been set, $results->last() contains the Photo entity that we need.

SharedEventManager

It's common that you may want to set up listeners before the objects with the event manager you need has been instantiated. Good examples are listeners that log or cache. ZF2 comes with the concept of the SharedEventManager. Essentially you attach listeners to the SharedEventManager and then when the particular event manager's trigger() is called, it will also call all the listeners attached to the shared event collection.

There are two ways to access the SharedEventManager: DIC injection into the object where you need it or via a static class called (rather unimaginatively) StaticEventManager. The exact details of how the injection will work are up in the air at the moment as it's expected that some additional defaults will be provided to make it easier in beta 4 and the use of the StaticEventManager will become deprecated.

In either case, the way to attach a listener to the SharedEventManager is the same:


// $events is a Zend\EventManager\SharedEventManager instance either injected
// or acquired using $events = StaticEventManager::getInstance();

$events->attach('Gallery\Model\PhotoMapper''findById.pre', function(Event $event) {
    $message "Trying to retrieve photo: " $event->getParam('id');
    MyLogger::log($message);
});

Note that the only change to the attach call is the addition of a new first parameter which is the name of the class where the event manager is to be found. In this case, it's Gallery\Model\PhotoMapper. The other three parameters are the same: event name, callback and priority.

In terms of execution of listeners with the same priority, those attached directly are executed before those attached via the SharedEventManager.

Summary

This article covers how to use the EventManager in your application. The biggest advantage to using EventManager in your application is that you can decouple classes that really shouldn't be coupled together which makes your code easier to write and maintain. In fact, this is so useful, that ZF2's MVC system makes heavy use of EventManager; The module manager, routing, dispatching and the view layer are all implemented using listeners attached to events.