Setting Up a Zend Framework Project From Scratch

I’m finally back with yet another tutorial about Zend Framework. It has been a long time since I last written because of the work load I currently have. So with the release of the Zend Studio 7.0 (or The Rise of Zend_Application and Zend_Tool), I have created a new Zend Framework Project with it. Just to see how the Zend_Application and Zend_Tool duo is working. And I can say that it’s pretty good.

I will try to mention some goodies about Bootstraps thing which It took me nearly two days to fully understand how this was working. Finally there is a easy way to configure your project’s settings with an ini file.

So let’s look for the worst part: The initial directory structure creation. Everyone has it’s own directory structure. You can find out different setups on different blogs and my setup is just a setup created by the Zend Studio 7.0’s new project tool. I will stick with it and assume that everyone else will use it including the files created with it.

Here is a screenshot of my directory structure:

zf_initial

So you can see that it’s pretty easy setup and it includes nearly everything you need to go. So let’s mention what directory holds what:
app contains my whole application with controllers, views, models, configurations and other kind of project related things.
library holds the third party code libraries which will be used in my project
public contains everything that can be accessed via a web client. It’s my www directory. As the app folder cannot be reached by a web page request it must not be put in a directory which can be accessed publicly.

So I had said that you are ready to go but I suppose you cannot yet go to your project’s web page using: http://localhost/KartacaZFProject/ and you have to write http://localhost/KartacaZFProject/public/. and this is generally something that I don’t want. So you will need to add an aliasfor this in the apache, if you are on windows and using wamp server it’s pretty just left click on the wamp tray icon select Apache and then add an alias. If you are on a *nix environment, I’m sure you know what you should do :)

So let’s come to the Bootstrap thing which was the most confusing part. Which one is the real initializing file? /public/index.php or /application/Bootstrap.php? The answer is both and there is third file named /application/configs/application.ini. ini file is the most important part I guess. Of course that your configuration file can only be Bootstrap.php file and you can set everything from there but, I prefer an ini file instead of a php class as a configuration file. So let’s get to the relation with the index.php, Bootstrap.php and applcation.ini files.

First, every web page request is done to the index.php file and the request is dispatched to controllers from this point. The content of the index.php file is:

// Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    get_include_path(),
)));

/** Zend_Application */
require_once 'Zend/Application.php';  

// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV, 
    APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap()
            ->run();

these lines in creates two constants and and sets the new include path. Well remember the part where library folder was holding the third part code libraries. This was the part where that folder is added to the include path.

The part which can be confusing is the APPLICATION_ENV part I guess. This is a new concept for most of the PHP developers I guess. Generally PHP developers (talking about freelancers not the corporate people) have just one environment used for development and production. What some one said testing environment? What’s that? That’s a general problem for developers to ignore everything about tests. So Zend Framework brings this. The best concept for seperating development, testing and production environments. You can set different options for different envrionments or you can inherit from another environment. Details later. You can change the environment you are using from here.

So and from the below the real magic begins. A new Zend_Application object is created with options as environment and configuration file (application.ini) and bootstraping is called.

//code code code
$application->bootstrap()
//code code code

This line is the key for the parsing application.ini file. So now we are at the Bootstrap.php file lets look to it’s content:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
}

Dissapointed? Surprised? Impressed? Or, just like me, confused? How is the application.ini file is related with Bootstrap.php. So before bootstrap we should look for the application.ini

[production]
phpSettings.display_startup_errors = 0
phpSettings.display_errors = 0
includePaths.library = APPLICATION_PATH "/../library"
includePaths.model = APPLICATION_PATH "/models"
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "root"
resources.db.params.password = "the_really_secret_password"
resources.db.params.dbname = "dbname"
resources.db.isDefaultTableAdapter = true

resources.layout.layout = "myLayout"
resources.layout.layoutPath[] = APPLICATION_PATH "/layouts/scripts"
resources.layout.layoutPath[] = APPLICATION_PATH "/layouts/generics"


;This one means inherit from the production values.
;So the testing env inherits the values from the production env
; every value defined in the production is also defined in the testing yet
; if value is defined in both, value defined in testing env is used.
[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1


[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

Looks pretty simple but it’s still confusing and we are missing the part where the database connection is made and layout defined (yes layout part is not seen in the screenshot but you can guess where it will be located) and other things. Now that’s the magic of Zend_Application.

So let’s get how the things are configured. Notice the “resources.db” part. This one is mapped to a function like _initDb in the Zend_Application_Bootstrap_Bootstrap. And the other values defined in the resources.db are used in that function. The same applies for layout too. But I should be able to override this as I may require to do some changes. So let’s return to our Bootstrap.php file and change it like:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initDb()
    {
    }
}

Now if you refresh the page you will notice that you don’t have a database connection anymore. Because, db variabled were initialized using _initDb() and you overriden it in your Bootstrap. So instead of overriding this method, let’s use another method and change things about db from there. How can I do this?

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    protected function _initProjectSpecific()
    {
        $this->bootstrap("db");
        $db = $this->getResource("db");
        $db->setProfiler(new Zend_Db_Profiler_Firebug());
        $db->getProfiler()->setEnabled(true);
    }
}

What I have done is this: I have bootstraped my db configuration using bootstrap() method, then I have “get”ed the bootstraped “resource” using getResource() and then I have enabled the profiling for database queries. But I can do this on the application.ini file without writing any code at all like this:

//Find the block with resources.db and these lines below them.
resources.db.params.profiler.enabled = true
resources.db.params.profiler.class = Zend_Db_Profiler_Firebug

That’s pretty cool and easy but you have to know the right syntax for the right option you want to enable. You could have noticed that params is a part of the constructor for the Zend_Db_Adapter and the next word after the dot is the array key of the array. In the case of the profiler, it’s a nested array. If you can send arrays to the constructors you can define them on the application.ini. For an example you can do something like:

    $params["profiler"] = array("class" => "Zend_Db_Profiler_Firebug", "enabled" => true);

You can see, still application.ini is not very easy but it’s very understanding and I’m sure you will get used to it once you understand the relation I have just mentioned.

So everything is fine like this, but what if I want to add some custom initializing things what should I do?

//Add these lines before [test : production], meaning to the production env.
logging.enabled = 1
logging.level = 3
logging.writer[] = Zend_Log_Writer_Firebug
logging.writer[] = Zend_Log_Writer_Stream
logging.filename = error.log

So I want a logger to log things that happens on the site, functions that I call during a request etc. How will be the Bootstrap part? Add this method to the Bootstrap class:

    protected function _initMyLoggingSettings()
    {
        $logger = new Zend_Log();
        $logger->addFilter(new Zend_Log_Filter_Priority((int) $this->_options["logging"]["level"]));
        foreach($this->_options["logging"]["writer"] as $wr) {
            if ($wr == "Zend_Log_Writer_Stream") {
                $w = new $wr($this->_options["logging"]["filename"]);
                $logger->addWriter($w);
            } else {
                $logger->addWriter(new $wr());
            }
        }
        Zend_Registry::set("logger", $logger);
    }

That’s really easy isn’t it? How is this function even called? Protected methods of the Bootstrap class starting with an “_init” is automatically called during initilization. So he keyword is “_init”. So the next point is get the settings from application.ini file. That’s easier. You can get the contents of the application.ini with an array as $this->_options. The part were you can be confused is the “[]” in the ini file. This means there will be an array. So I have two log writer, one will write to a file and the other will use the firebug/firephp duo. I suppose code block talks for itself too.

Let’s get to the final point, routes. An MVC framework is generally nothing without routes. So let’s create a file like /application/configs/routes.ini and add these lines in it.

routes.indexItem.route                        = index
routes.indexItem.defaults.module       = default
routes.indexItem.defaults.controller    = index
routes.indexItem.defaults.action          = index

routes.indexItem2.route                       = foo
routes.indexItem2.defaults.module      = default
routes.indexItem2.defaults.controller  = index
routes.indexItem2.defaults.action        = index

and on the bootstraps I should add a method like this:

    protected function _initSiteRoutes()
    {
        //Don't forget to bootstrap the front controller as the resource may not been created yet...
        $this->bootstrap("frontController");
        $front = $this->getResource("frontController");
        //Read the routes from an ini file and in that ini file use the options with routes prefix...
        $front->getRouter()->addConfig(new Zend_Config_Ini(APPLICATION_PATH . "/configs/routes.ini"), "routes");
    }

So when I go to the http://localhost/public/index I will see my index page and also if I visit http://localhost/public/foo, I will, again, see the index page. The relation with the router and the ini file is just like in the application.ini. They are keys of an array. You can change the action or controller or module for foo if you want.

So this is it. We have used application.ini and Bootstrap.php files along with a new file routes.ini and our project is ready to go and improve with my cool idea.

I hope everything in the article was helpful. It took me nearly two days or something to figure out these things, I googled a lot of places, especially ZF manual which is very useful and tried to wrap everything I found in a single post. Improvements and additional links are welcomed because I still did not learned all the magic of Zend_Application.

Update: I have included a simple module in the modules directory and how you can bootstrap it to explain the usage of modules in your app. You can check it out from my github.