LogoPhly, boy, phly
the weblog and site of Matthew Weier O'Phinney

Thursday, March 11. 2010

Module Bootstraps in Zend Framework: Do's and Don'ts

I see a number of questions regularly about module bootstraps in Zend Framework, and decided it was time to write a post about them finally.

In Zend Framework 1.8.0, we added Zend_Application, which is intended to (a) formalize the bootstrapping process, and (b) make it re-usable. One aspect of it was to allow bootstrapping of individual application modules -- which are discrete collections of controllers, views, and models.

The most common question I get regarding module bootstraps is:

Why are all module bootstraps run on every request, and not just the one for the requested module?

To answer that question, first I need to provide some background.

When it comes to modules, we have three typical problems or requirements:

  • Ensuring that module resources -- models, view helpers, etcc. -- are available elsewhere in the application
  • Initializing module-specific resources, such as routes, navigation elements, etc.
  • Running code specific to this module (selecting a specific layout, selecting a specific database adapter, etc)

Zend_Application answers the first two questions. By default, it sets up a resource autoloader with targets for all the common resources (models, forms, view helpers and filters, DbTable objects, etc.), and also allows you to specify resources to load at bootstrap time.

And that's where things get interesting.

The basic workflow of a ZF MVC request is as follows:

  1. Application bootstrapping
  2. Routing
  3. Dispatch

Zend_Application takes care of only the first item in that list, bootstrapping. At that time, we have no idea what the request actually is -- that happens during routing. It's only after we have routed that we know what module, controller, and action were requested.

So, what's the point of your module bootstraps, then?

Bootstrapping is for getting ready

As noted earlier, Zend_Application is intended for bootstrapping your application. This means "getting it ready to execute". The idea is to get all your dependencies in order so that once you're ready to route and/or dispatch, everything the application may need is in place.

When it comes to modules, the sorts of things you need to have in place before routing and dispatch include:

  • Autoloading support for module resources. This is so that, if you need to, code from anywhere in your application can make uses of the module's resources. Examples include access to view helpers, access to models, access to forms, etc. Autoloading of resources is enabled by default
  • Setting up module-specific routes. How can you get to the module's controllers in the first place? What routes does it answer to? The time to provide this information is during bootstrapping, before routing occurs.
  • Module-specific navigation elements. This usually goes hand-in-hand with your routes (most Zend_Navigation pages utilize named routes).
  • Setting up module-specific plugins. If there is functionality your module may be needing to enable as part of the routing/dispatch cycle, set this up in plugins and attach them to the front controller.

This last point is the key to understanding the appropriate place to do module-specific initializations -- that is, initialization and/or bootstrapping that should only be done if the module is matched during routing.

Use plugins to do specific initializations

To re-iterate: if you have initialization tasks that should only be done if the module is the one being executed, do it in a front controller plugin or action helper.

If doing it in a front controller plugin, do these initializations any time after routing, as this is the only time you'll know what the module is. For general tasks like switching the layout, routeShutdown() or dispatchLoopStartup() are the right places. Simply compare the module in the request object to your module, and bail early if they don't match.


class Foomodule_Plugin_Layout extends Zend_Controller_Plugin_Abstract
{
    public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
    {
        if ('foomodule' != $request->getModuleName()) {
            // If not in this module, return early
            return;
        }

        // Change layout
        Zend_Layout::getMvcInstance()->setLayout('foomodule');
    }
}
 

Your module bootstrap would take care of registering this plugin with the front controller:


class Foomodule_Boootstrap extends Zend_Application_Module_Bootstrap
{
    protected function _initPlugins()
    {
        $bootstrap = $this->getApplication();
        $bootstrap->bootstrap('frontcontroller');
        $front = $bootstrap->getResource('frontcontroller');

        $front->registerPlugin(new Foomodule_Plugin_Layout());
    }
}
 

To keep things simple, and to reduce the performance overhead of having a lot of plugins, you might want to create a single plugin that performs all initialization; the Facade pattern is a good one to use here.

If using action helpers, the idea is the same -- the only difference is that you register with the action helper broker, and will likely do your matching in a preDispatch() hook.

Isn't there a better way to do this?

Yes, likely there are better ways to accomplish this. The true problem is that modules are really second-class citizens in ZF currently. There are a few neat ideas floating around:

  • Kathryn's Active module config
  • Jeroen's Moduleconfig
  • Matthijs' ModuleConfig
  • Pádraic and Rob's Module Configurators proposal

For 2.0, we'll be analyzing the situation and seeing if we can come up with a way to make module's first-class citizens in ZF. My hope is that this will allow users to start sharing modules easily -- which can foster a more "plugin"-like approach to building websites, and lead to collaboration on oft-needed site functionality (such as modules for blog, news, contact, etc.).

In the meantime, hopefully this post has helped shed some light on how module configuration currently works, and provides some tips and techniques on how to setup your application to make use of module-specific resources and initialization.

Updates

  • 2010-03-12: added link to Paddy's proposal
Posted by Matthew Weier O'Phinney in PHP at 11:55 | Comments (11) | Trackback (1)
Defined tags for this entry: mvc, php, zend framework
Related entries by tags:
Autoloading Benchmarks
Applying FilterIterator to Directory Iteration
Running mod_php and FastCGI side-by-side
Creating Zend_Tool Providers
State of Zend Framework 2.0

Trackbacks
Trackback specific URI for this entry

Module Bootstraps in Zend Framework: Do's and Don'ts - phly, boy, phly
I see a number of questions regularly about module bootstraps in Zend Framework, and decided it was time to write a post about them finally. In Zend Framework 1.8.0, we added Zend_Application, which is intended to (a) formalize the bootstrapping process, and (b) make it re-usable. One aspect of it was to allow bootstrapping of individual application modules -- which are discrete collections of controllers, views, and models.
Weblog: abcphp.com
Tracked: Mar 12, 01:00

Comments
Display comments as (Linear | Threaded)

Excellent Post Matthew! This is what I was looking for the other night!
#1 Jon Whitcraft (Link) on 2010-03-11 15:21 (Reply)
Jon is right, this is an excellent post! I really like the depth of your post and the background experience you have with the Zend Framework. It's always very helpful to read your post to get more informations about ZF topics.

Especially this Bootstrap things are really complex, I frequently forward those post (this one, too!) to our team members.. ;-)

Thanks!
#2 Ben (Link) on 2010-03-12 02:25 (Reply)
There is a proposal also for this:

http://framework.zend.com/wiki/pages/viewpage.action?pageId=16023853
#3 Keith Pope on 2010-03-12 08:44 (Reply)
Thanks -- somehow missed that one. I've updated the entry to link to it as well.
#3.1 Matthew Weier O'Phinney (Link) on 2010-03-12 08:57 (Reply)
"Why are all module bootstraps run on every request, and not just the one for the requested module?"

How comes there are so many people forgetting that PHP is a script language...
#4 Nicolas BUI on 2010-03-12 20:04 (Reply)
Excellent post Matthew! This article brings some light into the dark abysses of module bootstrapping!
Thanks.
#5 Dennis Winter (Link) on 2010-03-13 05:39 (Reply)
Hi Matthew,

Thanks for this post, it's always nice to get the info right from the source!

I have been using the standard module bootstrap class functionality to set module configuration, either through the usage of set-, and getOptions or through the usage of resource plugins (module bootstrap resource container/registry) With this I have been able to easily configure modules but I have yet to see an easy (read easier) way to access those configurations other than through:

$front = Zend_Controller_Front::getInstance();
$bootstrap = $front->getParam('bootstrap');
$this->_bootstrap = $bootstrap->getResource('modules')->offsetGet('#MODULENAME#');

$options = $this->_bootstrap->getOptions()
//...
etc.

This above is all needed to access the module bootstrap class and it's registered options and/or resources!

To me going about module configuration this way feels very intuitive plus there are no dependencies from the main application a module resides in. I Just need a better handle on it, and I am perfectly happy.

Any ideas?

Here you can see what I mean have been working on:
http://www.sreknord.net/blog/zend-framework/zend-framework-module-config-the-easy-way
#6 Leonard Dronkers (Link) on 2010-03-14 05:43 (Reply)
Hi, Matthew.

First of all thanks for your insightful posts - I've been following your writings for more than a year now and it is indeed a great help in learning framework's best practices.

One thing i want to notice regarding the approach of using plugins extending Zend_Controller_Plugin_Abstract - i've been doing so since your post on frontcontroller pugins at zend's devzone some time ago. But unfortunately i found it really hard to write tests if application/module initialization is handled in frontcontroller plugin. It seems the plugins don't get run in testing environment since we don't do application->run() and only application->bootstrap().

I've asked on nabble a few times but still no response left me in confusion on how i can do testing of controllers that depend on frontcontroller plugin. So my controllers still remain untested. Maybe you can suggest something?

Dmitry.
#7 Dmitry on 2010-03-14 07:55 (Reply)
I made a change to the ControllerTestCase late in the 1.9 series that should help this -- if a Zend_Application instance is used as the bootstrap, dispatch() will now call $application->run(). This should ensure that everything works correctly.

If you're up-to-date with your ZF installation and still have issues with this, please open an issue in the ZF issue tracker, with a reproduce case.
#7.1 Matthew Weier O'Phinney (Link) on 2010-03-14 18:17 (Reply)
A poor man's alternative might be to explore tweaking the dispatcher class to test for individual action controller classes/files coupled with the path delimiter for organized sub directories and files. Although it doesn't have the slightly more desirable module status it does alleviate having to additionally pre-configure each of these components primary resources and locations (view paths for example), as shared/accessible resources, since they're all within the same module.
#8 Greg on 2010-03-15 20:31 (Reply)
Hey Matthew,

Nice post! Thank you.
This approach is not quite working for me, however.
I have a multimodule application. So I do the following:

I register this plugin in my bootstrap:
V3_Plugin_ModuleLoader

and in the routeShutdown() method of this plugin I register an action helper like: Default_Plugin_Loader or Admin_Plugin_Loader(depends on the module in the request) and then I do the bootstrap of the module in this action helper.

Now my action helper is in the plugin directories of the modules. My question is: Is that okay? Is there a special place in the modular directory layout of ZF where to put action helpers.

I just like to follow the ideas of the devs of ZF.

Thanks! Vlado
#9 Vlado on 2010-03-22 05:35 (Reply)

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
 
  • Home
  • Resume
  • Blog
  • Phly PEAR Channel
  • Twitter
  • Contact Me
  • About this site

ZCE

Zend Education Advisory Board Member

Add to Technorati Favorites

Calendar

Back September '10
Mon Tue Wed Thu Fri Sat Sun
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      

Quicksearch

Links

  • PHLY - PHp LibrarY
  • Planet PHP
  • Zend Framework, where I'm project lead
  • Sebastian Bergmann
  • Cal Evans
  • Shahar Evron
  • Paul M. Jones
  • Bill Karwin
  • Mike Naberezny
  • Fabien Potencier
  • Ben Ramsey
  • Derick Rethans
  • Ralph Schindler
  • Marco Tabini

Archives

September 2010
August 2010
July 2010
Recent...
Older...

Categories

XML Linux
XML Personal
XML Aikido
XML Family
XML Programming
XML Dojo
XML Perl
XML PHP

All categories

Syndicate This Blog

XML RSS 0.91 feed
XML RSS 1.0 feed
XML RSS 2.0 feed
ATOM/XML ATOM 0.3 feed
ATOM/XML ATOM 1.0 feed
XML RSS 2.0 Comments

Show tagged entries

xml apache
xml best practices
xml books
xml conferences
xml cw09
xml decorators
xml dojo
xml dpc08
xml file_fortune
xml git
xml linux
xml mvc
xml oop
xml pear
xml perl
xml personal
xml php
xml phpworks08
xml programming
xml rest
xml ubuntu
xml vim
xml webinar
xml zendcon
xml zendcon08
xml zendcon09
xml zend framework
© 2004 - present, Matthew Weier O'Phinney
matthew-web <at> weierophinney.net