PHP Classes

File: Manual/docs.php

Recommend this page to a friend!
  Classes of Jose Maria Rodriguez Millan   Siviglia Templating 2   Manual/docs.php   Download  
File: Manual/docs.php
Role: Auxiliary script
Content type: text/plain
Description: Auxiliary script
Class: Siviglia Templating 2
Template engine with configurable grammar
Author: By
Last change:
Date: 11 years ago
Size: 30,551 bytes
 

Contents

Class file image Download
<html> <head> <style type="text/css"> body {font-family:Calibri} .example { margin-bottom:10px; border:1px solid #E0E0E0; background-color:#F0F0F0; padding:5px; clear:both } h1 {border-bottom:1px solid black} .template, .usedWidgets, .result,.generated { color:#303030; background-color:white; border:1px solid black; margin-right:20px; } .starttag {color:red;font-weight:bold} .tagcontents {color:blue;font-weight:bold} .result,.generated { margin-top:5px; border:1px solid black; background-color:white; width:500px; } .resultCode {padding:10px} .widget { margin:2px; border:1px solid black } p { padding:3px; margin:0px; background-color:#A0A0A0; font-weight:bold; font-size:16px } .widget p {background-color:#C0C0C0} .firstRow , .secondRow {clear:both} .comment {margin-bottom:20px} .bolder,.bolder2 {font-size:14px;font-weight:bolder;} </style> </head> <body> <h1>Siviglia Templating System</h1> The Siviglia Templating System is the template engine used by the Siviglia Framework (work in process).<br> You can find more information on this class, at <a href="http://xphperiments.blogspot.com">my php development blog</a>.<br> <br> It was designed with the following goals in mind: <ul> <li><b>Intuitive</b>: It's syntax should be very intuitive.It should be usable without technical knowledge (specially, for content creators)</li> <li><b>Reusable</b>: Templates should be composed of smaller bits ("widgets") ,which, in turn may be composed of other widgets.Those widgets may be reusable.</li> <li><b>No need for "templating languages"</b>: Templates and widgets may use php freely, without needing of special template languages.</li> <li><b>Different levels of abstraction</b>: From the website structure, to the smallest boxes, should be expressed by templates.</li> <li><b>Cacheable</b>: Templates should generate code, which may be cached</li> <li><b>Output depending on platform</b>:Given a template, and using different widget resolution paths,code for different platforms may be generated.</li> <li><b>Extensible</b>: Using plugins, special actions can be taken.This distribution includes a plugin to handle localization,css and javascript code.</li> </ul> The Siviglia Templating System is based in <b>widgets</b>, used from a <b>template</b>.Widgets are bits of reusable code.<br> Templates are the collection of widgets needed to compose a certain page.<br> But, both of them are just plain php files.In any of these you can use php and html as you would in a regular .php file. <br><br> <div class="example"> <h1>Setup</h1> Adding the engine to your application is easy. You will need: <ul> <li>Define the constant <b>PROJECTPATH</b> to point to your project root folder.</li> <li>Define the constant <b>SIVIGLIA_PATH</b> to point to the folder where you've installed this class.</li> <li>Create folder(s) to store widgets.</li> <li>Initialize the class, specifying the widget folder paths, and plugin initialization options</li> <li>Specify the template to parse</li> </ul> Here's an example: <div class="template"> <pre> &lt;?php define(PROJECTPATH,getcwd()); define(SIVIGLIA_PATH,realpath(getcwd()."/templating")."/"); include_once(SIVIGLIA_PATH."TemplateParser2.php"); include_once(SIVIGLIA_PATH."TemplateHTMLParser.php"); /* First, we create a HTML generator */ $oLParser=new CLayoutHTMLParserManager(); /* Note: widgetPath is always relative to PROJECTPATH.You can add as many paths as needed. */ $widgetPath=array("/tests/widgets/"); /* The parser is created, setting it's output as html, specifying the widget path, and initializing the @L plugin to "en" */ $oManager=new CLayoutManager("html",$widgetPath,array("L"=>array("lang"=>"en"))); /* Here's the definition of the template we want to process */ $definition=array("TEMPLATE"=>PROJECTPATH."/templates/FullPage.html"); /* The template is rendered, using the HTML generator, and asking to also output the result, instead simply returning it */ $oManager->renderLayout($definition,$oLParser,true); ?&gt; </pre> </div> <div style="clear:both"> </div> </div> <br> <?php function boldify($cad) { $cad=preg_replace('/\[(\*|_|#|@)([^\]]*)\]/','<span class="starttag">[\1</span><span class="tagcontents">\2</span><span class="starttag">]</span>',$cad); return preg_replace('/(\[\:(?:_|\/)[^\]]*\])/','<b class="bolder2">\1</b>',$cad); } $tests=array( array("template"=>"HelloWorld.html", "title"=>"Hello world", "comment"=>"This is our HelloWorld template: **HelloWorld** This template uses the <b>B</b> widget, to boldify whatever is specified between the widget start [*B] and it's matching end tag [#] <br>The B widget is specified in a file named 'B.wid', located in a folder specified in the widget path.<br><br> The content of that file is simply: ##B## The tag [_*], means 'whatever the template contained within this tag'.The tag, in this case, is B. The upper-level tag for any widget, is its file name (without extension).<br><br> @@result@@ " ), array("template"=>"SimpleBox.html", "title"=>"Multilevel widgets", "comment"=>"The previous widget, had just one possible content.But widgets can specify a whole structure.<br> For example, a typical \"box\" in a html page, has a <b>title</b> component, and a <b>content</b> component.<br> So, first we create a template like this one: **SimpleBox** There you can see the start of the widget , <b>[*BOX]</b> and each one of its subtags.Subcomponents of a widget are specified using the <b>[_</b> tag. Now, the BOX widget should be created somewhere in the widget path, in a file named 'BOX.wid':<br> ##BOX## The meaning of the lines above,is:<br> <ul><li>When a BOX widget is used from a template, first, open a div.</li> <li>For each [_TITLE] present in the template, open a div, and add as contents of that div, whatever the template contains inside the [_TITLE] tag.</li> <li>For each [_CONTENTS] present in the template, open a div, and add as contents of that div, whatever the template contains inside the [_CONTENTS] tag.</li> <li>Finally, close the outer div</li> </ul><br> The final result is:<br> @@result@@ <br> A few rules to understand about widget subcomponents: <ul> <li>A subcomponent is rendered as many times as it's specified in the <b>template</b>.That ranges from 0 to n.Currently, there's no way to specify if a subcomponent is required,or how many times it can appear in the template.</li> <li>A subcomponent is rendered in <b>widget</b> order, not in template order.This means that the [_TITLE] will be rendered before the [_CONTENT], no matter the order specified in the template.</li> <li>Anything that has subcomponents (a widget, or a subcomponent), doesnt have a value of its own.So, a widget file, or a subcomponent of it, either has subcomponents, or has a [_*] tag.</li> <li>Similarly, when a template uses anything that has subcomponents, only those subcomponents should be used.This means that, if you write: <div><pre> [*BOX] &lt;?php //some php code ?&gt; &lt;---- This will be lost. [_TITLE]..[#] &lt;div ...&gt; &lt;---- This will be lost. [_CONTENTS]..[#] .... &lt;---- This will be lost. [#] </pre></div> </li> </ul> And, at this point, almost all components of the template engine have been presented: the [* , [_ and [_*] tags.<br> This is a great moment to stop reading, doing a few tests with what you've seen until now, and then keep reading, as understanding what has been presented so far will be enough to easily grasp all the rest. ", "widgets"=>array("B","I") ), array("template"=>"Simplebox2.html", "title"=>"Nesting widgets (template side)", "comment"=>"The content of a leaf subcomponent (ie, these that have no child subcomponents), could be html code, php code, or another widget.<br>This means, you can nest widgets in the template:**Simplebox2** As you see, there's a BOX widget nested in the CONTENTS subcomponent of an outer BOX widget.@@result@@", ), array("template"=>"NewsBox.html", "title"=>"Nesting widgets (widget side)", "comment"=>"But widgets can be nested from other widgets.Let's start with the template:**NewsBox**<br>By looking at that template, we can see that the NewsBox object has a TITLE,and a NEWS component.A NEWS component has a TITLE and a CONTENT.<br> Now you can start to see how templates specify <b>what</b> to render, but not <b>how</b> to render it, creating several isolated layers on the page rendering process.<br> We will be rendering it using the following widget:##NewsBox## In this case, the widget acts as if it were a template.So all the rules mentioned above, apply.Specially, when mixing subcomponents.<br> The NewsBox TITLE subcomponent, will be rendered using the SIMPLETITLE widget.You can see that when a widget uses another widget, the tags of the used subwidgets have the added character <b>:</b>.<br><br> Each of the NewsBox NEWS subcomponent, will be rendered using BOX widgets.To do that, the NEWS subcomponent specifies that its subcomponents TITLE and CONTENTS must be rendered inside the TITLE and CONTENTS of the BOX widget. <br> Also, note how the template uses the NEWS subcomponent three times. The SIMPLETITLE widget is just: ##SIMPLETITLE##<br> Here is the result:@@result@@ " ), array("template"=>"NewsBox2.html", "title"=>"Omitting subcomponents.", "comment"=>"We've seen the effect of repeating a subcomponent.In this template, we will omit the TITLE tag in our previous example**NewsBox2**@@result@@Currently, there's no way to indicate if a certain subcomponent is required or optional, or the number of times it can be repeated.<br> Omitting a subcomponent in a template, means that nothing inside that subcomponent in the widget file, will be included.And that also includes any php code that may exist inside that subcomponent. " ), array("template"=>"NewsBox3.html", "title"=>"Using PHP in the template", "comment"=>"Using PHP in the template is simple:**NewsBox3**@@result@@"), array("template"=>"NewsBox4.html", "title"=>"Widget parameters", "comment"=>"Widgets and subcomponents may have parameters.Let's first see an example. This is the template: notice how parameters are passed:**NewsBox4**Now, see how the widget simply uses these parameters:##NEWSBOX2##@@result@@"), array("template"=>"NewsBox5.html", "title"=>"Widget Parameters (II)", "comment"=>"You can also use fixed values as parameter values:**NewsBox5**@@result@@"), array("template"=>"NewsBox7.html", "title"=>"Iteration", "comment"=>"The recommended way to do iteration, is to use a source and iterator widget variables.This variable is passed by reference, as you can see in the template:**NewsBox7**.The widget implementation is: ##NEWSBOX3##<br> In this case, the template doesnt specify several NEWS subcomponents.It's the widget the one that chooses to iterate over the subcomponent definition. The widget iterates over the source parameter,and sets the iterator with the value of the current row.As it has been passed by reference, the iterator variable in the widget is linked to the iterator variable in the template.<br> @@result@@" ), array("template"=>"NewsBox6.html", "title"=>"Iteration (II) and conditional subcomponents", "comment"=>'Suppose that, in some cases, you dont want to render a certain subcomponent.That would need an "if" sentence surrounding the subcomponent.<br> Something like this:<div class="firstRow">'. '<div class="template"><p>Template code</p>'. '<div class="templateCode"><pre>'. boldify(htmlentities(' <?php $includeTitle=true; ?> [*NEWSBOX3] <?php if($includeTitle){ ?> [_TITLE]This is the title[#] <?php } ?> <... Rest of the widget...> [#]')."</div></div><br><div style=\"clear:both\"></div>The problem is that this would not work.Remeber what can be contained inside a widget or subcomponents?Just subcomponent content, if the subcomponent has no children, or child subcomponents in other case.<br> The php code is outside the [_TITLE] subcomponent, and, when the template is parsed, the php code will not be included in the output.<br><br> To solve this, you just have to move the php code <b>inside</b> the subcomponent tags:**NewsBox6** Notice how both the open and close subcomponent tags, are used to insert the php code.<br> Also, how this syntax allows iteration as well.Iteration is done as parameter passing, but this time, the value of those parameters are calculated in the code inside the subcomponent tag.<br><br> Code inserted in this way, cant span outside the line where the widget or subcomponent tag is used;<br><br> This is the widget implementation: ##NEWSBOX2## <br>Notice how the widget implementation is not special.It's , in fact, the same used when we presented widgets parameters. @@result@@ ") ), array("template"=>"PathWidget.html", "title"=>"Organizing your widgets", "comment"=>"You can store your widgets in subfolders of folders specified as widget paths.Then, add the subfolders as a prefix to the widget name: **PathWidget** It's not important the content of those widgets, but to note that the BOX2 widget will be searched in the Container/Boxes folder inside the folders specified in the widget path." ), array("template"=>"FullPage.html", "title"=>"Putting it all together", "comment"=>"One of the key advantages of the Siviglia templating system is that it allows to build the user interface in several layers.<br> The top layer is the template file.Each page of your website has it's own template.But this template doesn't handle any HTML code.Instead, it should include classes (models, controllers) to get the data needed to render the page, and send it to appropiate widgets.<br> So, you can begin writing templates using very high abstractions.For example: many pages shares the same headers,footers,menus,etc.We can call all this simply a <b>PAGE</b>,and what makes each page different, it's just the content.<br> This is enough to allow us to write our template.We're using the same News widget: **FullPage** We're using the PAGE widget, and setting its contents to a NEWSBOX object.<br> Does the PAGE widget include HTML code? No!:##PAGE## The PAGE widget is a <b>semantic</b> widget.When present in a template,its purpose is to represent the concept of a certain \"web page\", not how it should be rendered.<br> In turn, the PAGE widget file, redirects its conceptual components (title, contents) to a certain <b>renderer</b> widget, in this case, LAYOUT: ##LAYOUT## This widget, LAYOUT, is a concrete implementation of an HTML layout.<br><br> Suppose now that we want to change the whole site layout.The only file we'll need to modify, it's the PAGE widget, so it complies with the new LAYOUT widget.<br> But all the pages on the site should remain the same, as, most probably, the \"News\" page, as defined in the previous template, shouldnt need any change. @@result@@" ), array("template"=>"Ltest.html", "title"=>"Plugins", "comment"=>"Plugins are special classes that are able to process the template parsing tree at <b>parse</b> time.So, they doesnt have access to variables,etc used by the php code present in the template.<br><br> In a template, or widget, plugins are invoked using the <b>[@</b> tag.<br><br> "), array("template"=>"Ltest.html", "title"=>"The Language (<b>@L</b>) plugin", "comment"=>"The language plugin is pretty straightforward to use.Just insert any text (including php code) inside the [@L] tag. **Ltest** If we go back to the class initialization, the following line initializes the L plugin, specifying for what language we're rendering the template: <div class=\"template\"> ....<br> \$oManager=new CLayoutManager(\"html\",\$widgetPath,array(\"L\"=>array(\"lang\"=>\"en\")));<br> ...<br> </div> <div style=\"clear:both\"></div> So, when the previous template is rendered, the result is the following: @@result@@ But also, the plugin has created (or updated) the (PROJECTPATH)/lang/(language).txt file.Let's see its contents: <div class=\"template\"> <pre> translate this!::=:: inserting text with variables: {%1} and {%2}::=:: </pre> </div> <div style=\"clear:both\"></div><br> This file can be edited: <div class=\"template\"> <pre> translate this!::=::Static translation inserting text with variables: {%1} and {%2}::=:: {%2} flipped {%1} </pre> </div><div style=\"clear:both\"></div> After editing the file, we need to <b>remove the cache file</b>.All the cache files are stored, by default, under the (PROJECTPATH)/cache directory. For now, we'll just remove the whole cache folder.When the template is re-evaluated, it will render as: @@result@@ The reason why you need to remove the cache folder after editing the translations, will be clearer later in this documentation.<br><br> But, what is important here, is that translations done in this way are resolved at <b>template parsing</b> time, not at <b>execution</b> time. "), array("template"=>"SampleJs.html", "title"=>"The @SCRIPT plugin", "comment"=>"The purpose of the SCRIPT plugin is to have both the presentation (html) and behaviour (js) code in the same file (simpler from a coding perspective), but, when the template is parsed, the javascript code will be stored in a separate file, possibly along with javascript code from other widgets.<br> This time, we will start showing the widget file: ##SAMPLEJS## Here you can see how the code and the target js file (where it'll be finally stored) are specified.<br><br> The target js file needs no extension, and it will be always stored in (PROJECTPATH)/scripts/widgets/(filename).js You need to load that script file from the template.A very simple template could be: **SampleJs** Here is the result: @@result@@"), array("template"=>"SampleCss.html", "title"=>"The @CSS plugin", "comment"=>"The purpose of the CSS plugin is similar to the SCRIPT plugin: keeping styles local to the current widget, inside the same file where the widget is defined.<br> The destination CSS file is specified with the FILE subcomponent, and the rest of the text is considered CSS rules. Here's the widget file: ##SAMPLECSS## And here is the template using it: **SampleCss** As you can see, the default location for CSS files is in (PROJECTPATH)/html/css/(file).css @@result@@" ), array("template"=>"SampleCss.html", "title"=>"Plugins revisited", "comment"=>"After reading about the CSS and the SCRIPT plugins, you can see why they exist:<br> <ul> <li>First, they keep everything related to a piece of html in the same file, and that's good to maintain the code.<br> At the same time, CSS and SCRIPTs will be stored in js and css files, and that's good from the browser perspective.<br> So, you have the best of both worlds :-). </li> <li>Widgets are self-contained, so they can be easily reused and shared, as in a single file, they have all they need to run: php, html, js and css.</li> </ul>" ), array( "template"=>"SampleCss.html", "title"=>"The code generation", "comment"=>" The first time the template is parsed, its contents, and the contents of all the recursively included widgets, must be parsed and combined.This <br> may be slow with the current code.<br> Once the template has been fully parsed, the resulting php code is stored in a file under the (PROJECTPATH)cache folder. The cache has a separate folder for each language, so the same template rendered for different languages are saved in different cache files.<br> Also, the widgets used while parsing the template, are stored in a dependencies file.<br> The next time the template is rendered, it'll check the dependencies file, and see if any widget or the template is more recent than the cached php file.<br><br> If any of the widgets, or the template, changed since the cache file was created, the template will be reparsed.<br><br> In other case, it'll be simply included.<br><br> That's why , when the language files are edited, the template cache must be cleared: the language files, in this version, are not part of the template dependencies.<br> The same happens with widgets that cant be found.If, when a template is rendered, a certain widget is not found, the current version doesnt include it as a dependency.So, adding it later, will not cause the template to be regenerated.<br> Simply \"touching\" (re-saving) the template will also force a full re-parse.<br><br> " ), array( "template"=>"SampleCss.html", "title"=>"Debugging widgets", "comment"=>"There are two kind of problems you can run into: <ul> <li>Parse-Time errors (problem with the widget syntax)</li> <li>Run-Time errors (problems with php code inside templates or widgets)</li> </ul> The Siviglia templating system can't,in its current version, give much information on Parse-Time errors: It will just print out the offending widget or template, but will not indicate in which line it has found the error.<br> Future versions will be able to give this information<br>. <br> As the parsing of templates and widgets generate a single php file, errors found in the \"cache\" php file should be mapped to a certain widget/template file and linenumber.Currently, the parser is unable to find this information, but there are plans to support it in future versions." ), array( "template"=>"SampleCss.html", "title"=>"About", "comment"=>"You can follow the development at the <a href=\"http://code.google.com/p/siviglia-templates/\">Siviglia template engine for php</a> page in Google code.<br><br> Also, new insights and ideas about how to use this templating system may appear in my <a href=\"http://xphperiments.blogspot.com\">php blog</a>.<br><br> For any comments, please use these pages to contact me, or email me at dashiad - at - hotmail.com." ) ); define(PROJECTPATH,getcwd()); echo realpath(getcwd()."/templating"); define(SIVIGLIA_PATH,realpath(getcwd()."/templating")."/"); include_once(SIVIGLIA_PATH."TemplateParser2.php"); include_once(SIVIGLIA_PATH."TemplateHTMLParser.php"); $oLParser=new CLayoutHTMLParserManager(); $widgetPath=array("/tests/widgets/"); $oManager=new CLayoutManager("html",$widgetPath,array("L"=>array("lang"=>"en"))); for($k=0;$k<count($tests);$k++) { $current=$tests[$k]; $source='<div class="firstRow">'. '<div class="template"><p>Template code</p>'. '<div class="templateCode"><pre>'. boldify(htmlentities(file_get_contents(PROJECTPATH."/tests/templates/".$current["template"]))). '</pre></div></div><div style="clear:both"></div><br>'; echo '<div class="example"><h1>'.$current["title"].'</h1>'; $definition=array("TEMPLATE"=>PROJECTPATH."/tests/templates/".$current["template"]); $result='<div style="clear:both></div><div class="result"><p>Result</p><div class="resultCode">'; ob_start(); $oManager->renderLayout($definition,$oLParser,true). $buf=ob_get_clean(); $result.=$buf.'</div></div><div style="clear:both"></div><br>'; $comment=str_replace("@@result@@",$result,$current["comment"]); $comment=str_replace("**".str_replace(".html","",$current["template"])."**",$source,$comment); $matches=array(); preg_match_all("/\#\#([^\#]*)\#\#/",$comment,$matches); foreach($matches[1] as $value) { $replacement='<div class="widget"><p>'.$value.' (File: widgets/'.$value.'.wid)</p>'. '<div class="widgetCode"><pre>'. boldify(htmlentities(file_get_contents(PROJECTPATH."/tests/widgets/".$value.".wid"))). '</pre></div></div>'; $comment=str_replace("##".$value."##",$replacement,$comment); } echo '<div class="comment">'.$comment.'</div>'; echo '</div></div>'; } ?> </body> </html>