Initial notes on Zend_Application
Zend_Application is now in the incubator and being actively developed for version 1.8 of Zend Framework. I’ve had a little play using it with a standard ZF application with no modules and this is what I’ve worked out so far.
As a result this post rambles a bit; sorry about that.
Zend_Application is intended to make bootstrapping your application easier, presumably with less code. It also has a new autoloader, Zend_Loader_Autoloader, but I haven’t worked out why I care about that yet.
Back to bootstrapping. The general use-case is to extend Zend_Application_Bootstrap_Base and put your code in there. There are two ways to do this bit:
- Add functions to your Zend_Application_Bootstrap_Base class that start with _init, for example _initConfig().
- Use pre-supplied (or write your own) plugin classes. The idea is that these are then easily reusable across multiple bootstrap scenarios such as testing, web service provision, standard website, etc. Presumably they are a bit slower as a result, compared to just using functions within the Zend_Application_Bootstrap_Base sub-class.
Inline initialisation functions
To use Zend_Application‘s bootstrap with inline functions, we need to set up an index.php that looks something like this:
//File: public/index.php
<?php
define('BASE_PATH', realpath(dirname(__FILE__) . '../'));
define('APPLICATION_PATH', BASE_PATH . '/application');
set_include_path(BASE_PATH . '/library/incubator'
. PATH_SEPARATOR .BASE_PATH . '/library'
. PATH_SEPARATOR . get_include_path()
);
// APPLICATION_ENVIROMENT defines which config section is loaded
if(!defined('APPLICATION_ENVIRONMENT')) {
define('APPLICATION_ENVIRONMENT', 'development');
}
require_once 'Zend/Application.php';
$application = new Zend_Application(APPLICATION_ENVIRONMENT,
array(
'bootstrap'=>array('path'=>APPLICATION_PATH.'/Bootstrap.php'),
'autoloadernamespaces' => array('Zend', 'App')
));
$application->bootstrap();
$application->run();
All we are doing here is setting some constants and the include path so that we can load the Zend Framework. As Zend_Application is in the incubator, we need that too.
We then instantiate Zend_Application passing in the environment string and an array of options. In our case, we need to tell it where to find the bootstrap class and that our library have Zend_XXX and App_XXX classes. Though, I’m not sure what it does with the autoloader info.
The Bootstrap class looks like this:
//File: application/Bootstrap.php
<?php
class Bootstrap extends Zend_Application_Bootstrap_Base
{
protected $_config;
function _initConfig()
{
// config
$this->_config = new Zend_Config_Ini(APPLICATION_PATH
. '/config/app.ini', APPLICATION_ENVIRONMENT);
Zend_Registry::set('config', $this->_config);
Zend_Registry::set('env', APPLICATION_ENVIRONMENT);
// debugging
if($this->_config->debug) {
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 'on');
}
}
function _initDB()
{
// Database
if($this->_config->db) {
$dbAdapter = Zend_Db::factory($this->_config->db);
Zend_Db_Table_Abstract::setDefaultAdapter($dbAdapter);
Zend_Registry::set('dbAdapter', $dbAdapter);
}
}
function _initView()
{
// view and layout setup
Zend_Layout::startMvc(APPLICATION_PATH . '/views/layouts');
$view = Zend_Layout::getMvcInstance()->getView();
$view->doctype('XHTML1_STRICT');
$view->headTitle()->setSeparator(' - ');
}
function _initFrontController()
{
$frontController = Zend_Controller_Front::getInstance();
$frontController->setControllerDirectory(APPLICATION_PATH .'/controllers');
$frontController->setParam('env', APPLICATION_ENVIRONMENT);
// action helpers
Zend_Controller_Action_HelperBroker::addPath(APPLICATION_PATH .'/controllers/helpers');
}
public function run()
{
$frontController = Zend_Controller_Front::getInstance();
$frontController->dispatch();
}
}
In this case, I’ve grouped my init logic into four separate functions, but I could equally have created one function called _initAll(). The only function that you absolutely must have is run(), which in this case we simply dispatch the front controller. It seems a little odd that run() isn’t already defined in Zend_Application_Bootstrap_Base though as it’s going to be the same code for everyone.
The order of loading of the _init methods is the same order as they are defined in the class. You can change the order by calling $this->bootstrap('xxx'); in any given _init method to force _initXxx() to be run. The system is clever enough not to call the _init function twice.
Incidentally, the config file I’ve been using look something like this:
// File: application/config/app.ini
[site]
; All the standard site-specific settings
db.adapter = PDO_MYSQL
db.params.dbname = "test"
db.params.username = "test"
db.params.password = "test"
db.params.hostname = "test"
debug = 1
[production: site]
debug = 0
[staging : site]
[development : site]
[unittesting]
; specific config for unit testing
I’ve included this so you can compare it to the one used when we add the Zend_Application configuration information.
Now that we’ve seen how to do it with all our own code, let’s repeat the entire process with Zend_Application‘s resource plugin system.
Using resource plugins and a config file
Another way to use Zend_Application is to use a config file to configure the bootstrapping process and point Zend_Application at it. By the way, it’s not clear if the classes that we plug in to the boostrap process are called resources or plugins at the point, so I’m calling them resource plugins :)
Zend_Application comes with a number of plugins, but it’s inevitable that you’ll need your own. In our case, to replicate what we’ve done in the inline example, we’re going to need a new view resource which we’ll call App_Application_Resource_View as it will extend Zend_Application_Resource_View.
This resource plugin is easy to write. The system will call an init() method and you can put whatever you like there. In our case we are extending an already existing resource plugin, so we call up to the parent’s init() and then do our own stuff:
// FILE: library/App/Application/Resource/View.php
<?php
class App_Application_Resource_View extends Zend_Application_Resource_View
{
public function init()
{
parent::init();
$view = $this->getView();
$view->doctype('XHTML1_STRICT');
$view->headTitle()->setSeparator(' - ');
Zend_Layout::startMvc(APPLICATION_PATH . '/views/layouts');
}
}
Presumably, at some point they’ll be a Zend_Layout resource plugin supplied by by default, but for now, there isn’t so I’ve started up the layout here too. It would be nice if Zend_Application_Resource_View allowed setting up the view helpers like doctype directly from the config file too.
The index.php file looks very similar to last time, except that we now tell Zend_Application about the config file:
//File: public/index.php
<?php
define('BASE_PATH', realpath(dirname(__FILE__) . '../'));
define('APPLICATION_PATH', BASE_PATH . '/application');
set_include_path(BASE_PATH . '/library/incubator'
. PATH_SEPARATOR .BASE_PATH . '/library'
. PATH_SEPARATOR . get_include_path()
);
// APPLICATION_ENVIROMENT defines which config section is loaded
if(!defined('APPLICATION_ENVIRONMENT')) {
define('APPLICATION_ENVIRONMENT', 'development');
}
require_once 'Zend/Application.php';
$application = new Zend_Application(APPLICATION_ENVIRONMENT,
APPLICATION_PATH.'/config/app.ini');
$application->bootstrap();
$application->run();
The Bootstrap class is very simple as most of the work is now done elsewhere:
//File: application/Bootstrap.php
<?php
class Bootstrap extends Zend_Application_Bootstrap_Base
{
public function run()
{
Zend_Registry::set('config', $this->getOptions());
Zend_Registry::set('env', APPLICATION_ENVIRONMENT);
$this->frontController->dispatch();
}
}
Note that Zend_Application doesn’t store the environment string or the loaded config file to the registry and so they are unavailable to the rest of the application unless we do it ourselves. The easiest place to do it is in the run() method, but it would be nice if it did it automatically for us.
Obviously, as all our bootstrap code has disappeared, we need to tell Zend_Application what we want to do. This is done in the app.ini config file that we passed in the constructor:
//File: application/config/app.ini
[bootstrap]
; Zend_Application configuration
phpsettings.display_errors = 0
phpsettings.error_reporting = 8191
includepaths=
autoloadernamespaces.0 = "Zend"
autoloadernamespaces.1 = "App"
bootstrap.path = APPLICATION_PATH"/Bootstrap_Resource.php"
pluginpaths.App_Application_Resource = "App/Application/Resource"
resources.frontcontroller.controllerdirectory = APPLICATION_PATH "/controllers"
resources.frontcontroller.params.0.env = APPLICATION_ENVIRONMENT
resources.view =
resources.db.adapter = PDO_MYSQL
resources.db.params.dbname = "test"
resources.db.params.username = "test"
resources.db.params.password = "test"
resources.db.params.hostname = "test"
[site: bootstrap]
; All the standard site-specific settings
debug = 1
[production: site]
debug = 0
[staging : site]
[development : site]
phpsettings.display_errors = 1
[unittesting]
; specific Zend_Application and other config for unit testing
Note that I’m using the PHP constants that were defined in index.php within this config file. That’s part of the standard parse_ini_file() functionality, but not many people seem to take advantage of it.
Essentially, Zend_Application takes an array of options as in the second parameter of the constructor and if you don’t supply one, it looks for the specific keys it uses in the config file instead. That means that in principle you don’t have to use a config file, you could just pass in an array with the right information. I’ve not tested it though :)
The app.ini file also shows the disconnect over whether the resource plugins are called resources or plugins. We have to use the key “pluginpath” to define the directory where we store them, but use the key “resources” to configure them. It would be nice if we just called them one thing or the other. Note that we have to have a key in the resources section even if there’s no parameters to set as in the case of the view resource plugin.
Note also that one phpsetting that you can’t set is date.timezone as the dot is used as a separator in Zend_Config_Ini. Ideally, we’d change the separator to a “|” or something which is possible via the third parameter to Zend_Config_Ini‘s constructor. We can’t do that though as the call to load the app.ini file is buried in Zend_Application and we can’t pass any options to it. I’d guess this is by design. The easiest solution is to just call date_timezone_set() directly in your Boostrap’s run() method instead.
Summary
Zend_Application has the potential to simplify the bootstrapping of an application, but, at the moment at least, you need to be aware of what it does and more importantly, what it doesn’t do for you. I can’t see a case where you can use it directly without either writing a few of your own _init methods or few resource plugins.
There’s also the new loader which presumably will make something easier too, but that will be a separate post when I understand it and can see a use-case for it.
Finally, this should be obvious, but as the Zend_Application code is still under heavy development don’t take this as gospel – by the time it is released, it could be very different!
Note that the issue with phpsettings has been solved in http://framework.zend.com/issues/browse/ZF-6011
Hi Rob,
Nice post, I too have been working with Zend_Application and have found it very useful. The Autoloader is the part I like the most, this has enabled me to drop lots of my own library code that was used to load classes etc. I think it really comes in handy when using modules though, as you can add autoloaders for each module easily. The main component behind this is Zend_Loader_Autoloader_Resource, this enables us to have a set of resources that the autoloader can load.
$loader = new Zend_Loader_Autoloader_Resource(array(
'namespace' => 'Stuff_',
'basePath' => '/path/to/some/stuff',
))
$loader->addResourceType('Model', 'models', 'Model');
$foo = $loader->getModel('Foo'); // get instance of Stuff_Model_Foo class
We can then add multiple resource to be loaded, be they forms, services etc etc
Couple of notes about your post:
'autoloadernamespaces' => array('Zend', 'App')
should be
'autoloadernamespaces' => array('Zend_', 'App_')
The autoloader can/will load any library not just ones that follow the Zend underscore convention.
Also your bootstrap files init methods should be protected not public.
Thanks Keith,
I can see a use for Zend_Loader_Autoloader now! I wonder though, if you aren't using modules and you use PEAR naming for your classes in lib/, does Zend_Loader_Autoloader do anything useful?
Regards,
Rob…
Should do, maybe ask Matthew about that one, he did say something along those lines but I am not 100% on it.
Thx for this article.
But i dont get it how to set the app.ini to autoload models in a modular structure.
Which ini settings are required for that?
To setup modules you use the modules resource, I am using this on my storefront example app here:
http://code.google.com/p/zendframeworkstorefront/source/browse/#svn/branches/TRY-KP-Zapp
Dont use the trunk it is pre zapp :)
When using the modules resource you need to still configure your default module in the bootstrap file. The modules resource looks for a Bootstrap.php file inside the modules dir like:
modules
default
module1
Bootstrap.php
Where bootstrap.php is a class that extends the module base bootstrap class.
I think this aspect is still being worked on so it could change!
Thanks for the article.
There are three more resources or, taking your words, resource plugins. You can see them here:
http://framework.zend.com/svn/framework/standard/branches/user/freak/Zend/Application/Resource/ .
You may want to take a look to Zym framework too:
http://www.zym-project.com/docs/reference/zym.app.html#zym.app.environments
Cheers
holo
Seems a bit bloated if you ask me, my index.php contains only setting the environment, requiring a bootstrap.php outside the documentroot (with a bootstrap class). And a try catch around a Bootstrap::run();
Everything is handled within the bootstrap class, including setting the include paths and setting the autoloader.
That way if for some reason php would be disabled the index.php shows the user nothing that might give away information about the system. So from just seeing that file you can't even see I use Zend, nor any other information about the include path.
The bootstrap is so general I could even have differend includes based on certain settings (include the incubator only in development mode, because it's not ready for production yet).
Geeez … you are bringing php to its knees, this is such a bloat, but at least, there is finally a standard way we should startup our projects, getting rid of that flexi…jungle we all have done before these components.
Is there anyone using Zend Framework caring about performance, memory consumption, speed of execution, or are you all design patterns freaks :)
I tend to worry about maintaining the code base so I can roll features fast. Obviously you take the normal optimizations but not all of us work for twitter :P
I would rather buy another server or add memory than spend weeks trying to refactor a business change into an application….
I'm trying to inject jQuery helper in bootstrap like this :
=============================== Zend_Layout::startMvc(array('layout' => 'main',
'layoutPath' => APPLICATION_PATH . '/layouts/scripts'
));
$view = Zend_Layout::getMvcInstance()->getView();
$view->addHelperPath('ZendX/JQuery/View/Helper', 'ZendX_JQuery_View_Helper');
$view->jQuery()->setLocalPath('../scripts/jquery-1.3.2.min.js');
$view->jQuery()->setUiLocalPath('../scripts/jquery.ui/jquery-ui.min.js');
======================
but jquery not loaded on browser, unless I'm put the following code on index.phtml script :
==================
ajaxLink("",
"/index/demo",
array('update' => '#content')); ?>
======================
Am I missing somthing?
Thanks
I must have a fundamental lack of understanding of the infrastructure. The way you write it you're assuming I should know stuff but I clearly don't. You say things like: "to enable a x helper you just write such and such a class provided you have enabled the namespace." or something like that. Basically any time you use the word "just" it's not "just". It means there's an assumption. I am not stupid. I just don't see how it all fits together. In most of your examples I have no clue how to modify it to suit my situation, whether it goes under my custom stuff in library or whether that long naming thing works elsewhere and how that whole config thing works. Do you have a basic and I mean EXTREMELY BASIC example that explains in an airtight and general way that has no dependencies on ANYTHING except just an out of the box Zend Tool install.
Idea: Take a plain vanilla Zend Tool install and write a simple plugin. Show where to put it and how to get ZF to find it in the app. Explain the name. If there is some namespace thing going on explain that. Explain how to put it in the config and use it with minimum code writing.
I have just followed the manual to retrofit an app that worked under 1.7. Of course I have broken it, that was to be expected. My error message is this:
Uncaught exception 'Zend_Controller_Action_Exception' with message 'Action Helper by name Acl not found'
I have several files called Acl.php so it's clear that ZF isnt' finding them because I haven't properly registered them. Is it configuration or code? What is best practics for making it find it?
Hey…thank you for your post. Did you have any stuff about Zend_Navigation & Co (Version 1.8)
Thanx
isicom,
If I ever us Zend_Navigation, I'll probably put up some notes!
Rob…
Thank you a lot!!!
Can I add resource type at autoloader via application.ini. Now it is in Bootstrap:
protected function _initAutoloaderResources()
{
$resourceLoader = new Zend_Loader_Autoloader_Resource(array(
'basePath' => APPLICATION_PATH,
'namespace' => "
));
$resourceLoader->addResourceType('Model', 'models', 'Model_');
}