Pros and Cons of Development With Zend Framework

While the definition of "Framework" may not be understood or agreed upon in some programming circles, the Inversion of Control distinction can help clarify the difference between a "Library" and a "Framework".

Generally speaking, a Library is a reusable collection of code that can be used by a controlling program.  The library implements specialized behavior, image libraries for example perform tasks to generate or manipulate images. The use of a library frees programmers from having to deal with some complexities, abstracting complex problems of a specific type. The programmer using a library writes the code that drives interaction, applying business logic to decide what to do. A library is essentially a service for such programs.

In contrast, a Framework is a system that takes programs implementing specialized behaviors, and uses them to drive interaction. The framework acts as a reusable shell which abstracts common needs of a type of program, such as accepting input and generating output. The framework delegates control to the code written by the programmer in predetermined ways, but it is the framework that is ultimately in charge.

Frameworks are useful in making it easier to write similar types of programs that have different business logic:
  • a web form to accept a bank transaction, and
  • a web form to schedule an event might both be implemented easily with the same framework.
A library on the other hand is useful in making it easier to accomplish a collection of related tasks:
  • one library might be used to save and retrive information from a database, while
  • another library might be use to generate the HTML of a web page.
In both cases the code written by the programmer affects side effects and outputs. In the case of a library, the library may generate side effects and outputs, but only as allowed by the controlling program. In the case of a framework, the framework always has control at some level of what the side effects and outputs are even when the programmer's code that generates them.

To make matters complicated, many libraries perform tasks similar to a framework in that the library may be given control of large portions of the program's behavior. Frameworks also often include library like functions that are driven by the code written by the programmer while it is in turn being driven by the framework.

The key distinction is that of control, but the as one might imagine there are reasonable cases where control goes back and forth making the distinction difficult. There are proponents that will argue that one or either of the two models should be used as purely as possible, and others that will insist that it is function and not theory that should drive such decisions. Both opinions have merit.

A good framework can save time in development and maintenance efforts. Unlike a library, where there is nothing dictating the design of the program being written, a framework provides an outer layer of standardization. When a programmer uses a library, the library dictates the interfaces by which it may be used. Frameworks on the other hand dictate the interface of the program being written. They must conform to an interface that the framework expects.

The most attractive part of the Zend Framework for the development group I am a part of is that it is flexible. It can be used as a library, or a framework (or as one might imagine both at the same time). In my particular case it has become more of a library by which I am writing my own framework.

Zend Framework implements a MVC (Model-View-Controller) pattern as the primary framework components. Most of the Zend Framework documentation and training is unfortunately written from the assumption that one is using the MVC components. Aside from this issue I and (most if not all) the other developers I work with have found it to be very flexible and powerful whether you use MVC or not.

One of the real tests of this flexibility came this week when I realized the HTML Forms being rendered by the Zend Framework Form component were not to my liking. It is programmed to always insert a "value=" attribute on form input tags, even when the value is blank or undefined. This causes most major browsers to have an undesirable behavior.

For an input tag such as:

<input name="myText" id="myText" value="" type="text"/>

The web browser will automatically set the text field generated by the tag to be blank when the form is first loaded into the browser, which is the same behavior as:

<input name="myText" id="myText" type="text"/>

The key distinction occurs if a user enters something in the text field, submits, and then decides to use the back button. In the case where there is a blank value attribute, the browser will again display a blank text box, even if something had been entered previously. In the case where there is no value attribute at all however, many browsers will repopulate the text field with the text the user had entered prior to submitting the form.

Whether the behavior or repopulating the form input element is a standards compliant behavior, or even good from a usability stand point is beside the point. What matters to me is that users can and will use the back button, and they will be annoyed if they have to fill in the form from scratch. Some of them will have likely used a form in the past where using the back button did not clear the form and will expect what they consider to be the most user friendly result. It is certainly possible to solve the issue with PHP Sessions, lessening the dependence on browser behavior. Implementing such a solution properly however requires a level of business logic that I am not interested in tackling at this time if browser behavior will acquiesce to meet the need.

In the Zend Framework, there are many many layers to the implementation of rendering a form. A non-exhaustive list includes:
  • The Zend_Form object requires a Zend_View object to render, but any instance of Zend_View will do, even one with an empty constructor:
    $view = new Zend_View();
  • The Zend_Form object is given a number of subforms, element groups, and elements (Zend_Form_Element).
  • The Zend_Form_Element(s) each have filters, and validators for the value(s) entered into the form element.
  • The Zend_Form object, it's subforms, element groups, and elements each have one or more decorators.
  • The Zend_View, uses helpers and decorators to decide how to generate output.
The obvious down side to using a framework like Zend Framework is the level of complexity. Many libraries that are well designed and properly documented have a relatively small interface "surface area", meaning there are a few easy to learn ways to interact with the library. In contrast a powerful, flexible framework might easily have a very large interface "surface area", much like the wrinkles in a highly evolved brain.

After some API and code digging (because the documentation is a bit lacking) I discovered that the Zend_View_Helper_FormText object was responsible for the undesirable behavior. The trick to producing a more desirable outcome was to create an alternative class which I added to my own code base.

The code for Zend_View_Helper_FormText is as follows. The undesirable behavior has been highlighted:

<?php
/**
 * ...
 */


/**
 * Abstract class for extension
 */
require_once 'Zend/View/Helper/FormElement.php';


/**
 * Helper to generate a "text" element
 *
 * ...
 */
class Zend_View_Helper_FormText extends Zend_View_Helper_FormElement
{
    /**
     * ...
     */
    public function formText($name, $value = null, $attribs = null)
    {
        $info = $this->_getInfo($name, $value, $attribs);
        extract($info); // name, value, attribs, options, listsep, disable

        // build the element
        $disabled = '';
        if ($disable) {
            // disabled
            $disabled = ' disabled="disabled"';
        }
       
        // XHTML or HTML end tag?
        $endTag = ' />';
        if (($this->view instanceof Zend_View_Abstract) && !$this->view->doctype()->isXhtml()) {
            $endTag= '>';
        }

        $xhtml = '<input type="text"'
                . ' name="' . $this->view->escape($name) . '"'
                . ' id="' . $this->view->escape($id) . '"'
                . ' value="' . $this->view->escape($value) . '"'
                . $disabled
                . $this->_htmlAttribs($attribs)
                . $endTag;

        return $xhtml;
    }
}

The replacement class is as follows. The changes have been highlighted:

<?php
/**
 * Quepie Framework
 *
 * ...
 */


/**
 * Abstract class for extension
 */
require_once 'Zend/View/Helper/FormElement.php';


/**
 * ...
 */
class Quepie_View_Helper_FormText extends Zend_View_Helper_FormElement{
    /**
     * ...
     */
    public function formText($name, $value = null, $attribs = null)
    {
        $info = $this->_getInfo($name, $value, $attribs);
        extract($info); // name, value, attribs, options, listsep, disable

        // build the element
        $disabled = '';
        if ($disable) {
            // disabled
            $disabled = ' disabled="disabled"';
        }
       
        // XHTML or HTML end tag?
        $endTag = ' />';
        if (($this->view instanceof Zend_View_Abstract) && !$this->view->doctype()->isXhtml()) {
            $endTag= '>';
        }

        $xhtml = '<input type="text"'
                . ' name="' . $this->view->escape($name) . '"'
                . ' id="' . $this->view->escape($id) . '"'
                . ($this->view->escape($value) != '' ? ' value="' . $this->view->escape($value) . '"' : '')
                . $disabled
                . $this->_htmlAttribs($attribs)
                . $endTag;

        return $xhtml;
    }
}

So that the new class behaves identically, except it only outputs the value attribute if the value is not an empty string. Code that executes in the framework prior to the above code filters an undefined value into an empty string, so testing only for the empty string should be a sufficiently safe assumption.

Now the remaining trick is figuring out how to make Zend Framework use this new helper instead of the default helper. Fortunately that is also easy to accomplish in a line of code or two. A working form must already have a view object, as it turns out there is a function that will prepend an alternative package to the view's search list for helpers:

$view = new Zend_View();

$view->addHelperPath('Quepie/View/Helper/', 'Quepie_View_Helper_');

$form->setView($view);

With the above code the forms which used to output "bad" HTML now output as desired. It still used the default helpers for all other form elements, but uses the new helper for text input elements instead of the one provided by the Zend Framework.

Selecting a good framework, or deciding if a framework is even right for your development team requires an understanding of needs and tolerances. In this case the Zend Framework proved to be sufficiently flexible, and even with minimal code effort. The time investment for learning the Zend Framework was considerable, figuring out what to do took much longer than actually doing it. In our group, the need for flexibility while still working from a common code base is high enough that the demands for learning are tolerable. We also have just enough developers and strong communication among group members to help offset the demands of learning. If we had smaller numbers, or lower communication the strain of learning to use the framework might not outweigh the gained benefits.

Comments [0]

Trackback URL: http://blogs.lib.ncsu.edu/fabulousit/entry/pros_and_cons_of_development
Comments:

Post a Comment:

Name:
E-Mail:
URL:

Your Comment:

HTML Syntax: Allowed