Pros and Cons of Development With Zend 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.
- 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.
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:
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:
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.
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:
/**
* ...
*/
/**
* 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;
}
}
/**
* 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->addHelperPath('Quepie/View/Helper/', 'Quepie_View_Helper_');
$form->setView($view);
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.
Tradition holds that a picture is worth a thousand words. A config file may well be worth a thousand lines of code (or easily more). Reconfigurable code can simplify maintenance, portability, reuse, and even development.
Everyone has different interests and strengths. While there
are some people that claim to excel at "multi-tasking", and some people
are jacks-of-all-trades, most people generally do better when focusing
on one task at a time, or very narrow set of related tasks. One of the
primary tenants of object-oriented programming is to assign purpose to
classes, limiting the scope of any one class. Each object performs its
role, avoids meddling in the affairs of other objects, and what
generally results is a system that is reasonably easy to understand and
predict. This approach exemplifies a major theme of computer science:
break down a problem into smaller, more easily solved problems.
Sometimes code just happens. The example code is one such case. There were no formal requirements, I simply wanted to be able to receive Sysnews posts via text message, and Twitter was a low barrier means to achieve that goal. Once the proof of concept was realized however, the next logical step is to explore options for moving the code into production.
One of the biggest advances in software engineering didn't originate from a programmer, but from a architect. Christopher Alexander didn't design code, he designed buildings. None the less his ideas on effective design are the underpinning of modern software development as popularized by the "gang of four" publication:
Lolcats (or dogs in this case) make everything better. Refactoring is a method for improving software by applying various techniques. Adding a lolcat is easy, refactoring requires practice and experience. As one might reasonably imagine, it is hard to write good code unless one knows how to code well. Refactoring integrates into the learning experience, it helps the programmer learn as they go. Writing good code also requires proper motivation. Some of the best programmers start projects with bad code, but there is a reason.
There is a third form of success in Agile methods, but it is dependent on the to the other two: technical success. Technical success can only be achieved when personal and organizational success are also realized. James Shore and Shane Warden illustrate the overlap of these three forms of success as a traditional triquetra-esque vin diagram. In contrast, a more proper view of the relationship between these three forms is more spiral and in nature. That is to say that there are many ways we can succeed personally, independent of any level of organizational success or technical success. There may be a few ways we can succeed organizationally and not personally, but there are most successful organizational outcomes depend on personal success with or without technical success. Similarly there are even fewer ways to technically succeed that do not have some overlap with either personal success, organizational success, or both. The sweet spot is dead in the center of area where all three forms of success overlap.