fenom/src/Cytro/Compiler.php

846 lines
27 KiB
PHP
Raw Normal View History

2013-01-25 18:36:16 +04:00
<?php
2013-04-28 11:33:36 +04:00
/*
* This file is part of Cytro.
*
* (c) 2013 Ivan Shalganov
*
2013-04-28 18:08:57 +04:00
* For the full copyright and license information, please view the license.md
2013-04-28 11:33:36 +04:00
* file that was distributed with this source code.
*/
2013-04-04 10:56:44 +04:00
namespace Cytro;
use Cytro\Tokenizer;
use Cytro\Template;
use Cytro\Scope;
2013-01-25 18:36:16 +04:00
/**
* Compilers collection
2013-04-28 11:33:36 +04:00
* @package Cytro
*/
2013-01-25 18:36:16 +04:00
class Compiler {
/**
* Tag {include ...}
*
* @static
* @param Tokenizer $tokens
* @param Template $tpl
* @throws ImproperUseException
2013-01-25 18:36:16 +04:00
* @return string
*/
public static function tagInclude(Tokenizer $tokens, Template $tpl) {
2013-04-28 11:33:36 +04:00
$cname = $tpl->parsePlainArg($tokens, $name);
$p = $tpl->parseParams($tokens);
2013-02-20 19:51:06 +04:00
if($p) { // if we have additionally variables
2013-04-04 10:56:44 +04:00
if($name && ($tpl->getStorage()->getOptions() & \Cytro::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known
2013-02-20 19:51:06 +04:00
$inc = $tpl->getStorage()->compile($name, false);
$tpl->addDepend($inc);
2013-02-23 02:03:05 +04:00
return '$_tpl = (array)$tpl; $tpl->exchangeArray('.self::toArray($p).'+$_tpl); ?>'.$inc->_body.'<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
2013-02-20 19:51:06 +04:00
} else {
2013-02-23 02:03:05 +04:00
return '$tpl->getStorage()->getTemplate('.$cname.')->display('.self::toArray($p).'+(array)$tpl);';
2013-02-20 19:51:06 +04:00
}
2013-01-25 18:36:16 +04:00
} else {
2013-04-04 10:56:44 +04:00
if($name && ($tpl->getStorage()->getOptions() & \Cytro::FORCE_INCLUDE)) { // if FORCE_INCLUDE enabled and template name known
2013-02-20 19:51:06 +04:00
$inc = $tpl->getStorage()->compile($name, false);
$tpl->addDepend($inc);
return '$_tpl = (array)$tpl; ?>'.$inc->_body.'<?php $tpl->exchangeArray($_tpl); unset($_tpl);';
} else {
return '$tpl->getStorage()->getTemplate('.$cname.')->display((array)$tpl);';
2013-02-20 19:51:06 +04:00
}
2013-01-25 18:36:16 +04:00
}
}
2013-01-25 18:36:16 +04:00
/**
* Open tag {if ...}
*
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function ifOpen(Tokenizer $tokens, Scope $scope) {
$scope["else"] = false;
return 'if('.$scope->tpl->parseExp($tokens, true).') {';
}
/**
* Tag {elseif ...}
*
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @throws ImproperUseException
* @return string
*/
2013-01-25 18:36:16 +04:00
public static function tagElseIf(Tokenizer $tokens, Scope $scope) {
if($scope["else"]) {
throw new ImproperUseException('Incorrect use of the tag {elseif}');
2013-01-25 18:36:16 +04:00
}
return '} elseif('.$scope->tpl->parseExp($tokens, true).') {';
}
2013-01-25 18:36:16 +04:00
/**
* Tag {else}
*
* @param Tokenizer $tokens
* @param Scope $scope
* @internal param $
* @param Scope $scope
* @return string
*/
public static function tagElse(Tokenizer $tokens, Scope $scope) {
$scope["else"] = true;
return '} else {';
}
2013-01-25 18:36:16 +04:00
/**
* Open tag {foreach ...}
*
* @static
* @param Tokenizer $tokens
2013-02-20 19:51:06 +04:00
* @param Scope $scope
2013-03-15 00:12:02 +04:00
* @throws UnexpectedTokenException
* @throws ImproperUseException
2013-01-25 18:36:16 +04:00
* @return string
*/
public static function foreachOpen(Tokenizer $tokens, Scope $scope) {
$p = array("index" => false, "first" => false, "last" => false);
$key = null;
$before = $body = array();
if($tokens->is(T_VARIABLE)) {
2013-03-17 14:37:23 +04:00
$from = $scope->tpl->parseVariable($tokens, Template::DENY_MODS);
2013-01-25 18:36:16 +04:00
$prepend = "";
} elseif($tokens->is('[')) {
$from = $scope->tpl->parseArray($tokens);
$uid = '$v'.$scope->tpl->i++;
$prepend = $uid.' = '.$from.';';
$from = $uid;
} else {
2013-03-15 00:12:02 +04:00
throw new UnexpectedTokenException($tokens, null, "tag {foreach}");
2013-01-25 18:36:16 +04:00
}
$tokens->get(T_AS);
$tokens->next();
2013-03-17 14:37:23 +04:00
$value = $scope->tpl->parseVariable($tokens, Template::DENY_MODS | Template::DENY_ARRAY);
2013-01-25 18:36:16 +04:00
if($tokens->is(T_DOUBLE_ARROW)) {
$tokens->next();
$key = $value;
2013-03-17 14:37:23 +04:00
$value = $scope->tpl->parseVariable($tokens, Template::DENY_MODS | Template::DENY_ARRAY);
2013-01-25 18:36:16 +04:00
}
$scope["after"] = array();
$scope["else"] = false;
while($token = $tokens->key()) {
$param = $tokens->get(T_STRING);
if(!isset($p[ $param ])) {
throw new ImproperUseException("Unknown parameter '$param' in {foreach}");
2013-01-25 18:36:16 +04:00
}
$tokens->getNext("=");
$tokens->next();
2013-03-17 14:37:23 +04:00
$p[ $param ] = $scope->tpl->parseVariable($tokens, Template::DENY_MODS | Template::DENY_ARRAY);
2013-01-25 18:36:16 +04:00
}
if($p["index"]) {
$before[] = $p["index"].' = 0';
$scope["after"][] = $p["index"].'++';
}
if($p["first"]) {
$before[] = $p["first"].' = true';
$scope["after"][] = $p["first"] .' && ('. $p["first"].' = false )';
}
if($p["last"]) {
$before[] = $p["last"].' = false';
$scope["uid"] = "v".$scope->tpl->i++;
$before[] = '$'.$scope["uid"]." = count($from)";
$body[] = 'if(!--$'.$scope["uid"].') '.$p["last"].' = true';
}
$before = $before ? implode("; ", $before).";" : "";
$body = $body ? implode("; ", $body).";" : "";
$scope["after"] = $scope["after"] ? implode("; ", $scope["after"]).";" : "";
if($key) {
return "$prepend if($from) { $before foreach($from as $key => $value) { $body";
} else {
return "$prepend if($from) { $before foreach($from as $value) { $body";
}
}
/**
* Tag {foreachelse}
*
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function foreachElse($tokens, Scope $scope) {
2013-01-25 18:36:16 +04:00
$scope["no-break"] = $scope["no-continue"] = $scope["else"] = true;
return " {$scope['after']} } } else {";
}
/**
* Close tag {/foreach}
*
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function foreachClose($tokens, Scope $scope) {
2013-01-25 18:36:16 +04:00
if($scope["else"]) {
return '}';
} else {
return " {$scope['after']} } }";
}
}
/**
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
* @throws ImproperUseException
2013-01-25 18:36:16 +04:00
*/
public static function forOpen(Tokenizer $tokens, Scope $scope) {
$p = array("index" => false, "first" => false, "last" => false, "step" => 1, "to" => false, "max" => false, "min" => false);
$scope["after"] = $before = $body = array();
$i = array('', '');
$c = "";
2013-03-17 14:37:23 +04:00
$var = $scope->tpl->parseVariable($tokens, Template::DENY_MODS);
2013-01-25 18:36:16 +04:00
$tokens->get("=");
$tokens->next();
$val = $scope->tpl->parseExp($tokens, true);
$p = $scope->tpl->parseParams($tokens, $p);
if(is_numeric($p["step"])) {
if($p["step"] > 0) {
$condition = "$var <= {$p['to']}";
if($p["last"]) $c = "($var + {$p['step']}) > {$p['to']}";
} elseif($p["step"] < 0) {
$condition = "$var >= {$p['to']}";
if($p["last"]) $c = "($var + {$p['step']}) < {$p['to']}";
} else {
throw new ImproperUseException("Invalid step value if {for}");
2013-01-25 18:36:16 +04:00
}
} else {
$condition = "({$p['step']} > 0 && $var <= {$p['to']} || {$p['step']} < 0 && $var >= {$p['to']})";
if($p["last"]) $c = "({$p['step']} > 0 && ($var + {$p['step']}) <= {$p['to']} || {$p['step']} < 0 && ($var + {$p['step']}) >= {$p['to']})";
}
if($p["first"]) {
$before[] = $p["first"].' = true';
$scope["after"][] = $p["first"] .' && ('. $p["first"].' = false )';
}
if($p["last"]) {
$before[] = $p["last"].' = false';
$body[] = "if($c) {$p['last']} = true";
}
if($p["index"]) {
$i[0] .= $p["index"].' = 0,';
$i[1] .= $p["index"].'++,';
}
$scope["else"] = false;
$scope["else_cond"] = "$var==$val";
$before = $before ? implode("; ", $before).";" : "";
$body = $body ? implode("; ", $body).";" : "";
$scope["after"] = $scope["after"] ? implode("; ", $scope["after"]).";" : "";
return "$before for({$i[0]} $var=$val; $condition;{$i[1]} $var+={$p['step']}) { $body";
}
/**
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function forElse(Tokenizer $tokens, Scope $scope) {
$scope["no-break"] = $scope["no-continue"] = true;
$scope["else"] = true;
return " } if({$scope['else_cond']}) {";
}
/**
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function forClose($tokens, Scope $scope) {
2013-01-25 18:36:16 +04:00
if($scope["else"]) {
return '}';
} else {
return " {$scope['after']} }";
}
}
/**
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function whileOpen(Tokenizer $tokens, Scope $scope) {
return 'while('.$scope->tpl->parseExp($tokens, true).') {';
}
/**
* Open tag {switch}
*
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function switchOpen(Tokenizer $tokens, Scope $scope) {
$scope["no-break"] = $scope["no-continue"] = true;
$scope["switch"] = 'switch('.$scope->tpl->parseExp($tokens, true).') {';
2013-02-20 19:51:06 +04:00
// lazy init
2013-01-25 18:36:16 +04:00
return '';
}
/**
* Tag {case ...}
*
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function tagCase(Tokenizer $tokens, Scope $scope) {
$code = 'case '.$scope->tpl->parseExp($tokens, true).': ';
if($scope["switch"]) {
unset($scope["no-break"], $scope["no-continue"]);
$code = $scope["switch"]."\n".$code;
$scope["switch"] = "";
}
return $code;
}
/**
* Tag {continue}
*
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @throws ImproperUseException
2013-01-25 18:36:16 +04:00
* @return string
*/
public static function tagContinue($tokens, Scope $scope) {
2013-01-25 18:36:16 +04:00
if(empty($scope["no-continue"])) {
return 'continue;';
2013-01-25 18:36:16 +04:00
} else {
throw new ImproperUseException("Improper usage of the tag {continue}");
2013-01-25 18:36:16 +04:00
}
}
2013-01-25 18:36:16 +04:00
/**
* Tag {default}
*
* @static
2013-02-20 19:51:06 +04:00
* @param Tokenizer $tokens
* @param Scope $scope
2013-01-25 18:36:16 +04:00
* @return string
*/
public static function tagDefault($tokens, Scope $scope) {
2013-01-25 18:36:16 +04:00
$code = 'default: ';
if($scope["switch"]) {
unset($scope["no-break"], $scope["no-continue"]);
$code = $scope["switch"]."\n".$code;
$scope["switch"] = "";
}
return $code;
}
2013-01-25 18:36:16 +04:00
/**
* Tag {break}
*
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @throws ImproperUseException
2013-01-25 18:36:16 +04:00
* @return string
*/
public static function tagBreak($tokens, Scope $scope) {
2013-01-25 18:36:16 +04:00
if(empty($scope["no-break"])) {
return 'break;';
} else {
throw new ImproperUseException("Improper usage of the tag {break}");
2013-01-25 18:36:16 +04:00
}
}
2013-01-25 18:36:16 +04:00
/**
* Dispatch {extends} tag
* @param Tokenizer $tokens
* @param Template $tpl
* @throws ImproperUseException
* @return string
*/
public static function tagExtends(Tokenizer $tokens, Template $tpl) {
2013-01-25 18:36:16 +04:00
if(!empty($tpl->_extends)) {
throw new ImproperUseException("Only one {extends} allowed");
2013-01-25 18:36:16 +04:00
}
2013-04-28 11:33:36 +04:00
$tpl_name = $tpl->parsePlainArg($tokens, $name);
2013-03-04 12:40:32 +04:00
if(empty($tpl->_extended)) {
$tpl->addPostCompile(__CLASS__."::extendBody");
}
2013-04-04 10:56:44 +04:00
/*if($tpl->getOptions() & Template::EXTENDED) {
$tpl->_compatible = true;
} else {
$tpl->_compatible = false;
}*/
2013-02-13 20:51:27 +04:00
if($name) { // static extends
$tpl->_extends = $tpl->getStorage()->getRawTemplate()->load($name, false);
2013-02-26 23:56:06 +04:00
$tpl->_compatible = &$tpl->_extends->_compatible;
2013-04-04 10:56:44 +04:00
$tpl->addDepend($tpl->_extends);
return "";
} else { // dynamic extends
$tpl->_extends = $tpl_name;
2013-04-04 10:56:44 +04:00
return '$parent = $tpl->getStorage()->getTemplate('.$tpl_name.', \Cytro\Template::EXTENDED);';
2013-01-25 18:36:16 +04:00
}
}
/**
2013-03-04 12:40:32 +04:00
* Post compile action for {extends ...} tag
* @param $body
* @param Template $tpl
*/
public static function extendBody(&$body, $tpl) {
2013-03-04 12:40:32 +04:00
$t = $tpl;
while(isset($t->_extends)) {
$t = $t->_extends;
if(is_object($t)) {
$t->_extended = true;
$t->_compatible = &$tpl->_compatible;
$t->blocks = &$tpl->blocks;
$t->compile();
if(!isset($t->_extends)) { // last item => parent
if(empty($tpl->_compatible)) {
$body = $t->getBody();
} else {
$body = '<?php ob_start(); ?>'.$body.'<?php ob_end_clean(); ?>'.$t->getBody();
}
return;
2013-02-26 23:56:06 +04:00
} else {
2013-03-04 12:40:32 +04:00
$body .= $t->getBody();
2013-02-26 23:56:06 +04:00
}
2013-03-04 12:40:32 +04:00
} else {
2013-02-26 23:56:06 +04:00
$body = '<?php ob_start(); ?>'.$body.'<?php ob_end_clean(); $parent->b = &$tpl->b; $parent->display((array)$tpl); unset($tpl->b, $parent->b); ?>';
2013-03-04 12:40:32 +04:00
return;
}
}
2013-01-25 18:36:16 +04:00
}
2013-02-20 19:51:06 +04:00
/**
2013-04-28 11:33:36 +04:00
* Tag {use ...}
2013-02-20 19:51:06 +04:00
* @param Tokenizer $tokens
* @param Template $tpl
2013-03-04 12:40:32 +04:00
* @throws ImproperUseException
* @return string
2013-02-20 19:51:06 +04:00
*/
public static function tagUse(Tokenizer $tokens, Template $tpl) {
2013-04-28 11:33:36 +04:00
$tpl->parsePlainArg($tokens, $name);
2013-03-04 12:40:32 +04:00
if($name) {
$donor = $tpl->getStorage()->getRawTemplate()->load($name, false);
$donor->_extended = true;
$tpl->_compatible = &$donor->_compatible;
$donor->compile();
if(empty($tpl->_compatible)) {
$tpl->blocks += $donor->blocks;
}
return '?>'.$donor->getBody().'<?php ';
} else {
throw new ImproperUseException('template name must be given explicitly');
//return '';
//return '$donor = $tpl->getStorage()->getTemplate('.$cname.'); ';
//$tpl->_compatible = true;
//$tpl->_ = false;
2013-02-20 19:51:06 +04:00
}
}
/**
* Tag {block ...}
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
* @throws ImproperUseException
*/
2013-01-25 18:36:16 +04:00
public static function tagBlockOpen(Tokenizer $tokens, Scope $scope) {
2013-04-28 11:33:36 +04:00
$p = $scope->tpl->parsePlainArg($tokens, $name);
2013-02-27 20:55:08 +04:00
$scope["name"] = $name;
2013-02-26 23:56:06 +04:00
$scope["cname"] = $p;
2013-01-25 18:36:16 +04:00
}
/**
* Close tag {/block}
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function tagBlockClose($tokens, Scope $scope) {
2013-02-26 23:56:06 +04:00
$tpl = $scope->tpl;
if(isset($tpl->_extends)) { // is child
if($scope["name"]) { // is scalar name
if(!isset($tpl->blocks[ $scope["name"] ])) { // is block still doesn't preset
if($tpl->_compatible) { // is compatible mode
2013-02-27 20:55:08 +04:00
$scope->replaceContent(
'<?php if(empty($tpl->blocks['.$scope["cname"].'])) { '.
'$tpl->b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL.
2013-02-26 23:56:06 +04:00
$scope->getContent().
2013-03-04 12:40:32 +04:00
"<?php };".
"} ?>".PHP_EOL
2013-02-26 23:56:06 +04:00
);
} else {
$tpl->blocks[ $scope["name"] ] = $scope->getContent();
2013-02-27 20:55:08 +04:00
$scope->replaceContent(
'<?php $tpl->b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL.
2013-02-26 23:56:06 +04:00
$scope->getContent().
2013-03-04 12:40:32 +04:00
"<?php }; ?>".PHP_EOL
2013-02-26 23:56:06 +04:00
);
}
}
} else { // dynamic name
2013-02-27 20:55:08 +04:00
$tpl->_compatible = true; // enable compatible mode
$scope->replaceContent(
'<?php if(empty($tpl->b['.$scope["cname"].'])) { '.
'$tpl->b['.$scope["cname"].'] = function($tpl) { ?>'.PHP_EOL.
2013-02-26 23:56:06 +04:00
$scope->getContent().
2013-03-04 12:40:32 +04:00
"<?php };".
"} ?>".PHP_EOL
2013-02-26 23:56:06 +04:00
);
}
} else { // is parent
2013-02-26 23:56:06 +04:00
if(isset($tpl->blocks[ $scope["name"] ])) { // has block
2013-02-27 20:55:08 +04:00
if($tpl->_compatible) { // compatible mode enabled
$scope->replaceContent(
'<?php if(isset($tpl->b['.$scope["cname"].'])) { echo $tpl->b['.$scope["cname"].']->__invoke($tpl); } else {?>'.PHP_EOL.
2013-03-04 12:40:32 +04:00
$tpl->blocks[ $scope["name"] ].
2013-02-27 20:55:08 +04:00
'<?php } ?>'.PHP_EOL
2013-02-26 23:56:06 +04:00
);
2013-03-04 12:40:32 +04:00
2013-02-26 23:56:06 +04:00
} else {
2013-02-27 20:55:08 +04:00
$scope->replaceContent($tpl->blocks[ $scope["name"] ]);
2013-02-15 01:49:26 +04:00
}
2013-02-27 20:55:08 +04:00
} elseif(isset($tpl->_extended) && $tpl->_compatible || empty($tpl->_extended)) {
$scope->replaceContent(
'<?php if(isset($tpl->b['.$scope["cname"].'])) { echo $tpl->b['.$scope["cname"].']->__invoke($tpl); } else {?>'.PHP_EOL.
2013-02-26 23:56:06 +04:00
$scope->getContent().
2013-02-27 20:55:08 +04:00
'<?php } ?>'.PHP_EOL
);
}
}
2013-02-26 23:56:06 +04:00
return '';
2013-01-25 18:36:16 +04:00
}
2013-02-20 19:51:06 +04:00
public static function tagParent($tokens, Scope $scope) {
if(empty($scope->tpl->_extends)) {
throw new ImproperUseException("Tag {parent} may be declared in childs");
}
}
2013-01-25 18:36:16 +04:00
/**
* Standard close tag {/...}
*
* @static
* @return string
*/
public static function stdClose() {
return '}';
}
2013-01-25 18:36:16 +04:00
/**
* Standard function parser
2013-01-25 18:36:16 +04:00
*
* @static
* @param $function
* @param Tokenizer $tokens
* @param Template $tpl
* @return string
*/
public static function stdFuncParser($function, Tokenizer $tokens, Template $tpl) {
2013-02-23 02:03:05 +04:00
return "echo $function(".self::toArray($tpl->parseParams($tokens)).', $tpl);';
2013-01-25 18:36:16 +04:00
}
/**
* Smart function parser
*
* @static
* @param $function
* @param Tokenizer $tokens
* @param Template $tpl
* @return string
*/
public static function smartFuncParser($function, Tokenizer $tokens, Template $tpl) {
if(strpos($function, "::")) {
list($class, $method) = explode("::", $function, 2);
$ref = new \ReflectionMethod($class, $method);
} else {
$ref = new \ReflectionFunction($function);
}
$args = array();
$params = $tpl->parseParams($tokens);
foreach($ref->getParameters() as $param) {
if(isset($params[ $param->getName() ])) {
$args[] = $params[ $param->getName() ];
} elseif(isset($params[ $param->getPosition() ])) {
$args[] = $params[ $param->getPosition() ];
} elseif($param->isOptional()) {
$args[] = $param->getDefaultValue();
}
}
return "echo $function(".implode(", ", $args).');';
}
2013-01-25 18:36:16 +04:00
/**
* Standard function open tag parser
*
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function stdFuncOpen(Tokenizer $tokens, Scope $scope) {
2013-02-23 02:03:05 +04:00
$scope["params"] = self::toArray($scope->tpl->parseParams($tokens));
2013-01-25 18:36:16 +04:00
return 'ob_start();';
}
/**
* Standard function close tag parser
*
* @static
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
public static function stdFuncClose($tokens, Scope $scope) {
2013-01-25 18:36:16 +04:00
return "echo ".$scope["function"].'('.$scope["params"].', ob_get_clean(), $tpl);';
}
2013-02-23 02:03:05 +04:00
/**
* Convert array of code to string array
* @param $params
* @return string
*/
public static function toArray($params) {
2013-01-25 18:36:16 +04:00
$_code = array();
foreach($params as $k => $v) {
$_code[] = '"'.$k.'" => '.$v;
}
return 'array('.implode(",", $_code).')';
}
2013-03-17 14:37:23 +04:00
public static function varOpen(Tokenizer $tokens, Scope $scope) {
$scope->is_closed = true;
return self::setVar($tokens, $scope->tpl).';';
}
public static function varClose() {
return '';
}
2013-01-25 18:36:16 +04:00
/**
* Tag {var ...}
*
* @static
* @param Tokenizer $tokens
* @param Template $tpl
* @return string
*/
2013-03-17 14:37:23 +04:00
//public static function assign(Tokenizer $tokens, Template $tpl) {
// return self::setVar($tokens, $tpl).';';
//}
2013-01-25 18:36:16 +04:00
/**
2013-02-20 19:51:06 +04:00
* Set variable expression
* @param Tokenizer $tokens
* @param Template $tpl
* @param bool $allow_array
* @return string
*/
2013-01-25 18:36:16 +04:00
public static function setVar(Tokenizer $tokens, Template $tpl, $allow_array = true) {
2013-03-17 14:37:23 +04:00
$var = $tpl->parseVariable($tokens, $tpl::DENY_MODS);
2013-01-25 18:36:16 +04:00
$tokens->get('=');
$tokens->next();
if($tokens->is("[") && $allow_array) {
return $var.'='.$tpl->parseArray($tokens);
} else {
return $var.'='.$tpl->parseExp($tokens, true);
}
}
public static function filterOpen(Tokenizer $tokens, Scope $scope) {
$scope["filter"] = $scope->tpl->parseModifier($tokens, "ob_get_clean()");
return "ob_start();";
}
public static function filterClose($tokens, Scope $scope) {
return "echo ".$scope["filter"].";";
}
/**
* @param Tokenizer $tokens
* @param Scope $scope
* @return string
*/
2013-02-20 19:51:06 +04:00
public static function captureOpen(Tokenizer $tokens, Scope $scope) {
if($tokens->is("|")) {
$scope["value"] = $scope->tpl->parseModifier($tokens, "ob_get_clean()");
} else {
$scope["value"] = "ob_get_clean()";
}
2013-03-17 14:37:23 +04:00
$scope["var"] = $scope->tpl->parseVariable($tokens, Template::DENY_MODS);
2013-02-20 19:51:06 +04:00
return "ob_start();";
}
public static function captureClose($tokens, Scope $scope) {
return $scope["var"]." = ".$scope["value"].";";
}
/**
* Tag {cycle}
*
* @param Tokenizer $tokens
* @param Template $tpl
* @return string
* @throws ImproperUseException
*/
2013-02-20 19:51:06 +04:00
public static function tagCycle(Tokenizer $tokens, Template $tpl) {
$exp = $tpl->parseExp($tokens, true);
if($tokens->valid()) {
$p = $tpl->parseParams($tokens);
if(empty($p["index"])) {
throw new ImproperUseException("Cycle may contain only index attribute");
} else {
return __CLASS__.'::cycle((array)'.$exp.', '.$p["index"].');';
}
} else {
2013-03-15 00:57:28 +04:00
$var = $tpl->tmpVar();
return "is_array($exp) ? ".__CLASS__.'::cycle('.$exp.", isset($var) ? $var++ : ($var = 0) ) : $exp";
2013-02-20 19:51:06 +04:00
}
}
2013-03-15 00:57:28 +04:00
public static function cycle($vals, $index) {
return $vals[$index % count($vals)];
}
2013-02-21 22:51:24 +04:00
/**
* Import macros from templates
*
* @param Tokenizer $tokens
* @param Template $tpl
2013-03-15 00:12:02 +04:00
* @throws UnexpectedTokenException
2013-02-21 22:51:24 +04:00
* @throws ImproperUseException
2013-03-15 00:12:02 +04:00
* @return string
2013-02-21 22:51:24 +04:00
*/
public static function tagImport(Tokenizer $tokens, Template $tpl) {
$import = array();
if($tokens->is('[')) {
$tokens->next();
while($tokens->valid()) {
if($tokens->is(Tokenizer::MACRO_STRING)) {
$import[ $tokens->current() ] = true;
$tokens->next();
} elseif($tokens->is(']')) {
$tokens->next();
break;
2013-03-15 00:12:02 +04:00
} elseif($tokens->is(',')) {
$tokens->next();
} else {
break;
}
}
if($tokens->current() != "from") {
2013-03-15 00:12:02 +04:00
throw new UnexpectedTokenException($tokens);
}
$tokens->next();
}
2013-04-28 11:33:36 +04:00
$tpl->parsePlainArg($tokens, $name);
2013-02-21 22:51:24 +04:00
if(!$name) {
throw new ImproperUseException("Invalid usage tag {import}");
}
2013-02-23 13:29:20 +04:00
if($tokens->is(T_AS)) {
$alias = $tokens->next()->get(Tokenizer::MACRO_STRING);
if($alias === "macro") {
$alias = "";
}
2013-02-23 13:29:20 +04:00
$tokens->next();
} else {
$alias = "";
}
2013-02-21 22:51:24 +04:00
$donor = $tpl->getStorage()->getRawTemplate()->load($name, true);
2013-02-23 13:29:20 +04:00
if($donor->macros) {
foreach($donor->macros as $name => $macro) {
if($p = strpos($name, ".")) {
$name = substr($name, $p);
}
2013-03-15 00:12:02 +04:00
if($import && !isset($import[$name])) {
continue;
}
2013-02-23 13:29:20 +04:00
if($alias) {
$name = $alias.'.'.$name;
}
2013-03-15 00:12:02 +04:00
2013-02-23 13:29:20 +04:00
$tpl->macros[$name] = $macro;
}
2013-02-21 22:51:24 +04:00
$tpl->addDepend($donor);
}
return '';
2013-02-21 22:51:24 +04:00
}
/**
* Define macro
2013-02-21 22:51:24 +04:00
*
* @param Tokenizer $tokens
* @param Scope $scope
2013-02-22 00:05:20 +04:00
* @throws ImproperUseException
2013-02-21 22:51:24 +04:00
*/
2013-02-22 00:05:20 +04:00
public static function macroOpen(Tokenizer $tokens, Scope $scope) {
2013-02-23 02:03:05 +04:00
$scope["name"] = $tokens->get(Tokenizer::MACRO_STRING);
$scope["args"] = array();
$scope["defaults"] = array();
if(!$tokens->valid()) {
return;
}
$tokens->next()->need('(')->next();
if($tokens->is(')')) {
return;
}
while($tokens->is(Tokenizer::MACRO_STRING)) {
$scope["args"][] = $param = $tokens->getAndNext();
if($tokens->is('=')) {
2013-02-23 13:29:20 +04:00
$tokens->next();
2013-02-23 02:03:05 +04:00
if($tokens->is(T_CONSTANT_ENCAPSED_STRING, T_LNUMBER, T_DNUMBER) || $tokens->isSpecialVal()) {
2013-02-23 13:29:20 +04:00
$scope["defaults"][ $param ] = $tokens->getAndNext();
2013-02-23 02:03:05 +04:00
} else {
throw new ImproperUseException("Macro parameters may have only scalar defaults");
}
}
$tokens->skipIf(',');
2013-02-21 22:51:24 +04:00
}
2013-02-23 02:03:05 +04:00
$tokens->skipIf(')');
return;
2013-02-21 22:51:24 +04:00
}
2013-02-23 02:03:05 +04:00
/**
* @param Tokenizer $tokens
* @param Scope $scope
*/
2013-02-22 00:05:20 +04:00
public static function macroClose(Tokenizer $tokens, Scope $scope) {
2013-02-23 02:03:05 +04:00
$scope->tpl->macros[ $scope["name"] ] = array(
"body" => $content = $scope->getContent(),
"args" => $scope["args"],
"defaults" => $scope["defaults"]
);
$scope->tpl->_body = substr($scope->tpl->_body, 0, strlen($scope->tpl->_body) - strlen($content));
2013-02-21 22:51:24 +04:00
}
2013-01-25 18:36:16 +04:00
}