Template engine simple et puissant. extention de fonctions par plugins (fdml parser).

Description

      • Français: ***

Si vous cherchez un template engine en PHP, voici votre solution!

Un template engine, leger, flexible, mais tres puissant.
Pas de code php dans vos template, mais des tags html avec un namespace <fdml:tagname>.

Des fonctions sont intégrées au template engine: variables (sous la forme %var% coté template), boucles, sections (mettez plusieurs templates dans un fichier, et selectionnez quel partie du fichier vous souhaitez utiliser via useSection() ), affichage conditionnel (utilisez show() pour afficher ou pas une portion de code), ...

Mais l'intérêt de ce template engine, est la flexibilité: Vous pouvez creer vos propres tags sous forme de plugin; Une simple fonction php sous la forme codeparser_tagname().
Le contenu du tag sera remplacé par ce que cette fonction retourne. Pas besoin de declarer la fonction, ou de faire un include. PLacez le fichier qui la contient dans le repertoire /plugins/ (modifiable en runtime) et le parser va la trouver et la charger pour vous.

Le fichier zip contient une série d'exemples divers, simple et complexes.
      • English: ***


If you are searching for a new template parser for your PHP projects, here is your solution!

FDML stands for Flexible Dynamic Markup Language.

Instead of having nasty php code in your templates, or weird template tags (like smarty's tags for example), the FDML language is based on HTML. That mean you will have tags like <fdml:tagname arg="value" /> in your template.

To control your template from your php code, you have a few functions like: Loop() -> Loop the portion of code located between the <fdml:loop name=""> </fdml:loop> tags. Show() -> Display the portion of code located between the <fdml:show name=""> </fdml:show> tags. If the function is not called, the portion of code will not be displayed. UseSection?() -> Use only a section of the template as the main template. Allow you to put many templates in a single file. AddVar?() -> allow you to define and replace a variable in the template. Variable name are enclosed in % sign in the template. Example: %variable% will be replaced by addVar("variable","value");

But this parser is more interesting than other solution because it allows you to create your own tags; If you need for example a new function in your template, let say, you want a piece of code to be displayed only is the current user is an admin. Instead of putting in your template a lot of code based on conditions, simply create the <fdml:is_admin> tag! When the parser will find this unknown tag, it will automatically search for a function named fdml_is_admin() in the /plugins/ directory, and simply replace the content of the tag by the return of this function. The content of the tag and its attributes will be send to the function as an array.

Oh, and did I mentioned the parser is composed of just one single file with no dependency?

Source / Exemple :


<?php
/*

  • @Created: v2 - 15 avr. 07 - 14:25:10
  • v3 - 20 janv. 09 - 13:44:00
  • @author: blackwizzard <blackwizzard@gmail.com>
  • @project: FDML - Flexible Dynamic Markup Language
  • @filename: fdml.parser.php
  • /
class codeparser { // declarations var $_instance; var $_vars; // Constructor function codeparser($file, $pluginDir=null) { /** vars **/ $this->_vars = array(); $this->_vars["buffer"] = ""; $this->_vars["log"] = array(); $this->_vars["log"]["file"] = $file; $this->_vars["log"]["tags"] = array(); $this->_vars["vars"] = array(); $this->_vars["functionbuffer"] = array(); $this->_vars["namespace"] = "fdml"; // you can change the namespace here $this->pluginfolder = "plugins/"; // You can change the plugin directory here if ($pluginDir!=null) $this->pluginfolder = $pluginDir; // functions buffers $this->_vars["functionbuffer"]["loop"] = array(); $this->_vars["functionbuffer"]["loopbuffer"] = array(); $this->_vars["functionbuffer"]["onemptyloop"] = array(); $this->_vars["functionbuffer"]["onemptyloopbuffer"] = array(); $this->_vars["functionbuffer"]["show"] = array(); $this->_vars["functionbuffer"]["showbuffer"] = array(); $this->_vars["functionbuffer"]["section"] = array(); $this->_vars["functionbuffer"]["sectionbuffer"] = array(); $this->_vars["functionbuffer"]["savedloop"] = array(); $this->_vars["functionbuffer"]["savedloopbuffer"] = array(); /** read the template file **/ $this->_vars["buffer"] = $this->freadFile($file); /** plugin loader **/ $this->loadPlugins("preparser"); $this->loadPlugins("dynamics"); $this->_vars["buffer"] = $this->searchtags($this->_vars["buffer"]); } /**
  • Output the parsed template
  • @return Template buffer
  • /
function output() { $this->applyLoops(); return $this->_vars["buffer"]; } function applyLoops() { // apply loops foreach ($this->_vars["functionbuffer"]["loop"] as $loopName=>$loopOutput) { if ($loopOutput == "") { $loopOutput = $this->_vars["functionbuffer"]["onemptyloopbuffer"][$loopName]; } // required... $loopOutput = $this->searchtags($loopOutput); $this->_vars["buffer"] = str_replace("<loop_$loopName/>",$loopOutput,$this->_vars["buffer"]); } } function applySavedTags() { $this->applyLoops(); $regex = '#<savedloop id\=[\"]([\w-]+)[\"] name\=[\"]([\w-]+)[\"] \/>#mis'; while(preg_match($regex, $this->_vars["buffer"], $match)) { $loop_id=$match[1]; $loop_name=$match[2]; $this->_vars["buffer"] = preg_replace("#{$match[0]}#", "<".$this->_vars["namespace"].":loop name=\"$loop_name\">".$this->_vars["functionbuffer"]["savedloopbuffer"][$loop_id]."</".$this->_vars["namespace"].":loop>", $this->_vars["buffer"]); } $this->parseAgain(); } function parseAgain() { $this->_vars["buffer"] = $this->searchtags($this->_vars["buffer"]); } /**
  • Search and parse all FDML tags
  • @return pre-parsed template
  • /
function searchtags($in) { // match all tags in the form <type:name args>content</type:name> and singleton <type:name args /> // old versions: //$reg = "#<([a-z]+):(.*?)\s+(.*?)\s*(>(.*?)</\\1:\\2>|/>)#mis"; // global regex to match all tags with all namespaces. //$reg = "#<".$this->_vars["namespace"].":(.*?)\s+(.*?)\s*(>(.*?)</".$this->_vars["namespace"].":\\1>|/>)#mis"; //$reg = "#<".$this->_vars["namespace"].":(.*?)\s+(.*?)\s*(>(.*?)</".$this->_vars["namespace"].":\\1>|/>)#mis"; $reg = "#<".$this->_vars["namespace"].":([a-zA-Z0-9_-]+)\s*((?:(?!>).)*)\s*(>((?:(?!".$this->_vars["namespace"].":\\1).)*)</".$this->_vars["namespace"].":\\1>|/>)#mis"; if (preg_match($reg, $in)) { $in = preg_replace_callback($reg,array($this,"tagparser"),$in,-1); return $this->searchtags($in); //return $in; } else { return $in; } } /**
  • Parse a single tag
  • @return the parsed tag
  • /
function tagparser($array) { $query = $array[0]; //$tagType = $array[1]; $tagName = $array[1]; $tagArgs = $array[2]; $tagContent = $array[4]; $argsArray = array(); $tagArgs = str_replace("\\\"","QUOTE",$tagArgs); // attribute parser preg_match_all("#([a-z1-9]+)\s*=(([a-z0-9_-]+)|[\"?]([^\"]*)[\"?])#mis",$tagArgs,$args); foreach ($args[1] as $id=>$var) { $argsArray[$var] = $this->correctValue($args[2][$id]); } array_push($this->_vars["log"]["tags"],array( "Tag name"=>$tagName, "String args"=>$tagArgs, "Parsed args"=>$argsArray, "Complete tag"=>$query, "Content"=>$tagContent )); switch (strtolower($this->superTrim($tagName))) { case "loop": // a loop... if ($argsArray["waitforparsing"] == "true") { // put the content in the function buffer $this->_vars["functionbuffer"]["savedloopbuffer"][$argsArray["id"]] = $tagContent; // saved loop buffer using the loop ID. The localization loop name is saved in the tag itself. // replace by a localization tag return "<savedloop id=\"".$argsArray["id"]."\" name=\"".$argsArray["name"]."\" />"; } else { // put the content in the function buffer $this->_vars["functionbuffer"]["loop"][$argsArray["name"]] = ""; // output of the loop is empty $this->_vars["functionbuffer"]["loopbuffer"][$argsArray["name"]] = $tagContent; // loop buffer // replace by a localization tag return "<loop_".$argsArray["name"]."/>"; } break; case "onemptyloop": // emptyloop... $tagContent = $this->searchtags($tagContent); // put the content in the function buffer $this->_vars["functionbuffer"]["onemptyloop"][$argsArray["name"]] = ""; // output of the onemptyloop is empty $this->_vars["functionbuffer"]["onemptyloopbuffer"][$argsArray["name"]] = $tagContent; // onemptyloop buffer // strip the tag return ""; break; case "section": // a section tag... $tagContent = $this->searchtags($tagContent); // put the content in the function buffer $this->_vars["functionbuffer"]["section"][$argsArray["name"]] = ""; // output of the section is empty $this->_vars["functionbuffer"]["sectionbuffer"][$argsArray["name"]] = $tagContent; // section buffer // replace by a localization tag return "<section_".$argsArray["name"]."/>"; break; case "show": // a show tag... $tagContent = $this->searchtags($tagContent); // put the content in the function buffer $this->_vars["functionbuffer"]["show"][$argsArray["name"]] = ""; // output of the section is empty $this->_vars["functionbuffer"]["showbuffer"][$argsArray["name"]] = $tagContent; // section buffer // replace by a localization tag return "<show_".$argsArray["name"]."/>"; break; default: // Unknown type. Probably a plugin. Else, strip the tag. if (function_exists("codeparser_$tagName")) { $function = "codeparser_$tagName"; $return = $function($tagContent, $argsArray); $return = $this->searchtags($return); return $return; } else { $tagContent = $this->searchtags($tagContent); return $tagContent; } break; } } function parseAndApply($option=null) { $functions = get_defined_functions(); $functions = $functions["user"]; switch ($option) { case "before": foreach ($functions as $function) { if (substr($function,0,strlen("preparser_")) == "preparser_") { $func = $function; $this->_vars["buffer"] = $func($this->_vars["buffer"], $this); } } break; case "after": foreach ($functions as $function) { if (substr($function,0,strlen("postparser_")) == "postparser_") { $func = $function; $this->_vars["buffer"] = $func($this->_vars["buffer"], $this); } } break; } } /**
  • Register a template variable
  • @param String the variable name without the variable identifier (for variable "%var%", just "var")
  • String The value of the variable
  • @return nothing
  • /
function addVar($label, $value) { $this->_vars["vars"][$label] = $value; $this->_vars["buffer"] = str_replace("%".$label."%",$value,$this->_vars["buffer"]); } /**
  • Register more than one template variable at a time, in an array
  • @param Array an array of variables type array("var1"=>"value1","var2"=>"value2")
  • @return nothing
  • /
function addVars($array) { foreach ($array as $varLabel=>$varValue) { $this->addVar($varLabel, $varValue); } } /**
  • Loop a code section
  • @param String the loop name as defined in the template
  • Array an array of variables type array("var1"=>"value1","var2"=>"value2")
  • @return Template buffer
  • /
function loop($name, $array) { global $_PARSER; $buffer = $this->_vars["functionbuffer"]["loopbuffer"][$name]; foreach ($array as $label=>$value) { $buffer = str_replace("%".$label."%",$value,$buffer); } //$this->debug($name, $buffer); //$buffer = $this->searchtags($buffer); $this->_vars["functionbuffer"]["loop"][$name] .= $buffer; } /**
  • Function used to handle the <fn:section> tag. Any call to this function will replace the current buffer with the content of the <fn:section> tag.
  • @param String the name of the section to load, defined on the tag as name="[name]"
  • @return true
  • /
function useSection($sectionName) { global $_PARSER; $tmpBuffer = $this->_vars["functionbuffer"]["sectionbuffer"][$sectionName]; $this->_vars["buffer"] = $tmpBuffer; $this->parseAndApply(); return true; } /**
  • Function used to handle the <fn:show> tag. It will result as the display of the content of the tag.
  • If a tag is not called, it will be deleted before any output.
  • @param String the name of the <fn:show> tag to load, defined on the tag as name="[name]"
  • @return true
  • /
function show($name) { global $_PARSER; $tmpBuffer = $this->_vars["functionbuffer"]["showbuffer"][$name]; $this->_vars["buffer"] = str_replace("<show_$name/>",$tmpBuffer,$this->_vars["buffer"]); $this->parseAndApply(); return true; } function loadPlugins($type) { $pluginfolder=$this->pluginfolder; if ($handle = opendir($pluginfolder.$type)) { while (false !== ($file = readdir($handle))) { if ($file != "." && $file != "..") { if (!is_dir($pluginfolder.$type."/".$file) && strtolower($this->STRING_get_file_ext($file))=="php") { @include_once($pluginfolder.$type."/".$file); } } } closedir($handle); } $functions = get_defined_functions(); $functions = $functions["user"]; switch ($type) { case "preparser": foreach ($functions as $function) { if (substr($function,0,strlen("preparser_")) == "preparser_") { $func = $function; $this->_vars["buffer"] = $func($this->_vars["buffer"], $this); } } break; case "postparser": foreach ($functions as $function) { if (substr($function,0,strlen("postparser_")) == "postparser_") { $func = $function; $this->_vars["buffer"] = $func($this->_vars["buffer"], $this); } } break; } } function correctValue($value) { $firstChar = substr($value,0,1); $lastChar = substr($value,strlen($value)-1,1); if ($firstChar == "\"" && $lastChar == "\"") { // remove quotes $value = substr($value,1,strlen($value)-2); } $value = str_replace("QUOTE","\"",$value); $value = str_replace("\\>",">",$value); return $value; } function freadFile($file) { if (!$handle = fopen ($file, "r")) { exit; } $contents = fread ($handle, filesize($file)+1); fclose($handle); return $contents; } function STRING_get_file_ext($filename) { if (strrpos($filename, '.') >= 1) { return strtolower(substr($filename, strrpos($filename, '.') + 1)); } else { return ""; } } function debug($label, $value) { if (is_array($value)) { echo "<b>$label</b> :: <div style=\"border:1px dashed #000000;margin-left:20px;\">".nl2br(str_replace(" ","&nbsp;",str_replace("<","<",print_r($value,true))))."</div><br>"; } else { echo "<b>$label</b> :: <div style=\"border:1px dashed #000000;margin-left:20px;\">".nl2br(str_replace(" ","&nbsp;",str_replace("\t"," ",$value)))."</div><br>"; } } function tagLog() { $this->debug("Tag Log",$this->_vars["log"]["tags"]); } function superTrim($in) { /** remove extra spaces, line breaks, tabs **/ $in = str_replace("\t"," ",$in); $in = str_replace("\r"," ",$in); $in = preg_replace('/\s\s+/', ' ', trim($in)); return $in; } function encodeAsArg($in) { $in = str_replace("\"","\\\"",$in); $in = str_replace("}","\\}",$in); return $in; } } ?>

Conclusion :


Updates et code: http://code.google.com/p/fdml-php-template/

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.