/modules/kotal/vendor/phptal/PHPTAL/Php/Transformer.php
PHP | 418 lines | 305 code | 24 blank | 89 comment | 63 complexity | b061eb928f9ddaaf6c8c7058950c7d50 MD5 | raw file
Possible License(s): LGPL-2.1, MIT, BSD-3-Clause
- <?php
- /**
- * PHPTAL templating engine
- *
- * PHP Version 5
- *
- * @category HTML
- * @package PHPTAL
- * @author Laurent Bedubourg <lbedubourg@motion-twin.com>
- * @author Kornel LesiĹski <kornel@aardvarkmedia.co.uk>
- * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
- * @version SVN: $Id$
- * @link http://phptal.org/
- */
- /**
- * Tranform php: expressions into their php equivalent.
- *
- * This transformer produce php code for expressions like :
- *
- * - a.b["key"].c().someVar[10].foo()
- * - (a or b) and (c or d)
- * - not myBool
- * - ...
- *
- * The $prefix variable may be changed to change the context lookup.
- *
- * example:
- *
- * $res = PHPTAL_Php_Transformer::transform('a.b.c[x]', '$ctx->');
- * $res == '$ctx->a->b->c[$ctx->x]';
- *
- * @package PHPTAL
- * @subpackage Php
- * @author Laurent Bedubourg <lbedubourg@motion-twin.com>
- */
- class PHPTAL_Php_Transformer
- {
- const ST_WHITE = -1; // start of string or whitespace
- const ST_NONE = 0; // pass through (operators, parens, etc.)
- const ST_STR = 1; // 'foo'
- const ST_ESTR = 2; // "foo ${x} bar"
- const ST_VAR = 3; // abcd
- const ST_NUM = 4; // 123.02
- const ST_EVAL = 5; // $somevar
- const ST_MEMBER = 6; // abcd.x
- const ST_STATIC = 7; // class::[$]static|const
- const ST_DEFINE = 8; // @MY_DEFINE
- /**
- * transform PHPTAL's php-like syntax into real PHP
- */
- public static function transform($str, $prefix='$')
- {
- $len = strlen($str);
- $state = self::ST_WHITE;
- $result = '';
- $i = 0;
- $inString = false;
- $backslashed = false;
- $instanceof = false;
- $eval = false;
- for ($i = 0; $i <= $len; $i++) {
- if ($i == $len) $c = "\0";
- else $c = $str[$i];
- switch ($state) {
- // after whitespace a variable-variable may start, ${var} â $ctx->{$ctx->var}
- case self::ST_WHITE:
- if ($c === '$' && $i+1 < $len && $str[$i+1] === '{')
- {
- $result .= $prefix;
- $state = self::ST_NONE;
- continue;
- }
- /* NO BREAK - ST_WHITE is almost the same as ST_NONE */
- // no specific state defined, just eat char and see what to do with it.
- case self::ST_NONE:
- // begin of eval without {
- if ($c === '$' && $i+1 < $len && self::isAlpha($str[$i+1])) {
- $state = self::ST_EVAL;
- $mark = $i+1;
- $result .= $prefix.'{';
- }
- elseif (self::isDigit($c))
- {
- $state = self::ST_NUM;
- $mark = $i;
- }
- // that an alphabetic char, then it should be the begining
- // of a var or static
- // && !self::isDigit($c) checked earlier
- elseif (self::isVarNameChar($c)) {
- $state = self::ST_VAR;
- $mark = $i;
- }
- // begining of double quoted string
- elseif ($c === '"') {
- $state = self::ST_ESTR;
- $mark = $i;
- $inString = true;
- }
- // begining of single quoted string
- elseif ($c === '\'') {
- $state = self::ST_STR;
- $mark = $i;
- $inString = true;
- }
- // closing a method, an array access or an evaluation
- elseif ($c === ')' || $c === ']' || $c === '}') {
- $result .= $c;
- // if next char is dot then an object member must
- // follow
- if ($i+1 < $len && $str[$i+1] === '.') {
- $result .= '->';
- $state = self::ST_MEMBER;
- $mark = $i+2;
- $i+=2;
- }
- }
- // @ is an access to some defined variable
- elseif ($c === '@') {
- $state = self::ST_DEFINE;
- $mark = $i+1;
- }
- elseif (ctype_space($c)) {
- $state = self::ST_WHITE;
- $result .= $c;
- }
- // character we don't mind about
- else {
- $result .= $c;
- }
- break;
- // $xxx
- case self::ST_EVAL:
- if (!self::isVarNameChar($c)) {
- $result .= $prefix . substr($str, $mark, $i-$mark);
- $result .= '}';
- $state = self::ST_NONE;
- }
- break;
- // single quoted string
- case self::ST_STR:
- if ($c === '\\') {
- $backslashed = true;
- } elseif ($backslashed) {
- $backslashed = false;
- }
- // end of string, back to none state
- elseif ($c === '\'') {
- $result .= substr($str, $mark, $i-$mark+1);
- $inString = false;
- $state = self::ST_NONE;
- }
- break;
- // double quoted string
- case self::ST_ESTR:
- if ($c === '\\') {
- $backslashed = true;
- } elseif ($backslashed) {
- $backslashed = false;
- }
- // end of string, back to none state
- elseif ($c === '"') {
- $result .= substr($str, $mark, $i-$mark+1);
- $inString = false;
- $state = self::ST_NONE;
- }
- // instring interpolation, search } and transform the
- // interpollation to insert it into the string
- elseif ($c === '$' && $i+1 < $len && $str[$i+1] === '{') {
- $result .= substr($str, $mark, $i-$mark) . '{';
- $sub = 0;
- for ($j = $i; $j<$len; $j++) {
- if ($str[$j] === '{') {
- $sub++;
- } elseif ($str[$j] === '}' && (--$sub) == 0) {
- $part = substr($str, $i+2, $j-$i-2);
- $result .= self::transform($part, $prefix);
- $i = $j;
- $mark = $i;
- }
- }
- }
- break;
- // var state
- case self::ST_VAR:
- if (self::isVarNameChar($c)) {
- }
- // end of var, begin of member (method or var)
- elseif ($c === '.') {
- $result .= $prefix . substr($str, $mark, $i-$mark);
- $result .= '->';
- $state = self::ST_MEMBER;
- $mark = $i+1;
- }
- // static call, the var is a class name
- elseif ($c === ':' && $i+1 < $len && $str[$i+1] === ':') {
- $result .= substr($str, $mark, $i-$mark+1);
- $mark = $i+1;
- $i++;
- $state = self::ST_STATIC;
- break;
- }
- // function invocation, the var is a function name
- elseif ($c === '(') {
- $result .= substr($str, $mark, $i-$mark+1);
- $state = self::ST_NONE;
- }
- // array index, the var is done
- elseif ($c === '[') {
- if ($str[$mark]==='_') { // superglobal?
- $result .= '$' . substr($str, $mark, $i-$mark+1);
- } else {
- $result .= $prefix . substr($str, $mark, $i-$mark+1);
- }
- $state = self::ST_NONE;
- }
- // end of var with non-var-name character, handle keywords
- // and populate the var name
- else {
- $var = substr($str, $mark, $i-$mark);
- $low = strtolower($var);
- // boolean and null
- if ($low === 'true' || $low === 'false' || $low === 'null') {
- $result .= $var;
- }
- // lt, gt, ge, eq, ...
- elseif (array_key_exists($low, self::$TranslationTable)) {
- $result .= self::$TranslationTable[$low];
- }
- // instanceof keyword
- elseif ($low === 'instanceof') {
- $result .= $var;
- $instanceof = true;
- }
- // previous was instanceof
- elseif ($instanceof) {
- // last was instanceof, this var is a class name
- $result .= $var;
- $instanceof = false;
- }
- // regular variable
- else {
- $result .= $prefix . $var;
- }
- $i--;
- $state = self::ST_NONE;
- }
- break;
- // object member
- case self::ST_MEMBER:
- if (self::isVarNameChar($c)) {
- }
- // eval mode ${foo}
- elseif ($c === '$' && ($i >= $len-2 || $str[$i+1] !== '{')) {
- $result .= '{' . $prefix;
- $mark++;
- $eval = true;
- }
- // x.${foo} x->{foo}
- elseif ($c === '$') {
- $mark++;
- }
- // end of var member var, begin of new member
- elseif ($c === '.') {
- $result .= substr($str, $mark, $i-$mark);
- if ($eval) { $result .='}'; $eval = false; }
- $result .= '->';
- $mark = $i+1;
- $state = self::ST_MEMBER;
- }
- // begin of static access
- elseif ($c === ':') {
- $result .= substr($str, $mark, $i-$mark+1);
- if ($eval) { $result .='}'; $eval = false; }
- $state = self::ST_STATIC;
- break;
- }
- // the member is a method or an array
- elseif ($c === '(' || $c === '[') {
- $result .= substr($str, $mark, $i-$mark+1);
- if ($eval) { $result .='}'; $eval = false; }
- $state = self::ST_NONE;
- }
- // regular end of member, it is a var
- else {
- $var = substr($str, $mark, $i-$mark);
- if ($var !== '' && !preg_match('/^[a-z][a-z0-9_\x7f-\xff]*$/i',$var)) {
- throw new PHPTAL_ParserException("Invalid field name '$var' in expression php:$str");
- }
- $result .= $var;
- if ($eval) { $result .='}'; $eval = false; }
- $state = self::ST_NONE;
- $i--;
- }
- break;
- // wait for separator
- case self::ST_DEFINE:
- if (self::isVarNameChar($c)) {
- } else {
- $state = self::ST_NONE;
- $result .= substr($str, $mark, $i-$mark);
- $i--;
- }
- break;
- // static call, can be const, static var, static method
- // Klass::$static
- // Klass::const
- // Kclass::staticMethod()
- //
- case self::ST_STATIC:
- if (self::isVarNameChar($c)) {
- }
- // static var
- elseif ($c === '$') {
- }
- // end of static var which is an object and begin of member
- elseif ($c === '.') {
- $result .= substr($str, $mark, $i-$mark);
- $result .= '->';
- $mark = $i+1;
- $state = self::ST_MEMBER;
- }
- // end of static var which is a class name
- elseif ($c === ':') {
- $result .= substr($str, $mark, $i-$mark+1);
- $state = self::ST_STATIC;
- break;
- }
- // static method or array
- elseif ($c === '(' || $c === '[') {
- $result .= substr($str, $mark, $i-$mark+1);
- $state = self::ST_NONE;
- }
- // end of static var or const
- else {
- $result .= substr($str, $mark, $i-$mark);
- $state = self::ST_NONE;
- $i--;
- }
- break;
- // numeric value
- case self::ST_NUM:
- if (!self::isDigitCompound($c)) {
- $var = substr($str, $mark, $i-$mark);
- if (self::isAlpha($c) || $c === '_') {
- throw new PHPTAL_ParserException("Syntax error in number '$var$c' in expression php:$str");
- }
- if (!is_numeric($var)) {
- throw new PHPTAL_ParserException("Syntax error in number '$var' in expression php:$str");
- }
- $result .= $var;
- $state = self::ST_NONE;
- $i--;
- }
- break;
- }
- }
- $result = trim($result);
- // CodeWriter doesn't like expressions that look like blocks
- if ($result[strlen($result)-1] === '}') return '('.$result.')';
- return $result;
- }
- private static function isAlpha($c)
- {
- $c = strtolower($c);
- return $c >= 'a' && $c <= 'z';
- }
- private static function isDigit($c)
- {
- return ($c >= '0' && $c <= '9');
- }
- private static function isDigitCompound($c)
- {
- return ($c >= '0' && $c <= '9' || $c === '.');
- }
- private static function isVarNameChar($c)
- {
- return self::isAlpha($c) || ($c >= '0' && $c <= '9') || $c === '_' || $c === '\\';
- }
- private static $TranslationTable = array(
- 'not' => '!',
- 'ne' => '!=',
- 'and' => '&&',
- 'or' => '||',
- 'lt' => '<',
- 'gt' => '>',
- 'ge' => '>=',
- 'le' => '<=',
- 'eq' => '==',
- );
- }