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