Source for file Dwoo.php

Documentation is available at Dwoo.php

  1. <?php
  2.  
  3. define('DWOO_DIRECTORY'dirname(__FILE__DIRECTORY_SEPARATOR);
  4.  
  5. /**
  6.  * main dwoo class, allows communication between the compiler, template and data classes
  7.  *
  8.  * <pre>
  9.  * requirements :
  10.  *  php 5.2.0 or above (might work below, it's a rough estimate)
  11.  *  SPL and PCRE extensions (for php versions prior to 5.3.0)
  12.  *  mbstring extension for some string manipulation plugins (especially if you intend to use UTF-8)
  13.  * recommended :
  14.  *  hash extension (for Dwoo_Template_String - minor performance boost)
  15.  *
  16.  * project created :
  17.  *  2008-01-05
  18.  * </pre>
  19.  *
  20.  * This software is provided 'as-is', without any express or implied warranty.
  21.  * In no event will the authors be held liable for any damages arising from the use of this software.
  22.  *
  23.  * @author     Jordi Boggiano <j.boggiano@seld.be>
  24.  * @copyright  Copyright (c) 2008, Jordi Boggiano
  25.  * @license    http://dwoo.org/LICENSE   Modified BSD License
  26.  * @link       http://dwoo.org/
  27.  * @version    1.1.0
  28.  * @date       2009-07-18
  29.  * @package    Dwoo
  30.  */
  31. class Dwoo
  32. {
  33.     /**
  34.      * current version number
  35.      *
  36.      * @var string 
  37.      */
  38.     const VERSION '1.1.1';
  39.  
  40.     /**
  41.      * unique number of this dwoo release
  42.      *
  43.      * this can be used by templates classes to check whether the compiled template
  44.      * has been compiled before this release or not, so that old templates are
  45.      * recompiled automatically when Dwoo is updated
  46.      */
  47.     const RELEASE_TAG 17;
  48.  
  49.     /**#@+
  50.      * constants that represents all plugin types
  51.      *
  52.      * these are bitwise-operation-safe values to allow multiple types
  53.      * on a single plugin
  54.      *
  55.      * @var int
  56.      */
  57.     const CLASS_PLUGIN 1;
  58.     const FUNC_PLUGIN 2;
  59.     const NATIVE_PLUGIN 4;
  60.     const BLOCK_PLUGIN 8;
  61.     const COMPILABLE_PLUGIN 16;
  62.     const CUSTOM_PLUGIN 32;
  63.     const SMARTY_MODIFIER 64;
  64.     const SMARTY_BLOCK 128;
  65.     const SMARTY_FUNCTION 256;
  66.     const PROXY_PLUGIN 512;
  67.     const TEMPLATE_PLUGIN 1024;
  68.     /**#@-*/
  69.  
  70.     /**
  71.      * character set of the template, used by string manipulation plugins
  72.      *
  73.      * it must be lowercase, but setCharset() will take care of that
  74.      *
  75.      * @see setCharset
  76.      * @see getCharset
  77.      * @var string 
  78.      */
  79.     protected $charset = 'utf-8';
  80.  
  81.     /**
  82.      * global variables that are accessible through $dwoo.* in the templates
  83.      *
  84.      * default values include:
  85.      *
  86.      * $dwoo.version - current version number
  87.      * $dwoo.ad - a Powered by Dwoo link pointing to dwoo.org
  88.      * $dwoo.now - the current time
  89.      * $dwoo.template - the current template filename
  90.      * $dwoo.charset - the character set used by the template
  91.      *
  92.      * on top of that, foreach and other plugins can store special values in there,
  93.      * see their documentation for more details.
  94.      *
  95.      * @private
  96.      * @var array 
  97.      */
  98.     public $globals;
  99.  
  100.     /**
  101.      * directory where the compiled templates are stored
  102.      *
  103.      * defaults to DWOO_COMPILEDIR (= dwoo_dir/compiled by default)
  104.      *
  105.      * @var string 
  106.      */
  107.     protected $compileDir;
  108.  
  109.     /**
  110.      * directory where the cached templates are stored
  111.      *
  112.      * defaults to DWOO_CACHEDIR (= dwoo_dir/cache by default)
  113.      *
  114.      * @var string 
  115.      */
  116.     protected $cacheDir;
  117.  
  118.     /**
  119.      * defines how long (in seconds) the cached files must remain valid
  120.      *
  121.      * can be overriden on a per-template basis
  122.      *
  123.      * -1 = never delete
  124.      * 0 = disabled
  125.      * >0 = duration in seconds
  126.      *
  127.      * @var int 
  128.      */
  129.     protected $cacheTime = 0;
  130.  
  131.     /**
  132.      * security policy object
  133.      *
  134.      * @var Dwoo_Security_Policy 
  135.      */
  136.     protected $securityPolicy = null;
  137.  
  138.     /**
  139.      * stores the custom plugins callbacks
  140.      *
  141.      * @see addPlugin
  142.      * @see removePlugin
  143.      * @var array 
  144.      */
  145.     protected $plugins = array();
  146.  
  147.     /**
  148.      * stores the filter callbacks
  149.      *
  150.      * @see addFilter
  151.      * @see removeFilter
  152.      * @var array 
  153.      */
  154.     protected $filters = array();
  155.  
  156.     /**
  157.      * stores the resource types and associated
  158.      * classes / compiler classes
  159.      *
  160.      * @var array 
  161.      */
  162.     protected $resources = array
  163.     (
  164.         'file'        =>    array
  165.         (
  166.             'class'        =>    'Dwoo_Template_File',
  167.             'compiler'    =>    null
  168.         ),
  169.         'string'    =>    array
  170.         (
  171.             'class'        =>    'Dwoo_Template_String',
  172.             'compiler'    =>    null
  173.         )
  174.     );
  175.  
  176.     /**
  177.      * the dwoo loader object used to load plugins by this dwoo instance
  178.      *
  179.      * @var Dwoo_ILoader 
  180.      */
  181.     protected $loader = null;
  182.  
  183.     /**
  184.      * currently rendered template, set to null when not-rendering
  185.      *
  186.      * @var Dwoo_ITemplate 
  187.      */
  188.     protected $template = null;
  189.  
  190.     /**
  191.      * stores the instances of the class plugins during template runtime
  192.      *
  193.      * @var array 
  194.      */
  195.     protected $runtimePlugins;
  196.  
  197.     /**
  198.      * stores the data during template runtime
  199.      *
  200.      * @var array 
  201.      * @private
  202.      */
  203.     public $data;
  204.  
  205.     /**
  206.      * stores the current scope during template runtime
  207.      *
  208.      * this should ideally not be accessed directly from outside template code
  209.      *
  210.      * @var mixed 
  211.      * @private
  212.      */
  213.     public $scope;
  214.  
  215.     /**
  216.      * stores the scope tree during template runtime
  217.      *
  218.      * @var array 
  219.      */
  220.     protected $scopeTree;
  221.  
  222.     /**
  223.      * stores the block plugins stack during template runtime
  224.      *
  225.      * @var array 
  226.      */
  227.     protected $stack;
  228.  
  229.     /**
  230.      * stores the current block plugin at the top of the stack during template runtime
  231.      *
  232.      * @var Dwoo_Block_Plugin 
  233.      */
  234.     protected $curBlock;
  235.  
  236.     /**
  237.      * stores the output buffer during template runtime
  238.      *
  239.      * @var string 
  240.      */
  241.     protected $buffer;
  242.  
  243.     /**
  244.      * stores plugin proxy
  245.      *
  246.      * @var Dwoo_IPluginProxy 
  247.      */
  248.     protected $pluginProxy;
  249.  
  250.      /**
  251.      * constructor, sets the cache and compile dir to the default values if not provided
  252.      *
  253.      * @param string $compileDir path to the compiled directory, defaults to lib/compiled
  254.      * @param string $cacheDir path to the cache directory, defaults to lib/cache
  255.      */
  256.     public function __construct($compileDir null$cacheDir null)
  257.     {
  258.         if ($compileDir !== null{
  259.             $this->setCompileDir($compileDir);
  260.         }
  261.         if ($cacheDir !== null{
  262.             $this->setCacheDir($cacheDir);
  263.         }
  264.         $this->initGlobals();
  265.     }
  266.  
  267.     /**
  268.      * resets some runtime variables to allow a cloned object to be used to render sub-templates
  269.      */
  270.     public function __clone()
  271.     {
  272.         $this->template = null;
  273.         unset($this->data);
  274.     }
  275.  
  276.     /**
  277.      * outputs the template instead of returning it, this is basically a shortcut for get(*, *, *, true)
  278.      *
  279.      * @see get
  280.      * @param mixed $tpl template, can either be a Dwoo_ITemplate object (i.e. Dwoo_Template_File), a valid path to a template, or
  281.      *                       a template as a string it is recommended to provide a Dwoo_ITemplate as it will probably make things faster,
  282.      *                       especially if you render a template multiple times
  283.      * @param mixed $data the data to use, can either be a Dwoo_IDataProvider object (i.e. Dwoo_Data) or an associative array. if you're
  284.      *                        rendering the template from cache, it can be left null
  285.      * @param Dwoo_ICompiler $compiler the compiler that must be used to compile the template, if left empty a default
  286.      *                                    Dwoo_Compiler will be used.
  287.      * @return string nothing or the template output if $output is true
  288.      */
  289.     public function output($tpl$data array()Dwoo_ICompiler $compiler null)
  290.     {
  291.         return $this->get($tpl$data$compilertrue);
  292.     }
  293.  
  294.     /**
  295.      * returns the given template rendered using the provided data and optional compiler
  296.      *
  297.      * @param mixed $tpl template, can either be a Dwoo_ITemplate object (i.e. Dwoo_Template_File), a valid path to a template, or
  298.      *                       a template as a string it is recommended to provide a Dwoo_ITemplate as it will probably make things faster,
  299.      *                       especially if you render a template multiple times
  300.      * @param mixed $data the data to use, can either be a Dwoo_IDataProvider object (i.e. Dwoo_Data) or an associative array. if you're
  301.      *                        rendering the template from cache, it can be left null
  302.      * @param Dwoo_ICompiler $compiler the compiler that must be used to compile the template, if left empty a default
  303.      *                                    Dwoo_Compiler will be used.
  304.      * @param bool $output flag that defines whether the function returns the output of the template (false, default) or echoes it directly (true)
  305.      * @return string nothing or the template output if $output is true
  306.      */
  307.     public function get($_tpl$data array()$_compiler null$_output false)
  308.     {
  309.         // a render call came from within a template, so we need a new dwoo instance in order to avoid breaking this one
  310.         if ($this->template instanceof Dwoo_ITemplate{
  311.             $proxy clone $this;
  312.             return $proxy->get($_tpl$data$_compiler$_output);
  313.         }
  314.  
  315.         // auto-create template if required
  316.         if ($_tpl instanceof Dwoo_ITemplate{
  317.             // valid, skip
  318.         elseif (is_string($_tpl&& file_exists($_tpl)) {
  319.             $_tpl new Dwoo_Template_File($_tpl);
  320.         else {
  321.             throw new Dwoo_Exception('Dwoo->get/Dwoo->output\'s first argument must be a Dwoo_ITemplate (i.e. Dwoo_Template_File) or a valid path to a template file'E_USER_NOTICE);
  322.         }
  323.  
  324.         // save the current template, enters render mode at the same time
  325.         // if another rendering is requested it will be proxied to a new Dwoo instance
  326.         $this->template = $_tpl;
  327.  
  328.         // load data
  329.         if ($data instanceof Dwoo_IDataProvider{
  330.             $this->data = $data->getData();
  331.         elseif (is_array($data)) {
  332.             $this->data = $data;
  333.         else {
  334.             throw new Dwoo_Exception('Dwoo->get/Dwoo->output\'s data argument must be a Dwoo_IDataProvider object (i.e. Dwoo_Data) or an associative array'E_USER_NOTICE);
  335.         }
  336.  
  337.         $this->globals['template'$_tpl->getName();
  338.         $this->initRuntimeVars($_tpl);
  339.  
  340.         // try to get cached template
  341.         $file $_tpl->getCachedTemplate($this);
  342.         $doCache $file === true;
  343.         $cacheLoaded is_string($file);
  344.  
  345.         if ($cacheLoaded === true{
  346.             // cache is present, run it
  347.             if ($_output === true{
  348.                 include $file;
  349.                 $this->template = null;
  350.             else {
  351.                 ob_start();
  352.                 include $file;
  353.                 $this->template = null;
  354.                 return ob_get_clean();
  355.             }
  356.         else {
  357.             // no cache present
  358.             if ($doCache === true{
  359.                 $dynamicId uniqid();
  360.             }
  361.  
  362.             // render template
  363.             $compiledTemplate $_tpl->getCompiledTemplate($this$_compiler);
  364.             $out include $compiledTemplate;
  365.  
  366.             // template returned false so it needs to be recompiled
  367.             if ($out === false{
  368.                 $_tpl->forceCompilation();
  369.                 $compiledTemplate $_tpl->getCompiledTemplate($this$_compiler);
  370.                 $out include $compiledTemplate;
  371.             }
  372.  
  373.             if ($doCache === true{
  374.                 $out preg_replace('/(<%|%>|<\?php|<\?|\?>)/''<?php /*'.$dynamicId.'*/ echo \'$1\'; ?>'$out);
  375.                 if (!class_exists('Dwoo_plugin_dynamic'false)) {
  376.                     $this->getLoader()->loadPlugin('dynamic');
  377.                 }
  378.                 $out Dwoo_Plugin_dynamic::unescape($out$dynamicId$compiledTemplate);
  379.             }
  380.  
  381.             // process filters
  382.             foreach ($this->filters as $filter{
  383.                 if (is_array($filter&& $filter[0instanceof Dwoo_Filter{
  384.                     $out call_user_func($filter$out);
  385.                 else {
  386.                     $out call_user_func($filter$this$out);
  387.                 }
  388.             }
  389.  
  390.             if ($doCache === true{
  391.                 // building cache
  392.                 $file $_tpl->cache($this$out);
  393.  
  394.                 // run it from the cache to be sure dynamics are rendered
  395.                 if ($_output === true{
  396.                     include $file;
  397.                     // exit render mode
  398.                     $this->template = null;
  399.                 else {
  400.                     ob_start();
  401.                     include $file;
  402.                     // exit render mode
  403.                     $this->template = null;
  404.                     return ob_get_clean();
  405.                 }
  406.             else {
  407.                 // no need to build cache
  408.                 // exit render mode
  409.                 $this->template = null;
  410.                 // output
  411.                 if ($_output === true{
  412.                     echo $out;
  413.                 }
  414.                 return $out;
  415.             }
  416.         }
  417.     }
  418.  
  419.     /**
  420.      * re-initializes the globals array before each template run
  421.      *
  422.      * this method is only callede once when the Dwoo object is created
  423.      */
  424.     protected function initGlobals()
  425.     {
  426.         $this->globals = array
  427.         (
  428.             'version'    =>    self::VERSION,
  429.             'ad'        =>    '<a href="http://dwoo.org/">Powered by Dwoo</a>',
  430.             'now'        =>    $_SERVER['REQUEST_TIME'],
  431.             'charset'    =>    $this->charset,
  432.         );
  433.     }
  434.  
  435.     /**
  436.      * re-initializes the runtime variables before each template run
  437.      *
  438.      * override this method to inject data in the globals array if needed, this
  439.      * method is called before each template execution
  440.      *
  441.      * @param Dwoo_ITemplate $tpl the template that is going to be rendered
  442.      */
  443.     protected function initRuntimeVars(Dwoo_ITemplate $tpl)
  444.     {
  445.         $this->runtimePlugins = array();
  446.         $this->scope =$this->data;
  447.         $this->scopeTree = array();
  448.         $this->stack = array();
  449.         $this->curBlock = null;
  450.         $this->buffer = '';
  451.     }
  452.  
  453.     /*
  454.      * --------- settings functions ---------
  455.      */
  456.  
  457.     /**
  458.      * adds a custom plugin that is not in one of the plugin directories
  459.      *
  460.      * @param string $name the plugin name to be used in the templates
  461.      * @param callback $callback the plugin callback, either a function name,
  462.      *                               a class name or an array containing an object
  463.      *                               or class name and a method name
  464.      * @param bool $compilable if set to true, the plugin is assumed to be compilable
  465.      */
  466.     public function addPlugin($name$callback$compilable false)
  467.     {
  468.         $compilable $compilable self::COMPILABLE_PLUGIN 0;
  469.         if (is_array($callback)) {
  470.             if (is_subclass_of(is_object($callback[0]get_class($callback[0]$callback[0]'Dwoo_Block_Plugin')) {
  471.                 $this->plugins[$namearray('type'=>self::BLOCK_PLUGIN $compilable'callback'=>$callback'class'=>(is_object($callback[0]get_class($callback[0]$callback[0]));
  472.             else {
  473.                 $this->plugins[$namearray('type'=>self::CLASS_PLUGIN $compilable'callback'=>$callback'class'=>(is_object($callback[0]get_class($callback[0]$callback[0])'function'=>$callback[1]);
  474.             }
  475.         elseif (class_exists($callbackfalse)) {
  476.             if (is_subclass_of($callback'Dwoo_Block_Plugin')) {
  477.                 $this->plugins[$namearray('type'=>self::BLOCK_PLUGIN $compilable'callback'=>$callback'class'=>$callback);
  478.             else {
  479.                 $this->plugins[$namearray('type'=>self::CLASS_PLUGIN $compilable'callback'=>$callback'class'=>$callback'function'=>'process');
  480.             }
  481.         elseif (function_exists($callback)) {
  482.             $this->plugins[$namearray('type'=>self::FUNC_PLUGIN $compilable'callback'=>$callback);
  483.         else {
  484.             throw new Dwoo_Exception('Callback could not be processed correctly, please check that the function/class you used exists');
  485.         }
  486.     }
  487.  
  488.     /**
  489.      * removes a custom plugin
  490.      *
  491.      * @param string $name the plugin name
  492.      */
  493.     public function removePlugin($name)
  494.     {
  495.         if (isset($this->plugins[$name])) {
  496.             unset($this->plugins[$name]);
  497.         }
  498.     }
  499.  
  500.     /**
  501.      * adds a filter to this Dwoo instance, it will be used to filter the output of all the templates rendered by this instance
  502.      *
  503.      * @param mixed $callback a callback or a filter name if it is autoloaded from a plugin directory
  504.      * @param bool $autoload if true, the first parameter must be a filter name from one of the plugin directories
  505.      */
  506.     public function addFilter($callback$autoload false)
  507.     {
  508.         if ($autoload{
  509.             $class 'Dwoo_Filter_'.$callback;
  510.  
  511.             if (!class_exists($classfalse&& !function_exists($class)) {
  512.                 try {
  513.                     $this->getLoader()->loadPlugin($callback);
  514.                 catch (Dwoo_Exception $e{
  515.                     if (strstr($callback'Dwoo_Filter_')) {
  516.                         throw new Dwoo_Exception('Wrong filter name : '.$callback.', the "Dwoo_Filter_" prefix should not be used, please only use "'.str_replace('Dwoo_Filter_'''$callback).'"');
  517.                     else {
  518.                         throw new Dwoo_Exception('Wrong filter name : '.$callback.', when using autoload the filter must be in one of your plugin dir as "name.php" containg a class or function named "Dwoo_Filter_name"');
  519.                     }
  520.                 }
  521.             }
  522.  
  523.             if (class_exists($classfalse)) {
  524.                 $callback array(new $class($this)'process');
  525.             elseif (function_exists($class)) {
  526.                 $callback $class;
  527.             else {
  528.                 throw new Dwoo_Exception('Wrong filter name : '.$callback.', when using autoload the filter must be in one of your plugin dir as "name.php" containg a class or function named "Dwoo_Filter_name"');
  529.             }
  530.  
  531.             $this->filters[$callback;
  532.         else {
  533.             $this->filters[$callback;
  534.         }
  535.     }
  536.  
  537.     /**
  538.      * removes a filter
  539.      *
  540.      * @param mixed $callback callback or filter name if it was autoloaded
  541.      */
  542.     public function removeFilter($callback)
  543.     {
  544.         if (($index array_search('Dwoo_Filter_'.$callback$this->filterstrue)) !== false{
  545.             unset($this->filters[$index]);
  546.         elseif (($index array_search($callback$this->filterstrue)) !== false{
  547.             unset($this->filters[$index]);
  548.         else    {
  549.             $class 'Dwoo_Filter_' $callback;
  550.             foreach ($this->filters as $index=>$filter{
  551.                 if (is_array($filter&& $filter[0instanceof $class{
  552.                     unset($this->filters[$index]);
  553.                     break;
  554.                 }
  555.             }
  556.         }
  557.     }
  558.  
  559.     /**
  560.      * adds a resource or overrides a default one
  561.      *
  562.      * @param string $name the resource name
  563.      * @param string $class the resource class (which must implement Dwoo_ITemplate)
  564.      * @param callback $compilerFactory the compiler factory callback, a function that must return a compiler instance used to compile this resource, if none is provided. by default it will produce a Dwoo_Compiler object
  565.      */
  566.     public function addResource($name$class$compilerFactory null)
  567.     {
  568.         if (strlen($name2{
  569.             throw new Dwoo_Exception('Resource names must be at least two-character long to avoid conflicts with Windows paths');
  570.         }
  571.  
  572.         if (!class_exists($class)) {
  573.             throw new Dwoo_Exception('Resource class does not exist');
  574.         }
  575.  
  576.         $interfaces class_implements($class);
  577.         if (in_array('Dwoo_ITemplate'$interfaces=== false{
  578.             throw new Dwoo_Exception('Resource class must implement Dwoo_ITemplate');
  579.         }
  580.  
  581.         $this->resources[$namearray('class'=>$class'compiler'=>$compilerFactory);
  582.     }
  583.  
  584.     /**
  585.      * removes a custom resource
  586.      *
  587.      * @param string $name the resource name
  588.      */
  589.     public function removeResource($name)
  590.     {
  591.         unset($this->resources[$name]);
  592.         if ($name==='file'{
  593.             $this->resources['file'array('class'=>'Dwoo_Template_File''compiler'=>null);
  594.         }
  595.     }
  596.  
  597.     /*
  598.      * --------- getters and setters ---------
  599.      */
  600.  
  601.     /**
  602.      * sets the loader object to use to load plugins
  603.      *
  604.      * @param Dwoo_ILoader $loader loader object
  605.      */
  606.     public function setLoader(Dwoo_ILoader $loader)
  607.     {
  608.         $this->loader = $loader;
  609.     }
  610.  
  611.     /**
  612.      * returns the current loader object or a default one if none is currently found
  613.      *
  614.      * @param Dwoo_ILoader 
  615.      */
  616.     public function getLoader()
  617.     {
  618.         if ($this->loader === null{
  619.             $this->loader = new Dwoo_Loader($this->getCompileDir());
  620.         }
  621.  
  622.         return $this->loader;
  623.     }
  624.  
  625.     /**
  626.      * returns the custom plugins loaded
  627.      *
  628.      * used by the Dwoo_ITemplate classes to pass the custom plugins to their Dwoo_ICompiler instance
  629.      *
  630.      * @return array 
  631.      */
  632.     public function getCustomPlugins()
  633.     {
  634.         return $this->plugins;
  635.     }
  636.  
  637.     /**
  638.      * returns the cache directory with a trailing DIRECTORY_SEPARATOR
  639.      *
  640.      * @return string 
  641.      */
  642.     public function getCacheDir()
  643.     {
  644.         if ($this->cacheDir === null{
  645.             $this->setCacheDir(dirname(__FILE__).DIRECTORY_SEPARATOR.'cache'.DIRECTORY_SEPARATOR);
  646.         }
  647.  
  648.         return $this->cacheDir;
  649.     }
  650.  
  651.     /**
  652.      * sets the cache directory and automatically appends a DIRECTORY_SEPARATOR
  653.      *
  654.      * @param string $dir the cache directory
  655.      */
  656.     public function setCacheDir($dir)
  657.     {
  658.         $this->cacheDir = rtrim($dir'/\\').DIRECTORY_SEPARATOR;
  659.         if (is_writable($this->cacheDir=== false{
  660.             throw new Dwoo_Exception('The cache directory must be writable, chmod "'.$this->cacheDir.'" to make it writable');
  661.         }
  662.     }
  663.  
  664.     /**
  665.      * returns the compile directory with a trailing DIRECTORY_SEPARATOR
  666.      *
  667.      * @return string 
  668.      */
  669.     public function getCompileDir()
  670.     {
  671.         if ($this->compileDir === null{
  672.             $this->setCompileDir(dirname(__FILE__).DIRECTORY_SEPARATOR.'compiled'.DIRECTORY_SEPARATOR);
  673.         }
  674.  
  675.         return $this->compileDir;
  676.     }
  677.  
  678.     /**
  679.      * sets the compile directory and automatically appends a DIRECTORY_SEPARATOR
  680.      *
  681.      * @param string $dir the compile directory
  682.      */
  683.     public function setCompileDir($dir)
  684.     {
  685.         $this->compileDir = rtrim($dir'/\\').DIRECTORY_SEPARATOR;
  686.         if (is_writable($this->compileDir=== false{
  687.             throw new Dwoo_Exception('The compile directory must be writable, chmod "'.$this->compileDir.'" to make it writable');
  688.         }
  689.     }
  690.  
  691.     /**
  692.      * returns the default cache time that is used with templates that do not have a cache time set
  693.      *
  694.      * @return int the duration in seconds
  695.      */
  696.     public function getCacheTime()
  697.     {
  698.         return $this->cacheTime;
  699.     }
  700.  
  701.     /**
  702.      * sets the default cache time to use with templates that do not have a cache time set
  703.      *
  704.      * @param int $seconds the duration in seconds
  705.      */
  706.     public function setCacheTime($seconds)
  707.     {
  708.         $this->cacheTime = (int) $seconds;
  709.     }
  710.  
  711.     /**
  712.      * returns the character set used by the string manipulation plugins
  713.      *
  714.      * the charset is automatically lowercased
  715.      *
  716.      * @return string 
  717.      */
  718.     public function getCharset()
  719.     {
  720.         return $this->charset;
  721.     }
  722.  
  723.     /**
  724.      * sets the character set used by the string manipulation plugins
  725.      *
  726.      * the charset will be automatically lowercased
  727.      *
  728.      * @param string $charset the character set
  729.      */
  730.     public function setCharset($charset)
  731.     {
  732.         $this->charset = strtolower((string) $charset);
  733.     }
  734.  
  735.     /**
  736.      * returns the current template being rendered, when applicable, or null
  737.      *
  738.      * @return Dwoo_ITemplate|null
  739.      */
  740.     public function getTemplate()
  741.     {
  742.         return $this->template;
  743.     }
  744.  
  745.     /**
  746.      * sets the current template being rendered
  747.      *
  748.      * @param Dwoo_ITemplate $tpl template object
  749.      */
  750.     public function setTemplate(Dwoo_ITemplate $tpl)
  751.     {
  752.         $this->template = $tpl;
  753.     }
  754.  
  755.     /**
  756.      * sets the default compiler factory function for the given resource name
  757.      *
  758.      * a compiler factory must return a Dwoo_ICompiler object pre-configured to fit your needs
  759.      *
  760.      * @param string $resourceName the resource name (i.e. file, string)
  761.      * @param callback $compilerFactory the compiler factory callback
  762.      */
  763.     public function setDefaultCompilerFactory($resourceName$compilerFactory)
  764.     {
  765.         $this->resources[$resourceName]['compiler'$compilerFactory;
  766.     }
  767.  
  768.     /**
  769.      * returns the default compiler factory function for the given resource name
  770.      *
  771.      * @param string $resourceName the resource name
  772.      * @return callback the compiler factory callback
  773.      */
  774.     public function getDefaultCompilerFactory($resourceName)
  775.     {
  776.         return $this->resources[$resourceName]['compiler'];
  777.     }
  778.  
  779.     /**
  780.      * sets the security policy object to enforce some php security settings
  781.      *
  782.      * use this if untrusted persons can modify templates
  783.      *
  784.      * @param Dwoo_Security_Policy $policy the security policy object
  785.      */
  786.     public function setSecurityPolicy(Dwoo_Security_Policy $policy null)
  787.     {
  788.         $this->securityPolicy = $policy;
  789.     }
  790.  
  791.     /**
  792.      * returns the current security policy object or null by default
  793.      *
  794.      * @return Dwoo_Security_Policy|nullthe security policy object if any
  795.      */
  796.     public function getSecurityPolicy()
  797.     {
  798.         return $this->securityPolicy;
  799.     }
  800.  
  801.     /**
  802.      * sets the object that must be used as a plugin proxy when plugin can't be found
  803.      * by dwoo's loader
  804.      *
  805.      * @param Dwoo_IPluginProxy $pluginProxy the proxy object
  806.      */
  807.     public function setPluginProxy(Dwoo_IPluginProxy $pluginProxy{
  808.         $this->pluginProxy = $pluginProxy;
  809.     }
  810.  
  811.     /**
  812.      * returns the current plugin proxy object or null by default
  813.      *
  814.      * @param Dwoo_IPluginProxy|nullthe proxy object if any
  815.      */
  816.     public function getPluginProxy({
  817.         return $this->pluginProxy;
  818.     }
  819.  
  820.     /*
  821.      * --------- util functions ---------
  822.      */
  823.  
  824.     /**
  825.      * [util function] checks whether the given template is cached or not
  826.      *
  827.      * @param Dwoo_ITemplate $tpl the template object
  828.      * @return bool 
  829.      */
  830.     public function isCached(Dwoo_ITemplate $tpl)
  831.     {
  832.         return is_string($tpl->getCachedTemplate($this));
  833.     }
  834.  
  835.     /**
  836.      * [util function] clears the cached templates if they are older than the given time
  837.      *
  838.      * @param int $olderThan minimum time (in seconds) required for a cached template to be cleared
  839.      * @return int the amount of templates cleared
  840.      */
  841.     public function clearCache($olderThan=-1)
  842.     {
  843.         $cacheDirs new RecursiveDirectoryIterator($this->getCacheDir());
  844.         $cache new RecursiveIteratorIterator($cacheDirs);
  845.         $expired time($olderThan;
  846.         $count 0;
  847.         foreach ($cache as $file{
  848.             if ($cache->isDot(|| $cache->isDir(|| substr($file-5!== '.html'{
  849.                 continue;
  850.             }
  851.             if ($cache->getCTime($expired{
  852.                 $count += unlink((string) $file0;
  853.             }
  854.         }
  855.         return $count;
  856.     }
  857.  
  858.     /**
  859.      * [util function] fetches a template object of the given resource
  860.      *
  861.      * @param string $resourceName the resource name (i.e. file, string)
  862.      * @param string $resourceId the resource identifier (i.e. file path)
  863.      * @param int $cacheTime the cache time setting for this resource
  864.      * @param string $cacheId the unique cache identifier
  865.      * @param string $compileId the unique compiler identifier
  866.      * @return Dwoo_ITemplate 
  867.      */
  868.     public function templateFactory($resourceName$resourceId$cacheTime null$cacheId null$compileId nullDwoo_ITemplate $parentTemplate null)
  869.     {
  870.         if (isset($this->resources[$resourceName])) {
  871.             // TODO could be changed to $this->resources[$resourceName]['class']::templateFactory(..) in 5.3 maybe
  872.             return call_user_func(array($this->resources[$resourceName]['class']'templateFactory')$this$resourceId$cacheTime$cacheId$compileId$parentTemplate);
  873.         else {
  874.             throw new Dwoo_Exception('Unknown resource type : '.$resourceName);
  875.         }
  876.     }
  877.  
  878.     /**
  879.      * [util function] checks if the input is an array or an iterator object, optionally it can also check if it's empty
  880.      *
  881.      * @param mixed $value the variable to check
  882.      * @param bool $checkIsEmpty if true, the function will also check if the array is empty,
  883.      *                                  and return true only if it's not empty
  884.      * @return bool true if it's an array (and not empty) or false if it's not an array (or if it's empty)
  885.      */
  886.     public function isArray($value$checkIsEmpty=false)
  887.     {
  888.         if (is_array($value=== true{
  889.             if ($checkIsEmpty === false{
  890.                 return true;
  891.             else {
  892.                 return count($value0;
  893.             }
  894.         elseif ($value instanceof Iterator{
  895.             if ($checkIsEmpty === false{
  896.                 return true;
  897.             elseif ($value instanceof Countable{
  898.                 return count($value0;
  899.             else {
  900.                 $value->rewind();
  901.                 return $value->valid();
  902.             }
  903.         elseif ($value instanceof ArrayAccess{
  904.             if ($checkIsEmpty === false{
  905.                 return true;
  906.             elseif ($value instanceof Countable{
  907.                 return count($value0;
  908.             else {
  909.                 return $value->offsetExists(0);
  910.             }
  911.         }
  912.         return false;
  913.     }
  914.  
  915.     /**
  916.      * [util function] triggers a dwoo error
  917.      *
  918.      * @param string $message the error message
  919.      * @param int $level the error level, one of the PHP's E_* constants
  920.      */
  921.     public function triggerError($message$level=E_USER_NOTICE)
  922.     {
  923.         if (!($tplIdentifier $this->template->getResourceIdentifier())) {
  924.             $tplIdentifier $this->template->getResourceName();
  925.         }
  926.         trigger_error('Dwoo error (in '.$tplIdentifier.') : '.$message$level);
  927.     }
  928.  
  929.     /*
  930.      * --------- runtime functions ---------
  931.      */
  932.  
  933.     /**
  934.      * [runtime function] adds a block to the block stack
  935.      *
  936.      * @param string $blockName the block name (without Dwoo_Plugin_ prefix)
  937.      * @param array $args the arguments to be passed to the block's init() function
  938.      * @return Dwoo_Block_Plugin the newly created block
  939.      */
  940.     public function addStack($blockNamearray $args=array())
  941.     {
  942.         if (isset($this->plugins[$blockName])) {
  943.             $class $this->plugins[$blockName]['class'];
  944.         else {
  945.             $class 'Dwoo_Plugin_'.$blockName;
  946.         }
  947.  
  948.         if ($this->curBlock !== null{
  949.             $this->curBlock->buffer(ob_get_contents());
  950.             ob_clean();
  951.         else {
  952.             $this->buffer .= ob_get_contents();
  953.             ob_clean();
  954.         }
  955.  
  956.         $block new $class($this);
  957.  
  958.         $cnt count($args);
  959.         if ($cnt===0{
  960.             $block->init();
  961.         elseif ($cnt===1{
  962.             $block->init($args[0]);
  963.         elseif ($cnt===2{
  964.             $block->init($args[0]$args[1]);
  965.         elseif ($cnt===3{
  966.             $block->init($args[0]$args[1]$args[2]);
  967.         elseif ($cnt===4{
  968.             $block->init($args[0]$args[1]$args[2]$args[3]);
  969.         else {
  970.             call_user_func_array(array($block,'init')$args);
  971.         }
  972.  
  973.         $this->stack[$this->curBlock = $block;
  974.         return $block;
  975.     }
  976.  
  977.     /**
  978.      * [runtime function] removes the plugin at the top of the block stack
  979.      *
  980.      * calls the block buffer() function, followed by a call to end()
  981.      * and finally a call to process()
  982.      */
  983.     public function delStack()
  984.     {
  985.         $args func_get_args();
  986.  
  987.         $this->curBlock->buffer(ob_get_contents());
  988.         ob_clean();
  989.  
  990.         $cnt count($args);
  991.         if ($cnt===0{
  992.             $this->curBlock->end();
  993.         elseif ($cnt===1{
  994.             $this->curBlock->end($args[0]);
  995.         elseif ($cnt===2{
  996.             $this->curBlock->end($args[0]$args[1]);
  997.         elseif ($cnt===3{
  998.             $this->curBlock->end($args[0]$args[1]$args[2]);
  999.         elseif ($cnt===4{
  1000.             $this->curBlock->end($args[0]$args[1]$args[2]$args[3]);
  1001.         else {
  1002.             call_user_func_array(array($this->curBlock'end')$args);
  1003.         }
  1004.  
  1005.         $tmp array_pop($this->stack);
  1006.  
  1007.         if (count($this->stack0{
  1008.             $this->curBlock = end($this->stack);
  1009.             $this->curBlock->buffer($tmp->process());
  1010.         else {
  1011.             $this->curBlock = null;
  1012.             echo $tmp->process();
  1013.         }
  1014.  
  1015.         unset($tmp);
  1016.     }
  1017.  
  1018.     /**
  1019.      * [runtime function] returns the parent block of the given block
  1020.      *
  1021.      * @param Dwoo_Block_Plugin $block 
  1022.      * @return Dwoo_Block_Plugin or false if the given block isn't in the stack
  1023.      */
  1024.     public function getParentBlock(Dwoo_Block_Plugin $block)
  1025.     {
  1026.         $index array_search($block$this->stacktrue);
  1027.         if ($index !== false && $index 0{
  1028.             return $this->stack[$index-1];
  1029.         }
  1030.         return false;
  1031.     }
  1032.  
  1033.     /**
  1034.      * [runtime function] finds the closest block of the given type, starting at the top of the stack
  1035.      *
  1036.      * @param string $type the type of plugin you want to find
  1037.      * @return Dwoo_Block_Plugin or false if no plugin of such type is in the stack
  1038.      */
  1039.     public function findBlock($type)
  1040.     {
  1041.         if (isset($this->plugins[$type])) {
  1042.             $type $this->plugins[$type]['class'];
  1043.         else {
  1044.             $type 'Dwoo_Plugin_'.str_replace('Dwoo_Plugin_'''$type);
  1045.         }
  1046.  
  1047.         $keys array_keys($this->stack);
  1048.         while (($key array_pop($keys)) !== false{
  1049.             if ($this->stack[$keyinstanceof $type{
  1050.                 return $this->stack[$key];
  1051.             }
  1052.         }
  1053.         return false;
  1054.     }
  1055.  
  1056.     /**
  1057.      * [runtime function] returns a Dwoo_Plugin of the given class
  1058.      *
  1059.      * this is so a single instance of every class plugin is created at each template run,
  1060.      * allowing class plugins to have "per-template-run" static variables
  1061.      *
  1062.      * @private
  1063.      * @param string $class the class name
  1064.      * @return mixed an object of the given class
  1065.      */
  1066.     public function getObjectPlugin($class)
  1067.     {
  1068.         if (isset($this->runtimePlugins[$class])) {
  1069.             return $this->runtimePlugins[$class];
  1070.         }
  1071.         return $this->runtimePlugins[$classnew $class($this);
  1072.     }
  1073.  
  1074.     /**
  1075.      * [runtime function] calls the process() method of the given class-plugin name
  1076.      *
  1077.      * @param string $plugName the class plugin name (without Dwoo_Plugin_ prefix)
  1078.      * @param array $params an array of parameters to send to the process() method
  1079.      * @return string the process() return value
  1080.      */
  1081.     public function classCall($plugNamearray $params array())
  1082.     {
  1083.         $class 'Dwoo_Plugin_'.$plugName;
  1084.  
  1085.         $plugin $this->getObjectPlugin($class);
  1086.  
  1087.         $cnt count($params);
  1088.         if ($cnt===0{
  1089.             return $plugin->process();
  1090.         elseif ($cnt===1{
  1091.             return $plugin->process($params[0]);
  1092.         elseif ($cnt===2{
  1093.             return $plugin->process($params[0]$params[1]);
  1094.         elseif ($cnt===3{
  1095.             return $plugin->process($params[0]$params[1]$params[2]);
  1096.         elseif ($cnt===4{
  1097.             return $plugin->process($params[0]$params[1]$params[2]$params[3]);
  1098.         else {
  1099.             return call_user_func_array(array($plugin'process')$params);
  1100.         }
  1101.     }
  1102.  
  1103.     /**
  1104.      * [runtime function] calls a php function
  1105.      *
  1106.      * @param string $callback the function to call
  1107.      * @param array $params an array of parameters to send to the function
  1108.      * @return mixed the return value of the called function
  1109.      */
  1110.     public function arrayMap($callbackarray $params)
  1111.     {
  1112.         if ($params[0=== $this{
  1113.             $addThis true;
  1114.             array_shift($params);
  1115.         }
  1116.         if ((is_array($params[0]|| ($params[0instanceof Iterator && $params[0instanceof ArrayAccess))) {
  1117.             if (empty($params[0])) {
  1118.                 return $params[0];
  1119.             }
  1120.  
  1121.             // array map
  1122.             $out array();
  1123.             $cnt count($params);
  1124.  
  1125.             if (isset($addThis)) {
  1126.                 array_unshift($params$this);
  1127.                 $items $params[1];
  1128.                 $keys array_keys($items);
  1129.  
  1130.                 if (is_string($callback=== false{
  1131.                     while (($i array_shift($keys)) !== null{
  1132.                         $out[call_user_func_array($callbackarray(1=>$items[$i]$params);
  1133.                     }
  1134.                 elseif ($cnt===1{
  1135.                     while (($i array_shift($keys)) !== null{
  1136.                         $out[$callback($this$items[$i]);
  1137.                     }
  1138.                 elseif ($cnt===2{
  1139.                     while (($i array_shift($keys)) !== null{
  1140.                         $out[$callback($this$items[$i]$params[2]);
  1141.                     }
  1142.                 elseif ($cnt===3{
  1143.                     while (($i array_shift($keys)) !== null{
  1144.                         $out[$callback($this$items[$i]$params[2]$params[3]);
  1145.                     }
  1146.                 else {
  1147.                     while (($i array_shift($keys)) !== null{
  1148.                         $out[call_user_func_array($callbackarray(1=>$items[$i]$params);
  1149.                     }
  1150.                 }
  1151.             else {
  1152.                 $items $params[0];
  1153.                 $keys array_keys($items);
  1154.  
  1155.                 if (is_string($callback=== false{
  1156.                     while (($i array_shift($keys)) !== null{
  1157.                         $out[call_user_func_array($callbackarray($items[$i]$params);
  1158.                     }
  1159.                 elseif ($cnt===1{
  1160.                     while (($i array_shift($keys)) !== null{
  1161.                         $out[$callback($items[$i]);
  1162.                     }
  1163.                 elseif ($cnt===2{
  1164.                     while (($i array_shift($keys)) !== null{
  1165.                         $out[$callback($items[$i]$params[1]);
  1166.                     }
  1167.                 elseif ($cnt===3{
  1168.                     while (($i array_shift($keys)) !== null{
  1169.                         $out[$callback($items[$i]$params[1]$params[2]);
  1170.                     }
  1171.                 elseif ($cnt===4{
  1172.                     while (($i array_shift($keys)) !== null{
  1173.                         $out[$callback($items[$i]$params[1]$params[2]$params[3]);
  1174.                     }
  1175.                 else {
  1176.                     while (($i array_shift($keys)) !== null{
  1177.                         $out[call_user_func_array($callbackarray($items[$i]$params);
  1178.                     }
  1179.                 }
  1180.             }
  1181.             return $out;
  1182.         else {
  1183.             return $params[0];
  1184.         }
  1185.     }
  1186.  
  1187.     /**
  1188.      * [runtime function] reads a variable into the given data array
  1189.      *
  1190.      * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
  1191.      * @param mixed $data the data array or object to read from
  1192.      * @param bool $safeRead if true, the function will check whether the index exists to prevent any notices from being output
  1193.      * @return mixed 
  1194.      */
  1195.     public function readVarInto($varstr$data$safeRead false)
  1196.     {
  1197.         if ($data === null{
  1198.             return null;
  1199.         }
  1200.  
  1201.         if (is_array($varstr=== false{
  1202.             preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i'$varstr$m);
  1203.         else {
  1204.             $m $varstr;
  1205.         }
  1206.         unset($varstr);
  1207.  
  1208.         while (list($k$sepeach($m[1])) {
  1209.             if ($sep === '.' || $sep === '[' || $sep === ''{
  1210.                 if ((is_array($data|| $data instanceof ArrayAccess&& ($safeRead === false || isset($data[$m[2][$k]]))) {
  1211.                     $data $data[$m[2][$k]];
  1212.                 else {
  1213.                     return null;
  1214.                 }
  1215.             else {
  1216.                 if (is_object($data&& ($safeRead === false || isset($data->$m[2][$k]|| is_callable(array($data'__get')))) {
  1217.                     $data $data->$m[2][$k];
  1218.                 else {
  1219.                     return null;
  1220.                 }
  1221.             }
  1222.         }
  1223.  
  1224.         return $data;
  1225.     }
  1226.  
  1227.     /**
  1228.      * [runtime function] reads a variable into the parent scope
  1229.      *
  1230.      * @param int $parentLevels the amount of parent levels to go from the current scope
  1231.      * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
  1232.      * @return mixed 
  1233.      */
  1234.     public function readParentVar($parentLevels$varstr null)
  1235.     {
  1236.         $tree $this->scopeTree;
  1237.         $cur $this->data;
  1238.  
  1239.         while ($parentLevels--!==0{
  1240.             array_pop($tree);
  1241.         }
  1242.  
  1243.         while (($i array_shift($tree)) !== null{
  1244.             if (is_object($cur)) {
  1245.                 $cur $cur->$i;
  1246.             else {
  1247.                 $cur $cur[$i];
  1248.             }
  1249.         }
  1250.  
  1251.         if ($varstr!==null{
  1252.             return $this->readVarInto($varstr$cur);
  1253.         else {
  1254.             return $cur;
  1255.         }
  1256.     }
  1257.  
  1258.     /**
  1259.      * [runtime function] reads a variable into the current scope
  1260.      *
  1261.      * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
  1262.      * @return mixed 
  1263.      */
  1264.     public function readVar($varstr)
  1265.     {
  1266.         if (is_array($varstr)===true{
  1267.             $m $varstr;
  1268.             unset($varstr);
  1269.         else {
  1270.             if (strstr($varstr'.'=== false && strstr($varstr'['=== false && strstr($varstr'->'=== false{
  1271.                 if ($varstr === 'dwoo'{
  1272.                     return $this->globals;
  1273.                 elseif ($varstr === '__' || $varstr === '_root' {
  1274.                     return $this->data;
  1275.                     $varstr substr($varstr6);
  1276.                 elseif ($varstr === '_' || $varstr === '_parent'{
  1277.                     $varstr '.'.$varstr;
  1278.                     $tree $this->scopeTree;
  1279.                     $cur $this->data;
  1280.                     array_pop($tree);
  1281.  
  1282.                     while (($i array_shift($tree)) !== null{
  1283.                         if (is_object($cur)) {
  1284.                             $cur $cur->$i;
  1285.                         else {
  1286.                             $cur $cur[$i];
  1287.                         }
  1288.                     }
  1289.  
  1290.                     return $cur;
  1291.                 }
  1292.  
  1293.                 $cur $this->scope;
  1294.  
  1295.                 if (isset($cur[$varstr])) {
  1296.                     return $cur[$varstr];
  1297.                 else {
  1298.                     return null;
  1299.                 }
  1300.             }
  1301.  
  1302.             if (substr($varstr01=== '.'{
  1303.                 $varstr 'dwoo'.$varstr;
  1304.             }
  1305.  
  1306.             preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i'$varstr$m);
  1307.         }
  1308.  
  1309.         $i $m[2][0];
  1310.         if ($i === 'dwoo'{
  1311.             $cur $this->globals;
  1312.             array_shift($m[2]);
  1313.             array_shift($m[1]);
  1314.             switch ($m[2][0]{
  1315.  
  1316.             case 'get':
  1317.                 $cur $_GET;
  1318.                 break;
  1319.             case 'post':
  1320.                 $cur $_POST;
  1321.                 break;
  1322.             case 'session':
  1323.                 $cur $_SESSION;
  1324.                 break;
  1325.             case 'cookies':
  1326.             case 'cookie':
  1327.                 $cur $_COOKIE;
  1328.                 break;
  1329.             case 'server':
  1330.                 $cur $_SERVER;
  1331.                 break;
  1332.             case 'env':
  1333.                 $cur $_ENV;
  1334.                 break;
  1335.             case 'request':
  1336.                 $cur $_REQUEST;
  1337.                 break;
  1338.             case 'const':
  1339.                 array_shift($m[2]);
  1340.                 if (defined($m[2][0])) {
  1341.                     return constant($m[2][0]);
  1342.                 else {
  1343.                     return null;
  1344.                 }
  1345.  
  1346.             }
  1347.             if ($cur !== $this->globals{
  1348.                 array_shift($m[2]);
  1349.                 array_shift($m[1]);
  1350.             }
  1351.         elseif ($i === '__' || $i === '_root'{
  1352.             $cur $this->data;
  1353.             array_shift($m[2]);
  1354.             array_shift($m[1]);
  1355.         elseif ($i === '_' || $i === '_parent'{
  1356.             $tree $this->scopeTree;
  1357.             $cur $this->data;
  1358.  
  1359.             while (true{
  1360.                 array_pop($tree);
  1361.                 array_shift($m[2]);
  1362.                 array_shift($m[1]);
  1363.                 if (current($m[2]=== '_' || current($m[2]=== '_parent'{
  1364.                     continue;
  1365.                 }
  1366.  
  1367.                 while (($i array_shift($tree)) !== null{
  1368.                     if (is_object($cur)) {
  1369.                         $cur $cur->$i;
  1370.                     else {
  1371.                         $cur $cur[$i];
  1372.                     }
  1373.                 }
  1374.                 break;
  1375.             }
  1376.         else {
  1377.             $cur $this->scope;
  1378.         }
  1379.  
  1380.         while (list($k$sepeach($m[1])) {
  1381.             if ($sep === '.' || $sep === '[' || $sep === ''{
  1382.                 if ((is_array($cur|| $cur instanceof ArrayAccess&& isset($cur[$m[2][$k]])) {
  1383.                     $cur $cur[$m[2][$k]];
  1384.                 else {
  1385.                     return null;
  1386.                 }
  1387.             elseif ($sep === '->'{
  1388.                 if (is_object($cur)) {
  1389.                     $cur $cur->$m[2][$k];
  1390.                 else {
  1391.                     return null;
  1392.                 }
  1393.             else {
  1394.                 return null;
  1395.             }
  1396.         }
  1397.  
  1398.         return $cur;
  1399.     }
  1400.  
  1401.     /**
  1402.      * [runtime function] assign the value to the given variable
  1403.      *
  1404.      * @param mixed $value the value to assign
  1405.      * @param string $scope the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
  1406.      * @return bool true if assigned correctly or false if a problem occured while parsing the var string
  1407.      */
  1408.     public function assignInScope($value$scope)
  1409.     {
  1410.         $tree =$this->scopeTree;
  1411.         $data =$this->data;
  1412.  
  1413.         if (!is_string($scope)) {
  1414.             return $this->triggerError('Assignments must be done into strings, ('.gettype($scope).') '.var_export($scopetrue).' given'E_USER_ERROR);
  1415.         }
  1416.         if (strstr($scope'.'=== false && strstr($scope'->'=== false{
  1417.             $this->scope[$scope$value;
  1418.         else {
  1419.             // TODO handle _root/_parent scopes ?
  1420.             preg_match_all('#(\[|->|\.)?([^.[\]-]+)\]?#i'$scope$m);
  1421.  
  1422.             $cur =$this->scope;
  1423.             $last array(array_pop($m[1])array_pop($m[2]));
  1424.  
  1425.             while (list($k$sepeach($m[1])) {
  1426.                 if ($sep === '.' || $sep === '[' || $sep === ''{
  1427.                     if (is_array($cur=== false{
  1428.                         $cur array();
  1429.                     }
  1430.                     $cur =$cur[$m[2][$k]];
  1431.                 elseif ($sep === '->'{
  1432.                     if (is_object($cur=== false{
  1433.                         $cur new stdClass;
  1434.                     }
  1435.                     $cur =$cur->$m[2][$k];
  1436.                 else {
  1437.                     return false;
  1438.                 }
  1439.             }
  1440.  
  1441.             if ($last[0=== '.' || $last[0=== '[' || $last[0=== ''{
  1442.                 if (is_array($cur=== false{
  1443.                     $cur array();
  1444.                 }
  1445.                 $cur[$last[1]] $value;
  1446.             elseif ($last[0=== '->'{
  1447.                 if (is_object($cur=== false{
  1448.                     $cur new stdClass;
  1449.                 }
  1450.                 $cur->$last[1$value;
  1451.             else {
  1452.                 return false;
  1453.             }
  1454.         }
  1455.     }
  1456.  
  1457.     /**
  1458.      * [runtime function] sets the scope to the given scope string or array
  1459.      *
  1460.      * @param mixed $scope a string i.e. "level1.level2" or an array i.e. array("level1", "level2")
  1461.      * @param bool $absolute if true, the scope is set from the top level scope and not from the current scope
  1462.      * @return array the current scope tree
  1463.      */
  1464.     public function setScope($scope$absolute false)
  1465.     {
  1466.         $old $this->scopeTree;
  1467.  
  1468.         if (is_string($scope)===true{
  1469.             $scope explode('.'$scope);
  1470.         }
  1471.  
  1472.         if ($absolute===true{
  1473.             $this->scope =$this->data;
  1474.             $this->scopeTree array();
  1475.         }
  1476.  
  1477.         while (($bit array_shift($scope)) !== null{
  1478.             if ($bit === '_' || $bit === '_parent'{
  1479.                 array_pop($this->scopeTree);
  1480.                 $this->scope =$this->data;
  1481.                 $cnt count($this->scopeTree);
  1482.                 for ($i=0;$i<$cnt;$i++)
  1483.                     $this->scope =$this->scope[$this->scopeTree[$i]];
  1484.             elseif ($bit === '__' || $bit === '_root'{
  1485.                 $this->scope =$this->data;
  1486.                 $this->scopeTree array();
  1487.             elseif (isset($this->scope[$bit])) {
  1488.                 $this->scope =$this->scope[$bit];
  1489.                 $this->scopeTree[$bit;
  1490.             else {
  1491.                 unset($this->scope);
  1492.                 $this->scope null;
  1493.             }
  1494.         }
  1495.  
  1496.         return $old;
  1497.     }
  1498.  
  1499.     /**
  1500.      * [runtime function] returns the entire data array
  1501.      *
  1502.      * @return array 
  1503.      */
  1504.     public function getData()
  1505.     {
  1506.         return $this->data;
  1507.     }
  1508.  
  1509.     /**
  1510.      * [runtime function] returns a reference to the current scope
  1511.      *
  1512.      * @return &mixed 
  1513.      */
  1514.     public function &getScope()
  1515.     {
  1516.         return $this->scope;
  1517.     }
  1518.  
  1519.     /**
  1520.      * Redirects all calls to unexisting to plugin proxy.
  1521.      *
  1522.      * @param string Method name
  1523.      * @param array  List of arguments
  1524.      * @return mixed 
  1525.      */
  1526.     public function __call($method$args{
  1527.         $proxy $this->getPluginProxy();
  1528.         if (!$proxy{
  1529.             throw new Dwoo_Exception('Call to undefined method '.__CLASS__.'::'.$method.'()');
  1530.         }
  1531.         return call_user_func_array($proxy->getCallback($method)$args);
  1532.     }
  1533. }

Documentation generated on Sun, 07 Feb 2010 17:53:39 +0000 by phpDocumentor 1.4.0