[PHP5] LambdaTemplates : Gestion de templates

Eléphant du PHP | 199 Messages

27 mai 2007, 14:14

Voici un ensemble de 3 classes permettant de gérer un système de templates pour ainsi séparer le PHP du HTML.

Le set est composé de :
  • une classe basique qui permet uniquement l'inclusion du fichier template et le remplacement des variables-utilisateur
  • une classe étendue qui ajoute la mise en cache via la classe LambdaCache
  • une dernière classe qui cette fois ne supporte pas le PHP dans le fichier template mais un langage particulier (que je n'ai pas encore documenté mais qui est assez facilement identifiable en observant les expressions régulières utilisées pour le parsing)

Exemple d'utilisation :
//First, the basic templates class
	$template = new LambdaTemplates();
	$template -> firstname = 'Rasmus';
	$template -> surname = 'Lerdorf';
	$template -> call('basic.tpl');
	
	//Now, the economic templates class, with a cache system
	$ecotemplate = new EcoLambdaTemplates();
	$ecotemplate -> firstname = 'Rasmus';
	$ecotemplate -> surname = 'Lerdorf';
	$ecotemplate -> call('basic.tpl');
	
	//To finish, the extended templates class, with the cache system and a new template language
	$extratemplate = new ExtraLambdaTemplates();
	$extratemplate -> title = 'ExtraLambdaTemplates';
	$extratemplate -> people = array ('Rasmus' => 'Lerdorf', 'Zeev' => 'Suraski');
	$extratemplate -> call('extra.tpl');
?>
basic.tpl :
<html>
	<head>
		<title>Basic Template</title>
	</head>
	
	<body>
		<p>
			<?php
				print $firstname.' '.$surname;
			?>
		</p>
	</body>
</html>
extra.tpl :

Code : Tout sélectionner

<html> <head> <title>{tpl: title}</title> </head> <body> <p> <for i 1 to 4> <foreach people> {iterators: i} {key} {value}<br /> </foreach> </for> </p> </body> </html>
Résultat :
Rasmus Lerdorf

Rasmus Lerdorf

1 Rasmus Lerdorf
1 Zeev Suraski
2 Rasmus Lerdorf
2 Zeev Suraski
3 Rasmus Lerdorf
3 Zeev Suraski
4 Rasmus Lerdorf
4 Zeev Suraski

Code source de la classe :
<?php
	/*
		Lambda System - by Klomac ([email protected])
		
		LambdaTemplates : These classes allows the user to parse templates files.
		
		-----------------------------GNU GPL--------------------------------------
		This program is free software; you can redistribute it and/or modify
		it under the terms of the GNU General Public License as published by
		the Free Software Foundation; either version 2 of the License, or
		(at your option) any later version.

		This program is distributed in the hope that it will be useful, but 
		WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
		or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
		for more details.

		You should have received a copy of the GNU General Public License along 
		with this program; if not, write to the Free Software Foundation, Inc., 
		51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
		---------------------------------------------------------------------------
	*/
	
	//This set of classes require the LambdaCache class to work
	require_once('LambdaCache.class.php');
	
	class LambdaTemplates
	{
		private $userVars = array(); //The user-variables array
		private $tplDir = 'templates/'; //The directory which contains the templates files
		
		//The errors which may be displayed
		private $errors = array(
			'This directory doesn\'t exist',
			'Unable to open the template file');
		
		
		//Magical methods
		public function __construct () {}
		
		public function __set ($name, $value) //This method is called when the user try to set a variable
		{
			$this -> userVars[$name] = $value;
		}
		
		public function __get ($name) //Accessor
		{
			return $this -> userVars[$name];
		}
		
		
		//Protected methods
		protected function returnTpl ($start, $print) //This method is used when trying to call a template file but without printing it. It starts a buffer at start, and return its contents at end
		{
			if ($start AND !$print) ob_start();
			elseif (!$start AND !$print) return ob_get_clean();
		}
		
		protected function includeTpl ($file) //This method associates the user-variables and include the template file
		{
			foreach ($this -> userVars AS $key => $value)
			{
				${$key} = $value;
			}
			
			if (is_readable($this -> tplDir.$file)) include $this -> tplDir.$file;
			else die($this -> errors[1]);
		}
		
		
		//Public methods
		public function setTplDir ($dir) //This method change the template directory after checking if it exists
		{
			if (is_dir($dir)) $this -> tplDir = $dir;
			else die ($this -> errors[0]);
		}
		
		public function call ($file, $print = TRUE) //This method include the template file and print (or not) its contents
		{
			$this -> returnTpl(TRUE, $print);
			$this -> includeTpl($file);
			return $this -> returnTpl(FALSE,$print);
		}
	}
	
	
	class EcoLambdaTemplates extends LambdaTemplates
	{
		public $cacheDir = 'cache/templates/'; //The directory which contains the cache templates files
		
		//The errors which may be displayed
		private $errors = array(
			'This directory doesn\'t exist',
			'Unable to open the template file',
			'Unable to create the cache directory');
		
		
		//Magical methods
		public function __construct () {}
		
		
		//Protected methods
		protected function loadCache ($file, $compression) //This method instanciate a new cache object and return it
		{
			$cache = new LambdaCache($file, $this -> cacheDir, $compression);
			return $cache;
		}
		
		
		//Public methods
		public function call ($file, $print = TRUE, $compression = TRUE) //This method is the same as the parent's one but with cache support
		{
			$this -> returnTpl(TRUE, $print);
			$cache = $this -> loadCache($file, $compression);
			
			if (!$cache -> show())
			{
				$this -> includeTpl($file);
				$cache -> close();
			}
			
			return $this -> returnTpl(FALSE, $print);
		}
	}
	
	class ExtraLambdaTemplates extends EcoLambdaTemplates
	{
		private $useCache; //Indicate if the cache system is used or not
		private $parsingTime = array(); //This array contains the parsing time of each called template
		
		//The errors which may be displayed
		private $errors = array(
			'This directory doesn\'t exist',
			'Unable to open the template file',
			'Unable to create the cache directory',
			'Unable to include a file',
			'Syntax error with the conditions',
			'Syntax error with the loops');
		
		
		//Magical methods
		public function __construct ($useCache = TRUE) //The constructor just set if the cache system is used or not
		{
			$this -> useCache = $useCache === TRUE ? TRUE : FALSE;
		}
		
		
		//Static methods
		private function solveIncludes ($code, $localPath = '') //This method solves the include instructions in the template file (with recursivity)
		{
			$firstSearch = '#<include src="(.+)">#sU';
			
			if (preg_match_all($firstSearch, $code, $includes, PREG_SET_ORDER))
			{
				foreach ($includes AS $file)
				{
					if (file_exists($localPath.$file[1]) AND is_readable($localPath.$file[1]))
					{
						$dirs = explode(DIRECTORY_SEPARATOR, $file[1]);
						array_pop($dirs);
						$dirs = implode(DIRECTORY_SEPARATOR, $dirs);
						if ($dirs) $dirs .= DIRECTORY_SEPARATOR;
						
						$includeContent = file_get_contents($localPath.$file[1]);
						$includeContent = $this -> solveIncludes($includeContent, $localPath.$dirs);
						
						$secondSearch = '#<include src="'.$file[1].'">#';
						$code = preg_replace($secondSearch, $includeContent, $code);
					}
					else die ($this -> errors[3]);
				}
			}
			
			return $code;
		}
		
		private function parseCode ($code) //This method parse the template code to PHP code
		{
			$code = $this -> solveIncludes($code, $this -> tplDir);
			$code = '$contents .= \''.str_replace('\'', '\\\'', $code).'\';';
			
			//Parsing user variables
			$searches[0] = '#\{tpl: (.+)\}#U';
			$replaces[0] = '\'.\$this -> $1.\'';
			
			//Parsing conditions
			$searches[1] = '#<if \{(.+)\}(.+)>#U';
			$replaces[1] = '\'; if (\$this -> $1$2) { $contents .= \'';
			$searches[2] = '#</if>#U';
			$replaces[2] = '\'; } $contents .= \'';
			$searches[3] = '#<elseif \{(.+)\}(.+)>#U';
			$replaces[3] = '\'; } elseif (\$this -> $1$2) { $contents .= \'';
			$searches[4] = '#<else>#U';
			$replaces[4] = '\'; } else { $contents .= \'';
			
			//Parsing loops
			$searches[5] = '#\{(key|value)\}#U';
			$replaces[5] = '\'.\$$1.\'';
			$searches[6] = '#<foreach (.+)>#U';
			$replaces[6] = '\'; foreach (\$this -> $1 AS \$key => \$value) { $contents .= \'';
			$searches[7] = '#<for (.+) ([0-9]+) to ([0-9]+)>#U';
			$replaces[7] = '\'; for (\$$1 = $2 ; \$$1 <= $3 ; \$$1++) { $contents .= \'';
			$searches[8] = '#</(for|foreach)>#U';
			$replaces[8] = '\'; } $contents .= \'';
			$searches[9] = '#\{iterators: (.+)\}#U';
			$replaces[9] = '\'.\$$1.\'';
			
			//Checking the conditions and loops validity
			if (preg_match_all($searches[1], $code, $useless) != preg_match_all($searches[2], $code, $useless))
			{
				die ($this -> errors[4]);
			}
			elseif((preg_match_all($searches[6], $code, $useless) + preg_match_all($searches[7], $code, $useless)) != preg_match_all($searches[8], $code, $useless))
			{
				
				die ($this -> errors[5]);
			}
			
			//Return the result
			return preg_replace($searches, $replaces, $code);
		}
		
		
		//User methods
		public function parsingTime ($total = TRUE) //This method returns the parsing time (all or just the last)
		{
			if ($size = count($this -> parsingTime))
			{
				if ($total === TRUE)
				{
					$i = 0;
					foreach ($this -> parsingTime AS $parsingTime) $i += $parsingTime;
					
					return $i;
				}
				else return $this -> parsingTime[$size];
			}
			else return 'Nothing parsed yet';
		}
		
		public function call ($file, $print = TRUE, $compression = TRUE) //This method print the cache contents if it exists, or call and parse the template file if is doesn't
		{
			$microtime[0] = microtime(TRUE);
			
			$this -> returnTpl(TRUE, $print);
			
			if ($this -> useCache)
			{
				$cache = $this -> loadCache($file, $compression);
				$cacheExists = $cache -> show();
			}
			else $cacheExists = FALSE;
			
			if (!$cacheExists)
			{
				ob_start();
				$this -> includeTpl($file);
				eval($this -> parseCode(ob_get_clean()));
				print $contents;
				
				if ($this -> useCache) $cache -> close();
			}
			
			$microtime[1] = microtime(TRUE);
			array_push($this -> parsingTime, $microtime[1] - $microtime[0]);
			
			return $this -> returnTpl(FALSE, $print);
		}
	}
?>
Klomac - Blog Lambda