True Templating

True Templating

You've met PHP templating, but what is true templating and how is it different?

Recapping Native PHP Templating

You've been well introduced to the concept of MVC by now, and with luck, it has become clear enough for you to realize that approaching large PHP projects using templating makes sense.

So far, all the templating you've looked at has revolved around the principle of populating a single, multidimensional hash of content and then injecting HTML (or some other markup language) with that content using simple PHP statements such as if, for, while, or others.

However, the very fact that you are required to discipline yourself into only using certain PHP keywords within the templates points to some very clear pitfalls with this approach.

The Pitfalls of Native Templating

The first and most obvious problem is that you are using a very powerful language (PHP) to do a very simple job (spitting out content). The problem is not a performance issue, though. It's simply that by using PHP to take care of the content rendering, you are introducing yet another point in your project where things can go wrong.

Think of the common problems you face when writing big classes typographical errors, misspelling variable names, accidentally reusing counter variables in heavily nested for loops. All these problems are just as constant if you also use PHP for your templating.

The second, and perhaps more serious problem, becomes obvious when you consider larger projects. One of the big advantages of using MVC in PHP projects is that the view and the controller can be delegated to developers skilled in the art of design and HTML, but possibly not so skilled with PHP. This allows the more expensive software architects to work on the heavy-duty PHP driving the model of the project, and the less expensive Web developers to work on the interface.

This is a great use of resources. Not only does it save time and money, it also tends to ensure that the project is as slick on the interface side of things as it is on the back-end code.

However, if you choose to use PHP templating, then the Web developers have no choice but to write in PHP. Admittedly, there's not much PHP they need to know. It's the kind of PHP that could easily go on a cheat sheet. The problem is not so much one of a learning curve but rather one of danger. PHP, after all, does not restrict what these Web developers may or may not use in their templates. Rather, they must restrict themselves. Infinite loops, dangerous system calls, and other potentially critical mistakes are just a tiny typographical error away.

If there were some way to allow Web developers to render the output of a page using flexible tags as part of an arbitrary language, rather than using PHP, this problem could easily be negated. Smarty provides just such a solution.

True Templating with Smarty Templates

Smarty is a package for PHP that allows developers to easily prepare output for a template and then leave the template to display it in whatever way it sees fit.

Smarty is unique in that it provides not just simple 'dump value' methodology, but also allows loops, conditionals, and the traversing of multidimensional arrays. In a nutshell, it replicates all the traditional simple PHP methods used in native PHP templating, without using PHP at all.

Another big advantage of Smarty is its speed. Believe it or not, it actually works by creating native PHP from the template. That native PHP is then cached, such that if the template is not changed with subsequent requests, the template itself does not have to be translated into PHP again. It is simply pulled straight from the cache and executed.

Installing Smarty

Installing Smarty is straightforward. Unlike many other template engines, however, it is not a traditional PEAR package, so you have to download and install it manually.

Currently, Smarty resides at http://smarty.php.net.

After you have downloaded it, unpack it:

   root@linuxvm:~# tar -xzvf Smarty-2.6.2.tar.gz    Smarty-2.6.2/    Smarty-2.6.2/COPYING.lib    Smarty-2.6.2/.cvsignore    Smarty-2.6.2/BUGS    Smarty-2.6.2/demo/         Smarty-2.6.2/misc/smarty_icon.README    Smarty-2.6.2/misc/smarty_icon.gif    root@linuxvm:~# 

Next, you need to shift the libraries provided by Smarty to somewhere PHP can see them. Generally, all you need to do is to copy the full contents of the libs/ subfolder into /usr/local/lib/php, like so:

   root@linuxvm:~/Smarty-2.6.2# cp -r libs/ /usr/local/lib/php 

Next, you need to hook up your application to use Smarty. Immediately beneath the PHP files that will be using Smarty in your application, you need to create directories called templates, templates_c, cache, and configs.

   root@linuxvm:~# cd public_html/tests/prophp5/mvc/    root@linuxvm:~/public_html/tests/prophp5/mvc# mkdir templates_c    root@linuxvm:~/public_html/tests/prophp5/mvc# mkdir configs    root@linuxvm:~/public_html/tests/prophp5/mvc# mkdir templates    root@linuxvm:~/public_html/tests/prophp5/mvc# mkdir cache 

Mercifully, Smarty is sympathetic to the needs of real-world projects, for which configuration steps such as these might not always be possible, such as when your PHP scripts reside in multiple directories. You can also manually specify where these folders should exist. You will learn how later in this section.

Next, you need to set some permissions on two of these folders, template_c and cache. You will need to set the ownership of these folders to be that of the user name of your Web server. In most cases this is the nobody user, but it may also be apache or various others. If you are unsure, you can check by issuing:

   ps axuw | grep httpd | grep -v grep | awk '{print $1}' | tail -1 

The key thing is that this user and the group of which that user is a member are able to read and write to these folders freely. Set permissions as follows:

   chown nobody.users ./cache    chown nobody.users ./template_c    chmod 770 ./cache    chmod 770 ./template_c 

The mode 770 means that the owner of these folders may read, write, and execute their contents and that nobody else may. If this bothers you, and you are unable to become root on this machine, you may wish to use 775 instead, which will allow others read and write access. You may, however, rightly deem this to be a security risk.

Using Smarty

A typical Smarty implementation consists of a control file, much as before, ending in .php, and a template file, this time ending in .tpl.

smartytest.php

Take a look at a very simple example:

   <?    require('Smarty.class.php');    $objSmarty = new Smarty;    $strTemplate = "smartytest.tpl";    $objSmarty->assign("test_variable", "This is the value of my test variable");    $objSmarty->display($strTemplate);    ?> 

You also need to enter the template code for this script. The script needs to reside in the folder called template that you have created underneath this one.

smartytest.tpl

Take a look at the following code:

   <html>    <head>      <title>Untitled</title>    </head>    <body>      This is my template!<BR>      The value of the variable is: {$test_variable}    </body>    </html> 

You've probably guessed that the output from running smartytest.php, which then invokes the output template smartytest.tpl, is something like the following:

   This is my template!    The value of the variable is: This is the value of my test variable 

As you can see, we have assigned our Smarty template object a variable called test_variable, the output of which we have successfully rendered by using the syntax {$test_variable} in the template itself.

Simple enough, but how do you go about expressing arrays, displaying content conditionally, and taking advantage of all the functionality traditionally available in the native PHP templating discussed earlier?

Linear Arrays in Smarty

Pushing a linear array to Smarty and then recursing its contents are relatively simple. Consider the following code in your PHP control script:

   $objSmarty->assign("FirstName",array("John","Mary","James","Henry")); 

As you can see, you can assign PHP variables to your Smarty template object in exactly the same way as you would assign a normal garden-variety string. To render it, you would typically use the Smarty equivalent of a for loop:

   {section name=x loop=$FirstName}       {$FirstName[x]}<BR>      {/section} 

The output is predictable enough:

   John    Mary    James    Henry 

Associative Arrays in Smarty

You can approach associative arrays (hashes) in a very similar manner to how you approach linear arrays. Consider the following control code to supply the Smarty template:

   $arHash["Name"] = "Ed";    $arHash["Age"] = 22;    $arHash["Location"] = "London";    $objSmarty->assign("Writer", $arHash); 

Again, the approach for assignment appears to be very similar. Take a look at how you can now render the output in your template:

   The author of this chapter is {$Writer.Name}, {$Writer.Age} years of age,    currently residing in {$Writer.Location}. 

Notice how we use the simple syntax of {$X.Y}, where X is the name of our Smarty variable and Y is the key from the hash which has been assigned to it. This yields the following output:

   The author of this chapter is Ed, 22 years of age, currently residing    in London. 

Conditionals in Smarty

What if you want to display content only if a certain condition is met? This would be of particular interest to you in displaying error messages. You would want to display the error only if an error has definitely occurred.

You would assign variables in your PHP as follows:

   $objSmarty->assign("isError", 1);    $objSmarty->assign("ErrorText", "You did not specify a search term."); 

The following template logic can support this:

   {if $isError == 1}        The following error occurred: {$ErrorText}    {/if} 

As you can see, we have used a conditional expression very similar to that which you would use in PHP to perform the test. This will render only the introductory text, and the error itself, if isError is set to 1.

   The following error occurred: You did not specify a search term. 

To prove this to yourself, change the assignment to isError in your PHP to 0, and you will see that the output is not rendered at all.

This is somewhat laborious, though. In native PHP templating you would probably just have checked to see whether an error message has been defined. You can also do this in Smarty:

   {if $ErrorText}        The following error occurred: {$ErrorText}    {/if} 

This completely negates the need to explicitly tell Smarty whether an error exists. It can determine this for itself.

Rewriting search.php with Smarty

Now put these skills into practice with a slightly more complex example. Using Smarty, you can quite easily rewrite the search.php and search.phtml pair from the first part of the chapter.

Look at the content you are creating in search.php so that you know what you have to reproduce using Smarty.

Currently, the $displayHash variable may, by the time it reaches search.phtml, contain:

Quickly modify search.php so that it looks like this. The shaded area indicates the code we've modified or added from our original version:

   require_once("constants.phpm");    require_once("request.phpm");    require_once("constraint.phpm");    require_once("constraintfailure.phpm");    $strTemplateFile = "s_search.tpl";    require('Smarty.class.php');    $objSmarty = new Smarty;    $objRequest = new request();    $blHadProblems = ($objRequest->IsRedirectFollowingConstraintFailure());    if ($blHadProblems) {      $objSmarty->assign("HADPROBLEMS", "true");    };    if ($blHadProblems) {      $objFailingRequest = $objRequest-    >GetOriginalRequestObjectFollowingConstraintFailure();      $arConstraintFailures = $objFailingRequest->GetConstraintFailures();      $problemArray = Array();      for ($i=0; $i<=sizeof($arConstraintFailures)-1; $i++) {        $objThisConstraintFailure = &$arConstraintFailures[$i];        $objThisFailingConstraintObject = $objThisConstraintFailure-    >GetFailedConstraintObject();        $intTypeOfFailure = $objThisFailingConstraintObject->GetConstraintType();        switch ($intTypeOfFailure) {          case CT_MINLENGTH:            $problemArray[] = "Your search term was too short.";            break;          case CT_MAXLENGTH:            $problemArray[] = "Your search term was too long.";            break;          case CT_PERMITTEDCHARACTERS:            $problemArray[] = "Your search term contained characters I didn't    understand.";            break;        };     };    };    if ($problemArray) {      $objSmarty->assign("PROBLEMS", $problemArray);    };    $objSmarty->display($strTemplateFile); 

Create a corresponding template file in the templates subfolder called s_search.tpl:

   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">    <html>    <head>     <title>Ed's Search Page</title>    </head>    <body>    <H1>Ed's Search Page</H1>    <hr>    You can search for types of steak here.    <BR><BR>    {if $HADPROBLEMS}     <B>Sorry, there were problems with your search!</B>      {section name=x loop=$PROBLEMS}        {$PROBLEMS[x]}      {/section}    {/if}    <FORM METHOD="GET" ACTION="searchresults.php">      <TABLE BORDER="0">        <TR>          <TD>Type of Steak</TD>          <TD><INPUT TYPE="TEXT" NAME="typeOfSteak"></TD>        </TR>     </TABLE><BR>     <INPUT TYPE="SUBMIT">    </FORM>    </body>    </html> 

Run the revised search.php and try to generate errors as you did before. You'll notice that the output upon receiving such errors is almost exactly the same, so the template conversion has been a resounding success.

There is one small problem, however. Generate only one error, and you will still receive a message stating that "there were problems,'' rather than "there was a problem,'' with your search.

In the original .PHTML native PHP template, you accounted for this using:

   <B>Sorry, there <?=((sizeof($displayHash["PROBLEMS"]) > 1) ? "were problems" :    "was a problem")?> with your search!</B> 

Thankfully, you can account for this in Smarty, too. All you need to do is measure the size of the array and adjust the output accordingly, exactly as you have done in native PHP.

   <B>Sorry, there {if $PROBLEMS[1]}were problems{else}was a problem{/if}    with your search!</B> 

There is some slight cheating going on, however. There is no way in Smarty to measure the size of an array without calling upon PHP code, so the code simply checks for the existence of a second element in the array. If it exists, the user sees the plural text. Otherwise, the user sees the singular text.

If you modify your template to use this new code, you should find that the output of the script is exactly the same now as it was when you used native PHP templating in the first half of the chapter.

Advanced Smarty

Now that you've familiarized yourself with basic Smarty use, it's worth looking in a little more detail at some of the more advanced features it offers.

Runtime Configuration

First, as mentioned at the beginning of this section, you may well find yourself confronted with situations in which creating subfolders under every single location where .php files reside is undesirable. Thankfully, Smarty allows you to specify these locations at runtime:

   $smarty->template_dir = '/web/www.mydomain.com/smarty/guestbook/templates/';    $smarty->compile_dir = '/web/www.mydomain.com/smarty/guestbook/templates_c/';    $smarty->config_dir = '/web/www.mydomain.com/smarty/guestbook/configs/';    $smarty->cache_dir = '/web/www.mydomain.com/smarty/guestbook/cache/'; 

Multidimensional Arrays

Multidimensional arrays may be treated in much the same manner as their single-dimensional counterparts, whether they are associative, linear, or a mixture:

   $multiArray = Array(Array("x","o","x"),Array("o","x","x"),    Array("o","o","x")); $objSmarty->assign("TicTacToBoard", $multiArray); 

Tie this to the following template:

   <TABLE BORDER="1">      {section name=y loop=$TicTacToBoard}        <TR>          {section name=x loop=$TicTacToBoard[y]}            <TD>{$TicTacToBoard[y][x]}</TD>          {/section}        </TR>      {/section}    </TABLE> 

This is the expected familiar Tic Tac Toe board with X winning down the left-to-right diagonal.

Variable Modifiers

Variable modifiers may be used by template designers to perform very simple modifications to the variables they have received.

   $smarty->assign('bookTitle', "Beginning PHP5"); 

Couple this with the template code:

   {$bookTitle|replace:"Beginning":"Professional"} 

The output is very much as expected.

Functions

Smarty defines a number of functions in its language. It considers some to be built in unable to be modified or overwritten. The functions provided in Smarty are really better described as methods, in that they do not generally perform any function on an input, unlike the modifiers you have just met, such as replace, which changes its input before rendering it.

One very useful example is the strip function, which allows you to neatly lay out HTML properly tabulated and spaced, safe in the knowledge that any extraneous whitespace will be stripped out before it is sent to the browser. This makes the templates very easy to maintain.

   {strip}    <table border="0">     <tr>        <td>            Here is some content.        </td>     </tr>    </table>    {/strip} 

This is a great deal easier for a human being to read. At the time when the HTML is sent to the browser, however, how it looks behind the scenes is far less important than how it displays to the user. So, the {strip} tag removes any whitespace for you that might disturb the output:

   <table border="0"><tr><td>Here is some content.</td></tr></table> 

Capturing Output

Sometimes, rather than simply spit parsed template output directly to screen, you may want to capture it into a variable. This can be particularly useful should you, for example, want to render XML content using Smarty and then pass it through an XSL stylesheet to actually render it to the Web browser. Alternatively, you may not want to pass the rendered content at all. For example, you may be sending an HTML e-mail. Here, Smarty is very useful indeed, as you will discover in the next chapter, "Communicating with Users.''

   // capture the output    $strOutput = $objSmarty->fetch("index.tpl"); 

Simply assign the rendered template's output HTML to the variable $strOutput, with which you can then do whatever you desire.

Including Other Templates

Smarty even allows you to include other templates, which will be parsed in the normal way. You may use this to add common headers and footers to your pages:

   {include file="header.tpl"} 

Further Reading

The functionality of Smarty is enormous and far beyond the scope of this chapter. Having been introduced to some examples of standard usage, however, you should now be well equipped to explore its functionality further.

The obvious starting point is the Smarty reference site at http://smarty.php.net/manual/en/. All modifiers, functions, and even obscure configuration parameters are documented here in full. Conveniently, the documentation is split into two sections, targeting programmers and HTML template developers alike.

When to Use Smarty vs. Traditional Templating

The decision to use Smarty over traditional native PHP templating should not be taken lightly. Although Smarty is clearly a great tool for simple templating, its functionality is still only a subset of that which might be needed in large-scale projects.

Although it can be extended with native PHP through the use of the {php} and {/php} tags, doing so rather defeats the purpose of using safe templates in the first place. Indeed, Smarty's own Web site states: "embedding PHP code into templates is highly discouraged.''

The skills and experience of any other developers working with you on the project are likely to be the deciding factor. If you are the sole engineer working on a project, it is almost certainly better to use native templating. However, if the project requires a number of designers and HTML specialists working on the output from your scripts, Smarty templates are a much safer bet because they allow such individuals to manipulate the display logic of your application to their hearts' content, without touching the PHP code behind it.



Professional PHP5 (Programmer to Programmer Series)
Professional PHP5 (Programmer to Programmer Series)
ISBN: N/A
EAN: N/A
Year: 2003
Pages: 182
BUY ON AMAZON

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net