Zend Framework Bootstrapping Modules

I am working on a small API for bluesignal and i wanted a modular architecture. I have done this before using the Zend Framework. But this time i wanted a bit more control while loading the modules. And adding a Bootstrap class would seem like a good option. The only example i could find involved loading all bootstraps on every request. Which doesn’t seem like a good idea. So after reading through the Manual and some blog posts. I decided to give it s shot my self.

The structure i want looks like this.

The application.ini file has the following contents:

includePaths.library = APPLICATION_PATH “/../library”
bootstrap.path = APPLICATION_PATH “/Bootstrap.php”
bootstrap.class = “Bootstrap”
resources.frontController.moduleDirectory = APPLICATION_PATH “/modules”
resources.modules[] = “default”
resources.modules[] = “admin”

includePaths
This sets the applications local library location. Any shared code for this application goes here.

bootstrap.path & class
Define the location and type of the Bootstrap class.

resources
Define the modules location and create a list of modules.

The main Bootstrap class

application/Bootstrap.php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{

Load the config parameters for this application and set some debugging settings if needed.

    protected function _initConfiguration()
    {
    	$app = $this->getApplication();
    	$config = $app->getOptions();

    	if (APPLICATION_ENV == 'development') {
	    	    error_reporting(E_ALL & E_STRICT);
	    	    if (isset($config['phpsettings'])) {
		    	        foreach ($config['phpsettings'] as $setting => $value) {
		    		        ini_set($setting, $value);
		    	        }
	    	    }
    	}
    }

We need autoloading here because we are using a class from the application library. Right now this causes a problem. A notice is thrown

Warning: include_once(FrontController.php) [function.include-once]: failed to open stream: No such file or directory in Zend/Loader.php on line 147

The application responds fine. And this problem seems to be a recurring issue (ZF-7224, ZF-7550) for the framework. Until now i have not find a graceful fix for this. besides a small patch reversion.

    protected function _initAutoload()
    {
		    $autoloader = Zend_Loader_Autoloader::getInstance();
		    $autoloader->setFallbackAutoloader(true);

		    return $autoloader;
    }

Setup the controller to register the Bluess_Modules_Loader plug-in. And set the prefixDefaultModule parameter so we can prefix the default module controllers as well. Just for the sake of consistency. The Bluess_ namespace is part of my API. And can be changed at will.

   protected function _initController()
    {
    	$this->bootstrap('FrontController');
    	$controller = $this->getResource('FrontController');
       $modules = $controller->getControllerDirectory();
       $controller->setParam('prefixDefaultModule', true);

        $controller->registerPlugin(
               new Bluess_Modules_Loader($modules)
        );

        return $controller;
    }

Now the last method. which is a bit weird. And i am probably missing a key factor here. But if this method resource is not declared only the default module functions. When declared empty all modules function as they should. This would indicate that this method could be used to load the modules. But i haven’t found a way to achieve this yet. Except for loading all modules in a row. Which makes no sense for this purpose. So we leave it empty.

protected function _initModules()
    {
		// Call to prevent ZF from loading all modules
    }

The most important part here is the controller plug-in. This will be the place where module bootstraps are called from.

application/../library/Bluess/Modules/Loader.php

class Bluess_Modules_Loader extends Zend_Controller_Plugin_Abstract
{
	protected $_modules;

Setup the plug-in by passing the applications module list.

	public function __construct(array $modulesList)
	{
		$this->_modules = $modulesList;
	}

The dispatchLoopStartup method will be called on every request and will do the magic. Based on the current module name we create a new Zend_Application with the current modules config file module.ini. And we bootstrap it.

	public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
	{
		$module = $request->getModuleName();

		if (!isset($this->_modules[$module])) {
			throw new Exception("Module does not exist!");
		}

		$bootstrapPath = $this->_modules[$module];

		$bootstrapFile = dirname($bootstrapPath) . '/Bootstrap.php';
        $class         = ucfirst($module) . '_Bootstrap';
        $application   = new Zend_Application(
        	APPLICATION_ENV,
    		APPLICATION_PATH . '/modules/' . $module . '/configs/module.ini'
		);  

        if (Zend_Loader::loadFile('Bootstrap.php', dirname($bootstrapPath))
        	&& class_exists($class)) {
            $bootstrap = new $class($application);
            $bootstrap->bootstrap();
        }
	}
}

Now setup the default module. Once this is done it’s a nice example for further modules. Make sure the module has it’s own layout set.

application/modules/default/configs/module.ini

default.resources.layout.layout = “default”
default.resources.layout.layoutPath = APPLICATION_PATH “/modules/default/layout”

Setup the modules bootstrap and use it to set the modules model location.

application/modules/default/Bootstrap.php

class Default_Bootstrap extends Zend_Application_Module_Bootstrap
{
	protected $_moduleName = 'default';

	protected function _initConfiguration()
    {
		$options = $this->getApplication()->getOptions();

    	set_include_path(implode(PATH_SEPARATOR, array(
		    realpath(APPLICATION_PATH . '/modules/' . $this->_moduleName . '/models'),
		    get_include_path(),
		)));

		return $options;
    }
}

That’s all. Now make sure your layout is set correctly and the controllers are prefixed

application/modules/default/layout/default.phtml

echo $this->layout()->content;

application/modules/default/controllers/IndexController.php

class Default_IndexController extends Zend_Controller_Action
{

It took me a while to get this working like i had it in my mind. But it’s going the right way. If your interested in a working copy. You can download one here.

UPDATE

Matthew has a nice post about some do’s and don’ts concerning module based applications

24 Responses to 'Zend Framework Bootstrapping Modules'

Subscribe to comments with RSS or TrackBack to 'Zend Framework Bootstrapping Modules'.

  1. [...] Read this article: Zend Framework Bootstrapping Modules at Web Development and stuff… [...]

  2. [...] Zend Framework Bootstrapping Modules at Web Development and stuff… [...]

  3. Hi, I tried implementing it but its throwing an error, it can not find application.php hence giving “Fatal error: Class ‘Zend_Application’ not found”

    Mohammad Azhar

    9 Mar 10 at 11:39

  4. What version of ZF are you using?

    Thijs Lensselink

    9 Mar 10 at 19:01

  5. Hey Thijs , I found it the easiest way to achieve module based bootstrapping
    This code worked absolutely fine with version 1.8.4 but i want to use it for the latest version like 1.10….
    Hence we are not having ‘Application.php’ here.
    can you please help me out ?
    Thanks in advance!!!

    Mohammad Azhar

    10 Mar 10 at 07:58

  6. Hello Mohammad, I’ve checked the documentation. and Zend_Application should still be available in 1.10. I don’t know if my solution works for 1.10 though. I will take a look at it this evening.

    Thijs Lensselink

    10 Mar 10 at 08:35

  7. Thanks for the quick reply.
    :)

    Mohammad Azhar

    10 Mar 10 at 08:51

  8. Hey Thijs,
    Its Working fine with the other Versions as well.
    my bad!!! i was referring the wrong library.
    Thanks, i found it really useful.

    Mohammad Azhar

    10 Mar 10 at 14:41

  9. Great to see it worked out. And good that my post could be of some value. Happy hacking :)

    Thijs Lensselink

    10 Mar 10 at 18:37

  10. Hi,I tried implementing it . But I not familiar with Zend_Acl and Zend_Auth integration checking, please gets advice, thanks!

    Hr

    11 May 10 at 11:09

  11. $this->getInvokeArgs() not eligible configuration(application.ini) parameters ,why?

    Hr

    20 May 10 at 15:40

  12. Hey Thijs,

    can you please tell me how are we going to handle Exceptions here, coz handling exception (providing try catch) in Bootstrap will be a good practice and will remove our overhead of doing it at all places.

    Mohammad Azhar

    3 Sep 10 at 07:22

  13. Mohammed,

    We can catch Exceptions in the module loader plugin. So we can catch Exceptions locally in the module bootstraps and re-throw them.

    Thijs Lensselink

    5 Sep 10 at 13:06

  14. Hi Thijs, the plugin works for me perfectly, I have the module “default”, “admin” and “personal.” All is well.

    The problem I have is that in the module “admin” in the “configs” I created the file “routes.ini” and I want to load it from “admin / Bootstrap.php” but I load my routes.

    “admin/Bootstrap.php”
    =====================
    // This is my function
    protected function _initRoutes()
    { $frontController=Zend_Controller_Front::getInstance();
    $router=$frontController->getRouter();
    $router->addConfig(new Zend_Config_Ini(APPLICATION_PATH.”/modules/admin/configs/routes.ini”, APPLICATION_ENV), “routes”);
    }

    “admin/configs/routes.ini”
    ==========================
    [production]
    routes.adminlogout.type = “Zend_Controller_Router_Route_Regex”
    routes.adminlogout.route = “admin/(logout)”
    routes.adminlogout.defaults.module = “admin”
    routes.adminlogout.defaults.controller = “index”
    routes.adminlogout.defaults.action = “logout”

    I hope you can give me help with this, thanks.

    victor

    6 Oct 10 at 01:43

  15. Hello Victor,

    i didn’t use the module based structure posted here for a while. But i remembered i ran into the same issue some while back. So i had to take a look at what the code was trying to do.

    I wrote the module_loader plugin so had a bit more control over the module loading process. This is causing problems. It’s not possible to set the routing since the frontController is already instantiated.

    The solution is pretty simple though.

    In the application bootstrap comment out the initModules() method

    //protected function _initModules() { }

    Also comment out the module_loader plugin inside _initController()

    //$controller->registerPlugin(
    //new Bluess_Modules_Loader($modules)
    //);

    Now you can add routes to your module bootstraps like you are used to.

    protected function _initRoutes()
    {
    $front = Zend_Controller_Front::getInstance();
    $router = $front->getRouter();

    $foo = new Zend_Controller_Router_Route(
    ‘foo/:test’,
    array (
    ‘module’ => ‘default’,
    ‘controller’ => ‘index’,
    ‘action’ => ‘index’
    )
    );

    $router->addRoute(‘foo’, $foo);
    }

    Thijs Lensselink

    6 Oct 10 at 10:26

  16. Hi sir,

    Sir i done what you had mention above steps it coming but it is coming as blank page sir so please i don know please can you tell me about this sir please

    Thanks
    Bakkesh

    Bakkesh

    10 Dec 10 at 14:18

  17. Hee,
    The download link is broken, can you please fix it

    Silverbullet

    12 Mar 11 at 21:31

  18. Seems i forgot some files when i moved the site. Thanks for the heads up. The downloads should function properly again.

    Thijs Lensselink

    13 Mar 11 at 12:12

  19. Hi,
    I’m totally new to ZF. I tried to setup your example it works fine but still having a small problem. If you set error_reporting(E_ALL | E_STRICT); in your application/Bootstrap.php php warning message is displayed.

    Warning: include_once(FrontController.php) [function.include-once]: failed to open stream: No such file or directory in C:\wamp\www\rnd\library\Zend\Loader.php on line 146

    Warning: include_once() [function.include]: Failed opening ‘FrontController.php’ for inclusion (include_path=’C:\wamp\www\rnd\public/../application/../library;C:\wamp\www\rnd\library;.;c:\php\includes;C:\wamp\www\common\’) in C:\wamp\www\rnd\library\Zend\Loader.php on line 146

    I’m using the latest version of ZF. Any idea to solve it?

    Hans

    28 Apr 11 at 10:17

  20. Hey Hans,

    This is quite an old post. This could be an autoloader issue. It looks like the core ZF files are not found. But this might be caused by changes to ZF
    since i write the post. I will have a look at the code later today. But can’t promise anything.. Take a look on Matthew’s site for more inspiration.

    Thijs Lensselink

    28 Apr 11 at 12:38

  21. Moin,

    thanks for this post – though it’s old it was the only one who really helped me setting up a module structure. The only point I was struggeling with for hours and hours: Autoloader didn’t found the classes in forms and/or models etc. Solution: Uppercase the module name in the module’s Bootstrap file.

    sonja

    2 Aug 11 at 09:04

  22. Hi,

    Thanks for the tutorial and the sources.

    One things which doesn’t work using your solution and my architecture is when you need to load a class which is in another module.

    I solve this problem using :

    protected function _initModules() {
    // Call to prefent ZF from loading all modules
    $this->bootstrap('frontController');
    $front = $this->frontController;
    $autoloader = Zend_Loader_Autoloader::getInstance();

    $modules = $front->getControllerDirectory();
    $default = $front->getDefaultModule();
    foreach (array_keys($modules) as $module) {
    if ($module === $default) {
    continue;
    }

    $autoloader->pushAutoloader(new Zend_Application_Module_Autoloader(array(
    'namespace' => ucwords($module),
    'basePath' => $front->getModuleDirectory($module),
    )));
    }
    }

    In the application bootstrap.

    Sorry for my bad english writting…

    Thomas Dutrion

    30 Jan 12 at 00:38

  23. Thanks for the code addition. I’ll see if i can add that to the post soon. Your english is fine by the way.

    Cheers,
    Thijs

    Thijs Lensselink

    30 Jan 12 at 09:16

Leave a Reply

 
Stop ACTA