Module autoloader in ZF
The manual on Zend_Application_Module_Autoloader states the following:
When using module bootstraps with Zend_Application, an instance of Zend_Application_Module_Autoloader will be created by default for each discrete module, allowing you to autoload module resources.
Source: http://framework.zend.com/manual/zh/zend.loader.autoloader-resource.html#zend.loader.autoloader-resource.module
This requires me to create an empty bootstrap class for each of my modules or else resource autoloading per module won't work with the build-in autoloader.
Now I have two questions
- What is a discrete module?
- Is there a way to 开发者_StackOverflowhave this resource autoloader registered by default for each module without the need to create a bootstrap file for each module? I want it available in each module and creating so many empty bootstrap classes is something i'd rather prevent.
I understand your reluctance to add an empty bootstrap class to each module. However, consider the case for re-use: if you are able to bundle your module separately, you can then drop it into another application later, and autoloading will work immediately, with no extra work. This was one of the use cases for having module bootstraps, and why it currently works the way it does.
("Discrete" in this case means "self-contained", and not part of the "application" module.)
If you don't like how this operates, you're free to omit the module bootstrap -- you'll simply need to add in a resource autoloader for the module yourself somehow. This can be done via a bootstrap resource method fairly easily. However, as someone earlier posted: why re-invent the wheel when something that's tested and documented gets the job done? :)
The reason modules bootstraps enable autoload is because they extend Zend_Application_Module_Bootstrap which sets the autoloader in the constructor like so
public function __construct($application)
{
//...
if ($application->hasOption('resourceloader')) {
$this->setOptions(array(
'resourceloader' => $application->getOption('resourceloader')
));
}
$this->initResourceLoader();
//...
}
This runs because the modules resource runs the bootstrap for each module in the init function ...
foreach ($modules as $module => $moduleDirectory) {
$bootstrapClass = $this->_formatModuleName($module) . '_Bootstrap';
if (!class_exists($bootstrapClass, false)) {
$bootstrapPath = dirname($moduleDirectory) . '/Bootstrap.php';
if (file_exists($bootstrapPath)) {
$eMsgTpl = 'Bootstrap file found for module "%s" but bootstrap class "%s" not found';
include_once $bootstrapPath;
if (($default != $module)
&& !class_exists($bootstrapClass, false)
) {
throw new Zend_Application_Resource_Exception(sprintf(
$eMsgTpl, $module, $bootstrapClass
));
} elseif ($default == $module) {
if (!class_exists($bootstrapClass, false)) {
$bootstrapClass = 'Bootstrap';
if (!class_exists($bootstrapClass, false)) {
throw new Zend_Application_Resource_Exception(sprintf(
$eMsgTpl, $module, $bootstrapClass
));
}
}
}
} else {
continue;
}
}
if ($bootstrapClass == $curBootstrapClass) {
// If the found bootstrap class matches the one calling this
// resource, don't re-execute.
continue;
}
$moduleBootstrap = new $bootstrapClass($bootstrap);
$moduleBootstrap->bootstrap();
$this->_bootstraps[$module] = $moduleBootstrap;
}
The short answer is if you don't write the empty bootstrap files, you'll have to abstract some of this well functioning, well tested code into your own global bootstrap file, and then lose the flexibility of having bootstraps for you modules when / if you need to bootstrap them later in your app.
Modules allow you to separate your application into specific concerns. Frequently my larger applications will have a default module for users and an admin module to contain all administrative functions. I use the directory structure recommended in the Recommended Project Structure for Zend Framework MVC Applications -> Module Structure section of the Zend Framework Documentation.
As to your second question, the answer is yes and no. If you want to take advantage of the default autoloading functionality (loading Admin_Form_Settings from the admin/forms directory), you will need a bootstrap in each module. See Matthew Weier O'Phinney's article on Module Bootstraps in Zend Framework: Do's and Don'ts for more info. You may also want to Google for and review Rob Allen's post "Bootstrapping modules in ZF 1.8 and up."
Answering no to your second question: one technique that I like to use that doesn't require empty bootstraps in each module is placing all of your application classes in the application's lib folder, and mimic the Zend Framework's directory structure. If my application is named Example, I'll create a folder named Example in my /lib directory. My user registration form would be placed in /lib/Example/Form, and might be named UserRegistration.php. My class would be named Example_Form_UserRegistration. Autoloading my form would require the following in the Bootstrap.php file:
protected function _initAppAutoload() {
$autoloader = Zend_Loader_Autoloader::getInstance();
return $autoloader;
}
My application.ini would include the lines
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules[] =
autoloaderNamespaces[] = "Example_"
Using this technique you should then be able to autoload any class in /lib/Example anywhere in your application without placing empty bootstraps in each module.
NOTE: I tried posting links directly to the docs and to Rob Allen's article, but since I'm a new guy I was only allowed a single link. Apologies for asking you to Google items that should be links.
There are two ways (that I know of) to enable the module resource autoloaders. The first has been covered in above answers, namely:
Add the modules resource line in your application.ini:
resources.modules[] =
And then create an empty module bootstrap file.
The second is to add the following bootstrap method to your application-wide (non-module) bootstrap:
protected function _initModuleAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Foo',
'basePath' => APPLICATION_PATH . "/modules/Foo",
));
return $autoloader;
}
But you would need to create each module's autoloader individually. Personally, I prefer the empty bootstrap files for reasons mentioned above - this feels more manual to me.
Note: I believe this is the "bootstrap resource method" @weierophinney mentions above.
Bootstrap:
$uri = explode('/',$_SERVER['REQUEST_URI']);
if($uri['1'] == 'flok'){
$flok = new Zend_Controller_Router_Route('flok/:controller/:action/:id/*', array('module' => 'flok', 'controller' => 'index', 'action' => 'index', 'id' =>null));
$router->addRoute('flok', $flok);
$resourceLoader = new Zend_Application_Module_Autoloader(array(
'basePath' => APPLICATION_PATH . "/flok/flok",
'namespace' => 'Flok',
));
//Init
$frontController->registerPlugin(new Flok_Plugin_Init(),'flok');
//Auth
$frontController->registerPlugin(new Flok_Plugin_Auth(),'flok');
// dynamic modules
$ruta = APPLICATION_PATH.'/flok';
foreach(scandir($ruta) as $mod) {
if(!is_dir($mod) and $mod != '.DS_Store'){
$Modululflok = new Zend_Controller_Router_Route('flok/'.$mod.'/:controller/:action/:id/*', array('submodules' => 'flok','module' => $mod , 'controller' => 'index', 'action' => 'index', 'id' =>null));
$router->addRoute($mod, $Modululflok);
$resourceLoader = new Zend_Application_Module_Autoloader(array(
'basePath' => APPLICATION_PATH . "/flok/".$mod,
'namespace' => ucfirst($mod),
));
}
}
$layout = Zend_Layout::getMvcInstance();
$layout
->setLayout('layout')
->setLayoutPath(APPLICATION_PATH . '/flok/flok/views/scripts');
$viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
$viewRenderer->initView();
$viewRenderer->view->addHelperPath(APPLICATION_PATH . '/flok/flok/helpers');
} else {
$default = new Zend_Controller_Router_Route('*', array('module' => 'default', 'controller' => 'index', 'action' => 'index'));
$router->addRoute('default', $default);
}
This helper to insert data (menus, views, etc.) in the core module:
class Zend_View_Helper_Models
{
public function Models($tipo)
{
// load modules
$ruta = APPLICATION_PATH.'/flok';
foreach(scandir($ruta) as $mod) {
if(!is_dir($mod) and $mod != '.DS_Store'){
$rutaphp = $ruta.'/'.$mod.'/'.$mod.'.php';
if(file_exists($rutaphp)){
include_once($rutaphp);
$modul = new $mod;
if(isset($modul->$tipo) and $modul->$tipo === true){
$data = $tipo.'Data';
$m[] = $modul->$data;
}
}
}
}
return $m;
}
}
精彩评论