Source for file Compiler.php

Documentation is available at Compiler.php

  1. <?php
  2.  
  3. include dirname(__FILE__'/Compilation/Exception.php';
  4.  
  5. /**
  6.  * default dwoo compiler class, compiles dwoo templates into php
  7.  *
  8.  * This software is provided 'as-is', without any express or implied warranty.
  9.  * In no event will the authors be held liable for any damages arising from the use of this software.
  10.  *
  11.  * @author     Jordi Boggiano <j.boggiano@seld.be>
  12.  * @copyright  Copyright (c) 2008, Jordi Boggiano
  13.  * @license    http://dwoo.org/LICENSE   Modified BSD License
  14.  * @link       http://dwoo.org/
  15.  * @version    1.1.0
  16.  * @date       2009-07-18
  17.  * @package    Dwoo
  18.  */
  19. class Dwoo_Compiler implements Dwoo_ICompiler
  20. {
  21.     /**
  22.      * constant that represents a php opening tag
  23.      *
  24.      * use it in case it needs to be adjusted
  25.      *
  26.      * @var string 
  27.      */
  28.     const PHP_OPEN "<?php ";
  29.  
  30.     /**
  31.      * constant that represents a php closing tag
  32.      *
  33.      * use it in case it needs to be adjusted
  34.      *
  35.      * @var string 
  36.      */
  37.     const PHP_CLOSE "?>";
  38.  
  39.     /**
  40.      * boolean flag to enable or disable debugging output
  41.      *
  42.      * @var bool 
  43.      */
  44.     public $debug = false;
  45.  
  46.     /**
  47.      * left script delimiter
  48.      *
  49.      * @var string 
  50.      */
  51.     protected $ld = '{';
  52.  
  53.     /**
  54.      * left script delimiter with escaped regex meta characters
  55.      *
  56.      * @var string 
  57.      */
  58.     protected $ldr = '\\{';
  59.  
  60.     /**
  61.      * right script delimiter
  62.      *
  63.      * @var string 
  64.      */
  65.     protected $rd = '}';
  66.  
  67.     /**
  68.      * right script delimiter with escaped regex meta characters
  69.      *
  70.      * @var string 
  71.      */
  72.     protected $rdr = '\\}';
  73.  
  74.     /**
  75.      * defines whether the nested comments should be parsed as nested or not
  76.      *
  77.      * defaults to false (classic block comment parsing as in all languages)
  78.      *
  79.      * @var bool 
  80.      */
  81.     protected $allowNestedComments = false;
  82.  
  83.     /**
  84.      * defines whether opening and closing tags can contain spaces before valid data or not
  85.      *
  86.      * turn to true if you want to be sloppy with the syntax, but when set to false it allows
  87.      * to skip javascript and css tags as long as they are in the form "{ something", which is
  88.      * nice. default is false.
  89.      *
  90.      * @var bool 
  91.      */
  92.     protected $allowLooseOpenings = false;
  93.  
  94.     /**
  95.      * defines whether the compiler will automatically html-escape variables or not
  96.      *
  97.      * default is false
  98.      *
  99.      * @var bool 
  100.      */
  101.     protected $autoEscape = false;
  102.  
  103.     /**
  104.      * security policy object
  105.      *
  106.      * @var Dwoo_Security_Policy 
  107.      */
  108.     protected $securityPolicy;
  109.  
  110.     /**
  111.      * stores the custom plugins registered with this compiler
  112.      *
  113.      * @var array 
  114.      */
  115.     protected $customPlugins = array();
  116.  
  117.     /**
  118.      * stores the template plugins registered with this compiler
  119.      *
  120.      * @var array 
  121.      */
  122.     protected $templatePlugins = array();
  123.  
  124.     /**
  125.      * stores the pre- and post-processors callbacks
  126.      *
  127.      * @var array 
  128.      */
  129.     protected $processors = array('pre'=>array()'post'=>array());
  130.  
  131.     /**
  132.      * stores a list of plugins that are used in the currently compiled
  133.      * template, and that are not compilable. these plugins will be loaded
  134.      * during the template's runtime if required.
  135.      *
  136.      * it is a 1D array formatted as key:pluginName value:pluginType
  137.      *
  138.      * @var array 
  139.      */
  140.     protected $usedPlugins;
  141.  
  142.     /**
  143.      * stores the template undergoing compilation
  144.      *
  145.      * @var string 
  146.      */
  147.     protected $template;
  148.  
  149.     /**
  150.      * stores the current pointer position inside the template
  151.      *
  152.      * @var int 
  153.      */
  154.     protected $pointer;
  155.  
  156.     /**
  157.      * stores the current line count inside the template for debugging purposes
  158.      *
  159.      * @var int 
  160.      */
  161.     protected $line;
  162.  
  163.     /**
  164.      * stores the current template source while compiling it
  165.      *
  166.      * @var string 
  167.      */
  168.     protected $templateSource;
  169.  
  170.     /**
  171.      * stores the data within which the scope moves
  172.      *
  173.      * @var array 
  174.      */
  175.     protected $data;
  176.  
  177.     /**
  178.      * variable scope of the compiler, set to null if
  179.      * it can not be resolved to a static string (i.e. if some
  180.      * plugin defines a new scope based on a variable array key)
  181.      *
  182.      * @var mixed 
  183.      */
  184.     protected $scope;
  185.  
  186.     /**
  187.      * variable scope tree, that allows to rebuild the current
  188.      * scope if required, i.e. when going to a parent level
  189.      *
  190.      * @var array 
  191.      */
  192.     protected $scopeTree;
  193.  
  194.     /**
  195.      * block plugins stack, accessible through some methods
  196.      *
  197.      * @see findBlock
  198.      * @see getCurrentBlock
  199.      * @see addBlock
  200.      * @see addCustomBlock
  201.      * @see injectBlock
  202.      * @see removeBlock
  203.      * @see removeTopBlock
  204.      *
  205.      * @var array 
  206.      */
  207.     protected $stack = array();
  208.  
  209.     /**
  210.      * current block at the top of the block plugins stack,
  211.      * accessible through getCurrentBlock
  212.      *
  213.      * @see getCurrentBlock
  214.      *
  215.      * @var Dwoo_Block_Plugin 
  216.      */
  217.     protected $curBlock;
  218.  
  219.     /**
  220.      * current dwoo object that uses this compiler, or null
  221.      *
  222.      * @var Dwoo 
  223.      */
  224.     protected $dwoo;
  225.  
  226.     /**
  227.      * holds an instance of this class, used by getInstance when you don't
  228.      * provide a custom compiler in order to save resources
  229.      *
  230.      * @var Dwoo_Compiler 
  231.      */
  232.     protected static $instance;
  233.  
  234.     /**
  235.      * token types
  236.      * @var int 
  237.      */
  238.     const T_UNQUOTED_STRING 1;
  239.     const T_NUMERIC 2;
  240.     const T_NULL 4;
  241.     const T_BOOL 8;
  242.     const T_MATH 16;
  243.     const T_BREAKCHAR 32;
  244.  
  245.     /**
  246.      * constructor
  247.      *
  248.      * saves the created instance so that child templates get the same one
  249.      */
  250.     public function __construct()
  251.     {
  252.         self::$instance $this;
  253.     }
  254.  
  255.     /**
  256.      * sets the delimiters to use in the templates
  257.      *
  258.      * delimiters can be multi-character strings but should not be one of those as they will
  259.      * make it very hard to work with templates or might even break the compiler entirely : "\", "$", "|", ":" and finally "#" only if you intend to use config-vars with the #var# syntax.
  260.      *
  261.      * @param string $left left delimiter
  262.      * @param string $right right delimiter
  263.      */
  264.     public function setDelimiters($left$right)
  265.     {
  266.         $this->ld = $left;
  267.         $this->rd = $right;
  268.         $this->ldr = preg_quote($left'/');
  269.         $this->rdr = preg_quote($right'/');
  270.     }
  271.  
  272.     /**
  273.      * returns the left and right template delimiters
  274.      *
  275.      * @return array containing the left and the right delimiters
  276.      */
  277.     public function getDelimiters()
  278.     {
  279.         return array($this->ld$this->rd);
  280.     }
  281.  
  282.     /**
  283.      * sets the way to handle nested comments, if set to true
  284.      * {* foo {* some other *} comment *} will be stripped correctly.
  285.      *
  286.      * if false it will remove {* foo {* some other *} and leave "comment *}" alone,
  287.      * this is the default behavior
  288.      *
  289.      * @param bool $allow allow nested comments or not, defaults to true (but the default internal value is false)
  290.      */
  291.     public function setNestedCommentsHandling($allow true{
  292.         $this->allowNestedComments = (bool) $allow;
  293.     }
  294.  
  295.     /**
  296.      * returns the nested comments handling setting
  297.      *
  298.      * @see setNestedCommentsHandling
  299.      * @return bool true if nested comments are allowed
  300.      */
  301.     public function getNestedCommentsHandling({
  302.         return $this->allowNestedComments;
  303.     }
  304.  
  305.     /**
  306.      * sets the tag openings handling strictness, if set to true, template tags can
  307.      * contain spaces before the first function/string/variable such as { $foo} is valid.
  308.      *
  309.      * if set to false (default setting), { $foo} is invalid but that is however a good thing
  310.      * as it allows css (i.e. #foo { color:red; }) to be parsed silently without triggering
  311.      * an error, same goes for javascript.
  312.      *
  313.      * @param bool $allow true to allow loose handling, false to restore default setting
  314.      */
  315.     public function setLooseOpeningHandling($allow false)
  316.     {
  317.         $this->allowLooseOpenings = (bool) $allow;
  318.     }
  319.  
  320.     /**
  321.      * returns the tag openings handling strictness setting
  322.      *
  323.      * @see setLooseOpeningHandling
  324.      * @return bool true if loose tags are allowed
  325.      */
  326.     public function getLooseOpeningHandling()
  327.     {
  328.         return $this->allowLooseOpenings;
  329.     }
  330.  
  331.     /**
  332.      * changes the auto escape setting
  333.      *
  334.      * if enabled, the compiler will automatically html-escape variables,
  335.      * unless they are passed through the safe function such as {$var|safe}
  336.      * or {safe $var}
  337.      *
  338.      * default setting is disabled/false
  339.      *
  340.      * @param bool $enabled set to true to enable, false to disable
  341.      */
  342.     public function setAutoEscape($enabled)
  343.     {
  344.         $this->autoEscape = (bool) $enabled;
  345.     }
  346.  
  347.     /**
  348.      * returns the auto escape setting
  349.      *
  350.      * default setting is disabled/false
  351.      *
  352.      * @return bool 
  353.      */
  354.     public function getAutoEscape()
  355.     {
  356.         return $this->autoEscape;
  357.     }
  358.  
  359.     /**
  360.      * adds a preprocessor to the compiler, it will be called
  361.      * before the template is compiled
  362.      *
  363.      * @param mixed $callback either a valid callback to the preprocessor or a simple name if the autoload is set to true
  364.      * @param bool $autoload if set to true, the preprocessor is auto-loaded from one of the plugin directories, else you must provide a valid callback
  365.      */
  366.     public function addPreProcessor($callback$autoload false)
  367.     {
  368.         if ($autoload{
  369.             $name str_replace('Dwoo_Processor_'''$callback);
  370.             $class 'Dwoo_Processor_'.$name;
  371.  
  372.             if (class_exists($classfalse)) {
  373.                 $callback array(new $class($this)'process');
  374.             elseif (function_exists($class)) {
  375.                 $callback $class;
  376.             else {
  377.                 $callback array('autoload'=>true'class'=>$class'name'=>$name);
  378.             }
  379.  
  380.             $this->processors['pre'][$callback;
  381.         else {
  382.             $this->processors['pre'][$callback;
  383.         }
  384.     }
  385.  
  386.     /**
  387.      * removes a preprocessor from the compiler
  388.      *
  389.      * @param mixed $callback either a valid callback to the preprocessor or a simple name if it was autoloaded
  390.      */
  391.     public function removePreProcessor($callback)
  392.     {
  393.         if (($index array_search($callback$this->processors['pre']true)) !== false{
  394.             unset($this->processors['pre'][$index]);
  395.         elseif (($index array_search('Dwoo_Processor_'.str_replace('Dwoo_Processor_'''$callback)$this->processors['pre']true)) !== false{
  396.             unset($this->processors['pre'][$index]);
  397.         else {
  398.             $class 'Dwoo_Processor_' str_replace('Dwoo_Processor_'''$callback);
  399.             foreach ($this->processors['pre'as $index=>$proc{
  400.                 if (is_array($proc&& ($proc[0instanceof $class|| (isset($proc['class']&& $proc['class'== $class)) {
  401.                     unset($this->processors['pre'][$index]);
  402.                     break;
  403.                 }
  404.             }
  405.         }
  406.     }
  407.  
  408.     /**
  409.      * adds a postprocessor to the compiler, it will be called
  410.      * before the template is compiled
  411.      *
  412.      * @param mixed $callback either a valid callback to the postprocessor or a simple name if the autoload is set to true
  413.      * @param bool $autoload if set to true, the postprocessor is auto-loaded from one of the plugin directories, else you must provide a valid callback
  414.      */
  415.     public function addPostProcessor($callback$autoload false)
  416.     {
  417.         if ($autoload{
  418.             $name str_replace('Dwoo_Processor_'''$callback);
  419.             $class 'Dwoo_Processor_'.$name;
  420.  
  421.             if (class_exists($classfalse)) {
  422.                 $callback array(new $class($this)'process');
  423.             elseif (function_exists($class)) {
  424.                 $callback $class;
  425.             else {
  426.                 $callback array('autoload'=>true'class'=>$class'name'=>$name);
  427.             }
  428.  
  429.             $this->processors['post'][$callback;
  430.         else {
  431.             $this->processors['post'][$callback;
  432.         }
  433.     }
  434.  
  435.     /**
  436.      * removes a postprocessor from the compiler
  437.      *
  438.      * @param mixed $callback either a valid callback to the postprocessor or a simple name if it was autoloaded
  439.      */
  440.     public function removePostProcessor($callback)
  441.     {
  442.         if (($index array_search($callback$this->processors['post']true)) !== false{
  443.             unset($this->processors['post'][$index]);
  444.         elseif (($index array_search('Dwoo_Processor_'.str_replace('Dwoo_Processor_'''$callback)$this->processors['post']true)) !== false{
  445.             unset($this->processors['post'][$index]);
  446.         else    {
  447.             $class 'Dwoo_Processor_' str_replace('Dwoo_Processor_'''$callback);
  448.             foreach ($this->processors['post'as $index=>$proc{
  449.                 if (is_array($proc&& ($proc[0instanceof $class|| (isset($proc['class']&& $proc['class'== $class)) {
  450.                     unset($this->processors['post'][$index]);
  451.                     break;
  452.                 }
  453.             }
  454.         }
  455.     }
  456.  
  457.     /**
  458.      * internal function to autoload processors at runtime if required
  459.      *
  460.      * @param string $class the class/function name
  461.      * @param string $name the plugin name (without Dwoo_Plugin_ prefix)
  462.      */
  463.     protected function loadProcessor($class$name)
  464.     {
  465.         if (!class_exists($classfalse&& !function_exists($class)) {
  466.             try {
  467.                 $this->dwoo->getLoader()->loadPlugin($name);
  468.             catch (Dwoo_Exception $e{
  469.                 throw new Dwoo_Exception('Processor '.$name.' could not be found in your plugin directories, please ensure it is in a file named '.$name.'.php in the plugin directory');
  470.             }
  471.         }
  472.  
  473.         if (class_exists($classfalse)) {
  474.             return array(new $class($this)'process');
  475.         }
  476.  
  477.         if (function_exists($class)) {
  478.             return $class;
  479.         }
  480.  
  481.         throw new Dwoo_Exception('Wrong processor name, when using autoload the processor must be in one of your plugin dir as "name.php" containg a class or function named "Dwoo_Processor_name"');
  482.     }
  483.  
  484.     /**
  485.      * adds an used plugin, this is reserved for use by the {template} plugin
  486.      *
  487.      * this is required so that plugin loading bubbles up from loaded
  488.      * template files to the current one
  489.      *
  490.      * @private
  491.      * @param string $name function name
  492.      * @param int $type plugin type (Dwoo::*_PLUGIN)
  493.      */
  494.     public function addUsedPlugin($name$type)
  495.     {
  496.         $this->usedPlugins[$name$type;
  497.     }
  498.  
  499.     /**
  500.      * returns all the plugins this template uses
  501.      *
  502.      * @private
  503.      * @return array the list of used plugins in the parsed template
  504.      */
  505.     public function getUsedPlugins()
  506.     {
  507.         return $this->usedPlugins;
  508.     }
  509.  
  510.     /**
  511.      * adds a template plugin, this is reserved for use by the {template} plugin
  512.      *
  513.      * this is required because the template functions are not declared yet
  514.      * during compilation, so we must have a way of validating their argument
  515.      * signature without using the reflection api
  516.      *
  517.      * @private
  518.      * @param string $name function name
  519.      * @param array $params parameter array to help validate the function call
  520.      * @param string $uuid unique id of the function
  521.      * @param string $body function php code
  522.      */
  523.     public function addTemplatePlugin($namearray $params$uuid$body null)
  524.     {
  525.         $this->templatePlugins[$namearray('params'=> $params'body' => $body'uuid' => $uuid);
  526.     }
  527.  
  528.     /**
  529. /**
  530.      * returns all the parsed sub-templates
  531.      *
  532.      * @private
  533.      * @return array the parsed sub-templates
  534.      */
  535.     public function getTemplatePlugins()
  536.     {
  537.         return $this->templatePlugins;
  538.     }
  539.  
  540.     /**
  541.      * marks a template plugin as being called, which means its source must be included in the compiled template
  542.      *
  543.      * @param string $name function name
  544.      */
  545.     public function useTemplatePlugin($name)
  546.     {
  547.         $this->templatePlugins[$name]['called'true;
  548.     }
  549.  
  550.     /**
  551.      * adds the custom plugins loaded into Dwoo to the compiler so it can load them
  552.      *
  553.      * @see Dwoo::addPlugin
  554.      * @param array $customPlugins an array of custom plugins
  555.      */
  556.     public function setCustomPlugins(array $customPlugins)
  557.     {
  558.         $this->customPlugins $customPlugins;
  559.     }
  560.  
  561.     /**
  562. /**
  563.      * sets the security policy object to enforce some php security settings
  564.      *
  565.      * use this if untrusted persons can modify templates,
  566.      * set it on the Dwoo object as it will be passed onto the compiler automatically
  567.      *
  568.      * @param Dwoo_Security_Policy $policy the security policy object
  569.      */
  570.     public function setSecurityPolicy(Dwoo_Security_Policy $policy null)
  571.     {
  572.         $this->securityPolicy = $policy;
  573.     }
  574.  
  575.     /**
  576.      * returns the current security policy object or null by default
  577.      *
  578.      * @return Dwoo_Security_Policy|nullthe security policy object if any
  579.      */
  580.     public function getSecurityPolicy()
  581.     {
  582.         return $this->securityPolicy;
  583.     }
  584.  
  585.     /**
  586.      * sets the pointer position
  587.      *
  588.      * @param int $position the new pointer position
  589.      * @param bool $isOffset if set to true, the position acts as an offset and not an absolute position
  590.      */
  591.     public function setPointer($position$isOffset false)
  592.     {
  593.         if ($isOffset{
  594.             $this->pointer += $position;
  595.         else {
  596.             $this->pointer = $position;
  597.         }
  598.     }
  599.  
  600.     /**
  601.      * returns the current pointer position, only available during compilation of a template
  602.      *
  603.      * @return int 
  604.      */
  605.     public function getPointer()
  606.     {
  607.         return $this->pointer;
  608.     }
  609.  
  610.     /**
  611.      * sets the line number
  612.      *
  613.      * @param int $number the new line number
  614.      * @param bool $isOffset if set to true, the position acts as an offset and not an absolute position
  615.      */
  616.     public function setLine($number$isOffset false)
  617.     {
  618.         if ($isOffset{
  619.             $this->line += $number;
  620.         else {
  621.             $this->line = $number;
  622.         }
  623.     }
  624.  
  625.     /**
  626.      * returns the current line number, only available during compilation of a template
  627.      *
  628.      * @return int 
  629.      */
  630.     public function getLine()
  631.     {
  632.         return $this->line;
  633.     }
  634.  
  635.     /**
  636.      * returns the dwoo object that initiated this template compilation, only available during compilation of a template
  637.      *
  638.      * @return Dwoo 
  639.      */
  640.     public function getDwoo()
  641.     {
  642.         return $this->dwoo;
  643.     }
  644.  
  645.     /**
  646.      * overwrites the template that is being compiled
  647.      *
  648.      * @param string $newSource the template source that must replace the current one
  649.      * @param bool $fromPointer if set to true, only the source from the current pointer position is replaced
  650.      * @return string the template or partial template
  651.      */
  652.     public function setTemplateSource($newSource$fromPointer false)
  653.     {
  654.         if ($fromPointer === true{
  655.             $this->templateSource = substr($this->templateSource0$this->pointer$newSource;
  656.         else {
  657.             $this->templateSource = $newSource;
  658.         }
  659.     }
  660.  
  661.     /**
  662.      * returns the template that is being compiled
  663.      *
  664.      * @param mixed $fromPointer if set to true, only the source from the current pointer
  665.      *                                position is returned, if a number is given it overrides the current pointer
  666.      * @return string the template or partial template
  667.      */
  668.     public function getTemplateSource($fromPointer false)
  669.     {
  670.         if ($fromPointer === true{
  671.             return substr($this->templateSource$this->pointer);
  672.         elseif (is_numeric($fromPointer)) {
  673.             return substr($this->templateSource$fromPointer);
  674.         else {
  675.             return $this->templateSource;
  676.         }
  677.     }
  678.  
  679.     /**
  680.      * resets the compilation pointer, effectively restarting the compilation process
  681.      *
  682.      * this is useful if a plugin modifies the template source since it might need to be recompiled
  683.      */
  684.     public function recompile()
  685.     {
  686.         $this->setPointer(0);
  687.     }
  688.  
  689.     /**
  690.      * compiles the provided string down to php code
  691.      *
  692.      * @param string $tpl the template to compile
  693.      * @return string a compiled php string
  694.      */
  695.     public function compile(Dwoo $dwooDwoo_ITemplate $template)
  696.     {
  697.         // init vars
  698.         $tpl $template->getSource();
  699.         $ptr 0;
  700.         $this->dwoo = $dwoo;
  701.         $this->template = $template;
  702.         $this->templateSource =$tpl;
  703.         $this->pointer =$ptr;
  704.  
  705.         while (true{
  706.             // if pointer is at the beginning, reset everything, that allows a plugin to externally reset the compiler if everything must be reparsed
  707.             if ($ptr===0{
  708.                 // resets variables
  709.                 $this->usedPlugins = array();
  710.                 $this->data = array();
  711.                 $this->scope =$this->data;
  712.                 $this->scopeTree = array();
  713.                 $this->stack = array();
  714.                 $this->line = 1;
  715.                 $this->templatePlugins = array();
  716.                 // add top level block
  717.                 $compiled $this->addBlock('topLevelBlock'array()0);
  718.                 $this->stack[0]['buffer''';
  719.  
  720.                 if ($this->debugecho 'COMPILER INIT<br />';
  721.  
  722.                 if ($this->debugecho 'PROCESSING PREPROCESSORS ('.count($this->processors['pre']).')<br>';
  723.  
  724.                 // runs preprocessors
  725.                 foreach ($this->processors['pre'as $preProc{
  726.                     if (is_array($preProc&& isset($preProc['autoload'])) {
  727.                         $preProc $this->loadProcessor($preProc['class']$preProc['name']);
  728.                     }
  729.                     if (is_array($preProc&& $preProc[0instanceof Dwoo_Processor{
  730.                         $tpl call_user_func($preProc$tpl);
  731.                     else {
  732.                         $tpl call_user_func($preProc$this$tpl);
  733.                     }
  734.                 }
  735.                 unset($preProc);
  736.  
  737.                 // show template source if debug
  738.                 if ($this->debugecho '<pre>'.print_r(htmlentities($tpl)true).'</pre><hr />';
  739.  
  740.                 // strips php tags if required by the security policy
  741.                 if ($this->securityPolicy !== null{
  742.                     $search array('{<\?php.*?\?>}');
  743.                     if (ini_get('short_open_tags')) {
  744.                         $search array('{<\?.*?\?>}''{<%.*?%>}');
  745.                     }
  746.                     switch($this->securityPolicy->getPhpHandling()) {
  747.  
  748.                     case Dwoo_Security_Policy::PHP_ALLOW:
  749.                         break;
  750.                     case Dwoo_Security_Policy::PHP_ENCODE:
  751.                         $tpl preg_replace_callback($searcharray($this'phpTagEncodingHelper')$tpl);
  752.                         break;
  753.                     case Dwoo_Security_Policy::PHP_REMOVE:
  754.                         $tpl preg_replace($search''$tpl);
  755.  
  756.                     }
  757.                 }
  758.             }
  759.  
  760.             $pos strpos($tpl$this->ld$ptr);
  761.  
  762.             if ($pos === false{
  763.                 $this->push(substr($tpl$ptr)0);
  764.                 break;
  765.             elseif (substr($tpl$pos-11=== '\\' && substr($tpl$pos-21!== '\\'{
  766.                 $this->push(substr($tpl$ptr$pos-$ptr-1$this->ld);
  767.                 $ptr $pos+strlen($this->ld);
  768.             elseif (preg_match('/^'.$this->ldr . ($this->allowLooseOpenings ? '\s*' '''literal' ($this->allowLooseOpenings ? '\s*' ''$this->rdr.'/s'substr($tpl$pos)$litOpen)) {
  769.                 if (!preg_match('/'.$this->ldr . ($this->allowLooseOpenings ? '\s*' '''\/literal' ($this->allowLooseOpenings ? '\s*' ''$this->rdr.'/s'$tpl$litClosePREG_OFFSET_CAPTURE$pos)) {
  770.                     throw new Dwoo_Compilation_Exception($this'The {literal} blocks must be closed explicitly with {/literal}');
  771.                 }
  772.                 $endpos $litClose[0][1];
  773.                 $this->push(substr($tpl$ptr$pos-$ptrsubstr($tpl$pos strlen($litOpen[0])$endpos-$pos-strlen($litOpen[0])));
  774.                 $ptr $endpos+strlen($litClose[0][0]);
  775.             else {
  776.                 if (substr($tpl$pos-21=== '\\' && substr($tpl$pos-11=== '\\'{
  777.                     $this->push(substr($tpl$ptr$pos-$ptr-1));
  778.                     $ptr $pos;
  779.                 }
  780.  
  781.                 $this->push(substr($tpl$ptr$pos-$ptr));
  782.                 $ptr $pos;
  783.  
  784.                 $pos += strlen($this->ld);
  785.                 if ($this->allowLooseOpenings{
  786.                     while (substr($tpl$pos1=== ' '{
  787.                         $pos+=1;
  788.                     }
  789.                 else {
  790.                     if (substr($tpl$pos1=== ' ' || substr($tpl$pos1=== "\r" || substr($tpl$pos1=== "\n" || substr($tpl$pos1=== "\t"{
  791.                         $ptr $pos;
  792.                         $this->push($this->ld);
  793.                         continue;
  794.                     }
  795.                 }
  796.  
  797.                 // check that there is an end tag present
  798.                 if (strpos($tpl$this->rd$pos=== false{
  799.                     throw new Dwoo_Compilation_Exception($this'A template tag was not closed, started with "'.substr($tpl$ptr30).'"');
  800.                 }
  801.  
  802.  
  803.                 $ptr += strlen($this->ld);
  804.                 $subptr $ptr;
  805.  
  806.                 while (true{
  807.                     $parsed $this->parse($tpl$subptrnullfalse'root'$subptr);
  808.  
  809.                     // reload loop if the compiler was reset
  810.                     if ($ptr === 0{
  811.                         continue 2;
  812.                     }
  813.  
  814.                     $len $subptr $ptr;
  815.                     $this->push($parsedsubstr_count(substr($tpl$ptr$len)"\n"));
  816.                     $ptr += $len;
  817.  
  818.                     if ($parsed === false{
  819.                         break;
  820.                     }
  821.                 }
  822.  
  823.                 // adds additional line breaks between php closing and opening tags because the php parser removes those if there is just a single line break
  824.                 if (substr($this->curBlock['buffer']-2=== '?>' && preg_match('{^(([\r\n])([\r\n]?))}'substr($tpl$ptr3)$m)) {
  825.                     if ($m[3=== ''{
  826.                         $ptr+=1;
  827.                         $this->push($m[1].$m[1]1);
  828.                     else {
  829.                         $ptr+=2;
  830.                         $this->push($m[1]."\n"2);
  831.                     }
  832.                 }
  833.             }
  834.         }
  835.  
  836.         $compiled .= $this->removeBlock('topLevelBlock');
  837.  
  838.         if ($this->debugecho 'PROCESSING POSTPROCESSORS<br>';
  839.  
  840.         foreach ($this->processors['post'as $postProc{
  841.             if (is_array($postProc&& isset($postProc['autoload'])) {
  842.                 $postProc $this->loadProcessor($postProc['class']$postProc['name']);
  843.             }
  844.             if (is_array($postProc&& $postProc[0instanceof Dwoo_Processor{
  845.                 $compiled call_user_func($postProc$compiled);
  846.             else {
  847.                 $compiled call_user_func($postProc$this$compiled);
  848.             }
  849.         }
  850.         unset($postProc);
  851.  
  852.         if ($this->debugecho 'COMPILATION COMPLETE : MEM USAGE : '.memory_get_usage().'<br>';
  853.  
  854.         $output "<?php\n/* template head */\n";
  855.  
  856.         // build plugin preloader
  857.         foreach ($this->usedPlugins as $plugin=>$type{
  858.             if ($type Dwoo::CUSTOM_PLUGIN{
  859.                 continue;
  860.             }
  861.  
  862.             switch($type{
  863.  
  864.             case Dwoo::BLOCK_PLUGIN:
  865.             case Dwoo::CLASS_PLUGIN:
  866.                 $output .= "if (class_exists('Dwoo_Plugin_$plugin', false)===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
  867.                 break;
  868.             case Dwoo::FUNC_PLUGIN:
  869.                 $output .= "if (function_exists('Dwoo_Plugin_$plugin')===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
  870.                 break;
  871.             case Dwoo::SMARTY_MODIFIER:
  872.                 $output .= "if (function_exists('smarty_modifier_$plugin')===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
  873.                 break;
  874.             case Dwoo::SMARTY_FUNCTION:
  875.                 $output .= "if (function_exists('smarty_function_$plugin')===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
  876.                 break;
  877.             case Dwoo::SMARTY_BLOCK:
  878.                 $output .= "if (function_exists('smarty_block_$plugin')===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
  879.                 break;
  880.             case Dwoo::PROXY_PLUGIN:
  881.                 $output .= $this->getDwoo()->getPluginProxy()->getPreloader($plugin);
  882.                 break;
  883.             default:
  884.                 throw new Dwoo_Compilation_Exception($this'Type error for '.$plugin.' with type'.$type);
  885.  
  886.             }
  887.         }
  888.  
  889.         foreach ($this->templatePlugins as $function => $attr{
  890.             if (isset($attr['called']&& $attr['called'=== true && !isset($attr['checked'])) {
  891.                 $this->resolveSubTemplateDependencies($function);
  892.             }
  893.         }
  894.         foreach ($this->templatePlugins as $function{
  895.             if (isset($function['called']&& $function['called'=== true{
  896.                 $output .= $function['body'].PHP_EOL;
  897.             }
  898.         }
  899.  
  900.         $output .= $compiled."\n?>";
  901.  
  902.         $output preg_replace('/(?<!;|\}|\*\/|\n|\{)(\s*'.preg_quote(self::PHP_CLOSE'/'preg_quote(self::PHP_OPEN'/').')/'";\n"$output);
  903.         $output str_replace(self::PHP_CLOSE self::PHP_OPEN"\n"$output);
  904.  
  905.         // handle <?xml tag at the beginning
  906.         $output preg_replace('#(/\* template body \*/ \?>\s*)<\?xml#is''$1<?php echo \'<?xml\'; ?>'$output);
  907.  
  908.         if ($this->debug{
  909.             echo '<hr><pre>';
  910.             $lines preg_split('{\r\n|\n|<br />}'highlight_string(($output)true));
  911.             array_shift($lines);
  912.             foreach ($lines as $i=>$line{
  913.                 echo ($i+1).'. '.$line."\r\n";
  914.             }
  915.         }
  916.         if ($this->debugecho '<hr></pre></pre>';
  917.  
  918.         $this->template = $this->dwoo = null;
  919.         $tpl null;
  920.  
  921.         return $output;
  922.     }
  923.  
  924.     /**
  925.      * checks what sub-templates are used in every sub-template so that we're sure they are all compiled
  926.      *
  927.      * @param string $function the sub-template name
  928.      */
  929.     protected function resolveSubTemplateDependencies($function)
  930.     {
  931.         $body $this->templatePlugins[$function]['body'];
  932.         foreach ($this->templatePlugins as $func => $attr{
  933.             if ($func !== $function && !isset($attr['called']&& strpos($body'Dwoo_Plugin_'.$func!== false{
  934.                 $this->templatePlugins[$func]['called'true;
  935.                 $this->resolveSubTemplateDependencies($func);
  936.             }
  937.         }
  938.         $this->templatePlugins[$function]['checked'true;
  939.     }
  940.  
  941.     /**
  942.      * adds compiled content to the current block
  943.      *
  944.      * @param string $content the content to push
  945.      * @param int $lineCount newlines count in content, optional
  946.      */
  947.     public function push($content$lineCount null)
  948.     {
  949.         if ($lineCount === null{
  950.             $lineCount substr_count($content"\n");
  951.         }
  952.  
  953.         if ($this->curBlock['buffer'=== null && count($this->stack1{
  954.             // buffer is not initialized yet (the block has just been created)
  955.             $this->stack[count($this->stack)-2]['buffer'.= (string) $content;
  956.             $this->curBlock['buffer''';
  957.         else {
  958.             if (!isset($this->curBlock['buffer'])) {
  959.                 throw new Dwoo_Compilation_Exception($this'The template has been closed too early, you probably have an extra block-closing tag somewhere');
  960.             }
  961.             // append current content to current block's buffer
  962.             $this->curBlock['buffer'.= (string) $content;
  963.         }
  964.         $this->line += $lineCount;
  965.     }
  966.  
  967.     /**
  968.      * sets the scope
  969.      *
  970.      * set to null if the scope becomes "unstable" (i.e. too variable or unknown) so that
  971.      * variables are compiled in a more evaluative way than just $this->scope['key']
  972.      *
  973.      * @param mixed $scope a string i.e. "level1.level2" or an array i.e. array("level1", "level2")
  974.      * @param bool $absolute if true, the scope is set from the top level scope and not from the current scope
  975.      * @return array the current scope tree
  976.      */
  977.     public function setScope($scope$absolute false)
  978.     {
  979.         $old $this->scopeTree;
  980.  
  981.         if ($scope===null{
  982.             unset($this->scope);
  983.             $this->scope = null;
  984.         }
  985.  
  986.         if (is_array($scope)===false{
  987.             $scope explode('.'$scope);
  988.         }
  989.  
  990.         if ($absolute===true{
  991.             $this->scope =$this->data;
  992.             $this->scopeTree = array();
  993.         }
  994.  
  995.         while (($bit array_shift($scope)) !== null{
  996.             if ($bit === '_parent' || $bit === '_'{
  997.                 array_pop($this->scopeTree);
  998.                 reset($this->scopeTree);
  999.                 $this->scope =$this->data;
  1000.                 $cnt count($this->scopeTree);
  1001.                 for ($i=0;$i<$cnt;$i++)
  1002.                     $this->scope =$this->scope[$this->scopeTree[$i]];
  1003.             elseif ($bit === '_root' || $bit === '__'{
  1004.                 $this->scope =$this->data;
  1005.                 $this->scopeTree = array();
  1006.             elseif (isset($this->scope[$bit])) {
  1007.                 $this->scope =$this->scope[$bit];
  1008.                 $this->scopeTree[$bit;
  1009.             else {
  1010.                 $this->scope[$bitarray();
  1011.                 $this->scope =$this->scope[$bit];
  1012.                 $this->scopeTree[$bit;
  1013.             }
  1014.         }
  1015.  
  1016.         return $old;
  1017.     }
  1018.  
  1019.     /**
  1020.      * adds a block to the top of the block stack
  1021.      *
  1022.      * @param string $type block type (name)
  1023.      * @param array $params the parameters array
  1024.      * @param int $paramtype the parameters type (see mapParams), 0, 1 or 2
  1025.      * @return string the preProcessing() method's output
  1026.      */
  1027.     public function addBlock($typearray $params$paramtype)
  1028.     {
  1029.         $class 'Dwoo_Plugin_'.$type;
  1030.         if (class_exists($classfalse=== false{
  1031.             $this->dwoo->getLoader()->loadPlugin($type);
  1032.         }
  1033.  
  1034.         $params $this->mapParams($paramsarray($class'init')$paramtype);
  1035.  
  1036.         $this->stack[array('type' => $type'params' => $params'custom' => false'class' => $class'buffer' => null);
  1037.         $this->curBlock =$this->stack[count($this->stack)-1];
  1038.         return call_user_func(array($class,'preProcessing')$this$params''''$type);
  1039.     }
  1040.  
  1041.     /**
  1042.      * adds a custom block to the top of the block stack
  1043.      *
  1044.      * @param string $type block type (name)
  1045.      * @param array $params the parameters array
  1046.      * @param int $paramtype the parameters type (see mapParams), 0, 1 or 2
  1047.      * @return string the preProcessing() method's output
  1048.      */
  1049.     public function addCustomBlock($typearray $params$paramtype)
  1050.     {
  1051.         $callback $this->customPlugins[$type]['callback'];
  1052.         if (is_array($callback)) {
  1053.             $class is_object($callback[0]get_class($callback[0]$callback[0];
  1054.         else {
  1055.             $class $callback;
  1056.         }
  1057.  
  1058.         $params $this->mapParams($paramsarray($class'init')$paramtype);
  1059.  
  1060.         $this->stack[array('type' => $type'params' => $params'custom' => true'class' => $class'buffer' => null);
  1061.         $this->curBlock =$this->stack[count($this->stack)-1];
  1062.         return call_user_func(array($class,'preProcessing')$this$params''''$type);
  1063.     }
  1064.  
  1065.     /**
  1066.      * injects a block at the top of the plugin stack without calling its preProcessing method
  1067.      *
  1068.      * used by {else} blocks to re-add themselves after having closed everything up to their parent
  1069.      *
  1070.      * @param string $type block type (name)
  1071.      * @param array $params parameters array
  1072.      */
  1073.     public function injectBlock($typearray $params)
  1074.     {
  1075.         $class 'Dwoo_Plugin_'.$type;
  1076.         if (class_exists($classfalse=== false{
  1077.             $this->dwoo->getLoader()->loadPlugin($type);
  1078.         }
  1079.         $this->stack[array('type' => $type'params' => $params'custom' => false'class' => $class'buffer' => null);
  1080.         $this->curBlock =$this->stack[count($this->stack)-1];
  1081.     }
  1082.  
  1083.     /**
  1084.      * removes the closest-to-top block of the given type and all other
  1085.      * blocks encountered while going down the block stack
  1086.      *
  1087.      * @param string $type block type (name)
  1088.      * @return string the output of all postProcessing() method's return values of the closed blocks
  1089.      */
  1090.     public function removeBlock($type)
  1091.     {
  1092.         $output '';
  1093.  
  1094.         $pluginType $this->getPluginType($type);
  1095.         if ($pluginType Dwoo::SMARTY_BLOCK{
  1096.             $type 'smartyinterface';
  1097.         }
  1098.         while (true{
  1099.             while ($top array_pop($this->stack)) {
  1100.                 if ($top['custom']{
  1101.                     $class $top['class'];
  1102.                 else {
  1103.                     $class 'Dwoo_Plugin_'.$top['type'];
  1104.                 }
  1105.                 if (count($this->stack)) {
  1106.                     $this->curBlock =$this->stack[count($this->stack)-1];
  1107.                     $this->push(call_user_func(array($class'postProcessing')$this$top['params']''''$top['buffer'])0);
  1108.                 else {
  1109.                     $null null;
  1110.                     $this->curBlock =$null;
  1111.                     $output call_user_func(array($class'postProcessing')$this$top['params']''''$top['buffer']);
  1112.                 }
  1113.  
  1114.                 if ($top['type'=== $type{
  1115.                     break 2;
  1116.                 }
  1117.             }
  1118.  
  1119.             throw new Dwoo_Compilation_Exception($this'Syntax malformation, a block of type "'.$type.'" was closed but was not opened');
  1120.             break;
  1121.         }
  1122.  
  1123.         return $output;
  1124.     }
  1125.  
  1126.     /**
  1127.      * returns a reference to the first block of the given type encountered and
  1128.      * optionally closes all blocks until it finds it
  1129.      *
  1130.      * this is mainly used by {else} plugins to close everything that was opened
  1131.      * between their parent and themselves
  1132.      *
  1133.      * @param string $type the block type (name)
  1134.      * @param bool $closeAlong whether to close all blocks encountered while going down the block stack or not
  1135.      * @return &array the array is as such: array('type'=>pluginName, 'params'=>parameter array,
  1136.      *                    'custom'=>bool defining whether it's a custom plugin or not, for internal use)
  1137.      */
  1138.     public function &findBlock($type$closeAlong false)
  1139.     {
  1140.         if ($closeAlong===true{
  1141.             while ($b end($this->stack)) {
  1142.                 if ($b['type']===$type{
  1143.                     return $this->stack[key($this->stack)];
  1144.                 }
  1145.                 $this->push($this->removeTopBlock()0);
  1146.             }
  1147.         else {
  1148.             end($this->stack);
  1149.             while ($b current($this->stack)) {
  1150.                 if ($b['type']===$type{
  1151.                     return $this->stack[key($this->stack)];
  1152.                 }
  1153.                 prev($this->stack);
  1154.             }
  1155.         }
  1156.  
  1157.         throw new Dwoo_Compilation_Exception($this'A parent block of type "'.$type.'" is required and can not be found');
  1158.     }
  1159.  
  1160.     /**
  1161.      * returns a reference to the current block array
  1162.      *
  1163.      * @return &array the array is as such: array('type'=>pluginName, 'params'=>parameter array,
  1164.      *                    'custom'=>bool defining whether it's a custom plugin or not, for internal use)
  1165.      */
  1166.     public function &getCurrentBlock()
  1167.     {
  1168.         return $this->curBlock;
  1169.     }
  1170.  
  1171.     /**
  1172.      * removes the block at the top of the stack and calls its postProcessing() method
  1173.      *
  1174.      * @return string the postProcessing() method's output
  1175.      */
  1176.     public function removeTopBlock()
  1177.     {
  1178.         $o array_pop($this->stack);
  1179.         if ($o === null{
  1180.             throw new Dwoo_Compilation_Exception($this'Syntax malformation, a block of unknown type was closed but was not opened.');
  1181.         }
  1182.         if ($o['custom']{
  1183.             $class $o['class'];
  1184.         else {
  1185.             $class 'Dwoo_Plugin_'.$o['type'];
  1186.         }
  1187.  
  1188.         $this->curBlock =$this->stack[count($this->stack)-1];
  1189.  
  1190.         return call_user_func(array($class'postProcessing')$this$o['params']''''$o['buffer']);
  1191.     }
  1192.  
  1193.     /**
  1194.      * returns the compiled parameters (for example a variable's compiled parameter will be "$this->scope['key']") out of the given parameter array
  1195.      *
  1196.      * @param array $params parameter array
  1197.      * @return array filtered parameters
  1198.      */
  1199.     public function getCompiledParams(array $params)
  1200.     {
  1201.         foreach ($params as $k=>$p{
  1202.             if (is_array($p)) {
  1203.                 $params[$k$p[0];
  1204.             }
  1205.         }
  1206.         return $params;
  1207.     }
  1208.  
  1209.     /**
  1210.      * returns the real parameters (for example a variable's real parameter will be its key, etc) out of the given parameter array
  1211.      *
  1212.      * @param array $params parameter array
  1213.      * @return array filtered parameters
  1214.      */
  1215.     public function getRealParams(array $params)
  1216.     {
  1217.         foreach ($params as $k=>$p{
  1218.             if (is_array($p)) {
  1219.                 $params[$k$p[1];
  1220.             }
  1221.         }
  1222.         return $params;
  1223.     }
  1224.  
  1225.     /**
  1226.      * returns the token of each parameter out of the given parameter array
  1227.      *
  1228.      * @param array $params parameter array
  1229.      * @return array tokens
  1230.      */
  1231.     public function getParamTokens(array $params)
  1232.     {
  1233.         foreach ($params as $k=>$p{
  1234.             if (is_array($p)) {
  1235.                 $params[$k= isset($p[2]$p[20;
  1236.             }
  1237.         }
  1238.         return $params;
  1239.     }
  1240.  
  1241.     /**
  1242.      * entry point of the parser, it redirects calls to other parse* functions
  1243.      *
  1244.      * @param string $in the string within which we must parse something
  1245.      * @param int $from the starting offset of the parsed area
  1246.      * @param int $to the ending offset of the parsed area
  1247.      * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
  1248.      * @param string $curBlock the current parser-block being processed
  1249.      * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
  1250.      * @return string parsed values
  1251.      */
  1252.     protected function parse($in$from$to$parsingParams false$curBlock=''&$pointer null)
  1253.     {
  1254.         if ($to === null{
  1255.             $to strlen($in);
  1256.         }
  1257.         $first substr($in$from1);
  1258.  
  1259.         if ($first === false{
  1260.             throw new Dwoo_Compilation_Exception($this'Unexpected EOF, a template tag was not closed');
  1261.         }
  1262.  
  1263.         while ($first===" " || $first==="\n" || $first==="\t" || $first==="\r"{
  1264.             if ($curBlock === 'root' && substr($in$fromstrlen($this->rd)) === $this->rd{
  1265.                 // end template tag
  1266.                 $pointer += strlen($this->rd);
  1267.                 if ($this->debugecho 'TEMPLATE PARSING ENDED<br />';
  1268.                 return false;
  1269.             }
  1270.             $from++;
  1271.             if ($pointer !== null{
  1272.                 $pointer++;
  1273.             }
  1274.             if ($from >= $to{
  1275.                 if (is_array($parsingParams)) {
  1276.                     return $parsingParams;
  1277.                 else {
  1278.                     return '';
  1279.                 }
  1280.             }
  1281.             $first $in[$from];
  1282.         }
  1283.  
  1284.         $substr substr($in$from$to-$from);
  1285.  
  1286.         if ($this->debugecho '<br />PARSE CALL : PARSING "<b>'.htmlentities(substr($in$frommin($to-$from50))).(($to-$from50 '...':'').'</b>" @ '.$from.':'.$to.' in '.$curBlock.' : pointer='.$pointer.'<br/>';
  1287.         $parsed "";
  1288.  
  1289.         if ($curBlock === 'root' && $first === '*'{
  1290.             $src $this->getTemplateSource();
  1291.             $startpos $this->getPointer(strlen($this->ld);
  1292.             if (substr($src$startposstrlen($this->ld)) === $this->ld{
  1293.                 if ($startpos 0{
  1294.                     do {
  1295.                         $char substr($src--$startpos1);
  1296.                         if ($char == "\n"{
  1297.                             $startpos++;
  1298.                             $whitespaceStart true;
  1299.                             break;
  1300.                         }
  1301.                     while ($startpos && ($char == ' ' || $char == "\t"));
  1302.                 }
  1303.  
  1304.                 if (!isset($whitespaceStart)) {
  1305.                     $startpos $this->getPointer();
  1306.                 else {
  1307.                     $pointer -= $this->getPointer($startpos;
  1308.                 }
  1309.  
  1310.                 if ($this->allowNestedComments && strpos($src$this->ld.'*'$this->getPointer()) !== false{
  1311.                     $comOpen $this->ld.'*';
  1312.                     $comClose '*'.$this->rd;
  1313.                     $level 1;
  1314.                     $start $startpos;
  1315.                     $ptr $this->getPointer('*';
  1316.  
  1317.                     while ($level && $ptr strlen($src)) {
  1318.                         $open strpos($src$comOpen$ptr);
  1319.                         $close strpos($src$comClose$ptr);
  1320.  
  1321.                         if ($open !== false && $close !== false{
  1322.                             if ($open $close{
  1323.                                 $ptr $open strlen($comOpen);
  1324.                                 $level++;
  1325.                             else {
  1326.                                 $ptr $close strlen($comClose);
  1327.                                 $level--;
  1328.                             }
  1329.                         elseif ($open !== false{
  1330.                             $ptr $open strlen($comOpen);
  1331.                             $level++;
  1332.                         elseif ($close !== false{
  1333.                             $ptr $close strlen($comClose);
  1334.                             $level--;
  1335.                         else {
  1336.                             $ptr strlen($src);
  1337.                         }
  1338.                     }
  1339.                     $endpos $ptr strlen('*'.$this->rd);
  1340.                 else {
  1341.                     $endpos strpos($src'*'.$this->rd$startpos);
  1342.                     if ($endpos == false{
  1343.                         throw new Dwoo_Compilation_Exception($this'Un-ended comment');
  1344.                     }
  1345.                 }
  1346.                 $pointer += $endpos $startpos strlen('*'.$this->rd);
  1347.                 if (isset($whitespaceStart&& preg_match('#^[\t ]*\r?\n#'substr($src$endpos+strlen('*'.$this->rd))$m)) {
  1348.                     $pointer += strlen($m[0]);
  1349.                     $this->curBlock['buffer'substr($this->curBlock['buffer']0strlen($this->curBlock['buffer']($this->getPointer($startpos strlen($this->ld)));
  1350.                 }
  1351.                 return false;
  1352.             }
  1353.         }
  1354.  
  1355.         if ($first==='$'{
  1356.             // var
  1357.             $out $this->parseVar($in$from$to$parsingParams$curBlock$pointer);
  1358.             $parsed 'var';
  1359.         elseif ($first==='%' && preg_match('#^%[a-z]#i'$substr)) {
  1360.             // const
  1361.             $out $this->parseConst($in$from$to$parsingParams$curBlock$pointer);
  1362.         elseif ($first==='"' || $first==="'"{
  1363.             // string
  1364.             $out $this->parseString($in$from$to$parsingParams$curBlock$pointer);
  1365.         elseif (preg_match('/^[a-z][a-z0-9_]*(?:::[a-z][a-z0-9_]*)?('.(is_array($parsingParams)||$curBlock!='root'?'':'\s+[^(]|').'\s*\(|\s*'.$this->rdr.'|\s*;)/i'$substr)) {
  1366.             // func
  1367.             $out $this->parseFunction($in$from$to$parsingParams$curBlock$pointer);
  1368.             $parsed 'func';
  1369.         elseif ($first === ';'{
  1370.             // instruction end
  1371.             if ($this->debugecho 'END OF INSTRUCTION<br />';
  1372.             if ($pointer !== null{
  1373.                 $pointer++;
  1374.             }
  1375.             return $this->parse($in$from+1$tofalse'root'$pointer);
  1376.         elseif ($curBlock === 'root' && preg_match('#^/([a-z][a-z0-9_]*)?#i'$substr$match)) {
  1377.             // close block
  1378.             if (!empty($match[1]&& $match[1== 'else'{
  1379.                 throw new Dwoo_Compilation_Exception($this'Else blocks must not be closed explicitly, they are automatically closed when their parent block is closed');
  1380.             }
  1381.             if (!empty($match[1]&& $match[1== 'elseif'{
  1382.                 throw new Dwoo_Compilation_Exception($this'Elseif blocks must not be closed explicitly, they are automatically closed when their parent block is closed or a new else/elseif block is declared after them');
  1383.             }
  1384.             if ($pointer !== null{
  1385.                 $pointer += strlen($match[0]);
  1386.             }
  1387.             if (empty($match[1])) {
  1388.                 if ($this->curBlock['type'== 'else' || $this->curBlock['type'== 'elseif'{
  1389.                     $pointer -= strlen($match[0]);
  1390.                 }
  1391.                 if ($this->debugecho 'TOP BLOCK CLOSED<br />';
  1392.                 return $this->removeTopBlock();
  1393.             else {
  1394.                 if ($this->debugecho 'BLOCK OF TYPE '.$match[1].' CLOSED<br />';
  1395.                 return $this->removeBlock($match[1]);
  1396.             }
  1397.         elseif ($curBlock === 'root' && substr($substr0strlen($this->rd)) === $this->rd{
  1398.             // end template tag
  1399.             if ($this->debugecho 'TAG PARSING ENDED<br />';
  1400.             $pointer += strlen($this->rd);
  1401.             return false;
  1402.         elseif (is_array($parsingParams&& preg_match('#^([a-z0-9_]+\s*=)(?:\s+|[^=]).*#i'$substr$match)) {
  1403.             // named parameter
  1404.             if ($this->debugecho 'NAMED PARAM FOUND<br />';
  1405.             $len strlen($match[1]);
  1406.             while (substr($in$from+$len1)===' '{
  1407.                 $len++;
  1408.             }
  1409.             if ($pointer !== null{
  1410.                 $pointer += $len;
  1411.             }
  1412.  
  1413.             $output array(trim(substr(trim($match[1])0-1))$this->parse($in$from+$len$tofalse'namedparam'$pointer));
  1414.  
  1415.             $parsingParams[$output;
  1416.             return $parsingParams;
  1417.         elseif (preg_match('#^([a-z0-9_]+::\$[a-z0-9_]+)#i'$substr$match)) {
  1418.             // static member access
  1419.             $parsed 'var';
  1420.             if (is_array($parsingParams)) {
  1421.                 $parsingParams[array($match[1]$match[1]);
  1422.                 $out $parsingParams;
  1423.             else {
  1424.                 $out $match[1];
  1425.             }
  1426.             $pointer += strlen($match[1]);
  1427.         elseif ($substr!=='' && (is_array($parsingParams|| $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'expression')) {
  1428.             // unquoted string, bool or number
  1429.             $out $this->parseOthers($in$from$to$parsingParams$curBlock$pointer);
  1430.         else {
  1431.             // parse error
  1432.             throw new Dwoo_Compilation_Exception($this'Parse error in "'.substr($in$from$to-$from).'"');
  1433.         }
  1434.  
  1435.         if (empty($out)) {
  1436.             return '';
  1437.         }
  1438.  
  1439.         $substr substr($in$pointer$to-$pointer);
  1440.  
  1441.         // var parsed, check if any var-extension applies
  1442.         if ($parsed==='var'{
  1443.             if (preg_match('#^\s*([/%+*-])\s*([a-z0-9]|\$)#i'$substr$match)) {
  1444.                 if($this->debugecho 'PARSING POST-VAR EXPRESSION '.$substr.'<br />';
  1445.                 // parse expressions
  1446.                 $pointer += strlen($match[0]1;
  1447.                 if (is_array($parsingParams)) {
  1448.                     if ($match[2== '$'{
  1449.                         $expr $this->parseVar($in$pointer$toarray()$curBlock$pointer);
  1450.                     else {
  1451.                         $expr $this->parse($in$pointer$toarray()'expression'$pointer);
  1452.                     }
  1453.                     $out[count($out)-1][0.= $match[1$expr[0][0];
  1454.                     $out[count($out)-1][1.= $match[1$expr[0][1];
  1455.                 else {
  1456.                     if ($match[2== '$'{
  1457.                         $expr $this->parseVar($in$pointer$tofalse$curBlock$pointer);
  1458.                     else {
  1459.                         $expr $this->parse($in$pointer$tofalse'expression'$pointer);
  1460.                     }
  1461.                     if (is_array($out&& is_array($expr)) {
  1462.                         $out[0.= $match[1$expr[0];
  1463.                         $out[1.= $match[1$expr[1];
  1464.                     elseif (is_array($out)) {
  1465.                         $out[0.= $match[1$expr;
  1466.                         $out[1.= $match[1$expr;
  1467.                     elseif (is_array($expr)) {
  1468.                         $out .= $match[1$expr[0];
  1469.                     else {
  1470.                         $out .= $match[1$expr;
  1471.                     }
  1472.                 }
  1473.             else if ($curBlock === 'root' && preg_match('#^(\s*(?:[+/*%-.]=|=|\+\+|--)\s*)(.*)#s'$substr$match)) {
  1474.                 if($this->debugecho 'PARSING POST-VAR ASSIGNMENT '.$substr.'<br />';
  1475.                 // parse assignment
  1476.                 $value $match[2];
  1477.                 $operator trim($match[1]);
  1478.                 if (substr($value01== '='{
  1479.                     throw new Dwoo_Compilation_Exception($this'Unexpected "=" in <em>'.$substr.'</em>');
  1480.                 }
  1481.  
  1482.                 if ($pointer !== null{
  1483.                     $pointer += strlen($match[1]);
  1484.                 }
  1485.  
  1486.                 if ($operator !== '++' && $operator !== '--'{
  1487.                     $parts array();
  1488.                     $ptr 0;
  1489.                     $parts $this->parse($value0strlen($value)$parts'condition'$ptr);
  1490.                     $pointer += $ptr;
  1491.  
  1492.                     // load if plugin
  1493.                     try {
  1494.                         $this->getPluginType('if');
  1495.                     catch (Dwoo_Exception $e{
  1496.                         throw new Dwoo_Compilation_Exception($this'Assignments require the "if" plugin to be accessible');
  1497.                     }
  1498.  
  1499.                     $parts $this->mapParams($partsarray('Dwoo_Plugin_if''init')1);
  1500.                     $tokens $this->getParamTokens($parts);
  1501.                     $parts $this->getCompiledParams($parts);
  1502.  
  1503.                     $value Dwoo_Plugin_if::replaceKeywords($parts['*']$tokens['*']$this);
  1504.                     $echo '';
  1505.                 else {
  1506.                     $value array();
  1507.                     $echo 'echo ';
  1508.                 }
  1509.  
  1510.                 if ($this->autoEscape{
  1511.                     $out preg_replace('#\(is_string\(\$tmp=(.+?)\) \? htmlspecialchars\(\$tmp, ENT_QUOTES, \$this->charset\) : \$tmp\)#''$1'$out);
  1512.                 }
  1513.                 $out Dwoo_Compiler::PHP_OPEN$echo $out $operator implode(' '$valueDwoo_Compiler::PHP_CLOSE;
  1514.             }
  1515.         }
  1516.  
  1517.         if ($curBlock !== 'modifier' && ($parsed === 'func' || $parsed === 'var'&& preg_match('#^\|@?[a-z0-9_]+(:.*)?#i'$substr$match)) {
  1518.             // parse modifier on funcs or vars
  1519.             $srcPointer $pointer;
  1520.             if (is_array($parsingParams)) {
  1521.                 $tmp $this->replaceModifiers(array(nullnull$out[count($out)-1][0]$match[0])'var'$pointer);
  1522.                 $out[count($out)-1][0$tmp;
  1523.                 $out[count($out)-1][1.= substr($substr$srcPointer$srcPointer $pointer);
  1524.             else {
  1525.                 $out $this->replaceModifiers(array(nullnull$out$match[0])'var'$pointer);
  1526.             }
  1527.         }
  1528.  
  1529.         // func parsed, check if any func-extension applies
  1530.         if ($parsed==='func' && preg_match('#^->[a-z0-9_]+(\s*\(.+|->[a-z].*)?#is'$substr$match)) {
  1531.             // parse method call or property read
  1532.             $ptr 0;
  1533.  
  1534.             if (is_array($parsingParams)) {
  1535.                 $output $this->parseMethodCall($out[count($out)-1][1]$match[0]$curBlock$ptr);
  1536.  
  1537.                 $out[count($out)-1][0$output;
  1538.                 $out[count($out)-1][1.= substr($match[0]0$ptr);
  1539.             else {
  1540.                 $out $this->parseMethodCall($out$match[0]$curBlock$ptr);
  1541.             }
  1542.  
  1543.             $pointer += $ptr;
  1544.         }
  1545.  
  1546.         if ($curBlock === 'root' && substr($out0strlen(self::PHP_OPEN)) !== self::PHP_OPEN{
  1547.             return self::PHP_OPEN .'echo '.$out.';'self::PHP_CLOSE;
  1548.         else {
  1549.             return $out;
  1550.         }
  1551.     }
  1552.  
  1553.     /**
  1554.      * parses a function call
  1555.      *
  1556.      * @param string $in the string within which we must parse something
  1557.      * @param int $from the starting offset of the parsed area
  1558.      * @param int $to the ending offset of the parsed area
  1559.      * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
  1560.      * @param string $curBlock the current parser-block being processed
  1561.      * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
  1562.      * @return string parsed values
  1563.      */
  1564.     protected function parseFunction($in$from$to$parsingParams false$curBlock=''&$pointer null)
  1565.     {
  1566.         $cmdstr substr($in$from$to-$from);
  1567.         preg_match('/^([a-z][a-z0-9_]*(?:::[a-z][a-z0-9_]*)?)(\s*'.$this->rdr.'|\s*;)?/i'$cmdstr$match);
  1568.  
  1569.         if (empty($match[1])) {
  1570.             throw new Dwoo_Compilation_Exception($this'Parse error, invalid function name : '.substr($cmdstr015));
  1571.         }
  1572.  
  1573.         $func $match[1];
  1574.  
  1575.         if (!empty($match[2])) {
  1576.             $cmdstr $match[1];
  1577.         }
  1578.  
  1579.         if ($this->debugecho 'FUNC FOUND ('.$func.')<br />';
  1580.  
  1581.         $paramsep '';
  1582.  
  1583.         if (is_array($parsingParams|| $curBlock != 'root'{
  1584.             $paramspos strpos($cmdstr'(');
  1585.             $paramsep ')';
  1586.         elseif(preg_match_all('#[a-z0-9_]+(\s*\(|\s+[^(])#i'$cmdstr$matchPREG_OFFSET_CAPTURE)) {
  1587.             $paramspos $match[1][0][1];
  1588.             $paramsep substr($match[1][0][0]-1=== '(' ')':'';
  1589.             if($paramsep === ')'{
  1590.                 $paramspos += strlen($match[1][0][0]1;
  1591.                 if(substr($cmdstr02=== 'if' || substr($cmdstr06=== 'elseif'{
  1592.                     $paramsep '';
  1593.                     if(strlen($match[1][0][0]1{
  1594.                         $paramspos--;
  1595.                     }
  1596.                 }
  1597.             }
  1598.         else {
  1599.             $paramspos false;
  1600.         }
  1601.  
  1602.         $state 0;
  1603.  
  1604.         if ($paramspos === false{
  1605.             $params array();
  1606.  
  1607.             if ($curBlock !== 'root'{
  1608.                 return $this->parseOthers($in$from$to$parsingParams$curBlock$pointer);
  1609.             }
  1610.         else {
  1611.             if ($curBlock === 'condition'{
  1612.                 // load if plugin
  1613.                 $this->getPluginType('if');
  1614.  
  1615.                 if (Dwoo_Plugin_if::replaceKeywords(array($func)array(self::T_UNQUOTED_STRING)$this!== array($func)) {
  1616.                     return $this->parseOthers($in$from$to$parsingParams$curBlock$pointer);
  1617.                 }
  1618.             }
  1619.             $whitespace strlen(substr($cmdstrstrlen($func)$paramspos-strlen($func)));
  1620.             $paramstr substr($cmdstr$paramspos+1);
  1621.             if (substr($paramstr-11=== $paramsep{
  1622.                 $paramstr substr($paramstr0-1);
  1623.             }
  1624.  
  1625.             if (strlen($paramstr)===0{
  1626.                 $params array();
  1627.                 $paramstr '';
  1628.             else {
  1629.                 $ptr 0;
  1630.                 $params array();
  1631.                 if ($func === 'empty'{
  1632.                     $params $this->parseVar($paramstr$ptrstrlen($paramstr)$params'root'$ptr);
  1633.                 else {
  1634.                     while ($ptr strlen($paramstr)) {
  1635.                         while (true{
  1636.                             if ($ptr >= strlen($paramstr)) {
  1637.                                 break 2;
  1638.                             }
  1639.  
  1640.                             if ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr=== ')'{
  1641.                                 if ($this->debugecho 'PARAM PARSING ENDED, ")" FOUND, POINTER AT '.$ptr.'<br/>';
  1642.                                 break 2;
  1643.                             elseif ($paramstr[$ptr=== ';'{
  1644.                                 $ptr++;
  1645.                                 if ($this->debugecho 'PARAM PARSING ENDED, ";" FOUND, POINTER AT '.$ptr.'<br/>';
  1646.                                 break 2;
  1647.                             elseif ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr=== '/'{
  1648.                                 if ($this->debugecho 'PARAM PARSING ENDED, "/" FOUND, POINTER AT '.$ptr.'<br/>';
  1649.                                 break 2;
  1650.                             elseif (substr($paramstr$ptrstrlen($this->rd)) === $this->rd{
  1651.                                 if ($this->debugecho 'PARAM PARSING ENDED, RIGHT DELIMITER FOUND, POINTER AT '.$ptr.'<br/>';
  1652.                                 break 2;
  1653.                             }
  1654.  
  1655.                             if ($paramstr[$ptr=== ' ' || $paramstr[$ptr=== ',' || $paramstr[$ptr=== "\r" || $paramstr[$ptr=== "\n" || $paramstr[$ptr=== "\t"{
  1656.                                 $ptr++;
  1657.                             else {
  1658.                                 break;
  1659.                             }
  1660.                         }
  1661.  
  1662.                         if ($this->debugecho 'FUNC START PARAM PARSING WITH POINTER AT '.$ptr.'<br/>';
  1663.  
  1664.                         if ($func === 'if' || $func === 'elseif' || $func === 'tif'{
  1665.                             $params $this->parse($paramstr$ptrstrlen($paramstr)$params'condition'$ptr);
  1666.                         else {
  1667.                             $params $this->parse($paramstr$ptrstrlen($paramstr)$params'function'$ptr);
  1668.                         }
  1669.  
  1670.                         if ($this->debugecho 'PARAM PARSED, POINTER AT '.$ptr.' ('.substr($paramstr$ptr-13).')<br/>';
  1671.                     }
  1672.                 }
  1673.                 $paramstr substr($paramstr0$ptr);
  1674.                 $state 0;
  1675.                 foreach ($params as $k=>$p{
  1676.                     if (is_array($p&& is_array($p[1])) {
  1677.                         $state |= 2;
  1678.                     else {
  1679.                         if (($state 2&& preg_match('#^(["\'])(.+?)\1$#'$p[0]$m)) {
  1680.                             $params[$karray($m[2]array('true''true'));
  1681.                         else {
  1682.                             if ($state 2{
  1683.                                 throw new Dwoo_Compilation_Exception($this'You can not use an unnamed parameter after a named one');
  1684.                             }
  1685.                             $state |= 1;
  1686.                         }
  1687.                     }
  1688.                 }
  1689.             }
  1690.         }
  1691.  
  1692.         if ($pointer !== null{
  1693.             $pointer += (isset($paramstrstrlen($paramstr0(')' === $paramsep ($paramspos === false 1)) strlen($func(isset($whitespace$whitespace 0);
  1694.             if ($this->debugecho 'FUNC ADDS '.((isset($paramstrstrlen($paramstr0(')' === $paramsep ($paramspos === false 1)) strlen($func)).' TO POINTER<br/>';
  1695.         }
  1696.  
  1697.         if ($curBlock === 'method' || $func === 'do' || strstr($func'::'!== false{
  1698.             $pluginType Dwoo::NATIVE_PLUGIN;
  1699.         else {
  1700.             $pluginType $this->getPluginType($func);
  1701.         }
  1702.  
  1703.         // blocks
  1704.         if ($pluginType Dwoo::BLOCK_PLUGIN{
  1705.             if ($curBlock !== 'root' || is_array($parsingParams)) {
  1706.                 throw new Dwoo_Compilation_Exception($this'Block plugins can not be used as other plugin\'s arguments');
  1707.             }
  1708.             if ($pluginType Dwoo::CUSTOM_PLUGIN{
  1709.                 return $this->addCustomBlock($func$params$state);
  1710.             else {
  1711.                 return $this->addBlock($func$params$state);
  1712.             }
  1713.         elseif ($pluginType Dwoo::SMARTY_BLOCK{
  1714.             if ($curBlock !== 'root' || is_array($parsingParams)) {
  1715.                 throw new Dwoo_Compilation_Exception($this'Block plugins can not be used as other plugin\'s arguments');
  1716.             }
  1717.  
  1718.             if ($state 2{
  1719.                 array_unshift($paramsarray('__functype'array($pluginType$pluginType)));
  1720.                 array_unshift($paramsarray('__funcname'array($func$func)));
  1721.             else {
  1722.                 array_unshift($paramsarray($pluginType$pluginType));
  1723.                 array_unshift($paramsarray($func$func));
  1724.             }
  1725.  
  1726.             return $this->addBlock('smartyinterface'$params$state);
  1727.         }
  1728.  
  1729.         // funcs
  1730.         if ($pluginType Dwoo::NATIVE_PLUGIN || $pluginType Dwoo::SMARTY_FUNCTION || $pluginType Dwoo::SMARTY_BLOCK{
  1731.             $params $this->mapParams($paramsnull$state);
  1732.         elseif ($pluginType Dwoo::CLASS_PLUGIN{
  1733.             if ($pluginType Dwoo::CUSTOM_PLUGIN{
  1734.                 $params $this->mapParams($paramsarray($this->customPlugins[$func]['class']$this->customPlugins[$func]['function'])$state);
  1735.             else {
  1736.                 $params $this->mapParams($paramsarray('Dwoo_Plugin_'.$func($pluginType Dwoo::COMPILABLE_PLUGIN'compile' 'process')$state);
  1737.             }
  1738.         elseif ($pluginType Dwoo::FUNC_PLUGIN{
  1739.             if ($pluginType Dwoo::CUSTOM_PLUGIN{
  1740.                 $params $this->mapParams($params$this->customPlugins[$func]['callback']$state);
  1741.             else {
  1742.                 $params $this->mapParams($params'Dwoo_Plugin_'.$func.(($pluginType Dwoo::COMPILABLE_PLUGIN'_compile' '')$state);
  1743.             }
  1744.         elseif ($pluginType Dwoo::SMARTY_MODIFIER{
  1745.             $output 'smarty_modifier_'.$func.'('.implode(', '$params).')';
  1746.         elseif ($pluginType Dwoo::PROXY_PLUGIN{
  1747.             $params $this->mapParams($params$this->getDwoo()->getPluginProxy()->getCallback($func)$state);
  1748.         elseif ($pluginType Dwoo::TEMPLATE_PLUGIN{
  1749.             // transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values))
  1750.             $map array();
  1751.             foreach ($this->templatePlugins[$func]['params'as $param=>$defValue{
  1752.                 if ($param == 'rest'{
  1753.                     $param '*';
  1754.                 }
  1755.                 $hasDefault $defValue !== null;
  1756.                 if ($defValue === 'null'{
  1757.                     $defValue null;
  1758.                 elseif ($defValue === 'false'{
  1759.                     $defValue false;
  1760.                 elseif ($defValue === 'true'{
  1761.                     $defValue true;
  1762.                 elseif (preg_match('#^([\'"]).*?\1$#'$defValue)) {
  1763.                     $defValue substr($defValue1-1);
  1764.                 }
  1765.                 $map[array($param$hasDefault$defValue);
  1766.             }
  1767.  
  1768.             $params $this->mapParams($paramsnull$state$map);
  1769.         }
  1770.  
  1771.         // only keep php-syntax-safe values for non-block plugins
  1772.         $tokens array();
  1773.         foreach ($params as $k => $p{
  1774.             $tokens[$k= isset($p[2]$p[20;
  1775.             $params[$k$p[0];
  1776.         }
  1777.         if ($pluginType Dwoo::NATIVE_PLUGIN{
  1778.             if ($func === 'do'{
  1779.                 if (isset($params['*'])) {
  1780.                     $output implode(';'$params['*']).';';
  1781.                 else {
  1782.                     $output '';
  1783.                 }
  1784.  
  1785.                 if (is_array($parsingParams|| $curBlock !== 'root'{
  1786.                     throw new Dwoo_Compilation_Exception($this'Do can not be used inside another function or block');
  1787.                 else {
  1788.                     return self::PHP_OPEN.$output.self::PHP_CLOSE;
  1789.                 }
  1790.             else {
  1791.                 if (isset($params['*'])) {
  1792.                     $output $func.'('.implode(', '$params['*']).')';
  1793.                 else {
  1794.                     $output $func.'()';
  1795.                 }
  1796.             }
  1797.         elseif ($pluginType Dwoo::FUNC_PLUGIN{
  1798.             if ($pluginType Dwoo::COMPILABLE_PLUGIN{
  1799.                 if ($pluginType Dwoo::CUSTOM_PLUGIN{
  1800.                     $funcCompiler $this->customPlugins[$func]['callback'];
  1801.                 else {
  1802.                     $funcCompiler 'Dwoo_Plugin_'.$func.'_compile';
  1803.                 }
  1804.                 array_unshift($params$this);
  1805.                 if ($func === 'tif'{
  1806.                     $params[$tokens;
  1807.                 }
  1808.                 $output call_user_func_array($funcCompiler$params);
  1809.             else {
  1810.                 array_unshift($params'$this');
  1811.                 $params self::implode_r($params);
  1812.  
  1813.                 if ($pluginType Dwoo::CUSTOM_PLUGIN{
  1814.                     $callback $this->customPlugins[$func]['callback'];
  1815.                     $output 'call_user_func(\''.$callback.'\', '.$params.')';
  1816.                 else {
  1817.                     $output 'Dwoo_Plugin_'.$func.'('.$params.')';
  1818.                 }
  1819.             }
  1820.         elseif ($pluginType Dwoo::CLASS_PLUGIN{
  1821.             if ($pluginType Dwoo::COMPILABLE_PLUGIN{
  1822.                 if ($pluginType Dwoo::CUSTOM_PLUGIN{
  1823.                     $callback $this->customPlugins[$func]['callback'];
  1824.                     if (!is_array($callback)) {
  1825.                         if (!method_exists($callback'compile')) {
  1826.                             throw new Dwoo_Exception('Custom plugin '.$func.' must implement the "compile" method to be compilable, or you should provide a full callback to the method to use');
  1827.                         }
  1828.                         if (($ref new ReflectionMethod($callback'compile')) && $ref->isStatic()) {
  1829.                             $funcCompiler array($callback'compile');
  1830.                         else {
  1831.                             $funcCompiler array(new $callback'compile');
  1832.                         }
  1833.                     else {
  1834.                         $funcCompiler $callback;
  1835.                     }
  1836.                 else {
  1837.                     $funcCompiler array('Dwoo_Plugin_'.$func'compile');
  1838.                     array_unshift($params$this);
  1839.                 }
  1840.                 $output call_user_func_array($funcCompiler$params);
  1841.             else {
  1842.                 $params self::implode_r($params);
  1843.                 if ($pluginType Dwoo::CUSTOM_PLUGIN{
  1844.                     $callback $this->customPlugins[$func]['callback'];
  1845.                     if (!is_array($callback)) {
  1846.                         if (!method_exists($callback'process')) {
  1847.                             throw new Dwoo_Exception('Custom plugin '.$func.' must implement the "process" method to be usable, or you should provide a full callback to the method to use');
  1848.                         }
  1849.                         if (($ref new ReflectionMethod($callback'process')) && $ref->isStatic()) {
  1850.                             $output 'call_user_func(array(\''.$callback.'\', \'process\'), '.$params.')';
  1851.                         else {
  1852.                             $output 'call_user_func(array($this->getObjectPlugin(\''.$callback.'\'), \'process\'), '.$params.')';
  1853.                         }
  1854.                     elseif (is_object($callback[0])) {
  1855.                         $output 'call_user_func(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), '.$params.')';
  1856.                     elseif (($ref new ReflectionMethod($callback[0]$callback[1])) && $ref->isStatic()) {
  1857.                         $output 'call_user_func(array(\''.$callback[0].'\', \''.$callback[1].'\'), '.$params.')';
  1858.                     else {
  1859.                         $output 'call_user_func(array($this->getObjectPlugin(\''.$callback[0].'\'), \''.$callback[1].'\'), '.$params.')';
  1860.                     }
  1861.                     if (empty($params)) {
  1862.                         $output substr($output0-3).')';
  1863.                     }
  1864.                 else {
  1865.                     $output '$this->classCall(\''.$func.'\', array('.$params.'))';
  1866.                 }
  1867.             }
  1868.         elseif ($pluginType Dwoo::PROXY_PLUGIN{
  1869.             $output call_user_func(array($this->dwoo->getPluginProxy()'getCode')$func$params);
  1870.         elseif ($pluginType Dwoo::SMARTY_FUNCTION{
  1871.             if (isset($params['*'])) {
  1872.                 $params self::implode_r($params['*']true);
  1873.             else {
  1874.                 $params '';
  1875.             }
  1876.  
  1877.             if ($pluginType Dwoo::CUSTOM_PLUGIN{
  1878.                 $callback $this->customPlugins[$func]['callback'];
  1879.                 if (is_array($callback)) {
  1880.                     if (is_object($callback[0])) {
  1881.                         $output 'call_user_func_array(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), array(array('.$params.'), $this))';
  1882.                     else {
  1883.                         $output 'call_user_func_array(array(\''.$callback[0].'\', \''.$callback[1].'\'), array(array('.$params.'), $this))';
  1884.                     }
  1885.                 else {
  1886.                     $output $callback.'(array('.$params.'), $this)';
  1887.                 }
  1888.             else {
  1889.                 $output 'smarty_function_'.$func.'(array('.$params.'), $this)';
  1890.             }
  1891.         elseif ($pluginType Dwoo::TEMPLATE_PLUGIN{
  1892.             array_unshift($params'$this');
  1893.             $params self::implode_r($params);
  1894.             $output 'Dwoo_Plugin_'.$func.'_'.$this->templatePlugins[$func]['uuid'].'('.$params.')';
  1895.             $this->templatePlugins[$func]['called'true;
  1896.         }
  1897.  
  1898.         if (is_array($parsingParams)) {
  1899.             $parsingParams[array($output$output);
  1900.             return $parsingParams;
  1901.         elseif ($curBlock === 'namedparam'{
  1902.             return array($output$output);
  1903.         else {
  1904.             return $output;
  1905.         }
  1906.     }
  1907.  
  1908.     /**
  1909.      * parses a string
  1910.      *
  1911.      * @param string $in the string within which we must parse something
  1912.      * @param int $from the starting offset of the parsed area
  1913.      * @param int $to the ending offset of the parsed area
  1914.      * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
  1915.      * @param string $curBlock the current parser-block being processed
  1916.      * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
  1917.      * @return string parsed values
  1918.      */
  1919.     protected function parseString($in$from$to$parsingParams false$curBlock=''&$pointer null)
  1920.     {
  1921.         $substr substr($in$from$to-$from);
  1922.         $first $substr[0];
  1923.  
  1924.         if ($this->debugecho 'STRING FOUND (in '.htmlentities(substr($in$frommin($to-$from50))).(($to-$from50 '...':'').')<br />';
  1925.         $strend false;
  1926.         $o $from+1;
  1927.         while ($strend === false{
  1928.             $strend strpos($in$first$o);
  1929.             if ($strend === false{
  1930.                 throw new Dwoo_Compilation_Exception($this'Unfinished string, started with '.substr($in$from$to-$from));
  1931.             }
  1932.             if (substr($in$strend-11=== '\\'{
  1933.                 $o $strend+1;
  1934.                 $strend false;
  1935.             }
  1936.         }
  1937.         if ($this->debugecho 'STRING DELIMITED: '.substr($in$from$strend+1-$from).'<br/>';
  1938.  
  1939.         $srcOutput substr($in$from$strend+1-$from);
  1940.  
  1941.         if ($pointer !== null{
  1942.             $pointer += strlen($srcOutput);
  1943.         }
  1944.  
  1945.         $output $this->replaceStringVars($srcOutput$first);
  1946.  
  1947.         // handle modifiers
  1948.         if ($curBlock !== 'modifier' && preg_match('#^((?:\|(?:@?[a-z0-9_]+(?::.*)*))+)#i'substr($substr$strend+1-$from)$match)) {
  1949.             $modstr $match[1];
  1950.  
  1951.             if ($curBlock === 'root' && substr($modstr-1=== '}'{
  1952.                 $modstr substr($modstr0-1);
  1953.             }
  1954.             $modstr str_replace('\\'.$first$first$modstr);
  1955.             $ptr 0;
  1956.             $output $this->replaceModifiers(array(nullnull$output$modstr)'string'$ptr);
  1957.  
  1958.             $strend += $ptr;
  1959.             if ($pointer !== null{
  1960.                 $pointer += $ptr;
  1961.             }
  1962.             $srcOutput .= substr($substr$strend+1-$from$ptr);
  1963.         }
  1964.  
  1965.         if (is_array($parsingParams)) {
  1966.             $parsingParams[array($outputsubstr($srcOutput1-1));
  1967.             return $parsingParams;
  1968.         elseif ($curBlock === 'namedparam'{
  1969.             return array($outputsubstr($srcOutput1-1));
  1970.         else {
  1971.             return $output;
  1972.         }
  1973.     }
  1974.  
  1975.     /**
  1976.      * parses a constant
  1977.      *
  1978.      * @param string $in the string within which we must parse something
  1979.      * @param int $from the starting offset of the parsed area
  1980.      * @param int $to the ending offset of the parsed area
  1981.      * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
  1982.      * @param string $curBlock the current parser-block being processed
  1983.      * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
  1984.      * @return string parsed values
  1985.      */
  1986.     protected function parseConst($in$from$to$parsingParams false$curBlock=''&$pointer null)
  1987.     {
  1988.         $substr substr($in$from$to-$from);
  1989.  
  1990.         if ($this->debug{
  1991.             echo 'CONST FOUND : '.$substr.'<br />';
  1992.         }
  1993.  
  1994.         if (!preg_match('#^%([a-z0-9_:]+)#i'$substr$m)) {
  1995.             throw new Dwoo_Compilation_Exception($this'Invalid constant');
  1996.         }
  1997.  
  1998.         if ($pointer !== null{
  1999.             $pointer += strlen($m[0]);
  2000.         }
  2001.  
  2002.         $output $this->parseConstKey($m[1]$curBlock);
  2003.  
  2004.         if (is_array($parsingParams)) {
  2005.             $parsingParams[array($output$m[1]);
  2006.             return $parsingParams;
  2007.         elseif ($curBlock === 'namedparam'{
  2008.             return array($output$m[1]);
  2009.         else {
  2010.             return $output;
  2011.         }
  2012.     }
  2013.  
  2014.     /**
  2015.      * parses a constant
  2016.      *
  2017.      * @param string $key the constant to parse
  2018.      * @param string $curBlock the current parser-block being processed
  2019.      * @return string parsed constant
  2020.      */
  2021.     protected function parseConstKey($key$curBlock)
  2022.     {
  2023.         if ($this->securityPolicy !== null && $this->securityPolicy->getConstantHandling(=== Dwoo_Security_Policy::CONST_DISALLOW{
  2024.             return 'null';
  2025.         }
  2026.  
  2027.         if ($curBlock !== 'root'{
  2028.             $output '(defined("'.$key.'") ? '.$key.' : null)';
  2029.         else {
  2030.             $output $key;
  2031.         }
  2032.  
  2033.         return $output;
  2034.     }
  2035.  
  2036.     /**
  2037.      * parses a variable
  2038.      *
  2039.      * @param string $in the string within which we must parse something
  2040.      * @param int $from the starting offset of the parsed area
  2041.      * @param int $to the ending offset of the parsed area
  2042.      * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
  2043.      * @param string $curBlock the current parser-block being processed
  2044.      * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
  2045.      * @return string parsed values
  2046.      */
  2047.     protected function parseVar($in$from$to$parsingParams false$curBlock=''&$pointer null)
  2048.     {
  2049.         $substr substr($in$from$to-$from);
  2050.  
  2051.         if (preg_match('#(\$?\.?[a-z0-9_:]*(?:(?:(?:\.|->)(?:[a-z0-9_:]+|(?R))|\[(?:[a-z0-9_:]+|(?R)|(["\'])[^\2]*?\2)\]))*)' // var key
  2052.             ($curBlock==='root' || $curBlock==='function' || $curBlock==='namedparam' || $curBlock==='condition' || $curBlock==='variable' || $curBlock==='expression' '(\(.*)?' '()'// method call
  2053.             ($curBlock==='root' || $curBlock==='function' || $curBlock==='namedparam' || $curBlock==='condition' || $curBlock==='variable' || $curBlock==='delimited_string' '((?:(?:[+/*%=-])(?:(?<!=)=?-?[$%][a-z0-9.[\]>_:-]+(?:\([^)]*\))?|(?<!=)=?-?[0-9.,]*|[+-]))*)':'()'// simple math expressions
  2054.             ($curBlock!=='modifier' '((?:\|(?:@?[a-z0-9_]+(?:(?::("|\').*?\5|:[^`]*))*))+)?':'(())'// modifiers
  2055.             '#i'$substr$match)) {
  2056.             $key substr($match[1]1);
  2057.  
  2058.             $matchedLength strlen($match[0]);
  2059.             $hasModifiers !empty($match[5]);
  2060.             $hasExpression !empty($match[4]);
  2061.             $hasMethodCall !empty($match[3]);
  2062.  
  2063.             if (substr($key-1== "."{
  2064.                 $key substr($key0-1);
  2065.                 $matchedLength--;
  2066.             }
  2067.  
  2068.             if ($hasMethodCall{
  2069.                 $matchedLength -= strlen($match[3]strlen(substr($match[1]strrpos($match[1]'->')));
  2070.                 $key substr($match[1]1strrpos($match[1]'->')-1);
  2071.                 $methodCall substr($match[1]strrpos($match[1]'->')) $match[3];
  2072.             }
  2073.  
  2074.             if ($hasModifiers{
  2075.                 $matchedLength -= strlen($match[5]);
  2076.             }
  2077.  
  2078.             if ($pointer !== null{
  2079.                 $pointer += $matchedLength;
  2080.             }
  2081.  
  2082.             // replace useless brackets by dot accessed vars
  2083.             $key preg_replace('#\[([^$%\[.>-]+)\]#''.$1'$key);
  2084.  
  2085.             // prevent $foo->$bar calls because it doesn't seem worth the trouble
  2086.             if (strpos($key'->$'!== false{
  2087.                 throw new Dwoo_Compilation_Exception($this'You can not access an object\'s property using a variable name.');
  2088.             }
  2089.  
  2090.             if ($this->debug{
  2091.                 if ($hasMethodCall{
  2092.                     echo 'METHOD CALL FOUND : $'.$key.substr($methodCall030).'<br />';
  2093.                 else {
  2094.                     echo 'VAR FOUND : $'.$key.'<br />';
  2095.                 }
  2096.             }
  2097.  
  2098.             $key str_replace('"''\\"'$key);
  2099.  
  2100.             $cnt=substr_count($key'$');
  2101.             if ($cnt 0{
  2102.                 $uid 0;
  2103.                 $parsed array($uid => '');
  2104.                 $current =$parsed;
  2105.                 $curTxt =$parsed[$uid++];
  2106.                 $tree array();
  2107.                 $chars str_split($key1);
  2108.                 $inSplittedVar false;
  2109.                 $bracketCount 0;
  2110.  
  2111.                 while (($char array_shift($chars)) !== null{
  2112.                     if ($char === '['{
  2113.                         if (count($tree0{
  2114.                             $bracketCount++;
  2115.                         else {
  2116.                             $tree[=$current;
  2117.                             $current[$uidarray($uid+=> '');
  2118.                             $current =$current[$uid++];
  2119.                             $curTxt =$current[$uid++];
  2120.                             continue;
  2121.                         }
  2122.                     elseif ($char === ']'{
  2123.                         if ($bracketCount 0{
  2124.                             $bracketCount--;
  2125.                         else {
  2126.                             $current =$tree[count($tree)-1];
  2127.                             array_pop($tree);
  2128.                             if (current($chars!== '[' && current($chars!== false && current($chars!== ']'{
  2129.                                 $current[$uid'';
  2130.                                 $curTxt =$current[$uid++];
  2131.                             }
  2132.                             continue;
  2133.                         }
  2134.                     elseif ($char === '$'{
  2135.                         if (count($tree== 0{
  2136.                             $curTxt =$current[$uid++];
  2137.                             $inSplittedVar true;
  2138.                         }
  2139.                     elseif (($char === '.' || $char === '-'&& count($tree== && $inSplittedVar{
  2140.                         $curTxt =$current[$uid++];
  2141.                         $inSplittedVar false;
  2142.                     }
  2143.  
  2144.                     $curTxt .= $char;
  2145.                 }
  2146.                 unset($uid$current$curTxt$tree$chars);
  2147.  
  2148.                 if ($this->debugecho 'RECURSIVE VAR REPLACEMENT : '.$key.'<br>';
  2149.  
  2150.                 $key $this->flattenVarTree($parsed);
  2151.  
  2152.                 if ($this->debugecho 'RECURSIVE VAR REPLACEMENT DONE : '.$key.'<br>';
  2153.  
  2154.                 $output preg_replace('#(^""\.|""\.|\.""$|(\()""\.|\.""(\)))#''$2$3''$this->readVar("'.$key.'")');
  2155.             else {
  2156.                 $output $this->parseVarKey($key$hasModifiers 'modifier' $curBlock);
  2157.             }
  2158.  
  2159.             // methods
  2160.             if ($hasMethodCall{
  2161.                 $ptr 0;
  2162.  
  2163.                 $output $this->parseMethodCall($output$methodCall$curBlock$ptr);
  2164.  
  2165.                 if ($pointer !== null{
  2166.                     $pointer += $ptr;
  2167.                 }
  2168.                 $matchedLength += $ptr;
  2169.             }
  2170.  
  2171.             if ($hasExpression{
  2172.                 // expressions
  2173.                 preg_match_all('#(?:([+/*%=-])(=?-?[%$][a-z0-9.[\]>_:-]+(?:\([^)]*\))?|=?-?[0-9.,]+|\1))#i'$match[4]$expMatch);
  2174.  
  2175.                 foreach ($expMatch[1as $k=>$operator{
  2176.                     if (substr($expMatch[2][$k]01)==='='{
  2177.                         $assign true;
  2178.                         if ($operator === '='{
  2179.                             throw new Dwoo_Compilation_Exception($this'Invalid expression <em>'.$substr.'</em>, can not use "==" in expressions');
  2180.                         }
  2181.                         if ($curBlock !== 'root'{
  2182.                             throw new Dwoo_Compilation_Exception($this'Invalid expression <em>'.$substr.'</em>, assignments can only be used in top level expressions like {$foo+=3} or {$foo="bar"}');
  2183.                         }
  2184.                         $operator .= '=';
  2185.                         $expMatch[2][$ksubstr($expMatch[2][$k]1);
  2186.                     }
  2187.  
  2188.                     if (substr($expMatch[2][$k]01)==='-' && strlen($expMatch[2][$k]1{
  2189.                         $operator .= '-';
  2190.                         $expMatch[2][$ksubstr($expMatch[2][$k]1);
  2191.                     }
  2192.                     if (($operator==='+'||$operator==='-'&& $expMatch[2][$k]===$operator{
  2193.                         $output '('.$output.$operator.$operator.')';
  2194.                         break;
  2195.                     elseif (substr($expMatch[2][$k]01=== '$'{
  2196.                         $output '('.$output.' '.$operator.' '.$this->parseVar($expMatch[2][$k]0strlen($expMatch[2][$k])false'expression').')';
  2197.                     elseif (substr($expMatch[2][$k]01=== '%'{
  2198.                         $output '('.$output.' '.$operator.' '.$this->parseConst($expMatch[2][$k]0strlen($expMatch[2][$k])false'expression').')';
  2199.                     elseif (!empty($expMatch[2][$k])) {
  2200.                         $output '('.$output.' '.$operator.' '.str_replace(',''.'$expMatch[2][$k]).')';
  2201.                     else {
  2202.                         throw new Dwoo_Compilation_Exception($this'Unfinished expression <em>'.$substr.'</em>, missing var or number after math operator');
  2203.                     }
  2204.                 }
  2205.             }
  2206.  
  2207.             if ($this->autoEscape === true{
  2208.                 $output '(is_string($tmp='.$output.') ? htmlspecialchars($tmp, ENT_QUOTES, $this->charset) : $tmp)';
  2209.             }
  2210.  
  2211.             // handle modifiers
  2212.             if ($curBlock !== 'modifier' && $hasModifiers{
  2213.                 $ptr 0;
  2214.                 $output $this->replaceModifiers(array(nullnull$output$match[5])'var'$ptr);
  2215.                 if ($pointer !== null{
  2216.                     $pointer += $ptr;
  2217.                 }
  2218.                 $matchedLength += $ptr;
  2219.             }
  2220.  
  2221.             if (is_array($parsingParams)) {
  2222.                 $parsingParams[array($output$key);
  2223.                 return $parsingParams;
  2224.             elseif ($curBlock === 'namedparam'{
  2225.                 return array($output$key);
  2226.             elseif ($curBlock === 'string' || $curBlock === 'delimited_string'{
  2227.                 return array($matchedLength$output);
  2228.             elseif ($curBlock === 'expression' || $curBlock === 'variable'{
  2229.                 return $output;
  2230.             elseif (isset($assign)) {
  2231.                 return self::PHP_OPEN.$output.';'.self::PHP_CLOSE;
  2232.             else {
  2233.                 return $output;
  2234.             }
  2235.         else {
  2236.             if ($curBlock === 'string' || $curBlock === 'delimited_string'{
  2237.                 return array(0'');
  2238.             else {
  2239.                 throw new Dwoo_Compilation_Exception($this'Invalid variable name <em>'.$substr.'</em>');
  2240.             }
  2241.         }
  2242.     }
  2243.  
  2244.     /**
  2245.      * parses any number of chained method calls/property reads
  2246.      *
  2247.      * @param string $output the variable or whatever upon which the method are called
  2248.      * @param string $methodCall method call source, starting at "->"
  2249.      * @param string $curBlock the current parser-block being processed
  2250.      * @param int $pointer a reference to a pointer that will be increased by the amount of characters parsed
  2251.      * @return string parsed call(s)/read(s)
  2252.      */
  2253.     protected function parseMethodCall($output$methodCall$curBlock&$pointer)
  2254.     {
  2255.         $ptr 0;
  2256.         $len strlen($methodCall);
  2257.  
  2258.         while ($ptr $len{
  2259.             if (strpos($methodCall'->'$ptr=== $ptr{
  2260.                 $ptr += 2;
  2261.             }
  2262.  
  2263.             if (in_array($methodCall[$ptr]array(';''/'' '"\t""\r""\n"')''+''*''%''=''-''|')) || substr($methodCall$ptrstrlen($this->rd)) === $this->rd{
  2264.                 // break char found
  2265.                 break;
  2266.             }
  2267.  
  2268.             if(!preg_match('/^([a-z0-9_]+)(\(.*?\))?/i'substr($methodCall$ptr)$methMatch)) {
  2269.                 throw new Dwoo_Compilation_Exception($this'Invalid method name : '.substr($methodCall$ptr20));
  2270.             }
  2271.  
  2272.             if (empty($methMatch[2])) {
  2273.                 // property
  2274.                 if ($curBlock === 'root'{
  2275.                     $output .= '->'.$methMatch[1];
  2276.                 else {
  2277.                     $output '(($tmp = '.$output.') ? $tmp->'.$methMatch[1].' : null)';
  2278.                 }
  2279.                 $ptr += strlen($methMatch[1]);
  2280.             else {
  2281.                 // method
  2282.                 if (substr($methMatch[2]02=== '()'{
  2283.                     $parsedCall '->'.$methMatch[1].'()';
  2284.                     $ptr += strlen($methMatch[1]2;
  2285.                 else {
  2286.                     $parsedCall '->'.$this->parseFunction($methodCall$ptrstrlen($methodCall)false'method'$ptr);
  2287.                 }
  2288.                 if ($curBlock === 'root'{
  2289.                     $output .= $parsedCall;
  2290.                 else {
  2291.                     $output '(($tmp = '.$output.') ? $tmp'.$parsedCall.' : null)';
  2292.                 }
  2293.             }
  2294.         }
  2295.  
  2296.         $pointer += $ptr;
  2297.         return $output;
  2298.     }
  2299.  
  2300.     /**
  2301.      * parses a constant variable (a variable that doesn't contain another variable) and preprocesses it to save runtime processing time
  2302.      *
  2303.      * @param string $key the variable to parse
  2304.      * @param string $curBlock the current parser-block being processed
  2305.      * @return string parsed variable
  2306.      */
  2307.     protected function parseVarKey($key$curBlock)
  2308.     {
  2309.         if ($key === ''{
  2310.             return '$this->scope';
  2311.         }
  2312.         if (substr($key01=== '.'{
  2313.             $key 'dwoo'.$key;
  2314.         }
  2315.         if (preg_match('#dwoo\.(get|post|server|cookies|session|env|request)((?:\.[a-z0-9_-]+)+)#i'$key$m)) {
  2316.             $global strtoupper($m[1]);
  2317.             if ($global === 'COOKIES'{
  2318.                 $global 'COOKIE';
  2319.             }
  2320.             $key '$_'.$global;
  2321.             foreach (explode('.'ltrim($m[2]'.')) as $part)
  2322.                 $key .= '['.var_export($parttrue).']';
  2323.             if ($curBlock === 'root'{
  2324.                 $output $key;
  2325.             else {
  2326.                 $output '(isset('.$key.')?'.$key.':null)';
  2327.             }
  2328.         elseif (preg_match('#dwoo\.const\.([a-z0-9_:]+)#i'$key$m)) {
  2329.             return $this->parseConstKey($m[1]$curBlock);
  2330.         elseif ($this->scope !== null{
  2331.             if (strstr($key'.'=== false && strstr($key'['=== false && strstr($key'->'=== false{
  2332.                 if ($key === 'dwoo'{
  2333.                     $output '$this->globals';
  2334.                 elseif ($key === '_root' || $key === '__'{
  2335.                     $output '$this->data';
  2336.                 elseif ($key === '_parent' || $key === '_'{
  2337.                     $output '$this->readParentVar(1)';
  2338.                 elseif ($key === '_key'{
  2339.                     $output '$tmp_key';
  2340.                 else {
  2341.                     if ($curBlock === 'root'{
  2342.                         $output '$this->scope["'.$key.'"]';
  2343.                     else {
  2344.                         $output '(isset($this->scope["'.$key.'"]) ? $this->scope["'.$key.'"] : null)';
  2345.                     }
  2346.                 }
  2347.             else {
  2348.                 preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+|(\\\?[\'"])[^\3]*?\3)\]?#i'$key$m);
  2349.  
  2350.                 $i $m[2][0];
  2351.                 if ($i === '_parent' || $i === '_'{
  2352.                     $parentCnt 0;
  2353.  
  2354.                     while (true{
  2355.                         $parentCnt++;
  2356.                         array_shift($m[2]);
  2357.                         array_shift($m[1]);
  2358.                         if (current($m[2]=== '_parent'{
  2359.                             continue;
  2360.                         }
  2361.                         break;
  2362.                     }
  2363.  
  2364.                     $output '$this->readParentVar('.$parentCnt.')';
  2365.                 else {
  2366.                     if ($i === 'dwoo'{
  2367.                         $output '$this->globals';
  2368.                         array_shift($m[2]);
  2369.                         array_shift($m[1]);
  2370.                     elseif ($i === '_root' || $i === '__'{
  2371.                         $output '$this->data';
  2372.                         array_shift($m[2]);
  2373.                         array_shift($m[1]);
  2374.                     elseif ($i === '_key'{
  2375.                         $output '$tmp_key';
  2376.                     else {
  2377.                         $output '$this->scope';
  2378.                     }
  2379.  
  2380.                     while (count($m[1]&& $m[1][0!== '->'{
  2381.                         $m[2][0preg_replace('/(^\\\([\'"])|\\\([\'"])$)/x''$2$3'$m[2][0]);
  2382.                         if(substr($m[2][0]01== '"' || substr($m[2][0]01== "'"{
  2383.                             $output .= '['.$m[2][0].']';
  2384.                         else {
  2385.                             $output .= '["'.$m[2][0].'"]';
  2386.                         }
  2387.                         array_shift($m[2]);
  2388.                         array_shift($m[1]);
  2389.                     }
  2390.  
  2391.                     if ($curBlock !== 'root'{
  2392.                         $output '(isset('.$output.') ? '.$output.':null)';
  2393.                     }
  2394.                 }
  2395.  
  2396.                 if (count($m[2])) {
  2397.                     unset($m[0]);
  2398.                     $output '$this->readVarInto('.str_replace("\n"''var_export($mtrue)).', '.$output.', '.($curBlock == 'root' 'false''true').')';
  2399.                 }
  2400.             }
  2401.         else {
  2402.             preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+)\]?#i'$key$m);
  2403.             unset($m[0]);
  2404.             $output '$this->readVar('.str_replace("\n"''var_export($mtrue)).')';
  2405.         }
  2406.  
  2407.         return $output;
  2408.     }
  2409.  
  2410.     /**
  2411.      * flattens a variable tree, this helps in parsing very complex variables such as $var.foo[$foo.bar->baz].baz,
  2412.      * it computes the contents of the brackets first and works out from there
  2413.      *
  2414.      * @param array $tree the variable tree parsed by he parseVar() method that must be flattened
  2415.      * @param bool $recursed leave that to false by default, it is only for internal use
  2416.      * @return string flattened tree
  2417.      */
  2418.     protected function flattenVarTree(array $tree$recursed=false)
  2419.     {
  2420.         $out $recursed ?  '".$this->readVarInto(' '';
  2421.         foreach ($tree as $bit{
  2422.             if (is_array($bit)) {
  2423.                 $out.='.'.$this->flattenVarTree($bitfalse);
  2424.             else {
  2425.                 $key str_replace('"''\\"'$bit);
  2426.  
  2427.                 if (substr($key01)==='$'{
  2428.                     $out .= '".'.$this->parseVar($key0strlen($key)false'variable').'."';
  2429.                 else {
  2430.                     $cnt substr_count($key'$');
  2431.  
  2432.                     if ($this->debugecho 'PARSING SUBVARS IN : '.$key.'<br>';
  2433.                     if ($cnt 0{
  2434.                         while (--$cnt >= 0{
  2435.                             if (isset($last)) {
  2436.                                 $last strrpos($key'$'(strlen($key$last 1));
  2437.                             else {
  2438.                                 $last strrpos($key'$');
  2439.                             }
  2440.                             preg_match('#\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*'.
  2441.                                       '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i'substr($key$last)$submatch);
  2442.  
  2443.                             $len strlen($submatch[0]);
  2444.                             $key substr_replace(
  2445.                                 $key,
  2446.                                 preg_replace_callback(
  2447.                                     '#(\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*)'.
  2448.                                     '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i',
  2449.                                     array($this'replaceVarKeyHelper')substr($key$last$len)
  2450.                                 ),
  2451.                                 $last,
  2452.                                 $len
  2453.                             );
  2454.                             if ($this->debugecho 'RECURSIVE VAR REPLACEMENT DONE : '.$key.'<br>';
  2455.                         }
  2456.                         unset($last);
  2457.  
  2458.                         $out .= $key;
  2459.                     else {
  2460.                         $out .= $key;
  2461.                     }
  2462.                 }
  2463.             }
  2464.         }
  2465.         $out .= $recursed ', true)."' '';
  2466.         return $out;
  2467.     }
  2468.  
  2469.     /**
  2470.      * helper function that parses a variable
  2471.      *
  2472.      * @param array $match the matched variable, array(1=>"string match")
  2473.      * @return string parsed variable
  2474.      */
  2475.     protected function replaceVarKeyHelper($match)
  2476.     {
  2477.         return '".'.$this->parseVar($match[0]0strlen($match[0])false'variable').'."';
  2478.     }
  2479.  
  2480.     /**
  2481.      * parses various constants, operators or non-quoted strings
  2482.      *
  2483.      * @param string $in the string within which we must parse something
  2484.      * @param int $from the starting offset of the parsed area
  2485.      * @param int $to the ending offset of the parsed area
  2486.      * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
  2487.      * @param string $curBlock the current parser-block being processed
  2488.      * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
  2489.      * @return string parsed values
  2490.      */
  2491.     protected function parseOthers($in$from$to$parsingParams false$curBlock=''&$pointer null)
  2492.     {
  2493.         $first $in[$from];
  2494.         $substr substr($in$from$to-$from);
  2495.  
  2496.         $end strlen($substr);
  2497.  
  2498.         if ($curBlock === 'condition'{
  2499.             $breakChars array('('')'' ''||''&&''|''&''>=''<=''===''==''=''!==''!=''<<''<''>>''>''^''~'',''+''-''*''/''%''!''?'':'$this->rd';');
  2500.         elseif ($curBlock === 'modifier'{
  2501.             $breakChars array(' '','')'':''|'"\r""\n""\t"";"$this->rd);
  2502.         elseif ($curBlock === 'expression'{
  2503.             $breakChars array('/''%''+''-''*'' '','')'"\r""\n""\t"";"$this->rd);
  2504.         else {
  2505.             $breakChars array(' '','')'"\r""\n""\t"";"$this->rd);
  2506.         }
  2507.  
  2508.         $breaker false;
  2509.         while (list($k,$chareach($breakChars)) {
  2510.             $test strpos($substr$char);
  2511.             if ($test !== false && $test $end{
  2512.                 $end $test;
  2513.                 $breaker $k;
  2514.             }
  2515.         }
  2516.  
  2517.         if ($curBlock === 'condition'{
  2518.             if ($end === && $breaker !== false{
  2519.                 $end strlen($breakChars[$breaker]);
  2520.             }
  2521.         }
  2522.  
  2523.         if ($end !== false{
  2524.             $substr substr($substr0$end);
  2525.         }
  2526.  
  2527.         if ($pointer !== null{
  2528.             $pointer += strlen($substr);
  2529.         }
  2530.  
  2531.         $src $substr;
  2532.         $substr trim($substr);
  2533.  
  2534.         if (strtolower($substr=== 'false' || strtolower($substr=== 'no' || strtolower($substr=== 'off'{
  2535.             if ($this->debugecho 'BOOLEAN(FALSE) PARSED<br />';
  2536.             $substr 'false';
  2537.             $type self::T_BOOL;
  2538.         elseif (strtolower($substr=== 'true' || strtolower($substr=== 'yes' || strtolower($substr=== 'on'{
  2539.             if ($this->debugecho 'BOOLEAN(TRUE) PARSED<br />';
  2540.             $substr 'true';
  2541.             $type self::T_BOOL;
  2542.         elseif ($substr === 'null' || $substr === 'NULL'{
  2543.             if ($this->debugecho 'NULL PARSED<br />';
  2544.             $substr 'null';
  2545.             $type self::T_NULL;
  2546.         elseif (is_numeric($substr)) {
  2547.             $substr = (float) $substr;
  2548.             if ((int) $substr == $substr{
  2549.                 $substr = (int) $substr;
  2550.             }
  2551.             $type self::T_NUMERIC;
  2552.             if ($this->debugecho 'NUMBER ('.$substr.') PARSED<br />';
  2553.         elseif (preg_match('{^-?(\d+|\d*(\.\d+))\s*([/*%+-]\s*-?(\d+|\d*(\.\d+)))+$}'$substr)) {
  2554.             if ($this->debugecho 'SIMPLE MATH PARSED<br />';
  2555.             $type self::T_MATH;
  2556.             $substr '('.$substr.')';
  2557.         elseif ($curBlock === 'condition' && array_search($substr$breakCharstrue!== false{
  2558.             if ($this->debugecho 'BREAKCHAR ('.$substr.') PARSED<br />';
  2559.             $type self::T_BREAKCHAR;
  2560.             //$substr = '"'.$substr.'"';
  2561.         else {
  2562.             $substr $this->replaceStringVars('\''.str_replace('\'''\\\''$substr).'\'''\''$curBlock);
  2563.             $type self::T_UNQUOTED_STRING;
  2564.             if ($this->debugecho 'BLABBER ('.$substr.') CASTED AS STRING<br />';
  2565.         }
  2566.  
  2567.         if (is_array($parsingParams)) {
  2568.             $parsingParams[array($substr$src$type);
  2569.             return $parsingParams;
  2570.         elseif ($curBlock === 'namedparam'{
  2571.             return array($substr$src$type);
  2572.         elseif ($curBlock === 'expression'{
  2573.             return $substr;
  2574.         else {
  2575.             throw new Exception('Something went wrong');
  2576.         }
  2577.     }
  2578.  
  2579.     /**
  2580.      * replaces variables within a parsed string
  2581.      *
  2582.      * @param string $string the parsed string
  2583.      * @param string $first the first character parsed in the string, which is the string delimiter (' or ")
  2584.      * @param string $curBlock the current parser-block being processed
  2585.      * @return string the original string with variables replaced
  2586.      */
  2587.     protected function replaceStringVars($string$first$curBlock='')
  2588.     {
  2589.         $pos 0;
  2590.         if ($this->debugecho 'STRING VAR REPLACEMENT : '.$string.'<br>';
  2591.         // replace vars
  2592.         while (($pos strpos($string'$'$pos)) !== false{
  2593.             $prev substr($string$pos-11);
  2594.             if ($prev === '\\'{
  2595.                 $pos++;
  2596.                 continue;
  2597.             }
  2598.  
  2599.             $var $this->parse($string$posnullfalse($curBlock === 'modifier' 'modifier' ($prev === '`' 'delimited_string':'string')));
  2600.             $len $var[0];
  2601.             $var $this->parse(str_replace('\\'.$first$first$string)$posnullfalse($curBlock === 'modifier' 'modifier' ($prev === '`' 'delimited_string':'string')));
  2602.  
  2603.             if ($prev === '`' && substr($string$pos+$len1=== '`'{
  2604.                 $string substr_replace($string$first.'.'.$var[1].'.'.$first$pos-1$len+2);
  2605.             else {
  2606.                 $string substr_replace($string$first.'.'.$var[1].'.'.$first$pos$len);
  2607.             }
  2608.             $pos += strlen($var[1]2;
  2609.             if ($this->debugecho 'STRING VAR REPLACEMENT DONE : '.$string.'<br>';
  2610.         }
  2611.  
  2612.         // handle modifiers
  2613.         // TODO Obsolete?
  2614.         $string preg_replace_callback('#("|\')\.(.+?)\.\1((?:\|(?:@?[a-z0-9_]+(?:(?::("|\').+?\4|:[^`]*))*))+)#i'array($this'replaceModifiers')$string);
  2615.  
  2616.         // replace escaped dollar operators by unescaped ones if required
  2617.         if ($first==="'"{
  2618.             $string str_replace('\\$''$'$string);
  2619.         }
  2620.  
  2621.         return $string;
  2622.     }
  2623.  
  2624.     /**
  2625.      * replaces the modifiers applied to a string or a variable
  2626.      *
  2627.      * @param array $m the regex matches that must be array(1=>"double or single quotes enclosing a string, when applicable", 2=>"the string or var", 3=>"the modifiers matched")
  2628.      * @param string $curBlock the current parser-block being processed
  2629.      * @return string the input enclosed with various function calls according to the modifiers found
  2630.      */
  2631.     protected function replaceModifiers(array $m$curBlock null&$pointer null)
  2632.     {
  2633.         if ($this->debugecho 'PARSING MODIFIERS : '.$m[3].'<br />';
  2634.  
  2635.         if ($pointer !== null{
  2636.             $pointer += strlen($m[3]);
  2637.         }
  2638.         // remove first pipe
  2639.         $cmdstrsrc substr($m[3]1);
  2640.         // remove last quote if present
  2641.         if (substr($cmdstrsrc-11=== $m[1]{
  2642.             $cmdstrsrc substr($cmdstrsrc0-1);
  2643.             $add $m[1];
  2644.         }
  2645.  
  2646.         $output $m[2];
  2647.  
  2648.         $continue true;
  2649.         while (strlen($cmdstrsrc&& $continue{
  2650.             if ($cmdstrsrc[0=== '|'{
  2651.                 $cmdstrsrc substr($cmdstrsrc1);
  2652.                 continue;
  2653.             }
  2654.             if ($cmdstrsrc[0=== ' ' || $cmdstrsrc[0=== ';' || substr($cmdstrsrc0strlen($this->rd)) === $this->rd{
  2655.                 if ($this->debugecho 'MODIFIER PARSING ENDED, RIGHT DELIMITER or ";" FOUND<br/>';
  2656.                 $continue false;
  2657.                 if ($pointer !== null{
  2658.                     $pointer -= strlen($cmdstrsrc);
  2659.                 }
  2660.                 break;
  2661.             }
  2662.             $cmdstr $cmdstrsrc;
  2663.             $paramsep ':';
  2664.             if (!preg_match('/^(@{0,2}[a-z][a-z0-9_]*)(:)?/i'$cmdstr$match)) {
  2665.                 throw new Dwoo_Compilation_Exception($this'Invalid modifier name, started with : '.substr($cmdstr010));
  2666.             }
  2667.             $paramspos !empty($match[2]strlen($match[1]false;
  2668.             $func $match[1];
  2669.  
  2670.             $state 0;
  2671.             if ($paramspos === false{
  2672.                 $cmdstrsrc substr($cmdstrsrcstrlen($func));
  2673.                 $params array();
  2674.                 if ($this->debugecho 'MODIFIER ('.$func.') CALLED WITH NO PARAMS<br/>';
  2675.             else {
  2676.                 $paramstr substr($cmdstr$paramspos+1);
  2677.                 if (substr($paramstr-11=== $paramsep{
  2678.                     $paramstr substr($paramstr0-1);
  2679.                 }
  2680.  
  2681.                 $ptr 0;
  2682.                 $params array();
  2683.                 while ($ptr strlen($paramstr)) {
  2684.                     if ($this->debugecho 'MODIFIER ('.$func.') START PARAM PARSING WITH POINTER AT '.$ptr.'<br/>';
  2685.                     if ($this->debugecho $paramstr.'--'.$ptr.'--'.strlen($paramstr).'--modifier<br/>';
  2686.                     $params $this->parse($paramstr$ptrstrlen($paramstr)$params'modifier'$ptr);
  2687.                     if ($this->debugecho 'PARAM PARSED, POINTER AT '.$ptr.'<br/>';
  2688.  
  2689.                     if ($ptr >= strlen($paramstr)) {
  2690.                         if ($this->debugecho 'PARAM PARSING ENDED, PARAM STRING CONSUMED<br/>';
  2691.                         break;
  2692.                     }
  2693.  
  2694.                     if ($paramstr[$ptr=== ' ' || $paramstr[$ptr=== '|' || $paramstr[$ptr=== ';' || substr($paramstr$ptrstrlen($this->rd)) === $this->rd{
  2695.                         if ($this->debugecho 'PARAM PARSING ENDED, " ", "|", RIGHT DELIMITER or ";" FOUND, POINTER AT '.$ptr.'<br/>';
  2696.                         if ($paramstr[$ptr!== '|'{
  2697.                             $continue false;
  2698.                             if ($pointer !== null{
  2699.                                 $pointer -= strlen($paramstr$ptr;
  2700.                             }
  2701.                         }
  2702.                         $ptr++;
  2703.                         break;
  2704.                     }
  2705.                     if ($ptr strlen($paramstr&& $paramstr[$ptr=== ':'{
  2706.                         $ptr++;
  2707.                     }
  2708.                 }
  2709.                 $cmdstrsrc substr($cmdstrsrcstrlen($func)+1+$ptr);
  2710.                 $paramstr substr($paramstr0$ptr);
  2711.                 foreach ($params as $k=>$p{
  2712.                     if (is_array($p&& is_array($p[1])) {
  2713.                         $state |= 2;
  2714.                     else {
  2715.                         if (($state 2&& preg_match('#^(["\'])(.+?)\1$#'$p[0]$m)) {
  2716.                             $params[$karray($m[2]array('true''true'));
  2717.                         else {
  2718.                             if ($state 2{
  2719.                                 throw new Dwoo_Compilation_Exception($this'You can not use an unnamed parameter after a named one');
  2720.                             }
  2721.                             $state |= 1;
  2722.                         }
  2723.                     }
  2724.                 }
  2725.             }
  2726.  
  2727.             // check if we must use array_map with this plugin or not
  2728.             $mapped false;
  2729.             if (substr($func01=== '@'{
  2730.                 $func substr($func1);
  2731.                 $mapped true;
  2732.             }
  2733.  
  2734.             $pluginType $this->getPluginType($func);
  2735.  
  2736.             if ($state 2{
  2737.                 array_unshift($paramsarray('value'array($output$output)));
  2738.             else {
  2739.                 array_unshift($paramsarray($output$output));
  2740.             }
  2741.  
  2742.             if ($pluginType Dwoo::NATIVE_PLUGIN{
  2743.                 $params $this->mapParams($paramsnull$state);
  2744.  
  2745.                 $params $params['*'][0];
  2746.  
  2747.                 $params self::implode_r($params);
  2748.  
  2749.                 if ($mapped{
  2750.                     $output '$this->arrayMap(\''.$func.'\', array('.$params.'))';
  2751.                 else {
  2752.                     $output $func.'('.$params.')';
  2753.                 }
  2754.             elseif ($pluginType Dwoo::PROXY_PLUGIN{
  2755.                 $params $this->mapParams($params$this->getDwoo()->getPluginProxy()->getCallback($func)$state);
  2756.                 foreach ($params as &$p)
  2757.                     $p $p[0];
  2758.                 $output call_user_func(array($this->dwoo->getPluginProxy()'getCode')$func$params);
  2759.             elseif ($pluginType Dwoo::SMARTY_MODIFIER{
  2760.                 $params $this->mapParams($paramsnull$state);
  2761.                 $params $params['*'][0];
  2762.  
  2763.                 $params self::implode_r($params);
  2764.  
  2765.                 if ($pluginType Dwoo::CUSTOM_PLUGIN{
  2766.                     $callback $this->customPlugins[$func]['callback'];
  2767.                     if (is_array($callback)) {
  2768.                         if (is_object($callback[0])) {
  2769.                             $output ($mapped '$this->arrayMap' 'call_user_func_array').'(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), array('.$params.'))';
  2770.                         else {
  2771.                             $output ($mapped '$this->arrayMap' 'call_user_func_array').'(array(\''.$callback[0].'\', \''.$callback[1].'\'), array('.$params.'))';
  2772.                         }
  2773.                     elseif ($mapped{
  2774.                         $output '$this->arrayMap(\''.$callback.'\', array('.$params.'))';
  2775.                     else {
  2776.                         $output $callback.'('.$params.')';
  2777.                     }
  2778.                 elseif ($mapped{
  2779.                     $output '$this->arrayMap(\'smarty_modifier_'.$func.'\', array('.$params.'))';
  2780.                 else {
  2781.                     $output 'smarty_modifier_'.$func.'('.$params.')';
  2782.                 }
  2783.             else {
  2784.                 if ($pluginType Dwoo::CUSTOM_PLUGIN{
  2785.                     $callback $this->customPlugins[$func]['callback'];
  2786.                     $pluginName $callback;
  2787.                 else {
  2788.                     $pluginName 'Dwoo_Plugin_'.$func;
  2789.  
  2790.                     if ($pluginType Dwoo::CLASS_PLUGIN{
  2791.                         $callback array($pluginName($pluginType Dwoo::COMPILABLE_PLUGIN'compile' 'process');
  2792.                     else {
  2793.                         $callback $pluginName (($pluginType Dwoo::COMPILABLE_PLUGIN'_compile' '');
  2794.                     }
  2795.                 }
  2796.  
  2797.                 $params $this->mapParams($params$callback$state);
  2798.  
  2799.                 foreach ($params as &$p)
  2800.                     $p $p[0];
  2801.  
  2802.                 if ($pluginType Dwoo::FUNC_PLUGIN{
  2803.                     if ($pluginType Dwoo::COMPILABLE_PLUGIN{
  2804.                         if ($mapped{
  2805.                             throw new Dwoo_Compilation_Exception($this'The @ operator can not be used on compiled plugins.');
  2806.                         }
  2807.                         if ($pluginType Dwoo::CUSTOM_PLUGIN{
  2808.                             $funcCompiler $this->customPlugins[$func]['callback'];
  2809.                         else {
  2810.                             $funcCompiler 'Dwoo_Plugin_'.$func.'_compile';
  2811.                         }
  2812.                         array_unshift($params$this);
  2813.                         $output call_user_func_array($funcCompiler$params);
  2814.                     else {
  2815.                         array_unshift($params'$this');
  2816.  
  2817.                         $params self::implode_r($params);
  2818.                         if ($mapped{
  2819.                             $output '$this->arrayMap(\''.$pluginName.'\', array('.$params.'))';
  2820.                         else {
  2821.                             $output $pluginName.'('.$params.')';
  2822.                         }
  2823.                     }
  2824.                 else {
  2825.                     if ($pluginType Dwoo::COMPILABLE_PLUGIN{
  2826.                         if ($mapped{
  2827.                             throw new Dwoo_Compilation_Exception($this'The @ operator can not be used on compiled plugins.');
  2828.                         }
  2829.                         if ($pluginType Dwoo::CUSTOM_PLUGIN{
  2830.                             $callback $this->customPlugins[$func]['callback'];
  2831.                             if (!is_array($callback)) {
  2832.                                 if (!method_exists($callback'compile')) {
  2833.                                     throw new Dwoo_Exception('Custom plugin '.$func.' must implement the "compile" method to be compilable, or you should provide a full callback to the method to use');
  2834.                                 }
  2835.                                 if (($ref new ReflectionMethod($callback'compile')) && $ref->isStatic()) {
  2836.                                     $funcCompiler array($callback'compile');
  2837.                                 else {
  2838.                                     $funcCompiler array(new $callback'compile');
  2839.                                 }
  2840.                             else {
  2841.                                 $funcCompiler $callback;
  2842.                             }
  2843.                         else {
  2844.                             $funcCompiler array('Dwoo_Plugin_'.$func'compile');
  2845.                             array_unshift($params$this);
  2846.                         }
  2847.                         $output call_user_func_array($funcCompiler$params);
  2848.                     else {
  2849.                         $params self::implode_r($params);
  2850.  
  2851.                         if ($pluginType Dwoo::CUSTOM_PLUGIN{
  2852.                             if (is_object($callback[0])) {
  2853.                                 $output ($mapped '$this->arrayMap' 'call_user_func_array').'(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), array('.$params.'))';
  2854.                             else {
  2855.                                 $output ($mapped '$this->arrayMap' 'call_user_func_array').'(array(\''.$callback[0].'\', \''.$callback[1].'\'), array('.$params.'))';
  2856.                             }
  2857.                         elseif ($mapped{
  2858.                             $output '$this->arrayMap(array($this->getObjectPlugin(\'Dwoo_Plugin_'.$func.'\'), \'process\'), array('.$params.'))';
  2859.                         else {
  2860.                             $output '$this->classCall(\''.$func.'\', array('.$params.'))';
  2861.                         }
  2862.                     }
  2863.                 }
  2864.             }
  2865.         }
  2866.  
  2867.         if ($curBlock === 'var' || $m[1=== null{
  2868.             return $output;
  2869.         elseif ($curBlock === 'string' || $curBlock === 'root'{
  2870.             return $m[1].'.'.$output.'.'.$m[1].(isset($add)?$add:null);
  2871.         }
  2872.     }
  2873.  
  2874.     /**
  2875.      * recursively implodes an array in a similar manner as var_export() does but with some tweaks
  2876.      * to handle pre-compiled values and the fact that we do not need to enclose everything with
  2877.      * "array" and do not require top-level keys to be displayed
  2878.      *
  2879.      * @param array $params the array to implode
  2880.      * @param bool $recursiveCall if set to true, the function outputs key names for the top level
  2881.      * @return string the imploded array
  2882.      */
  2883.     public static function implode_r(array $params$recursiveCall false)
  2884.     {
  2885.         $out '';
  2886.         foreach ($params as $k=>$p{
  2887.             if (is_array($p)) {
  2888.                 $out2 'array(';
  2889.                 foreach ($p as $k2=>$v)
  2890.                     $out2 .= var_export($k2true).' => '.(is_array($v'array('.self::implode_r($vtrue).')' $v).', ';
  2891.                 $p rtrim($out2', ').')';
  2892.             }
  2893.             if ($recursiveCall{
  2894.                 $out .= var_export($ktrue).' => '.$p.', ';
  2895.             else {
  2896.                 $out .= $p.', ';
  2897.             }
  2898.         }
  2899.         return rtrim($out', ');
  2900.     }
  2901.  
  2902.     /**
  2903.      * returns the plugin type of a plugin and adds it to the used plugins array if required
  2904.      *
  2905.      * @param string $name plugin name, as found in the template
  2906.      * @return int type as a multi bit flag composed of the Dwoo plugin types constants
  2907.      */
  2908.     protected function getPluginType($name)
  2909.     {
  2910.         $pluginType = -1;
  2911.  
  2912.         if (($this->securityPolicy === null && (function_exists($name|| strtolower($name=== 'isset' || strtolower($name=== 'empty')) ||
  2913.             ($this->securityPolicy !== null && in_array(strtolower($name)$this->securityPolicy->getAllowedPhpFunctions()) !== false)) {
  2914.             $phpFunc true;
  2915.         }
  2916.  
  2917.         while ($pluginType <= 0{
  2918.             if (isset($this->templatePlugins[$name])) {
  2919.                 $pluginType Dwoo::TEMPLATE_PLUGIN Dwoo::COMPILABLE_PLUGIN;
  2920.             elseif (isset($this->customPlugins[$name])) {
  2921.                 $pluginType $this->customPlugins[$name]['type'Dwoo::CUSTOM_PLUGIN;
  2922.             elseif (class_exists('Dwoo_Plugin_'.$namefalse!== false{
  2923.                 if (is_subclass_of('Dwoo_Plugin_'.$name'Dwoo_Block_Plugin')) {
  2924.                     $pluginType Dwoo::BLOCK_PLUGIN;
  2925.                 else {
  2926.                     $pluginType Dwoo::CLASS_PLUGIN;
  2927.                 }
  2928.                 $interfaces class_implements('Dwoo_Plugin_'.$namefalse);
  2929.                 if (in_array('Dwoo_ICompilable'$interfaces!== false || in_array('Dwoo_ICompilable_Block'$interfaces!== false{
  2930.                     $pluginType |= Dwoo::COMPILABLE_PLUGIN;
  2931.                 }
  2932.             elseif (function_exists('Dwoo_Plugin_'.$name!== false{
  2933.                 $pluginType Dwoo::FUNC_PLUGIN;
  2934.             elseif (function_exists('Dwoo_Plugin_'.$name.'_compile')) {
  2935.                 $pluginType Dwoo::FUNC_PLUGIN Dwoo::COMPILABLE_PLUGIN;
  2936.             elseif (function_exists('smarty_modifier_'.$name!== false{
  2937.                 $pluginType Dwoo::SMARTY_MODIFIER;
  2938.             elseif (function_exists('smarty_function_'.$name!== false{
  2939.                 $pluginType Dwoo::SMARTY_FUNCTION;
  2940.             elseif (function_exists('smarty_block_'.$name!== false{
  2941.                 $pluginType Dwoo::SMARTY_BLOCK;
  2942.             else {
  2943.                 if ($pluginType===-1{
  2944.                     try {
  2945.                         $this->dwoo->getLoader()->loadPlugin($nameisset($phpFunc)===false);
  2946.                     catch (Exception $e{
  2947.                         if (isset($phpFunc)) {
  2948.                             $pluginType Dwoo::NATIVE_PLUGIN;
  2949.                         elseif (is_object($this->dwoo->getPluginProxy()) && $this->dwoo->getPluginProxy()->handles($name)) {
  2950.                             $pluginType Dwoo::PROXY_PLUGIN;
  2951.                             break;
  2952.                         else {
  2953.                             throw $e;
  2954.                         }
  2955.                     }
  2956.                 else {
  2957.                     throw new Dwoo_Exception('Plugin "'.$name.'" could not be found');
  2958.                 }
  2959.                 $pluginType++;
  2960.             }
  2961.         }
  2962.  
  2963.         if (($pluginType Dwoo::COMPILABLE_PLUGIN=== && ($pluginType Dwoo::NATIVE_PLUGIN=== && ($pluginType Dwoo::PROXY_PLUGIN=== 0{
  2964.             $this->addUsedPlugin($name$pluginType);
  2965.         }
  2966.  
  2967.         return $pluginType;
  2968.     }
  2969.  
  2970.     /**
  2971.      * allows a plugin to load another one at compile time, this will also mark
  2972.      * it as used by this template so it will be loaded at runtime (which can be
  2973.      * useful for compiled plugins that rely on another plugin when their compiled
  2974.      * code runs)
  2975.      *
  2976.      * @param string $name the plugin name
  2977.      */
  2978.     public function loadPlugin($name{
  2979.         $this->getPluginType($name);
  2980.     }
  2981.  
  2982.     /**
  2983.      * runs htmlentities over the matched <?php ?> blocks when the security policy enforces that
  2984.      *
  2985.      * @param array $match matched php block
  2986.      * @return string the htmlentities-converted string
  2987.      */
  2988.     protected function phpTagEncodingHelper($match)
  2989.     {
  2990.         return htmlspecialchars($match[0]);
  2991.     }
  2992.  
  2993.     /**
  2994.      * maps the parameters received from the template onto the parameters required by the given callback
  2995.      *
  2996.      * @param array $params the array of parameters
  2997.      * @param callback $callback the function or method to reflect on to find out the required parameters
  2998.      * @param int $callType the type of call in the template, 0 = no params, 1 = php-style call, 2 = named parameters call
  2999.      * @param array $map the parameter map to use, if not provided it will be built from the callback
  3000.      * @return array parameters sorted in the correct order with missing optional parameters filled
  3001.      */
  3002.     protected function mapParams(array $params$callback$callType=2$map null)
  3003.     {
  3004.         if (!$map{
  3005.             $map $this->getParamMap($callback);
  3006.         }
  3007.  
  3008.         $paramlist array();
  3009.  
  3010.         // transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values))
  3011.         $ps array();
  3012.         foreach ($params as $p{
  3013.             if (is_array($p[1])) {
  3014.                 $ps[$p[0]] $p[1];
  3015.             else {
  3016.                 $ps[$p;
  3017.             }
  3018.         }
  3019.  
  3020.         // loops over the param map and assigns values from the template or default value for unset optional params
  3021.         while (list($k,$veach($map)) {
  3022.             if ($v[0=== '*'{
  3023.                 // "rest" array parameter, fill every remaining params in it and then break
  3024.                 if (count($ps=== 0{
  3025.                     if ($v[1]===false{
  3026.                         throw new Dwoo_Compilation_Exception($this'Rest argument missing for '.str_replace(array('Dwoo_Plugin_''_compile')''(is_array($callback$callback[0$callback)));
  3027.                     else {
  3028.                         break;
  3029.                     }
  3030.                 }
  3031.                 $tmp array();
  3032.                 $tmp2 array();
  3033.                 $tmp3 array();
  3034.                 foreach ($ps as $i=>$p{
  3035.                     $tmp[$i$p[0];
  3036.                     $tmp2[$i$p[1];
  3037.                     $tmp3[$i= isset($p[2]$p[20;
  3038.                     unset($ps[$i]);
  3039.                 }
  3040.                 $paramlist[$v[0]] array($tmp$tmp2$tmp3);
  3041.                 unset($tmp$tmp2$i$p);
  3042.                 break;
  3043.             elseif (isset($ps[$v[0]])) {
  3044.                 // parameter is defined as named param
  3045.                 $paramlist[$v[0]] $ps[$v[0]];
  3046.                 unset($ps[$v[0]]);
  3047.             elseif (isset($ps[$k])) {
  3048.                 // parameter is defined as ordered param
  3049.                 $paramlist[$v[0]] $ps[$k];
  3050.                 unset($ps[$k]);
  3051.             elseif ($v[1]===false{
  3052.                 // parameter is not defined and not optional, throw error
  3053.                 if (is_array($callback)) {
  3054.                     if (is_object($callback[0])) {
  3055.                         $name get_class($callback[0]'::' $callback[1];
  3056.                     else {
  3057.                         $name $callback[0];
  3058.                     }
  3059.                 else {
  3060.                     $name $callback;
  3061.                 }
  3062.  
  3063.                 throw new Dwoo_Compilation_Exception($this'Argument '.$k.'/'.$v[0].' missing for '.str_replace(array('Dwoo_Plugin_''_compile')''$name));
  3064.             elseif ($v[2]===null{
  3065.                 // enforce lowercased null if default value is null (php outputs NULL with var export)
  3066.                 $paramlist[$v[0]] array('null'nullself::T_NULL);
  3067.             else {
  3068.                 // outputs default value with var_export
  3069.                 $paramlist[$v[0]] array(var_export($v[2]true)$v[2]);
  3070.             }
  3071.         }
  3072.  
  3073.         if (count($ps)) {
  3074.             foreach ($ps as $i=>$p{
  3075.                 array_push($paramlist$p);
  3076.             }
  3077.         }
  3078.  
  3079.         return $paramlist;
  3080.     }
  3081.  
  3082.     /**
  3083.      * returns the parameter map of the given callback, it filters out entries typed as Dwoo and Dwoo_Compiler and turns the rest parameter into a "*"
  3084.      *
  3085.      * @param callback $callback the function/method to reflect on
  3086.      * @return array processed parameter map
  3087.      */
  3088.     protected function getParamMap($callback)
  3089.     {
  3090.         if (is_null($callback)) {
  3091.             return array(array('*'true));
  3092.         }
  3093.         if (is_array($callback)) {
  3094.             $ref new ReflectionMethod($callback[0]$callback[1]);
  3095.         else {
  3096.             $ref new ReflectionFunction($callback);
  3097.         }
  3098.  
  3099.         $out array();
  3100.         foreach ($ref->getParameters(as $param{
  3101.             if (($class $param->getClass()) !== null && $class->name === 'Dwoo'{
  3102.                 continue;
  3103.             }
  3104.             if (($class $param->getClass()) !== null && $class->name === 'Dwoo_Compiler'{
  3105.                 continue;
  3106.             }
  3107.             if ($param->getName(=== 'rest' && $param->isArray(=== true{
  3108.                 $out[array('*'$param->isOptional()null);
  3109.             }
  3110.             $out[array($param->getName()$param->isOptional()$param->isOptional($param->getDefaultValue(null);
  3111.         }
  3112.  
  3113.         return $out;
  3114.     }
  3115.  
  3116.     /**
  3117.      * returns a default instance of this compiler, used by default by all Dwoo templates that do not have a
  3118.      * specific compiler assigned and when you do not override the default compiler factory function
  3119.      *
  3120.      * @see Dwoo::setDefaultCompilerFactory()
  3121.      * @return Dwoo_Compiler 
  3122.      */
  3123.     public static function compilerFactory()
  3124.     {
  3125.         if (self::$instance === null{
  3126.             new self;
  3127.         }
  3128.         return self::$instance;
  3129.     }
  3130. }

Documentation generated on Sun, 07 Feb 2010 17:03:45 +0000 by phpDocumentor 1.4.0