2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
4 // Copyright (c) 2003 Laurent Bedubourg
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
24 * @var $_phptal_es_namespaces
27 * This array contains phptales namespaces, it means php:, string:, exists:,
28 * not: and path: at this time.
30 global $_phptal_es_namespaces;
31 $_phptal_es_namespaces = array('not', 'php', 'string', 'exists', 'path', 'not-exists');
34 * @var $_phptal_es_keywords
37 * List of reserved phptales keywords.
39 global $_phptal_es_keywords;
40 $_phptal_es_keywords = array('nothing', 'default', 'structure');
42 global $_phptal_es_php_types;
43 $_phptal_es_php_types = array('true', 'false', 'null');
46 * string: expression type
48 function PHPTAL_ES_string(&$exp, $value)
50 $value = PHPTAL_ES_path_in_string($value);
51 // $value = str_replace('$$', '\$\$', $value);
52 $value = '"' . $value . '"';
53 $value = preg_replace('/^\" ?\"\. /', '', $value);
54 $value = preg_replace('/\.\"\"$/', '', $value);
59 * not: expression type
61 function PHPTAL_ES_not(&$exp, $value)
63 return "! " . PHPTAL_ES_path($exp, $value);
67 * not-exists: expression type
69 function PHPTAL_ES_not_exists(&$exp, $value)
71 return "! " . PHPTAL_ES_exists($exp, $value);
75 * path: expression type
77 * @todo default -- existing text inside tag
78 * options -- ?? keyword arguments passed to template ??
79 * repeat -- repeat variables
80 * attrs -- initial values of tag attributes
81 * CONTEXTS -- list of standard names (above list) in case one of these
82 * names was hidden by a local variable.
84 function PHPTAL_ES_path(&$exp, $value)
86 $value = trim($value);
87 // special case : 'string' authorized
88 if ($value[0] == "'") { return PHPTAL_ES_path_in_string($value,"'"); }
90 if (preg_match('/^[0-9\.]*$/', $value)) { return $value; }
91 // php constants are accessed though @ keyword
92 if (preg_match('/^@[_a-z][0-9a-z_]*$/i', $value)) { return substr($value, 1); }
93 if ($value == "nothing") { return 'null'; }
94 if ($value == "default") { return '$__default__'; }
95 $value = PHPTAL_ES_path_in_string($value);
96 return '$__ctx__->get("'. $value .'")';
100 function PHPTAL_ES_path_toString(&$exp, $value)
102 $value = trim($value);
103 if ($value == "nothing") { return 'null'; }
104 if ($value == "default") { return '$__default__'; }
105 if (preg_match('/^@[_a-z][0-9a-z_]*$/i', $value)) { return substr($value, 1); }
106 return '$__ctx__->getToString("'. $value .'")';
111 * exists: expression type
113 function PHPTAL_ES_exists(&$exp, $value)
115 return '$__ctx__->has("'. trim($value). '")';
119 * php: expression type
121 function PHPTAL_ES_php(&$exp, $str)
123 $parser = new PHPTAL_ES_PHP_Parser($exp, $str);
124 return $parser->evaluate();
129 // utility functions (internal)
132 function PHPTAL_ES_path_in_string($arg, $c='"')
134 list($match,$repl) = _PHPTAL_context_accessed($arg);
135 for ($i=0; $i<count($match); $i++
) {
137 $repl[$i] = $c . ". " . PHPTAL_ES_path_toString($null, $repl[$i]) . " ." . $c;
138 $pos = strpos($arg, $match[$i]);
139 $arg = substr($arg, 0, $pos)
141 . substr($arg, $pos +
strlen($match[$i]));
144 $arg = str_replace('$$','\$', $arg);
146 $arg = str_replace('$$', '$', $arg);
151 function _PHPTAL_context_accessed($str)
153 if (preg_match_all('/((?<!\$)\$\{?([@_a-zA-z0-9.\/\-]+)\}?)/', $str, $match)) {
154 return array_slice($match, 1);
156 return array(array(),array());
160 // php: expression parser
163 class PHPTAL_ES_PHP_Parser
168 var $_aliases = array();
170 var $_last_was_array = false;
172 function PHPTAL_ES_PHP_Parser(&$expression, $str)
174 $this->_exp
=& $expression;
175 $this->_gen
=& $expression->_gen
;
181 $value = $this->_str
;
184 // extract strings and replace context calls
185 if (preg_match_all('/(\'.*?(?<!\\\)\')/sm', $value, $m)) {
187 foreach ($m as $str) {
188 $s_rplc = PHPTAL_ES_path_in_string($str, "'");
189 $s_rplc = preg_replace('/^\' ?\'\. /', '', $s_rplc);
190 $s_rplc = preg_replace('/\.\'\'$/', '', $s_rplc);
191 $value = str_replace($str, '#_STRING_'. count($strings) . '_#', $value);
192 $strings[] = $s_rplc;
196 list($match, $replc) = _PHPTAL_context_accessed($value);
198 foreach ($match as $m) {
199 $i = count($contexts);
200 $contexts[] = $replc[$i];
201 $value = str_replace($m, '#_CONTEXT_'. $i . '_#', $value);
204 // replace or, and, lt, gt, etc...
205 $value = $this->_php_test_modifiers($value);
206 $value = $this->_php_vars($value);
210 foreach ($strings as $str) {
211 $value = str_replace('#_STRING_'. $i . '_#', $str, $value);
216 foreach ($contexts as $c) {
217 $value = str_replace('#_CONTEXT_' . $i . '_#', PHPTAL_ES_path($this->_exp
, $c), $value);
221 // experimental, compile php: content
223 require_once PT_IP
. "/Types/Code.php";
226 $code->setCode($value .";");
227 $err = $code->compile();
228 if (PEAR
::isError($err)) {
229 return $this->_raiseCompileError($value, $err);
235 function _php_test_modifiers($exp)
237 $exp = preg_replace('/\bnot\b/i', ' !', $exp);
238 $exp = preg_replace('/\bne\b/i', ' != ', $exp);
239 $exp = preg_replace('/\band\b/i', ' && ', $exp);
240 $exp = preg_replace('/\bor\b/i', ' || ', $exp);
241 $exp = preg_replace('/\blt\b/i', ' < ', $exp);
242 $exp = preg_replace('/\bgt\b/i', ' > ', $exp);
243 $exp = preg_replace('/\bge\b/i', ' >= ', $exp);
244 $exp = preg_replace('/\ble\b/i', ' <= ', $exp);
245 $exp = preg_replace('/\beq\b/i', ' == ', $exp);
249 function _php_vars($arg)
251 $arg = preg_replace('/\s*\/\s*/',' / ', $arg);
252 $arg = preg_replace("/\s*\(\s*/","(", $arg );
253 $arg = preg_replace('/\s*\)\s*/',') ', $arg );
254 $arg = preg_replace('/\s*\[\s*/','[', $arg );
255 $arg = preg_replace('/\s*\]\s*/',']', $arg );
256 $arg = preg_replace('/\s*,\s*/',' , ', $arg );
261 $last_was_array = false;
264 while ($i < strlen($arg)) {
266 if (preg_match('/[a-z_]/i', $c)) {
268 } else if (preg_match('/[0-9]/', $c) && $path) {
270 } else if (preg_match('/[\/\.]/', $c) && $path) {
273 } else if (preg_match('/[\/\.]/', $c) && $this->_last_was_array
) {
275 } else if ($c == '(') {
277 $result .= $this->pathRequest($last_path);
279 $path = substr($path, strlen($last_path) +
1);
282 $result .= $path . '(';
283 $this->_last_was_array
= false;
285 } else if ($c == '#') {
287 $result .= $this->pathRequest($path);
291 $next = strpos($arg, '#', $i+
1);
292 $result .= substr($arg, $i, $next - $i +
1);
294 } else if ($c == ':') {
295 if ($arg[$i+
1] != ':') { // error
303 } else if ($c == '@') {
304 // access to defined variable, append to result
305 // up to non word character
306 $i++
; // skip @ character
311 } while ($i < strlen($arg) && preg_match('/[a-z_0-9]/i', $arg[$i]));
312 $i--; // reset last character
316 $result .= $this->pathRequest($path);
323 $this->_last_was_array
= true;
325 $this->_last_was_array
= false;
331 // don't forget the last bit
332 if (isset($path) && $path) {
333 $result .= $this->pathRequest($path);
339 function pathRequest($path)
341 global $_phptal_es_php_types;
343 if (!preg_match('/[a-z]/i', $path)){
346 if ($this->_last_was_array
) {
347 return str_replace('.', '->', $path);
349 if (in_array($path, $_phptal_es_php_types)) {
353 $concatenate = false;
354 $path = str_replace('.','/', $path);
355 if (substr($path,-1) == '/') {
356 $path = substr($path, 0, -1);
360 if (array_key_exists($path, $this->_aliases
)) {
361 $res = $this->_aliases
[$path];
363 $res = $this->_gen
->newTemporaryVar();
364 $this->_aliases
[$path] = $res;
365 $this->_gen
->doContextGet($res, $path);
374 function _raiseCompileError($code, $err)
376 $str = sprintf("Expression 'php:' compilation error in %s:%d".endl
,
377 $this->_exp
->_tag
->_parser
->_file
,
378 $this->_exp
->_tag
->line
);
379 return PEAR
::raiseError($str);