I first discovered Zend Framework in June 2007, it was love at first sight, the perfect framework in my opinion. Zend Framework truly applies the definition of a framework, a collection of tools that are repeatedly used while developing web applications.
The hardest part was bootstrapping it, without any good tutorial at hand it was time to get my hands dirty.
Most of the examples around the web show you how to use the MVC part of the framework (setting up the controller) but to be honest, most of the web apps these days use databases, sessions and what not.
Here is a short example on how to tie Zend Framework's shoelaces and get your application up and running.
The directory structure I usually have in place is:
/application
/application/config
/application/controllers
/application/data
/application/library
/application/models
/application/tmp
/application/views
/www
As a security measure, I always place the application core (anything else apart from javascript, css and multimedia files) one level above the document root of my Apache server, so no files are directly accessible to the user. I will at the moment assume you do have access to one level above the document root. I will also assume you do have at least medium PHP experience.
Firstly lets define some constants to make our work easier and the application more cross environment.
<?php
/**
* Constants
*/
define('APP_DIR', dirname(__FILE__) . "/application/");
define("WEB_DIR", dirname(__FILE__));
date_default_timezone_set('Europe/London');
I have also set the timezone, as required by PHP 5 and also by Zend_Date
Now continuing with our "work optimization", lets create an autoloader so we don't have to worry about including files from the Zend library.
set_include_path('.' . PATH_SEPARATOR . APP_DIR . 'library'
. PATH_SEPARATOR . get_include_path());
include "Zend/Loader.php"; // Main Zend loader class
include("Loader.php"); // Custom loader class
Zend_Loader::registerAutoload("My_Loader");
I have extended the Zend_Loader class to include my own custom directories, you can find my custom loader file attached (Loader.php)
Now, before anything else, we should configure our application (database settings, base paths, and what not). My config files are always placed in /application/config and I usually have a main config.php which holds misc configuration, and database.php which obviously holds my database access details.
/**
* Deal with the configuration
*/
require_once(APP_DIR . 'config/config.php');
try {
$config = new Zend_Config($config);
} catch (Zend_Exception $e) {
echo "Error: " . $e->getMessage();
}
Zend_Registry::set("config", $config);
The $config variable is set in the config.php file and its formed of an array. The log level in the config array tells us what to log (everything or just errors mainly). When in the development stage, I usually like to know everything that happens with my application, from errors to loading times.
As you can see I have used Zend_Registry, Zend_Registry is, as the framework manual rightly says, a "container for storing objects and values in the application space". By storing objects in Zend_Registry we have access to those objects everywhere in the application.
Talking about logs, lets create a log formatter and a file writer. A log formatter obviously makes our logs pretty (lol). In Zend Framework the logger is made up by multiple components, one of them is the log formatter, another is the writer. A writer specifies the target of our logs.
/**
* Create a log formatter
*/
$format = '%timestamp% %priorityName% (%priority%): %message%' . PHP_EOL;
$formatter = new Zend_Log_Formatter_Simple($format);
/**
* Create a file logger
*/
$stream = @fopen(APP_DIR . "tmp/logs/" . date("Y-m-d") . ".php", 'a', false);
if (!$stream) {
throw new Exception('Failed to open stream');
}
try {
$fileWriter = new Zend_Log_Writer_Stream($stream);
$fileWriter->setFormatter($formatter);
$fileLogger = new Zend_Log($fileWriter);
Zend_Registry::set("fileLogger", $fileLogger);
} catch (Zend_Log_Exception $e) {
echo "Error: " . $e->getMessage();
} catch (Zend_Exception $e) {
echo "Error: " . $e->getMessage();
}
I usually organize my logs on a day to day basis to make the log files shorter and easier to read.
If everything went fine, our file logger is now $fileLogger. To log a simple message use:
$fileLogger->info("My first log message");
Or to log a message with a log level, use:
$fileLogger->log("My warning", 4);
This would log a warning. Zend_Log has a list of predefined log priorities which you can find here: http://framework.zend.com/manual/en/zend.l...ltin-priorities
Moving on to the database setup:
/**
* Deal with the database
*/
$options = array(
Zend_Db::AUTO_QUOTE_IDENTIFIERS => true,
);
try {
$db = new Zend_Db_Adapter_Pdo_Mysql(array(
'host' => $config->database->host,
'username' => $config->database->user,
'password' => $config->database->pass,
'dbname' => $config->database->name,
'options' => $options,
));
$db->getConnection();
Zend_Db_Tablet::setDefaultAdapter($db);
Zend_Registry::set("database", $db);
if($config->log_level == 2) {
$fileLogger->info("Database setup complete!");
}
} catch (Zend_Db_Adapter_Exception $e) {
$fileLogger->log('Database Error: ' . $e->getMessage(), 1);
} catch (Zend_Exception $e) {
$fileLogger->log('Error: ' . $e->getMessage(), 1);
}
Everything should be pretty self explanatory in the above piece of code. Zend_Db_Table is our main model class, all the Zend Framework models extend this class, which is the interface to our database tables.
Now that we have a database connection in place, we can also create a database logger, for those times when FileZilla or Putty are not at hand and all we have is Firefox.
/**
* Create a database logger
*/
$columnMapping = array('level' => 'priority', 'message' => 'message');
try {
$dbWriter = new Zend_Log_Writer_Db($db, 'application_log', $columnMapping);
$dbLogger = new Zend_Log($dbWriter);
if($config->log_level == 2) {
$fileLogger->info("Database logger created!");
}
Zend_Registry::set("dbLogger", $dbLogger);
} catch (Zend_Exception $e) {
$fileLogger->log("Error: " . $e->getMessage() . " - " . $e->getFile() . " - Line: " . $e->getLine(), 4);
}
The table name is "application_log" and the table columns are: id (int autoincrement), level (int), message (varchar), timestamp (timestamp default: CURRENT_TIMESTAMP).
Now that all of those are set, lets get sessions out of the way.
/**
* Setup sessions
*/
try {
Zend_Session::setOptions(array(
'save_path' => APP_DIR . "/tmp/sessions",
'remember_me_seconds' => 7200,
));
Zend_Session::start();
} catch (Zend_Session_Exception $e) {
$dbLogger->log('Error: ' . $e->getMessage(), 1);
$fileLogger->log($e->getMessage(), 1);
}
$defaultNs = new Zend_Session_Namespace('default');
Zend_Registry::set("defaultNs", $defaultNs);
if($config->log_level == 2)
{
$fileLogger->info("Sessions setup finished!");
}
In Zend, sessions are based on namespaces and you can set as many namespaces as you want. To set a value in a namespace you can just assigned like so:
$defaultNs->variable = "value";
To retrieve it:
echo $defaultNs->variable;
Now that we have all those set, the final touch, setting up the controller.
/**
* setup controller
*/
try {
$frontController = Zend_Controller_Front::getInstance();
$frontController->addControllerDirectory(APP_DIR . "controllers");
$frontController->dispatch();
if($config->log_level == 2)
{
$fileLogger->info("Dispatched!");
}
} catch (Zend_Exception $e) {
$dbLogger->log("Error: " . $e->getMessage() . " - " . $e->getFile() . " - Line: " . $e->getLine(), 1);
$fileLogger->log($e->getMessage(), 1);
}
Fairly easy huh?
There are many ways in doing all this, you could create a class for each of the modules (sessions, database, etc) and set them up dynamically, when you need them, where you need them, but I prefer having the base environment set up and ready to go.
For more info check out the Zend framework manual at: http://framework.zend.com/manual/en/en
Hope this helps.
If you have any questions on Zend Framework, don't hesitate to ask.
Loader.php
config.php