rm trailing white space
[mediawiki.git] / PHPTAL-NP-0.7.0 / libs / PHPTAL / Expression.php
blob81bae03397ce5cda7813d125e49b03242bd0a14d
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 //
4 // Copyright (c) 2003 Laurent Bedubourg
5 //
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.
10 //
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.
15 //
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
19 //
20 // Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
21 //
23 define('_PHPTAL_ES_RECEIVER_IS_CONTEXT', 1);
24 define('_PHPTAL_ES_RECEIVER_IS_NONE', 2);
25 define('_PHPTAL_ES_RECEIVER_IS_TEMP', 4);
26 define('_PHPTAL_ES_RECEIVER_IS_OUTPUT', 8);
29 // Important note:
31 // The PHPTALES system works quite well but it's not yet fully optimized, it
32 // should be refactored at some points of my TODO list.
36 class PHPTAL_Expression
38 var $_tag;
39 var $_gen;
40 var $_src;
41 var $_subs = array();
42 var $_policy;
43 var $_receiver = false;
44 var $_prepared = false;
45 var $_structure = false;
47 function PHPTAL_Expression(&$generator, &$tag, $str)
49 $this->_tag =& $tag;
50 $this->_gen =& $generator;
51 $this->_src = $str;
54 function setPolicy($policy)
56 $this->_policy = $policy;
59 /**
60 * Prepare the expression.
62 * This method explode the expression into sub expression and prepare each
63 * expression for parsing.
65 * @throws PHPTAL_ExpressionError
66 * If receiver policy fail.
68 function prepare()
70 if ($this->_prepared) { return; }
71 $test = $this->_src;
73 // some sub expression detected
74 while (preg_match('/^(.*?)(?<!;);(?!;)/sm', $test, $m)) {
75 list($src, $exp) = $m;
77 $x = new PHPTAL_Expression($this->_gen, $this->_tag, $exp);
78 $x->setPolicy($this->_policy);
79 $x->setReceiver($this->getReceiver());
80 $err = $x->prepare();
81 if (PEAR::isError($err)) {
82 return $err;
84 $this->_subs[] = $x;
86 $test = substr($test, strlen($src));
88 // if subs, insert last one
89 if ($this->countSubs() > 0 && strlen(trim($test)) > 0) {
90 $exp = $test;
91 $x = new PHPTAL_Expression($this->_gen, $this->_tag, $exp);
92 $x->setPolicy($this->_policy);
93 $x->setReceiver($this->getReceiver());
94 $err = $x->prepare();
95 if (PEAR::isError($err)) {
96 return $err;
98 $this->_subs[] = $x;
99 } else {
100 // otherwise, just remove expression delimiters from source
101 // and apply the receiver policy
102 $exp = $test;
103 $exp = str_replace(';;', ';', $exp);
104 $this->_src = $exp;
105 if (strlen($exp) == 0) return;
107 $err = $this->_extractReceiver();
108 if (PEAR::isError($err)){
109 return $err;
112 if (!$this->_receiver
113 && ($this->_policy & _PHPTAL_ES_RECEIVER_IS_CONTEXT
114 || $this->_policy & _PHPTAL_ES_RECEIVER_IS_TEMP)) {
115 $str = sprintf('Receiver required in expression \'%s\' from %s:%d',
116 $this->_src,
117 $this->_tag->_parser->_file,
118 $this->_tag->line);
119 $err = new PHPTAL_ExpressionError($str);
120 return PEAR::raiseError($err);
123 if ($this->_policy & _PHPTAL_ES_RECEIVER_IS_NONE && $this->_receiver) {
124 $str = sprintf('Unexpected receiver \'%s\' in expression \'%s\' from %s:%d',
125 $this->_receiver,
126 $this->_src,
127 $this->_tag->_parser->_file,
128 $this->_tag->line);
129 $err = new PHPTAL_ExpressionError($str);
130 return PEAR::raiseError($err);
134 $this->_prepared = true;
137 function _extractReceiver()
139 global $_phptal_es_namespaces;
141 $this->_src = preg_replace('/^\s+/sm', '', $this->_src);
142 if (preg_match('/^([a-z:A-Z_0-9]+)\s+([^\|].*?)$/sm', $this->_src, $m)) {
143 // the receiver looks like xxxx:aaaa
145 // we must ensure that it's not a known phptales namespaces
146 if (preg_match('/^([a-zA-Z_0-9]+):/', $m[1], $sub)) {
147 $ns = $sub[1];
148 // known namespace, just break
149 if (function_exists('phptal_es_'.$ns)) {
150 // in_array(strtolower($ns), $_phptal_es_namespaces)) {
151 return;
155 if ($this->_receiver) {
156 $str = sprintf('Receiver already set to \'%s\' in \'%s\'',
157 $this->_receiver,
158 $this->_src);
159 $err = new PHPTAL_ExpressionError($str);
160 return PEAR::raiseError($err);
163 $this->_receiver = $m[1];
165 // that the way to replace : in setters (usually this this should
166 // only be used under tal:attributes tag !!!!
168 $this->_receiver = str_replace(':', '__phptales_dd__', $this->_receiver);
169 $this->_src = $m[2];
170 if ($this->_receiver == "structure") {
171 $this->_structure = true;
172 $this->_receiver = false;
173 $this->_extractReceiver();
178 /**
179 * Retrieve the number of sub expressions.
181 function countSubs()
183 return count($this->_subs);
186 function &subs()
188 return $this->_subs;
192 * Returns true if a receiver is set for this expression.
194 function hasReceiver()
196 return $this->_receiver != false;
200 * Retrieve receiver's name.
202 function getReceiver()
204 return $this->_receiver;
208 * Set expression receiver.
210 function setReceiver($name)
212 $this->_receiver = $name;
216 * Generate php code for this expression.
218 function generate()
220 $err = $this->prepare();
221 if (PEAR::isError($err)) {
222 return $err;
225 if ($this->countSubs() > 0) {
226 foreach ($this->_subs as $sub) {
227 $err = $sub->generate();
228 if (PEAR::isError($err)) {
229 return $err;
232 } else {
233 $exp = $this->_src;
234 if (strlen($exp) == 0) return;
236 // expression may be composed of alternatives | list of expression
237 // they are evaluated with a specific policy : _PHPTAL_ES_SEQUENCE
239 // 'string:' break the sequence as no alternative exists after a
240 // string which is always true.
241 if (preg_match('/\s*?\|\s*?string:/sm', $exp, $m)) {
242 $search = $m[0];
243 $str = strpos($exp, $search);
244 // $str = strpos($exp, ' | string:');
245 // if ($str !== false) {
246 $seq = preg_split('/(\s*?\|\s*?)/sm', substr($exp, 0, $str));
247 $seq[]= substr($exp, $str + 2);
248 } else {
249 $seq = preg_split('/(\s*?\|\s*?)/sm', $exp);
252 // not a sequence
253 if (count($seq) == 1) {
254 $code = PHPTAL_Expression::_GetCode($this, $exp);
255 if (PEAR::isError($code)) { return $code; }
257 $temp = $this->_gen->newTemporaryVar();
258 $this->_gen->doAffectResult($temp, $code);
259 return $this->_useResult($temp);
260 } else {
261 return $this->_evaluateSequence($seq);
267 function _evaluateSequence($seq)
269 $temp = $this->_gen->newTemporaryVar();
271 $this->_gen->doDo();
272 foreach ($seq as $s) {
273 // skip empty parts
274 if (strlen(trim($s)) > 0) {
275 $code = PHPTAL_Expression::_GetCode($this, $s);
276 if (PEAR::isError($code)) { return $code; }
278 $this->_gen->doUnset($temp);
279 $this->_gen->doAffectResult($temp, $code);
280 $this->_gen->doIf('!PEAR::isError(' . $temp . ') && ' .
281 // $temp .' != false && '.
282 $temp . ' !== null');
283 $this->_gen->execute('$__ctx__->_errorRaised = false');
284 $this->_gen->execute('break');
285 $this->_gen->endBlock();
288 $this->_gen->doEndDoWhile('0');
289 // test errorRaised
290 // error handling, disabled for performance..
291 /* $this->_gen->doIf('$__ctx__->_errorRaised');
292 $this->_gen->doAffectResult($temp, '$__ctx__->_errorRaised');
293 $this->_gen->execute('$__ctx__->_errorRaised = false');
294 $this->_gen->endBlock(); */
296 $err = $this->_useResult($temp);
298 // $this->_gen->endBlock();
299 return $err;
302 function _useResult($temp)
304 if ($this->_policy & _PHPTAL_ES_RECEIVER_IS_TEMP) {
305 $this->_gen->doReference($this->_receiver, $temp);
306 } else if ($this->_policy & _PHPTAL_ES_RECEIVER_IS_OUTPUT) {
307 $this->_gen->doPrintVar($temp, $this->_structure);
308 } else if ($this->_policy & _PHPTAL_ES_RECEIVER_IS_CONTEXT) {
309 $this->_gen->doContextSet($this->_receiver, $temp);
310 } else {
311 $err = new PHPTAL_ExpressionError("Expression '$this->_src' Don't know what to do with result.");
312 return PEAR::raiseError($err);
317 * Retrieve a function namespace for given string and the associated
318 * expression.
320 * Examples:
322 * The function namespace of 'php:XXXX' is 'php'
323 * The function namespace of 'XXXX' is 'path'
324 * The function namespace of 'foo:bar::baz' is 'foo'
326 * @param string $str
327 * Expression string without receiver
329 * @return array
330 * An array composed as follow : array('ns', 'exp'),
331 * Where 'ns' is the function namespace and 'exp', is the
332 * source string without the 'ns:' part.
334 function _FindFunctionNamespace($str)
336 $str = preg_replace('/^\s/sm', '', $str);
337 if (preg_match('/^([a-z0-9\-]+):(?!>:)(.*?)$/ism', $str, $m)) {
338 list($ns, $path) = array_slice($m, 1);
339 $ns = str_replace('-', '_', $ns);
340 return array($ns, $path);
342 return array('path', $str);
346 * Get the code for a ns:args string.
348 function _GetCode(&$exp, $str)
350 list($ns, $args) = PHPTAL_Expression::_FindFunctionNamespace($str);
351 $func = "PHPTAL_ES_$ns";
352 if (!function_exists($func)) {
353 $err = new PHPTAL_ExpressionError("Unknown function $func in '$str'");
354 return PEAR::raiseError($err);
356 return $func($exp, $args);
361 * Thrown on expression parsing error.
363 class PHPTAL_ExpressionError extends PEAR_Error