Discovering the Zend Framework (0.2.0 Preview)

At ZendCon, I heard there was a new version of the Zend Framework being released. The version number is 0.2.0. That does not sound very stable, but I decided to give it a try anyway, something I did not do back when 0.1.0 was released. This was quite an adventure. The new release is incorporating major changes in the MVC framework and quite a few other libraries. Most of the changes in the MVC section are made to improve testability, or so I was told. Since new changes may break a few things, they were placed in the incubator folder until the next major release. This seemed all right to me.

I downloaded the source code for the framework, added the incubator libs and normal libs to the PHP include path (with incubator first, so it takes those in priority) and started playing with it. Actually, I did not get to play too long before I ran into some problems. It seems like the new version relies on some PHP 5.2 only features. There was no warning for this anywhere, or if there was, I did not run into them. Still, this is acceptable. The Zend Framework is meant to be PHP 5 only and should be switching to bleeding edge technology before it gets too large of a user base. Anyway, as a user of *ubuntu, the bundled version of PHP is 5.1.x, so I removed the incubator libs and decided to play with the old versions instead. I was told the changes were mostly internal, so I should be able to live without them and simply switch later.

MVC

I simply followed the documentation to build the skeleton to start from. Everything went very well. Documentation is one of the very strong aspects of the framework. The API documentation is a little poor (as in auto-generated from source code and function headers were also auto-generated), but the end user documentation is great for the most part. Some libraries are under-documented, but I guess this will be resolved in a near future.

Once I had the MVC framework set-up, I decided to build a small form and use some input, just to play with view and such. The Zend_View class is very simple, and yet very powerful. I never really liked template engines like Smarty or others. I think PHP does HTML display good enough, and this is the default behaviour of the View class. It does not do much more than call a PHP script and catch the output. It lets you assign parameters and get them from the template, but it really forces you to separate the display and logic, and suggest good coding practices.


<?php if ($this->books): ?>
    
    <!-- A table of some books. -->
    <table>
        <tr>
            <th>Author</th>
            <th>Title</th>
        </tr>
        
        <?php foreach ($this->books as $key => $val): ?>
        <tr>
            <td><?php echo $this->escape($val['author']) ?></td>
            <td><?php echo $this->escape($val['title']) ?></td>
        </tr>
        <?php endforeach; ?>
        
    </table>
    
<?php else: ?>
    
    <p>There are no books to display.</p>
    
<?php endif; ?>

I took this code sample straight from the documentation. I always liked this alternate syntax when writing conditions or loops in HTML. It simply looks clean. But the very nice part about this template is that a special escape method is defined to escape output. No need to ask yourself which function to use to escape the information. Actually, you can actually still ask yourself that question, because the function used internally by escape can be chosen. I think this is a very good decision on the long run. With all security alerts that come up, if one is actually affecting what ever function is being used, it can actually be redefined to handle a specific case and the entire code base will be fixed.

You probably noticed that the values were accessed as attributes. The Zend_View class allows you to define properties on the fly. I think it simply looks more elegant than calling an assign() method everywhere with a key and value. The view actually does even more. It manages those display helper functions for you. All you have to do is indicate the path where you store your helpers and you will be able to call your own functions in a fashion similar to the call to escape().

Defining the path to controllers, views and helper functions, that makes quite a lot of configuration. It does not matter at all, because all those things can be initialized in your index page, objects can be stored for access using the Zend::registry() function and you no longer need to touch any configuration. This is a very nice aspect of the Zend Framework. Everything is independent. Once you’ve set-up the __autoload() function, you no longer need to worry about including files, paths to scripts or anything. The right controller will be called, the view is managed and everything in between is magic.

Input filtering

After playing with the views, I decided to handle some input. Naturally, I looked at the Zend_Filter_Input class. This was probably the worst decision I made that day. I wasted a whole lot of time to end up figuring out that the testEmail() method was not implemented, which was totally undocumented. I decided to fall back to the ext/filter extension, which was not installed by default in 5.1.x and that I could not get installed using pecl. I had to install 5.2.0, so I did.

0.2.0 incubator

With the incubator issue resolved, I reactivated the include path for it. Bad surprises there. Very bad. Nothing was working any more. Even if most of the changes were actually internal, it seemed like quite a few changes were required in the initialization phase. The worst part was that none of the samples in the incubator documentation had been updated to reflect those changes. I somehow had the feeling that release was pushed out early for ZendCon. So I searched on the web to find some documentation about it. The 0.2.0 version had been released for quite a few days now. Someone, somewhere must have figured out how to solve this. I simply didn’t feel like searching for samples in the unit tests or read too much API documentation to find the changes.

I found this piece of code on some website (I removed some information that could help identify it, because really, it’s a shame it was ever published). The article actually had the mention ‘updated for 0.2.0’, and they seemed to be proud of it. Actually, they might have used the 0.2.0 release without the incubator packages, which really makes MVC the same thing as 0.1.0.

<?php

require_once 'Zend.php';

Zend::loadClass('Zend_Controller_Front');
Zend::loadClass('Zend_Controller_RewriteRouter');

$controller = Zend_Controller_Front::getInstance();
$router = new Zend_Controller_RewriteRouter();
$router->setRewriteBase('/');
$controller->setRouter($router);
$controller->setControllerDirectory('../app/controllers');

// View init
Zend::loadClass('Zend_View');
$view = new Zend_View;
$view->setScriptPath('../app/views');
Zend::register('view', $view);

$controller->dispatch();
?>

So, what was wrong about it? Well, it looked pretty much like the code I previously had. I can tell you, that had nothing to do with 0.2.0. The author sure did not try to run the samples. First, Zend_Controller_Front::getInstance() no longer existed. The front controller class was now a simple class that had to be created like any other object. Same for setRewriteBase(), it’s gone.

The other major change in 0.2.0, the path routing is now independent from the controller/action mechanism. In the previous release, a path like http://www.example.com/foo/bar would have called barAction() on the FooController class. Additional parameters could be passed in the URI using /foo/bar/key1/value1/key2/value2/, which is quite ugly. The new routing mechanism allows to define custom paths using a syntax like ‘article/:year/:id’, where elements starting with a colon are variables. Using the previous example, http://www.example.com/article/2006/10 would be a valid URL and send the ‘year’ parameter as 2006 and ‘id’ as 10 to the selected controller/action. So, extra steps are actually required to bring a compatibility layer to the previous version. This is what the code should look like.

<?php

include 'Zend.php';

function __autoload($class)
{
    Zend::loadClass($class);
}

$view = new Zend_View;
$view->setScriptPath('../app/views');

Zend::register('view', $view);

$router = new Zend_Controller_RewriteRouter();
$router->addRoute('compat', new Zend_Controller_Router_Route(
	':controller/:action',
	array( 'controller' => 'index', 'action' => 'index' ) ) );

$front = new Zend_Controller_Front;
$front->setControllerDirectory('../app/controllers');
$front->setRequest( new Zend_Controller_Request_Http(
	'http://www.example.com' . $_SERVER['REQUEST_URI'] ) );
$front->setRouter($router);

$front->dispatch();

?>

I had to spend quite a while reading API documentation, unit tests and bug reports to figure out a routing problem. The line with the setRequest call really should not be required. The framework should find out about it, just like it did in the previous version, but without it, there is no way to get anything routed to anything else than the default controller/action.

Other than this problem with the preview 0.2.0 release, the Zend Framework is really promising. The MVC pattern is highly inspired by Ruby on Rails, there is no doubt about it. In fact, the framework documentation is refering to the RoR documentation in the section about routing. There is a whole lot more to the framework than MVC thought, I will get to other components an other day.

4 thoughts on “Discovering the Zend Framework (0.2.0 Preview)”

  1. As the principal code monkey behind the 0.2.0 MVC changes, I thought I should reply.

    First off, when I developed, I was using php 5.1.4, the same version noted as the minimum version requirement for the framework. If you ran into issues indicating version specific functionality was needed, please file a bug report so we can fix this.

    Second, one of the last items you had was a comment about “setRequest() should not be required.” For the record, it’s not; Zend_Controller_Request_Http() is instantiated by default if no request object is registered. The main reason people are suggesting using it in their examples is that setRewriteBase() has been put into the request object instead of the router; this should probably be changed.

    As for the documentation, this was completely rewritten for the 0.2.0 changes, but, unfortunately, we do not yet publish incubator documentation in the official manual at http://framework.zend.com/manual. However, incubator documentation is now being built automatically and ported into the framework wiki at http://framework.zend.com/wiki/display/ZFDOC/Home. If you have suggestions for documentation improvements, I’d be very excited to hear them!

    That said, the RewriteRouter documentation was not updated in time for the release, and this was probably one of the most confusing changes to the MVC components. This documentation has been updated at this point in subversion, and the process should be clearer now.

    Based on your post, I already see a few action items that will dramatically increase user perception: reinstate the front controller’s getInstance() method; use Zend_Controller_Router() by default if none provided; add setRewriteBase() to both the router and front controller, proxying to the request object.

    Thanks for your detailed overview!

    (BTW, if there is an error submitting a comment, your blog does not retain the information from the previous post…)

  2. Actually, the getInstance() part does not really matter. It was more of a complaint about the unverified tutorial with bad examples I found. Having the front controller as a singleton or not does not bother me.

    For the setRequest(), I had to do it to get the routing to work. I noticed a bug report about Apache with virtual hosts or something like that, which seemed to be related, but it had nothing to do with my configuration. I do have a virtual host set-up locally, but the rewrite rules are placed in a .htaccess file.

    As for the version number, after upgrading my system, the bundled version now is 5.1.6 and it seems to work (altought I still need the setRequest() part). The error was relating to ReflexionClass::newInstanceArgs if I remember well. It might have been a problem with something else, causing a wrong call. I have to admit I’m a bad bug reporter.

    Great work so far!

    (As for the problem with the blog, I have no idea. Maybe I should try to see if there is an update.)

  3. I don’t know about other tutorials, but mine certainly does not take the incubator into account. The incubator code is far too much like a moving target and will see many changes before getting into the main library.

    The MVC code is a good example. The code in the incubator now is significantly different from what was released with 0.2.

    Regards,

    Rob…

Leave a Reply

Your email address will not be published. Required fields are marked *