2011-06-15

Neo4j for PHP

Update 2011-09-14: I've put a lot of effort into Neo4jPHP since this post was written. It's pretty full-featured and covers almost 100% of the Neo4j REST interface. Anyone interested in playing with Neo4j from PHP should definitely check it out. I would love some feedback!

Lately, I've been playing around with the graph database Neo4j and its application to certain classes of problems. Graph databases are meant to solve problems in domains where data relationships can be multiple levels deep. For example, in a relational database, it's very easy to answer the question "Give me a list of all actors who have been in a movie with Kevin Bacon":
> desc roles;
+-------------+--------------+------+-----+---------+----------------+
| Field       | Type         | Null | Key | Default | Extra          |
+-------------+--------------+------+-----+---------+----------------+
| id          | int(11)      | NO   | PRI | NULL    | auto_increment |
| actor_name  | varchar(100) | YES  |     | NULL    |                |
| role_name   | varchar(100) | YES  |     | NULL    |                |
| movie_title | varchar(100) | YES  |     | NULL    |                |
+-------------+--------------+------+-----+---------+----------------+

> SELECT actor_name, role_name FROM roles WHERE movie_title IN (SELECT DISTINCT movie_title FROM roles WHERE actor_name='Kevin Bacon')
Excuse my use of sub-queries (re-write it to a JOIN in your head if you wish.)

But suppose you want to get the names of all the actors who have been in a movie with someone who has been in a movie with Kevin Bacon. Suddenly, you have yet another JOIN against the same table. Now add a third degree: someone who has been in a movie with someone who has been in a movie with someone who has been in a movie with Kevin Bacon. As you continue to add degrees, the query becomes increasingly unwieldy, harder to maintain, and less performant.

This is precisely the type of problem graph databases are meant to solve: finding paths between pieces of data that may be one or more relationships removed from each other. They solve it very elegantly by modeling domain objects as graph nodes and edges (relationships in graph db parlance) and then traversing the graph using well-known and efficient algorithms.

The above example can be very easily modeled this way: every actor is a node, every movie is a node, and every role is a relationship going from the actor to the movie they were in:
Now it becomes very easy to find a path from a given actor to Kevin Bacon.

Neo4j is an open source graph database, with both community and enterprise licensing structures. It supports transactions and can handle billions of nodes and relationships in a single instance. It was originally built to be embedded in Java applications, and most of the documentation and examples are evidence of that. Unfortunately, there is no native PHP wrapper for talking to Neo4j.

Luckily, Neo4j also has a built-in REST server and PHP is very good at consuming REST services. There's already a good Neo4j REST PHP library out there, but I decided to write my own to get a better understanding of how the REST interface actually works. You can grab it here and all the code examples below are written using it. The concepts can easily be ported to any Neo4j REST client.

First, we need to initialize a connection to the database. Since this is a REST interface, there is no persistent connection, and in fact, no data communication happens until the first time we need to read or write data:
use Everyman\Neo4j\Client,
    Everyman\Neo4j\Transport,
    Everyman\Neo4j\Node,
    Everyman\Neo4j\Relationship;

$client = new Client(new Transport('localhost', 7474));
Now we need to create a node for each of our actors and movies. This is analogous to the INSERT statements used to enter data into a traditional RDBMS:
$keanu = new Node($client);
$keanu->setProperty('name', 'Keanu Reeves')->save();
$laurence = new Node($client);
$laurence->setProperty('name', 'Laurence Fishburne')->save();
$jennifer = new Node($client);
$jennifer->setProperty('name', 'Jennifer Connelly')->save();
$kevin = new Node($client);
$kevin->setProperty('name', 'Kevin Bacon')->save();

$matrix = new Node($client);
$matrix->setProperty('title', 'The Matrix')->save();
$higherLearning = new Node($client);
$higherLearning->setProperty('title', 'Higher Learning')->save();
$mysticRiver = new Node($client);
$mysticRiver->setProperty('title', 'Mystic River')->save();
Each node has `setProperty` and `getProperty` methods that allow storing arbitrary data on the node. No server communication happens until the `save()` call, which must be called for each node.

Linking an actor to a movie means setting up a relationship between them. In RDBMS terms, the relationship takes the place of a join table or a foreign key column. In the example, the relationship always starts with the actor pointing to the movie, and is tagged as an "acted in" type relationship:
$keanu->relateTo($matrix, 'IN')->save();
$laurence->relateTo($matrix, 'IN')->save();

$laurence->relateTo($higherLearning, 'IN')->save();
$jennifer->relateTo($higherLearning, 'IN')->save();

$laurence->relateTo($mysticRiver, 'IN')->save();
$kevin->relateTo($mysticRiver, 'IN')->save();
The `relateTo` call returns a Relationship object, which is like a node in that it can have arbitrary properties stored on it. Each relationship is also saved to the database.

The direction of the relationship is totally arbitrary; paths can be found regardless of which direction a relationship points. You can use whichever semantics make sense for your problem domain. In the example above, it makes sense that an actor is "in" a movie, but it could just as easily be phrased that a movie "has" an actor. The same two nodes can have multiple relationships to each other, with different directions, types and properties.

The relationships are all set up, and now we are ready to find links between any actor in our system and Kevin Bacon. Note that the maximum length of the path is going to be 12 (6 degrees of separation multiplied by 2 nodes for each degree; an actor node and a movie node.)
$path = $keanu->findPathsTo($kevin)
    ->setMaxDepth(12)
    ->getSinglePath();

foreach ($path as $i => $node) {
    if ($i % 2 == 0) {
        echo $node->getProperty('name');
        if ($i+1 != count($path)) {
            echo " was in\n";
        }
    } else {
        echo "\t" . $node->getProperty('title') . " with\n";
    }
}
A path is an ordered array of nodes. The nodes alternate between being actor and movie nodes.

You can also do the typical "find all the related movies" type queries:
echo $laurence->getProperty('name') . " was in:\n";
$relationships = $laurence->getRelationships('IN');
foreach ($relationships as $relationship) {
    $movie = $relationship->getEndNode();
    echo "\t" . $movie->getProperty('title') . "\n";
}
`getRelationships` returns an array of all relationships that a node has, optionally limiting to only relationships of a given type. It is also possible to specify only incoming or outgoing relationships. We've set up our data so that all 'IN' relationships are from an actor to a movie, so we know that the end node of any 'IN' relationship is a movie.

There is more available in the REST interface, including node and relationship indexing, querying, and traversal (which allows more complicated path finding behaviors.) Transaction/batch operation support over REST is marked "experimental" for now. I'm hoping to add wrappers for more of this functionality soon. I'll also be posting more on the different types of problems that can be solved very elegantly with graphing databases.

The next post dives into a bit more detail about path-finding and some of the different algorithms available.

2011-05-16

Logging User Sessions Across Requests

When tracking down a bug, few things aid the process more than good application logs. And this is especially true when the bug has already escaped to your production systems. In these cases, you may not be able to inject test data into your database, cowboy-code in some `var_dump`s or perform the same action over and over again. Logs can save a lot of time and effort when tracking down issues in a live application.

But logs also have a few shortcomings. In an active application with multiple users, all the log messages for every user are piled together in the same log file. If the application is multi-user (like most web applications), log messages from different requests can be interwoven. This makes it a nightmare to try and track the log messages for a single user request, let alone messages across several requests by the same user.

One way to handle this is to put a request-specific identifier in every log message. But I shouldn't have to remember to append or prepend the identifier to their log messages. I'd rather have it happen automatically, without me or my teammates having to think about it.

Here's a method we've been using to try and untangle the mess and retain the usefulness of our logs. The code uses Zend's logging component, but can easily be adapted to other log systems.

First, we start up a log and tell it how to format each log line.
$format = "%timestamp% [%logId%]: %message%" . PHP_EOL;
$formatter = new Zend_Log_Formatter_Simple($format);

$writer = new Zend_Log_Writer_Stream('/path/to/application.log');
$writer->setFormatter($formatter);

$log = new Zend_Log();
$log->addWriter($writer);
In the format string, names wrapped in "%" signs are log variables that will be filled in when a message is logged. "timestamp" and "message" are provided by Zend and will be filled with the appropriate values when the logging call is made. "logId" is a custom symbol, which means we need to tell the log object what value to give it.

In order to track all the log messages across a single user request, we need to pick a unique value for the log id:
// The last 8 characters of a uniqid are unique enough for our purposes
$logId = substr(uniqid(), -8);
$log->setEventItem('logId', $logId);
Now we wait for a few users to make requests at the same time:
// A web request
$log->info('The user made some request');
$log->info('Here is some output of that request: '.$output);

// A different, simultaneous request from another user
$log->info('The user made some request');
$log->info('Here is some output of that request: '.$output);

// Another request from the first user
$log->info('Another user request');
Here's the log output:
2011-05-16 19:04 [a73fe12a]: The user made some request
2011-05-16 19:04 [24f90ae1]: The user made some request
2011-05-16 19:04 [24f90ae1]: Here is some output of that request: Hello, World
2011-05-16 19:04 [a73fe12a]: Here is some output of that request: Hello, World
2011-05-16 19:04 [feb40a52]: Another user request
Now we can easily track a single request's messages in the log, even if multiple requests happen at the same time.

But we can do better. Often times, bugs are not the result of a single user request, but of a sequence of requests. If we only track logs on a per request basis, we haven't helped ourselves track request-spanning bugs. Luckily, we already have a value that uniquely identifies a user across multiple requests: their PHP session id. We can use it, in addition to a request identifier:
// The first 8 characters of the session id are unique enough
session_start();
$sessId = substr(session_id(), 0, 8);
$requestId = substr(uniqid(), -8);
$logId = $sessId.'-'.$requestId;
$log->setEventItem('logId', $logId);
And here's what our logs look like now:
2011-05-16 19:04 [b74be12f-a73fe12a]: The user made some request
2011-05-16 19:04 [075aef5e-24f90ae1]: The user made some request
2011-05-16 19:04 [075aef5e-24f90ae1]: Here is some output of that request: Hello, World
2011-05-16 19:04 [b74be12f-a73fe12a]: Here is some output of that request: Hello, World
2011-05-16 19:04 [b74be12f-feb40a52]: Another user request
And there it is. We can use the log id in useful ways, such as displaying it to the user with a "contact tech support" message. When a user calls or emails and says their "support id" is "b74be12f-a73fe12a" we not only have the log messages for the request that gave them the error, but all the messages for their entire session:
# All the request log messages
> grep 'b74be12f-a73fe12a' /path/to/application.log
# All the session log messages
> grep 'b74be12f-' /path/to/application.log

Here's all the code together:
$format = "%timestamp% [%logId%]: %message%" . PHP_EOL;
$formatter = new Zend_Log_Formatter_Simple($format);

$writer = new Zend_Log_Writer_Stream('/path/to/application.log');
$writer->setFormatter($formatter);

$log = new Zend_Log();
$log->addWriter($writer);

session_start();
$sessId = substr(session_id(), 0, 8);
$requestId = substr(uniqid(), -8);
$logId = $sessId.'-'.$requestId;
$log->setEventItem('logId', $logId);

$log->info('The user made some request');
$log->info('Here is some output of that request: '.$output);

2011-05-03

Interview with Bulat Shakirzyanov

Today on Cal Evans' "Voices of the ElePHPant" there was an interview with Bulat Shakirzyanov. His comments about mocking PHP's built-in functions reminded me of this PHP built-in mocking library I had made a little while ago, then forgotten about. Bulat's example talks about `mkdir`, which the built-in mock library would handle, but for filesystem specific functionality, I also highly recommend vfStream. It allows you to create a virtual filesystem, comeplete with directory hierarchy and files with contents, all from within a unit test, and all without needing an actual filesystem.

2011-04-22

Tropo-Turing Collision: Voice Chat Bots with Tropo

On the first evening of PHPComCon this year, Tropo sponsored a beer-and-pizza-fueled hackathon. Having never heard of Tropo before, I decided to check it out.

Tropo provides a platform for bridging voice, SMS, IM and Twitter data to create interactive applications in Javascript, PHP, Python and a couple other languages. An example would be the call-center navigation menus with which many people are familiar, or a voicemail system that sends a text message when a new message is received. But those are only the tip of the iceberg.

To explore a little deeper, I thought it would be neat to create a voice chat-bot. The idea would be that a caller could talk to an automated voice in a natural way, and the voice would respond in a relevant way and push the conversation along. Since I wasn't aiming for Turing test worthiness, a good starting point was ELIZA, one of the first automated chat-bots. I thought it was a pretty ambitious project, but it turns out that Tropo's system handles all of the functionality right out of the gate.

The docs do a great job of explaining setting up an account and creating an application, so I'm going to jump right into the code (in PHP).

I like to keep my code relatively clean and organized, with functions and classes in their own files. So the first thing I did was create a hosted file called "Eliza.php" with the following contents:
class Eliza
{
  public function respondsTo($statement="")
  {
     $responses = array(
       "0" => "one",
       "1" => "two",
       "2" => "three",
       "3" => "four",
       "4" => "five",
       "5" => "six",
       "6" => "seven",
       "7" => "eight",
       "8" => "nine",
       "9" => "zero",
     );

     $response = "Please pick a number from 0 to 9";
     if (isset($responses[$statement])) {
       $response = $responses[$statement];
     }

     return $response;
  }

  public function hears($prompt)
  {
    $result = ask($prompt, array(
      "choices" => "[1 DIGIT]"
    ));
    $statement = strtolower($result->value);
    return $statement;
  }
}
It's important to note that the opening <?php and closing ?> should be left out of this file, or the next bit will fail.

The main functionality of the application is in the `Eliza` class. Eliza will translate a user input string into a response, which it will then use to prompt the user. The `ask()` function in the `hears()` method is functionality provided by Tropo's system that takes care of the text-to-speech and speech-to-text aspect of prompting the caller, and then waits for the caller to respond. The `choices => [1 DIGIT]` option to `ask()` hints that we expect the user to respond to our prompt with a single 0-9 character.

Next, I created a file called "chatbot.php" with the following contents:
<?php
$url = "http://hosting.tropo.com/00001/www/Eliza.php";
$ElizaFile = file_get_contents($url);
eval($ElizaFile);

$eliza = new Eliza();
$statement = "";
do {
  $prompt = $eliza->respondsTo($statement);
  $statement = $eliza->hears($prompt);
  _log("They said ".$statement);
} while (true);
?>
This is the entry script for the application, which can be set on the "Application Settings" page. Unfortunately, it does not look like Tropo supports `require` and `include` in their system. Fortunately, what they do provide are URLs to download the contents of any hosted file via simple `file_get_contents`. So we "inlcude" our Eliza.php file by downloading its contents, then `eval`ing them into the running scripts scope. Note: Yes I could have just written the contents of Eliza.php into the chatbot.php file, but a) it was more fun to try and find a way around that limitation :-) and b) many developers separate their code this way to keep it clean, encapsulated and reusable and this demonstrates a way to accomplish that.

The code is fairly self-explanatory: "include" and instantiate Eliza, then enter a prompt-respond loop which will last until the caller hangs up. `_log()` outputs to Tropo's built in application debugger.

I have to say congratulations to Tropo for creating a platform that makes all this easy. I had this up and running (except the file include portion) in about 15 minutes. You can try this out for yourself by calling (919) 500-7747.

So now that prompt-response was working, I could get started on making a real Eliza chat-bot that would parse the caller's speech and provide an Eliza-like response:

Caller: I'm building a voice chat application.
Eliza: Tell me more about voice chat application.
Caller: You're it!
Eliza: How do you feel about that?
Caller: I think it's pretty neat.

There are probably a hundred Eliza implementations on the web, and at least a dozen are written in PHP. I grabbed the first one I found, shoved it into the `Eliza::respondsTo()` method, and called up my application.

And this is where I hit the iceberg. In order to accomplish what I wanted, I needed the `ask()` function to be able to accept and parse any spoken words into a string. This meant getting rid of the `choices => [1 DIGIT]` line. As soon as I called, Eliza started prompting me over and over for input, until Tropo's system killed the loop and hung up.

Luckily, a Tropo guy (who was great to talk to, but who's name I have unfortunately forgotten) informed me that `ask()` works by using the `choices` option as training for the speech-to-text parser. There is a way to do generalized speech-to-text but it is incredibly processor intensive, and would have to make use of an asynchronous call. Later that evening, @akalsey confirmed that the current state of the technology (everyone's, not just Tropo's) makes generalized real-time speech-to-text processing impossible. So the dream of speaking with Eliza instead of just IMing with her dies unfulfilled, or is at least put on the shelf until technology catches up with ideas.

I did learn some about Tropo's service, though, so I count the hackathon successful. Thanks again, Tropo, for the great new tool!

2011-04-14

Syntactic sugar for Vows.js

We end up writing quite a few full-stack integration tests. As the workflows for our projects get longer and more complex, the corresponding Zombie and Vows tests also get longer and more complex. Soon enough, a single test can be nested 15 or 20 levels deep, with a mix of anonymous functions and commonly-used macros.

Needless to say, these tests can become monsters to try and read through and understand:
var vows = require('vows');

vows.describe("Floozle Workflow").addBatch({
    "Go to login page" : {
        topic : function () {
            zombie.visit("http://widgetfactory.com", this.callback);
        },
        "then login" : {
            topic : function (browser) {
                browser
                  .fill("#username", "testuser")
                  .fill("#password", "testpass")
                  .pressButton("Login", this.callback);
            },
            "then navigate to Floozle listing" : {
                topic : function (browser) {
                    browser.click("Floozles", this.callback);
                },
                "should be on the Floozle listing" : function (browser) {
                    assert.include(browser.text("h3"), "Floozles");
                },
                "should have a 'Create Floozle' link" : function (browser) {
                    assert.include(browser.text("Create Floozle"));
                },
                "then click 'Create Floozle'" : {
                    topic : function (browser) {
                        browser.click("Create Floozle");
                    },
                    "then fill out Floozle form" : {
                        topic : function (browser) {
                            browser
                              .fill("#name", "Klaxometer")
                              .fill("#whizzbangs", "27")
                              .choose("#rate", "klaxes per cubic freep")
                              .pressButton("Save", this.callback);
                        },
                        // .... Continue on from there, with more steps
                    }
                }
            }
        }
    }
}).export(module);
So we created prenup, a syntactic sugar library for easily creating Vows tests. Prenup provides a fluent interface for generating easy to read test structures as well as the ability to reuse and chain testing contexts.

Here is the above test written with prenup:
var vows = require('vows'),
    prenup = require('prenup');

var floozleWorkflow = prenup.createContext(function () {
    zombie.visit("http://widgetfactory.com", this.callback);
})
.sub("then login", function (browser) {
    browser
      .fill("#username", "testuser")
      .fill("#password", "testpass")
      .pressButton("Login", this.callback);
})
.sub("then navigate to Floozle listing", function (browser) {
    browser.click("Floozles", this.callback);
})
.vow("should be on the Floozle listing", function (browser) {
    assert.include(browser.text("h3"), "Floozles");
})
.vow("should have a 'Create Floozle' link", function (browser) {
    assert.include(browser.text("Create Floozle"));
})
.sub("then click 'Create Floozle'", function (browser) {
    browser.click("Create Floozle");
})
.sub("then fill out Floozle form", function (browser) {
    browser
      .fill("#name", "Klaxometer")
      .fill("#whizzbangs", "27")
      .choose("#rate", "klaxes per cubic freep")
      .pressButton("Save", this.callback);
})
.root();

vows.describe("Floozle Workflow").addBatch({
    "Go to login page" : floozleWorkflow.seal()
}).export(module);
Every `sub()` call generates a new sub-context, and every `vows()` call attaches a vow assertion to the most recent context. `parent()` can be called to pop back up the chain and attach parallel contexts. If a context is saved, it can be reused in other chains. When `seal()` is called, it will generate the same structure as the original test.

More examples, including branching and reusing contexts, are given in the documentation. Prenup is available on NPM via `npm install prenup`

2011-04-07

Dynamic Assets: Part III - Entry Forms

Now that we have instantiated our dynamically defined objects, we can get to the meat of our project: displaying a form based on the dynamic object.

My team uses Zend to build our forms, but the technique can easily be adapted for other frameworks. Since Zend_Form affords adding fields one at a time, we can easily build a form by looping through an Asset's field list:
$asset = $assetFactory->build('some_type');

$form = new Zend_Form();
// ... Set up our form action, method, etc. ...

$typeField = new Zend_Form_Element_Hidden('__asset_type');
$typeField->setValue($asset->getType())
    ->setRequired(true);
$form->addElement($typeField);

foreach ($asset->listFields() as $fieldName => $field) {
    if (!$field->isSubAsset()) {
        $fieldElement = new AssetFieldFormElement($field, $asset->$fieldName);
        $form->addElement($fieldElement);
    }
}

// ... Add a submit button, other form housekeeping ...
There are two interesting bits to the above snippet. Firstly, we intentionally skip rendering sub-asset fields as part of the form. We found it was easier to make the user create a parent asset before allowing them to assign sub-assets to it. This simplifies the form generation, display, validation and data persistence.

The second interesting bit is the introduction of a new class: AssetFieldFormElement. We give this class the field object for which to build form element(s), and the value of the field.

This class is where the heavy-lifting is done. The basic idea is to read the field information, instantiate a proper Zend_Form_Element object (or set of objects) and set the properties of that element. The rest of the class is mainly methods to pass through calls to things like "addValidator()", "render()", etc.

Using this class, we can turn any given AssetField object into a wide variety of HTML form elements. The simplest is a basic text box:
$field = new AssetField();
$field->setName("first_name");
$element = new AssetFieldFormElement($field, "Zaphod");
Rendered HTML:
<label>First Name</label> <input name="first_name" type="text" value="Zaphod" />

 
If the field has a list of options, it renders as a drop-down. For things like the "date" type, we have a form element which will automatically attach a date-picker to the text box.

In a lot of cases, a single field will render as multiple form elements. For instance, sometimes a field's value can be an array of strings:
$field = new AssetField();
$field->setName("favorite_restaurants")
    ->setCollection(true);
$element = new AssetFieldFormElement($field, array("Milliways", "Stavro Mueller Beta"));
Rendered HTML:
<label>Favorite Restaurants</label>
<input name="favorite_restaurants[]" type="text" value="Milliways" />
<input name="favorite_restaurants[]" type="text" value="Stavro Mueller Beta" />
<input name="favorite_restaurants[]" type="text" value="" /><a href="#" class="adder" rel="favorite_restaurants">[+]</a>




[+]
A bit of Javascript on the "[+]" link allows the user to add more text boxes as needed (we can use the AssetField's "max" property to limit that if need be.)

Hang on, what if we we have a list of options for the user to choose from, but they can only choose each option once, and they need to be able to add their own if necessary? Not a problem:
$field = new AssetField();
$field->setName("drinks")
    ->setCollection(true)
    ->setUnique(true)
    ->setOptions(array("jynnan tonnyx", "jinond-o-nicks", "Ouisghian Zodah"))
    ->setOther("Something else?");
$element = new AssetFieldFormElement($field, array("jinond-o-nicks", "Pan Galactic Gargleblaster"));
Rendered HTML:
<label>Drinks</label>
<input name="drinks[]" type="checkbox" value="jynnan tonnyx" /> jynnan tonnyx
<input name="drinks[]" type="checkbox" value="jinond-o-nicks" checked="true" /> jinond-o-nicks
<input name="drinks[]" type="checkbox" value="Ouisghian Zodah" /> Ouisghian Zodah
<label>Something else?</label> <input name="drinks[]" type="text" value="Pan Galactic Gargleblaster" />


 jynnan tonnyx
 jinond-o-nicks
 Ouisghian Zodah
 
A wide variety of form effects can be achieved through combining different asset field flags.

The other function that AssetFieldFormElement performs is adding appropriate validators to each form element. This allows us to use `$form->isValid($params)` just as we do with our application's static forms.

This is the last of the posts on dynamic class definition. I hope it was helpful.

2011-04-04

Dynamic Assets: Part II - Construction

In the last post I introduced a feature in my team's current project that will allow our users (and us!) to dynamically define "assets", and I explained the syntax for defining an asset.

Now that we have our definition, we need to actually construct an asset instance. There are several classes involved here: the AssetField, which stores the display and validation information contained in the definition for a single field; the Asset which is basically a container wrapped around a list of fields and field values; and the AssetFactory, which reads a definition and constructs an Asset by hanging fields on it.

In the code below, I'm intentionally leaving out most of the gory details because they're boring and I'm focusing more on the design than the implementation.

AssetField is essentially a code translation of the JSON definition. Here's some of what it looks like:
class AssetField
{
    const TypeString   = 'string';
    const TypeInteger  = 'int';
    // etc. for each type
    
    protected $name = null;
    protected $display = null;
    protected $type = self::TypeString;
    // etc. for each attribute that can exist in a field definition
    
    public function getName()
    {
        return $this->name;
    }
    
    // All setters return $this to provide a fluent interface
    public function setName($name)
    {
        $this->name = $name;
        return $this;
    }

    // ... other getters and setters for each property
    // listed in the definition attributes ...
}

// Create a field named "power_sources" whose value is a unique array
// of one or more of the values "Gas", "Electric", "Solar"
// which defaults to having Gas and Solar turned on
// and allows the user to enter their own value if they need to.
$powerSourceField = new AssetField();
$powerSourceField->setName("power_sources")
    ->setUnique(true)
    ->setCollection(true)
    ->setOptions(array("Gas", "Electric", "Solar"))
    ->setDefault(array("Gas", "Solar"));
    ->setOther(true);

Every Asset object will carry around its AssetField objects, so that it can provide information about how it is built. This allows our form construction and validation code to be very generic.

Since Assets don't know what fields will be hung on them, we use PHP's magic `__get` and `__set` methods to set and return field values. However, during implementation we realized that the times we want to know the value of a field are very rare; more often, we want to know the properties of a field. So we also utilize the magic `__call` to give our code access to the underlying field object.

Here is what most of the Asset class looks like:
class Asset
{
    protected $type = null;
    protected $display = null;

    protected $fields = array();
    protected $values = array();

    // Return the field object when its name is called as a class method
    public function __call($fieldName, $args)
    {
        return isset($this->fields[$fieldName]) ? $this->fields[$fieldName] : null;
    }

    // Get the value of the named field
    public function __get($fieldName)
    {
        return isset($this->fields[$fieldName]) ? $this->values[$fieldName] : null;
    }

    // Set the value of the named field
    public function __set($fieldName, $value)
    {
        if (isset($this->fields[$fieldName])) {
           $this->values[$fieldName] = value;
        }
    }

    // Hang a new field on this asset
    public function addField(AssetField $field)
    {
        $name = $field->getName();
        $this->fields[$name] = $field;
        $this->values[$name] = $field->getDefault();
        return $this;
    }

    // ... Helper methods for returning the list of all fields
    // setting the Asset type, display string and instance name format ...
}

// Get an instantiation of our plumbing system example
$plumbing = new Asset();
$plumbing->setType("plumbing")
    ->setDisplay("Home Plumbing")
    ->setInstanceNameFormat("Installed %installation_date%");

$waterSource = new AssetField();
$waterSource->setName("water_source")
    ->setOptions(array("city", "well"))
    ->setOther("Where does the water come from")
    ->setDefault("city");

$installationDate = new AssetField();
$installationDate->setName("installation_date")
    ->setType(AssetField::TypeDate)
    ->setRequired(true);

$waterHeater = new AssetField();
$waterHeater->setName("water_heater")
    ->setType(AssetField::TypeSub)
    ->setOptions(array("gas_heater", "electric_heater"));

$showers = new AssetField();
$showers->setName("showers")
    ->setType(AssetField::TypeSub)
    ->setOptions(array("shower"))
    ->setCollection(true)
    ->setMax(5)
    ->setDefault(array());

$plumbing->addField($waterSource)
    ->addField($installationDate)
    ->addField($waterHeater)
    ->addField($showers);

// Use the asset 
$plumbing->water_source = "well";
$plumbing->installation_date = "06/05/2004";

echo $plumbing->getDisplay() . ": $plumbing";
// "Home Plumbing: Installed 06/05/2004"  <--- comes from an overloaded __toString method

echo "My water comes from a {$plumbing->water_source}"
// "My water comes from a well"

// Instantiate a new shower asset
$shower = new Asset();
// ... set up the asset ...

// add the shower to the plumbing system
$plumbing->showers[] = $shower;

// What is the default value for the water source?
$field = $plumbing->water_source();
$default = $field->getDefault();
Calling a field as a method will return the AssetField object for that property. The field object can then be used in forms and validation. Another benefit of dynamically constructing our Assets in this way is that we can customize any asset on the fly without affecting any other asset of that type. From our plumbing example, let's suppose one user wants to track the serial number of their water heater, but no one else does. We just make sure that any instantiation of a water_heater asset for that user gets an additional "serial_number" field:
// Continuing from above
if ($userId == $customAssetUserId) {
    $serialNumber = new AssetField();
    $serialNumber->setName("serial_number");
    $plumbing->addField($serialNumber);
}
Any form generation and validation code will automatically pick that field up and display it for that user.

The last important class for constructing Assets is the AssetFactory. All Asset instances are constructed through this factory. It takes a type definition, which in the plumbing example is a JSON string. The factory doesn't actually care where the definitions come from or how they are stored, as long as if receives a properly formatted array. AssetFactory is given definitions and then uses the definitions to construct Assets on demand:
class AssetFactory
{
    // List of asset recipes this factory knows how to bake
    protected $definitions = array();

    public function define($definition)
    {
        // ... Validate proper formed-ness of the definition ...
        // ... Set some reasonable defaults for non-specified field attributes ...

        // All assets get an id field
        $definition['fields']['id'] = array(
            'type' => DW_Asset_Field::TypeString,
            'hidden' => true,
        );

        $this->definitions[$definition['type']] = $definition;
    }

    // Construct an asset of the given type
    public function build($type)
    {
        $definition = $this->definitions[$type];

        $asset = new Asset();
        $asset->setType($definition['type'])
            ->setDisplay($definition['display'])
            ->setInstanceNameFormat($format);

        foreach ($definition['fields'] as $name => $fieldDef) {
            $field = new AssetField();
            $field->setName($name)
                ->setType($fieldDef['type'])
                ->setDisplay($fieldDef['display'])
                ->setHidden($fieldDef['hidden'])
                ->setUnique($fieldDef['unique'])
                ->setRequired($fieldDef['required'])
                ->setMin($fieldDef['min'])
                ->setMax($fieldDef['max'])
                ->setOptions($fieldDef['options'])
                ->setOther($fieldDef['other'])
                ->setDefault($fieldDef['default']);

            $asset->addField($field);
            $asset->$name = $fieldDef['default'];
        }

        return $asset;
    }
}

$factory = new AssetFactory();
$factory->define(json_decode('{
    "type" : "plumbing",
    "display" : "Home Plumbing",
    "instance_name" : "Installed %installation_date%",
    "fields" : {
      "water_source" : {
        "type" : "string",
        "options" : ["city", "well"],
        "other" : "Where does the water come from",
        "default" : "city"
      },
      "installation_date" : {
        "type" : "date",
        "required" : true
      },
      "water_heater" : {
        "type" : "subasset",
        "options" : ["gas_heater", "electric_heater"],
      },
      "showers" : {
        "type" : "subasset",
        "options" : ["shower"],
        "collection" : true,
        "max" : 5
      }
    }
  }'));

$plumbing = $factory->build("plumbing");
If we have any customizations, like our serial_number from above, we can call a `customize()` method on the factory from within the `build()` method, or pass the result of the `build()` to some other customization class. At the moment, we don't have any requirements like that. The important thing is that we now have an Asset object that we can pass around to our generic Asset handling code.

Next up, a description of how we generate a form from a generic Asset object.