PHP is templating engine itself. But you start to look at it as something more...
----
(note: A lot of the discussion here might be applicable to ASP, JSP, etc.)
There seems to be no shortage of php templating solutions out there. They range from the most simplistic (including simple little header and footers) to the downright mind-bogglingly complex.
smarty (http://www.smarty.net) is one of the more widely used templating solutions, but a quick browse through the documentation will show you that smarty is almost a programming language in of itself.
(NOTE: phpsavant is a pure PHP templating system, as opposed to "almost a programming language in and of itself"; see http://phpsavant.com.)
Generally, the idea behind using a templating engine is to attempt to separate the presentation and some elements of the content, from the code.
The theory being that it is easier to maintain a web-application that has this separation then one where the content and the code are mixed together. Also, the theory is that a web-designer could update the HTML, and change the layout and design of the website without ever having to bother the programmer.
In actual fact, what frequently ends up happening is that you have a web-application that contains lots of little files containing the template fragments, and it is a pain in the arse for the designer to figure out what goes where, even if they can understand how the templating system works.
A large part of this problem has to do with organization. If the programmer (or on larger projects, the architect) cannot organize everything in a logical structure, then you have problems.
Overall there are 2 separate, but related goals to templating. What your goal is will determine what method of templating you choose.
'''Separation is king:''' You don't care how it is done, but everything must be separate. Content, display, and code. Generally, more programmatic templating systems will be used like XsltLanguage and Smarty. Designers would need to learn the templating language, and probably have to take a crash course in programming. ("You see, it's like a photoshop action, but totally different....")
'''Design is king:''' the separation of content, display and code is nice, but it's a pleasant side-effect of allowing designers full control. These templating systems are usually small and sleek, but can't do anything remotely complex. (i.e. no variables, no if structures, etc.)
----
I'm using a modified version of Template_PHPLib from PEAR. Unfortunately, this appears to be orphaned and it's hard to get documentation for it, but the source is available from the PEAR installer, and you can pick up some tutorials from http://www.devshed.com/c/a/PHP/PHPLib-Templates/.
The template files are basically just HTML documents that use braces for variables and comments for "blocks". For example, to create a simple page that displays a table, you might have:
Widgets for {COMPANY_DIVISION}
| Part ID |
Part Name |
Quantity |
Price |
{PART_ID} |
{PART_NAME} |
{QUANTITY} |
{PRICE} |
Then, in the code, you'd first call $template->setBlock("PART", ...) to break out the block, then $template->setVar to set the variables (you can pass it a hash, like what you get off of most DB abstraction libraries, and it'll set all of them), then $template->parse("PART", ...) to parse the block.
The parsed result gets stored in another variable within the Template object, which is something I found rather annoying. It allows a fair bit of flexibility - since you can append to variables in addition to replacing, this lets you do repeating blocks, where you just keep appending to the placeholder you created when you called setBlock(). But I found that I had to keep too much context in my head, remembering what I had called the parsed version of each block and having to specify that as an argument for almost all calls to the template object.
So I created a little wrapper class that redefines the API. Instead of keeping each block internally, I had it return a new template object with the text of the block. Then I could just reason about one block at a time, which also lets me pass individual blocks to methods and encapsulate all the processing. -- JonathanTang
Using the above template example, it seems to me using it could potentially be as simple as:
while ($row = nextRow($qryRslt)) { // loop thru result set
templateAddItem($tmpltHandle, "PART", $row);
}
Or
while ($row = nextRow($qryRslt)) {
$template->addItem("PART", $row);
}
(Assuming the dictionary array's elements match the braced variables.)
However, in practice one often ends up needing finer control. For example, if we want to make the columns displayed be a conditional, such as hiding price for certain users. Templates are not sophisticated enough to handle these sort of things unless designers become programmers, which is often not desired, as described above.
Thus, things like style sheets are perhaps the better way to control style. But style sheets have their own set of problems. If somebody finds a magic and simple way to handle such, I am all ears.
(I agree that this page should have a wider scope than just PHP.)
----
I used Smarty once for an e-commerce system. The result was a complete mess that took about 10 times as long to complete as I know I could do it now. PHP ''is'' a templating language. Get the designer to make a template.html. Then you go through it and insert PHP tags:
Now, all your central PHP code has to do, is
1) Process GET and POST information
2) Find the data it's going to display
3) Render that data in whatever way you need
4) Store the rendered data in a global array called $html
5) include "template.html"
function render($key) {
global $html;
print $html[$key];
}
Obviously no web app is this simplistic, but you can split the HTML into various files which can be conditionally included. There's no need to use a third-party templating system, when you can set your own up in a few seconds which will work exactly the way you want.
I find this method nicely keeps everything separate and is highly readable.
The only problem that arises comes when you want a block inside of a block.
''Granularity of frameworks is never small enough to handle the real world. Either we chop everything into tiny pieces or copy-and-paste.''
-- PeteHurst
----
''The problem with templating frameworks is that they usually break the design or the code, or both. A clear separation of design from logic is not that simple. Maybe a component oriented approach (ApacheWicket, JavaServerFaces, ApacheTapestry)is the way to go. In that case, web designers should learn to design with components in mind, which is less of a requirement.''
-- HernanSoulages
----
I got this function from PHP.net's documentation page on ob_start() (http://php.net/manual/fr/function.ob-start.php). It was posted by one 'fordiman' who has an account at Google's email service. It's served my needs for years. You just pack your vars in an array and call it like
parse('/path/to/template.tpl.php',$tpl_vars)...
$GLOBALS['BufferedErrors']=Array();
function errorParse($errno, $errstr, $errfile, $errline, $errcontext) {
$errorTypes = Array(
E_ERROR => 'Fatal Error',
E_WARNING => 'Warning',
E_PARSE => 'Parse Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Fatal Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compilation Error',
E_COMPILE_WARNING => 'Compilation Warning',
E_USER_ERROR => 'Triggered Error',
E_USER_WARNING => 'Triggered Warning',
E_USER_NOTICE => 'Triggered Notice',
E_STRICT => 'Deprecation Notice',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error'
);
$ret=(object)Array(
'number'=>$errno,
'message'=>$errstr,
'file'=>$errfile,
'line'=>$errline,
'context'=>$errcontext,
'type'=>$errorTypes[$errno]
);
$GLOBALS['BufferedErrors'][]=$ret;
return false;
}
function parse($fileToInclude, $argumentsToFile=false) {
$bufferedErrorStack = $GLOBALS['BufferedErrors'];
set_error_handler('errorParse', error_reporting());
$GLOBALS['BufferedErrors']=Array();
if (!file_exists($fileToInclude))
return '';
if ($argumentsToFile === false)
$argumentsToFile = Array();
$argumentsToFile = array_merge($GLOBALS, $argumentsToFile);
foreach ($argumentsToFile as $variableName => $variableValue)
$$variableName = $variableValue;
ob_start();
include($fileToInclude);
$ret = ob_get_contents();
ob_end_clean();
restore_error_handler();
$errors = $GLOBALS['BufferedErrors'];
$GLOBALS['BufferedErrors'] = $bufferedErrorStack;
if (count($errors)>0) {
$ret.='';
foreach ($errors as $error)
$ret.=
'- '.
''.$error->type.': '.
$error->message.
'
'.
'file: '.$error->file.'
'.
'line: '.$error->line.
'
'.
' ';
$ret.='
';
}
return $ret;
}
-------
Just get ColdFusion, which makes templating a snap. There are at least 4 different ways to manage templates: variable substitution, function calls (cfFunction tag), built-in tags, and user-defined tags. Each is more appropriate in different settings. To reinvent these 4 from scratch would take too long to get right. (There are open-source versions, but they are less mature.)
----
See: WebFormMethodologies, SeparateDomainFromPresentation
----
CategoryWebDesign