Last active
December 28, 2015 11:59
-
-
Save georgepsarakis/7497784 to your computer and use it in GitHub Desktop.
PHP Templating
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class T { | |
protected $Context = array(); | |
protected $Compiled = NULL; | |
public $Regex_Variable = '~\{\{\s+[a-z_0-9\|:\.]+\s+\}\}~imsu'; | |
public $Separator_Filter = '|'; | |
public $Separator_Directive = ':'; | |
protected $HTML_Encoder = NULL; | |
protected $HTML_ENCODE = TRUE; | |
protected $VALUE_CACHE = array(); | |
function __construct($template, $context) { | |
$this->Context = is_object($context) ? get_object_vars($context) : $context; | |
$this->Template = $template; | |
$this->set_html_encoder('html_encode_default'); | |
if ( is_array($this->Context) ) | |
array_walk_recursive($this->Context, array($this, 'array_context')); | |
} | |
protected function array_context(&$context, $key) { | |
if ( is_object($context) ) | |
$context = get_object_vars($context); | |
} | |
public function set_html_encoder($function) { | |
if ( function_exists($function) ) { | |
$this->HTML_Encoder = $function; | |
} elseif ( method_exists($this, $function) ){ | |
$this->HTML_Encoder = array($this, $function); | |
} else { | |
return FALSE; | |
} | |
return TRUE; | |
} | |
public function html_encode_default($html) { | |
return htmlspecialchars($html, ENT_COMPAT, 'UTF-8', FALSE); | |
} | |
protected function get_value($var){ | |
if ( array_key_exists($var, $this->VALUE_CACHE) ) | |
return $this->VALUE_CACHE[$var]; | |
$filters = array(); | |
if ( strpos($var, $this->Separator_Filter) !== FALSE ){ | |
$filters = explode($this->Separator_Filter, $var); | |
$var = array_shift($filters); | |
} | |
$context = $this->Context; | |
if ( is_array($context) ){ | |
$variable = $context; | |
foreach(explode('.', $var) as $p) { | |
if ( is_array($variable) && array_key_exists($p, $variable) ) { | |
$variable = $variable[$p]; | |
} else { | |
$variable = ""; | |
break; | |
} | |
} | |
} else { | |
$variable = $this->Context; | |
} | |
foreach($filters as $filter) | |
$variable = call_user_func($filter, $variable); | |
$value = $this->HTML_ENCODE ? call_user_func($this->HTML_Encoder, $variable) : $variable; | |
$this->VALUE_CACHE[$var] = $value; | |
return $value; | |
} | |
public function compile() { | |
$this->Compiled = preg_replace_callback( | |
$this->Regex_Variable, | |
array($this, 'replacer'), | |
$this->Template | |
); | |
} | |
public function get_variables() { | |
$matches = array(); | |
preg_match_all($this->Regex_Variable, $this->Template, $matches); | |
if ( sizeof($matches) >= 1 ){ | |
$matches = array_map(array($this, '_variable'), $matches[0]); | |
} | |
return $matches; | |
} | |
protected function _variable($v) { | |
return trim($v, '{ }'); | |
} | |
protected function replacer($match) { | |
$var = $this->_variable($match[0]); | |
return $this->get_value($var); | |
} | |
public function __toString() { | |
if ( is_null($this->Compiled) ) | |
$this->compile(); | |
return $this->Compiled; | |
} | |
} | |
$tmpl = "<div>Some <p>HTML</p> that contains {{ n }} variables and {{ var_1 }}.</div>\n"; | |
/* Passing an array as Context */ | |
$T = new T('Array Example: ' . $tmpl, array('n' => 2, 'var_1' => 'a <b>sample</b> string & some HTML')); | |
print_r($T->get_variables()); | |
/* the result is passed through htmlspecialchars and thus is HTML encoded */ | |
print $T; // or (string)$T | |
/* Passing a simple string. All variables will be replaced with this value. */ | |
$T = new T('String Example: ' . $tmpl, 'VARIABLE'); | |
print $T; | |
/* Passing Object Instance as Context */ | |
$context = new stdClass; | |
$context->n = 2; | |
$context->var_1 = 'a second sample <span class="highlight">string</span>'; | |
$T = new T('Object Example: ' . $tmpl, $context); | |
print $T; | |
/* Using filters */ | |
$tmpl = "<div>Some <p>HTML</p> that contains {{ n }} variables and {{ var_1|md5|strlen }}.</div>\n"; | |
$T = new T('Filter Example: ' . $tmpl, array('n' => 2, 'var_1' => 'a <b>sample</b> string & some HTML')); | |
print $T; | |
/* Nested arrays */ | |
$tmpl = "<div>Some <p>HTML</p> that contains {{ n.m }} variables and {{ a.b|strlen }}.</div>\n"; | |
$T = new T('Nested Arrays Example: ' . $tmpl, array('n' => array( 'm' => 2), 'a' => array('b' => 'a <b>sample</b> string & some HTML'))); | |
print $T; | |
/* | |
Simple benchmark. | |
Get a rough estimate of the required time to parse the template. | |
Probably a test with more variables should be included. | |
*/ | |
$tmpl = str_repeat("<div>Some <p>HTML</p> that contains {{ n }} variables and {{ var_1 }}.</div>\n", 100); | |
print sprintf("Template Size: %d\n", strlen($tmpl)); | |
$start = microtime(TRUE); | |
foreach(range(1, 1000) as $iteration){ | |
$T = new T($tmpl, array('n' => 2, 'var_1' => 'a second sample string')); | |
$T->compile(); | |
} | |
$benchmark = number_format(1000*(microtime(TRUE) - $start), 6); | |
print 'Simple Benchmark: ' . $benchmark . "μsec\n"; | |
// ~700μsec per template compilation |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment