PHP Zend Framework Hello World Tutorial under Apache + Linux

The following tutorial shows the steps involved in creating a simple Hello World example for the free PHP Zend Framework using Apache server on a Linux openSUSE platform. I am assuming here you are familiar with the concept of the Model-View-Controller (MVC) - if not, click on the link and do some mugging up on Wikipedia.

Firstly, we need to create the directory structure for our project. My project is going to be called api - not quite arbitrary since the project will ultimately morph into a REST web service, but for now lets settle on something less ambitious. So the directory structure will look like:

laptop4:/ # cd /srv/www/htdocs
laptop4:/srv/www/htdocs # mkdir api
laptop4:/srv/www/htdocs # mkdir api/application
laptop4:/srv/www/htdocs # mkdir api/application/controllers
laptop4:/srv/www/htdocs # mkdir api/application/models
laptop4:/srv/www/htdocs # mkdir api/application/views
laptop4:/srv/www/htdocs # mkdir api/application/views/scripts
laptop4:/srv/www/htdocs # mkdir api/application/views/scripts/index
laptop4:/srv/www/htdocs # mkdir api/library
laptop4:/srv/www/htdocs # mkdir api/public
laptop4:/srv/www/htdocs # mkdir api/public/css
laptop4:/srv/www/htdocs # mkdir api/public/img
laptop4:/srv/www/htdocs # mkdir api/public/js
laptop4:/srv/www/htdocs # chown -R wwwrun:www api
laptop4:/srv/www/htdocs #

The library directory will soon contain our Zend Framework but lets wait for a second. Next we'll edit our Apache server configuration file httpd.conf. I am running multiple websites on my development laptop so I will be using VirtualHosts. Add the following:
/etc/apache2/httpd.conf
Alias /api /srv/www/htdocs/api
<VirtualHost *>
ServerName affiliateapi.com
DocumentRoot /srv/www/htdocs/api

<Directory /srv/www/htdocs/api>
  Options -Indexes FollowSymLinks Multiviews
  AllowOverride All
  DirectoryIndex index.php
  Order allow,deny
  Allow from all
</Directory>

  RewriteEngine Off

  <Location />
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule !\.(js|ico|gif|jpg|png|css)$ /index.php
  </Location>
</VirtualHost>

You will obviously have to change all references to api to whatever you want to call your project. Next we need to reboot our Apache server so the changes take effect.
laptop4:/srv/www/htdocs # /etc/init.d/apache2 restart
Syntax OK
Shutting down httpd2 (waiting for all children to terminate) done
Starting httpd2 (prefork) done
laptop4:/srv/www/htdocs #

I like checking my work as I go along - so lets check that the Apache configuration is correct. Create a quick and nasty regular page controller version of index.php in the api document root.
api/index.php
<?php
   
print "Hello World!";
?>

You should now be able to point a web browser at localhost/api/index.php or localhost/api. Note this is NOT MVC!! This is NOT the final solution!! This is just an interim check to see if the Apache configuration is correct.

Ok, now we need to download the latest version of the Zend Framework. I downloaded the minimal 1.10.7 zipped tarball from http://framework.zend.com/download/latest. My browser downloads to the /tmp directory where we will unzip and untar, then copy over the library itself to the api/library directory for our development.

laptop4:/> cd /tmp
laptop4:/tmp> ls -lash Zend*
3.5M -rw-r--r-- 1 badzilla users 3.5M 2010-08-09 17:36 ZendFramework-1.10.7-minimal.tar.gz
laptop4:/tmp> tar zxf Zend*
laptop4:/tmp> cd /srv/www/htdocs/api/library
laptop4:/srv/www/htdocs/api/library> cp -R /tmp/Zend*/library/* .
laptop4:/srv/www/htdocs/api/library> ls
Zend
laptop4:/srv/www/htdocs/api/library>

Now we need our MVC controller bootstrap to get things going. Unsurprisingly, it will be the index.php file. So open up the one we created before in fun, and over type the content with:
api/index.php
<?php

    $rdir
= realpath(dirname('SCRIPT_NAME'));

   
set_include_path($rdir . '/library' . PATH_SEPARATOR . get_include_path());

    require_once(
'Zend/Loader.php');

    try {
       
Zend_Loader::loadClass('Zend_Controller_Front');

       
$fcontroller = Zend_Controller_Front::getInstance();

       
$fcontroller->throwExceptions(TRUE);
       
$fcontroller->setParam('noErrorHandler', TRUE);
       
$fcontroller->setControllerDirectory("$rdir/application/controllers");

       
$fcontroller->dispatch();

    } catch (
Exception $e) {
       
$contentType = 'text/html';
       
header("Content-Type: $contentType; charset=utf-8");
        print
'An unexpected error occurred:';
        print
'<h2>Unexpected Exception: ' . $e->getMessage() . '</h2><br /><pre>';
        print
$e->getTraceAsString();
    }
?>

Some notes to accompany our bootstrap.

$rdir = realpath(dirname('SCRIPT_NAME')); I had some major grief here - the purpose of this code is to get the document root of the Virtual Host api but my first few stabs at it were returning the document root of the master virtual host on my machine - my Badzilla development domain. I finally stumbled upon the combination I used with the "SCRIPT_NAME" as the constant that pins Apache down to the correct virtual host.

set_include_path($rdir . '/library' . PATH_SEPARATOR . get_include_path()); Add the Zend library to the existing PHP search path.

require_once('Zend/Loader.php'); Include the Zend loader class.

Zend_Loader::loadClass('Zend_Controller_Front'); Load the front controller.

$fcontroller = Zend_Controller_Front::getInstance(); Get the singleton instance for the front controller. If you are a little shaky on Object Oriented PHP, check out http://en.wikipedia.org/wiki/Singleton_pattern.

$fcontroller->throwExceptions(TRUE); We want to be able to see if something goes wrong!

$fcontroller->setParam('noErrorHandler', TRUE); But as this is simple example code, we won't be using the exception handler that comes with the Zend Framework.

$fcontroller->setControllerDirectory("$rdir/application/controllers"); Zend is not prescriptive - we can use any directory structure we want, so we'll tell it that we are sticking to convention as to where we put the controller.

$fcontroller->dispatch(); Let's rock and roll Smile

catch (Exception $e) No need for lengthy explanations here - display any error diagnostic.

No Controller ErrorAs I said before, I love to check my work as I am progressing, so time to check out the bootstrapper. Obviously it'll throw an exception, but hey, that's what I am expecting! If we get an exception error then everything is working as it should i.e. no runtime 'compilation' errors - but double check your Apache log just in case. We can see that it is saying the controller was not found - of course not since we haven't coded it yet. So lets do it next.

The index controller needs to go in the directory we have already set up for it, so:
api/application/controllers/IndexController.php

<?php


class IndexController extends Zend_Controller_Action {

    public function
indexAction() {

       
$this->view->assign('title', 'Hello World!');
    }

}
?>

We need to be a little bit savvy here - the file must be called IndexController for Zend to be able to find it, the class must be IndexController and the function must be indexAction. In other words, the convention is:

function named {actionName}Action() within {ControllerName}Controller class within file {ControllerName}Controller.php

Aside from that, the function itself doesn't need to do too much - so all we are doing is assigning the title to be displayed in our template file. So lets create the template file:
api/application/views/scripts/index/index.phtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 STRICT//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<title>
    <?php echo $this->escape($this->title); ?>
</head>
<body>
    <h1><?php echo $this->escape($this->title); ?></h1>
</body>
</html>

The template file is saved with the name of the controller in the views/scripts/index directory, and with the .phtml extension - these are requirements of the ViewRenderer which is part of the Zend_Controller_Action inherited by our indexController class. You will also note that we are passing our output through $this->escape which converts special characters to HTML entities.

Hello World!Ok - that's it - if you point your web browser to localhost/api you should now see our Hello World! text. Not too difficult at all! Laughing out loud

Hopefully that'll give you the confidence to have a go at Zend Framework coding - nothing like as scary as you might have initially thought. Now lets move on to something a little more sophisticated, and something that actually achieves something useful (-ish) - my simple Zend REST server,



a little modification

Interesting explanation. I'm using your documents to teach Zend framework,
you have the patience to explain everything! In the index template,
you must close the title tag. I like to use that wrong page as the boys must also show me they understand how to find bugs! Smile