diff --git a/LICENSE b/LICENSE
index a8c1b79..bb2cfb2 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,29 +1,29 @@
-BSD 3-Clause License
+BSD 4 License
-Copyright (c) 2017, alllinux
+Copyright (c) 2016/2017, Stephan Kasdorf
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by the alllinux.de.
+4. Neither the name of the alllinux.de nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
-* Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-* Neither the name of the copyright holder nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THIS SOFTWARE IS PROVIDED BY Stephan Kasdorf ''AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL Stephan Kasdorf BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 1f42c12..1c123a3 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,46 @@
# Nibiru
+#####Version 0.1 beta
+## Introduction
-Nibiru is a rapid prototyping framework written in PHP and based on the MVC design pattern. Now one may say that writing another framework is not cool because there are so many, such as Symphony, ZendFramework ( where as I have prefered the first version of that framework ), Laravel, etc.. But let's be honest they are complete overkill for smaller projects and are not fit to quick start your "own" development.
+
Nibiru is a rapid prototyping framework written in PHP and based on the MVC design pattern. Now one may say that writing
+another framework is not cool because there are so many, such as Symphony, ZendFramework ( where as I have prefered the
+first version of that framework ), Laravel, etc.. But let's be honest they are complete overkill for smaller projects and
+are not fit to quick start your "own" development.
-That is why I have started my own little framework and am happy to provide the first version of Nibiru, in the version 0.1, there is still a lot of work to be done in refactoring the core features for the View, Controller and the main Template Engine Implementation.
+
That is why I have started my own little framework and am happy to provide the first version of Nibiru, in the version 0.1,
+there is still a lot of work to be done in refactoring the core features for the View, Controller and the main Template
+Engine Implementation.
-But for now the start is done. And always remember, have fun!
+
+
Currently supported features
+
+
Controller, Model, View ( already tested )
+
Smarty template engine ( already tested )
+
Dwoo template engine ( untested )
+
Twig template engine ( untested )
+
PDO adapter to the MySQL database
+
+
read datasets from a complete table
+
read datasets by selection from a table
+
write datasets by array into a table
+
+
Bootstrap template of a dashboard
+
Example page, on how to setup a View and Controller
+
Example page, on how to setup a navigation with a json file
+
Debugbar access through the configuration, sould be set to true in order to use it
+
+
In progress for the next version
+
+
framework documentation
+
class documentation
+
soap interface to a given SOAP server
+
Dwoo tempalte eninge tests
+
Twig tempalte eninge tests
+
Improved routing
+
+
+
+
+
The start is done, have success with PHP prototyping, and always remember to have fun!
";
+ }
+ elseif(is_string($value))
+ {
+ echo ' ' . $value;
+ }
+ die("DEBUG END ----->");
+
+ }
+}
\ No newline at end of file
diff --git a/core/c/settings.php b/core/c/settings.php
new file mode 100644
index 0000000..8e8848e
--- /dev/null
+++ b/core/c/settings.php
@@ -0,0 +1,94 @@
+getConfig()[Engine::T_ENGINE][Engine::T_ENGINE_NAME])
+ {
+ case Engine::T_ENGINE_SMARTY:
+ self::$engine = new \Smarty();
+ self::$engine->setTemplateDir(Config::getInstance()->getConfig()[Engine::T_ENGINE]["templates"]);
+ self::$engine->setCompileDir(Config::getInstance()->getConfig()[Engine::T_ENGINE]["templates_c"]);
+ self::$engine->setCacheDir(Config::getInstance()->getConfig()[Engine::T_ENGINE]["cache"]);
+ self::$engine->setConfigDir(Config::getInstance()->getConfig()[Engine::T_ENGINE]["config_dir"]);
+ self::$engine->assign('debuging', Config::getInstance()->getConfig()[Engine::T_ENGINE]["debugbar"]);
+ break;
+ case Engine::T_ENGINE_TWIG:
+ $twig = new \Twig_Autoloader();
+ $twig::register();
+ self::$engine = new \Twig_Environment(new \Twig_Loader_Filesystem(Config::getInstance()->getConfig()[Engine::T_ENGINE]["templates"]), array(
+ 'cache' => Config::getInstance()->getConfig()[Engine::T_ENGINE]["cache"],
+ ));
+ break;
+ case Engine::T_ENGINE_DWOO:
+ // Implement Dwoo Template Engine
+ self::$engine = new \Dwoo\Core();
+ self::$engine->setCacheDir(Config::getInstance()->getConfig()[Engine::T_ENGINE]["cache"]);
+ self::$engine->setCompileDir(Config::getInstance()->getConfig()[Engine::T_ENGINE]["templates_c"]);
+ self::$engine->setTemplateDir(Config::getInstance()->getConfig()[Engine::T_ENGINE]["templates"]);
+ self::$engine->get('debugbar.tpl', array('debuging' => Config::getInstance()->getConfig()[Engine::T_ENGINE]["debugbar"]));
+ break;
+ }
+ }
+
+ public function assign( $varname = array() )
+ {
+ if(is_array($varname))
+ {
+ Controller::getInstance()->varname( $this->getEngine(), $varname );
+ }
+ }
+
+ public static function forwardTo( $page )
+ {
+ header('Location: ' . $page);
+ exit();
+ }
+
+ public function display( $page )
+ {
+ preg_match_all("/".self::ATM_FILE_END."/", $page, $matches);
+ if(!array_key_exists(self::ATM_FILE_END, array_flip(array_shift($matches))))
+ {
+ $page = str_replace("/", "", $page) . self::ATM_FILE_END;
+ }
+ Controller::getInstance()->action( $this->getEngine(), $page );
+ }
+
+}
\ No newline at end of file
diff --git a/core/dispatch.php b/core/dispatch.php
new file mode 100644
index 0000000..8d74cd2
--- /dev/null
+++ b/core/dispatch.php
@@ -0,0 +1,15 @@
+route();
+require_once __DIR__ . '/../application/controller/' . Nibiru\Router::getInstance()->tplName() . 'Controller.php';
+eval("new Nibiru\\".Nibiru\Router::getInstance()->tplName()."Controller();");
+Nibiru\Debug::getInstance();
+Nibiru\Display::getInstance()->display();
\ No newline at end of file
diff --git a/core/framework.php b/core/framework.php
new file mode 100644
index 0000000..9e34424
--- /dev/null
+++ b/core/framework.php
@@ -0,0 +1,38 @@
+" . "\n" . "{fields}" . "\n" . "\t\t" . "" . "\n";
+ const TYPE_FORM_FIELDSET = "" . "\n";
+ /**
+ * @desc add the form action in order to set the path
+ * for the controller
+ * @param $action
+ * @return mixed
+ */
+ public static function setFormAction($action);
+
+ /**
+ * @desc set the form type, two types (post, get)
+ * @param $type
+ * @return mixed
+ */
+ public static function setFormType($type);
+
+ /**
+ * @desc set the name for the form
+ * @param $name
+ * @return mixed
+ */
+ public static function setFormName($name);
+
+ /**
+ * @desc display the form data on the html layout
+ * @return mixed
+ */
+ public function displayForm();
+}
\ No newline at end of file
diff --git a/core/i/formaction.php b/core/i/formaction.php
new file mode 100644
index 0000000..ace26eb
--- /dev/null
+++ b/core/i/formaction.php
@@ -0,0 +1,20 @@
+" . "\n";
+ const INPUT_LABELED = "\t\t\t" . ""."" . "\n";
+
+ /**
+ * @desc set the form fields, they must be passed within an
+ * array: array(
+ * 'fieldnames' => array('name', 'name' ...),
+ * 'fieldtypes' => 'array(text, radio ...)',
+ * 'min' => array(number, number ...),
+ * 'max' => array(number, nunber ...)
+ * );
+ * @param $fields
+ * @return mixed
+ */
+ public function setInputFields($fields);
+
+ /**
+ * @desc offer the main types for an input field
+ * e.g. type="checkbox",
+ * type="radio",
+ * type="email",
+ * type="text",
+ * type="hidden",
+ * type="date",
+ * type="image"
+ * type="password"
+ * type="week"
+ * type="time"
+ * type="tel"
+ * type="submit"
+ * type="button"
+ * type="url"
+ * @param (string) $type
+ * @return (string)
+ */
+
+}
\ No newline at end of file
diff --git a/core/i/mysql.php b/core/i/mysql.php
new file mode 100644
index 0000000..c314f8e
--- /dev/null
+++ b/core/i/mysql.php
@@ -0,0 +1,31 @@
+ array('name1', 'name2'),
+ * 'value' => array('value1', 'value2')
+ * )
+ * @param $optionNames
+ * @return mixed
+ */
+ public static function setOptionNames($optionNames);
+
+ /**
+ * @desc set the select type possible parameter values
+ * multiple, single
+ * @param $selectType
+ * @return mixed
+ */
+ public static function setSelectType($selectType);
+
+ /**
+ * @desc set the size of the select type if it is multiple
+ * @param $selectTypeSize
+ * @return mixed
+ */
+ public static function setSelectTypeSize($selectTypeSize);
+}
\ No newline at end of file
diff --git a/core/i/soap.php b/core/i/soap.php
new file mode 100644
index 0000000..44b9948
--- /dev/null
+++ b/core/i/soap.php
@@ -0,0 +1,15 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Block;
+
+use Dwoo\Plugin as DwooPlugin;
+use Dwoo\Compiler as DwooCompiler;
+
+/**
+ * Base class for block plugins.
+ * you have to implement the init() method, it will receive the parameters that
+ * are in the template code and is called when the block starts
+ * 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.
+ */
+abstract class Plugin extends DwooPlugin
+{
+ /**
+ * Stores the contents of the block while it runs.
+ *
+ * @var string
+ */
+ protected $buffer = '';
+
+ /**
+ * Buffers input, override only if necessary.
+ *
+ * @var string the content that must be buffered
+ */
+ public function buffer($input)
+ {
+ $this->buffer .= $input;
+ }
+
+ // initialization code, receives the parameters from {block param1 param2}
+ // public function init($arg, $arg, ...);
+
+ /**
+ * Called when the block ends, this is most of the time followed right away by a call
+ * of process() but not always, so this should be used to do any shutdown operations on the
+ * block object, if required.
+ */
+ public function end()
+ {
+ }
+
+ /**
+ * Called when the block output is required by a parent block.
+ * this must read $this->buffer and return it processed
+ *
+ * @return string
+ */
+ public function process()
+ {
+ return $this->buffer;
+ }
+
+ /**
+ * Called at compile time to define what the block should output in the compiled template code, happens when the
+ * block is declared basically this will replace the {block arg arg arg} tag in the template.
+ *
+ * @param DwooCompiler $compiler the compiler instance that calls this function
+ * @param array $params an array containing original and compiled parameters
+ * @param string $prepend that is just meant to allow a child class to call
+ * parent::postProcessing($compiler, $params, "foo();") to add a command before the
+ * default commands are executed parent::postProcessing($compiler, $params, "foo();")
+ * to add a command before the default commands are executed
+ * @param string $append that is just meant to allow a child class to call
+ * parent::postProcessing($compiler, $params, null, "foo();") to add a command after
+ * the default commands are executed parent::postProcessing($compiler, $params, null,
+ * "foo();") to add a command after the default commands are executed
+ * @param string $type the type is the plugin class name used
+ *
+ * @return string
+ */
+ public static function preProcessing(DwooCompiler $compiler, array $params, $prepend, $append, $type)
+ {
+ return DwooCompiler::PHP_OPEN . $prepend . '$this->addStack("' . $type . '", array(' . DwooCompiler::implode_r($compiler->getCompiledParams($params)) . '));' . $append . DwooCompiler::PHP_CLOSE;
+ }
+
+ /**
+ * Called at compile time to define what the block should output in the compiled template code, happens when the
+ * block is ended basically this will replace the {/block} tag in the template.
+ *
+ * @see preProcessing
+ *
+ * @param DwooCompiler $compiler the compiler instance that calls this function
+ * @param array $params an array containing original and compiled parameters, see preProcessing() for more
+ * details more details
+ * @param string $prepend that is just meant to allow a child class to call
+ * parent::postProcessing($compiler, $params, "foo();") to add a command before the
+ * default commands are executed parent::postProcessing($compiler, $params, "foo();")
+ * to add a command before the default commands are executed
+ * @param string $append that is just meant to allow a child class to call
+ * parent::postProcessing($compiler, $params, null, "foo();") to add a command after
+ * the default commands are executed parent::postProcessing($compiler, $params, null,
+ * "foo();") to add a command after the default commands are executed
+ * @param string $content the entire content of the block being closed
+ *
+ * @return string
+ */
+ public static function postProcessing(DwooCompiler $compiler, array $params, $prepend, $append, $content)
+ {
+ return $content . DwooCompiler::PHP_OPEN . $prepend . '$this->delStack();' . $append . DwooCompiler::PHP_CLOSE;
+ }
+}
diff --git a/core/l/Dwoo/Compilation/Exception.php b/core/l/Dwoo/Compilation/Exception.php
new file mode 100644
index 0000000..732a8d4
--- /dev/null
+++ b/core/l/Dwoo/Compilation/Exception.php
@@ -0,0 +1,60 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.3.6
+ * @date 2017-03-21
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Compilation;
+
+use Dwoo\Exception as DwooException;
+use Dwoo\Compiler as DwooCompiler;
+
+/**
+ * Dwoo compilation exception class.
+ * 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.
+ */
+class Exception extends DwooException
+{
+ protected $compiler;
+ protected $template;
+
+ /**
+ * Exception constructor.
+ *
+ * @param DwooCompiler $compiler
+ * @param int $message
+ */
+ public function __construct(DwooCompiler $compiler, $message)
+ {
+ $this->compiler = $compiler;
+ $this->template = $compiler->getCore()->getTemplate();
+ parent::__construct('Compilation error at line ' . $compiler->getLine() . ' in "' . $this->template->getResourceName() . ':' . $this->template->getResourceIdentifier() . '" : ' . $message);
+ }
+
+ /**
+ * @return DwooCompiler
+ */
+ public function getCompiler()
+ {
+ return $this->compiler;
+ }
+
+ /**
+ * @return \Dwoo\ITemplate|null
+ */
+ public function getTemplate()
+ {
+ return $this->template;
+ }
+}
diff --git a/core/l/Dwoo/Compiler.php b/core/l/Dwoo/Compiler.php
new file mode 100644
index 0000000..fff2e05
--- /dev/null
+++ b/core/l/Dwoo/Compiler.php
@@ -0,0 +1,3671 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.3.6
+ * @date 2017-03-22
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+use Closure;
+use Dwoo\Plugins\Blocks\PluginIf;
+use Dwoo\Security\Exception as SecurityException;
+use Dwoo\Security\Policy as SecurityPolicy;
+use Dwoo\Compilation\Exception as CompilationException;
+use ReflectionFunction;
+use ReflectionMethod;
+
+/**
+ * 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.
+ */
+class Compiler implements ICompiler
+{
+ /**
+ * Constant that represents a php opening tag.
+ * use it in case it needs to be adjusted
+ *
+ * @var string
+ */
+ const PHP_OPEN = '';
+
+ /**
+ * 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 SecurityPolicy
+ */
+ 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 array
+ */
+ protected $curBlock;
+
+ /**
+ * Current dwoo object that uses this compiler, or null.
+ *
+ * @var Core
+ */
+ public $core;
+
+ /**
+ * Holds an instance of this class, used by getInstance when you don't
+ * provide a custom compiler in order to save resources.
+ *
+ * @var 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;
+
+ /**
+ * Compiler 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(Core::NAMESPACE_PLUGINS_PROCESSORS, '', Core::toCamelCase($callback));
+ $class = Core::NAMESPACE_PLUGINS_PROCESSORS . $name;
+
+ if (class_exists($class)) {
+ $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(Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '',
+ $callback),
+ $this->processors['pre'], true)) !== false) {
+ unset($this->processors['pre'][$index]);
+ } else {
+ $class = Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $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(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $callback);
+ $class = Core::NAMESPACE_PLUGINS_PROCESSORS . Core::toCamelCase($name);
+
+ if (class_exists($class)) {
+ $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(Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '',
+ $callback),
+ $this->processors['post'], true)) !== false) {
+ unset($this->processors['post'][$index]);
+ } else {
+ $class = Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $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)
+ *
+ * @return array|string
+ * @throws Exception
+ */
+ protected function loadProcessor($class, $name)
+ {
+ if (!class_exists($class) && !function_exists($class)) {
+ try {
+ $this->getCore()->getLoader()->loadPlugin($name);
+ }
+ catch (Exception $e) {
+ throw new 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)) {
+ return array(new $class($this), 'process');
+ }
+
+ if (function_exists($class)) {
+ return $class;
+ }
+
+ throw new 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 (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 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 SecurityPolicy $policy the security policy object
+ */
+ public function setSecurityPolicy(SecurityPolicy $policy = null)
+ {
+ $this->securityPolicy = $policy;
+ }
+
+ /**
+ * Returns the current security policy object or null by default.
+ *
+ * @return SecurityPolicy|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 Core
+ */
+ public function getCore()
+ {
+ return $this->core;
+ }
+
+ /**
+ * 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 void
+ */
+ 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 Core $core
+ * @param ITemplate $template the template to compile
+ *
+ * @return string a compiled php string
+ * @throws CompilationException
+ */
+ public function compile(Core $core, ITemplate $template)
+ {
+ // init vars
+ // $compiled = '';
+ $tpl = $template->getSource();
+ $ptr = 0;
+ $this->core = $core;
+ $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 "\n";
+ echo 'COMPILER INIT' . "\n";
+ }
+
+ if ($this->debug) {
+ echo 'PROCESSING PREPROCESSORS (' . count($this->processors['pre']) . ')' . "\n";
+ }
+
+ // 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 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 '
'.print_r(htmlentities($tpl), true).'
'."\n";
+ }
+
+ // 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 SecurityPolicy::PHP_ALLOW:
+ break;
+ case SecurityPolicy::PHP_ENCODE:
+ $tpl = preg_replace_callback($search, array($this, 'phpTagEncodingHelper'), $tpl);
+ break;
+ case SecurityPolicy::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 CompilationException($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 CompilationException($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;
+ }
+ }
+ }
+ }
+
+ $compiled .= $this->removeBlock('TopLevelBlock');
+
+ if ($this->debug) {
+ echo 'PROCESSING POSTPROCESSORS' . "\n";
+ }
+
+ 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 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() . "\n";
+ }
+
+ $output = "getUsedPlugins() as $plugin => $type) {
+ if ($type & Core::CUSTOM_PLUGIN) {
+ continue;
+ }
+
+ switch ($type) {
+ case Core::CLASS_PLUGIN:
+ case Core::CLASS_PLUGIN + Core::BLOCK_PLUGIN:
+ if (class_exists('Plugin' . $plugin) !== false) {
+ $output .= "if (class_exists('" . "Plugin" . $plugin . "')===false)".
+ "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
+ } else {
+ $output .= "if (class_exists('" . Core::NAMESPACE_PLUGINS_BLOCKS . "Plugin" . $plugin . "')===false)".
+ "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
+ }
+ break;
+ case Core::CLASS_PLUGIN + Core::FUNC_PLUGIN:
+ if (class_exists('Plugin' . $plugin) !== false) {
+ $output .= "if (class_exists('" . "Plugin" . $plugin . "')===false)".
+ "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
+ } else {
+ $output .= "if (class_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)".
+ "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
+ }
+ break;
+ case Core::FUNC_PLUGIN:
+ if (function_exists('Plugin' . $plugin) !== false) {
+ $output .= "if (function_exists('" . "Plugin" . $plugin . "')===false)".
+ "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
+ } else {
+ $output .= "if (function_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)".
+ "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
+ }
+ break;
+ case Core::SMARTY_MODIFIER:
+ $output .= "if (function_exists('smarty_modifier_$plugin')===false)".
+ "\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
+ break;
+ case Core::SMARTY_FUNCTION:
+ $output .= "if (function_exists('smarty_function_$plugin')===false)".
+ "\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
+ break;
+ case Core::SMARTY_BLOCK:
+ $output .= "if (function_exists('smarty_block_$plugin')===false)".
+ "\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
+ break;
+ case Core::PROXY_PLUGIN:
+ $output .= $this->getCore()->getPluginProxy()->getLoader($plugin);
+ break;
+ default:
+ throw new CompilationException($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('/(?\s*)<\?xml#is', '$1', $output);
+
+ // add another line break after PHP closing tags that have a line break following,
+ // as we do not know whether it's intended, and PHP will strip it otherwise
+ $output = preg_replace('/(?\n/', '$0' . "\n", $output);
+
+ if ($this->debug) {
+ echo '=============================================================================================' . "\n";
+ $lines = preg_split('{\r\n|\n| }', $output);
+ array_shift($lines);
+ foreach ($lines as $i => $line) {
+ echo ($i + 1) . '. ' . $line . "\r\n";
+ }
+ echo '=============================================================================================' . "\n";
+ }
+
+ $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)
+ {
+ if ($this->debug) {
+ echo 'Compiler::' . __FUNCTION__ . "\n";
+ }
+
+ $body = $this->templatePlugins[$function]['body'];
+ foreach ($this->templatePlugins as $func => $attr) {
+ if ($func !== $function && !isset($attr['called']) && strpos($body, Core::NAMESPACE_PLUGINS_FUNCTIONS .
+ 'Plugin' . Core::toCamelCase($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
+ *
+ * @throws CompilationException
+ */
+ 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 CompilationException($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)
+ {
+ if ($this->debug) {
+ echo 'Compiler::' . __FUNCTION__ . "\n";
+ }
+
+ $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type);
+ if (class_exists($class) === false) {
+ $this->getCore()->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)
+ {
+ if ($this->debug) {
+ echo 'Compiler::' . __FUNCTION__ . "\n";
+ }
+
+ $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type);
+ if (class_exists($class) === false) {
+ $this->getCore()->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
+ * @throws CompilationException
+ */
+ public function removeBlock($type)
+ {
+ if ($this->debug) {
+ echo 'Compiler::' . __FUNCTION__ . "\n";
+ }
+
+ $output = '';
+
+ $pluginType = $this->getPluginType($type);
+ if ($pluginType & Core::SMARTY_BLOCK) {
+ $type = 'Smartyinterface';
+ }
+ while (true) {
+ while ($top = array_pop($this->stack)) {
+ if ($top['custom']) {
+ $class = $top['class'];
+ } else {
+ $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($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 CompilationException($this, 'Syntax malformation, a block of type "' . $type . '" was closed but was not opened');
+ }
+
+ 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 mixed &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)
+ * @throws CompilationException
+ */
+ 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 CompilationException($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
+ * @throws CompilationException
+ */
+ public function removeTopBlock()
+ {
+ if ($this->debug) {
+ echo 'Compiler::' . __FUNCTION__ . "\n";
+ }
+
+ $o = array_pop($this->stack);
+ if ($o === null) {
+ throw new CompilationException($this, 'Syntax malformation, a block of unknown type was closed but was not opened.');
+ }
+ if ($o['custom']) {
+ $class = $o['class'];
+ } else {
+ $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($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
+ * @throws CompilationException
+ */
+ protected function parse($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
+ {
+ if ($this->debug) {
+ echo 'Compiler::' . __FUNCTION__ . "\n";
+ }
+
+ if ($to === null) {
+ $to = strlen($in);
+ }
+ $first = substr($in, $from, 1);
+
+ if ($first === false) {
+ throw new CompilationException($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' . "\n";
+ }
+
+ 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 'PARSE CALL : PARSING "' . htmlentities(substr($in, $from, min($to - $from, 50))) . (($to - $from) > 50 ? '...' : '') . '" @ ' . $from . ':' . $to . ' in ' . $curBlock . ' : pointer=' . $pointer . "\n";
+ }
+ $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;
+ $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 CompilationException($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)) {
+ // Short constant
+ $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' . "\n";
+ }
+ 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 CompilationException($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 CompilationException($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' . "\n";
+ }
+
+ return $this->removeTopBlock();
+ } else {
+ if ($this->debug) {
+ echo 'BLOCK OF TYPE ' . $match[1] . ' CLOSED' . "\n";
+ }
+
+ 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' . "\n";
+ }
+ $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' . "\n";
+ }
+ $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 CompilationException($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 . "\n";
+ }
+ // 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;
+ }
+ }
+ } elseif ($curBlock === 'root' && preg_match('#^(\s*(?:[+/*%-.]=|=|\+\+|--)\s*)(.*)#s', $substr, $match)) {
+ if ($this->debug) {
+ echo 'PARSING POST-VAR ASSIGNMENT ' . $substr . "\n";
+ }
+ // parse assignment
+ $value = $match[2];
+ $operator = trim($match[1]);
+ if (substr($value, 0, 1) == '=') {
+ throw new CompilationException($this, 'Unexpected "=" in ' . $substr . '');
+ }
+
+ 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 (Exception $e) {
+ throw new CompilationException($this, 'Assignments require the "if" plugin to be accessible');
+ }
+
+ $parts = $this->mapParams($parts, array(Core::NAMESPACE_PLUGINS_BLOCKS . 'PluginIf', 'init'), 1);
+ $tokens = $this->getParamTokens($parts);
+ $parts = $this->getCompiledParams($parts);
+
+ $value = PluginIf::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 = self::PHP_OPEN . $echo . $out . $operator . implode(' ', $value) . self::PHP_CLOSE;
+ } elseif ($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' . "\n";
+ }
+ $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]
+ ), $curBlock, $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]), $curBlock, $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;
+ }
+
+ 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
+ * @throws CompilationException
+ * @throws Exception
+ * @throws SecurityException
+ */
+ protected function parseFunction($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
+ {
+ $output = '';
+ $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 CompilationException($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 . ')' . "\n";
+ }
+
+ $paramsep = '';
+
+ if (is_array($parsingParams) || $curBlock != 'root') {
+ $paramspos = strpos($cmdstr, '(');
+ $paramsep = ')';
+ } elseif (preg_match_all('#^\s*[\\\\: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 (PluginIf::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 . "\n";
+ }
+ break 2;
+ } elseif ($paramstr[$ptr] === ';') {
+ ++ $ptr;
+ if ($this->debug) {
+ echo 'PARAM PARSING ENDED, ";" FOUND, POINTER AT ' . $ptr . "\n";
+ }
+ break 2;
+ } elseif ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === '/') {
+ if ($this->debug) {
+ echo 'PARAM PARSING ENDED, "/" FOUND, POINTER AT ' . $ptr . "\n";
+ }
+ break 2;
+ } elseif (substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) {
+ if ($this->debug) {
+ echo 'PARAM PARSING ENDED, RIGHT DELIMITER FOUND, POINTER AT ' . $ptr . "\n";
+ }
+ 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 . "\n";
+ }
+
+ 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) . ')' . "\n";
+ }
+ }
+ }
+ $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 CompilationException($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' . "\n";
+ }
+ }
+
+ 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 SecurityException('Call to a disallowed php function : ' . $func);
+ }
+ $pluginType = Core::NATIVE_PLUGIN;
+ } else {
+ $pluginType = $this->getPluginType($func);
+ }
+
+ // Blocks plugin
+ if ($pluginType & Core::BLOCK_PLUGIN) {
+ if ($curBlock !== 'root' || is_array($parsingParams)) {
+ throw new CompilationException($this, 'Block plugins can not be used as other plugin\'s arguments');
+ }
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ return $this->addCustomBlock($func, $params, $state);
+ } else {
+ return $this->addBlock($func, $params, $state);
+ }
+ } elseif ($pluginType & Core::SMARTY_BLOCK) {
+ if ($curBlock !== 'root' || is_array($parsingParams)) {
+ throw new CompilationException($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);
+ }
+
+ // Native & Smarty plugins
+ if ($pluginType & Core::NATIVE_PLUGIN || $pluginType & Core::SMARTY_FUNCTION || $pluginType & Core::SMARTY_BLOCK) {
+ $params = $this->mapParams($params, null, $state);
+ } // PHP class plugin
+ elseif ($pluginType & Core::CLASS_PLUGIN) {
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $params = $this->mapParams(
+ $params, array(
+ $this->customPlugins[$func]['class'],
+ $this->customPlugins[$func]['function']
+ ), $state);
+ } else {
+ if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
+ $params = $this->mapParams($params, array(
+ 'Plugin' . Core::toCamelCase($func),
+ ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
+ ), $state);
+ } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) {
+ $params = $this->mapParams($params, array(
+ Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func),
+ ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
+ ), $state);
+ } else {
+ $params = $this->mapParams($params, array(
+ Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
+ ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
+ ), $state);
+ }
+ }
+ } // PHP function plugin
+ elseif ($pluginType & Core::FUNC_PLUGIN) {
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $params = $this->mapParams($params, $this->customPlugins[$func]['callback'], $state);
+ } else {
+ // Custom plugin
+ if (function_exists('Plugin' . Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ?
+ 'Compile' : '')) !== false) {
+ $params = $this->mapParams($params, 'Plugin' . Core::toCamelCase($func) . (($pluginType &
+ Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
+ } // Builtin helper plugin
+ elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . (
+ ($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '')) !== false) {
+ $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase
+ ($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
+ } // Builtin function plugin
+ else {
+ $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase
+ ($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
+ }
+ }
+ } // Smarty modifier
+ elseif ($pluginType & Core::SMARTY_MODIFIER) {
+ $output = 'smarty_modifier_' . $func . '(' . implode(', ', $params) . ')';
+ } // Proxy plugin
+ elseif ($pluginType & Core::PROXY_PLUGIN) {
+ $params = $this->mapParams($params, $this->getCore()->getPluginProxy()->getCallback($func), $state);
+ } // Template plugin
+ elseif ($pluginType & 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];
+ }
+
+ // Native plugin
+ if ($pluginType & Core::NATIVE_PLUGIN) {
+ if ($func === 'do') {
+ $output = '';
+ if (isset($params['*'])) {
+ $output = implode(';', $params['*']) . ';';
+ }
+
+ if (is_array($parsingParams) || $curBlock !== 'root') {
+ throw new CompilationException($this, 'Do can not be used inside another function or block');
+ }
+
+ return self::PHP_OPEN . $output . self::PHP_CLOSE;
+ } else {
+ if (isset($params['*'])) {
+ $output = $func . '(' . implode(', ', $params['*']) . ')';
+ } else {
+ $output = $func . '()';
+ }
+ }
+ } // Block class OR Function class
+ elseif ($pluginType & Core::CLASS_PLUGIN || ($pluginType & Core::FUNC_PLUGIN && $pluginType & Core::CLASS_PLUGIN)) {
+ if ($pluginType & Core::COMPILABLE_PLUGIN) {
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $callback = $this->customPlugins[$func]['callback'];
+ if (!is_array($callback)) {
+ if (!method_exists($callback, 'compile')) {
+ throw new 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 {
+ if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
+ $funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile');
+ } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) {
+ $funcCompiler = array(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func), 'compile');
+ } else {
+ $funcCompiler = array(
+ Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
+ 'compile'
+ );
+ }
+ array_unshift($params, $this);
+ }
+ // @TODO: Is it a real fix ?
+ if ($func === 'tif') {
+ $params[] = $tokens;
+ }
+ $output = call_user_func_array($funcCompiler, $params);
+ } else {
+ $params = self::implode_r($params);
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $callback = $this->customPlugins[$func]['callback'];
+ if (!is_array($callback)) {
+ if (!method_exists($callback, 'process')) {
+ throw new 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 (is_object($callback)) {
+ $callback = get_class($callback);
+ }
+ 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 {
+ if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
+ $output = '$this->classCall(\'Plugin' . $func . '\', array(' . $params . '))';
+ } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) {
+ $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . $func . '\',
+ array(' . $params . '))';
+ } else {
+ $output = '$this->classCall(\'' . $func . '\', array(' . $params . '))';
+ }
+ }
+ }
+ } // Function plugin only (cannot be a class)
+ elseif ($pluginType & Core::FUNC_PLUGIN) {
+ if ($pluginType & Core::COMPILABLE_PLUGIN) {
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $funcCompiler = $this->customPlugins[$func]['callback'];
+ } else {
+ // Custom plugin
+ if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
+ $funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile';
+ } // Builtin helper plugin
+ elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
+ $funcCompiler = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) .
+ 'Compile';
+ } // Builtin function plugin
+ else {
+ $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) .
+ 'Compile';
+ }
+ }
+ array_unshift($params, $this);
+ // @TODO: Is it a real fix ?
+ if ($func === 'tif') {
+ $params[] = $tokens;
+ }
+ $output = call_user_func_array($funcCompiler, $params);
+ } else {
+ array_unshift($params, '$this');
+ $params = self::implode_r($params);
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $callback = $this->customPlugins[$func]['callback'];
+ if ($callback instanceof Closure) {
+ $output = 'call_user_func($this->getCustomPlugin(\'' . $func . '\'), ' . $params . ')';
+ } else {
+ $output = 'call_user_func(\'' . $callback . '\', ' . $params . ')';
+ }
+ } else {
+ // Custom plugin
+ if (function_exists('Plugin' . Core::toCamelCase($func)) !== false) {
+ $output = 'Plugin' . Core::toCamelCase($func) . '(' . $params .
+ ')';
+ } // Builtin helper plugin
+ elseif(function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !==
+ false) {
+ $output = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . '(' .
+ $params . ')';
+ } // Builtin function plugin
+ else {
+ $output = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '(' .
+ $params . ')';
+ }
+ }
+ }
+ } // Proxy plugin
+ elseif ($pluginType & Core::PROXY_PLUGIN) {
+ $output = call_user_func(array($this->getCore()->getPluginProxy(), 'getCode'), $func, $params);
+ } // Smarty function (@deprecated)
+ elseif ($pluginType & Core::SMARTY_FUNCTION) {
+ $params = '';
+ if (isset($params['*'])) {
+ $params = self::implode_r($params['*'], true);
+ }
+
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $callback = $this->customPlugins[$func]['callback'];
+ if (is_array($callback)) {
+ if (is_object($callback[0])) {
+ $output = 'call_user_func_array(array($this->getCustomPlugin(\'' . $func . '\'), \'' . $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)';
+ }
+ } // Template plugin
+ elseif ($pluginType & Core::TEMPLATE_PLUGIN) {
+ array_unshift($params, '$this');
+ $params = self::implode_r($params);
+ $output = 'Plugin' . Core::toCamelCase($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);
+ }
+
+ 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
+ * @throws CompilationException
+ */
+ 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 ? '...' : '') . ')' . "\n";
+ }
+ $strend = false;
+ $o = $from + 1;
+ while ($strend === false) {
+ $strend = strpos($in, $first, $o);
+ if ($strend === false) {
+ throw new CompilationException($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) . "\n";
+ }
+
+ $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));
+ }
+
+ 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
+ * @throws CompilationException
+ */
+ protected function parseConst($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
+ {
+ $substr = substr($in, $from, $to - $from);
+
+ if ($this->debug) {
+ echo 'CONST FOUND : ' . $substr . "\n";
+ }
+
+ if (!preg_match('#^%([\\\\a-z0-9_:]+)#i', $substr, $m)) {
+ throw new CompilationException($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') {
+ return array($output, $m[1]);
+ }
+
+ return $output;
+ }
+
+ /**
+ * Parses a constant.
+ *
+ * @param string $key the constant to parse
+ * @param string $curBlock the current parser-block being processed
+ *
+ * @return string parsed constant
+ */
+ protected function parseConstKey($key, $curBlock)
+ {
+ $key = str_replace('\\\\', '\\', $key);
+
+ if ($this->securityPolicy !== null && $this->securityPolicy->getConstantHandling() === SecurityPolicy::CONST_DISALLOW) {
+ return 'null';
+ }
+
+ if ($curBlock !== 'root') {
+ return '(defined("' . $key . '") ? ' . $key . ' : null)';
+ }
+
+ return $key;
+ }
+
+ /**
+ * Parses a variable.
+ *
+ * @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
+ * @throws CompilationException
+ */
+ protected function parseVar($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
+ {
+ $substr = substr($in, $from, $to - $from);
+
+ // var key
+ $varRegex = '(\\$?\\.?[a-z0-9\\\\_:]*(?:(?:(?:\\.|->)(?:[a-z0-9\\\\_:]+|(?R))|\\[(?:[a-z0-9\\\\_:]+|(?R)|(["\'])[^\\2]*?\\2)\\]))*)';
+ // method call
+ $methodCall = ($curBlock === 'root' || $curBlock === 'function' || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'variable' || $curBlock === 'expression' || $curBlock === 'delimited_string' ? '(\(.*)?' : '()');
+ // simple math expressions
+ $simpleMathExpressions = ($curBlock === 'root' || $curBlock === 'function' || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'variable' || $curBlock === 'delimited_string' ? '((?:(?:[+\/*%=-])(?:(?_:-]+(?:\([^)]*\))?|(?')));
+ $key = substr($match[1], 1, strrpos($match[1], '->') - 1);
+ $methodCall = substr($match[1], strrpos($match[1], '->')) . $match[3];
+ }
+
+ if ($hasModifiers) {
+ $matchedLength -= strlen($match[5]);
+ }
+
+ if ($pointer !== null) {
+ $pointer += $matchedLength;
+ }
+
+ // replace useless brackets by dot accessed vars and strip enclosing quotes if present
+ $key = preg_replace('#\[(["\']?)([^$%\[.>-]+)\1\]#', '.$2', $key);
+
+ if ($this->debug) {
+ if ($hasMethodCall) {
+ echo 'METHOD CALL FOUND : $' . $key . substr($methodCall, 0, 30) . "\n";
+ } else {
+ echo 'VAR FOUND : $' . $key . "\n";
+ }
+ }
+
+ $key = str_replace('"', '\\"', $key);
+
+ $cnt = substr_count($key, '$');
+ if ($cnt > 0) {
+ $uid = 0;
+ $parsed = array($uid => '');
+ $current = &$parsed;
+ $curTxt = &$parsed[$uid ++];
+ $tree = array();
+ $chars = str_split($key, 1);
+ $inSplittedVar = false;
+ $bracketCount = 0;
+
+ while (($char = array_shift($chars)) !== null) {
+ if ($char === '[') {
+ if (count($tree) > 0) {
+ ++ $bracketCount;
+ } else {
+ $tree[] = &$current;
+ $current[$uid] = array($uid + 1 => '');
+ $current = &$current[$uid ++];
+ $curTxt = &$current[$uid ++];
+ continue;
+ }
+ } elseif ($char === ']') {
+ if ($bracketCount > 0) {
+ -- $bracketCount;
+ } else {
+ $current = &$tree[count($tree) - 1];
+ array_pop($tree);
+ if (current($chars) !== '[' && current($chars) !== false && current($chars) !== ']') {
+ $current[$uid] = '';
+ $curTxt = &$current[$uid ++];
+ }
+ continue;
+ }
+ } elseif ($char === '$') {
+ if (count($tree) == 0) {
+ $curTxt = &$current[$uid ++];
+ $inSplittedVar = true;
+ }
+ } elseif (($char === '.' || $char === '-') && count($tree) == 0 && $inSplittedVar) {
+ $curTxt = &$current[$uid ++];
+ $inSplittedVar = false;
+ }
+
+ $curTxt .= $char;
+ }
+ unset($uid, $current, $curTxt, $tree, $chars);
+
+ if ($this->debug) {
+ echo 'RECURSIVE VAR REPLACEMENT : ' . $key . "\n";
+ }
+
+ $key = $this->flattenVarTree($parsed);
+
+ if ($this->debug) {
+ echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n";
+ }
+
+ $output = preg_replace('#(^""\.|""\.|\.""$|(\()""\.|\.""(\)))#', '$2$3', '$this->readVar("' . $key . '")');
+ } else {
+ $output = $this->parseVarKey($key, $hasModifiers ? 'modifier' : $curBlock);
+ }
+
+
+ // methods
+ if ($hasMethodCall) {
+ $ptr = 0;
+
+ $output = $this->parseMethodCall($output, $methodCall, $curBlock, $ptr);
+
+ if ($pointer !== null) {
+ $pointer += $ptr;
+ }
+ $matchedLength += $ptr;
+ }
+
+ if ($hasExpression) {
+ // expressions
+ preg_match_all('#(?:([+/*%=-])(=?-?[%$][a-z0-9\\\\.[\]>_:-]+(?:\([^)]*\))?|=?-?[0-9.,]+|\1))#i', $match[4], $expMatch);
+ foreach ($expMatch[1] as $k => $operator) {
+ if (substr($expMatch[2][$k], 0, 1) === '=') {
+ $assign = true;
+ if ($operator === '=') {
+ throw new CompilationException($this, 'Invalid expression ' . $substr . ', can not use "==" in expressions');
+ }
+ if ($curBlock !== 'root') {
+ throw new CompilationException($this, 'Invalid expression ' . $substr . ', assignments can only be used in top level expressions like {$foo+=3} or {$foo="bar"}');
+ }
+ $operator .= '=';
+ $expMatch[2][$k] = substr($expMatch[2][$k], 1);
+ }
+
+ if (substr($expMatch[2][$k], 0, 1) === '-' && strlen($expMatch[2][$k]) > 1) {
+ $operator .= '-';
+ $expMatch[2][$k] = substr($expMatch[2][$k], 1);
+ }
+ if (($operator === '+' || $operator === '-') && $expMatch[2][$k] === $operator) {
+ $output = '(' . $output . $operator . $operator . ')';
+ break;
+ } elseif (substr($expMatch[2][$k], 0, 1) === '$') {
+ $output = '(' . $output . ' ' . $operator . ' ' . $this->parseVar($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')';
+ } elseif (substr($expMatch[2][$k], 0, 1) === '%') {
+ $output = '(' . $output . ' ' . $operator . ' ' . $this->parseConst($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')';
+ } elseif (!empty($expMatch[2][$k])) {
+ $output = '(' . $output . ' ' . $operator . ' ' . str_replace(',', '.', $expMatch[2][$k]) . ')';
+ } else {
+ throw new CompilationException($this, 'Unfinished expression ' . $substr . ', missing var or number after math operator');
+ }
+ }
+ }
+
+ if ($this->autoEscape === true && $curBlock !== 'condition') {
+ $output = '(is_string($tmp=' . $output . ') ? htmlspecialchars($tmp, ENT_QUOTES, $this->charset) : $tmp)';
+ }
+
+ // handle modifiers
+ if ($curBlock !== 'modifier' && $hasModifiers) {
+ $ptr = 0;
+ $output = $this->replaceModifiers(array(null, null, $output, $match[5]), 'var', $ptr);
+ if ($pointer !== null) {
+ $pointer += $ptr;
+ }
+ $matchedLength += $ptr;
+ }
+
+ if (is_array($parsingParams)) {
+ $parsingParams[] = array($output, $key);
+
+ return $parsingParams;
+ } elseif ($curBlock === 'namedparam') {
+ return array($output, $key);
+ } elseif ($curBlock === 'string' || $curBlock === 'delimited_string') {
+ return array($matchedLength, $output);
+ } elseif ($curBlock === 'expression' || $curBlock === 'variable') {
+ return $output;
+ } elseif (isset($assign)) {
+ return self::PHP_OPEN . $output . ';' . self::PHP_CLOSE;
+ }
+
+ return $output;
+ } else {
+ if ($curBlock === 'string' || $curBlock === 'delimited_string') {
+ return array(0, '');
+ }
+ throw new CompilationException($this, 'Invalid variable name ' . $substr . '');
+ }
+ }
+
+ /**
+ * Parses any number of chained method calls/property reads.
+ *
+ * @param string $output the variable or whatever upon which the method are called
+ * @param string $methodCall method call source, starting at "->"
+ * @param string $curBlock the current parser-block being processed
+ * @param int $pointer a reference to a pointer that will be increased by the amount of characters parsed
+ *
+ * @return string parsed call(s)/read(s)
+ */
+ protected function parseMethodCall($output, $methodCall, $curBlock, &$pointer)
+ {
+ $ptr = 0;
+ $len = strlen($methodCall);
+
+ while ($ptr < $len) {
+ if (strpos($methodCall, '->', $ptr) === $ptr) {
+ $ptr += 2;
+ }
+
+ if (in_array(
+ $methodCall[$ptr], array(
+ ';',
+ ',',
+ '/',
+ ' ',
+ "\t",
+ "\r",
+ "\n",
+ ')',
+ '+',
+ '*',
+ '%',
+ '=',
+ '-',
+ '|'
+ )
+ ) || substr($methodCall, $ptr, strlen($this->rd)) === $this->rd
+ ) {
+ // break char found
+ break;
+ }
+
+ if (!preg_match('/^([a-z0-9_]+)(\(.*?\))?/i', substr($methodCall, $ptr), $methMatch)) {
+ break;
+ }
+
+ if (empty($methMatch[2])) {
+ // property
+ if ($curBlock === 'root') {
+ $output .= '->' . $methMatch[1];
+ } else {
+ $output = '(($tmp = ' . $output . ') ? $tmp->' . $methMatch[1] . ' : null)';
+ }
+ $ptr += strlen($methMatch[1]);
+ } else {
+ // method
+ if (substr($methMatch[2], 0, 2) === '()') {
+ $parsedCall = $methMatch[1] . '()';
+ $ptr += strlen($methMatch[1]) + 2;
+ } else {
+ $parsedCall = $this->parseFunction($methodCall, $ptr, strlen($methodCall), false, 'method', $ptr);
+ }
+ if ($this->securityPolicy !== null) {
+ $argPos = strpos($parsedCall, '(');
+ $method = strtolower(substr($parsedCall, 0, $argPos));
+ $args = substr($parsedCall, $argPos);
+ if ($curBlock === 'root') {
+ $output = '$this->getSecurityPolicy()->callMethod($this, ' . $output . ', ' . var_export($method, true) . ', array' . $args . ')';
+ } else {
+ $output = '(($tmp = ' . $output . ') ? $this->getSecurityPolicy()->callMethod($this, $tmp, ' . var_export($method, true) . ', array' . $args . ') : null)';
+ }
+ } else {
+ if ($curBlock === 'root') {
+ $output .= '->' . $parsedCall;
+ } else {
+ $output = '(($tmp = ' . $output . ') ? $tmp->' . $parsedCall . ' : null)';
+ }
+ }
+ }
+ }
+
+ $pointer += $ptr;
+
+ return $output;
+ }
+
+ /**
+ * Parses a constant variable (a variable that doesn't contain another variable) and preprocesses it to save
+ * runtime processing time.
+ *
+ * @param string $key the variable to parse
+ * @param string $curBlock the current parser-block being processed
+ *
+ * @return string parsed variable
+ */
+ protected function parseVarKey($key, $curBlock)
+ {
+ if ($key === '') {
+ return '$this->scope';
+ }
+ if (substr($key, 0, 1) === '.') {
+ $key = 'dwoo' . $key;
+ }
+ if (preg_match('#dwoo\.(get|post|server|cookies|session|env|request)((?:\.[a-z0-9_-]+)+)#i', $key, $m)) {
+ $global = strtoupper($m[1]);
+ if ($global === 'COOKIES') {
+ $global = 'COOKIE';
+ }
+ $key = '$_' . $global;
+ foreach (explode('.', ltrim($m[2], '.')) as $part) {
+ $key .= '[' . var_export($part, true) . ']';
+ }
+ if ($curBlock === 'root') {
+ $output = $key;
+ } else {
+ $output = '(isset(' . $key . ')?' . $key . ':null)';
+ }
+ } elseif (preg_match('#dwoo\\.const\\.([a-z0-9\\\\_:]+)#i', $key, $m)) {
+ return $this->parseConstKey($m[1], $curBlock);
+ } elseif ($this->scope !== null) {
+ if (strstr($key, '.') === false && strstr($key, '[') === false && strstr($key, '->') === false) {
+ if ($key === 'dwoo') {
+ $output = '$this->globals';
+ } elseif ($key === '_root' || $key === '__') {
+ $output = '$this->data';
+ } elseif ($key === '_parent' || $key === '_') {
+ $output = '$this->readParentVar(1)';
+ } elseif ($key === '_key') {
+ $output = '$tmp_key';
+ } else {
+ if ($curBlock === 'root') {
+ $output = '$this->scope["' . $key . '"]';
+ } else {
+ $output = '(isset($this->scope["' . $key . '"]) ? $this->scope["' . $key . '"] : null)';
+ }
+ }
+ } else {
+ preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+|(\\\?[\'"])[^\3]*?\3)\]?#i', $key, $m);
+
+ $i = $m[2][0];
+ if ($i === '_parent' || $i === '_') {
+ $parentCnt = 0;
+
+ while (true) {
+ ++ $parentCnt;
+ array_shift($m[2]);
+ array_shift($m[1]);
+ if (current($m[2]) === '_parent') {
+ continue;
+ }
+ break;
+ }
+
+ $output = '$this->readParentVar(' . $parentCnt . ')';
+ } else {
+ if ($i === 'dwoo') {
+ $output = '$this->globals';
+ array_shift($m[2]);
+ array_shift($m[1]);
+ } elseif ($i === '_root' || $i === '__') {
+ $output = '$this->data';
+ array_shift($m[2]);
+ array_shift($m[1]);
+ } elseif ($i === '_key') {
+ $output = '$tmp_key';
+ } else {
+ $output = '$this->scope';
+ }
+
+ while (count($m[1]) && $m[1][0] !== '->') {
+ $m[2][0] = preg_replace('/(^\\\([\'"])|\\\([\'"])$)/x', '$2$3', $m[2][0]);
+ if (substr($m[2][0], 0, 1) == '"' || substr($m[2][0], 0, 1) == "'") {
+ $output .= '[' . $m[2][0] . ']';
+ } else {
+ $output .= '["' . $m[2][0] . '"]';
+ }
+ array_shift($m[2]);
+ array_shift($m[1]);
+ }
+
+ if ($curBlock !== 'root') {
+ $output = '(isset(' . $output . ') ? ' . $output . ':null)';
+ }
+ }
+
+ if (count($m[2])) {
+ unset($m[0]);
+ $output = '$this->readVarInto(' . str_replace("\n", '', var_export($m, true)) . ', ' . $output . ', ' . ($curBlock == 'root' ? 'false' : 'true') . ')';
+ }
+ }
+ } else {
+ preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+)\]?#i', $key, $m);
+ unset($m[0]);
+ $output = '$this->readVar(' . str_replace("\n", '', var_export($m, true)) . ')';
+ }
+
+ return $output;
+ }
+
+ /**
+ * Flattens a variable tree, this helps in parsing very complex variables such as $var.foo[$foo.bar->baz].baz,
+ * it computes the contents of the brackets first and works out from there.
+ *
+ * @param array $tree the variable tree parsed by he parseVar() method that must be flattened
+ * @param bool $recursed leave that to false by default, it is only for internal use
+ *
+ * @return string flattened tree
+ */
+ protected function flattenVarTree(array $tree, $recursed = false)
+ {
+ $out = $recursed ? '".$this->readVarInto(' : '';
+ foreach ($tree as $bit) {
+ if (is_array($bit)) {
+ $out .= '.' . $this->flattenVarTree($bit, false);
+ } else {
+ $key = str_replace('"', '\\"', $bit);
+
+ if (substr($key, 0, 1) === '$') {
+ $out .= '".' . $this->parseVar($key, 0, strlen($key), false, 'variable') . '."';
+ } else {
+ $cnt = substr_count($key, '$');
+
+ if ($this->debug) {
+ echo 'PARSING SUBVARS IN : ' . $key . "\n";
+ }
+ if ($cnt > 0) {
+ while (-- $cnt >= 0) {
+ if (isset($last)) {
+ $last = strrpos($key, '$', - (strlen($key) - $last + 1));
+ } else {
+ $last = strrpos($key, '$');
+ }
+ preg_match('#\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', substr($key, $last), $submatch);
+
+ $len = strlen($submatch[0]);
+ $key = substr_replace(
+ $key, preg_replace_callback(
+ '#(\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*)' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', array(
+ $this,
+ 'replaceVarKeyHelper'
+ ), substr($key, $last, $len)
+ ), $last, $len
+ );
+ if ($this->debug) {
+ echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n";
+ }
+ }
+ unset($last);
+
+ $out .= $key;
+ } else {
+ $out .= $key;
+ }
+ }
+ }
+ }
+ $out .= $recursed ? ', true)."' : '';
+
+ return $out;
+ }
+
+ /**
+ * Helper function that parses a variable.
+ *
+ * @param array $match the matched variable, array(1=>"string match")
+ *
+ * @return string parsed variable
+ */
+ protected function replaceVarKeyHelper($match)
+ {
+ return '".' . $this->parseVar($match[0], 0, strlen($match[0]), false, 'variable') . '."';
+ }
+
+ /**
+ * Parses various constants, operators or non-quoted strings.
+ *
+ * @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
+ * @throws Exception
+ */
+ protected function parseOthers($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
+ {
+ $substr = substr($in, $from, $to - $from);
+
+ $end = strlen($substr);
+
+ if ($curBlock === 'condition') {
+ $breakChars = array(
+ '(',
+ ')',
+ ' ',
+ '||',
+ '&&',
+ '|',
+ '&',
+ '>=',
+ '<=',
+ '===',
+ '==',
+ '=',
+ '!==',
+ '!=',
+ '<<',
+ '<',
+ '>>',
+ '>',
+ '^',
+ '~',
+ ',',
+ '+',
+ '-',
+ '*',
+ '/',
+ '%',
+ '!',
+ '?',
+ ':',
+ $this->rd,
+ ';'
+ );
+ } elseif ($curBlock === 'modifier') {
+ $breakChars = array(' ', ',', ')', ':', '|', "\r", "\n", "\t", ';', $this->rd);
+ } elseif ($curBlock === 'expression') {
+ $breakChars = array('/', '%', '+', '-', '*', ' ', ',', ')', "\r", "\n", "\t", ';', $this->rd);
+ } else {
+ $breakChars = array(' ', ',', ')', "\r", "\n", "\t", ';', $this->rd);
+ }
+
+ $breaker = false;
+ foreach ($breakChars as $k => $char) {
+ $test = strpos($substr, $char);
+ if ($test !== false && $test < $end) {
+ $end = $test;
+ $breaker = $k;
+ }
+ }
+
+ if ($curBlock === 'condition') {
+ if ($end === 0 && $breaker !== false) {
+ $end = strlen($breakChars[$breaker]);
+ }
+ }
+
+ if ($end !== false) {
+ $substr = substr($substr, 0, $end);
+ }
+
+ if ($pointer !== null) {
+ $pointer += strlen($substr);
+ }
+
+ $src = $substr;
+ $substr = trim($substr);
+
+ if (strtolower($substr) === 'false' || strtolower($substr) === 'no' || strtolower($substr) === 'off') {
+ if ($this->debug) {
+ echo 'BOOLEAN(FALSE) PARSED' . "\n";
+ }
+ $substr = 'false';
+ $type = self::T_BOOL;
+ } elseif (strtolower($substr) === 'true' || strtolower($substr) === 'yes' || strtolower($substr) === 'on') {
+ if ($this->debug) {
+ echo 'BOOLEAN(TRUE) PARSED' . "\n";
+ }
+ $substr = 'true';
+ $type = self::T_BOOL;
+ } elseif ($substr === 'null' || $substr === 'NULL') {
+ if ($this->debug) {
+ echo 'NULL PARSED' . "\n";
+ }
+ $substr = 'null';
+ $type = self::T_NULL;
+ } elseif (is_numeric($substr)) {
+ $substr = (float)$substr;
+ if ((int)$substr == $substr) {
+ $substr = (int)$substr;
+ }
+ $type = self::T_NUMERIC;
+ if ($this->debug) {
+ echo 'NUMBER (' . $substr . ') PARSED' . "\n";
+ }
+ } elseif (preg_match('{^-?(\d+|\d*(\.\d+))\s*([/*%+-]\s*-?(\d+|\d*(\.\d+)))+$}', $substr)) {
+ if ($this->debug) {
+ echo 'SIMPLE MATH PARSED . "\n"';
+ }
+ $type = self::T_MATH;
+ $substr = '(' . $substr . ')';
+ } elseif ($curBlock === 'condition' && array_search($substr, $breakChars, true) !== false) {
+ if ($this->debug) {
+ echo 'BREAKCHAR (' . $substr . ') PARSED' . "\n";
+ }
+ $type = self::T_BREAKCHAR;
+ //$substr = '"'.$substr.'"';
+ } else {
+ $substr = $this->replaceStringVars('\'' . str_replace('\'', '\\\'', $substr) . '\'', '\'', $curBlock);
+ $type = self::T_UNQUOTED_STRING;
+ if ($this->debug) {
+ echo 'BLABBER (' . $substr . ') CASTED AS STRING' . "\n";
+ }
+ }
+
+ if (is_array($parsingParams)) {
+ $parsingParams[] = array($substr, $src, $type);
+
+ return $parsingParams;
+ } elseif ($curBlock === 'namedparam') {
+ return array($substr, $src, $type);
+ } elseif ($curBlock === 'expression') {
+ return $substr;
+ } else {
+ throw new Exception('Something went wrong');
+ }
+ }
+
+ /**
+ * Replaces variables within a parsed string.
+ *
+ * @param string $string the parsed string
+ * @param string $first the first character parsed in the string, which is the string delimiter (' or ")
+ * @param string $curBlock the current parser-block being processed
+ *
+ * @return string the original string with variables replaced
+ */
+ protected function replaceStringVars($string, $first, $curBlock = '')
+ {
+ $pos = 0;
+ if ($this->debug) {
+ echo 'STRING VAR REPLACEMENT : ' . $string . "\n";
+ }
+ // replace vars
+ while (($pos = strpos($string, '$', $pos)) !== false) {
+ $prev = substr($string, $pos - 1, 1);
+ if ($prev === '\\') {
+ ++ $pos;
+ continue;
+ }
+
+ $var = $this->parse($string, $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string')));
+ $len = $var[0];
+ $var = $this->parse(str_replace('\\' . $first, $first, $string), $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string')));
+
+ if ($prev === '`' && substr($string, $pos + $len, 1) === '`') {
+ $string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos - 1, $len + 2);
+ } else {
+ $string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos, $len);
+ }
+ $pos += strlen($var[1]) + 2;
+ if ($this->debug) {
+ echo 'STRING VAR REPLACEMENT DONE : ' . $string . "\n";
+ }
+ }
+
+ // handle modifiers
+ // TODO Obsolete?
+ $string = preg_replace_callback(
+ '#("|\')\.(.+?)\.\1((?:\|(?:@?[a-z0-9_]+(?:(?::("|\').+?\4|:[^`]*))*))+)#i', array(
+ $this,
+ 'replaceModifiers'
+ ), $string
+ );
+
+ // replace escaped dollar operators by unescaped ones if required
+ if ($first === "'") {
+ $string = str_replace('\\$', '$', $string);
+ }
+
+ return $string;
+ }
+
+ /**
+ * Replaces the modifiers applied to a string or a variable.
+ *
+ * @param array $m the regex matches that must be array(1=>"double or single quotes enclosing a string,
+ * when applicable", 2=>"the string or var", 3=>"the modifiers matched")
+ * @param string $curBlock the current parser-block being processed
+ * @param null $pointer
+ *
+ * @return string the input enclosed with various function calls according to the modifiers found
+ * @throws CompilationException
+ * @throws Exception
+ */
+ protected function replaceModifiers(array $m, $curBlock = null, &$pointer = null)
+ {
+ if ($this->debug) {
+ echo 'PARSING MODIFIERS : ' . $m[3] . "\n";
+ }
+
+ if ($pointer !== null) {
+ $pointer += strlen($m[3]);
+ }
+ // remove first pipe
+ $cmdstrsrc = substr($m[3], 1);
+ // remove last quote if present
+ if (substr($cmdstrsrc, - 1, 1) === $m[1]) {
+ $cmdstrsrc = substr($cmdstrsrc, 0, - 1);
+ $add = $m[1];
+ }
+
+ $output = $m[2];
+
+ $continue = true;
+ while (strlen($cmdstrsrc) > 0 && $continue) {
+ if ($cmdstrsrc[0] === '|') {
+ $cmdstrsrc = substr($cmdstrsrc, 1);
+ continue;
+ }
+ if ($cmdstrsrc[0] === ' ' || $cmdstrsrc[0] === ';' || substr($cmdstrsrc, 0, strlen($this->rd)) === $this->rd) {
+ if ($this->debug) {
+ echo 'MODIFIER PARSING ENDED, RIGHT DELIMITER or ";" FOUND' . "\n";
+ }
+ $continue = false;
+ if ($pointer !== null) {
+ $pointer -= strlen($cmdstrsrc);
+ }
+ break;
+ }
+ $cmdstr = $cmdstrsrc;
+ $paramsep = ':';
+ if (!preg_match('/^(@{0,2}[a-z_][a-z0-9_]*)(:)?/i', $cmdstr, $match)) {
+ throw new CompilationException($this, 'Invalid modifier name, started with : ' . substr($cmdstr, 0, 10));
+ }
+ $paramspos = !empty($match[2]) ? strlen($match[1]) : false;
+ $func = $match[1];
+
+ $state = 0;
+ if ($paramspos === false) {
+ $cmdstrsrc = substr($cmdstrsrc, strlen($func));
+ $params = array();
+ if ($this->debug) {
+ echo 'MODIFIER (' . $func . ') CALLED WITH NO PARAMS' . "\n";
+ }
+ } else {
+ $paramstr = substr($cmdstr, $paramspos + 1);
+ if (substr($paramstr, - 1, 1) === $paramsep) {
+ $paramstr = substr($paramstr, 0, - 1);
+ }
+
+ $ptr = 0;
+ $params = array();
+ while ($ptr < strlen($paramstr)) {
+ if ($this->debug) {
+ echo 'MODIFIER (' . $func . ') START PARAM PARSING WITH POINTER AT ' . $ptr . "\n";
+ }
+ if ($this->debug) {
+ echo $paramstr . '--' . $ptr . '--' . strlen($paramstr) . '--modifier' . "\n";
+ }
+ $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'modifier', $ptr);
+ if ($this->debug) {
+ echo 'PARAM PARSED, POINTER AT ' . $ptr . "\n";
+ }
+
+ if ($ptr >= strlen($paramstr)) {
+ if ($this->debug) {
+ echo 'PARAM PARSING ENDED, PARAM STRING CONSUMED' . "\n";
+ }
+ break;
+ }
+
+ if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === '|' || $paramstr[$ptr] === ';' || substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) {
+ if ($this->debug) {
+ echo 'PARAM PARSING ENDED, " ", "|", RIGHT DELIMITER or ";" FOUND, POINTER AT ' . $ptr . "\n";
+ }
+ if ($paramstr[$ptr] !== '|') {
+ $continue = false;
+ if ($pointer !== null) {
+ $pointer -= strlen($paramstr) - $ptr;
+ }
+ }
+ ++ $ptr;
+ break;
+ }
+ if ($ptr < strlen($paramstr) && $paramstr[$ptr] === ':') {
+ ++ $ptr;
+ }
+ }
+ $cmdstrsrc = substr($cmdstrsrc, strlen($func) + 1 + $ptr);
+ 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)) {
+ $params[$k] = array($m[2], array('true', 'true'));
+ } else {
+ if ($state & 2) {
+ throw new CompilationException($this, 'You can not use an unnamed parameter after a named one');
+ }
+ $state |= 1;
+ }
+ }
+ }
+ }
+
+ // check if we must use array_map with this plugin or not
+ $mapped = false;
+ if (substr($func, 0, 1) === '@') {
+ $func = substr($func, 1);
+ $mapped = true;
+ }
+
+ $pluginType = $this->getPluginType($func);
+
+ if ($state & 2) {
+ array_unshift($params, array('value', is_array($output) ? $output : array($output, $output)));
+ } else {
+ array_unshift($params, is_array($output) ? $output : array($output, $output));
+ }
+
+ if ($pluginType & Core::NATIVE_PLUGIN) {
+ $params = $this->mapParams($params, null, $state);
+
+ $params = $params['*'][0];
+
+ $params = self::implode_r($params);
+
+ if ($mapped) {
+ $output = '$this->arrayMap(\'' . $func . '\', array(' . $params . '))';
+ } else {
+ $output = $func . '(' . $params . ')';
+ }
+ } elseif ($pluginType & Core::PROXY_PLUGIN) {
+ $params = $this->mapParams($params, $this->getCore()->getPluginProxy()->getCallback($func), $state);
+ foreach ($params as &$p) {
+ $p = $p[0];
+ }
+ $output = call_user_func(array($this->getCore()->getPluginProxy(), 'getCode'), $func, $params);
+ } elseif ($pluginType & Core::SMARTY_MODIFIER) {
+ $params = $this->mapParams($params, null, $state);
+ $params = $params['*'][0];
+
+ $params = self::implode_r($params);
+
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $callback = $this->customPlugins[$func]['callback'];
+ if (is_array($callback)) {
+ if (is_object($callback[0])) {
+ $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->getCustomPlugin(\'' . $func . '\'), \'' . $callback[1] . '\'), array(' . $params . '))';
+ } else {
+ $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))';
+ }
+ } elseif ($mapped) {
+ $output = '$this->arrayMap(\'' . $callback . '\', array(' . $params . '))';
+ } else {
+ $output = $callback . '(' . $params . ')';
+ }
+ } elseif ($mapped) {
+ $output = '$this->arrayMap(\'smarty_modifier_' . $func . '\', array(' . $params . '))';
+ } else {
+ $output = 'smarty_modifier_' . $func . '(' . $params . ')';
+ }
+ } else {
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $pluginName = $callback = $this->customPlugins[$func]['callback'];
+ if (($pluginType & Core::CLASS_PLUGIN) && !is_array($callback)) {
+ $pluginName = $this->customPlugins[$func]['callback'];
+ $callback = array($pluginName, ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process');
+ }
+ } else {
+ if (class_exists('Plugin' . Core::toCamelCase($func)) !== false || function_exists('Plugin' .
+ Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''))
+ !== false) {
+ $pluginName = 'Plugin' . Core::toCamelCase($func);
+ } else {
+ $pluginName = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func);
+ }
+ if ($pluginType & Core::CLASS_PLUGIN) {
+ $callback = array($pluginName, ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process');
+ } else {
+ $callback = $pluginName . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '');
+ }
+ }
+ $params = $this->mapParams($params, $callback, $state);
+
+ foreach ($params as &$p) {
+ $p = $p[0];
+ }
+
+ // Only for PHP function, who is not a PHP class
+ if ($pluginType & Core::FUNC_PLUGIN && !($pluginType & Core::CLASS_PLUGIN)) {
+ if ($pluginType & Core::COMPILABLE_PLUGIN) {
+ if ($mapped) {
+ throw new CompilationException($this, 'The @ operator can not be used on compiled plugins.');
+ }
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $funcCompiler = $this->customPlugins[$func]['callback'];
+ } else {
+ if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
+ $funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile';
+ } else {
+ $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) .
+ 'Compile';
+ }
+ }
+ array_unshift($params, $this);
+ $output = call_user_func_array($funcCompiler, $params);
+ } else {
+ array_unshift($params, '$this');
+
+ $params = self::implode_r($params);
+ if ($mapped) {
+ $output = '$this->arrayMap(\'' . $pluginName . '\', array(' . $params . '))';
+ } else {
+ $output = $pluginName . '(' . $params . ')';
+ }
+ }
+ } else {
+ if ($pluginType & Core::COMPILABLE_PLUGIN) {
+ if ($mapped) {
+ throw new CompilationException($this, 'The @ operator can not be used on compiled plugins.');
+ }
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ $callback = $this->customPlugins[$func]['callback'];
+ if (!is_array($callback)) {
+ if (!method_exists($callback, 'compile')) {
+ throw new 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 {
+ if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
+ $funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile');
+ } else {
+ $funcCompiler = array(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func), 'compile');
+ }
+ array_unshift($params, $this);
+ }
+ $output = call_user_func_array($funcCompiler, $params);
+ } else {
+ $params = self::implode_r($params);
+
+ if ($pluginType & Core::CUSTOM_PLUGIN) {
+ if (is_object($callback[0])) {
+ if (is_array($this->getCore()->getCustomPlugin($func))) {
+ $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(' . $params . '))';
+ } else {
+ $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->getCustomPlugin(\'' . $func . '\'), \'' . $callback[1] . '\'), array(' . $params . '))';
+ }
+ } else {
+ $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))';
+ }
+ } elseif ($mapped) {
+ $output = '$this->arrayMap(array($this->getObjectPlugin(\''.
+ Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '\'),
+ \'process\'), array(' . $params . '))';
+ } else {
+ if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
+ $output = '$this->classCall(\'Plugin' . Core::toCamelCase($func) . '\', array(' . $params . '))';
+ } elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($func)) !== false) {
+ $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . $func . '\', array(' . $params . '))';
+ } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) {
+ $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . $func . '\', array(' . $params . '))';
+ } else {
+ $output = '$this->classCall(\'' . $func . '\', array(' . $params . '))';
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ($curBlock === 'namedparam') {
+ return array($output, $output);
+ } elseif ($curBlock === 'var' || $m[1] === null) {
+ return $output;
+ } elseif ($curBlock === 'string' || $curBlock === 'root') {
+ return $m[1] . '.' . $output . '.' . $m[1] . (isset($add) ? $add : null);
+ }
+
+ return '';
+ }
+
+ /**
+ * Recursively implodes an array in a similar manner as var_export() does but with some tweaks
+ * to handle pre-compiled values and the fact that we do not need to enclose everything with
+ * "array" and do not require top-level keys to be displayed.
+ *
+ * @param array $params the array to implode
+ * @param bool $recursiveCall if set to true, the function outputs key names for the top level
+ *
+ * @return string the imploded array
+ */
+ public static function implode_r(array $params, $recursiveCall = false)
+ {
+ $out = '';
+ foreach ($params as $k => $p) {
+ if (is_array($p)) {
+ $out2 = 'array(';
+ foreach ($p as $k2 => $v) {
+ $out2 .= var_export($k2, true) . ' => ' . (is_array($v) ? 'array(' . self::implode_r($v, true) . ')' : $v) . ', ';
+ }
+ $p = rtrim($out2, ', ') . ')';
+ }
+ if ($recursiveCall) {
+ $out .= var_export($k, true) . ' => ' . $p . ', ';
+ } else {
+ $out .= $p . ', ';
+ }
+ }
+
+ return rtrim($out, ', ');
+ }
+
+ /**
+ * Returns the plugin type of a plugin and adds it to the used plugins array if required.
+ *
+ * @param string $name plugin name, as found in the template
+ *
+ * @return int type as a multi bit flag composed of the Dwoo plugin types constants
+ * @throws Exception
+ * @throws SecurityException
+ * @throws Exception
+ */
+ protected function getPluginType($name)
+ {
+ $pluginType = - 1;
+
+ if (($this->securityPolicy === null && (function_exists($name) || strtolower($name) === 'isset' || strtolower($name) === 'empty')) || ($this->securityPolicy !== null && array_key_exists(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) !== false)) {
+ $phpFunc = true;
+ } elseif ($this->securityPolicy !== null && function_exists($name) && array_key_exists(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) === false) {
+ throw new SecurityException('Call to a disallowed php function : ' . $name);
+ }
+
+ while ($pluginType <= 0) {
+ // Template plugin compilable
+ if (isset($this->templatePlugins[$name])) {
+ $pluginType = Core::TEMPLATE_PLUGIN | Core::COMPILABLE_PLUGIN;
+ } // Custom plugin
+ elseif (isset($this->customPlugins[$name])) {
+ $pluginType = $this->customPlugins[$name]['type'] | Core::CUSTOM_PLUGIN;
+ } // Class blocks plugin
+ elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name)) !== false) {
+ $pluginType = Core::CLASS_PLUGIN;
+ if (is_subclass_of(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name), 'Dwoo\Block\Plugin')) {
+ $pluginType += Core::BLOCK_PLUGIN;
+ }
+ $interfaces = class_implements(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name));
+ if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
+ $pluginType |= Core::COMPILABLE_PLUGIN;
+ }
+ } // Class functions plugin
+ elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name)) !== false) {
+ $pluginType = Core::FUNC_PLUGIN + Core::CLASS_PLUGIN;
+ $interfaces = class_implements(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name));
+ if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
+ $pluginType |= Core::COMPILABLE_PLUGIN;
+ }
+ } // Class without namespace
+ elseif (class_exists('Plugin' . Core::toCamelCase($name)) !== false) {
+ $pluginType = Core::CLASS_PLUGIN;
+ $interfaces = class_implements('Plugin' . Core::toCamelCase($name));
+ if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
+ $pluginType |= Core::COMPILABLE_PLUGIN;
+ }
+ } // Function plugin (with/without namespaces)
+ elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase ($name)) !==
+ false || function_exists('Plugin' . Core::toCamelCase($name)) !== false) {
+ $pluginType = Core::FUNC_PLUGIN;
+ } // Function plugin compile (with/without namespaces)
+ elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name) .
+ 'Compile') !== false || function_exists('Plugin' . Core::toCamelCase($name) . 'Compile') !==
+ false) {
+ $pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN;
+ } // Helper plugin class compile
+ elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name)) !== false) {
+ $pluginType = Core::CLASS_PLUGIN | Core::COMPILABLE_PLUGIN;
+ } // Helper plugin function compile
+ elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name) . 'Compile') !== false) {
+ $pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN;
+ } // Smarty modifier
+ elseif (function_exists('smarty_modifier_' . $name) !== false) {
+ $pluginType = Core::SMARTY_MODIFIER;
+ } // Smarty function
+ elseif (function_exists('smarty_function_' . $name) !== false) {
+ $pluginType = Core::SMARTY_FUNCTION;
+ } // Smarty block
+ elseif (function_exists('smarty_block_' . $name) !== false) {
+ $pluginType = Core::SMARTY_BLOCK;
+ } // Everything else
+ else {
+ if ($pluginType === - 1) {
+ try {
+ $this->getCore()->getLoader()->loadPlugin('Plugin' . Core::toCamelCase($name));
+ }
+ catch (Exception $e) {
+ if (isset($phpFunc)) {
+ $pluginType = Core::NATIVE_PLUGIN;
+ } elseif (is_object($this->getCore()->getPluginProxy()) && $this->getCore()->getPluginProxy()->handles($name)) {
+ $pluginType = Core::PROXY_PLUGIN;
+ break;
+ } else {
+ throw $e;
+ }
+ }
+ } else {
+ throw new Exception('Plugin "' . $name . '" could not be found, type:' . $pluginType);
+ }
+ ++ $pluginType;
+ }
+ }
+
+ if (($pluginType & Core::COMPILABLE_PLUGIN) === 0 && ($pluginType & Core::NATIVE_PLUGIN) === 0 && ($pluginType & Core::PROXY_PLUGIN) === 0) {
+ $this->addUsedPlugin(Core::toCamelCase($name), $pluginType);
+ }
+
+ return $pluginType;
+ }
+
+ /**
+ * Allows a plugin to load another one at compile time, this will also mark
+ * it as used by this template so it will be loaded at runtime (which can be
+ * useful for compiled plugins that rely on another plugin when their compiled
+ * code runs).
+ *
+ * @param string $name the plugin name
+ *
+ * @return void
+ */
+ public function loadPlugin($name)
+ {
+ $this->getPluginType($name);
+ }
+
+ /**
+ * Runs htmlentities over the matched blocks when the security policy enforces that.
+ *
+ * @param array $match matched php block
+ *
+ * @return string the htmlentities-converted string
+ */
+ protected function phpTagEncodingHelper($match)
+ {
+ return htmlspecialchars($match[0]);
+ }
+
+ /**
+ * Maps the parameters received from the template onto the parameters required by the given callback.
+ *
+ * @param array $params the array of parameters
+ * @param callback $callback the function or method to reflect on to find out the required parameters
+ * @param int $callType the type of call in the template, 0 = no params, 1 = php-style call, 2 = named
+ * parameters call
+ * @param array $map the parameter map to use, if not provided it will be built from the callback
+ *
+ * @return array parameters sorted in the correct order with missing optional parameters filled
+ * @throws CompilationException
+ */
+ protected function mapParams(array $params, $callback, $callType = 2, $map = null)
+ {
+ if (!$map) {
+ $map = $this->getParamMap($callback);
+ }
+
+ $paramlist = array();
+
+ // transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values))
+ $ps = array();
+ foreach ($params as $p) {
+ if (is_array($p[1])) {
+ $ps[$p[0]] = $p[1];
+ } else {
+ $ps[] = $p;
+ }
+ }
+
+ // loops over the param map and assigns values from the template or default value for unset optional params
+ foreach ($map as $k => $v){
+ if ($v[0] === '*') {
+ // "rest" array parameter, fill every remaining params in it and then break
+ if (count($ps) === 0) {
+ if ($v[1] === false) {
+ throw new CompilationException(
+ $this, 'Rest argument missing for ' . str_replace(
+ array(
+ Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin',
+ 'Compile'
+ ), '', (is_array($callback) ? $callback[0] : $callback)
+ )
+ );
+ } else {
+ break;
+ }
+ }
+ $tmp = array();
+ $tmp2 = array();
+ $tmp3 = array();
+ foreach ($ps as $i => $p) {
+ $tmp[$i] = $p[0];
+ $tmp2[$i] = $p[1];
+ $tmp3[$i] = isset($p[2]) ? $p[2] : 0;
+ unset($ps[$i]);
+ }
+ $paramlist[$v[0]] = array($tmp, $tmp2, $tmp3);
+ unset($tmp, $tmp2, $i, $p);
+ break;
+ } elseif (isset($ps[$v[0]])) {
+ // parameter is defined as named param
+ $paramlist[$v[0]] = $ps[$v[0]];
+ unset($ps[$v[0]]);
+ } elseif (isset($ps[$k])) {
+ // parameter is defined as ordered param
+ $paramlist[$v[0]] = $ps[$k];
+ unset($ps[$k]);
+ } elseif ($v[1] === false) {
+ // parameter is not defined and not optional, throw error
+ if (is_array($callback)) {
+ if (is_object($callback[0])) {
+ $name = get_class($callback[0]) . '::' . $callback[1];
+ } else {
+ $name = $callback[0];
+ }
+ } else {
+ $name = $callback;
+ }
+
+ throw new CompilationException(
+ $this, 'Argument ' . $k . '/' . $v[0] . ' missing for ' . str_replace(
+ array(
+ Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin',
+ 'Compile'
+ ), '', $name
+ )
+ );
+ } elseif ($v[2] === null) {
+ // enforce lowercased null if default value is null (php outputs NULL with var export)
+ $paramlist[$v[0]] = array('null', null, self::T_NULL);
+ } else {
+ // outputs default value with var_export
+ $paramlist[$v[0]] = array(var_export($v[2], true), $v[2]);
+ }
+ }
+
+ if (count($ps)) {
+ foreach ($ps as $i => $p) {
+ array_push($paramlist, $p);
+ }
+ }
+
+ return $paramlist;
+ }
+
+ /**
+ * Returns the parameter map of the given callback, it filters out entries typed as Dwoo and Compiler and turns the
+ * rest parameter into a "*".
+ *
+ * @param callback $callback the function/method to reflect on
+ *
+ * @return array processed parameter map
+ */
+ protected function getParamMap($callback)
+ {
+ if (is_null($callback)) {
+ return array(array('*', true));
+ }
+ if (is_array($callback)) {
+ $ref = new ReflectionMethod($callback[0], $callback[1]);
+ } else {
+ $ref = new ReflectionFunction($callback);
+ }
+
+ $out = array();
+ foreach ($ref->getParameters() as $param) {
+ if (($class = $param->getClass()) !== null && $class->name === 'Dwoo\Core') {
+ continue;
+ }
+ if (($class = $param->getClass()) !== null && $class->name === 'Dwoo\Compiler') {
+ continue;
+ }
+ if ($param->getName() === 'rest' && $param->isArray() === true) {
+ $out[] = array('*', $param->isOptional(), null);
+ continue;
+ }
+ $out[] = array(
+ $param->getName(),
+ $param->isOptional(),
+ $param->isOptional() ? $param->getDefaultValue() : null
+ );
+ }
+
+ return $out;
+ }
+
+ /**
+ * Returns a default instance of this compiler, used by default by all Dwoo templates that do not have a
+ * specific compiler assigned and when you do not override the default compiler factory function.
+ *
+ * @see Core::setDefaultCompilerFactory()
+ * @return Compiler
+ */
+ public static function compilerFactory()
+ {
+ if (self::$instance === null) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+}
diff --git a/core/l/Dwoo/Core.php b/core/l/Dwoo/Core.php
new file mode 100644
index 0000000..a0386c0
--- /dev/null
+++ b/core/l/Dwoo/Core.php
@@ -0,0 +1,1792 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.3.6
+ * @date 2017-03-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+use ArrayAccess;
+use Closure;
+use Countable;
+use Dwoo\Plugins\Blocks\PluginDynamic;
+use Dwoo\Security\Policy as SecurityPolicy;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\Template\File as TemplateFile;
+use Iterator;
+use stdClass;
+use Traversable;
+
+/**
+ * Main dwoo class, allows communication between the compiler, template and data classes.
+ *
+ * requirements :
+ * php 5.3.0 or above (might work below, it's a rough estimate)
+ * SPL and PCRE extensions (for php versions prior to 5.3.0)
+ * mbstring extension for some string manipulation plugins (especially if you intend to use UTF-8)
+ * recommended :
+ * hash extension (for Dwoo\Template\Str - minor performance boost)
+ * project created :
+ * 2008-01-05
+ *
+ * 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.
+ */
+class Core
+{
+ /**
+ * Current version number.
+ *
+ * @var string
+ */
+ const VERSION = '1.3.6';
+
+ /**
+ * Unique number of this dwoo release, based on version number.
+ * this can be used by templates classes to check whether the compiled template
+ * has been compiled before this release or not, so that old templates are
+ * recompiled automatically when Dwoo is updated
+ */
+ const RELEASE_TAG = 136;
+
+ /**
+ * Constants that represents all plugin types
+ * these are bitwise-operation-safe values to allow multiple types
+ * on a single plugin
+ *
+ * @var int
+ */
+ const CLASS_PLUGIN = 1;
+ const FUNC_PLUGIN = 2;
+ const NATIVE_PLUGIN = 4;
+ const BLOCK_PLUGIN = 8;
+ const COMPILABLE_PLUGIN = 16;
+ const CUSTOM_PLUGIN = 32;
+ const SMARTY_MODIFIER = 64;
+ const SMARTY_BLOCK = 128;
+ const SMARTY_FUNCTION = 256;
+ const PROXY_PLUGIN = 512;
+ const TEMPLATE_PLUGIN = 1024;
+
+ /**
+ * Constant to default namespaces of builtin plugins
+ *
+ * @var string
+ */
+ const NAMESPACE_PLUGINS_BLOCKS = 'Dwoo\Plugins\Blocks\\';
+ const NAMESPACE_PLUGINS_FILTERS = 'Dwoo\Plugins\Filters\\';
+ const NAMESPACE_PLUGINS_FUNCTIONS = 'Dwoo\Plugins\Functions\\';
+ const NAMESPACE_PLUGINS_HELPERS = 'Dwoo\Plugins\Helpers\\';
+ const NAMESPACE_PLUGINS_PROCESSORS = 'Dwoo\Plugins\Processors\\';
+
+ /**
+ * Character set of the template, used by string manipulation plugins.
+ * it must be lowercase, but setCharset() will take care of that
+ *
+ * @see setCharset
+ * @see getCharset
+ * @var string
+ */
+ protected $charset = 'UTF-8';
+
+ /**
+ * Global variables that are accessible through $dwoo.* in the templates.
+ * default values include:
+ * $dwoo.version - current version number
+ * $dwoo.ad - a Powered by Dwoo link pointing to dwoo.org
+ * $dwoo.now - the current time
+ * $dwoo.template - the current template filename
+ * $dwoo.charset - the character set used by the template
+ * on top of that, foreach and other plugins can store special values in there,
+ * see their documentation for more details.
+ *
+ * @var array
+ */
+ protected $globals = array();
+
+ /**
+ * Directory where the compiled templates are stored.
+ * defaults to DWOO_COMPILEDIR (= dwoo_dir/compiled by default)
+ *
+ * @var string
+ */
+ protected $compileDir;
+
+ /**
+ * Directory where the cached templates are stored.
+ * defaults to DWOO_CACHEDIR (= dwoo_dir/cache by default)
+ *
+ * @var string
+ */
+ protected $cacheDir;
+
+ /**
+ * Directory where the template files are stored
+ *
+ * @var array
+ */
+ protected $templateDir = array();
+
+ /**
+ * Defines how long (in seconds) the cached files must remain valid.
+ * can be overridden on a per-template basis
+ * -1 = never delete
+ * 0 = disabled
+ * >0 = duration in seconds
+ *
+ * @var int
+ */
+ protected $cacheTime = 0;
+
+ /**
+ * Security policy object.
+ *
+ * @var SecurityPolicy
+ */
+ protected $securityPolicy = null;
+
+ /**
+ * Stores the custom plugins callbacks.
+ *
+ * @see addPlugin
+ * @see removePlugin
+ * @var array
+ */
+ protected $plugins = array();
+
+ /**
+ * Stores the filter callbacks.
+ *
+ * @see addFilter
+ * @see removeFilter
+ * @var array
+ */
+ protected $filters = array();
+
+ /**
+ * Stores the resource types and associated
+ * classes / compiler classes.
+ *
+ * @var array
+ */
+ protected $resources = array(
+ 'file' => array(
+ 'class' => 'Dwoo\Template\File',
+ 'compiler' => null,
+ ),
+ 'string' => array(
+ 'class' => 'Dwoo\Template\Str',
+ 'compiler' => null,
+ ),
+ );
+
+ /**
+ * The dwoo loader object used to load plugins by this dwoo instance.
+ *
+ * @var ILoader
+ */
+ protected $loader = null;
+
+ /**
+ * Currently rendered template, set to null when not-rendering.
+ *
+ * @var ITemplate
+ */
+ protected $template = null;
+
+ /**
+ * Stores the instances of the class plugins during template runtime.
+ *
+ * @var array
+ */
+ protected $runtimePlugins = array();
+
+ /**
+ * Stores the returned values during template runtime.
+ *
+ * @var array
+ */
+ protected $returnData = array();
+
+ /**
+ * Stores the data during template runtime.
+ *
+ * @var array
+ */
+ protected $data = array();
+
+ /**
+ * Stores the current scope during template runtime.
+ * this should ideally not be accessed directly from outside template code
+ *
+ * @var mixed
+ */
+ public $scope;
+
+ /**
+ * Stores the scope tree during template runtime.
+ *
+ * @var array
+ */
+ protected $scopeTree = array();
+
+ /**
+ * Stores the block plugins stack during template runtime.
+ *
+ * @var array
+ */
+ protected $stack = array();
+
+ /**
+ * Stores the current block plugin at the top of the stack during template runtime.
+ *
+ * @var BlockPlugin
+ */
+ protected $curBlock;
+
+ /**
+ * Stores the output buffer during template runtime.
+ *
+ * @var string
+ */
+ protected $buffer;
+
+ /**
+ * Stores plugin proxy.
+ *
+ * @var IPluginProxy
+ */
+ protected $pluginProxy;
+
+ /**
+ * Constructor, sets the cache and compile dir to the default values if not provided.
+ *
+ * @param string $compileDir path to the compiled directory, defaults to lib/compiled
+ * @param string $cacheDir path to the cache directory, defaults to lib/cache
+ */
+ public function __construct($compileDir = null, $cacheDir = null)
+ {
+ if ($compileDir !== null) {
+ $this->setCompileDir($compileDir);
+ }
+ if ($cacheDir !== null) {
+ $this->setCacheDir($cacheDir);
+ }
+ $this->initGlobals();
+ }
+
+ /**
+ * Resets some runtime variables to allow a cloned object to be used to render sub-templates.
+ *
+ * @return void
+ */
+ public function __clone()
+ {
+ $this->template = null;
+ unset($this->data);
+ unset($this->returnData);
+ }
+
+ /**
+ * Returns the given template rendered using the provided data and optional compiler.
+ *
+ * @param mixed $_tpl template, can either be a ITemplate object (i.e. TemplateFile), a
+ * valid path to a template, or a template as a string it is recommended to
+ * provide a ITemplate as it will probably make things faster, especially if
+ * you render a template multiple times
+ * @param mixed $data the data to use, can either be a IDataProvider object (i.e. Data) or
+ * an associative array. if you're rendering the template from cache, it can be
+ * left null
+ * @param ICompiler $_compiler the compiler that must be used to compile the template, if left empty a default
+ * Compiler will be used
+ *
+ * @return string|void or the template output if $output is false
+ * @throws Exception
+ */
+ public function get($_tpl, $data = array(), $_compiler = null)
+ {
+ // a render call came from within a template, so we need a new dwoo instance in order to avoid breaking this one
+ if ($this->template instanceof ITemplate) {
+ $clone = clone $this;
+
+ return $clone->get($_tpl, $data, $_compiler);
+ }
+
+ // auto-create template if required
+ if ($_tpl instanceof ITemplate) {
+ // valid, skip
+ } elseif (is_string($_tpl)) {
+ $_tpl = new TemplateFile($_tpl);
+ $_tpl->setIncludePath($this->getTemplateDir());
+ } else {
+ throw new Exception('Dwoo->get\'s first argument must be a ITemplate (i.e. TemplateFile) or a valid path to a template file', E_USER_NOTICE);
+ }
+
+ // save the current template, enters render mode at the same time
+ // if another rendering is requested it will be proxied to a new Core(instance
+ $this->template = $_tpl;
+
+ // load data
+ if ($data instanceof IDataProvider) {
+ $this->data = $data->getData();
+ } elseif (is_array($data)) {
+ $this->data = $data;
+ } elseif ($data instanceof ArrayAccess) {
+ $this->data = $data;
+ } else {
+ throw new Exception('Dwoo->get/Dwoo->output\'s data argument must be a IDataProvider object (i.e. Data) or an associative array', E_USER_NOTICE);
+ }
+
+ $this->addGlobal('template', $_tpl->getName());
+ $this->initRuntimeVars($_tpl);
+
+ // try to get cached template
+ $file = $_tpl->getCachedTemplate($this);
+ $doCache = $file === true;
+ $cacheLoaded = is_string($file);
+
+ if ($cacheLoaded === true) {
+ // cache is present, run it
+ ob_start();
+ include $file;
+ $this->template = null;
+
+ return ob_get_clean();
+ } else {
+ $dynamicId = uniqid();
+
+ // render template
+ $compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler);
+ $out = include $compiledTemplate;
+
+ // template returned false so it needs to be recompiled
+ if ($out === false) {
+ $_tpl->forceCompilation();
+ $compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler);
+ $out = include $compiledTemplate;
+ }
+
+ if ($doCache === true) {
+ $out = preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '', $out);
+ if (!class_exists(self::NAMESPACE_PLUGINS_BLOCKS . 'PluginDynamic')) {
+ $this->getLoader()->loadPlugin('PluginDynamic');
+ }
+ $out = PluginDynamic::unescape($out, $dynamicId, $compiledTemplate);
+ }
+
+ // process filters
+ foreach ($this->filters as $filter) {
+ if (is_array($filter) && $filter[0] instanceof Filter) {
+ $out = call_user_func($filter, $out);
+ } else {
+ $out = call_user_func($filter, $this, $out);
+ }
+ }
+
+ if ($doCache === true) {
+ // building cache
+ $file = $_tpl->cache($this, $out);
+
+ // run it from the cache to be sure dynamics are rendered
+ ob_start();
+ include $file;
+ // exit render mode
+ $this->template = null;
+
+ return ob_get_clean();
+ } else {
+ // no need to build cache
+ // exit render mode
+ $this->template = null;
+
+ return $out;
+ }
+ }
+ }
+
+ /**
+ * Registers a Global.
+ * New globals can be added before compiling or rendering a template
+ * but after, you can only update existing globals.
+ *
+ * @param string $name
+ * @param mixed $value
+ *
+ * @return $this
+ * @throws Exception
+ */
+ public function addGlobal($name, $value)
+ {
+ if (null === $this->globals) {
+ $this->initGlobals();
+ }
+
+ $this->globals[$name] = $value;
+
+ return $this;
+ }
+
+ /**
+ * Gets the registered Globals.
+ *
+ * @return array
+ */
+ public function getGlobals()
+ {
+ return $this->globals;
+ }
+
+ /**
+ * Re-initializes the globals array before each template run.
+ * this method is only callede once when the Dwoo object is created
+ *
+ * @return void
+ */
+ protected function initGlobals()
+ {
+ $this->globals = array(
+ 'version' => self::VERSION,
+ 'ad' => 'Powered by Dwoo',
+ 'now' => $_SERVER['REQUEST_TIME'],
+ 'charset' => $this->getCharset(),
+ );
+ }
+
+ /**
+ * Re-initializes the runtime variables before each template run.
+ * override this method to inject data in the globals array if needed, this
+ * method is called before each template execution
+ *
+ * @param ITemplate $tpl the template that is going to be rendered
+ *
+ * @return void
+ */
+ protected function initRuntimeVars(ITemplate $tpl)
+ {
+ $this->runtimePlugins = array();
+ $this->scope = &$this->data;
+ $this->scopeTree = array();
+ $this->stack = array();
+ $this->curBlock = null;
+ $this->buffer = '';
+ $this->returnData = array();
+ }
+
+ /**
+ * Adds a custom plugin that is not in one of the plugin directories.
+ *
+ * @param string $name the plugin name to be used in the templates
+ * @param callback $callback the plugin callback, either a function name,
+ * a class name or an array containing an object
+ * or class name and a method name
+ * @param bool $compilable if set to true, the plugin is assumed to be compilable
+ *
+ * @return void
+ * @throws Exception
+ */
+ public function addPlugin($name, $callback, $compilable = false)
+ {
+ $compilable = $compilable ? self::COMPILABLE_PLUGIN : 0;
+ if (is_array($callback)) {
+ if (is_subclass_of(is_object($callback[0]) ? get_class($callback[0]) : $callback[0], 'Dwoo\Block\Plugin')) {
+ $this->plugins[$name] = array(
+ 'type' => self::BLOCK_PLUGIN | $compilable,
+ 'callback' => $callback,
+ 'class' => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0])
+ );
+ } else {
+ $this->plugins[$name] = array(
+ 'type' => self::CLASS_PLUGIN | $compilable,
+ 'callback' => $callback,
+ 'class' => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0]),
+ 'function' => $callback[1]
+ );
+ }
+ } elseif (is_string($callback)) {
+ if (class_exists($callback)) {
+ if (is_subclass_of($callback, 'Dwoo\Block\Plugin')) {
+ $this->plugins[$name] = array(
+ 'type' => self::BLOCK_PLUGIN | $compilable,
+ 'callback' => $callback,
+ 'class' => $callback
+ );
+ } else {
+ $this->plugins[$name] = array(
+ 'type' => self::CLASS_PLUGIN | $compilable,
+ 'callback' => $callback,
+ 'class' => $callback,
+ 'function' => ($compilable ? 'compile' : 'process')
+ );
+ }
+ } elseif (function_exists($callback)) {
+ $this->plugins[$name] = array(
+ 'type' => self::FUNC_PLUGIN | $compilable,
+ 'callback' => $callback
+ );
+ } else {
+ throw new Exception('Callback could not be processed correctly, please check that the function/class you used exists');
+ }
+ } elseif ($callback instanceof Closure) {
+ $this->plugins[$name] = array(
+ 'type' => self::FUNC_PLUGIN | $compilable,
+ 'callback' => $callback
+ );
+ } elseif (is_object($callback)) {
+ if (is_subclass_of($callback, 'Dwoo\Block\Plugin')) {
+ $this->plugins[$name] = array(
+ 'type' => self::BLOCK_PLUGIN | $compilable,
+ 'callback' => get_class($callback),
+ 'class' => $callback
+ );
+ } else {
+ $this->plugins[$name] = array(
+ 'type' => self::CLASS_PLUGIN | $compilable,
+ 'callback' => $callback,
+ 'class' => $callback,
+ 'function' => ($compilable ? 'compile' : 'process')
+ );
+ }
+ } else {
+ throw new Exception('Callback could not be processed correctly, please check that the function/class you used exists');
+ }
+ }
+
+ /**
+ * Removes a custom plugin.
+ *
+ * @param string $name the plugin name
+ *
+ * @return void
+ */
+ public function removePlugin($name)
+ {
+ if (isset($this->plugins[$name])) {
+ unset($this->plugins[$name]);
+ }
+ }
+
+ /**
+ * Adds a filter to this Dwoo instance, it will be used to filter the output of all the templates rendered by this
+ * instance.
+ *
+ * @param mixed $callback a callback or a filter name if it is autoloaded from a plugin directory
+ * @param bool $autoload if true, the first parameter must be a filter name from one of the plugin directories
+ *
+ * @return void
+ * @throws Exception
+ */
+ public function addFilter($callback, $autoload = false)
+ {
+ if ($autoload) {
+ $class = self::NAMESPACE_PLUGINS_FILTERS . self::toCamelCase($callback);
+ if (!class_exists($class) && !function_exists($class)) {
+ try {
+ $this->getLoader()->loadPlugin($callback);
+ }
+ catch (Exception $e) {
+ if (strstr($callback, self::NAMESPACE_PLUGINS_FILTERS)) {
+ throw new Exception('Wrong filter name : ' . $callback . ', the "Filter" prefix should not be used, please only use "' . str_replace('Filter', '', $callback) . '"');
+ } else {
+ throw new Exception('Wrong filter name : ' . $callback . ', when using autoload the filter must be in one of your plugin dir as "name.php" containig a class or function named "Filter"');
+ }
+ }
+ }
+
+ if (class_exists($class)) {
+ $callback = array(new $class($this), 'process');
+ } elseif (function_exists($class)) {
+ $callback = $class;
+ } else {
+ throw new Exception('Wrong filter name : ' . $callback . ', when using autoload the filter must be in one of your plugin dir as "name.php" containig a class or function named "Filter"');
+ }
+
+ $this->filters[] = $callback;
+ } else {
+ $this->filters[] = $callback;
+ }
+ }
+
+ /**
+ * Removes a filter.
+ *
+ * @param mixed $callback callback or filter name if it was autoloaded
+ *
+ * @return void
+ */
+ public function removeFilter($callback)
+ {
+ if (($index = array_search(self::NAMESPACE_PLUGINS_FILTERS. 'Filter' . self::toCamelCase($callback), $this->filters,
+ true)) !==
+ false) {
+ unset($this->filters[$index]);
+ } elseif (($index = array_search($callback, $this->filters, true)) !== false) {
+ unset($this->filters[$index]);
+ } else {
+ $class = self::NAMESPACE_PLUGINS_FILTERS . 'Filter' . $callback;
+ foreach ($this->filters as $index => $filter) {
+ if (is_array($filter) && $filter[0] instanceof $class) {
+ unset($this->filters[$index]);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds a resource or overrides a default one.
+ *
+ * @param string $name the resource name
+ * @param string $class the resource class (which must implement ITemplate)
+ * @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 Compiler object
+ *
+ * @return void
+ * @throws Exception
+ */
+ public function addResource($name, $class, $compilerFactory = null)
+ {
+ if (strlen($name) < 2) {
+ throw new Exception('Resource names must be at least two-character long to avoid conflicts with Windows paths');
+ }
+
+ if (!class_exists($class)) {
+ throw new Exception(sprintf('Resource class %s does not exist', $class));
+ }
+
+ $interfaces = class_implements($class);
+ if (in_array('Dwoo\ITemplate', $interfaces) === false) {
+ throw new Exception('Resource class must implement ITemplate');
+ }
+
+ $this->resources[$name] = array(
+ 'class' => $class,
+ 'compiler' => $compilerFactory
+ );
+ }
+
+ /**
+ * Removes a custom resource.
+ *
+ * @param string $name the resource name
+ *
+ * @return void
+ */
+ public function removeResource($name)
+ {
+ unset($this->resources[$name]);
+ if ($name === 'file') {
+ $this->resources['file'] = array(
+ 'class' => 'Dwoo\Template\File',
+ 'compiler' => null
+ );
+ }
+ }
+
+ /**
+ * Sets the loader object to use to load plugins.
+ *
+ * @param ILoader $loader loader
+ *
+ * @return void
+ */
+ public function setLoader(ILoader $loader)
+ {
+ $this->loader = $loader;
+ }
+
+ /**
+ * Returns the current loader object or a default one if none is currently found.
+ *
+ * @return ILoader|Loader
+ */
+ public function getLoader()
+ {
+ if ($this->loader === null) {
+ $this->loader = new Loader($this->getCompileDir());
+ }
+
+ return $this->loader;
+ }
+
+ /**
+ * Returns the custom plugins loaded.
+ * Used by the ITemplate classes to pass the custom plugins to their ICompiler instance.
+ *
+ * @return array
+ */
+ public function getCustomPlugins()
+ {
+ return $this->plugins;
+ }
+
+ /**
+ * Return a specified custom plugin loaded by his name.
+ * Used by the compiler, for executing a Closure.
+ *
+ * @param string $name
+ *
+ * @return mixed|null
+ */
+ public function getCustomPlugin($name)
+ {
+ if (isset($this->plugins[$name])) {
+ return $this->plugins[$name]['callback'];
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the cache directory with a trailing DIRECTORY_SEPARATOR.
+ *
+ * @return string
+ */
+ public function getCacheDir()
+ {
+ if ($this->cacheDir === null) {
+ $this->setCacheDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR);
+ }
+
+ return $this->cacheDir;
+ }
+
+ /**
+ * Sets the cache directory and automatically appends a DIRECTORY_SEPARATOR.
+ *
+ * @param string $dir the cache directory
+ *
+ * @return void
+ * @throws Exception
+ */
+ public function setCacheDir($dir)
+ {
+ $this->cacheDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
+ if (!file_exists($this->cacheDir)) {
+ mkdir($this->cacheDir, 0777, true);
+ }
+ if (is_writable($this->cacheDir) === false) {
+ throw new Exception('The cache directory must be writable, chmod "' . $this->cacheDir . '" to make it writable');
+ }
+ }
+
+ /**
+ * Returns the compile directory with a trailing DIRECTORY_SEPARATOR.
+ *
+ * @return string
+ */
+ public function getCompileDir()
+ {
+ if ($this->compileDir === null) {
+ $this->setCompileDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR);
+ }
+
+ return $this->compileDir;
+ }
+
+ /**
+ * Sets the compile directory and automatically appends a DIRECTORY_SEPARATOR.
+ *
+ * @param string $dir the compile directory
+ *
+ * @return void
+ * @throws Exception
+ */
+ public function setCompileDir($dir)
+ {
+ $this->compileDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
+ if (!file_exists($this->compileDir)) {
+ mkdir($this->compileDir, 0777, true);
+ }
+ if (is_writable($this->compileDir) === false) {
+ throw new Exception('The compile directory must be writable, chmod "' . $this->compileDir . '" to make it writable');
+ }
+ }
+
+ /**
+ * Returns an array of the template directory with a trailing DIRECTORY_SEPARATOR
+ *
+ * @return array
+ */
+ public function getTemplateDir()
+ {
+ return $this->templateDir;
+ }
+
+ /**
+ * sets the template directory and automatically appends a DIRECTORY_SEPARATOR
+ * template directory is stored in an array
+ *
+ * @param string $dir
+ *
+ * @throws Exception
+ */
+ public function setTemplateDir($dir)
+ {
+ $tmpDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
+ if (is_dir($tmpDir) === false) {
+ throw new Exception('The template directory: "' . $tmpDir . '" does not exists, create the directory or specify an other location !');
+ }
+ $this->templateDir[] = $tmpDir;
+ }
+
+ /**
+ * Returns the default cache time that is used with templates that do not have a cache time set.
+ *
+ * @return int the duration in seconds
+ */
+ public function getCacheTime()
+ {
+ return $this->cacheTime;
+ }
+
+ /**
+ * Sets the default cache time to use with templates that do not have a cache time set.
+ *
+ * @param int $seconds the duration in seconds
+ *
+ * @return void
+ */
+ public function setCacheTime($seconds)
+ {
+ $this->cacheTime = (int)$seconds;
+ }
+
+ /**
+ * Returns the character set used by the string manipulation plugins.
+ * the charset is automatically lowercased
+ *
+ * @return string
+ */
+ public function getCharset()
+ {
+ return $this->charset;
+ }
+
+ /**
+ * Sets the character set used by the string manipulation plugins.
+ * the charset will be automatically lowercased
+ *
+ * @param string $charset the character set
+ *
+ * @return void
+ */
+ public function setCharset($charset)
+ {
+ $this->charset = strtolower((string)$charset);
+ }
+
+ /**
+ * Returns the current template being rendered, when applicable, or null.
+ *
+ * @return ITemplate|null
+ */
+ public function getTemplate()
+ {
+ return $this->template;
+ }
+
+ /**
+ * Sets the current template being rendered.
+ *
+ * @param ITemplate $tpl template object
+ *
+ * @return void
+ */
+ public function setTemplate(ITemplate $tpl)
+ {
+ $this->template = $tpl;
+ }
+
+ /**
+ * Sets the default compiler factory function for the given resource name.
+ * a compiler factory must return a ICompiler object pre-configured to fit your needs
+ *
+ * @param string $resourceName the resource name (i.e. file, string)
+ * @param callback $compilerFactory the compiler factory callback
+ *
+ * @return void
+ */
+ public function setDefaultCompilerFactory($resourceName, $compilerFactory)
+ {
+ $this->resources[$resourceName]['compiler'] = $compilerFactory;
+ }
+
+ /**
+ * Returns the default compiler factory function for the given resource name.
+ *
+ * @param string $resourceName the resource name
+ *
+ * @return callback the compiler factory callback
+ */
+ public function getDefaultCompilerFactory($resourceName)
+ {
+ return $this->resources[$resourceName]['compiler'];
+ }
+
+ /**
+ * Sets the security policy object to enforce some php security settings.
+ * use this if untrusted persons can modify templates
+ *
+ * @param SecurityPolicy $policy the security policy object
+ *
+ * @return void
+ */
+ public function setSecurityPolicy(SecurityPolicy $policy = null)
+ {
+ $this->securityPolicy = $policy;
+ }
+
+ /**
+ * Returns the current security policy object or null by default.
+ *
+ * @return SecurityPolicy|null the security policy object if any
+ */
+ public function getSecurityPolicy()
+ {
+ return $this->securityPolicy;
+ }
+
+ /**
+ * Sets the object that must be used as a plugin proxy when plugin can't be found
+ * by dwoo's loader.
+ *
+ * @param IPluginProxy $pluginProxy the proxy object
+ *
+ * @return void
+ */
+ public function setPluginProxy(IPluginProxy $pluginProxy)
+ {
+ $this->pluginProxy = $pluginProxy;
+ }
+
+ /**
+ * Returns the current plugin proxy object or null by default.
+ *
+ * @return IPluginProxy
+ */
+ public function getPluginProxy()
+ {
+ return $this->pluginProxy;
+ }
+
+ /**
+ * Checks whether the given template is cached or not.
+ *
+ * @param ITemplate $tpl the template object
+ *
+ * @return bool
+ */
+ public function isCached(ITemplate $tpl)
+ {
+ return is_string($tpl->getCachedTemplate($this));
+ }
+
+ /**
+ * Clear templates inside the compiled directory.
+ *
+ * @return int
+ */
+ public function clearCompiled()
+ {
+ $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCompileDir()), \RecursiveIteratorIterator::SELF_FIRST);
+ $count = 0;
+ foreach ($iterator as $file) {
+ if ($file->isFile()) {
+ $count += unlink($file->__toString()) ? 1 : 0;
+ }
+ }
+
+ return $count;
+ }
+
+ /**
+ * Clears the cached templates if they are older than the given time.
+ *
+ * @param int $olderThan minimum time (in seconds) required for a cached template to be cleared
+ *
+ * @return int the amount of templates cleared
+ */
+ public function clearCache($olderThan = - 1)
+ {
+ $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCacheDir()), \RecursiveIteratorIterator::SELF_FIRST);
+ $expired = time() - $olderThan;
+ $count = 0;
+ foreach ($iterator as $file) {
+ if ($file->isFile() && $file->getCTime() < $expired) {
+ $count += unlink((string)$file) ? 1 : 0;
+ }
+ }
+
+ return $count;
+ }
+
+ /**
+ * Fetches a template object of the given resource.
+ *
+ * @param string $resourceName the resource name (i.e. file, string)
+ * @param string $resourceId the resource identifier (i.e. file path)
+ * @param int $cacheTime the cache time setting for this resource
+ * @param string $cacheId the unique cache identifier
+ * @param string $compileId the unique compiler identifier
+ * @param ITemplate $parentTemplate the parent template
+ *
+ * @return ITemplate
+ * @throws Exception
+ */
+ public function templateFactory($resourceName, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, ITemplate $parentTemplate = null)
+ {
+ if (isset($this->resources[$resourceName])) {
+ /**
+ * Interface ITemplate
+ *
+ * @var ITemplate $class
+ */
+ $class = $this->resources[$resourceName]['class'];
+
+ return $class::templateFactory($this, $resourceId, $cacheTime, $cacheId, $compileId, $parentTemplate);
+ }
+
+ throw new Exception('Unknown resource type : ' . $resourceName);
+ }
+
+ /**
+ * Checks if the input is an array or arrayaccess object, optionally it can also check if it's
+ * empty.
+ *
+ * @param mixed $value the variable to check
+ * @param bool $checkIsEmpty if true, the function will also check if the array|arrayaccess is empty,
+ * and return true only if it's not empty
+ *
+ * @return int|bool true if it's an array|arrayaccess (or the item count if $checkIsEmpty is true) or false if it's
+ * not an array|arrayaccess (or 0 if $checkIsEmpty is true)
+ */
+ public function isArray($value, $checkIsEmpty = false)
+ {
+ if (is_array($value) === true || $value instanceof ArrayAccess) {
+ if ($checkIsEmpty === false) {
+ return true;
+ }
+
+ return $this->count($value);
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if the input is an array or a traversable object, optionally it can also check if it's
+ * empty.
+ *
+ * @param mixed $value the variable to check
+ * @param bool $checkIsEmpty if true, the function will also check if the array|traversable is empty,
+ * and return true only if it's not empty
+ *
+ * @return int|bool true if it's an array|traversable (or the item count if $checkIsEmpty is true) or false if it's
+ * not an array|traversable (or 0 if $checkIsEmpty is true)
+ */
+ public function isTraversable($value, $checkIsEmpty = false)
+ {
+ if (is_array($value) === true) {
+ if ($checkIsEmpty === false) {
+ return true;
+ } else {
+ return count($value) > 0;
+ }
+ } elseif ($value instanceof Traversable) {
+ if ($checkIsEmpty === false) {
+ return true;
+ } else {
+ return $this->count($value);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Counts an array or arrayaccess/traversable object.
+ *
+ * @param mixed $value the value to count
+ *
+ * @return int|bool the count for arrays and objects that implement countable, true for other objects that don't,
+ * and 0 for empty elements
+ */
+ public function count($value)
+ {
+ if (is_array($value) === true || $value instanceof Countable) {
+ return count($value);
+ } elseif ($value instanceof ArrayAccess) {
+ if ($value->offsetExists(0)) {
+ return true;
+ }
+ } elseif ($value instanceof Iterator) {
+ $value->rewind();
+ if ($value->valid()) {
+ return true;
+ }
+ } elseif ($value instanceof Traversable) {
+ foreach ($value as $dummy) {
+ return true;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * Triggers a dwoo error.
+ *
+ * @param string $message the error message
+ * @param int $level the error level, one of the PHP's E_* constants
+ *
+ * @return void
+ */
+ public function triggerError($message, $level = E_USER_NOTICE)
+ {
+ if (!($tplIdentifier = $this->template->getResourceIdentifier())) {
+ $tplIdentifier = $this->template->getResourceName();
+ }
+ trigger_error('Dwoo error (in ' . $tplIdentifier . ') : ' . $message, $level);
+ }
+
+ /**
+ * Adds a block to the block stack.
+ *
+ * @param string $blockName the block name (without `Plugin` prefix)
+ * @param array $args the arguments to be passed to the block's init() function
+ *
+ * @return BlockPlugin the newly created block
+ */
+ public function addStack($blockName, array $args = array())
+ {
+ if (isset($this->plugins[$blockName])) {
+ $class = $this->plugins[$blockName]['class'];
+ } else {
+ $class = self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . self::toCamelCase($blockName);
+ }
+
+ if ($this->curBlock !== null) {
+ $this->curBlock->buffer(ob_get_contents());
+ ob_clean();
+ } else {
+ $this->buffer .= ob_get_contents();
+ ob_clean();
+ }
+
+ $block = new $class($this);
+
+ call_user_func_array(array($block, 'init'), $args);
+
+ $this->stack[] = $this->curBlock = $block;
+
+ return $block;
+ }
+
+ /**
+ * Removes the plugin at the top of the block stack.
+ * Calls the block buffer() function, followed by a call to end() and finally a call to process()
+ *
+ * @return void
+ */
+ public function delStack()
+ {
+ $args = func_get_args();
+
+ $this->curBlock->buffer(ob_get_contents());
+ ob_clean();
+
+ call_user_func_array(array($this->curBlock, 'end'), $args);
+
+ $tmp = array_pop($this->stack);
+
+ if (count($this->stack) > 0) {
+ $this->curBlock = end($this->stack);
+ $this->curBlock->buffer($tmp->process());
+ } else {
+ if ($this->buffer !== '') {
+ echo $this->buffer;
+ $this->buffer = '';
+ }
+ $this->curBlock = null;
+ echo $tmp->process();
+ }
+
+ unset($tmp);
+ }
+
+ /**
+ * Returns the parent block of the given block.
+ *
+ * @param BlockPlugin $block the block class plugin
+ *
+ * @return BlockPlugin|false if the given block isn't in the stack
+ */
+ public function getParentBlock(BlockPlugin $block)
+ {
+ $index = array_search($block, $this->stack, true);
+ if ($index !== false && $index > 0) {
+ return $this->stack[$index - 1];
+ }
+
+ return false;
+ }
+
+ /**
+ * Finds the closest block of the given type, starting at the top of the stack.
+ *
+ * @param string $type the type of plugin you want to find
+ *
+ * @return BlockPlugin|false if no plugin of such type is in the stack
+ */
+ public function findBlock($type)
+ {
+ if (isset($this->plugins[$type])) {
+ $type = $this->plugins[$type]['class'];
+ } else {
+ $type = self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin_' . str_replace(self::NAMESPACE_PLUGINS_BLOCKS.'Plugin',
+ '', $type);
+ }
+
+ $keys = array_keys($this->stack);
+ while (($key = array_pop($keys)) !== false) {
+ if ($this->stack[$key] instanceof $type) {
+ return $this->stack[$key];
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a Plugin of the given class.
+ * this is so a single instance of every class plugin is created at each template run,
+ * allowing class plugins to have "per-template-run" static variables
+ *
+ * @param string $class the class name
+ *
+ * @return mixed an object of the given class
+ */
+ public function getObjectPlugin($class)
+ {
+ if (isset($this->runtimePlugins[$class])) {
+ return $this->runtimePlugins[$class];
+ }
+
+ return $this->runtimePlugins[$class] = new $class($this);
+ }
+
+ /**
+ * Calls the process() method of the given class-plugin name.
+ *
+ * @param string $plugName the class plugin name (without `Plugin` prefix)
+ * @param array $params an array of parameters to send to the process() method
+ *
+ * @return string the process() return value
+ */
+ public function classCall($plugName, array $params = array())
+ {
+ $class = self::toCamelCase($plugName);
+ $plugin = $this->getObjectPlugin($class);
+
+ return call_user_func_array(array($plugin, 'process'), $params);
+ }
+
+ /**
+ * Calls a php function.
+ *
+ * @param string $callback the function to call
+ * @param array $params an array of parameters to send to the function
+ *
+ * @return mixed the return value of the called function
+ */
+ public function arrayMap($callback, array $params)
+ {
+ if ($params[0] === $this) {
+ $addThis = true;
+ array_shift($params);
+ }
+ if ((is_array($params[0]) || ($params[0] instanceof Iterator && $params[0] instanceof ArrayAccess))) {
+ if (empty($params[0])) {
+ return $params[0];
+ }
+
+ // array map
+ $out = array();
+ $cnt = count($params);
+
+ if (isset($addThis)) {
+ array_unshift($params, $this);
+ $items = $params[1];
+ $keys = array_keys($items);
+
+ if (is_string($callback) === false) {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params);
+ }
+ } elseif ($cnt === 1) {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = $callback($this, $items[$i]);
+ }
+ } elseif ($cnt === 2) {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = $callback($this, $items[$i], $params[2]);
+ }
+ } elseif ($cnt === 3) {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = $callback($this, $items[$i], $params[2], $params[3]);
+ }
+ } else {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params);
+ }
+ }
+ } else {
+ $items = $params[0];
+ $keys = array_keys($items);
+
+ if (is_string($callback) === false) {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = call_user_func_array($callback, array($items[$i]) + $params);
+ }
+ } elseif ($cnt === 1) {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = $callback($items[$i]);
+ }
+ } elseif ($cnt === 2) {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = $callback($items[$i], $params[1]);
+ }
+ } elseif ($cnt === 3) {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = $callback($items[$i], $params[1], $params[2]);
+ }
+ } elseif ($cnt === 4) {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = $callback($items[$i], $params[1], $params[2], $params[3]);
+ }
+ } else {
+ while (($i = array_shift($keys)) !== null) {
+ $out[] = call_user_func_array($callback, array($items[$i]) + $params);
+ }
+ }
+ }
+
+ return $out;
+ } else {
+ return $params[0];
+ }
+ }
+
+ /**
+ * Reads a variable into the given data array.
+ *
+ * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
+ * @param mixed $data the data array or object to read from
+ * @param bool $safeRead if true, the function will check whether the index exists to prevent any notices from
+ * being output
+ *
+ * @return mixed
+ */
+ public function readVarInto($varstr, $data, $safeRead = false)
+ {
+ if ($data === null) {
+ return null;
+ }
+
+ if (is_array($varstr) === false) {
+ preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m);
+ } else {
+ $m = $varstr;
+ }
+ unset($varstr);
+
+ foreach ($m[1] as $k => $sep) {
+ if ($sep === '.' || $sep === '[' || $sep === '') {
+ // strip enclosing quotes if present
+ $m[2][$k] = preg_replace('#^(["\']?)(.*?)\1$#', '$2', $m[2][$k]);
+
+ if ((is_array($data) || $data instanceof ArrayAccess) && ($safeRead === false || isset($data[$m[2][$k]]))) {
+ $data = $data[$m[2][$k]];
+ } else {
+ return null;
+ }
+ } else {
+ if (is_object($data) && ($safeRead === false || isset($data->{$m[2][$k]}))) {
+ $data = $data->{$m[2][$k]};
+ } else {
+ return null;
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ * Reads a variable into the parent scope.
+ *
+ * @param int $parentLevels the amount of parent levels to go from the current scope
+ * @param string $varstr the variable string, using dwoo variable syntax (i.e.
+ * "var.subvar[subsubvar]->property")
+ *
+ * @return mixed
+ */
+ public function readParentVar($parentLevels, $varstr = null)
+ {
+ $tree = $this->scopeTree;
+ $cur = $this->data;
+
+ while ($parentLevels -- !== 0) {
+ array_pop($tree);
+ }
+
+ while (($i = array_shift($tree)) !== null) {
+ if (is_object($cur)) {
+ $cur = $cur->{$i};
+ } else {
+ $cur = $cur[$i];
+ }
+ }
+
+ if ($varstr !== null) {
+ return $this->readVarInto($varstr, $cur);
+ } else {
+ return $cur;
+ }
+ }
+
+ /**
+ * Reads a variable into the current scope.
+ *
+ * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
+ *
+ * @return mixed
+ */
+ public function readVar($varstr)
+ {
+ if (is_array($varstr) === true) {
+ $m = $varstr;
+ unset($varstr);
+ } else {
+ if (strstr($varstr, '.') === false && strstr($varstr, '[') === false && strstr($varstr, '->') === false) {
+ if ($varstr === 'dwoo') {
+ return $this->getGlobals();
+ } elseif ($varstr === '__' || $varstr === '_root') {
+ return $this->data;
+ } elseif ($varstr === '_' || $varstr === '_parent') {
+ $varstr = '.' . $varstr;
+ $tree = $this->scopeTree;
+ $cur = $this->data;
+ array_pop($tree);
+
+ while (($i = array_shift($tree)) !== null) {
+ if (is_object($cur)) {
+ $cur = $cur->{$i};
+ } else {
+ $cur = $cur[$i];
+ }
+ }
+
+ return $cur;
+ }
+
+ $cur = $this->scope;
+
+ if (isset($cur[$varstr])) {
+ return $cur[$varstr];
+ } else {
+ return null;
+ }
+ }
+
+ if (substr($varstr, 0, 1) === '.') {
+ $varstr = 'dwoo' . $varstr;
+ }
+
+ preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m);
+ }
+
+ $i = $m[2][0];
+ if ($i === 'dwoo') {
+ $cur = $this->getGlobals();
+ array_shift($m[2]);
+ array_shift($m[1]);
+ switch ($m[2][0]) {
+ case 'get':
+ $cur = $_GET;
+ break;
+ case 'post':
+ $cur = $_POST;
+ break;
+ case 'session':
+ $cur = $_SESSION;
+ break;
+ case 'cookies':
+ case 'cookie':
+ $cur = $_COOKIE;
+ break;
+ case 'server':
+ $cur = $_SERVER;
+ break;
+ case 'env':
+ $cur = $_ENV;
+ break;
+ case 'request':
+ $cur = $_REQUEST;
+ break;
+ case 'const':
+ array_shift($m[2]);
+ if (defined($m[2][0])) {
+ return constant($m[2][0]);
+ } else {
+ return null;
+ }
+ }
+ if ($cur !== $this->getGlobals()) {
+ array_shift($m[2]);
+ array_shift($m[1]);
+ }
+ } elseif ($i === '__' || $i === '_root') {
+ $cur = $this->data;
+ array_shift($m[2]);
+ array_shift($m[1]);
+ } elseif ($i === '_' || $i === '_parent') {
+ $tree = $this->scopeTree;
+ $cur = $this->data;
+
+ while (true) {
+ array_pop($tree);
+ array_shift($m[2]);
+ array_shift($m[1]);
+ if (current($m[2]) === '_' || current($m[2]) === '_parent') {
+ continue;
+ }
+
+ while (($i = array_shift($tree)) !== null) {
+ if (is_object($cur)) {
+ $cur = $cur->{$i};
+ } else {
+ $cur = $cur[$i];
+ }
+ }
+ break;
+ }
+ } else {
+ $cur = $this->scope;
+ }
+
+ foreach ($m[1] as $k => $sep) {
+ if ($sep === '.' || $sep === '[' || $sep === '') {
+ if ((is_array($cur) || $cur instanceof ArrayAccess) && isset($cur[$m[2][$k]])) {
+ $cur = $cur[$m[2][$k]];
+ } else {
+ return null;
+ }
+ } elseif ($sep === '->') {
+ if (is_object($cur)) {
+ $cur = $cur->{$m[2][$k]};
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ return $cur;
+ }
+
+ /**
+ * Assign the value to the given variable.
+ *
+ * @param mixed $value the value to assign
+ * @param string $scope the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
+ *
+ * @return bool true if assigned correctly or false if a problem occured while parsing the var string
+ */
+ public function assignInScope($value, $scope)
+ {
+ if (!is_string($scope)) {
+ $this->triggerError('Assignments must be done into strings, (' . gettype($scope) . ') ' . var_export($scope, true) . ' given', E_USER_ERROR);
+ }
+ if (strstr($scope, '.') === false && strstr($scope, '->') === false) {
+ $this->scope[$scope] = $value;
+ } else {
+ // TODO handle _root/_parent scopes ?
+ preg_match_all('#(\[|->|\.)?([^.[\]-]+)\]?#i', $scope, $m);
+
+ $cur = &$this->scope;
+ $last = array(
+ array_pop($m[1]),
+ array_pop($m[2])
+ );
+
+ foreach ($m[1] as $k => $sep) {
+ if ($sep === '.' || $sep === '[' || $sep === '') {
+ if (is_array($cur) === false) {
+ $cur = array();
+ }
+ $cur = &$cur[$m[2][$k]];
+ } elseif ($sep === '->') {
+ if (is_object($cur) === false) {
+ $cur = new stdClass();
+ }
+ $cur = &$cur->{$m[2][$k]};
+ } else {
+ return false;
+ }
+ }
+
+ if ($last[0] === '.' || $last[0] === '[' || $last[0] === '') {
+ if (is_array($cur) === false) {
+ $cur = array();
+ }
+ $cur[$last[1]] = $value;
+ } elseif ($last[0] === '->') {
+ if (is_object($cur) === false) {
+ $cur = new stdClass();
+ }
+ $cur->{$last[1]} = $value;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Sets the scope to the given scope string or array.
+ *
+ * @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 (is_string($scope) === true) {
+ $scope = explode('.', $scope);
+ }
+
+ if ($absolute === true) {
+ $this->scope = &$this->data;
+ $this->scopeTree = array();
+ }
+
+ while (($bit = array_shift($scope)) !== null) {
+ if ($bit === '_' || $bit === '_parent') {
+ array_pop($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 === '__' || $bit === '_root') {
+ $this->scope = &$this->data;
+ $this->scopeTree = array();
+ } elseif (isset($this->scope[$bit])) {
+ if ($this->scope instanceof ArrayAccess) {
+ $tmp = $this->scope[$bit];
+ $this->scope = &$tmp;
+ } else {
+ $this->scope = &$this->scope[$bit];
+ }
+ $this->scopeTree[] = $bit;
+ } else {
+ unset($this->scope);
+ $this->scope = null;
+ }
+ }
+
+ return $old;
+ }
+
+ /**
+ * Returns the entire data array.
+ *
+ * @return array
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Sets a return value for the currently running template.
+ *
+ * @param string $name var name
+ * @param mixed $value var value
+ *
+ * @return void
+ */
+ public function setReturnValue($name, $value)
+ {
+ $this->returnData[$name] = $value;
+ }
+
+ /**
+ * Retrieves the return values set by the template.
+ *
+ * @return array
+ */
+ public function getReturnValues()
+ {
+ return $this->returnData;
+ }
+
+ /**
+ * Returns a reference to the current scope.
+ *
+ * @return mixed
+ */
+ public function &getScope()
+ {
+ return $this->scope;
+ }
+
+ /**
+ * Redirects all calls to unexisting to plugin proxy.
+ *
+ * @param string $method the method name
+ * @param array $args array of arguments
+ *
+ * @return mixed
+ * @throws Exception
+ */
+ public function __call($method, $args)
+ {
+ $proxy = $this->getPluginProxy();
+ if (!$proxy) {
+ throw new Exception('Call to undefined method ' . __CLASS__ . '::' . $method . '()');
+ }
+
+ return call_user_func_array($proxy->getCallback($method), $args);
+ }
+
+ /**
+ * Convert plugin name from `auto_escape` to `AutoEscape`.
+ * @param string $input
+ * @param string $separator
+ *
+ * @return mixed
+ */
+ public static function toCamelCase($input, $separator = '_')
+ {
+ return join(array_map('ucfirst', explode($separator, $input)));
+
+ // TODO >= PHP5.4.32
+ //return str_replace($separator, '', ucwords($input, $separator));
+ }
+}
diff --git a/core/l/Dwoo/Data.php b/core/l/Dwoo/Data.php
new file mode 100644
index 0000000..85226e7
--- /dev/null
+++ b/core/l/Dwoo/Data.php
@@ -0,0 +1,264 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.4
+ * @date 2017-03-01
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Dwoo data object, use it for complex data assignments or if you want to easily pass it
+ * around multiple functions to avoid passing an array by reference.
+ * 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.
+ */
+class Data implements IDataProvider
+{
+ /**
+ * Data array.
+ *
+ * @var array
+ */
+ protected $data = array();
+
+ /**
+ * Returns the data array.
+ *
+ * @return array
+ */
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Clears a the entire data or only the given key.
+ *
+ * @param array|string $name clears only one value if you give a name, multiple values if
+ * you give an array of names, or the entire data if left null
+ */
+ public function clear($name = null)
+ {
+ if ($name === null) {
+ $this->data = array();
+ } elseif (is_array($name)) {
+ foreach ($name as $index) {
+ unset($this->data[$index]);
+ }
+ } else {
+ unset($this->data[$name]);
+ }
+ }
+
+ /**
+ * Overwrites the entire data with the given array.
+ *
+ * @param array $data the new data array to use
+ */
+ public function setData(array $data)
+ {
+ $this->data = $data;
+ }
+
+ /**
+ * merges the given array(s) with the current data with array_merge.
+ *
+ * @param array $data the array to merge
+ */
+ public function mergeData(array $data)
+ {
+ $args = func_get_args();
+ foreach ($args as $key => $v) {
+ if (is_array($v)) {
+ $this->data = array_merge($this->data, $v);
+ }
+ }
+ }
+
+ /**
+ * Assigns a value or an array of values to the data object.
+ *
+ * @param array|string $name an associative array of multiple (index=>value) or a string
+ * that is the index to use, i.e. a value assigned to "foo" will be
+ * accessible in the template through {$foo}
+ * @param mixed $val the value to assign, or null if $name was an array
+ */
+ public function assign($name, $val = null)
+ {
+ if (is_array($name)) {
+ reset($name);
+ foreach ($name as $k => $v){
+ $this->data[$k] = $v;
+ }
+ } else {
+ $this->data[$name] = $val;
+ }
+ }
+
+ /**
+ * Allows to assign variables using the object syntax.
+ *
+ * @param string $name the variable name
+ * @param string $value the value to assign to it
+ */
+ public function __set($name, $value)
+ {
+ $this->assign($name, $value);
+ }
+
+ /**
+ * Assigns a value by reference to the data object.
+ *
+ * @param string $name the index to use, i.e. a value assigned to "foo" will be
+ * accessible in the template through {$foo}
+ * @param mixed $val the value to assign by reference
+ */
+ public function assignByRef($name, &$val)
+ {
+ $this->data[$name] = &$val;
+ }
+
+ /**
+ * Appends values or an array of values to the data object.
+ *
+ * @param array|string $name an associative array of multiple (index=>value) or a string
+ * that is the index to use, i.e. a value assigned to "foo" will be
+ * accessible in the template through {$foo}
+ * @param mixed $val the value to assign, or null if $name was an array
+ * @param bool $merge true to merge data or false to append, defaults to false
+ */
+ public function append($name, $val = null, $merge = false)
+ {
+ if (is_array($name)) {
+ foreach ($name as $key => $val) {
+ if (isset($this->data[$key]) && !is_array($this->data[$key])) {
+ settype($this->data[$key], 'array');
+ }
+
+ if ($merge === true && is_array($val)) {
+ $this->data[$key] = $val + $this->data[$key];
+ } else {
+ $this->data[$key][] = $val;
+ }
+ }
+ } elseif ($val !== null) {
+ if (isset($this->data[$name]) && !is_array($this->data[$name])) {
+ settype($this->data[$name], 'array');
+ } elseif (!isset($this->data[$name])) {
+ $this->data[$name] = array();
+ }
+
+ if ($merge === true && is_array($val)) {
+ $this->data[$name] = $val + $this->data[$name];
+ } else {
+ $this->data[$name][] = $val;
+ }
+ }
+ }
+
+ /**
+ * Appends a value by reference to the data object.
+ *
+ * @param string $name the index to use, i.e. a value assigned to "foo" will be
+ * accessible in the template through {$foo}
+ * @param mixed $val the value to append by reference
+ * @param bool $merge true to merge data or false to append, defaults to false
+ */
+ public function appendByRef($name, &$val, $merge = false)
+ {
+ if (isset($this->data[$name]) && !is_array($this->data[$name])) {
+ settype($this->data[$name], 'array');
+ }
+
+ if ($merge === true && is_array($val)) {
+ foreach ($val as $key => &$value) {
+ $this->data[$name][$key] = &$value;
+ }
+ } else {
+ $this->data[$name][] = &$val;
+ }
+ }
+
+ /**
+ * Returns true if the variable has been assigned already, false otherwise.
+ *
+ * @param string $name the variable name
+ *
+ * @return bool
+ */
+ public function isAssigned($name)
+ {
+ return isset($this->data[$name]);
+ }
+
+ /**
+ * Supports calls to isset($dwoo->var).
+ *
+ * @param string $name the variable name
+ *
+ * @return bool
+ */
+ public function __isset($name)
+ {
+ return isset($this->data[$name]);
+ }
+
+ /**
+ * Unassigns/removes a variable.
+ *
+ * @param string $name the variable name
+ */
+ public function unassign($name)
+ {
+ unset($this->data[$name]);
+ }
+
+ /**
+ * Supports unsetting variables using the object syntax.
+ *
+ * @param string $name the variable name
+ */
+ public function __unset($name)
+ {
+ unset($this->data[$name]);
+ }
+
+ /**
+ * Returns a variable if it was assigned.
+ *
+ * @param string $name the variable name
+ *
+ * @return mixed
+ */
+ public function get($name)
+ {
+ return $this->__get($name);
+ }
+
+ /**
+ * Allows to read variables using the object syntax.
+ *
+ * @param string $name the variable name
+ *
+ * @return mixed
+ * @throws Exception
+ */
+ public function __get($name)
+ {
+ if (isset($this->data[$name])) {
+ return $this->data[$name];
+ } else {
+ throw new Exception('Tried to read a value that was not assigned yet : "' . $name . '"');
+ }
+ }
+}
diff --git a/core/l/Dwoo/Exception.php b/core/l/Dwoo/Exception.php
new file mode 100644
index 0000000..f61795f
--- /dev/null
+++ b/core/l/Dwoo/Exception.php
@@ -0,0 +1,26 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Main dwoo exception class.
+ * 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.
+ */
+class Exception extends \Exception
+{
+}
diff --git a/core/l/Dwoo/Filter.php b/core/l/Dwoo/Filter.php
new file mode 100644
index 0000000..5fdb4ed
--- /dev/null
+++ b/core/l/Dwoo/Filter.php
@@ -0,0 +1,52 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Base class for filters.
+ * 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.
+ */
+abstract class Filter
+{
+ /**
+ * The dwoo instance that runs this filter.
+ *
+ * @var Core
+ */
+ protected $dwoo;
+
+ /**
+ * Constructor, if you override it, call parent::__construct($dwoo); or assign
+ * the dwoo instance yourself if you need it.
+ *
+ * @param Core $dwoo the dwoo instance that runs this plugin
+ */
+ public function __construct(Core $dwoo)
+ {
+ $this->dwoo = $dwoo;
+ }
+
+ /**
+ * Processes the input and returns it filtered.
+ *
+ * @param string $input the template to process
+ *
+ * @return string
+ */
+ abstract public function process($input);
+}
diff --git a/core/l/Dwoo/ICompilable.php b/core/l/Dwoo/ICompilable.php
new file mode 100644
index 0000000..40e1155
--- /dev/null
+++ b/core/l/Dwoo/ICompilable.php
@@ -0,0 +1,31 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Interface that represents a compilable plugin.
+ * implement this to notify the compiler that this plugin does not need to be loaded at runtime.
+ * to implement it right, you must implement public static function compile(Compiler $compiler, $arg, $arg,
+ * ...), which replaces the process() method (that means compile() should have all arguments it
+ * requires). 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.
+ */
+interface ICompilable
+{
+ // this replaces the process function
+ //public static function compile(Compiler $compiler, $arg, $arg, ...);
+}
diff --git a/core/l/Dwoo/ICompilable/Block.php b/core/l/Dwoo/ICompilable/Block.php
new file mode 100644
index 0000000..241db4e
--- /dev/null
+++ b/core/l/Dwoo/ICompilable/Block.php
@@ -0,0 +1,27 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\ICompilable;
+
+/**
+ * Interface that represents a compilable block plugin.
+ * implement this to notify the compiler that this plugin does not need to be loaded at runtime.
+ * 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.
+ */
+interface Block
+{
+}
diff --git a/core/l/Dwoo/ICompiler.php b/core/l/Dwoo/ICompiler.php
new file mode 100644
index 0000000..619aaab
--- /dev/null
+++ b/core/l/Dwoo/ICompiler.php
@@ -0,0 +1,59 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.3.6
+ * @date 2017-03-21
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+use Dwoo\Security\Policy as SecurityPolicy;
+
+/**
+ * Interface that represents a dwoo compiler.
+ * while implementing this is enough to interact with Dwoo/Templates, it is not
+ * sufficient to interact with Dwoo/Plugins, however the main purpose of creating a
+ * new compiler would be to interact with other/different plugins, that is why this
+ * interface has been left with the minimum requirements.
+ * 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.
+ */
+interface ICompiler
+{
+ /**
+ * Compiles the provided string down to php code.
+ *
+ * @param Core $core
+ * @param ITemplate $template the template to compile
+ *
+ * @return string a compiled php code string
+ */
+ public function compile(Core $core, ITemplate $template);
+
+ /**
+ * Adds the custom plugins loaded into Dwoo to the compiler so it can load them.
+ *
+ * @see Core::addPlugin
+ *
+ * @param array $customPlugins an array of custom plugins
+ */
+ public function setCustomPlugins(array $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 SecurityPolicy $policy the security policy object
+ */
+ public function setSecurityPolicy(SecurityPolicy $policy = null);
+}
diff --git a/core/l/Dwoo/IDataProvider.php b/core/l/Dwoo/IDataProvider.php
new file mode 100644
index 0000000..9392b2d
--- /dev/null
+++ b/core/l/Dwoo/IDataProvider.php
@@ -0,0 +1,32 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Interface that represents a dwoo data object.
+ * 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.
+ */
+interface IDataProvider
+{
+ /**
+ * Returns the data as an associative array that will be used in the template.
+ *
+ * @return array
+ */
+ public function getData();
+}
diff --git a/core/l/Dwoo/IElseable.php b/core/l/Dwoo/IElseable.php
new file mode 100644
index 0000000..b8f3483
--- /dev/null
+++ b/core/l/Dwoo/IElseable.php
@@ -0,0 +1,30 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Interface that represents a block plugin that supports the else functionality.
+ * the else block will enter an "hasElse" parameter inside the parameters array
+ * of the closest parent implementing this interface, the hasElse parameter contains
+ * the else output that should be appended to the block's content (see foreach or other
+ * block for examples)
+ * 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.
+ */
+interface IElseable
+{
+}
diff --git a/core/l/Dwoo/ILoader.php b/core/l/Dwoo/ILoader.php
new file mode 100644
index 0000000..febf202
--- /dev/null
+++ b/core/l/Dwoo/ILoader.php
@@ -0,0 +1,37 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Interface for dwoo plugin loaders.
+ * 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.
+ */
+interface ILoader
+{
+ /**
+ * Loads a plugin file.
+ * the second parameter is used to avoid permanent rehashing when using php functions,
+ * however this means that if you have add a plugin that overrides a php function you have
+ * to delete the classpath.cache file(s) by hand to force a rehash of the plugins
+ *
+ * @param string $class the plugin name, without the `Plugin` prefix
+ * @param bool $forceRehash if true, the class path caches will be rebuilt if the plugin is not found, in case it
+ * has just been added, defaults to true
+ */
+ public function loadPlugin($class, $forceRehash = true);
+}
diff --git a/core/l/Dwoo/IPluginProxy.php b/core/l/Dwoo/IPluginProxy.php
new file mode 100644
index 0000000..d20f3f7
--- /dev/null
+++ b/core/l/Dwoo/IPluginProxy.php
@@ -0,0 +1,74 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Interface that represents a dwoo plugin proxy.
+ * 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.
+ */
+interface IPluginProxy
+{
+ /**
+ * Returns true or false to say whether the given plugin is handled by this proxy or not.
+ *
+ * @param string $name the plugin name
+ *
+ * @return bool true if the plugin is known and usable, otherwise false
+ */
+ public function handles($name);
+
+ /**
+ * Returns the code (as a string) to call the plugin
+ * (this will be executed at runtime inside the Dwoo class).
+ *
+ * @param string $name the plugin name
+ * @param array $params a parameter array, array key "*" is the rest array
+ *
+ * @return string
+ */
+ public function getCode($name, $params);
+
+ /**
+ * Returns a callback to the plugin, this is used with the reflection API to
+ * find out about the plugin's parameter names etc.
+ * should you need a rest array without the possibility to edit the
+ * plugin's code, you can provide a callback to some
+ * other function with the correct parameter signature, i.e. :
+ *
+ * return array($this, "callbackHelper");
+ * // and callbackHelper would be as such:
+ * public function callbackHelper(array $rest=array()){}
+ *
+ *
+ * @param string $name the plugin name
+ *
+ * @return callback
+ */
+ public function getCallback($name);
+
+ /**
+ * Returns some code that will check if the plugin is loaded and if not load it
+ * this is optional, if your plugins are autoloaded or whatever, just return an
+ * empty string.
+ *
+ * @param string $name the plugin name
+ *
+ * @return string
+ */
+ public function getLoader($name);
+}
diff --git a/core/l/Dwoo/ITemplate.php b/core/l/Dwoo/ITemplate.php
new file mode 100644
index 0000000..6fa65f3
--- /dev/null
+++ b/core/l/Dwoo/ITemplate.php
@@ -0,0 +1,161 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.4
+ * @date 2017-03-07
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Interface that represents a dwoo template.
+ * 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.
+ */
+interface ITemplate
+{
+ /**
+ * Returns the cache duration for this template.
+ * defaults to null if it was not provided
+ *
+ * @return int|null
+ */
+ public function getCacheTime();
+
+ /**
+ * Sets the cache duration for this template.
+ * can be used to set it after the object is created if you did not provide
+ * it in the constructor
+ *
+ * @param int $seconds duration of the cache validity for this template, if
+ * null it defaults to the Dwoo instance's cache time. 0 = disable and
+ * -1 = infinite cache
+ */
+ public function setCacheTime($seconds = null);
+
+ /**
+ * Returns the cached template output file name, true if it's cache-able but not cached
+ * or false if it's not cached.
+ *
+ * @param Core $core the dwoo instance that requests it
+ *
+ * @return string|bool
+ */
+ public function getCachedTemplate(Core $core);
+
+ /**
+ * Caches the provided output into the cache file.
+ *
+ * @param Core $core the dwoo instance that requests it
+ * @param string $output the template output
+ *
+ * @return mixed full path of the cached file or false upon failure
+ */
+ public function cache(Core $core, $output);
+
+ /**
+ * Clears the cached template if it's older than the given time.
+ *
+ * @param Core $core the dwoo instance that was used to cache that template
+ * @param int $olderThan minimum time (in seconds) required for the cache to be cleared
+ *
+ * @return bool true if the cache was not present or if it was deleted, false if it remains there
+ */
+ public function clearCache(Core $core, $olderThan = - 1);
+
+ /**
+ * Returns the compiled template file name.
+ *
+ * @param Core $core the dwoo instance that requests it
+ * @param ICompiler $compiler the compiler that must be used
+ *
+ * @return string
+ */
+ public function getCompiledTemplate(Core $core, ICompiler $compiler = null);
+
+ /**
+ * Returns the template name.
+ *
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * Returns the resource name for this template class.
+ *
+ * @return string
+ */
+ public function getResourceName();
+
+ /**
+ * Returns the resource identifier for this template or false if it has no identifier.
+ *
+ * @return string|false
+ */
+ public function getResourceIdentifier();
+
+ /**
+ * Returns the template source of this template.
+ *
+ * @return string
+ */
+ public function getSource();
+
+ /**
+ * Returns an unique string identifying the current version of this template,
+ * for example a timestamp of the last modified date or a hash of the template source.
+ *
+ * @return string
+ */
+ public function getUid();
+
+ /**
+ * Returns the compiler used by this template, if it was just compiled, or null.
+ *
+ * @return ICompiler
+ */
+ public function getCompiler();
+
+ /**
+ * Returns some php code that will check if this template has been modified or not.
+ * if the function returns null, the template will be instanciated and then the Uid checked
+ *
+ * @return string
+ */
+ public function getIsModifiedCode();
+
+ /**
+ * Returns a new template object from the given resource identifier, null if no include is
+ * possible (resource not found), or false if include is not permitted by this resource type.
+ * this method should also check if $dwoo->getSecurityPolicy() is null or not and do the
+ * necessary permission checks if required, if the security policy prevents the template
+ * generation it should throw a new Security\Exception with a relevant message
+ *
+ * @param Core $core
+ * @param mixed $resourceId the resource identifier
+ * @param int $cacheTime duration of the cache validity for this template, if null it defaults to the
+ * Dwoo instance that will render this template if null it defaults to the Dwoo
+ * instance that will render this template
+ * @param string $cacheId the unique cache identifier of this page or anything else that makes this
+ * template's content unique, if null it defaults to the current url makes this
+ * template's content unique, if null it defaults to the current url
+ * @param string $compileId the unique compiled identifier, which is used to distinguish this template from
+ * others, if null it defaults to the filename+bits of the path template from
+ * others, if null it defaults to the filename+bits of the path
+ * @param ITemplate $parentTemplate the template that is requesting a new template object (through an include,
+ * extends or any other plugin) an include, extends or any other plugin)
+ *
+ * @return ITemplate|false|null
+ */
+ public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null,
+ $compileId = null, ITemplate $parentTemplate = null);
+}
diff --git a/core/l/Dwoo/Loader.php b/core/l/Dwoo/Loader.php
new file mode 100644
index 0000000..db6f88a
--- /dev/null
+++ b/core/l/Dwoo/Loader.php
@@ -0,0 +1,181 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.4.0
+ * @date 2017-03-16
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Handles plugin loading and caching of plugins names/paths relationships.
+ * 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.
+ */
+class Loader implements ILoader
+{
+ /**
+ * Stores the plugin directories.
+ *
+ * @see addDirectory
+ * @var array
+ */
+ protected $paths = array();
+
+ /**
+ * Stores the plugins names/paths relationships
+ * don't edit this on your own, use addDirectory.
+ *
+ * @see addDirectory
+ * @var array
+ */
+ protected $classPath = array();
+
+ /**
+ * Path where class paths cache files are written.
+ *
+ * @var string
+ */
+ protected $cacheDir;
+
+ /**
+ * Path where builtin plugins are stored.
+ *
+ * @var string
+ */
+ protected $corePluginDir;
+
+ /**
+ * Loader constructor.
+ *
+ * @param $cacheDir
+ */
+ public function __construct($cacheDir)
+ {
+ $this->corePluginDir = __DIR__ . DIRECTORY_SEPARATOR;
+ $this->cacheDir = rtrim($cacheDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+
+ // include class paths or rebuild paths if the cache file isn't there
+ $cacheFile = $this->cacheDir . 'classpath.cache.d' . Core::RELEASE_TAG . '.php';
+ if (file_exists($cacheFile)) {
+ $classpath = file_get_contents($cacheFile);
+ $this->classPath = unserialize($classpath) + $this->classPath;
+ } else {
+ $this->rebuildClassPathCache($this->corePluginDir, $cacheFile);
+ }
+ }
+
+ /**
+ * Rebuilds class paths, scans the given directory recursively and saves all paths in the given file.
+ *
+ * @param string $path the plugin path to scan
+ * @param string|boolean $cacheFile the file where to store the plugin paths cache, it will be overwritten
+ *
+ * @throws Exception
+ */
+ protected function rebuildClassPathCache($path, $cacheFile)
+ {
+ $tmp = array();
+ if ($cacheFile !== false) {
+ $tmp = $this->classPath;
+ $this->classPath = array();
+ }
+
+ // iterates over all files/folders
+ foreach (new \DirectoryIterator($path) as $fileInfo) {
+ if (!$fileInfo->isDot()) {
+ if ($fileInfo->isDir()) {
+ $this->rebuildClassPathCache($fileInfo->getPathname(), false);
+ } else {
+ $this->classPath[$fileInfo->getBasename('.php')] = $fileInfo->getPathname();
+ }
+ }
+ }
+
+ // save in file if it's the first call (not recursed)
+ if ($cacheFile !== false) {
+ if (!file_put_contents($cacheFile, serialize($this->classPath))) {
+ throw new Exception('Could not write into ' . $cacheFile . ', either because the folder is not there (create it) or because of the chmod configuration (please ensure this directory is writable by php), alternatively you can change the directory used with $dwoo->setCompileDir() or provide a custom loader object with $dwoo->setLoader()');
+ }
+ $this->classPath += $tmp;
+ }
+ }
+
+ /**
+ * Loads a plugin file.
+ *
+ * @param string $class the plugin name, without the `Plugin` prefix
+ * @param bool $forceRehash if true, the class path caches will be rebuilt if the plugin is not found, in case it
+ * has just been added, defaults to true
+ *
+ * @throws Exception
+ */
+ public function loadPlugin($class, $forceRehash = true)
+ {
+ /**
+ * An unknown class was requested (maybe newly added) or the
+ * include failed so we rebuild the cache. include() will fail
+ * with an uncatchable error if the file doesn't exist, which
+ * usually means that the cache is stale and must be rebuilt,
+ * so we check for that before trying to include() the plugin.
+ */
+ if ((!isset($this->classPath[$class]) || !is_readable($this->classPath[$class])) || (!isset
+ ($this->classPath[$class . 'Compile']) || !is_readable($this->classPath[$class . 'Compile']))) {
+ if ($forceRehash) {
+ $this->rebuildClassPathCache($this->corePluginDir, $this->cacheDir . 'classpath.cache.d' .
+ Core::RELEASE_TAG . '.php');
+ foreach ($this->paths as $path => $file) {
+ $this->rebuildClassPathCache($path, $file);
+ }
+ if (isset($this->classPath[$class])) {
+ include_once $this->classPath[$class];
+ } elseif (isset($this->classPath[$class . 'Compile'])) {
+ include_once $this->classPath[$class . 'Compile'];
+ } else {
+ throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
+ }
+ } else {
+ throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
+ }
+ }
+ }
+
+ /**
+ * Adds a plugin directory, the plugins found in the new plugin directory
+ * will take precedence over the other directories (including the default
+ * dwoo plugin directory), you can use this for example to override plugins
+ * in a specific directory for a specific application while keeping all your
+ * usual plugins in the same place for all applications.
+ * TOCOM don't forget that php functions overrides are not rehashed so you
+ * need to clear the classpath caches by hand when adding those.
+ *
+ * @param string $pluginDirectory the plugin path to scan
+ *
+ * @throws Exception
+ */
+ public function addDirectory($pluginDirectory)
+ {
+ $pluginDir = realpath($pluginDirectory);
+ if (!$pluginDir) {
+ throw new Exception('Plugin directory does not exist or can not be read : ' . $pluginDirectory);
+ }
+ $cacheFile = $this->cacheDir . 'classpath-' . substr(strtr($pluginDir, '/\\:' . PATH_SEPARATOR, '----'),
+ strlen($pluginDir) > 80 ? - 80 : 0) . '.d' . Core::RELEASE_TAG . '.php';
+ $this->paths[$pluginDir] = $cacheFile;
+ if (file_exists($cacheFile)) {
+ $classpath = file_get_contents($cacheFile);
+ $this->classPath = unserialize($classpath) + $this->classPath;
+ } else {
+ $this->rebuildClassPathCache($pluginDir, $cacheFile);
+ }
+ }
+}
diff --git a/core/l/Dwoo/Plugin.php b/core/l/Dwoo/Plugin.php
new file mode 100644
index 0000000..c607806
--- /dev/null
+++ b/core/l/Dwoo/Plugin.php
@@ -0,0 +1,105 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Base plugin class.
+ * you have to implement the process() method, it will receive the parameters that
+ * are in the template code
+ * 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.
+ */
+abstract class Plugin
+{
+ /**
+ * The dwoo instance that runs this plugin.
+ *
+ * @var Core
+ */
+ protected $core;
+
+ /**
+ * Constructor, if you override it, call parent::__construct($core); or assign
+ * the dwoo instance yourself if you need it.
+ *
+ * @param Core $core the dwoo instance that runs this plugin
+ */
+ public function __construct(Core $core)
+ {
+ $this->core = $core;
+ }
+
+ // plugins should always implement :
+ // public function process($arg, $arg, ...)
+ // or for block plugins :
+ // public function init($arg, $arg, ...)
+
+ // this could be enforced with :
+ // abstract public function process(...);
+ // if my feature request gets enough interest one day
+ // see => http://bugs.php.net/bug.php?id=44043
+
+ /**
+ * Utility function that converts an array of compiled parameters (or rest array) to a string of xml/html tag
+ * attributes. this is to be used in preProcessing or postProcessing functions, example :
+ * $p = $compiler->getCompiledParams($params);
+ * // get only the rest array as attributes
+ * $attributes = Plugin::paramsToAttributes($p['*']);
+ * // get all the parameters as attributes (if there is a rest array, it will be included)
+ * $attributes = Plugin::paramsToAttributes($p);
+ *
+ * @param array $params an array of attributeName=>value items that will be compiled to be ready for inclusion in a php string
+ * inclusion in a php string
+ * @param string $delim the string delimiter you want to use (defaults to ')
+ * @param Compiler $compiler the compiler instance (optional for BC, but recommended to pass it for proper escaping behavior)
+ * escaping behavior)
+ *
+ * @return string
+ */
+ public static function paramsToAttributes(array $params, $delim = '\'', Compiler $compiler = null)
+ {
+ if (isset($params['*'])) {
+ $params = array_merge($params, $params['*']);
+ unset($params['*']);
+ }
+
+ $out = '';
+ foreach ($params as $attr => $val) {
+ $out .= ' ' . $attr . '=';
+ if (trim($val, '"\'') == '' || $val == 'null') {
+ $out .= str_replace($delim, '\\' . $delim, '""');
+ } elseif (substr($val, 0, 1) === $delim && substr($val, - 1) === $delim) {
+ $out .= str_replace($delim, '\\' . $delim, '"' . substr($val, 1, - 1) . '"');
+ } else {
+ if (!$compiler) {
+ // disable double encoding since it can not be determined if it was encoded
+ $escapedVal = '.(is_string($tmp2=' . $val . ') ? htmlspecialchars($tmp2, ENT_QUOTES, $this->charset, false) : $tmp2).';
+ } elseif (!$compiler->getAutoEscape() || false === strpos($val, 'isset($this->scope')) {
+ // escape if auto escaping is disabled, or there was no variable in the string
+ $escapedVal = '.(is_string($tmp2=' . $val . ') ? htmlspecialchars($tmp2, ENT_QUOTES, $this->charset) : $tmp2).';
+ } else {
+ // print as is
+ $escapedVal = '.' . $val . '.';
+ }
+
+ $out .= str_replace($delim, '\\' . $delim, '"') . $delim . $escapedVal . $delim . str_replace($delim, '\\' . $delim, '"');
+ }
+ }
+
+ return ltrim($out);
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginA.php b/core/l/Dwoo/Plugins/Blocks/PluginA.php
new file mode 100644
index 0000000..6210d84
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginA.php
@@ -0,0 +1,95 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * Outputs a html <a> tag
+ *
+ * * href : the target URI where the link must point
+ * * rest : any other attributes you want to add to the tag can be added as named parameters
+ *
.
+ * Example :
+ *
+ * {* Create a simple link out of an url variable and add a special class attribute: *}
+ * {a $url class="external" /}
+ * {* Mark a link as active depending on some other variable : *}
+ * {a $link.url class=tif($link.active "active"); $link.title /}
+ * {* This is similar to: {$link.title} *}
+ *
+ * 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.
+ */
+class PluginA extends BlockPlugin implements ICompilableBlock
+{
+ /**
+ * @param $href
+ * @param array $rest
+ */
+ public function init($href, array $rest = array())
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ $p = $compiler->getCompiledParams($params);
+
+ $out = Compiler::PHP_OPEN . 'echo \'\';' . Compiler::PHP_CLOSE;
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ $p = $compiler->getCompiledParams($params);
+
+ // no content was provided so use the url as display text
+ if ($content == '') {
+ // merge into the href if href is a string
+ if (substr($p['href'], - 1) === '"' || substr($p['href'], - 1) === '\'') {
+ return Compiler::PHP_OPEN . 'echo ' . substr($p['href'], 0, - 1) . '' . substr($p['href'], - 1) . ';' . Compiler::PHP_CLOSE;
+ }
+
+ // otherwise append
+ return Compiler::PHP_OPEN . 'echo ' . $p['href'] . '.\'\';' . Compiler::PHP_CLOSE;
+ }
+
+ // return content
+ return $content . '';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginAutoEscape.php b/core/l/Dwoo/Plugins/Blocks/PluginAutoEscape.php
new file mode 100644
index 0000000..7b45ec9
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginAutoEscape.php
@@ -0,0 +1,98 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+use Dwoo\Compilation\Exception as CompilationException;
+
+/**
+ * Overrides the compiler auto-escape setting within the block
+ *
+ * * enabled : if set to "on", "enable", true or 1 then the compiler autoescaping is enabled inside this block. set to
+ * "off", "disable", false or 0 to disable it
+ *
+ * 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.
+ */
+class PluginAutoEscape extends BlockPlugin implements ICompilableBlock
+{
+ protected static $stack = array();
+
+ /**
+ * @param $enabled
+ */
+ public function init($enabled)
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ * @throws CompilationException
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ $params = $compiler->getCompiledParams($params);
+ switch (strtolower(trim((string)$params['enabled'], '"\''))) {
+
+ case 'on':
+ case 'true':
+ case 'enabled':
+ case 'enable':
+ case '1':
+ $enable = true;
+ break;
+ case 'off':
+ case 'false':
+ case 'disabled':
+ case 'disable':
+ case '0':
+ $enable = false;
+ break;
+ default:
+ throw new CompilationException($compiler, 'Auto_Escape : Invalid parameter (' . $params['enabled'] . '), valid parameters are "enable"/true or "disable"/false');
+ }
+
+ self::$stack[] = $compiler->getAutoEscape();
+ $compiler->setAutoEscape($enable);
+
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ $compiler->setAutoEscape(array_pop(self::$stack));
+
+ return $content;
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginBlock.php b/core/l/Dwoo/Plugins/Blocks/PluginBlock.php
new file mode 100644
index 0000000..7e66182
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginBlock.php
@@ -0,0 +1,66 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * This is used only when rendering a template that has blocks but is not extending anything,
+ * it doesn't do anything by itself and should not be used outside of template inheritance context,
+ * see {@link http://wiki.dwoo.org/index.php/TemplateInheritance} to read more about it.
+ * 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.
+ */
+class PluginBlock extends BlockPlugin implements ICompilableBlock
+{
+ /**
+ * @param string $name
+ */
+ public function init($name = '')
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ return $content;
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginCapture.php b/core/l/Dwoo/Plugins/Blocks/PluginCapture.php
new file mode 100644
index 0000000..f8c9c03
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginCapture.php
@@ -0,0 +1,95 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * Captures all the output within this block and saves it into {$.capture.default} by default,
+ * or {$.capture.name} if you provide another name.
+ *
+ * * name : capture name, used to read the value afterwards
+ * * assign : if set, the value is also saved in the given variable
+ * * cat : if true, the value is appended to the previous one (if any) instead of overwriting it
+ *
+ * If the cat parameter is true, the content
+ * will be appended to the existing content.
+ * Example :
+ *
+ * {capture "foo"}
+ * Anything in here won't show, it will be saved for later use..
+ * {/capture}
+ * Output was : {$.capture.foo}
+ *
+ * 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.
+ */
+class PluginCapture extends BlockPlugin implements ICompilableBlock
+{
+ /**
+ * @param string $name
+ * @param null $assign
+ * @param bool $cat
+ * @param bool $trim
+ */
+ public function init($name = 'default', $assign = null, $cat = false, $trim = false)
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ return Compiler::PHP_OPEN . $prepend . 'ob_start();' . $append . Compiler::PHP_CLOSE;
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ $params = $compiler->getCompiledParams($params);
+
+ $out = $content . Compiler::PHP_OPEN . $prepend . "\n" . '$tmp = ob_get_clean();';
+ if ($params['trim'] !== 'false' && $params['trim'] !== 0) {
+ $out .= "\n" . '$tmp = trim($tmp);';
+ }
+ if ($params['cat'] === 'true' || $params['cat'] === 1) {
+ $out .= "\n" . '$tmp = $this->readVar(\'dwoo.capture.\'.' . $params['name'] . ') . $tmp;';
+ }
+ if ($params['assign'] !== 'null') {
+ $out .= "\n" . '$this->scope[' . $params['assign'] . '] = $tmp;';
+ }
+
+ return $out . "\n" . '$this->globals[\'capture\'][' . $params['name'] . '] = $tmp;' . $append . Compiler::PHP_CLOSE;
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginDynamic.php b/core/l/Dwoo/Plugins/Blocks/PluginDynamic.php
new file mode 100644
index 0000000..3b1f61a
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginDynamic.php
@@ -0,0 +1,116 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+use Dwoo\Compilation\Exception as CompilationException;
+
+/**
+ * Marks the contents of the block as dynamic. Which means that it will not be cached.
+ * 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.
+ */
+class PluginDynamic extends BlockPlugin implements ICompilableBlock
+{
+ /**
+ *
+ */
+ public function init()
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ try {
+ $compiler->findBlock('dynamic');
+
+ return $content;
+ }
+ catch (CompilationException $e) {
+ }
+ $output = Compiler::PHP_OPEN . 'if($doCache) {' . "\n\t" . 'echo \'' . str_replace('\'', '\\\'', $content) . '\';' . "\n} else {\n\t";
+ if (substr($content, 0, strlen(Compiler::PHP_OPEN)) == Compiler::PHP_OPEN) {
+ $output .= substr($content, strlen(Compiler::PHP_OPEN));
+ } else {
+ $output .= Compiler::PHP_CLOSE . $content;
+ }
+ if (substr($output, - strlen(Compiler::PHP_CLOSE)) == Compiler::PHP_CLOSE) {
+ $output = substr($output, 0, - strlen(Compiler::PHP_CLOSE));
+ } else {
+ $output .= Compiler::PHP_OPEN;
+ }
+ $output .= "\n}" . Compiler::PHP_CLOSE;
+
+ return $output;
+ }
+
+ /**
+ * @param $output
+ * @param $dynamicId
+ * @param $compiledFile
+ *
+ * @return mixed|string
+ */
+ public static function unescape($output, $dynamicId, $compiledFile)
+ {
+ $output = preg_replace_callback('/(.+?)<\/dwoo:dynamic_' . $dynamicId . '>/s', array(
+ 'self',
+ 'unescapePhp'
+ ), $output, - 1, $count);
+ // re-add the includes on top of the file
+ if ($count && preg_match('#/\* template head \*/(.+?)/\* end template head \*/#s', file_get_contents($compiledFile), $m)) {
+ $output = '' . $output;
+ }
+
+ return $output;
+ }
+
+ /**
+ * @param $match
+ *
+ * @return mixed
+ */
+ public static function unescapePhp($match)
+ {
+ return preg_replace('{<\?php /\*' . $match[1] . '\*/ echo \'(.+?)\'; \?>}s', '$1', $match[2]);
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginElse.php b/core/l/Dwoo/Plugins/Blocks/PluginElse.php
new file mode 100644
index 0000000..14143a2
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginElse.php
@@ -0,0 +1,96 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+use Dwoo\Compilation\Exception as CompilationException;
+
+/**
+ * Generic else block, it supports all builtin optional-display blocks which are if/for/foreach/loop/with.
+ * If any of those block contains an else statement, the content between {else} and {/block} (you do not
+ * need to close the else block) will be shown if the block's condition has no been met
+ * Example :
+ *
+ * {foreach $array val}
+ * $array is not empty so we display it's values : {$val}
+ * {else}
+ * if this shows, it means that $array is empty or doesn't exist.
+ * {/foreach}
+ *
+ * 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.
+ */
+class PluginElse extends BlockPlugin implements ICompilableBlock
+{
+ public function init()
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ * @throws CompilationException
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ $preContent = '';
+ while (true) {
+ $preContent .= $compiler->removeTopBlock();
+ $block = &$compiler->getCurrentBlock();
+ if (!$block) {
+ throw new CompilationException($compiler, 'An else block was found but it was not preceded by an if or other else-able construct');
+ }
+ $interfaces = class_implements($block['class']);
+ if (in_array('Dwoo\IElseable', $interfaces) !== false) {
+ break;
+ }
+ }
+
+ $params['initialized'] = true;
+ $compiler->injectBlock($type, $params);
+
+ return $preContent;
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ if (!isset($params['initialized'])) {
+ return '';
+ }
+
+ $block = &$compiler->getCurrentBlock();
+ $block['params']['hasElse'] = Compiler::PHP_OPEN . "else {\n" . Compiler::PHP_CLOSE . $content . Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE;
+
+ return '';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginElseif.php b/core/l/Dwoo/Plugins/Blocks/PluginElseif.php
new file mode 100644
index 0000000..033aa59
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginElseif.php
@@ -0,0 +1,95 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\IElseable;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * Acts as a php elseif block, allowing you to add one more condition
+ * if the previous one(s) didn't match. See the {if} plugin for syntax details.
+ * 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.
+ */
+class PluginElseif extends PluginIf implements ICompilableBlock, IElseable
+{
+ /**
+ * @param array $rest
+ */
+ public function init(array $rest)
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ $preContent = '';
+ while (true) {
+ $preContent .= $compiler->removeTopBlock();
+ $block = &$compiler->getCurrentBlock();
+ $interfaces = class_implements($block['class']);
+ if (in_array('Dwoo\IElseable', $interfaces) !== false) {
+ break;
+ }
+ }
+
+ $params['initialized'] = true;
+ $compiler->injectBlock($type, $params);
+
+ return $preContent;
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ if (!isset($params['initialized'])) {
+ return '';
+ }
+
+ $tokens = $compiler->getParamTokens($params);
+ $params = $compiler->getCompiledParams($params);
+
+ $pre = Compiler::PHP_OPEN . 'elseif (' . implode(' ', self::replaceKeywords($params['*'], $tokens['*'], $compiler)) . ") {\n" . Compiler::PHP_CLOSE;
+ $post = Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE;
+
+ if (isset($params['hasElse'])) {
+ $post .= $params['hasElse'];
+ }
+
+ $block = &$compiler->getCurrentBlock();
+ $block['params']['hasElse'] = $pre . $content . $post;
+
+ return '';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginFor.php b/core/l/Dwoo/Plugins/Blocks/PluginFor.php
new file mode 100644
index 0000000..b696094
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginFor.php
@@ -0,0 +1,192 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\IElseable;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * Similar to the php for block
+ *
+ * * name : for name to access it's iterator variables through {$.for.name.var} see {@link
+ * http://wiki.dwoo.org/index.php/IteratorVariables} for details
+ * * from : array to iterate from (which equals 0) or a number as a start value
+ * * to : value to stop iterating at (equals count($array) by default if you set an array in from)
+ * * step : defines the incrementation of the pointer at each iteration
+ *
+ * * from : the array that you want to iterate over
+ * * key : variable name for the key (or for the item if item is not defined)
+ * * item : variable name for each item
+ * * name : foreach name to access it's iterator variables through {$.foreach.name.var} see {@link
+ * http://wiki.dwoo.org/index.php/IteratorVariables} for details
+ *
+ * Example :
+ *
+ * {foreach $array val}
+ * {$val.something}
+ * {/foreach}
+ *
+ * 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.
+ */
+class PluginForeach extends BlockPlugin implements ICompilableBlock, IElseable
+{
+ public static $cnt = 0;
+
+ /**
+ * @param $from
+ * @param null $key
+ * @param null $item
+ * @param string $name
+ * @param null $implode
+ */
+ public function init($from, $key = null, $item = null, $name = 'default', $implode = null)
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ // get block params and save the current template pointer to use it in the postProcessing method
+ $currentBlock = &$compiler->getCurrentBlock();
+ $currentBlock['params']['tplPointer'] = $compiler->getPointer();
+
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ * @throws CompilationException
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ $params = $compiler->getCompiledParams($params);
+ $tpl = $compiler->getTemplateSource($params['tplPointer']);
+
+ // assigns params
+ $src = $params['from'];
+
+ if ($params['item'] !== 'null') {
+ if ($params['key'] !== 'null') {
+ $key = $params['key'];
+ }
+ $val = $params['item'];
+ } elseif ($params['key'] !== 'null') {
+ $val = $params['key'];
+ } else {
+ throw new CompilationException($compiler, 'Foreach item parameter missing');
+ }
+ $name = $params['name'];
+
+ if (substr($val, 0, 1) !== '"' && substr($val, 0, 1) !== '\'') {
+ throw new CompilationException($compiler, 'Foreach item parameter must be of type string');
+ }
+ if (isset($key) && substr($val, 0, 1) !== '"' && substr($val, 0, 1) !== '\'') {
+ throw new CompilationException($compiler, 'Foreach key parameter must be of type string');
+ }
+
+ // evaluates which global variables have to be computed
+ $varName = '$dwoo.foreach.' . trim($name, '"\'') . '.';
+ $shortVarName = '$.foreach.' . trim($name, '"\'') . '.';
+ $usesAny = strpos($tpl, $varName) !== false || strpos($tpl, $shortVarName) !== false;
+ $usesFirst = strpos($tpl, $varName . 'first') !== false || strpos($tpl, $shortVarName . 'first') !== false;
+ $usesLast = strpos($tpl, $varName . 'last') !== false || strpos($tpl, $shortVarName . 'last') !== false;
+ $usesIndex = $usesFirst || strpos($tpl, $varName . 'index') !== false || strpos($tpl, $shortVarName . 'index') !== false;
+ $usesIteration = $usesLast || strpos($tpl, $varName . 'iteration') !== false || strpos($tpl, $shortVarName . 'iteration') !== false;
+ $usesShow = strpos($tpl, $varName . 'show') !== false || strpos($tpl, $shortVarName . 'show') !== false;
+ $usesTotal = $usesLast || strpos($tpl, $varName . 'total') !== false || strpos($tpl, $shortVarName . 'total') !== false;
+
+ if (strpos($name, '$this->scope[') !== false) {
+ $usesAny = $usesFirst = $usesLast = $usesIndex = $usesIteration = $usesShow = $usesTotal = true;
+ }
+
+ // override globals vars if implode is used
+ if ($params['implode'] !== 'null') {
+ $implode = $params['implode'];
+ $usesAny = true;
+ $usesLast = true;
+ $usesIteration = true;
+ $usesTotal = true;
+ }
+
+ // gets foreach id
+ $cnt = self::$cnt ++;
+
+ // build pre content output
+ $pre = Compiler::PHP_OPEN . "\n" . '$_fh' . $cnt . '_data = ' . $src . ';';
+ // adds foreach properties
+ if ($usesAny) {
+ $pre .= "\n" . '$this->globals["foreach"][' . $name . '] = array' . "\n(";
+ if ($usesIndex) {
+ $pre .= "\n\t" . '"index" => 0,';
+ }
+ if ($usesIteration) {
+ $pre .= "\n\t" . '"iteration" => 1,';
+ }
+ if ($usesFirst) {
+ $pre .= "\n\t" . '"first" => null,';
+ }
+ if ($usesLast) {
+ $pre .= "\n\t" . '"last" => null,';
+ }
+ if ($usesShow) {
+ $pre .= "\n\t" . '"show" => $this->isArray($_fh' . $cnt . '_data, true),';
+ }
+ if ($usesTotal) {
+ $pre .= "\n\t" . '"total" => $this->count($_fh' . $cnt . '_data),';
+ }
+ $pre .= "\n);\n" . '$_fh' . $cnt . '_glob =& $this->globals["foreach"][' . $name . '];';
+ }
+ // checks if foreach must be looped
+ $pre .= "\n" . 'if ($this->isTraversable($_fh' . $cnt . '_data' . (isset($params['hasElse']) ? ', true' : '') . ') == true)' . "\n{";
+ // iterates over keys
+ $pre .= "\n\t" . 'foreach ($_fh' . $cnt . '_data as ' . (isset($key) ? '$this->scope[' . $key . ']=>' : '') . '$this->scope[' . $val . '])' . "\n\t{";
+ // updates properties
+ if ($usesFirst) {
+ $pre .= "\n\t\t" . '$_fh' . $cnt . '_glob["first"] = (string) ($_fh' . $cnt . '_glob["index"] === 0);';
+ }
+ if ($usesLast) {
+ $pre .= "\n\t\t" . '$_fh' . $cnt . '_glob["last"] = (string) ($_fh' . $cnt . '_glob["iteration"] === $_fh' . $cnt . '_glob["total"]);';
+ }
+ $pre .= "\n/* -- foreach start output */\n" . Compiler::PHP_CLOSE;
+
+ // build post content output
+ $post = Compiler::PHP_OPEN . "\n";
+
+ if (isset($implode)) {
+ $post .= '/* -- implode */' . "\n" . 'if (!$_fh' . $cnt . '_glob["last"]) {' . "\n\t" . 'echo ' . $implode . ";\n}\n";
+ }
+ $post .= '/* -- foreach end output */';
+ // update properties
+ if ($usesIndex) {
+ $post .= "\n\t\t" . '$_fh' . $cnt . '_glob["index"]+=1;';
+ }
+ if ($usesIteration) {
+ $post .= "\n\t\t" . '$_fh' . $cnt . '_glob["iteration"]+=1;';
+ }
+ // end loop
+ $post .= "\n\t}\n}" . Compiler::PHP_CLOSE;
+ if (isset($params['hasElse'])) {
+ $post .= $params['hasElse'];
+ }
+
+ return $pre . $content . $post;
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginForeachelse.php b/core/l/Dwoo/Plugins/Blocks/PluginForeachelse.php
new file mode 100644
index 0000000..5a09327
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginForeachelse.php
@@ -0,0 +1,73 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * This plugin serves as a {else} block specifically for the {foreach} plugin.
+ * 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.
+ */
+class PluginForeachelse extends BlockPlugin implements ICompilableBlock
+{
+ public function init()
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ $with = &$compiler->findBlock('foreach', true);
+
+ $params['initialized'] = true;
+ $compiler->injectBlock($type, $params);
+
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ if (!isset($params['initialized'])) {
+ return '';
+ }
+
+ $block = &$compiler->getCurrentBlock();
+ $block['params']['hasElse'] = Compiler::PHP_OPEN . "else {\n" . Compiler::PHP_CLOSE . $content . Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE;
+
+ return '';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginForelse.php b/core/l/Dwoo/Plugins/Blocks/PluginForelse.php
new file mode 100644
index 0000000..4d4a588
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginForelse.php
@@ -0,0 +1,73 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * This plugin serves as a {else} block specifically for the {for} plugin.
+ * 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.
+ */
+class PluginForelse extends BlockPlugin implements ICompilableBlock
+{
+ public function init()
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ $with = &$compiler->findBlock('for', true);
+
+ $params['initialized'] = true;
+ $compiler->injectBlock($type, $params);
+
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ if (!isset($params['initialized'])) {
+ return '';
+ }
+
+ $block = &$compiler->getCurrentBlock();
+ $block['params']['hasElse'] = Compiler::PHP_OPEN . "else {\n" . Compiler::PHP_CLOSE . $content . Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE;
+
+ return '';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginIf.php b/core/l/Dwoo/Plugins/Blocks/PluginIf.php
new file mode 100644
index 0000000..d982327
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginIf.php
@@ -0,0 +1,274 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\IElseable;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+use Dwoo\Compilation\Exception as CompilationException;
+
+/**
+ * Conditional block, the syntax is very similar to the php one, allowing () || && and
+ * other php operators. Additional operators and their equivalent php syntax are as follow :.
+ * eq -> ==
+ * neq or ne -> !=
+ * gte or ge -> >=
+ * lte or le -> <=
+ * gt -> >
+ * lt -> <
+ * mod -> %
+ * not -> !
+ * X is [not] div by Y -> (X % Y) == 0
+ * X is [not] even [by Y] -> (X % 2) == 0 or ((X/Y) % 2) == 0
+ * X is [not] odd [by Y] -> (X % 2) != 0 or ((X/Y) % 2) != 0
+ * 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.
+ */
+class PluginIf extends BlockPlugin implements ICompilableBlock, IElseable
+{
+ /**
+ * @param array $rest
+ */
+ public function init(array $rest)
+ {
+ }
+
+ /**
+ * @param array $params
+ * @param array $tokens
+ * @param Compiler $compiler
+ *
+ * @return array
+ * @throws CompilationException
+ */
+ public static function replaceKeywords(array $params, array $tokens, Compiler $compiler)
+ {
+ $p = array();
+
+ reset($params);
+ while (list($k, $v) = each($params)) {
+ $v = (string)$v;
+ if (substr($v, 0, 1) === '"' || substr($v, 0, 1) === '\'') {
+ $vmod = strtolower(substr($v, 1, - 1));
+ } else {
+ $vmod = strtolower($v);
+ }
+ switch ($vmod) {
+
+ case 'and':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '&&';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'or':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '||';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'xor':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '^';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'eq':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '==';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'ne':
+ case 'neq':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '!=';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'gte':
+ case 'ge':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '>=';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'lte':
+ case 'le':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '<=';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'gt':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '>';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'lt':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '<';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'mod':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '%';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case 'not':
+ if ($tokens[$k] === Compiler::T_UNQUOTED_STRING) {
+ $p[] = '!';
+ } else {
+ $p[] = $v;
+ }
+ break;
+ case '<>':
+ $p[] = '!=';
+ break;
+ case '==':
+ case '!=':
+ case '>=':
+ case '<=':
+ case '>':
+ case '<':
+ case '===':
+ case '!==':
+ case '%':
+ case '!':
+ case '^':
+ $p[] = $vmod;
+ break;
+ case 'is':
+ if ($tokens[$k] !== Compiler::T_UNQUOTED_STRING) {
+ $p[] = $v;
+ break;
+ }
+ if (isset($params[$k + 1]) && strtolower(trim($params[$k + 1], '"\'')) === 'not' && $tokens[$k + 1] === Compiler::T_UNQUOTED_STRING) {
+ $negate = true;
+ next($params);
+ } else {
+ $negate = false;
+ }
+ $ptr = 1 + (int)$negate;
+ if ($tokens[$k + $ptr] !== Compiler::T_UNQUOTED_STRING) {
+ break;
+ }
+ if (!isset($params[$k + $ptr])) {
+ $params[$k + $ptr] = '';
+ } else {
+ $params[$k + $ptr] = trim($params[$k + $ptr], '"\'');
+ }
+ switch ($params[$k + $ptr]) {
+
+ case 'div':
+ if (isset($params[$k + $ptr + 1]) && strtolower(trim($params[$k + $ptr + 1], '"\'')) === 'by') {
+ $p[] = ' % ' . $params[$k + $ptr + 2] . ' ' . ($negate ? '!' : '=') . '== 0';
+ next($params);
+ next($params);
+ next($params);
+ } else {
+ throw new CompilationException($compiler, 'If : Syntax error : syntax should be "if $a is [not] div by $b", found ' . $params[$k - 1] . ' is ' . ($negate ? 'not ' : '') . 'div ' . $params[$k + $ptr + 1] . ' ' . $params[$k + $ptr + 2]);
+ }
+ break;
+ case 'even':
+ $a = array_pop($p);
+ if (isset($params[$k + $ptr + 1]) && strtolower(trim($params[$k + $ptr + 1], '"\'')) === 'by') {
+ $b = $params[$k + $ptr + 2];
+ $p[] = '(' . $a . ' / ' . $b . ') % 2 ' . ($negate ? '!' : '=') . '== 0';
+ next($params);
+ next($params);
+ } else {
+ $p[] = $a . ' % 2 ' . ($negate ? '!' : '=') . '== 0';
+ }
+ next($params);
+ break;
+ case 'odd':
+ $a = array_pop($p);
+ if (isset($params[$k + $ptr + 1]) && strtolower(trim($params[$k + $ptr + 1], '"\'')) === 'by') {
+ $b = $params[$k + $ptr + 2];
+ $p[] = '(' . $a . ' / ' . $b . ') % 2 ' . ($negate ? '=' : '!') . '== 0';
+ next($params);
+ next($params);
+ } else {
+ $p[] = $a . ' % 2 ' . ($negate ? '=' : '!') . '== 0';
+ }
+ next($params);
+ break;
+ default:
+ throw new CompilationException($compiler, 'If : Syntax error : syntax should be "if $a is [not] (div|even|odd) [by $b]", found ' . $params[$k - 1] . ' is ' . $params[$k + $ptr + 1]);
+ }
+ break;
+ default:
+ $p[] = $v;
+ }
+ }
+
+ return $p;
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ $tokens = $compiler->getParamTokens($params);
+ $params = $compiler->getCompiledParams($params);
+ $pre = Compiler::PHP_OPEN . 'if (' . implode(' ', self::replaceKeywords($params['*'], $tokens['*'], $compiler)) . ") {\n" . Compiler::PHP_CLOSE;
+
+ $post = Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE;
+
+ if (isset($params['hasElse'])) {
+ $post .= $params['hasElse'];
+ }
+
+ return $pre . $content . $post;
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginLoop.php b/core/l/Dwoo/Plugins/Blocks/PluginLoop.php
new file mode 100644
index 0000000..5efbf43
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginLoop.php
@@ -0,0 +1,170 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\IElseable;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * Loops over an array and moves the scope into each value, allowing for shorter loop constructs.
+ * Note that to access the array key within a loop block, you have to use the {$_key} variable,
+ * you can not specify it yourself.
+ *
+ * * from : the array that you want to iterate over
+ * * name : loop name to access it's iterator variables through {$.loop.name.var} see {@link
+ * http://wiki.dwoo.org/index.php/IteratorVariables} for details
+ *
+ * * mode : sets the content being stripped, available mode are 'default' or 'js'
+ * for javascript, which strips the comments to prevent syntax errors
+ *
.
+ * 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.
+ */
+class PluginStrip extends BlockPlugin implements ICompilableBlock
+{
+ /**
+ * @param string $mode
+ */
+ public function init($mode = 'default')
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return mixed|string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ $params = $compiler->getCompiledParams($params);
+
+ $mode = trim($params['mode'], '"\'');
+ switch ($mode) {
+ case 'js':
+ case 'javascript':
+ $content = preg_replace('#(?
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-20
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Core;
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+use Dwoo\Compilation\Exception as CompilationException;
+
+/**
+ * Defines a sub-template that can then be called (even recursively) with the defined arguments
+ *
+ * * name : template name
+ * * rest : list of arguments and optional default values
+ *
+ * * wrap : maximum line length
+ * * wrap_char : the character(s) to use to break the line
+ * * wrap_cut : if true, the words that are longer than $wrap are cut instead of overflowing
+ * * indent : amount of $indent_char to insert before every line
+ * * indent_char : character(s) to insert before every line
+ * * indent_first : amount of additional $indent_char to insert before the first line of each paragraphs
+ * * style : some predefined formatting styles that set up every required variables, can be "email" or "html"
+ * * assign : if set, the formatted text is assigned to that variable instead of being output
+ *
+ * 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.
+ */
+class PluginTextformat extends BlockPlugin
+{
+ protected $wrap;
+ protected $wrapChar;
+ protected $wrapCut;
+ protected $indent;
+ protected $indChar;
+ protected $indFirst;
+ protected $assign;
+
+ /**
+ * @param int $wrap
+ * @param string $wrap_char
+ * @param bool $wrap_cut
+ * @param int $indent
+ * @param string $indent_char
+ * @param int $indent_first
+ * @param string $style
+ * @param string $assign
+ */
+ public function init($wrap = 80, $wrap_char = "\r\n", $wrap_cut = false, $indent = 0, $indent_char = ' ', $indent_first = 0, $style = '', $assign = '')
+ {
+ if ($indent_char === 'tab') {
+ $indent_char = "\t";
+ }
+
+ switch ($style) {
+
+ case 'email':
+ $wrap = 72;
+ $indent_first = 0;
+ break;
+ case 'html':
+ $wrap_char = ' ';
+ $indent_char = $indent_char == "\t" ? ' ' : ' ';
+ break;
+ }
+
+ $this->wrap = (int)$wrap;
+ $this->wrapChar = (string)$wrap_char;
+ $this->wrapCut = (bool)$wrap_cut;
+ $this->indent = (int)$indent;
+ $this->indChar = (string)$indent_char;
+ $this->indFirst = (int)$indent_first + $this->indent;
+ $this->assign = (string)$assign;
+ }
+
+ /**
+ * @return string
+ */
+ public function process()
+ {
+ // gets paragraphs
+ $pgs = explode("\n", str_replace(array(
+ "\r\n",
+ "\r"
+ ), "\n", $this->buffer));
+
+ while (list($i) = each($pgs)) {
+ if (empty($pgs[$i])) {
+ continue;
+ }
+
+ // removes line breaks and extensive white space
+ $pgs[$i] = preg_replace(array(
+ '#\s+#',
+ '#^\s*(.+?)\s*$#m'
+ ), array(
+ ' ',
+ '$1'
+ ), str_replace("\n", '', $pgs[$i]));
+
+ // wordwraps + indents lines
+ $pgs[$i] = str_repeat($this->indChar, $this->indFirst) . wordwrap($pgs[$i], max($this->wrap - $this->indent, 1), $this->wrapChar . str_repeat($this->indChar, $this->indent), $this->wrapCut);
+ }
+
+ if ($this->assign !== '') {
+ $this->core->assignInScope(implode($this->wrapChar . $this->wrapChar, $pgs), $this->assign);
+ } else {
+ return implode($this->wrapChar . $this->wrapChar, $pgs);
+ }
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginTopLevelBlock.php b/core/l/Dwoo/Plugins/Blocks/PluginTopLevelBlock.php
new file mode 100644
index 0000000..2694be2
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginTopLevelBlock.php
@@ -0,0 +1,60 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * Internal plugin used to wrap the template output, do not use in your templates as it will break them.
+ * This software is provided 'as-is', without any express or implied warranty.
+ */
+final class PluginTopLevelBlock extends BlockPlugin implements ICompilableBlock
+{
+ public function init()
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ return '/* end template head */ ob_start(); /* template body */ ' . Compiler::PHP_CLOSE;
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ return $content . Compiler::PHP_OPEN . ' /* end template body */' . "\n" . 'return $this->buffer . ob_get_clean();';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginWith.php b/core/l/Dwoo/Plugins/Blocks/PluginWith.php
new file mode 100644
index 0000000..f5e94fc
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginWith.php
@@ -0,0 +1,99 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\IElseable;
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * Moves the scope down into the provided variable, allowing you to use shorter
+ * variable names if you repeatedly access values into a single array.
+ * The with block won't display anything at all if the provided scope is empty,
+ * so in effect it acts as {if $var}*content*{/if}
+ *
+ * * var : the variable name to move into
+ *
+ * Example :
+ * instead of the following :
+ *
+ * {if $long.boring.prefix}
+ * {$long.boring.prefix.val} - {$long.boring.prefix.secondVal} - {$long.boring.prefix.thirdVal}
+ * {/if}
+ *
+ * you can use :
+ *
+ * {with $long.boring.prefix}
+ * {$val} - {$secondVal} - {$thirdVal}
+ * {/with}
+ *
+ * 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.
+ */
+class PluginWith extends BlockPlugin implements ICompilableBlock, IElseable
+{
+ protected static $cnt = 0;
+
+ /**
+ * @param $var
+ */
+ public function init($var)
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ $rparams = $compiler->getRealParams($params);
+ $cparams = $compiler->getCompiledParams($params);
+
+ $compiler->setScope($rparams['var']);
+
+ $pre = Compiler::PHP_OPEN . 'if (' . $cparams['var'] . ')' . "\n{\n" . '$_with' . (self::$cnt) . ' = $this->setScope("' . $rparams['var'] . '");' . "\n/* -- start with output */\n" . Compiler::PHP_CLOSE;
+
+ $post = Compiler::PHP_OPEN . "\n/* -- end with output */\n" . '$this->setScope($_with' . (self::$cnt ++) . ', true);' . "\n}\n" . Compiler::PHP_CLOSE;
+
+ if (isset($params['hasElse'])) {
+ $post .= $params['hasElse'];
+ }
+
+ return $pre . $content . $post;
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Blocks/PluginWithelse.php b/core/l/Dwoo/Plugins/Blocks/PluginWithelse.php
new file mode 100644
index 0000000..a614322
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Blocks/PluginWithelse.php
@@ -0,0 +1,73 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Blocks;
+
+use Dwoo\Compiler;
+use Dwoo\Block\Plugin as BlockPlugin;
+use Dwoo\ICompilable\Block as ICompilableBlock;
+
+/**
+ * This plugin serves as a {else} block specifically for the {with} plugin.
+ * 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.
+ */
+class PluginWithelse extends BlockPlugin implements ICompilableBlock
+{
+ public function init()
+ {
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $type
+ *
+ * @return string
+ */
+ public static function preProcessing(Compiler $compiler, array $params, $prepend, $append, $type)
+ {
+ $with = &$compiler->findBlock('with', true);
+
+ $params['initialized'] = true;
+ $compiler->injectBlock($type, $params);
+
+ return '';
+ }
+
+ /**
+ * @param Compiler $compiler
+ * @param array $params
+ * @param string $prepend
+ * @param string $append
+ * @param string $content
+ *
+ * @return string
+ */
+ public static function postProcessing(Compiler $compiler, array $params, $prepend, $append, $content)
+ {
+ if (!isset($params['initialized'])) {
+ return '';
+ }
+
+ $block = &$compiler->getCurrentBlock();
+ $block['params']['hasElse'] = Compiler::PHP_OPEN . "else {\n" . Compiler::PHP_CLOSE . $content . Compiler::PHP_OPEN . "\n}" . Compiler::PHP_CLOSE;
+
+ return '';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Filters/PluginHtmlFormat.php b/core/l/Dwoo/Plugins/Filters/PluginHtmlFormat.php
new file mode 100644
index 0000000..f923de4
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Filters/PluginHtmlFormat.php
@@ -0,0 +1,190 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-18
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Filters;
+
+use Dwoo\Filter;
+
+/**
+ * Formats any html output (must be valid xml where every tag opened is closed)
+ * using a single tab for indenting. 'pre' and other whitespace sensitive
+ * tags should not be affected.
+ * It is not recommended to use this on every template if you render multiple
+ * templates per page, you should only use it once on the main page template so that
+ * everything is formatted in one pass.
+ * 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.
+ */
+class PluginHtmlFormat extends Filter
+{
+ /**
+ * tab count to auto-indent the source.
+ *
+ * @var int
+ */
+ protected static $tabCount = - 1;
+
+ /**
+ * stores the additional data (following a tag) of the last call to open/close/singleTag.
+ *
+ * @var string
+ */
+ protected static $lastCallAdd = '';
+
+ /**
+ * formats the input using the singleTag/closeTag/openTag functions.
+ * It is auto indenting the whole code, excluding ') {
+ return self::singleTag($input[1], $input[3], $input[2]);
+ }
+ // it's the closing tag
+ if ($input[0][1] == '/') {
+ return self::closeTag($input[1], $input[3], $input[2]);
+ }
+
+ // opening tag
+ return self::openTag($input[1], $input[3], $input[2]);
+ }
+
+ /**
+ * returns an open tag and adds a tab into the auto indenting.
+ *
+ * @param string $tag content of the tag
+ * @param string $add additional data (anything before the following tag)
+ * @param string $whitespace white space between the tag and the additional data
+ *
+ * @return string
+ */
+ protected static function openTag($tag, $add, $whitespace)
+ {
+ $tabs = str_pad('', self::$tabCount ++, "\t");
+
+ if (preg_match('#^<(a|label|option|textarea|h1|h2|h3|h4|h5|h6|strong|b|em|i|abbr|acronym|cite|span|sub|sup|u|s|title)(?: [^>]*|)>#', $tag)) {
+ // if it's one of those tag it's inline so it does not require a leading line break
+ $result = $tag . $whitespace . str_replace("\n", "\n" . $tabs, $add);
+ } elseif (substr($tag, 0, 9) == '#', $tag)) {
+ $result = $tag . $whitespace . str_replace("\n", "\n" . $tabs, $add);
+ } else {
+ $result = "\n" . $tabs . $tag;
+
+ if (!empty($add)) {
+ $result .= "\n" . $tabs . "\t" . str_replace("\n", "\n\t" . $tabs, $add);
+ }
+ }
+
+ self::$lastCallAdd = $add;
+
+ return $result;
+ }
+
+ /**
+ * returns a single tag with auto indenting.
+ *
+ * @param string $tag content of the tag
+ * @param string $add additional data (anything before the following tag)
+ *
+ * @return string
+ */
+ protected static function singleTag($tag, $add, $whitespace)
+ {
+ $tabs = str_pad('', self::$tabCount, "\t");
+
+ // if it's img, br it's inline so it does not require a leading line break
+ // if it's a closing textarea, code or pre tag, it does not require a leading line break either or it creates whitespace at the end of those blocks
+ if (preg_match('#^<(img|br|/textarea|/pre|/code)(?: [^>]*|)>#', $tag)) {
+ $result = $tag . $whitespace;
+
+ if (!empty($add)) {
+ $result .= str_replace("\n", "\n" . $tabs, $add);
+ }
+ } else {
+ $result = "\n" . $tabs . $tag;
+
+ if (!empty($add)) {
+ $result .= "\n" . $tabs . str_replace("\n", "\n" . $tabs, $add);
+ }
+ }
+
+ self::$lastCallAdd = $add;
+
+ return $result;
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Functions/PluginAssign.php b/core/l/Dwoo/Plugins/Functions/PluginAssign.php
new file mode 100644
index 0000000..0e3f69d
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginAssign.php
@@ -0,0 +1,46 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Assigns a value to a variable
+ *
+ * * value : the value that you want to save
+ * * var : the variable name (without the leading $)
+ *
+ * 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.
+ */
+class PluginAssign extends Plugin implements ICompilable
+{
+
+ /**
+ * @param Compiler $compiler
+ * @param mixed $value
+ * @param mixed $var
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $var)
+ {
+ return '$this->assignInScope(' . $value . ', ' . $var . ')';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginCapitalize.php b/core/l/Dwoo/Plugins/Functions/PluginCapitalize.php
new file mode 100644
index 0000000..b54a602
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginCapitalize.php
@@ -0,0 +1,56 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.4
+ * @date 2017-03-01
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Plugin;
+
+/**
+ * Capitalizes the first letter of each word
+ *
+ * * value : the string to capitalize
+ * * numwords : if true, the words containing numbers are capitalized as well
+ *
+ * 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.
+ */
+class PluginCapitalize extends Plugin
+{
+ /**
+ * @param string $value
+ * @param bool $numwords
+ *
+ * @return string
+ */
+ public function process($value, $numwords = false)
+ {
+ if ($numwords || preg_match('#^[^0-9]+$#', $value)) {
+ return mb_convert_case((string)$value, MB_CASE_TITLE, $this->core->getCharset());
+ } else {
+ $bits = explode(' ', (string)$value);
+ $out = '';
+ foreach ($bits as $k => $v){
+ if (preg_match('#^[^0-9]+$#', $v)) {
+ $out .= ' ' . mb_convert_case($v, MB_CASE_TITLE, $this->core->getCharset());
+ } else {
+ $out .= ' ' . $v;
+ }
+ }
+
+ return substr($out, 1);
+ }
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Functions/PluginCat.php b/core/l/Dwoo/Plugins/Functions/PluginCat.php
new file mode 100644
index 0000000..20a88bc
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginCat.php
@@ -0,0 +1,44 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Concatenates any number of variables or strings fed into it
+ *
+ * * rest : two or more strings that will be merged into one
+ *
+ * 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.
+ */
+class PluginCat extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ * @param array $rest
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, array $rest)
+ {
+ return '(' . $value . ').(' . implode(').(', $rest) . ')';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Functions/PluginCountCharacters.php b/core/l/Dwoo/Plugins/Functions/PluginCountCharacters.php
new file mode 100644
index 0000000..f2c01b2
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginCountCharacters.php
@@ -0,0 +1,49 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Counts the characters in a string
+ *
+ * * value : the string to process
+ * * count_spaces : if true, the white-space characters are counted as well
+ *
+ * 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.
+ */
+class PluginCountCharacters extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ * @param bool $count_spaces
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $count_spaces = false)
+ {
+ if ($count_spaces === 'false') {
+ return 'preg_match_all(\'#[^\s\pZ]#u\', ' . $value . ', $tmp)';
+ }
+
+ return 'mb_strlen(' . $value . ', $this->charset)';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginCountParagraphs.php b/core/l/Dwoo/Plugins/Functions/PluginCountParagraphs.php
new file mode 100644
index 0000000..2ab75e3
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginCountParagraphs.php
@@ -0,0 +1,43 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Counts the paragraphs in a string
+ *
+ * * value : the string to process
+ *
+ * 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.
+ */
+class PluginCountParagraphs extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value)
+ {
+ return '(preg_match_all(\'#[\r\n]+#\', ' . $value . ', $tmp)+1)';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginCountSentences.php b/core/l/Dwoo/Plugins/Functions/PluginCountSentences.php
new file mode 100644
index 0000000..ebbfd34
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginCountSentences.php
@@ -0,0 +1,43 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Counts the sentences in a string
+ *
+ * * value : the string to process
+ *
+ * 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.
+ */
+class PluginCountSentences extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value)
+ {
+ return "preg_match_all('#[\\w\\pL]\\.(?![\\w\\pL])#u', $value, \$tmp)";
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginCountWords.php b/core/l/Dwoo/Plugins/Functions/PluginCountWords.php
new file mode 100644
index 0000000..e5d165e
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginCountWords.php
@@ -0,0 +1,37 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Counts the words in a string
+ *
+ * * value : the string to process
+ *
+ * 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.
+ */
+class PluginCountWords extends Plugin implements ICompilable
+{
+ public static function compile(Compiler $compiler, $value)
+ {
+ return 'preg_match_all(strcasecmp($this->charset, \'utf-8\')===0 ? \'#[\w\pL]+#u\' : \'#\w+#\', ' . $value . ', $tmp)';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginCounter.php b/core/l/Dwoo/Plugins/Functions/PluginCounter.php
new file mode 100644
index 0000000..c5fb721
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginCounter.php
@@ -0,0 +1,94 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Plugin;
+
+/**
+ * Initiates a counter that is incremented every time you call it
+ *
+ * * name : the counter name, define it if you want to have multiple concurrent counters
+ * * start : the start value, if it's set, it will reset the counter to this value, defaults to 1
+ * * skip : the value to add to the counter at each call, defaults to 1
+ * * direction : "up" (default) or "down" to define whether the counter increments or decrements
+ * * print : if false, the counter will not output the current count, defaults to true
+ * * assign : if set, the counter is saved into the given variable and does not output anything, overriding the print
+ * parameter
+ *
+ * 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.
+ */
+class PluginCounter extends Plugin
+{
+ protected $counters = array();
+
+ /**
+ * @param string $name
+ * @param null $start
+ * @param null $skip
+ * @param null $direction
+ * @param null $print
+ * @param null $assign
+ *
+ * @return mixed
+ */
+ public function process($name = 'default', $start = null, $skip = null, $direction = null, $print = null, $assign = null)
+ {
+ // init counter
+ if (!isset($this->counters[$name])) {
+ $this->counters[$name] = array(
+ 'count' => $start === null ? 1 : (int)$start,
+ 'skip' => $skip === null ? 1 : (int)$skip,
+ 'print' => $print === null ? true : (bool)$print,
+ 'assign' => $assign === null ? null : (string)$assign,
+ 'direction' => strtolower($direction) === 'down' ? - 1 : 1,
+ );
+ } // increment
+ else {
+ // override setting if present
+ if ($skip !== null) {
+ $this->counters[$name]['skip'] = (int)$skip;
+ }
+
+ if ($direction !== null) {
+ $this->counters[$name]['direction'] = strtolower($direction) === 'down' ? - 1 : 1;
+ }
+
+ if ($print !== null) {
+ $this->counters[$name]['print'] = (bool)$print;
+ }
+
+ if ($assign !== null) {
+ $this->counters[$name]['assign'] = (string)$assign;
+ }
+
+ if ($start !== null) {
+ $this->counters[$name]['count'] = (int)$start;
+ } else {
+ $this->counters[$name]['count'] += ($this->counters[$name]['skip'] * $this->counters[$name]['direction']);
+ }
+ }
+
+ $out = $this->counters[$name]['count'];
+
+ if ($this->counters[$name]['assign'] !== null) {
+ $this->core->assignInScope($out, $this->counters[$name]['assign']);
+ } elseif ($this->counters[$name]['print'] === true) {
+ return $out;
+ }
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Functions/PluginCycle.php b/core/l/Dwoo/Plugins/Functions/PluginCycle.php
new file mode 100644
index 0000000..3741da4
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginCycle.php
@@ -0,0 +1,91 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Plugin;
+
+/**
+ * Cycles between several values and returns one of them on each call
+ *
+ * * name : the cycler name, specify if you need to have multiple concurrent cycles running
+ * * values : an array of values or a string of values delimited by $delimiter
+ * * print : if false, the pointer will go to the next one but not print anything
+ * * advance : if false, the pointer will not advance to the next value
+ * * delimiter : the delimiter used to split values if they are provided as a string
+ * * assign : if set, the value is saved in that variable instead of being output
+ * * reset : if true, the pointer is reset to the first value
+ *
+ * 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.
+ */
+class PluginCycle extends Plugin
+{
+ protected $cycles = array();
+
+ /**
+ * @param string $name
+ * @param null $values
+ * @param bool $print
+ * @param bool $advance
+ * @param string $delimiter
+ * @param null $assign
+ * @param bool $reset
+ *
+ * @return string|null
+ */
+ public function process($name = 'default', $values = null, $print = true, $advance = true, $delimiter = ',', $assign = null, $reset = false)
+ {
+ if ($values !== null) {
+ if (is_string($values)) {
+ $values = explode($delimiter, $values);
+ }
+
+ if (!isset($this->cycles[$name]) || $this->cycles[$name]['values'] !== $values) {
+ $this->cycles[$name]['index'] = 0;
+ }
+
+ $this->cycles[$name]['values'] = array_values($values);
+ } elseif (isset($this->cycles[$name])) {
+ $values = $this->cycles[$name]['values'];
+ }
+
+ if ($reset) {
+ $this->cycles[$name]['index'] = 0;
+ }
+
+ if ($print) {
+ $out = $values[$this->cycles[$name]['index']];
+ } else {
+ $out = null;
+ }
+
+ if ($advance) {
+ if ($this->cycles[$name]['index'] >= count($values) - 1) {
+ $this->cycles[$name]['index'] = 0;
+ } else {
+ ++ $this->cycles[$name]['index'];
+ }
+ }
+
+ if ($assign === null) {
+ return $out;
+ }
+ $this->core->assignInScope($out, $assign);
+
+ return null;
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Functions/PluginDateFormat.php b/core/l/Dwoo/Plugins/Functions/PluginDateFormat.php
new file mode 100644
index 0000000..c6c5258
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginDateFormat.php
@@ -0,0 +1,92 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Core;
+use Dwoo\Plugin;
+
+/**
+ * Formats a date
+ *
+ * * value : the date, as a unix timestamp, mysql datetime or whatever strtotime() can parse
+ * * format : output format, see {@link http://php.net/strftime} for details
+ * * default : a default timestamp value, if the first one is empty
+ *
+ * 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.
+ */
+class PluginDateFormat extends Plugin
+{
+ /**
+ * @param mixed $value
+ * @param string $format
+ * @param null $default
+ *
+ * @return string
+ */
+ public function process($value, $format = '%b %e, %Y', $default = null)
+ {
+ if (!empty($value)) {
+ // convert if it's not a valid unix timestamp
+ if (preg_match('#^-?\d{1,10}$#', $value) === 0) {
+ $value = strtotime($value);
+ }
+ } elseif (!empty($default)) {
+ // convert if it's not a valid unix timestamp
+ if (preg_match('#^-?\d{1,10}$#', $default) === 0) {
+ $value = strtotime($default);
+ } else {
+ $value = $default;
+ }
+ } else {
+ return '';
+ }
+
+ // Credits for that windows compat block to Monte Ohrt who made smarty's date_format plugin
+ if (DIRECTORY_SEPARATOR == '\\') {
+ $_win_from = array(
+ '%D',
+ '%h',
+ '%n',
+ '%r',
+ '%R',
+ '%t',
+ '%T'
+ );
+ $_win_to = array(
+ '%m/%d/%y',
+ '%b',
+ "\n",
+ '%I:%M:%S %p',
+ '%H:%M',
+ "\t",
+ '%H:%M:%S'
+ );
+ if (strpos($format, '%e') !== false) {
+ $_win_from[] = '%e';
+ $_win_to[] = sprintf('%\' 2d', date('j', $value));
+ }
+ if (strpos($format, '%l') !== false) {
+ $_win_from[] = '%l';
+ $_win_to[] = sprintf('%\' 2d', date('h', $value));
+ }
+ $format = str_replace($_win_from, $_win_to, $format);
+ }
+
+ return strftime($format, $value);
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginDefault.php b/core/l/Dwoo/Plugins/Functions/PluginDefault.php
new file mode 100644
index 0000000..a9c3076
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginDefault.php
@@ -0,0 +1,45 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Returns a variable or a default value if it's empty
+ *
+ * * value : the variable to check
+ * * default : fallback value if the first one is empty
+ *
+ * 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.
+ */
+class PluginDefault extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param mixed $value
+ * @param string $default
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $default = '')
+ {
+ return '(($tmp = ' . $value . ')===null||$tmp===\'\' ? ' . $default . ' : $tmp)';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginDump.php b/core/l/Dwoo/Plugins/Functions/PluginDump.php
new file mode 100644
index 0000000..09e9afd
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginDump.php
@@ -0,0 +1,210 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Plugin;
+use Iterator;
+use ReflectionObject;
+
+/**
+ * Dumps values of the given variable, or the entire data if nothing provided
+ *
+ * * var : the variable to display
+ * * show_methods : if set to true, the public methods of any object encountered are also displayed
+ *
+ * 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.
+ */
+class PluginDump extends Plugin
+{
+ protected $outputObjects;
+ protected $outputMethods;
+
+ /**
+ * @param string $var
+ * @param bool $show_methods
+ *
+ * @return string
+ */
+ public function process($var = '$', $show_methods = false)
+ {
+ $this->outputMethods = $show_methods;
+ if ($var === '$') {
+ $var = $this->core->getData();
+ $out = '
';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Functions/PluginEol.php b/core/l/Dwoo/Plugins/Functions/PluginEol.php
new file mode 100644
index 0000000..f4f8504
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginEol.php
@@ -0,0 +1,39 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Returns the correct end of line character(s) for the current operating system.
+ * 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.
+ */
+class PluginEol extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler)
+ {
+ return 'PHP_EOL';
+ }
+}
diff --git a/core/l/Dwoo/Plugins/Functions/PluginEscape.php b/core/l/Dwoo/Plugins/Functions/PluginEscape.php
new file mode 100644
index 0000000..1bb8745
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginEscape.php
@@ -0,0 +1,103 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Plugin;
+
+/**
+ * Applies various escaping schemes on the given string
+ *
+ * * value : the string to process
+ * * format : escaping format to use, valid formats are : html, htmlall, url, urlpathinfo, quotes, hex, hexentity,
+ * javascript and mail
+ * * charset : character set to use for the conversion (applies to some formats only), defaults to the current Dwoo
+ * charset
+ *
+ * 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.
+ *
+ * @return mixed|string
+ */
+class PluginEscape extends Plugin
+{
+ /**
+ * @param string $value
+ * @param string $format
+ * @param null $charset
+ *
+ * @return mixed|string
+ */
+ public function process($value = '', $format = 'html', $charset = null)
+ {
+ if ($charset === null) {
+ $charset = $this->core->getCharset();
+ }
+
+ switch ($format) {
+ case 'html':
+ return htmlspecialchars((string)$value, ENT_QUOTES, $charset);
+ case 'htmlall':
+ return htmlentities((string)$value, ENT_QUOTES, $charset);
+ case 'url':
+ return rawurlencode((string)$value);
+ case 'urlpathinfo':
+ return str_replace('%2F', '/', rawurlencode((string)$value));
+ case 'quotes':
+ return preg_replace("#(? '\\\\',
+ "'" => "\\'",
+ '"' => '\\"',
+ "\r" => '\\r',
+ "\n" => '\\n',
+ '' => '<\/'
+ ));
+ case 'mail':
+ return str_replace(array(
+ '@',
+ '.'
+ ),
+ array(
+ ' (AT) ',
+ ' (DOT) '
+ ),
+ (string)$value);
+ default:
+ $this->core->triggerError('Escape\'s format argument must be one of : html, htmlall, url, urlpathinfo, hex, hexentity, javascript, js or mail, "' . $format . '" given.',
+ E_USER_WARNING);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginEval.php b/core/l/Dwoo/Plugins/Functions/PluginEval.php
new file mode 100644
index 0000000..f787c14
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginEval.php
@@ -0,0 +1,62 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Core;
+use Dwoo\Plugin;
+use Dwoo\Template\Str as TemplateString;
+
+/**
+ * Evaluates the given string as if it was a template.
+ * Although this plugin is kind of optimized and will
+ * not recompile your string each time, it is still not
+ * a good practice to use it. If you want to have templates
+ * stored in a database or something you should probably use
+ * the Dwoo\Template\Str class or make another class that
+ * extends it
+ *
+ * * var : the string to use as a template
+ * * assign : if set, the output of the template will be saved in this variable instead of being output
+ *
+ * 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.
+ */
+class PluginEval extends Plugin
+{
+ /**
+ * @param string $var
+ * @param null $assign
+ *
+ * @return string
+ */
+ public function process($var, $assign = null)
+ {
+ if ($var == '') {
+ return '';
+ }
+
+ $tpl = new TemplateString($var);
+ $clone = clone $this->core;
+ $out = $clone->get($tpl, $this->core->readVar('_parent'));
+
+ if ($assign !== null) {
+ $this->core->assignInScope($out, $assign);
+ } else {
+ return $out;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginExtends.php b/core/l/Dwoo/Plugins/Functions/PluginExtends.php
new file mode 100644
index 0000000..3ab5f98
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginExtends.php
@@ -0,0 +1,206 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.3.6
+ * @date 2017-03-21
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+use Dwoo\Exception as Exception;
+use Dwoo\Security\Exception as SecurityException;
+use Dwoo\Compilation\Exception as CompilationException;
+
+/**
+ * Extends another template, read more about template inheritance at {@link
+ * http://wiki.dwoo.org/index.php/TemplateInheritance}
+ *
+ * * file : path or URI of the file to read (however reading from another website is not recommended for performance
+ * reasons)
+ * * assign : if set, the file will be saved in this variable instead of being output
+ *
+ * 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.
+ */
+class PluginFetch extends Plugin
+{
+ /**
+ * @param string $file
+ * @param null $assign
+ *
+ * @return string
+ */
+ public function process($file, $assign = null)
+ {
+ if ($file === '') {
+ return '';
+ }
+
+ if ($policy = $this->core->getSecurityPolicy()) {
+ while (true) {
+ if (preg_match('{^([a-z]+?)://}i', $file)) {
+ $this->core->triggerError('The security policy prevents you to read files from external sources.',
+ E_USER_WARNING);
+ }
+
+ $file = realpath($file);
+ $dirs = $policy->getAllowedDirectories();
+ foreach ($dirs as $dir => $dummy) {
+ if (strpos($file, $dir) === 0) {
+ break 2;
+ }
+ }
+ $this->core->triggerError('The security policy prevents you to read ' . $file . '',
+ E_USER_WARNING);
+ }
+ }
+ $file = str_replace(array(
+ "\t",
+ "\n",
+ "\r"
+ ),
+ array(
+ '\\t',
+ '\\n',
+ '\\r'
+ ),
+ $file);
+
+ $out = file_get_contents($file);
+
+ if ($assign === null) {
+ return $out;
+ }
+ $this->core->assignInScope($out, $assign);
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginInclude.php b/core/l/Dwoo/Plugins/Functions/PluginInclude.php
new file mode 100644
index 0000000..ce3a370
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginInclude.php
@@ -0,0 +1,110 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Exception as Exception;
+use Dwoo\Plugin;
+use Dwoo\Security\Exception as SecurityException;
+
+/**
+ * Inserts another template into the current one
+ *
+ * * file : the resource name of the template
+ * * cache_time : cache length in seconds
+ * * cache_id : cache identifier for the included template
+ * * compile_id : compilation identifier for the included template
+ * * data : data to feed into the included template, it can be any array and will default to $_root (the current data)
+ * * assign : if set, the output of the included template will be saved in this variable instead of being output
+ * * rest : any additional parameter/value provided will be added to the data array
+ *
+ * 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.
+ */
+class PluginInclude extends Plugin
+{
+ /**
+ * @param $file
+ * @param null $cache_time
+ * @param null $cache_id
+ * @param null $compile_id
+ * @param string $data
+ * @param null $assign
+ * @param array $rest
+ *
+ * @return string
+ */
+ public function process($file, $cache_time = null, $cache_id = null, $compile_id = null, $data = '_root', $assign = null, array $rest = array())
+ {
+ $include = null;
+ if ($file === '') {
+ return '';
+ }
+
+ if (preg_match('#^([a-z]{2,}):(.*)$#i', $file, $m)) {
+ // resource:identifier given, extract them
+ $resource = $m[1];
+ $identifier = $m[2];
+ } else {
+ // get the current template's resource
+ $resource = $this->core->getTemplate()->getResourceName();
+ $identifier = $file;
+ }
+
+ try {
+ $include = $this->core->templateFactory($resource, $identifier, $cache_time, $cache_id, $compile_id);
+ }
+ catch (SecurityException $e) {
+ $this->core->triggerError('Include : Security restriction : ' . $e->getMessage(), E_USER_WARNING);
+ }
+ catch (Exception $e) {
+ $this->core->triggerError('Include : ' . $e->getMessage(), E_USER_WARNING);
+ }
+
+ if ($include === null) {
+ $this->core->triggerError('Include : Resource "' . $resource . ':' . $identifier . '" not found.',
+ E_USER_WARNING);
+ } elseif ($include === false) {
+ $this->core->triggerError('Include : Resource "' . $resource . '" does not support includes.',
+ E_USER_WARNING);
+ }
+
+ if (is_string($data)) {
+ $vars = $this->core->readVar($data);
+ } else {
+ $vars = $data;
+ }
+
+ if (count($rest)) {
+ $vars = $rest + $vars;
+ }
+
+ $clone = clone $this->core;
+ $out = $clone->get($include, $vars);
+
+ if ($assign !== null) {
+ $this->core->assignInScope($out, $assign);
+ }
+
+ foreach ($clone->getReturnValues() as $name => $value) {
+ $this->core->assignInScope($value, $name);
+ }
+
+ if ($assign === null) {
+ return $out;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginIndent.php b/core/l/Dwoo/Plugins/Functions/PluginIndent.php
new file mode 100644
index 0000000..5d1105e
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginIndent.php
@@ -0,0 +1,47 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Indents every line of a text by the given amount of characters
+ *
+ * * value : the string to indent
+ * * by : how many characters should be inserted before each line
+ * * char : the character(s) to insert
+ *
+ * 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.
+ */
+class PluginIndent extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ * @param int $by
+ * @param string $char
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $by = 4, $char = ' ')
+ {
+ return "preg_replace('#^#m', '" . str_repeat(substr($char, 1, - 1), trim($by, '"\'')) . "', $value)";
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginIsset.php b/core/l/Dwoo/Plugins/Functions/PluginIsset.php
new file mode 100644
index 0000000..756312a
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginIsset.php
@@ -0,0 +1,43 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Checks whether a variable is not null
+ *
+ * * var : variable to check
+ *
+ * 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.
+ */
+class PluginIsset extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param mixed $var
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $var)
+ {
+ return '(' . $var . ' !== null)';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginLoadTemplates.php b/core/l/Dwoo/Plugins/Functions/PluginLoadTemplates.php
new file mode 100644
index 0000000..65a2cae
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginLoadTemplates.php
@@ -0,0 +1,99 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.3.6
+ * @date 2017-03-21
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\Compilation\Exception as CompilationException;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Loads sub-templates contained in an external file
+ *
+ * * file : the resource name of the file to load
+ *
+ * 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.
+ */
+class PluginLoadTemplates extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $file
+ *
+ * @return string
+ * @throws CompilationException
+ */
+ public static function compile(Compiler $compiler, $file)
+ {
+ $file = substr($file, 1, - 1);
+
+ if ($file === '') {
+ return '';
+ }
+
+ if (preg_match('#^([a-z]{2,}):(.*)$#i', $file, $m)) {
+ // resource:identifier given, extract them
+ $resource = $m[1];
+ $identifier = $m[2];
+ } else {
+ // get the current template's resource
+ $resource = $compiler->getCore()->getTemplate()->getResourceName();
+ $identifier = $file;
+ }
+
+ $tpl = $compiler->getCore()->templateFactory($resource, $identifier);
+
+ if ($tpl === null) {
+ throw new CompilationException($compiler,
+ 'Load Templates : Resource "' . $resource . ':' . $identifier . '" not found.');
+ } elseif ($tpl === false) {
+ throw new CompilationException($compiler,
+ 'Load Templates : Resource "' . $resource . '" does not support includes.');
+ }
+
+ $cmp = clone $compiler;
+ $cmp->compile($compiler->getCore(), $tpl);
+ foreach ($cmp->getTemplatePlugins() as $template => $args) {
+ $compiler->addTemplatePlugin($template, $args['params'], $args['uuid'], $args['body']);
+ }
+ foreach ($cmp->getUsedPlugins() as $plugin => $type) {
+ $compiler->addUsedPlugin($plugin, $type);
+ }
+
+ $out = '\'\';// checking for modification in ' . $resource . ':' . $identifier . "\r\n";
+
+ $modCheck = $tpl->getIsModifiedCode();
+
+ if ($modCheck) {
+ $out .= 'if (!(' . $modCheck . ')) { ob_end_clean(); return false; }';
+ } else {
+ $out .= 'try {
+ $tpl = $this->templateFactory("' . $resource . '", "' . $identifier . '");
+} catch (Dwoo\Exception $e) {
+ $this->triggerError(\'Load Templates : Resource ' . $resource . ' was not added to Dwoo, can not extend ' . $identifier . '\', E_USER_WARNING);
+}
+if ($tpl === null)
+ $this->triggerError(\'Load Templates : Resource "' . $resource . ':' . $identifier . '" was not found.\', E_USER_WARNING);
+elseif ($tpl === false)
+ $this->triggerError(\'Load Templates : Resource "' . $resource . '" does not support extends.\', E_USER_WARNING);
+if ($tpl->getUid() != "' . $tpl->getUid() . '") { ob_end_clean(); return false; }';
+ }
+
+ return $out;
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginLower.php b/core/l/Dwoo/Plugins/Functions/PluginLower.php
new file mode 100644
index 0000000..f729dd1
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginLower.php
@@ -0,0 +1,43 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Makes the input string lower cased
+ *
+ * * value : the string to process
+ *
+ * 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.
+ */
+class PluginLower extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value)
+ {
+ return 'mb_strtolower((string) ' . $value . ', $this->charset)';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginMailto.php b/core/l/Dwoo/Plugins/Functions/PluginMailto.php
new file mode 100644
index 0000000..00fb768
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginMailto.php
@@ -0,0 +1,144 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Plugin;
+
+/**
+ * Outputs a mailto link with optional spam-proof (okay probably not) encoding
+ *
+ * * address : target email address
+ * * text : display text to show for the link, defaults to the address if not provided
+ * * subject : the email subject
+ * * encode : one of the available encoding (none, js, jscharcode or hex)
+ * * cc : address(es) to carbon copy, comma separated
+ * * bcc : address(es) to blind carbon copy, comma separated
+ * * newsgroups : newsgroup(s) to post to, comma separated
+ * * followupto : address(es) to follow up, comma separated
+ * * extra : additional attributes to add to the <a> tag
+ *
+ * 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.
+ */
+class PluginMailto extends Plugin
+{
+ /**
+ * @param $address
+ * @param null $text
+ * @param null $subject
+ * @param null $encode
+ * @param null $cc
+ * @param null $bcc
+ * @param null $newsgroups
+ * @param null $followupto
+ * @param null $extra
+ *
+ * @return string
+ */
+ public function process($address, $text = null, $subject = null, $encode = null, $cc = null, $bcc = null, $newsgroups = null, $followupto = null, $extra = null)
+ {
+ if (empty($address)) {
+ return '';
+ }
+ if (empty($text)) {
+ $text = $address;
+ }
+
+ // build address string
+ $address .= '?';
+
+ if (!empty($subject)) {
+ $address .= 'subject=' . rawurlencode($subject) . '&';
+ }
+ if (!empty($cc)) {
+ $address .= 'cc=' . rawurlencode($cc) . '&';
+ }
+ if (!empty($bcc)) {
+ $address .= 'bcc=' . rawurlencode($bcc) . '&';
+ }
+ if (!empty($newsgroups)) {
+ $address .= 'newsgroups=' . rawurlencode($newsgroups) . '&';
+ }
+ if (!empty($followupto)) {
+ $address .= 'followupto=' . rawurlencode($followupto) . '&';
+ }
+
+ $address = rtrim($address, '?&');
+
+ // output
+ switch ($encode) {
+
+ case 'none':
+ case null:
+ return '' . $text . '';
+
+ case 'js':
+ case 'javascript':
+ $str = 'document.write(\'' . $text . '\');';
+ $len = strlen($str);
+
+ $out = '';
+ for ($i = 0; $i < $len; ++ $i) {
+ $out .= '%' . bin2hex($str[$i]);
+ }
+
+ return '';
+
+ break;
+ case 'javascript_charcode':
+ case 'js_charcode':
+ case 'jscharcode':
+ case 'jschar':
+ $str = '' . $text . '';
+ $len = strlen($str);
+
+ $out = '\n";
+
+ break;
+
+ case 'hex':
+ if (strpos($address, '?') !== false) {
+ $this->core->triggerError('Mailto: Hex encoding is not possible with extra attributes, use one of : js, jscharcode or none.', E_USER_WARNING);
+ }
+
+ $out = '';
+ $len = strlen($text);
+ for ($i = 0; $i < $len; ++ $i) {
+ $out .= '' . bin2hex($text[$i]);
+ }
+
+ return $out . '';
+
+ default:
+ $this->core->triggerError('Mailto: encode argument is invalid, it must be one of : none (= no value), js, js_charcode or hex', E_USER_WARNING);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginMath.php b/core/l/Dwoo/Plugins/Functions/PluginMath.php
new file mode 100644
index 0000000..0e93ee4
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginMath.php
@@ -0,0 +1,199 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\Compilation\Exception as CompilationException;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Computes a mathematical equation
+ *
+ * * equation : the equation to compute, it can include normal variables with $foo or special math variables without
+ * the dollar sign
+ * * format : output format, see {@link http://php.net/sprintf} for details
+ * * assign : if set, the output is assigned into the given variable name instead of being output
+ * * rest : all math specific variables that you use must be defined, see the example
+ *
+ * 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.
+ */
+class PluginNl2br extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value)
+ {
+ return 'nl2br((string) ' . $value . ')';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginOptional.php b/core/l/Dwoo/Plugins/Functions/PluginOptional.php
new file mode 100644
index 0000000..d9a10b8
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginOptional.php
@@ -0,0 +1,43 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Prints out a variable without any notice if it doesn't exist.
+ *
+ * * value : the variable to print
+ *
+ * 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.
+ */
+class PluginOptional extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ *
+ * @return mixed
+ */
+ public static function compile(Compiler $compiler, $value)
+ {
+ return $value;
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginRegexReplace.php b/core/l/Dwoo/Plugins/Functions/PluginRegexReplace.php
new file mode 100644
index 0000000..bfd587f
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginRegexReplace.php
@@ -0,0 +1,63 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Plugin;
+
+/**
+ * Replaces the search string by the replace string using regular expressions
+ *
+ * * value : the string to search into
+ * * search : the string to search for, must be a complete regular expression including delimiters
+ * * replace : the string to use as a replacement, must be a complete regular expression including delimiters
+ *
+ * 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.
+ */
+class PluginRegexReplace extends Plugin
+{
+ /**
+ * @param string $value
+ * @param string $search
+ * @param string $replace
+ *
+ * @return mixed
+ */
+ public function process($value, $search, $replace)
+ {
+ $search = (array)$search;
+ $cnt = count($search);
+
+ for ($i = 0; $i < $cnt; ++ $i) {
+ // Credits for this to Monte Ohrt who made smarty's regex_replace modifier
+ if (($pos = strpos($search[$i], "\0")) !== false) {
+ $search[$i] = substr($search[$i], 0, $pos);
+ }
+
+ if (preg_match('#[a-z\s]+$#is', $search[$i], $m) && (strpos($m[0], 'e') !== false)) {
+ $search[$i] = substr($search[$i], 0, - strlen($m[0])) . str_replace(array(
+ 'e',
+ ' '
+ ),
+ '',
+ $m[0]);
+ }
+ }
+
+ return preg_replace($search, $replace, $value);
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginReplace.php b/core/l/Dwoo/Plugins/Functions/PluginReplace.php
new file mode 100644
index 0000000..b78138e
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginReplace.php
@@ -0,0 +1,52 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Replaces the search string by the replace string
+ *
+ * * value : the string to search into
+ * * search : the string to search for
+ * * replace : the string to use as a replacement
+ *
+ * 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.
+ */
+class PluginReplace extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ * @param string $search
+ * @param string $replace
+ * @param bool $case_sensitive
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $search, $replace, $case_sensitive = true)
+ {
+ if ($case_sensitive == 'false' || (bool)$case_sensitive === false) {
+ return 'str_ireplace(' . $search . ', ' . $replace . ', ' . $value . ')';
+ } else {
+ return 'str_replace(' . $search . ', ' . $replace . ', ' . $value . ')';
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginReturn.php b/core/l/Dwoo/Plugins/Functions/PluginReturn.php
new file mode 100644
index 0000000..5026f18
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginReturn.php
@@ -0,0 +1,54 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Inserts another template into the current one
+ *
+ * * file : the resource name of the template
+ * * cache_time : cache length in seconds
+ * * cache_id : cache identifier for the included template
+ * * compile_id : compilation identifier for the included template
+ * * data : data to feed into the included template, it can be any array and will default to $_root (the current data)
+ * * assign : if set, the output of the included template will be saved in this variable instead of being output
+ * * rest : any additional parameter/value provided will be added to the data array
+ *
+ * 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.
+ */
+class PluginReturn extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param array $rest
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, array $rest = array())
+ {
+ $out = array();
+ foreach ($rest as $var => $val) {
+ $out[] = '$this->setReturnValue(' . var_export($var, true) . ', ' . $val . ')';
+ }
+
+ return '(' . implode('.', $out) . ')';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginReverse.php b/core/l/Dwoo/Plugins/Functions/PluginReverse.php
new file mode 100644
index 0000000..fc15d69
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginReverse.php
@@ -0,0 +1,54 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Plugin;
+
+/**
+ * Reverses a string or an array
+ *
+ * * value : the string or array to reverse
+ * * preserve_keys : if value is an array and this is true, then the array keys are left intact
+ *
+ * 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.
+ */
+class PluginReverse extends Plugin
+{
+ /**
+ * @param string $value
+ * @param bool $preserve_keys
+ *
+ * @return array|string
+ */
+ public function process($value, $preserve_keys = false)
+ {
+ if (is_array($value)) {
+ return array_reverse($value, $preserve_keys);
+ } elseif (($charset = $this->core->getCharset()) === 'iso-8859-1') {
+ return strrev((string)$value);
+ }
+
+ $strlen = mb_strlen($value);
+ $out = '';
+ while ($strlen --) {
+ $out .= mb_substr($value, $strlen, 1, $charset);
+ }
+
+ return $out;
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginSafe.php b/core/l/Dwoo/Plugins/Functions/PluginSafe.php
new file mode 100644
index 0000000..bb4118e
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginSafe.php
@@ -0,0 +1,46 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Marks the variable as safe and removes the auto-escape function, only useful if you turned auto-escaping on
+ *
+ * * var : the variable to pass through untouched
+ *
+ * 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.
+ */
+class PluginSafe extends Plugin implements ICompilable
+{
+
+ /**
+ * @param Compiler $compiler
+ * @param mixed $var
+ *
+ * @return mixed
+ */
+ public static function compile(Compiler $compiler, $var)
+ {
+ return preg_replace('#\(is_string\(\$tmp=(.+)\) \? htmlspecialchars\(\$tmp, ENT_QUOTES, \$this->charset\) : \$tmp\)#',
+ '$1',
+ $var);
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginSpacify.php b/core/l/Dwoo/Plugins/Functions/PluginSpacify.php
new file mode 100644
index 0000000..68f069e
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginSpacify.php
@@ -0,0 +1,45 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Adds spaces (or the given character(s)) between every character of a string
+ *
+ * * value : the string to process
+ * * space_char : the character(s) to insert between each character
+ *
+ * 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.
+ */
+class PluginSpacify extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ * @param string $space_char
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $space_char = ' ')
+ {
+ return 'implode(' . $space_char . ', str_split(' . $value . ', 1))';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginStringFormat.php b/core/l/Dwoo/Plugins/Functions/PluginStringFormat.php
new file mode 100644
index 0000000..b1e2394
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginStringFormat.php
@@ -0,0 +1,45 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Formats a string using the sprintf function
+ *
+ * * value : the string to format
+ * * format : the format to use, see {@link http://php.net/sprintf} for details
+ *
+ * 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.
+ */
+class PluginStringFormat extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ * @param string $format
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $format)
+ {
+ return 'sprintf(' . $format . ',' . $value . ')';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginStripTags.php b/core/l/Dwoo/Plugins/Functions/PluginStripTags.php
new file mode 100644
index 0000000..37e7e89
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginStripTags.php
@@ -0,0 +1,55 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.3
+ * @date 2017-01-07
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Removes all html tags
+ *
+ * * value: the string to process
+ * * addspace: if true, a space is added in place of every removed tag
+ * * allowable_tags: specify tags which should not be stripped
+ *
+ * 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.
+ */
+class PluginStripTags extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ * @param bool $addspace
+ * @param null|string $allowable_tags
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $addspace = true, $allowable_tags = null)
+ {
+ if ($addspace === 'true') {
+ if ("null" == $allowable_tags) {
+ return "preg_replace('#<[^>]*>#', ' ', $value)";
+ }
+
+ return "preg_replace('#<\\s*\\/?(" . $allowable_tags . ")\\s*[^>]*?>#im', ' ', $value)";
+ }
+
+ return "strip_tags($value, $allowable_tags)";
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginTif.php b/core/l/Dwoo/Plugins/Functions/PluginTif.php
new file mode 100644
index 0000000..9a876e0
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginTif.php
@@ -0,0 +1,105 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.3.6
+ * @date 2017-03-21
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\Core;
+use Dwoo\Exception;
+use Dwoo\Compilation\Exception as CompilationException;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+use Dwoo\Plugins\Blocks\PluginIf;
+
+/**
+ * Ternary if operation.
+ * It evaluates the first argument and returns the second if it's true, or the third if it's false
+ *
+ * * rest : you can not use named parameters to call this, use it either with three arguments in the correct order
+ * (expression, true result, false result) or write it as in php (expression ? true result : false result)
+ *
+ * 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.
+ */
+class PluginTif extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param array $rest
+ * @param array $tokens
+ *
+ * @return mixed|string
+ * @throws CompilationException
+ */
+ public static function compile(Compiler $compiler, array $rest, array $tokens)
+ {
+ // load if plugin
+ if (!class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'PluginIf')) {
+ try {
+ $compiler->getCore()->getLoader()->loadPlugin('if');
+ }
+ catch (Exception $e) {
+ throw new CompilationException($compiler, 'Tif: the if plugin is required to use Tif');
+ }
+ }
+
+ if (count($rest) == 1) {
+ return $rest[0];
+ }
+
+ // fetch false result and remove the ":" if it was present
+ $falseResult = array_pop($rest);
+
+ if (trim(end($rest), '"\'') === ':') {
+ // remove the ':' if present
+ array_pop($rest);
+ } elseif (trim(end($rest), '"\'') === '?' || count($rest) === 1) {
+ if ($falseResult === '?' || $falseResult === ':') {
+ throw new CompilationException($compiler,
+ 'Tif: incomplete tif statement, value missing after ' . $falseResult);
+ }
+ // there was in fact no false result provided, so we move it to be the true result instead
+ $trueResult = $falseResult;
+ $falseResult = "''";
+ }
+
+ // fetch true result if needed
+ if (!isset($trueResult)) {
+ $trueResult = array_pop($rest);
+ // no true result provided so we use the expression arg
+ if ($trueResult === '?') {
+ $trueResult = true;
+ }
+ }
+
+ // remove the '?' if present
+ if (trim(end($rest), '"\'') === '?') {
+ array_pop($rest);
+ }
+
+ // check params were correctly provided
+ if (empty($rest) || $trueResult === null || $falseResult === null) {
+ throw new CompilationException($compiler,
+ 'Tif: you must provide three parameters serving as ? : ');
+ }
+
+ // parse condition
+ $condition = PluginIf::replaceKeywords($rest, $tokens, $compiler);
+
+ return '((' . implode(' ', $condition) . ') ? ' . ($trueResult === true ? implode(' ',
+ $condition) : $trueResult) . ' : ' . $falseResult . ')';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginTruncate.php b/core/l/Dwoo/Plugins/Functions/PluginTruncate.php
new file mode 100644
index 0000000..c998ea8
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginTruncate.php
@@ -0,0 +1,69 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Plugin;
+
+/**
+ * Truncates a string at the given length
+ *
+ * * value : text to truncate
+ * * length : the maximum length for the string
+ * * etc : the characters that are added to show that the string was cut off
+ * * break : if true, the string will be cut off at the exact length, instead of cutting at the nearest space
+ * * middle : if true, the string will contain the beginning and the end, and the extra characters will be removed
+ * from the middle
+ *
+ * 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.
+ */
+class PluginTruncate extends Plugin
+{
+ /**
+ * @param string $value
+ * @param int $length
+ * @param string $etc
+ * @param bool $break
+ * @param bool $middle
+ *
+ * @return mixed|string
+ */
+ public function process($value, $length = 80, $etc = '...', $break = false, $middle = false)
+ {
+ if ($length == 0) {
+ return '';
+ }
+
+ $value = (string)$value;
+ $etc = (string)$etc;
+ $length = (int)$length;
+
+ if (strlen($value) < $length) {
+ return $value;
+ }
+
+ $length = max($length - strlen($etc), 0);
+ if ($break === false && $middle === false) {
+ $value = preg_replace('#\s+(\S*)?$#', '', substr($value, 0, $length + 1));
+ }
+ if ($middle === false) {
+ return substr($value, 0, $length) . $etc;
+ }
+
+ return substr($value, 0, ceil($length / 2)) . $etc . substr($value, - floor($length / 2));
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginUpper.php b/core/l/Dwoo/Plugins/Functions/PluginUpper.php
new file mode 100644
index 0000000..c85f0b1
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginUpper.php
@@ -0,0 +1,43 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Makes a string uppercased
+ *
+ * * value : the text to uppercase
+ *
+ * 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.
+ */
+class PluginUpper extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value)
+ {
+ return 'mb_strtoupper((string) ' . $value . ', $this->charset)';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginWhitespace.php b/core/l/Dwoo/Plugins/Functions/PluginWhitespace.php
new file mode 100644
index 0000000..c30fd33
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginWhitespace.php
@@ -0,0 +1,53 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Block\Plugin;
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+
+/**
+ * Replaces all white-space characters with the given string
+ *
+ * * value : the text to process
+ * * with : the replacement string, note that any number of consecutive white-space characters will be replaced by a
+ * single replacement string
+ *
+ * Example :.
+ *
+ * {"a b c d
+ *
+ * e"|whitespace}
+ * results in : a b c d e
+ *
+ * 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.
+ */
+class PluginWhitespace extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ * @param string $with
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $with = ' ')
+ {
+ return "preg_replace('#\s+#'.(strcasecmp(\$this->charset, 'utf-8')===0?'u':''), $with, $value)";
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Functions/PluginWordwrap.php b/core/l/Dwoo/Plugins/Functions/PluginWordwrap.php
new file mode 100644
index 0000000..83d884d
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Functions/PluginWordwrap.php
@@ -0,0 +1,49 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Functions;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Wraps a text at the given line length
+ *
+ * * value : the text to wrap
+ * * length : maximum line length
+ * * break : the character(s) to use to break the line
+ * * cut : if true, the line is cut at the exact length instead of breaking at the nearest space
+ *
+ * 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.
+ */
+class PluginWordwrap extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param string $value
+ * @param int $length
+ * @param string $break
+ * @param bool $cut
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, $value, $length = 80, $break = "\n", $cut = false)
+ {
+ return 'wordwrap(' . $value . ',' . $length . ',' . $break . ',' . $cut . ')';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Helpers/PluginArray.php b/core/l/Dwoo/Plugins/Helpers/PluginArray.php
new file mode 100644
index 0000000..838e65f
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Helpers/PluginArray.php
@@ -0,0 +1,56 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.2
+ * @date 2017-01-06
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Helpers;
+
+use Dwoo\Compiler;
+use Dwoo\ICompilable;
+use Dwoo\Plugin;
+
+/**
+ * Builds an array with all the provided variables, use named parameters to make an associative array
+ *
+ * * rest : any number of variables, strings or anything that you want to store in the array
+ *
+ * Example :
+ *
+ * {array(a, b, c)} results in array(0=>'a', 1=>'b', 2=>'c')
+ * {array(a=foo, b=5, c=array(4,5))} results in array('a'=>'foo', 'b'=>5, 'c'=>array(0=>4, 1=>5))
+ *
+ * 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.
+ */
+class PluginArray extends Plugin implements ICompilable
+{
+ /**
+ * @param Compiler $compiler
+ * @param array $rest
+ *
+ * @return string
+ */
+ public static function compile(Compiler $compiler, array $rest = array())
+ {
+ $out = array();
+ foreach ($rest as $key => $value) {
+ if (!is_numeric($key) && !strstr($key, '$this->scope')) {
+ $key = "'" . $key . "'";
+ }
+ $out[] = $key . '=>' . $value;
+ }
+
+ return 'array(' . implode(', ', $out) . ')';
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Plugins/Processors/PluginSmartyCompatible.php b/core/l/Dwoo/Plugins/Processors/PluginSmartyCompatible.php
new file mode 100644
index 0000000..5b82219
--- /dev/null
+++ b/core/l/Dwoo/Plugins/Processors/PluginSmartyCompatible.php
@@ -0,0 +1,94 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-18
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Plugins\Processors;
+
+use Dwoo\Processor;
+
+/**
+ * Performs some template conversions to allow smarty templates to be used by
+ * the Dwoo compiler.
+ * 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.
+ */
+class PluginSmartyCompatible extends Processor
+{
+ /**
+ * @param string $input
+ *
+ * @return mixed
+ */
+ public function process($input)
+ {
+ list($l, $r) = $this->compiler->getDelimiters();
+
+ $rl = preg_quote($l, '/');
+ $rr = preg_quote($r, '/');
+ $sectionParam = '(?:(name|loop|start|step|max|show)\s*=\s*(\S+))?\s*';
+ $input = preg_replace_callback('/' . $rl . '\s*section ' . str_repeat($sectionParam, 6) . '\s*' . $rr . '(.+?)(?:' . $rl . '\s*sectionelse\s*' . $rr . '(.+?))?' . $rl . '\s*\/section\s*' . $rr . '/is', array(
+ $this,
+ 'convertSection'
+ ), $input);
+ $input = str_replace('$smarty.section.', '$smarty.for.', $input);
+
+ $smarty = array(
+ '/' . $rl . '\s*ldelim\s*' . $rr . '/',
+ '/' . $rl . '\s*rdelim\s*' . $rr . '/',
+ '/' . $rl . '\s*\$smarty\.ldelim\s*' . $rr . '/',
+ '/' . $rl . '\s*\$smarty\.rdelim\s*' . $rr . '/',
+ '/\$smarty\./',
+ '/' . $rl . '\s*php\s*' . $rr . '/',
+ '/' . $rl . '\s*\/php\s*' . $rr . '/',
+ '/\|(@?)strip(\||' . $rr . ')/',
+ '/' . $rl . '\s*sectionelse\s*' . $rr . '/',
+ );
+
+ $dwoo = array(
+ '\\' . $l,
+ $r,
+ '\\' . $l,
+ $r,
+ '$dwoo.',
+ '',
+ '|$1whitespace$2',
+ $l . 'else' . $r,
+ );
+
+ if (preg_match('{\|@([a-z][a-z0-9_]*)}i', $input, $matches)) {
+ trigger_error('The Smarty Compatibility Module has detected that you use |@' . $matches[1] . ' in your template, this might lead to problems as Dwoo interprets the @ operator differently than Smarty, see http://wiki.dwoo.org/index.php/Syntax#The_.40_Operator', E_USER_NOTICE);
+ }
+
+ return preg_replace($smarty, $dwoo, $input);
+ }
+
+ /**
+ * @param array $matches
+ *
+ * @return mixed
+ */
+ protected function convertSection(array $matches)
+ {
+ $params = array();
+ $index = 1;
+ while (!empty($matches[$index]) && $index < 13) {
+ $params[$matches[$index]] = $matches[$index + 1];
+ $index += 2;
+ }
+
+ return str_replace('[' . trim($params['name'], '"\'') . ']', '[$' . trim($params['name'], '"\'') . ']', $matches[0]);
+ }
+}
diff --git a/core/l/Dwoo/Processor.php b/core/l/Dwoo/Processor.php
new file mode 100644
index 0000000..f1941bf
--- /dev/null
+++ b/core/l/Dwoo/Processor.php
@@ -0,0 +1,52 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo;
+
+/**
+ * Base class for processors.
+ * 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.
+ */
+abstract class Processor
+{
+ /**
+ * The compiler instance that runs this processor.
+ *
+ * @var Core
+ */
+ protected $compiler;
+
+ /**
+ * Constructor, if you override it, call parent::__construct($compiler); or assign
+ * the dwoo instance yourself if you need it.
+ *
+ * @param Compiler $compiler the compiler class
+ */
+ public function __construct(Compiler $compiler)
+ {
+ $this->compiler = $compiler;
+ }
+
+ /**
+ * Processes the input and returns it filtered.
+ *
+ * @param string $input the template to process
+ *
+ * @return string
+ */
+ abstract public function process($input);
+}
diff --git a/core/l/Dwoo/Security/Exception.php b/core/l/Dwoo/Security/Exception.php
new file mode 100644
index 0000000..075f6a6
--- /dev/null
+++ b/core/l/Dwoo/Security/Exception.php
@@ -0,0 +1,28 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-19
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Security;
+
+use Dwoo\Exception as DwooException;
+
+/**
+ * Dwoo security exception class.
+ * 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.
+ */
+class Exception extends DwooException
+{
+}
diff --git a/core/l/Dwoo/Security/Policy.php b/core/l/Dwoo/Security/Policy.php
new file mode 100644
index 0000000..3752537
--- /dev/null
+++ b/core/l/Dwoo/Security/Policy.php
@@ -0,0 +1,322 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Security;
+
+use Dwoo\Core;
+
+/**
+ * Represents the security settings of a dwoo instance, it can be passed around to different dwoo instances.
+ * 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.
+ */
+class Policy
+{
+ /**
+ * Php handling constants, defaults to PHP_REMOVE
+ * PHP_ENCODE : run htmlentities over them
+ * PHP_REMOVE : remove all (+ short tags if your short tags option is on) from the input template
+ * PHP_ALLOW : leave them as they are
+ *
+ * @var int
+ */
+ const PHP_ENCODE = 1;
+ const PHP_REMOVE = 2;
+ const PHP_ALLOW = 3;
+
+ /**
+ * Constant handling constants, defaults to CONST_DISALLOW
+ * CONST_DISALLOW : throw an error if {$dwoo.const.*} is used in the template
+ * CONST_ALLOW : allow {$dwoo.const.*} calls
+ */
+ const CONST_DISALLOW = false;
+ const CONST_ALLOW = true;
+
+ /**
+ * Php functions that are allowed to be used within the template.
+ *
+ * @var array
+ */
+ protected $allowedPhpFunctions = array(
+ 'str_repeat' => true,
+ 'number_format' => true,
+ 'htmlentities' => true,
+ 'htmlspecialchars' => true,
+ 'long2ip' => true,
+ 'strlen' => true,
+ 'list' => true,
+ 'empty' => true,
+ 'count' => true,
+ 'sizeof' => true,
+ 'in_array' => true,
+ 'is_array' => true,
+ );
+
+ /**
+ * Methods that are allowed to be used within the template.
+ *
+ * @var array
+ */
+ protected $allowedMethods = array();
+
+ /**
+ * Paths that are safe to use with include or other file-access plugins.
+ *
+ * @var array
+ */
+ protected $allowedDirectories = array();
+
+ /**
+ * Stores the php handling level.
+ * defaults to self::PHP_REMOVE
+ *
+ * @var int
+ */
+ protected $phpHandling = self::PHP_REMOVE;
+
+ /**
+ * Stores the constant handling level.
+ * defaults to self::CONST_DISALLOW
+ *
+ * @var bool
+ */
+ protected $constHandling = self::CONST_DISALLOW;
+
+ /**
+ * Adds a php function to the allowed list.
+ *
+ * @param mixed $func function name or array of function names
+ */
+ public function allowPhpFunction($func)
+ {
+ if (is_array($func)) {
+ foreach ($func as $fname) {
+ $this->allowedPhpFunctions[strtolower($fname)] = true;
+ }
+ } else {
+ $this->allowedPhpFunctions[strtolower($func)] = true;
+ }
+ }
+
+ /**
+ * Removes a php function from the allowed list.
+ *
+ * @param mixed $func function name or array of function names
+ */
+ public function disallowPhpFunction($func)
+ {
+ if (is_array($func)) {
+ foreach ($func as $fname) {
+ unset($this->allowedPhpFunctions[strtolower($fname)]);
+ }
+ } else {
+ unset($this->allowedPhpFunctions[strtolower($func)]);
+ }
+ }
+
+ /**
+ * Returns the list of php functions allowed to run, note that the function names
+ * are stored in the array keys and not values.
+ *
+ * @return array
+ */
+ public function getAllowedPhpFunctions()
+ {
+ return $this->allowedPhpFunctions;
+ }
+
+ /**
+ * Adds a class method to the allowed list, this must be used for
+ * both static and non static method by providing the class name
+ * and method name to use.
+ *
+ * @param mixed $class class name or array of array('class', 'method') couples
+ * @param string $method method name
+ */
+ public function allowMethod($class, $method = null)
+ {
+ if (is_array($class)) {
+ foreach ($class as $elem) {
+ $this->allowedMethods[strtolower($elem[0])][strtolower($elem[1])] = true;
+ }
+ } else {
+ $this->allowedMethods[strtolower($class)][strtolower($method)] = true;
+ }
+ }
+
+ /**
+ * Removes a class method from the allowed list.
+ *
+ * @param mixed $class class name or array of array('class', 'method') couples
+ * @param string $method method name
+ */
+ public function disallowMethod($class, $method = null)
+ {
+ if (is_array($class)) {
+ foreach ($class as $elem) {
+ unset($this->allowedMethods[strtolower($elem[0])][strtolower($elem[1])]);
+ }
+ } else {
+ unset($this->allowedMethods[strtolower($class)][strtolower($method)]);
+ }
+ }
+
+ /**
+ * Returns the list of class methods allowed to run, note that the class names
+ * and method names are stored in the array keys and not values.
+ *
+ * @return array
+ */
+ public function getAllowedMethods()
+ {
+ return $this->allowedMethods;
+ }
+
+ /**
+ * Adds a directory to the safelist for includes and other file-access plugins.
+ * note that all the includePath directories you provide to the Dwoo_Template_File class
+ * are automatically marked as safe
+ *
+ * @param mixed $path a path name or an array of paths
+ */
+ public function allowDirectory($path)
+ {
+ if (is_array($path)) {
+ foreach ($path as $dir) {
+ $this->allowedDirectories[realpath($dir)] = true;
+ }
+ } else {
+ $this->allowedDirectories[realpath($path)] = true;
+ }
+ }
+
+ /**
+ * Removes a directory from the safe list.
+ *
+ * @param mixed $path a path name or an array of paths
+ */
+ public function disallowDirectory($path)
+ {
+ if (is_array($path)) {
+ foreach ($path as $dir) {
+ unset($this->allowedDirectories[realpath($dir)]);
+ }
+ } else {
+ unset($this->allowedDirectories[realpath($path)]);
+ }
+ }
+
+ /**
+ * Returns the list of safe paths, note that the paths are stored in the array
+ * keys and not values.
+ *
+ * @return array
+ */
+ public function getAllowedDirectories()
+ {
+ return $this->allowedDirectories;
+ }
+
+ /**
+ * Sets the php handling level, defaults to REMOVE.
+ *
+ * @param int $level one of the Dwoo_Security_Policy::PHP_* constants
+ */
+ public function setPhpHandling($level = self::PHP_REMOVE)
+ {
+ $this->phpHandling = $level;
+ }
+
+ /**
+ * Returns the php handling level.
+ *
+ * @return int the current level, one of the Dwoo_Security_Policy::PHP_* constants
+ */
+ public function getPhpHandling()
+ {
+ return $this->phpHandling;
+ }
+
+ /**
+ * Sets the constant handling level, defaults to CONST_DISALLOW.
+ *
+ * @param bool $level one of the Dwoo_Security_Policy::CONST_* constants
+ */
+ public function setConstantHandling($level = self::CONST_DISALLOW)
+ {
+ $this->constHandling = $level;
+ }
+
+ /**
+ * Returns the constant handling level.
+ *
+ * @return bool the current level, one of the Dwoo_Security_Policy::CONST_* constants
+ */
+ public function getConstantHandling()
+ {
+ return $this->constHandling;
+ }
+
+ /**
+ * This is used at run time to check whether method calls are allowed or not.
+ *
+ * @param Core $dwoo dwoo instance that calls this
+ * @param object $obj any object on which the method must be called
+ * @param string $method lowercased method name
+ * @param array $args arguments array
+ *
+ * @return mixed result of method call or unll + E_USER_NOTICE if not allowed
+ */
+ public function callMethod(Core $dwoo, $obj, $method, $args)
+ {
+ foreach ($this->allowedMethods as $class => $methods) {
+ if (!isset($methods[$method])) {
+ continue;
+ }
+ if ($obj instanceof $class) {
+ return call_user_func_array(array($obj, $method), $args);
+ }
+ }
+ $dwoo->triggerError('The current security policy prevents you from calling ' . get_class($obj) . '::' . $method . '()');
+
+ return null;
+ }
+
+ /**
+ * This is used at compile time to check whether static method calls are allowed or not.
+ *
+ * @param mixed $class lowercased class name or array('class', 'method') couple
+ * @param string $method lowercased method name
+ *
+ * @return bool
+ */
+ public function isMethodAllowed($class, $method = null)
+ {
+ if (is_array($class)) {
+ list($class, $method) = $class;
+ }
+ foreach ($this->allowedMethods as $allowedClass => $methods) {
+ if (!isset($methods[$method])) {
+ continue;
+ }
+ if ($class === $allowedClass || is_subclass_of($class, $allowedClass)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/core/l/Dwoo/Smarty/Adapter.php b/core/l/Dwoo/Smarty/Adapter.php
new file mode 100644
index 0000000..ff85160
--- /dev/null
+++ b/core/l/Dwoo/Smarty/Adapter.php
@@ -0,0 +1,698 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Smarty;
+
+use Dwoo\Core;
+use Dwoo\Compiler;
+use Dwoo\Data;
+use Dwoo\Security\Policy as SecurityPolicy;
+use Dwoo\Exception as Exception;
+use Dwoo\Template\File as TemplateFile;
+use Dwoo\Smarty\Filter\Adapter as FilterAdapter;
+use Dwoo\Smarty\Processor\Adapter as ProcessorAdapter;
+
+if (!defined('DIR_SEP')) {
+ define('DIR_SEP', DIRECTORY_SEPARATOR);
+}
+
+if (!defined('SMARTY_PHP_PASSTHRU')) {
+ define('SMARTY_PHP_PASSTHRU', 0);
+ define('SMARTY_PHP_QUOTE', 1);
+ define('SMARTY_PHP_REMOVE', 2);
+ define('SMARTY_PHP_ALLOW', 3);
+}
+
+/**
+ * A Smarty compatibility layer for Dwoo.
+ * 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.
+ */
+class Adapter extends Core
+{
+ /**
+ * Magic get/set/call functions that handle unsupported features
+ *
+ * @param string $p
+ * @param string $v
+ */
+ public function __set($p, $v)
+ {
+ if ($p === 'scope') {
+ $this->scope = $v;
+
+ return;
+ }
+ if ($p === 'data') {
+ $this->data = $v;
+
+ return;
+ }
+ if (array_key_exists($p, $this->compat['properties']) !== false) {
+ if ($this->show_compat_errors) {
+ $this->triggerError('Property ' . $p . ' is not available in the Dwoo\Smarty\Adapter, however it might be implemented in the future, check out http://wiki.dwoo.org/index.php/SmartySupport for more details.', E_USER_NOTICE);
+ }
+ $this->compat['properties'][$p] = $v;
+ } else {
+ if ($this->show_compat_errors) {
+ $this->triggerError('Property ' . $p . ' is not available in the Dwoo\Smarty\Adapter, but it is not listed as such, so you might want to tell me about it at j.boggiano@seld.be', E_USER_NOTICE);
+ }
+ }
+ }
+
+ /**
+ * @param $p
+ *
+ * @return mixed
+ */
+ public function __get($p)
+ {
+ if (array_key_exists($p, $this->compat['properties']) !== false) {
+ if ($this->show_compat_errors) {
+ $this->triggerError('Property ' . $p . ' is not available in the Dwoo\Smarty\Adapter, however it might be implemented in the future, check out http://wiki.dwoo.org/index.php/SmartySupport for more details.', E_USER_NOTICE);
+ }
+
+ return $this->compat['properties'][$p];
+ } else {
+ if ($this->show_compat_errors) {
+ $this->triggerError('Property ' . $p . ' is not available in the Dwoo\Smarty\Adapter, but it is not listed as such, so you might want to tell me about it at j.boggiano@seld.be', E_USER_NOTICE);
+ }
+ }
+ }
+
+ /**
+ * @param string $m
+ * @param array $a
+ *
+ * @return mixed|void
+ */
+ public function __call($m, $a)
+ {
+ if (method_exists($this->dataProvider, $m)) {
+ call_user_func_array(
+ array(
+ $this->dataProvider,
+ $m
+ ), $a
+ );
+ } elseif ($this->show_compat_errors) {
+ if (array_search($m, $this->compat['methods']) !== false) {
+ $this->triggerError('Method ' . $m . ' is not available in the Dwoo\Smarty\Adapter, however it might be implemented in the future, check out http://wiki.dwoo.org/index.php/SmartySupport for more details.', E_USER_NOTICE);
+ } else {
+ $this->triggerError('Method ' . $m . ' is not available in the Dwoo\Smarty\Adapter, but it is not listed as such, so you might want to tell me about it at j.boggiano@seld.be', E_USER_NOTICE);
+ }
+ }
+ }
+
+ /**
+ * List of unsupported properties and methods
+ */
+ protected $compat = array(
+ 'methods' => array(
+ 'register_resource',
+ 'unregister_resource',
+ 'load_filter',
+ 'clear_compiled_tpl',
+ 'clear_config',
+ 'get_config_vars',
+ 'config_load',
+ ),
+ 'properties' => array(
+ 'cache_handler_func' => null,
+ 'debugging' => false,
+ 'error_reporting' => null,
+ 'debugging_ctrl' => 'NONE',
+ 'request_vars_order' => 'EGPCS',
+ 'request_use_auto_globals' => true,
+ 'use_sub_dirs' => false,
+ 'autoload_filters' => array(),
+ 'default_template_handler_func' => '',
+ 'debug_tpl' => '',
+ 'cache_modified_check' => false,
+ 'default_modifiers' => array(),
+ 'default_resource_type' => 'file',
+ 'config_overwrite' => true,
+ 'config_booleanize' => true,
+ 'config_read_hidden' => false,
+ 'config_fix_newlines' => true,
+ 'config_class' => 'Config_File',
+ ),
+ );
+
+ /**
+ * Security vars
+ */
+ public $security = false;
+ public $trusted_dir = array();
+ public $secure_dir = array();
+ public $php_handling = SMARTY_PHP_PASSTHRU;
+ public $security_settings = array(
+ 'PHP_HANDLING' => false,
+ 'IF_FUNCS' => array(
+ 'list',
+ 'empty',
+ 'count',
+ 'sizeof',
+ 'in_array',
+ 'is_array',
+ ),
+ 'INCLUDE_ANY' => false,
+ 'PHP_TAGS' => false,
+ 'MODIFIER_FUNCS' => array(),
+ 'ALLOW_CONSTANTS' => false,
+ );
+
+ /**
+ * Paths
+ */
+ public $template_dir = 'templates';
+ public $compile_dir = 'templates_c';
+ public $config_dir = 'configs';
+ public $cache_dir = 'cache';
+ public $plugins_dir = array();
+
+ /**
+ * Misc options
+ */
+ public $left_delimiter = '{';
+ public $right_delimiter = '}';
+ public $compile_check = true;
+ public $force_compile = false;
+ public $caching = 0;
+ public $cache_lifetime = 3600;
+ public $compile_id = null;
+ public $compiler_file = null;
+ public $compiler_class = null;
+
+ /**
+ * Dwoo/Smarty compat layer
+ */
+ public $show_compat_errors = false;
+ protected $dataProvider;
+ protected $_filters = array(
+ 'pre' => array(),
+ 'post' => array(),
+ 'output' => array()
+ );
+ protected static $tplCache = array();
+ protected $compiler = null;
+
+ /**
+ * Adapter constructor.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ $this->charset = 'iso-8859-1';
+ $this->dataProvider = new Data();
+ $this->compiler = new Compiler();
+ }
+
+ /**
+ * @param $filename
+ * @param null $cacheId
+ * @param null $compileId
+ */
+ public function display($filename, $cacheId = null, $compileId = null)
+ {
+ $this->fetch($filename, $cacheId, $compileId, true);
+ }
+
+ /**
+ * @param $filename
+ * @param null $cacheId
+ * @param null $compileId
+ * @param bool $display
+ *
+ * @return string|void
+ */
+ public function fetch($filename, $cacheId = null, $compileId = null, $display = false)
+ {
+ $this->setCacheDir($this->cache_dir);
+ $this->setCompileDir($this->compile_dir);
+
+ if ($this->security) {
+ $policy = new SecurityPolicy();
+ $policy->addPhpFunction(array_merge($this->security_settings['IF_FUNCS'], $this->security_settings['MODIFIER_FUNCS']));
+
+ $phpTags = $this->security_settings['PHP_HANDLING'] ? SMARTY_PHP_ALLOW : $this->php_handling;
+ if ($this->security_settings['PHP_TAGS']) {
+ $phpTags = SMARTY_PHP_ALLOW;
+ }
+ switch ($phpTags) {
+ case SMARTY_PHP_ALLOW:
+ case SMARTY_PHP_PASSTHRU:
+ $phpTags = SecurityPolicy::PHP_ALLOW;
+ break;
+ case SMARTY_PHP_QUOTE:
+ $phpTags = SecurityPolicy::PHP_ENCODE;
+ break;
+ case SMARTY_PHP_REMOVE:
+ default:
+ $phpTags = SecurityPolicy::PHP_REMOVE;
+ break;
+ }
+ $policy->setPhpHandling($phpTags);
+
+ $policy->setConstantHandling($this->security_settings['ALLOW_CONSTANTS']);
+
+ if ($this->security_settings['INCLUDE_ANY']) {
+ $policy->allowDirectory(preg_replace('{^((?:[a-z]:)?[\\\\/]).*}i', '$1', __FILE__));
+ } else {
+ $policy->allowDirectory($this->secure_dir);
+ }
+
+ $this->setSecurityPolicy($policy);
+ }
+
+ if (!empty($this->plugins_dir)) {
+ foreach ($this->plugins_dir as $dir) {
+ $this->getLoader()->addDirectory(rtrim($dir, '\\/'));
+ }
+ }
+
+ $tpl = $this->makeTemplate($filename, $cacheId, $compileId);
+ if ($this->force_compile) {
+ $tpl->forceCompilation();
+ }
+
+ if ($this->caching > 0) {
+ $this->cacheTime = $this->cache_lifetime;
+ } else {
+ $this->cacheTime = 0;
+ }
+
+ if ($this->compiler_class !== null) {
+ if ($this->compiler_file !== null && !class_exists($this->compiler_class)) {
+ include $this->compiler_file;
+ }
+ $this->compiler = new $this->compiler_class();
+ } else {
+ $this->compiler->addPreProcessor('PluginSmartyCompatible', true);
+ $this->compiler->setLooseOpeningHandling(true);
+ }
+
+ $this->compiler->setDelimiters($this->left_delimiter, $this->right_delimiter);
+
+ return $this->get($tpl, $this->dataProvider, $this->compiler, $display === true);
+ }
+
+ /**
+ * @param mixed $_tpl
+ * @param array $data
+ * @param null $_compiler
+ * @param bool $_output
+ *
+ * @return string|void
+ */
+ public function get($_tpl, $data = array(), $_compiler = null, $_output = false)
+ {
+ if ($_compiler === null) {
+ $_compiler = $this->compiler;
+ }
+
+ return parent::get($_tpl, $data, $_compiler, $_output);
+ }
+
+ /**
+ * @param $name
+ * @param $callback
+ * @param bool $cacheable
+ * @param null $cache_attrs
+ *
+ * @throws Exception
+ */
+ public function register_function($name, $callback, $cacheable = true, $cache_attrs = null)
+ {
+ if (isset($this->plugins[$name]) && $this->plugins[$name][0] !== self::SMARTY_FUNCTION) {
+ throw new Exception('Multiple plugins of different types can not share the same name');
+ }
+ $this->plugins[$name] = array(
+ 'type' => self::SMARTY_FUNCTION,
+ 'callback' => $callback
+ );
+ }
+
+ /**
+ * @param $name
+ */
+ public function unregister_function($name)
+ {
+ unset($this->plugins[$name]);
+ }
+
+ /**
+ * @param $name
+ * @param $callback
+ * @param bool $cacheable
+ * @param null $cache_attrs
+ *
+ * @throws Exception
+ */
+ public function register_block($name, $callback, $cacheable = true, $cache_attrs = null)
+ {
+ if (isset($this->plugins[$name]) && $this->plugins[$name][0] !== self::SMARTY_BLOCK) {
+ throw new Exception('Multiple plugins of different types can not share the same name');
+ }
+ $this->plugins[$name] = array(
+ 'type' => self::SMARTY_BLOCK,
+ 'callback' => $callback
+ );
+ }
+
+ /**
+ * @param $name
+ */
+ public function unregister_block($name)
+ {
+ unset($this->plugins[$name]);
+ }
+
+ /**
+ * @param $name
+ * @param $callback
+ *
+ * @throws Exception
+ */
+ public function register_modifier($name, $callback)
+ {
+ if (isset($this->plugins[$name]) && $this->plugins[$name][0] !== self::SMARTY_MODIFIER) {
+ throw new Exception('Multiple plugins of different types can not share the same name');
+ }
+ $this->plugins[$name] = array(
+ 'type' => self::SMARTY_MODIFIER,
+ 'callback' => $callback
+ );
+ }
+
+ /**
+ * @param $name
+ */
+ public function unregister_modifier($name)
+ {
+ unset($this->plugins[$name]);
+ }
+
+ /**
+ * @param $callback
+ */
+ public function register_prefilter($callback)
+ {
+ $processor = new ProcessorAdapter($this->compiler);
+ $processor->registerCallback($callback);
+ $this->_filters['pre'][] = $processor;
+ $this->compiler->addPreProcessor($processor);
+ }
+
+ /**
+ * @param $callback
+ */
+ public function unregister_prefilter($callback)
+ {
+ foreach ($this->_filters['pre'] as $index => $processor) {
+ if ($processor->callback === $callback) {
+ $this->compiler->removePostProcessor($processor);
+ unset($this->_filters['pre'][$index]);
+ }
+ }
+ }
+
+ /**
+ * @param $callback
+ */
+ public function register_postfilter($callback)
+ {
+ $processor = new ProcessorAdapter($this->compiler);
+ $processor->registerCallback($callback);
+ $this->_filters['post'][] = $processor;
+ $this->compiler->addPostProcessor($processor);
+ }
+
+ /**
+ * @param $callback
+ */
+ public function unregister_postfilter($callback)
+ {
+ foreach ($this->_filters['post'] as $index => $processor) {
+ if ($processor->callback === $callback) {
+ $this->compiler->removePostProcessor($processor);
+ unset($this->_filters['post'][$index]);
+ }
+ }
+ }
+
+ /**
+ * @param $callback
+ */
+ public function register_outputfilter($callback)
+ {
+ $filter = new FilterAdapter($this);
+ $filter->registerCallback($callback);
+ $this->_filters['output'][] = $filter;
+ $this->addFilter($filter);
+ }
+
+ /**
+ * @param $callback
+ */
+ public function unregister_outputfilter($callback)
+ {
+ foreach ($this->_filters['output'] as $index => $filter) {
+ if ($filter->callback === $callback) {
+ $this->removeOutputFilter($filter);
+ unset($this->_filters['output'][$index]);
+ }
+ }
+ }
+
+ /**
+ * @param $object
+ * @param $object_impl
+ * @param array $allowed
+ * @param bool $smarty_args
+ * @param array $block_methods
+ */
+ public function register_object($object, $object_impl, $allowed = array(), $smarty_args = false, $block_methods = array())
+ {
+ settype($allowed, 'array');
+ settype($block_methods, 'array');
+ settype($smarty_args, 'boolean');
+
+ if (!empty($allowed) && $this->show_compat_errors) {
+ $this->triggerError('You can register objects but can not restrict the method/properties used, this is PHP5, you have proper OOP access restrictions so use them.', E_USER_NOTICE);
+ }
+
+ if ($smarty_args) {
+ $this->triggerError('You can register objects but methods will be called using method($arg1, $arg2, $arg3), not as an argument array like smarty did.', E_USER_NOTICE);
+ }
+
+ if (!empty($block_methods)) {
+ $this->triggerError('You can register objects but can not use methods as being block methods, you have to build a plugin for that.', E_USER_NOTICE);
+ }
+
+ $this->dataProvider->assign($object, $object_impl);
+ }
+
+ /**
+ * @param $object
+ */
+ public function unregister_object($object)
+ {
+ $this->dataProvider->clear($object);
+ }
+
+ /**
+ * @param $name
+ *
+ * @return mixed
+ */
+ public function get_registered_object($name)
+ {
+ $data = $this->dataProvider->getData();
+ if (isset($data[$name]) && is_object($data[$name])) {
+ return $data[$name];
+ } else {
+ trigger_error('Dwoo_Compiler: object "' . $name . '" was not registered or is not an object', E_USER_ERROR);
+ }
+ }
+
+ /**
+ * @param $filename
+ *
+ * @return bool
+ */
+ public function template_exists($filename)
+ {
+ if (!is_array($this->template_dir)) {
+ return file_exists($this->template_dir . DIRECTORY_SEPARATOR . $filename);
+ } else {
+ foreach ($this->template_dir as $tpl_dir) {
+ if (file_exists($tpl_dir . DIRECTORY_SEPARATOR . $filename)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * @param $tpl
+ * @param null $cacheId
+ * @param null $compileId
+ *
+ * @return bool
+ */
+ public function is_cached($tpl, $cacheId = null, $compileId = null)
+ {
+ return $this->isCached($this->makeTemplate($tpl, $cacheId, $compileId));
+ }
+
+ /**
+ * @param $var
+ * @param $value
+ * @param bool $merge
+ */
+ public function append_by_ref($var, &$value, $merge = false)
+ {
+ $this->dataProvider->appendByRef($var, $value, $merge);
+ }
+
+ /**
+ * @param $name
+ * @param $val
+ */
+ public function assign_by_ref($name, &$val)
+ {
+ $this->dataProvider->assignByRef($name, $val);
+ }
+
+ /**
+ * @param $var
+ */
+ public function clear_assign($var)
+ {
+ $this->dataProvider->clear($var);
+ }
+
+ /**
+ *
+ */
+ public function clear_all_assign()
+ {
+ $this->dataProvider->clear();
+ }
+
+ /**
+ * @param null $name
+ *
+ * @return array|null
+ */
+ public function get_template_vars($name = null)
+ {
+ if ($this->show_compat_errors) {
+ trigger_error('get_template_vars does not return values by reference, if you try to modify the data that way you should modify your code.', E_USER_NOTICE);
+ }
+
+ $data = $this->dataProvider->getData();
+ if ($name === null) {
+ return $data;
+ } elseif (isset($data[$name])) {
+ return $data[$name];
+ }
+
+ return null;
+ }
+
+ /**
+ * @param int $olderThan
+ */
+ public function clear_all_cache($olderThan = 0)
+ {
+ $this->clearCache($olderThan);
+ }
+
+ /**
+ * @param $template
+ * @param null $cacheId
+ * @param null $compileId
+ * @param int $olderThan
+ */
+ public function clear_cache($template, $cacheId = null, $compileId = null, $olderThan = 0)
+ {
+ $this->makeTemplate($template, $cacheId, $compileId)->clearCache($olderThan);
+ }
+
+ /**
+ * @param $error_msg
+ * @param int $error_type
+ */
+ public function trigger_error($error_msg, $error_type = E_USER_WARNING)
+ {
+ $this->triggerError($error_msg, $error_type);
+ }
+
+ /**
+ *
+ */
+ protected function initGlobals()
+ {
+ parent::initGlobals();
+ $this->globals['ldelim'] = '{';
+ $this->globals['rdelim'] = '}';
+ }
+
+ /**
+ * @param $file
+ * @param $cacheId
+ * @param $compileId
+ *
+ * @return mixed
+ * @throws Exception
+ */
+ protected function makeTemplate($file, $cacheId, $compileId)
+ {
+ if ($compileId === null) {
+ $compileId = $this->compile_id;
+ }
+
+ $hash = bin2hex(md5($file . $cacheId . $compileId, true));
+ if (!isset(self::$tplCache[$hash])) {
+ // abs path
+ if (substr($file, 0, 1) === '/' || substr($file, 1, 1) === ':') {
+ self::$tplCache[$hash] = new TemplateFile($file, null, $cacheId, $compileId);
+ } elseif (is_string($this->template_dir) || is_array($this->template_dir)) {
+ self::$tplCache[$hash] = new TemplateFile($file, null, $cacheId, $compileId, $this->template_dir);
+ } else {
+ throw new Exception('Unable to load "' . $file . '", check the template_dir');
+ }
+ }
+
+ return self::$tplCache[$hash];
+ }
+
+ /**
+ * @param string $message
+ * @param int $level
+ */
+ public function triggerError($message, $level = E_USER_NOTICE)
+ {
+ if (is_object($this->template)) {
+ parent::triggerError($message, $level);
+ }
+ trigger_error('Dwoo error : ' . $message, $level);
+ }
+}
diff --git a/core/l/Dwoo/Smarty/Filter/Adapter.php b/core/l/Dwoo/Smarty/Filter/Adapter.php
new file mode 100644
index 0000000..9fa52fd
--- /dev/null
+++ b/core/l/Dwoo/Smarty/Filter/Adapter.php
@@ -0,0 +1,45 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Smarty\Filter;
+
+use Dwoo\Filter;
+
+/**
+ * Class Adapter
+ */
+class Adapter extends Filter
+{
+ public $callback;
+
+ /**
+ * @param string $input
+ *
+ * @return mixed
+ */
+ public function process($input)
+ {
+ return call_user_func($this->callback, $input);
+ }
+
+ /**
+ * @param $callback
+ */
+ public function registerCallback($callback)
+ {
+ $this->callback = $callback;
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Smarty/Processor/Adapter.php b/core/l/Dwoo/Smarty/Processor/Adapter.php
new file mode 100644
index 0000000..2510091
--- /dev/null
+++ b/core/l/Dwoo/Smarty/Processor/Adapter.php
@@ -0,0 +1,45 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2016 David Sanchez
+ * @license http://dwoo.org/LICENSE Modified BSD License
+ * @version 1.3.0
+ * @date 2016-09-23
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Smarty\Processor;
+
+use Dwoo\Processor;
+
+/**
+ * Class Adapter
+ */
+class Adapter extends Processor
+{
+ public $callback;
+
+ /**
+ * @param string $input
+ *
+ * @return mixed
+ */
+ public function process($input)
+ {
+ return call_user_func($this->callback, $input);
+ }
+
+ /**
+ * @param $callback
+ */
+ public function registerCallback($callback)
+ {
+ $this->callback = $callback;
+ }
+}
\ No newline at end of file
diff --git a/core/l/Dwoo/Template/File.php b/core/l/Dwoo/Template/File.php
new file mode 100644
index 0000000..186466d
--- /dev/null
+++ b/core/l/Dwoo/Template/File.php
@@ -0,0 +1,276 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.4.0
+ * @date 2017-03-16
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Template;
+
+use Dwoo\Exception as DwooException;
+use Dwoo\Core as Core;
+use Dwoo\ICompiler;
+use Dwoo\ITemplate as ITemplate;
+use Dwoo\Security\Exception as SecurityException;
+use Dwoo\Template\File as TemplateFile;
+
+/**
+ * Represents a Dwoo template contained in a file.
+ * 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.
+ */
+class File extends Str
+{
+ /**
+ * Template filename.
+ *
+ * @var string
+ */
+ protected $file;
+
+ /**
+ * Include path(s) to look into to find this template.
+ *
+ * @var array
+ */
+ protected $includePath = array();
+
+ /**
+ * Resolved path cache when looking for a file in multiple include paths.
+ * this is reset when the include path is changed
+ *
+ * @var string
+ */
+ protected $resolvedPath = null;
+
+ /**
+ * Creates a template from a file.
+ *
+ * @param string $file the path to the template file, make sure it exists
+ * @param int $cacheTime duration of the cache validity for this template,
+ * if null it defaults to the Dwoo instance that will
+ * render this template
+ * @param string $cacheId the unique cache identifier of this page or anything else that
+ * makes this template's content unique, if null it defaults
+ * to the current url
+ * @param string $compileId the unique compiled identifier, which is used to distinguish this
+ * template from others, if null it defaults to the filename+bits of the path
+ * @param mixed $includePath a string for a single path to look into for the given file, or an array of paths
+ */
+ public function __construct($file, $cacheTime = null, $cacheId = null, $compileId = null, $includePath = array())
+ {
+ parent::__construct($file, $cacheTime, $cacheId, $compileId);
+ $this->template = null;
+ $this->file = $file;
+ $this->name = basename($file);
+ $this->setIncludePath($includePath);
+ $this->compileId = $this->getResourceIdentifier();
+ }
+
+ /**
+ * Sets the include path(s) to where the given template filename must be looked up.
+ *
+ * @param mixed $paths the path to look into, can be string for a single path or an array of paths
+ */
+ public function setIncludePath($paths)
+ {
+ if (is_array($paths) === false) {
+ $paths = array($paths);
+ }
+
+ $this->includePath = $paths;
+ $this->resolvedPath = null;
+ }
+
+ /**
+ * Return the current include path(s).
+ *
+ * @return array
+ */
+ public function getIncludePath()
+ {
+ return $this->includePath;
+ }
+
+ /**
+ * Checks if compiled file is valid (exists and it's the modification is greater or
+ * equal to the modification time of the template file).
+ *
+ * @param string file
+ *
+ * @return bool True cache file existance and it's modification time
+ */
+ protected function isValidCompiledFile($file)
+ {
+ return parent::isValidCompiledFile($file) && (int)$this->getUid() <= filemtime($file);
+ }
+
+ /**
+ * Returns the template source of this template.
+ *
+ * @return string
+ */
+ public function getSource()
+ {
+ return file_get_contents($this->getResourceIdentifier());
+ }
+
+ /**
+ * Returns the resource name for this template class.
+ *
+ * @return string
+ */
+ public function getResourceName()
+ {
+ return 'file';
+ }
+
+ /**
+ * Returns this template's source filename.
+ *
+ * @return string
+ * @throws DwooException
+ */
+ public function getResourceIdentifier()
+ {
+ if ($this->resolvedPath !== null) {
+ return $this->resolvedPath;
+ } elseif (array_filter($this->getIncludePath()) == array()) {
+ return $this->file;
+ } else {
+ foreach ($this->getIncludePath() as $path) {
+ $path = rtrim($path, DIRECTORY_SEPARATOR);
+ if (file_exists($path . DIRECTORY_SEPARATOR . $this->file) === true) {
+ return $this->resolvedPath = $path . DIRECTORY_SEPARATOR . $this->file;
+ }
+ }
+
+ throw new DwooException('Template "' . $this->file . '" could not be found in any of your include path(s)');
+ }
+ }
+
+ /**
+ * Returns an unique value identifying the current version of this template,
+ * in this case it's the unix timestamp of the last modification.
+ *
+ * @return string
+ */
+ public function getUid()
+ {
+ return (string)filemtime($this->getResourceIdentifier());
+ }
+
+ /**
+ * Returns a new template object from the given include name, null if no include is
+ * possible (resource not found), or false if include is not permitted by this resource type.
+ *
+ * @param Core $core the dwoo instance requiring it
+ * @param mixed $resourceId the filename (relative to this template's dir) of the template to
+ * include
+ * @param int $cacheTime duration of the cache validity for this template, if null it defaults
+ * to the Dwoo instance that will render this template if null it
+ * defaults to the Dwoo instance that will render this template if null
+ * it defaults to the Dwoo instance that will render this template
+ * @param string $cacheId the unique cache identifier of this page or anything else that makes
+ * this template's content unique, if null it defaults to the current
+ * url makes this template's content unique, if null it defaults to the
+ * current url makes this template's content unique, if null it defaults
+ * to the current url
+ * @param string $compileId the unique compiled identifier, which is used to distinguish this
+ * template from others, if null it defaults to the filename+bits of the
+ * path template from others, if null it defaults to the filename+bits
+ * of the path template from others, if null it defaults to the
+ * filename+bits of the path
+ * @param ITemplate $parentTemplate the template that is requesting a new template object (through an
+ * include, extends or any other plugin) an include, extends or any
+ * other plugin) an include, extends or any other plugin)
+ *
+ * @return TemplateFile|null
+ * @throws DwooException
+ * @throws SecurityException
+ */
+ public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null,
+ $compileId = null, ITemplate $parentTemplate = null)
+ {
+ if (DIRECTORY_SEPARATOR === '\\') {
+ $resourceId = str_replace(array("\t", "\n", "\r", "\f", "\v"), array(
+ '\\t',
+ '\\n',
+ '\\r',
+ '\\f',
+ '\\v'
+ ), $resourceId);
+ }
+ $resourceId = strtr($resourceId, '\\', '/');
+
+ $includePath = null;
+
+ if (file_exists($resourceId) === false) {
+ if ($parentTemplate === null) {
+ $parentTemplate = $core->getTemplate();
+ }
+ if ($parentTemplate instanceof self) {
+ if ($includePath = $parentTemplate->getIncludePath()) {
+ if (strstr($resourceId, '../')) {
+ throw new DwooException('When using an include path you can not reference a template into a parent directory (using ../)');
+ }
+ } else {
+ $resourceId = dirname($parentTemplate->getResourceIdentifier()) . DIRECTORY_SEPARATOR . $resourceId;
+ if (file_exists($resourceId) === false) {
+ return null;
+ }
+ }
+ } else {
+ return null;
+ }
+ }
+
+ if ($policy = $core->getSecurityPolicy()) {
+ while (true) {
+ if (preg_match('{^([a-z]+?)://}i', $resourceId)) {
+ throw new SecurityException('The security policy prevents you to read files from external sources : ' . $resourceId . '.');
+ }
+
+ if ($includePath) {
+ break;
+ }
+
+ $resourceId = realpath($resourceId);
+ $dirs = $policy->getAllowedDirectories();
+ foreach ($dirs as $dir => $dummy) {
+ if (strpos($resourceId, $dir) === 0) {
+ break 2;
+ }
+ }
+ throw new SecurityException('The security policy prevents you to read ' . $resourceId . '');
+ }
+ }
+
+ $class = 'Dwoo\Template\File';
+ if ($parentTemplate) {
+ $class = get_class($parentTemplate);
+ }
+
+ return new $class($resourceId, $cacheTime, $cacheId, $compileId, $includePath);
+ }
+
+ /**
+ * Returns some php code that will check if this template has been modified or not.
+ * if the function returns null, the template will be instanciated and then the Uid checked
+ *
+ * @return string
+ */
+ public function getIsModifiedCode()
+ {
+ return '"' . $this->getUid() . '" == filemtime(' . var_export($this->getResourceIdentifier(), true) . ')';
+ }
+}
diff --git a/core/l/Dwoo/Template/Str.php b/core/l/Dwoo/Template/Str.php
new file mode 100644
index 0000000..ab81e31
--- /dev/null
+++ b/core/l/Dwoo/Template/Str.php
@@ -0,0 +1,535 @@
+
+ * @author David Sanchez
+ * @copyright 2008-2013 Jordi Boggiano
+ * @copyright 2013-2017 David Sanchez
+ * @license http://dwoo.org/LICENSE LGPLv3
+ * @version 1.4.0
+ * @date 2017-03-16
+ * @link http://dwoo.org/
+ */
+
+namespace Dwoo\Template;
+
+use Dwoo\Core;
+use Dwoo\Compiler;
+use Dwoo\ITemplate;
+use Dwoo\ICompiler;
+use Dwoo\Exception;
+
+/**
+ * Represents a Dwoo template contained in a string.
+ * 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.
+ */
+class Str implements ITemplate
+{
+ /**
+ * Template name.
+ *
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * Template compilation id.
+ *
+ * @var string
+ */
+ protected $compileId;
+
+ /**
+ * Template cache id, if not provided in the constructor, it is set to
+ * the md4 hash of the request_uri. it is however highly recommended to
+ * provide one that will fit your needs.
+ * in all cases, the compilation id is prepended to the cache id to separate
+ * templates with similar cache ids from one another
+ *
+ * @var string
+ */
+ protected $cacheId;
+
+ /**
+ * Validity duration of the generated cache file (in seconds).
+ * set to -1 for infinite cache, 0 to disable and null to inherit the Dwoo instance's cache time
+ *
+ * @var int
+ */
+ protected $cacheTime;
+
+ /**
+ * Boolean flag that defines whether the compilation should be enforced (once) or
+ * not use this if you have issues with the compiled templates not being updated
+ * but if you do need this it's most likely that you should file a bug report.
+ *
+ * @var bool
+ */
+ protected $compilationEnforced;
+
+ /**
+ * Caches the results of the file checks to save some time when the same
+ * templates is rendered several times.
+ *
+ * @var array
+ */
+ protected static $cache = array(
+ 'cached' => array(),
+ 'compiled' => array()
+ );
+
+ /**
+ * Holds the compiler that built this template.
+ *
+ * @var ICompiler
+ */
+ protected $compiler;
+
+ /**
+ * Chmod value for all files written (cached or compiled ones).
+ * set to null if you don't want any chmod operation to happen
+ *
+ * @var int
+ */
+ protected $chmod = 0777;
+
+ /**
+ * Containing template string.
+ *
+ * @var string
+ */
+ protected $template;
+
+ /**
+ * Creates a template from a string.
+ *
+ * @param string $templateString the template to use
+ * @param int $cacheTime duration of the cache validity for this template,
+ * if null it defaults to the Dwoo instance that will
+ * render this template, set to -1 for infinite cache or 0 to disable
+ * @param string $cacheId the unique cache identifier of this page or anything else that
+ * makes this template's content unique, if null it defaults
+ * to the current url
+ * @param string $compileId the unique compiled identifier, which is used to distinguish this
+ * template from others, if null it defaults to the md4 hash of the template
+ */
+ public function __construct($templateString, $cacheTime = null, $cacheId = null, $compileId = null)
+ {
+ $this->template = $templateString;
+ $this->name = hash('md4', $templateString);
+ $this->cacheTime = $cacheTime;
+
+ if ($compileId !== null) {
+ $this->compileId = str_replace('../', '__', strtr($compileId, '\\%?=!:;' . PATH_SEPARATOR, '/-------'));
+ } else {
+ $this->compileId = $templateString;
+ }
+
+ if ($cacheId !== null) {
+ $this->cacheId = str_replace('../', '__', strtr($cacheId, '\\%?=!:;' . PATH_SEPARATOR, '/-------'));
+ }
+ }
+
+ /**
+ * Returns the cache duration for this template.
+ * defaults to null if it was not provided
+ *
+ * @return int|null
+ */
+ public function getCacheTime()
+ {
+ return $this->cacheTime;
+ }
+
+ /**
+ * Sets the cache duration for this template.
+ * can be used to set it after the object is created if you did not provide
+ * it in the constructor
+ *
+ * @param int $seconds duration of the cache validity for this template, if
+ * null it defaults to the Dwoo instance's cache time. 0 = disable and
+ * -1 = infinite cache
+ */
+ public function setCacheTime($seconds = null)
+ {
+ $this->cacheTime = $seconds;
+ }
+
+ /**
+ * Returns the chmod value for all files written (cached or compiled ones).
+ * defaults to 0777
+ *
+ * @return int|null
+ */
+ public function getChmod()
+ {
+ return $this->chmod;
+ }
+
+ /**
+ * Set the chmod value for all files written (cached or compiled ones).
+ * set to null if you don't want to do any chmod() operation
+ *
+ * @param int $mask new bitmask to use for all files
+ */
+ public function setChmod($mask = null)
+ {
+ $this->chmod = $mask;
+ }
+
+ /**
+ * Returns the template name.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Returns the resource name for this template class.
+ *
+ * @return string
+ */
+ public function getResourceName()
+ {
+ return 'string';
+ }
+
+ /**
+ * Returns the resource identifier for this template, false here as strings don't have identifiers.
+ *
+ * @return false
+ */
+ public function getResourceIdentifier()
+ {
+ return false;
+ }
+
+ /**
+ * Returns the template source of this template.
+ *
+ * @return string
+ */
+ public function getSource()
+ {
+ return $this->template;
+ }
+
+ /**
+ * Returns an unique value identifying the current version of this template,
+ * in this case it's the md4 hash of the content.
+ *
+ * @return string
+ */
+ public function getUid()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Returns the compiler used by this template, if it was just compiled, or null.
+ *
+ * @return ICompiler
+ */
+ public function getCompiler()
+ {
+ return $this->compiler;
+ }
+
+ /**
+ * Marks this template as compile-forced, which means it will be recompiled even if it
+ * was already saved and wasn't modified since the last compilation. do not use this in production,
+ * it's only meant to be used in development (and the development of dwoo particularly).
+ */
+ public function forceCompilation()
+ {
+ $this->compilationEnforced = true;
+ }
+
+ /**
+ * Returns the cached template output file name, true if it's cache-able but not cached
+ * or false if it's not cached.
+ *
+ * @param Core $core the dwoo instance that requests it
+ *
+ * @return string|bool
+ */
+ public function getCachedTemplate(Core $core)
+ {
+ $cacheLength = $core->getCacheTime();
+ if ($this->cacheTime !== null) {
+ $cacheLength = $this->cacheTime;
+ }
+
+ // file is not cacheable
+ if ($cacheLength == 0) {
+ return false;
+ }
+
+ $cachedFile = $this->getCacheFilename($core);
+
+ if (isset(self::$cache['cached'][$this->cacheId]) === true && file_exists($cachedFile)) {
+ // already checked, return cache file
+ return $cachedFile;
+ } elseif ($this->compilationEnforced !== true && file_exists($cachedFile) && ($cacheLength === - 1 || filemtime($cachedFile) > ($_SERVER['REQUEST_TIME'] - $cacheLength)) && $this->isValidCompiledFile($this->getCompiledFilename($core))) {
+ // cache is still valid and can be loaded
+ self::$cache['cached'][$this->cacheId] = true;
+
+ return $cachedFile;
+ }
+
+ // file is cacheable
+ return true;
+ }
+
+ /**
+ * Caches the provided output into the cache file.
+ *
+ * @param Core $core the dwoo instance that requests it
+ * @param string $output the template output
+ *
+ * @return mixed full path of the cached file or false upon failure
+ */
+ public function cache(Core $core, $output)
+ {
+ $cacheDir = $core->getCacheDir();
+ $cachedFile = $this->getCacheFilename($core);
+
+ // the code below is courtesy of Rasmus Schultz,
+ // thanks for his help on avoiding concurency issues
+ $temp = tempnam($cacheDir, 'temp');
+ if (!($file = @fopen($temp, 'wb'))) {
+ $temp = $cacheDir . uniqid('temp');
+ if (!($file = @fopen($temp, 'wb'))) {
+ trigger_error('Error writing temporary file \'' . $temp . '\'', E_USER_WARNING);
+
+ return false;
+ }
+ }
+
+ fwrite($file, $output);
+ fclose($file);
+
+ $this->makeDirectory(dirname($cachedFile), $cacheDir);
+ if (!@rename($temp, $cachedFile)) {
+ @unlink($cachedFile);
+ @rename($temp, $cachedFile);
+ }
+
+ if ($this->chmod !== null) {
+ chmod($cachedFile, $this->chmod);
+ }
+
+ self::$cache['cached'][$this->cacheId] = true;
+
+ return $cachedFile;
+ }
+
+ /**
+ * Clears the cached template if it's older than the given time.
+ *
+ * @param Core $core the dwoo instance that was used to cache that template
+ * @param int $olderThan minimum time (in seconds) required for the cache to be cleared
+ *
+ * @return bool true if the cache was not present or if it was deleted, false if it remains there
+ */
+ public function clearCache(Core $core, $olderThan = - 1)
+ {
+ $cachedFile = $this->getCacheFilename($core);
+
+ return !file_exists($cachedFile) || (filectime($cachedFile) < (time() - $olderThan) && unlink($cachedFile));
+ }
+
+ /**
+ * Returns the compiled template file name.
+ *
+ * @param Core $core the dwoo instance that requests it
+ * @param ICompiler $compiler the compiler that must be used
+ *
+ * @return string
+ */
+ public function getCompiledTemplate(Core $core, ICompiler $compiler = null)
+ {
+ $compiledFile = $this->getCompiledFilename($core);
+
+ if ($this->compilationEnforced !== true && isset(self::$cache['compiled'][$this->compileId]) === true) {
+ // already checked, return compiled file
+ } elseif ($this->compilationEnforced !== true && $this->isValidCompiledFile($compiledFile)) {
+ // template is compiled
+ self::$cache['compiled'][$this->compileId] = true;
+ } else {
+ // compiles the template
+ $this->compilationEnforced = false;
+
+ if ($compiler === null) {
+ $compiler = $core->getDefaultCompilerFactory($this->getResourceName());
+
+ if ($compiler === null || $compiler === array('Dwoo\Compiler', 'compilerFactory')) {
+ $compiler = Compiler::compilerFactory();
+ } else {
+ $compiler = call_user_func($compiler);
+ }
+ }
+
+ $this->compiler = $compiler;
+
+ $compiler->setCustomPlugins($core->getCustomPlugins());
+ $compiler->setSecurityPolicy($core->getSecurityPolicy());
+ $this->makeDirectory(dirname($compiledFile), $core->getCompileDir());
+ file_put_contents($compiledFile, $compiler->compile($core, $this));
+ if ($this->chmod !== null) {
+ chmod($compiledFile, $this->chmod);
+ }
+
+ if (extension_loaded('Zend OPcache')) {
+ opcache_invalidate($compiledFile);
+ } elseif (extension_loaded('apc') && ini_get('apc.enabled')) {
+ apc_delete_file($compiledFile);
+ }
+
+ self::$cache['compiled'][$this->compileId] = true;
+ }
+
+ return $compiledFile;
+ }
+
+ /**
+ * Checks if compiled file is valid (it exists).
+ *
+ * @param string $file
+ *
+ * @return bool True cache file existence
+ */
+ protected function isValidCompiledFile($file)
+ {
+ return file_exists($file);
+ }
+
+ /**
+ * Returns a new template string object with the resource id being the template source code.
+ *
+ * @param Core $core the dwoo instance requiring it
+ * @param mixed $resourceId the filename (relative to this template's dir) of the template to include
+ * @param int $cacheTime duration of the cache validity for this template, if null it defaults to the
+ * Dwoo instance that will render this template if null it defaults to the Dwoo
+ * instance that will render this template
+ * @param string $cacheId the unique cache identifier of this page or anything else that makes this
+ * template's content unique, if null it defaults to the current url makes this
+ * template's content unique, if null it defaults to the current url
+ * @param string $compileId the unique compiled identifier, which is used to distinguish this template from
+ * others, if null it defaults to the filename+bits of the path template from
+ * others, if null it defaults to the filename+bits of the path
+ * @param ITemplate $parentTemplate the template that is requesting a new template object (through an include,
+ * extends or any other plugin) an include, extends or any other plugin)
+ *
+ * @return $this
+ */
+ public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null,
+ $compileId = null, ITemplate $parentTemplate = null)
+ {
+ return new self($resourceId, $cacheTime, $cacheId, $compileId);
+ }
+
+ /**
+ * Returns the full compiled file name and assigns a default value to it if
+ * required.
+ *
+ * @param Core $core the Core instance that requests the file name
+ *
+ * @return string the full path to the compiled file
+ */
+ protected function getCompiledFilename(Core $core)
+ {
+ return $core->getCompileDir() . hash('md4', $this->compileId) . '.d' . Core::RELEASE_TAG . '.php';
+ }
+
+ /**
+ * Returns the full cached file name and assigns a default value to it if
+ * required.
+ *
+ * @param Core $core the dwoo instance that requests the file name
+ *
+ * @return string the full path to the cached file
+ */
+ protected function getCacheFilename(Core $core)
+ {
+ // no cache id provided, use request_uri as default
+ if ($this->cacheId === null) {
+ if (isset($_SERVER['REQUEST_URI']) === true) {
+ $cacheId = $_SERVER['REQUEST_URI'];
+ } elseif (isset($_SERVER['SCRIPT_FILENAME']) && isset($_SERVER['argv'])) {
+ $cacheId = $_SERVER['SCRIPT_FILENAME'] . '-' . implode('-', $_SERVER['argv']);
+ } else {
+ $cacheId = '';
+ }
+ // force compiled id generation
+ $this->getCompiledFilename($core);
+
+ $this->cacheId = str_replace('../', '__',
+ $this->compileId . strtr($cacheId, '\\%?=!:;' . PATH_SEPARATOR, '/-------'));
+ }
+
+ return $core->getCacheDir() . $this->cacheId . '.html';
+ }
+
+ /**
+ * Returns some php code that will check if this template has been modified or not.
+ * if the function returns null, the template will be instanciated and then the Uid checked
+ *
+ * @return string
+ */
+ public function getIsModifiedCode()
+ {
+ return null;
+ }
+
+ /**
+ * Ensures the given path exists.
+ *
+ * @param string $path any path
+ * @param string $baseDir the base directory where the directory is created
+ * ($path must still contain the full path, $baseDir
+ * is only used for unix permissions)
+ *
+ * @throws Exception
+ */
+ protected function makeDirectory($path, $baseDir = null)
+ {
+ if (is_dir($path) === true) {
+ return;
+ }
+
+ if ($this->chmod === null) {
+ $chmod = 0777;
+ } else {
+ $chmod = $this->chmod;
+ }
+
+ $retries = 3;
+ while ($retries --) {
+ @mkdir($path, $chmod, true);
+ if (is_dir($path)) {
+ break;
+ }
+ usleep(20);
+ }
+
+ // enforce the correct mode for all directories created
+ if (strpos(PHP_OS, 'WIN') !== 0 && $baseDir !== null) {
+ $path = strtr(str_replace($baseDir, '', $path), '\\', '/');
+ $folders = explode('/', trim($path, '/'));
+ foreach ($folders as $folder) {
+ $baseDir .= $folder . DIRECTORY_SEPARATOR;
+ if (!chmod($baseDir, $chmod)) {
+ throw new Exception('Unable to chmod ' . "$baseDir to $chmod: " . print_r(error_get_last(), true));
+ }
+ }
+ }
+ }
+}
diff --git a/core/l/Smarty/Autoloader.php b/core/l/Smarty/Autoloader.php
new file mode 100644
index 0000000..e888553
--- /dev/null
+++ b/core/l/Smarty/Autoloader.php
@@ -0,0 +1,107 @@
+ 'Smarty.class.php', 'smartybc' => 'SmartyBC.class.php',);
+
+ /**
+ * Registers Smarty_Autoloader backward compatible to older installations.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not.
+ */
+ public static function registerBC($prepend = false)
+ {
+ /**
+ * register the class autoloader
+ */
+ if (!defined('SMARTY_SPL_AUTOLOAD')) {
+ define('SMARTY_SPL_AUTOLOAD', 0);
+ }
+ if (SMARTY_SPL_AUTOLOAD &&
+ set_include_path(get_include_path() . PATH_SEPARATOR . SMARTY_SYSPLUGINS_DIR) !== false
+ ) {
+ $registeredAutoLoadFunctions = spl_autoload_functions();
+ if (!isset($registeredAutoLoadFunctions[ 'spl_autoload' ])) {
+ spl_autoload_register();
+ }
+ } else {
+ self::register($prepend);
+ }
+ }
+
+ /**
+ * Registers Smarty_Autoloader as an SPL autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not.
+ */
+ public static function register($prepend = false)
+ {
+ self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : dirname(__FILE__) . DIRECTORY_SEPARATOR;
+ self::$SMARTY_SYSPLUGINS_DIR = defined('SMARTY_SYSPLUGINS_DIR') ? SMARTY_SYSPLUGINS_DIR :
+ self::$SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR;
+ if (version_compare(phpversion(), '5.3.0', '>=')) {
+ spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
+ } else {
+ spl_autoload_register(array(__CLASS__, 'autoload'));
+ }
+ }
+
+ /**
+ * Handles auto loading of classes.
+ *
+ * @param string $class A class name.
+ */
+ public static function autoload($class)
+ {
+ $_class = strtolower($class);
+ if (strpos($_class, 'smarty') !== 0) {
+ return;
+ }
+ $file = self::$SMARTY_SYSPLUGINS_DIR . $_class . '.php';
+ if (is_file($file)) {
+ include $file;
+ } else if (isset(self::$rootClasses[ $_class ])) {
+ $file = self::$SMARTY_DIR . self::$rootClasses[ $_class ];
+ if (is_file($file)) {
+ include $file;
+ }
+ }
+ return;
+ }
+}
diff --git a/core/l/Smarty/Smarty.class.php b/core/l/Smarty/Smarty.class.php
new file mode 100644
index 0000000..eee48c4
--- /dev/null
+++ b/core/l/Smarty/Smarty.class.php
@@ -0,0 +1,1530 @@
+
+ * @author Uwe Tews
+ * @author Rodney Rehm
+ * @package Smarty
+ * @version 3.1.30
+ */
+
+/**
+ * define shorthand directory separator constant
+ */
+if (!defined('DS')) {
+ define('DS', DIRECTORY_SEPARATOR);
+}
+
+/**
+ * set SMARTY_DIR to absolute path to Smarty library files.
+ * Sets SMARTY_DIR only if user application has not already defined it.
+ */
+if (!defined('SMARTY_DIR')) {
+ define('SMARTY_DIR', dirname(__FILE__) . DS);
+}
+
+/**
+ * set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins.
+ * Sets SMARTY_SYSPLUGINS_DIR only if user application has not already defined it.
+ */
+if (!defined('SMARTY_SYSPLUGINS_DIR')) {
+ define('SMARTY_SYSPLUGINS_DIR', SMARTY_DIR . 'sysplugins' . DS);
+}
+if (!defined('SMARTY_PLUGINS_DIR')) {
+ define('SMARTY_PLUGINS_DIR', SMARTY_DIR . 'plugins' . DS);
+}
+if (!defined('SMARTY_MBSTRING')) {
+ define('SMARTY_MBSTRING', function_exists('mb_get_info'));
+}
+if (!defined('SMARTY_RESOURCE_CHAR_SET')) {
+ // UTF-8 can only be done properly when mbstring is available!
+ /**
+ * @deprecated in favor of Smarty::$_CHARSET
+ */
+ define('SMARTY_RESOURCE_CHAR_SET', SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1');
+}
+if (!defined('SMARTY_RESOURCE_DATE_FORMAT')) {
+ /**
+ * @deprecated in favor of Smarty::$_DATE_FORMAT
+ */
+ define('SMARTY_RESOURCE_DATE_FORMAT', '%b %e, %Y');
+}
+
+/**
+ * Try loading the Smarty_Internal_Data class
+ * If we fail we must load Smarty's autoloader.
+ * Otherwise we may have a global autoloader like Composer
+ */
+if (!class_exists('Smarty_Autoloader', false)) {
+ if (!class_exists('Smarty_Internal_Data', true)) {
+ require_once dirname(__FILE__) . '/Autoloader.php';
+ Smarty_Autoloader::registerBC();
+ }
+}
+
+/**
+ * Load always needed external class files
+ */
+if (!class_exists('Smarty_Internal_Data', false)) {
+ require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_data.php';
+}
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_extension_handler.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_templatebase.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_template.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_resource.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_variable.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_source.php';
+require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_resource_base.php';
+
+/**
+ * This is the main Smarty class
+ *
+ * @package Smarty
+ *
+ * The following methods will be dynamically loaded by the extension handler when they are called.
+ * They are located in a corresponding Smarty_Internal_Method_xxxx class
+ *
+ * @method int clearAllCache(int $exp_time = null, string $type = null)
+ * @method int clearCache(string $template_name, string $cache_id = null, string $compile_id = null, int $exp_time = null, string $type = null)
+ * @method int compileAllTemplates(string $extension = '.tpl', bool $force_compile = false, int $time_limit = 0, int $max_errors = null)
+ * @method int compileAllConfig(string $extension = '.conf', bool $force_compile = false, int $time_limit = 0, int $max_errors = null)
+ * @method int clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null)
+ */
+class Smarty extends Smarty_Internal_TemplateBase
+{
+ /**#@+
+ * constant definitions
+ */
+
+ /**
+ * smarty version
+ */
+ const SMARTY_VERSION = '3.1.30';
+
+ /**
+ * define variable scopes
+ */
+ const SCOPE_LOCAL = 1;
+
+ const SCOPE_PARENT = 2;
+
+ const SCOPE_TPL_ROOT = 4;
+
+ const SCOPE_ROOT = 8;
+
+ const SCOPE_SMARTY = 16;
+
+ const SCOPE_GLOBAL = 32;
+
+ /**
+ * define caching modes
+ */
+ const CACHING_OFF = 0;
+
+ const CACHING_LIFETIME_CURRENT = 1;
+
+ const CACHING_LIFETIME_SAVED = 2;
+
+ /**
+ * define constant for clearing cache files be saved expiration dates
+ */
+ const CLEAR_EXPIRED = - 1;
+
+ /**
+ * define compile check modes
+ */
+ const COMPILECHECK_OFF = 0;
+
+ const COMPILECHECK_ON = 1;
+
+ const COMPILECHECK_CACHEMISS = 2;
+
+ /**
+ * define debug modes
+ */
+ const DEBUG_OFF = 0;
+
+ const DEBUG_ON = 1;
+
+ const DEBUG_INDIVIDUAL = 2;
+
+ /**
+ * modes for handling of "" tags in templates.
+ */
+ const PHP_PASSTHRU = 0; //-> print tags as plain text
+
+ const PHP_QUOTE = 1; //-> escape tags as entities
+
+ const PHP_REMOVE = 2; //-> escape tags as entities
+
+ const PHP_ALLOW = 3; //-> escape tags as entities
+
+ /**
+ * filter types
+ */
+ const FILTER_POST = 'post';
+
+ const FILTER_PRE = 'pre';
+
+ const FILTER_OUTPUT = 'output';
+
+ const FILTER_VARIABLE = 'variable';
+
+ /**
+ * plugin types
+ */
+ const PLUGIN_FUNCTION = 'function';
+
+ const PLUGIN_BLOCK = 'block';
+
+ const PLUGIN_COMPILER = 'compiler';
+
+ const PLUGIN_MODIFIER = 'modifier';
+
+ const PLUGIN_MODIFIERCOMPILER = 'modifiercompiler';
+
+ /**
+ * Resource caching modes
+ * (not used since 3.1.30)
+ */
+ const RESOURCE_CACHE_OFF = 0;
+
+ const RESOURCE_CACHE_AUTOMATIC = 1; // cache template objects by rules
+
+ const RESOURCE_CACHE_TEMPLATE = 2; // cache all template objects
+
+ const RESOURCE_CACHE_ON = 4; // cache source and compiled resources
+
+ /**#@-*/
+
+ /**
+ * assigned global tpl vars
+ */
+ public static $global_tpl_vars = array();
+
+ /**
+ * error handler returned by set_error_handler() in Smarty::muteExpectedErrors()
+ */
+ public static $_previous_error_handler = null;
+
+ /**
+ * contains directories outside of SMARTY_DIR that are to be muted by muteExpectedErrors()
+ */
+ public static $_muted_directories = array();
+
+ /**
+ * Flag denoting if Multibyte String functions are available
+ */
+ public static $_MBSTRING = SMARTY_MBSTRING;
+
+ /**
+ * The character set to adhere to (e.g. "UTF-8")
+ */
+ public static $_CHARSET = SMARTY_RESOURCE_CHAR_SET;
+
+ /**
+ * The date format to be used internally
+ * (accepts date() and strftime())
+ */
+ public static $_DATE_FORMAT = SMARTY_RESOURCE_DATE_FORMAT;
+
+ /**
+ * Flag denoting if PCRE should run in UTF-8 mode
+ */
+ public static $_UTF8_MODIFIER = 'u';
+
+ /**
+ * Flag denoting if operating system is windows
+ */
+ public static $_IS_WINDOWS = false;
+
+ /**#@+
+ * variables
+ */
+
+ /**
+ * auto literal on delimiters with whitespace
+ *
+ * @var boolean
+ */
+ public $auto_literal = true;
+
+ /**
+ * display error on not assigned variables
+ *
+ * @var boolean
+ */
+ public $error_unassigned = false;
+
+ /**
+ * look up relative file path in include_path
+ *
+ * @var boolean
+ */
+ public $use_include_path = false;
+
+ /**
+ * template directory
+ *
+ * @var array
+ */
+ protected $template_dir = array('./templates/');
+
+ /**
+ * flags for normalized template directory entries
+ *
+ * @var array
+ */
+ protected $_processedTemplateDir = array();
+
+ /**
+ * flag if template_dir is normalized
+ *
+ * @var bool
+ */
+ public $_templateDirNormalized = false;
+
+ /**
+ * joined template directory string used in cache keys
+ *
+ * @var string
+ */
+ public $_joined_template_dir = null;
+
+ /**
+ * config directory
+ *
+ * @var array
+ */
+ protected $config_dir = array('./configs/');
+
+ /**
+ * flags for normalized template directory entries
+ *
+ * @var array
+ */
+ protected $_processedConfigDir = array();
+
+ /**
+ * flag if config_dir is normalized
+ *
+ * @var bool
+ */
+ public $_configDirNormalized = false;
+
+ /**
+ * joined config directory string used in cache keys
+ *
+ * @var string
+ */
+ public $_joined_config_dir = null;
+
+ /**
+ * default template handler
+ *
+ * @var callable
+ */
+ public $default_template_handler_func = null;
+
+ /**
+ * default config handler
+ *
+ * @var callable
+ */
+ public $default_config_handler_func = null;
+
+ /**
+ * default plugin handler
+ *
+ * @var callable
+ */
+ public $default_plugin_handler_func = null;
+
+ /**
+ * compile directory
+ *
+ * @var string
+ */
+ protected $compile_dir = './templates_c/';
+
+ /**
+ * flag if template_dir is normalized
+ *
+ * @var bool
+ */
+ public $_compileDirNormalized = false;
+
+ /**
+ * plugins directory
+ *
+ * @var array
+ */
+ protected $plugins_dir = array();
+
+ /**
+ * flag if plugins_dir is normalized
+ *
+ * @var bool
+ */
+ public $_pluginsDirNormalized = false;
+
+ /**
+ * cache directory
+ *
+ * @var string
+ */
+ protected $cache_dir = './cache/';
+
+ /**
+ * flag if template_dir is normalized
+ *
+ * @var bool
+ */
+ public $_cacheDirNormalized = false;
+
+ /**
+ * force template compiling?
+ *
+ * @var boolean
+ */
+ public $force_compile = false;
+
+ /**
+ * check template for modifications?
+ *
+ * @var boolean
+ */
+ public $compile_check = true;
+
+ /**
+ * use sub dirs for compiled/cached files?
+ *
+ * @var boolean
+ */
+ public $use_sub_dirs = false;
+
+ /**
+ * allow ambiguous resources (that are made unique by the resource handler)
+ *
+ * @var boolean
+ */
+ public $allow_ambiguous_resources = false;
+
+ /**
+ * merge compiled includes
+ *
+ * @var boolean
+ */
+ public $merge_compiled_includes = false;
+
+ /**
+ * force cache file creation
+ *
+ * @var boolean
+ */
+ public $force_cache = false;
+
+ /**
+ * template left-delimiter
+ *
+ * @var string
+ */
+ public $left_delimiter = "{";
+
+ /**
+ * template right-delimiter
+ *
+ * @var string
+ */
+ public $right_delimiter = "}";
+
+ /**#@+
+ * security
+ */
+ /**
+ * class name
+ * This should be instance of Smarty_Security.
+ *
+ * @var string
+ * @see Smarty_Security
+ */
+ public $security_class = 'Smarty_Security';
+
+ /**
+ * implementation of security class
+ *
+ * @var Smarty_Security
+ */
+ public $security_policy = null;
+
+ /**
+ * controls handling of PHP-blocks
+ *
+ * @var integer
+ */
+ public $php_handling = self::PHP_PASSTHRU;
+
+ /**
+ * controls if the php template file resource is allowed
+ *
+ * @var bool
+ */
+ public $allow_php_templates = false;
+
+ /**#@-*/
+ /**
+ * debug mode
+ * Setting this to true enables the debug-console.
+ *
+ * @var boolean
+ */
+ public $debugging = false;
+
+ /**
+ * This determines if debugging is enable-able from the browser.
+ *
+ *
NONE => no debugging control allowed
+ *
URL => enable debugging when SMARTY_DEBUG is found in the URL.
+ *
+ *
+ * @var string
+ */
+ public $debugging_ctrl = 'NONE';
+
+ /**
+ * Name of debugging URL-param.
+ * Only used when $debugging_ctrl is set to 'URL'.
+ * The name of the URL-parameter that activates debugging.
+ *
+ * @var string
+ */
+ public $smarty_debug_id = 'SMARTY_DEBUG';
+
+ /**
+ * Path of debug template.
+ *
+ * @var string
+ */
+ public $debug_tpl = null;
+
+ /**
+ * When set, smarty uses this value as error_reporting-level.
+ *
+ * @var int
+ */
+ public $error_reporting = null;
+
+ /**#@+
+ * config var settings
+ */
+
+ /**
+ * Controls whether variables with the same name overwrite each other.
+ *
+ * @var boolean
+ */
+ public $config_overwrite = true;
+
+ /**
+ * Controls whether config values of on/true/yes and off/false/no get converted to boolean.
+ *
+ * @var boolean
+ */
+ public $config_booleanize = true;
+
+ /**
+ * Controls whether hidden config sections/vars are read from the file.
+ *
+ * @var boolean
+ */
+ public $config_read_hidden = false;
+
+ /**#@-*/
+
+ /**#@+
+ * resource locking
+ */
+
+ /**
+ * locking concurrent compiles
+ *
+ * @var boolean
+ */
+ public $compile_locking = true;
+
+ /**
+ * Controls whether cache resources should use locking mechanism
+ *
+ * @var boolean
+ */
+ public $cache_locking = false;
+
+ /**
+ * seconds to wait for acquiring a lock before ignoring the write lock
+ *
+ * @var float
+ */
+ public $locking_timeout = 10;
+
+ /**#@-*/
+
+ /**
+ * resource type used if none given
+ * Must be an valid key of $registered_resources.
+ *
+ * @var string
+ */
+ public $default_resource_type = 'file';
+
+ /**
+ * caching type
+ * Must be an element of $cache_resource_types.
+ *
+ * @var string
+ */
+ public $caching_type = 'file';
+
+ /**
+ * config type
+ *
+ * @var string
+ */
+ public $default_config_type = 'file';
+
+ /**
+ * check If-Modified-Since headers
+ *
+ * @var boolean
+ */
+ public $cache_modified_check = false;
+
+ /**
+ * registered plugins
+ *
+ * @var array
+ */
+ public $registered_plugins = array();
+
+ /**
+ * registered objects
+ *
+ * @var array
+ */
+ public $registered_objects = array();
+
+ /**
+ * registered classes
+ *
+ * @var array
+ */
+ public $registered_classes = array();
+
+ /**
+ * registered filters
+ *
+ * @var array
+ */
+ public $registered_filters = array();
+
+ /**
+ * registered resources
+ *
+ * @var array
+ */
+ public $registered_resources = array();
+
+ /**
+ * registered cache resources
+ *
+ * @var array
+ */
+ public $registered_cache_resources = array();
+
+ /**
+ * autoload filter
+ *
+ * @var array
+ */
+ public $autoload_filters = array();
+
+ /**
+ * default modifier
+ *
+ * @var array
+ */
+ public $default_modifiers = array();
+
+ /**
+ * autoescape variable output
+ *
+ * @var boolean
+ */
+ public $escape_html = false;
+
+ /**
+ * start time for execution time calculation
+ *
+ * @var int
+ */
+ public $start_time = 0;
+
+ /**
+ * required by the compiler for BC
+ *
+ * @var string
+ */
+ public $_current_file = null;
+
+ /**
+ * internal flag to enable parser debugging
+ *
+ * @var bool
+ */
+ public $_parserdebug = false;
+
+ /**
+ * This object type (Smarty = 1, template = 2, data = 4)
+ *
+ * @var int
+ */
+ public $_objType = 1;
+
+ /**
+ * Debug object
+ *
+ * @var Smarty_Internal_Debug
+ */
+ public $_debug = null;
+
+ /**
+ * removed properties
+ *
+ * @var string[]
+ */
+ private $obsoleteProperties = array('resource_caching', 'template_resource_caching', 'direct_access_security',
+ '_dir_perms', '_file_perms', 'plugin_search_order',
+ 'inheritance_merge_compiled_includes', 'resource_cache_mode',);
+
+ /**
+ * List of private properties which will call getter/setter on a direct access
+ *
+ * @var string[]
+ */
+ private $accessMap = array('template_dir' => 'TemplateDir', 'config_dir' => 'ConfigDir',
+ 'plugins_dir' => 'PluginsDir', 'compile_dir' => 'CompileDir',
+ 'cache_dir' => 'CacheDir',);
+
+ /**#@-*/
+
+ /**
+ * Initialize new Smarty object
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ if (is_callable('mb_internal_encoding')) {
+ mb_internal_encoding(Smarty::$_CHARSET);
+ }
+ $this->start_time = microtime(true);
+
+ if (isset($_SERVER[ 'SCRIPT_NAME' ])) {
+ Smarty::$global_tpl_vars[ 'SCRIPT_NAME' ] = new Smarty_Variable($_SERVER[ 'SCRIPT_NAME' ]);
+ }
+
+ // Check if we're running on windows
+ Smarty::$_IS_WINDOWS = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
+ // let PCRE (preg_*) treat strings as ISO-8859-1 if we're not dealing with UTF-8
+ if (Smarty::$_CHARSET !== 'UTF-8') {
+ Smarty::$_UTF8_MODIFIER = '';
+ }
+ }
+
+ /**
+ * Check if a template resource exists
+ *
+ * @param string $resource_name template name
+ *
+ * @return boolean status
+ */
+ public function templateExists($resource_name)
+ {
+ // create source object
+ $source = Smarty_Template_Source::load(null, $this, $resource_name);
+ return $source->exists;
+ }
+
+ /**
+ * Loads security class and enables security
+ *
+ * @param string|Smarty_Security $security_class if a string is used, it must be class-name
+ *
+ * @return Smarty current Smarty instance for chaining
+ * @throws SmartyException when an invalid class name is provided
+ */
+ public function enableSecurity($security_class = null)
+ {
+ Smarty_Security::enableSecurity($this, $security_class);
+ return $this;
+ }
+
+ /**
+ * Disable security
+ *
+ * @return Smarty current Smarty instance for chaining
+ */
+ public function disableSecurity()
+ {
+ $this->security_policy = null;
+
+ return $this;
+ }
+
+ /**
+ * Set template directory
+ *
+ * @param string|array $template_dir directory(s) of template sources
+ * @param bool $isConfig true for config_dir
+ *
+ * @return \Smarty current Smarty instance for chaining
+ */
+ public function setTemplateDir($template_dir, $isConfig = false)
+ {
+ if ($isConfig) {
+ $this->config_dir = array();
+ $this->_processedConfigDir = array();
+ } else {
+ $this->template_dir = array();
+ $this->_processedTemplateDir = array();
+ }
+ $this->addTemplateDir($template_dir, null, $isConfig);
+ return $this;
+ }
+
+ /**
+ * Add template directory(s)
+ *
+ * @param string|array $template_dir directory(s) of template sources
+ * @param string $key of the array element to assign the template dir to
+ * @param bool $isConfig true for config_dir
+ *
+ * @return Smarty current Smarty instance for chaining
+ */
+ public function addTemplateDir($template_dir, $key = null, $isConfig = false)
+ {
+ if ($isConfig) {
+ $processed = &$this->_processedConfigDir;
+ $dir = &$this->config_dir;
+ $this->_configDirNormalized = false;
+ } else {
+ $processed = &$this->_processedTemplateDir;
+ $dir = &$this->template_dir;
+ $this->_templateDirNormalized = false;
+ }
+ if (is_array($template_dir)) {
+ foreach ($template_dir as $k => $v) {
+ if (is_int($k)) {
+ // indexes are not merged but appended
+ $dir[] = $v;
+ } else {
+ // string indexes are overridden
+ $dir[ $k ] = $v;
+ unset($processed[ $key ]);
+ }
+ }
+ } else {
+ if ($key !== null) {
+ // override directory at specified index
+ $dir[ $key ] = $template_dir;
+ unset($processed[ $key ]);
+ } else {
+ // append new directory
+ $dir[] = $template_dir;
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Get template directories
+ *
+ * @param mixed $index index of directory to get, null to get all
+ * @param bool $isConfig true for config_dir
+ *
+ * @return array list of template directories, or directory of $index
+ */
+ public function getTemplateDir($index = null, $isConfig = false)
+ {
+ if ($isConfig) {
+ $dir = &$this->config_dir;
+ } else {
+ $dir = &$this->template_dir;
+ }
+ if ($isConfig ? !$this->_configDirNormalized : !$this->_templateDirNormalized) {
+ $this->_nomalizeTemplateConfig($isConfig);
+ }
+ if ($index !== null) {
+ return isset($dir[ $index ]) ? $dir[ $index ] : null;
+ }
+ return $dir;
+ }
+
+ /**
+ * Set config directory
+ *
+ * @param $config_dir
+ *
+ * @return Smarty current Smarty instance for chaining
+ */
+ public function setConfigDir($config_dir)
+ {
+ return $this->setTemplateDir($config_dir, true);
+ }
+
+ /**
+ * Add config directory(s)
+ *
+ * @param string|array $config_dir directory(s) of config sources
+ * @param mixed $key key of the array element to assign the config dir to
+ *
+ * @return Smarty current Smarty instance for chaining
+ */
+ public function addConfigDir($config_dir, $key = null)
+ {
+ return $this->addTemplateDir($config_dir, $key, true);
+ }
+
+ /**
+ * Get config directory
+ *
+ * @param mixed $index index of directory to get, null to get all
+ *
+ * @return array configuration directory
+ */
+ public function getConfigDir($index = null)
+ {
+ return $this->getTemplateDir($index, true);
+ }
+
+ /**
+ * Set plugins directory
+ *
+ * @param string|array $plugins_dir directory(s) of plugins
+ *
+ * @return Smarty current Smarty instance for chaining
+ */
+ public function setPluginsDir($plugins_dir)
+ {
+ $this->plugins_dir = (array) $plugins_dir;
+ $this->_pluginsDirNormalized = false;
+ return $this;
+ }
+
+ /**
+ * Adds directory of plugin files
+ *
+ * @param null|array $plugins_dir
+ *
+ * @return Smarty current Smarty instance for chaining
+ */
+ public function addPluginsDir($plugins_dir)
+ {
+ if (empty($this->plugins_dir)) {
+ $this->plugins_dir[] = SMARTY_PLUGINS_DIR;
+ }
+ $this->plugins_dir = array_merge($this->plugins_dir, (array) $plugins_dir);
+ $this->_pluginsDirNormalized = false;
+ return $this;
+ }
+
+ /**
+ * Get plugin directories
+ *
+ * @return array list of plugin directories
+ */
+ public function getPluginsDir()
+ {
+ if (empty($this->plugins_dir)) {
+ $this->plugins_dir[] = SMARTY_PLUGINS_DIR;
+ $this->_pluginsDirNormalized = false;
+ }
+ if (!$this->_pluginsDirNormalized) {
+ if (!is_array($this->plugins_dir)) {
+ $this->plugins_dir = (array) $this->plugins_dir;
+ }
+ foreach ($this->plugins_dir as $k => $v) {
+ $this->plugins_dir[ $k ] = $this->_realpath(rtrim($v, "/\\") . DS, true);
+ }
+ $this->_cache[ 'plugin_files' ] = array();
+ $this->_pluginsDirNormalized = true;
+ }
+ return $this->plugins_dir;
+ }
+
+ /**
+ *
+ * @param string $compile_dir directory to store compiled templates in
+ *
+ * @return Smarty current Smarty instance for chaining
+ */
+ public function setCompileDir($compile_dir)
+ {
+ $this->_normalizeDir('compile_dir', $compile_dir);
+ $this->_compileDirNormalized = true;
+ return $this;
+ }
+
+ /**
+ * Get compiled directory
+ *
+ * @return string path to compiled templates
+ */
+ public function getCompileDir()
+ {
+ if (!$this->_compileDirNormalized) {
+ $this->_normalizeDir('compile_dir', $this->compile_dir);
+ $this->_compileDirNormalized = true;
+ }
+ return $this->compile_dir;
+ }
+
+ /**
+ * Set cache directory
+ *
+ * @param string $cache_dir directory to store cached templates in
+ *
+ * @return Smarty current Smarty instance for chaining
+ */
+ public function setCacheDir($cache_dir)
+ {
+ $this->_normalizeDir('cache_dir', $cache_dir);
+ $this->_cacheDirNormalized = true;
+ return $this;
+ }
+
+ /**
+ * Get cache directory
+ *
+ * @return string path of cache directory
+ */
+ public function getCacheDir()
+ {
+ if (!$this->_cacheDirNormalized) {
+ $this->_normalizeDir('cache_dir', $this->cache_dir);
+ $this->_cacheDirNormalized = true;
+ }
+ return $this->cache_dir;
+ }
+
+ /**
+ * Normalize and set directory string
+ *
+ * @param string $dirName cache_dir or compile_dir
+ * @param string $dir filepath of folder
+ */
+ private function _normalizeDir($dirName, $dir)
+ {
+ $this->{$dirName} = $this->_realpath(rtrim($dir, "/\\") . DS, true);
+ if (!isset(Smarty::$_muted_directories[ $this->{$dirName} ])) {
+ Smarty::$_muted_directories[ $this->{$dirName} ] = null;
+ }
+ }
+
+ /**
+ * Normalize template_dir or config_dir
+ *
+ * @param bool $isConfig true for config_dir
+ *
+ */
+ private function _nomalizeTemplateConfig($isConfig)
+ {
+ if ($isConfig) {
+ $processed = &$this->_processedConfigDir;
+ $dir = &$this->config_dir;
+ } else {
+ $processed = &$this->_processedTemplateDir;
+ $dir = &$this->template_dir;
+ }
+ if (!is_array($dir)) {
+ $dir = (array) $dir;
+ }
+ foreach ($dir as $k => $v) {
+ if (!isset($processed[ $k ])) {
+ $dir[ $k ] = $v = $this->_realpath(rtrim($v, "/\\") . DS, true);
+ $processed[ $k ] = true;
+ }
+ }
+ $isConfig ? $this->_configDirNormalized = true : $this->_templateDirNormalized = true;
+ $isConfig ? $this->_joined_config_dir = join('#', $this->config_dir) :
+ $this->_joined_template_dir = join('#', $this->template_dir);
+ }
+
+ /**
+ * creates a template object
+ *
+ * @param string $template the resource handle of the template file
+ * @param mixed $cache_id cache id to be used with this template
+ * @param mixed $compile_id compile id to be used with this template
+ * @param object $parent next higher level of Smarty variables
+ * @param boolean $do_clone flag is Smarty object shall be cloned
+ *
+ * @return object template object
+ */
+ public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true)
+ {
+ if ($cache_id !== null && (is_object($cache_id) || is_array($cache_id))) {
+ $parent = $cache_id;
+ $cache_id = null;
+ }
+ if ($parent !== null && is_array($parent)) {
+ $data = $parent;
+ $parent = null;
+ } else {
+ $data = null;
+ }
+ $_templateId = $this->_getTemplateId($template, $cache_id, $compile_id);
+ $tpl = null;
+ if ($this->caching && isset($this->_cache[ 'isCached' ][ $_templateId ])) {
+ $tpl = $do_clone ? clone $this->_cache[ 'isCached' ][ $_templateId ] :
+ $this->_cache[ 'isCached' ][ $_templateId ];
+ $tpl->tpl_vars = $tpl->config_vars = array();
+ } else if (!$do_clone && isset($this->_cache[ 'tplObjects' ][ $_templateId ])) {
+ $tpl = clone $this->_cache[ 'tplObjects' ][ $_templateId ];
+ } else {
+ /* @var Smarty_Internal_Template $tpl */
+ $tpl = new $this->template_class($template, $this, null, $cache_id, $compile_id, null, null);
+ $tpl->templateId = $_templateId;
+ }
+ if ($do_clone) {
+ $tpl->smarty = clone $tpl->smarty;
+ }
+ $tpl->parent = $parent ? $parent : $this;
+ // fill data if present
+ if (!empty($data) && is_array($data)) {
+ // set up variable values
+ foreach ($data as $_key => $_val) {
+ $tpl->tpl_vars[ $_key ] = new Smarty_Variable($_val);
+ }
+ }
+ if ($this->debugging || $this->debugging_ctrl == 'URL') {
+ $tpl->smarty->_debug = new Smarty_Internal_Debug();
+ // check URL debugging control
+ if (!$this->debugging && $this->debugging_ctrl == 'URL') {
+ $tpl->smarty->_debug->debugUrl($tpl->smarty);
+ }
+ }
+ return $tpl;
+ }
+
+ /**
+ * Takes unknown classes and loads plugin files for them
+ * class name format: Smarty_PluginType_PluginName
+ * plugin filename format: plugintype.pluginname.php
+ *
+ * @param string $plugin_name class plugin name to load
+ * @param bool $check check if already loaded
+ *
+ * @throws SmartyException
+ * @return string |boolean filepath of loaded file or false
+ */
+ public function loadPlugin($plugin_name, $check = true)
+ {
+ return $this->ext->loadPlugin->loadPlugin($this, $plugin_name, $check);
+ }
+
+ /**
+ * Get unique template id
+ *
+ * @param string $template_name
+ * @param null|mixed $cache_id
+ * @param null|mixed $compile_id
+ * @param null $caching
+ * @param \Smarty_Internal_Template $template
+ *
+ * @return string
+ */
+ public function _getTemplateId($template_name, $cache_id = null, $compile_id = null, $caching = null,
+ Smarty_Internal_Template $template = null)
+ {
+ $template_name = (strpos($template_name, ':') === false) ? "{$this->default_resource_type}:{$template_name}" :
+ $template_name;
+ $cache_id = $cache_id === null ? $this->cache_id : $cache_id;
+ $compile_id = $compile_id === null ? $this->compile_id : $compile_id;
+ $caching = (int) ($caching === null ? $this->caching : $caching);
+
+ if ((isset($template) && strpos($template_name, ':.') !== false) || $this->allow_ambiguous_resources) {
+ $_templateId =
+ Smarty_Resource::getUniqueTemplateName((isset($template) ? $template : $this), $template_name) .
+ "#{$cache_id}#{$compile_id}#{$caching}";
+ } else {
+ $_templateId = $this->_joined_template_dir . "#{$template_name}#{$cache_id}#{$compile_id}#{$caching}";
+ }
+ if (isset($_templateId[ 150 ])) {
+ $_templateId = sha1($_templateId);
+ }
+ return $_templateId;
+ }
+
+ /**
+ * Normalize path
+ * - remove /./ and /../
+ * - make it absolute if required
+ *
+ * @param string $path file path
+ * @param bool $realpath if true - convert to absolute
+ * false - convert to relative
+ * null - keep as it is but remove /./ /../
+ *
+ * @return string
+ */
+ public function _realpath($path, $realpath = null)
+ {
+ $nds = DS == '/' ? '\\' : '/';
+ // normalize DS
+ $path = str_replace($nds, DS, $path);
+ preg_match('%^(?(?:[[:alpha:]]:[\\\\]|/|[\\\\]{2}[[:alpha:]]+|[[:print:]]{2,}:[/]{2}|[\\\\])?)(?(?:[[:print:]]*))$%',
+ $path, $parts);
+ $path = $parts[ 'path' ];
+ if ($parts[ 'root' ] == '\\') {
+ $parts[ 'root' ] = substr(getcwd(), 0, 2) . $parts[ 'root' ];
+ } else {
+ if ($realpath !== null && !$parts[ 'root' ]) {
+ $path = getcwd() . DS . $path;
+ }
+ }
+ // remove noop 'DS DS' and 'DS.DS' patterns
+ $path = preg_replace('#([\\\\/]([.]?[\\\\/])+)#', DS, $path);
+ // resolve '..DS' pattern, smallest first
+ if (strpos($path, '..' . DS) != false &&
+ preg_match_all('#(([.]?[\\\\/])*([.][.])[\\\\/]([.]?[\\\\/])*)+#', $path, $match)
+ ) {
+ $counts = array();
+ foreach ($match[ 0 ] as $m) {
+ $counts[] = (int) ((strlen($m) - 1) / 3);
+ }
+ sort($counts);
+ foreach ($counts as $count) {
+ $path = preg_replace('#(([\\\\/]([.]?[\\\\/])*[^\\\\/.]+){' . $count .
+ '}[\\\\/]([.]?[\\\\/])*([.][.][\\\\/]([.]?[\\\\/])*){' . $count . '})(?=[^.])#',
+ DS, $path);
+ }
+ }
+
+ return $parts[ 'root' ] . $path;
+ }
+
+ /**
+ * Empty template objects cache
+ */
+ public function _clearTemplateCache()
+ {
+ $this->_cache[ 'isCached' ] = array();
+ $this->_cache[ 'tplObjects' ] = array();
+ }
+
+ /**
+ * @param boolean $compile_check
+ */
+ public function setCompileCheck($compile_check)
+ {
+ $this->compile_check = $compile_check;
+ }
+
+ /**
+ * @param boolean $use_sub_dirs
+ */
+ public function setUseSubDirs($use_sub_dirs)
+ {
+ $this->use_sub_dirs = $use_sub_dirs;
+ }
+
+ /**
+ * @param int $error_reporting
+ */
+ public function setErrorReporting($error_reporting)
+ {
+ $this->error_reporting = $error_reporting;
+ }
+
+ /**
+ * @param boolean $escape_html
+ */
+ public function setEscapeHtml($escape_html)
+ {
+ $this->escape_html = $escape_html;
+ }
+
+ /**
+ * @param boolean $auto_literal
+ */
+ public function setAutoLiteral($auto_literal)
+ {
+ $this->auto_literal = $auto_literal;
+ }
+
+ /**
+ * @param boolean $force_compile
+ */
+ public function setForceCompile($force_compile)
+ {
+ $this->force_compile = $force_compile;
+ }
+
+ /**
+ * @param boolean $merge_compiled_includes
+ */
+ public function setMergeCompiledIncludes($merge_compiled_includes)
+ {
+ $this->merge_compiled_includes = $merge_compiled_includes;
+ }
+
+ /**
+ * @param string $left_delimiter
+ */
+ public function setLeftDelimiter($left_delimiter)
+ {
+ $this->left_delimiter = $left_delimiter;
+ }
+
+ /**
+ * @param string $right_delimiter
+ */
+ public function setRightDelimiter($right_delimiter)
+ {
+ $this->right_delimiter = $right_delimiter;
+ }
+
+ /**
+ * @param boolean $debugging
+ */
+ public function setDebugging($debugging)
+ {
+ $this->debugging = $debugging;
+ }
+
+ /**
+ * @param boolean $config_overwrite
+ */
+ public function setConfigOverwrite($config_overwrite)
+ {
+ $this->config_overwrite = $config_overwrite;
+ }
+
+ /**
+ * @param boolean $config_booleanize
+ */
+ public function setConfigBooleanize($config_booleanize)
+ {
+ $this->config_booleanize = $config_booleanize;
+ }
+
+ /**
+ * @param boolean $config_read_hidden
+ */
+ public function setConfigReadHidden($config_read_hidden)
+ {
+ $this->config_read_hidden = $config_read_hidden;
+ }
+
+ /**
+ * @param boolean $compile_locking
+ */
+ public function setCompileLocking($compile_locking)
+ {
+ $this->compile_locking = $compile_locking;
+ }
+
+ /**
+ * @param string $default_resource_type
+ */
+ public function setDefaultResourceType($default_resource_type)
+ {
+ $this->default_resource_type = $default_resource_type;
+ }
+
+ /**
+ * @param string $caching_type
+ */
+ public function setCachingType($caching_type)
+ {
+ $this->caching_type = $caching_type;
+ }
+
+ /**
+ * Test install
+ *
+ * @param null $errors
+ */
+ public function testInstall(&$errors = null)
+ {
+ Smarty_Internal_TestInstall::testInstall($this, $errors);
+ }
+
+ /**
+ * <> Generic getter.
+ * Calls the appropriate getter function.
+ * Issues an E_USER_NOTICE if no valid getter is found.
+ *
+ * @param string $name property name
+ *
+ * @return mixed
+ */
+ public function __get($name)
+ {
+ if (isset($this->accessMap[ $name ])) {
+ $method = 'get' . $this->accessMap[ $name ];
+ return $this->{$method}();
+ } elseif (isset($this->_cache[ $name ])) {
+ return $this->_cache[ $name ];
+ } elseif (in_array($name, $this->obsoleteProperties)) {
+ return null;
+ } else {
+ trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE);
+ }
+ return null;
+ }
+
+ /**
+ * <> Generic setter.
+ * Calls the appropriate setter function.
+ * Issues an E_USER_NOTICE if no valid setter is found.
+ *
+ * @param string $name property name
+ * @param mixed $value parameter passed to setter
+ */
+ public function __set($name, $value)
+ {
+ if (isset($this->accessMap[ $name ])) {
+ $method = 'set' . $this->accessMap[ $name ];
+ $this->{$method}($value);
+ } elseif (in_array($name, $this->obsoleteProperties)) {
+ return;
+ } else {
+ if (is_object($value) && method_exists($value, $name)) {
+ $this->$name = $value;
+ } else {
+ trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE);
+ }
+ }
+ }
+
+ /**
+ * Error Handler to mute expected messages
+ *
+ * @link http://php.net/set_error_handler
+ *
+ * @param integer $errno Error level
+ * @param $errstr
+ * @param $errfile
+ * @param $errline
+ * @param $errcontext
+ *
+ * @return bool|void
+ */
+ public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $errcontext)
+ {
+ $_is_muted_directory = false;
+
+ // add the SMARTY_DIR to the list of muted directories
+ if (!isset(Smarty::$_muted_directories[ SMARTY_DIR ])) {
+ $smarty_dir = realpath(SMARTY_DIR);
+ if ($smarty_dir !== false) {
+ Smarty::$_muted_directories[ SMARTY_DIR ] =
+ array('file' => $smarty_dir, 'length' => strlen($smarty_dir),);
+ }
+ }
+
+ // walk the muted directories and test against $errfile
+ foreach (Smarty::$_muted_directories as $key => &$dir) {
+ if (!$dir) {
+ // resolve directory and length for speedy comparisons
+ $file = realpath($key);
+ if ($file === false) {
+ // this directory does not exist, remove and skip it
+ unset(Smarty::$_muted_directories[ $key ]);
+ continue;
+ }
+ $dir = array('file' => $file, 'length' => strlen($file),);
+ }
+ if (!strncmp($errfile, $dir[ 'file' ], $dir[ 'length' ])) {
+ $_is_muted_directory = true;
+ break;
+ }
+ }
+ // pass to next error handler if this error did not occur inside SMARTY_DIR
+ // or the error was within smarty but masked to be ignored
+ if (!$_is_muted_directory || ($errno && $errno & error_reporting())) {
+ if (Smarty::$_previous_error_handler) {
+ return call_user_func(Smarty::$_previous_error_handler, $errno, $errstr, $errfile, $errline,
+ $errcontext);
+ } else {
+ return false;
+ }
+ }
+ return;
+ }
+
+ /**
+ * Enable error handler to mute expected messages
+ *
+ * @return void
+ */
+ public static function muteExpectedErrors()
+ {
+ /*
+ error muting is done because some people implemented custom error_handlers using
+ http://php.net/set_error_handler and for some reason did not understand the following paragraph:
+
+ It is important to remember that the standard PHP error handler is completely bypassed for the
+ error types specified by error_types unless the callback function returns FALSE.
+ error_reporting() settings will have no effect and your error handler will be called regardless -
+ however you are still able to read the current value of error_reporting and act appropriately.
+ Of particular note is that this value will be 0 if the statement that caused the error was
+ prepended by the @ error-control operator.
+
+ Smarty deliberately uses @filemtime() over file_exists() and filemtime() in some places. Reasons include
+ - @filemtime() is almost twice as fast as using an additional file_exists()
+ - between file_exists() and filemtime() a possible race condition is opened,
+ which does not exist using the simple @filemtime() approach.
+ */
+ $error_handler = array('Smarty', 'mutingErrorHandler');
+ $previous = set_error_handler($error_handler);
+
+ // avoid dead loops
+ if ($previous !== $error_handler) {
+ Smarty::$_previous_error_handler = $previous;
+ }
+ }
+
+ /**
+ * Disable error handler muting expected messages
+ *
+ * @return void
+ */
+ public static function unmuteExpectedErrors()
+ {
+ restore_error_handler();
+ }
+}
diff --git a/core/l/Smarty/SmartyBC.class.php b/core/l/Smarty/SmartyBC.class.php
new file mode 100644
index 0000000..3955e4f
--- /dev/null
+++ b/core/l/Smarty/SmartyBC.class.php
@@ -0,0 +1,455 @@
+
+ * @author Uwe Tews
+ * @author Rodney Rehm
+ * @package Smarty
+ */
+/**
+ * @ignore
+ */
+require_once(dirname(__FILE__) . '/Smarty.class.php');
+
+/**
+ * Smarty Backward Compatibility Wrapper Class
+ *
+ * @package Smarty
+ */
+class SmartyBC extends Smarty
+{
+ /**
+ * Smarty 2 BC
+ *
+ * @var string
+ */
+ public $_version = self::SMARTY_VERSION;
+
+ /**
+ * This is an array of directories where trusted php scripts reside.
+ *
+ * @var array
+ */
+ public $trusted_dir = array();
+
+ /**
+ * Initialize new SmartyBC object
+ *
+ */
+ public function __construct()
+ {
+ parent::__construct();
+ }
+
+ /**
+ * wrapper for assign_by_ref
+ *
+ * @param string $tpl_var the template variable name
+ * @param mixed &$value the referenced value to assign
+ */
+ public function assign_by_ref($tpl_var, &$value)
+ {
+ $this->assignByRef($tpl_var, $value);
+ }
+
+ /**
+ * wrapper for append_by_ref
+ *
+ * @param string $tpl_var the template variable name
+ * @param mixed &$value the referenced value to append
+ * @param boolean $merge flag if array elements shall be merged
+ */
+ public function append_by_ref($tpl_var, &$value, $merge = false)
+ {
+ $this->appendByRef($tpl_var, $value, $merge);
+ }
+
+ /**
+ * clear the given assigned template variable.
+ *
+ * @param string $tpl_var the template variable to clear
+ */
+ public function clear_assign($tpl_var)
+ {
+ $this->clearAssign($tpl_var);
+ }
+
+ /**
+ * Registers custom function to be used in templates
+ *
+ * @param string $function the name of the template function
+ * @param string $function_impl the name of the PHP function to register
+ * @param bool $cacheable
+ * @param mixed $cache_attrs
+ */
+ public function register_function($function, $function_impl, $cacheable = true, $cache_attrs = null)
+ {
+ $this->registerPlugin('function', $function, $function_impl, $cacheable, $cache_attrs);
+ }
+
+ /**
+ * Unregister custom function
+ *
+ * @param string $function name of template function
+ */
+ public function unregister_function($function)
+ {
+ $this->unregisterPlugin('function', $function);
+ }
+
+ /**
+ * Registers object to be used in templates
+ *
+ * @param string $object name of template object
+ * @param object $object_impl the referenced PHP object to register
+ * @param array $allowed list of allowed methods (empty = all)
+ * @param boolean $smarty_args smarty argument format, else traditional
+ * @param array $block_methods list of methods that are block format
+ *
+ * @throws SmartyException
+ * @internal param array $block_functs list of methods that are block format
+ */
+ public function register_object($object, $object_impl, $allowed = array(), $smarty_args = true,
+ $block_methods = array())
+ {
+ settype($allowed, 'array');
+ settype($smarty_args, 'boolean');
+ $this->registerObject($object, $object_impl, $allowed, $smarty_args, $block_methods);
+ }
+
+ /**
+ * Unregister object
+ *
+ * @param string $object name of template object
+ */
+ public function unregister_object($object)
+ {
+ $this->unregisterObject($object);
+ }
+
+ /**
+ * Registers block function to be used in templates
+ *
+ * @param string $block name of template block
+ * @param string $block_impl PHP function to register
+ * @param bool $cacheable
+ * @param mixed $cache_attrs
+ */
+ public function register_block($block, $block_impl, $cacheable = true, $cache_attrs = null)
+ {
+ $this->registerPlugin('block', $block, $block_impl, $cacheable, $cache_attrs);
+ }
+
+ /**
+ * Unregister block function
+ *
+ * @param string $block name of template function
+ */
+ public function unregister_block($block)
+ {
+ $this->unregisterPlugin('block', $block);
+ }
+
+ /**
+ * Registers compiler function
+ *
+ * @param string $function name of template function
+ * @param string $function_impl name of PHP function to register
+ * @param bool $cacheable
+ */
+ public function register_compiler_function($function, $function_impl, $cacheable = true)
+ {
+ $this->registerPlugin('compiler', $function, $function_impl, $cacheable);
+ }
+
+ /**
+ * Unregister compiler function
+ *
+ * @param string $function name of template function
+ */
+ public function unregister_compiler_function($function)
+ {
+ $this->unregisterPlugin('compiler', $function);
+ }
+
+ /**
+ * Registers modifier to be used in templates
+ *
+ * @param string $modifier name of template modifier
+ * @param string $modifier_impl name of PHP function to register
+ */
+ public function register_modifier($modifier, $modifier_impl)
+ {
+ $this->registerPlugin('modifier', $modifier, $modifier_impl);
+ }
+
+ /**
+ * Unregister modifier
+ *
+ * @param string $modifier name of template modifier
+ */
+ public function unregister_modifier($modifier)
+ {
+ $this->unregisterPlugin('modifier', $modifier);
+ }
+
+ /**
+ * Registers a resource to fetch a template
+ *
+ * @param string $type name of resource
+ * @param array $functions array of functions to handle resource
+ */
+ public function register_resource($type, $functions)
+ {
+ $this->registerResource($type, $functions);
+ }
+
+ /**
+ * Unregister a resource
+ *
+ * @param string $type name of resource
+ */
+ public function unregister_resource($type)
+ {
+ $this->unregisterResource($type);
+ }
+
+ /**
+ * Registers a prefilter function to apply
+ * to a template before compiling
+ *
+ * @param callable $function
+ */
+ public function register_prefilter($function)
+ {
+ $this->registerFilter('pre', $function);
+ }
+
+ /**
+ * Unregister a prefilter function
+ *
+ * @param callable $function
+ */
+ public function unregister_prefilter($function)
+ {
+ $this->unregisterFilter('pre', $function);
+ }
+
+ /**
+ * Registers a postfilter function to apply
+ * to a compiled template after compilation
+ *
+ * @param callable $function
+ */
+ public function register_postfilter($function)
+ {
+ $this->registerFilter('post', $function);
+ }
+
+ /**
+ * Unregister a postfilter function
+ *
+ * @param callable $function
+ */
+ public function unregister_postfilter($function)
+ {
+ $this->unregisterFilter('post', $function);
+ }
+
+ /**
+ * Registers an output filter function to apply
+ * to a template output
+ *
+ * @param callable $function
+ */
+ public function register_outputfilter($function)
+ {
+ $this->registerFilter('output', $function);
+ }
+
+ /**
+ * Unregister an outputfilter function
+ *
+ * @param callable $function
+ */
+ public function unregister_outputfilter($function)
+ {
+ $this->unregisterFilter('output', $function);
+ }
+
+ /**
+ * load a filter of specified type and name
+ *
+ * @param string $type filter type
+ * @param string $name filter name
+ */
+ public function load_filter($type, $name)
+ {
+ $this->loadFilter($type, $name);
+ }
+
+ /**
+ * clear cached content for the given template and cache id
+ *
+ * @param string $tpl_file name of template file
+ * @param string $cache_id name of cache_id
+ * @param string $compile_id name of compile_id
+ * @param string $exp_time expiration time
+ *
+ * @return boolean
+ */
+ public function clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null)
+ {
+ return $this->clearCache($tpl_file, $cache_id, $compile_id, $exp_time);
+ }
+
+ /**
+ * clear the entire contents of cache (all templates)
+ *
+ * @param string $exp_time expire time
+ *
+ * @return boolean
+ */
+ public function clear_all_cache($exp_time = null)
+ {
+ return $this->clearCache(null, null, null, $exp_time);
+ }
+
+ /**
+ * test to see if valid cache exists for this template
+ *
+ * @param string $tpl_file name of template file
+ * @param string $cache_id
+ * @param string $compile_id
+ *
+ * @return boolean
+ */
+ public function is_cached($tpl_file, $cache_id = null, $compile_id = null)
+ {
+ return $this->isCached($tpl_file, $cache_id, $compile_id);
+ }
+
+ /**
+ * clear all the assigned template variables.
+ */
+ public function clear_all_assign()
+ {
+ $this->clearAllAssign();
+ }
+
+ /**
+ * clears compiled version of specified template resource,
+ * or all compiled template files if one is not specified.
+ * This function is for advanced use only, not normally needed.
+ *
+ * @param string $tpl_file
+ * @param string $compile_id
+ * @param string $exp_time
+ *
+ * @return boolean results of {@link smarty_core_rm_auto()}
+ */
+ public function clear_compiled_tpl($tpl_file = null, $compile_id = null, $exp_time = null)
+ {
+ return $this->clearCompiledTemplate($tpl_file, $compile_id, $exp_time);
+ }
+
+ /**
+ * Checks whether requested template exists.
+ *
+ * @param string $tpl_file
+ *
+ * @return boolean
+ */
+ public function template_exists($tpl_file)
+ {
+ return $this->templateExists($tpl_file);
+ }
+
+ /**
+ * Returns an array containing template variables
+ *
+ * @param string $name
+ *
+ * @return array
+ */
+ public function get_template_vars($name = null)
+ {
+ return $this->getTemplateVars($name);
+ }
+
+ /**
+ * Returns an array containing config variables
+ *
+ * @param string $name
+ *
+ * @return array
+ */
+ public function get_config_vars($name = null)
+ {
+ return $this->getConfigVars($name);
+ }
+
+ /**
+ * load configuration values
+ *
+ * @param string $file
+ * @param string $section
+ * @param string $scope
+ */
+ public function config_load($file, $section = null, $scope = 'global')
+ {
+ $this->ConfigLoad($file, $section, $scope);
+ }
+
+ /**
+ * return a reference to a registered object
+ *
+ * @param string $name
+ *
+ * @return object
+ */
+ public function get_registered_object($name)
+ {
+ return $this->getRegisteredObject($name);
+ }
+
+ /**
+ * clear configuration values
+ *
+ * @param string $var
+ */
+ public function clear_config($var = null)
+ {
+ $this->clearConfig($var);
+ }
+
+ /**
+ * trigger Smarty error
+ *
+ * @param string $error_msg
+ * @param integer $error_type
+ */
+ public function trigger_error($error_msg, $error_type = E_USER_WARNING)
+ {
+ trigger_error("Smarty error: $error_msg", $error_type);
+ }
+}
diff --git a/core/l/Smarty/debug.tpl b/core/l/Smarty/debug.tpl
new file mode 100644
index 0000000..37975ba
--- /dev/null
+++ b/core/l/Smarty/debug.tpl
@@ -0,0 +1,54 @@
+{capture name='_smarty_debug' assign=debug_output}
+
+ * - name - name of cycle (optional)
+ * - values - comma separated list of values to cycle, or an array of values to cycle
+ * (this can be left out for subsequent calls)
+ * - reset - boolean - resets given var to true
+ * - print - boolean - print var or not. default is true
+ * - advance - boolean - whether or not to advance the cycle
+ * - delimiter - the value delimiter, default is ","
+ * - assign - boolean, assigns to template var instead of printed.
+ *