Zend Framework 1 tutorial Part 6: The application bootstrap.php file

Zend Framework 1 Logo

It's time to create a bootstrap file, in the bootstrap we will initialize the application. Open the myapp/application directory and create and empty bootstrap.php file.

For a basic bootstrap I have added a lot of code, that I will explain in the few chapters of this article, but there are several things a haven't added just yet. For example, one thing I haven't included yet is the database initialization as well as the database profiler initialization, I will add that part as soon as we need it.

The autoloader

Ok lets start with our bootstrap, first we retrieve the autoloader instance we have initialized in the public/index.php file. We add our own namespace (Myapp_) to the autoloader namespaces, for our collection of classes that we will create in the future in library/Myapp/. All the Zend Framework classes will be in library/Zend our own custom classes will be in library/Myapp. The namespace of our classes will be Myapp_COMPONENT_CLASS. If we use a class (one of our owns, a class from a vendor library or a ZF class), the autoloader will automatically load it for us. There is no need to use require or include (once) in our code anymore. The autoloader will take care of loading the classes for us as soon as it detects that we want to use one of them. The autoloader will replace the underscores in the class names with a slash to build the file path and load it using that path. So for a class called Myapp_Mycomponent_Myclass the autoloader will try to find it at Myapp/Mycomponent/Myclass. For more information about how to name classes so that the autoloader can find them, you can check out the Zend Framework naming conventions guide.

If we are not in production mode (because we are in development mode for example) we want the autoloader to throw exceptions for when a syntax error results in a class not to be found. That's why we tell the autoloader to not suppress errors by setting suppressNotFoundWarnings to false, as soon as we are in production mode we will hide those errors to avoid disclosing paths to the public, in production we could still those errors to a log file.

If you look at the code you will notice that all methods of our bootstrap, start with _init. All the methods prefixed with "_init" are bootstrap resources and will be called by bootstrap() one after the other from top to bottom when your application get bootstrapped. For more information about the theory of operation of Zend application bootstrap check out this very good "bootstrap theory of operation" article from the documentation.

Finally we return the data, because perhaps we want to use the autoloader again somewhere in our app. I will explain at the end of this article howto get the returned value of a bootstrap _init method.

For more information about the autoloader check out Matthew Weier O'Phinney's article about the Zend Framework autoloader on devzone.


<?php

/**
 * MAIN APPLICATION BOOTSTRAP
 *
 * @author weber chris
 * @version 1.0
 *
 */
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {

    /**
     * configuration variable
     *
     * @var Zend_Config_Ini
     */
    protected $_configuration;

    /**
     * frontcontroller variable
     *
     * @var Zend_Controller_Front
     */
    protected $_frontController;

    /**
     * AUTOLOADING
     *
     * @return Zend_Loader_Autoloader
     */
    protected function _initCore() {

        $autoloader = Zend_Registry::get('Autoloader');
        $autoloader->registerNamespace('Myapp_');

        if (APPLICATION_ENVIRONMENT != 'production') {
            $autoloader->suppressNotFoundWarnings(false);
        } else {
            $autoloader->suppressNotFoundWarnings(true);
        }

        return $autoloader;

    }

As we need the configuration variables in some bootstrap methods, we take the configuration variables from the registry (we have loaded those in the public/index.php previously). We also return the configuration abject so that we can access it later, in controllers and other files, like any other bootstrap resource.


    /**
     * CONFIGURATION
     *
     * @return Zend_Config_Ini
     */
    protected function _initConfig() {

        $this->_configuration = Zend_Registry::get('Configuration');

        return $this->_configuration;

    }

Next step is to use one of the Zend Framework resource plugins to get an instance of the frontcontroller. We create a custom response about, set an header that will set the encoding of our pages to utf-8 and then pass that response object to the frontcontroller.

But wait, we just introduced two new things, the frontController and the resource plugins. Let's start with the frontController.

The frontController handles a lot of stuff for us, so we don't have to bother. In an MVC environment like ours, the frontController holds the request object. It sends the request to an action depending on the defined routes. If the frontController does not find the action defined by the routes or if there is an error in the action itself then it will call the errorHandler plugin. Finally it takes the response object and passes it to the viewRenderer so that the data can be send the data back to the user as an html page. At every step of the frontController emits events. We will see later that you can listen to those events with plugins. So our plugins could modify the request before it reaches an action or for example modify a response after the action has been executed.

The resource plugins are a new feature that got introduced in ZF 1.8 along with Zend_Application. It allows you to reduce the amount of code in the bootstrap by putting resources code into plugins instead of having all the code in the bootstrap. The other advantage of the resource plugins is that they can be reused and shared between multiple apps. The Zend Framework has some default resource plugins, you can find them in "library/Zend/Application/Resource/". You can also find more information about the available resources in the resources documentation. Of course you can also write your own resources. To get an instance of a bootstrap resource plugin just call the getResource method of the bootstrap object. To configure resources open your application.ini, as you can see we have already configured the frontController resource plugin there in a previous article of this series.


    /**
     * FRONT CONTROLLER
     *
     * @return Zend_Controller_Front
     */
    protected function _initFrontControllerOutput() {

        $this->bootstrap('FrontController');
        $frontController = $this->getResource('FrontController');

        $response = new Zend_Controller_Response_Http;
        $response->setHeader('Content-Type', 'text/html; charset=UTF-8', true);
        $frontController->setResponse($response);

        $this->_frontController = $frontController;

        return $frontController;

    }

We now setup our logger, to log errors and other information. We use the Zend Framework log stream write component. Here we tell the logging component that we want to write our messages in a file called application.log, and that this file will be located in our logs folder application/logs/. Instead of writing log messages into a file we could also send them to firebug using the firebug writer.


    /**
     * ERROR LOG
     *
     * to log errors in controllers use the logging action helper
     *
     * @return Zend_Log
     */
    protected function _initLogging() {

        $logsDir = APPLICATION_PATH.'/logs/';

        if (!is_dir($logsDir)) @mkdir($logsDir, 0755);

        // init error logger
        $logger = new Zend_Log();

        $writer = new Zend_Log_Writer_Stream($logsDir.'application.log');
        $logger->addWriter($writer);

        return $logger;

    }

In this part we set the encoding of our html views to utf-8 (or use another encoding, but it should be the same you used for the response when initializing the frontcontroller). As doctype we use html5 because for the layout we will use html5 markup.

Next we add a default title (html title tag) for all our pages and we tell the zend framework to use "-" as separator for the different title parts. The title value we want to use is taken from the configuration file. Afterwards we will add a second part to the title which better describes the page the user is visiting, that part and the first part will be separated by the separator we have defined.

Finally we add a path and a namespace for our "global" view helpers that we will put in the layout folder. Those helpers can be used by all the modules. View helpers are bits of reusable code, for the views. We will have specific module view helpers in "/modules/MODULE_NAME/views/helpers" that are related to that module, but we will also have "global" view helpers in the "application/layout" folder that can be used by any of the modules of our application.

	
    /**
     * VIEWS & LAYOUT
     *
     * @return Zend_View
     */
    protected function _initViewHelpers() {
		
        $this->bootstrap('layout');
        $layout = $this->getResource('layout');
        $view = $layout->getView();
        $view->setEncoding('UTF-8');
        $view->doctype('HTML5');
		
        $websiteTitle = $this->_configuration->website->title;
        $view   ->headTitle()->setSeparator(' - ')
                ->headTitle($websiteTitle);
		
        $view->addHelperPath(APPLICATION_PATH.'/layouts/helpers/', 'Application_Layouts_Helpers');
		
        return $view;
		
    }

First we set the useDefaultControllerAlways parameter to false, because we don't want the dispatcher to redirect our visitors to a default controller if no controller could be found. I don't think that of an SEO point of view, its a good idea to enable this, because every invalid url pointing to a non existing controller would redirect the user to the default controller. So multiple pages with different URL's could be delivering the same content, the search engines could think that you try to spam them with lots of duplicate content and finally punish you for that.

Then we remove all the default routes because we don't need them. We wont use /module/controller/action/parameters/ routes, we will use routes like /language/module/action/parameters/ and /language/admin/module/action/parameters/. We will create those routes in another article of this series.


    /**
     * DEFAULT ROUTES
     *
     * @return Zend_Controller_Router_Rewrite
     */
    protected function _initRoutes() {

        //ROUTING
        $this->_frontController->setParam('useDefaultControllerAlways', false);

        $router = $this->_frontController->getRouter();

        $router->removeDefaultRoutes();

        $this->_frontController->setRouter($router);

        return $router;

    }

That's it, our app is ready. The next step will be to create a module bootstrap and add some code to handle the routes, so that the frontController understands which action gets executed for a given URL.

This is the full bootstrap:


<?php

/**
 * MAIN APPLICATION BOOTSTRAP
 *
 * @author weber chris
 * @version 1.0
 *
 */
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap {

    /**
     * configuration variable
     *
     * @var Zend_Config_Ini
     */
    protected $_configuration;

    /**
     * frontcontroller variable
     *
     * @var Zend_Controller_Front
     */
    protected $_frontController;

    /**
     * AUTOLOADING
     *
     * @return Zend_Loader_Autoloader
     */
    protected function _initCore() {

        $autoloader = Zend_Registry::get('Autoloader');
        $autoloader->registerNamespace('Myapp_');

        if (APPLICATION_ENVIRONMENT != 'production') {
            $autoloader->suppressNotFoundWarnings(false);
        } else {
            $autoloader->suppressNotFoundWarnings(true);
        }

        return $autoloader;

    }
    
    /**
     * CONFIGURATION
     *
     * @return Zend_Config_Ini
     */
    protected function _initConfig() {

        $this->_configuration = Zend_Registry::get('Configuration');

        return $this->_configuration;

    }
    
    /**
     * FRONT CONTROLLER
     *
     * @return Zend_Controller_Front
     */
    protected function _initFrontControllerOutput() {

        $this->bootstrap('FrontController');
        $frontController = $this->getResource('FrontController');

        $response = new Zend_Controller_Response_Http;
        $response->setHeader('Content-Type', 'text/html; charset=UTF-8', true);
        $frontController->setResponse($response);

        $this->_frontController = $frontController;

        return $frontController;

    }
    
    /**
     * ERROR LOG
     *
     * to log errors in controllers use the logging action helper
     *
     * @return Zend_Log
     */
    protected function _initLogging() {

        $logsDir = APPLICATION_PATH.'/logs/';

        if (!is_dir($logsDir)) @mkdir($logsDir, 0755);

        // init error logger
        $logger = new Zend_Log();

        $writer = new Zend_Log_Writer_Stream($logsDir.'application.log');
        $logger->addWriter($writer);

        return $logger;

    }
    
    /**
     * VIEWS & LAYOUT
     *
     * @return Zend_View
     */
    protected function _initViewHelpers() {
		
        $this->bootstrap('layout');
        $layout = $this->getResource('layout');
        $view = $layout->getView();
        $view->setEncoding('UTF-8');
        $view->doctype('HTML5');
		
        $websiteTitle = $this->_configuration->website->title;
        $view   ->headTitle()->setSeparator(' - ')
                ->headTitle($websiteTitle);
		
        $view->addHelperPath(APPLICATION_PATH.'/layouts/helpers/', 'Application_Layouts_Helpers');
		
        return $view;
		
    }
    
    /**
     * DEFAULT ROUTES
     *
     * @return Zend_Controller_Router_Rewrite
     */
    protected function _initRoutes() {

        //ROUTING
        $this->_frontController->setParam('useDefaultControllerAlways', false);

        $router = $this->_frontController->getRouter();

        $router->removeDefaultRoutes();

        $this->_frontController->setRouter($router);

        return $router;

    }
    
}