<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <title>Tag: mvc :: phly, boy, phly</title>
    <description>Tag: mvc :: phly, boy, phly</description>
    <pubDate>Mon, 05 Jan 2009 16:51:07 +0000</pubDate>
    <generator>Zend_Feed_Writer 2.1.4dev (http://framework.zend.com)</generator>
    <link>http://mwop.net/blog/tag/mvc.html</link>
    <atom:link rel="self" type="application/rss+xml" href="http://mwop.net/blog/tag/mvc-rss.xml"/>
    <item>
      <title>Model Infrastructure</title>
      <pubDate>Mon, 05 Jan 2009 16:51:07 +0000</pubDate>
      <link>http://mwop.net/blog/202-Model-Infrastructure.html</link>
      <guid>http://mwop.net/blog/202-Model-Infrastructure.html</guid>
      <author>me@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>
    In the last two entries in this series on models, I covered 
    <a href="http://weierophinney.net/matthew/archives/200-Using-Zend_Form-in-Your-Models.html">using forms as input filters</a> 
    and <a href="http://weierophinney.net/matthew/archives/201-Applying-ACLs-to-Models.html">integrating ACLs into models</a>. 
    In this entry, I tackle some potential infrastructure for your models.
</p>

<p>
    The Model is a complex subject. However, it is often boiled down to either a
    single model class or a full object relational mapping (ORM). I personally
    have never been much of a fan of ORMs as they tie models to the underlying
    database structure; I don't always use a database, nor do I want
    to rely on an ORM solution too heavily on the off-chance that I later need
    to refactor to use services or another type of persistence store. On the
    other hand, the model as a single class is typically too simplistic.
</p><p>
    I <em>am</em>, however, a fan of the <a href="http://en.wikipedia.org/wiki/Domain_model">Domain Model</a>. To quote wikipedia, 
</p>

<blockquote>
    [The] domain model can be thought of as a conceptual model of a system
    which describes the various entities involved in that system and their
    relationships.
</blockquote>

<p>
    When you think in these terms, you start breaking your system into
    discrete pieces that you need to manipulate, as well as consider how each
    piece relates to the others. This type of exercise also helps you stop
    thinking of your model in terms of database tables; instead, your database
    becomes the container in which data is persisted from one use of your model
    to the next. Your model instead is an object that can <em>do</em> things
    with either incoming or stored data -- or even completely autonomously.
</p>

<p>
    As an example, when starting with Zend Framework, it's tempting to use
    <code>Zend_Db_Table</code> and <code>Zend_Db_Table_Row</code> as models.
    However, there's one big argument against doing so: when using a Table
    Data Gateway (TDG) or a Row Data Gateway (RDG), you're returning an object
    that is tied to the data storage implementation. You're basically putting on
    blinders and thinking of your model as simply the database table or an
    individual row, and the returned objects reflect this narrow view point.
    Furthermore, if you want to re-use your models with service layers, many web
    services do not work with objects, and of those that do, you likely do not
    want to expose <em>all</em> the properties and methods of the objects
    returned by your data provider. A row object in ZF, for instance, actually
    stores the data in protected members, effectively hiding it from services,
    and also includes methods for deleting the row, ArrayAccess methods, and
    access to the table object -- which gives you full control over the table!
    The security implications of exposing this directly over a service should be
    obvious.
</p>

<p>
    Additionally, if in the future you wish to refactor your application to
    utilize <a href="http://www.danga.com/memcached/">memcached</a> or a web
    service, you now not only need to completely rewrite your models, but also
    all <em>consumer</em> code, because the return values from your model have
    changed.
</p>

<p>
    So, if you're not going to use an ORM or a Table Data Gateway directly, how
    should you architect your model infrastructure?
</p>

<h2>What are you modelling?</h2>

<p>
    The principal question to ask is, "What am I modelling?"
</p>

<p>
    Let's look at a rather standard website issue: user management. Typically,
    you'll get a requirement such as, "Users should be able to register for an
    account on the site. Once registered, they should be able to login with the
    credentials they provided. Administrators should be able to ban accounts or
    grant users higher levels of privileges." That's assuming you actually get
    good requirement documents, of course.
</p>

<p>
    Most developers will immediately setup a database with a few fields that
    represent a user -- full name, username, email, password, etc -- create a
    form for registration and another for login, write a routine to validate
    each, create a page to list users for the administration screen... you know
    the drill. But <em>what are you modelling?</em>
</p>

<p>
    The answer is: <em>users</em>. So, now it's time to define what a user is,
    and what a user can do. We have to decide what constitutes a new user, and
    what constitutes an authenticated user.  We have an additional modelling
    consideration that's often overlooked: user <em>roles</em>. There's also the
    matter of what a <em>group</em> of users might look like (since the
    administrator needs to be able to <em>list</em> users), and how we might
    want to work with groups.
</p>

<p>
    Let's start with narrowing down the definition of a user:
</p>

<ul>
    <li>A user consists of the following metadata:<ul>
        <li>Unique username</li>
        <li>Full name</li>
        <li>Email address</li>
        <li>Hashed password</li>
        <li>A role within the site</li>
    </ul></li>
    <li>A <em>new user</em> must provide a unique username, their full name, a
    valid email address, and a password and password verification.</li>
    <li>An <em>authenticated</em> user is one who has provided a matching
    combination of <em>username</em> and <em>password</em>.</li>
    <li>A user may <em>logout</em> of the site.</li>
    <li>A user may be granted a new role.</li>
    <li>A user may be marked as banned.</li>
</ul>

<p>
    Notice the fifth piece of metadata? It mentions a "role"? That's something
    to do with our ACLs -- which means that ACLs are part of our user domain.
    I'll touch on this later.
</p>

<p>
    If you look at the remaining points carefully, you'll note that there's talk
    of validation, authentication, and user and session persistence.  Validation
    rules are part of our model -- and we'll use <code>Zend_Form</code> to
    fulfill that role.  Authentication on the web usually consists of both
    <em>validating</em> submitted credentials against <em>stored</em>
    credentials, as well as <em>persisting</em> a verified identity in the
    <em>session</em>. This means that other parts of our model include <em>data
        persistence</em> and <em>session management</em>. We'll use
    <code>Zend_Db_Table</code> for data persistence, and
    <code>Zend_Auth</code>/<code>Zend_Session</code> for identity persistence.
</p>

<p>
    Now, let's turn to defining <em>lists</em> of users:
</p>

<ul>
    <li>Administrators should be able to pull lists of users. These lists should
    allow for:<ul>
        <li>Sorting by username, full name, email address, or role</li>
        <li>Pagination (i.e., pulling a set number of users from a given offset)</li>
        <li>Iteration</li>
    </ul></li>
    <li>Administrators should be able to specify criteria for selecting users to
    list.</li>
</ul>

<p>
    These criteria indicate that a <em>list</em> of users should be an object.
    This list will likely implement the SPL class <code>Traversable</code> in
    some fashion. Looking at this criteria, another aspect of our model becomes
    clear: we are modelling <em>user selection</em> -- which includes the
    ability to specify sorting and selection criteria. The <em>user
        selection</em> object would return a <em>user list</em> object, which
    would consist of <em>user</em> objects. User objects define ACL roles and
    can authenticate users. 
</p>

<p>
    We started this article by discussing the Domain Model, and defined it as a
    system, its entities, and the relations between those entities. We've now
    identified our domain: user management. The various entities include users,
    lists of users, ACL roles, a user persistence layer (database), and session
    persistence layer (web server sessions).
</p>

<p>
    Now that we know what we're modelling, let's look at some of the objects in
    our model.
</p>

<h2>Gateway to the Domain</h2>

<p>
    We've identified "user management" as the purpose of our model. This will
    include retrieving and saving individual users, as well as selecting groups
    of users.
</p>

<p>
    It's clear that we'll need an object to represent a user, as well as another
    to represent a selection or group of users. But what may not be entirely
    clear is that we should likely have an object that is used to create new
    user objects, create selections of users, and basically coordinate several
    of the related objects -- the root ACL and data access, in particular.
</p>

<p>
    This object will be what I'll term our domain <em>gateway</em>. It will be
    used to fetch other objects in our model, and will inject various
    dependencies into them when doing so, such as the data access and ACLs. The
    various dependencies may themselves be injected into the gateway -- or it
    can lazy-load them.
</p>
    
<p>
    The API of this gateway might look something like the following.
</p>

<div class="example"><pre><code lang="php">
// Instantiate the gateway
$userGateway = new Spindle_Model_UserGateway();

// configure the gateway:
$userGateway-&gt;setAcl(new Spindle_Acl_Spindle())
            -&gt;setDbAdapter(Zend_Registry::get('db'));

// Alternately, do it all at instantiation:
$userGateway = new Spindle_Model_UserGateway(array(
    'acl'       =&gt; new Spindle_Acl_Spindle(),
    'dbAdapter' =&gt; Zend_Registry::get('db'),
));

// Grab a single user
$user = $userGateway-&gt;retrieve('matthew');

// Grab many users
$users = $userGateway-&gt;sort('email', 'ASC')
                     -&gt;criteria(array('banned' =&gt; true))
                     -&gt;fetch(array('offset' =&gt; 20, 'limit' =&gt; 20));

// Better yet, add some transaction script methods with preset criteria:
$users = $userGateway-&gt;fetchBannedUsers(array(
    'offset' =&gt; 20, 
    'limit'  =&gt; 20,
    'sort'   = array('email', 'ASC'),
));

// Create a new user:
$user = $userGateway-&gt;createUser(array(
    'username' =&gt; 'matthew',
    'fullname' =&gt; \&quot;Matthew Weier O'Phinney\&quot;,
    'password' =&gt; 'secret',
    'email'    =&gt; 'matthew@local',
));
</code></pre></div>

<p>
    The basic idea is to provide a scaffold for lazyloading necessary objects,
    methods for specifying options (such as sort order, criteria, limits, etc),
    and transaction methods for retrieving individual users and groups of users.
</p>

<h2>Of Value Objects and Record Sets</h2>

<p>
    To other objects we've identified in our model are <em>users</em>
    and <em>user lists</em>. How should we define these?
</p>

<p>
    The traditional answer is as <em>value</em> or <em>data transfer</em>
    objects and <em>record sets</em>.  The Value Object is a standard design
    pattern used to aggregate all metadata that defines a single value. The
    Record Set is an aggregation of Value Objects.
</p>

<h3>Value Objects</h3>

<p>
    Martin Fowler makes a differentiation between value objects and data
    transfer objects in his book "Patterns of Enterprise Application
    Architecture" (PoEAA). In it, he associates value objects with language
    variable types (i.e., Value Objects act as custom variable types), while
    defining data transfer objects as aggregating related values for the purpose
    of serialization and data transfer between objects. 
</p>
    
<p>
    In Java, however, value objects are arbitrary objects used to store a
    specific set of attributes -- very similar to the data transfer object. For
    the purposes of this discussion, I'll use the term "value object," as it
    will be familiar to those with a Java background, and to indicate that we
    are aggregating a unique <em>value</em> that is the sum of a number of
    <em>attributes</em>.
</p>

<p>
    Basically, all of this verbiage describes something incredibly simple in
    implementation: an object with a specific set of attributes or properties.
    If you've been doing any OOP programming in PHP, this is the most natural
    and fundamental thing you can do.
</p>

<div class="example"><pre><code lang="php">
class Spindle_Model_User
{
    protected $_data = array(
        'username' =&gt; null,
        'email'    =&gt; null,
        'fullname' =&gt; '',
        'role'     =&gt; 'guest',
    );

    public function __construct($data)
    {
        $this-&gt;populate($data);

        if (!isset($this-&gt;username)) {
            throw new Exception('Initial data must contain an id');
        }
    }

    public function populate($data)
    {
        if (is_object($data)) {
            $data = (array) $data;
        }
        if (!is_array($data)) {
            throw new Exception('Initial data must be an array or object');
        }

        foreach ($data as $key =&gt; $value) {
            $this-&gt;$key = $value;
        }
        return $this;
    }

    public function __set($name, $value)
    {
        if (!array_key_exists($name, $this-&gt;_data)) {
            throw new Exception('Invalid property \&quot;' . $name . '\&quot;');
        }
        $this-&gt;_data[$name] = $value;
    }

    public function __get($name)
    {
        if (array_key_exists($name, $this-&gt;_data)) {
            return $this-&gt;_data[$name];
        }
        return null;
    }

    public function __isset($name)
    {
        return isset($this-&gt;_data[$name]);
    }

    public function __unset($name)
    {
        if (isset($this-&gt;$name)) {
            $this-&gt;_data[$name] = null;
        }
    }
}
</code></pre></div>

<p>
    The above example is fairly simplistic, but it gets the idea across: the
    object defines a limited range of valid values, and enforces that only these
    values may be set -- as well as which values are required. You could
    certainly add accessor and mutator methods to enforce consistent access
    to member data, but the above will certainly suffice for many use cases.
    (I'll look at data integrity momentarily.)
</p>

<p>
    One addition you might make to the class definition is to add some
    conversions from different types of objects. For instance, if you know that
    you'll be using <code>Zend_Db_Table</code> within your model, you might want
    to add the ability for your value object to accept a
    <code>Zend_Db_Table_Row</code> object, and pull its values from there:
</p>

<div class="example"><pre><code lang="php">
class Spindle_Model_User
{
    /* ... */

    public function populate($data)
    {
        if ($data instanceof Zend_Db_Table_Row_Abstract) {
            $data = $data-&gt;toArray();
        } elseif (is_object($data)) {
            $data = (array) $data;
        }

        if (!is_array($data)) {
            throw new Exception('Initial data must be an array or object');
        }

        foreach ($data as $key =&gt; $value) {
            $this-&gt;$key = $value;
        }
        return $this;
    }

    /* ... */
}
</code></pre></div>

<p>
    This will help keep your model code clean, as you can potentially take the
    results of data storage operations and push them directly into your value
    object -- resulting in less re-working of code.
</p>

<p>
    Now, what about data integrity? This is where <code>Zend_Form</code> comes
    into play. Don't think of <code>Zend_Form</code> as a web form; think of it
    as an input filter that has the ability to render itself as a form if so
    desired. If we think of it as an input filter, we can use it for data
    integrity:
</p>

<div class="example"><pre><code lang="php">
class Spindle_Model_User
{
    /* ... */

    public function __set($name, $value)
    {
        if (!array_key_exists($name, $this-&gt;_data)) {
            throw new Exception('Invalid property \&quot;' . $name . '\&quot;');
        }

        $inputFilter = $this-&gt;getForm();
        if ($element = $inputFilter-&gt;getElement($name)) {
            if (!$element-&gt;isValid($value)) {
                throw new Exception(sprintf(
                    'Invalid value provided for \&quot;%s\&quot;: %s', 
                    $name, 
                    implode(', ', $element-&gt;getMessages())
                );
            }
        }

        $this-&gt;_data[$name] = $value;
    }

    /* ... */

    protected $_form;

    public function getForm()
    {
        if (null === $this-&gt;_form) {
            $this-&gt;_form = new Spindle_Form_User();
        }
        return $this-&gt;_form;
    }

    /* ... */
}
</code></pre></div>

<p>
    One note: if your model contains metadata that will never be represented as
    part of a form, you shoould look into using <code>Zend_Filter_Input</code> 
    or custom validation chains instead of <code>Zend_Form</code>. That's
    outside the scope of this article, however.
</p>

<p>
    Now that we have input filtering out of the way, how shall we address saving
    a user? Recall in our discussion of the domain gateway that one of its
    responsibilities is injecting other dependencies into our objects. I find
    it's often easier to inject the <em>gateway</em> into objects, and then pull
    what I need from it. Let's look at how that might work for saving the user.
</p>

<div class="example"><pre><code lang="php">
class Spindle_Model_User
{
    /* ... */

    protected $_gateway;

    public function __construct($data, $gateway)
    {
        $this-&gt;setGateway($gateway);

        /* ... */
    }

    public function setGateway(Spindle_Model_UserGateway $gateway)
    {
        $this-&gt;_gateway = $gateway;
        return $this;
    }

    public function getGateway()
    {
        return $this-&gt;_gateway;
    }

    public function save()
    {
        $gateway = $this-&gt;getGateway();
        $dbTable = $gateway-&gt;getDbTable('user');

        if ($row = $dbTable-&gt;find($this-&gt;username)) {
            foreach ($this-&gt;_data as $key =&gt; $value) {
                $row-&gt;$key = $value;
            }
            $row-&gt;save();
        } else {
            $dbTable-&gt;insert($this-&gt;_data);
        }
    }

    /* ... */
}
</code></pre></div>

<p>
    Note that the constructor now has a second argument -- the gateway. This
    ensures that the user always has a gateway instance, which further ensures
    that operations like the one listed -- retrieving the
    <code>Zend_Db_Table</code> instance from the gateway -- will always work. In
    this example, we simply check to see if a row already exists, and then save
    the record accordingly. 
</p>

<p>
    Another requirement we identified was that a user be able to authenticate
    itself. This can be done trivially by implementing
    <code>Zend_Auth_Adapter_Interface</code>:
</p>

<div class="example"><pre><code lang="php">
class Spindle_Model_User implements Zend_Auth_Adapter_Interface
{
    /* ... */

    public function authenticate()
    {
        $gateway = $this-&gt;getGateway();
        $table   = $manager-&gt;getDbTable('user');
        $select  = $table-&gt;select();
        $select-&gt;where('username = ?', $this-&gt;username)
               -&gt;where('password = ?', $this-&gt;password)
               -&gt;where('date_banned IS NULL');
        $user = $table-&gt;fetchRow($select);
        if (null === $user) {
            // failed
            $result = new Zend_Auth_Result(
                Zend_Auth_Result::FAILURE_UNCATEGORIZED,
                null
            );
        } else {
            // passed
            $this-&gt;populate($user);
            unset($this-&gt;password);
            $result = new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $this);
        }
        return $result;
    }

    /* ... */
}
</code></pre></div>

<p>
    To authenticate a user, you would create a new user object with the username
    and password, and then attempt to authenticate it:
</p>

<div class="example"><pre><code lang="php">
$auth = Zend_Auth::getInstance();
$user = $gateway-&gt;createUser(array(
    'username' =&gt; $username,
    'password' =&gt; $password,
));
if ($auth-&gt;authenticate($user)) {
    // AUTHENTICATED!
}
</code></pre></div>

<p>
    This also has the effect of populating the user from the persistence store,
    as well as storing the identity in the session.
</p>

<p>
    I covered ACL roles <a href="http://weierophinney.net/matthew/archives/201-Applying-ACLs-to-Models.html">previously</a>,
    so I won't go into that here. However, you should now be getting a pretty
    clear understanding of how this object works, and how it coordinates with
    the user gateway. It should also illustrate that this aspect of our model
    is much, much more than simply data access: we're coordinating
    authentication, input filtering, and ACLs -- and providing a fairly simple
    API for manipulating the user itself.
</p>

<h3>Record Sets</h3>

<p>
    A Record Set is similarly easy to create. Typically, you will merely want
    the object to be iterable and countable. Like the user object, we'll require
    a gateway instance in the constructor.
</p>

<div class="example"><pre><code lang="php">
&lt;?php
class Spindle_Model_Users implements Iterator,Countable
{
    protected $_count;
    protected $_gateway;
    protected $_resultSet;

    public function __construct($results, $gateway)
    {
        $this-&gt;setGateway($gateway);
        $this-&gt;_resultSet = $results;
    }

    public function setGateway(Spindle_Model_UserGateway $gateway)
    {
        $this-&gt;_gateway = $gateway;
        return $this;
    }

    public function getGateway()
    {
        return $this-&gt;_gateway;
    }

    public function count()
    {
        if (null === $this-&gt;_count) {
            $this-&gt;_count = count($this-&gt;_resultSet);
        }
        return $this-&gt;_count;
    }

    public function current()
    {
        if ($this-&gt;_resultSet instanceof Iterator) {
            $key = $this-&gt;_resultSet-&gt;key();
        } else {
            $key = key($this-&gt;_resultSet);
        }
        $result  = $this-&gt;_resultSet[$key];
        if (!$result instanceof Spindle_Model_User) {
            $gateway = $this-&gt;getGateway();
            $result  = $gateway-&gt;createUser($result);
            $this-&gt;_resultSet[$key] = $result;
        }
        return $result;
    }

    public function key()
    {
        return key($this-_resultSet);
    }

    public function next()
    {
        return next($this-&gt;_resultSet);
    }

    public function rewind()
    {
        return reset($this-&gt;_resultSet);
    }

    public function valid()
    {
        return (bool) $this-&gt;current();
    }
}
</code></pre></div>

<p>
    The logic here is incredibly simple. The main benefit from using a Record
    Set over an array is that it allows you to ensure the types of each item in
    the set, as well as allow your consuming code to perform type hinting on the
    Record Set class.
</p>

<h2>Using Value Objects and Record Sets in Your Gateway</h2>

<p>
    Within  your gateway class, it is then your responsibility to ensure that
    instances of your new classes are returned. As an example, let's look at
    some easy <code>fetch()</code> and <code>fetchAll()</code> methods:
</p>

<div class="example"><pre><code lang="php">
class Spindle_Model_UserGateway
{
    /* ... */

    public function fetch($id)
    {
        $dbTable = $this-&gt;getDbTable();
        $select  = $dbTable-&gt;select();
        $select-&gt;where('id = ?', $id);
        $result = $dbTable-&gt;fetchRow($select);
        if (null !== $result) {
            $result = $this-&gt;createUser($result);
        }
        return $result;
    }

    public function fetchAll()
    {
        $result = $this-&gt;getDbTable()-&gt;fetchAll();
        return new Spindle_Model_Users($result, $this);
    }

    /* ... */
}
</code></pre></div>

<p>
    You'll notice the downside immediately: you have to introduce new objects,
    and that means re-casting of data. But let's look at it from a consumer
    viewpoint: the consuming code is looking for return types of
    <code>Spindle_Model_User</code> and <code>Spindle_Model_Users</code>. 
</p>
    
<p>
    But what's the point of the gateway, really? Couldn't both the value object
    and result set object simply inherit from a common base? Certainly they
    could. However, one common use case I have for gateways is providing
    pre-defined methods encapsulating common selection criteria. For instance,
    let's say you wanted to retrieve all <em>banned</em> users, and that this
    will be a common task. Define a method for it:
</p>

<div class="example"><pre><code lang="php">
class Spindle_Model_UserGateway
{
    /* ... */

    public function fetchBannedUsers()
    {
        $dbTable = $this-&gt;getDbTable();
        $select  = $dbTable-&gt;select()-&gt;where('date_banned IS NOT NULL');
        $result  = $dbTable-&gt;fetchAll($select);
        return new Spindle_Model_Users($result, $this);
    }

    /* ... */
}
</code></pre></div>

<p>
    This is admittedly a trivial example, but it clearly illustrates the
    benefits: we now have an API method that tells us, in plain English, what
    operation we are performing, and provides a repeatable way to do it. The
    user consuming the model needs not know anything about how it works under
    the hood, only that they can expect to get a list of banned users when they
    call it.
</p>
    
<p>
    Another key benefit to creating a gateway is for those times when we need to
    replace our data access layer with something else.  Let's refactor our code
    to use a service instead:
</p>

<div class="example"><pre><code lang="php">
class Spindle_Model_UserGateway
{
    /* ... */

    public function fetch($id)
    {
        $result  = $this-&gt;getService()-&gt;fetchUser($id);
        return $this-&gt;createUser($result);
    }

    public function fetchAll()
    {
        $result = $this-&gt;getService()-&gt;fetchAll();
        return new Spindle_Model_Users($result, $this);
    }

    /* ... */
}
</code></pre></div>

<p>
    From a consumer standpoint, <em>nothing has changed</em>; they are still
    calling the same methods, and receiving the same responses. This is
    absolutely key in creating maintainable, future proof code.
</p>

<h2>Summary</h2>

<p>
    The solutions presented here are by no means canonical. You may find that
    your own models do not need a gateway class, or that you never work with
    lists of objects. Hopefully, however, I've illustrated that a model should
    cleanly provide a separation of concerns and consist of discrete objects --
    whether they are directly related to your model, or related to aspects of
    how your model <em>does stuff</em>, like validation and data persistence.
    You should strive to make your models as simple as possible, while still
    meeting each of your requirements. The end result should be a re-usable,
    testable suite of functionality, and careful architecture of your solution
    should make it robust and easy to refactor in the future.
</p>

<p>
    <b>Updates:</b>
</p>
<ul>
    <li><em>2009-01-04:</em> Updated <code>__unset()</code> per Gabriel's
    feedback (comment #14)</li>
    <li><em>2009-01-05:</em> Updated <code>current()</code> implementation per
    Falk's feedback (comment #15)</li>
    <li><em>2009-01-05:</em> Updated <code>current()</code> implementation per
    Martin's feedback (comment #15.1.1)</li>
</ul>]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Setting up your Zend_Test test suites</title>
      <pubDate>Sat, 13 Sep 2008 13:37:40 +0000</pubDate>
      <link>http://mwop.net/blog/190-Setting-up-your-Zend_Test-test-suites.html</link>
      <guid>http://mwop.net/blog/190-Setting-up-your-Zend_Test-test-suites.html</guid>
      <author>me@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>
    Now that <a href="http://framework.zend.com/manual/en/zend.test.html">Zend_Test</a>
    has shipped, developers are of course asking, "How do I setup my test
    suite?" Fortunately, after some discussion with my colleagues and a little
    experimenting on my one, I can answer that now.
</p><p>
    <a href="http://phpunit.de">PHPUnit</a> offers a variety of methods for
    setting up test suites, some trivial and some complex. The Zend Framework
    test suite, for instance, goes for a more complex route, adding
    component-level suites that require a fair amount of initial setup, but
    which allow us fairly fine-grained control.
</p>

<p>
    However, testing and test automation should be easy and the complex approach
    is overkill for most of our applications. Fortunately, PHPUnit offers some
    other methods that make doing so relatively simple. The easiest method is to
    use an <a href="http://www.phpunit.de/pocket_guide/3.2/en/appendixes.configuration.html">XML configuration file</a>.
</p>

<p>
    As an example, consider the following:
</p>

<div class="example"><pre><code lang="xml">
&lt;phpunit&gt;
    &lt;testsuite name=\&quot;My Test Suite\&quot;&gt;
        &lt;directory&gt;./&lt;/directory&gt;
    &lt;/testsuite&gt;

    &lt;filter&gt;
        &lt;whitelist&gt;
            &lt;directory suffix=\&quot;.php\&quot;&gt;../library/&lt;/directory&gt;
            &lt;directory suffix=\&quot;.php\&quot;&gt;../application/&lt;/directory&gt;
            &lt;exclude&gt;
                &lt;directory suffix=\&quot;.phtml\&quot;&gt;../application/&lt;/directory&gt;
            &lt;/exclude&gt;
        &lt;/whitelist&gt;
    &lt;/filter&gt;

    &lt;logging&gt;
        &lt;log type=\&quot;coverage-html\&quot; target=\&quot;./log/report\&quot; charset=\&quot;UTF-8\&quot;
            yui=\&quot;true\&quot; highlight=\&quot;true\&quot;
            lowUpperBound=\&quot;50\&quot; highLowerBound=\&quot;80\&quot;/&gt;
        &lt;log type=\&quot;testdox-html\&quot; target=\&quot;./log/testdox.html\&quot; /&gt;
    &lt;/logging&gt;
&lt;/phpunit&gt;
</code></pre></div>

<p>
    First thing to note, relative paths are relative to the configuration file.
    This allows you to run your tests from anywhere in your tests tree, Second,
    providing a <code>directory</code> directive to the
    <code>testsuite</code> directive scans for all files ending in "Test.php" in
    that directory, meaning you don't have to keep a list of your test cases
    manually. It's a great way to automate the suite. Third, the filter
    directive allows us to determine what classes to include and/or exclude
    from coverage reports. Finally, the <code>logging</code> directive lets us
    specify what kinds of logs to create and where.
</p>

<p>
    Drop the above into "tests/phpunit.xml" in your application, and you can
    start writing test cases and running the suite immediately, using the
    following command:
</p>

<div class="example"><pre><code lang="text">
% phpunit --configuration phpunit.xml
</code></pre></div>

<p>
    I like to group my test cases by type. I have controllers, models, and often
    library code, and need to keep the tests organized both on the filesystem as
    well as for running the actual tests. There are two things I do to
    facilitate this.
</p>

<p>
    First, I create directories. For instance, I have the following hierarchy in
    my test suite:
</p>

<div class="example"><pre><code lang="text">
tests/
    phpunit.xml
    TestHelper.php
    controllers/
        IndexControllerTest.php (contains IndexControllerTest)
        ErrorControllerTest.php (contains ErrorControllerTest)
        ...
    models/
        PasteTest.php           (contains PasteTest)
        DbTable/
            PasteTest.php       (contains DbTable_PasteTest)
        ...
    My/
        Form/
            Element/
                SimpleTextareaTest.php
</code></pre></div>

<p>
    "controllers/" contains my controllers, "models/" contains my models. If I
    were developing a modular application, I'd have something like
    "blog/controllers/" instead. Library code is given the same hierarchy as is
    found in my "library/" directory.
</p>

<p>
    Second, I use docblock annotations to group my tests. I add the following to
    my class-level docblock in my controller test cases:
</p>

<div class="example"><pre><code lang="php">
/**
 * @group Controllers
 */
</code></pre></div>

<p>
    Models get the annotation "@group Models", etc. This allows me to run
    individual sets of tests on demand:
</p>

<div class="example"><pre><code lang="text">
% phpunit --configuration phpunit.xml --group Controllers
</code></pre></div>

<p>
    You can specify multiple @group annotations, which means you can separate
    tests into modules, issue report identifiers, etc; additionally, you can add
    the annotations to individual test methods themselves to have really
    fine-grained test running capabilities.
</p>

<p>
    Astute readers will have noticed the "TestHelper.php" file in that directory
    listing earlier, and will be wondering what that's all about.
</p>

<p>
    A test suite needs some environmental information, just like your
    application does. It may need a default database adapter, altered
    include_paths, autoloading set up, and more. Here's what my TestHelper.php
    looks like:
</p>

<div class="example"><pre><code lang="php">
&lt;?php
/*
 * Start output buffering
 */
ob_start();

/*
 * Set error reporting to the level to which code must comply.
 */
error_reporting( E_ALL | E_STRICT );

/*
 * Set default timezone
 */
date_default_timezone_set('GMT');

/*
 * Testing environment
 */
define('APPLICATION_ENV', 'testing');

/*
 * Determine the root, library, tests, and models directories
 */
$root        = realpath(dirname(__FILE__) . '/../');
$library     = $root . '/library';
$tests       = $root . '/tests';
$models      = $root . '/application/models';
$controllers = $root . '/application/controllers';

/*
 * Prepend the library/, tests/, and models/ directories to the
 * include_path. This allows the tests to run out of the box.
 */
$path = array(
    $models,
    $library,
    $tests,
    get_include_path()
);
set_include_path(implode(PATH_SEPARATOR, $path));

/**
 * Register autoloader
 */
require_once 'Zend/Loader.php';
Zend_Loader::registerAutoload();

/**
 * Store application root in registry
 */
Zend_Registry::set('testRoot', $root);
Zend_Registry::set('testBootstrap', $root . '/application/bootstrap.php');

/*
 * Unset global variables that are no longer needed.
 */
unset($root, $library, $models, $controllers, $tests, $path);
</code></pre></div>

<p>
    The above ensures that my APPLICATION_ENV constant is set appropriately,
    that error reporting is appropriate for tests (i.e., I want to see
    <em>all</em> errors), and that autoloading is enabled. Additionally, I place
    a couple items in my registry -- the bootstrap and test root directory.
</p>

<p>
    In each test case file, I then do a require_once on this file. In future
    versions of PHPUnit, you'll be able to specify a bootstrap file in your
    configuration XML that gets pulled in for each test case, and you'll be able
    to even further automate your testing environment setup.
</p>

<p>
    Hopefully this will get you started with your application testing; what are
    you waiting for?
</p>]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Testing Zend Framework MVC Applications</title>
      <pubDate>Thu, 03 Jul 2008 08:47:17 +0000</pubDate>
      <link>http://mwop.net/blog/182-Testing-Zend-Framework-MVC-Applications.html</link>
      <guid>http://mwop.net/blog/182-Testing-Zend-Framework-MVC-Applications.html</guid>
      <author>me@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>
    Since I originally started hacking on the <a
    href="http://framework.zend.com/">Zend Framework</a> MVC in the fall of
    2006, I've been touting the fact that you can test ZF MVC projects by
    utilizing the Request and Response objects; indeed, this is what I actually
    did to test the Front Controller and Dispatcher. However, until recently,
    there was never an easy way to do so in your userland projects; the default
    request and response objects make it difficult to easily and quickly setup
    tests, and the methods introduced into the front controller to make it
    testable are largely undocumented. 
</p>

<p>
    So, one of my ongoing projects the past few months has been to create an
    infrastructure for functional testing of ZF projects using <a
        href="http://phpunit.de/">PHPUnit</a>. This past weekend, I made the
    final commits that make this functionality feature complete.
</p>

<p>
    The new functionality provides several facets:
</p>

<ul>
    <li>Stub test case classes for the HTTP versions of our Request and Response
        objects, containing methods for setting up the request environment
        (including setting GET, POST, and COOKIE parameters, HTTP request
        headers, etc).</li>
    <li><code>Zend_Dom_Query</code>, a class for using CSS selectors (and XPath)
        to query (X)HTML and XML documents.</li>
    <li>PHPUnit constraints that consume <code>Zend_Dom_Query</code> and the
        Response object to make their comparisons.</li>
    <li>A specialized PHPUnit test case that contains functionality for
        bootstrapping an MVC application, dispatching requests, and a variety of
        assertions that utilize the above constraints and objects.</li>
</ul><p>
    What might you want to test?
</p>

<ul>
    <li>HTTP response codes</li>
    <li>Whether or not the action resulted in a redirect, and where it
        redirected to</li>
    <li>Whether or not certain DOM artifacts are present (particularly helpful
        for ensuring that the DOM structure is correct for JS actions)</li>
    <li>Presence of specific HTTP response headers and/or their content</li>
    <li>What module, controller, and/or action was used in the last iteration of
        the dispatch loop</li>
    <li>What route was selected</li>
</ul>

<p>
    The aim is to make testing your controllers trivial and fun. Let's look at
    an example:
</p>

<div class="example"><pre><code lang="php">
class UserControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
    public function setUp()
    {
        $this-&gt;bootstrap = array($this, 'appBootstrap');
        parent::setUp();
    }

    public function appBootstrap()
    {
        $this-&gt;frontController-&gt;registerPlugin(
            new Bugapp_Plugin_Initialize('test')
        );
    }

    public function testCallingControllerWithoutActionShouldPullFromIndexAction()
    {
        $this-&gt;dispatch('/user');
        $this-&gt;assertResponseCode(200);
        $this-&gt;assertController('user');
        $this-&gt;assertAction('index');
    }

    public function testIndexActionShouldContainLoginForm()
    {
        $this-&gt;dispatch('/user');
        $this-&gt;assertResponseCode(200);
        $this-&gt;assertSelect('form#login');
    }

    public function testValidLoginShouldInitializeAuthSessionAndRedirectToProfilePage()
    {
        $this-&gt;request
             -&gt;setMethod('POST')
             -&gt;setPost(array(
                 'username' =&gt; 'foobar',
                 'password' =&gt; 'foobar'
             ));
        $this-&gt;dispatch('/user/login');
        $this-&gt;assertTrue(Zend_Auth::getInstance()-&gt;hasIdentity());
        $this-&gt;assertRedirectTo('/user/view');
    }
}
</code></pre></div>

<p>
    You'll note that the <code>setUp()</code> method assigns a callback to the
    <code>$bootstrap</code> property. This allows the test case to call that
    callback to bootstrap the application; alternately, you can specify the path
    to a file to include that would do your bootstrapping. In the example above,
    I actually simply add a single "initialization" plugin to the front
    controller that takes care of bootstrapping my application (via the
    <code>routeStartup()</code> hook).
</p>

<p>
    I then have a few test cases. The first checks to ensure that the default
    action is called when no action is provided. The second checks to ensure
    that the login form is present on that page (by using a CSS selector to find
    a form with the id of 'login'). The third checks to see if I get a valid
    authentication session when logging in with good credentials, and that I get
    redirected to the appropriate location.
</p>

<p>
    This is, of course, just the tip of the iceberg; I've created a couple dozen
    other assertions as well.
</p>

<p>
    You can preview the functionality in the 
    <a href="http://framework.zend.com/svn/framework/standard/incubator/">Zend 
        Framework standard incubator</a>; look for
    Zend_Test_PHPUnit_ControllerTestCase in there, as well as the Zend_Test
    documentation in the documentation tree (in human-readable DocBook XML).
</p>

<p>
    For those of you who decide to start playing with this, I'd love any
    feedback I can get. The best place to do so, however, is on the fw-mvc
    mailing list; <a href="http://framework.zend.com/wiki/display/ZFDEV/Contributing+to+Zend+Framework#ContributingtoZendFramework-Subscribetotheappropriatemailinglists">instructions are on the ZF wiki</a>.
</p>]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Zend Framework Dojo Integration</title>
      <pubDate>Sun, 25 May 2008 16:40:37 +0000</pubDate>
      <link>http://mwop.net/blog/176-Zend-Framework-Dojo-Integration.html</link>
      <guid>http://mwop.net/blog/176-Zend-Framework-Dojo-Integration.html</guid>
      <author>me@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>
    I'm pleased to announce that 
    <a href="http://framework.zend.com/">Zend Framework</a> will be partnering
    with <a href="http://dojotoolkit.org/">Dojo Toolkit</a> to deliver
    out-of-the-box Ajax and rich user interfaces for sites developed in Zend
    Framework.
</p>

<p>
    First off, for those ZF users who are using other Javascript toolkits: Zend
    Framework will continue to be basically JS toolkit agnostic. You will still
    be able to use whatever toolkit you want with ZF applications. ZF will
    simply be shipping Dojo so that users have a toolkit by default. Several
    points of integration have been defined, and my hope is that these can be
    used as a blueprint for community contributions relating to other javascript
    frameworks. In the meantime, developers choosing to use Dojo will have a
    rich set of components and integration points to work with.
</p>

<p>
    The integration points we have defined for our initial release are as
    follows:
</p><ul>
    <li><p>
        <b>JSON-RPC Server:</b> We are re-working the Zend_Json_Server that has
        been in our incubator since, oh, what? 0.2.0? and never released to
        actually follow a specification: 
        <a href="http://groups.google.com/group/json-rpc">JSON-RPC</a>. This
        will allow it to work seamlessly with Dojo, as well as other toolkits
        that have JSON-RPC client implementations. I have actually completed
        work on this, though the proposal is waiting to be approved; if you want
        to check it out, you can find it in the 
        <a href="http://framework.zend.com/svn/framework/branch/user/matthew/zed_json_server">ZF svn</a>.
        </p><p>
        The original Zend_Json_Server implementation will be abandoned. It was
        never fully tested nor fully documented, which has prevented its
        release. Additionally, since it implemented its own ad-hoc standard, it
        did not provide the type of interoperability that a true JSON-RPC server
        implementation will provide. I am excited that we will finally be able
        to provide a standards-compliant solution for general availability.
        </p><p>
        One final note: there are currently two different JSON-RPC
        specifications, 1.0 and 2.0. Currently, the implementation I've been
        working on will switch payload formats based on the request, and can
        deliver different SMD formats appropriately as well.
    </p></li>

    <li>
        <b>dojo() View Helper:</b> Enabling Dojo for a page is not typically as
        trivial as just loading the <code>dojo.js</code> script -- you have a
        choice of loading it from the AOL CDN or a local path, and also may want
        or need to load additional dojo, dijit, or dojox modules, specify custom
        modules and paths, specify code to run at <code>onLoad()</code>, and
        specify stylesheets for decorating dijits. On top of this, this
        information may change from page to page, and may only be needed for
        a subset of pages. The <code>dojo()</code> view helper will act as a 
        <a href="http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.placeholder">placeholder</a>
        implementation, and facilitate all of the above tasks, as well as take
        care of rendering the necessary <code>style</code> and
        <code>script</code> elements in your page.
    </li>

    <li>
        <b>Form Element implementations:</b> One area that developers really
        leverage javascript and ajax toolkits is forms. In particular, many
        types of form input can benefit from advanced and rich user interfaces
        that only javascript can provide: calendar choosers, time selectors,
        etc. Additionally, many like to use client-side validation in order to
        provide instantaneous validation feedback to users (instead of requiring
        a round-trip to the server). We will be identifying a small group of
        form elements that we feel solve the most relevant use cases, and write
        Dojo-specific versions that can be utilized with <code>Zend_Form</code>.
        (One thing to note: <code>Zend_Form</code>'s design already works very
        well with Dojo, allowing many widgets and client-side validations to be
        created by simply setting the appropriate element attributes.)
    </li>

    <li>
        <b>dojo.data Compatibility:</b> <code>dojo.data</code> defines a
        standard storage interface; services providing data in this format can
        then be consumed by a variety of Dojo facilities to provide highly
        flexible and dynamic content for your user interfaces. We will be
        building a component that will create dojo.data compatible payloads with
        which to respond to XmlHttpRequests; you will simply need to pass in the
        data, and provide metadata regarding it.
    </li>
</ul>

<p>
    So, some examples are in order. First off, <code>Zend_Json_Server</code>
    operates like all of ZF's server components: if follows the 
    <a href="http://php.net/soap_soapserver_construct">SoapServer API</a>. This
    allows you to attach arbitrary classes and functions to the server
    component. Additionally, it can build a Service Mapping Description (SMD)
    that Dojo can consume in order to discover valid methods and signatures. As
    an example, on the server side you could have the following:
</p>

<div class="example"><pre><code lang="php">
// /json-rpc.php
// Assumes you have a class 'Foo' with methods 'bar' and 'baz':
$server = new Zend_Json_Server();
$server-&gt;setClass('Foo')
       -&gt;setTarget('/json-rpc.php')
       -&gt;setEnvelope('JSON-RPC-1.0')
       -&gt;setDojoCompatible(true);

// For GET requests, simply return the service map
if ('GET' == $_SERVER['REQUEST_METHOD']) {
    $smd = $server-&gt;getServiceMap();
    header('Content-Type: application/json');
    echo $smd;
    exit;
}

$server-&gt;handle();
</code></pre></div>

<p>
    On your view script side, you might then do the following:
</p>

<div class="example"><pre><code lang="php">
&lt;h2&gt;Dojo JSON-RPC Demo&lt;/h2&gt;
&lt;input name=\&quot;foo\&quot; type=\&quot;button\&quot; value=\&quot;Demo\&quot; onClick=\&quot;demoRpc()\&quot;/&gt;
&lt;? 
$this-&gt;dojo()-&gt;setLocalPath('/js/dojo/dojo.js')
             -&gt;addStyleSheetModule('dijit.themes.tundra')
             -&gt;requireModule('dojo.rpc.JsonService');
$this-&gt;headScript()-&gt;captureStart(); ?&gt;
function demoRpc()
{
    var myObject = new dojo.rpc.JsonService('/json-rpc.php');
    console.log(myObject.bar());
}
&lt;? $this-&gt;headScript()-&gt;captureEnd() ?&gt;
</code></pre></div>

<p>
    And, finally, in your layout script, you might have the following:
</p>

<div class="example"><pre><code lang="php">
&lt;?= $this-&gt;doctype() ?&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;...&lt;/title&gt;
        &lt;?= $this-&gt;dojo() ?&gt;
        &lt;?= $this-&gt;headScript() ?&gt;
    &lt;/head&gt;
    &lt;body class=\&quot;tundra\&quot;&gt;
        &lt;?= $this-&gt;layout()-&gt;content ?&gt;
    &lt;/body&gt;
&lt;/html&gt;
</code></pre></div>

<p>
    The example doesn't do much -- it simply logs the results of the JSON-RPC
    call to the console -- but it demonstrates a number of things:
</p>

<ul>
    <li>
        <b>dojo() View Helper:</b> The example shows using dojo from a local
        path relative to the server's document root; using the 'Tundra'
        stylesheet shipped with Dojo and attaching it to the layout; capturing a
        required module ('dojo.rpc.JsonService'); and rendering the necessary
        Dojo stylsheet and script includes in the layout.
    </li>
    <li>
        <b>JSON-RPC client:</b> Dojo requires that you point the JsonService to
        an endpoint that delivers a Service Mapping Description; in this
        example, I use any GET request to return the SMD. Once the SMD is
        retrieved, any methods exposed are available to the Javascript object as
        if they were internal methods -- hence <code>myObject.bar()</code>.
        Dojo's current implementation performs all other requests as POST
        requests, passing the data via the raw POST body.
    </li>
</ul>

<p>
    There will be more to come in the future, and I will be blogging about
    developments as I get more proposals up and code into the repository. All in
    all, this is a very exciting collaboration, and should help provide ZF
    developers the ability to rapidly create web applications with rich, dynamic
    user interfaces.
</p>

<p>
    <b>Update:</b> <a href="http://andigutmans.blogspot.com/2008/05/dojo-and-zend-framework-partnership.html">Andi has posted an FAQ on our integration</a>.]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>2007 Retrospective</title>
      <pubDate>Thu, 03 Jan 2008 11:22:35 +0000</pubDate>
      <link>http://mwop.net/blog/154-2007-Retrospective.html</link>
      <guid>http://mwop.net/blog/154-2007-Retrospective.html</guid>
      <author>me@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>
    2007 was a busy year, both personally and professionally. I won't go into
    the personal too much, because, well, it's personal, and some of the details
    are simply inappropriate for blogging material.
</p>

<p>
    Here's the short version:
</p>

<ul>
    <li>One trip to Belgium and The Netherlands.</li>
    <li>Two trips to Israel.</li>
    <li>Two trips to Atlanta, GA (not counting the return trip from Europe, when
    I was stranded for a day due to storms in the Northeast).</li>
    <li>Three different user groups attended, with three presentations.</li>
    <li>One major Zend Framework release</li>
    <li>One PEAR release.</li>
    <li>One podcast.</li>
    <li>One webinar.</li>
    <li>One book published.</li>
    <li>One conference attended.</li>
</ul>

<p>
    What follows is my month-by-month breakdown:
</p><h3>January</h3>
<p>
    I finished up the last of my three chapters for 
    <a href="http://sitepoint.com/books/phpant2/">The PHP Anthology, 2nd Edition</a>, 
    and promptly used my advance to buy the family a Wii. 
</p>

<p>
    I was also introduced to <a href="http://jansch.nl/">Ivo Jansch</a> through
    work, and had him wow me with one of the 
    <a href="http://ibuildings.nl">ibuildings</a> products.
</p>

<h3>February</h3>
<p>
    Mid-month, my boss at <a href="http://www.zend.com/">Zend</a>, Boaz, got the
    go-ahead to use the ibuildings WDE platform to build our new website CMS;
    part of the plan would include training at the ibuildings home office in
    Vlissingen, NL... which meant I had to get my passport pronto.
</p>

<p>
    Towards the end of the month, I was invited to 
    <a href="http://bostonphp.org">BostonPHP</a> to present 
    building a simple <a href="http://framework.zend.com/">Zend Framework</a>
    CRUD application, along with <a href="http://hagunbu.ch/">Chuck Hagenbuch</a> 
    of the <a href="http://horde.org/">HORDE project</a>. While there, 
    <a href="http://cake.insertdesignhere.com/">Nate Abele</a> heckled me, and
    then joined Chuck and myself for an impromptu framework panel; a good time
    was had by all.
</p>

<h3>March</h3>
<p>
    I flew to Brussels, Belgium, where I met my supervisor, Boaz, so we could go
    to Vlissingen. We spent the day in Brussels, walking around and visiting
    such sites as the Cathedral of St. Michael, La Grand Place, and the Mannekin
    Pis. 
</p>

<p>
    Our visit to ibuildings was very productive, and I was very impressed by the
    team there; everybody was very knowledgeable and skilled. I presented a Zend
    Framework overview, as well as an abbreviated version of the Best Practices
    talk I'd given with Mike Naberezny at the 2006 ZendCon; the latter ignited a
    ton of questions and enthusiasm.
</p>

<p>
    On returning home, I had a ton of work to do on the zend.com CMS, and this
    continued in spurts through November. The job was made much easier by the
    ibuildings WDE product.
</p>

<p>
    I closed out a ton of MVC issues in the Zend Framework, and we released the
    first beta version late in the month.
</p>

<h3>April</h3>
<p>
    At the beginning of the month, our landlord threw us for a loop and
    announced he was going to sell our apartment... meaning that we either had
    to step up our plans to purchase a home, or start looking for a new rental.
    Ultimately, we ended up looking for a rental, due to time constraints. The
    next two months would be highlighted with the look for a new place as well
    as countless showings of our apartment to potential buyers.
</p>

<p>
    Mid-month, we packed up the family and flew down to Atlanta, GA, to visit my
    wife's family. While there, we were able to go to the Atlanta Zoo and see
    Mei Lan, their baby panda -- way cute!
</p>

<h3>May</h3>
<p>
    Mid-month, we found a new place in Richmond, VT -- a small village about 10
    minutes from Burlington, near where we originally lived when we first moved
    to Vermont. 
</p>

<p>
    During the first RC for Zend Framework, released at the end of the month, I
    introduced the ViewRenderer, a feature for auto-rendering views based on the
    current controller and action name -- a feature common to many frameworks.
    However, it ostensibly broke a ton of existing applications by being enabled
    by default -- not one of my more popular decisions. Since the 1.0.0 release,
    I've heard little grumbling about it, and it's now often cited as an
    ease-of-use feature -- go figure.
</p>

<h3>June</h3>
<p>
    The first week of June, I flew to Tel Aviv, Israel, to start training people
    on the new CMS, as well as to work with our entire ebiz team to finalize the
    work plan for completing the CMS. It was, needless to say, my first time to
    Israel or the Middle East, and I was constantly confronted with culture
    shock. Europe was an easy transition to make, but Israel was completely
    foreign to me -- everything from the way people drove, to the architecture,
    to the food was different. Unfortunately, I arrived a day late due to a
    flight cancellation, and missed the tour of Jerusalem my supervisor had
    planned for all of us. However, he took me to the city of Yafa, an Arabic
    city where the Israeli's originally tried to settle before building Tel Aviv
    to the north. The architecture was amazing, as were the winding, narrow
    streets of the old city.
</p>

<p>
    I was also told during this trip that Andi had requested transferring me
    full-time to the Zend Framework team. I would spend the next week or two
    weighing my options, and ultimately decided to do so.
</p>

<p>
    A week after I returned, we moved into our new rental in Richmond. The kids
    love the new place, which has a bedroom for each of them, a yard, and
    porches on each entrance.
</p>

<p>
    Somehow, I also found time to record my first (and so far only) 
    <a href="http://devzone.zend.com/article/2140-PHP-Abstract-Podcast-Episode-2---Backup-or-Die">PHP Abstract podcast</a>.
</p>

<h3>July</h3>
<p>
    We released <a href="http://framework.zend.com/">Zend Framework</a> 1.0.0 at
    the beginning of the month, marking our first stable release. While many
    still view it as incomplete, the overwhelming feedback has been positive,
    and we've had over 2 million downloads to date.
</p>

<p>
    I accepted the transfer to the Zend Framework team, but the condition was
    made that I would stay part-time on the ebiz team until the new site was
    launched.  This meant that the next 5 months were spent splitting my time
    between the two projects, often working late and on weekends to get work
    done.
</p>

<p>
    Towards the end of the month, we took a long weekend camping in Vermont's
    Northeast Kingdom. The weather was unseasonably wet, but we persevered and
    had a great time. 5 days of offline time was definitely needed!
</p>

<p>
    I also finally released the first stable version of 
    <a href="http://pear.php.net/packages/File_Fortune">File_Fortune</a> on 
    <a href="http://pear.php.net/">PEAR</a>, over a year since I'd first
    proposed it. The package interfaces with mod_fortune files, allowing both
    the ability to read and write such files, with full binary compatability.
</p>

<h3>August</h3>
<p>
    Not much to report in August, except work, work, and more work.
</p>

<h3>September</h3>
<p>
    My ebiz supervisor, Boaz, flew me to Tel Aviv for a second time, this time
    to perform a "brain dump" for the rest of the team before I transitioned
    fully out of the team, and also to help setup our new data center and
    release procedures. This time, Boaz took me to Jerusalem himself during my
    last full day in the country. If you've never been to the city, you should
    definitely put it on your list of things to do before you die. With my
    degree in religion, the place was full of meaning for me, but it would be
    putting it lightly to say that religion is palpable in the air there. We
    visited the Wailing Wall, the Via Dolorosa, the Church of the Holy
    Sepulchre, and listened to the muezzins sing the call to prayer for the
    muslims. The tour was simply amazing.
</p>

<p>
    A few days after I returned, I flew down to New York City for a special
    meeting of <a href="http://nyphp.org/">NYPHP</a>, where 
    <a href="http://blogs.zend.com/author/mark/">Mark de Visser</a> presented on
    various Zend products and initiatives, and I gave a Zend Framework overview.
</p>

<p>
    A week after the NYPHP presentation, I did a
    <a href="http://www.zend.com/webinars">zend.com webinar</a>
    on the Zend Framework MVC layer.
</p>

<h3>October</h3>
<p>
    October was the month of <a href="http://www.zendcon.con/">ZendCon</a>. I
    presented a full-day tutorial on best practices and unit testing with 
    <a href="http://sebastian-bergmann.de/">Sebastian Bergmann</a> and 
    <a href="http://naberezny.com/">Mike Naberezny</a>; despite the length and
    subject matter, we were SRO for most of the day. 
</p>
<p>
    I also did a main-stage presentation on Zend Framework's MVC components,
    directly following <a href="http://terrychay.com/blog/">Terry Chay</a> -- an
    intimidating situation at best. From the feedback I've seen, the
    presentation was well-received, and I had somewhere between 120 and 150
    attendees -- phenomenal! (Even more amazing was how many people were
    familiar with MVC in general!)
</p>

<p>
    One great thing about the conference was the fact that I got to network with
    a number of framework developers, both Zend Framework and otherwise,
    including Nate Abele of CakePHP as well as 
    <a href="http://paul-m-jones.com">Paul M. Jones</a> of 
    <a href="http://solarphp.com">the Solar framework</a>. Many good
    conversations were had.
</p>

<p>
    Late in the month, 
    <a href="http://sitepoint.com/books/phpant2/">The PHP Anthology, 2nd Edition</a>, 
    my first published book as an author, was finally released!
</p>

<h3>November</h3>
<p>
    I spent much of the month working on 
    <a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Layout">Zend_Layout</a>,
    a much requested component that simplifies and automates Two Step Views in
    Zend Framework. I also started work implementing 
    <a href="http://framework.zend.com/wiki/pages/viewpage.action?pageId=33071">Zend_View Enhanced</a>,
    a set of view helpers for making complex views with Zend_View possible.
</p>

<p>
    I also started playing with <a href="http://twitter.com/">Twitter</a> a bit,
    and came up with a 
    <a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Service_Twitter">Zend_Service_Twitter</a>
    proposal for interacting with the Twitter API via PHP.
</p>

<p>
    And finally, the Sunday before Thanksgiving, we finally launched the new 
    <a href="http://www.zend.com/">Zend.com</a> site, which was well-received in
    the blogosphere.
</p>

<h3>December</h3>
<p>
    A goal I've had for some time has been to form a PHP user group in the
    Burlington area. A friend of mine pointed out to me sometime this fall that
    there's actually already
    <a href="http://groups.google.com/group/Burlington-VT-PHP">a Google Group</a> 
    formed; he and the original founder started planning a meeting for early
    December. I spoke at this inaugural meeting, presenting Zend Framework's MVC
    layer yet again; a good time was had by all, and a lot of enthusiasm for
    future meetings was generated.
</p>

<p>
    I finished up Zend_Layout and Zend_View Enhanced with the help of Ralph
    Schindler, and got a new proposal up for 
    <a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Form">Zend_Form</a>,
    just in time for my holidays to begin -- 11 days with family and with little
    to no internet connectivity during a trip to Atlanta, GA for one of only a
    handful of Christmases I've spent without snow.
</p>

<h2>Summary</h2>
<p>
    This year was <em>incredibly</em> busy -- three cross-seas trips, one
    cross-continent trip, a move, and several trips along the Eastern Seaboard;
    three user group presentations, and eight presentations over the course of
    the year; one conference; one move; one PEAR release; one podcast; one
    webinar; one book; and countless hours of programming.
</p>

<p>
    My goals for the coming year? I'm too tired to even think about it ;-).
</p>]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
  </channel>
</rss>
