Zend Framework: module specific frontcontroller plugins
Here is another situation I have run into while trying to build a modular application: sometimes you need to run a frontcontroller plugin ONLY when a specific module is requested. An example could be authentication. Say I have a public site and an admin section. The most obvious solution is to enable the authentication plugin only when the admin section is requested. This is something that is not possible with Zend_Application and the frontcontroller resource. So, as I did for loading module specific layouts, I wrote a frontcontroller plugin for registering module specific frontcontroller plugins: the Rexus_Controller_Plugin_RequestedModulePluginLoader. It works together with a Zend_Application resource called Rexus_Application_Resource_Moduleplugins. Let’s get started.
Usage Example
First we load the frontcontroller plugin Rexus_Controller_Plugin_RequestedModulePluginLoader in the main application.ini:
resources.frontController.plugins.pluginloader = Rexus_Controller_Plugin_RequestedModulePluginLoader;
Then we use the Zend_Application resource Rexus_Application_Resource_Moduleplugins in our config file to load the the authentication plugin only for our module.
If you use my ModuleSetup resource as described in module config, you can set the module plugin options in the module.ini for that module :
resources.moduleplugins.auth = "Rexus_Controller_Plugin_Authentication"
or, you can set the module plugins config in your application.ini normally, as described in the Zend Framework reference:
modulename.resources.moduleplugins.auth = "Rexus_Controller_Plugin_Authentication"
The result is that the frontcontroller plugin Rexus_Controller_Plugin_Authentication is only loaded if the module ‘modulename’ is requested.
Code
class Rexus_Controller_Plugin_RequestedModulePluginLoader extends Zend_Controller_Plugin_Abstract { protected $_modulePlugins = array(); public function registerFrontControllerPlugin($module, $pluginName) { if (array_key_exists($module, $this->_modulePlugins) && is_array($this->_modulePlugins[$module])) { array_push($this->_modulePlugins[$module], $pluginName); } else { $this->_modulePlugins[$module] = array($pluginName); } } public function routeShutdown(Zend_Controller_Request_Abstract $request) { if (isset($this->_modulePlugins[$request->getModuleName()])) { $frontController = Zend_Controller_Front::getInstance(); foreach ($this->_modulePlugins[$request->getModuleName()] as $pluginName) { $frontController->registerPlugin( new $pluginName); } } } }
Just drop this class in your library and register the plugin with the frontcontroller as shown above to use it.
Note that this plugin does its work on routeShutdown. On routShutDown is the earliest hook in the dispatch loop where the request has been parsed and routed. This is important because all plugins we register with this plugin can subsequently only be registered with the frontcontroller as early as the next step after the one on which this plugin is executed.
Next we need the code for the Zend_Application resource Rexus_Application_Resource_Moduleplugins:
class Rexus_Application_Resource_Moduleplugins extends Zend_Application_Resource_ResourceAbstract { public function init() { $bootstrap = $this->getBootstrap(); $bootstrap->bootstrap('frontcontroller'); $front = $bootstrap->getResource('frontcontroller'); $pluginLoader = $front->getPlugin('Rexus_Controller_Plugin_RequestedModulePluginLoader'); $options = $this->getOptions(); foreach ($options as $pluginName) { $pluginLoader->registerFrontControllerPlugin( strtolower($this->getBootstrap()->getModuleName()), $pluginName); } } }
Where is the class “Rexus_Controller_Plugin_Authentication” ? Thanks.
I’m not too sure how you load Rexus_Application_Resource_Moduleplugins, is it in the application.ini?
Just in case, this is how I got around the module specific loading of an authentication mechanism:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initAuth()
{
$frontController = $this->bootstrap(‘frontController’)->frontController;
$frontController->registerPlugin(new Cantah_Controller_Plugin_Auth(array(‘modules’=>’admin’)));
}
Then in the preDispatch of the Cantah_Controller_Plugin_Auth plugin I check if a module is set, and if the module is admin, do the appropriate Zend_Auth checking.
i subscribe to what leon said.. how do I load the Rexus_Application_Resource_Moduleplugins ?
apache crashes if i use only the code above.. something is missing
ok, i managed to load the moduleplugins resource.
using ZF 1.9 the class Rexus_Application_Resource_Moduleplugins doesn’t work at all..
php crashes because of this line $this->getBootstrap()->getModuleName()
This didn’t work for me either until I made the following modifications:
1. Make sure you’ve created a bootstrap class for your module extending from Zend_Application_Module_Bootstrap.
2. Modify Rexus_Application_Resource_Moduleplugins replacing the foreach loop in init() with the following code:
$bootstrap = $this->getBootstrap();
if ($bootstrap instanceof Zend_Application_Module_Bootstrap) {
foreach ($options as $pluginName) {
$pluginLoader->registerFrontControllerPlugin(
strtolower($this->getBootstrap()->getModuleName()), $pluginName);
}
}
The problem is the code is expecting a getModuleName method on all the bootstrap classes (including the default), but this method only exists on Zend_Application_Module_Bootstrap.
My solution, after reading this article and few others, was to subclass Zend_Application_Resource_Modules and override the init() method. Iterate through all modules, getting the options from the config files, merge them to the application’s bootstrap options passing it as an array to setOptions(array($moduleName => $moduleOptions)). This is done before parent::init() so the bootstrap’s options get updated before each module bootstrap, since all module bootstraps look for their configs inside the application’s bootstrap configuration.