Paul M. Jones

Don't listen to the crowd, they say "jump."

PDO at wiki.cc

You can see the PDO Basics entry at wiki.cc here. PDO unifies the various PHP RDBMS function calls; database API abstraction in PHP is a Good Thing.

But PDO brings something I did not expect: table instrospection. You can get meta-information about the statement results that tells you what the native type, size, precision, etc. are for the returned columns. This can make automated type-checking and conversion super-easy. Just one more reason I was lucky to think of making Solar PDO-ready.

Hey, PDO guys: neat stuff, and thanks!


Quick Hits: DB_Table, Text_Wiki

I just released the first 1.0.0 stable version of DB_Table, and released RC2 of Text_Wiki earlier today. That's another two things I don't need to think about any more. :-)

Now that I think about it, it's been a good couple of days for getting software out the door. I guess this is one way to spend my spring break. ;-)


Savant3: Second Development Release

The second development revision of the Savant template system version 3 is now available; you can get it from the usual location here. The change notes are:

* second development release

* error codes are now strings, not integer constants (e.g., 'ERR_UNKNOWN' instead of -1).

* added 'freeze' property to Savant3_Plugin_form

* added auto() and fullauto() methods to Savant3_Plugin_form

* changed element description arrays in Savant3_Plugin_form

* all plugins using htmlspecialchars() to filter output now use Savant3::scrub plugin instead

* added error resource for Solar errors

* updated comments throughout

As always, if you find any problems, please let me know via the Savant mailing list.

Please note that Savant2 is still being actively maintained for PHP4 and non-strict PHP5. In fact, I want to backport the Savant3 version of the 'form' plugin to Savant2; that plugin is just too good to be limited to PHP5.


Solar 0.0.4 released

I released a new revision of Solar today; we're at 0.0.4 now. (UPDATE (2004-03-07): I failed to put the tarball on the Solar website earlier; it is now in place. Thanks to Jens for pointing it out.)

Solar is still in development and as such is unstable. However, I am continuing work on the first proof-of-concept application, a simple bug tracker, which combines the generic commenting application component (called a "Cell" in Solar parlance, in this case "Solar_Cell_Talk") with the specific data maintained by the Solar_Cell_Bugs component. All it lacks is edit restrictions based on user name and user role.

The change notes for this revision are:

* This is a public development release, and is not yet stable.
* Added Solar_Form class (Solar/Form.php) for form element collation and
  input validation (not for output or rendering)
* Added Solar_Locale class (Solar/Locale.php)
* Solar.php:
  * Modified locale() method signature
  * Added public static $locale object
  * Removed Solar::setLocale()
  * Properties $config and $shared are now public
* Solar_Base: modified to use new locale structure
* Solar_Sql:
  * dropped support for Oracle (30-char limit on names is just too strict)
  * max table name is now 30 chars, max col/idx name is now 27 chars
  * double underscores not allowed in table, index, or column names
    (to prevent name collisions)
  * indexes now suffixed with '__idx', sequences with '__seq'
  * in index names, the table/index separator is now '__'
  * Converted fetch(), fetchNum(), and free() methods to static (now
    there's no need to pass a reference to the driver into the result class)
* Solar_Sql_Result: modified to make static calls to Solar_Sql for free(),
  fetch(), and fetchNum()
* Various modifications to the Solar_Cell classes for Bugs and Talk to go
  with Solar_Sql changes
* Various schema changes to the Solar_Cell classes for Bugs and Talk

Solar is a "simple object library and application repository" for PHP5. It is designed for developers who wnat to publish their code worldwide in heterogenous environments.

For example, the database drivers are highly abstracted and are intended to work identically on all supported RDBMS backends (including date and time representations). In addition, localization is built in from the start, so it is a simple matter to create translated strings and formats for your applications.

Above all, I am striving to make the Solar codebase comprehensible and intelligble for all adoptive developers, whether new or experienced, with as little "tricky stuff" as possible and verbose commenting throughout.


Updates to Savant Site

While there is no new documentation, I have converted the backend to its own database so I can use YaWiki to manage the existing Savant2 documentation in parallel with the new Savant3 documentation. (The Savant3 pages are lamentably lacking, with only a home page, but that should change as I move it closer to a stable release.)

Savant is a lightweight, object-oriented template system for PHP. I call it the simple, elegant, and powerful alternative to Smarty. Savant2 was written for PHP4; while it works with PHP5 out of the box, it is not E_STRICT compliant. Savant3 *is* E_STRICT compliant, sports a nicer plugin system using __call() magic, and comes with a modified set of default plugins.


Should Solar_Sql_Entity Support Oracle?

Solar is a simple object library and application repository for PHP5. It is currently in development. This post refers to the object-relation management tool, Solar_Sql_Entity, which follows closely the table data gateway pattern described by Martin Fowler.

Oracle only allows 30 characters for an object name (table, field, index, sequence). While that by itself may be OK, it seriously restricts the operation of Solar_Sql_Entity. (This class is the natural successor to DB_Table.)

One of the points of Solar_Sql_Entity is to make it possible to create a table that will work on *all* of the supported RDBMS backends. This means that it has to submit to a lowest common denominator in some ways.

For example, in PostgreSQL, your index names must be unique across all tables in the database. For example, if you have a "table1" and "table2", each with an "id" field, it's not enough to specify an index called "id" on table1 and annother index also named "id" on table2; PgSQL sees that as a conflict. You have to make the index names unique even though they refer to different tables.

Thus, to support PgSQL, the index names would have to be "table1_id" and "table2_id". This is why DB_Table and Solar_Sql_Entity prefix the table name automatically to your index name (i.e., to make sure there are no index name conflicts if the schema is created in PgSQL). As an additional identifier, the index name gets "_idx" suffixed to it.

You can see where this is going: because of the 30-char limit in Oracle, this means you're pretty limited in how long your table and field names can be when using Solar_Sql_Entity. Right now, Solar_Sql_Entity limits you to 15 characters for table names and 10 characters for field names, all to make sure we stay under 30 chars for Oracle. This is pretty short, almost too short to be universally useful.

If Solar_Sql_Entity were not to support Oracle, the next highest character limit is 64 (MySQL and MS-SQL). That effectively doubles the available length (~30 chars for table, ~25 chars for field), but at the cost of dropping Oracle support within Solar_Sql_Entity.

Frankly, I'm not sure Oracle is in the "target market" for Solar anyway; the point of Solar is to be provide a toolset to help build apps-for-distribution (although it's fine for in-place apps, too).

Anybody have any feelings about (or experiences with) this kind of issue, one way or another?


28 Feb 1991: Desert Storm Ceasefire

On this day in 1991, after 100 hours of land battle following roughly 5 weeks of air operations, the United States declared a ceasefire in its military campaign to drive Saddam Hussein's invading forces out of Kuwait. That was a good day, but looking back now it was the first major battle of World War IV, and the conclusion to that battle has been accomplished only recently with the sovreignty of a free Iraq. Let's hope it sticks.


Simplifying Automated Form Generation, Validation, and Output

First Praise, Then Critique

HTML_QuickForm is the standard class within PEAR for building forms programmatically. Here's an example:


$form =& new HTML_QuickForm();
$type = 'text';
$name = 'city';
$label = 'Your City:';
$form->addElement($type, $name, $label);

(All XHTML element types are supported, including some pseudo-types such as 'date' and 'time').

When you call $form->display(), the class builds all the HTML for the form and the elements you have added. That's just the start, though. If you have a POST variable called 'city', it will populate the form with that value for you. You can add validation methods to each element, and the class will check the values and print error messages inside the form if the values are not valid. You can apply special processing to the values before retrieving them as an array with $form->export().

It's quite a neat package, but I have some quibbles with its operation. These are not design flaws; indeed, its design seems quite internally consistent. For example, each element is represented internally as an object itself, with its own methods and properties (usually descended from a base element class). For example, to set the value of an individual element, you have to call its setValue() method -- but any submitted values (e.g., in POST) will override that value. There are ways around this, but none of them seem simple or straightforward.

More to the point, while I am not a strict model-view-controller devotee, I find that HTML_QuickForm not only pierces the veil between model and view (or controller and view, depending on your definition), it positively shreds that veil to pieces. Yes, it has renderers that allow you to parse through the internal objectified representatiion of the form elements, but they system seems overly complex.

While I like HTML_QuickForm, and have written code (in DB_Table) that automatically generates forms from tables using it, I don't fully comprehend the methodologies underlying its operation. It seems to me there should be a simpler and more straightforward way of automating form generation, one that is both more comprehensible and more open to MVC separate while retaining the obvious strengths of validation and automated population of values.

Savant and Solar

Note: Savant3 and Solar are publicly available but are still in development. I present the remainder of this article as advance information on how they will interoperate, and while I am using the described methodologies on a continuing basis, the specifics may change in the future.

With the above points in mind, I developed a plugin for Savant2 that accepts an array of element definitions and generates forms from it (called Savant2_Plugin_form for obvious reasons). In Savant3, to be released, I have refined and extended that original idea to be a bit more full-featured (and I intend to backport it to Savant2, so don't worry about missing out). The Savant plugins cover the 'view' half of form generation. For the 'model' (or the 'controller' depending on how you think about these things) portion, I have implemented a complementary system in Solar_Form to collect and manage these element arrays.

The key here is that the form generation happens not in one location, as in HTML_QuickForm, but in two: Solar_Form defines the form elements, and Savant renders it. One benefit of this separation of duties is that neither class cares what the other class is. If you like, you can send a hand-built array of elements to Savant, and it will build the form just the same. Similarly, you can pass a Solar_Form element array to any template system, and if your template system knows how to deal with that array, it can render a form too. The point is that the information about the form is transmitted in a standard format as an array.

Therefore, the format of the element array is of paramount importance. Lucky for us, it is quite simple. For each element in the form, you have an array that looks like this:


$element = array(

    // the 'name' attribute for the XHTML form element
    'name'     => null,

    // the 'type' attribute
    'type'     => null,

    // a text label for the element
    'label'    => null,

    // the 'value' attribute of the element
    'value'    => null,

    // whether or not the element is required
    'require'  => false,

    // whether or not the element is disabled
    // (i.e., 'read-only')
    'disable'  => false,

    // any options related to the element values,
    // typically for select options, radios, or
    // checkbox values
    'options'  => array(),

    // additional attributes for the XHTML form element
    'attribs'  => array()
);

From our earlier example above, you would create an element under this system simply by specifying the array keys for it:


$element = array(
    'name' => 'city',
    'type' => 'text',
    'label' => 'Your City:',
    'value' => 'Memphis'
);

The Savant3 template system can take this array and render a form within a template script like this.


// assume that $element has been assigned as 'elem'
$this->form('start');
$this->form('auto', $this->elem);
$this->form('end');

Note the use of the 'auto' call, which automatically builds a single form element from an array; if you use the 'fullauto' call, it will build multiple elements from a sequential array of elements, in order.

The above discussion only describes the manual generation of form elements (the display is automated via Savant3). To get more of the functionality of HTML_QuickForm, we would need to use Solar_Form.

Solar_Form collects an array of elements together in a class property; you can add them one-by-one like this:


$form = Solar::object('Solar_Form');
$element = array(
    'type' => 'text',
    'label' => 'Your City:',
    'value' => 'Memphis'
);

// note that the 'name' key is optional when
// using setElement(); Solar_Form will use the
// first parameter as the 'name' value, overriding
// the 'name' key.  this has benefits when
// building arrays of elements.
$form->setElement('city', $element);

To populate the form elements with their related POST values, simply call the populate() method.


$form->populate();

You can then pass that form to Savant3 ...


$Savant3->form_elements = $form->elements;

... and render the form in a template script like so:


$this->form('start');
$this->form('fullauto', $this->form_elements);
$this->form('end');

Finally, you can add automated validation to the Solar_Form elements with the addValidate() method. Below, the $method relates to a standard Solar_Valid method; in addition to 'alpha', there are 'alphanumeric', 'numeric', 'isoDate', 'email', and other validation methods, as well as 'regex' (for user-defined regular expressions), 'custom' (for call_user_func callbacks), and 'multiple' (for arrays of validation routines).


$form = Solar::object('Solar_Form');

$element = array(
    'type' => 'text',
    'label' => 'Your City:',
    'value' => 'Memphis'
);

$form->setElement('city', $element);

$name = 'city';
$method = 'alpha';
$feedback = 'Please use only letters in the city name';
$form->addValidate($name, $method, $feedback);

Then you can call the validate() method, which will return true or false, and will automatically set the feedback messages for elements that failed validation.


if ($form->validate()) {
    $values = $form->values();
    // insert values into a table
}

$Savant3->form_elements = $form->elements;

Conclusion

While I shamelessly promote Solar and Savant throughout this entry, my main goal is to point out that MVC separation of form building, validation, automated population, and output display is possible with a unified format describing the form elements. It is not necessary to wrap all those functions into one class; indeed, separating the concerns seems vital to extended development cycles when differing framework systems must interoperate. Having it all in one class is great to start with, but after a while you find yourself coding to the class and trying to learn its intricacies, instead of using it for your own purposes.


Upgraded to WP 1.5

As you can see, the default theme is not really working for me. Have not figured out the problem yet, looks like a CSS thing.

Update: Had a mismatched anchor tag in an earlier post. Glad it was just me. :-)


DB_Table and Text_Wiki Release Candidates

For more information, please visit the related PEAR pages:

DB_Table

Text_Wiki

If I don't get any new (critical) bug reports for the next week or so, I can release them as "stable" and check them off my to-do list. While Text_Wiki could still use some add-ons (more renderers, more parsing rulesets) the core is complete. These two projects are each 18-24 months old at this point, so it's nice to be able to say "done!" to them.

Now all I have to do is finish the respective documentation. With any luck, the addition of YaWiki to the PEAR website will aid in that (then I can keep everything related to those two projects in one place; i.e., at PEAR).