Skip to content

Instantly share code, notes, and snippets.

@ducktype
Created June 20, 2015 16:01
Show Gist options
  • Save ducktype/f05b4feb4501cbbf7a36 to your computer and use it in GitHub Desktop.
Save ducktype/f05b4feb4501cbbf7a36 to your computer and use it in GitHub Desktop.
My own PHP native template engine in ~90 lines of code!
<?php
/*
----------------------------------------------
TEMPLATE EXAMPLES:
----------------------------------------------
----------------------------------------
htmlpage_base2.ptpl
----------------------------------------
BASE2
<? $block('main_content') ?>
BASE2 main_content BLOCK
<? $endblock() ?>
----------------------------------------
htmlpage_base.ptpl
----------------------------------------
<? $extends('htmlpage_base2.ptpl') ?>
<nav>
NAV HEAD
</nav>
<? $block('main_content') ?>
DEFAULT <?=$model['a']?> TEST
<? $endblock() ?>
<hr>
FOOTER
----------------------------------------
index.ptpl
----------------------------------------
<? $extends('htmlpage_base.ptpl') ?>
BASE1 HEAD
<? $block('main_content') ?>
<? $block(null,['spaceless']) ?>
PRE
<main>
INCLUDE WITH DIFFERENT MODEL:
<?=$include('htmlpage_base.ptpl',['a'=>'NX'])?>
</main>
<? $block('main_content3') ?>
<span>
B1
</span>
<span>
B2
</span>
<? $endblock() ?>
POST
<? $endblock() ?>
<? $endblock() ?>
<?=$model['a']?>
BASE1 FOOTER
----------------------------------------
email_notify.ptpl
----------------------------------------
<? $block('subject') ?>
Subject with model data <?=$model[user][name]?> <?=$model[user][surname]?>!
<? $endblock() ?>
<? $block('body_html') ?>
MESSAGE BODY HTML
<?=print_r($model,true)?>
<? $endblock() ?>
<? $block('body_text') ?>
MESSAGE BODY TEXT
<? $endblock() ?>
----------------------------------------------
USAGE:
----------------------------------------------
$ptpl = new \PTpl("/template/base_path",array(
'global_variable1' => 'global_value1',
'global_variable2' => 'global_value2',
));
$tpl_globals = array('model'=>array(
'model_value1' => 'value1',
'model_value2' => 'value2',
));
$tpl_output_string = $ptpl->render("index.ptpl",$tpl_globals);
$tpl_data = $ptpl->render("index.ptpl",$tpl_globals,true);
----------------------------------------------
OUTPUT FILTERS:
----------------------------------------------
THERE IS A ALSO AN OUTPUT FILTER MECHANISM:
by default the block output is trimmed and an additional 'spaceless' filter is available
you can pass an array of filter names to the second argument of $block()
also note that the $block() first argument (the block name) is optional for example you can do:
<? $block(null,['spaceless']) ?>
for filter definitions and apply global filters see:
$ptpl->output_filters_def
and
$ptpl->->global_output_filters
*/
class PTpl {
public $bp = null;
public $global_extract_model;
public $output_filters_def;
public $global_output_filters;
function __construct($bp,$global_extract_model=null) {
$this->bp = $bp;
$this->global_extract_model = $global_extract_model?:array();
$this->output_filters_def = array(
'trim' => 'trim',
'spaceless' => array($this,'spacelessOutputFilter')
);
$this->global_output_filters = array(
'trim',
);
}
public function spacelessOutputFilter($input,$block_info) {
return preg_replace('#>\s+<#','><',$input);
}
function render($path,$extract_model=array()) {
$extract_model = array_merge($this->global_extract_model,$extract_model);
extract($extract_model);
$self = $this;
$str_from = array('\\','/');
$str_to = array(DIRECTORY_SEPARATOR,DIRECTORY_SEPARATOR);
$tpl_basepath = $this->bp;
$tpl_basepath = trim($tpl_basepath,'\\/');
$tpl_extends = null;
$block_stack = array();
$blocks = array();
$extends = function($tpl_path) use(&$tpl_extends) {
$tpl_extends = $tpl_path;
};
$block = function() use(&$block_stack) {
$block_args = func_get_args();
array_push($block_stack,$block_args);
ob_start();
};
$endblock = function() use(&$blocks,&$block_stack,$self) {
$block_output = ob_get_clean();
$block_args = array_pop($block_stack);
$block_name = $block_args[0];
$output_filters = $block_args[1];
$output_filters = array_merge($self->global_output_filters?:array(),
$output_filters?:array());
foreach ($output_filters as $of_name) {
$of_callable = $self->output_filters_def[$of_name];
if(!$of_callable)
continue;
$block_output = call_user_func_array($of_callable,array($block_output,$block_args));
}
if(!$block_name) {
echo $block_output;
return;
} else {
if(!array_key_exists($block_name,$blocks) || $block_name=='_output')
$blocks[$block_name] = $block_output;
echo $blocks[$block_name];
}
};
$include = function($tpl_path,&$inc_model=null) use($self,&$extract_model) {
$tpl_data = $self->render($tpl_path,$inc_model?:$extract_model);
return $tpl_data['_output'];
};
$prev_level = ob_get_level();
ob_start();
try {
$curr_tpl = $path;
while($curr_tpl) {
$tpl_extends = null;
$tpl_full_path = $tpl_basepath.DIRECTORY_SEPARATOR.$curr_tpl;
$tpl_full_path = str_replace($str_from,$str_to,$tpl_full_path);
$block('_output');
include $tpl_full_path;
$endblock('_output');
$curr_tpl = $tpl_extends?:null;
}
} catch(\Exception $ex) {
$blocks['_error'] = $ex;
}
while(ob_get_level()!=$prev_level) {
ob_end_clean();
}
return $blocks;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment