PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/kotal/vendor/phptal/PHPTAL/Php/Transformer.php

https://bitbucket.org/chrispiechowicz/zepto
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
  1. <?php
  2. /**
  3. * PHPTAL templating engine
  4. *
  5. * PHP Version 5
  6. *
  7. * @category HTML
  8. * @package PHPTAL
  9. * @author Laurent Bedubourg <lbedubourg@motion-twin.com>
  10. * @author Kornel Lesiński <kornel@aardvarkmedia.co.uk>
  11. * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
  12. * @version SVN: $Id$
  13. * @link http://phptal.org/
  14. */
  15. /**
  16. * Tranform php: expressions into their php equivalent.
  17. *
  18. * This transformer produce php code for expressions like :
  19. *
  20. * - a.b["key"].c().someVar[10].foo()
  21. * - (a or b) and (c or d)
  22. * - not myBool
  23. * - ...
  24. *
  25. * The $prefix variable may be changed to change the context lookup.
  26. *
  27. * example:
  28. *
  29. * $res = PHPTAL_Php_Transformer::transform('a.b.c[x]', '$ctx->');
  30. * $res == '$ctx->a->b->c[$ctx->x]';
  31. *
  32. * @package PHPTAL
  33. * @subpackage Php
  34. * @author Laurent Bedubourg <lbedubourg@motion-twin.com>
  35. */
  36. class PHPTAL_Php_Transformer
  37. {
  38. const ST_WHITE = -1; // start of string or whitespace
  39. const ST_NONE = 0; // pass through (operators, parens, etc.)
  40. const ST_STR = 1; // 'foo'
  41. const ST_ESTR = 2; // "foo ${x} bar"
  42. const ST_VAR = 3; // abcd
  43. const ST_NUM = 4; // 123.02
  44. const ST_EVAL = 5; // $somevar
  45. const ST_MEMBER = 6; // abcd.x
  46. const ST_STATIC = 7; // class::[$]static|const
  47. const ST_DEFINE = 8; // @MY_DEFINE
  48. /**
  49. * transform PHPTAL's php-like syntax into real PHP
  50. */
  51. public static function transform($str, $prefix='$')
  52. {
  53. $len = strlen($str);
  54. $state = self::ST_WHITE;
  55. $result = '';
  56. $i = 0;
  57. $inString = false;
  58. $backslashed = false;
  59. $instanceof = false;
  60. $eval = false;
  61. for ($i = 0; $i <= $len; $i++) {
  62. if ($i == $len) $c = "\0";
  63. else $c = $str[$i];
  64. switch ($state) {
  65. // after whitespace a variable-variable may start, ${var} → $ctx->{$ctx->var}
  66. case self::ST_WHITE:
  67. if ($c === '$' && $i+1 < $len && $str[$i+1] === '{')
  68. {
  69. $result .= $prefix;
  70. $state = self::ST_NONE;
  71. continue;
  72. }
  73. /* NO BREAK - ST_WHITE is almost the same as ST_NONE */
  74. // no specific state defined, just eat char and see what to do with it.
  75. case self::ST_NONE:
  76. // begin of eval without {
  77. if ($c === '$' && $i+1 < $len && self::isAlpha($str[$i+1])) {
  78. $state = self::ST_EVAL;
  79. $mark = $i+1;
  80. $result .= $prefix.'{';
  81. }
  82. elseif (self::isDigit($c))
  83. {
  84. $state = self::ST_NUM;
  85. $mark = $i;
  86. }
  87. // that an alphabetic char, then it should be the begining
  88. // of a var or static
  89. // && !self::isDigit($c) checked earlier
  90. elseif (self::isVarNameChar($c)) {
  91. $state = self::ST_VAR;
  92. $mark = $i;
  93. }
  94. // begining of double quoted string
  95. elseif ($c === '"') {
  96. $state = self::ST_ESTR;
  97. $mark = $i;
  98. $inString = true;
  99. }
  100. // begining of single quoted string
  101. elseif ($c === '\'') {
  102. $state = self::ST_STR;
  103. $mark = $i;
  104. $inString = true;
  105. }
  106. // closing a method, an array access or an evaluation
  107. elseif ($c === ')' || $c === ']' || $c === '}') {
  108. $result .= $c;
  109. // if next char is dot then an object member must
  110. // follow
  111. if ($i+1 < $len && $str[$i+1] === '.') {
  112. $result .= '->';
  113. $state = self::ST_MEMBER;
  114. $mark = $i+2;
  115. $i+=2;
  116. }
  117. }
  118. // @ is an access to some defined variable
  119. elseif ($c === '@') {
  120. $state = self::ST_DEFINE;
  121. $mark = $i+1;
  122. }
  123. elseif (ctype_space($c)) {
  124. $state = self::ST_WHITE;
  125. $result .= $c;
  126. }
  127. // character we don't mind about
  128. else {
  129. $result .= $c;
  130. }
  131. break;
  132. // $xxx
  133. case self::ST_EVAL:
  134. if (!self::isVarNameChar($c)) {
  135. $result .= $prefix . substr($str, $mark, $i-$mark);
  136. $result .= '}';
  137. $state = self::ST_NONE;
  138. }
  139. break;
  140. // single quoted string
  141. case self::ST_STR:
  142. if ($c === '\\') {
  143. $backslashed = true;
  144. } elseif ($backslashed) {
  145. $backslashed = false;
  146. }
  147. // end of string, back to none state
  148. elseif ($c === '\'') {
  149. $result .= substr($str, $mark, $i-$mark+1);
  150. $inString = false;
  151. $state = self::ST_NONE;
  152. }
  153. break;
  154. // double quoted string
  155. case self::ST_ESTR:
  156. if ($c === '\\') {
  157. $backslashed = true;
  158. } elseif ($backslashed) {
  159. $backslashed = false;
  160. }
  161. // end of string, back to none state
  162. elseif ($c === '"') {
  163. $result .= substr($str, $mark, $i-$mark+1);
  164. $inString = false;
  165. $state = self::ST_NONE;
  166. }
  167. // instring interpolation, search } and transform the
  168. // interpollation to insert it into the string
  169. elseif ($c === '$' && $i+1 < $len && $str[$i+1] === '{') {
  170. $result .= substr($str, $mark, $i-$mark) . '{';
  171. $sub = 0;
  172. for ($j = $i; $j<$len; $j++) {
  173. if ($str[$j] === '{') {
  174. $sub++;
  175. } elseif ($str[$j] === '}' && (--$sub) == 0) {
  176. $part = substr($str, $i+2, $j-$i-2);
  177. $result .= self::transform($part, $prefix);
  178. $i = $j;
  179. $mark = $i;
  180. }
  181. }
  182. }
  183. break;
  184. // var state
  185. case self::ST_VAR:
  186. if (self::isVarNameChar($c)) {
  187. }
  188. // end of var, begin of member (method or var)
  189. elseif ($c === '.') {
  190. $result .= $prefix . substr($str, $mark, $i-$mark);
  191. $result .= '->';
  192. $state = self::ST_MEMBER;
  193. $mark = $i+1;
  194. }
  195. // static call, the var is a class name
  196. elseif ($c === ':' && $i+1 < $len && $str[$i+1] === ':') {
  197. $result .= substr($str, $mark, $i-$mark+1);
  198. $mark = $i+1;
  199. $i++;
  200. $state = self::ST_STATIC;
  201. break;
  202. }
  203. // function invocation, the var is a function name
  204. elseif ($c === '(') {
  205. $result .= substr($str, $mark, $i-$mark+1);
  206. $state = self::ST_NONE;
  207. }
  208. // array index, the var is done
  209. elseif ($c === '[') {
  210. if ($str[$mark]==='_') { // superglobal?
  211. $result .= '$' . substr($str, $mark, $i-$mark+1);
  212. } else {
  213. $result .= $prefix . substr($str, $mark, $i-$mark+1);
  214. }
  215. $state = self::ST_NONE;
  216. }
  217. // end of var with non-var-name character, handle keywords
  218. // and populate the var name
  219. else {
  220. $var = substr($str, $mark, $i-$mark);
  221. $low = strtolower($var);
  222. // boolean and null
  223. if ($low === 'true' || $low === 'false' || $low === 'null') {
  224. $result .= $var;
  225. }
  226. // lt, gt, ge, eq, ...
  227. elseif (array_key_exists($low, self::$TranslationTable)) {
  228. $result .= self::$TranslationTable[$low];
  229. }
  230. // instanceof keyword
  231. elseif ($low === 'instanceof') {
  232. $result .= $var;
  233. $instanceof = true;
  234. }
  235. // previous was instanceof
  236. elseif ($instanceof) {
  237. // last was instanceof, this var is a class name
  238. $result .= $var;
  239. $instanceof = false;
  240. }
  241. // regular variable
  242. else {
  243. $result .= $prefix . $var;
  244. }
  245. $i--;
  246. $state = self::ST_NONE;
  247. }
  248. break;
  249. // object member
  250. case self::ST_MEMBER:
  251. if (self::isVarNameChar($c)) {
  252. }
  253. // eval mode ${foo}
  254. elseif ($c === '$' && ($i >= $len-2 || $str[$i+1] !== '{')) {
  255. $result .= '{' . $prefix;
  256. $mark++;
  257. $eval = true;
  258. }
  259. // x.${foo} x->{foo}
  260. elseif ($c === '$') {
  261. $mark++;
  262. }
  263. // end of var member var, begin of new member
  264. elseif ($c === '.') {
  265. $result .= substr($str, $mark, $i-$mark);
  266. if ($eval) { $result .='}'; $eval = false; }
  267. $result .= '->';
  268. $mark = $i+1;
  269. $state = self::ST_MEMBER;
  270. }
  271. // begin of static access
  272. elseif ($c === ':') {
  273. $result .= substr($str, $mark, $i-$mark+1);
  274. if ($eval) { $result .='}'; $eval = false; }
  275. $state = self::ST_STATIC;
  276. break;
  277. }
  278. // the member is a method or an array
  279. elseif ($c === '(' || $c === '[') {
  280. $result .= substr($str, $mark, $i-$mark+1);
  281. if ($eval) { $result .='}'; $eval = false; }
  282. $state = self::ST_NONE;
  283. }
  284. // regular end of member, it is a var
  285. else {
  286. $var = substr($str, $mark, $i-$mark);
  287. if ($var !== '' && !preg_match('/^[a-z][a-z0-9_\x7f-\xff]*$/i',$var)) {
  288. throw new PHPTAL_ParserException("Invalid field name '$var' in expression php:$str");
  289. }
  290. $result .= $var;
  291. if ($eval) { $result .='}'; $eval = false; }
  292. $state = self::ST_NONE;
  293. $i--;
  294. }
  295. break;
  296. // wait for separator
  297. case self::ST_DEFINE:
  298. if (self::isVarNameChar($c)) {
  299. } else {
  300. $state = self::ST_NONE;
  301. $result .= substr($str, $mark, $i-$mark);
  302. $i--;
  303. }
  304. break;
  305. // static call, can be const, static var, static method
  306. // Klass::$static
  307. // Klass::const
  308. // Kclass::staticMethod()
  309. //
  310. case self::ST_STATIC:
  311. if (self::isVarNameChar($c)) {
  312. }
  313. // static var
  314. elseif ($c === '$') {
  315. }
  316. // end of static var which is an object and begin of member
  317. elseif ($c === '.') {
  318. $result .= substr($str, $mark, $i-$mark);
  319. $result .= '->';
  320. $mark = $i+1;
  321. $state = self::ST_MEMBER;
  322. }
  323. // end of static var which is a class name
  324. elseif ($c === ':') {
  325. $result .= substr($str, $mark, $i-$mark+1);
  326. $state = self::ST_STATIC;
  327. break;
  328. }
  329. // static method or array
  330. elseif ($c === '(' || $c === '[') {
  331. $result .= substr($str, $mark, $i-$mark+1);
  332. $state = self::ST_NONE;
  333. }
  334. // end of static var or const
  335. else {
  336. $result .= substr($str, $mark, $i-$mark);
  337. $state = self::ST_NONE;
  338. $i--;
  339. }
  340. break;
  341. // numeric value
  342. case self::ST_NUM:
  343. if (!self::isDigitCompound($c)) {
  344. $var = substr($str, $mark, $i-$mark);
  345. if (self::isAlpha($c) || $c === '_') {
  346. throw new PHPTAL_ParserException("Syntax error in number '$var$c' in expression php:$str");
  347. }
  348. if (!is_numeric($var)) {
  349. throw new PHPTAL_ParserException("Syntax error in number '$var' in expression php:$str");
  350. }
  351. $result .= $var;
  352. $state = self::ST_NONE;
  353. $i--;
  354. }
  355. break;
  356. }
  357. }
  358. $result = trim($result);
  359. // CodeWriter doesn't like expressions that look like blocks
  360. if ($result[strlen($result)-1] === '}') return '('.$result.')';
  361. return $result;
  362. }
  363. private static function isAlpha($c)
  364. {
  365. $c = strtolower($c);
  366. return $c >= 'a' && $c <= 'z';
  367. }
  368. private static function isDigit($c)
  369. {
  370. return ($c >= '0' && $c <= '9');
  371. }
  372. private static function isDigitCompound($c)
  373. {
  374. return ($c >= '0' && $c <= '9' || $c === '.');
  375. }
  376. private static function isVarNameChar($c)
  377. {
  378. return self::isAlpha($c) || ($c >= '0' && $c <= '9') || $c === '_' || $c === '\\';
  379. }
  380. private static $TranslationTable = array(
  381. 'not' => '!',
  382. 'ne' => '!=',
  383. 'and' => '&&',
  384. 'or' => '||',
  385. 'lt' => '<',
  386. 'gt' => '>',
  387. 'ge' => '>=',
  388. 'le' => '<=',
  389. 'eq' => '==',
  390. );
  391. }