Библиотека для cis, online, cms1
This commit is contained in:
commit
3c2e614d87
269 changed files with 39854 additions and 0 deletions
384
PHPTAL/Php/Transformer.php
Normal file
384
PHPTAL/Php/Transformer.php
Normal file
|
|
@ -0,0 +1,384 @@
|
|||
<?php
|
||||
/* vim: set expandtab tabstop=4 shiftwidth=4: */
|
||||
//
|
||||
// Copyright (c) 2004-2005 Laurent Bedubourg
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
// Authors: Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
//
|
||||
|
||||
|
||||
/**
|
||||
* 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.php
|
||||
* @author Laurent Bedubourg <lbedubourg@motion-twin.com>
|
||||
*/
|
||||
class PHPTAL_Php_Transformer
|
||||
{
|
||||
const ST_NONE = 0;
|
||||
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
|
||||
|
||||
public static function transform( $str, $prefix='$' )
|
||||
{
|
||||
|
||||
//
|
||||
// Here comes the good old state machine.
|
||||
// TODO: benchmark this version and then benchmark a refactored version
|
||||
// with states behaviour separated into methods, keep the fastest.
|
||||
//
|
||||
|
||||
$len = strlen($str);
|
||||
$state = self::ST_NONE;
|
||||
$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) {
|
||||
// no state defined, just eat char and see what to do with it.
|
||||
case self::ST_NONE:
|
||||
// begin of eval without {
|
||||
if ($c == '$' && $i < $len && self::isAlpha($str[$i+1])){
|
||||
$state = self::ST_EVAL;
|
||||
$mark = $i+1;
|
||||
$result .= $prefix.'{';
|
||||
}
|
||||
// that an alphabetic char, then it should be the begining
|
||||
// of a var
|
||||
else if (self::isAlpha($c) || $c==='_') {
|
||||
$state = self::ST_VAR;
|
||||
$mark = $i;
|
||||
}
|
||||
// begining of double quoted string
|
||||
else if ($c == '"') {
|
||||
$state = self::ST_ESTR;
|
||||
$mark = $i;
|
||||
$inString = true;
|
||||
}
|
||||
// begining of single quoted string
|
||||
else if ($c == '\'') {
|
||||
$state = self::ST_STR;
|
||||
$mark = $i;
|
||||
$inString = true;
|
||||
}
|
||||
// closing a method, an array access or an evaluation
|
||||
else if ($c == ')' || $c == ']' || $c == '}') {
|
||||
$result .= $c;
|
||||
// if next char is dot then an object member must
|
||||
// follow
|
||||
if ($i < $len-1 && $str[$i+1] == '.') {
|
||||
$result .= '->';
|
||||
$state = self::ST_MEMBER;
|
||||
$mark = $i+2;
|
||||
$i+=2;
|
||||
}
|
||||
}
|
||||
// @ is an access to some defined variable
|
||||
else if ($c == '@') {
|
||||
$state = self::ST_DEFINE;
|
||||
$mark = $i+1;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
else if ($backslashed) {
|
||||
$backslashed = false;
|
||||
}
|
||||
// end of string, back to none state
|
||||
else if ($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;
|
||||
}
|
||||
else if ($backslashed) {
|
||||
$backslashed = false;
|
||||
}
|
||||
// end of string, back to none state
|
||||
else if ($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
|
||||
else if ($c == '$' && $i < $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)
|
||||
else if ($c == '.') {
|
||||
$result .= $prefix . substr( $str, $mark, $i-$mark );
|
||||
$result .= '->';
|
||||
$state = self::ST_MEMBER;
|
||||
$mark = $i+1;
|
||||
}
|
||||
// static call, the var is a class name
|
||||
else if ($c == ':') {
|
||||
$result .= substr( $str, $mark, $i-$mark+1 );
|
||||
$mark = $i+1;
|
||||
$i++;
|
||||
$state = self::ST_STATIC;
|
||||
break;
|
||||
}
|
||||
// function invocation, the var is a function name
|
||||
else if ($c == '(') {
|
||||
$result .= substr( $str, $mark, $i-$mark+1 );
|
||||
$state = self::ST_NONE;
|
||||
}
|
||||
// array index, the var is done
|
||||
else if ($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, ...
|
||||
else if (array_key_exists($low, self::$TranslationTable)){
|
||||
$result .= self::$TranslationTable[$low];
|
||||
}
|
||||
// instanceof keyword
|
||||
else if ($low == 'instanceof'){
|
||||
$result .= $var;
|
||||
$instanceOf = true;
|
||||
}
|
||||
// previous was instanceof
|
||||
else if ($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}
|
||||
else if ($c == '$') {
|
||||
$result .= '{' . $prefix;
|
||||
$mark++;
|
||||
$eval = true;
|
||||
}
|
||||
// end of var member var, begin of new member
|
||||
else if ($c == '.') {
|
||||
$result .= substr( $str, $mark, $i-$mark );
|
||||
if ($eval) { $result .='}'; $eval = false; }
|
||||
$result .= '->';
|
||||
$mark = $i+1;
|
||||
$state = self::ST_MEMBER;
|
||||
}
|
||||
// begin of static access
|
||||
else if ($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
|
||||
else if ($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 {
|
||||
$result .= substr( $str, $mark, $i-$mark );
|
||||
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
|
||||
else if ($c == '$') {
|
||||
}
|
||||
// end of static var which is an object and begin of member
|
||||
else if ($c == '.') {
|
||||
$result .= substr( $str, $mark, $i-$mark );
|
||||
$result .= '->';
|
||||
$mark = $i+1;
|
||||
$state = self::ST_MEMBER;
|
||||
}
|
||||
// end of static var which is a class name
|
||||
else if ($c == ':') {
|
||||
$result .= substr( $str, $mark, $i-$mark+1 );
|
||||
$state = self::ST_STATIC;
|
||||
break;
|
||||
}
|
||||
// static method or array
|
||||
else if ($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)) {
|
||||
$result .= substr( $str, $mark, $i-$mark );
|
||||
$state = self::ST_NONE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return trim($result);
|
||||
}
|
||||
|
||||
private static function isAlpha($c)
|
||||
{
|
||||
$c = strtolower($c);
|
||||
return $c >= 'a' && $c <= 'z';
|
||||
}
|
||||
|
||||
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 == '_';
|
||||
}
|
||||
|
||||
private static $TranslationTable = array(
|
||||
'not' => '!',
|
||||
'ne' => '!=',
|
||||
'and' => '&&',
|
||||
'or' => '||',
|
||||
'lt' => '<',
|
||||
'gt' => '>',
|
||||
'ge' => '>=',
|
||||
'le' => '<=',
|
||||
'eq' => '==',
|
||||
);
|
||||
}
|
||||
|
||||
?>
|
||||
Loading…
Add table
Add a link
Reference in a new issue