From 91868a39d22c58f552b52fb8bf43869aa0b990a5 Mon Sep 17 00:00:00 2001 From: Sam Stevens Date: Sat, 27 Jul 2013 21:02:21 +0100 Subject: [PATCH] Added readme and code --- DemoTemplates/helloworld.php | 10 +++ DemoTemplates/master.php | 10 +++ README.md | 82 +++++++++++++++++++++ Template.php | 136 +++++++++++++++++++++++++++++++++++ demo.php | 18 +++++ 5 files changed, 256 insertions(+) create mode 100644 DemoTemplates/helloworld.php create mode 100644 DemoTemplates/master.php create mode 100644 Template.php create mode 100644 demo.php diff --git a/DemoTemplates/helloworld.php b/DemoTemplates/helloworld.php new file mode 100644 index 0000000..70d7dfb --- /dev/null +++ b/DemoTemplates/helloworld.php @@ -0,0 +1,10 @@ +{% $context['title'] = 'Hello World -- Demo' %} +{%t SetMaster('master') %} + +{%t StartSection('content') %} + +{%t EndSection() %} + +{%t StartSection('content') %} +Hello {{$world}} +{%t EndSection() %} \ No newline at end of file diff --git a/DemoTemplates/master.php b/DemoTemplates/master.php new file mode 100644 index 0000000..c7fec38 --- /dev/null +++ b/DemoTemplates/master.php @@ -0,0 +1,10 @@ + + + + {{$title}} + {{t GetSection('head') }} + + + {{t GetSection('content') }} + + \ No newline at end of file diff --git a/README.md b/README.md index db23d65..e56a154 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,85 @@ YAPTE ===== Yet Another PHP Template Engine + +Useful little (<150 lines) templater that doesn't get in the way. + +Supports parent-child/master templates with sections (think TWIG). + +PHP 5.3+ Recommended. + +Basic Usage +----------- + +The core of the templater is focused on some simple reg-ex replcements. As Below. + + * __Output / Logic__ + * {{ $hello }} --> + * {% if(true): %}True!{% endif %} --> True! + * __Templater Shortcuts__ + * {{t Render('menu') }} --> Render('menu') ;?> + * {%t StartSection('content') %} --> StartSection('content') ;?> + +The theme here is that double brackets output and bracket-percent performs actions/does logic. + +You are of course free to use normal php tags where you see fit, these are there to help but are not required. + +Compiled Templates +------------------ + +The templates can be compiled to skip the step of reg-exing them, the configuration is explained below. + +The compiled templates can be cleared at run-time by calling `Template::ClearCompiled()` + +Configuration +------------- + +The configuration is done via constants, as this is simpler than some sort of config system (except where one already exists). + + * DIR_TEMPLATES -- The template directory. + * DIR_TEMPLATES_COMPILED - The compiled templates directory. + If not defined or false compiled templates are not used. + This is not relative to the template directory. + +Both can be either absolute paths or relative based on what `file_get_contents` can find. + +Master Template Example +----------------------- + +Child Template `hello.php`: + + {%t SetMaster('master') %} + + {%t StartSection('content') %} + Hello {{$world}} + {%t EndSection() %} + +Any text/content outside the sections will be lost but executed (ie not output). + +When evaluated the master template will be rendered and the content of the sections will be available to output. + +Parent Template `master.php`: + + + + + {{$title}} + + + {{t GetSection('content') }} + + + +`$this->sections` may be used for the available sections. + +Context +------- + +The context is the data initially provided to the template engine and is available via `$this->context`; these variables are extracted as references for use. + +A new context is created for each recursive call (ie master template, menu, etc) which contains the 'upper' context. modification is one way a higher context may affect lower contexts but not vice versa. + +For instance, if a child template were to set the title in the current context this will be available in context when the master template is rendered. +IE. `{%t context['title'] = 'Hello!' }%`. The master can then use `{{$title}}` to display 'Hello!' + +Due to the nature of `extract` some variables cannot be used in render data, these include `context`, `this`, `file` among others. \ No newline at end of file diff --git a/Template.php b/Template.php new file mode 100644 index 0000000..d1f8f9e --- /dev/null +++ b/Template.php @@ -0,0 +1,136 @@ + + * @license http://opensource.org/licenses/MIT MIT License + */ + +if (!defined('DIR_TEMPLATES_COMPILED')) + define('DIR_TEMPLATES_COMPILED', False); + +class Template +{ + private static $currentTemplater = false; + /** + * @return Template + */ + public static function GetInstance() + { + if (self::$currentTemplater != false) + return self::$currentTemplater; + return new self(); + } + public static function ClearCompiled() + { + $files = glob(DIR_TEMPLATES_COMPILED.'/*.template.php'); + foreach($files as $file) + { + if (is_file($file)) + unlink($file); + } + } + + private $context = array(); + + private $level = 0; + + private $sections = array(); + private $currentSection = false; + private $master = false; + + private function __construct() { + } + + private function GetCompiledFilename($tFile) { + if (DIR_TEMPLATES_COMPILED == false) { + return false; + } + $tFileSafe = preg_replace(array('%^'.preg_quote(DIR_TEMPLATES).'/%','/[^a-z0-9_\.]/i'), array('','_'), $tFile); + return DIR_TEMPLATES_COMPILED.DS.$tFileSafe.'-'.md5($tFile).'.template.php'; + } + private function GetCompiled($tFile) { + $compiledFile = $this->GetCompiledFilename($tFile); + if ($compiledFile === false) { + return false; + } + if (file_exists($compiledFile) && filemtime($tFile) > filemtime($compiledFile)) { + unlink($compiledFile); + return False; + } + if (file_exists($compiledFile)) { + return $compiledFile; + } + } + private function SaveCompiled($tFile, $contents) { + $compiledFile = $this->GetCompiledFilename($tFile); + if ($compiledFile === false) { + return false; + } + file_put_contents($compiledFile, ''.$contents); + } + + public function Render($file, array $context = array()) { + self::$currentTemplater = $this; + + $tFile = str_replace('.', DS, $file); + $tFile = DIR_TEMPLATES.DS.$tFile.'.php'; + if (!is_file($tFile)) + throw new Exception("Unknown Template ".$file); + + if (isset($this->context[$this->level])) + $context = array_replace($this->context[$this->level], $context); + $this->level++; + $this->context[$this->level] = &$context; + + extract($this->context[$this->level], EXTR_REFS | EXTR_SKIP); + + $this->master = false; + + $compiledTemplate = $this->GetCompiled($tFile); + if ($compiledTemplate == false) { + $templateContents = file_get_contents($tFile); + $templateContents = preg_replace( + array('%{{t\s((?!}}).*?)\}}%', '%{{((?!}}).*?)}}%', '%{\%t\s((?!}}).*?)\%}%', '%{\%((?!}}).*?)\%}%'), + array('$1 ;?>', '', '$1 ;?>', ''), + $templateContents); + $this->SaveCompiled($tFile, $templateContents); + $result = eval('?>'.$templateContents); + } else { + ob_start(); + include($compiledTemplate); + $result = ob_get_clean(); + } + + self::$currentTemplater = false; + if ($this->currentSection != False) + $this->EndSection(); + + if ($this->master != false) { + return $this->Render($this->master); + } + + unset($this->context[$this->level]); + $this->level--; + + return $result; + } + + private function SetMaster($file) { + $this->master = $file; + } + private function StartSection($name) { + $this->currentSection = $name; + ob_start(); + } + private function EndSection() { + $this->sections[$this->currentSection] = ob_get_clean(); + $this->currentSection = false; + } + private function GetSection($name) { + if (isset($this->sections[$name])) + return $this->sections[$name]; + return ''; + } +} \ No newline at end of file diff --git a/demo.php b/demo.php new file mode 100644 index 0000000..0090044 --- /dev/null +++ b/demo.php @@ -0,0 +1,18 @@ + 'You!' +); + +echo $t->Render('helloworld',$data); \ No newline at end of file