Initializing only one module per request in Zend_Framework.
Posted by: adji in Zend FrameworkZend Framework recently introduced the concept of modules so that your applications become more manegable. The application on startup will load the resources defined in the main application.ini file. To configure each module there is a resource called modules that calls the bootstraping code for each module on every request. If you want to load resources on a per module basis you need to write a bootstrap for each module. This can also generate clashes if two modules try to load the same resource. Additionally this process can have a negative impact on your applications performance.
In this blog I propose a different strategy: I created a controller action plugin that will load resources dynamically using the main bootstraping code. The idea is that after the routes are parsed and before the action is executed this code will load the module’s config file and call the bootstrap again so that it will load the resources defined in this config.
This method has several advantages: only the resources for the module that is being called are loaded. Becuase only one config file is read there is no possiblity of clashes.
Here’s the code:
/**
* Bootstraps the module used in the current request
* It merges the options from main application.ini with the options from a
* module specific application.ini.
* I’m using the fact that a resources are loaded only once, so a resource
* declared in main application.ini is initialized there. If you wish to use some resource
* in every module use the key default (see bellow).
*
* The keys default and disable in the main application.ini have a special meaning.
* default: It is merged initally so a key default.resources.xxx is added as resource.xxx
* so it load the resource xxx as a default for all modules
* disable: It is used to disable keys after merging so a key disable.resources.xxx disables
* this resource in all modules.
*/
class Easytech_Controller_Plugin_ModuleBootstrap extends Zend_Controller_Plugin_Abstract
{
const MODULE_APPLICATION_INI = "configs/application.ini";
protected $_bootstrap;
protected $_autoloader;
public function dispatchLoopStartup( Zend_Controller_Request_Abstract $request )
{
$moduleName = $request->getModuleName();
if ( empty( $moduleName ) ) {
$moduleName = ‘default’;
}
$front = Zend_Controller_Front::getInstance();
$moduleDir = $front->getModuleDirectory( $moduleName );
if ( empty( $moduleDir ) ) {
$moduleDir = APPLICATION_PATH;
}
// Para ser usado en los ini.
define(‘MODULE_PATH’, $moduleDir );
define(‘MODULE_NAME’, $moduleName );
// Create a autoloader for this module
$autoloader = new Zend_Application_Module_Autoloader(array(
‘namespace’ => $moduleName,
‘basePath’ => $moduleDir,
));
$this->_bootstrap = $front->getParam(‘bootstrap’);
$globalOptions = $this->_bootstrap->getOptions();
// Default options from main application.ini
$options = array();
if ( isset( $globalOptions[‘default’] ) ) {
$options = $globalOptions[‘default’];
}
if ( isset( $globalOptions[ $moduleName ] ) ) {
$options = $this->_bootstrap->mergeOptions($options, $globalOptions[ $moduleName ]);
}
$applicationFile = $moduleDir . DIRECTORY_SEPARATOR . self::MODULE_APPLICATION_INI;
if ( isset( $globalOptions[ ‘application_file’ ] ) ) {
$applicationFile = $moduleDir . DIRECTORY_SEPARATOR . $globalOptions[ ‘application_file’ ];
}
$options = $this->_bootstrap->mergeOptions( $options, $this->_loadConfig( $applicationFile ) );
if ( isset( $globalOptions[‘disable’] ) ) {
$options = $this->unsetOptions($options, $globalOptions[‘disable’] );
}
$this->_bootstrap->setOptions( $options );
$this->_bootstrap->bootstrap();
}
/**
* Unset options recursively.
*
* @param array $array1
* @param mixed $array2
* @return array
*/
public function unsetOptions(array $array1, $array2 = null)
{
if (is_array($array2)) {
foreach ($array2 as $key => $val) {
if ( ! isset( $array1[ $key ] ) ) {
continue;
}
if ( is_array( $array2[ $key ] ) ) {
$array1[ $key ] = $this->unsetOptions( $array1[ $key ], $array2[ $key ]);
} else {
unset( $array1[ $key ] );
}
}
}
return $array1;
}
/**
* Based heavily on Zend_Application->_loadConfig
* Load configuration file of options
*
* @param string $file
* @throws Zend_Application_Exception When invalid configuration file is provided
* @return array
*/
protected function _loadConfig($file)
{
if (! Zend_Loader::isReadable($file) ) {
return;
}
$environment = $this->_bootstrap->getApplication()->getEnvironment();
$suffix = strtolower(pathinfo($file, PATHINFO_EXTENSION));
switch ($suffix) {
case ‘ini’:
$config = new Zend_Config_Ini($file, $environment);
break;
case ‘xml’:
$config = new Zend_Config_Xml($file, $environment);
break;
case ‘php’:
case ‘inc’:
$config = include $file;
if (!is_array($config)) {
throw new Zend_Application_Exception(‘Invalid configuration file provided; PHP file does not return array value’);
}
return $config;
break;
default:
throw new Zend_Application_Exception(‘Invalid configuration file provided; unknown config type’);
break;
}
return $config->toArray();
}
}
Entries (RSS)