Active Module Based Config with Zend Framework

January 4, 2010 in Zend Framework

I’ve recently taken to using Zend Framework for a project that I needed to bring up to date. I won’t go into the pros and cons of choosing a framework as there are many much more qualified people who have done a much better job of this subject than I would or could. So Instead I bring to you How I managed to get Active Module Based Configuration within Zend Framework.

The Problem

The Concept I wanted to achieve was to have unique Configuration based upon the module that was active. The Issue with this is that the Bootstrap files and the _init functions for ALL modules are called with no bias as to which module is active. Thus if you created a 3 modules wanted to make menu alterations in one, those alterations will be applied to all. I also wanted to have a a system where if i added extra modules i could just add extra functions to the bootstrap file and it would work in a similar way.

With this in mind, I set about trying to figure out the solution.

New Version

Though this version still works and you should read through the code to see how to implement the plugin, there is a new version available at:
http://binarykitten.me.uk/dev/zend-framework/296-active-module-config-v2.html
Please refer to this as the latest version.. Thanks

—– Original Post —–

Solution 1 – Failure

With the Idea that no-body is perfect, including myself. My 1st attempt ended in failure. This attempt was to modify/extend the Bootstrap class to add extra functions to the resources list.. In the end I couldn’t determine if the Module bootstrap was the active one. ok So Attempt 1 was a failure, onto the next

Solution 2 – Success!

A quick conversation with Matthew Weier O’Phinney (@weierophinney) pointed me in the direction of Controller plugins and the routeShutdown method, as after the route had been finished which module was active would be able to be discerned.
At this point I must apologise to Pieter Kokx ( @kokxie ) who I had a small disagreement with in the #zftalk channel. Pieter had done his best to point me down this route to start with, though being a stubborn mule I am refused to see the quality and precision of his comments.
Thank you both for your help here.

The way that this works is that is scans the active modules bootstrap for functions starting with activeInit or modulenameInit just like the _init functions but these would only be called if the module is active.
I was successfully able to create the plugin and trigger the functions, unfortunately it was triggering/calling them in a static sense.. which meant that standard _init style code wouldn’t work. Luckily with a little digging in the source of the framework i found a storage of the modules and their initiated bootstrap classes. Lucky Me! So finally we call the methods within the right context.

So Here it is the Final Code, Please do comment, I learn from people as I hope that others can learn from me.

I’ve used the “Namespace” of BinaryKitten here. If you want to use a different “Namespace” Replace BinaryKitten with what you want.
The “Namespace” allows for the use of the BinaryKitten folder within the Libray Folder.
Remember the Controller Plugin should go in the right place for your application, if you are using the autoloader you can add the “Namespace” to be autoloaded via your application.ini

autoloadernamespaces[] = "BinaryKitten"

First off we have the Controller Plugin.
This should go into the “Namespace” folder within the Library Folder and should be called ModuleConfig.php

<?php
class BinaryKitten_ModuleConfig extends Zend_Controller_Plugin_Abstract
{
    public function routeShutdown(Zend_Controller_Request_Abstract $request)
    {
        $frontController = Zend_Controller_Front::getInstance();
        $bootstrap =  $frontController->getParam('bootstrap');
        $activeModuleName = $request->getModuleName();
        $moduleList = $bootstrap->modules;

        $moduleInitName = strtolower($activeModuleName)."Init";
        $moduleInitNameLength = strlen($moduleInitName);

        if (isset($moduleList[$activeModuleName])) {
            $activeModule = $moduleList[$activeModuleName];

            $bootstrapMethodNames = get_class_methods($bootstrap);
            foreach ($bootstrapMethodNames as $key=>$method) {
                $runMethod = false;
                $methodNameLength = strlen($method);
                if ($moduleInitNameLength < $methodNameLength &&
                    $moduleInitName == substr($method, 0, $moduleInitNameLength)) {
                    call_user_func(array($bootstrap,$method));
                }
            }
        } else {
            $activeModule = $bootstrap;
        }

        $methodNames = get_class_methods($activeModule);
        foreach ($methodNames as $key=>$method) {
            $runMethod = false;
            $methodNameLength = strlen($method);
            if (10 < $methodNameLength && 'activeInit' === substr($method, 0, 10)) {
                $runMethod = true;
            } elseif ($moduleInitNameLength < $methodNameLength &&
                    $moduleInitName == substr($method, 0, $moduleInitNameLength)) {
                $runMethod = true;
            }
            if ($runMethod) {
                call_user_func(array($activeModule,$method));
            }
        }
    }
}

Next we need to make sure the Controller Plugin is loaded.
We can do this in one of two ways. Either in the Application Bootstrap via an _init function

public function _initControllerPlugins()
{
    $plugin = Zend_Controller_Front::getInstance()->registerPlugin(
        new BinaryKitten_ModuleConfig()
    );
}

*– Or –*

We can add a line to the application.ini

resources.frontController.plugins.BKModuleConfig = "BinaryKitten_ModuleConfig"

Finally some example init code from the module bootstrap.
Please remember that the activeInit*() Functions need to be public for this to work properly

class Default_Bootstrap extends Zend_Application_Module_Bootstrap {
    public function activeInitMenus() {
        $layout = $this
                    ->bootstrap('layout')
                    ->getResource('layout');

        $view = $layout->getView();
        $config = new Zend_Config_Xml(APPLICATION_PATH.'/configs/navigation_default.xml',"menu");
        $navigation = new Zend_Navigation($config);
        $view->navigation($navigation);
    }
    public function activeInitDoSomethingElse() {
        /* some other code */
    }
    public function defaultInitSomething() {
    	/* more code */
    }
}

We can also add module inits to the application bootstrap like so:

    public function modulenameInitFunction() {
    	/* place code here */
    }

Where modulename is the lowercase version of the Modules name, eg if you have the Admin module, then you would use:

    public function adminInitFunction() {
    	/* place code here */
    }

Hopefully someone will find this code useful.

[ Edit January 5th 2010 ]
Thanks to:
Matthew Weier O’Phinney for pointing out places for update.
Rob Allen (@Akrabat) for the info that the plugin could be loaded via the application.ini
Elizabeth Marie Smith and Matthew Turland for questioning the use of the Reflection.

  • Updated the Bootstrap code to properly define the functions as Public
  • Removed the reflection as this wasn’t actually required any more.
  • Updated Post to make clean up the order of things and to properly designate that the BinaryKitten is the “Namespace”
    “Namespace” is used to reference that we’re not using PHP5.3 Namespaces, but the Namespaces within the Zend Framework.

[ Edit January 11th 2010 ]
Thanks to:
septem for pointing out a typo where i had $boostrap instead of $bootstrap
Gerard Roche for pointing out that by default, the default module doesn’t require a module bootstrap (in my code it has one)

  • Updated to fix the typos
  • Added in a quick check to see if the module exists in the modules list of bootstraps
  • Removed the _ from the function name that it searches for, this should please the people who are adamant over the Zend Coding Standards
  • Added in the functionality to have $modulenameInit() functions as well in both active module bootstrap and the application bootstrap

[ Edit February 13th 2010 ]
Ran the code through codesniffer against the Zend Standard supplied.. updated so no errors found. 3 Warnings are left .. they are as follows:
22 | WARNING | Line exceeds 80 characters; contains 83 characters
35 | WARNING | Line exceeds 80 characters; contains 84 characters
38 | WARNING | Line exceeds 80 characters; contains 83 characters
Don’t think it’s worth the change for 3/4 characters