/fuel/application/libraries/dwoo/Dwoo/Compiler.php
PHP | 2023 lines | 1270 code | 199 blank | 554 comment | 378 complexity | a6e9132f10f311121438a94ee11dd6a6 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception
- <?php
- include dirname(__FILE__) . '/Compilation/Exception.php';
- /**
- * default dwoo compiler class, compiles dwoo templates into php
- *
- * This software is provided 'as-is', without any express or implied warranty.
- * In no event will the authors be held liable for any damages arising from the use of this software.
- *
- * @author Jordi Boggiano <j.boggiano@seld.be>
- * @copyright Copyright (c) 2008, Jordi Boggiano
- * @license http://dwoo.org/LICENSE Modified BSD License
- * @link http://dwoo.org/
- * @version 1.1.0
- * @date 2009-07-18
- * @package Dwoo
- */
- class Dwoo_Compiler implements Dwoo_ICompiler
- {
- /**
- * constant that represents a php opening tag
- *
- * use it in case it needs to be adjusted
- *
- * @var string
- */
- const PHP_OPEN = "<?php ";
- /**
- * constant that represents a php closing tag
- *
- * use it in case it needs to be adjusted
- *
- * @var string
- */
- const PHP_CLOSE = "?>";
- /**
- * boolean flag to enable or disable debugging output
- *
- * @var bool
- */
- public $debug = false;
- /**
- * left script delimiter
- *
- * @var string
- */
- protected $ld = '{';
- /**
- * left script delimiter with escaped regex meta characters
- *
- * @var string
- */
- protected $ldr = '\\{';
- /**
- * right script delimiter
- *
- * @var string
- */
- protected $rd = '}';
- /**
- * right script delimiter with escaped regex meta characters
- *
- * @var string
- */
- protected $rdr = '\\}';
- /**
- * defines whether the nested comments should be parsed as nested or not
- *
- * defaults to false (classic block comment parsing as in all languages)
- *
- * @var bool
- */
- protected $allowNestedComments = false;
- /**
- * defines whether opening and closing tags can contain spaces before valid data or not
- *
- * turn to true if you want to be sloppy with the syntax, but when set to false it allows
- * to skip javascript and css tags as long as they are in the form "{ something", which is
- * nice. default is false.
- *
- * @var bool
- */
- protected $allowLooseOpenings = false;
- /**
- * defines whether the compiler will automatically html-escape variables or not
- *
- * default is false
- *
- * @var bool
- */
- protected $autoEscape = false;
- /**
- * security policy object
- *
- * @var Dwoo_Security_Policy
- */
- protected $securityPolicy;
- /**
- * stores the custom plugins registered with this compiler
- *
- * @var array
- */
- protected $customPlugins = array();
- /**
- * stores the template plugins registered with this compiler
- *
- * @var array
- */
- protected $templatePlugins = array();
- /**
- * stores the pre- and post-processors callbacks
- *
- * @var array
- */
- protected $processors = array('pre'=>array(), 'post'=>array());
- /**
- * stores a list of plugins that are used in the currently compiled
- * template, and that are not compilable. these plugins will be loaded
- * during the template's runtime if required.
- *
- * it is a 1D array formatted as key:pluginName value:pluginType
- *
- * @var array
- */
- protected $usedPlugins;
- /**
- * stores the template undergoing compilation
- *
- * @var string
- */
- protected $template;
- /**
- * stores the current pointer position inside the template
- *
- * @var int
- */
- protected $pointer;
- /**
- * stores the current line count inside the template for debugging purposes
- *
- * @var int
- */
- protected $line;
- /**
- * stores the current template source while compiling it
- *
- * @var string
- */
- protected $templateSource;
- /**
- * stores the data within which the scope moves
- *
- * @var array
- */
- protected $data;
- /**
- * variable scope of the compiler, set to null if
- * it can not be resolved to a static string (i.e. if some
- * plugin defines a new scope based on a variable array key)
- *
- * @var mixed
- */
- protected $scope;
- /**
- * variable scope tree, that allows to rebuild the current
- * scope if required, i.e. when going to a parent level
- *
- * @var array
- */
- protected $scopeTree;
- /**
- * block plugins stack, accessible through some methods
- *
- * @see findBlock
- * @see getCurrentBlock
- * @see addBlock
- * @see addCustomBlock
- * @see injectBlock
- * @see removeBlock
- * @see removeTopBlock
- *
- * @var array
- */
- protected $stack = array();
- /**
- * current block at the top of the block plugins stack,
- * accessible through getCurrentBlock
- *
- * @see getCurrentBlock
- *
- * @var Dwoo_Block_Plugin
- */
- protected $curBlock;
- /**
- * current dwoo object that uses this compiler, or null
- *
- * @var Dwoo
- */
- protected $dwoo;
- /**
- * holds an instance of this class, used by getInstance when you don't
- * provide a custom compiler in order to save resources
- *
- * @var Dwoo_Compiler
- */
- protected static $instance;
- /**
- * token types
- * @var int
- */
- const T_UNQUOTED_STRING = 1;
- const T_NUMERIC = 2;
- const T_NULL = 4;
- const T_BOOL = 8;
- const T_MATH = 16;
- const T_BREAKCHAR = 32;
- /**
- * constructor
- *
- * saves the created instance so that child templates get the same one
- */
- public function __construct()
- {
- self::$instance = $this;
- }
- /**
- * sets the delimiters to use in the templates
- *
- * delimiters can be multi-character strings but should not be one of those as they will
- * 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.
- *
- * @param string $left left delimiter
- * @param string $right right delimiter
- */
- public function setDelimiters($left, $right)
- {
- $this->ld = $left;
- $this->rd = $right;
- $this->ldr = preg_quote($left, '/');
- $this->rdr = preg_quote($right, '/');
- }
- /**
- * returns the left and right template delimiters
- *
- * @return array containing the left and the right delimiters
- */
- public function getDelimiters()
- {
- return array($this->ld, $this->rd);
- }
- /**
- * sets the way to handle nested comments, if set to true
- * {* foo {* some other *} comment *} will be stripped correctly.
- *
- * if false it will remove {* foo {* some other *} and leave "comment *}" alone,
- * this is the default behavior
- *
- * @param bool $allow allow nested comments or not, defaults to true (but the default internal value is false)
- */
- public function setNestedCommentsHandling($allow = true) {
- $this->allowNestedComments = (bool) $allow;
- }
- /**
- * returns the nested comments handling setting
- *
- * @see setNestedCommentsHandling
- * @return bool true if nested comments are allowed
- */
- public function getNestedCommentsHandling() {
- return $this->allowNestedComments;
- }
- /**
- * sets the tag openings handling strictness, if set to true, template tags can
- * contain spaces before the first function/string/variable such as { $foo} is valid.
- *
- * if set to false (default setting), { $foo} is invalid but that is however a good thing
- * as it allows css (i.e. #foo { color:red; }) to be parsed silently without triggering
- * an error, same goes for javascript.
- *
- * @param bool $allow true to allow loose handling, false to restore default setting
- */
- public function setLooseOpeningHandling($allow = false)
- {
- $this->allowLooseOpenings = (bool) $allow;
- }
- /**
- * returns the tag openings handling strictness setting
- *
- * @see setLooseOpeningHandling
- * @return bool true if loose tags are allowed
- */
- public function getLooseOpeningHandling()
- {
- return $this->allowLooseOpenings;
- }
- /**
- * changes the auto escape setting
- *
- * if enabled, the compiler will automatically html-escape variables,
- * unless they are passed through the safe function such as {$var|safe}
- * or {safe $var}
- *
- * default setting is disabled/false
- *
- * @param bool $enabled set to true to enable, false to disable
- */
- public function setAutoEscape($enabled)
- {
- $this->autoEscape = (bool) $enabled;
- }
- /**
- * returns the auto escape setting
- *
- * default setting is disabled/false
- *
- * @return bool
- */
- public function getAutoEscape()
- {
- return $this->autoEscape;
- }
- /**
- * adds a preprocessor to the compiler, it will be called
- * before the template is compiled
- *
- * @param mixed $callback either a valid callback to the preprocessor or a simple name if the autoload is set to true
- * @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
- */
- public function addPreProcessor($callback, $autoload = false)
- {
- if ($autoload) {
- $name = str_replace('Dwoo_Processor_', '', $callback);
- $class = 'Dwoo_Processor_'.$name;
- if (class_exists($class, false)) {
- $callback = array(new $class($this), 'process');
- } elseif (function_exists($class)) {
- $callback = $class;
- } else {
- $callback = array('autoload'=>true, 'class'=>$class, 'name'=>$name);
- }
- $this->processors['pre'][] = $callback;
- } else {
- $this->processors['pre'][] = $callback;
- }
- }
- /**
- * removes a preprocessor from the compiler
- *
- * @param mixed $callback either a valid callback to the preprocessor or a simple name if it was autoloaded
- */
- public function removePreProcessor($callback)
- {
- if (($index = array_search($callback, $this->processors['pre'], true)) !== false) {
- unset($this->processors['pre'][$index]);
- } elseif (($index = array_search('Dwoo_Processor_'.str_replace('Dwoo_Processor_', '', $callback), $this->processors['pre'], true)) !== false) {
- unset($this->processors['pre'][$index]);
- } else {
- $class = 'Dwoo_Processor_' . str_replace('Dwoo_Processor_', '', $callback);
- foreach ($this->processors['pre'] as $index=>$proc) {
- if (is_array($proc) && ($proc[0] instanceof $class) || (isset($proc['class']) && $proc['class'] == $class)) {
- unset($this->processors['pre'][$index]);
- break;
- }
- }
- }
- }
- /**
- * adds a postprocessor to the compiler, it will be called
- * before the template is compiled
- *
- * @param mixed $callback either a valid callback to the postprocessor or a simple name if the autoload is set to true
- * @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
- */
- public function addPostProcessor($callback, $autoload = false)
- {
- if ($autoload) {
- $name = str_replace('Dwoo_Processor_', '', $callback);
- $class = 'Dwoo_Processor_'.$name;
- if (class_exists($class, false)) {
- $callback = array(new $class($this), 'process');
- } elseif (function_exists($class)) {
- $callback = $class;
- } else {
- $callback = array('autoload'=>true, 'class'=>$class, 'name'=>$name);
- }
- $this->processors['post'][] = $callback;
- } else {
- $this->processors['post'][] = $callback;
- }
- }
- /**
- * removes a postprocessor from the compiler
- *
- * @param mixed $callback either a valid callback to the postprocessor or a simple name if it was autoloaded
- */
- public function removePostProcessor($callback)
- {
- if (($index = array_search($callback, $this->processors['post'], true)) !== false) {
- unset($this->processors['post'][$index]);
- } elseif (($index = array_search('Dwoo_Processor_'.str_replace('Dwoo_Processor_', '', $callback), $this->processors['post'], true)) !== false) {
- unset($this->processors['post'][$index]);
- } else {
- $class = 'Dwoo_Processor_' . str_replace('Dwoo_Processor_', '', $callback);
- foreach ($this->processors['post'] as $index=>$proc) {
- if (is_array($proc) && ($proc[0] instanceof $class) || (isset($proc['class']) && $proc['class'] == $class)) {
- unset($this->processors['post'][$index]);
- break;
- }
- }
- }
- }
- /**
- * internal function to autoload processors at runtime if required
- *
- * @param string $class the class/function name
- * @param string $name the plugin name (without Dwoo_Plugin_ prefix)
- */
- protected function loadProcessor($class, $name)
- {
- if (!class_exists($class, false) && !function_exists($class)) {
- try {
- $this->dwoo->getLoader()->loadPlugin($name);
- } catch (Dwoo_Exception $e) {
- 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');
- }
- }
- if (class_exists($class, false)) {
- return array(new $class($this), 'process');
- }
- if (function_exists($class)) {
- return $class;
- }
- 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"');
- }
- /**
- * adds an used plugin, this is reserved for use by the {template} plugin
- *
- * this is required so that plugin loading bubbles up from loaded
- * template files to the current one
- *
- * @private
- * @param string $name function name
- * @param int $type plugin type (Dwoo_Core::*_PLUGIN)
- */
- public function addUsedPlugin($name, $type)
- {
- $this->usedPlugins[$name] = $type;
- }
- /**
- * returns all the plugins this template uses
- *
- * @private
- * @return array the list of used plugins in the parsed template
- */
- public function getUsedPlugins()
- {
- return $this->usedPlugins;
- }
- /**
- * adds a template plugin, this is reserved for use by the {template} plugin
- *
- * this is required because the template functions are not declared yet
- * during compilation, so we must have a way of validating their argument
- * signature without using the reflection api
- *
- * @private
- * @param string $name function name
- * @param array $params parameter array to help validate the function call
- * @param string $uuid unique id of the function
- * @param string $body function php code
- */
- public function addTemplatePlugin($name, array $params, $uuid, $body = null)
- {
- $this->templatePlugins[$name] = array('params'=> $params, 'body' => $body, 'uuid' => $uuid);
- }
- /**
- * returns all the parsed sub-templates
- *
- * @private
- * @return array the parsed sub-templates
- */
- public function getTemplatePlugins()
- {
- return $this->templatePlugins;
- }
- /**
- * marks a template plugin as being called, which means its source must be included in the compiled template
- *
- * @param string $name function name
- */
- public function useTemplatePlugin($name)
- {
- $this->templatePlugins[$name]['called'] = true;
- }
- /**
- * adds the custom plugins loaded into Dwoo to the compiler so it can load them
- *
- * @see Dwoo_Core::addPlugin
- * @param array $customPlugins an array of custom plugins
- */
- public function setCustomPlugins(array $customPlugins)
- {
- $this->customPlugins = $customPlugins;
- }
- /**
- * sets the security policy object to enforce some php security settings
- *
- * use this if untrusted persons can modify templates,
- * set it on the Dwoo object as it will be passed onto the compiler automatically
- *
- * @param Dwoo_Security_Policy $policy the security policy object
- */
- public function setSecurityPolicy(Dwoo_Security_Policy $policy = null)
- {
- $this->securityPolicy = $policy;
- }
- /**
- * returns the current security policy object or null by default
- *
- * @return Dwoo_Security_Policy|null the security policy object if any
- */
- public function getSecurityPolicy()
- {
- return $this->securityPolicy;
- }
- /**
- * sets the pointer position
- *
- * @param int $position the new pointer position
- * @param bool $isOffset if set to true, the position acts as an offset and not an absolute position
- */
- public function setPointer($position, $isOffset = false)
- {
- if ($isOffset) {
- $this->pointer += $position;
- } else {
- $this->pointer = $position;
- }
- }
- /**
- * returns the current pointer position, only available during compilation of a template
- *
- * @return int
- */
- public function getPointer()
- {
- return $this->pointer;
- }
- /**
- * sets the line number
- *
- * @param int $number the new line number
- * @param bool $isOffset if set to true, the position acts as an offset and not an absolute position
- */
- public function setLine($number, $isOffset = false)
- {
- if ($isOffset) {
- $this->line += $number;
- } else {
- $this->line = $number;
- }
- }
- /**
- * returns the current line number, only available during compilation of a template
- *
- * @return int
- */
- public function getLine()
- {
- return $this->line;
- }
- /**
- * returns the dwoo object that initiated this template compilation, only available during compilation of a template
- *
- * @return Dwoo
- */
- public function getDwoo()
- {
- return $this->dwoo;
- }
- /**
- * overwrites the template that is being compiled
- *
- * @param string $newSource the template source that must replace the current one
- * @param bool $fromPointer if set to true, only the source from the current pointer position is replaced
- * @return string the template or partial template
- */
- public function setTemplateSource($newSource, $fromPointer = false)
- {
- if ($fromPointer === true) {
- $this->templateSource = substr($this->templateSource, 0, $this->pointer) . $newSource;
- } else {
- $this->templateSource = $newSource;
- }
- }
- /**
- * returns the template that is being compiled
- *
- * @param mixed $fromPointer if set to true, only the source from the current pointer
- * position is returned, if a number is given it overrides the current pointer
- * @return string the template or partial template
- */
- public function getTemplateSource($fromPointer = false)
- {
- if ($fromPointer === true) {
- return substr($this->templateSource, $this->pointer);
- } elseif (is_numeric($fromPointer)) {
- return substr($this->templateSource, $fromPointer);
- } else {
- return $this->templateSource;
- }
- }
- /**
- * resets the compilation pointer, effectively restarting the compilation process
- *
- * this is useful if a plugin modifies the template source since it might need to be recompiled
- */
- public function recompile()
- {
- $this->setPointer(0);
- }
- /**
- * compiles the provided string down to php code
- *
- * @param string $tpl the template to compile
- * @return string a compiled php string
- */
- public function compile(Dwoo_Core $dwoo, Dwoo_ITemplate $template)
- {
- // init vars
- $tpl = $template->getSource();
- $ptr = 0;
- $this->dwoo = $dwoo;
- $this->template = $template;
- $this->templateSource =& $tpl;
- $this->pointer =& $ptr;
- while (true) {
- // if pointer is at the beginning, reset everything, that allows a plugin to externally reset the compiler if everything must be reparsed
- if ($ptr===0) {
- // resets variables
- $this->usedPlugins = array();
- $this->data = array();
- $this->scope =& $this->data;
- $this->scopeTree = array();
- $this->stack = array();
- $this->line = 1;
- $this->templatePlugins = array();
- // add top level block
- $compiled = $this->addBlock('topLevelBlock', array(), 0);
- $this->stack[0]['buffer'] = '';
- if ($this->debug) echo 'COMPILER INIT<br />';
- if ($this->debug) echo 'PROCESSING PREPROCESSORS ('.count($this->processors['pre']).')<br>';
- // runs preprocessors
- foreach ($this->processors['pre'] as $preProc) {
- if (is_array($preProc) && isset($preProc['autoload'])) {
- $preProc = $this->loadProcessor($preProc['class'], $preProc['name']);
- }
- if (is_array($preProc) && $preProc[0] instanceof Dwoo_Processor) {
- $tpl = call_user_func($preProc, $tpl);
- } else {
- $tpl = call_user_func($preProc, $this, $tpl);
- }
- }
- unset($preProc);
- // show template source if debug
- if ($this->debug) echo '<pre>'.print_r(htmlentities($tpl), true).'</pre><hr />';
- // strips php tags if required by the security policy
- if ($this->securityPolicy !== null) {
- $search = array('{<\?php.*?\?>}');
- if (ini_get('short_open_tags')) {
- $search = array('{<\?.*?\?>}', '{<%.*?%>}');
- }
- switch($this->securityPolicy->getPhpHandling()) {
- case Dwoo_Security_Policy::PHP_ALLOW:
- break;
- case Dwoo_Security_Policy::PHP_ENCODE:
- $tpl = preg_replace_callback($search, array($this, 'phpTagEncodingHelper'), $tpl);
- break;
- case Dwoo_Security_Policy::PHP_REMOVE:
- $tpl = preg_replace($search, '', $tpl);
- }
- }
- }
- $pos = strpos($tpl, $this->ld, $ptr);
- if ($pos === false) {
- $this->push(substr($tpl, $ptr), 0);
- break;
- } elseif (substr($tpl, $pos-1, 1) === '\\' && substr($tpl, $pos-2, 1) !== '\\') {
- $this->push(substr($tpl, $ptr, $pos-$ptr-1) . $this->ld);
- $ptr = $pos+strlen($this->ld);
- } elseif (preg_match('/^'.$this->ldr . ($this->allowLooseOpenings ? '\s*' : '') . 'literal' . ($this->allowLooseOpenings ? '\s*' : '') . $this->rdr.'/s', substr($tpl, $pos), $litOpen)) {
- if (!preg_match('/'.$this->ldr . ($this->allowLooseOpenings ? '\s*' : '') . '\/literal' . ($this->allowLooseOpenings ? '\s*' : '') . $this->rdr.'/s', $tpl, $litClose, PREG_OFFSET_CAPTURE, $pos)) {
- throw new Dwoo_Compilation_Exception($this, 'The {literal} blocks must be closed explicitly with {/literal}');
- }
- $endpos = $litClose[0][1];
- $this->push(substr($tpl, $ptr, $pos-$ptr) . substr($tpl, $pos + strlen($litOpen[0]), $endpos-$pos-strlen($litOpen[0])));
- $ptr = $endpos+strlen($litClose[0][0]);
- } else {
- if (substr($tpl, $pos-2, 1) === '\\' && substr($tpl, $pos-1, 1) === '\\') {
- $this->push(substr($tpl, $ptr, $pos-$ptr-1));
- $ptr = $pos;
- }
- $this->push(substr($tpl, $ptr, $pos-$ptr));
- $ptr = $pos;
- $pos += strlen($this->ld);
- if ($this->allowLooseOpenings) {
- while (substr($tpl, $pos, 1) === ' ') {
- $pos+=1;
- }
- } else {
- if (substr($tpl, $pos, 1) === ' ' || substr($tpl, $pos, 1) === "\r" || substr($tpl, $pos, 1) === "\n" || substr($tpl, $pos, 1) === "\t") {
- $ptr = $pos;
- $this->push($this->ld);
- continue;
- }
- }
- // check that there is an end tag present
- if (strpos($tpl, $this->rd, $pos) === false) {
- throw new Dwoo_Compilation_Exception($this, 'A template tag was not closed, started with "'.substr($tpl, $ptr, 30).'"');
- }
- $ptr += strlen($this->ld);
- $subptr = $ptr;
- while (true) {
- $parsed = $this->parse($tpl, $subptr, null, false, 'root', $subptr);
- // reload loop if the compiler was reset
- if ($ptr === 0) {
- continue 2;
- }
- $len = $subptr - $ptr;
- $this->push($parsed, substr_count(substr($tpl, $ptr, $len), "\n"));
- $ptr += $len;
- if ($parsed === false) {
- break;
- }
- }
- // adds additional line breaks between php closing and opening tags because the php parser removes those if there is just a single line break
- if (substr($this->curBlock['buffer'], -2) === '?>' && preg_match('{^(([\r\n])([\r\n]?))}', substr($tpl, $ptr, 3), $m)) {
- if ($m[3] === '') {
- $ptr+=1;
- $this->push($m[1].$m[1], 1);
- } else {
- $ptr+=2;
- $this->push($m[1]."\n", 2);
- }
- }
- }
- }
- $compiled .= $this->removeBlock('topLevelBlock');
- if ($this->debug) echo 'PROCESSING POSTPROCESSORS<br>';
- foreach ($this->processors['post'] as $postProc) {
- if (is_array($postProc) && isset($postProc['autoload'])) {
- $postProc = $this->loadProcessor($postProc['class'], $postProc['name']);
- }
- if (is_array($postProc) && $postProc[0] instanceof Dwoo_Processor) {
- $compiled = call_user_func($postProc, $compiled);
- } else {
- $compiled = call_user_func($postProc, $this, $compiled);
- }
- }
- unset($postProc);
- if ($this->debug) echo 'COMPILATION COMPLETE : MEM USAGE : '.memory_get_usage().'<br>';
- $output = "<?php\n/* template head */\n";
- // build plugin preloader
- foreach ($this->usedPlugins as $plugin=>$type) {
- if ($type & Dwoo_Core::CUSTOM_PLUGIN) {
- continue;
- }
- switch($type) {
- case Dwoo_Core::BLOCK_PLUGIN:
- case Dwoo_Core::CLASS_PLUGIN:
- $output .= "if (class_exists('Dwoo_Plugin_$plugin', false)===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
- break;
- case Dwoo_Core::FUNC_PLUGIN:
- $output .= "if (function_exists('Dwoo_Plugin_$plugin')===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
- break;
- case Dwoo_Core::SMARTY_MODIFIER:
- $output .= "if (function_exists('smarty_modifier_$plugin')===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
- break;
- case Dwoo_Core::SMARTY_FUNCTION:
- $output .= "if (function_exists('smarty_function_$plugin')===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
- break;
- case Dwoo_Core::SMARTY_BLOCK:
- $output .= "if (function_exists('smarty_block_$plugin')===false)\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
- break;
- case Dwoo_Core::PROXY_PLUGIN:
- $output .= $this->getDwoo()->getPluginProxy()->getPreloader($plugin);
- break;
- default:
- throw new Dwoo_Compilation_Exception($this, 'Type error for '.$plugin.' with type'.$type);
- }
- }
- foreach ($this->templatePlugins as $function => $attr) {
- if (isset($attr['called']) && $attr['called'] === true && !isset($attr['checked'])) {
- $this->resolveSubTemplateDependencies($function);
- }
- }
- foreach ($this->templatePlugins as $function) {
- if (isset($function['called']) && $function['called'] === true) {
- $output .= $function['body'].PHP_EOL;
- }
- }
- $output .= $compiled."\n?>";
- $output = preg_replace('/(?<!;|\}|\*\/|\n|\{)(\s*'.preg_quote(self::PHP_CLOSE, '/') . preg_quote(self::PHP_OPEN, '/').')/', ";\n", $output);
- $output = str_replace(self::PHP_CLOSE . self::PHP_OPEN, "\n", $output);
- // handle <?xml tag at the beginning
- $output = preg_replace('#(/\* template body \*/ \?>\s*)<\?xml#is', '$1<?php echo \'<?xml\'; ?>', $output);
- if ($this->debug) {
- echo '<hr><pre>';
- $lines = preg_split('{\r\n|\n|<br />}', highlight_string(($output), true));
- array_shift($lines);
- foreach ($lines as $i=>$line) {
- echo ($i+1).'. '.$line."\r\n";
- }
- }
- if ($this->debug) echo '<hr></pre></pre>';
- $this->template = $this->dwoo = null;
- $tpl = null;
- return $output;
- }
- /**
- * checks what sub-templates are used in every sub-template so that we're sure they are all compiled
- *
- * @param string $function the sub-template name
- */
- protected function resolveSubTemplateDependencies($function)
- {
- $body = $this->templatePlugins[$function]['body'];
- foreach ($this->templatePlugins as $func => $attr) {
- if ($func !== $function && !isset($attr['called']) && strpos($body, 'Dwoo_Plugin_'.$func) !== false) {
- $this->templatePlugins[$func]['called'] = true;
- $this->resolveSubTemplateDependencies($func);
- }
- }
- $this->templatePlugins[$function]['checked'] = true;
- }
- /**
- * adds compiled content to the current block
- *
- * @param string $content the content to push
- * @param int $lineCount newlines count in content, optional
- */
- public function push($content, $lineCount = null)
- {
- if ($lineCount === null) {
- $lineCount = substr_count($content, "\n");
- }
- if ($this->curBlock['buffer'] === null && count($this->stack) > 1) {
- // buffer is not initialized yet (the block has just been created)
- $this->stack[count($this->stack)-2]['buffer'] .= (string) $content;
- $this->curBlock['buffer'] = '';
- } else {
- if (!isset($this->curBlock['buffer'])) {
- throw new Dwoo_Compilation_Exception($this, 'The template has been closed too early, you probably have an extra block-closing tag somewhere');
- }
- // append current content to current block's buffer
- $this->curBlock['buffer'] .= (string) $content;
- }
- $this->line += $lineCount;
- }
- /**
- * sets the scope
- *
- * set to null if the scope becomes "unstable" (i.e. too variable or unknown) so that
- * variables are compiled in a more evaluative way than just $this->scope['key']
- *
- * @param mixed $scope a string i.e. "level1.level2" or an array i.e. array("level1", "level2")
- * @param bool $absolute if true, the scope is set from the top level scope and not from the current scope
- * @return array the current scope tree
- */
- public function setScope($scope, $absolute = false)
- {
- $old = $this->scopeTree;
- if ($scope===null) {
- unset($this->scope);
- $this->scope = null;
- }
- if (is_array($scope)===false) {
- $scope = explode('.', $scope);
- }
- if ($absolute===true) {
- $this->scope =& $this->data;
- $this->scopeTree = array();
- }
- while (($bit = array_shift($scope)) !== null) {
- if ($bit === '_parent' || $bit === '_') {
- array_pop($this->scopeTree);
- reset($this->scopeTree);
- $this->scope =& $this->data;
- $cnt = count($this->scopeTree);
- for ($i=0;$i<$cnt;$i++)
- $this->scope =& $this->scope[$this->scopeTree[$i]];
- } elseif ($bit === '_root' || $bit === '__') {
- $this->scope =& $this->data;
- $this->scopeTree = array();
- } elseif (isset($this->scope[$bit])) {
- $this->scope =& $this->scope[$bit];
- $this->scopeTree[] = $bit;
- } else {
- $this->scope[$bit] = array();
- $this->scope =& $this->scope[$bit];
- $this->scopeTree[] = $bit;
- }
- }
- return $old;
- }
- /**
- * adds a block to the top of the block stack
- *
- * @param string $type block type (name)
- * @param array $params the parameters array
- * @param int $paramtype the parameters type (see mapParams), 0, 1 or 2
- * @return string the preProcessing() method's output
- */
- public function addBlock($type, array $params, $paramtype)
- {
- $class = 'Dwoo_Plugin_'.$type;
- if (class_exists($class, false) === false) {
- $this->dwoo->getLoader()->loadPlugin($type);
- }
- $params = $this->mapParams($params, array($class, 'init'), $paramtype);
- $this->stack[] = array('type' => $type, 'params' => $params, 'custom' => false, 'class' => $class, 'buffer' => null);
- $this->curBlock =& $this->stack[count($this->stack)-1];
- return call_user_func(array($class,'preProcessing'), $this, $params, '', '', $type);
- }
- /**
- * adds a custom block to the top of the block stack
- *
- * @param string $type block type (name)
- * @param array $params the parameters array
- * @param int $paramtype the parameters type (see mapParams), 0, 1 or 2
- * @return string the preProcessing() method's output
- */
- public function addCustomBlock($type, array $params, $paramtype)
- {
- $callback = $this->customPlugins[$type]['callback'];
- if (is_array($callback)) {
- $class = is_object($callback[0]) ? get_class($callback[0]) : $callback[0];
- } else {
- $class = $callback;
- }
- $params = $this->mapParams($params, array($class, 'init'), $paramtype);
- $this->stack[] = array('type' => $type, 'params' => $params, 'custom' => true, 'class' => $class, 'buffer' => null);
- $this->curBlock =& $this->stack[count($this->stack)-1];
- return call_user_func(array($class,'preProcessing'), $this, $params, '', '', $type);
- }
- /**
- * injects a block at the top of the plugin stack without calling its preProcessing method
- *
- * used by {else} blocks to re-add themselves after having closed everything up to their parent
- *
- * @param string $type block type (name)
- * @param array $params parameters array
- */
- public function injectBlock($type, array $params)
- {
- $class = 'Dwoo_Plugin_'.$type;
- if (class_exists($class, false) === false) {
- $this->dwoo->getLoader()->loadPlugin($type);
- }
- $this->stack[] = array('type' => $type, 'params' => $params, 'custom' => false, 'class' => $class, 'buffer' => null);
- $this->curBlock =& $this->stack[count($this->stack)-1];
- }
- /**
- * removes the closest-to-top block of the given type and all other
- * blocks encountered while going down the block stack
- *
- * @param string $type block type (name)
- * @return string the output of all postProcessing() method's return values of the closed blocks
- */
- public function removeBlock($type)
- {
- $output = '';
- $pluginType = $this->getPluginType($type);
- if ($pluginType & Dwoo_Core::SMARTY_BLOCK) {
- $type = 'smartyinterface';
- }
- while (true) {
- while ($top = array_pop($this->stack)) {
- if ($top['custom']) {
- $class = $top['class'];
- } else {
- $class = 'Dwoo_Plugin_'.$top['type'];
- }
- if (count($this->stack)) {
- $this->curBlock =& $this->stack[count($this->stack)-1];
- $this->push(call_user_func(array($class, 'postProcessing'), $this, $top['params'], '', '', $top['buffer']), 0);
- } else {
- $null = null;
- $this->curBlock =& $null;
- $output = call_user_func(array($class, 'postProcessing'), $this, $top['params'], '', '', $top['buffer']);
- }
- if ($top['type'] === $type) {
- break 2;
- }
- }
- throw new Dwoo_Compilation_Exception($this, 'Syntax malformation, a block of type "'.$type.'" was closed but was not opened');
- break;
- }
- return $output;
- }
- /**
- * returns a reference to the first block of the given type encountered and
- * optionally closes all blocks until it finds it
- *
- * this is mainly used by {else} plugins to close everything that was opened
- * between their parent and themselves
- *
- * @param string $type the block type (name)
- * @param bool $closeAlong whether to close all blocks encountered while going down the block stack or not
- * @return &array the array is as such: array('type'=>pluginName, 'params'=>parameter array,
- * 'custom'=>bool defining whether it's a custom plugin or not, for internal use)
- */
- public function &findBlock($type, $closeAlong = false)
- {
- if ($closeAlong===true) {
- while ($b = end($this->stack)) {
- if ($b['type']===$type) {
- return $this->stack[key($this->stack)];
- }
- $this->push($this->removeTopBlock(), 0);
- }
- } else {
- end($this->stack);
- while ($b = current($this->stack)) {
- if ($b['type']===$type) {
- return $this->stack[key($this->stack)];
- }
- prev($this->stack);
- }
- }
- throw new Dwoo_Compilation_Exception($this, 'A parent block of type "'.$type.'" is required and can not be found');
- }
- /**
- * returns a reference to the current block array
- *
- * @return &array the array is as such: array('type'=>pluginName, 'params'=>parameter array,
- * 'custom'=>bool defining whether it's a custom plugin or not, for internal use)
- */
- public function &getCurrentBlock()
- {
- return $this->curBlock;
- }
- /**
- * removes the block at the top of the stack and calls its postProcessing() method
- *
- * @return string the postProcessing() method's output
- */
- public function removeTopBlock()
- {
- $o = array_pop($this->stack);
- if ($o === null) {
- throw new Dwoo_Compilation_Exception($this, 'Syntax malformation, a block of unknown type was closed but was not opened.');
- }
- if ($o['custom']) {
- $class = $o['class'];
- } else {
- $class = 'Dwoo_Plugin_'.$o['type'];
- }
- $this->curBlock =& $this->stack[count($this->stack)-1];
- return call_user_func(array($class, 'postProcessing'), $this, $o['params'], '', '', $o['buffer']);
- }
- /**
- * returns the compiled parameters (for example a variable's compiled parameter will be "$this->scope['key']") out of the given parameter array
- *
- * @param array $params parameter array
- * @return array filtered parameters
- */
- public function getCompiledParams(array $params)
- {
- foreach ($params as $k=>$p) {
- if (is_array($p)) {
- $params[$k] = $p[0];
- }
- }
- return $params;
- }
- /**
- * returns the real parameters (for example a variable's real parameter will be its key, etc) out of the given parameter array
- *
- * @param array $params parameter array
- * @return array filtered parameters
- */
- public function getRealParams(array $params)
- {
- foreach ($params as $k=>$p) {
- if (is_array($p)) {
- $params[$k] = $p[1];
- }
- }
- return $params;
- }
- /**
- * returns the token of each parameter out of the given parameter array
- *
- * @param array $params parameter array
- * @return array tokens
- */
- public function getParamTokens(array $params)
- {
- foreach ($params as $k=>$p) {
- if (is_array($p)) {
- $params[$k] = isset($p[2]) ? $p[2] : 0;
- }
- }
- return $params;
- }
- /**
- * entry point of the parser, it redirects calls to other parse* functions
- *
- * @param string $in the string within which we must parse something
- * @param int $from the starting offset of the parsed area
- * @param int $to the ending offset of the parsed area
- * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
- * @param string $curBlock the current parser-block being processed
- * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
- * @return string parsed values
- */
- protected function parse($in, $from, $to, $parsingParams = false, $curBlock='', &$pointer = null)
- {
- if ($to === null) {
- $to = strlen($in);
- }
- $first = substr($in, $from, 1);
- if ($first === false) {
- throw new Dwoo_Compilation_Exception($this, 'Unexpected EOF, a template tag was not closed');
- }
- while ($first===" " || $first==="\n" || $first==="\t" || $first==="\r") {
- if ($curBlock === 'root' && substr($in, $from, strlen($this->rd)) === $this->rd) {
- // end template tag
- $pointer += strlen($this->rd);
- if ($this->debug) echo 'TEMPLATE PARSING ENDED<br />';
- return false;
- }
- $from++;
- if ($pointer !== null) {
- $pointer++;
- }
- if ($from >= $to) {
- if (is_array($parsingParams)) {
- return $parsingParams;
- } else {
- return '';
- }
- }
- $first = $in[$from];
- }
- $substr = substr($in, $from, $to-$from);
- if ($this->debug) echo '<br />PARSE CALL : PARSING "<b>'.htmlentities(substr($in, $from, min($to-$from, 50))).(($to-$from) > 50 ? '...':'').'</b>" @ '.$from.':'.$to.' in '.$curBlock.' : pointer='.$pointer.'<br/>';
- $parsed = "";
- if ($curBlock === 'root' && $first === '*') {
- $src = $this->getTemplateSource();
- $startpos = $this->getPointer() - strlen($this->ld);
- if (substr($src, $startpos, strlen($this->ld)) === $this->ld) {
- if ($startpos > 0) {
- do {
- $char = substr($src, --$startpos, 1);
- if ($char == "\n") {
- $startpos++;
- $whitespaceStart = true;
- break;
- }
- } while ($startpos > 0 && ($char == ' ' || $char == "\t"));
- }
- if (!isset($whitespaceStart)) {
- $startpos = $this->getPointer();
- } else {
- $pointer -= $this->getPointer() - $startpos;
- }
- if ($this->allowNestedComments && strpos($src, $this->ld.'*', $this->getPointer()) !== false) {
- $comOpen = $this->ld.'*';
- $comClose = '*'.$this->rd;
- $level = 1;
- $start = $startpos;
- $ptr = $this->getPointer() + '*';
- while ($level > 0 && $ptr < strlen($src)) {
- $open = strpos($src, $comOpen, $ptr);
- $close = strpos($src, $comClose, $ptr);
- if ($open !== false && $close !== false) {
- if ($open < $close) {
- $ptr = $open + strlen($comOpen);
- $level++;
- } else {
- $ptr = $close + strlen($comClose);
- $level--;
- }
- } elseif ($open !== false) {
- $ptr = $open + strlen($comOpen);
- $level++;
- } elseif ($close !== false) {
- $ptr = $close + strlen($comClose);
- $level--;
- } else {
- $ptr = strlen($src);
- }
- }
- $endpos = $ptr - strlen('*'.$this->rd);
- } else {
- $endpos = strpos($src, '*'.$this->rd, $startpos);
- if ($endpos == false) {
- throw new Dwoo_Compilation_Exception($this, 'Un-ended comment');
- }
- }
- $pointer += $endpos - $startpos + strlen('*'.$this->rd);
- if (isset($whitespaceStart) && preg_match('#^[\t ]*\r?\n#', substr($src, $endpos+strlen('*'.$this->rd)), $m)) {
- $pointer += strlen($m[0]);
- $this->curBlock['buffer'] = substr($this->curBlock['buffer'], 0, strlen($this->curBlock['buffer']) - ($this->getPointer() - $startpos - strlen($this->ld)));
- }
- return false;
- }
- }
- if ($first==='$') {
- // var
- $out = $this->parseVar($in, $from, $to, $parsingParams, $curBlock, $pointer);
- $parsed = 'var';
- } elseif ($first==='%' && preg_match('#^%[a-z_]#i', $substr)) {
- // const
- $out = $this->parseConst($in, $from, $to, $parsingParams, $curBlock, $pointer);
- } elseif (($first==='"' || $first==="'") && !(is_array($parsingParams) && preg_match('#^([\'"])[a-z0-9_]+\1\s*=>?(?:\s+|[^=])#i', $substr))) {
- // string
- $out = $this->parseString($in, $from, $to, $parsingParams, $curBlock, $pointer);
- } 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)) {
- // func
- $out = $this->parseFunction($in, $from, $to, $parsingParams, $curBlock, $pointer);
- $parsed = 'func';
- } elseif ($first === ';') {
- // instruction end
- if ($this->debug) echo 'END OF INSTRUCTION<br />';
- if ($pointer !== null) {
- $pointer++;
- }
- return $this->parse($in, $from+1, $to, false, 'root', $pointer);
- } elseif ($curBlock === 'root' && preg_match('#^/([a-z_][a-z0-9_]*)?#i', $substr, $match)) {
- // close block
- if (!empty($match[1]) && $match[1] == 'else') {
- throw new Dwoo_Compilation_Exception($this, 'Else blocks must not be closed explicitly, they are automatically closed when their parent block is closed');
- }
- if (!empty($match[1]) && $match[1] == 'elseif') {
- throw new Dwoo_Compilation_Exception($this, 'Elseif blocks must not be closed explicitly, they are automatically closed when their parent block is closed or a new else/elseif block is declared after them');
- }
- if ($pointer !== null) {
- $pointer += strlen($match[0]);
- }
- if (empty($match[1])) {
- if ($this->curBlock['type'] == 'else' || $this->curBlock['type'] == 'elseif') {
- $pointer -= strlen($match[0]);
- }
- if ($this->debug) echo 'TOP BLOCK CLOSED<br />';
- return $this->removeTopBlock();
- } else {
- if ($this->debug) echo 'BLOCK OF TYPE '.$match[1].' CLOSED<br />';
- return $this->removeBlock($match[1]);
- }
- } elseif ($curBlock === 'root' && substr($substr, 0, strlen($this->rd)) === $this->rd) {
- // end template tag
- if ($this->debug) echo 'TAG PARSING ENDED<br />';
- $pointer += strlen($this->rd);
- return false;
- } elseif (is_array($parsingParams) && preg_match('#^(([\'"]?)[a-z0-9_]+\2\s*='.($curBlock === 'array' ? '>?':'').')(?:\s+|[^=]).*#i', $substr, $match)) {
- // named parameter
- if ($this->debug) echo 'NAMED PARAM FOUND<br />';
- $len = strlen($match[1]);
- while (substr($in, $from+$len, 1)===' ') {
- $len++;
- }
- if ($pointer !== null) {
- $pointer += $len;
- }
- $output = array(trim($match[1], " \t\r\n=>'\""), $this->parse($in, $from+$len, $to, false, 'namedparam', $pointer));
- $parsingParams[] = $output;
- return $parsingParams;
- } elseif (preg_match('#^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*::\$[a-z0-9_]+)#i', $substr, $match)) {
- // static member access
- $parsed = 'var';
- if (is_array($parsingParams)) {
- $parsingParams[] = array($match[1], $match[1]);
- $out = $parsingParams;
- } else {
- $out = $match[1];
- }
- $pointer += strlen($match[1]);
- } elseif ($substr!=='' && (is_array($parsingParams) || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'expression')) {
- // unquoted string, bool or number
- $out = $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
- } else {
- // parse error
- throw new Dwoo_Compilation_Exception($this, 'Parse error in "'.substr($in, $from, $to-$from).'"');
- }
- if (empty($out)) {
- return '';
- }
- $substr = substr($in, $pointer, $to-$pointer);
- // var parsed, check if any var-extension applies
- if ($parsed==='var') {
- if (preg_match('#^\s*([/%+*-])\s*([a-z0-9]|\$)#i', $substr, $match)) {
- if($this->debug) echo 'PARSING POST-VAR EXPRESSION '.$substr.'<br />';
- // parse expressions
- $pointer += strlen($match[0]) - 1;
- if (is_array($parsingParams)) {
- if ($match[2] == '$') {
- $expr = $this->parseVar($in, $pointer, $to, array(), $curBlock, $pointer);
- } else {
- $expr = $this->parse($in, $pointer, $to, array(), 'expression', $pointer);
- }
- $out[count($out)-1][0] .= $match[1] . $expr[0][0];
- $out[count($out)-1][1] .= $match[1] . $expr[0][1];
- } else {
- if ($match[2] == '$') {
- $expr = $this->parseVar($in, $pointer, $to, false, $curBlock, $pointer);
- } else {
- $expr = $this->parse($in, $pointer, $to, false, 'expression', $pointer);
- }
- if (is_array($out) && is_array($expr)) {
- $out[0] .= $match[1] . $expr[0];
- $out[1] .= $match[1] . $expr[1];
- } elseif (is_array($out)) {
- $out[0] .= $match[1] . $expr;
- $out[1] .= $match[1] . $expr;
- } elseif (is_array($expr)) {
- $out .= $match[1] . $expr[0];
- } else {
- $out .= $match[1] . $expr;
- }
- }
- } else if ($curBlock === 'root' && preg_match('#^(\s*(?:[+/*%-.]=|=|\+\+|--)\s*)(.*)#s', $substr, $match)) {
- if($this->debug) echo 'PARSING POST-VAR ASSIGNMENT '.$substr.'<br />';
- // parse assignment
- $value = $match[2];
- $operator = trim($match[1]);
- if (substr($value, 0, 1) == '=') {
- throw new Dwoo_Compilation_Exception($this, 'Unexpected "=" in <em>'.$substr.'</em>');
- }
- if ($pointer !== null) {
- $pointer += strlen($match[1]);
- }
- if ($operator !== '++' && $operator !== '--') {
- $parts = array();
- $ptr = 0;
- $parts = $this->parse($value, 0, strlen($value), $parts, 'condition', $ptr);
- $pointer += $ptr;
- // load if plugin
- try {
- $this->getPluginType('if');
- } catch (Dwoo_Exception $e) {
- throw new Dwoo_Compilation_Exception($this, 'Assignments require the "if" plugin to be accessible');
- }
- $parts = $this->mapParams($parts, array('Dwoo_Plugin_if', 'init'), 1);
- $tokens = $this->getParamTokens($parts);
- $parts = $this->getCompiledParams($parts);
- $value = Dwoo_Plugin_if::replaceKeywords($parts['*'], $tokens['*'], $this);
- $echo = '';
- } else {
- $value = array();
- $echo = 'echo ';
- }
- if ($this->autoEscape) {
- $out = preg_replace('#\(is_string\(\$tmp=(.+?)\) \? htmlspecialchars\(\$tmp, ENT_QUOTES, \$this->charset\) : \$tmp\)#', '$1', $out);
- }
- $out = Dwoo_Compiler::PHP_OPEN. $echo . $out . $operator . implode(' ', $value) . Dwoo_Compiler::PHP_CLOSE;
- } else if ($curBlock === 'array' && is_array($parsingParams) && preg_match('#^(\s*=>?\s*)#', $substr, $match)) {
- // parse namedparam with var as name (only for array)
- if ($this->debug) echo 'VARIABLE NAMED PARAM (FOR ARRAY) FOUND<br />';
- $len = strlen($match[1]);
- $var = $out[count($out)-1];
- $pointer += $len;
- $output = array($var[0], $this->parse($substr, $len, null, false, 'namedparam', $pointer));
- $parsingParams[] = $output;
- return $parsingParams;
- }
- }
- if ($curBlock !== 'modifier' && ($parsed === 'func' || $parsed === 'var') && preg_match('#^\|@?[a-z0-9_]+(:.*)?#i', $substr, $match)) {
- // parse modifier on funcs or vars
- $srcPointer = $pointer;
- if (is_array($parsingParams)) {
- $tmp = $this->replaceModifiers(array(null, null, $out[count($out)-1][0], $match[0]), 'var', $pointer);
- $out[count($out)-1][0] = $tmp;
- $out[count($out)-1][1] .= substr($substr, $srcPointer, $srcPointer - $pointer);
- } else {
- $out = $this->replaceModifiers(array(null, null, $out, $match[0]), 'var', $pointer);
- }
- }
- // func parsed, check if any func-extension applies
- if ($parsed==='func' && preg_match('#^->[a-z0-9_]+(\s*\(.+|->[a-z_].*)?#is', $substr, $match)) {
- // parse method call or property read
- $ptr = 0;
- if (is_array($parsingParams)) {
- $output = $this->parseMethodCall($out[count($out)-1][1], $match[0], $curBlock, $ptr);
- $out[count($out)-1][0] = $output;
- $out[count($out)-1][1] .= substr($match[0], 0, $ptr);
- } else {
- $out = $this->parseMethodCall($out, $match[0], $curBlock, $ptr);
- }
- $pointer += $ptr;
- }
- if ($curBlock === 'root' && substr($out, 0, strlen(self::PHP_OPEN)) !== self::PHP_OPEN) {
- return self::PHP_OPEN .'echo '.$out.';'. self::PHP_CLOSE;
- } else {
- return $out;
- }
- }
- /**
- * parses a function call
- *
- * @param string $in the string within which we must parse something
- * @param int $from the starting offset of the parsed area
- * @param int $to the ending offset of the parsed area
- * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
- * @param string $curBlock the current parser-block being processed
- * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
- * @return string parsed values
- */
- protected function parseFunction($in, $from, $to, $parsingParams = false, $curBlock='', &$pointer = null)
- {
- $cmdstr = substr($in, $from, $to-$from);
- preg_match('/^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*(?:::[a-z_][a-z0-9_]*)?)(\s*'.$this->rdr.'|\s*;)?/i', $cmdstr, $match);
- if (empty($match[1])) {
- throw new Dwoo_Compilation_Exception($this, 'Parse error, invalid function name : '.substr($cmdstr, 0, 15));
- }
- $func = $match[1];
- if (!empty($match[2])) {
- $cmdstr = $match[1];
- }
- if ($this->debug) echo 'FUNC FOUND ('.$func.')<br />';
- $paramsep = '';
- if (is_array($parsingParams) || $curBlock != 'root') {
- $paramspos = strpos($cmdstr, '(');
- $paramsep = ')';
- } elseif(preg_match_all('#[a-z0-9_]+(\s*\(|\s+[^(])#i', $cmdstr, $match, PREG_OFFSET_CAPTURE)) {
- $paramspos = $match[1][0][1];
- $paramsep = substr($match[1][0][0], -1) === '(' ? ')':'';
- if($paramsep === ')') {
- $paramspos += strlen($match[1][0][0]) - 1;
- if(substr($cmdstr, 0, 2) === 'if' || substr($cmdstr, 0, 6) === 'elseif') {
- $paramsep = '';
- if(strlen($match[1][0][0]) > 1) {
- $paramspos--;
- }
- }
- }
- } else {
- $paramspos = false;
- }
- $state = 0;
- if ($paramspos === false) {
- $params = array();
- if ($curBlock !== 'root') {
- return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
- }
- } else {
- if ($curBlock === 'condition') {
- // load if plugin
- $this->getPluginType('if');
- if (Dwoo_Plugin_if::replaceKeywords(array($func), array(self::T_UNQUOTED_STRING), $this) !== array($func)) {
- return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
- }
- }
- $whitespace = strlen(substr($cmdstr, strlen($func), $paramspos-strlen($func)));
- $paramstr = substr($cmdstr, $paramspos+1);
- if (substr($paramstr, -1, 1) === $paramsep) {
- $paramstr = substr($paramstr, 0, -1);
- }
- if (strlen($paramstr)===0) {
- $params = array();
- $paramstr = '';
- } else {
- $ptr = 0;
- $params = array();
- if ($func === 'empty') {
- $params = $this->parseVar($paramstr, $ptr, strlen($paramstr), $params, 'root', $ptr);
- } else {
- while ($ptr < strlen($paramstr)) {
- while (true) {
- if ($ptr >= strlen($paramstr)) {
- break 2;
- }
- if ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === ')') {
- if ($this->debug) echo 'PARAM PARSING ENDED, ")" FOUND, POINTER AT '.$ptr.'<br/>';
- break 2;
- } elseif ($paramstr[$ptr] === ';') {
- $ptr++;
- if ($this->debug) echo 'PARAM PARSING ENDED, ";" FOUND, POINTER AT '.$ptr.'<br/>';
- break 2;
- } elseif ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === '/') {
- if ($this->debug) echo 'PARAM PARSING ENDED, "/" FOUND, POINTER AT '.$ptr.'<br/>';
- break 2;
- } elseif (substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) {
- if ($this->debug) echo 'PARAM PARSING ENDED, RIGHT DELIMITER FOUND, POINTER AT '.$ptr.'<br/>';
- break 2;
- }
- if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === ',' || $paramstr[$ptr] === "\r" || $paramstr[$ptr] === "\n" || $paramstr[$ptr] === "\t") {
- $ptr++;
- } else {
- break;
- }
- }
- if ($this->debug) echo 'FUNC START PARAM PARSING WITH POINTER AT '.$ptr.'<br/>';
- if ($func === 'if' || $func === 'elseif' || $func === 'tif') {
- $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'condition', $ptr);
- } elseif ($func === 'array') {
- $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'array', $ptr);
- } else {
- $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'function', $ptr);
- }
- if ($this->debug) echo 'PARAM PARSED, POINTER AT '.$ptr.' ('.substr($paramstr, $ptr-1, 3).')<br/>';
- }
- }
- $paramstr = substr($paramstr, 0, $ptr);
- $state = 0;
- foreach ($params as $k=>$p) {
- if (is_array($p) && is_array($p[1])) {
- $state |= 2;
- } else {
- if (($state & 2) && preg_match('#^(["\'])(.+?)\1$#', $p[0], $m) && $func !== 'array') {
- $params[$k] = array($m[2], array('true', 'true'));
- } else {
- if ($state & 2 && $func !== 'array') {
- throw new Dwoo_Compilation_Exception($this, 'You can not use an unnamed parameter after a named one');
- }
- $state |= 1;
- }
- }
- }
- }
- }
- if ($pointer !== null) {
- $pointer += (isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func) + (isset($whitespace) ? $whitespace : 0);
- if ($this->debug) echo 'FUNC ADDS '.((isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func)).' TO POINTER<br/>';
- }
- if ($curBlock === 'method' || $func === 'do' || strstr($func, '::') !== false) {
- // handle static method calls with security policy
- if (strstr($func, '::') !== false && $this->securityPolicy !== null && $this->securityPolicy->isMethodAllowed(explode('::', strtolower($func))) !== true) {
- throw new Dwoo_Security_Exception('Call to a disallowed php function : '.$func);
- }
- $pluginType = Dwoo::NATIVE_PLUGIN;
- } else {
- $pluginType = $this->getPluginType($func);
- }
- // blocks
- if ($pluginType & Dwoo_Core::BLOCK_PLUGIN) {
- if ($curBlock !== 'root' || is_array($parsingParams)) {
- throw new Dwoo_Compilation_Exception($this, 'Block plugins can not be used as other plugin\'s arguments');
- }
- if ($pluginType & Dwoo_Core::CUSTOM_PLUGIN) {
- return $this->addCustomBlock($func, $params, $state);
- } else {
- return $this->addBlock($func, $params, $state);
- }
- } elseif ($pluginType & Dwoo_Core::SMARTY_BLOCK) {
- if ($curBlock !== 'root' || is_array($parsingParams)) {
- throw new Dwoo_Compilation_Exception($this, 'Block plugins can not be used as other plugin\'s arguments');
- }
- if ($state & 2) {
- array_unshift($params, array('__functype', array($pluginType, $pluginType)));
- array_unshift($params, array('__funcname', array($func, $func)));
- } else {
- array_unshift($params, array($pluginType, $pluginType));
- array_unshift($params, array($func, $func));
- }
- return $this->addBlock('smartyinterface', $params, $state);
- }
- // funcs
- if ($pluginType & Dwoo_Core::NATIVE_PLUGIN || $pluginType & Dwoo_Core::SMARTY_FUNCTION || $pluginType & Dwoo_Core::SMARTY_BLOCK) {
- $params = $this->mapParams($params, null, $state);
- } elseif ($pluginType & Dwoo_Core::CLASS_PLUGIN) {
- if ($pluginType & Dwoo_Core::CUSTOM_PLUGIN) {
- $params = $this->mapParams($params, array($this->customPlugins[$func]['class'], $this->customPlugins[$func]['function']), $state);
- } else {
- $params = $this->mapParams($params, array('Dwoo_Plugin_'.$func, ($pluginType & Dwoo_Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'), $state);
- }
- } elseif ($pluginType & Dwoo_Core::FUNC_PLUGIN) {
- if ($pluginType & Dwoo_Core::CUSTOM_PLUGIN) {
- $params = $this->mapParams($params, $this->customPlugins[$func]['callback'], $state);
- } else {
- $params = $this->mapParams($params, 'Dwoo_Plugin_'.$func.(($pluginType & Dwoo_Core::COMPILABLE_PLUGIN) ? '_compile' : ''), $state);
- }
- } elseif ($pluginType & Dwoo_Core::SMARTY_MODIFIER) {
- $output = 'smarty_modifier_'.$func.'('.implode(', ', $params).')';
- } elseif ($pluginType & Dwoo_Core::PROXY_PLUGIN) {
- $params = $this->mapParams($params, $this->getDwoo()->getPluginProxy()->getCallback($func), $state);
- } elseif ($pluginType & Dwoo_Core::TEMPLATE_PLUGIN) {
- // transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values))
- $map = array();
- foreach ($this->templatePlugins[$func]['params'] as $param=>$defValue) {
- if ($param == 'rest') {
- $param = '*';
- }
- $hasDefault = $defValue !== null;
- if ($defValue === 'null') {
- $defValue = null;
- } elseif ($defValue === 'false') {
- $defValue = false;
- } elseif ($defValue === 'true') {
- $defValue = true;
- } elseif (preg_match('#^([\'"]).*?\1$#', $defValue)) {
- $defValue = substr($defValue, 1, -1);
- }
- $map[] = array($param, $hasDefault, $defValue);
- }
- $params = $this->mapParams($params, null, $state, $map);
- }
- // only keep php-syntax-safe values for non-block plugins
- $tokens = array();
- foreach ($params as $k => $p) {
- $tokens[$k] = isset($p[2]) ? $p[2] : 0;
- $params[$k] = $p[0];
- }
- if ($pluginType & Dwoo_Core::NATIVE_PLUGIN) {
- if ($func === 'do') {
- if (isset($params['*'])) {
- $output = implode(';', $params['*']).';';
- } else {
- $output = '';
- }
- if (is_array($parsingParams) || $curBlock !== 'root') {
- throw new Dwoo_Compilation_Exception($this, 'Do can not be used inside another function or block');
- } else {
- return self::PHP_OPEN.$output.self::PHP_CLOSE;
- }
- } else {
- if (isset($params['*'])) {
- $output = $func.'('.implode(', ', $params['*']).')';
- } else {
- $output = $func.'()';
- }
- }
- } elseif ($pluginType & Dwoo_Core::FUNC_PLUGIN) {
- if ($pluginType & Dwoo_Core::COMPILABLE_PLUGIN) {
- if ($pluginType & Dwoo_Core::CUSTOM_PLUGIN) {
- $funcCompiler = $this->customPlugins[$func]['callback'];
- } else {
- $funcCompiler = 'Dwoo_Plugin_'.$func.'_compile';
- }
- array_unshift($params, $this);
- if ($func === 'tif') {
- $params[] = $tokens;
- }
- $output = call_user_func_array($funcCompiler, $params);
- } else {
- array_unshift($params, '$this');
- $params = self::implode_r($params);
- if ($pluginType & Dwoo_Core::CUSTOM_PLUGIN) {
- $callback = $this->customPlugins[$func]['callback'];
- $output = 'call_user_func(\''.$callback.'\', '.$params.')';
- } else {
- $output = 'Dwoo_Plugin_'.$func.'('.$params.')';
- }
- }
- } elseif ($pluginType & Dwoo_Core::CLASS_PLUGIN) {
- if ($pluginType & Dwoo_Core::COMPILABLE_PLUGIN) {
- if ($pluginType & Dwoo_Core::CUSTOM_PLUGIN) {
- $callback = $this->customPlugins[$func]['callback'];
- if (!is_array($callback)) {
- if (!method_exists($callback, 'compile')) {
- 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');
- }
- if (($ref = new ReflectionMethod($callback, 'compile')) && $ref->isStatic()) {
- $funcCompiler = array($callback, 'compile');
- } else {
- $funcCompiler = array(new $callback, 'compile');
- }
- } else {
- $funcCompiler = $callback;
- }
- } else {
- $funcCompiler = array('Dwoo_Plugin_'.$func, 'compile');
- array_unshift($params, $this);
- }
- $output = call_user_func_array($funcCompiler, $params);
- } else {
- $params = self::implode_r($params);
- if ($pluginType & Dwoo_Core::CUSTOM_PLUGIN) {
- $callback = $this->customPlugins[$func]['callback'];
- if (!is_array($callback)) {
- if (!method_exists($callback, 'process')) {
- 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');
- }
- if (($ref = new ReflectionMethod($callback, 'process')) && $ref->isStatic()) {
- $output = 'call_user_func(array(\''.$callback.'\', \'process\'), '.$params.')';
- } else {
- $output = 'call_user_func(array($this->getObjectPlugin(\''.$callback.'\'), \'process\'), '.$params.')';
- }
- } elseif (is_object($callback[0])) {
- $output = 'call_user_func(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), '.$params.')';
- } elseif (($ref = new ReflectionMethod($callback[0], $callback[1])) && $ref->isStatic()) {
- $output = 'call_user_func(array(\''.$callback[0].'\', \''.$callback[1].'\'), '.$params.')';
- } else {
- $output = 'call_user_func(array($this->getObjectPlugin(\''.$callback[0].'\'), \''.$callback[1].'\'), '.$params.')';
- }
- if (empty($params)) {
- $output = substr($output, 0, -3).')';
- }
- } else {
- $output = '$this->classCall(\''.$func.'\', array('.$params.'))';
- }
- }
- } elseif ($pluginType & Dwoo_Core::PROXY_PLUGIN) {
- $output = call_user_func(array($this->dwoo->getPluginProxy(), 'getCode'), $func, $params);
- } elseif ($pluginType & Dwoo_Core::SMARTY_FUNCTION) {
- if (isset($params['*'])) {
- $params = self::implode_r($params['*'], true);
- } else {
- $params = '';
- }
- if ($pluginType & Dwoo_Core::CUSTOM_PLUGIN) {
- $callback = $this->customPlugins[$func]['callback'];
- if (is_array($callback)) {
- if (is_object($callback[0])) {
- $output = 'call_user_func_array(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), array(array('.$params.'), $this))';
- } else {
- $output = 'call_user_func_array(array(\''.$callback[0].'\', \''.$callback[1].'\'), array(array('.$params.'), $this))';
- }
- } else {
- $output = $callback.'(array('.$params.'), $this)';
- }
- } else {
- $output = 'smarty_function_'.$func.'(array('.$params.'), $this)';
- }
- } elseif ($pluginType & Dwoo_Core::TEMPLATE_PLUGIN) {
- array_unshift($params, '$this');
- $params = self::implode_r($params);
- $output = 'Dwoo_Plugin_'.$func.'_'.$this->templatePlugins[$func]['uuid'].'('.$params.')';
- $this->templatePlugins[$func]['called'] = true;
- }
- if (is_array($parsingParams)) {
- $parsingParams[] = array($output, $output);
- return $parsingParams;
- } elseif ($curBlock === 'namedparam') {
- return array($output, $output);
- } else {
- return $output;
- }
- }
- /**
- * parses a string
- *
- * @param string $in the string within which we must parse something
- * @param int $from the starting offset of the parsed area
- * @param int $to the ending offset of the parsed area
- * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
- * @param string $curBlock the current parser-block being processed
- * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
- * @return string parsed values
- */
- protected function parseString($in, $from, $to, $parsingParams = false, $curBlock='', &$pointer = null)
- {
- $substr = substr($in, $from, $to-$from);
- $first = $substr[0];
- if ($this->debug) echo 'STRING FOUND (in '.htmlentities(substr($in, $from, min($to-$from, 50))).(($to-$from) > 50 ? '...':'').')<br />';
- $strend = false;
- $o = $from+1;
- while ($strend === false) {
- $strend = strpos($in, $first, $o);
- if ($strend === false) {
- throw new Dwoo_Compilation_Exception($this, 'Unfinished string, started with '.substr($in, $from, $to-$from));
- }
- if (substr($in, $strend-1, 1) === '\\') {
- $o = $strend+1;
- $strend = false;
- }
- }
- if ($this->debug) echo 'STRING DELIMITED: '.substr($in, $from, $strend+1-$from).'<br/>';
- $srcOutput = substr($in, $from, $strend+1-$from);
- if ($pointer !== null) {
- $pointer += strlen($srcOutput);
- }
- $output = $this->replaceStringVars($srcOutput, $first);
- // handle modifiers
- if ($curBlock !== 'modifier' && preg_match('#^((?:\|(?:@?[a-z0-9_]+(?::.*)*))+)#i', substr($substr, $strend+1-$from), $match)) {
- $modstr = $match[1];
- if ($curBlock === 'root' && substr($modstr, -1) === '}') {
- $modstr = substr($modstr, 0, -1);
- }
- $modstr = str_replace('\\'.$first, $first, $modstr);
- $ptr = 0;
- $output = $this->replaceModifiers(array(null, null, $output, $modstr), 'string', $ptr);
- $strend += $ptr;
- if ($pointer !== null) {
- $pointer += $ptr;
- }
- $srcOutput .= substr($substr, $strend+1-$from, $ptr);
- }
- if (is_array($parsingParams)) {
- $parsingParams[] = array($output, substr($srcOutput, 1, -1));
- return $parsingParams;
- } elseif ($curBlock === 'namedparam') {
- return array($output, substr($srcOutput, 1, -1));
- } else {
- return $output;
- }
- }
- /**
- * parses a constant
- *
- * @param string $in the string within which we must parse something
- * @param int $from the starting offset of the parsed area
- * @param int $to the ending offset of the parsed area
- * @param mixed $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by default
- * @param string $curBlock the current parser-block being processed
- * @param mixed $pointer a reference to a pointer that will be increased by the amount of characters parsed, or null by default
- * @return string parsed values
- */
- protected function parseConst($in, $from, $to, $parsingParams = false, $curBlock='', &$pointer = null)
- {
- $substr = substr($in, $from, $to-$from);
- if ($this->debug) {
- echo 'CONST FOUND : '.$substr.'<br />';
- }
- if (!preg_match('#^%([a-z0-9_:]+)#i', $substr, $m)) {
- throw new Dwoo_Compilation_Exception($this, 'Invalid constant');
- }
- if ($pointer !== null) {
- $pointer += strlen($m[0]);
- }
- $output = $this->parseConstKey($m[1], $curBlock);
- if (is_array($parsingParams)) {
- $parsingParams[] = array($output, $m[1]);
- return $parsingParams;
- } elseif ($curBlock === 'namedparam') {
-