368 lines
8.9 KiB
PHP
368 lines
8.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* The TplBlock class.
|
|
*
|
|
* @category Template
|
|
* @package TplBlock
|
|
* @author gnieark <gnieark@tinad.fr>
|
|
* @license GNU General Public License V3
|
|
* @link https://github.com/gnieark/tplBlock/
|
|
*/
|
|
class TplBlock
|
|
{
|
|
/**
|
|
* The string starting a block start.
|
|
*
|
|
* @var string
|
|
*/
|
|
const BLOCKSTARTSTART = '<!--\s+BEGIN\s+';
|
|
|
|
/**
|
|
* The string ending a block start.
|
|
*
|
|
* @var string
|
|
*/
|
|
const BLOCKSTARTEND = '\s+-->';
|
|
|
|
/**
|
|
* The string starting a block end.
|
|
*
|
|
* @var string
|
|
*/
|
|
const BLOCKENDSTART = '<!--\s+END\s+';
|
|
|
|
/**
|
|
* The string ending a block end.
|
|
*
|
|
* @var string
|
|
*/
|
|
const BLOCKENDEND = '\s+-->';
|
|
|
|
/**
|
|
* The string starting an enclosure.
|
|
*
|
|
* @var string
|
|
*/
|
|
const STARTENCLOSURE = '{{';
|
|
|
|
/**
|
|
* The string ending an enclosure.
|
|
*
|
|
* @var string
|
|
*/
|
|
const ENDENCLOSURE = '}}';
|
|
|
|
/**
|
|
* The name of the block.
|
|
*
|
|
* @var string
|
|
*/
|
|
public $name = '';
|
|
|
|
/**
|
|
* The array containing the variables used by TplBlock.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $vars = [];
|
|
|
|
/**
|
|
* The array containing the sub blocks.
|
|
*
|
|
* @var array
|
|
*/
|
|
private $subBlocs = [];
|
|
|
|
/**
|
|
* The regex recognizing that a block is unused.
|
|
*
|
|
* @var string
|
|
*/
|
|
private $unusedRegex = "";
|
|
|
|
/**
|
|
* Should we trim?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
private $trim = true;
|
|
|
|
/**
|
|
* Should we replace non set template vars by an empty string?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
private $replaceNonGivenVars = true;
|
|
|
|
/**
|
|
* Use strict mode?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
private $strictMode = true;
|
|
/**
|
|
* Initialize TplBlock
|
|
*
|
|
* The name can be empty only for the top one block.
|
|
*
|
|
* @param string $name The template name
|
|
*/
|
|
|
|
public function __construct($name = "")
|
|
{
|
|
// Checks that name is valid.
|
|
if ($name !== "" and ! ctype_alnum($name)) {
|
|
throw new \UnexpectedValueException(
|
|
"Only alpha-numerics chars are allowed on the block name"
|
|
);
|
|
}
|
|
|
|
$this->name = $name;
|
|
|
|
// Build the unused regex.
|
|
$this->unusedRegex = '/'
|
|
. self::BLOCKSTARTSTART
|
|
. ' *([a-z][a-z0-9.]*) *'
|
|
. self::BLOCKSTARTEND
|
|
. '(.*?)'
|
|
. self::BLOCKENDSTART
|
|
. ' *\1 *'
|
|
. self::BLOCKENDEND
|
|
. '/is'
|
|
;
|
|
}
|
|
|
|
/**
|
|
* Add simple variables
|
|
*
|
|
* The array must be structured like this:
|
|
*
|
|
* [ "key" => "value", "key2" => "value2" ]
|
|
*
|
|
* @param array $vars Variables to add.
|
|
*
|
|
* @return TplBlock For chaining.
|
|
*/
|
|
public function addVars(array $vars)
|
|
{
|
|
$this->vars = array_merge($this->vars, $vars);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add a sub block.
|
|
*
|
|
* @param TplBlock $bloc The block to add as a sub block.
|
|
*
|
|
* @return TplBlock For chaining.
|
|
*/
|
|
public function addSubBlock(TplBlock $bloc)
|
|
{
|
|
// An unnamed block cannot be a sub block.
|
|
if ($bloc->name === "") {
|
|
throw new \UnexpectedValueException(
|
|
"A sub tpl block can't have an empty name"
|
|
);
|
|
}
|
|
|
|
$this->subBlocs[$bloc->name][] = $bloc;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Automatically add subs blocs and sub sub blocs ..., and vars
|
|
* directly from an associative array
|
|
* @param $subBlocsDefinitions the associative array
|
|
* @return TplBlock For chaining.
|
|
*/
|
|
public function addSubBlocsDefinitions(ARRAY $subBlocsDefinitions, $deleteExistingsBlocs=false)
|
|
{
|
|
|
|
foreach($subBlocsDefinitions as $itemKey => $itemValue){
|
|
if(is_array($itemValue)){
|
|
$subBloc = new TplBlock($itemKey);
|
|
$subBloc->addSubBlocsDefinitions($itemValue,$deleteExistingsBlocs);
|
|
$this->addSubBlock($subBloc);
|
|
}else{
|
|
$this->addVars(array($itemKey => $itemValue));
|
|
}
|
|
}
|
|
return $this;
|
|
|
|
}
|
|
/**
|
|
* Generate the sub block regex.
|
|
*
|
|
* @param string $prefix The prefix to add to the block name.
|
|
* @param string $blocName The block name.
|
|
*
|
|
* @return string The regex.
|
|
*/
|
|
private function subBlockRegex($prefix, $blocName)
|
|
{
|
|
return '/'
|
|
. self::BLOCKSTARTSTART
|
|
. preg_quote($prefix . $blocName)
|
|
. self::BLOCKSTARTEND
|
|
. ($this->trim === false ? '' : '(?:\R|)?' )
|
|
. '(.*?)'
|
|
. ($this->trim === false ? '' : '(?:\R|)?' )
|
|
. self::BLOCKENDSTART
|
|
. preg_quote($prefix . $blocName)
|
|
. self::BLOCKENDEND
|
|
. '/is';
|
|
}
|
|
|
|
/**
|
|
* Shake the template string and input vars then returns the parsed text.
|
|
*
|
|
* @param string $str containing the template to parse
|
|
* @param string $subBlocsPath optional, for this class internal use.
|
|
* The path should look like "bloc.subbloc".
|
|
*
|
|
* @return string The processed output.
|
|
*/
|
|
public function applyTplStr($str, $subBlocsPath = "")
|
|
{
|
|
// Replace all simple vars.
|
|
$prefix = $subBlocsPath === "" ? "" : $subBlocsPath . ".";
|
|
|
|
foreach ($this->vars as $key => $value) {
|
|
$str = str_replace(
|
|
self::STARTENCLOSURE . $prefix . $key . self::ENDENCLOSURE,
|
|
$value,
|
|
$str
|
|
);
|
|
}
|
|
|
|
// Parse blocs.
|
|
foreach ($this->subBlocs as $blocName => $blocsArr) {
|
|
$str = preg_replace_callback(
|
|
$this->subBlockRegex($prefix, $blocName),
|
|
function ($m) use ($blocName, $blocsArr, $prefix) {
|
|
$out = "";
|
|
foreach ($blocsArr as $bloc) {
|
|
// Recursion.
|
|
$out .= $bloc->applyTplStr(
|
|
$m[1],
|
|
$prefix . $blocName
|
|
);
|
|
}
|
|
|
|
return $out;
|
|
},
|
|
$str
|
|
);
|
|
}
|
|
|
|
// Delete unused blocs.
|
|
$str = preg_replace($this->unusedRegex, "", $str);
|
|
|
|
//Replace non setted vars by empty string
|
|
if($this->replaceNonGivenVars) {
|
|
$str = preg_replace( "/" .self::STARTENCLOSURE .'([a-z][a-z0-9.]*)' .self::ENDENCLOSURE ."/", '', $str );
|
|
}
|
|
|
|
|
|
//check if loops patterns are still presents
|
|
if (($this->strictMode)
|
|
&& (
|
|
preg_match( "/".self::BLOCKSTARTSTART."/", $str)
|
|
|| preg_match( "/".self::BLOCKENDSTART."/", $str)
|
|
)
|
|
){
|
|
throw new \UnexpectedValueException("Template string not consistent");
|
|
|
|
}
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* Load a file, and pass his content to applyTplStr function.
|
|
*
|
|
* @param string $file The file path of the template to load
|
|
*
|
|
* @return string The processed output.
|
|
*/
|
|
public function applyTplFile($file)
|
|
{
|
|
if (! $tplStr = file_get_contents($file)) {
|
|
throw new \UnexpectedValueException("Cannot read given file $file");
|
|
}
|
|
|
|
return $this->applyTplStr($tplStr, "");
|
|
}
|
|
|
|
/**
|
|
* Enables trimming.
|
|
*
|
|
* @return TplBlock For chaining.
|
|
*/
|
|
public function doTrim()
|
|
{
|
|
$this->trim = true;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Disables trimming.
|
|
*
|
|
* @return TplBlock For chaining.
|
|
*/
|
|
public function dontTrim()
|
|
{
|
|
$this->trim = false;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Enable the behaviour: Non given vars will be replaced by an empty string.
|
|
*
|
|
* @return TplBlock For chaining.
|
|
*/
|
|
public function doReplaceNonGivenVars()
|
|
{
|
|
$this->replaceNonGivenVars = true;
|
|
return $this;
|
|
|
|
}
|
|
|
|
/**
|
|
* Enable the behaviour: Non given vars will be replaced by an empty string.
|
|
*
|
|
* @return TplBlock For chaining.
|
|
*/
|
|
public function dontReplaceNonGivenVars()
|
|
{
|
|
$this->replaceNonGivenVars = false;
|
|
return $this;
|
|
|
|
}
|
|
|
|
/**
|
|
* Enable mode strict. If template is inconsistent, will throw an exception
|
|
* and return nothing
|
|
*
|
|
* @return TplBlock For chaining.
|
|
*/
|
|
public function doStrictMode()
|
|
{
|
|
$this->strictMode = true;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Disable mode strict. If template is inconsistent, will be parsed anyway.
|
|
* and no errors will be returned.
|
|
*
|
|
* @return TplBlock For chaining.
|
|
*/
|
|
public function dontStrictMode(){
|
|
$this->strictMode = false;
|
|
return $this;
|
|
|
|
}
|
|
}
|