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>
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);
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
43 var $_receiver = false;
44 var $_prepared = false;
45 var $_structure = false;
47 function PHPTAL_Expression(&$generator, &$tag, $str)
50 $this->_gen
=& $generator;
54 function setPolicy($policy)
56 $this->_policy
= $policy;
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.
70 if ($this->_prepared
) { return; }
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());
81 if (PEAR
::isError($err)) {
86 $test = substr($test, strlen($src));
88 // if subs, insert last one
89 if ($this->countSubs() > 0 && strlen(trim($test)) > 0) {
91 $x = new PHPTAL_Expression($this->_gen
, $this->_tag
, $exp);
92 $x->setPolicy($this->_policy
);
93 $x->setReceiver($this->getReceiver());
95 if (PEAR
::isError($err)) {
100 // otherwise, just remove expression delimiters from source
101 // and apply the receiver policy
103 $exp = str_replace(';;', ';', $exp);
105 if (strlen($exp) == 0) return;
107 $err = $this->_extractReceiver();
108 if (PEAR
::isError($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',
117 $this->_tag
->_parser
->_file
,
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',
127 $this->_tag
->_parser
->_file
,
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)) {
148 // known namespace, just break
149 if (function_exists('phptal_es_'.$ns)) {
150 // in_array(strtolower($ns), $_phptal_es_namespaces)) {
155 if ($this->_receiver
) {
156 $str = sprintf('Receiver already set to \'%s\' in \'%s\'',
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
);
170 if ($this->_receiver
== "structure") {
171 $this->_structure
= true;
172 $this->_receiver
= false;
173 $this->_extractReceiver();
179 * Retrieve the number of sub expressions.
183 return count($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.
220 $err = $this->prepare();
221 if (PEAR
::isError($err)) {
225 if ($this->countSubs() > 0) {
226 foreach ($this->_subs
as $sub) {
227 $err = $sub->generate();
228 if (PEAR
::isError($err)) {
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)) {
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);
249 $seq = preg_split('/(\s*?\|\s*?)/sm', $exp);
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);
261 return $this->_evaluateSequence($seq);
267 function _evaluateSequence($seq)
269 $temp = $this->_gen
->newTemporaryVar();
272 foreach ($seq as $s) {
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');
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();
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);
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
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'
327 * Expression string without receiver
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