public static function compile(Compiler $compiler, $file) { list($l, $r) = $compiler->getDelimiters(); self::$l = preg_quote($l, '/'); self::$r = preg_quote($r, '/'); self::$regex = '/ ' . self::$l . 'block\\s(["\']?)(.+?)\\1' . self::$r . '(?:\\r?\\n?) ((?: (?R) | [^' . self::$l . ']* (?: (?! ' . self::$l . '\\/?block\\b ) ' . self::$l . ' [^' . self::$l . ']*+ )* )*) ' . self::$l . '\\/block' . self::$r . ' /six'; if ($compiler->getLooseOpeningHandling()) { self::$l .= '\\s*'; self::$r = '\\s*' . self::$r; } $inheritanceTree = array(array('source' => $compiler->getTemplateSource())); //$curPath = dirname($compiler->getDwoo()->getTemplate()->getResourceIdentifier()) . DIRECTORY_SEPARATOR; $curTpl = $compiler->getDwoo()->getTemplate(); while (!empty($file)) { if ($file === '""' || $file === "''" || substr($file, 0, 1) !== '"' && substr($file, 0, 1) !== '\'') { throw new CompilationException($compiler, 'Extends : The file name must be a non-empty string'); return; } if (preg_match('#^["\']([a-z]{2,}):(.*?)["\']$#i', $file, $m)) { // resource:identifier given, extract them $resource = $m[1]; $identifier = $m[2]; } else { // get the current template's resource $resource = $curTpl->getResourceName(); $identifier = substr($file, 1, -1); } try { $parent = $compiler->getDwoo()->templateFactory($resource, $identifier, null, null, null, $curTpl); } catch (\Dwoo\Exception\SecurityException $e) { throw new CompilationException($compiler, 'Extends : Security restriction : ' . $e->getMessage()); } catch (\Dwoo\Exception $e) { throw new Exception($compiler, 'Extends : ' . $e->getMessage()); } if ($parent === null) { throw new CompilationException($compiler, 'Extends : Resource "' . $resource . ':' . $identifier . '" not found.'); } elseif ($parent === false) { throw new CompilationException($compiler, 'Extends : Resource "' . $resource . '" does not support extends.'); } $curTpl = $parent; $newParent = array('source' => $parent->getSource(), 'resource' => $resource, 'identifier' => $parent->getResourceIdentifier(), 'uid' => $parent->getUid()); if (array_search($newParent, $inheritanceTree, true) !== false) { throw new CompilationException($compiler, 'Extends : Recursive template inheritance detected'); } $inheritanceTree[] = $newParent; if (preg_match('/^' . self::$l . 'extends(?:\\(?\\s*|\\s+)(?:file=)?\\s*((["\']).+?\\2|\\S+?)\\s*\\)?\\s*?' . self::$r . '/i', $parent->getSource(), $match)) { $curPath = dirname($identifier) . DIRECTORY_SEPARATOR; if (isset($match[2]) && $match[2] == '"') { $file = '"' . str_replace('"', '\\"', substr($match[1], 1, -1)) . '"'; } elseif (isset($match[2]) && $match[2] == "'") { $file = '"' . substr($match[1], 1, -1) . '"'; } else { $file = '"' . $match[1] . '"'; } } else { $file = false; } } while (true) { $parent = array_pop($inheritanceTree); $child = end($inheritanceTree); self::$childSource = $child['source']; self::$lastReplacement = count($inheritanceTree) === 1; if (!isset($newSource)) { $newSource = $parent['source']; } $newSource = preg_replace_callback(self::$regex, array(__CLASS__, 'replaceBlock'), $newSource); $newSource = $l . 'do extendsCheck(' . var_export($parent['resource'] . ':' . $parent['identifier'], true) . ')' . $r . $newSource; if (self::$lastReplacement) { break; } } $compiler->setTemplateSource($newSource); $compiler->recompile(); }
public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) { $params = $compiler->getCompiledParams($params); $tpl = $compiler->getTemplateSource($params['tplPointer']); // assigns params $src = $params['from']; $name = $params['name']; // evaluates which global variables have to be computed $varName = '$dwoo.loop.' . trim($name, '"\'') . '.'; $shortVarName = '$.loop.' . trim($name, '"\'') . '.'; $usesAny = strpos($tpl, $varName) !== false || strpos($tpl, $shortVarName) !== false; $usesFirst = strpos($tpl, $varName . 'first') !== false || strpos($tpl, $shortVarName . 'first') !== false; $usesLast = strpos($tpl, $varName . 'last') !== false || strpos($tpl, $shortVarName . 'last') !== false; $usesIndex = $usesFirst || strpos($tpl, $varName . 'index') !== false || strpos($tpl, $shortVarName . 'index') !== false; $usesIteration = $usesLast || strpos($tpl, $varName . 'iteration') !== false || strpos($tpl, $shortVarName . 'iteration') !== false; $usesShow = strpos($tpl, $varName . 'show') !== false || strpos($tpl, $shortVarName . 'show') !== false; $usesTotal = $usesLast || strpos($tpl, $varName . 'total') !== false || strpos($tpl, $shortVarName . 'total') !== false; if (strpos($name, '$this->scope[') !== false) { $usesAny = $usesFirst = $usesLast = $usesIndex = $usesIteration = $usesShow = $usesTotal = true; } // gets foreach id $cnt = self::$cnt++; // builds pre processing output $pre = Compiler::PHP_OPEN . "\n" . '$_loop' . $cnt . '_data = ' . $src . ';'; // adds foreach properties if ($usesAny) { $pre .= "\n" . '$this->globals["loop"][' . $name . '] = array' . "\n("; if ($usesIndex) { $pre .= "\n\t" . '"index" => 0,'; } if ($usesIteration) { $pre .= "\n\t" . '"iteration" => 1,'; } if ($usesFirst) { $pre .= "\n\t" . '"first" => null,'; } if ($usesLast) { $pre .= "\n\t" . '"last" => null,'; } if ($usesShow) { $pre .= "\n\t" . '"show" => $this->isTraversable($_loop' . $cnt . '_data, true),'; } if ($usesTotal) { $pre .= "\n\t" . '"total" => $this->count($_loop' . $cnt . '_data),'; } $pre .= "\n);\n" . '$_loop' . $cnt . '_glob =& $this->globals["loop"][' . $name . '];'; } // checks if the loop must be looped $pre .= "\n" . 'if ($this->isTraversable($_loop' . $cnt . '_data' . (isset($params['hasElse']) ? ', true' : '') . ') == true)' . "\n{"; // iterates over keys $pre .= "\n\t" . 'foreach ($_loop' . $cnt . '_data as $tmp_key => $this->scope["-loop-"])' . "\n\t{"; // updates properties if ($usesFirst) { $pre .= "\n\t\t" . '$_loop' . $cnt . '_glob["first"] = (string) ($_loop' . $cnt . '_glob["index"] === 0);'; } if ($usesLast) { $pre .= "\n\t\t" . '$_loop' . $cnt . '_glob["last"] = (string) ($_loop' . $cnt . '_glob["iteration"] === $_loop' . $cnt . '_glob["total"]);'; } $pre .= "\n\t\t" . '$_loop' . $cnt . '_scope = $this->setScope(array("-loop-"));' . "\n/* -- loop start output */\n" . Compiler::PHP_CLOSE; // build post processing output and cache it $post = Compiler::PHP_OPEN . "\n" . '/* -- loop end output */' . "\n\t\t" . '$this->setScope($_loop' . $cnt . '_scope, true);'; // update properties if ($usesIndex) { $post .= "\n\t\t" . '$_loop' . $cnt . '_glob["index"]+=1;'; } if ($usesIteration) { $post .= "\n\t\t" . '$_loop' . $cnt . '_glob["iteration"]+=1;'; } // end loop $post .= "\n\t}\n}\n" . Compiler::PHP_CLOSE; if (isset($params['hasElse'])) { $post .= $params['hasElse']; } return $pre . $content . $post; }
public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) { $params = $compiler->getCompiledParams($params); $tpl = $compiler->getTemplateSource($params['tplPointer']); // assigns params $src = $params['from']; if ($params['item'] !== 'null') { if ($params['key'] !== 'null') { $key = $params['key']; } $val = $params['item']; } elseif ($params['key'] !== 'null') { $val = $params['key']; } else { throw new CompilationException($compiler, 'Foreach <em>item</em> parameter missing'); } $name = $params['name']; if (substr($val, 0, 1) !== '"' && substr($val, 0, 1) !== '\'') { throw new CompilationException($compiler, 'Foreach <em>item</em> parameter must be of type string'); } if (isset($key) && substr($val, 0, 1) !== '"' && substr($val, 0, 1) !== '\'') { throw new CompilationException($compiler, 'Foreach <em>key</em> parameter must be of type string'); } // evaluates which global variables have to be computed $varName = '$dwoo.foreach.' . trim($name, '"\'') . '.'; $shortVarName = '$.foreach.' . trim($name, '"\'') . '.'; $usesAny = strpos($tpl, $varName) !== false || strpos($tpl, $shortVarName) !== false; $usesFirst = strpos($tpl, $varName . 'first') !== false || strpos($tpl, $shortVarName . 'first') !== false; $usesLast = strpos($tpl, $varName . 'last') !== false || strpos($tpl, $shortVarName . 'last') !== false; $usesIndex = $usesFirst || strpos($tpl, $varName . 'index') !== false || strpos($tpl, $shortVarName . 'index') !== false; $usesIteration = $usesLast || strpos($tpl, $varName . 'iteration') !== false || strpos($tpl, $shortVarName . 'iteration') !== false; $usesShow = strpos($tpl, $varName . 'show') !== false || strpos($tpl, $shortVarName . 'show') !== false; $usesTotal = $usesLast || strpos($tpl, $varName . 'total') !== false || strpos($tpl, $shortVarName . 'total') !== false; if (strpos($name, '$this->scope[') !== false) { $usesAny = $usesFirst = $usesLast = $usesIndex = $usesIteration = $usesShow = $usesTotal = true; } // override globals vars if implode is used if ($params['implode'] !== 'null') { $implode = $params['implode']; $usesAny = true; $usesLast = true; $usesIteration = true; $usesTotal = true; } // gets foreach id $cnt = self::$cnt++; // build pre content output $pre = Compiler::PHP_OPEN . "\n" . '$_fh' . $cnt . '_data = ' . $src . ';'; // adds foreach properties if ($usesAny) { $pre .= "\n" . '$this->globals["foreach"][' . $name . '] = array' . "\n("; if ($usesIndex) { $pre .= "\n\t" . '"index" => 0,'; } if ($usesIteration) { $pre .= "\n\t" . '"iteration" => 1,'; } if ($usesFirst) { $pre .= "\n\t" . '"first" => null,'; } if ($usesLast) { $pre .= "\n\t" . '"last" => null,'; } if ($usesShow) { $pre .= "\n\t" . '"show" => $this->isArray($_fh' . $cnt . '_data, true),'; } if ($usesTotal) { $pre .= "\n\t" . '"total" => $this->count($_fh' . $cnt . '_data),'; } $pre .= "\n);\n" . '$_fh' . $cnt . '_glob =& $this->globals["foreach"][' . $name . '];'; } // checks if foreach must be looped $pre .= "\n" . 'if ($this->isTraversable($_fh' . $cnt . '_data' . (isset($params['hasElse']) ? ', true' : '') . ') == true)' . "\n{"; // iterates over keys $pre .= "\n\t" . 'foreach ($_fh' . $cnt . '_data as ' . (isset($key) ? '$this->scope[' . $key . ']=>' : '') . '$this->scope[' . $val . '])' . "\n\t{"; // updates properties if ($usesFirst) { $pre .= "\n\t\t" . '$_fh' . $cnt . '_glob["first"] = (string) ($_fh' . $cnt . '_glob["index"] === 0);'; } if ($usesLast) { $pre .= "\n\t\t" . '$_fh' . $cnt . '_glob["last"] = (string) ($_fh' . $cnt . '_glob["iteration"] === $_fh' . $cnt . '_glob["total"]);'; } $pre .= "\n/* -- foreach start output */\n" . Compiler::PHP_CLOSE; // build post content output $post = Compiler::PHP_OPEN . "\n"; if (isset($implode)) { $post .= '/* -- implode */' . "\n" . 'if (!$_fh' . $cnt . '_glob["last"]) {' . "\n\t" . 'echo ' . $implode . ";\n}\n"; } $post .= '/* -- foreach end output */'; // update properties if ($usesIndex) { $post .= "\n\t\t" . '$_fh' . $cnt . '_glob["index"]+=1;'; } if ($usesIteration) { $post .= "\n\t\t" . '$_fh' . $cnt . '_glob["iteration"]+=1;'; } // end loop $post .= "\n\t}\n}" . Compiler::PHP_CLOSE; if (isset($params['hasElse'])) { $post .= $params['hasElse']; } return $pre . $content . $post; }
public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content) { $params = $compiler->getCompiledParams($params); $tpl = $compiler->getTemplateSource($params['tplPointer']); // assigns params $from = $params['from']; $name = $params['name']; $step = $params['step']; $to = $params['to']; // evaluates which global variables have to be computed $varName = '$dwoo.for.' . trim($name, '"\'') . '.'; $shortVarName = '$.for.' . trim($name, '"\'') . '.'; $usesAny = strpos($tpl, $varName) !== false || strpos($tpl, $shortVarName) !== false; $usesFirst = strpos($tpl, $varName . 'first') !== false || strpos($tpl, $shortVarName . 'first') !== false; $usesLast = strpos($tpl, $varName . 'last') !== false || strpos($tpl, $shortVarName . 'last') !== false; $usesIndex = strpos($tpl, $varName . 'index') !== false || strpos($tpl, $shortVarName . 'index') !== false; $usesIteration = $usesFirst || $usesLast || strpos($tpl, $varName . 'iteration') !== false || strpos($tpl, $shortVarName . 'iteration') !== false; $usesShow = strpos($tpl, $varName . 'show') !== false || strpos($tpl, $shortVarName . 'show') !== false; $usesTotal = $usesLast || strpos($tpl, $varName . 'total') !== false || strpos($tpl, $shortVarName . 'total') !== false; if (strpos($name, '$this->scope[') !== false) { $usesAny = $usesFirst = $usesLast = $usesIndex = $usesIteration = $usesShow = $usesTotal = true; } // gets foreach id $cnt = self::$cnt++; // builds pre processing output for $out = Compiler::PHP_OPEN . "\n" . '$_for' . $cnt . '_from = ' . $from . ';' . "\n" . '$_for' . $cnt . '_to = ' . $to . ';' . "\n" . '$_for' . $cnt . '_step = abs(' . $step . ');' . "\n" . 'if (is_numeric($_for' . $cnt . '_from) && !is_numeric($_for' . $cnt . '_to)) { $this->triggerError(\'For requires the <em>to</em> parameter when using a numerical <em>from</em>\'); }' . "\n" . '$tmp_shows = $this->isArray($_for' . $cnt . '_from, true) || (is_numeric($_for' . $cnt . '_from) && (abs(($_for' . $cnt . '_from - $_for' . $cnt . '_to)/$_for' . $cnt . '_step) !== 0 || $_for' . $cnt . '_from == $_for' . $cnt . '_to));'; // adds for properties if ($usesAny) { $out .= "\n" . '$this->globals["for"][' . $name . '] = array' . "\n("; if ($usesIndex) { $out .= "\n\t" . '"index" => 0,'; } if ($usesIteration) { $out .= "\n\t" . '"iteration" => 1,'; } if ($usesFirst) { $out .= "\n\t" . '"first" => null,'; } if ($usesLast) { $out .= "\n\t" . '"last" => null,'; } if ($usesShow) { $out .= "\n\t" . '"show" => $tmp_shows,'; } if ($usesTotal) { $out .= "\n\t" . '"total" => $this->isArray($_for' . $cnt . '_from) ? floor($this->count($_for' . $cnt . '_from) / $_for' . $cnt . '_step) : (is_numeric($_for' . $cnt . '_from) ? abs(($_for' . $cnt . '_to + 1 - $_for' . $cnt . '_from)/$_for' . $cnt . '_step) : 0),'; } $out .= "\n);\n" . '$_for' . $cnt . '_glob =& $this->globals["for"][' . $name . '];'; } // checks if for must be looped $out .= "\n" . 'if ($tmp_shows)' . "\n{"; // set from/to to correct values if an array was given $out .= "\n\t" . 'if ($this->isArray($_for' . $cnt . '_from' . (isset($params['hasElse']) ? ', true' : '') . ') == true) { $_for' . $cnt . '_to = is_numeric($_for' . $cnt . '_to) ? $_for' . $cnt . '_to - $_for' . $cnt . '_step : $this->count($_for' . $cnt . '_from) - 1; $_for' . $cnt . '_from = 0; }'; // if input are pure numbers it shouldn't reorder them, if it's variables it gets too messy though so in that case a counter should be used $reverse = false; $condition = '<='; $incrementer = '+'; if (preg_match('{^(["\']?)([0-9]+)\\1$}', $from, $mN1) && preg_match('{^(["\']?)([0-9]+)\\1$}', $to, $mN2)) { $from = (int) $mN1[2]; $to = (int) $mN2[2]; if ($from > $to) { $reverse = true; $condition = '>='; $incrementer = '-'; } } // reverse from and to if needed if (!$reverse) { $out .= "\n\t" . 'if ($_for' . $cnt . '_from > $_for' . $cnt . '_to) { $tmp = $_for' . $cnt . '_from; $_for' . $cnt . '_from = $_for' . $cnt . '_to; $_for' . $cnt . '_to = $tmp; }'; } $out .= "\n\t" . 'for ($this->scope[' . $name . '] = $_for' . $cnt . '_from; $this->scope[' . $name . '] ' . $condition . ' $_for' . $cnt . '_to; $this->scope[' . $name . '] ' . $incrementer . '= $_for' . $cnt . '_step)' . "\n\t{"; // updates properties if ($usesIndex) { $out .= "\n\t\t" . '$_for' . $cnt . '_glob["index"] = $this->scope[' . $name . '];'; } if ($usesFirst) { $out .= "\n\t\t" . '$_for' . $cnt . '_glob["first"] = (string) ($_for' . $cnt . '_glob["iteration"] === 1);'; } if ($usesLast) { $out .= "\n\t\t" . '$_for' . $cnt . '_glob["last"] = (string) ($_for' . $cnt . '_glob["iteration"] === $_for' . $cnt . '_glob["total"]);'; } $out .= "\n/* -- for start output */\n" . Compiler::PHP_CLOSE; // build post processing output and cache it $postOut = Compiler::PHP_OPEN . '/* -- for end output */'; // update properties if ($usesIteration) { $postOut .= "\n\t\t" . '$_for' . $cnt . '_glob["iteration"]+=1;'; } // end loop $postOut .= "\n\t}\n}\n" . Compiler::PHP_CLOSE; if (isset($params['hasElse'])) { $postOut .= $params['hasElse']; } return $out . $content . $postOut; }