3 +---------------------------------------------------------------------------------+
4 | Copyright (c) 2010 Haanga |
5 +---------------------------------------------------------------------------------+
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met: |
8 | 1. Redistributions of source code must retain the above copyright |
9 | notice, this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
15 | 3. All advertising materials mentioning features or use of this software |
16 | must display the following acknowledgement: |
17 | This product includes software developed by César D. Rodas. |
19 | 4. Neither the name of the César D. Rodas nor the |
20 | names of its contributors may be used to endorse or promote products |
21 | derived from this software without specific prior written permission. |
23 | THIS SOFTWARE IS PROVIDED BY CÉSAR D. RODAS ''AS IS'' AND ANY |
24 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
25 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
26 | DISCLAIMED. IN NO EVENT SHALL CÉSAR D. RODAS BE LIABLE FOR ANY |
27 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
28 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
29 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
30 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE |
33 +---------------------------------------------------------------------------------+
34 | Authors: César Rodas <crodas@php.net> |
35 +---------------------------------------------------------------------------------+
42 protected static $block_var=NULL;
44 protected $forloop = array();
46 protected $sub_template = FALSE;
48 protected $check_function = FALSE;
49 protected $blocks=array();
51 * number of blocks :-)
53 protected $in_block=0;
57 protected $ob_start=0;
59 protected $prepend_op;
61 * Context at compile time
65 * Table which contains all variables
66 * aliases defined in the template
70 * Flag the current variable as safe. This means
71 * that escape won't be called if autoescape is
72 * activated (which is activated by default)
74 public $var_is_safe=FALSE;
77 /* compiler options */
78 protected $autoescape = TRUE;
79 protected $if_empty = TRUE;
80 protected $dot_as_object = TRUE;
81 protected $strip_whitespace = FALSE;
82 protected $is_exec_enabled = FALSE;
90 function __construct()
92 $this->generator
= new Haanga_Generator_PHP
;
93 if (self
::$block_var===NULL) {
94 self
::$block_var = '{{block.'.md5('super').'}}';
98 // isExecEnabled() {{{
100 * Return TRUE if the special tag 'exec' is enabled (FALSE by default)
103 function isExecAllowed()
105 return $this->is_exec_enabled
;
109 function setOption($option, $value)
111 switch (strtolower($option)) {
113 $this->if_empty
= (bool)$value;
116 $this->autoescape
= (bool)$value;
118 case 'dot_as_object':
119 $this->dot_as_object
= (bool)$value;
121 case 'strip_whitespace':
122 $this->strip_whitespace
= (bool)$value;
124 case 'is_exec_enabled':
126 $this->is_exec_enabled
= (bool)$value;
131 // setDebug($file) {{{
132 function setDebug($file)
134 $this->debug
= $file;
141 $avoid_cleaning = array(
142 'strip_whitespace' => 1, 'block_var' => 1, 'autoescape'=>1,
143 'if_empty' => 1, 'dot_as_object' => 1, 'is_exec_enabled' => 1,
145 foreach (array_keys(get_object_vars($this)) as $key) {
146 if (isset($avoid_cleaning[$key])) {
151 $this->generator
= new Haanga_Generator_PHP
;
152 $this->blocks
= array();
153 $this->cycle
= array();
157 // get_template_name() {{{
158 final function get_template_name()
164 // Set template name {{{
165 function set_template_name($path)
167 $file = basename($path);
168 $pos = strpos($file,'.');
169 return ($this->name
= substr($file, 0, $pos));
173 // get_function_name(string $name) {{{
174 function get_function_name($name)
176 return "{$name}_template";
180 // Compile ($code, $name=NULL) {{{
181 final function compile($code, $name=NULL)
185 $parsed = Haanga_Compiler_Lexer
::init($code, $this);
187 $this->subtemplate
= FALSE;
189 $body = new Haanga_AST
;
190 $this->prepend_op
= hcode();
192 if (isset($parsed[0]) && $parsed[0]['operation'] == 'base') {
193 /* {% base ... %} found */
194 $base = $parsed[0][0];
195 $code .= $this->get_base_template($base);
200 $func_name = $this->get_function_name($name);
201 if ($this->check_function
) {
202 $body->do_if(hexpr(hexec('function_exists', $func_name), '===', FALSE));
204 if (isset($this->_file
)) {
205 $body->comment("Generated from {$this->_base_dir}/{$this->_file}");
208 $body->declare_function($func_name);
209 $body->do_exec('extract', hvar('vars'));
210 $body->do_if(hexpr(hvar('return'), '==', TRUE));
211 $body->do_exec('ob_start');
216 $this->generate_op_code($parsed, $body);
217 if ($this->subtemplate
) {
218 $expr = $this->expr_call_base_template();
219 $this->do_print($body, $expr);
222 $body->do_if(hexpr(hvar('return'), '==', TRUE));
223 $body->do_return(hexec('ob_get_clean'));
227 $body->do_endfunction();
228 if ($this->check_function
) {
233 if ($this->prepend_op
->stack_size() > 0) {
234 $this->prepend_op
->append_ast($body);
235 $body = $this->prepend_op
;
238 $op_code = $body->getArray(TRUE);
239 $code .= $this->generator
->getCode($op_code);
240 if (!empty($this->append
)) {
241 $code .= $this->append
;
244 if (!empty($this->debug
)) {
245 $op_code['php'] = $code;
246 file_put_contents($this->debug
, print_r($op_code, TRUE), LOCK_EX
);
252 // compile_file($file) {{{
256 * @param string $file File path
257 * @param bool $safe Whether or not add check if the function is already defined
259 * @return Generated PHP code
261 final function compile_file($file, $safe=FALSE, $context=array())
263 if (!is_readable($file)) {
264 throw new Haanga_Compiler_Exception("$file is not a file");
266 $this->_base_dir
= dirname($file);
267 $this->_file
= basename($file);
268 $this->check_function
= $safe;
269 $this->context
= $context;
270 $name = $this->set_template_name($file);
271 return $this->compile(file_get_contents($file), $name);
275 // is_expr methods {{{
276 function is_var_filter($cmd)
278 return isset($cmd['var_filter']);
283 // expr_call_base_template() {{{
285 * Generate code to call base template
288 function expr_call_base_template()
291 $this->get_function_name($this->subtemplate
),
298 // get_base_template($base) {{{
300 * Handle {% base "" %} definition. By default only
301 * static (string) are supported, but this can be overrided
304 * This method load the base class, compile it and return
305 * the generated code.
307 * @param array $base Base structure
309 * @return string Generated source code
311 function get_base_template($base)
313 if (!Haanga_AST
::is_str($base)) {
314 throw new Haanga_Compiler_Exception("Dynamic inheritance is not supported for compilated templates");
316 $file = $base['string'];
317 list($this->subtemplate
, $new_code) = $this->compile_required_template($file);
318 return $new_code."\n\n";
322 // {% base "foo.html" %} {{{
323 protected function generate_op_base()
325 throw new exception("{% base %} can be only as first statement");
330 protected function generate_op_code($parsed, &$body)
332 if (!is_array($parsed)) {
333 throw new Haanga_Compiler_Exception("Invalid \$parsed array");
335 foreach ($parsed as $op) {
336 if (!is_array($op)) {
339 if (!isset($op['operation'])) {
340 throw new Haanga_Compiler_Exception("Malformed array:".print_r($op, TRUE));
342 if ($this->subtemplate
&& $this->in_block
== 0 && $op['operation'] != 'block') {
343 /* ignore most of tokens in subtemplates */
346 $method = "generate_op_".$op['operation'];
347 if (!is_callable(array($this, $method))) {
348 throw new Haanga_Compiler_Exception("Compiler: Missing method $method");
350 $this->$method($op, $body);
355 // Check the current expr {{{
356 protected function check_expr(&$expr)
358 if (Haanga_AST
::is_expr($expr)) {
359 if ($expr['op_expr'] == 'in') {
360 for ($id=0; $id < 2; $id++
) {
361 if ($this->is_var_filter($expr[$id])) {
362 $expr[$id] = $this->get_filtered_var($expr[$id]['var_filter'], $var);
365 if (Haanga_AST
::is_str($expr[1])) {
366 $expr = hexpr(hexec('strpos', $expr[1], $expr[0]), '!==', FALSE);
370 hexec('is_array', $expr[1]),
371 hexec('array_search', $expr[0], $expr[1]),
372 hexec('strpos', $expr[1], $expr[0])
378 if (is_object($expr)) {
379 $expr = $expr->getArray();
381 $this->check_expr($expr[0]);
382 $this->check_expr($expr[1]);
383 } else if (is_array($expr)) {
384 if ($this->is_var_filter($expr)) {
385 $expr = $this->get_filtered_var($expr['var_filter'], $var);
386 } else if (isset($expr['args'])) {
387 /* check every arguments */
388 foreach ($expr['args'] as &$v) {
389 $this->check_expr($v);
392 } else if (isset($expr['expr_cond'])) {
393 /* Check expr conditions */
394 $this->check_expr($expr['expr_cond']);
395 $this->check_expr($expr['true']);
396 $this->check_expr($expr['false']);
402 // buffer <varname> {{{
403 public function generate_op_buffer($details, &$body)
405 $this->ob_start($body);
406 $this->generate_op_code($details['body'], $body);
407 $body->decl($details['name'], hvar('buffer'.$this->ob_start
));
412 // ifequal|ifnot equal <var_filtered|string|number> <var_fitlered|string|number> ... else ... {{{
413 protected function generate_op_ifequal($details, &$body)
415 $if['expr'] = hexpr($details[1], $details['cmp'], $details[2])->getArray();
416 $if['body'] = $details['body'];
417 if (isset($details['else'])) {
418 $if['else'] = $details['else'];
420 $this->generate_op_if($if, $body);
424 // {% if <expr> %} HTML {% else %} TWO {% endif $} {{{
425 protected function generate_op_if($details, &$body)
427 if ($this->if_empty
&& $this->is_var_filter($details['expr']) && count($details['expr']['var_filter']) == 1) {
428 /* if we are doing if <Variable> it should check
429 if it exists without throw any warning */
430 $expr = $details['expr'];
431 $expr['var_filter'][] = 'empty';
433 $variable = $this->get_filtered_var($expr['var_filter'], $var);
435 $details['expr'] = hexpr($variable, '===', FALSE)->getArray();
437 $this->check_expr($details['expr']);
438 $expr = Haanga_AST
::fromArrayGetAST($details['expr']);
440 $this->generate_op_code($details['body'], $body);
441 if (isset($details['else'])) {
443 $this->generate_op_code($details['else'], $body);
449 // Override template {{{
450 protected function compile_required_template($file)
452 if (!is_file($file)) {
453 if (isset($this->_base_dir
)) {
454 $file = $this->_base_dir
.'/'.$file;
457 if (!is_file($file)) {
458 throw new Haanga_Compiler_Exception("can't find {$file} file template");
460 $class = get_class($this);
463 $code = $comp->compile_file($file, $this->check_function
);
464 return array($comp->get_template_name(), $code);
468 // include "file.html" | include <var1> {{{
469 protected function generate_op_include($details, &$body)
471 if (!$details[0]['string']) {
472 throw new Haanga_Compiler_Exception("Dynamic inheritance is not supported for compilated templates");
474 list($name,$code) = $this->compile_required_template($details[0]['string']);
475 $this->append
.= "\n\n{$code}";
476 $this->do_print($body,
477 hexec($this->get_function_name($name),
478 hvar('vars'), TRUE, hvar('blocks'))
483 // Handle HTML code {{{
484 protected function generate_op_html($details, &$body)
486 $string = Haanga_AST
::str($details['html']);
487 $this->do_print($body, $string);
491 // get_var_filtered {{{
493 * This method handles all the filtered variable (piped_list(X)'s
494 * output in the parser.
497 * @param array $variable (Output of piped_list(B) (parser))
498 * @param array &$varname Variable name
499 * @param bool $accept_string TRUE is string output are OK (ie: block.parent)
504 function get_filtered_var($variable, &$varname, $accept_string=FALSE)
506 $this->var_is_safe
= FALSE;
508 if (count($variable) > 1) {
509 $count = count($variable);
510 $target = $this->generate_variable_name($variable[0]);
512 if (!Haanga_AST
::is_var($target)) {
513 /* block.super can't have any filter */
514 throw new Haanga_Compiler_Exception("This variable can't have any filter");
517 for ($i=1; $i < $count; $i++
) {
518 $func_name = $variable[$i];
519 if ($func_name == 'escape') {
520 /* to avoid double cleaning */
521 $this->var_is_safe
= TRUE;
523 $args = array(isset($exec) ?
$exec : $target);
524 $exec = $this->do_filtering($func_name, $args);
530 $details = $this->generate_variable_name($variable[0]);
531 $varname = $variable[0];
533 if (!Haanga_AST
::is_var($details) && !$accept_string) {
534 /* generate_variable_name didn't replied a variable, weird case
535 currently just used for {{block.super}}.
537 throw new Haanga_Compiler_Exception("Invalid variable name {$variable[0]}");
545 // generate_op_print_var {{{
547 * Generate code to print a variable with its filters, if there is any.
549 * All variable (except those flagged as |safe) are automatically
550 * escaped if autoescape is "on".
553 protected function generate_op_print_var($details, &$body)
556 $details = $this->get_filtered_var($details['variable'], $variable, TRUE);
558 if (!Haanga_AST
::is_var($details) && !Haanga_AST
::is_exec($details)) {
559 /* generate_variable_name didn't replied a variable, weird case
560 currently just used for {{block.super}}.
562 $this->do_print($body, $details);
566 if (!$this->is_safe($details) && $this->autoescape
) {
567 $args = array($details);
568 $details = $this->do_filtering('escape', $args);
572 if (is_array($details)) {
573 $details = Haanga_AST
::fromArrayGetAST($details);
575 $this->do_print($body, $details);
579 // {# something #} {{{
580 protected function generate_op_comment($details, &$body)
582 /* comments are annoying */
583 //$body->comment($details['comment']);
587 // {% block 'name' %} ... {% endblock %} {{{
588 protected function generate_op_block($details, &$body)
590 if (is_array($details['name'])) {
592 foreach ($details['name'] as $part) {
593 if (is_string($part)) {
595 } else if (is_array($part)) {
596 if (Haanga_AST
::is_str($part)) {
597 $name .= "{$part['string']}";
598 } elseif (isset($part['object'])) {
599 $name .= "{$part['object']}";
601 throw new Haanga_Compiler_Exception("Invalid blockname");
606 $details['name'] = substr($name, 0, -1);
609 $this->blocks
[] = $details['name'];
610 $block_name = hvar('blocks', $details['name']);
612 $this->ob_start($body);
613 $buffer_var = 'buffer'.$this->ob_start
;
616 $this->generate_op_code($details['body'], $content);
618 $body->append_ast($content);
621 $buffer = hvar($buffer_var);
625 * isset previous block (parent block)?
627 * has reference to self::$block_var ?
629 * replace self::$block_var for current block value (buffer)
633 * print current block
636 $declare = hexpr_cond(
637 hexec('isset', $block_name),
639 hexpr(hexec('strpos', $block_name, self
::$block_var), '===', FALSE),
641 hexec('str_replace', self
::$block_var, $buffer, $block_name)
645 if (!$this->subtemplate
) {
646 $this->do_print($body, $declare);
648 $body->decl($block_name, $declare);
649 if ($this->in_block
> 1) {
650 $this->do_print($body, $block_name);
653 array_pop($this->blocks
);
659 // regroup <var1> by <field> as <foo> {{{
660 protected function generate_op_regroup($details, &$body)
662 $body->comment("Temporary sorting");
664 $array = $this->get_filtered_var($details['array'], $varname);
666 if (Haanga_AST
::is_exec($array)) {
667 $varname = hvar($details['as']);
668 $body->decl($varname, $array);
670 $var = hvar('item', $details['row']);
672 $body->decl('temp_group', array());
674 $body->do_foreach($varname, 'item', NULL,
675 hcode()->decl(hvar('temp_group', $var, NULL), hvar('item'))
678 $body->comment("Proper format");
679 $body->decl($details['as'], array());
680 $body->do_foreach('temp_group', 'item', 'group',
682 hvar($details['as'], NULL),
683 array("grouper" => hvar('group'), "list" => hvar('item'))
686 $body->comment("Sorting done");
690 // variable context {{{
694 * These two functions are useful to detect if a variable
695 * separated by dot (foo.bar) is an array or object. To avoid
696 * overhead we decide it at compile time, rather than
697 * ask over and over at rendering time.
700 * + If foo exists at compile time,
701 * and it is an array, it would be foo['bar']
702 * otherwise it'd be foo->bar.
703 * + If foo don't exists at compile time,
704 * it would be foo->bar if the compiler option
705 * dot_as_object is TRUE (by default) otherwise
709 * @author gallir (ideas)
712 function set_context($varname, $value)
714 $this->context
[$varname] = $value;
717 function var_is_object(Array $variable)
719 $varname = $variable[0];
732 return FALSE; /* these are arrays */
735 if (isset($this->context
[$varname])) {
736 if (count($variable) == 1) {
737 return is_object($this->context
[$varname]);
739 $var = & $this->context
[$varname];
740 foreach ($variable as $id => $part) {
742 if (is_array($part) && isset($part['object'])) {
743 $var = &$var->$part['object'];
744 } else if (is_object($var)) {
752 $type = is_object($var);
754 /* delete reference */
760 return $this->dot_as_object
;
764 // Get variable name {{{
765 protected function generate_variable_name($variable)
767 if (is_array($variable)) {
768 switch ($variable[0]) {
771 throw new Haanga_Compiler_Exception("Invalid forloop reference outside of a loop");
773 switch ($variable[1]) {
775 $this->forloop
[$this->forid
]['counter'] = TRUE;
776 $variable = 'forcounter1_'.$this->forid
;
779 $this->forloop
[$this->forid
]['counter0'] = TRUE;
780 $variable = 'forcounter0_'.$this->forid
;
783 $this->forloop
[$this->forid
]['counter'] = TRUE;
784 $this->forloop
[$this->forid
]['last'] = TRUE;
785 $variable = 'islast_'.$this->forid
;
788 $this->forloop
[$this->forid
]['first'] = TRUE;
789 $variable = 'isfirst_'.$this->forid
;
792 $this->forloop
[$this->forid
]['revcounter'] = TRUE;
793 $variable = 'revcount_'.$this->forid
;
796 $this->forloop
[$this->forid
]['revcounter0'] = TRUE;
797 $variable = 'revcount0_'.$this->forid
;
802 $variable = $this->generate_variable_name(array_values($variable));
803 $variable = $variable['var'];
807 throw new Haanga_Compiler_Exception("Unexpected forloop.{$variable[1]}");
809 /* no need to escape it */
810 $this->var_is_safe
= TRUE;
813 if ($this->in_block
== 0) {
814 throw new Haanga_Compiler_Exception("Can't use block.super outside a block");
816 if (!$this->subtemplate
) {
817 throw new Haanga_Compiler_Exception("Only subtemplates can call block.super");
819 /* no need to escape it */
820 $this->var_is_safe
= TRUE;
821 return Haanga_AST
::str(self
::$block_var);
825 } else if (isset($this->var_alias
[$variable])) {
826 $variable = $this->var_alias
[$variable];
829 return hvar($variable)->getArray();
834 public function do_print(Haanga_AST
$code, $stmt)
836 /* Flag this object as a printing one */
837 $code->doesPrint
= TRUE;
839 if ($this->strip_whitespace
&& Haanga_AST
::is_str($stmt)) {
840 $stmt['string'] = preg_replace('/\s+/', ' ', $stmt['string']);
841 if (trim($stmt['string']) == "") {
842 return; /* avoid whitespaces */
846 if ($this->ob_start
== 0) {
847 $code->do_echo($stmt);
851 $buffer = hvar('buffer'.$this->ob_start
);
852 $code->append($buffer, $stmt);
858 // for [<key>,]<val> in <array> {{{
859 protected function generate_op_loop($details, &$body)
861 if (isset($details['empty'])) {
862 $body->do_if(hexpr(hexec('count', hvar($details['array'])), '==', 0));
863 $this->generate_op_code($details['empty'], $body);
868 $oldid = $this->forid
;
869 $this->forid
= $oldid+
1;
870 $this->forloop
[$this->forid
] = array();
873 $array = $this->get_filtered_var($details['array'], $varname);
876 if ($this->is_safe(hvar($varname))) {
877 $this->set_safe(hvar($details['variable']));
881 $this->generate_op_code($details['body'], $for_body);
883 if ($this->is_safe(hvar($varname))) {
884 $this->set_unsafe($details['variable']);
888 $size = hvar('psize_'.$oid);
891 if (isset($this->forloop
[$oid]['counter'])) {
892 $var = hvar('forcounter1_'.$oid);
893 $body->decl($var, 1);
894 $for_body->decl($var, hexpr($var, '+', 1));
899 if (isset($this->forloop
[$oid]['counter0'])) {
900 $var = hvar('forcounter0_'.$oid);
901 $body->decl($var, 0);
902 $for_body->decl($var, hexpr($var, '+', 1));
907 if (isset($this->forloop
[$oid]['last'])) {
909 $body->decl('psize_'.$oid, hexec('count', hvar_ex($details['array'])));
912 $var = 'islast_'.$oid;
913 $body->decl($var, hexpr(hvar('forcounter1_'.$oid), '==', $size));
914 $for_body->decl($var, hexpr(hvar('forcounter1_'.$oid), '==', $size));
919 if (isset($this->forloop
[$oid]['first'])) {
920 $var = hvar('isfirst_'.$oid);
921 $body->decl($var, TRUE);
922 $for_body->decl($var, FALSE);
927 if (isset($this->forloop
[$oid]['revcounter'])) {
929 $body->decl('psize_'.$oid, hexec('count', hvar_ex($details['array'])));
932 $var = hvar('revcount_'.$oid);
933 $body->decl($var, $size);
934 $for_body->decl($var, hexpr($var, '-', 1));
939 if (isset($this->forloop
[$oid]['revcounter0'])) {
941 $body->decl('psize_'.$oid, hexec('count', hvar_ex($details['array'])));
944 $var = hvar('revcount0_'.$oid);
945 $body->decl($var, hexpr($size, "-", 1));
946 $for_body->decl($var, hexpr($var, '-', 1));
950 /* Restore old ForID */
951 $this->forid
= $oldid;
953 /* Merge loop body */
954 $body->do_foreach($array, $details['variable'], $details['index'], $for_body);
956 if (isset($details['empty'])) {
962 // ifchanged [<var1> <var2] {{{
963 protected function generate_op_ifchanged($details, &$body)
965 static $ifchanged = 0;
968 $var1 = 'ifchanged'.$ifchanged;
969 if (!isset($details['check'])) {
971 $this->ob_start($body);
972 $var2 = hvar('buffer'.$this->ob_start
);
975 $this->generate_op_code($details['body'], $body);
977 $body->do_if(hexpr(hexec('isset', hvar($var1)), '==', FALSE, '||', hvar($var1), '!=', $var2));
978 $this->do_print($body, $var2);
979 $body->decl($var1, $var2);
982 foreach ($details['check'] as $id=>$type) {
983 if (!Haanga_AST
::is_var($type)) {
984 throw new Haanga_Compiler_Exception("Unexpected string {$type['string']}, expected a varabile");
987 $this_expr = hexpr(hexpr(
988 hexec('isset', hvar($var1, $id)), '==', FALSE,
989 '||', hvar($var1, $id), '!=', $type
993 $this_expr = hexpr($expr, '&&', $this_expr);
1000 $this->generate_op_code($details['body'], $body);
1001 $body->decl($var1, $details['check']);
1004 if (isset($details['else'])) {
1006 $this->generate_op_code($details['else'], $body);
1012 // autoescape ON|OFF {{{
1013 function generate_op_autoescape($details, &$body)
1015 $old_autoescape = $this->autoescape
;
1016 $this->autoescape
= strtolower($details['value']) == 'on';
1017 $this->generate_op_code($details['body'], $body);
1018 $this->autoescape
= $old_autoescape;
1022 // ob_Start(array &$body) {{{
1024 * Start a new buffering
1027 function ob_start(&$body)
1030 $body->decl('buffer'.$this->ob_start
, "");
1035 function get_custom_tag($name)
1037 $function = $this->get_function_name($this->name
).'_tag_'.$name;
1038 $this->append
.= "\n\n".Haanga_Extension
::getInstance('Tag')->getFunctionBody($name, $function);
1043 * Generate needed code for custom tags (tags that aren't
1044 * handled by the compiler).
1047 function generate_op_custom_tag($details, &$body)
1051 $tags = Haanga_Extension
::getInstance('Tag');
1054 $tag_name = $details['name'];
1055 $tagFunction = $tags->getFunctionAlias($tag_name);
1057 if (!$tagFunction && !$tags->hasGenerator($tag_name)) {
1058 $function = $this->get_custom_tag($tag_name, isset($details['as']));
1060 $function = $tagFunction;
1063 if (isset($details['body'])) {
1065 if the custom tag has 'body'
1066 then it behave the same way as a filter
1068 $this->ob_start($body);
1069 $this->generate_op_code($details['body'], $body);
1070 $target = hvar('buffer'.$this->ob_start
);
1071 if ($tags->hasGenerator($tag_name)) {
1072 $exec = $tags->generator($tag_name, $this, array($target));
1073 if (!$exec InstanceOf Haanga_AST
) {
1074 throw new Haanga_Compiler_Exception("Invalid output of custom filter {$tag_name}");
1077 $exec = hexec($function, $target);
1080 $this->do_print($body, $exec);
1084 $var = isset($details['as']) ?
$details['as'] : NULL;
1085 $args = array_merge(array($function), $details['list']);
1087 if ($tags->hasGenerator($tag_name)) {
1088 $exec = $tags->generator($tag_name, $this, $details['list'], $var);
1089 if ($exec InstanceOf Haanga_AST
) {
1090 if ($exec->stack_size() >= 2 ||
$exec->doesPrint ||
$var !== NULL) {
1092 The generator returned more than one statement,
1093 so we assume the output is already handled
1094 by one of those stmts.
1096 $body->append_ast($exec);
1100 throw new Haanga_Compiler_Exception("Invalid output of the custom tag {$tag_name}");
1103 $fnc = array_shift($args);
1104 $exec = hexec($fnc);
1105 foreach ($args as $arg) {
1111 $body->decl($var, $exec);
1113 $this->do_print($body, $exec);
1118 // with <variable> as <var> {{{
1123 function generate_op_alias($details, &$body)
1125 $this->var_alias
[ $details['as'] ] = $details['var'];
1126 $this->generate_op_code($details['body'], $body);
1127 unset($this->var_alias
[ $details['as'] ] );
1131 // Custom Filters {{{
1132 function get_custom_filter($name)
1134 $function = $this->get_function_name($this->name
).'_filter_'.$name;
1135 $this->append
.= "\n\n".Haanga_Extension
::getInstance('Filter')->getFunctionBody($name, $function);
1140 function do_filtering($name, $args)
1144 $filter = Haanga_Extension
::getInstance('Filter');
1147 if (is_array($name)) {
1149 prepare array for ($func_name, $arg1, $arg2 ... )
1150 where $arg1 = last expression and $arg2.. $argX is
1151 defined in the template
1153 $args = array_merge($args, $name['args']);
1157 if (!$filter->isValid($name)) {
1158 throw new Haanga_Compiler_Exception("{$name} is an invalid filter");
1161 if ($filter->hasGenerator($name)) {
1162 return $filter->generator($name, $this, $args);
1164 $fnc = $filter->getFunctionAlias($name);
1166 $fnc = $this->get_custom_filter($name);
1169 $args = array_merge(array($fnc), $args);
1170 $exec = call_user_func_array('hexec', $args);
1175 function generate_op_filter($details, &$body)
1177 $this->ob_start($body);
1178 $this->generate_op_code($details['body'], $body);
1179 $target = hvar('buffer'.$this->ob_start
);
1180 foreach ($details['functions'] as $f) {
1181 $param = (isset($exec) ?
$exec : $target);
1182 $exec = $this->do_filtering($f, array($param));
1185 $this->do_print($body, $exec);
1189 /* variable safety {{{ */
1190 function set_safe($name)
1192 if (!Haanga_AST
::is_Var($name)) {
1193 $name = hvar($name)->getArray();
1195 $this->safes
[serialize($name)] = TRUE;
1198 function set_unsafe($name)
1200 if (!Haanga_AST
::is_Var($name)) {
1201 $name = hvar($name)->getArray();
1203 unset($this->safes
[serialize($name)]);
1206 function is_safe($name)
1208 if ($this->var_is_safe
) {
1211 if (isset($this->safes
[serialize($name)])) {
1218 final static function main_cli()
1220 $argv = $GLOBALS['argv'];
1221 $haanga = new Haanga_Compiler
;
1222 $code = $haanga->compile_file($argv[1], TRUE);
1223 if (!isset($argv[2]) ||
$argv[2] != '--notags') {
1224 $code = "<?php\n\n$code";
1236 * vim600: sw=4 ts=4 fdm=marker
1237 * vim<600: sw=4 ts=4