blog has moved
I’ve moved my blog under my personal domain:
http://bsagols.com/blog
I didn’t do the proper redirection ’cause it costs 12 $ a year and I don’t see the point paying for this (12 $ (a year !) to put 1 line in an .htaccess file ?!).
Here is the new URL of this post:
bsagols / blog - Zend_Loader_Autoloader Stand-alone and Modular approaches
Here follows the original post…
- -
As I was desperately browsing the web for some information about how to configure the Zend_Loader_Autoloader in stand-alone mode, I found myself a bit confused about the different approaches of the Zend Autoloader.
I’ve identified 3 cases of using the Autoloader:
- Stand-alone: Let’s say this is the case when you do not use the MVC model of the Zend Framework, but you need to load classes “on the fly”
- Non-modular ZF MVC application
- Modular ZF MVC application
These are the 3 cases where the configuration of the Autoloader differs.
Stand-alone
This is the basic configuration of the Autoloader, the fundamental one. You need it to load any class dynamically as long as it is included in your include path.
First, the official Zend documentation is great:
http://framework.zend.com/manual/en/zend.loader.autoloader.html#zend.loader.autoloader.usage
What’s important here: instantiating the Autoloader Singleton…
Zend_Loader_Autoloader::getInstance();
… Does register 2 default namespaces:
So you can load dynamically any class of the Zend library without any additional configuration.
If you wish to add other namespaces to be loaded dynamically, just register them:
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->registerNamespace('Foo_');
$autoloader->registerNamespace('Bar_');
...
Alternatively (this is equivalent) you can configure this in your application.ini file (cleaner, in my opinion):
[ application.ini ]
autoloadernamespaces[] = "Foo_"
autoloadernamespaces[] = "Bar_"
...
Non-modular ZF MVC application
So now that I am able to load any class from my library folder, what about loading classes located in my application folder (models, mappers, forms, etc…) ?
If you follow the standard non-modular structure of a ZF MVC project, the Zend Framework comes with a solution for that. Instead of configuring the (Stand-alone) Autoloader with all these new namespaces:
- Model
- Model_DbTable
- Form_
- etc…
You can instantiate the Zend_Application_Module_Autoloader which will configure / register automatically these namespaces for you.
Here the official documentation lacks of information, in my opinion… Or, at least, this is what I thought before finding another interesting section in the official Zend Framework documentation:
Autoloading in Zend Framework
And especialy that section, talking about the “Resource Autoloader”:
http://framework.zend.com/manual/en/learning.autoloading.resources.html#learning.autoloading.resources
Zend_Application_Module_Autoloader will look for the standard “Resources” and configure the associated namespaces:
RESOURCE => NAMESPACE
forms/ => Form
models/ => Model
models/DbTable/ => Model_DbTable
models/mappers/ => Model_Mapper
plugins/ => Plugin
services/ => Service
views/helpers => View_Helper
views/filters => View_Filter
Just specify the folder where to look for the standard “Resources” (here APPLICATION_PATH):
[ Bootstrap.php ]
protected function _initAutoloader()
{
new Zend_Application_Module_Autoloader(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH,
));
}
Alternatively you can configure this in your application.ini file (this is completely equivalent as long as your Bootstrap.php file is under the APPLICATION_PATH folder):
[ application.ini ]
appnamespace = "Application"
Note: The “Application” namespace is recommended by the official documentation of the Zend Framework.
Behind the scene (application.ini)
The Bootstrap.php (Zend_Application_Bootstrap_Bootstrap) tries to launch an instance of Zend_Application_Module_Autoloader with the following options:
- namespace is the property “appnamespace” of application.ini (getAppNamespace()…)
- basePath is under the folder where the Bootstrap.php file is located (dirname($path)…)
[ Zend_Application_Bootstrap_Bootstrap ]
/**
* Retrieve module resource loader
*
* @return Zend_Loader_Autoloader_Resource
*/
public function getResourceLoader()
{
if ((null === $this->_resourceLoader)
&& (false !== ($namespace = $this->getAppNamespace()))
) {
$r = new ReflectionClass($this);
$path = $r->getFileName();
$this->setResourceLoader(new Zend_Application_Module_Autoloader(array(
'namespace' => $namespace,
'basePath' => dirname($path),
)));
}
return $this->_resourceLoader;
}
Modular ZF MVC application
Configuring the Autoloader for a modular ZF MVC application is just about configuring each module Autoloader independently:
[ Bootstrap.php ]
protected function _initAutoloader()
{
new Zend_Application_Module_Autoloader(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH . '/modules/default',
));
new Zend_Application_Module_Autoloader(array(
'namespace' => 'Admin',
'basePath' => APPLICATION_PATH . '/modules/admin',
));
new Zend_Application_Module_Autoloader(array(
'namespace' => 'Blog',
'basePath' => APPLICATION_PATH . '/modules/blog',
));
...
}
Alternatively you can configure this in your application.ini file :
[ application.ini ]
resources.modules[] =
Behind the scene (application.ini)
It loads an instance of Zend_Application_Resource_Modules. Behind the scene, this Application_Resource scans your modules folder, looking for modules.
Let’s say you have the following folder structure:
application/
modules/
default/
admin/
blog/
1. Zend_Application_Resource_Modules looks for modules
The Zend_Application_Resource_Modules will find 3 modules. For each folder / module, it will look for a Bootstrap.php file containing a _Bootstrap class and instanciate it. So let’s say:
application/
modules/
default/
Bootstrap.php // Default_Bootstrap extends Zend_Application_Module_Bootstrap
admin/
Bootstrap.php // Admin_Bootstrap extends Zend_Application_Module_Bootstrap
blog/
Bootstrap.php // Blog_Bootstrap extends Zend_Application_Module_Bootstrap
Bootstrap.php // Bootstrap extends Zend_Application_Bootstrap_Bootstrap
2. Zend_Application_Module_Bootstrap configures the module Autoloader
This is when instantiating “Default_Bootstrap” that the Autoloader of the “Default” module is loaded / configured (because it extends Zend_Application_Module_Bootstrap…).
By default, it uses the module name as the namespace for the module Autoloader:
[ Zend_Application_Module_Bootstrap ]
/**
* Get default application namespace
*
* Proxies to {@link getModuleName()}, and returns the current module
* name
*
* @return string
*/
public function getAppNamespace()
{
return $this->getModuleName();
}
The module name being the prefix of the module Bootstrap classname:
[ Zend_Application_Module_Bootstrap ]
/**
* Retrieve module name
*
* @return string
*/
public function getModuleName()
{
if (empty($this->_moduleName)) {
$class = get_class($this);
if (preg_match('/^([a-z][a-z0-9]*)_/i', $class, $matches)) {
$prefix = $matches[1];
} else {
$prefix = $class;
}
$this->_moduleName = $prefix;
}
return $this->_moduleName;
}
For example, the “Admin_Bootstrap” classname corresponds to the module name “Admin”. So the Admin_Bootstrap class would launch behind the scene:
new Zend_Application_Module_Autoloader(array(
'namespace' => 'Admin',
'basePath' => APPLICATION_PATH . '/modules/admin',
));
But this is completely transparent…
The special case of the “default” module
In the case of the “Default” module, getModuleName() will return “Default” as the corresponding module name (because classname is Default_Bootstrap). So Zend_Application_Module_Bootstrap will configure the module Autoloader like this:
new Zend_Application_Module_Autoloader(array(
'namespace' => 'Default',
'basePath' => APPLICATION_PATH . '/modules/default',
));
It registers the namespace “Default” for the “default” folder… This is not the namespace we want to use, but “Application” is…
Meaning that trying to load:
// application/modules/default/models/User.php
$user = new Application_Model_User();
… Will fail. If we want this to work, we should prefix all the classes of the “Default” module, with “Default_” instead of “Application_”, but this is not what we want to do. We want the “Default” module to use the namespace “Application”.
A quick workaround (and so far the best and cleanest solution I have found), is to override the getModuleName() method:
[ in Default_Bootstrap extends Zend_Application_Module_Bootstrap ]
public function getModuleName()
{
return "Application";
}
So it will configure the “Default” module Autoloader like this:
new Zend_Application_Module_Autoloader(array(
'namespace' => 'Application',
'basePath' => APPLICATION_PATH . '/modules/default',
));
And you will be able to load your “Default” classes / Resources as usual:
// application/modules/default/models/User.php
$user = new Application_Model_User();
To sum up
Modular folder structure + Bootstrap files
application/
modules/
default/
Bootstrap.php // Default_Bootstrap extends Zend_Application_Module_Bootstrap
admin/
Bootstrap.php // Admin_Bootstrap extends Zend_Application_Module_Bootstrap
blog/
Bootstrap.php // Blog_Bootstrap extends Zend_Application_Module_Bootstrap
Bootstrap.php // Bootstrap extends Zend_Application_Bootstrap_Bootstrap
Application.ini
; bootstrap
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
; front controller
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
...
; modules
resources.modules[] =
This is all what you need in order to configure your modular application! All the default namespaces of the Zend Framework are automatically loaded for each module.
I hope this is gonna help somebody somewhere.
cheers.