Added readme and code

This commit is contained in:
2013-07-27 21:02:21 +01:00
parent 6b2d0fa18b
commit 91868a39d2
5 changed files with 256 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
{% $context['title'] = 'Hello World -- Demo' %}
{%t SetMaster('master') %}
{%t StartSection('content') %}
<!-- some head here -->
{%t EndSection() %}
{%t StartSection('content') %}
Hello {{$world}}
{%t EndSection() %}

10
DemoTemplates/master.php Normal file
View File

@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<title>{{$title}}</title>
{{t GetSection('head') }}
</head>
<body>
{{t GetSection('content') }}
</body>
</html>

View File

@@ -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 }} --> <?php echo $hello ;?>
* {% if(true): %}True!{% endif %} --> <?php if(true): ;?>True!<?php endif ;?>
* __Templater Shortcuts__
* {{t Render('menu') }} --> <?php echo $this->Render('menu') ;?>
* {%t StartSection('content') %} --> <?php $this->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`:
<!doctype html>
<html>
<head>
<title>{{$title}}</title>
</head>
<body>
{{t GetSection('content') }}
</body>
</html>
`$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.

136
Template.php Normal file
View File

@@ -0,0 +1,136 @@
<?php
/**
* Template renderer
*
* @author Sam Stevens <sam@xnet.tk>
* @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, '<?php if(!defined("DIR_TEMPLATES")) {echo "This file cannot be accessed directly";exit();} ?>'.$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('<?php echo $this->$1 ;?>', '<?php echo $1 ;?>', '<?php $this->$1 ;?>', '<?php $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 '';
}
}

18
demo.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
ini_set('display_errors', True);
error_reporting(E_ALL);
date_default_timezone_set('Europe/London');
define('DIR_TEMPLATES', dirname(__FILE__).DIRECTORY_SEPARATOR.'DemoTemplates');
define('DIR_TEMPLATES_COMPILED', false);
require('Template.php');
$t = Template::GetInstance();
$data = array(
'world' => 'You!'
);
echo $t->Render('helloworld',$data);