Source for file Compiler.php

Documentation is available at Compiler.php

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

Documentation generated on Sun, 03 Aug 2008 15:12:24 +0200 by phpDocumentor 1.4.0