WACT (Web Application Component Toolkit) is out! The first (EXPERIMENTAL!) release is up on SF. If you’ve been looking for an elegant PHP architecture, take a look. To risk hype, WACT may be PHPs answer to ASP.NET...
What is WACT?
http://wact.sourceforge.net (LGPL). Project Summary
To describe WACT as a framework is not entirely correct (although it’s close). WACT is more like a set of tools that, ideally, you’d like to have come as built in extensions to your PHP install, were it not for the fact that few people in their right mind would attempt this kind of project in C; PHP as an Agile langauge is essential to WACTs progress.
WACT is really an evolution of it’s lead developer, Jeff Moore, own PHP library, developed out of need rather than vision.
Jeff has been running his own development company Procata since 2000 and is one of a lucky few with a successful business model built around PHP. Jeff’s background as a software developer also sets him apart, in normal PHP circles, having spent ten years developing Enterprise Resource Planning systems and having first hand experience in most techologies, platforms and languages used in business today.
Along with Jon Ramsey of Bangoid I’ve been in the lucky position of helping out with WACT’s development; a unique and fascinating experience. Jon sums up the “WACT factor” nicely here.
WACT Objectives Although there’s never been any formal “mission statement”, Jeff already having a significant codebase since I started working on it, some of the broad criterion guiding WACTs development are;
- Ease of development: developing with WACT should (and IMO already does) significantly reduce the effort required to build PHP apps.
- Performance. Not matter what abstractions WACT uses, performance is critical. Jeff published some interesting benchmarks here (which you can re-run yourself). Comparing with common PHP toolsets used today, only native, unabstracted PHP / SQL appears to outperform it.
- Ease of deployment. To a great extent, writing an app that incorporates WACT tools is as easy as knocking out native PHP. There’s no need for endless messing with configuration files etc. etc. to set it up.
- Non intrusive to design. For example, it would be perfectly possible to use WACT within eZ publish 2.x, to replace existing page controllers / templates. If you don’t want to use, say, the database drivers WACT provides, but like the template engine, no problem.
- Reliable. Thanks to Simple Test, WACT is well unit tested. That provides visibility to both it’s developers and users. That’s not to say there’s no bugs but there’s a better chance of find them and it’s already clear how unit testing is allowing significant complexity to be possible.
- No special PHP requirements. Aside from the PCRE extension, WACT doesn’t require you have anything special installed and should “survive” whatever php.ini settings you have (magic quotes, short tags etc.). It’s possible (but not tested) that it would even run on PHP versions as old as 4.0.6. Sourceforge (4.1.2) has proved no problem. It’s also run fine on Linux, Mac (Jaguar) and Windows.
- Well documented. For a first release, there’s plenty to work with. Along with unit tests, most components WACT provides come with examples demonstrating their use (available online here). The API docs also link back to the relevant WIKI pages, where you’ll also find plenty of info, which you can add to as you go.
</ul>
From a more general perspective, as said on the WIKI;
“WACT assists in implementing EnterprisePatterns for PHP web applications. Wact is intended to facilitate the practices of Refactoring and TestDrivenDesign.”
And you’ll find more than a pattern or two...
What's in the Release?
The two strongest features WACT offers today are ;
Component Based Template Engine
The template engine, IMO, is something truly unique to PHP (and Perl or Python for that matter, from endless hours spent looking at template engines).
It bears a closer relationship to template engines like ASP.NET or the Java STL / JavaServerFaces. You may have already come across the Template View page on the WIKI, which studies the “problem” of templating in general. That probably serves as the best conceptual primer for what WACT.
It’s best demonstrated by example. Consider, for a moment, how much effort was required last time you had to develop some kind of page result set for a table. Even using some kind of class such as PEAR::DB_Pager_Sliding. How many lines of code, include HTML, SQL and all?
Here’s how it can look it WACT;
<body>
<!-- The list:list tag is for page elements like tables -->
<list:list id='ListExample1'>
<DIV ALIGN="RIGHT">
Page:
<!-- The page:navigator tag and children
controls how the pager looks -->
<page:navigator id="PageNav" items="10">
<page:first>First</page:first> <page:prev>Prev</page:prev>
<page:list>
<page:number>
<page:elipses>...</page:elipses>
<page:separator> </page:separator>
</page:list>
<page:next>Next</page:next> <page:last>Last</page:last>
</page:navigator>
</DIV>
<TABLE width="100%" BORDER="1" ALIGN="CENTER">
<!-- The list:item tag gets repeated and
filled with the db result -->
<list:item>
<TR>
<TD>{$Name}</TD>
<TD>{$Description}</TD>
</TR>
</list:item>
</TABLE>
</list:LIST>
</body>
First thing to notice is, apart from the variable references {$Name} and {$Description}, the template is all XML. Also note the page and list tags are declarative as opposed to the kind of imperative syntax often used in PHP template engines. WACT parses the template with PEAR::XML_HTMLSax and “compiles” it into lean, mean (and horrible to read) PHP. More on that in a moment.
But so far, perhaps you’re not impressed. How about this, the “code behind” this template - the page controller;
<?php
require '../../framework/common.inc.php';
require WACT_ROOT . '/template/template.inc.php';
require WACT_ROOT . '/db/db.inc.php';
$Page =& new Template('/list.html');
$List =& $Page->getChild('ListExample1');
$RecordSet = & DBC::NewPagedRecordSet('SELECT * FROM phpmodules');
$Pager->getChild('pagenav');
$List->registerDataSet($RecordSet,$Pager);
$Page->display();
?>
That’s it! If you’re unsure, you can see it live here.
Notice how the “code behind” talks to the components in the page, fetching them with a DOM-like (albeit much simpler) API. The names used to reference the components when $Page→getChild() is called above correspond to the id attributes of those tags in the template.
There’s wasn’t even a need to specific a LIMIT clause in the SQL - WACT takes care of it (even in a database independent manner if need by, thanks to adapters for PEAR::DB and ADODB).
There’s also some of the secret of WACTs performance here. Almost all common PHP template engines today have a “push style” when it comes to populating, say, a table with data. With a typical PHP template engine, you perform a query, loop through the results once, building an array, “push” the array to the template, and the template engine loops again, to build the output, for example;
$result = mysql_query($sql);
$template_data = array();
while ( $row = mysql_fetch_array($result) ) {
$template_data[]=$row;
}
$tpl->assign($template_data);
$tpl->display();
WACT’s list component (<list:list /> etc.) expects the record set that you register with it to conform to a well defined iterator API. The process (in this example) of querying the database and looping through the results happens together, resulting in a single loop.
Another example of WACTs template engine (showing a different facet of what it’s capable of) is the calendar tag.
If you’ve ever tried building a calendar from scratch (for example), you’ll know it’s hard work. Not only have you got some awkward calendar math to deal but also some painful work to do in rendering the HTML.
WACTs calendar tag (which is EXPERIMENTAL API-wise but meant to demonstrate tag development) reduces this something like an “XML macro” (or bean if you prefer), leaving the math to PEAR::Calendar while you control the look and feel of the calendar like;
<div align="right">
<calendar:month id="MyCal" yearuri="year"
monthuri="month" dayuri="day" border-style="groove"
background-color="WhiteSmoke" width="300px"
rules="all">
<calendar:title id="MyCalTitle" monthformat="long"
yearformat="two" font-size="18px" font-weight="bold"
font-family="Georgia"/>
<calendar:dayheader id="MyCalHeader" format="short"
font-family="Georgia" font-size="11px" font-weight="bold"
color="red" />
<calendar:nextprev id="MyCalNextPrev" style="text-decoration: none;
font-weight:bold; font-color:black"/>
<calendar:daystyle background-color="white" align="right"
font-family="Verdana" font-size="15px"/>
<calendar:selecteddaystyle background-color="LightSlateGray" align="right"
font-family="Verdana" font-size="15px" font-weight="bold"/>
<calendar:emptydaystyle/>
</calendar:month>
</div>
That’s all you need to define the look of the calendar - no messing with HTML table tags. In it’s rawest form, you can generate a calendar with just <calendar:calendar /> Some of the HTML design decisions are taken away from you, using this approach (for example if you want a calendar built with div tags, you’ll have to re-write it) but at the same time, enough power is still available to control all the look and feel.
Note if you’re concerned about how style and content are mixed here, you could simply use “class” instead of most of the styling attributes here. The above is really comparison of WACT with one of these.
The template engine works by splitting the parsing and rendering operations from each other. As a template is parsed, the parser has a “dictionary” of component XML tags to be on the look out for. If it encounters one in the source template, it creates an instance of a compile time component, to which it delegates the work of adding it’s part (raw PHP code) to the compiled template.
At runtime, you’re dealing with the compiled template (and assuming you don’t change the source, the compiling overhead is avoided), with access to the tags via lightweight runtime components. The $List→registerDataSet() method you saw above is part of the runtime api of the list tag, for example. The runtime API for the calendar needs more work but should eventually be easy to hook up with your “events database”.
Developing your own components (which we hope you will and make them available to all) requires some effort but the mileage, in terms of re-use, is high. It leads to what I was musing about here.
RecordSets WACTs database drivers provide a RecordSet implementation which provides an efficient mechanism for accessing data, which is wrapped in an iterator which can be reproduced for other data sources. It takes care of connecting, querying and fetching from the cursor for you, without you needing to call anything but the iterator API, for example;
$RecordSet = & DBC::NewRecordSet('SELECT * FROM phpmodules');
$RecordSet->reset();
whie ( $RecordSet->next() ) {
echo ( $RecordSet->get('Name').'<br />' );
}
The connection happens when DBC::NewRecordSet() is called, the connection settings automatically being read from your config.ini file, while the query is performed by the reset method.
Normally you wouldn’t have to worry about this, components like the list tag writing the necessary PHP to the compiled template, but what’s useful about it is how other record sets can be set up to conform to the same API, and also be passed to the list, such as;
$rows = array (
array('Name'=>'John'),
array('Name'=>'Mary'),
array('Name'=>'Peter')
);
$RecordSet = new ArrayDataSet($rows);
$RecordSet->reset(); // Not necessary in this case, the data being available
while ( $RecordSet->next() ) {
echo ( $RecordSet->get('Name').'<br />' );
}
Form Processing WACT also has a well evolved form contoller (but one that may change it’s API in the near future so be warned), which makes it easy to handle form validation, reporting data entry errors to the user and even building “wizards” (forms with a number of stages).
The WACT form controller has the Struts-like notion of “Action” classes, these being a class containing the method performAction(), which is executed when particular “events” happen in the form, such as successful validation. Explaining the form handling in detail will takes a few paragraphs, so something for another time but the examples http://wact.sourceforge.net/examples/?dir=tags (see “input”, “select” and “textarea”) should get you started.
There’s already validation rules in place for a bunch of common “web” data types such as IP addresses, URLs, member rules (value must exist in a provided list).
What Else
That’s about it right now (there’s some other stuff but APIs are flowing so best left unmentioned for the time being). Jeffs pondering ways to implement Front Controllers / Intercepting filters in manner that’s flexible and extensible while there’s probably more support in store for other types of record sets (would be nice to have something for XML / XML-RPC / SOAP), plus there’s plenty to do in writing more components - that’s potentially endless in fact. When you start thinking about components which generate JavaScript, perhaps capable also of converting XUL to HTML, for browsers lacking support, things start to get pretty interesting. Perhaps next up is some form of Data Grid - a specialized form of list tag, designed to generate table for you.
Documentation There is documentation scattered around the WIKI. Most classes in fact should have at least a few words said about them. What’s important is this project needs to be “self documenting”. You’ll notice that classes in the API Documentation have links pointing to the appropriate page in the WIKI. We’re hoping something like the PHP manual will be the result, the contributed notes filling in the blanks.
So if you find WACT is something useful, please (please, please) contribute to the WIKI. We hoping there’ll be more people involved in development, but user friendly documentation always lags with Open Source, so the more help the better.