Files...
April 5, 2009
OEM Scripts' php Framework Rewrite
OEM Scripts had its beginnings in 2004 when a New Zealand web designer, Ryan Halls, hired me to write custom scripts for his web clients. Asked why he didn't use open source CMS packages, his reply was that they didn't give him enough freedom to be creative. You can skin CMS packages but it is not what Ryan wanted to do. The use of custom scripts gave him total liberty to design as he pleased.
I used the opportunity to create a php framework to facilitate the production of my custom scripts. Initially they were mostly procedural and while they worked rather well, installing them proved to be quite difficult.
The OOP Rewrite
I'm a bit of a slow starter and it wasn't until my web host upgraded to php5 that I decided to take an interest in the supposedly better OOP resources of version 5. As part of my then current assignment, I wrote a shopping cart class which proved to be quite functional. This encouraged me to take on the rather major job of rewriting my framework as OOP starting in February 2009.
Processing complex HTML forms is at the core of many of my custom WebApps. Investigating how to write a Form class in php, what I found was based on top-down thinking which leads to very complex solutions because you have to solve the whole problem looking at it from the top. I dislike complex solutions.
In the beginning there was the TAG
In time I came to realize that HTML pages are just tags nested inside tags nested inside tags to any number of levels. Why not look at the issue a tag at a time -> bottom-up? An HTML form, the object I was looking to code, is just a collection of tags, some used for layout and others used for user interaction.
The first step was to analyze tags. They are rather simple affairs consisting of a tag name, zero or more attribute pairs and optional content. The content, if present, could be either a string or one or more tags which, in turn, are objects. A Tag class would them have the following properties:
$name; // string
$attributes = array();
$contents = array();
The Tag class needs five methods:
function __construct($name);
// to create the tag object and set its name
function setAttribute($name, $value);
// to add attribute name=value pairs
function setContent();
// to add content
// this function can take any number of arguments
function render();
// to output the tag (or nested tags) as HTML code
function feedback();
// to report error conditions, etc.
This is quite simple code except for the $Tag->render() function which needs to be recursive to dig into the nested tag objects.
While testing I was thinking about how to add dynamic content, actually, how to add script or user generated values to the attribute value pairs. For example, the <form> tag's action attribute often uses a server variable: $_SERVER['PHP_SELF']. It turned out to be simplicity itself:
$form = new Tag('form');
$form->setAttribute('action', $_SERVER['PHP_SELF']);
similarly with <input> tags:
$input = new Tag('input');
$input->setAttribute('type', 'text');
$input->setAttribute('name', 'foo');
$input->setAttribute('value', $_POST['foo']);
To simplify creating input tags I created an Input class:
class Input extends Tag {
public function __construct($type, $name = '', $value = '') {
$this->tag = 'input';
$this->attributes[] = array('type', $type);
$this->attributes[] = array('name', $name);
$this->attributes[] = array('value', $value);
}
now the above code can be written in a single line:
$input = new Input('text', 'foo', $_POST['foo']);
Pleasant Surprise
The feedback I was getting was to the effect that this is a very complicated way to write simple HTML and this certainly seemed to be a valid compliant. Pondering this issue I came to realize that not all tags need to be objects. Only those tags that have some functionality or have other objects inside need to be objects. For example, the break tag <br> can be rendered as a literal string. You can freely mix objects and strings. Suppose you wanted to write an unordered list as HTML, it might look like this:
$ul = <<<LISTLIST
<ul id="latestlinks">
<li><a href="search.php?do=getnew">Latest Forum</a></li>
<li><a href="search.php?do=noreplies">Unanswered Posts</a></li>
<li><a href="/marketplace/">Latest Marketplace</a></li>
<li><a href="/contests/">Latest Design Contests</a></li>
</ul>
LISTLIST;
Now you can use $ul as the content of a tag, say a div:
$div = new Tag('div');
$div->setContent($ul);
echo $div->render();
You can generate $ul or anything else any which way you want, just feed it to the Tag class as the final step before rendering the page. You could have included the div with the unordered list into you body tag as follows:
$body = new Tag('body');
$body->setContent($menuBar, $logo, '<br>', $div, $anotherDiv, $somethingElse, '<hr>', $footer);
echo $body->render();
All of a sudden all you need to build web pages is the Tag::render() function. You build the components in any way you want, writing it as straight html or generating it with some other code. At the end of the day, an html page is just a bunch of nested tags.
Forms are not the only active tags. Say you have rows in a table with alternating backgrounds, a live object. You can extend the Tag class with an AltRow class that does that functionality for you. Or say you have the need to show random images, you could extend the Tag class with a RandomImg class.
Anyway, I'm all excited. This is a whole new way to look at a php framework!
Denny Schlesinger
The guys at SitePoint were very helpful:
How to design an HTML Form class