Monthly Archive for March, 2010

Why I Gave Up on the Zend Framework

The Zend Framework is a really nifty thing. Really, it is. The amount of functionality that you get merely by installing it is extremely exciting: internationalization, forms, an MVC layout for your program, a cute class loader, a database abstraction layer, a templating engine, a request dispatcher, mail-sending functions, pretty debugging “dump” functions… and there are so many people working on it and using it that basically all the bugs left in there are shallow. It has been a staple dependency of many of my projects for quite a while now, and still is.

Zend Framework is actually available for your projects in two flavors, «use what you need» and «obey the hive mind», with a continuous spectrum in-between these two extremes.

We are Zend. Resistance is futile.

We are Zend. Resistance is futile.

The «use what you need» approach leaves the maintenance programmers with a warm and fuzzy feeling. All you have to do is dump all the framework files somewhere in your include path, include the files for the bits you want to use, and just call the functions. The framework takes care of recursively including the appropriate dependencies for you and carefully avoids treading on any toes by prefixing everything with «Zend».

In fact, if you use Zend_Loader, you can skip the include-source-files step completely (except for Zend/Loader.php obviously), and since auto-loading is reverse-compatible with loading files manually, it’s also a good step towards a well-deserved refactoring.

So, if you need to send multi-part mail, with HTML-and-text content, in UTF-8 format, you can just use Zend_Mail and everything will work fine regardless of the rest of the code base. There are dozens of such small features (for PDF generation, LDAP, access control, localization, and so on).

There is virtually no excuse for not using a plug-in class from the Zend Framework in your application if it solves the problem you’re having. Besides, since the files are not included until you need them, the worst that could happen is that you’re having some PHP code taking up a few megabytes of disk storage for nothing. So I have a lib/Zend directory on all my projects, just in case I need something.

Obey the Hive Mind

While many pieces of Zend are independent of each other, there’s a central functionality core that’s designed to act well together. There are many examples:

  • it’s easier to use Zend_Dispatcher and Zend_Controller together.
  • it’s easier to render a Zend_View if you’re also using Zend_Controller.
  • it’s easier to turn a Zend_Form into HTML if you’re using Zend_View.
  • it’s easier to set up a “login already in use” validator with Zend_Form if you have a field in a Zend_Db_Table to connect it to.
  • it’s easier to translate Zend_Form error messages with Zend_Translate (and Zend_Registry).

Sure, it’s usually possible to take advantage of 99% of the functionality without having to add new dependencies, but there’s always that tiny voice in the back of your head, nagging that you could get that additional 1% so easily if you just gave in.

Giving in means, of course, going all the way to Bootstrap heaven: now your project is laid out across the lines of the ideal Zend Framework template, your files cleanly stashed in their folders with a cosmic Feng Shui feeling to it all, and the Zend approach to MVC pervades your every HTTP request.

This isn’t so bad: actually, such an approach has some huge selling points for shops that write lots of small projects, such as the ability to get 20% of your basic functionality up and running in days, the ability to hire any Zend-certified developer and not have to educate them about the framework, and you don’t need them lousy architects on your team.

I’ve had some trouble with the Zend way before, though. There are some bits of functionality that I won’t touch with a ten-foot-pole, such as Zend_View, Zend_Controller or Zend_Db_Table, because the havoc they wreak in situations I find myself in outweighs the benefit.

Documentation

My main issue is that I find Zend quite lacking on the documentation side.

«But the Zend Framework is possibly the most documented there is!» you say, before trailing off in a rant about how the “FM” should be “R” and the “FW” should be “S”.

You’re probably right. But I don’t really care about that documentation. I’m talking about project documentation—to know what happens in code written by my team.

«What does Zend have to do with that? Document your code, you lazy slob!»

Humans are lazy, and I would argue that laziness is actually an essential quality of a good programmer. I can require that documentation be written, but I expect it to be missing, inaccurate or monosyllabic. Things like that happen when you’re rushing out a bug patch at 3:00 am. And even if I could ensure that documentation is written and kept up to date, I’d rather have my code be self-documented—not only does it take less time, but it’s harder to get inaccurate self-documentation and you can even get the language to check things for you.

It’s the difference between documenting the parameter type as a @param MyClass $obj in a comment and documenting it as a MyClass $obj type hint in the function signature.

Look at the average .phtml template, and you’ll see something like this:

<div>
  ...
  <a href="<?php echo $this->getUrl() ?>"><?php
    echo $this->escape($this->user->name)
  ?></a>
  ...
  <?echo $this->partial('preferences.phtml', $this->pref); ?>
  ...
</div>

Half the point of a view in the MVC approach is that I should be able to easily reuse that view from any controller, or even from within another view. Of course, Zend lets me do this very easily:

$view = new Zend_View();
$view->xxx = yyy; // Fill in members
$view->render('template.phtml');

The red line, of course, is where trouble begins. Since Zend_View fields are by definition dynamic, there’s no way to get auto-completion to help you find what they should be. Nor can you look at a list of these fields in a class definition or function definition, because there’s none. You have to read the template file and find out by yourself what values are used by the template and what their types should be. Oh, and if the template passes some of that data to other templates, you have to read those templates too, because they might use specific information. And you have to look at view helpers too, because they might be accessing view elements behind your back.

Your best bet is to look at an existing controller that uses the view, and hope that you don’t stray too far from what that controller is doing. You never know: a certain member might be expected to be present if another has a certain value (this never happened with the first controller, but it happens in yours), there’s no compiler checking that all values are being provided appropriately, and runtime testing doesn’t reveal such special cases on the first try.

And they say Zend_View is an object-oriented approach to rendering…

The most important aspect of Zend_View templating is that it is object oriented. You may use absolutely any value type in a template: arrays, scalars, objects and even PHP resources. There is no intermediary tag system between you and the full power of PHP. Part of this OOP approach is that all templates are effectively executed within the variable scope of the current Zend_View instance. To explain this consider the following template.

That’s not what object-oriented means. OOP means if two views behave differently, then they should be instances of different classes, instead of injecting arbitrary code and data into a single class and spitting in the face of encapsulation.

The bottom line is that reusing Zend_View templates is a pain in the derrière unless you take special steps about it (steps that you wouldn’t need with a standard class-with-members).

What’s in that row?

This is futher compounded by the way Zend_Db works: an ORM that generates SQL from a sequence of PHP calls, and then turns the result into a list of Zend_Db_Table_Row objects. Which leads to the question of what fields can be found in a given row, and that question is hard to answer.

A typical application will follow a rule along the lines of «every table row is, by definition, a row of a table, so you just peek at the table definition and you know that each column is mapped to a field,» and that is a fine rule to follow, because then the only issue is you can’t type-hint the row based on the table, so you can’t make sure a given argument is always a row from “account”.

But following that rule is hard. In addition to those 80% plain old CRUD cases where you’re working with a single table at once, you’ll have those 20% that use joins where you need data from both tables (never mind the pain of doing that in PHP). Then you end up with a row that breaks the rule, so you keep it in tightly enclosed areas of your application, until it gets too frustrating not being able to use a view-that-renders-accounts on a record-that-contains-accounts-and-sessions, and the next thing you remember is that you don’t know if a given view expects an account or an account-and-session.

And the language can’t help you.

Auto-complete me

Nor can your editor, for that matter, since auto-completing $row-> requires knowledge that your editor simply cannot have (the list of columns defined when you configured your Zend_Db_Table).

I really do enjoy it when my code editor helps eliminate some of the tedium of writing code. In fact, I’m quite ready to make a small additional effort tagging my members, arguments and functions with some type information just so that writing code can be easier.

My editor is Eclipse PDT. It has several nice features that I use extensively.

The first is, of course, its ability to suggest members of classes and objects. Having well-defined classes to represent your data means that Eclipse can use the type hints you leave around to determine that $account is of class Account, so that it has a $firstname member. That’s:

  1. one less round-trip to the database documentation
  2. zero chances of typing $account->firstName by mistake
  3. being told immediately if $account has entirely different members (because it’s another type)

Since Zend_Db_Table_Row and Zend_View actually go out of their way to make sure that you can have arbitrary data in there based on runtime considerations, getting this functionality out of them is impossible.

The other nice feature I use a lot is the ability to control-click a class or function to see its definition. This lets me navigate around the code in seconds instead of having to open the project file explorer, expand several layers of directories usually far from each other, and spend precious brain power translating a class/member naming scheme into file naming schemes.

Finding a file is a job for the editor, not for the programmer.

My view helpers look like this:

View_Account::renderSimple($account);

Clicking on that function name brings up the file and scrolls it down to where it matters. Took me less than a second. Zend View Helpers look like this:

$this->renderSimpleAccount($account);

I dare anyone to navigate to the definition of that helper in less than a second. [EDIT: apparently I shouldn't dare people on the internets :) ]

What about links? The typical approach to generating a link to a different part of a site, with the Zend Framework, is to spell out its controller and action:

<a href="<?php echo $this->url(array(
  'controller' => 'user',
  'action' => 'edit',
  'id' => '123'
));?>">click me!</a>

Now you have to click on every single URL on your website to make sure links are correct and you still manage to forget one and the end user will click on that link that’s spelled out as «edti». And even if you do get it right, you still have to navigate to the appropriate controller class, open it up and scroll down the right action.

My urls look like this:

<a href="<?=Action_User_Edit::url(123)?>">click me!</a>

Since every one of my actions is a class (as opposed to a function in a controller class), they get to have members, and one of these members is a static url() function that:

  • lets me ctrl-click through to the action itself
  • has PHP check that my link is correct (or else, die with a class-not-found answer)
  • documents the expected URL arguments as function arguments
  • even lets me find out who links to a certain controller, in case I have to move it

The bottom line…

…is that I don’t use Zend_View, Zend_Controller or Zend_Db in my projects. I need my code to be self-documenting, and there’s nothing self-documenting about Zend_View or Zend_Db. I need my code to be easy to navigate through and simple enough for my editor to handle, and the full dynamic behavior of Zend_View and Zend_Db prevent that.

Your needs might be different. Are they?

Affordances

We are conditioned by experience and education to interact with certain things in certain ways.

We expect to find little crosses in the top right corner of things we can close. Rectangular-ish elements that stand out from the page can be clicked for effects. The same goes for underlined text, but we know nothing dangerous will happen (a button can delete a file, a link can’t). A straight left-pointing arrow means “back” while a curved left-pointing arrow means “cancel”. Scissors mean “cut” and ALL CAPS MEANS SHOUTING.

Some of these affordances stick around even after the original reason they appeared is long gone. How long until we have to tell our kids why the save button looks like this:

disk

PHP 5.3 Closures as Block Literals?

I explained earlier a few things about writing reusable CSS code, and how it interacted with PHP. Let’s start with this basic HTML for generating two columns, with the right one being flexible and resizing to fill all available space:

<div class="col2">
  <div class="col2-l">
    [Content of left column]
  </div>
  <div class="col2-r">
    <div class="col2-ri">
      [Content of right column]
    </div>
  </div>
  <div class="clearer"></div>
</div>

.col2    { }
.col2-l  { float: left ; padding: 0 ; margin: 0 ; width: 120px }
.col2-r  { padding: 0 0 0 120px ; margin: 0 ; width:auto }
.col2-ri { float: left ; width : 100% }
.clearer { clear: both }

Elementary PHP

How does this translate to PHP? Basically as a series of constants (plus documentation detailing what the column HTML should look like):

class CSS_Col2
{
  const ROOT = "col2";
  const LEFT = "col2-l";
  const RIGHT = "col2-r";
  const RIGHT_INNER = "col2-ri";
}

This serves both as documentation for the existence of this component, and as an entry in the auto-completion tool to avoid typing incorrect classes by mistake. However, you still have to get the actual code right:

<div class="<?=CSS_Col2::ROOT?>">
  <div class="<?=CSS_Col2::LEFT?>">
    [20 lines of left column here]
  </div>
  <div class="<?=CSS_Col2::RIGHT?>">
    <div class="<?=CSS_Col2::RIGHT_INNER?>">
      [40 lines of right column here]
    </div>
  </div>
  <?CSS::CLEARER?>
</div>

Did I write everything correctly? Did I forget or misplace a clearer? Forgetting about the inner container in the right column is an easy mistake, and you won’t notice it until you put a clearing element in that column. And if your script is long enough, you won’t be able to see which opening tag matches which closing tag. Surely there must be a way to improve this.

Using HTML constants

A possibility is using constants to contain the relevant HTML:

class CSS_Col2
{
  // as above ...
  const _BEGIN_LEFT = '<div class="col2"><div class="col2-l">';
  const _BEGIN_RIGHT = '</div><div class="col2-r"><div class="col2-ri">';
  const _END = '</div></div><div class="clearer"></div>';
}

This makes code shorter, and you can’t mismatch or misplace tags as easily:

<?=CSS_Col2::_BEGIN_LEFT?>
  [20 lines of left column here]
<?=CSS_Col2::_BEGIN_RIGHT?>
  [40 lines of right column here]
<?=CSS_Col2::_END?>

However, all benefits of a nice and clean HTML editor are lost, because HTML constants don’t react as code, and there is therefore no validation performed. At least Eclipse could detect mismatching open/closing tags on raw HTML. Now, if you forget to “_END” your columns, your life is pain.

Using helpers

A common technique is to use a helper function for such rendering tasks. The function accepts some arguments that let it configure what ought to be displayed, then renders the wrapper HTML and inserts the data. Staying within the previous code:

class View_Helper_Col2
{
  static function Render($left, $right)
  {
    ?>
<div class="<?=CSS_Col2::ROOT?>">
  <div class="<?=CSS_Col2::LEFT?>">
    <?php call_user_func($left); ?>
  </div>
  <div class="<?=CSS_Col2::RIGHT?>">
    <div class="<?=CSS_Col2::RIGHT_INNER?>">
      <?php call_user_func($right); ?>
    </div>
  </div>
  <?CSS::CLEARER?>
</div><?
  }
}

I used callbacks here to do the rendering, because they are the most versatile (it sure beats having to instantiate a “renderable” class for each column). This approach provides the obvious benefit that now the entire rendering is taken care of by a single function, so there is no risk of forgetting or misplacing a tag, and the auto-completion tool can now help check which arguments are provided and in what order.

Still, this means that one should create two functions to render the two columns, and that any necessary data should be made available to them (due to the absence of closures in PHP < 5.3, this often means calling a member function of a view object containing the appropriate data). In the Zend Framework, for instance, one would just write two helpers, and provide them as callbacks knowing that they will have access to the data of the current view:

<?php
  $this->render2col(
    array($this,'myLeftCol'),
    array($this,'myRightCol')
  );
?>

Of course, it’s questionable whether moving a three-line for-each loop to a helper of its own actually increases the readability of the code. If defining a new class for every view, there’s the possibility of defining the columns as member functions within that same class, but it’s still somewhat awkward.

Helpers and Closures

PHP 5.3 introduces closures and optional arguments. This means that one can now write the behavior inline:

<?php
 $self = &$this;
 $this->render2col(
   function() use($self)
   {
     ?><h1><?=esc($self->user->name)?></h1><?php
   },
   function() use($self)
   {
     ?><ul><?php foreach ($self->items as $item): ?>
       <li><?php $self->render($item); ?></li>
     <?php endforeach; ?></ul><?php
   }
 );

However, making those functions inline creates a new issue: its not so obvious anymore what exactly a function is doing (because it’s too far away from the original call to the helper function). This can be solved by using a command pattern (while simultaneously noticing that one can get rid of the use keyword by providing $self as an argument (the helper does that):

<?php
 View_Col2::start($this)

 ->left(function($view){
   ?><h1><?=esc($view->user->name)?></h1><?php
 })

 ->right(function($view){
   ?><ul><?php foreach ($view->items as $item): ?>
     <li><?php $view->render($item); ?></li>
   <?php endforeach; ?></ul><?php
 })

 ->render();

Labels are now clearly mentioned, allowing empty lines to be inserted to separate the columns without forgetting what they are, so that the code looks cleaner overall.

Lorem Ipsum

Lorem Ipsum is a sample phrase used as a filler in typesetting, to illustrate how some text would look. Here’s a sample paragraph:

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

This approach to filler text is superior in many ways to most alternatives:

  • It’s long, certainly longer than “Test” or “AAA”, so it can fill several lines (and test whether there is an unexpected length limit when saving the data).
  • Unlike random strings of characters copy-pasted several times, it is split into words of uneven length, spaces between words do not align horizontally or vertically.
  • It is readily recognizable as random text by any typesetter (or developer) worth its salt.

A typical testing strategy, when filling forms by hand, is to copy-paste one or two Lorem Ipsum paragraph to test such things as how the text area reacts, whether it is saved correctly, and so on.

Lorem Ipsum does have some limitations:

  • It’s written in latin, so it fits nicely in the ASCII range of characters. As such, it does not test for Unicode support.
  • It contains no quotes of any kind, so no testing of database escaping processing either.
  • It contains no HTML-specific characters like < or &, so HTML character escaping is not tested either.
  • For that matter, it does not contain exceedingly long words that would overflow a single line, so you cannot test for this kind of overflow either.
  • Sometimes, you want to auto-linkify links and URLs.
  • Sometimes, Skype turns numbers into … clickable numbers.

I need to test these things on my web applications, so I’ve developed my own version of a “Modern Lorem Ipsum“:

Lorem <a href=”javascript:document.write(”)”>ipsum</a> dòlor sit àmet, consectetur adipisicing élit, sèd do eiusmod tempor incididunt ut labore & dolore magna aliqua. <hr/>Ut enim@minim.com veniam, quis nostrud exercitation `”ullamco laboris nisi & aliquip ex æ commodo consequat. Duis aute irure dolor 01 23 45 67 89 in reprehenderit in voluptate velit esse cillum dolore `eu fugiat https://nulla.biz/pariatur. aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Excepteur [u]sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit ‘anim id est laborum.[u] Lorem” ipsum dòlor sit àmet, consectetur adipisicing élit, sèd do eiusmod tempor http://incididunt.ut.com/labore & dolore magna aliqua. <b>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex æ commodo consequat.</b> “Duis aute irure dolor in ‘reprehenderit in voluptate velit — esse cillum dolore eu fugiat nulla pariatur.

Feel free to copy-paste it away. WordPress certainly does seem to have a hard time with these long lines in post bodies—I wonder if it happens in comments as well :)



1150 feed subscribers
(readers who polled a feed this week)