Building a Site Engine with PHP, Part 4

In the last three articles we went over a basic idea of how a site engine works, how to build the plug-in, module, and block systems, the basic directory structure, the database structure, and the authentication methods. In this, the fourth article, we’ll be going over the template system, and the basic page loading methods. So, let’s go ahead and get started.

XML and HTML and CSS, Oh My!

XML is a really cool language to use for a template system. Coupled with PHP5, you almost have a hybrid of languages that I like to refer to as Object-Oriented HTML. It makes you want to say “OOH!” and “Ahh!”.

The templates are made up of an XML document that contains each piece of our layout as a separate XML element in HTML form. At that point most people say “What!?” Well, for each element, we embed the HTML as CDATA. What we end up with is a Header element that contains the HTML from the <html> to the spot that your first content column starts. Then there is the Body element that contains the area that will hold all our content columns. Following that we have the Footer, which contains our content to the </html>. Then we have our three column elements which hold the HTML for each of our content columns, then the Block element, which contains the HTML for the layout of our blocks.

Basically this is what the XML document will look like:

<template>

<header> <![CDATA[]]></header>

<body> <![CDATA[]]></body>

<footer> <![CDATA[]]></footer>

<column1> <![CDATA[]]></column1>

<column2> <![CDATA[]]></column2>

<column3> <![CDATA[]]></column3>

<block> <![CDATA[]]></block>

</template>

Not too hard to follow. If we were to break it down into the order it is loaded, it would look like this:

<header>

</header>

<body>

<column1>

<block>

</block>

</column1>

<column2>

<block>

</block>

</column2>

<column3>

<block>

</block>

</column3>

</body>

<footer>

</footer>

The example above shows only one block in each column, but chances are most pages will contain more than one block per column. Here is a rather crude example of what a finished template file would look like.

<template>

<header><![CDATA[<html>

<title><!title></title>

<body>

<div style=”width:100%;text-align:center;”>header!</div>]]></header>

<body><![CDATA[<div style=”width:100%”><!content></div>]]></body>

<footer><![CDATA[<div style=”width:100%;text-align:center;”>footer!</div></body></html>]]></footer>

<column1><![CDATA[<div style=”width:25%;float:left;text-align:center;”><!column1></div>]]></column1>

<column2><![CDATA[<div style=”width:50%;float:left;text-align:center;”><!column2></div>]]></column2>

<column3><![CDATA[<div style=”width:25%;text-align:center;”><!column3></div>]]></column3>

<block><![CDATA[<div style=”width:100%;text-align:center;”><!block_data></div>]]></block>

</template>

A real one would contain much more complex html, but for the sake of an example, that does the job. It will work as a full template. As you can see there are a few proprietary tags in the html, such as <!title>. We talked a little about those in the fourth part of the first article. Now is the time to talk a little more about them.

{mospagebreak title=You’ve been Tagged}

The tagging system, as I like to call it, is one of the biggest parts of the entire engine, not in size, rather, in functionality. Through out the entire engine we have things being sent to the replace() function, which is the function that replaces all the tags with the correct data, before it’s sent to the browser. So with no further interruptions, let’s look at the replace function.

First we define the function and all the parameters it will accept. $load is the array of tags to replace. The array should be set up like this array(‘tagname’=>”data to place where the tag is”); $string is the variable that holds the XML string that we’ll later get from our template using simpleXML; and $mask is an optional variable that you can use in case you want to use a different tag style, such as <*tag>.

function replace($load,$string,$mask='<!-tag->’){

Then we make sure that $load is an array so we don’t get any nasty unexpected errors. If it’s not an array, the function will just return the XML string without processing it:

if(!is_array($load)){
return $string;
}else{

If $load is an array, first make sure that there isn’t a tag in the $load named “-tag-“. Then explode the $mask tag by “-tag-“, so that we have an array that has the opening and closing brackets for each tag, “<!” and “>”. Now you might say “why all this stuff about the tags being explode to simply get the ‘<!’ and ’>’.” The reason behind that is like I said before, so you can change the tag style to something else like <*-tag-> if you want to or need too.

if(!strpos(@$load,’-tag-‘)) $mask='<!-tag->';
$masks=explode(‘-tag-‘,$mask);

Just so you know the part of the previous lines of code “!strpos(@$load,’-tag-‘)” will produce a warning through PHP because we’re reading an array as if it were a string, however it still works as expected.

Now we’re get to use my favorite PHP functions, array_keys(). array_keys() returns the keys of the input array as the values of the output array. We do this because we need to get the tag name so that we can then build our tag in the function to match it to the tag in the $string to replace it with our data.

Anyway, now we’ll use array_keys() to get all the names of the tags from the $load array we load that into $tags . Then we run the $tags array through a loop to get the value of each of the array keys. Now we build our new list of the actual tags to replace (this is a little easier than searching the XML document to find all the replaceable tags).

$tags=array_keys($load);
foreach($tags as $tag){
$newtags[]=$masks[0].$tag.$masks[1];
}

Now we use another built in function that’s just like array_keys() but instead of operating on the keys it operates on the values. It’s surprisingly named array_values(). Then we put it all together and run it through srt_replace() which will loop through the arrays that we pass to it. Str_replace() will return a string with all occurrences of $newtags in $string replaced with values from $load respectively.

$loads=array_values($load);
return str_replace($newtags,$loads,$string);
}
}

That’s about it for the replace() function. Now let’s move on to how we plan on getting the XML elements from our template to the replace() function.

{mospagebreak title=Give it a Little Class:}

Now we need to talk about the template plug-in and how it works. This is basically the only part of the entire engine that makes it only compatible with PHP5 because we use SimpleXML to parse our XML file. SimpleXML works a lot like your average PHP class. You simply set simplexml_load_file() to the value of a variable and call a file name with it to load the XML document into the script, then you use the -> operator to call each element of the XML file. Here’s a little thing you can do to test out simpleXML. Take the XML example I showed you earlier and save it as template.xml, then save the following script to the same directory as test.php.

<?
//load the template.xml file.
$template = simpleXML_load_file(template.xml);
//load the header element as a string
$header = $template->header;
//output the header element
echo $header;
?>

Go ahead and try that, then check the page source in your browser and you’ll see the exact html that’s in the header element of the template.xml file. Compare that to trying to make a parser do the same thing in PHP4 and you’ll understand why it’s called simpleXML. As you can see simplexml_load_file() basically loads the XML file into the $template variable and sets it up as if it’s a class and you call each element as if it were a class’s variable. Pretty simple isn’t it, hence the name simpleXML. Now that you know how simpleXML is going to help us load the template file, we’re going to talk about how to set up or plug-in so that it will easily load your template file into the engine. So, let’s go ahead and take a look at the class that handles the template system.

First we start off by defining the class, and all the variables that we plan on using throughout the engine. $template_list is an array that holds a list of all the templates that we have, this is useful for listing them out on a page to get information on what templates you have, without having to look at the database. $cur_template will hold all the information on the template that we’re currently working with. $loaded is the place holder for the template file after we’ve loaded it with simpleXML. $elements is an array of all the elements in the XML file that aren’t columns. $columns is the array that will hold (gasp!) the column elements from our template file. $tags is a rather important variable. It holds all the tags that we use through out the engine. And last but not least, $page holds the final output of the entire engine that will be sent to the browser.

class template{

public $template_list;
public $cur_template;
public $loaded;
public $elements;
public $columns;
public $tags;
public $page;

The template function has been named the same as the class so that it will run when we call the class. Basically it builds the list of templates that we can use with the particular site, from the database. Then we call a few functions so that those too load when the class is called.


function template(){
global $sql, $site;
$templates = $sql->_query(“SELECT t_ID, t_name, t_file, t_status, t_author, t_date FROM templates WHERE site_ID = ‘{$site->site['ID']}'”);
while($template=$sql->_fetch_object($templates)){
$this->template_list['t_ID'][]=$template->t_ID;
$this->template_list['t_name'][]=$template->t_name;
$this->template_list['t_file'][]=$template->t_file;
$this->template_list['t_status'][]=$template->t_status;
$this->template_list['t_author'][]=$template->t_author;
$this->template_list['t_date'][]=$template->t_date;
}
$this->set_template();
$this->load_template();
$this->tags['host']=$site->site['host'];
}

Set template loads the template that the user has set as the template they want to use as the $cur_template. If a user_ID isn’t found, it will load the template that has the status that’s set to “loaded” as the default template.


function set_template(){
global $sql,$user;
if(isset($user->user['ID'])){
$U_ID=$user->user['ID'];
$user_template=$sql->_query(“SELECT t_ID FROM template_users WHERE user_ID = ‘$U_ID'”);
while($user_temp=$sql->_fetch_object($user_template)){
$tempate_key=array_keys($this->template_list['t_ID'],$user_temp->t_ID);
}
}else{
$tempate_key=array_keys($this->template_list['t_status'],”loaded”);
}
$this->cur_template['t_name']=$this->template_list['t_name'][$tempate_key[0]];
$this->cur_template['t_file']=$this->template_list['t_file'][$tempate_key[0]];
}

Load_template actually gets all the information from the template’s XML file and breaks it down into each component. Then it sections them off into the correct array based on the name of the element. As you can see, if the element’s name isn’t “header”, “body”, “footer”, or “block”, it will get sent to the column’s array because the function recognizes anything other than those four elements as a column, which releases you from the regular three column layout and lets you add as many columns as you’d like. You can have one column or fifty columns if you’d like. There’s also a small amount of error handling put into the function so that if the template isn’t loaded, you’ll be informed and you can correct the problem, which is usually a misspelling of a templates name.


function load_template(){
global $site;
if(is_array($this->cur_template)){
if(file_exists(“templates/{$site->site['host']}/{$this->cur_template['t_file']}/{$this->cur_template['t_name']}.xml”)){
$this->cur_template['template']=”templates/{$site->site['host']}/{$this->cur_template['t_file']}/{$this->cur_template['t_name']}.xml”;
$this->loaded=simplexml_load_file($this->cur_template['template']);
$elements = array_keys(get_object_vars($this->loaded));
for($i=0;$i<count($elements);$i++){
if($elements[$i]==”header”||$elements[$i]==”body”||$elements[$i]==”footer”||$elements[$i]==”block”){
$this->elements[$elements[$i]]=$this->loaded->$elements[$i];
}else{
$this->columns[$elements[$i]]=$this->loaded->$elements[$i];
}
}
}else{
echo”ERROR Template <b>templates/{$site->site['host']}/{$this->cur_template['t_file']}/{$this->cur_template['t_name']}.xml</b> Not Found”;
}
}else{
echo”ERROR Template Data Not Specified”;
}
}

This is the function that we call from the blocks plug-in after we’ve loaded and sorted all the blocks. It reads the names of each column and matches them to the array of blocks that have been set by the parse_block() function in the blocks plug-in, then it sends all the information to the tags array as a single tag which replaces the <!content> tag in the body element.


function build_cols($col_content,$bcolumns){
global $blocks,$plugins;
if(is_array($this->columns)){
$columns_keys=array_keys($this->columns);
foreach($columns_keys as $column){
if(in_array($column,$bcolumns)){
$this->tags['content'].=$this->replace($col_content,$this->loaded->$column);
}
}
}
}

The parse_page function isn’t complicated at all, but it is still very important. It send the rest of the remaining elements to the replace function to get the tags replaced, then it puts them to the $page variable which I mentioned above that holds all the data that will be sent to the browser. And, just as an added amount of security that all our tags get replaced and to make sure any tags in the block’s loaded content get replaced, we run the entire $page variable through the replace function before the final output.


function parse_page(){
$this->page.=$this->replace($this->tags,$this->loaded->header);
$this->page.=$this->replace($this->tags,$this->loaded->body);
$this->page.=$this->replace($this->tags,$this->loaded->footer);
return $this->replace($this->tags,$this->page);
}
?>

That’s about it for the template plug-in. Like all the other parts of the engine, it’s not actually all that complicated if you just take a second to look at it.

{mospagebreak title=The Final Words}

It’s not hard to create your own tags; all you have to do is send them to the array of tags. Here’s a small example, if you wanted tags that will show the date and time — all you’d have to do is put in the following code, after you’ve declared the template class.

$template->tags['date']=date(“l F, j Y”);

$template->tags['time']=date(“g:i:sa”);

Then anywhere you have the tag that looks like <!date>, it will show the date like this: Thursday June, 10 2004, and the <!time> tag will show the time like this: 2:17:04pm. On top of that a tag can show anything — a tag can be an include, a string, even an image. For example, if you do this $template->tags['test']=”foo”; the tag <!test> will show the word “Foo”. It’s pretty cool if you ask me.

To add columns to your layout, all you have to do is add them to the template file named column4, column5 and so on.

Well that’s about it for this article. In the fifth and final article I’ll show you what you need to do to add plug-ins , modules, and blocks to the engine.

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort