Page Templates without Parsing
Hello, guys!
Having analyzed lots of open-source scripts I learnt that there are as many ways of processing HTML templates, as programmers who work on them. Sometimes they invent their own sophisticated parsers that treat special characters in templates as variables or even functions and procedures. Often there are separate files in a “/tmpl” or “/templates” or whatever-called directory, and inside them text and HTML is impregnated with short PHP procedures (mainly loops and conditions).
Sometimes there are no templates at all. Coders simply mix up the code and HTML, using echo command or separating them with PHP tags:
<? Code.. ?>text<? Code?>
I don’t like this way actually. I always think of people that may come after me and re-work or support my code. And if they have to search among dozens of files to change several words on a page, they’ll think their time has been wasted.
Usually I create two files with equal names. One is a script that contains all the code, database queries, conditions, calculation etc. Everything that later will appear on the screen is stored in variables and objects. The other script is a template, and it is stored in “templates/” directory. Inside there is pure HTML, text and variables that were defined in the script.
In the end of the script I include the template using require or include command:
Example: Script: about.php
<? $TITLE="About Us"; $action= intval($_GET["act"]); if ($action==1) $TITLE_END="Our History"; elseif ($action==2) $TITLE_END="Our Mission"; else $TITLE_END="Choose what you want to see"; $resTable=mysql_query("select articleText from siteArticles where articleID='".$action."'"); if (mysql_num_rows($resTable)) { $resSet=mysql_fetch_array($resTable)); $ARTICLE_TEXT=$resSet["articleText"]; } require ($TMPL_DIR."about.html"); ?>
template: about.html
<html> <head> <title><?=$TITLE?> - <?=$TITLE_END?></title> </head> <body bgcolor=white> <h1><?=$=$TITLE_END?></h1> <?=$ARTICLE_TEXT?> </body> </html>
This is very simple and doesn’t need to be discussed in details.
The problem to me was always in listing some items in HTML format. For example, I have many photos, information of which is stored in the database. I need to list the photos on a page, in a table or one after another. The contents of each row in this table (a row in this case is for one photo) is fetched from the DB. As I said before, all DB queries and calculations (perhaps, the parameters of the image, reducing its size) have to be made in the script. According to this I must write:
<? $TITLE="About Us"; $action= intval($_GET["act"]); if ($action==1) $TITLE_END="Our History"; elseif ($action==2) $TITLE_END="Our Mission"; else $TITLE_END="Choose what you want to see"; $resTable=mysql_query("select articleText from siteArticles where articleID='".$action."'"); if (mysql_num_rows($resTable)) { $resSet=mysql_fetch_array($resTable)); $ARTICLE_TEXT=$resSet["articleText"]; } $resTable=mysql_query("select photoID, photoName, photoExt, photoComment from sitePhotos where photoCategory='".$action."' order by photoName"); for ($i=0; $i<mysql_num_rows($resTable); $i++) { $resSet=mysql_fetch_array($resTable)); $photoID=$resSet["photoID"]; $photoName=$resSet["photoName"]; $photoExt=$resSet["photoExt"]; $photoComment=$resSet["photoComment"]; ?><img src="<?=$PHOTO_DIR.$photoName.".".$photoExt?>" alt=<?=$photoComment?>><br>Type: <?=$photoExt?><hr size=1><? } require ($TMPL_DIR."about.html"); ?>
What will I see on the screen after I launch the script? Something ugly I guess:  Type: gif
 Type: gif
 Type: jpg
Our History … here goes the description of our history …
Of course the output generated in the script went before the template’s contents.
To avoid this we can change the script a little:
<? //.. previous code … $resTable=mysql_query("select photoID, photoName, photoExt, photoComment from sitePhotos where photoCategory='".$action."' order by photoName"); for ($i=0; $i<mysql_num_rows($resTable); $i++) { $resSet=mysql_fetch_array($resTable)); $photoID=$resSet["photoID"]; $photoName=$resSet["photoName"]; $photoExt=$resSet["photoExt"]; $photoComment=$resSet["photoComment"]; $LIST_ROWS.="<img src=\"".$PHOTO_DIR.$photoName.".".$photoExt."\" alt=\"".$photoComment."\"><br>Type: $photoExt<hr size=1>"; } //.. previous code ?>
and to add $LIST_ROWS variable to the template:
<html> <head> <title><?=$TITLE?> - <?=$TITLE_END?></title> </head> <body bgcolor=white> <h1><?=$=$TITLE_END?></h1> <?=$ARTICLE_TEXT?> <br>Here come the photos:<br> <?=$LIST_ROWS?> </body> </html>
This works for lists that don’t have much HTML in them. What is we needed to outline each photo with a border, to put it on a background and to add some description to the left? Designers won’t let you display images just separated with a line :)
In this case you need a special template for an entry in a list. The script can be modified this way:
<? // … previous code $resTable=mysql_query("select photoID, photoName, photoExt, photoComment from sitePhotos where photoCategory='".$action."' order by photoName"); for ($i=0; $i<mysql_num_rows($resTable); $i++) { $resSet=mysql_fetch_array($resTable)); $photoID=$resSet["photoID"]; $photoName=$resSet["photoName"]; $photoExt=$resSet["photoExt"]; $photoComment=$resSet["photoComment"]; include ($TMPL_DIR."photoRow.html"); } // … previous code ?>
template: photoRow.html
<? $LIST_ROWS.="<img src=\"".$PHOTO_DIR.$photoName.".".$photoExt."\" alt=\"".$photoComment."\"><br>Type: $photoExt<hr size=1>";?>
But the above template is very hard to cope with. It doesn’t look like HTML to designers and HTML coders. They won’t thank you for that.
Here we get closer to the topic. PHP allows you to buffer output and store it in memory rather than passing it to the browser window. You may in fact process some HTML text “before” including your main template. You may include the same HTML template several times for each row of your list, and then assign the contents of the buffered output to a variable, that can be added to your main HTML template.
Let me show what I mean:
<? // … previous code $resTable=mysql_query("select photoID, photoName, photoExt, photoComment from sitePhotos where photoCategory='".$action."' order by photoName"); ob_start(); for ($i=0; $i<mysql_num_rows($resTable); $i++) { $resSet=mysql_fetch_array($resTable)); $photoID=$resSet["photoID"]; $photoName=$resSet["photoName"]; $photoExt=$resSet["photoExt"]; $photoComment=$resSet["photoComment"]; include ($TMPL_DIR."photoRow.html"); } $LIST_ROWS=ob_get_contents(); ob_end_clean(); // … previous code ?>
template: photoRow.html
<img src="<?=$PHOTO_DIR.$photoName.".".$photoExt?>" alt=<?=$photoComment?>><br>Type: <?=$photoExt?><hr size=1> <!—any other HTML tags that you want -->
You see the difference? In the template for the row entry (every image that you list) you right regular HTML tags with some PHP variables that can be ignored by designers. And PHP withholds the output until you process all the rows in the table. This is made with a ob_start() command. Then you assign the buffered output to $LIST_ROWS and turn buffering off.
The result will be the following:
Our History … here goes the description of our history …  Type: gif
 Type: gif
 Type: jpg
Note: output buffering is available in PHP 4.0 and higher. Happy programming!
|