*
  • will be replaced by the variable value *
  • will be replaced by the variable value, with the support of setting values *
  • will be replaced by the object attribute value *
  • will create a loop *
  • will create a condition * * @author Stefan Daurer * @package Mammut\Template */ class CTemplate extends \Mammut\StrictObject implements iTemplate { /** @var \stdClass the value object */ private $values = array(); private $param = array(); private $filter = array(); /** @var array the parsed template */ private $blocks; private $root = NULL; private $relPathReplacer = NULL; /** * Creates a new template object */ public function __construct() { } public function getRoot() { return $this->root; } public function setRoot($root) { $this->root = $root; } public function getRelativePathReplacer() { return $this->relPathReplacer; } public function setRelativePathReplacer($path) { $this->relPathReplacer = $path; } public function getParam($name) { return $this->values[$name]; } public function setParam($name, $value) { $this->values[$name] = $value; } public function setParamFilter($name, $callback, array $add = array()) { $this->filter[$name] = array('f' => $callback, 'p' => $add); } public function getParamList() { $result = array(); foreach ($blocks as $b) { if (is_object($b)) $result[] = $b->content; } return $result; } public function getParamParams($name) { if (key_exists($name, $this->param)) return $this->param[$name]; return NULL; } public function loadTemplate($file,$runPHP = true) { if ($file instanceof File) $file = $file->getPath(); if (!include_exists($file)) throw new FileNotFoundException($file); if ($runPHP) { ob_start(); include($file); $this->loadString(ob_get_clean()); } else $this->loadString(file_get_contents($file)); } public function loadString($data) { $data = preg_replace('#.*?#is','',$data); $me = &$this; $data = preg_replace_callback('#(.*?)#is', function($param) use (&$me) { $me->setParam($param[1], $param[2]); return ''; }, $data); $input = preg_split('#()#is', $data,0,PREG_SPLIT_DELIM_CAPTURE); $this->blocks = $this->parseTemplate($input); } /** * Parses the template string to an array ob objects and strings. * @param string $data the template data * @return array the parsed template */ private function parseTemplate($data) { $result = array(); $this->param = array(); foreach($data as $value) { if (!isset($value[1]) || $value[1] != '!') $result[] = $value; elseif (preg_match('#^#is', $value,$matches)) { $o = new \stdClass(); $o->type = 'end'; $o->content = false; $result[] = $o; } elseif (preg_match('#^#is', $value,$matches)) { $o = new \stdClass(); $o->type = 'func'; $o->not = ($matches[1] == '!'); $o->content = trim($matches[2]); $o->param = isset($matches[3]) ? explode('|',trim($matches[4])) : false; if (is_array($o->param)) { $newParam = array(); foreach ($o->param as $id => $val) { if (($p = strpos($val, '=')) !== false) { $key = substr($val, 0, $p); $value = substr($val, $p+1); $newParam[$key] = $value; } } $o->param = $newParam; $this->param[$o->content] = $newParam; unset($newParam); } $result[] = $o; } elseif (preg_match('#^#is', $value,$matches)) { $o = new \stdClass(); $o->type = 'value'; $o->not = ($matches[1] == '!'); $o->content = trim($matches[2]); $o->param = isset($matches[3]) ? explode('|',trim($matches[4])) : false; if (is_array($o->param)) { $newParam = array(); foreach ($o->param as $id => $val) { if (($p = strpos($val, '=')) !== false) { $key = substr($val, 0, $p); $value = substr($val, $p+1); $newParam[$key] = $value; } } $o->param = $newParam; $this->param[$o->content] = $newParam; unset($newParam); } $result[] = $o; } elseif (preg_match('#^#is', $value,$matches)) { $o = new \stdClass(); $o->type = ($matches[5] == '?') ? 'condition' : 'loop'; $o->not = ($matches[1] == '!'); if (empty($matches[3])) $o->compareto = false; else { $data = $matches[4]; $o->compareto = ($data[0] == "'") ? substr($data, 1, strlen($data)-2) : $o->compareto = (int) $data; } $o->content = trim($matches[2]); $o->target = ($matches[5] != '?') ? $matches[6] : false; $result[] = $o; } else $result[] = $value; } return $result; } /** * builds the output by replacing the template variables with their values */ private function buildOutput(CTContext $ctx, &$output, $start = 0) { $count = count($this->blocks); for ($id = $start; $id < $count; $id++) { $current = $this->blocks[$id]; if (!is_object($current)) { // CHECK: is there a solution with highter performance? $current = preg_replace('%(src|href)=([\'"])./%i','\\1=\\2'.$this->relPathReplacer.'/',$current); // if (isset($this->filter[$current->content])) // $var = call_user_func_array($this->filter[$current->content]['f'], array_merge(array($var),$this->filter[$current->content]['p'])); $output .= (string) $current; } else { if ($current->type == 'value') { $content = $current->content; if (strpos($content,'.') === false) { // plain variables if ($ctx->isValueSet($content)) $var = $ctx->getValue($content); else $var = '!undefined variable: $'.$content; } else { // object $names = explode('.',$content); if (!$ctx->isValueObject($names[0])) $var = '!undefined object: $'.$content; else { $name = $names[0]; unset($names[0]); $var = $ctx->getValueObjAtr($name, $names); } } if (isset($this->filter[$current->content])) $var = call_user_func_array($this->filter[$current->content]['f'], array_merge(array($var),$this->filter[$current->content]['p'])); $output .= $var; } elseif ($current->type == 'end') { return $id; } elseif ($current->type == 'condition') { $content = $current->content; $context = false; if (strpos($content,'.') === false) { // plain variables if ($ctx->isValueSet($content)) $context = $ctx->getValue($content); else $output .= '!undefined variable: $'.$content; } else { // object $names = explode('.',$content); if (!$ctx->isValueObject($names[0])) $output .= '!undefined object: $'.$content; else { $name = $names[0]; unset($names[0]); $context = $ctx->getValueObjAtr($name, $names); } } if ($current->compareto !== false) { $context = ($context == $current->compareto); } if ($current->not) $context = !$context; $suboutput = ''; $id = $this->buildOutput($ctx, $suboutput,$id+1); if ($context) $output .= $suboutput; unset($suboutput); } elseif ($current->type == 'loop') { $content = $current->content; $context = array(); if (strpos($content,'.') === false) { // plain variables if ($ctx->isValueSet($content)) $context = $ctx->getValue($content); else $output .= '!undefined variable: $'.$content; } else { // object $names = explode('.',$content); if (!$ctx->isValueObject($names[0])) $output .= '!undefined object: $'.$content; else { $name = $names[0]; unset($names[0]); $context = $ctx->getValueObjAtr($name, $names); } } $newId = 0; if (!is_array($context)) throw new \InvalidArgumentException('context is not an array: '.$content); if (count($context) != 0) { $row = 1; $loopCount = count($context); foreach ($context as $entry) { $dataObj = new CTContext(); $dataObj->loop = $row++; $dataObj->maxloop = $loopCount; // TODO: support array properties in objects foreach ($ctx->values as $name=>$value) $dataObj->values[$name] = isset($entry->$name) ? $entry->$name : $value; unset($dataObj->values[$content]); $dataObj->values[$current->target] = $entry; $newId = $this->buildOutput($dataObj ,$output,$id+1); } } else { $dataObj = new CTContext(); $dummy = ''; $newId = $this->buildOutput($dataObj ,$dummy,$id+1); unset($dummy); } $id = $newId; } } } } public function getDocument() { $output = ''; $context = new CTContext(); $context->values = $this->values; $this->buildOutput($context,$output); return $output; } }