Completed
Push — master ( a400a4...020752 )
by David
07:41 queued 04:43
created
lib/Dwoo/Core.php 1 patch
Indentation   +1734 added lines, -1734 removed lines patch added patch discarded remove patch
@@ -44,1744 +44,1744 @@
 block discarded – undo
44 44
  */
45 45
 class Core
46 46
 {
47
-    /**
48
-     * Current version number.
49
-     *
50
-     * @var string
51
-     */
52
-    const VERSION = '1.3.4';
53
-
54
-    /**
55
-     * Unique number of this dwoo release, based on version number.
56
-     * this can be used by templates classes to check whether the compiled template
57
-     * has been compiled before this release or not, so that old templates are
58
-     * recompiled automatically when Dwoo is updated
59
-     */
60
-    const RELEASE_TAG = 134;
61
-
62
-    /**
63
-     * Constants that represents all plugin types
64
-     * these are bitwise-operation-safe values to allow multiple types
65
-     * on a single plugin
66
-     *
67
-     * @var int
68
-     */
69
-    const CLASS_PLUGIN      = 1;
70
-    const FUNC_PLUGIN       = 2;
71
-    const NATIVE_PLUGIN     = 4;
72
-    const BLOCK_PLUGIN      = 8;
73
-    const COMPILABLE_PLUGIN = 16;
74
-    const CUSTOM_PLUGIN     = 32;
75
-    const SMARTY_MODIFIER   = 64;
76
-    const SMARTY_BLOCK      = 128;
77
-    const SMARTY_FUNCTION   = 256;
78
-    const PROXY_PLUGIN      = 512;
79
-    const TEMPLATE_PLUGIN   = 1024;
80
-
81
-    /**
82
-     * Constant to default namespaces of builtin plugins
83
-     *
84
-     * @var string
85
-     */
86
-    const NAMESPACE_PLUGINS_BLOCKS     = 'Dwoo\Plugins\Blocks\\';
87
-    const NAMESPACE_PLUGINS_FILTERS    = 'Dwoo\Plugins\Filters\\';
88
-    const NAMESPACE_PLUGINS_FUNCTIONS  = 'Dwoo\Plugins\Functions\\';
89
-    const NAMESPACE_PLUGINS_HELPERS    = 'Dwoo\Plugins\Helpers\\';
90
-    const NAMESPACE_PLUGINS_PROCESSORS = 'Dwoo\Plugins\Processors\\';
91
-
92
-    /**
93
-     * Character set of the template, used by string manipulation plugins.
94
-     * it must be lowercase, but setCharset() will take care of that
95
-     *
96
-     * @see setCharset
97
-     * @see getCharset
98
-     * @var string
99
-     */
100
-    protected $charset = 'UTF-8';
101
-
102
-    /**
103
-     * Global variables that are accessible through $dwoo.* in the templates.
104
-     * default values include:
105
-     * $dwoo.version - current version number
106
-     * $dwoo.ad - a Powered by Dwoo link pointing to dwoo.org
107
-     * $dwoo.now - the current time
108
-     * $dwoo.template - the current template filename
109
-     * $dwoo.charset - the character set used by the template
110
-     * on top of that, foreach and other plugins can store special values in there,
111
-     * see their documentation for more details.
112
-     *
113
-     * @var array
114
-     */
115
-    protected $globals = array();
116
-
117
-    /**
118
-     * Directory where the compiled templates are stored.
119
-     * defaults to DWOO_COMPILEDIR (= dwoo_dir/compiled by default)
120
-     *
121
-     * @var string
122
-     */
123
-    protected $compileDir;
124
-
125
-    /**
126
-     * Directory where the cached templates are stored.
127
-     * defaults to DWOO_CACHEDIR (= dwoo_dir/cache by default)
128
-     *
129
-     * @var string
130
-     */
131
-    protected $cacheDir;
132
-
133
-    /**
134
-     * Directory where the template files are stored
135
-     *
136
-     * @var array
137
-     */
138
-    protected $templateDir = array();
139
-
140
-    /**
141
-     * Defines how long (in seconds) the cached files must remain valid.
142
-     * can be overridden on a per-template basis
143
-     * -1 = never delete
144
-     * 0 = disabled
145
-     * >0 = duration in seconds
146
-     *
147
-     * @var int
148
-     */
149
-    protected $cacheTime = 0;
150
-
151
-    /**
152
-     * Security policy object.
153
-     *
154
-     * @var SecurityPolicy
155
-     */
156
-    protected $securityPolicy = null;
157
-
158
-    /**
159
-     * Stores the custom plugins callbacks.
160
-     *
161
-     * @see addPlugin
162
-     * @see removePlugin
163
-     * @var array
164
-     */
165
-    protected $plugins = array();
166
-
167
-    /**
168
-     * Stores the filter callbacks.
169
-     *
170
-     * @see addFilter
171
-     * @see removeFilter
172
-     * @var array
173
-     */
174
-    protected $filters = array();
175
-
176
-    /**
177
-     * Stores the resource types and associated
178
-     * classes / compiler classes.
179
-     *
180
-     * @var array
181
-     */
182
-    protected $resources = array(
183
-        'file'   => array(
184
-            'class'    => 'Dwoo\Template\File',
185
-            'compiler' => null,
186
-        ),
187
-        'string' => array(
188
-            'class'    => 'Dwoo\Template\Str',
189
-            'compiler' => null,
190
-        ),
191
-    );
192
-
193
-    /**
194
-     * The dwoo loader object used to load plugins by this dwoo instance.
195
-     *
196
-     * @var ILoader
197
-     */
198
-    protected $loader = null;
199
-
200
-    /**
201
-     * Currently rendered template, set to null when not-rendering.
202
-     *
203
-     * @var ITemplate
204
-     */
205
-    protected $template = null;
206
-
207
-    /**
208
-     * Stores the instances of the class plugins during template runtime.
209
-     *
210
-     * @var array
211
-     */
212
-    protected $runtimePlugins = array();
213
-
214
-    /**
215
-     * Stores the returned values during template runtime.
216
-     *
217
-     * @var array
218
-     */
219
-    protected $returnData = array();
220
-
221
-    /**
222
-     * Stores the data during template runtime.
223
-     *
224
-     * @var array
225
-     */
226
-    protected $data = array();
227
-
228
-    /**
229
-     * Stores the current scope during template runtime.
230
-     * this should ideally not be accessed directly from outside template code
231
-     *
232
-     * @var mixed
233
-     */
234
-    public $scope;
235
-
236
-    /**
237
-     * Stores the scope tree during template runtime.
238
-     *
239
-     * @var array
240
-     */
241
-    protected $scopeTree = array();
242
-
243
-    /**
244
-     * Stores the block plugins stack during template runtime.
245
-     *
246
-     * @var array
247
-     */
248
-    protected $stack = array();
249
-
250
-    /**
251
-     * Stores the current block plugin at the top of the stack during template runtime.
252
-     *
253
-     * @var BlockPlugin
254
-     */
255
-    protected $curBlock;
256
-
257
-    /**
258
-     * Stores the output buffer during template runtime.
259
-     *
260
-     * @var string
261
-     */
262
-    protected $buffer;
263
-
264
-    /**
265
-     * Stores plugin proxy.
266
-     *
267
-     * @var IPluginProxy
268
-     */
269
-    protected $pluginProxy;
270
-
271
-    /**
272
-     * Constructor, sets the cache and compile dir to the default values if not provided.
273
-     *
274
-     * @param string $compileDir path to the compiled directory, defaults to lib/compiled
275
-     * @param string $cacheDir   path to the cache directory, defaults to lib/cache
276
-     */
277
-    public function __construct($compileDir = null, $cacheDir = null)
278
-    {
279
-        if ($compileDir !== null) {
280
-            $this->setCompileDir($compileDir);
281
-        }
282
-        if ($cacheDir !== null) {
283
-            $this->setCacheDir($cacheDir);
284
-        }
285
-        $this->initGlobals();
286
-    }
287
-
288
-    /**
289
-     * Resets some runtime variables to allow a cloned object to be used to render sub-templates.
290
-     *
291
-     * @return void
292
-     */
293
-    public function __clone()
294
-    {
295
-        $this->template = null;
296
-        unset($this->data);
297
-        unset($this->returnData);
298
-    }
299
-
300
-    /**
301
-     * Returns the given template rendered using the provided data and optional compiler.
302
-     *
303
-     * @param mixed     $_tpl      template, can either be a ITemplate object (i.e. TemplateFile), a
304
-     *                             valid path to a template, or a template as a string it is recommended to
305
-     *                             provide a ITemplate as it will probably make things faster, especially if
306
-     *                             you render a template multiple times
307
-     * @param mixed     $data      the data to use, can either be a IDataProvider object (i.e. Data) or
308
-     *                             an associative array. if you're rendering the template from cache, it can be
309
-     *                             left null
310
-     * @param ICompiler $_compiler the compiler that must be used to compile the template, if left empty a default
311
-     *                             Compiler will be used
312
-     *
313
-     * @return string|void or the template output if $output is false
314
-     * @throws Exception
315
-     */
316
-    public function get($_tpl, $data = array(), $_compiler = null)
317
-    {
318
-        // a render call came from within a template, so we need a new dwoo instance in order to avoid breaking this one
319
-        if ($this->template instanceof ITemplate) {
320
-            $clone = clone $this;
321
-
322
-            return $clone->get($_tpl, $data, $_compiler);
323
-        }
324
-
325
-        // auto-create template if required
326
-        if ($_tpl instanceof ITemplate) {
327
-            // valid, skip
328
-        } elseif (is_string($_tpl)) {
329
-            $_tpl = new TemplateFile($_tpl);
330
-            $_tpl->setIncludePath($this->getTemplateDir());
331
-        } else {
332
-            throw new Exception('Dwoo->get\'s first argument must be a ITemplate (i.e. TemplateFile) or a valid path to a template file', E_USER_NOTICE);
333
-        }
334
-
335
-        // save the current template, enters render mode at the same time
336
-        // if another rendering is requested it will be proxied to a new Core(instance
337
-        $this->template = $_tpl;
338
-
339
-        // load data
340
-        if ($data instanceof IDataProvider) {
341
-            $this->data = $data->getData();
342
-        } elseif (is_array($data)) {
343
-            $this->data = $data;
344
-        } elseif ($data instanceof ArrayAccess) {
345
-            $this->data = $data;
346
-        } else {
347
-            throw new Exception('Dwoo->get/Dwoo->output\'s data argument must be a IDataProvider object (i.e. Data) or an associative array', E_USER_NOTICE);
348
-        }
349
-
350
-        $this->addGlobal('template', $_tpl->getName());
351
-        $this->initRuntimeVars($_tpl);
352
-
353
-        // try to get cached template
354
-        $file        = $_tpl->getCachedTemplate($this);
355
-        $doCache     = $file === true;
356
-        $cacheLoaded = is_string($file);
357
-
358
-        if ($cacheLoaded === true) {
359
-            // cache is present, run it
360
-            ob_start();
361
-            include $file;
362
-            $this->template = null;
363
-
364
-            return ob_get_clean();
365
-        } else {
366
-            $dynamicId = uniqid();
367
-
368
-            // render template
369
-            $compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler);
370
-            $out              = include $compiledTemplate;
371
-
372
-            // template returned false so it needs to be recompiled
373
-            if ($out === false) {
374
-                $_tpl->forceCompilation();
375
-                $compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler);
376
-                $out              = include $compiledTemplate;
377
-            }
378
-
379
-            if ($doCache === true) {
380
-                $out = preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '<?php /*' . $dynamicId . '*/ echo \'$1\'; ?>', $out);
381
-                if (!class_exists(self::NAMESPACE_PLUGINS_BLOCKS . 'PluginDynamic')) {
382
-                    $this->getLoader()->loadPlugin('PluginDynamic');
383
-                }
384
-                $out = PluginDynamic::unescape($out, $dynamicId, $compiledTemplate);
385
-            }
386
-
387
-            // process filters
388
-            foreach ($this->filters as $filter) {
389
-                if (is_array($filter) && $filter[0] instanceof Filter) {
390
-                    $out = call_user_func($filter, $out);
391
-                } else {
392
-                    $out = call_user_func($filter, $this, $out);
393
-                }
394
-            }
395
-
396
-            if ($doCache === true) {
397
-                // building cache
398
-                $file = $_tpl->cache($this, $out);
399
-
400
-                // run it from the cache to be sure dynamics are rendered
401
-                ob_start();
402
-                include $file;
403
-                // exit render mode
404
-                $this->template = null;
405
-
406
-                return ob_get_clean();
407
-            } else {
408
-                // no need to build cache
409
-                // exit render mode
410
-                $this->template = null;
411
-
412
-                return $out;
413
-            }
414
-        }
415
-    }
416
-
417
-    /**
418
-     * Registers a Global.
419
-     * New globals can be added before compiling or rendering a template
420
-     * but after, you can only update existing globals.
421
-     *
422
-     * @param string $name
423
-     * @param mixed  $value
424
-     *
425
-     * @return $this
426
-     * @throws Exception
427
-     */
428
-    public function addGlobal($name, $value)
429
-    {
430
-        if (null === $this->globals) {
431
-            $this->initGlobals();
432
-        }
433
-
434
-        $this->globals[$name] = $value;
435
-
436
-        return $this;
437
-    }
438
-
439
-    /**
440
-     * Gets the registered Globals.
441
-     *
442
-     * @return array
443
-     */
444
-    public function getGlobals()
445
-    {
446
-        return $this->globals;
447
-    }
448
-
449
-    /**
450
-     * Re-initializes the globals array before each template run.
451
-     * this method is only callede once when the Dwoo object is created
452
-     *
453
-     * @return void
454
-     */
455
-    protected function initGlobals()
456
-    {
457
-        $this->globals = array(
458
-            'version' => self::VERSION,
459
-            'ad'      => '<a href="http://dwoo.org/">Powered by Dwoo</a>',
460
-            'now'     => $_SERVER['REQUEST_TIME'],
461
-            'charset' => $this->getCharset(),
462
-        );
463
-    }
464
-
465
-    /**
466
-     * Re-initializes the runtime variables before each template run.
467
-     * override this method to inject data in the globals array if needed, this
468
-     * method is called before each template execution
469
-     *
470
-     * @param ITemplate $tpl the template that is going to be rendered
471
-     *
472
-     * @return void
473
-     */
474
-    protected function initRuntimeVars(ITemplate $tpl)
475
-    {
476
-        $this->runtimePlugins = array();
477
-        $this->scope          = &$this->data;
478
-        $this->scopeTree      = array();
479
-        $this->stack          = array();
480
-        $this->curBlock       = null;
481
-        $this->buffer         = '';
482
-        $this->returnData     = array();
483
-    }
484
-
485
-    /**
486
-     * Adds a custom plugin that is not in one of the plugin directories.
487
-     *
488
-     * @param string   $name       the plugin name to be used in the templates
489
-     * @param callback $callback   the plugin callback, either a function name,
490
-     *                             a class name or an array containing an object
491
-     *                             or class name and a method name
492
-     * @param bool     $compilable if set to true, the plugin is assumed to be compilable
493
-     *
494
-     * @return void
495
-     * @throws Exception
496
-     */
497
-    public function addPlugin($name, $callback, $compilable = false)
498
-    {
499
-        $compilable = $compilable ? self::COMPILABLE_PLUGIN : 0;
500
-        if (is_array($callback)) {
501
-            if (is_subclass_of(is_object($callback[0]) ? get_class($callback[0]) : $callback[0], 'Dwoo\Block\Plugin')) {
502
-                $this->plugins[$name] = array(
503
-                    'type'     => self::BLOCK_PLUGIN | $compilable,
504
-                    'callback' => $callback,
505
-                    'class'    => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0])
506
-                );
507
-            } else {
508
-                $this->plugins[$name] = array(
509
-                    'type'     => self::CLASS_PLUGIN | $compilable,
510
-                    'callback' => $callback,
511
-                    'class'    => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0]),
512
-                    'function' => $callback[1]
513
-                );
514
-            }
515
-        } elseif (is_string($callback)) {
516
-            if (class_exists($callback)) {
517
-                if (is_subclass_of($callback, 'Dwoo\Block\Plugin')) {
518
-                    $this->plugins[$name] = array(
519
-                        'type'     => self::BLOCK_PLUGIN | $compilable,
520
-                        'callback' => $callback,
521
-                        'class'    => $callback
522
-                    );
523
-                } else {
524
-                    $this->plugins[$name] = array(
525
-                        'type'     => self::CLASS_PLUGIN | $compilable,
526
-                        'callback' => $callback,
527
-                        'class'    => $callback,
528
-                        'function' => ($compilable ? 'compile' : 'process')
529
-                    );
530
-                }
531
-            } elseif (function_exists($callback)) {
532
-                $this->plugins[$name] = array(
533
-                    'type'     => self::FUNC_PLUGIN | $compilable,
534
-                    'callback' => $callback
535
-                );
536
-            } else {
537
-                throw new Exception(
538
-                    'Callback could not be processed correctly, please check that the function/class 
47
+	/**
48
+	 * Current version number.
49
+	 *
50
+	 * @var string
51
+	 */
52
+	const VERSION = '1.3.4';
53
+
54
+	/**
55
+	 * Unique number of this dwoo release, based on version number.
56
+	 * this can be used by templates classes to check whether the compiled template
57
+	 * has been compiled before this release or not, so that old templates are
58
+	 * recompiled automatically when Dwoo is updated
59
+	 */
60
+	const RELEASE_TAG = 134;
61
+
62
+	/**
63
+	 * Constants that represents all plugin types
64
+	 * these are bitwise-operation-safe values to allow multiple types
65
+	 * on a single plugin
66
+	 *
67
+	 * @var int
68
+	 */
69
+	const CLASS_PLUGIN      = 1;
70
+	const FUNC_PLUGIN       = 2;
71
+	const NATIVE_PLUGIN     = 4;
72
+	const BLOCK_PLUGIN      = 8;
73
+	const COMPILABLE_PLUGIN = 16;
74
+	const CUSTOM_PLUGIN     = 32;
75
+	const SMARTY_MODIFIER   = 64;
76
+	const SMARTY_BLOCK      = 128;
77
+	const SMARTY_FUNCTION   = 256;
78
+	const PROXY_PLUGIN      = 512;
79
+	const TEMPLATE_PLUGIN   = 1024;
80
+
81
+	/**
82
+	 * Constant to default namespaces of builtin plugins
83
+	 *
84
+	 * @var string
85
+	 */
86
+	const NAMESPACE_PLUGINS_BLOCKS     = 'Dwoo\Plugins\Blocks\\';
87
+	const NAMESPACE_PLUGINS_FILTERS    = 'Dwoo\Plugins\Filters\\';
88
+	const NAMESPACE_PLUGINS_FUNCTIONS  = 'Dwoo\Plugins\Functions\\';
89
+	const NAMESPACE_PLUGINS_HELPERS    = 'Dwoo\Plugins\Helpers\\';
90
+	const NAMESPACE_PLUGINS_PROCESSORS = 'Dwoo\Plugins\Processors\\';
91
+
92
+	/**
93
+	 * Character set of the template, used by string manipulation plugins.
94
+	 * it must be lowercase, but setCharset() will take care of that
95
+	 *
96
+	 * @see setCharset
97
+	 * @see getCharset
98
+	 * @var string
99
+	 */
100
+	protected $charset = 'UTF-8';
101
+
102
+	/**
103
+	 * Global variables that are accessible through $dwoo.* in the templates.
104
+	 * default values include:
105
+	 * $dwoo.version - current version number
106
+	 * $dwoo.ad - a Powered by Dwoo link pointing to dwoo.org
107
+	 * $dwoo.now - the current time
108
+	 * $dwoo.template - the current template filename
109
+	 * $dwoo.charset - the character set used by the template
110
+	 * on top of that, foreach and other plugins can store special values in there,
111
+	 * see their documentation for more details.
112
+	 *
113
+	 * @var array
114
+	 */
115
+	protected $globals = array();
116
+
117
+	/**
118
+	 * Directory where the compiled templates are stored.
119
+	 * defaults to DWOO_COMPILEDIR (= dwoo_dir/compiled by default)
120
+	 *
121
+	 * @var string
122
+	 */
123
+	protected $compileDir;
124
+
125
+	/**
126
+	 * Directory where the cached templates are stored.
127
+	 * defaults to DWOO_CACHEDIR (= dwoo_dir/cache by default)
128
+	 *
129
+	 * @var string
130
+	 */
131
+	protected $cacheDir;
132
+
133
+	/**
134
+	 * Directory where the template files are stored
135
+	 *
136
+	 * @var array
137
+	 */
138
+	protected $templateDir = array();
139
+
140
+	/**
141
+	 * Defines how long (in seconds) the cached files must remain valid.
142
+	 * can be overridden on a per-template basis
143
+	 * -1 = never delete
144
+	 * 0 = disabled
145
+	 * >0 = duration in seconds
146
+	 *
147
+	 * @var int
148
+	 */
149
+	protected $cacheTime = 0;
150
+
151
+	/**
152
+	 * Security policy object.
153
+	 *
154
+	 * @var SecurityPolicy
155
+	 */
156
+	protected $securityPolicy = null;
157
+
158
+	/**
159
+	 * Stores the custom plugins callbacks.
160
+	 *
161
+	 * @see addPlugin
162
+	 * @see removePlugin
163
+	 * @var array
164
+	 */
165
+	protected $plugins = array();
166
+
167
+	/**
168
+	 * Stores the filter callbacks.
169
+	 *
170
+	 * @see addFilter
171
+	 * @see removeFilter
172
+	 * @var array
173
+	 */
174
+	protected $filters = array();
175
+
176
+	/**
177
+	 * Stores the resource types and associated
178
+	 * classes / compiler classes.
179
+	 *
180
+	 * @var array
181
+	 */
182
+	protected $resources = array(
183
+		'file'   => array(
184
+			'class'    => 'Dwoo\Template\File',
185
+			'compiler' => null,
186
+		),
187
+		'string' => array(
188
+			'class'    => 'Dwoo\Template\Str',
189
+			'compiler' => null,
190
+		),
191
+	);
192
+
193
+	/**
194
+	 * The dwoo loader object used to load plugins by this dwoo instance.
195
+	 *
196
+	 * @var ILoader
197
+	 */
198
+	protected $loader = null;
199
+
200
+	/**
201
+	 * Currently rendered template, set to null when not-rendering.
202
+	 *
203
+	 * @var ITemplate
204
+	 */
205
+	protected $template = null;
206
+
207
+	/**
208
+	 * Stores the instances of the class plugins during template runtime.
209
+	 *
210
+	 * @var array
211
+	 */
212
+	protected $runtimePlugins = array();
213
+
214
+	/**
215
+	 * Stores the returned values during template runtime.
216
+	 *
217
+	 * @var array
218
+	 */
219
+	protected $returnData = array();
220
+
221
+	/**
222
+	 * Stores the data during template runtime.
223
+	 *
224
+	 * @var array
225
+	 */
226
+	protected $data = array();
227
+
228
+	/**
229
+	 * Stores the current scope during template runtime.
230
+	 * this should ideally not be accessed directly from outside template code
231
+	 *
232
+	 * @var mixed
233
+	 */
234
+	public $scope;
235
+
236
+	/**
237
+	 * Stores the scope tree during template runtime.
238
+	 *
239
+	 * @var array
240
+	 */
241
+	protected $scopeTree = array();
242
+
243
+	/**
244
+	 * Stores the block plugins stack during template runtime.
245
+	 *
246
+	 * @var array
247
+	 */
248
+	protected $stack = array();
249
+
250
+	/**
251
+	 * Stores the current block plugin at the top of the stack during template runtime.
252
+	 *
253
+	 * @var BlockPlugin
254
+	 */
255
+	protected $curBlock;
256
+
257
+	/**
258
+	 * Stores the output buffer during template runtime.
259
+	 *
260
+	 * @var string
261
+	 */
262
+	protected $buffer;
263
+
264
+	/**
265
+	 * Stores plugin proxy.
266
+	 *
267
+	 * @var IPluginProxy
268
+	 */
269
+	protected $pluginProxy;
270
+
271
+	/**
272
+	 * Constructor, sets the cache and compile dir to the default values if not provided.
273
+	 *
274
+	 * @param string $compileDir path to the compiled directory, defaults to lib/compiled
275
+	 * @param string $cacheDir   path to the cache directory, defaults to lib/cache
276
+	 */
277
+	public function __construct($compileDir = null, $cacheDir = null)
278
+	{
279
+		if ($compileDir !== null) {
280
+			$this->setCompileDir($compileDir);
281
+		}
282
+		if ($cacheDir !== null) {
283
+			$this->setCacheDir($cacheDir);
284
+		}
285
+		$this->initGlobals();
286
+	}
287
+
288
+	/**
289
+	 * Resets some runtime variables to allow a cloned object to be used to render sub-templates.
290
+	 *
291
+	 * @return void
292
+	 */
293
+	public function __clone()
294
+	{
295
+		$this->template = null;
296
+		unset($this->data);
297
+		unset($this->returnData);
298
+	}
299
+
300
+	/**
301
+	 * Returns the given template rendered using the provided data and optional compiler.
302
+	 *
303
+	 * @param mixed     $_tpl      template, can either be a ITemplate object (i.e. TemplateFile), a
304
+	 *                             valid path to a template, or a template as a string it is recommended to
305
+	 *                             provide a ITemplate as it will probably make things faster, especially if
306
+	 *                             you render a template multiple times
307
+	 * @param mixed     $data      the data to use, can either be a IDataProvider object (i.e. Data) or
308
+	 *                             an associative array. if you're rendering the template from cache, it can be
309
+	 *                             left null
310
+	 * @param ICompiler $_compiler the compiler that must be used to compile the template, if left empty a default
311
+	 *                             Compiler will be used
312
+	 *
313
+	 * @return string|void or the template output if $output is false
314
+	 * @throws Exception
315
+	 */
316
+	public function get($_tpl, $data = array(), $_compiler = null)
317
+	{
318
+		// a render call came from within a template, so we need a new dwoo instance in order to avoid breaking this one
319
+		if ($this->template instanceof ITemplate) {
320
+			$clone = clone $this;
321
+
322
+			return $clone->get($_tpl, $data, $_compiler);
323
+		}
324
+
325
+		// auto-create template if required
326
+		if ($_tpl instanceof ITemplate) {
327
+			// valid, skip
328
+		} elseif (is_string($_tpl)) {
329
+			$_tpl = new TemplateFile($_tpl);
330
+			$_tpl->setIncludePath($this->getTemplateDir());
331
+		} else {
332
+			throw new Exception('Dwoo->get\'s first argument must be a ITemplate (i.e. TemplateFile) or a valid path to a template file', E_USER_NOTICE);
333
+		}
334
+
335
+		// save the current template, enters render mode at the same time
336
+		// if another rendering is requested it will be proxied to a new Core(instance
337
+		$this->template = $_tpl;
338
+
339
+		// load data
340
+		if ($data instanceof IDataProvider) {
341
+			$this->data = $data->getData();
342
+		} elseif (is_array($data)) {
343
+			$this->data = $data;
344
+		} elseif ($data instanceof ArrayAccess) {
345
+			$this->data = $data;
346
+		} else {
347
+			throw new Exception('Dwoo->get/Dwoo->output\'s data argument must be a IDataProvider object (i.e. Data) or an associative array', E_USER_NOTICE);
348
+		}
349
+
350
+		$this->addGlobal('template', $_tpl->getName());
351
+		$this->initRuntimeVars($_tpl);
352
+
353
+		// try to get cached template
354
+		$file        = $_tpl->getCachedTemplate($this);
355
+		$doCache     = $file === true;
356
+		$cacheLoaded = is_string($file);
357
+
358
+		if ($cacheLoaded === true) {
359
+			// cache is present, run it
360
+			ob_start();
361
+			include $file;
362
+			$this->template = null;
363
+
364
+			return ob_get_clean();
365
+		} else {
366
+			$dynamicId = uniqid();
367
+
368
+			// render template
369
+			$compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler);
370
+			$out              = include $compiledTemplate;
371
+
372
+			// template returned false so it needs to be recompiled
373
+			if ($out === false) {
374
+				$_tpl->forceCompilation();
375
+				$compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler);
376
+				$out              = include $compiledTemplate;
377
+			}
378
+
379
+			if ($doCache === true) {
380
+				$out = preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '<?php /*' . $dynamicId . '*/ echo \'$1\'; ?>', $out);
381
+				if (!class_exists(self::NAMESPACE_PLUGINS_BLOCKS . 'PluginDynamic')) {
382
+					$this->getLoader()->loadPlugin('PluginDynamic');
383
+				}
384
+				$out = PluginDynamic::unescape($out, $dynamicId, $compiledTemplate);
385
+			}
386
+
387
+			// process filters
388
+			foreach ($this->filters as $filter) {
389
+				if (is_array($filter) && $filter[0] instanceof Filter) {
390
+					$out = call_user_func($filter, $out);
391
+				} else {
392
+					$out = call_user_func($filter, $this, $out);
393
+				}
394
+			}
395
+
396
+			if ($doCache === true) {
397
+				// building cache
398
+				$file = $_tpl->cache($this, $out);
399
+
400
+				// run it from the cache to be sure dynamics are rendered
401
+				ob_start();
402
+				include $file;
403
+				// exit render mode
404
+				$this->template = null;
405
+
406
+				return ob_get_clean();
407
+			} else {
408
+				// no need to build cache
409
+				// exit render mode
410
+				$this->template = null;
411
+
412
+				return $out;
413
+			}
414
+		}
415
+	}
416
+
417
+	/**
418
+	 * Registers a Global.
419
+	 * New globals can be added before compiling or rendering a template
420
+	 * but after, you can only update existing globals.
421
+	 *
422
+	 * @param string $name
423
+	 * @param mixed  $value
424
+	 *
425
+	 * @return $this
426
+	 * @throws Exception
427
+	 */
428
+	public function addGlobal($name, $value)
429
+	{
430
+		if (null === $this->globals) {
431
+			$this->initGlobals();
432
+		}
433
+
434
+		$this->globals[$name] = $value;
435
+
436
+		return $this;
437
+	}
438
+
439
+	/**
440
+	 * Gets the registered Globals.
441
+	 *
442
+	 * @return array
443
+	 */
444
+	public function getGlobals()
445
+	{
446
+		return $this->globals;
447
+	}
448
+
449
+	/**
450
+	 * Re-initializes the globals array before each template run.
451
+	 * this method is only callede once when the Dwoo object is created
452
+	 *
453
+	 * @return void
454
+	 */
455
+	protected function initGlobals()
456
+	{
457
+		$this->globals = array(
458
+			'version' => self::VERSION,
459
+			'ad'      => '<a href="http://dwoo.org/">Powered by Dwoo</a>',
460
+			'now'     => $_SERVER['REQUEST_TIME'],
461
+			'charset' => $this->getCharset(),
462
+		);
463
+	}
464
+
465
+	/**
466
+	 * Re-initializes the runtime variables before each template run.
467
+	 * override this method to inject data in the globals array if needed, this
468
+	 * method is called before each template execution
469
+	 *
470
+	 * @param ITemplate $tpl the template that is going to be rendered
471
+	 *
472
+	 * @return void
473
+	 */
474
+	protected function initRuntimeVars(ITemplate $tpl)
475
+	{
476
+		$this->runtimePlugins = array();
477
+		$this->scope          = &$this->data;
478
+		$this->scopeTree      = array();
479
+		$this->stack          = array();
480
+		$this->curBlock       = null;
481
+		$this->buffer         = '';
482
+		$this->returnData     = array();
483
+	}
484
+
485
+	/**
486
+	 * Adds a custom plugin that is not in one of the plugin directories.
487
+	 *
488
+	 * @param string   $name       the plugin name to be used in the templates
489
+	 * @param callback $callback   the plugin callback, either a function name,
490
+	 *                             a class name or an array containing an object
491
+	 *                             or class name and a method name
492
+	 * @param bool     $compilable if set to true, the plugin is assumed to be compilable
493
+	 *
494
+	 * @return void
495
+	 * @throws Exception
496
+	 */
497
+	public function addPlugin($name, $callback, $compilable = false)
498
+	{
499
+		$compilable = $compilable ? self::COMPILABLE_PLUGIN : 0;
500
+		if (is_array($callback)) {
501
+			if (is_subclass_of(is_object($callback[0]) ? get_class($callback[0]) : $callback[0], 'Dwoo\Block\Plugin')) {
502
+				$this->plugins[$name] = array(
503
+					'type'     => self::BLOCK_PLUGIN | $compilable,
504
+					'callback' => $callback,
505
+					'class'    => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0])
506
+				);
507
+			} else {
508
+				$this->plugins[$name] = array(
509
+					'type'     => self::CLASS_PLUGIN | $compilable,
510
+					'callback' => $callback,
511
+					'class'    => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0]),
512
+					'function' => $callback[1]
513
+				);
514
+			}
515
+		} elseif (is_string($callback)) {
516
+			if (class_exists($callback)) {
517
+				if (is_subclass_of($callback, 'Dwoo\Block\Plugin')) {
518
+					$this->plugins[$name] = array(
519
+						'type'     => self::BLOCK_PLUGIN | $compilable,
520
+						'callback' => $callback,
521
+						'class'    => $callback
522
+					);
523
+				} else {
524
+					$this->plugins[$name] = array(
525
+						'type'     => self::CLASS_PLUGIN | $compilable,
526
+						'callback' => $callback,
527
+						'class'    => $callback,
528
+						'function' => ($compilable ? 'compile' : 'process')
529
+					);
530
+				}
531
+			} elseif (function_exists($callback)) {
532
+				$this->plugins[$name] = array(
533
+					'type'     => self::FUNC_PLUGIN | $compilable,
534
+					'callback' => $callback
535
+				);
536
+			} else {
537
+				throw new Exception(
538
+					'Callback could not be processed correctly, please check that the function/class 
539 539
                 you used exists'
540
-                );
541
-            }
542
-        } elseif ($callback instanceof Closure) {
543
-            $this->plugins[$name] = array(
544
-                'type'     => self::FUNC_PLUGIN | $compilable,
545
-                'callback' => $callback
546
-            );
547
-        } else {
548
-            throw new Exception(
549
-                'Callback could not be processed correctly, please check that the function/class you 
540
+				);
541
+			}
542
+		} elseif ($callback instanceof Closure) {
543
+			$this->plugins[$name] = array(
544
+				'type'     => self::FUNC_PLUGIN | $compilable,
545
+				'callback' => $callback
546
+			);
547
+		} else {
548
+			throw new Exception(
549
+				'Callback could not be processed correctly, please check that the function/class you 
550 550
             used exists'
551
-            );
552
-        }
553
-    }
554
-
555
-    /**
556
-     * Removes a custom plugin.
557
-     *
558
-     * @param string $name the plugin name
559
-     *
560
-     * @return void
561
-     */
562
-    public function removePlugin($name)
563
-    {
564
-        if (isset($this->plugins[$name])) {
565
-            unset($this->plugins[$name]);
566
-        }
567
-    }
568
-
569
-    /**
570
-     * Adds a filter to this Dwoo instance, it will be used to filter the output of all the templates rendered by this
571
-     * instance.
572
-     *
573
-     * @param mixed $callback a callback or a filter name if it is autoloaded from a plugin directory
574
-     * @param bool  $autoload if true, the first parameter must be a filter name from one of the plugin directories
575
-     *
576
-     * @return void
577
-     * @throws Exception
578
-     */
579
-    public function addFilter($callback, $autoload = false)
580
-    {
581
-        if ($autoload) {
582
-            $class = self::NAMESPACE_PLUGINS_FILTERS . self::toCamelCase($callback);
583
-            if (!class_exists($class) && !function_exists($class)) {
584
-                try {
585
-                    $this->getLoader()->loadPlugin($callback);
586
-                }
587
-                catch (Exception $e) {
588
-                    if (strstr($callback, self::NAMESPACE_PLUGINS_FILTERS)) {
589
-                        throw new Exception(
590
-                            'Wrong filter name : ' . $callback . ', the "Filter" prefix should 
551
+			);
552
+		}
553
+	}
554
+
555
+	/**
556
+	 * Removes a custom plugin.
557
+	 *
558
+	 * @param string $name the plugin name
559
+	 *
560
+	 * @return void
561
+	 */
562
+	public function removePlugin($name)
563
+	{
564
+		if (isset($this->plugins[$name])) {
565
+			unset($this->plugins[$name]);
566
+		}
567
+	}
568
+
569
+	/**
570
+	 * Adds a filter to this Dwoo instance, it will be used to filter the output of all the templates rendered by this
571
+	 * instance.
572
+	 *
573
+	 * @param mixed $callback a callback or a filter name if it is autoloaded from a plugin directory
574
+	 * @param bool  $autoload if true, the first parameter must be a filter name from one of the plugin directories
575
+	 *
576
+	 * @return void
577
+	 * @throws Exception
578
+	 */
579
+	public function addFilter($callback, $autoload = false)
580
+	{
581
+		if ($autoload) {
582
+			$class = self::NAMESPACE_PLUGINS_FILTERS . self::toCamelCase($callback);
583
+			if (!class_exists($class) && !function_exists($class)) {
584
+				try {
585
+					$this->getLoader()->loadPlugin($callback);
586
+				}
587
+				catch (Exception $e) {
588
+					if (strstr($callback, self::NAMESPACE_PLUGINS_FILTERS)) {
589
+						throw new Exception(
590
+							'Wrong filter name : ' . $callback . ', the "Filter" prefix should 
591 591
                         not be used, please only use "' . str_replace('Filter', '', $callback) . '"'
592
-                        );
593
-                    } else {
594
-                        throw new Exception(
595
-                            'Wrong filter name : ' . $callback . ', when using autoload the filter must
592
+						);
593
+					} else {
594
+						throw new Exception(
595
+							'Wrong filter name : ' . $callback . ', when using autoload the filter must
596 596
                          be in one of your plugin dir as "name.php" containig a class or function named
597 597
                          "Filter<name>"'
598
-                        );
599
-                    }
600
-                }
601
-            }
602
-
603
-            if (class_exists($class)) {
604
-                $callback = array(new $class($this), 'process');
605
-            } elseif (function_exists($class)) {
606
-                $callback = $class;
607
-            } else {
608
-                throw new Exception(
609
-                    'Wrong filter name : ' . $callback . ', when using autoload the filter must be in
598
+						);
599
+					}
600
+				}
601
+			}
602
+
603
+			if (class_exists($class)) {
604
+				$callback = array(new $class($this), 'process');
605
+			} elseif (function_exists($class)) {
606
+				$callback = $class;
607
+			} else {
608
+				throw new Exception(
609
+					'Wrong filter name : ' . $callback . ', when using autoload the filter must be in
610 610
                 one of your plugin dir as "name.php" containig a class or function named "Filter<name>"'
611
-                );
612
-            }
613
-
614
-            $this->filters[] = $callback;
615
-        } else {
616
-            $this->filters[] = $callback;
617
-        }
618
-    }
619
-
620
-    /**
621
-     * Removes a filter.
622
-     *
623
-     * @param mixed $callback callback or filter name if it was autoloaded
624
-     *
625
-     * @return void
626
-     */
627
-    public function removeFilter($callback)
628
-    {
629
-        if (($index = array_search(self::NAMESPACE_PLUGINS_FILTERS. 'Filter' . self::toCamelCase($callback), $this->filters,
630
-                true)) !==
631
-            false) {
632
-            unset($this->filters[$index]);
633
-        } elseif (($index = array_search($callback, $this->filters, true)) !== false) {
634
-            unset($this->filters[$index]);
635
-        } else {
636
-            $class = self::NAMESPACE_PLUGINS_FILTERS . 'Filter' . $callback;
637
-            foreach ($this->filters as $index => $filter) {
638
-                if (is_array($filter) && $filter[0] instanceof $class) {
639
-                    unset($this->filters[$index]);
640
-                    break;
641
-                }
642
-            }
643
-        }
644
-    }
645
-
646
-    /**
647
-     * Adds a resource or overrides a default one.
648
-     *
649
-     * @param string   $name            the resource name
650
-     * @param string   $class           the resource class (which must implement ITemplate)
651
-     * @param callback $compilerFactory the compiler factory callback, a function that must return a compiler instance
652
-     *                                  used to compile this resource, if none is provided. by default it will produce
653
-     *                                  a Compiler object
654
-     *
655
-     * @return void
656
-     * @throws Exception
657
-     */
658
-    public function addResource($name, $class, $compilerFactory = null)
659
-    {
660
-        if (strlen($name) < 2) {
661
-            throw new Exception('Resource names must be at least two-character long to avoid conflicts with Windows paths');
662
-        }
663
-
664
-        if (!class_exists($class)) {
665
-            throw new Exception(sprintf('Resource class %s does not exist', $class));
666
-        }
667
-
668
-        $interfaces = class_implements($class);
669
-        if (in_array('Dwoo\ITemplate', $interfaces) === false) {
670
-            throw new Exception('Resource class must implement ITemplate');
671
-        }
672
-
673
-        $this->resources[$name] = array(
674
-            'class'    => $class,
675
-            'compiler' => $compilerFactory
676
-        );
677
-    }
678
-
679
-    /**
680
-     * Removes a custom resource.
681
-     *
682
-     * @param string $name the resource name
683
-     *
684
-     * @return void
685
-     */
686
-    public function removeResource($name)
687
-    {
688
-        unset($this->resources[$name]);
689
-        if ($name === 'file') {
690
-            $this->resources['file'] = array(
691
-                'class'    => 'Dwoo\Template\File',
692
-                'compiler' => null
693
-            );
694
-        }
695
-    }
696
-
697
-    /**
698
-     * Sets the loader object to use to load plugins.
699
-     *
700
-     * @param ILoader $loader loader
701
-     *
702
-     * @return void
703
-     */
704
-    public function setLoader(ILoader $loader)
705
-    {
706
-        $this->loader = $loader;
707
-    }
708
-
709
-    /**
710
-     * Returns the current loader object or a default one if none is currently found.
711
-     *
712
-     * @return ILoader|Loader
713
-     */
714
-    public function getLoader()
715
-    {
716
-        if ($this->loader === null) {
717
-            $this->loader = new Loader($this->getCompileDir());
718
-        }
719
-
720
-        return $this->loader;
721
-    }
722
-
723
-    /**
724
-     * Returns the custom plugins loaded.
725
-     * Used by the ITemplate classes to pass the custom plugins to their ICompiler instance.
726
-     *
727
-     * @return array
728
-     */
729
-    public function getCustomPlugins()
730
-    {
731
-        return $this->plugins;
732
-    }
733
-
734
-    /**
735
-     * Return a specified custom plugin loaded by his name.
736
-     * Used by the compiler, for executing a Closure.
737
-     *
738
-     * @param string $name
739
-     *
740
-     * @return mixed|null
741
-     */
742
-    public function getCustomPlugin($name)
743
-    {
744
-        if (isset($this->plugins[$name])) {
745
-            return $this->plugins[$name]['callback'];
746
-        }
747
-
748
-        return null;
749
-    }
750
-
751
-    /**
752
-     * Returns the cache directory with a trailing DIRECTORY_SEPARATOR.
753
-     *
754
-     * @return string
755
-     */
756
-    public function getCacheDir()
757
-    {
758
-        if ($this->cacheDir === null) {
759
-            $this->setCacheDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR);
760
-        }
761
-
762
-        return $this->cacheDir;
763
-    }
764
-
765
-    /**
766
-     * Sets the cache directory and automatically appends a DIRECTORY_SEPARATOR.
767
-     *
768
-     * @param string $dir the cache directory
769
-     *
770
-     * @return void
771
-     * @throws Exception
772
-     */
773
-    public function setCacheDir($dir)
774
-    {
775
-        $this->cacheDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
776
-        if (is_writable($this->cacheDir) === false) {
777
-            throw new Exception('The cache directory must be writable, chmod "' . $this->cacheDir . '" to make it writable');
778
-        }
779
-    }
780
-
781
-    /**
782
-     * Returns the compile directory with a trailing DIRECTORY_SEPARATOR.
783
-     *
784
-     * @return string
785
-     */
786
-    public function getCompileDir()
787
-    {
788
-        if ($this->compileDir === null) {
789
-            $this->setCompileDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'compiled' . DIRECTORY_SEPARATOR);
790
-        }
791
-
792
-        return $this->compileDir;
793
-    }
794
-
795
-    /**
796
-     * Sets the compile directory and automatically appends a DIRECTORY_SEPARATOR.
797
-     *
798
-     * @param string $dir the compile directory
799
-     *
800
-     * @return void
801
-     * @throws Exception
802
-     */
803
-    public function setCompileDir($dir)
804
-    {
805
-        $this->compileDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
806
-        if (is_writable($this->compileDir) === false) {
807
-            throw new Exception('The compile directory must be writable, chmod "' . $this->compileDir . '" to make it writable');
808
-        }
809
-    }
810
-
811
-    /**
812
-     * Returns an array of the template directory with a trailing DIRECTORY_SEPARATOR
813
-     *
814
-     * @return array
815
-     */
816
-    public function getTemplateDir()
817
-    {
818
-        return $this->templateDir;
819
-    }
820
-
821
-    /**
822
-     * sets the template directory and automatically appends a DIRECTORY_SEPARATOR
823
-     * template directory is stored in an array
824
-     *
825
-     * @param string $dir
826
-     *
827
-     * @throws Exception
828
-     */
829
-    public function setTemplateDir($dir)
830
-    {
831
-        $tmpDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
832
-        if (is_dir($tmpDir) === false) {
833
-            throw new Exception('The template directory: "' . $tmpDir . '" does not exists, create the directory or specify an other location !');
834
-        }
835
-        $this->templateDir[] = $tmpDir;
836
-    }
837
-
838
-    /**
839
-     * Returns the default cache time that is used with templates that do not have a cache time set.
840
-     *
841
-     * @return int the duration in seconds
842
-     */
843
-    public function getCacheTime()
844
-    {
845
-        return $this->cacheTime;
846
-    }
847
-
848
-    /**
849
-     * Sets the default cache time to use with templates that do not have a cache time set.
850
-     *
851
-     * @param int $seconds the duration in seconds
852
-     *
853
-     * @return void
854
-     */
855
-    public function setCacheTime($seconds)
856
-    {
857
-        $this->cacheTime = (int)$seconds;
858
-    }
859
-
860
-    /**
861
-     * Returns the character set used by the string manipulation plugins.
862
-     * the charset is automatically lowercased
863
-     *
864
-     * @return string
865
-     */
866
-    public function getCharset()
867
-    {
868
-        return $this->charset;
869
-    }
870
-
871
-    /**
872
-     * Sets the character set used by the string manipulation plugins.
873
-     * the charset will be automatically lowercased
874
-     *
875
-     * @param string $charset the character set
876
-     *
877
-     * @return void
878
-     */
879
-    public function setCharset($charset)
880
-    {
881
-        $this->charset = strtolower((string)$charset);
882
-    }
883
-
884
-    /**
885
-     * Returns the current template being rendered, when applicable, or null.
886
-     *
887
-     * @return ITemplate|null
888
-     */
889
-    public function getTemplate()
890
-    {
891
-        return $this->template;
892
-    }
893
-
894
-    /**
895
-     * Sets the current template being rendered.
896
-     *
897
-     * @param ITemplate $tpl template object
898
-     *
899
-     * @return void
900
-     */
901
-    public function setTemplate(ITemplate $tpl)
902
-    {
903
-        $this->template = $tpl;
904
-    }
905
-
906
-    /**
907
-     * Sets the default compiler factory function for the given resource name.
908
-     * a compiler factory must return a ICompiler object pre-configured to fit your needs
909
-     *
910
-     * @param string   $resourceName    the resource name (i.e. file, string)
911
-     * @param callback $compilerFactory the compiler factory callback
912
-     *
913
-     * @return void
914
-     */
915
-    public function setDefaultCompilerFactory($resourceName, $compilerFactory)
916
-    {
917
-        $this->resources[$resourceName]['compiler'] = $compilerFactory;
918
-    }
919
-
920
-    /**
921
-     * Returns the default compiler factory function for the given resource name.
922
-     *
923
-     * @param string $resourceName the resource name
924
-     *
925
-     * @return callback the compiler factory callback
926
-     */
927
-    public function getDefaultCompilerFactory($resourceName)
928
-    {
929
-        return $this->resources[$resourceName]['compiler'];
930
-    }
931
-
932
-    /**
933
-     * Sets the security policy object to enforce some php security settings.
934
-     * use this if untrusted persons can modify templates
935
-     *
936
-     * @param SecurityPolicy $policy the security policy object
937
-     *
938
-     * @return void
939
-     */
940
-    public function setSecurityPolicy(SecurityPolicy $policy = null)
941
-    {
942
-        $this->securityPolicy = $policy;
943
-    }
944
-
945
-    /**
946
-     * Returns the current security policy object or null by default.
947
-     *
948
-     * @return SecurityPolicy|null the security policy object if any
949
-     */
950
-    public function getSecurityPolicy()
951
-    {
952
-        return $this->securityPolicy;
953
-    }
954
-
955
-    /**
956
-     * Sets the object that must be used as a plugin proxy when plugin can't be found
957
-     * by dwoo's loader.
958
-     *
959
-     * @param IPluginProxy $pluginProxy the proxy object
960
-     *
961
-     * @return void
962
-     */
963
-    public function setPluginProxy(IPluginProxy $pluginProxy)
964
-    {
965
-        $this->pluginProxy = $pluginProxy;
966
-    }
967
-
968
-    /**
969
-     * Returns the current plugin proxy object or null by default.
970
-     *
971
-     * @return IPluginProxy
972
-     */
973
-    public function getPluginProxy()
974
-    {
975
-        return $this->pluginProxy;
976
-    }
977
-
978
-    /**
979
-     * Checks whether the given template is cached or not.
980
-     *
981
-     * @param ITemplate $tpl the template object
982
-     *
983
-     * @return bool
984
-     */
985
-    public function isCached(ITemplate $tpl)
986
-    {
987
-        return is_string($tpl->getCachedTemplate($this));
988
-    }
989
-
990
-    /**
991
-     * Clear templates inside the compiled directory.
992
-     *
993
-     * @return int
994
-     */
995
-    public function clearCompiled()
996
-    {
997
-        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCompileDir()), \RecursiveIteratorIterator::SELF_FIRST);
998
-        $count    = 0;
999
-        foreach ($iterator as $file) {
1000
-            if ($file->isFile()) {
1001
-                $count += unlink($file->__toString()) ? 1 : 0;
1002
-            }
1003
-        }
1004
-
1005
-        return $count;
1006
-    }
1007
-
1008
-    /**
1009
-     * Clears the cached templates if they are older than the given time.
1010
-     *
1011
-     * @param int $olderThan minimum time (in seconds) required for a cached template to be cleared
1012
-     *
1013
-     * @return int the amount of templates cleared
1014
-     */
1015
-    public function clearCache($olderThan = - 1)
1016
-    {
1017
-        $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCacheDir()), \RecursiveIteratorIterator::SELF_FIRST);
1018
-        $expired  = time() - $olderThan;
1019
-        $count    = 0;
1020
-        foreach ($iterator as $file) {
1021
-            if ($file->isFile() && $file->getCTime() < $expired) {
1022
-                $count += unlink((string)$file) ? 1 : 0;
1023
-            }
1024
-        }
1025
-
1026
-        return $count;
1027
-    }
1028
-
1029
-    /**
1030
-     * Fetches a template object of the given resource.
1031
-     *
1032
-     * @param string    $resourceName   the resource name (i.e. file, string)
1033
-     * @param string    $resourceId     the resource identifier (i.e. file path)
1034
-     * @param int       $cacheTime      the cache time setting for this resource
1035
-     * @param string    $cacheId        the unique cache identifier
1036
-     * @param string    $compileId      the unique compiler identifier
1037
-     * @param ITemplate $parentTemplate the parent template
1038
-     *
1039
-     * @return ITemplate
1040
-     * @throws Exception
1041
-     */
1042
-    public function templateFactory($resourceName, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, ITemplate $parentTemplate = null)
1043
-    {
1044
-        if (isset($this->resources[$resourceName])) {
1045
-            /**
1046
-             * Interface ITemplate
1047
-             *
1048
-             * @var ITemplate $class
1049
-             */
1050
-            $class = $this->resources[$resourceName]['class'];
1051
-
1052
-            return $class::templateFactory($this, $resourceId, $cacheTime, $cacheId, $compileId, $parentTemplate);
1053
-        }
1054
-
1055
-        throw new Exception('Unknown resource type : ' . $resourceName);
1056
-    }
1057
-
1058
-    /**
1059
-     * Checks if the input is an array or arrayaccess object, optionally it can also check if it's
1060
-     * empty.
1061
-     *
1062
-     * @param mixed $value        the variable to check
1063
-     * @param bool  $checkIsEmpty if true, the function will also check if the array|arrayaccess is empty,
1064
-     *                            and return true only if it's not empty
1065
-     *
1066
-     * @return int|bool true if it's an array|arrayaccess (or the item count if $checkIsEmpty is true) or false if it's
1067
-     *                  not an array|arrayaccess (or 0 if $checkIsEmpty is true)
1068
-     */
1069
-    public function isArray($value, $checkIsEmpty = false)
1070
-    {
1071
-        if (is_array($value) === true || $value instanceof ArrayAccess) {
1072
-            if ($checkIsEmpty === false) {
1073
-                return true;
1074
-            }
1075
-
1076
-            return $this->count($value);
1077
-        }
1078
-
1079
-        return false;
1080
-    }
1081
-
1082
-    /**
1083
-     * Checks if the input is an array or a traversable object, optionally it can also check if it's
1084
-     * empty.
1085
-     *
1086
-     * @param mixed $value        the variable to check
1087
-     * @param bool  $checkIsEmpty if true, the function will also check if the array|traversable is empty,
1088
-     *                            and return true only if it's not empty
1089
-     *
1090
-     * @return int|bool true if it's an array|traversable (or the item count if $checkIsEmpty is true) or false if it's
1091
-     *                  not an array|traversable (or 0 if $checkIsEmpty is true)
1092
-     */
1093
-    public function isTraversable($value, $checkIsEmpty = false)
1094
-    {
1095
-        if (is_array($value) === true) {
1096
-            if ($checkIsEmpty === false) {
1097
-                return true;
1098
-            } else {
1099
-                return count($value) > 0;
1100
-            }
1101
-        } elseif ($value instanceof Traversable) {
1102
-            if ($checkIsEmpty === false) {
1103
-                return true;
1104
-            } else {
1105
-                return $this->count($value);
1106
-            }
1107
-        }
1108
-
1109
-        return false;
1110
-    }
1111
-
1112
-    /**
1113
-     * Counts an array or arrayaccess/traversable object.
1114
-     *
1115
-     * @param mixed $value the value to count
1116
-     *
1117
-     * @return int|bool the count for arrays and objects that implement countable, true for other objects that don't,
1118
-     *                  and 0 for empty elements
1119
-     */
1120
-    public function count($value)
1121
-    {
1122
-        if (is_array($value) === true || $value instanceof Countable) {
1123
-            return count($value);
1124
-        } elseif ($value instanceof ArrayAccess) {
1125
-            if ($value->offsetExists(0)) {
1126
-                return true;
1127
-            }
1128
-        } elseif ($value instanceof Iterator) {
1129
-            $value->rewind();
1130
-            if ($value->valid()) {
1131
-                return true;
1132
-            }
1133
-        } elseif ($value instanceof Traversable) {
1134
-            foreach ($value as $dummy) {
1135
-                return true;
1136
-            }
1137
-        }
1138
-
1139
-        return 0;
1140
-    }
1141
-
1142
-    /**
1143
-     * Triggers a dwoo error.
1144
-     *
1145
-     * @param string $message the error message
1146
-     * @param int    $level   the error level, one of the PHP's E_* constants
1147
-     *
1148
-     * @return void
1149
-     */
1150
-    public function triggerError($message, $level = E_USER_NOTICE)
1151
-    {
1152
-        if (!($tplIdentifier = $this->template->getResourceIdentifier())) {
1153
-            $tplIdentifier = $this->template->getResourceName();
1154
-        }
1155
-        trigger_error('Dwoo error (in ' . $tplIdentifier . ') : ' . $message, $level);
1156
-    }
1157
-
1158
-    /**
1159
-     * Adds a block to the block stack.
1160
-     *
1161
-     * @param string $blockName the block name (without `Plugin` prefix)
1162
-     * @param array  $args      the arguments to be passed to the block's init() function
1163
-     *
1164
-     * @return BlockPlugin the newly created block
1165
-     */
1166
-    public function addStack($blockName, array $args = array())
1167
-    {
1168
-        if (isset($this->plugins[$blockName])) {
1169
-            $class = $this->plugins[$blockName]['class'];
1170
-        } else {
1171
-            $class = self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . self::toCamelCase($blockName);
1172
-        }
1173
-
1174
-        if ($this->curBlock !== null) {
1175
-            $this->curBlock->buffer(ob_get_contents());
1176
-            ob_clean();
1177
-        } else {
1178
-            $this->buffer .= ob_get_contents();
1179
-            ob_clean();
1180
-        }
1181
-
1182
-        $block = new $class($this);
1183
-
1184
-        call_user_func_array(array($block, 'init'), $args);
1185
-
1186
-        $this->stack[] = $this->curBlock = $block;
1187
-
1188
-        return $block;
1189
-    }
1190
-
1191
-    /**
1192
-     * Removes the plugin at the top of the block stack.
1193
-     * Calls the block buffer() function, followed by a call to end() and finally a call to process()
1194
-     *
1195
-     * @return void
1196
-     */
1197
-    public function delStack()
1198
-    {
1199
-        $args = func_get_args();
1200
-
1201
-        $this->curBlock->buffer(ob_get_contents());
1202
-        ob_clean();
1203
-
1204
-        call_user_func_array(array($this->curBlock, 'end'), $args);
1205
-
1206
-        $tmp = array_pop($this->stack);
1207
-
1208
-        if (count($this->stack) > 0) {
1209
-            $this->curBlock = end($this->stack);
1210
-            $this->curBlock->buffer($tmp->process());
1211
-        } else {
1212
-            if ($this->buffer !== '') {
1213
-                echo $this->buffer;
1214
-                $this->buffer = '';
1215
-            }
1216
-            $this->curBlock = null;
1217
-            echo $tmp->process();
1218
-        }
1219
-
1220
-        unset($tmp);
1221
-    }
1222
-
1223
-    /**
1224
-     * Returns the parent block of the given block.
1225
-     *
1226
-     * @param BlockPlugin $block the block class plugin
1227
-     *
1228
-     * @return BlockPlugin|false if the given block isn't in the stack
1229
-     */
1230
-    public function getParentBlock(BlockPlugin $block)
1231
-    {
1232
-        $index = array_search($block, $this->stack, true);
1233
-        if ($index !== false && $index > 0) {
1234
-            return $this->stack[$index - 1];
1235
-        }
1236
-
1237
-        return false;
1238
-    }
1239
-
1240
-    /**
1241
-     * Finds the closest block of the given type, starting at the top of the stack.
1242
-     *
1243
-     * @param string $type the type of plugin you want to find
1244
-     *
1245
-     * @return BlockPlugin|false if no plugin of such type is in the stack
1246
-     */
1247
-    public function findBlock($type)
1248
-    {
1249
-        if (isset($this->plugins[$type])) {
1250
-            $type = $this->plugins[$type]['class'];
1251
-        } else {
1252
-            $type = self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin_' . str_replace(self::NAMESPACE_PLUGINS_BLOCKS.'Plugin',
1253
-                    '', $type);
1254
-        }
1255
-
1256
-        $keys = array_keys($this->stack);
1257
-        while (($key = array_pop($keys)) !== false) {
1258
-            if ($this->stack[$key] instanceof $type) {
1259
-                return $this->stack[$key];
1260
-            }
1261
-        }
1262
-
1263
-        return false;
1264
-    }
1265
-
1266
-    /**
1267
-     * Returns a Plugin of the given class.
1268
-     * this is so a single instance of every class plugin is created at each template run,
1269
-     * allowing class plugins to have "per-template-run" static variables
1270
-     *
1271
-     * @param string $class the class name
1272
-     *
1273
-     * @return mixed an object of the given class
1274
-     */
1275
-    public function getObjectPlugin($class)
1276
-    {
1277
-        if (isset($this->runtimePlugins[$class])) {
1278
-            return $this->runtimePlugins[$class];
1279
-        }
1280
-
1281
-        return $this->runtimePlugins[$class] = new $class($this);
1282
-    }
1283
-
1284
-    /**
1285
-     * Calls the process() method of the given class-plugin name.
1286
-     *
1287
-     * @param string $plugName the class plugin name (without `Plugin` prefix)
1288
-     * @param array  $params   an array of parameters to send to the process() method
1289
-     *
1290
-     * @return string the process() return value
1291
-     */
1292
-    public function classCall($plugName, array $params = array())
1293
-    {
1294
-        $class  = self::toCamelCase($plugName);
1295
-        $plugin = $this->getObjectPlugin($class);
1296
-
1297
-        return call_user_func_array(array($plugin, 'process'), $params);
1298
-    }
1299
-
1300
-    /**
1301
-     * Calls a php function.
1302
-     *
1303
-     * @param string $callback the function to call
1304
-     * @param array  $params   an array of parameters to send to the function
1305
-     *
1306
-     * @return mixed the return value of the called function
1307
-     */
1308
-    public function arrayMap($callback, array $params)
1309
-    {
1310
-        if ($params[0] === $this) {
1311
-            $addThis = true;
1312
-            array_shift($params);
1313
-        }
1314
-        if ((is_array($params[0]) || ($params[0] instanceof Iterator && $params[0] instanceof ArrayAccess))) {
1315
-            if (empty($params[0])) {
1316
-                return $params[0];
1317
-            }
1318
-
1319
-            // array map
1320
-            $out = array();
1321
-            $cnt = count($params);
1322
-
1323
-            if (isset($addThis)) {
1324
-                array_unshift($params, $this);
1325
-                $items = $params[1];
1326
-                $keys  = array_keys($items);
1327
-
1328
-                if (is_string($callback) === false) {
1329
-                    while (($i = array_shift($keys)) !== null) {
1330
-                        $out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params);
1331
-                    }
1332
-                } elseif ($cnt === 1) {
1333
-                    while (($i = array_shift($keys)) !== null) {
1334
-                        $out[] = $callback($this, $items[$i]);
1335
-                    }
1336
-                } elseif ($cnt === 2) {
1337
-                    while (($i = array_shift($keys)) !== null) {
1338
-                        $out[] = $callback($this, $items[$i], $params[2]);
1339
-                    }
1340
-                } elseif ($cnt === 3) {
1341
-                    while (($i = array_shift($keys)) !== null) {
1342
-                        $out[] = $callback($this, $items[$i], $params[2], $params[3]);
1343
-                    }
1344
-                } else {
1345
-                    while (($i = array_shift($keys)) !== null) {
1346
-                        $out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params);
1347
-                    }
1348
-                }
1349
-            } else {
1350
-                $items = $params[0];
1351
-                $keys  = array_keys($items);
1352
-
1353
-                if (is_string($callback) === false) {
1354
-                    while (($i = array_shift($keys)) !== null) {
1355
-                        $out[] = call_user_func_array($callback, array($items[$i]) + $params);
1356
-                    }
1357
-                } elseif ($cnt === 1) {
1358
-                    while (($i = array_shift($keys)) !== null) {
1359
-                        $out[] = $callback($items[$i]);
1360
-                    }
1361
-                } elseif ($cnt === 2) {
1362
-                    while (($i = array_shift($keys)) !== null) {
1363
-                        $out[] = $callback($items[$i], $params[1]);
1364
-                    }
1365
-                } elseif ($cnt === 3) {
1366
-                    while (($i = array_shift($keys)) !== null) {
1367
-                        $out[] = $callback($items[$i], $params[1], $params[2]);
1368
-                    }
1369
-                } elseif ($cnt === 4) {
1370
-                    while (($i = array_shift($keys)) !== null) {
1371
-                        $out[] = $callback($items[$i], $params[1], $params[2], $params[3]);
1372
-                    }
1373
-                } else {
1374
-                    while (($i = array_shift($keys)) !== null) {
1375
-                        $out[] = call_user_func_array($callback, array($items[$i]) + $params);
1376
-                    }
1377
-                }
1378
-            }
1379
-
1380
-            return $out;
1381
-        } else {
1382
-            return $params[0];
1383
-        }
1384
-    }
1385
-
1386
-    /**
1387
-     * Reads a variable into the given data array.
1388
-     *
1389
-     * @param string $varstr   the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
1390
-     * @param mixed  $data     the data array or object to read from
1391
-     * @param bool   $safeRead if true, the function will check whether the index exists to prevent any notices from
1392
-     *                         being output
1393
-     *
1394
-     * @return mixed
1395
-     */
1396
-    public function readVarInto($varstr, $data, $safeRead = false)
1397
-    {
1398
-        if ($data === null) {
1399
-            return null;
1400
-        }
1401
-
1402
-        if (is_array($varstr) === false) {
1403
-            preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m);
1404
-        } else {
1405
-            $m = $varstr;
1406
-        }
1407
-        unset($varstr);
1408
-
1409
-        foreach ($m[1] as $k => $sep) {
1410
-            if ($sep === '.' || $sep === '[' || $sep === '') {
1411
-                // strip enclosing quotes if present
1412
-                $m[2][$k] = preg_replace('#^(["\']?)(.*?)\1$#', '$2', $m[2][$k]);
1413
-
1414
-                if ((is_array($data) || $data instanceof ArrayAccess) && ($safeRead === false || isset($data[$m[2][$k]]))) {
1415
-                    $data = $data[$m[2][$k]];
1416
-                } else {
1417
-                    return null;
1418
-                }
1419
-            } else {
1420
-                if (is_object($data) && ($safeRead === false || isset($data->{$m[2][$k]}))) {
1421
-                    $data = $data->{$m[2][$k]};
1422
-                } else {
1423
-                    return null;
1424
-                }
1425
-            }
1426
-        }
1427
-
1428
-        return $data;
1429
-    }
1430
-
1431
-    /**
1432
-     * Reads a variable into the parent scope.
1433
-     *
1434
-     * @param int    $parentLevels the amount of parent levels to go from the current scope
1435
-     * @param string $varstr       the variable string, using dwoo variable syntax (i.e.
1436
-     *                             "var.subvar[subsubvar]->property")
1437
-     *
1438
-     * @return mixed
1439
-     */
1440
-    public function readParentVar($parentLevels, $varstr = null)
1441
-    {
1442
-        $tree = $this->scopeTree;
1443
-        $cur  = $this->data;
1444
-
1445
-        while ($parentLevels -- !== 0) {
1446
-            array_pop($tree);
1447
-        }
1448
-
1449
-        while (($i = array_shift($tree)) !== null) {
1450
-            if (is_object($cur)) {
1451
-                $cur = $cur->{$i};
1452
-            } else {
1453
-                $cur = $cur[$i];
1454
-            }
1455
-        }
1456
-
1457
-        if ($varstr !== null) {
1458
-            return $this->readVarInto($varstr, $cur);
1459
-        } else {
1460
-            return $cur;
1461
-        }
1462
-    }
1463
-
1464
-    /**
1465
-     * Reads a variable into the current scope.
1466
-     *
1467
-     * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
1468
-     *
1469
-     * @return mixed
1470
-     */
1471
-    public function readVar($varstr)
1472
-    {
1473
-        if (is_array($varstr) === true) {
1474
-            $m = $varstr;
1475
-            unset($varstr);
1476
-        } else {
1477
-            if (strstr($varstr, '.') === false && strstr($varstr, '[') === false && strstr($varstr, '->') === false) {
1478
-                if ($varstr === 'dwoo') {
1479
-                    return $this->getGlobals();
1480
-                } elseif ($varstr === '__' || $varstr === '_root') {
1481
-                    return $this->data;
1482
-                } elseif ($varstr === '_' || $varstr === '_parent') {
1483
-                    $varstr = '.' . $varstr;
1484
-                    $tree   = $this->scopeTree;
1485
-                    $cur    = $this->data;
1486
-                    array_pop($tree);
1487
-
1488
-                    while (($i = array_shift($tree)) !== null) {
1489
-                        if (is_object($cur)) {
1490
-                            $cur = $cur->{$i};
1491
-                        } else {
1492
-                            $cur = $cur[$i];
1493
-                        }
1494
-                    }
1495
-
1496
-                    return $cur;
1497
-                }
1498
-
1499
-                $cur = $this->scope;
1500
-
1501
-                if (isset($cur[$varstr])) {
1502
-                    return $cur[$varstr];
1503
-                } else {
1504
-                    return null;
1505
-                }
1506
-            }
1507
-
1508
-            if (substr($varstr, 0, 1) === '.') {
1509
-                $varstr = 'dwoo' . $varstr;
1510
-            }
1511
-
1512
-            preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m);
1513
-        }
1514
-
1515
-        $i = $m[2][0];
1516
-        if ($i === 'dwoo') {
1517
-            $cur = $this->getGlobals();
1518
-            array_shift($m[2]);
1519
-            array_shift($m[1]);
1520
-            switch ($m[2][0]) {
1521
-            case 'get':
1522
-                $cur = $_GET;
1523
-                break;
1524
-            case 'post':
1525
-                $cur = $_POST;
1526
-                break;
1527
-            case 'session':
1528
-                $cur = $_SESSION;
1529
-                break;
1530
-            case 'cookies':
1531
-            case 'cookie':
1532
-                $cur = $_COOKIE;
1533
-                break;
1534
-            case 'server':
1535
-                $cur = $_SERVER;
1536
-                break;
1537
-            case 'env':
1538
-                $cur = $_ENV;
1539
-                break;
1540
-            case 'request':
1541
-                $cur = $_REQUEST;
1542
-                break;
1543
-            case 'const':
1544
-                array_shift($m[2]);
1545
-                if (defined($m[2][0])) {
1546
-                    return constant($m[2][0]);
1547
-                } else {
1548
-                    return null;
1549
-                }
1550
-            }
1551
-            if ($cur !== $this->getGlobals()) {
1552
-                array_shift($m[2]);
1553
-                array_shift($m[1]);
1554
-            }
1555
-        } elseif ($i === '__' || $i === '_root') {
1556
-            $cur = $this->data;
1557
-            array_shift($m[2]);
1558
-            array_shift($m[1]);
1559
-        } elseif ($i === '_' || $i === '_parent') {
1560
-            $tree = $this->scopeTree;
1561
-            $cur  = $this->data;
1562
-
1563
-            while (true) {
1564
-                array_pop($tree);
1565
-                array_shift($m[2]);
1566
-                array_shift($m[1]);
1567
-                if (current($m[2]) === '_' || current($m[2]) === '_parent') {
1568
-                    continue;
1569
-                }
1570
-
1571
-                while (($i = array_shift($tree)) !== null) {
1572
-                    if (is_object($cur)) {
1573
-                        $cur = $cur->{$i};
1574
-                    } else {
1575
-                        $cur = $cur[$i];
1576
-                    }
1577
-                }
1578
-                break;
1579
-            }
1580
-        } else {
1581
-            $cur = $this->scope;
1582
-        }
1583
-
1584
-        foreach ($m[1] as $k => $sep) {
1585
-            if ($sep === '.' || $sep === '[' || $sep === '') {
1586
-                if ((is_array($cur) || $cur instanceof ArrayAccess) && isset($cur[$m[2][$k]])) {
1587
-                    $cur = $cur[$m[2][$k]];
1588
-                } else {
1589
-                    return null;
1590
-                }
1591
-            } elseif ($sep === '->') {
1592
-                if (is_object($cur)) {
1593
-                    $cur = $cur->{$m[2][$k]};
1594
-                } else {
1595
-                    return null;
1596
-                }
1597
-            } else {
1598
-                return null;
1599
-            }
1600
-        }
1601
-
1602
-        return $cur;
1603
-    }
1604
-
1605
-    /**
1606
-     * Assign the value to the given variable.
1607
-     *
1608
-     * @param mixed  $value the value to assign
1609
-     * @param string $scope the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
1610
-     *
1611
-     * @return bool true if assigned correctly or false if a problem occured while parsing the var string
1612
-     */
1613
-    public function assignInScope($value, $scope)
1614
-    {
1615
-        if (!is_string($scope)) {
1616
-            $this->triggerError('Assignments must be done into strings, (' . gettype($scope) . ') ' . var_export($scope, true) . ' given', E_USER_ERROR);
1617
-        }
1618
-        if (strstr($scope, '.') === false && strstr($scope, '->') === false) {
1619
-            $this->scope[$scope] = $value;
1620
-        } else {
1621
-            // TODO handle _root/_parent scopes ?
1622
-            preg_match_all('#(\[|->|\.)?([^.[\]-]+)\]?#i', $scope, $m);
1623
-
1624
-            $cur  = &$this->scope;
1625
-            $last = array(
1626
-                array_pop($m[1]),
1627
-                array_pop($m[2])
1628
-            );
1629
-
1630
-            foreach ($m[1] as $k => $sep) {
1631
-                if ($sep === '.' || $sep === '[' || $sep === '') {
1632
-                    if (is_array($cur) === false) {
1633
-                        $cur = array();
1634
-                    }
1635
-                    $cur = &$cur[$m[2][$k]];
1636
-                } elseif ($sep === '->') {
1637
-                    if (is_object($cur) === false) {
1638
-                        $cur = new stdClass();
1639
-                    }
1640
-                    $cur = &$cur->{$m[2][$k]};
1641
-                } else {
1642
-                    return false;
1643
-                }
1644
-            }
1645
-
1646
-            if ($last[0] === '.' || $last[0] === '[' || $last[0] === '') {
1647
-                if (is_array($cur) === false) {
1648
-                    $cur = array();
1649
-                }
1650
-                $cur[$last[1]] = $value;
1651
-            } elseif ($last[0] === '->') {
1652
-                if (is_object($cur) === false) {
1653
-                    $cur = new stdClass();
1654
-                }
1655
-                $cur->{$last[1]} = $value;
1656
-            } else {
1657
-                return false;
1658
-            }
1659
-        }
1660
-    }
1661
-
1662
-    /**
1663
-     * Sets the scope to the given scope string or array.
1664
-     *
1665
-     * @param mixed $scope    a string i.e. "level1.level2" or an array i.e. array("level1", "level2")
1666
-     * @param bool  $absolute if true, the scope is set from the top level scope and not from the current scope
1667
-     *
1668
-     * @return array the current scope tree
1669
-     */
1670
-    public function setScope($scope, $absolute = false)
1671
-    {
1672
-        $old = $this->scopeTree;
1673
-
1674
-        if (is_string($scope) === true) {
1675
-            $scope = explode('.', $scope);
1676
-        }
1677
-
1678
-        if ($absolute === true) {
1679
-            $this->scope     = &$this->data;
1680
-            $this->scopeTree = array();
1681
-        }
1682
-
1683
-        while (($bit = array_shift($scope)) !== null) {
1684
-            if ($bit === '_' || $bit === '_parent') {
1685
-                array_pop($this->scopeTree);
1686
-                $this->scope = &$this->data;
1687
-                $cnt         = count($this->scopeTree);
1688
-                for ($i = 0; $i < $cnt; ++ $i) {
1689
-                    $this->scope = &$this->scope[$this->scopeTree[$i]];
1690
-                }
1691
-            } elseif ($bit === '__' || $bit === '_root') {
1692
-                $this->scope     = &$this->data;
1693
-                $this->scopeTree = array();
1694
-            } elseif (isset($this->scope[$bit])) {
1695
-                if ($this->scope instanceof ArrayAccess) {
1696
-                    $tmp         = $this->scope[$bit];
1697
-                    $this->scope = &$tmp;
1698
-                } else {
1699
-                    $this->scope = &$this->scope[$bit];
1700
-                }
1701
-                $this->scopeTree[] = $bit;
1702
-            } else {
1703
-                unset($this->scope);
1704
-                $this->scope = null;
1705
-            }
1706
-        }
1707
-
1708
-        return $old;
1709
-    }
1710
-
1711
-    /**
1712
-     * Returns the entire data array.
1713
-     *
1714
-     * @return array
1715
-     */
1716
-    public function getData()
1717
-    {
1718
-        return $this->data;
1719
-    }
1720
-
1721
-    /**
1722
-     * Sets a return value for the currently running template.
1723
-     *
1724
-     * @param string $name  var name
1725
-     * @param mixed  $value var value
1726
-     *
1727
-     * @return void
1728
-     */
1729
-    public function setReturnValue($name, $value)
1730
-    {
1731
-        $this->returnData[$name] = $value;
1732
-    }
1733
-
1734
-    /**
1735
-     * Retrieves the return values set by the template.
1736
-     *
1737
-     * @return array
1738
-     */
1739
-    public function getReturnValues()
1740
-    {
1741
-        return $this->returnData;
1742
-    }
1743
-
1744
-    /**
1745
-     * Returns a reference to the current scope.
1746
-     *
1747
-     * @return mixed
1748
-     */
1749
-    public function &getScope()
1750
-    {
1751
-        return $this->scope;
1752
-    }
1753
-
1754
-    /**
1755
-     * Redirects all calls to unexisting to plugin proxy.
1756
-     *
1757
-     * @param string $method the method name
1758
-     * @param array  $args   array of arguments
1759
-     *
1760
-     * @return mixed
1761
-     * @throws Exception
1762
-     */
1763
-    public function __call($method, $args)
1764
-    {
1765
-        $proxy = $this->getPluginProxy();
1766
-        if (!$proxy) {
1767
-            throw new Exception('Call to undefined method ' . __CLASS__ . '::' . $method . '()');
1768
-        }
1769
-
1770
-        return call_user_func_array($proxy->getCallback($method), $args);
1771
-    }
1772
-
1773
-    /**
1774
-     * Convert plugin name from `auto_escape` to `AutoEscape`.
1775
-     * @param string $input
1776
-     * @param string $separator
1777
-     *
1778
-     * @return mixed
1779
-     */
1780
-    public static function toCamelCase($input, $separator = '_')
1781
-    {
1782
-        return join(array_map('ucfirst', explode($separator, $input)));
1783
-
1784
-        // TODO >= PHP5.4.32
1785
-        //return str_replace($separator, '', ucwords($input, $separator));
1786
-    }
611
+				);
612
+			}
613
+
614
+			$this->filters[] = $callback;
615
+		} else {
616
+			$this->filters[] = $callback;
617
+		}
618
+	}
619
+
620
+	/**
621
+	 * Removes a filter.
622
+	 *
623
+	 * @param mixed $callback callback or filter name if it was autoloaded
624
+	 *
625
+	 * @return void
626
+	 */
627
+	public function removeFilter($callback)
628
+	{
629
+		if (($index = array_search(self::NAMESPACE_PLUGINS_FILTERS. 'Filter' . self::toCamelCase($callback), $this->filters,
630
+				true)) !==
631
+			false) {
632
+			unset($this->filters[$index]);
633
+		} elseif (($index = array_search($callback, $this->filters, true)) !== false) {
634
+			unset($this->filters[$index]);
635
+		} else {
636
+			$class = self::NAMESPACE_PLUGINS_FILTERS . 'Filter' . $callback;
637
+			foreach ($this->filters as $index => $filter) {
638
+				if (is_array($filter) && $filter[0] instanceof $class) {
639
+					unset($this->filters[$index]);
640
+					break;
641
+				}
642
+			}
643
+		}
644
+	}
645
+
646
+	/**
647
+	 * Adds a resource or overrides a default one.
648
+	 *
649
+	 * @param string   $name            the resource name
650
+	 * @param string   $class           the resource class (which must implement ITemplate)
651
+	 * @param callback $compilerFactory the compiler factory callback, a function that must return a compiler instance
652
+	 *                                  used to compile this resource, if none is provided. by default it will produce
653
+	 *                                  a Compiler object
654
+	 *
655
+	 * @return void
656
+	 * @throws Exception
657
+	 */
658
+	public function addResource($name, $class, $compilerFactory = null)
659
+	{
660
+		if (strlen($name) < 2) {
661
+			throw new Exception('Resource names must be at least two-character long to avoid conflicts with Windows paths');
662
+		}
663
+
664
+		if (!class_exists($class)) {
665
+			throw new Exception(sprintf('Resource class %s does not exist', $class));
666
+		}
667
+
668
+		$interfaces = class_implements($class);
669
+		if (in_array('Dwoo\ITemplate', $interfaces) === false) {
670
+			throw new Exception('Resource class must implement ITemplate');
671
+		}
672
+
673
+		$this->resources[$name] = array(
674
+			'class'    => $class,
675
+			'compiler' => $compilerFactory
676
+		);
677
+	}
678
+
679
+	/**
680
+	 * Removes a custom resource.
681
+	 *
682
+	 * @param string $name the resource name
683
+	 *
684
+	 * @return void
685
+	 */
686
+	public function removeResource($name)
687
+	{
688
+		unset($this->resources[$name]);
689
+		if ($name === 'file') {
690
+			$this->resources['file'] = array(
691
+				'class'    => 'Dwoo\Template\File',
692
+				'compiler' => null
693
+			);
694
+		}
695
+	}
696
+
697
+	/**
698
+	 * Sets the loader object to use to load plugins.
699
+	 *
700
+	 * @param ILoader $loader loader
701
+	 *
702
+	 * @return void
703
+	 */
704
+	public function setLoader(ILoader $loader)
705
+	{
706
+		$this->loader = $loader;
707
+	}
708
+
709
+	/**
710
+	 * Returns the current loader object or a default one if none is currently found.
711
+	 *
712
+	 * @return ILoader|Loader
713
+	 */
714
+	public function getLoader()
715
+	{
716
+		if ($this->loader === null) {
717
+			$this->loader = new Loader($this->getCompileDir());
718
+		}
719
+
720
+		return $this->loader;
721
+	}
722
+
723
+	/**
724
+	 * Returns the custom plugins loaded.
725
+	 * Used by the ITemplate classes to pass the custom plugins to their ICompiler instance.
726
+	 *
727
+	 * @return array
728
+	 */
729
+	public function getCustomPlugins()
730
+	{
731
+		return $this->plugins;
732
+	}
733
+
734
+	/**
735
+	 * Return a specified custom plugin loaded by his name.
736
+	 * Used by the compiler, for executing a Closure.
737
+	 *
738
+	 * @param string $name
739
+	 *
740
+	 * @return mixed|null
741
+	 */
742
+	public function getCustomPlugin($name)
743
+	{
744
+		if (isset($this->plugins[$name])) {
745
+			return $this->plugins[$name]['callback'];
746
+		}
747
+
748
+		return null;
749
+	}
750
+
751
+	/**
752
+	 * Returns the cache directory with a trailing DIRECTORY_SEPARATOR.
753
+	 *
754
+	 * @return string
755
+	 */
756
+	public function getCacheDir()
757
+	{
758
+		if ($this->cacheDir === null) {
759
+			$this->setCacheDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR);
760
+		}
761
+
762
+		return $this->cacheDir;
763
+	}
764
+
765
+	/**
766
+	 * Sets the cache directory and automatically appends a DIRECTORY_SEPARATOR.
767
+	 *
768
+	 * @param string $dir the cache directory
769
+	 *
770
+	 * @return void
771
+	 * @throws Exception
772
+	 */
773
+	public function setCacheDir($dir)
774
+	{
775
+		$this->cacheDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
776
+		if (is_writable($this->cacheDir) === false) {
777
+			throw new Exception('The cache directory must be writable, chmod "' . $this->cacheDir . '" to make it writable');
778
+		}
779
+	}
780
+
781
+	/**
782
+	 * Returns the compile directory with a trailing DIRECTORY_SEPARATOR.
783
+	 *
784
+	 * @return string
785
+	 */
786
+	public function getCompileDir()
787
+	{
788
+		if ($this->compileDir === null) {
789
+			$this->setCompileDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'compiled' . DIRECTORY_SEPARATOR);
790
+		}
791
+
792
+		return $this->compileDir;
793
+	}
794
+
795
+	/**
796
+	 * Sets the compile directory and automatically appends a DIRECTORY_SEPARATOR.
797
+	 *
798
+	 * @param string $dir the compile directory
799
+	 *
800
+	 * @return void
801
+	 * @throws Exception
802
+	 */
803
+	public function setCompileDir($dir)
804
+	{
805
+		$this->compileDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
806
+		if (is_writable($this->compileDir) === false) {
807
+			throw new Exception('The compile directory must be writable, chmod "' . $this->compileDir . '" to make it writable');
808
+		}
809
+	}
810
+
811
+	/**
812
+	 * Returns an array of the template directory with a trailing DIRECTORY_SEPARATOR
813
+	 *
814
+	 * @return array
815
+	 */
816
+	public function getTemplateDir()
817
+	{
818
+		return $this->templateDir;
819
+	}
820
+
821
+	/**
822
+	 * sets the template directory and automatically appends a DIRECTORY_SEPARATOR
823
+	 * template directory is stored in an array
824
+	 *
825
+	 * @param string $dir
826
+	 *
827
+	 * @throws Exception
828
+	 */
829
+	public function setTemplateDir($dir)
830
+	{
831
+		$tmpDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR;
832
+		if (is_dir($tmpDir) === false) {
833
+			throw new Exception('The template directory: "' . $tmpDir . '" does not exists, create the directory or specify an other location !');
834
+		}
835
+		$this->templateDir[] = $tmpDir;
836
+	}
837
+
838
+	/**
839
+	 * Returns the default cache time that is used with templates that do not have a cache time set.
840
+	 *
841
+	 * @return int the duration in seconds
842
+	 */
843
+	public function getCacheTime()
844
+	{
845
+		return $this->cacheTime;
846
+	}
847
+
848
+	/**
849
+	 * Sets the default cache time to use with templates that do not have a cache time set.
850
+	 *
851
+	 * @param int $seconds the duration in seconds
852
+	 *
853
+	 * @return void
854
+	 */
855
+	public function setCacheTime($seconds)
856
+	{
857
+		$this->cacheTime = (int)$seconds;
858
+	}
859
+
860
+	/**
861
+	 * Returns the character set used by the string manipulation plugins.
862
+	 * the charset is automatically lowercased
863
+	 *
864
+	 * @return string
865
+	 */
866
+	public function getCharset()
867
+	{
868
+		return $this->charset;
869
+	}
870
+
871
+	/**
872
+	 * Sets the character set used by the string manipulation plugins.
873
+	 * the charset will be automatically lowercased
874
+	 *
875
+	 * @param string $charset the character set
876
+	 *
877
+	 * @return void
878
+	 */
879
+	public function setCharset($charset)
880
+	{
881
+		$this->charset = strtolower((string)$charset);
882
+	}
883
+
884
+	/**
885
+	 * Returns the current template being rendered, when applicable, or null.
886
+	 *
887
+	 * @return ITemplate|null
888
+	 */
889
+	public function getTemplate()
890
+	{
891
+		return $this->template;
892
+	}
893
+
894
+	/**
895
+	 * Sets the current template being rendered.
896
+	 *
897
+	 * @param ITemplate $tpl template object
898
+	 *
899
+	 * @return void
900
+	 */
901
+	public function setTemplate(ITemplate $tpl)
902
+	{
903
+		$this->template = $tpl;
904
+	}
905
+
906
+	/**
907
+	 * Sets the default compiler factory function for the given resource name.
908
+	 * a compiler factory must return a ICompiler object pre-configured to fit your needs
909
+	 *
910
+	 * @param string   $resourceName    the resource name (i.e. file, string)
911
+	 * @param callback $compilerFactory the compiler factory callback
912
+	 *
913
+	 * @return void
914
+	 */
915
+	public function setDefaultCompilerFactory($resourceName, $compilerFactory)
916
+	{
917
+		$this->resources[$resourceName]['compiler'] = $compilerFactory;
918
+	}
919
+
920
+	/**
921
+	 * Returns the default compiler factory function for the given resource name.
922
+	 *
923
+	 * @param string $resourceName the resource name
924
+	 *
925
+	 * @return callback the compiler factory callback
926
+	 */
927
+	public function getDefaultCompilerFactory($resourceName)
928
+	{
929
+		return $this->resources[$resourceName]['compiler'];
930
+	}
931
+
932
+	/**
933
+	 * Sets the security policy object to enforce some php security settings.
934
+	 * use this if untrusted persons can modify templates
935
+	 *
936
+	 * @param SecurityPolicy $policy the security policy object
937
+	 *
938
+	 * @return void
939
+	 */
940
+	public function setSecurityPolicy(SecurityPolicy $policy = null)
941
+	{
942
+		$this->securityPolicy = $policy;
943
+	}
944
+
945
+	/**
946
+	 * Returns the current security policy object or null by default.
947
+	 *
948
+	 * @return SecurityPolicy|null the security policy object if any
949
+	 */
950
+	public function getSecurityPolicy()
951
+	{
952
+		return $this->securityPolicy;
953
+	}
954
+
955
+	/**
956
+	 * Sets the object that must be used as a plugin proxy when plugin can't be found
957
+	 * by dwoo's loader.
958
+	 *
959
+	 * @param IPluginProxy $pluginProxy the proxy object
960
+	 *
961
+	 * @return void
962
+	 */
963
+	public function setPluginProxy(IPluginProxy $pluginProxy)
964
+	{
965
+		$this->pluginProxy = $pluginProxy;
966
+	}
967
+
968
+	/**
969
+	 * Returns the current plugin proxy object or null by default.
970
+	 *
971
+	 * @return IPluginProxy
972
+	 */
973
+	public function getPluginProxy()
974
+	{
975
+		return $this->pluginProxy;
976
+	}
977
+
978
+	/**
979
+	 * Checks whether the given template is cached or not.
980
+	 *
981
+	 * @param ITemplate $tpl the template object
982
+	 *
983
+	 * @return bool
984
+	 */
985
+	public function isCached(ITemplate $tpl)
986
+	{
987
+		return is_string($tpl->getCachedTemplate($this));
988
+	}
989
+
990
+	/**
991
+	 * Clear templates inside the compiled directory.
992
+	 *
993
+	 * @return int
994
+	 */
995
+	public function clearCompiled()
996
+	{
997
+		$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCompileDir()), \RecursiveIteratorIterator::SELF_FIRST);
998
+		$count    = 0;
999
+		foreach ($iterator as $file) {
1000
+			if ($file->isFile()) {
1001
+				$count += unlink($file->__toString()) ? 1 : 0;
1002
+			}
1003
+		}
1004
+
1005
+		return $count;
1006
+	}
1007
+
1008
+	/**
1009
+	 * Clears the cached templates if they are older than the given time.
1010
+	 *
1011
+	 * @param int $olderThan minimum time (in seconds) required for a cached template to be cleared
1012
+	 *
1013
+	 * @return int the amount of templates cleared
1014
+	 */
1015
+	public function clearCache($olderThan = - 1)
1016
+	{
1017
+		$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCacheDir()), \RecursiveIteratorIterator::SELF_FIRST);
1018
+		$expired  = time() - $olderThan;
1019
+		$count    = 0;
1020
+		foreach ($iterator as $file) {
1021
+			if ($file->isFile() && $file->getCTime() < $expired) {
1022
+				$count += unlink((string)$file) ? 1 : 0;
1023
+			}
1024
+		}
1025
+
1026
+		return $count;
1027
+	}
1028
+
1029
+	/**
1030
+	 * Fetches a template object of the given resource.
1031
+	 *
1032
+	 * @param string    $resourceName   the resource name (i.e. file, string)
1033
+	 * @param string    $resourceId     the resource identifier (i.e. file path)
1034
+	 * @param int       $cacheTime      the cache time setting for this resource
1035
+	 * @param string    $cacheId        the unique cache identifier
1036
+	 * @param string    $compileId      the unique compiler identifier
1037
+	 * @param ITemplate $parentTemplate the parent template
1038
+	 *
1039
+	 * @return ITemplate
1040
+	 * @throws Exception
1041
+	 */
1042
+	public function templateFactory($resourceName, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, ITemplate $parentTemplate = null)
1043
+	{
1044
+		if (isset($this->resources[$resourceName])) {
1045
+			/**
1046
+			 * Interface ITemplate
1047
+			 *
1048
+			 * @var ITemplate $class
1049
+			 */
1050
+			$class = $this->resources[$resourceName]['class'];
1051
+
1052
+			return $class::templateFactory($this, $resourceId, $cacheTime, $cacheId, $compileId, $parentTemplate);
1053
+		}
1054
+
1055
+		throw new Exception('Unknown resource type : ' . $resourceName);
1056
+	}
1057
+
1058
+	/**
1059
+	 * Checks if the input is an array or arrayaccess object, optionally it can also check if it's
1060
+	 * empty.
1061
+	 *
1062
+	 * @param mixed $value        the variable to check
1063
+	 * @param bool  $checkIsEmpty if true, the function will also check if the array|arrayaccess is empty,
1064
+	 *                            and return true only if it's not empty
1065
+	 *
1066
+	 * @return int|bool true if it's an array|arrayaccess (or the item count if $checkIsEmpty is true) or false if it's
1067
+	 *                  not an array|arrayaccess (or 0 if $checkIsEmpty is true)
1068
+	 */
1069
+	public function isArray($value, $checkIsEmpty = false)
1070
+	{
1071
+		if (is_array($value) === true || $value instanceof ArrayAccess) {
1072
+			if ($checkIsEmpty === false) {
1073
+				return true;
1074
+			}
1075
+
1076
+			return $this->count($value);
1077
+		}
1078
+
1079
+		return false;
1080
+	}
1081
+
1082
+	/**
1083
+	 * Checks if the input is an array or a traversable object, optionally it can also check if it's
1084
+	 * empty.
1085
+	 *
1086
+	 * @param mixed $value        the variable to check
1087
+	 * @param bool  $checkIsEmpty if true, the function will also check if the array|traversable is empty,
1088
+	 *                            and return true only if it's not empty
1089
+	 *
1090
+	 * @return int|bool true if it's an array|traversable (or the item count if $checkIsEmpty is true) or false if it's
1091
+	 *                  not an array|traversable (or 0 if $checkIsEmpty is true)
1092
+	 */
1093
+	public function isTraversable($value, $checkIsEmpty = false)
1094
+	{
1095
+		if (is_array($value) === true) {
1096
+			if ($checkIsEmpty === false) {
1097
+				return true;
1098
+			} else {
1099
+				return count($value) > 0;
1100
+			}
1101
+		} elseif ($value instanceof Traversable) {
1102
+			if ($checkIsEmpty === false) {
1103
+				return true;
1104
+			} else {
1105
+				return $this->count($value);
1106
+			}
1107
+		}
1108
+
1109
+		return false;
1110
+	}
1111
+
1112
+	/**
1113
+	 * Counts an array or arrayaccess/traversable object.
1114
+	 *
1115
+	 * @param mixed $value the value to count
1116
+	 *
1117
+	 * @return int|bool the count for arrays and objects that implement countable, true for other objects that don't,
1118
+	 *                  and 0 for empty elements
1119
+	 */
1120
+	public function count($value)
1121
+	{
1122
+		if (is_array($value) === true || $value instanceof Countable) {
1123
+			return count($value);
1124
+		} elseif ($value instanceof ArrayAccess) {
1125
+			if ($value->offsetExists(0)) {
1126
+				return true;
1127
+			}
1128
+		} elseif ($value instanceof Iterator) {
1129
+			$value->rewind();
1130
+			if ($value->valid()) {
1131
+				return true;
1132
+			}
1133
+		} elseif ($value instanceof Traversable) {
1134
+			foreach ($value as $dummy) {
1135
+				return true;
1136
+			}
1137
+		}
1138
+
1139
+		return 0;
1140
+	}
1141
+
1142
+	/**
1143
+	 * Triggers a dwoo error.
1144
+	 *
1145
+	 * @param string $message the error message
1146
+	 * @param int    $level   the error level, one of the PHP's E_* constants
1147
+	 *
1148
+	 * @return void
1149
+	 */
1150
+	public function triggerError($message, $level = E_USER_NOTICE)
1151
+	{
1152
+		if (!($tplIdentifier = $this->template->getResourceIdentifier())) {
1153
+			$tplIdentifier = $this->template->getResourceName();
1154
+		}
1155
+		trigger_error('Dwoo error (in ' . $tplIdentifier . ') : ' . $message, $level);
1156
+	}
1157
+
1158
+	/**
1159
+	 * Adds a block to the block stack.
1160
+	 *
1161
+	 * @param string $blockName the block name (without `Plugin` prefix)
1162
+	 * @param array  $args      the arguments to be passed to the block's init() function
1163
+	 *
1164
+	 * @return BlockPlugin the newly created block
1165
+	 */
1166
+	public function addStack($blockName, array $args = array())
1167
+	{
1168
+		if (isset($this->plugins[$blockName])) {
1169
+			$class = $this->plugins[$blockName]['class'];
1170
+		} else {
1171
+			$class = self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . self::toCamelCase($blockName);
1172
+		}
1173
+
1174
+		if ($this->curBlock !== null) {
1175
+			$this->curBlock->buffer(ob_get_contents());
1176
+			ob_clean();
1177
+		} else {
1178
+			$this->buffer .= ob_get_contents();
1179
+			ob_clean();
1180
+		}
1181
+
1182
+		$block = new $class($this);
1183
+
1184
+		call_user_func_array(array($block, 'init'), $args);
1185
+
1186
+		$this->stack[] = $this->curBlock = $block;
1187
+
1188
+		return $block;
1189
+	}
1190
+
1191
+	/**
1192
+	 * Removes the plugin at the top of the block stack.
1193
+	 * Calls the block buffer() function, followed by a call to end() and finally a call to process()
1194
+	 *
1195
+	 * @return void
1196
+	 */
1197
+	public function delStack()
1198
+	{
1199
+		$args = func_get_args();
1200
+
1201
+		$this->curBlock->buffer(ob_get_contents());
1202
+		ob_clean();
1203
+
1204
+		call_user_func_array(array($this->curBlock, 'end'), $args);
1205
+
1206
+		$tmp = array_pop($this->stack);
1207
+
1208
+		if (count($this->stack) > 0) {
1209
+			$this->curBlock = end($this->stack);
1210
+			$this->curBlock->buffer($tmp->process());
1211
+		} else {
1212
+			if ($this->buffer !== '') {
1213
+				echo $this->buffer;
1214
+				$this->buffer = '';
1215
+			}
1216
+			$this->curBlock = null;
1217
+			echo $tmp->process();
1218
+		}
1219
+
1220
+		unset($tmp);
1221
+	}
1222
+
1223
+	/**
1224
+	 * Returns the parent block of the given block.
1225
+	 *
1226
+	 * @param BlockPlugin $block the block class plugin
1227
+	 *
1228
+	 * @return BlockPlugin|false if the given block isn't in the stack
1229
+	 */
1230
+	public function getParentBlock(BlockPlugin $block)
1231
+	{
1232
+		$index = array_search($block, $this->stack, true);
1233
+		if ($index !== false && $index > 0) {
1234
+			return $this->stack[$index - 1];
1235
+		}
1236
+
1237
+		return false;
1238
+	}
1239
+
1240
+	/**
1241
+	 * Finds the closest block of the given type, starting at the top of the stack.
1242
+	 *
1243
+	 * @param string $type the type of plugin you want to find
1244
+	 *
1245
+	 * @return BlockPlugin|false if no plugin of such type is in the stack
1246
+	 */
1247
+	public function findBlock($type)
1248
+	{
1249
+		if (isset($this->plugins[$type])) {
1250
+			$type = $this->plugins[$type]['class'];
1251
+		} else {
1252
+			$type = self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin_' . str_replace(self::NAMESPACE_PLUGINS_BLOCKS.'Plugin',
1253
+					'', $type);
1254
+		}
1255
+
1256
+		$keys = array_keys($this->stack);
1257
+		while (($key = array_pop($keys)) !== false) {
1258
+			if ($this->stack[$key] instanceof $type) {
1259
+				return $this->stack[$key];
1260
+			}
1261
+		}
1262
+
1263
+		return false;
1264
+	}
1265
+
1266
+	/**
1267
+	 * Returns a Plugin of the given class.
1268
+	 * this is so a single instance of every class plugin is created at each template run,
1269
+	 * allowing class plugins to have "per-template-run" static variables
1270
+	 *
1271
+	 * @param string $class the class name
1272
+	 *
1273
+	 * @return mixed an object of the given class
1274
+	 */
1275
+	public function getObjectPlugin($class)
1276
+	{
1277
+		if (isset($this->runtimePlugins[$class])) {
1278
+			return $this->runtimePlugins[$class];
1279
+		}
1280
+
1281
+		return $this->runtimePlugins[$class] = new $class($this);
1282
+	}
1283
+
1284
+	/**
1285
+	 * Calls the process() method of the given class-plugin name.
1286
+	 *
1287
+	 * @param string $plugName the class plugin name (without `Plugin` prefix)
1288
+	 * @param array  $params   an array of parameters to send to the process() method
1289
+	 *
1290
+	 * @return string the process() return value
1291
+	 */
1292
+	public function classCall($plugName, array $params = array())
1293
+	{
1294
+		$class  = self::toCamelCase($plugName);
1295
+		$plugin = $this->getObjectPlugin($class);
1296
+
1297
+		return call_user_func_array(array($plugin, 'process'), $params);
1298
+	}
1299
+
1300
+	/**
1301
+	 * Calls a php function.
1302
+	 *
1303
+	 * @param string $callback the function to call
1304
+	 * @param array  $params   an array of parameters to send to the function
1305
+	 *
1306
+	 * @return mixed the return value of the called function
1307
+	 */
1308
+	public function arrayMap($callback, array $params)
1309
+	{
1310
+		if ($params[0] === $this) {
1311
+			$addThis = true;
1312
+			array_shift($params);
1313
+		}
1314
+		if ((is_array($params[0]) || ($params[0] instanceof Iterator && $params[0] instanceof ArrayAccess))) {
1315
+			if (empty($params[0])) {
1316
+				return $params[0];
1317
+			}
1318
+
1319
+			// array map
1320
+			$out = array();
1321
+			$cnt = count($params);
1322
+
1323
+			if (isset($addThis)) {
1324
+				array_unshift($params, $this);
1325
+				$items = $params[1];
1326
+				$keys  = array_keys($items);
1327
+
1328
+				if (is_string($callback) === false) {
1329
+					while (($i = array_shift($keys)) !== null) {
1330
+						$out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params);
1331
+					}
1332
+				} elseif ($cnt === 1) {
1333
+					while (($i = array_shift($keys)) !== null) {
1334
+						$out[] = $callback($this, $items[$i]);
1335
+					}
1336
+				} elseif ($cnt === 2) {
1337
+					while (($i = array_shift($keys)) !== null) {
1338
+						$out[] = $callback($this, $items[$i], $params[2]);
1339
+					}
1340
+				} elseif ($cnt === 3) {
1341
+					while (($i = array_shift($keys)) !== null) {
1342
+						$out[] = $callback($this, $items[$i], $params[2], $params[3]);
1343
+					}
1344
+				} else {
1345
+					while (($i = array_shift($keys)) !== null) {
1346
+						$out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params);
1347
+					}
1348
+				}
1349
+			} else {
1350
+				$items = $params[0];
1351
+				$keys  = array_keys($items);
1352
+
1353
+				if (is_string($callback) === false) {
1354
+					while (($i = array_shift($keys)) !== null) {
1355
+						$out[] = call_user_func_array($callback, array($items[$i]) + $params);
1356
+					}
1357
+				} elseif ($cnt === 1) {
1358
+					while (($i = array_shift($keys)) !== null) {
1359
+						$out[] = $callback($items[$i]);
1360
+					}
1361
+				} elseif ($cnt === 2) {
1362
+					while (($i = array_shift($keys)) !== null) {
1363
+						$out[] = $callback($items[$i], $params[1]);
1364
+					}
1365
+				} elseif ($cnt === 3) {
1366
+					while (($i = array_shift($keys)) !== null) {
1367
+						$out[] = $callback($items[$i], $params[1], $params[2]);
1368
+					}
1369
+				} elseif ($cnt === 4) {
1370
+					while (($i = array_shift($keys)) !== null) {
1371
+						$out[] = $callback($items[$i], $params[1], $params[2], $params[3]);
1372
+					}
1373
+				} else {
1374
+					while (($i = array_shift($keys)) !== null) {
1375
+						$out[] = call_user_func_array($callback, array($items[$i]) + $params);
1376
+					}
1377
+				}
1378
+			}
1379
+
1380
+			return $out;
1381
+		} else {
1382
+			return $params[0];
1383
+		}
1384
+	}
1385
+
1386
+	/**
1387
+	 * Reads a variable into the given data array.
1388
+	 *
1389
+	 * @param string $varstr   the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
1390
+	 * @param mixed  $data     the data array or object to read from
1391
+	 * @param bool   $safeRead if true, the function will check whether the index exists to prevent any notices from
1392
+	 *                         being output
1393
+	 *
1394
+	 * @return mixed
1395
+	 */
1396
+	public function readVarInto($varstr, $data, $safeRead = false)
1397
+	{
1398
+		if ($data === null) {
1399
+			return null;
1400
+		}
1401
+
1402
+		if (is_array($varstr) === false) {
1403
+			preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m);
1404
+		} else {
1405
+			$m = $varstr;
1406
+		}
1407
+		unset($varstr);
1408
+
1409
+		foreach ($m[1] as $k => $sep) {
1410
+			if ($sep === '.' || $sep === '[' || $sep === '') {
1411
+				// strip enclosing quotes if present
1412
+				$m[2][$k] = preg_replace('#^(["\']?)(.*?)\1$#', '$2', $m[2][$k]);
1413
+
1414
+				if ((is_array($data) || $data instanceof ArrayAccess) && ($safeRead === false || isset($data[$m[2][$k]]))) {
1415
+					$data = $data[$m[2][$k]];
1416
+				} else {
1417
+					return null;
1418
+				}
1419
+			} else {
1420
+				if (is_object($data) && ($safeRead === false || isset($data->{$m[2][$k]}))) {
1421
+					$data = $data->{$m[2][$k]};
1422
+				} else {
1423
+					return null;
1424
+				}
1425
+			}
1426
+		}
1427
+
1428
+		return $data;
1429
+	}
1430
+
1431
+	/**
1432
+	 * Reads a variable into the parent scope.
1433
+	 *
1434
+	 * @param int    $parentLevels the amount of parent levels to go from the current scope
1435
+	 * @param string $varstr       the variable string, using dwoo variable syntax (i.e.
1436
+	 *                             "var.subvar[subsubvar]->property")
1437
+	 *
1438
+	 * @return mixed
1439
+	 */
1440
+	public function readParentVar($parentLevels, $varstr = null)
1441
+	{
1442
+		$tree = $this->scopeTree;
1443
+		$cur  = $this->data;
1444
+
1445
+		while ($parentLevels -- !== 0) {
1446
+			array_pop($tree);
1447
+		}
1448
+
1449
+		while (($i = array_shift($tree)) !== null) {
1450
+			if (is_object($cur)) {
1451
+				$cur = $cur->{$i};
1452
+			} else {
1453
+				$cur = $cur[$i];
1454
+			}
1455
+		}
1456
+
1457
+		if ($varstr !== null) {
1458
+			return $this->readVarInto($varstr, $cur);
1459
+		} else {
1460
+			return $cur;
1461
+		}
1462
+	}
1463
+
1464
+	/**
1465
+	 * Reads a variable into the current scope.
1466
+	 *
1467
+	 * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
1468
+	 *
1469
+	 * @return mixed
1470
+	 */
1471
+	public function readVar($varstr)
1472
+	{
1473
+		if (is_array($varstr) === true) {
1474
+			$m = $varstr;
1475
+			unset($varstr);
1476
+		} else {
1477
+			if (strstr($varstr, '.') === false && strstr($varstr, '[') === false && strstr($varstr, '->') === false) {
1478
+				if ($varstr === 'dwoo') {
1479
+					return $this->getGlobals();
1480
+				} elseif ($varstr === '__' || $varstr === '_root') {
1481
+					return $this->data;
1482
+				} elseif ($varstr === '_' || $varstr === '_parent') {
1483
+					$varstr = '.' . $varstr;
1484
+					$tree   = $this->scopeTree;
1485
+					$cur    = $this->data;
1486
+					array_pop($tree);
1487
+
1488
+					while (($i = array_shift($tree)) !== null) {
1489
+						if (is_object($cur)) {
1490
+							$cur = $cur->{$i};
1491
+						} else {
1492
+							$cur = $cur[$i];
1493
+						}
1494
+					}
1495
+
1496
+					return $cur;
1497
+				}
1498
+
1499
+				$cur = $this->scope;
1500
+
1501
+				if (isset($cur[$varstr])) {
1502
+					return $cur[$varstr];
1503
+				} else {
1504
+					return null;
1505
+				}
1506
+			}
1507
+
1508
+			if (substr($varstr, 0, 1) === '.') {
1509
+				$varstr = 'dwoo' . $varstr;
1510
+			}
1511
+
1512
+			preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m);
1513
+		}
1514
+
1515
+		$i = $m[2][0];
1516
+		if ($i === 'dwoo') {
1517
+			$cur = $this->getGlobals();
1518
+			array_shift($m[2]);
1519
+			array_shift($m[1]);
1520
+			switch ($m[2][0]) {
1521
+			case 'get':
1522
+				$cur = $_GET;
1523
+				break;
1524
+			case 'post':
1525
+				$cur = $_POST;
1526
+				break;
1527
+			case 'session':
1528
+				$cur = $_SESSION;
1529
+				break;
1530
+			case 'cookies':
1531
+			case 'cookie':
1532
+				$cur = $_COOKIE;
1533
+				break;
1534
+			case 'server':
1535
+				$cur = $_SERVER;
1536
+				break;
1537
+			case 'env':
1538
+				$cur = $_ENV;
1539
+				break;
1540
+			case 'request':
1541
+				$cur = $_REQUEST;
1542
+				break;
1543
+			case 'const':
1544
+				array_shift($m[2]);
1545
+				if (defined($m[2][0])) {
1546
+					return constant($m[2][0]);
1547
+				} else {
1548
+					return null;
1549
+				}
1550
+			}
1551
+			if ($cur !== $this->getGlobals()) {
1552
+				array_shift($m[2]);
1553
+				array_shift($m[1]);
1554
+			}
1555
+		} elseif ($i === '__' || $i === '_root') {
1556
+			$cur = $this->data;
1557
+			array_shift($m[2]);
1558
+			array_shift($m[1]);
1559
+		} elseif ($i === '_' || $i === '_parent') {
1560
+			$tree = $this->scopeTree;
1561
+			$cur  = $this->data;
1562
+
1563
+			while (true) {
1564
+				array_pop($tree);
1565
+				array_shift($m[2]);
1566
+				array_shift($m[1]);
1567
+				if (current($m[2]) === '_' || current($m[2]) === '_parent') {
1568
+					continue;
1569
+				}
1570
+
1571
+				while (($i = array_shift($tree)) !== null) {
1572
+					if (is_object($cur)) {
1573
+						$cur = $cur->{$i};
1574
+					} else {
1575
+						$cur = $cur[$i];
1576
+					}
1577
+				}
1578
+				break;
1579
+			}
1580
+		} else {
1581
+			$cur = $this->scope;
1582
+		}
1583
+
1584
+		foreach ($m[1] as $k => $sep) {
1585
+			if ($sep === '.' || $sep === '[' || $sep === '') {
1586
+				if ((is_array($cur) || $cur instanceof ArrayAccess) && isset($cur[$m[2][$k]])) {
1587
+					$cur = $cur[$m[2][$k]];
1588
+				} else {
1589
+					return null;
1590
+				}
1591
+			} elseif ($sep === '->') {
1592
+				if (is_object($cur)) {
1593
+					$cur = $cur->{$m[2][$k]};
1594
+				} else {
1595
+					return null;
1596
+				}
1597
+			} else {
1598
+				return null;
1599
+			}
1600
+		}
1601
+
1602
+		return $cur;
1603
+	}
1604
+
1605
+	/**
1606
+	 * Assign the value to the given variable.
1607
+	 *
1608
+	 * @param mixed  $value the value to assign
1609
+	 * @param string $scope the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property")
1610
+	 *
1611
+	 * @return bool true if assigned correctly or false if a problem occured while parsing the var string
1612
+	 */
1613
+	public function assignInScope($value, $scope)
1614
+	{
1615
+		if (!is_string($scope)) {
1616
+			$this->triggerError('Assignments must be done into strings, (' . gettype($scope) . ') ' . var_export($scope, true) . ' given', E_USER_ERROR);
1617
+		}
1618
+		if (strstr($scope, '.') === false && strstr($scope, '->') === false) {
1619
+			$this->scope[$scope] = $value;
1620
+		} else {
1621
+			// TODO handle _root/_parent scopes ?
1622
+			preg_match_all('#(\[|->|\.)?([^.[\]-]+)\]?#i', $scope, $m);
1623
+
1624
+			$cur  = &$this->scope;
1625
+			$last = array(
1626
+				array_pop($m[1]),
1627
+				array_pop($m[2])
1628
+			);
1629
+
1630
+			foreach ($m[1] as $k => $sep) {
1631
+				if ($sep === '.' || $sep === '[' || $sep === '') {
1632
+					if (is_array($cur) === false) {
1633
+						$cur = array();
1634
+					}
1635
+					$cur = &$cur[$m[2][$k]];
1636
+				} elseif ($sep === '->') {
1637
+					if (is_object($cur) === false) {
1638
+						$cur = new stdClass();
1639
+					}
1640
+					$cur = &$cur->{$m[2][$k]};
1641
+				} else {
1642
+					return false;
1643
+				}
1644
+			}
1645
+
1646
+			if ($last[0] === '.' || $last[0] === '[' || $last[0] === '') {
1647
+				if (is_array($cur) === false) {
1648
+					$cur = array();
1649
+				}
1650
+				$cur[$last[1]] = $value;
1651
+			} elseif ($last[0] === '->') {
1652
+				if (is_object($cur) === false) {
1653
+					$cur = new stdClass();
1654
+				}
1655
+				$cur->{$last[1]} = $value;
1656
+			} else {
1657
+				return false;
1658
+			}
1659
+		}
1660
+	}
1661
+
1662
+	/**
1663
+	 * Sets the scope to the given scope string or array.
1664
+	 *
1665
+	 * @param mixed $scope    a string i.e. "level1.level2" or an array i.e. array("level1", "level2")
1666
+	 * @param bool  $absolute if true, the scope is set from the top level scope and not from the current scope
1667
+	 *
1668
+	 * @return array the current scope tree
1669
+	 */
1670
+	public function setScope($scope, $absolute = false)
1671
+	{
1672
+		$old = $this->scopeTree;
1673
+
1674
+		if (is_string($scope) === true) {
1675
+			$scope = explode('.', $scope);
1676
+		}
1677
+
1678
+		if ($absolute === true) {
1679
+			$this->scope     = &$this->data;
1680
+			$this->scopeTree = array();
1681
+		}
1682
+
1683
+		while (($bit = array_shift($scope)) !== null) {
1684
+			if ($bit === '_' || $bit === '_parent') {
1685
+				array_pop($this->scopeTree);
1686
+				$this->scope = &$this->data;
1687
+				$cnt         = count($this->scopeTree);
1688
+				for ($i = 0; $i < $cnt; ++ $i) {
1689
+					$this->scope = &$this->scope[$this->scopeTree[$i]];
1690
+				}
1691
+			} elseif ($bit === '__' || $bit === '_root') {
1692
+				$this->scope     = &$this->data;
1693
+				$this->scopeTree = array();
1694
+			} elseif (isset($this->scope[$bit])) {
1695
+				if ($this->scope instanceof ArrayAccess) {
1696
+					$tmp         = $this->scope[$bit];
1697
+					$this->scope = &$tmp;
1698
+				} else {
1699
+					$this->scope = &$this->scope[$bit];
1700
+				}
1701
+				$this->scopeTree[] = $bit;
1702
+			} else {
1703
+				unset($this->scope);
1704
+				$this->scope = null;
1705
+			}
1706
+		}
1707
+
1708
+		return $old;
1709
+	}
1710
+
1711
+	/**
1712
+	 * Returns the entire data array.
1713
+	 *
1714
+	 * @return array
1715
+	 */
1716
+	public function getData()
1717
+	{
1718
+		return $this->data;
1719
+	}
1720
+
1721
+	/**
1722
+	 * Sets a return value for the currently running template.
1723
+	 *
1724
+	 * @param string $name  var name
1725
+	 * @param mixed  $value var value
1726
+	 *
1727
+	 * @return void
1728
+	 */
1729
+	public function setReturnValue($name, $value)
1730
+	{
1731
+		$this->returnData[$name] = $value;
1732
+	}
1733
+
1734
+	/**
1735
+	 * Retrieves the return values set by the template.
1736
+	 *
1737
+	 * @return array
1738
+	 */
1739
+	public function getReturnValues()
1740
+	{
1741
+		return $this->returnData;
1742
+	}
1743
+
1744
+	/**
1745
+	 * Returns a reference to the current scope.
1746
+	 *
1747
+	 * @return mixed
1748
+	 */
1749
+	public function &getScope()
1750
+	{
1751
+		return $this->scope;
1752
+	}
1753
+
1754
+	/**
1755
+	 * Redirects all calls to unexisting to plugin proxy.
1756
+	 *
1757
+	 * @param string $method the method name
1758
+	 * @param array  $args   array of arguments
1759
+	 *
1760
+	 * @return mixed
1761
+	 * @throws Exception
1762
+	 */
1763
+	public function __call($method, $args)
1764
+	{
1765
+		$proxy = $this->getPluginProxy();
1766
+		if (!$proxy) {
1767
+			throw new Exception('Call to undefined method ' . __CLASS__ . '::' . $method . '()');
1768
+		}
1769
+
1770
+		return call_user_func_array($proxy->getCallback($method), $args);
1771
+	}
1772
+
1773
+	/**
1774
+	 * Convert plugin name from `auto_escape` to `AutoEscape`.
1775
+	 * @param string $input
1776
+	 * @param string $separator
1777
+	 *
1778
+	 * @return mixed
1779
+	 */
1780
+	public static function toCamelCase($input, $separator = '_')
1781
+	{
1782
+		return join(array_map('ucfirst', explode($separator, $input)));
1783
+
1784
+		// TODO >= PHP5.4.32
1785
+		//return str_replace($separator, '', ucwords($input, $separator));
1786
+	}
1787 1787
 }
Please login to merge, or discard this patch.
lib/Dwoo/Loader.php 2 patches
Indentation   +145 added lines, -145 removed lines patch added patch discarded remove patch
@@ -23,159 +23,159 @@
 block discarded – undo
23 23
  */
24 24
 class Loader implements ILoader
25 25
 {
26
-    /**
27
-     * Stores the plugin directories.
28
-     *
29
-     * @see addDirectory
30
-     * @var array
31
-     */
32
-    protected $paths = array();
26
+	/**
27
+	 * Stores the plugin directories.
28
+	 *
29
+	 * @see addDirectory
30
+	 * @var array
31
+	 */
32
+	protected $paths = array();
33 33
 
34
-    /**
35
-     * Stores the plugins names/paths relationships
36
-     * don't edit this on your own, use addDirectory.
37
-     *
38
-     * @see addDirectory
39
-     * @var array
40
-     */
41
-    protected $classPath = array();
34
+	/**
35
+	 * Stores the plugins names/paths relationships
36
+	 * don't edit this on your own, use addDirectory.
37
+	 *
38
+	 * @see addDirectory
39
+	 * @var array
40
+	 */
41
+	protected $classPath = array();
42 42
 
43
-    /**
44
-     * Path where class paths cache files are written.
45
-     *
46
-     * @var string
47
-     */
48
-    protected $cacheDir;
43
+	/**
44
+	 * Path where class paths cache files are written.
45
+	 *
46
+	 * @var string
47
+	 */
48
+	protected $cacheDir;
49 49
 
50
-    /**
51
-     * Path where builtin plugins are stored.
52
-     *
53
-     * @var string
54
-     */
55
-    protected $corePluginDir;
50
+	/**
51
+	 * Path where builtin plugins are stored.
52
+	 *
53
+	 * @var string
54
+	 */
55
+	protected $corePluginDir;
56 56
 
57
-    /**
58
-     * Loader constructor.
59
-     *
60
-     * @param $cacheDir
61
-     */
62
-    public function __construct($cacheDir)
63
-    {
64
-        $this->corePluginDir = __DIR__ . DIRECTORY_SEPARATOR . 'Plugins';
65
-        $this->cacheDir      = rtrim($cacheDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
57
+	/**
58
+	 * Loader constructor.
59
+	 *
60
+	 * @param $cacheDir
61
+	 */
62
+	public function __construct($cacheDir)
63
+	{
64
+		$this->corePluginDir = __DIR__ . DIRECTORY_SEPARATOR . 'Plugins';
65
+		$this->cacheDir      = rtrim($cacheDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
66 66
 
67
-        // include class paths or rebuild paths if the cache file isn't there
68
-        $cacheFile = $this->cacheDir . 'classpath.cache.d' . Core::RELEASE_TAG . '.php';
69
-        if (file_exists($cacheFile)) {
70
-            $classpath       = file_get_contents($cacheFile);
71
-            $this->classPath = unserialize($classpath) + $this->classPath;
72
-        } else {
73
-            $this->rebuildClassPathCache($this->corePluginDir, $cacheFile);
74
-        }
75
-    }
67
+		// include class paths or rebuild paths if the cache file isn't there
68
+		$cacheFile = $this->cacheDir . 'classpath.cache.d' . Core::RELEASE_TAG . '.php';
69
+		if (file_exists($cacheFile)) {
70
+			$classpath       = file_get_contents($cacheFile);
71
+			$this->classPath = unserialize($classpath) + $this->classPath;
72
+		} else {
73
+			$this->rebuildClassPathCache($this->corePluginDir, $cacheFile);
74
+		}
75
+	}
76 76
 
77
-    /**
78
-     * Rebuilds class paths, scans the given directory recursively and saves all paths in the given file.
79
-     *
80
-     * @param string         $path      the plugin path to scan
81
-     * @param string|boolean $cacheFile the file where to store the plugin paths cache, it will be overwritten
82
-     *
83
-     * @throws Exception
84
-     */
85
-    protected function rebuildClassPathCache($path, $cacheFile)
86
-    {
87
-        $tmp = array();
88
-        if ($cacheFile !== false) {
89
-            $tmp             = $this->classPath;
90
-            $this->classPath = array();
91
-        }
77
+	/**
78
+	 * Rebuilds class paths, scans the given directory recursively and saves all paths in the given file.
79
+	 *
80
+	 * @param string         $path      the plugin path to scan
81
+	 * @param string|boolean $cacheFile the file where to store the plugin paths cache, it will be overwritten
82
+	 *
83
+	 * @throws Exception
84
+	 */
85
+	protected function rebuildClassPathCache($path, $cacheFile)
86
+	{
87
+		$tmp = array();
88
+		if ($cacheFile !== false) {
89
+			$tmp             = $this->classPath;
90
+			$this->classPath = array();
91
+		}
92 92
 
93
-        // iterates over all files/folders
94
-        foreach (new \DirectoryIterator($path) as $fileInfo) {
95
-            if (!$fileInfo->isDot()) {
96
-                if ($fileInfo->isDir()) {
97
-                    $this->rebuildClassPathCache($fileInfo->getPathname(), false);
98
-                } else {
99
-                    $this->classPath[$fileInfo->getBasename('.php')] = $fileInfo->getPathname();
100
-                }
101
-            }
102
-        }
93
+		// iterates over all files/folders
94
+		foreach (new \DirectoryIterator($path) as $fileInfo) {
95
+			if (!$fileInfo->isDot()) {
96
+				if ($fileInfo->isDir()) {
97
+					$this->rebuildClassPathCache($fileInfo->getPathname(), false);
98
+				} else {
99
+					$this->classPath[$fileInfo->getBasename('.php')] = $fileInfo->getPathname();
100
+				}
101
+			}
102
+		}
103 103
 
104
-        // save in file if it's the first call (not recursed)
105
-        if ($cacheFile !== false) {
106
-            if (!file_put_contents($cacheFile, serialize($this->classPath))) {
107
-                throw new Exception('Could not write into ' . $cacheFile . ', either because the folder is not there (create it) or because of the chmod configuration (please ensure this directory is writable by php), alternatively you can change the directory used with $dwoo->setCompileDir() or provide a custom loader object with $dwoo->setLoader()');
108
-            }
109
-            $this->classPath += $tmp;
110
-        }
111
-    }
104
+		// save in file if it's the first call (not recursed)
105
+		if ($cacheFile !== false) {
106
+			if (!file_put_contents($cacheFile, serialize($this->classPath))) {
107
+				throw new Exception('Could not write into ' . $cacheFile . ', either because the folder is not there (create it) or because of the chmod configuration (please ensure this directory is writable by php), alternatively you can change the directory used with $dwoo->setCompileDir() or provide a custom loader object with $dwoo->setLoader()');
108
+			}
109
+			$this->classPath += $tmp;
110
+		}
111
+	}
112 112
 
113
-    /**
114
-     * Loads a plugin file.
115
-     *
116
-     * @param string $class       the plugin name, without the `Plugin` prefix
117
-     * @param bool   $forceRehash if true, the class path caches will be rebuilt if the plugin is not found, in case it
118
-     *                            has just been added, defaults to true
119
-     *
120
-     * @throws Exception
121
-     */
122
-    public function loadPlugin($class, $forceRehash = true)
123
-    {
124
-        /**
125
-         * An unknown class was requested (maybe newly added) or the
126
-         * include failed so we rebuild the cache. include() will fail
127
-         * with an uncatchable error if the file doesn't exist, which
128
-         * usually means that the cache is stale and must be rebuilt,
129
-         * so we check for that before trying to include() the plugin.
130
-         */
131
-        if ((!isset($this->classPath[$class]) || !is_readable($this->classPath[$class])) || (!isset
132
-                ($this->classPath[$class . 'Compile']) || !is_readable($this->classPath[$class . 'Compile']))) {
133
-            if ($forceRehash) {
134
-                $this->rebuildClassPathCache($this->corePluginDir, $this->cacheDir . 'classpath.cache.d' .
135
-                    Core::RELEASE_TAG . '.php');
136
-                foreach ($this->paths as $path => $file) {
137
-                    $this->rebuildClassPathCache($path, $file);
138
-                }
139
-                if (isset($this->classPath[$class])) {
140
-                    include_once $this->classPath[$class];
141
-                } elseif (isset($this->classPath[$class . 'Compile'])) {
142
-                    include_once $this->classPath[$class . 'Compile'];
143
-                } else {
144
-                    throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
145
-                }
146
-            } else {
147
-                throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
148
-            }
149
-        }
150
-    }
113
+	/**
114
+	 * Loads a plugin file.
115
+	 *
116
+	 * @param string $class       the plugin name, without the `Plugin` prefix
117
+	 * @param bool   $forceRehash if true, the class path caches will be rebuilt if the plugin is not found, in case it
118
+	 *                            has just been added, defaults to true
119
+	 *
120
+	 * @throws Exception
121
+	 */
122
+	public function loadPlugin($class, $forceRehash = true)
123
+	{
124
+		/**
125
+		 * An unknown class was requested (maybe newly added) or the
126
+		 * include failed so we rebuild the cache. include() will fail
127
+		 * with an uncatchable error if the file doesn't exist, which
128
+		 * usually means that the cache is stale and must be rebuilt,
129
+		 * so we check for that before trying to include() the plugin.
130
+		 */
131
+		if ((!isset($this->classPath[$class]) || !is_readable($this->classPath[$class])) || (!isset
132
+				($this->classPath[$class . 'Compile']) || !is_readable($this->classPath[$class . 'Compile']))) {
133
+			if ($forceRehash) {
134
+				$this->rebuildClassPathCache($this->corePluginDir, $this->cacheDir . 'classpath.cache.d' .
135
+					Core::RELEASE_TAG . '.php');
136
+				foreach ($this->paths as $path => $file) {
137
+					$this->rebuildClassPathCache($path, $file);
138
+				}
139
+				if (isset($this->classPath[$class])) {
140
+					include_once $this->classPath[$class];
141
+				} elseif (isset($this->classPath[$class . 'Compile'])) {
142
+					include_once $this->classPath[$class . 'Compile'];
143
+				} else {
144
+					throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
145
+				}
146
+			} else {
147
+				throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
148
+			}
149
+		}
150
+	}
151 151
 
152
-    /**
153
-     * Adds a plugin directory, the plugins found in the new plugin directory
154
-     * will take precedence over the other directories (including the default
155
-     * dwoo plugin directory), you can use this for example to override plugins
156
-     * in a specific directory for a specific application while keeping all your
157
-     * usual plugins in the same place for all applications.
158
-     * TOCOM don't forget that php functions overrides are not rehashed so you
159
-     * need to clear the classpath caches by hand when adding those.
160
-     *
161
-     * @param string $pluginDirectory the plugin path to scan
162
-     *
163
-     * @throws Exception
164
-     */
165
-    public function addDirectory($pluginDirectory)
166
-    {
167
-        $pluginDir = realpath($pluginDirectory);
168
-        if (!$pluginDir) {
169
-            throw new Exception('Plugin directory does not exist or can not be read : ' . $pluginDirectory);
170
-        }
171
-        $cacheFile = $this->cacheDir . 'classpath-' . substr(strtr($pluginDir, '/\\:' . PATH_SEPARATOR, '----'),
172
-                strlen($pluginDir) > 80 ? - 80 : 0) . '.d' . Core::RELEASE_TAG . '.php';
173
-        $this->paths[$pluginDir] = $cacheFile;
174
-        if (file_exists($cacheFile)) {
175
-            $classpath       = file_get_contents($cacheFile);
176
-            $this->classPath = unserialize($classpath) + $this->classPath;
177
-        } else {
178
-            $this->rebuildClassPathCache($pluginDir, $cacheFile);
179
-        }
180
-    }
152
+	/**
153
+	 * Adds a plugin directory, the plugins found in the new plugin directory
154
+	 * will take precedence over the other directories (including the default
155
+	 * dwoo plugin directory), you can use this for example to override plugins
156
+	 * in a specific directory for a specific application while keeping all your
157
+	 * usual plugins in the same place for all applications.
158
+	 * TOCOM don't forget that php functions overrides are not rehashed so you
159
+	 * need to clear the classpath caches by hand when adding those.
160
+	 *
161
+	 * @param string $pluginDirectory the plugin path to scan
162
+	 *
163
+	 * @throws Exception
164
+	 */
165
+	public function addDirectory($pluginDirectory)
166
+	{
167
+		$pluginDir = realpath($pluginDirectory);
168
+		if (!$pluginDir) {
169
+			throw new Exception('Plugin directory does not exist or can not be read : ' . $pluginDirectory);
170
+		}
171
+		$cacheFile = $this->cacheDir . 'classpath-' . substr(strtr($pluginDir, '/\\:' . PATH_SEPARATOR, '----'),
172
+				strlen($pluginDir) > 80 ? - 80 : 0) . '.d' . Core::RELEASE_TAG . '.php';
173
+		$this->paths[$pluginDir] = $cacheFile;
174
+		if (file_exists($cacheFile)) {
175
+			$classpath       = file_get_contents($cacheFile);
176
+			$this->classPath = unserialize($classpath) + $this->classPath;
177
+		} else {
178
+			$this->rebuildClassPathCache($pluginDir, $cacheFile);
179
+		}
180
+	}
181 181
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -61,14 +61,14 @@  discard block
 block discarded – undo
61 61
      */
62 62
     public function __construct($cacheDir)
63 63
     {
64
-        $this->corePluginDir = __DIR__ . DIRECTORY_SEPARATOR . 'Plugins';
65
-        $this->cacheDir      = rtrim($cacheDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
64
+        $this->corePluginDir = __DIR__.DIRECTORY_SEPARATOR.'Plugins';
65
+        $this->cacheDir      = rtrim($cacheDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
66 66
 
67 67
         // include class paths or rebuild paths if the cache file isn't there
68
-        $cacheFile = $this->cacheDir . 'classpath.cache.d' . Core::RELEASE_TAG . '.php';
68
+        $cacheFile = $this->cacheDir.'classpath.cache.d'.Core::RELEASE_TAG.'.php';
69 69
         if (file_exists($cacheFile)) {
70 70
             $classpath       = file_get_contents($cacheFile);
71
-            $this->classPath = unserialize($classpath) + $this->classPath;
71
+            $this->classPath = unserialize($classpath)+$this->classPath;
72 72
         } else {
73 73
             $this->rebuildClassPathCache($this->corePluginDir, $cacheFile);
74 74
         }
@@ -104,7 +104,7 @@  discard block
 block discarded – undo
104 104
         // save in file if it's the first call (not recursed)
105 105
         if ($cacheFile !== false) {
106 106
             if (!file_put_contents($cacheFile, serialize($this->classPath))) {
107
-                throw new Exception('Could not write into ' . $cacheFile . ', either because the folder is not there (create it) or because of the chmod configuration (please ensure this directory is writable by php), alternatively you can change the directory used with $dwoo->setCompileDir() or provide a custom loader object with $dwoo->setLoader()');
107
+                throw new Exception('Could not write into '.$cacheFile.', either because the folder is not there (create it) or because of the chmod configuration (please ensure this directory is writable by php), alternatively you can change the directory used with $dwoo->setCompileDir() or provide a custom loader object with $dwoo->setLoader()');
108 108
             }
109 109
             $this->classPath += $tmp;
110 110
         }
@@ -129,22 +129,22 @@  discard block
 block discarded – undo
129 129
          * so we check for that before trying to include() the plugin.
130 130
          */
131 131
         if ((!isset($this->classPath[$class]) || !is_readable($this->classPath[$class])) || (!isset
132
-                ($this->classPath[$class . 'Compile']) || !is_readable($this->classPath[$class . 'Compile']))) {
132
+                ($this->classPath[$class.'Compile']) || !is_readable($this->classPath[$class.'Compile']))) {
133 133
             if ($forceRehash) {
134
-                $this->rebuildClassPathCache($this->corePluginDir, $this->cacheDir . 'classpath.cache.d' .
135
-                    Core::RELEASE_TAG . '.php');
134
+                $this->rebuildClassPathCache($this->corePluginDir, $this->cacheDir.'classpath.cache.d'.
135
+                    Core::RELEASE_TAG.'.php');
136 136
                 foreach ($this->paths as $path => $file) {
137 137
                     $this->rebuildClassPathCache($path, $file);
138 138
                 }
139 139
                 if (isset($this->classPath[$class])) {
140 140
                     include_once $this->classPath[$class];
141
-                } elseif (isset($this->classPath[$class . 'Compile'])) {
142
-                    include_once $this->classPath[$class . 'Compile'];
141
+                } elseif (isset($this->classPath[$class.'Compile'])) {
142
+                    include_once $this->classPath[$class.'Compile'];
143 143
                 } else {
144
-                    throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
144
+                    throw new Exception('Plugin "'.$class.'" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
145 145
                 }
146 146
             } else {
147
-                throw new Exception('Plugin "' . $class . '" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
147
+                throw new Exception('Plugin "'.$class.'" can not be found, maybe you forgot to bind it if it\'s a custom plugin ?', E_USER_NOTICE);
148 148
             }
149 149
         }
150 150
     }
@@ -166,14 +166,14 @@  discard block
 block discarded – undo
166 166
     {
167 167
         $pluginDir = realpath($pluginDirectory);
168 168
         if (!$pluginDir) {
169
-            throw new Exception('Plugin directory does not exist or can not be read : ' . $pluginDirectory);
169
+            throw new Exception('Plugin directory does not exist or can not be read : '.$pluginDirectory);
170 170
         }
171
-        $cacheFile = $this->cacheDir . 'classpath-' . substr(strtr($pluginDir, '/\\:' . PATH_SEPARATOR, '----'),
172
-                strlen($pluginDir) > 80 ? - 80 : 0) . '.d' . Core::RELEASE_TAG . '.php';
171
+        $cacheFile = $this->cacheDir.'classpath-'.substr(strtr($pluginDir, '/\\:'.PATH_SEPARATOR, '----'),
172
+                strlen($pluginDir) > 80 ? -80 : 0).'.d'.Core::RELEASE_TAG.'.php';
173 173
         $this->paths[$pluginDir] = $cacheFile;
174 174
         if (file_exists($cacheFile)) {
175 175
             $classpath       = file_get_contents($cacheFile);
176
-            $this->classPath = unserialize($classpath) + $this->classPath;
176
+            $this->classPath = unserialize($classpath)+$this->classPath;
177 177
         } else {
178 178
             $this->rebuildClassPathCache($pluginDir, $cacheFile);
179 179
         }
Please login to merge, or discard this patch.
lib/Dwoo/Template/File.php 1 patch
Indentation   +222 added lines, -222 removed lines patch added patch discarded remove patch
@@ -30,247 +30,247 @@
 block discarded – undo
30 30
  */
31 31
 class File extends Str
32 32
 {
33
-    /**
34
-     * Template filename.
35
-     *
36
-     * @var string
37
-     */
38
-    protected $file;
33
+	/**
34
+	 * Template filename.
35
+	 *
36
+	 * @var string
37
+	 */
38
+	protected $file;
39 39
 
40
-    /**
41
-     * Include path(s) to look into to find this template.
42
-     *
43
-     * @var array
44
-     */
45
-    protected $includePath = array();
40
+	/**
41
+	 * Include path(s) to look into to find this template.
42
+	 *
43
+	 * @var array
44
+	 */
45
+	protected $includePath = array();
46 46
 
47
-    /**
48
-     * Resolved path cache when looking for a file in multiple include paths.
49
-     * this is reset when the include path is changed
50
-     *
51
-     * @var string
52
-     */
53
-    protected $resolvedPath = null;
47
+	/**
48
+	 * Resolved path cache when looking for a file in multiple include paths.
49
+	 * this is reset when the include path is changed
50
+	 *
51
+	 * @var string
52
+	 */
53
+	protected $resolvedPath = null;
54 54
 
55
-    /**
56
-     * Creates a template from a file.
57
-     *
58
-     * @param string $file        the path to the template file, make sure it exists
59
-     * @param int    $cacheTime   duration of the cache validity for this template,
60
-     *                            if null it defaults to the Dwoo instance that will
61
-     *                            render this template
62
-     * @param string $cacheId     the unique cache identifier of this page or anything else that
63
-     *                            makes this template's content unique, if null it defaults
64
-     *                            to the current url
65
-     * @param string $compileId   the unique compiled identifier, which is used to distinguish this
66
-     *                            template from others, if null it defaults to the filename+bits of the path
67
-     * @param mixed  $includePath a string for a single path to look into for the given file, or an array of paths
68
-     */
69
-    public function __construct($file, $cacheTime = null, $cacheId = null, $compileId = null, $includePath = array())
70
-    {
71
-        parent::__construct($file, $cacheTime, $cacheId, $compileId);
72
-        $this->template = null;
73
-        $this->file     = $file;
74
-        $this->name     = basename($file);
75
-        $this->setIncludePath($includePath);
76
-        $this->compileId = $this->getResourceIdentifier();
77
-    }
55
+	/**
56
+	 * Creates a template from a file.
57
+	 *
58
+	 * @param string $file        the path to the template file, make sure it exists
59
+	 * @param int    $cacheTime   duration of the cache validity for this template,
60
+	 *                            if null it defaults to the Dwoo instance that will
61
+	 *                            render this template
62
+	 * @param string $cacheId     the unique cache identifier of this page or anything else that
63
+	 *                            makes this template's content unique, if null it defaults
64
+	 *                            to the current url
65
+	 * @param string $compileId   the unique compiled identifier, which is used to distinguish this
66
+	 *                            template from others, if null it defaults to the filename+bits of the path
67
+	 * @param mixed  $includePath a string for a single path to look into for the given file, or an array of paths
68
+	 */
69
+	public function __construct($file, $cacheTime = null, $cacheId = null, $compileId = null, $includePath = array())
70
+	{
71
+		parent::__construct($file, $cacheTime, $cacheId, $compileId);
72
+		$this->template = null;
73
+		$this->file     = $file;
74
+		$this->name     = basename($file);
75
+		$this->setIncludePath($includePath);
76
+		$this->compileId = $this->getResourceIdentifier();
77
+	}
78 78
 
79
-    /**
80
-     * Sets the include path(s) to where the given template filename must be looked up.
81
-     *
82
-     * @param mixed $paths the path to look into, can be string for a single path or an array of paths
83
-     */
84
-    public function setIncludePath($paths)
85
-    {
86
-        if (is_array($paths) === false) {
87
-            $paths = array($paths);
88
-        }
79
+	/**
80
+	 * Sets the include path(s) to where the given template filename must be looked up.
81
+	 *
82
+	 * @param mixed $paths the path to look into, can be string for a single path or an array of paths
83
+	 */
84
+	public function setIncludePath($paths)
85
+	{
86
+		if (is_array($paths) === false) {
87
+			$paths = array($paths);
88
+		}
89 89
 
90
-        $this->includePath  = $paths;
91
-        $this->resolvedPath = null;
92
-    }
90
+		$this->includePath  = $paths;
91
+		$this->resolvedPath = null;
92
+	}
93 93
 
94
-    /**
95
-     * Return the current include path(s).
96
-     *
97
-     * @return array
98
-     */
99
-    public function getIncludePath()
100
-    {
101
-        return $this->includePath;
102
-    }
94
+	/**
95
+	 * Return the current include path(s).
96
+	 *
97
+	 * @return array
98
+	 */
99
+	public function getIncludePath()
100
+	{
101
+		return $this->includePath;
102
+	}
103 103
 
104
-    /**
105
-     * Checks if compiled file is valid (exists and it's the modification is greater or
106
-     * equal to the modification time of the template file).
107
-     *
108
-     * @param string file
109
-     *
110
-     * @return bool True cache file existance and it's modification time
111
-     */
112
-    protected function isValidCompiledFile($file)
113
-    {
114
-        return parent::isValidCompiledFile($file) && (int)$this->getUid() <= filemtime($file);
115
-    }
104
+	/**
105
+	 * Checks if compiled file is valid (exists and it's the modification is greater or
106
+	 * equal to the modification time of the template file).
107
+	 *
108
+	 * @param string file
109
+	 *
110
+	 * @return bool True cache file existance and it's modification time
111
+	 */
112
+	protected function isValidCompiledFile($file)
113
+	{
114
+		return parent::isValidCompiledFile($file) && (int)$this->getUid() <= filemtime($file);
115
+	}
116 116
 
117
-    /**
118
-     * Returns the template source of this template.
119
-     *
120
-     * @return string
121
-     */
122
-    public function getSource()
123
-    {
124
-        return file_get_contents($this->getResourceIdentifier());
125
-    }
117
+	/**
118
+	 * Returns the template source of this template.
119
+	 *
120
+	 * @return string
121
+	 */
122
+	public function getSource()
123
+	{
124
+		return file_get_contents($this->getResourceIdentifier());
125
+	}
126 126
 
127
-    /**
128
-     * Returns the resource name for this template class.
129
-     *
130
-     * @return string
131
-     */
132
-    public function getResourceName()
133
-    {
134
-        return 'file';
135
-    }
127
+	/**
128
+	 * Returns the resource name for this template class.
129
+	 *
130
+	 * @return string
131
+	 */
132
+	public function getResourceName()
133
+	{
134
+		return 'file';
135
+	}
136 136
 
137
-    /**
138
-     * Returns this template's source filename.
139
-     *
140
-     * @return string
141
-     * @throws DwooException
142
-     */
143
-    public function getResourceIdentifier()
144
-    {
145
-        if ($this->resolvedPath !== null) {
146
-            return $this->resolvedPath;
147
-        } elseif (array_filter($this->getIncludePath()) == array()) {
148
-            return $this->file;
149
-        } else {
150
-            foreach ($this->getIncludePath() as $path) {
151
-                $path = rtrim($path, DIRECTORY_SEPARATOR);
152
-                if (file_exists($path . DIRECTORY_SEPARATOR . $this->file) === true) {
153
-                    return $this->resolvedPath = $path . DIRECTORY_SEPARATOR . $this->file;
154
-                }
155
-            }
137
+	/**
138
+	 * Returns this template's source filename.
139
+	 *
140
+	 * @return string
141
+	 * @throws DwooException
142
+	 */
143
+	public function getResourceIdentifier()
144
+	{
145
+		if ($this->resolvedPath !== null) {
146
+			return $this->resolvedPath;
147
+		} elseif (array_filter($this->getIncludePath()) == array()) {
148
+			return $this->file;
149
+		} else {
150
+			foreach ($this->getIncludePath() as $path) {
151
+				$path = rtrim($path, DIRECTORY_SEPARATOR);
152
+				if (file_exists($path . DIRECTORY_SEPARATOR . $this->file) === true) {
153
+					return $this->resolvedPath = $path . DIRECTORY_SEPARATOR . $this->file;
154
+				}
155
+			}
156 156
 
157
-            throw new DwooException('Template "' . $this->file . '" could not be found in any of your include path(s)');
158
-        }
159
-    }
157
+			throw new DwooException('Template "' . $this->file . '" could not be found in any of your include path(s)');
158
+		}
159
+	}
160 160
 
161
-    /**
162
-     * Returns an unique value identifying the current version of this template,
163
-     * in this case it's the unix timestamp of the last modification.
164
-     *
165
-     * @return string
166
-     */
167
-    public function getUid()
168
-    {
169
-        return (string)filemtime($this->getResourceIdentifier());
170
-    }
161
+	/**
162
+	 * Returns an unique value identifying the current version of this template,
163
+	 * in this case it's the unix timestamp of the last modification.
164
+	 *
165
+	 * @return string
166
+	 */
167
+	public function getUid()
168
+	{
169
+		return (string)filemtime($this->getResourceIdentifier());
170
+	}
171 171
 
172
-    /**
173
-     * Returns a new template object from the given include name, null if no include is
174
-     * possible (resource not found), or false if include is not permitted by this resource type.
175
-     *
176
-     * @param Core      $core           the dwoo instance requiring it
177
-     * @param mixed     $resourceId     the filename (relative to this template's dir) of the template to
178
-     *                                  include
179
-     * @param int       $cacheTime      duration of the cache validity for this template, if null it defaults
180
-     *                                  to the Dwoo instance that will render this template if null it
181
-     *                                  defaults to the Dwoo instance that will render this template if null
182
-     *                                  it defaults to the Dwoo instance that will render this template
183
-     * @param string    $cacheId        the unique cache identifier of this page or anything else that makes
184
-     *                                  this template's content unique, if null it defaults to the current
185
-     *                                  url makes this template's content unique, if null it defaults to the
186
-     *                                  current url makes this template's content unique, if null it defaults
187
-     *                                  to the current url
188
-     * @param string    $compileId      the unique compiled identifier, which is used to distinguish this
189
-     *                                  template from others, if null it defaults to the filename+bits of the
190
-     *                                  path template from others, if null it defaults to the filename+bits
191
-     *                                  of the path template from others, if null it defaults to the
192
-     *                                  filename+bits of the path
193
-     * @param ITemplate $parentTemplate the template that is requesting a new template object (through an
194
-     *                                  include, extends or any other plugin) an include, extends or any
195
-     *                                  other plugin) an include, extends or any other plugin)
196
-     *
197
-     * @return TemplateFile|null
198
-     * @throws DwooException
199
-     * @throws SecurityException
200
-     */
201
-    public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null,
202
-                                           $compileId = null, ITemplate $parentTemplate = null)
203
-    {
204
-        if (DIRECTORY_SEPARATOR === '\\') {
205
-            $resourceId = str_replace(array("\t", "\n", "\r", "\f", "\v"), array(
206
-                '\\t',
207
-                '\\n',
208
-                '\\r',
209
-                '\\f',
210
-                '\\v'
211
-            ), $resourceId);
212
-        }
213
-        $resourceId = strtr($resourceId, '\\', '/');
172
+	/**
173
+	 * Returns a new template object from the given include name, null if no include is
174
+	 * possible (resource not found), or false if include is not permitted by this resource type.
175
+	 *
176
+	 * @param Core      $core           the dwoo instance requiring it
177
+	 * @param mixed     $resourceId     the filename (relative to this template's dir) of the template to
178
+	 *                                  include
179
+	 * @param int       $cacheTime      duration of the cache validity for this template, if null it defaults
180
+	 *                                  to the Dwoo instance that will render this template if null it
181
+	 *                                  defaults to the Dwoo instance that will render this template if null
182
+	 *                                  it defaults to the Dwoo instance that will render this template
183
+	 * @param string    $cacheId        the unique cache identifier of this page or anything else that makes
184
+	 *                                  this template's content unique, if null it defaults to the current
185
+	 *                                  url makes this template's content unique, if null it defaults to the
186
+	 *                                  current url makes this template's content unique, if null it defaults
187
+	 *                                  to the current url
188
+	 * @param string    $compileId      the unique compiled identifier, which is used to distinguish this
189
+	 *                                  template from others, if null it defaults to the filename+bits of the
190
+	 *                                  path template from others, if null it defaults to the filename+bits
191
+	 *                                  of the path template from others, if null it defaults to the
192
+	 *                                  filename+bits of the path
193
+	 * @param ITemplate $parentTemplate the template that is requesting a new template object (through an
194
+	 *                                  include, extends or any other plugin) an include, extends or any
195
+	 *                                  other plugin) an include, extends or any other plugin)
196
+	 *
197
+	 * @return TemplateFile|null
198
+	 * @throws DwooException
199
+	 * @throws SecurityException
200
+	 */
201
+	public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null,
202
+										   $compileId = null, ITemplate $parentTemplate = null)
203
+	{
204
+		if (DIRECTORY_SEPARATOR === '\\') {
205
+			$resourceId = str_replace(array("\t", "\n", "\r", "\f", "\v"), array(
206
+				'\\t',
207
+				'\\n',
208
+				'\\r',
209
+				'\\f',
210
+				'\\v'
211
+			), $resourceId);
212
+		}
213
+		$resourceId = strtr($resourceId, '\\', '/');
214 214
 
215
-        $includePath = null;
215
+		$includePath = null;
216 216
 
217
-        if (file_exists($resourceId) === false) {
218
-            if ($parentTemplate === null) {
219
-                $parentTemplate = $core->getTemplate();
220
-            }
221
-            if ($parentTemplate instanceof self) {
222
-                if ($includePath = $parentTemplate->getIncludePath()) {
223
-                    if (strstr($resourceId, '../')) {
224
-                        throw new DwooException('When using an include path you can not reference a template into a parent directory (using ../)');
225
-                    }
226
-                } else {
227
-                    $resourceId = dirname($parentTemplate->getResourceIdentifier()) . DIRECTORY_SEPARATOR . $resourceId;
228
-                    if (file_exists($resourceId) === false) {
229
-                        return null;
230
-                    }
231
-                }
232
-            } else {
233
-                return null;
234
-            }
235
-        }
217
+		if (file_exists($resourceId) === false) {
218
+			if ($parentTemplate === null) {
219
+				$parentTemplate = $core->getTemplate();
220
+			}
221
+			if ($parentTemplate instanceof self) {
222
+				if ($includePath = $parentTemplate->getIncludePath()) {
223
+					if (strstr($resourceId, '../')) {
224
+						throw new DwooException('When using an include path you can not reference a template into a parent directory (using ../)');
225
+					}
226
+				} else {
227
+					$resourceId = dirname($parentTemplate->getResourceIdentifier()) . DIRECTORY_SEPARATOR . $resourceId;
228
+					if (file_exists($resourceId) === false) {
229
+						return null;
230
+					}
231
+				}
232
+			} else {
233
+				return null;
234
+			}
235
+		}
236 236
 
237
-        if ($policy = $core->getSecurityPolicy()) {
238
-            while (true) {
239
-                if (preg_match('{^([a-z]+?)://}i', $resourceId)) {
240
-                    throw new SecurityException('The security policy prevents you to read files from external sources : <em>' . $resourceId . '</em>.');
241
-                }
237
+		if ($policy = $core->getSecurityPolicy()) {
238
+			while (true) {
239
+				if (preg_match('{^([a-z]+?)://}i', $resourceId)) {
240
+					throw new SecurityException('The security policy prevents you to read files from external sources : <em>' . $resourceId . '</em>.');
241
+				}
242 242
 
243
-                if ($includePath) {
244
-                    break;
245
-                }
243
+				if ($includePath) {
244
+					break;
245
+				}
246 246
 
247
-                $resourceId = realpath($resourceId);
248
-                $dirs       = $policy->getAllowedDirectories();
249
-                foreach ($dirs as $dir => $dummy) {
250
-                    if (strpos($resourceId, $dir) === 0) {
251
-                        break 2;
252
-                    }
253
-                }
254
-                throw new SecurityException('The security policy prevents you to read <em>' . $resourceId . '</em>');
255
-            }
256
-        }
247
+				$resourceId = realpath($resourceId);
248
+				$dirs       = $policy->getAllowedDirectories();
249
+				foreach ($dirs as $dir => $dummy) {
250
+					if (strpos($resourceId, $dir) === 0) {
251
+						break 2;
252
+					}
253
+				}
254
+				throw new SecurityException('The security policy prevents you to read <em>' . $resourceId . '</em>');
255
+			}
256
+		}
257 257
 
258
-        $class = 'Dwoo\Template\File';
259
-        if ($parentTemplate) {
260
-            $class = get_class($parentTemplate);
261
-        }
258
+		$class = 'Dwoo\Template\File';
259
+		if ($parentTemplate) {
260
+			$class = get_class($parentTemplate);
261
+		}
262 262
 
263
-        return new $class($resourceId, $cacheTime, $cacheId, $compileId, $includePath);
264
-    }
263
+		return new $class($resourceId, $cacheTime, $cacheId, $compileId, $includePath);
264
+	}
265 265
 
266
-    /**
267
-     * Returns some php code that will check if this template has been modified or not.
268
-     * if the function returns null, the template will be instanciated and then the Uid checked
269
-     *
270
-     * @return string
271
-     */
272
-    public function getIsModifiedCode()
273
-    {
274
-        return '"' . $this->getUid() . '" == filemtime(' . var_export($this->getResourceIdentifier(), true) . ')';
275
-    }
266
+	/**
267
+	 * Returns some php code that will check if this template has been modified or not.
268
+	 * if the function returns null, the template will be instanciated and then the Uid checked
269
+	 *
270
+	 * @return string
271
+	 */
272
+	public function getIsModifiedCode()
273
+	{
274
+		return '"' . $this->getUid() . '" == filemtime(' . var_export($this->getResourceIdentifier(), true) . ')';
275
+	}
276 276
 }
Please login to merge, or discard this patch.
lib/Dwoo/Template/Str.php 1 patch
Indentation   +503 added lines, -503 removed lines patch added patch discarded remove patch
@@ -29,507 +29,507 @@
 block discarded – undo
29 29
  */
30 30
 class Str implements ITemplate
31 31
 {
32
-    /**
33
-     * Template name.
34
-     *
35
-     * @var string
36
-     */
37
-    protected $name;
38
-
39
-    /**
40
-     * Template compilation id.
41
-     *
42
-     * @var string
43
-     */
44
-    protected $compileId;
45
-
46
-    /**
47
-     * Template cache id, if not provided in the constructor, it is set to
48
-     * the md4 hash of the request_uri. it is however highly recommended to
49
-     * provide one that will fit your needs.
50
-     * in all cases, the compilation id is prepended to the cache id to separate
51
-     * templates with similar cache ids from one another
52
-     *
53
-     * @var string
54
-     */
55
-    protected $cacheId;
56
-
57
-    /**
58
-     * Validity duration of the generated cache file (in seconds).
59
-     * set to -1 for infinite cache, 0 to disable and null to inherit the Dwoo instance's cache time
60
-     *
61
-     * @var int
62
-     */
63
-    protected $cacheTime;
64
-
65
-    /**
66
-     * Boolean flag that defines whether the compilation should be enforced (once) or
67
-     * not use this if you have issues with the compiled templates not being updated
68
-     * but if you do need this it's most likely that you should file a bug report.
69
-     *
70
-     * @var bool
71
-     */
72
-    protected $compilationEnforced;
73
-
74
-    /**
75
-     * Caches the results of the file checks to save some time when the same
76
-     * templates is rendered several times.
77
-     *
78
-     * @var array
79
-     */
80
-    protected static $cache = array(
81
-        'cached'   => array(),
82
-        'compiled' => array()
83
-    );
84
-
85
-    /**
86
-     * Holds the compiler that built this template.
87
-     *
88
-     * @var ICompiler
89
-     */
90
-    protected $compiler;
91
-
92
-    /**
93
-     * Chmod value for all files written (cached or compiled ones).
94
-     * set to null if you don't want any chmod operation to happen
95
-     *
96
-     * @var int
97
-     */
98
-    protected $chmod = 0777;
99
-
100
-    /**
101
-     * Containing template string.
102
-     *
103
-     * @var string
104
-     */
105
-    protected $template;
106
-
107
-    /**
108
-     * Creates a template from a string.
109
-     *
110
-     * @param string $templateString the template to use
111
-     * @param int    $cacheTime      duration of the cache validity for this template,
112
-     *                               if null it defaults to the Dwoo instance that will
113
-     *                               render this template, set to -1 for infinite cache or 0 to disable
114
-     * @param string $cacheId        the unique cache identifier of this page or anything else that
115
-     *                               makes this template's content unique, if null it defaults
116
-     *                               to the current url
117
-     * @param string $compileId      the unique compiled identifier, which is used to distinguish this
118
-     *                               template from others, if null it defaults to the md4 hash of the template
119
-     */
120
-    public function __construct($templateString, $cacheTime = null, $cacheId = null, $compileId = null)
121
-    {
122
-        $this->template  = $templateString;
123
-        $this->name      = hash('md4', $templateString);
124
-        $this->cacheTime = $cacheTime;
125
-
126
-        if ($compileId !== null) {
127
-            $this->compileId = str_replace('../', '__', strtr($compileId, '\\%?=!:;' . PATH_SEPARATOR, '/-------'));
128
-        } else {
129
-            $this->compileId = $templateString;
130
-        }
131
-
132
-        if ($cacheId !== null) {
133
-            $this->cacheId = str_replace('../', '__', strtr($cacheId, '\\%?=!:;' . PATH_SEPARATOR, '/-------'));
134
-        }
135
-    }
136
-
137
-    /**
138
-     * Returns the cache duration for this template.
139
-     * defaults to null if it was not provided
140
-     *
141
-     * @return int|null
142
-     */
143
-    public function getCacheTime()
144
-    {
145
-        return $this->cacheTime;
146
-    }
147
-
148
-    /**
149
-     * Sets the cache duration for this template.
150
-     * can be used to set it after the object is created if you did not provide
151
-     * it in the constructor
152
-     *
153
-     * @param int $seconds duration of the cache validity for this template, if
154
-     *                     null it defaults to the Dwoo instance's cache time. 0 = disable and
155
-     *                     -1 = infinite cache
156
-     */
157
-    public function setCacheTime($seconds = null)
158
-    {
159
-        $this->cacheTime = $seconds;
160
-    }
161
-
162
-    /**
163
-     * Returns the chmod value for all files written (cached or compiled ones).
164
-     * defaults to 0777
165
-     *
166
-     * @return int|null
167
-     */
168
-    public function getChmod()
169
-    {
170
-        return $this->chmod;
171
-    }
172
-
173
-    /**
174
-     * Set the chmod value for all files written (cached or compiled ones).
175
-     * set to null if you don't want to do any chmod() operation
176
-     *
177
-     * @param int $mask new bitmask to use for all files
178
-     */
179
-    public function setChmod($mask = null)
180
-    {
181
-        $this->chmod = $mask;
182
-    }
183
-
184
-    /**
185
-     * Returns the template name.
186
-     *
187
-     * @return string
188
-     */
189
-    public function getName()
190
-    {
191
-        return $this->name;
192
-    }
193
-
194
-    /**
195
-     * Returns the resource name for this template class.
196
-     *
197
-     * @return string
198
-     */
199
-    public function getResourceName()
200
-    {
201
-        return 'string';
202
-    }
203
-
204
-    /**
205
-     * Returns the resource identifier for this template, false here as strings don't have identifiers.
206
-     *
207
-     * @return false
208
-     */
209
-    public function getResourceIdentifier()
210
-    {
211
-        return false;
212
-    }
213
-
214
-    /**
215
-     * Returns the template source of this template.
216
-     *
217
-     * @return string
218
-     */
219
-    public function getSource()
220
-    {
221
-        return $this->template;
222
-    }
223
-
224
-    /**
225
-     * Returns an unique value identifying the current version of this template,
226
-     * in this case it's the md4 hash of the content.
227
-     *
228
-     * @return string
229
-     */
230
-    public function getUid()
231
-    {
232
-        return $this->name;
233
-    }
234
-
235
-    /**
236
-     * Returns the compiler used by this template, if it was just compiled, or null.
237
-     *
238
-     * @return ICompiler
239
-     */
240
-    public function getCompiler()
241
-    {
242
-        return $this->compiler;
243
-    }
244
-
245
-    /**
246
-     * Marks this template as compile-forced, which means it will be recompiled even if it
247
-     * was already saved and wasn't modified since the last compilation. do not use this in production,
248
-     * it's only meant to be used in development (and the development of dwoo particularly).
249
-     */
250
-    public function forceCompilation()
251
-    {
252
-        $this->compilationEnforced = true;
253
-    }
254
-
255
-    /**
256
-     * Returns the cached template output file name, true if it's cache-able but not cached
257
-     * or false if it's not cached.
258
-     *
259
-     * @param Core $core the dwoo instance that requests it
260
-     *
261
-     * @return string|bool
262
-     */
263
-    public function getCachedTemplate(Core $core)
264
-    {
265
-        $cacheLength = $core->getCacheTime();
266
-        if ($this->cacheTime !== null) {
267
-            $cacheLength = $this->cacheTime;
268
-        }
269
-
270
-        // file is not cacheable
271
-        if ($cacheLength == 0) {
272
-            return false;
273
-        }
274
-
275
-        $cachedFile = $this->getCacheFilename($core);
276
-
277
-        if (isset(self::$cache['cached'][$this->cacheId]) === true && file_exists($cachedFile)) {
278
-            // already checked, return cache file
279
-            return $cachedFile;
280
-        } elseif ($this->compilationEnforced !== true && file_exists($cachedFile) && ($cacheLength === - 1 || filemtime($cachedFile) > ($_SERVER['REQUEST_TIME'] - $cacheLength)) && $this->isValidCompiledFile($this->getCompiledFilename($core))) {
281
-            // cache is still valid and can be loaded
282
-            self::$cache['cached'][$this->cacheId] = true;
283
-
284
-            return $cachedFile;
285
-        }
286
-
287
-        // file is cacheable
288
-        return true;
289
-    }
290
-
291
-    /**
292
-     * Caches the provided output into the cache file.
293
-     *
294
-     * @param Core   $core   the dwoo instance that requests it
295
-     * @param string $output the template output
296
-     *
297
-     * @return mixed full path of the cached file or false upon failure
298
-     */
299
-    public function cache(Core $core, $output)
300
-    {
301
-        $cacheDir   = $core->getCacheDir();
302
-        $cachedFile = $this->getCacheFilename($core);
303
-
304
-        // the code below is courtesy of Rasmus Schultz,
305
-        // thanks for his help on avoiding concurency issues
306
-        $temp = tempnam($cacheDir, 'temp');
307
-        if (!($file = @fopen($temp, 'wb'))) {
308
-            $temp = $cacheDir . uniqid('temp');
309
-            if (!($file = @fopen($temp, 'wb'))) {
310
-                trigger_error('Error writing temporary file \'' . $temp . '\'', E_USER_WARNING);
311
-
312
-                return false;
313
-            }
314
-        }
315
-
316
-        fwrite($file, $output);
317
-        fclose($file);
318
-
319
-        $this->makeDirectory(dirname($cachedFile), $cacheDir);
320
-        if (!@rename($temp, $cachedFile)) {
321
-            @unlink($cachedFile);
322
-            @rename($temp, $cachedFile);
323
-        }
324
-
325
-        if ($this->chmod !== null) {
326
-            chmod($cachedFile, $this->chmod);
327
-        }
328
-
329
-        self::$cache['cached'][$this->cacheId] = true;
330
-
331
-        return $cachedFile;
332
-    }
333
-
334
-    /**
335
-     * Clears the cached template if it's older than the given time.
336
-     *
337
-     * @param Core $core      the dwoo instance that was used to cache that template
338
-     * @param int  $olderThan minimum time (in seconds) required for the cache to be cleared
339
-     *
340
-     * @return bool true if the cache was not present or if it was deleted, false if it remains there
341
-     */
342
-    public function clearCache(Core $core, $olderThan = - 1)
343
-    {
344
-        $cachedFile = $this->getCacheFilename($core);
345
-
346
-        return !file_exists($cachedFile) || (filectime($cachedFile) < (time() - $olderThan) && unlink($cachedFile));
347
-    }
348
-
349
-    /**
350
-     * Returns the compiled template file name.
351
-     *
352
-     * @param Core      $core     the dwoo instance that requests it
353
-     * @param ICompiler $compiler the compiler that must be used
354
-     *
355
-     * @return string
356
-     */
357
-    public function getCompiledTemplate(Core $core, ICompiler $compiler = null)
358
-    {
359
-        $compiledFile = $this->getCompiledFilename($core);
360
-
361
-        if ($this->compilationEnforced !== true && isset(self::$cache['compiled'][$this->compileId]) === true) {
362
-            // already checked, return compiled file
363
-        } elseif ($this->compilationEnforced !== true && $this->isValidCompiledFile($compiledFile)) {
364
-            // template is compiled
365
-            self::$cache['compiled'][$this->compileId] = true;
366
-        } else {
367
-            // compiles the template
368
-            $this->compilationEnforced = false;
369
-
370
-            if ($compiler === null) {
371
-                $compiler = $core->getDefaultCompilerFactory($this->getResourceName());
372
-
373
-                if ($compiler === null || $compiler === array('Dwoo\Compiler', 'compilerFactory')) {
374
-                    $compiler = Compiler::compilerFactory();
375
-                } else {
376
-                    $compiler = call_user_func($compiler);
377
-                }
378
-            }
379
-
380
-            $this->compiler = $compiler;
381
-
382
-            $compiler->setCustomPlugins($core->getCustomPlugins());
383
-            $compiler->setSecurityPolicy($core->getSecurityPolicy());
384
-            $this->makeDirectory(dirname($compiledFile), $core->getCompileDir());
385
-            file_put_contents($compiledFile, $compiler->compile($core, $this));
386
-            if ($this->chmod !== null) {
387
-                chmod($compiledFile, $this->chmod);
388
-            }
389
-
390
-            if (extension_loaded('Zend OPcache')) {
391
-                opcache_invalidate($compiledFile);
392
-            } elseif (extension_loaded('apc') && ini_get('apc.enabled')) {
393
-                apc_delete_file($compiledFile);
394
-            }
395
-
396
-            self::$cache['compiled'][$this->compileId] = true;
397
-        }
398
-
399
-        return $compiledFile;
400
-    }
401
-
402
-    /**
403
-     * Checks if compiled file is valid (it exists).
404
-     *
405
-     * @param string $file
406
-     *
407
-     * @return bool True cache file existence
408
-     */
409
-    protected function isValidCompiledFile($file)
410
-    {
411
-        return file_exists($file);
412
-    }
413
-
414
-    /**
415
-     * Returns a new template string object with the resource id being the template source code.
416
-     *
417
-     * @param Core      $core           the dwoo instance requiring it
418
-     * @param mixed     $resourceId     the filename (relative to this template's dir) of the template to include
419
-     * @param int       $cacheTime      duration of the cache validity for this template, if null it defaults to the
420
-     *                                  Dwoo instance that will render this template if null it defaults to the Dwoo
421
-     *                                  instance that will render this template
422
-     * @param string    $cacheId        the unique cache identifier of this page or anything else that makes this
423
-     *                                  template's content unique, if null it defaults to the current url makes this
424
-     *                                  template's content unique, if null it defaults to the current url
425
-     * @param string    $compileId      the unique compiled identifier, which is used to distinguish this template from
426
-     *                                  others, if null it defaults to the filename+bits of the path template from
427
-     *                                  others, if null it defaults to the filename+bits of the path
428
-     * @param ITemplate $parentTemplate the template that is requesting a new template object (through an include,
429
-     *                                  extends or any other plugin) an include, extends or any other plugin)
430
-     *
431
-     * @return $this
432
-     */
433
-    public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null,
434
-                                           $compileId = null, ITemplate $parentTemplate = null)
435
-    {
436
-        return new self($resourceId, $cacheTime, $cacheId, $compileId);
437
-    }
438
-
439
-    /**
440
-     * Returns the full compiled file name and assigns a default value to it if
441
-     * required.
442
-     *
443
-     * @param Core $core the Core instance that requests the file name
444
-     *
445
-     * @return string the full path to the compiled file
446
-     */
447
-    protected function getCompiledFilename(Core $core)
448
-    {
449
-        return $core->getCompileDir() . hash('md4', $this->compileId) . '.d' . Core::RELEASE_TAG . '.php';
450
-    }
451
-
452
-    /**
453
-     * Returns the full cached file name and assigns a default value to it if
454
-     * required.
455
-     *
456
-     * @param Core $core the dwoo instance that requests the file name
457
-     *
458
-     * @return string the full path to the cached file
459
-     */
460
-    protected function getCacheFilename(Core $core)
461
-    {
462
-        // no cache id provided, use request_uri as default
463
-        if ($this->cacheId === null) {
464
-            if (isset($_SERVER['REQUEST_URI']) === true) {
465
-                $cacheId = $_SERVER['REQUEST_URI'];
466
-            } elseif (isset($_SERVER['SCRIPT_FILENAME']) && isset($_SERVER['argv'])) {
467
-                $cacheId = $_SERVER['SCRIPT_FILENAME'] . '-' . implode('-', $_SERVER['argv']);
468
-            } else {
469
-                $cacheId = '';
470
-            }
471
-            // force compiled id generation
472
-            $this->getCompiledFilename($core);
473
-
474
-            $this->cacheId = str_replace('../', '__',
475
-                $this->compileId . strtr($cacheId, '\\%?=!:;' . PATH_SEPARATOR, '/-------'));
476
-        }
477
-
478
-        return $core->getCacheDir() . $this->cacheId . '.html';
479
-    }
480
-
481
-    /**
482
-     * Returns some php code that will check if this template has been modified or not.
483
-     * if the function returns null, the template will be instanciated and then the Uid checked
484
-     *
485
-     * @return string
486
-     */
487
-    public function getIsModifiedCode()
488
-    {
489
-        return null;
490
-    }
491
-
492
-    /**
493
-     * Ensures the given path exists.
494
-     *
495
-     * @param string $path    any path
496
-     * @param string $baseDir the base directory where the directory is created
497
-     *                        ($path must still contain the full path, $baseDir
498
-     *                        is only used for unix permissions)
499
-     *
500
-     * @throws Exception
501
-     */
502
-    protected function makeDirectory($path, $baseDir = null)
503
-    {
504
-        if (is_dir($path) === true) {
505
-            return;
506
-        }
507
-
508
-        if ($this->chmod === null) {
509
-            $chmod = 0777;
510
-        } else {
511
-            $chmod = $this->chmod;
512
-        }
513
-
514
-        $retries = 3;
515
-        while ($retries --) {
516
-            @mkdir($path, $chmod, true);
517
-            if (is_dir($path)) {
518
-                break;
519
-            }
520
-            usleep(20);
521
-        }
522
-
523
-        // enforce the correct mode for all directories created
524
-        if (strpos(PHP_OS, 'WIN') !== 0 && $baseDir !== null) {
525
-            $path    = strtr(str_replace($baseDir, '', $path), '\\', '/');
526
-            $folders = explode('/', trim($path, '/'));
527
-            foreach ($folders as $folder) {
528
-                $baseDir .= $folder . DIRECTORY_SEPARATOR;
529
-                if (!chmod($baseDir, $chmod)) {
530
-                    throw new Exception('Unable to chmod ' . "$baseDir to $chmod: " . print_r(error_get_last(), true));
531
-                }
532
-            }
533
-        }
534
-    }
32
+	/**
33
+	 * Template name.
34
+	 *
35
+	 * @var string
36
+	 */
37
+	protected $name;
38
+
39
+	/**
40
+	 * Template compilation id.
41
+	 *
42
+	 * @var string
43
+	 */
44
+	protected $compileId;
45
+
46
+	/**
47
+	 * Template cache id, if not provided in the constructor, it is set to
48
+	 * the md4 hash of the request_uri. it is however highly recommended to
49
+	 * provide one that will fit your needs.
50
+	 * in all cases, the compilation id is prepended to the cache id to separate
51
+	 * templates with similar cache ids from one another
52
+	 *
53
+	 * @var string
54
+	 */
55
+	protected $cacheId;
56
+
57
+	/**
58
+	 * Validity duration of the generated cache file (in seconds).
59
+	 * set to -1 for infinite cache, 0 to disable and null to inherit the Dwoo instance's cache time
60
+	 *
61
+	 * @var int
62
+	 */
63
+	protected $cacheTime;
64
+
65
+	/**
66
+	 * Boolean flag that defines whether the compilation should be enforced (once) or
67
+	 * not use this if you have issues with the compiled templates not being updated
68
+	 * but if you do need this it's most likely that you should file a bug report.
69
+	 *
70
+	 * @var bool
71
+	 */
72
+	protected $compilationEnforced;
73
+
74
+	/**
75
+	 * Caches the results of the file checks to save some time when the same
76
+	 * templates is rendered several times.
77
+	 *
78
+	 * @var array
79
+	 */
80
+	protected static $cache = array(
81
+		'cached'   => array(),
82
+		'compiled' => array()
83
+	);
84
+
85
+	/**
86
+	 * Holds the compiler that built this template.
87
+	 *
88
+	 * @var ICompiler
89
+	 */
90
+	protected $compiler;
91
+
92
+	/**
93
+	 * Chmod value for all files written (cached or compiled ones).
94
+	 * set to null if you don't want any chmod operation to happen
95
+	 *
96
+	 * @var int
97
+	 */
98
+	protected $chmod = 0777;
99
+
100
+	/**
101
+	 * Containing template string.
102
+	 *
103
+	 * @var string
104
+	 */
105
+	protected $template;
106
+
107
+	/**
108
+	 * Creates a template from a string.
109
+	 *
110
+	 * @param string $templateString the template to use
111
+	 * @param int    $cacheTime      duration of the cache validity for this template,
112
+	 *                               if null it defaults to the Dwoo instance that will
113
+	 *                               render this template, set to -1 for infinite cache or 0 to disable
114
+	 * @param string $cacheId        the unique cache identifier of this page or anything else that
115
+	 *                               makes this template's content unique, if null it defaults
116
+	 *                               to the current url
117
+	 * @param string $compileId      the unique compiled identifier, which is used to distinguish this
118
+	 *                               template from others, if null it defaults to the md4 hash of the template
119
+	 */
120
+	public function __construct($templateString, $cacheTime = null, $cacheId = null, $compileId = null)
121
+	{
122
+		$this->template  = $templateString;
123
+		$this->name      = hash('md4', $templateString);
124
+		$this->cacheTime = $cacheTime;
125
+
126
+		if ($compileId !== null) {
127
+			$this->compileId = str_replace('../', '__', strtr($compileId, '\\%?=!:;' . PATH_SEPARATOR, '/-------'));
128
+		} else {
129
+			$this->compileId = $templateString;
130
+		}
131
+
132
+		if ($cacheId !== null) {
133
+			$this->cacheId = str_replace('../', '__', strtr($cacheId, '\\%?=!:;' . PATH_SEPARATOR, '/-------'));
134
+		}
135
+	}
136
+
137
+	/**
138
+	 * Returns the cache duration for this template.
139
+	 * defaults to null if it was not provided
140
+	 *
141
+	 * @return int|null
142
+	 */
143
+	public function getCacheTime()
144
+	{
145
+		return $this->cacheTime;
146
+	}
147
+
148
+	/**
149
+	 * Sets the cache duration for this template.
150
+	 * can be used to set it after the object is created if you did not provide
151
+	 * it in the constructor
152
+	 *
153
+	 * @param int $seconds duration of the cache validity for this template, if
154
+	 *                     null it defaults to the Dwoo instance's cache time. 0 = disable and
155
+	 *                     -1 = infinite cache
156
+	 */
157
+	public function setCacheTime($seconds = null)
158
+	{
159
+		$this->cacheTime = $seconds;
160
+	}
161
+
162
+	/**
163
+	 * Returns the chmod value for all files written (cached or compiled ones).
164
+	 * defaults to 0777
165
+	 *
166
+	 * @return int|null
167
+	 */
168
+	public function getChmod()
169
+	{
170
+		return $this->chmod;
171
+	}
172
+
173
+	/**
174
+	 * Set the chmod value for all files written (cached or compiled ones).
175
+	 * set to null if you don't want to do any chmod() operation
176
+	 *
177
+	 * @param int $mask new bitmask to use for all files
178
+	 */
179
+	public function setChmod($mask = null)
180
+	{
181
+		$this->chmod = $mask;
182
+	}
183
+
184
+	/**
185
+	 * Returns the template name.
186
+	 *
187
+	 * @return string
188
+	 */
189
+	public function getName()
190
+	{
191
+		return $this->name;
192
+	}
193
+
194
+	/**
195
+	 * Returns the resource name for this template class.
196
+	 *
197
+	 * @return string
198
+	 */
199
+	public function getResourceName()
200
+	{
201
+		return 'string';
202
+	}
203
+
204
+	/**
205
+	 * Returns the resource identifier for this template, false here as strings don't have identifiers.
206
+	 *
207
+	 * @return false
208
+	 */
209
+	public function getResourceIdentifier()
210
+	{
211
+		return false;
212
+	}
213
+
214
+	/**
215
+	 * Returns the template source of this template.
216
+	 *
217
+	 * @return string
218
+	 */
219
+	public function getSource()
220
+	{
221
+		return $this->template;
222
+	}
223
+
224
+	/**
225
+	 * Returns an unique value identifying the current version of this template,
226
+	 * in this case it's the md4 hash of the content.
227
+	 *
228
+	 * @return string
229
+	 */
230
+	public function getUid()
231
+	{
232
+		return $this->name;
233
+	}
234
+
235
+	/**
236
+	 * Returns the compiler used by this template, if it was just compiled, or null.
237
+	 *
238
+	 * @return ICompiler
239
+	 */
240
+	public function getCompiler()
241
+	{
242
+		return $this->compiler;
243
+	}
244
+
245
+	/**
246
+	 * Marks this template as compile-forced, which means it will be recompiled even if it
247
+	 * was already saved and wasn't modified since the last compilation. do not use this in production,
248
+	 * it's only meant to be used in development (and the development of dwoo particularly).
249
+	 */
250
+	public function forceCompilation()
251
+	{
252
+		$this->compilationEnforced = true;
253
+	}
254
+
255
+	/**
256
+	 * Returns the cached template output file name, true if it's cache-able but not cached
257
+	 * or false if it's not cached.
258
+	 *
259
+	 * @param Core $core the dwoo instance that requests it
260
+	 *
261
+	 * @return string|bool
262
+	 */
263
+	public function getCachedTemplate(Core $core)
264
+	{
265
+		$cacheLength = $core->getCacheTime();
266
+		if ($this->cacheTime !== null) {
267
+			$cacheLength = $this->cacheTime;
268
+		}
269
+
270
+		// file is not cacheable
271
+		if ($cacheLength == 0) {
272
+			return false;
273
+		}
274
+
275
+		$cachedFile = $this->getCacheFilename($core);
276
+
277
+		if (isset(self::$cache['cached'][$this->cacheId]) === true && file_exists($cachedFile)) {
278
+			// already checked, return cache file
279
+			return $cachedFile;
280
+		} elseif ($this->compilationEnforced !== true && file_exists($cachedFile) && ($cacheLength === - 1 || filemtime($cachedFile) > ($_SERVER['REQUEST_TIME'] - $cacheLength)) && $this->isValidCompiledFile($this->getCompiledFilename($core))) {
281
+			// cache is still valid and can be loaded
282
+			self::$cache['cached'][$this->cacheId] = true;
283
+
284
+			return $cachedFile;
285
+		}
286
+
287
+		// file is cacheable
288
+		return true;
289
+	}
290
+
291
+	/**
292
+	 * Caches the provided output into the cache file.
293
+	 *
294
+	 * @param Core   $core   the dwoo instance that requests it
295
+	 * @param string $output the template output
296
+	 *
297
+	 * @return mixed full path of the cached file or false upon failure
298
+	 */
299
+	public function cache(Core $core, $output)
300
+	{
301
+		$cacheDir   = $core->getCacheDir();
302
+		$cachedFile = $this->getCacheFilename($core);
303
+
304
+		// the code below is courtesy of Rasmus Schultz,
305
+		// thanks for his help on avoiding concurency issues
306
+		$temp = tempnam($cacheDir, 'temp');
307
+		if (!($file = @fopen($temp, 'wb'))) {
308
+			$temp = $cacheDir . uniqid('temp');
309
+			if (!($file = @fopen($temp, 'wb'))) {
310
+				trigger_error('Error writing temporary file \'' . $temp . '\'', E_USER_WARNING);
311
+
312
+				return false;
313
+			}
314
+		}
315
+
316
+		fwrite($file, $output);
317
+		fclose($file);
318
+
319
+		$this->makeDirectory(dirname($cachedFile), $cacheDir);
320
+		if (!@rename($temp, $cachedFile)) {
321
+			@unlink($cachedFile);
322
+			@rename($temp, $cachedFile);
323
+		}
324
+
325
+		if ($this->chmod !== null) {
326
+			chmod($cachedFile, $this->chmod);
327
+		}
328
+
329
+		self::$cache['cached'][$this->cacheId] = true;
330
+
331
+		return $cachedFile;
332
+	}
333
+
334
+	/**
335
+	 * Clears the cached template if it's older than the given time.
336
+	 *
337
+	 * @param Core $core      the dwoo instance that was used to cache that template
338
+	 * @param int  $olderThan minimum time (in seconds) required for the cache to be cleared
339
+	 *
340
+	 * @return bool true if the cache was not present or if it was deleted, false if it remains there
341
+	 */
342
+	public function clearCache(Core $core, $olderThan = - 1)
343
+	{
344
+		$cachedFile = $this->getCacheFilename($core);
345
+
346
+		return !file_exists($cachedFile) || (filectime($cachedFile) < (time() - $olderThan) && unlink($cachedFile));
347
+	}
348
+
349
+	/**
350
+	 * Returns the compiled template file name.
351
+	 *
352
+	 * @param Core      $core     the dwoo instance that requests it
353
+	 * @param ICompiler $compiler the compiler that must be used
354
+	 *
355
+	 * @return string
356
+	 */
357
+	public function getCompiledTemplate(Core $core, ICompiler $compiler = null)
358
+	{
359
+		$compiledFile = $this->getCompiledFilename($core);
360
+
361
+		if ($this->compilationEnforced !== true && isset(self::$cache['compiled'][$this->compileId]) === true) {
362
+			// already checked, return compiled file
363
+		} elseif ($this->compilationEnforced !== true && $this->isValidCompiledFile($compiledFile)) {
364
+			// template is compiled
365
+			self::$cache['compiled'][$this->compileId] = true;
366
+		} else {
367
+			// compiles the template
368
+			$this->compilationEnforced = false;
369
+
370
+			if ($compiler === null) {
371
+				$compiler = $core->getDefaultCompilerFactory($this->getResourceName());
372
+
373
+				if ($compiler === null || $compiler === array('Dwoo\Compiler', 'compilerFactory')) {
374
+					$compiler = Compiler::compilerFactory();
375
+				} else {
376
+					$compiler = call_user_func($compiler);
377
+				}
378
+			}
379
+
380
+			$this->compiler = $compiler;
381
+
382
+			$compiler->setCustomPlugins($core->getCustomPlugins());
383
+			$compiler->setSecurityPolicy($core->getSecurityPolicy());
384
+			$this->makeDirectory(dirname($compiledFile), $core->getCompileDir());
385
+			file_put_contents($compiledFile, $compiler->compile($core, $this));
386
+			if ($this->chmod !== null) {
387
+				chmod($compiledFile, $this->chmod);
388
+			}
389
+
390
+			if (extension_loaded('Zend OPcache')) {
391
+				opcache_invalidate($compiledFile);
392
+			} elseif (extension_loaded('apc') && ini_get('apc.enabled')) {
393
+				apc_delete_file($compiledFile);
394
+			}
395
+
396
+			self::$cache['compiled'][$this->compileId] = true;
397
+		}
398
+
399
+		return $compiledFile;
400
+	}
401
+
402
+	/**
403
+	 * Checks if compiled file is valid (it exists).
404
+	 *
405
+	 * @param string $file
406
+	 *
407
+	 * @return bool True cache file existence
408
+	 */
409
+	protected function isValidCompiledFile($file)
410
+	{
411
+		return file_exists($file);
412
+	}
413
+
414
+	/**
415
+	 * Returns a new template string object with the resource id being the template source code.
416
+	 *
417
+	 * @param Core      $core           the dwoo instance requiring it
418
+	 * @param mixed     $resourceId     the filename (relative to this template's dir) of the template to include
419
+	 * @param int       $cacheTime      duration of the cache validity for this template, if null it defaults to the
420
+	 *                                  Dwoo instance that will render this template if null it defaults to the Dwoo
421
+	 *                                  instance that will render this template
422
+	 * @param string    $cacheId        the unique cache identifier of this page or anything else that makes this
423
+	 *                                  template's content unique, if null it defaults to the current url makes this
424
+	 *                                  template's content unique, if null it defaults to the current url
425
+	 * @param string    $compileId      the unique compiled identifier, which is used to distinguish this template from
426
+	 *                                  others, if null it defaults to the filename+bits of the path template from
427
+	 *                                  others, if null it defaults to the filename+bits of the path
428
+	 * @param ITemplate $parentTemplate the template that is requesting a new template object (through an include,
429
+	 *                                  extends or any other plugin) an include, extends or any other plugin)
430
+	 *
431
+	 * @return $this
432
+	 */
433
+	public static function templateFactory(Core $core, $resourceId, $cacheTime = null, $cacheId = null,
434
+										   $compileId = null, ITemplate $parentTemplate = null)
435
+	{
436
+		return new self($resourceId, $cacheTime, $cacheId, $compileId);
437
+	}
438
+
439
+	/**
440
+	 * Returns the full compiled file name and assigns a default value to it if
441
+	 * required.
442
+	 *
443
+	 * @param Core $core the Core instance that requests the file name
444
+	 *
445
+	 * @return string the full path to the compiled file
446
+	 */
447
+	protected function getCompiledFilename(Core $core)
448
+	{
449
+		return $core->getCompileDir() . hash('md4', $this->compileId) . '.d' . Core::RELEASE_TAG . '.php';
450
+	}
451
+
452
+	/**
453
+	 * Returns the full cached file name and assigns a default value to it if
454
+	 * required.
455
+	 *
456
+	 * @param Core $core the dwoo instance that requests the file name
457
+	 *
458
+	 * @return string the full path to the cached file
459
+	 */
460
+	protected function getCacheFilename(Core $core)
461
+	{
462
+		// no cache id provided, use request_uri as default
463
+		if ($this->cacheId === null) {
464
+			if (isset($_SERVER['REQUEST_URI']) === true) {
465
+				$cacheId = $_SERVER['REQUEST_URI'];
466
+			} elseif (isset($_SERVER['SCRIPT_FILENAME']) && isset($_SERVER['argv'])) {
467
+				$cacheId = $_SERVER['SCRIPT_FILENAME'] . '-' . implode('-', $_SERVER['argv']);
468
+			} else {
469
+				$cacheId = '';
470
+			}
471
+			// force compiled id generation
472
+			$this->getCompiledFilename($core);
473
+
474
+			$this->cacheId = str_replace('../', '__',
475
+				$this->compileId . strtr($cacheId, '\\%?=!:;' . PATH_SEPARATOR, '/-------'));
476
+		}
477
+
478
+		return $core->getCacheDir() . $this->cacheId . '.html';
479
+	}
480
+
481
+	/**
482
+	 * Returns some php code that will check if this template has been modified or not.
483
+	 * if the function returns null, the template will be instanciated and then the Uid checked
484
+	 *
485
+	 * @return string
486
+	 */
487
+	public function getIsModifiedCode()
488
+	{
489
+		return null;
490
+	}
491
+
492
+	/**
493
+	 * Ensures the given path exists.
494
+	 *
495
+	 * @param string $path    any path
496
+	 * @param string $baseDir the base directory where the directory is created
497
+	 *                        ($path must still contain the full path, $baseDir
498
+	 *                        is only used for unix permissions)
499
+	 *
500
+	 * @throws Exception
501
+	 */
502
+	protected function makeDirectory($path, $baseDir = null)
503
+	{
504
+		if (is_dir($path) === true) {
505
+			return;
506
+		}
507
+
508
+		if ($this->chmod === null) {
509
+			$chmod = 0777;
510
+		} else {
511
+			$chmod = $this->chmod;
512
+		}
513
+
514
+		$retries = 3;
515
+		while ($retries --) {
516
+			@mkdir($path, $chmod, true);
517
+			if (is_dir($path)) {
518
+				break;
519
+			}
520
+			usleep(20);
521
+		}
522
+
523
+		// enforce the correct mode for all directories created
524
+		if (strpos(PHP_OS, 'WIN') !== 0 && $baseDir !== null) {
525
+			$path    = strtr(str_replace($baseDir, '', $path), '\\', '/');
526
+			$folders = explode('/', trim($path, '/'));
527
+			foreach ($folders as $folder) {
528
+				$baseDir .= $folder . DIRECTORY_SEPARATOR;
529
+				if (!chmod($baseDir, $chmod)) {
530
+					throw new Exception('Unable to chmod ' . "$baseDir to $chmod: " . print_r(error_get_last(), true));
531
+				}
532
+			}
533
+		}
534
+	}
535 535
 }
Please login to merge, or discard this patch.
lib/Dwoo/Compiler.php 2 patches
Indentation   +3628 added lines, -3628 removed lines patch added patch discarded remove patch
@@ -31,3634 +31,3634 @@
 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::CLASS_PLUGIN:
879
-                case Core::CLASS_PLUGIN + Core::BLOCK_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::CLASS_PLUGIN + Core::FUNC_PLUGIN:
889
-                    if (class_exists('Plugin' . $plugin) !== false) {
890
-                        $output .= "if (class_exists('" . "Plugin" . $plugin . "')===false)".
891
-                            "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
892
-                    } else {
893
-                        $output .= "if (class_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)".
894
-                            "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
895
-                    }
896
-                    break;
897
-                case Core::FUNC_PLUGIN:
898
-                    if (function_exists('Plugin' . $plugin) !== false) {
899
-                        $output .= "if (function_exists('" . "Plugin" . $plugin . "')===false)".
900
-                        "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
901
-                    } else {
902
-                        $output .= "if (function_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)".
903
-                        "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
904
-                    }
905
-                    break;
906
-                case Core::SMARTY_MODIFIER:
907
-                    $output .= "if (function_exists('smarty_modifier_$plugin')===false)".
908
-                    "\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
909
-                    break;
910
-                case Core::SMARTY_FUNCTION:
911
-                    $output .= "if (function_exists('smarty_function_$plugin')===false)".
912
-                    "\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
913
-                    break;
914
-                case Core::SMARTY_BLOCK:
915
-                    $output .= "if (function_exists('smarty_block_$plugin')===false)".
916
-                    "\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
917
-                    break;
918
-                case Core::PROXY_PLUGIN:
919
-                    $output .= $this->getDwoo()->getPluginProxy()->getLoader($plugin);
920
-                    break;
921
-                default:
922
-                    throw new CompilationException($this, 'Type error for ' . $plugin . ' with type' . $type);
923
-            }
924
-        }
925
-
926
-        foreach ($this->templatePlugins as $function => $attr) {
927
-            if (isset($attr['called']) && $attr['called'] === true && !isset($attr['checked'])) {
928
-                $this->resolveSubTemplateDependencies($function);
929
-            }
930
-        }
931
-        foreach ($this->templatePlugins as $function) {
932
-            if (isset($function['called']) && $function['called'] === true) {
933
-                $output .= $function['body'] . PHP_EOL;
934
-            }
935
-        }
936
-
937
-        $output .= $compiled . "\n?>";
938
-
939
-        $output = preg_replace('/(?<!;|\}|\*\/|\n|\{)(\s*' . preg_quote(self::PHP_CLOSE, '/') . preg_quote(self::PHP_OPEN, '/') . ')/', ";\n", $output);
940
-        $output = str_replace(self::PHP_CLOSE . self::PHP_OPEN, "\n", $output);
941
-
942
-        // handle <?xml tag at the beginning
943
-        $output = preg_replace('#(/\* template body \*/ \?>\s*)<\?xml#is', '$1<?php echo \'<?xml\'; ?>', $output);
944
-
945
-        // add another line break after PHP closing tags that have a line break following,
946
-        // as we do not know whether it's intended, and PHP will strip it otherwise
947
-        $output = preg_replace('/(?<!"|<\?xml)\s*\?>\n/', '$0' . "\n", $output);
948
-
949
-        if ($this->debug) {
950
-            echo '=============================================================================================' . "\n";
951
-            $lines = preg_split('{\r\n|\n|<br />}', $output);
952
-            array_shift($lines);
953
-            foreach ($lines as $i => $line) {
954
-                echo ($i + 1) . '. ' . $line . "\r\n";
955
-            }
956
-            echo '=============================================================================================' . "\n";
957
-        }
958
-
959
-        $this->template = $this->dwoo = null;
960
-        $tpl            = null;
961
-
962
-        return $output;
963
-    }
964
-
965
-    /**
966
-     * Checks what sub-templates are used in every sub-template so that we're sure they are all compiled.
967
-     *
968
-     * @param string $function the sub-template name
969
-     */
970
-    protected function resolveSubTemplateDependencies($function)
971
-    {
972
-        if ($this->debug) {
973
-            echo 'Compiler::' . __FUNCTION__ . "\n";
974
-        }
975
-
976
-        $body = $this->templatePlugins[$function]['body'];
977
-        foreach ($this->templatePlugins as $func => $attr) {
978
-            if ($func !== $function && !isset($attr['called']) && strpos($body, Core::NAMESPACE_PLUGINS_FUNCTIONS .
979
-            'Plugin' . Core::toCamelCase($func)) !== false) {
980
-                $this->templatePlugins[$func]['called'] = true;
981
-                $this->resolveSubTemplateDependencies($func);
982
-            }
983
-        }
984
-        $this->templatePlugins[$function]['checked'] = true;
985
-    }
986
-
987
-    /**
988
-     * Adds compiled content to the current block.
989
-     *
990
-     * @param string $content   the content to push
991
-     * @param int    $lineCount newlines count in content, optional
992
-     *
993
-     * @throws CompilationException
994
-     */
995
-    public function push($content, $lineCount = null)
996
-    {
997
-        if ($lineCount === null) {
998
-            $lineCount = substr_count($content, "\n");
999
-        }
1000
-
1001
-        if ($this->curBlock['buffer'] === null && count($this->stack) > 1) {
1002
-            // buffer is not initialized yet (the block has just been created)
1003
-            $this->stack[count($this->stack) - 2]['buffer'] .= (string)$content;
1004
-            $this->curBlock['buffer'] = '';
1005
-        } else {
1006
-            if (!isset($this->curBlock['buffer'])) {
1007
-                throw new CompilationException($this, 'The template has been closed too early, you probably have an extra block-closing tag somewhere');
1008
-            }
1009
-            // append current content to current block's buffer
1010
-            $this->curBlock['buffer'] .= (string)$content;
1011
-        }
1012
-        $this->line += $lineCount;
1013
-    }
1014
-
1015
-    /**
1016
-     * Sets the scope.
1017
-     * set to null if the scope becomes "unstable" (i.e. too variable or unknown) so that
1018
-     * variables are compiled in a more evaluative way than just $this->scope['key']
1019
-     *
1020
-     * @param mixed $scope    a string i.e. "level1.level2" or an array i.e. array("level1", "level2")
1021
-     * @param bool  $absolute if true, the scope is set from the top level scope and not from the current scope
1022
-     *
1023
-     * @return array the current scope tree
1024
-     */
1025
-    public function setScope($scope, $absolute = false)
1026
-    {
1027
-        $old = $this->scopeTree;
1028
-
1029
-        if ($scope === null) {
1030
-            unset($this->scope);
1031
-            $this->scope = null;
1032
-        }
1033
-
1034
-        if (is_array($scope) === false) {
1035
-            $scope = explode('.', $scope);
1036
-        }
1037
-
1038
-        if ($absolute === true) {
1039
-            $this->scope     = &$this->data;
1040
-            $this->scopeTree = array();
1041
-        }
1042
-
1043
-        while (($bit = array_shift($scope)) !== null) {
1044
-            if ($bit === '_parent' || $bit === '_') {
1045
-                array_pop($this->scopeTree);
1046
-                reset($this->scopeTree);
1047
-                $this->scope = &$this->data;
1048
-                $cnt         = count($this->scopeTree);
1049
-                for ($i = 0; $i < $cnt; ++ $i) {
1050
-                    $this->scope = &$this->scope[$this->scopeTree[$i]];
1051
-                }
1052
-            } elseif ($bit === '_root' || $bit === '__') {
1053
-                $this->scope     = &$this->data;
1054
-                $this->scopeTree = array();
1055
-            } elseif (isset($this->scope[$bit])) {
1056
-                $this->scope       = &$this->scope[$bit];
1057
-                $this->scopeTree[] = $bit;
1058
-            } else {
1059
-                $this->scope[$bit] = array();
1060
-                $this->scope       = &$this->scope[$bit];
1061
-                $this->scopeTree[] = $bit;
1062
-            }
1063
-        }
1064
-
1065
-        return $old;
1066
-    }
1067
-
1068
-    /**
1069
-     * Adds a block to the top of the block stack.
1070
-     *
1071
-     * @param string $type      block type (name)
1072
-     * @param array  $params    the parameters array
1073
-     * @param int    $paramtype the parameters type (see mapParams), 0, 1 or 2
1074
-     *
1075
-     * @return string the preProcessing() method's output
1076
-     */
1077
-    public function addBlock($type, array $params, $paramtype)
1078
-    {
1079
-        if ($this->debug) {
1080
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1081
-        }
1082
-
1083
-        $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type);
1084
-        if (class_exists($class) === false) {
1085
-            $this->getDwoo()->getLoader()->loadPlugin($type);
1086
-        }
1087
-        $params = $this->mapParams($params, array($class, 'init'), $paramtype);
1088
-
1089
-        $this->stack[]  = array(
1090
-            'type'   => $type,
1091
-            'params' => $params,
1092
-            'custom' => false,
1093
-            'class'  => $class,
1094
-            'buffer' => null
1095
-        );
1096
-        $this->curBlock = &$this->stack[count($this->stack) - 1];
1097
-
1098
-        return call_user_func(array($class, 'preProcessing'), $this, $params, '', '', $type);
1099
-    }
1100
-
1101
-    /**
1102
-     * Adds a custom block to the top of the block stack.
1103
-     *
1104
-     * @param string $type      block type (name)
1105
-     * @param array  $params    the parameters array
1106
-     * @param int    $paramtype the parameters type (see mapParams), 0, 1 or 2
1107
-     *
1108
-     * @return string the preProcessing() method's output
1109
-     */
1110
-    public function addCustomBlock($type, array $params, $paramtype)
1111
-    {
1112
-        $callback = $this->customPlugins[$type]['callback'];
1113
-        if (is_array($callback)) {
1114
-            $class = is_object($callback[0]) ? get_class($callback[0]) : $callback[0];
1115
-        } else {
1116
-            $class = $callback;
1117
-        }
1118
-
1119
-        $params = $this->mapParams($params, array($class, 'init'), $paramtype);
1120
-
1121
-        $this->stack[]  = array(
1122
-            'type'   => $type,
1123
-            'params' => $params,
1124
-            'custom' => true,
1125
-            'class'  => $class,
1126
-            'buffer' => null
1127
-        );
1128
-        $this->curBlock = &$this->stack[count($this->stack) - 1];
1129
-
1130
-        return call_user_func(array($class, 'preProcessing'), $this, $params, '', '', $type);
1131
-    }
1132
-
1133
-    /**
1134
-     * Injects a block at the top of the plugin stack without calling its preProcessing method.
1135
-     * used by {else} blocks to re-add themselves after having closed everything up to their parent
1136
-     *
1137
-     * @param string $type   block type (name)
1138
-     * @param array  $params parameters array
1139
-     */
1140
-    public function injectBlock($type, array $params)
1141
-    {
1142
-        if ($this->debug) {
1143
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1144
-        }
1145
-
1146
-        $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type);
1147
-        if (class_exists($class) === false) {
1148
-            $this->getDwoo()->getLoader()->loadPlugin($type);
1149
-        }
1150
-        $this->stack[]  = array(
1151
-            'type'   => $type,
1152
-            'params' => $params,
1153
-            'custom' => false,
1154
-            'class'  => $class,
1155
-            'buffer' => null
1156
-        );
1157
-        $this->curBlock = &$this->stack[count($this->stack) - 1];
1158
-    }
1159
-
1160
-    /**
1161
-     * Removes the closest-to-top block of the given type and all other
1162
-     * blocks encountered while going down the block stack.
1163
-     *
1164
-     * @param string $type block type (name)
1165
-     *
1166
-     * @return string the output of all postProcessing() method's return values of the closed blocks
1167
-     * @throws CompilationException
1168
-     */
1169
-    public function removeBlock($type)
1170
-    {
1171
-        if ($this->debug) {
1172
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1173
-        }
1174
-
1175
-        $output = '';
1176
-
1177
-        $pluginType = $this->getPluginType($type);
1178
-        if ($pluginType & Core::SMARTY_BLOCK) {
1179
-            $type = 'Smartyinterface';
1180
-        }
1181
-        while (true) {
1182
-            while ($top = array_pop($this->stack)) {
1183
-                if ($top['custom']) {
1184
-                    $class = $top['class'];
1185
-                } else {
1186
-                    $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($top['type']);
1187
-                }
1188
-                if (count($this->stack)) {
1189
-                    $this->curBlock = &$this->stack[count($this->stack) - 1];
1190
-                    $this->push(call_user_func(array(
1191
-                        $class,
1192
-                        'postProcessing'
1193
-                    ), $this, $top['params'], '', '', $top['buffer']), 0);
1194
-                } else {
1195
-                    $null           = null;
1196
-                    $this->curBlock = &$null;
1197
-                    $output         = call_user_func(
1198
-                        array(
1199
-                        $class,
1200
-                        'postProcessing'
1201
-                        ), $this, $top['params'], '', '', $top['buffer']
1202
-                    );
1203
-                }
1204
-
1205
-                if ($top['type'] === $type) {
1206
-                    break 2;
1207
-                }
1208
-            }
1209
-
1210
-            throw new CompilationException($this, 'Syntax malformation, a block of type "' . $type . '" was closed but was not opened');
1211
-        }
1212
-
1213
-        return $output;
1214
-    }
1215
-
1216
-    /**
1217
-     * Returns a reference to the first block of the given type encountered and
1218
-     * optionally closes all blocks until it finds it
1219
-     * this is mainly used by {else} plugins to close everything that was opened
1220
-     * between their parent and themselves.
1221
-     *
1222
-     * @param string $type       the block type (name)
1223
-     * @param bool   $closeAlong whether to close all blocks encountered while going down the block stack or not
1224
-     *
1225
-     * @return mixed &array the array is as such: array('type'=>pluginName, 'params'=>parameter array,
1226
-     *               'custom'=>bool defining whether it's a custom plugin or not, for internal use)
1227
-     * @throws CompilationException
1228
-     */
1229
-    public function &findBlock($type, $closeAlong = false)
1230
-    {
1231
-        if ($closeAlong === true) {
1232
-            while ($b = end($this->stack)) {
1233
-                if ($b['type'] === $type) {
1234
-                    return $this->stack[key($this->stack)];
1235
-                }
1236
-                $this->push($this->removeTopBlock(), 0);
1237
-            }
1238
-        } else {
1239
-            end($this->stack);
1240
-            while ($b = current($this->stack)) {
1241
-                if ($b['type'] === $type) {
1242
-                    return $this->stack[key($this->stack)];
1243
-                }
1244
-                prev($this->stack);
1245
-            }
1246
-        }
1247
-
1248
-        throw new CompilationException($this, 'A parent block of type "' . $type . '" is required and can not be found');
1249
-    }
1250
-
1251
-    /**
1252
-     * Returns a reference to the current block array.
1253
-     *
1254
-     * @return array the array is as such: array('type'=>pluginName, 'params'=>parameter array,
1255
-     *                'custom'=>bool defining whether it's a custom plugin or not, for internal use)
1256
-     */
1257
-    public function &getCurrentBlock()
1258
-    {
1259
-        return $this->curBlock;
1260
-    }
1261
-
1262
-    /**
1263
-     * Removes the block at the top of the stack and calls its postProcessing() method.
1264
-     *
1265
-     * @return string the postProcessing() method's output
1266
-     * @throws CompilationException
1267
-     */
1268
-    public function removeTopBlock()
1269
-    {
1270
-        if ($this->debug) {
1271
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1272
-        }
1273
-
1274
-        $o = array_pop($this->stack);
1275
-        if ($o === null) {
1276
-            throw new CompilationException($this, 'Syntax malformation, a block of unknown type was closed but was not opened.');
1277
-        }
1278
-        if ($o['custom']) {
1279
-            $class = $o['class'];
1280
-        } else {
1281
-            $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($o['type']);
1282
-        }
1283
-
1284
-        $this->curBlock = &$this->stack[count($this->stack) - 1];
1285
-
1286
-        return call_user_func(array($class, 'postProcessing'), $this, $o['params'], '', '', $o['buffer']);
1287
-    }
1288
-
1289
-    /**
1290
-     * Returns the compiled parameters (for example a variable's compiled parameter will be "$this->scope['key']") out
1291
-     * of the given parameter array.
1292
-     *
1293
-     * @param array $params parameter array
1294
-     *
1295
-     * @return array filtered parameters
1296
-     */
1297
-    public function getCompiledParams(array $params)
1298
-    {
1299
-        foreach ($params as $k => $p) {
1300
-            if (is_array($p)) {
1301
-                $params[$k] = $p[0];
1302
-            }
1303
-        }
1304
-
1305
-        return $params;
1306
-    }
1307
-
1308
-    /**
1309
-     * Returns the real parameters (for example a variable's real parameter will be its key, etc) out of the given
1310
-     * parameter array.
1311
-     *
1312
-     * @param array $params parameter array
1313
-     *
1314
-     * @return array filtered parameters
1315
-     */
1316
-    public function getRealParams(array $params)
1317
-    {
1318
-        foreach ($params as $k => $p) {
1319
-            if (is_array($p)) {
1320
-                $params[$k] = $p[1];
1321
-            }
1322
-        }
1323
-
1324
-        return $params;
1325
-    }
1326
-
1327
-    /**
1328
-     * Returns the token of each parameter out of the given parameter array.
1329
-     *
1330
-     * @param array $params parameter array
1331
-     *
1332
-     * @return array tokens
1333
-     */
1334
-    public function getParamTokens(array $params)
1335
-    {
1336
-        foreach ($params as $k => $p) {
1337
-            if (is_array($p)) {
1338
-                $params[$k] = isset($p[2]) ? $p[2] : 0;
1339
-            }
1340
-        }
1341
-
1342
-        return $params;
1343
-    }
1344
-
1345
-    /**
1346
-     * Entry point of the parser, it redirects calls to other parse* functions.
1347
-     *
1348
-     * @param string $in            the string within which we must parse something
1349
-     * @param int    $from          the starting offset of the parsed area
1350
-     * @param int    $to            the ending offset of the parsed area
1351
-     * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
1352
-     *                              default
1353
-     * @param string $curBlock      the current parser-block being processed
1354
-     * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
1355
-     *                              or null by default
1356
-     *
1357
-     * @return string parsed values
1358
-     * @throws CompilationException
1359
-     */
1360
-    protected function parse($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
1361
-    {
1362
-        if ($this->debug) {
1363
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1364
-        }
1365
-
1366
-        if ($to === null) {
1367
-            $to = strlen($in);
1368
-        }
1369
-        $first = substr($in, $from, 1);
1370
-
1371
-        if ($first === false) {
1372
-            throw new CompilationException($this, 'Unexpected EOF, a template tag was not closed');
1373
-        }
1374
-
1375
-        while ($first === ' ' || $first === "\n" || $first === "\t" || $first === "\r") {
1376
-            if ($curBlock === 'root' && substr($in, $from, strlen($this->rd)) === $this->rd) {
1377
-                // end template tag
1378
-                $pointer += strlen($this->rd);
1379
-                if ($this->debug) {
1380
-                    echo 'TEMPLATE PARSING ENDED' . "\n";
1381
-                }
1382
-
1383
-                return false;
1384
-            }
1385
-            ++ $from;
1386
-            if ($pointer !== null) {
1387
-                ++ $pointer;
1388
-            }
1389
-            if ($from >= $to) {
1390
-                if (is_array($parsingParams)) {
1391
-                    return $parsingParams;
1392
-                } else {
1393
-                    return '';
1394
-                }
1395
-            }
1396
-            $first = $in[$from];
1397
-        }
1398
-
1399
-        $substr = substr($in, $from, $to - $from);
1400
-
1401
-        if ($this->debug) {
1402
-            echo 'PARSE CALL : PARSING "' . htmlentities(substr($in, $from, min($to - $from, 50))) . (($to - $from) > 50 ? '...' : '') . '" @ ' . $from . ':' . $to . ' in ' . $curBlock . ' : pointer=' . $pointer . "\n";
1403
-        }
1404
-        $parsed = '';
1405
-
1406
-        if ($curBlock === 'root' && $first === '*') {
1407
-            $src      = $this->getTemplateSource();
1408
-            $startpos = $this->getPointer() - strlen($this->ld);
1409
-            if (substr($src, $startpos, strlen($this->ld)) === $this->ld) {
1410
-                if ($startpos > 0) {
1411
-                    do {
1412
-                        $char = substr($src, -- $startpos, 1);
1413
-                        if ($char == "\n") {
1414
-                            ++ $startpos;
1415
-                            $whitespaceStart = true;
1416
-                            break;
1417
-                        }
1418
-                    }
1419
-                    while ($startpos > 0 && ($char == ' ' || $char == "\t"));
1420
-                }
1421
-
1422
-                if (!isset($whitespaceStart)) {
1423
-                    $startpos = $this->getPointer();
1424
-                } else {
1425
-                    $pointer -= $this->getPointer() - $startpos;
1426
-                }
1427
-
1428
-                if ($this->allowNestedComments && strpos($src, $this->ld . '*', $this->getPointer()) !== false) {
1429
-                    $comOpen  = $this->ld . '*';
1430
-                    $comClose = '*' . $this->rd;
1431
-                    $level    = 1;
1432
-                    $ptr      = $this->getPointer();
1433
-
1434
-                    while ($level > 0 && $ptr < strlen($src)) {
1435
-                        $open  = strpos($src, $comOpen, $ptr);
1436
-                        $close = strpos($src, $comClose, $ptr);
1437
-
1438
-                        if ($open !== false && $close !== false) {
1439
-                            if ($open < $close) {
1440
-                                $ptr = $open + strlen($comOpen);
1441
-                                ++ $level;
1442
-                            } else {
1443
-                                $ptr = $close + strlen($comClose);
1444
-                                -- $level;
1445
-                            }
1446
-                        } elseif ($open !== false) {
1447
-                            $ptr = $open + strlen($comOpen);
1448
-                            ++ $level;
1449
-                        } elseif ($close !== false) {
1450
-                            $ptr = $close + strlen($comClose);
1451
-                            -- $level;
1452
-                        } else {
1453
-                            $ptr = strlen($src);
1454
-                        }
1455
-                    }
1456
-                    $endpos = $ptr - strlen('*' . $this->rd);
1457
-                } else {
1458
-                    $endpos = strpos($src, '*' . $this->rd, $startpos);
1459
-                    if ($endpos == false) {
1460
-                        throw new CompilationException($this, 'Un-ended comment');
1461
-                    }
1462
-                }
1463
-                $pointer += $endpos - $startpos + strlen('*' . $this->rd);
1464
-                if (isset($whitespaceStart) && preg_match('#^[\t ]*\r?\n#', substr($src, $endpos + strlen('*' . $this->rd)), $m)) {
1465
-                    $pointer += strlen($m[0]);
1466
-                    $this->curBlock['buffer'] = substr($this->curBlock['buffer'], 0, strlen($this->curBlock['buffer']) - ($this->getPointer() - $startpos - strlen($this->ld)));
1467
-                }
1468
-
1469
-                return false;
1470
-            }
1471
-        }
1472
-
1473
-        if ($first === '$') {
1474
-            // var
1475
-            $out    = $this->parseVar($in, $from, $to, $parsingParams, $curBlock, $pointer);
1476
-            $parsed = 'var';
1477
-        } elseif ($first === '%' && preg_match('#^%[a-z_\\\\]#i', $substr)) {
1478
-            // Short constant
1479
-            $out = $this->parseConst($in, $from, $to, $parsingParams, $curBlock, $pointer);
1480
-        } elseif (($first === '"' || $first === "'") && !(is_array($parsingParams) && preg_match('#^([\'"])[a-z0-9_]+\1\s*=>?(?:\s+|[^=])#i', $substr))) {
1481
-            // string
1482
-            $out = $this->parseString($in, $from, $to, $parsingParams, $curBlock, $pointer);
1483
-        } 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)) {
1484
-            // func
1485
-            $out    = $this->parseFunction($in, $from, $to, $parsingParams, $curBlock, $pointer);
1486
-            $parsed = 'func';
1487
-        } elseif ($first === ';') {
1488
-            // instruction end
1489
-            if ($this->debug) {
1490
-                echo 'END OF INSTRUCTION' . "\n";
1491
-            }
1492
-            if ($pointer !== null) {
1493
-                ++ $pointer;
1494
-            }
1495
-
1496
-            return $this->parse($in, $from + 1, $to, false, 'root', $pointer);
1497
-        } elseif ($curBlock === 'root' && preg_match('#^/([a-z_][a-z0-9_]*)?#i', $substr, $match)) {
1498
-            // close block
1499
-            if (!empty($match[1]) && $match[1] == 'else') {
1500
-                throw new CompilationException($this, 'Else blocks must not be closed explicitly, they are automatically closed when their parent block is closed');
1501
-            }
1502
-            if (!empty($match[1]) && $match[1] == 'elseif') {
1503
-                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');
1504
-            }
1505
-            if ($pointer !== null) {
1506
-                $pointer += strlen($match[0]);
1507
-            }
1508
-            if (empty($match[1])) {
1509
-                if ($this->curBlock['type'] == 'else' || $this->curBlock['type'] == 'elseif') {
1510
-                    $pointer -= strlen($match[0]);
1511
-                }
1512
-                if ($this->debug) {
1513
-                    echo 'TOP BLOCK CLOSED' . "\n";
1514
-                }
1515
-
1516
-                return $this->removeTopBlock();
1517
-            } else {
1518
-                if ($this->debug) {
1519
-                    echo 'BLOCK OF TYPE ' . $match[1] . ' CLOSED' . "\n";
1520
-                }
1521
-
1522
-                return $this->removeBlock($match[1]);
1523
-            }
1524
-        } elseif ($curBlock === 'root' && substr($substr, 0, strlen($this->rd)) === $this->rd) {
1525
-            // end template tag
1526
-            if ($this->debug) {
1527
-                echo 'TAG PARSING ENDED' . "\n";
1528
-            }
1529
-            $pointer += strlen($this->rd);
1530
-
1531
-            return false;
1532
-        } elseif (is_array($parsingParams) && preg_match('#^(([\'"]?)[a-z0-9_]+\2\s*=' . ($curBlock === 'array' ? '>?' : '') . ')(?:\s+|[^=]).*#i', $substr, $match)) {
1533
-            // named parameter
1534
-            if ($this->debug) {
1535
-                echo 'NAMED PARAM FOUND' . "\n";
1536
-            }
1537
-            $len = strlen($match[1]);
1538
-            while (substr($in, $from + $len, 1) === ' ') {
1539
-                ++ $len;
1540
-            }
1541
-            if ($pointer !== null) {
1542
-                $pointer += $len;
1543
-            }
1544
-
1545
-            $output = array(
1546
-                trim($match[1], " \t\r\n=>'\""),
1547
-                $this->parse($in, $from + $len, $to, false, 'namedparam', $pointer)
1548
-            );
1549
-
1550
-            $parsingParams[] = $output;
1551
-
1552
-            return $parsingParams;
1553
-        } elseif (preg_match('#^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*::\$[a-z0-9_]+)#i', $substr, $match)) {
1554
-            // static member access
1555
-            $parsed = 'var';
1556
-            if (is_array($parsingParams)) {
1557
-                $parsingParams[] = array($match[1], $match[1]);
1558
-                $out             = $parsingParams;
1559
-            } else {
1560
-                $out = $match[1];
1561
-            }
1562
-            $pointer += strlen($match[1]);
1563
-        } elseif ($substr !== '' && (is_array($parsingParams) || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'expression')) {
1564
-            // unquoted string, bool or number
1565
-            $out = $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
1566
-        } else {
1567
-            // parse error
1568
-            throw new CompilationException($this, 'Parse error in "' . substr($in, $from, $to - $from) . '"');
1569
-        }
1570
-
1571
-        if (empty($out)) {
1572
-            return '';
1573
-        }
1574
-
1575
-        $substr = substr($in, $pointer, $to - $pointer);
1576
-
1577
-        // var parsed, check if any var-extension applies
1578
-        if ($parsed === 'var') {
1579
-            if (preg_match('#^\s*([/%+*-])\s*([a-z0-9]|\$)#i', $substr, $match)) {
1580
-                if ($this->debug) {
1581
-                    echo 'PARSING POST-VAR EXPRESSION ' . $substr . "\n";
1582
-                }
1583
-                // parse expressions
1584
-                $pointer += strlen($match[0]) - 1;
1585
-                if (is_array($parsingParams)) {
1586
-                    if ($match[2] == '$') {
1587
-                        $expr = $this->parseVar($in, $pointer, $to, array(), $curBlock, $pointer);
1588
-                    } else {
1589
-                        $expr = $this->parse($in, $pointer, $to, array(), 'expression', $pointer);
1590
-                    }
1591
-                    $out[count($out) - 1][0] .= $match[1] . $expr[0][0];
1592
-                    $out[count($out) - 1][1] .= $match[1] . $expr[0][1];
1593
-                } else {
1594
-                    if ($match[2] == '$') {
1595
-                        $expr = $this->parseVar($in, $pointer, $to, false, $curBlock, $pointer);
1596
-                    } else {
1597
-                        $expr = $this->parse($in, $pointer, $to, false, 'expression', $pointer);
1598
-                    }
1599
-                    if (is_array($out) && is_array($expr)) {
1600
-                        $out[0] .= $match[1] . $expr[0];
1601
-                        $out[1] .= $match[1] . $expr[1];
1602
-                    } elseif (is_array($out)) {
1603
-                        $out[0] .= $match[1] . $expr;
1604
-                        $out[1] .= $match[1] . $expr;
1605
-                    } elseif (is_array($expr)) {
1606
-                        $out .= $match[1] . $expr[0];
1607
-                    } else {
1608
-                        $out .= $match[1] . $expr;
1609
-                    }
1610
-                }
1611
-            } elseif ($curBlock === 'root' && preg_match('#^(\s*(?:[+/*%-.]=|=|\+\+|--)\s*)(.*)#s', $substr, $match)) {
1612
-                if ($this->debug) {
1613
-                    echo 'PARSING POST-VAR ASSIGNMENT ' . $substr . "\n";
1614
-                }
1615
-                // parse assignment
1616
-                $value    = $match[2];
1617
-                $operator = trim($match[1]);
1618
-                if (substr($value, 0, 1) == '=') {
1619
-                    throw new CompilationException($this, 'Unexpected "=" in <em>' . $substr . '</em>');
1620
-                }
1621
-
1622
-                if ($pointer !== null) {
1623
-                    $pointer += strlen($match[1]);
1624
-                }
1625
-
1626
-                if ($operator !== '++' && $operator !== '--') {
1627
-                    $parts = array();
1628
-                    $ptr   = 0;
1629
-                    $parts = $this->parse($value, 0, strlen($value), $parts, 'condition', $ptr);
1630
-                    $pointer += $ptr;
1631
-
1632
-                    // load if plugin
1633
-                    try {
1634
-                        $this->getPluginType('if');
1635
-                    }
1636
-                    catch (Exception $e) {
1637
-                        throw new CompilationException($this, 'Assignments require the "if" plugin to be accessible');
1638
-                    }
1639
-
1640
-                    $parts  = $this->mapParams($parts, array(Core::NAMESPACE_PLUGINS_BLOCKS . 'PluginIf', 'init'), 1);
1641
-                    $tokens = $this->getParamTokens($parts);
1642
-                    $parts  = $this->getCompiledParams($parts);
1643
-
1644
-                    $value = PluginIf::replaceKeywords($parts['*'], $tokens['*'], $this);
1645
-                    $echo  = '';
1646
-                } else {
1647
-                    $value = array();
1648
-                    $echo  = 'echo ';
1649
-                }
1650
-
1651
-                if ($this->autoEscape) {
1652
-                    $out = preg_replace('#\(is_string\(\$tmp=(.+?)\) \? htmlspecialchars\(\$tmp, ENT_QUOTES, \$this->charset\) : \$tmp\)#', '$1', $out);
1653
-                }
1654
-                $out = self::PHP_OPEN . $echo . $out . $operator . implode(' ', $value) . self::PHP_CLOSE;
1655
-            } elseif ($curBlock === 'array' && is_array($parsingParams) && preg_match('#^(\s*=>?\s*)#', $substr, $match)) {
1656
-                // parse namedparam with var as name (only for array)
1657
-                if ($this->debug) {
1658
-                    echo 'VARIABLE NAMED PARAM (FOR ARRAY) FOUND' . "\n";
1659
-                }
1660
-                $len = strlen($match[1]);
1661
-                $var = $out[count($out) - 1];
1662
-                $pointer += $len;
1663
-
1664
-                $output = array($var[0], $this->parse($substr, $len, null, false, 'namedparam', $pointer));
1665
-
1666
-                $parsingParams[] = $output;
1667
-
1668
-                return $parsingParams;
1669
-            }
1670
-        }
1671
-
1672
-        if ($curBlock !== 'modifier' && ($parsed === 'func' || $parsed === 'var') && preg_match('#^(\|@?[a-z0-9_]+(:.*)?)+#i', $substr, $match)) {
1673
-            // parse modifier on funcs or vars
1674
-            $srcPointer = $pointer;
1675
-            if (is_array($parsingParams)) {
1676
-                $tmp                     = $this->replaceModifiers(
1677
-                    array(
1678
-                    null,
1679
-                    null,
1680
-                    $out[count($out) - 1][0],
1681
-                    $match[0]
1682
-                    ), $curBlock, $pointer
1683
-                );
1684
-                $out[count($out) - 1][0] = $tmp;
1685
-                $out[count($out) - 1][1] .= substr($substr, $srcPointer, $srcPointer - $pointer);
1686
-            } else {
1687
-                $out = $this->replaceModifiers(array(null, null, $out, $match[0]), $curBlock, $pointer);
1688
-            }
1689
-        }
1690
-
1691
-        // func parsed, check if any func-extension applies
1692
-        if ($parsed === 'func' && preg_match('#^->[a-z0-9_]+(\s*\(.+|->[a-z_].*)?#is', $substr, $match)) {
1693
-            // parse method call or property read
1694
-            $ptr = 0;
1695
-
1696
-            if (is_array($parsingParams)) {
1697
-                $output = $this->parseMethodCall($out[count($out) - 1][1], $match[0], $curBlock, $ptr);
1698
-
1699
-                $out[count($out) - 1][0] = $output;
1700
-                $out[count($out) - 1][1] .= substr($match[0], 0, $ptr);
1701
-            } else {
1702
-                $out = $this->parseMethodCall($out, $match[0], $curBlock, $ptr);
1703
-            }
1704
-
1705
-            $pointer += $ptr;
1706
-        }
1707
-
1708
-        if ($curBlock === 'root' && substr($out, 0, strlen(self::PHP_OPEN)) !== self::PHP_OPEN) {
1709
-            return self::PHP_OPEN . 'echo ' . $out . ';' . self::PHP_CLOSE;
1710
-        }
1711
-
1712
-        return $out;
1713
-    }
1714
-
1715
-    /**
1716
-     * Parses a function call.
1717
-     *
1718
-     * @param string $in            the string within which we must parse something
1719
-     * @param int    $from          the starting offset of the parsed area
1720
-     * @param int    $to            the ending offset of the parsed area
1721
-     * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
1722
-     *                              default
1723
-     * @param string $curBlock      the current parser-block being processed
1724
-     * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
1725
-     *                              or null by default
1726
-     *
1727
-     * @return string parsed values
1728
-     * @throws CompilationException
1729
-     * @throws Exception
1730
-     * @throws SecurityException
1731
-     */
1732
-    protected function parseFunction($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
1733
-    {
1734
-        $output = '';
1735
-        $cmdstr = substr($in, $from, $to - $from);
1736
-        preg_match('/^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*(?:::[a-z_][a-z0-9_]*)?)(\s*' . $this->rdr . '|\s*;)?/i', $cmdstr, $match);
1737
-
1738
-        if (empty($match[1])) {
1739
-            throw new CompilationException($this, 'Parse error, invalid function name : ' . substr($cmdstr, 0, 15));
1740
-        }
1741
-
1742
-        $func = $match[1];
1743
-
1744
-        if (!empty($match[2])) {
1745
-            $cmdstr = $match[1];
1746
-        }
1747
-
1748
-        if ($this->debug) {
1749
-            echo 'FUNC FOUND (' . $func . ')' . "\n";
1750
-        }
1751
-
1752
-        $paramsep = '';
1753
-
1754
-        if (is_array($parsingParams) || $curBlock != 'root') {
1755
-            $paramspos = strpos($cmdstr, '(');
1756
-            $paramsep  = ')';
1757
-        } elseif (preg_match_all('#^\s*[\\\\:a-z0-9_]+(\s*\(|\s+[^(])#i', $cmdstr, $match, PREG_OFFSET_CAPTURE)) {
1758
-            $paramspos = $match[1][0][1];
1759
-            $paramsep  = substr($match[1][0][0], - 1) === '(' ? ')' : '';
1760
-            if ($paramsep === ')') {
1761
-                $paramspos += strlen($match[1][0][0]) - 1;
1762
-                if (substr($cmdstr, 0, 2) === 'if' || substr($cmdstr, 0, 6) === 'elseif') {
1763
-                    $paramsep = '';
1764
-                    if (strlen($match[1][0][0]) > 1) {
1765
-                        -- $paramspos;
1766
-                    }
1767
-                }
1768
-            }
1769
-        } else {
1770
-            $paramspos = false;
1771
-        }
1772
-
1773
-        $state = 0;
1774
-
1775
-        if ($paramspos === false) {
1776
-            $params = array();
1777
-
1778
-            if ($curBlock !== 'root') {
1779
-                return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
1780
-            }
1781
-        } else {
1782
-            if ($curBlock === 'condition') {
1783
-                // load if plugin
1784
-                $this->getPluginType('if');
1785
-
1786
-                if (PluginIf::replaceKeywords(array($func), array(self::T_UNQUOTED_STRING), $this) !== array($func)) {
1787
-                    return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
1788
-                }
1789
-            }
1790
-            $whitespace = strlen(substr($cmdstr, strlen($func), $paramspos - strlen($func)));
1791
-            $paramstr   = substr($cmdstr, $paramspos + 1);
1792
-            if (substr($paramstr, - 1, 1) === $paramsep) {
1793
-                $paramstr = substr($paramstr, 0, - 1);
1794
-            }
1795
-
1796
-            if (strlen($paramstr) === 0) {
1797
-                $params   = array();
1798
-                $paramstr = '';
1799
-            } else {
1800
-                $ptr    = 0;
1801
-                $params = array();
1802
-                if ($func === 'empty') {
1803
-                    $params = $this->parseVar($paramstr, $ptr, strlen($paramstr), $params, 'root', $ptr);
1804
-                } else {
1805
-                    while ($ptr < strlen($paramstr)) {
1806
-                        while (true) {
1807
-                            if ($ptr >= strlen($paramstr)) {
1808
-                                break 2;
1809
-                            }
1810
-
1811
-                            if ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === ')') {
1812
-                                if ($this->debug) {
1813
-                                    echo 'PARAM PARSING ENDED, ")" FOUND, POINTER AT ' . $ptr . "\n";
1814
-                                }
1815
-                                break 2;
1816
-                            } elseif ($paramstr[$ptr] === ';') {
1817
-                                ++ $ptr;
1818
-                                if ($this->debug) {
1819
-                                    echo 'PARAM PARSING ENDED, ";" FOUND, POINTER AT ' . $ptr . "\n";
1820
-                                }
1821
-                                break 2;
1822
-                            } elseif ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === '/') {
1823
-                                if ($this->debug) {
1824
-                                    echo 'PARAM PARSING ENDED, "/" FOUND, POINTER AT ' . $ptr . "\n";
1825
-                                }
1826
-                                break 2;
1827
-                            } elseif (substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) {
1828
-                                if ($this->debug) {
1829
-                                    echo 'PARAM PARSING ENDED, RIGHT DELIMITER FOUND, POINTER AT ' . $ptr . "\n";
1830
-                                }
1831
-                                break 2;
1832
-                            }
1833
-
1834
-                            if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === ',' || $paramstr[$ptr] === "\r" || $paramstr[$ptr] === "\n" || $paramstr[$ptr] === "\t") {
1835
-                                ++ $ptr;
1836
-                            } else {
1837
-                                break;
1838
-                            }
1839
-                        }
1840
-
1841
-                        if ($this->debug) {
1842
-                            echo 'FUNC START PARAM PARSING WITH POINTER AT ' . $ptr . "\n";
1843
-                        }
1844
-
1845
-                        if ($func === 'if' || $func === 'elseif' || $func === 'tif') {
1846
-                            $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'condition', $ptr);
1847
-                        } elseif ($func === 'array') {
1848
-                            $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'array', $ptr);
1849
-                        } else {
1850
-                            $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'function', $ptr);
1851
-                        }
1852
-
1853
-                        if ($this->debug) {
1854
-                            echo 'PARAM PARSED, POINTER AT ' . $ptr . ' (' . substr($paramstr, $ptr - 1, 3) . ')' . "\n";
1855
-                        }
1856
-                    }
1857
-                }
1858
-                $paramstr = substr($paramstr, 0, $ptr);
1859
-                $state    = 0;
1860
-                foreach ($params as $k => $p) {
1861
-                    if (is_array($p) && is_array($p[1])) {
1862
-                        $state |= 2;
1863
-                    } else {
1864
-                        if (($state & 2) && preg_match('#^(["\'])(.+?)\1$#', $p[0], $m) && $func !== 'array') {
1865
-                            $params[$k] = array($m[2], array('true', 'true'));
1866
-                        } else {
1867
-                            if ($state & 2 && $func !== 'array') {
1868
-                                throw new CompilationException($this, 'You can not use an unnamed parameter after a named one');
1869
-                            }
1870
-                            $state |= 1;
1871
-                        }
1872
-                    }
1873
-                }
1874
-            }
1875
-        }
1876
-
1877
-        if ($pointer !== null) {
1878
-            $pointer += (isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func) + (isset($whitespace) ? $whitespace : 0);
1879
-            if ($this->debug) {
1880
-                echo 'FUNC ADDS ' . ((isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func)) . ' TO POINTER' . "\n";
1881
-            }
1882
-        }
1883
-
1884
-        if ($curBlock === 'method' || $func === 'do' || strstr($func, '::') !== false) {
1885
-            // handle static method calls with security policy
1886
-            if (strstr($func, '::') !== false && $this->securityPolicy !== null && $this->securityPolicy->isMethodAllowed(explode('::', strtolower($func))) !== true) {
1887
-                throw new SecurityException('Call to a disallowed php function : ' . $func);
1888
-            }
1889
-            $pluginType = Core::NATIVE_PLUGIN;
1890
-        } else {
1891
-            $pluginType = $this->getPluginType($func);
1892
-        }
1893
-
1894
-        // Blocks plugin
1895
-        if ($pluginType & Core::BLOCK_PLUGIN) {
1896
-            if ($curBlock !== 'root' || is_array($parsingParams)) {
1897
-                throw new CompilationException($this, 'Block plugins can not be used as other plugin\'s arguments');
1898
-            }
1899
-            if ($pluginType & Core::CUSTOM_PLUGIN) {
1900
-                return $this->addCustomBlock($func, $params, $state);
1901
-            } else {
1902
-                return $this->addBlock($func, $params, $state);
1903
-            }
1904
-        } elseif ($pluginType & Core::SMARTY_BLOCK) {
1905
-            if ($curBlock !== 'root' || is_array($parsingParams)) {
1906
-                throw new CompilationException($this, 'Block plugins can not be used as other plugin\'s arguments');
1907
-            }
1908
-
1909
-            if ($state & 2) {
1910
-                array_unshift($params, array('__functype', array($pluginType, $pluginType)));
1911
-                array_unshift($params, array('__funcname', array($func, $func)));
1912
-            } else {
1913
-                array_unshift($params, array($pluginType, $pluginType));
1914
-                array_unshift($params, array($func, $func));
1915
-            }
1916
-
1917
-            return $this->addBlock('smartyinterface', $params, $state);
1918
-        }
1919
-
1920
-        // Native & Smarty plugins
1921
-        if ($pluginType & Core::NATIVE_PLUGIN || $pluginType & Core::SMARTY_FUNCTION || $pluginType & Core::SMARTY_BLOCK) {
1922
-            $params = $this->mapParams($params, null, $state);
1923
-        } // PHP class plugin
1924
-        elseif ($pluginType & Core::CLASS_PLUGIN) {
1925
-            if ($pluginType & Core::CUSTOM_PLUGIN) {
1926
-                $params = $this->mapParams(
1927
-                    $params, array(
1928
-                    $this->customPlugins[$func]['class'],
1929
-                    $this->customPlugins[$func]['function']
1930
-                ), $state);
1931
-            } else {
1932
-                if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
1933
-                    $params = $this->mapParams($params, array(
1934
-                        'Plugin' . Core::toCamelCase($func),
1935
-                        ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
1936
-                    ), $state);
1937
-                } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) {
1938
-                    $params = $this->mapParams($params, array(
1939
-                        Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func),
1940
-                        ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
1941
-                    ), $state);
1942
-                } else {
1943
-                    $params = $this->mapParams($params, array(
1944
-                        Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
1945
-                        ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
1946
-                    ), $state);
1947
-                }
1948
-            }
1949
-        } // PHP function plugin
1950
-        elseif ($pluginType & Core::FUNC_PLUGIN) {
1951
-            if ($pluginType & Core::CUSTOM_PLUGIN) {
1952
-                $params = $this->mapParams($params, $this->customPlugins[$func]['callback'], $state);
1953
-            } else {
1954
-                // Custom plugin
1955
-                if (function_exists('Plugin' . Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ?
1956
-                        'Compile' : '')) !== false) {
1957
-                    $params = $this->mapParams($params, 'Plugin' . Core::toCamelCase($func) . (($pluginType &
1958
-                            Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1959
-                } // Builtin helper plugin
1960
-                elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . (
1961
-                    ($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '')) !== false) {
1962
-                    $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase
1963
-                        ($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1964
-                } // Builtin function plugin
1965
-                else {
1966
-                    $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase
1967
-                        ($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1968
-                }
1969
-            }
1970
-        } // Smarty modifier
1971
-        elseif ($pluginType & Core::SMARTY_MODIFIER) {
1972
-            $output = 'smarty_modifier_' . $func . '(' . implode(', ', $params) . ')';
1973
-        } // Proxy plugin
1974
-        elseif ($pluginType & Core::PROXY_PLUGIN) {
1975
-            $params = $this->mapParams($params, $this->getDwoo()->getPluginProxy()->getCallback($func), $state);
1976
-        } // Template plugin
1977
-        elseif ($pluginType & Core::TEMPLATE_PLUGIN) {
1978
-            // transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values))
1979
-            $map = array();
1980
-            foreach ($this->templatePlugins[$func]['params'] as $param => $defValue) {
1981
-                if ($param == 'rest') {
1982
-                    $param = '*';
1983
-                }
1984
-                $hasDefault = $defValue !== null;
1985
-                if ($defValue === 'null') {
1986
-                    $defValue = null;
1987
-                } elseif ($defValue === 'false') {
1988
-                    $defValue = false;
1989
-                } elseif ($defValue === 'true') {
1990
-                    $defValue = true;
1991
-                } elseif (preg_match('#^([\'"]).*?\1$#', $defValue)) {
1992
-                    $defValue = substr($defValue, 1, - 1);
1993
-                }
1994
-                $map[] = array($param, $hasDefault, $defValue);
1995
-            }
1996
-
1997
-            $params = $this->mapParams($params, null, $state, $map);
1998
-        }
1999
-
2000
-        // only keep php-syntax-safe values for non-block plugins
2001
-        $tokens = array();
2002
-        foreach ($params as $k => $p) {
2003
-            $tokens[$k] = isset($p[2]) ? $p[2] : 0;
2004
-            $params[$k] = $p[0];
2005
-        }
2006
-
2007
-        // Native plugin
2008
-        if ($pluginType & Core::NATIVE_PLUGIN) {
2009
-            if ($func === 'do') {
2010
-                $output = '';
2011
-                if (isset($params['*'])) {
2012
-                    $output = implode(';', $params['*']) . ';';
2013
-                }
2014
-
2015
-                if (is_array($parsingParams) || $curBlock !== 'root') {
2016
-                    throw new CompilationException($this, 'Do can not be used inside another function or block');
2017
-                }
2018
-
2019
-                return self::PHP_OPEN . $output . self::PHP_CLOSE;
2020
-            } else {
2021
-                if (isset($params['*'])) {
2022
-                    $output = $func . '(' . implode(', ', $params['*']) . ')';
2023
-                } else {
2024
-                    $output = $func . '()';
2025
-                }
2026
-            }
2027
-        } // Block class OR Function class
2028
-        elseif ($pluginType & Core::CLASS_PLUGIN || ($pluginType & Core::FUNC_PLUGIN && $pluginType & Core::CLASS_PLUGIN)) {
2029
-            if ($pluginType & Core::COMPILABLE_PLUGIN) {
2030
-                if ($pluginType & Core::CUSTOM_PLUGIN) {
2031
-                    $callback = $this->customPlugins[$func]['callback'];
2032
-                    if (!is_array($callback)) {
2033
-                        if (!method_exists($callback, 'compile')) {
2034
-                            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');
2035
-                        }
2036
-                        if (($ref = new ReflectionMethod($callback, 'compile')) && $ref->isStatic()) {
2037
-                            $funcCompiler = array($callback, 'compile');
2038
-                        } else {
2039
-                            $funcCompiler = array(new $callback(), 'compile');
2040
-                        }
2041
-                    } else {
2042
-                        $funcCompiler = $callback;
2043
-                    }
2044
-                } else {
2045
-                    if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
2046
-                        $funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile');
2047
-                    } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) {
2048
-                        $funcCompiler = array(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func), 'compile');
2049
-                    } else {
2050
-                        $funcCompiler = array(
2051
-                            Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
2052
-                            'compile'
2053
-                        );
2054
-                    }
2055
-                    array_unshift($params, $this);
2056
-                }
2057
-                // @TODO: Is it a real fix ?
2058
-                if ($func === 'tif') {
2059
-                    $params[] = $tokens;
2060
-                }
2061
-                $output = call_user_func_array($funcCompiler, $params);
2062
-            } else {
2063
-                $params = self::implode_r($params);
2064
-                if ($pluginType & Core::CUSTOM_PLUGIN) {
2065
-                    $callback = $this->customPlugins[$func]['callback'];
2066
-                    if (!is_array($callback)) {
2067
-                        if (!method_exists($callback, 'process')) {
2068
-                            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');
2069
-                        }
2070
-                        if (($ref = new ReflectionMethod($callback, 'process')) && $ref->isStatic()) {
2071
-                            $output = 'call_user_func(array(\'' . $callback . '\', \'process\'), ' . $params . ')';
2072
-                        } else {
2073
-                            $output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback . '\'), \'process\'), ' . $params . ')';
2074
-                        }
2075
-                    } elseif (is_object($callback[0])) {
2076
-                        $output = 'call_user_func(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), ' . $params . ')';
2077
-                    } elseif (($ref = new ReflectionMethod($callback[0], $callback[1])) && $ref->isStatic()) {
2078
-                        $output = 'call_user_func(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), ' . $params . ')';
2079
-                    } else {
2080
-                        $output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback[0] . '\'), \'' . $callback[1] . '\'), ' . $params . ')';
2081
-                    }
2082
-                    if (empty($params)) {
2083
-                        $output = substr($output, 0, - 3) . ')';
2084
-                    }
2085
-                } else {
2086
-                    if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
2087
-                        $output = '$this->classCall(\'Plugin' . $func . '\', array(' . $params . '))';
2088
-                    } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) {
2089
-                        $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::CLASS_PLUGIN:
879
+				case Core::CLASS_PLUGIN + Core::BLOCK_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::CLASS_PLUGIN + Core::FUNC_PLUGIN:
889
+					if (class_exists('Plugin' . $plugin) !== false) {
890
+						$output .= "if (class_exists('" . "Plugin" . $plugin . "')===false)".
891
+							"\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
892
+					} else {
893
+						$output .= "if (class_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)".
894
+							"\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
895
+					}
896
+					break;
897
+				case Core::FUNC_PLUGIN:
898
+					if (function_exists('Plugin' . $plugin) !== false) {
899
+						$output .= "if (function_exists('" . "Plugin" . $plugin . "')===false)".
900
+						"\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
901
+					} else {
902
+						$output .= "if (function_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)".
903
+						"\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
904
+					}
905
+					break;
906
+				case Core::SMARTY_MODIFIER:
907
+					$output .= "if (function_exists('smarty_modifier_$plugin')===false)".
908
+					"\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
909
+					break;
910
+				case Core::SMARTY_FUNCTION:
911
+					$output .= "if (function_exists('smarty_function_$plugin')===false)".
912
+					"\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
913
+					break;
914
+				case Core::SMARTY_BLOCK:
915
+					$output .= "if (function_exists('smarty_block_$plugin')===false)".
916
+					"\n\t\$this->getLoader()->loadPlugin('$plugin');\n";
917
+					break;
918
+				case Core::PROXY_PLUGIN:
919
+					$output .= $this->getDwoo()->getPluginProxy()->getLoader($plugin);
920
+					break;
921
+				default:
922
+					throw new CompilationException($this, 'Type error for ' . $plugin . ' with type' . $type);
923
+			}
924
+		}
925
+
926
+		foreach ($this->templatePlugins as $function => $attr) {
927
+			if (isset($attr['called']) && $attr['called'] === true && !isset($attr['checked'])) {
928
+				$this->resolveSubTemplateDependencies($function);
929
+			}
930
+		}
931
+		foreach ($this->templatePlugins as $function) {
932
+			if (isset($function['called']) && $function['called'] === true) {
933
+				$output .= $function['body'] . PHP_EOL;
934
+			}
935
+		}
936
+
937
+		$output .= $compiled . "\n?>";
938
+
939
+		$output = preg_replace('/(?<!;|\}|\*\/|\n|\{)(\s*' . preg_quote(self::PHP_CLOSE, '/') . preg_quote(self::PHP_OPEN, '/') . ')/', ";\n", $output);
940
+		$output = str_replace(self::PHP_CLOSE . self::PHP_OPEN, "\n", $output);
941
+
942
+		// handle <?xml tag at the beginning
943
+		$output = preg_replace('#(/\* template body \*/ \?>\s*)<\?xml#is', '$1<?php echo \'<?xml\'; ?>', $output);
944
+
945
+		// add another line break after PHP closing tags that have a line break following,
946
+		// as we do not know whether it's intended, and PHP will strip it otherwise
947
+		$output = preg_replace('/(?<!"|<\?xml)\s*\?>\n/', '$0' . "\n", $output);
948
+
949
+		if ($this->debug) {
950
+			echo '=============================================================================================' . "\n";
951
+			$lines = preg_split('{\r\n|\n|<br />}', $output);
952
+			array_shift($lines);
953
+			foreach ($lines as $i => $line) {
954
+				echo ($i + 1) . '. ' . $line . "\r\n";
955
+			}
956
+			echo '=============================================================================================' . "\n";
957
+		}
958
+
959
+		$this->template = $this->dwoo = null;
960
+		$tpl            = null;
961
+
962
+		return $output;
963
+	}
964
+
965
+	/**
966
+	 * Checks what sub-templates are used in every sub-template so that we're sure they are all compiled.
967
+	 *
968
+	 * @param string $function the sub-template name
969
+	 */
970
+	protected function resolveSubTemplateDependencies($function)
971
+	{
972
+		if ($this->debug) {
973
+			echo 'Compiler::' . __FUNCTION__ . "\n";
974
+		}
975
+
976
+		$body = $this->templatePlugins[$function]['body'];
977
+		foreach ($this->templatePlugins as $func => $attr) {
978
+			if ($func !== $function && !isset($attr['called']) && strpos($body, Core::NAMESPACE_PLUGINS_FUNCTIONS .
979
+			'Plugin' . Core::toCamelCase($func)) !== false) {
980
+				$this->templatePlugins[$func]['called'] = true;
981
+				$this->resolveSubTemplateDependencies($func);
982
+			}
983
+		}
984
+		$this->templatePlugins[$function]['checked'] = true;
985
+	}
986
+
987
+	/**
988
+	 * Adds compiled content to the current block.
989
+	 *
990
+	 * @param string $content   the content to push
991
+	 * @param int    $lineCount newlines count in content, optional
992
+	 *
993
+	 * @throws CompilationException
994
+	 */
995
+	public function push($content, $lineCount = null)
996
+	{
997
+		if ($lineCount === null) {
998
+			$lineCount = substr_count($content, "\n");
999
+		}
1000
+
1001
+		if ($this->curBlock['buffer'] === null && count($this->stack) > 1) {
1002
+			// buffer is not initialized yet (the block has just been created)
1003
+			$this->stack[count($this->stack) - 2]['buffer'] .= (string)$content;
1004
+			$this->curBlock['buffer'] = '';
1005
+		} else {
1006
+			if (!isset($this->curBlock['buffer'])) {
1007
+				throw new CompilationException($this, 'The template has been closed too early, you probably have an extra block-closing tag somewhere');
1008
+			}
1009
+			// append current content to current block's buffer
1010
+			$this->curBlock['buffer'] .= (string)$content;
1011
+		}
1012
+		$this->line += $lineCount;
1013
+	}
1014
+
1015
+	/**
1016
+	 * Sets the scope.
1017
+	 * set to null if the scope becomes "unstable" (i.e. too variable or unknown) so that
1018
+	 * variables are compiled in a more evaluative way than just $this->scope['key']
1019
+	 *
1020
+	 * @param mixed $scope    a string i.e. "level1.level2" or an array i.e. array("level1", "level2")
1021
+	 * @param bool  $absolute if true, the scope is set from the top level scope and not from the current scope
1022
+	 *
1023
+	 * @return array the current scope tree
1024
+	 */
1025
+	public function setScope($scope, $absolute = false)
1026
+	{
1027
+		$old = $this->scopeTree;
1028
+
1029
+		if ($scope === null) {
1030
+			unset($this->scope);
1031
+			$this->scope = null;
1032
+		}
1033
+
1034
+		if (is_array($scope) === false) {
1035
+			$scope = explode('.', $scope);
1036
+		}
1037
+
1038
+		if ($absolute === true) {
1039
+			$this->scope     = &$this->data;
1040
+			$this->scopeTree = array();
1041
+		}
1042
+
1043
+		while (($bit = array_shift($scope)) !== null) {
1044
+			if ($bit === '_parent' || $bit === '_') {
1045
+				array_pop($this->scopeTree);
1046
+				reset($this->scopeTree);
1047
+				$this->scope = &$this->data;
1048
+				$cnt         = count($this->scopeTree);
1049
+				for ($i = 0; $i < $cnt; ++ $i) {
1050
+					$this->scope = &$this->scope[$this->scopeTree[$i]];
1051
+				}
1052
+			} elseif ($bit === '_root' || $bit === '__') {
1053
+				$this->scope     = &$this->data;
1054
+				$this->scopeTree = array();
1055
+			} elseif (isset($this->scope[$bit])) {
1056
+				$this->scope       = &$this->scope[$bit];
1057
+				$this->scopeTree[] = $bit;
1058
+			} else {
1059
+				$this->scope[$bit] = array();
1060
+				$this->scope       = &$this->scope[$bit];
1061
+				$this->scopeTree[] = $bit;
1062
+			}
1063
+		}
1064
+
1065
+		return $old;
1066
+	}
1067
+
1068
+	/**
1069
+	 * Adds a block to the top of the block stack.
1070
+	 *
1071
+	 * @param string $type      block type (name)
1072
+	 * @param array  $params    the parameters array
1073
+	 * @param int    $paramtype the parameters type (see mapParams), 0, 1 or 2
1074
+	 *
1075
+	 * @return string the preProcessing() method's output
1076
+	 */
1077
+	public function addBlock($type, array $params, $paramtype)
1078
+	{
1079
+		if ($this->debug) {
1080
+			echo 'Compiler::' . __FUNCTION__ . "\n";
1081
+		}
1082
+
1083
+		$class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type);
1084
+		if (class_exists($class) === false) {
1085
+			$this->getDwoo()->getLoader()->loadPlugin($type);
1086
+		}
1087
+		$params = $this->mapParams($params, array($class, 'init'), $paramtype);
1088
+
1089
+		$this->stack[]  = array(
1090
+			'type'   => $type,
1091
+			'params' => $params,
1092
+			'custom' => false,
1093
+			'class'  => $class,
1094
+			'buffer' => null
1095
+		);
1096
+		$this->curBlock = &$this->stack[count($this->stack) - 1];
1097
+
1098
+		return call_user_func(array($class, 'preProcessing'), $this, $params, '', '', $type);
1099
+	}
1100
+
1101
+	/**
1102
+	 * Adds a custom block to the top of the block stack.
1103
+	 *
1104
+	 * @param string $type      block type (name)
1105
+	 * @param array  $params    the parameters array
1106
+	 * @param int    $paramtype the parameters type (see mapParams), 0, 1 or 2
1107
+	 *
1108
+	 * @return string the preProcessing() method's output
1109
+	 */
1110
+	public function addCustomBlock($type, array $params, $paramtype)
1111
+	{
1112
+		$callback = $this->customPlugins[$type]['callback'];
1113
+		if (is_array($callback)) {
1114
+			$class = is_object($callback[0]) ? get_class($callback[0]) : $callback[0];
1115
+		} else {
1116
+			$class = $callback;
1117
+		}
1118
+
1119
+		$params = $this->mapParams($params, array($class, 'init'), $paramtype);
1120
+
1121
+		$this->stack[]  = array(
1122
+			'type'   => $type,
1123
+			'params' => $params,
1124
+			'custom' => true,
1125
+			'class'  => $class,
1126
+			'buffer' => null
1127
+		);
1128
+		$this->curBlock = &$this->stack[count($this->stack) - 1];
1129
+
1130
+		return call_user_func(array($class, 'preProcessing'), $this, $params, '', '', $type);
1131
+	}
1132
+
1133
+	/**
1134
+	 * Injects a block at the top of the plugin stack without calling its preProcessing method.
1135
+	 * used by {else} blocks to re-add themselves after having closed everything up to their parent
1136
+	 *
1137
+	 * @param string $type   block type (name)
1138
+	 * @param array  $params parameters array
1139
+	 */
1140
+	public function injectBlock($type, array $params)
1141
+	{
1142
+		if ($this->debug) {
1143
+			echo 'Compiler::' . __FUNCTION__ . "\n";
1144
+		}
1145
+
1146
+		$class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type);
1147
+		if (class_exists($class) === false) {
1148
+			$this->getDwoo()->getLoader()->loadPlugin($type);
1149
+		}
1150
+		$this->stack[]  = array(
1151
+			'type'   => $type,
1152
+			'params' => $params,
1153
+			'custom' => false,
1154
+			'class'  => $class,
1155
+			'buffer' => null
1156
+		);
1157
+		$this->curBlock = &$this->stack[count($this->stack) - 1];
1158
+	}
1159
+
1160
+	/**
1161
+	 * Removes the closest-to-top block of the given type and all other
1162
+	 * blocks encountered while going down the block stack.
1163
+	 *
1164
+	 * @param string $type block type (name)
1165
+	 *
1166
+	 * @return string the output of all postProcessing() method's return values of the closed blocks
1167
+	 * @throws CompilationException
1168
+	 */
1169
+	public function removeBlock($type)
1170
+	{
1171
+		if ($this->debug) {
1172
+			echo 'Compiler::' . __FUNCTION__ . "\n";
1173
+		}
1174
+
1175
+		$output = '';
1176
+
1177
+		$pluginType = $this->getPluginType($type);
1178
+		if ($pluginType & Core::SMARTY_BLOCK) {
1179
+			$type = 'Smartyinterface';
1180
+		}
1181
+		while (true) {
1182
+			while ($top = array_pop($this->stack)) {
1183
+				if ($top['custom']) {
1184
+					$class = $top['class'];
1185
+				} else {
1186
+					$class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($top['type']);
1187
+				}
1188
+				if (count($this->stack)) {
1189
+					$this->curBlock = &$this->stack[count($this->stack) - 1];
1190
+					$this->push(call_user_func(array(
1191
+						$class,
1192
+						'postProcessing'
1193
+					), $this, $top['params'], '', '', $top['buffer']), 0);
1194
+				} else {
1195
+					$null           = null;
1196
+					$this->curBlock = &$null;
1197
+					$output         = call_user_func(
1198
+						array(
1199
+						$class,
1200
+						'postProcessing'
1201
+						), $this, $top['params'], '', '', $top['buffer']
1202
+					);
1203
+				}
1204
+
1205
+				if ($top['type'] === $type) {
1206
+					break 2;
1207
+				}
1208
+			}
1209
+
1210
+			throw new CompilationException($this, 'Syntax malformation, a block of type "' . $type . '" was closed but was not opened');
1211
+		}
1212
+
1213
+		return $output;
1214
+	}
1215
+
1216
+	/**
1217
+	 * Returns a reference to the first block of the given type encountered and
1218
+	 * optionally closes all blocks until it finds it
1219
+	 * this is mainly used by {else} plugins to close everything that was opened
1220
+	 * between their parent and themselves.
1221
+	 *
1222
+	 * @param string $type       the block type (name)
1223
+	 * @param bool   $closeAlong whether to close all blocks encountered while going down the block stack or not
1224
+	 *
1225
+	 * @return mixed &array the array is as such: array('type'=>pluginName, 'params'=>parameter array,
1226
+	 *               'custom'=>bool defining whether it's a custom plugin or not, for internal use)
1227
+	 * @throws CompilationException
1228
+	 */
1229
+	public function &findBlock($type, $closeAlong = false)
1230
+	{
1231
+		if ($closeAlong === true) {
1232
+			while ($b = end($this->stack)) {
1233
+				if ($b['type'] === $type) {
1234
+					return $this->stack[key($this->stack)];
1235
+				}
1236
+				$this->push($this->removeTopBlock(), 0);
1237
+			}
1238
+		} else {
1239
+			end($this->stack);
1240
+			while ($b = current($this->stack)) {
1241
+				if ($b['type'] === $type) {
1242
+					return $this->stack[key($this->stack)];
1243
+				}
1244
+				prev($this->stack);
1245
+			}
1246
+		}
1247
+
1248
+		throw new CompilationException($this, 'A parent block of type "' . $type . '" is required and can not be found');
1249
+	}
1250
+
1251
+	/**
1252
+	 * Returns a reference to the current block array.
1253
+	 *
1254
+	 * @return array the array is as such: array('type'=>pluginName, 'params'=>parameter array,
1255
+	 *                'custom'=>bool defining whether it's a custom plugin or not, for internal use)
1256
+	 */
1257
+	public function &getCurrentBlock()
1258
+	{
1259
+		return $this->curBlock;
1260
+	}
1261
+
1262
+	/**
1263
+	 * Removes the block at the top of the stack and calls its postProcessing() method.
1264
+	 *
1265
+	 * @return string the postProcessing() method's output
1266
+	 * @throws CompilationException
1267
+	 */
1268
+	public function removeTopBlock()
1269
+	{
1270
+		if ($this->debug) {
1271
+			echo 'Compiler::' . __FUNCTION__ . "\n";
1272
+		}
1273
+
1274
+		$o = array_pop($this->stack);
1275
+		if ($o === null) {
1276
+			throw new CompilationException($this, 'Syntax malformation, a block of unknown type was closed but was not opened.');
1277
+		}
1278
+		if ($o['custom']) {
1279
+			$class = $o['class'];
1280
+		} else {
1281
+			$class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($o['type']);
1282
+		}
1283
+
1284
+		$this->curBlock = &$this->stack[count($this->stack) - 1];
1285
+
1286
+		return call_user_func(array($class, 'postProcessing'), $this, $o['params'], '', '', $o['buffer']);
1287
+	}
1288
+
1289
+	/**
1290
+	 * Returns the compiled parameters (for example a variable's compiled parameter will be "$this->scope['key']") out
1291
+	 * of the given parameter array.
1292
+	 *
1293
+	 * @param array $params parameter array
1294
+	 *
1295
+	 * @return array filtered parameters
1296
+	 */
1297
+	public function getCompiledParams(array $params)
1298
+	{
1299
+		foreach ($params as $k => $p) {
1300
+			if (is_array($p)) {
1301
+				$params[$k] = $p[0];
1302
+			}
1303
+		}
1304
+
1305
+		return $params;
1306
+	}
1307
+
1308
+	/**
1309
+	 * Returns the real parameters (for example a variable's real parameter will be its key, etc) out of the given
1310
+	 * parameter array.
1311
+	 *
1312
+	 * @param array $params parameter array
1313
+	 *
1314
+	 * @return array filtered parameters
1315
+	 */
1316
+	public function getRealParams(array $params)
1317
+	{
1318
+		foreach ($params as $k => $p) {
1319
+			if (is_array($p)) {
1320
+				$params[$k] = $p[1];
1321
+			}
1322
+		}
1323
+
1324
+		return $params;
1325
+	}
1326
+
1327
+	/**
1328
+	 * Returns the token of each parameter out of the given parameter array.
1329
+	 *
1330
+	 * @param array $params parameter array
1331
+	 *
1332
+	 * @return array tokens
1333
+	 */
1334
+	public function getParamTokens(array $params)
1335
+	{
1336
+		foreach ($params as $k => $p) {
1337
+			if (is_array($p)) {
1338
+				$params[$k] = isset($p[2]) ? $p[2] : 0;
1339
+			}
1340
+		}
1341
+
1342
+		return $params;
1343
+	}
1344
+
1345
+	/**
1346
+	 * Entry point of the parser, it redirects calls to other parse* functions.
1347
+	 *
1348
+	 * @param string $in            the string within which we must parse something
1349
+	 * @param int    $from          the starting offset of the parsed area
1350
+	 * @param int    $to            the ending offset of the parsed area
1351
+	 * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
1352
+	 *                              default
1353
+	 * @param string $curBlock      the current parser-block being processed
1354
+	 * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
1355
+	 *                              or null by default
1356
+	 *
1357
+	 * @return string parsed values
1358
+	 * @throws CompilationException
1359
+	 */
1360
+	protected function parse($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
1361
+	{
1362
+		if ($this->debug) {
1363
+			echo 'Compiler::' . __FUNCTION__ . "\n";
1364
+		}
1365
+
1366
+		if ($to === null) {
1367
+			$to = strlen($in);
1368
+		}
1369
+		$first = substr($in, $from, 1);
1370
+
1371
+		if ($first === false) {
1372
+			throw new CompilationException($this, 'Unexpected EOF, a template tag was not closed');
1373
+		}
1374
+
1375
+		while ($first === ' ' || $first === "\n" || $first === "\t" || $first === "\r") {
1376
+			if ($curBlock === 'root' && substr($in, $from, strlen($this->rd)) === $this->rd) {
1377
+				// end template tag
1378
+				$pointer += strlen($this->rd);
1379
+				if ($this->debug) {
1380
+					echo 'TEMPLATE PARSING ENDED' . "\n";
1381
+				}
1382
+
1383
+				return false;
1384
+			}
1385
+			++ $from;
1386
+			if ($pointer !== null) {
1387
+				++ $pointer;
1388
+			}
1389
+			if ($from >= $to) {
1390
+				if (is_array($parsingParams)) {
1391
+					return $parsingParams;
1392
+				} else {
1393
+					return '';
1394
+				}
1395
+			}
1396
+			$first = $in[$from];
1397
+		}
1398
+
1399
+		$substr = substr($in, $from, $to - $from);
1400
+
1401
+		if ($this->debug) {
1402
+			echo 'PARSE CALL : PARSING "' . htmlentities(substr($in, $from, min($to - $from, 50))) . (($to - $from) > 50 ? '...' : '') . '" @ ' . $from . ':' . $to . ' in ' . $curBlock . ' : pointer=' . $pointer . "\n";
1403
+		}
1404
+		$parsed = '';
1405
+
1406
+		if ($curBlock === 'root' && $first === '*') {
1407
+			$src      = $this->getTemplateSource();
1408
+			$startpos = $this->getPointer() - strlen($this->ld);
1409
+			if (substr($src, $startpos, strlen($this->ld)) === $this->ld) {
1410
+				if ($startpos > 0) {
1411
+					do {
1412
+						$char = substr($src, -- $startpos, 1);
1413
+						if ($char == "\n") {
1414
+							++ $startpos;
1415
+							$whitespaceStart = true;
1416
+							break;
1417
+						}
1418
+					}
1419
+					while ($startpos > 0 && ($char == ' ' || $char == "\t"));
1420
+				}
1421
+
1422
+				if (!isset($whitespaceStart)) {
1423
+					$startpos = $this->getPointer();
1424
+				} else {
1425
+					$pointer -= $this->getPointer() - $startpos;
1426
+				}
1427
+
1428
+				if ($this->allowNestedComments && strpos($src, $this->ld . '*', $this->getPointer()) !== false) {
1429
+					$comOpen  = $this->ld . '*';
1430
+					$comClose = '*' . $this->rd;
1431
+					$level    = 1;
1432
+					$ptr      = $this->getPointer();
1433
+
1434
+					while ($level > 0 && $ptr < strlen($src)) {
1435
+						$open  = strpos($src, $comOpen, $ptr);
1436
+						$close = strpos($src, $comClose, $ptr);
1437
+
1438
+						if ($open !== false && $close !== false) {
1439
+							if ($open < $close) {
1440
+								$ptr = $open + strlen($comOpen);
1441
+								++ $level;
1442
+							} else {
1443
+								$ptr = $close + strlen($comClose);
1444
+								-- $level;
1445
+							}
1446
+						} elseif ($open !== false) {
1447
+							$ptr = $open + strlen($comOpen);
1448
+							++ $level;
1449
+						} elseif ($close !== false) {
1450
+							$ptr = $close + strlen($comClose);
1451
+							-- $level;
1452
+						} else {
1453
+							$ptr = strlen($src);
1454
+						}
1455
+					}
1456
+					$endpos = $ptr - strlen('*' . $this->rd);
1457
+				} else {
1458
+					$endpos = strpos($src, '*' . $this->rd, $startpos);
1459
+					if ($endpos == false) {
1460
+						throw new CompilationException($this, 'Un-ended comment');
1461
+					}
1462
+				}
1463
+				$pointer += $endpos - $startpos + strlen('*' . $this->rd);
1464
+				if (isset($whitespaceStart) && preg_match('#^[\t ]*\r?\n#', substr($src, $endpos + strlen('*' . $this->rd)), $m)) {
1465
+					$pointer += strlen($m[0]);
1466
+					$this->curBlock['buffer'] = substr($this->curBlock['buffer'], 0, strlen($this->curBlock['buffer']) - ($this->getPointer() - $startpos - strlen($this->ld)));
1467
+				}
1468
+
1469
+				return false;
1470
+			}
1471
+		}
1472
+
1473
+		if ($first === '$') {
1474
+			// var
1475
+			$out    = $this->parseVar($in, $from, $to, $parsingParams, $curBlock, $pointer);
1476
+			$parsed = 'var';
1477
+		} elseif ($first === '%' && preg_match('#^%[a-z_\\\\]#i', $substr)) {
1478
+			// Short constant
1479
+			$out = $this->parseConst($in, $from, $to, $parsingParams, $curBlock, $pointer);
1480
+		} elseif (($first === '"' || $first === "'") && !(is_array($parsingParams) && preg_match('#^([\'"])[a-z0-9_]+\1\s*=>?(?:\s+|[^=])#i', $substr))) {
1481
+			// string
1482
+			$out = $this->parseString($in, $from, $to, $parsingParams, $curBlock, $pointer);
1483
+		} 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)) {
1484
+			// func
1485
+			$out    = $this->parseFunction($in, $from, $to, $parsingParams, $curBlock, $pointer);
1486
+			$parsed = 'func';
1487
+		} elseif ($first === ';') {
1488
+			// instruction end
1489
+			if ($this->debug) {
1490
+				echo 'END OF INSTRUCTION' . "\n";
1491
+			}
1492
+			if ($pointer !== null) {
1493
+				++ $pointer;
1494
+			}
1495
+
1496
+			return $this->parse($in, $from + 1, $to, false, 'root', $pointer);
1497
+		} elseif ($curBlock === 'root' && preg_match('#^/([a-z_][a-z0-9_]*)?#i', $substr, $match)) {
1498
+			// close block
1499
+			if (!empty($match[1]) && $match[1] == 'else') {
1500
+				throw new CompilationException($this, 'Else blocks must not be closed explicitly, they are automatically closed when their parent block is closed');
1501
+			}
1502
+			if (!empty($match[1]) && $match[1] == 'elseif') {
1503
+				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');
1504
+			}
1505
+			if ($pointer !== null) {
1506
+				$pointer += strlen($match[0]);
1507
+			}
1508
+			if (empty($match[1])) {
1509
+				if ($this->curBlock['type'] == 'else' || $this->curBlock['type'] == 'elseif') {
1510
+					$pointer -= strlen($match[0]);
1511
+				}
1512
+				if ($this->debug) {
1513
+					echo 'TOP BLOCK CLOSED' . "\n";
1514
+				}
1515
+
1516
+				return $this->removeTopBlock();
1517
+			} else {
1518
+				if ($this->debug) {
1519
+					echo 'BLOCK OF TYPE ' . $match[1] . ' CLOSED' . "\n";
1520
+				}
1521
+
1522
+				return $this->removeBlock($match[1]);
1523
+			}
1524
+		} elseif ($curBlock === 'root' && substr($substr, 0, strlen($this->rd)) === $this->rd) {
1525
+			// end template tag
1526
+			if ($this->debug) {
1527
+				echo 'TAG PARSING ENDED' . "\n";
1528
+			}
1529
+			$pointer += strlen($this->rd);
1530
+
1531
+			return false;
1532
+		} elseif (is_array($parsingParams) && preg_match('#^(([\'"]?)[a-z0-9_]+\2\s*=' . ($curBlock === 'array' ? '>?' : '') . ')(?:\s+|[^=]).*#i', $substr, $match)) {
1533
+			// named parameter
1534
+			if ($this->debug) {
1535
+				echo 'NAMED PARAM FOUND' . "\n";
1536
+			}
1537
+			$len = strlen($match[1]);
1538
+			while (substr($in, $from + $len, 1) === ' ') {
1539
+				++ $len;
1540
+			}
1541
+			if ($pointer !== null) {
1542
+				$pointer += $len;
1543
+			}
1544
+
1545
+			$output = array(
1546
+				trim($match[1], " \t\r\n=>'\""),
1547
+				$this->parse($in, $from + $len, $to, false, 'namedparam', $pointer)
1548
+			);
1549
+
1550
+			$parsingParams[] = $output;
1551
+
1552
+			return $parsingParams;
1553
+		} elseif (preg_match('#^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*::\$[a-z0-9_]+)#i', $substr, $match)) {
1554
+			// static member access
1555
+			$parsed = 'var';
1556
+			if (is_array($parsingParams)) {
1557
+				$parsingParams[] = array($match[1], $match[1]);
1558
+				$out             = $parsingParams;
1559
+			} else {
1560
+				$out = $match[1];
1561
+			}
1562
+			$pointer += strlen($match[1]);
1563
+		} elseif ($substr !== '' && (is_array($parsingParams) || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'expression')) {
1564
+			// unquoted string, bool or number
1565
+			$out = $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
1566
+		} else {
1567
+			// parse error
1568
+			throw new CompilationException($this, 'Parse error in "' . substr($in, $from, $to - $from) . '"');
1569
+		}
1570
+
1571
+		if (empty($out)) {
1572
+			return '';
1573
+		}
1574
+
1575
+		$substr = substr($in, $pointer, $to - $pointer);
1576
+
1577
+		// var parsed, check if any var-extension applies
1578
+		if ($parsed === 'var') {
1579
+			if (preg_match('#^\s*([/%+*-])\s*([a-z0-9]|\$)#i', $substr, $match)) {
1580
+				if ($this->debug) {
1581
+					echo 'PARSING POST-VAR EXPRESSION ' . $substr . "\n";
1582
+				}
1583
+				// parse expressions
1584
+				$pointer += strlen($match[0]) - 1;
1585
+				if (is_array($parsingParams)) {
1586
+					if ($match[2] == '$') {
1587
+						$expr = $this->parseVar($in, $pointer, $to, array(), $curBlock, $pointer);
1588
+					} else {
1589
+						$expr = $this->parse($in, $pointer, $to, array(), 'expression', $pointer);
1590
+					}
1591
+					$out[count($out) - 1][0] .= $match[1] . $expr[0][0];
1592
+					$out[count($out) - 1][1] .= $match[1] . $expr[0][1];
1593
+				} else {
1594
+					if ($match[2] == '$') {
1595
+						$expr = $this->parseVar($in, $pointer, $to, false, $curBlock, $pointer);
1596
+					} else {
1597
+						$expr = $this->parse($in, $pointer, $to, false, 'expression', $pointer);
1598
+					}
1599
+					if (is_array($out) && is_array($expr)) {
1600
+						$out[0] .= $match[1] . $expr[0];
1601
+						$out[1] .= $match[1] . $expr[1];
1602
+					} elseif (is_array($out)) {
1603
+						$out[0] .= $match[1] . $expr;
1604
+						$out[1] .= $match[1] . $expr;
1605
+					} elseif (is_array($expr)) {
1606
+						$out .= $match[1] . $expr[0];
1607
+					} else {
1608
+						$out .= $match[1] . $expr;
1609
+					}
1610
+				}
1611
+			} elseif ($curBlock === 'root' && preg_match('#^(\s*(?:[+/*%-.]=|=|\+\+|--)\s*)(.*)#s', $substr, $match)) {
1612
+				if ($this->debug) {
1613
+					echo 'PARSING POST-VAR ASSIGNMENT ' . $substr . "\n";
1614
+				}
1615
+				// parse assignment
1616
+				$value    = $match[2];
1617
+				$operator = trim($match[1]);
1618
+				if (substr($value, 0, 1) == '=') {
1619
+					throw new CompilationException($this, 'Unexpected "=" in <em>' . $substr . '</em>');
1620
+				}
1621
+
1622
+				if ($pointer !== null) {
1623
+					$pointer += strlen($match[1]);
1624
+				}
1625
+
1626
+				if ($operator !== '++' && $operator !== '--') {
1627
+					$parts = array();
1628
+					$ptr   = 0;
1629
+					$parts = $this->parse($value, 0, strlen($value), $parts, 'condition', $ptr);
1630
+					$pointer += $ptr;
1631
+
1632
+					// load if plugin
1633
+					try {
1634
+						$this->getPluginType('if');
1635
+					}
1636
+					catch (Exception $e) {
1637
+						throw new CompilationException($this, 'Assignments require the "if" plugin to be accessible');
1638
+					}
1639
+
1640
+					$parts  = $this->mapParams($parts, array(Core::NAMESPACE_PLUGINS_BLOCKS . 'PluginIf', 'init'), 1);
1641
+					$tokens = $this->getParamTokens($parts);
1642
+					$parts  = $this->getCompiledParams($parts);
1643
+
1644
+					$value = PluginIf::replaceKeywords($parts['*'], $tokens['*'], $this);
1645
+					$echo  = '';
1646
+				} else {
1647
+					$value = array();
1648
+					$echo  = 'echo ';
1649
+				}
1650
+
1651
+				if ($this->autoEscape) {
1652
+					$out = preg_replace('#\(is_string\(\$tmp=(.+?)\) \? htmlspecialchars\(\$tmp, ENT_QUOTES, \$this->charset\) : \$tmp\)#', '$1', $out);
1653
+				}
1654
+				$out = self::PHP_OPEN . $echo . $out . $operator . implode(' ', $value) . self::PHP_CLOSE;
1655
+			} elseif ($curBlock === 'array' && is_array($parsingParams) && preg_match('#^(\s*=>?\s*)#', $substr, $match)) {
1656
+				// parse namedparam with var as name (only for array)
1657
+				if ($this->debug) {
1658
+					echo 'VARIABLE NAMED PARAM (FOR ARRAY) FOUND' . "\n";
1659
+				}
1660
+				$len = strlen($match[1]);
1661
+				$var = $out[count($out) - 1];
1662
+				$pointer += $len;
1663
+
1664
+				$output = array($var[0], $this->parse($substr, $len, null, false, 'namedparam', $pointer));
1665
+
1666
+				$parsingParams[] = $output;
1667
+
1668
+				return $parsingParams;
1669
+			}
1670
+		}
1671
+
1672
+		if ($curBlock !== 'modifier' && ($parsed === 'func' || $parsed === 'var') && preg_match('#^(\|@?[a-z0-9_]+(:.*)?)+#i', $substr, $match)) {
1673
+			// parse modifier on funcs or vars
1674
+			$srcPointer = $pointer;
1675
+			if (is_array($parsingParams)) {
1676
+				$tmp                     = $this->replaceModifiers(
1677
+					array(
1678
+					null,
1679
+					null,
1680
+					$out[count($out) - 1][0],
1681
+					$match[0]
1682
+					), $curBlock, $pointer
1683
+				);
1684
+				$out[count($out) - 1][0] = $tmp;
1685
+				$out[count($out) - 1][1] .= substr($substr, $srcPointer, $srcPointer - $pointer);
1686
+			} else {
1687
+				$out = $this->replaceModifiers(array(null, null, $out, $match[0]), $curBlock, $pointer);
1688
+			}
1689
+		}
1690
+
1691
+		// func parsed, check if any func-extension applies
1692
+		if ($parsed === 'func' && preg_match('#^->[a-z0-9_]+(\s*\(.+|->[a-z_].*)?#is', $substr, $match)) {
1693
+			// parse method call or property read
1694
+			$ptr = 0;
1695
+
1696
+			if (is_array($parsingParams)) {
1697
+				$output = $this->parseMethodCall($out[count($out) - 1][1], $match[0], $curBlock, $ptr);
1698
+
1699
+				$out[count($out) - 1][0] = $output;
1700
+				$out[count($out) - 1][1] .= substr($match[0], 0, $ptr);
1701
+			} else {
1702
+				$out = $this->parseMethodCall($out, $match[0], $curBlock, $ptr);
1703
+			}
1704
+
1705
+			$pointer += $ptr;
1706
+		}
1707
+
1708
+		if ($curBlock === 'root' && substr($out, 0, strlen(self::PHP_OPEN)) !== self::PHP_OPEN) {
1709
+			return self::PHP_OPEN . 'echo ' . $out . ';' . self::PHP_CLOSE;
1710
+		}
1711
+
1712
+		return $out;
1713
+	}
1714
+
1715
+	/**
1716
+	 * Parses a function call.
1717
+	 *
1718
+	 * @param string $in            the string within which we must parse something
1719
+	 * @param int    $from          the starting offset of the parsed area
1720
+	 * @param int    $to            the ending offset of the parsed area
1721
+	 * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
1722
+	 *                              default
1723
+	 * @param string $curBlock      the current parser-block being processed
1724
+	 * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
1725
+	 *                              or null by default
1726
+	 *
1727
+	 * @return string parsed values
1728
+	 * @throws CompilationException
1729
+	 * @throws Exception
1730
+	 * @throws SecurityException
1731
+	 */
1732
+	protected function parseFunction($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
1733
+	{
1734
+		$output = '';
1735
+		$cmdstr = substr($in, $from, $to - $from);
1736
+		preg_match('/^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*(?:::[a-z_][a-z0-9_]*)?)(\s*' . $this->rdr . '|\s*;)?/i', $cmdstr, $match);
1737
+
1738
+		if (empty($match[1])) {
1739
+			throw new CompilationException($this, 'Parse error, invalid function name : ' . substr($cmdstr, 0, 15));
1740
+		}
1741
+
1742
+		$func = $match[1];
1743
+
1744
+		if (!empty($match[2])) {
1745
+			$cmdstr = $match[1];
1746
+		}
1747
+
1748
+		if ($this->debug) {
1749
+			echo 'FUNC FOUND (' . $func . ')' . "\n";
1750
+		}
1751
+
1752
+		$paramsep = '';
1753
+
1754
+		if (is_array($parsingParams) || $curBlock != 'root') {
1755
+			$paramspos = strpos($cmdstr, '(');
1756
+			$paramsep  = ')';
1757
+		} elseif (preg_match_all('#^\s*[\\\\:a-z0-9_]+(\s*\(|\s+[^(])#i', $cmdstr, $match, PREG_OFFSET_CAPTURE)) {
1758
+			$paramspos = $match[1][0][1];
1759
+			$paramsep  = substr($match[1][0][0], - 1) === '(' ? ')' : '';
1760
+			if ($paramsep === ')') {
1761
+				$paramspos += strlen($match[1][0][0]) - 1;
1762
+				if (substr($cmdstr, 0, 2) === 'if' || substr($cmdstr, 0, 6) === 'elseif') {
1763
+					$paramsep = '';
1764
+					if (strlen($match[1][0][0]) > 1) {
1765
+						-- $paramspos;
1766
+					}
1767
+				}
1768
+			}
1769
+		} else {
1770
+			$paramspos = false;
1771
+		}
1772
+
1773
+		$state = 0;
1774
+
1775
+		if ($paramspos === false) {
1776
+			$params = array();
1777
+
1778
+			if ($curBlock !== 'root') {
1779
+				return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
1780
+			}
1781
+		} else {
1782
+			if ($curBlock === 'condition') {
1783
+				// load if plugin
1784
+				$this->getPluginType('if');
1785
+
1786
+				if (PluginIf::replaceKeywords(array($func), array(self::T_UNQUOTED_STRING), $this) !== array($func)) {
1787
+					return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
1788
+				}
1789
+			}
1790
+			$whitespace = strlen(substr($cmdstr, strlen($func), $paramspos - strlen($func)));
1791
+			$paramstr   = substr($cmdstr, $paramspos + 1);
1792
+			if (substr($paramstr, - 1, 1) === $paramsep) {
1793
+				$paramstr = substr($paramstr, 0, - 1);
1794
+			}
1795
+
1796
+			if (strlen($paramstr) === 0) {
1797
+				$params   = array();
1798
+				$paramstr = '';
1799
+			} else {
1800
+				$ptr    = 0;
1801
+				$params = array();
1802
+				if ($func === 'empty') {
1803
+					$params = $this->parseVar($paramstr, $ptr, strlen($paramstr), $params, 'root', $ptr);
1804
+				} else {
1805
+					while ($ptr < strlen($paramstr)) {
1806
+						while (true) {
1807
+							if ($ptr >= strlen($paramstr)) {
1808
+								break 2;
1809
+							}
1810
+
1811
+							if ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === ')') {
1812
+								if ($this->debug) {
1813
+									echo 'PARAM PARSING ENDED, ")" FOUND, POINTER AT ' . $ptr . "\n";
1814
+								}
1815
+								break 2;
1816
+							} elseif ($paramstr[$ptr] === ';') {
1817
+								++ $ptr;
1818
+								if ($this->debug) {
1819
+									echo 'PARAM PARSING ENDED, ";" FOUND, POINTER AT ' . $ptr . "\n";
1820
+								}
1821
+								break 2;
1822
+							} elseif ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === '/') {
1823
+								if ($this->debug) {
1824
+									echo 'PARAM PARSING ENDED, "/" FOUND, POINTER AT ' . $ptr . "\n";
1825
+								}
1826
+								break 2;
1827
+							} elseif (substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) {
1828
+								if ($this->debug) {
1829
+									echo 'PARAM PARSING ENDED, RIGHT DELIMITER FOUND, POINTER AT ' . $ptr . "\n";
1830
+								}
1831
+								break 2;
1832
+							}
1833
+
1834
+							if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === ',' || $paramstr[$ptr] === "\r" || $paramstr[$ptr] === "\n" || $paramstr[$ptr] === "\t") {
1835
+								++ $ptr;
1836
+							} else {
1837
+								break;
1838
+							}
1839
+						}
1840
+
1841
+						if ($this->debug) {
1842
+							echo 'FUNC START PARAM PARSING WITH POINTER AT ' . $ptr . "\n";
1843
+						}
1844
+
1845
+						if ($func === 'if' || $func === 'elseif' || $func === 'tif') {
1846
+							$params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'condition', $ptr);
1847
+						} elseif ($func === 'array') {
1848
+							$params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'array', $ptr);
1849
+						} else {
1850
+							$params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'function', $ptr);
1851
+						}
1852
+
1853
+						if ($this->debug) {
1854
+							echo 'PARAM PARSED, POINTER AT ' . $ptr . ' (' . substr($paramstr, $ptr - 1, 3) . ')' . "\n";
1855
+						}
1856
+					}
1857
+				}
1858
+				$paramstr = substr($paramstr, 0, $ptr);
1859
+				$state    = 0;
1860
+				foreach ($params as $k => $p) {
1861
+					if (is_array($p) && is_array($p[1])) {
1862
+						$state |= 2;
1863
+					} else {
1864
+						if (($state & 2) && preg_match('#^(["\'])(.+?)\1$#', $p[0], $m) && $func !== 'array') {
1865
+							$params[$k] = array($m[2], array('true', 'true'));
1866
+						} else {
1867
+							if ($state & 2 && $func !== 'array') {
1868
+								throw new CompilationException($this, 'You can not use an unnamed parameter after a named one');
1869
+							}
1870
+							$state |= 1;
1871
+						}
1872
+					}
1873
+				}
1874
+			}
1875
+		}
1876
+
1877
+		if ($pointer !== null) {
1878
+			$pointer += (isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func) + (isset($whitespace) ? $whitespace : 0);
1879
+			if ($this->debug) {
1880
+				echo 'FUNC ADDS ' . ((isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func)) . ' TO POINTER' . "\n";
1881
+			}
1882
+		}
1883
+
1884
+		if ($curBlock === 'method' || $func === 'do' || strstr($func, '::') !== false) {
1885
+			// handle static method calls with security policy
1886
+			if (strstr($func, '::') !== false && $this->securityPolicy !== null && $this->securityPolicy->isMethodAllowed(explode('::', strtolower($func))) !== true) {
1887
+				throw new SecurityException('Call to a disallowed php function : ' . $func);
1888
+			}
1889
+			$pluginType = Core::NATIVE_PLUGIN;
1890
+		} else {
1891
+			$pluginType = $this->getPluginType($func);
1892
+		}
1893
+
1894
+		// Blocks plugin
1895
+		if ($pluginType & Core::BLOCK_PLUGIN) {
1896
+			if ($curBlock !== 'root' || is_array($parsingParams)) {
1897
+				throw new CompilationException($this, 'Block plugins can not be used as other plugin\'s arguments');
1898
+			}
1899
+			if ($pluginType & Core::CUSTOM_PLUGIN) {
1900
+				return $this->addCustomBlock($func, $params, $state);
1901
+			} else {
1902
+				return $this->addBlock($func, $params, $state);
1903
+			}
1904
+		} elseif ($pluginType & Core::SMARTY_BLOCK) {
1905
+			if ($curBlock !== 'root' || is_array($parsingParams)) {
1906
+				throw new CompilationException($this, 'Block plugins can not be used as other plugin\'s arguments');
1907
+			}
1908
+
1909
+			if ($state & 2) {
1910
+				array_unshift($params, array('__functype', array($pluginType, $pluginType)));
1911
+				array_unshift($params, array('__funcname', array($func, $func)));
1912
+			} else {
1913
+				array_unshift($params, array($pluginType, $pluginType));
1914
+				array_unshift($params, array($func, $func));
1915
+			}
1916
+
1917
+			return $this->addBlock('smartyinterface', $params, $state);
1918
+		}
1919
+
1920
+		// Native & Smarty plugins
1921
+		if ($pluginType & Core::NATIVE_PLUGIN || $pluginType & Core::SMARTY_FUNCTION || $pluginType & Core::SMARTY_BLOCK) {
1922
+			$params = $this->mapParams($params, null, $state);
1923
+		} // PHP class plugin
1924
+		elseif ($pluginType & Core::CLASS_PLUGIN) {
1925
+			if ($pluginType & Core::CUSTOM_PLUGIN) {
1926
+				$params = $this->mapParams(
1927
+					$params, array(
1928
+					$this->customPlugins[$func]['class'],
1929
+					$this->customPlugins[$func]['function']
1930
+				), $state);
1931
+			} else {
1932
+				if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
1933
+					$params = $this->mapParams($params, array(
1934
+						'Plugin' . Core::toCamelCase($func),
1935
+						($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
1936
+					), $state);
1937
+				} elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) {
1938
+					$params = $this->mapParams($params, array(
1939
+						Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func),
1940
+						($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
1941
+					), $state);
1942
+				} else {
1943
+					$params = $this->mapParams($params, array(
1944
+						Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
1945
+						($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
1946
+					), $state);
1947
+				}
1948
+			}
1949
+		} // PHP function plugin
1950
+		elseif ($pluginType & Core::FUNC_PLUGIN) {
1951
+			if ($pluginType & Core::CUSTOM_PLUGIN) {
1952
+				$params = $this->mapParams($params, $this->customPlugins[$func]['callback'], $state);
1953
+			} else {
1954
+				// Custom plugin
1955
+				if (function_exists('Plugin' . Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ?
1956
+						'Compile' : '')) !== false) {
1957
+					$params = $this->mapParams($params, 'Plugin' . Core::toCamelCase($func) . (($pluginType &
1958
+							Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1959
+				} // Builtin helper plugin
1960
+				elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . (
1961
+					($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '')) !== false) {
1962
+					$params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase
1963
+						($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1964
+				} // Builtin function plugin
1965
+				else {
1966
+					$params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase
1967
+						($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1968
+				}
1969
+			}
1970
+		} // Smarty modifier
1971
+		elseif ($pluginType & Core::SMARTY_MODIFIER) {
1972
+			$output = 'smarty_modifier_' . $func . '(' . implode(', ', $params) . ')';
1973
+		} // Proxy plugin
1974
+		elseif ($pluginType & Core::PROXY_PLUGIN) {
1975
+			$params = $this->mapParams($params, $this->getDwoo()->getPluginProxy()->getCallback($func), $state);
1976
+		} // Template plugin
1977
+		elseif ($pluginType & Core::TEMPLATE_PLUGIN) {
1978
+			// transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values))
1979
+			$map = array();
1980
+			foreach ($this->templatePlugins[$func]['params'] as $param => $defValue) {
1981
+				if ($param == 'rest') {
1982
+					$param = '*';
1983
+				}
1984
+				$hasDefault = $defValue !== null;
1985
+				if ($defValue === 'null') {
1986
+					$defValue = null;
1987
+				} elseif ($defValue === 'false') {
1988
+					$defValue = false;
1989
+				} elseif ($defValue === 'true') {
1990
+					$defValue = true;
1991
+				} elseif (preg_match('#^([\'"]).*?\1$#', $defValue)) {
1992
+					$defValue = substr($defValue, 1, - 1);
1993
+				}
1994
+				$map[] = array($param, $hasDefault, $defValue);
1995
+			}
1996
+
1997
+			$params = $this->mapParams($params, null, $state, $map);
1998
+		}
1999
+
2000
+		// only keep php-syntax-safe values for non-block plugins
2001
+		$tokens = array();
2002
+		foreach ($params as $k => $p) {
2003
+			$tokens[$k] = isset($p[2]) ? $p[2] : 0;
2004
+			$params[$k] = $p[0];
2005
+		}
2006
+
2007
+		// Native plugin
2008
+		if ($pluginType & Core::NATIVE_PLUGIN) {
2009
+			if ($func === 'do') {
2010
+				$output = '';
2011
+				if (isset($params['*'])) {
2012
+					$output = implode(';', $params['*']) . ';';
2013
+				}
2014
+
2015
+				if (is_array($parsingParams) || $curBlock !== 'root') {
2016
+					throw new CompilationException($this, 'Do can not be used inside another function or block');
2017
+				}
2018
+
2019
+				return self::PHP_OPEN . $output . self::PHP_CLOSE;
2020
+			} else {
2021
+				if (isset($params['*'])) {
2022
+					$output = $func . '(' . implode(', ', $params['*']) . ')';
2023
+				} else {
2024
+					$output = $func . '()';
2025
+				}
2026
+			}
2027
+		} // Block class OR Function class
2028
+		elseif ($pluginType & Core::CLASS_PLUGIN || ($pluginType & Core::FUNC_PLUGIN && $pluginType & Core::CLASS_PLUGIN)) {
2029
+			if ($pluginType & Core::COMPILABLE_PLUGIN) {
2030
+				if ($pluginType & Core::CUSTOM_PLUGIN) {
2031
+					$callback = $this->customPlugins[$func]['callback'];
2032
+					if (!is_array($callback)) {
2033
+						if (!method_exists($callback, 'compile')) {
2034
+							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');
2035
+						}
2036
+						if (($ref = new ReflectionMethod($callback, 'compile')) && $ref->isStatic()) {
2037
+							$funcCompiler = array($callback, 'compile');
2038
+						} else {
2039
+							$funcCompiler = array(new $callback(), 'compile');
2040
+						}
2041
+					} else {
2042
+						$funcCompiler = $callback;
2043
+					}
2044
+				} else {
2045
+					if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
2046
+						$funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile');
2047
+					} elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) {
2048
+						$funcCompiler = array(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func), 'compile');
2049
+					} else {
2050
+						$funcCompiler = array(
2051
+							Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
2052
+							'compile'
2053
+						);
2054
+					}
2055
+					array_unshift($params, $this);
2056
+				}
2057
+				// @TODO: Is it a real fix ?
2058
+				if ($func === 'tif') {
2059
+					$params[] = $tokens;
2060
+				}
2061
+				$output = call_user_func_array($funcCompiler, $params);
2062
+			} else {
2063
+				$params = self::implode_r($params);
2064
+				if ($pluginType & Core::CUSTOM_PLUGIN) {
2065
+					$callback = $this->customPlugins[$func]['callback'];
2066
+					if (!is_array($callback)) {
2067
+						if (!method_exists($callback, 'process')) {
2068
+							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');
2069
+						}
2070
+						if (($ref = new ReflectionMethod($callback, 'process')) && $ref->isStatic()) {
2071
+							$output = 'call_user_func(array(\'' . $callback . '\', \'process\'), ' . $params . ')';
2072
+						} else {
2073
+							$output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback . '\'), \'process\'), ' . $params . ')';
2074
+						}
2075
+					} elseif (is_object($callback[0])) {
2076
+						$output = 'call_user_func(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), ' . $params . ')';
2077
+					} elseif (($ref = new ReflectionMethod($callback[0], $callback[1])) && $ref->isStatic()) {
2078
+						$output = 'call_user_func(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), ' . $params . ')';
2079
+					} else {
2080
+						$output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback[0] . '\'), \'' . $callback[1] . '\'), ' . $params . ')';
2081
+					}
2082
+					if (empty($params)) {
2083
+						$output = substr($output, 0, - 3) . ')';
2084
+					}
2085
+				} else {
2086
+					if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
2087
+						$output = '$this->classCall(\'Plugin' . $func . '\', array(' . $params . '))';
2088
+					} elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) {
2089
+						$output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . $func . '\', 
2090 2090
                         array(' . $params . '))';
2091
-                    } else {
2092
-                        $output = '$this->classCall(\'' . $func . '\', array(' . $params . '))';
2093
-                    }
2094
-                }
2095
-            }
2096
-        } // Function plugin only (cannot be a class)
2097
-        elseif ($pluginType & Core::FUNC_PLUGIN) {
2098
-            if ($pluginType & Core::COMPILABLE_PLUGIN) {
2099
-                if ($pluginType & Core::CUSTOM_PLUGIN) {
2100
-                    $funcCompiler = $this->customPlugins[$func]['callback'];
2101
-                } else {
2102
-                    // Custom plugin
2103
-                    if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
2104
-                        $funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile';
2105
-                    } // Builtin helper plugin
2106
-                    elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
2107
-                        $funcCompiler = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) .
2108
-                            'Compile';
2109
-                    } // Builtin function plugin
2110
-                    else {
2111
-                        $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) .
2112
-                            'Compile';
2113
-                    }
2114
-                }
2115
-                array_unshift($params, $this);
2116
-                // @TODO: Is it a real fix ?
2117
-                if ($func === 'tif') {
2118
-                    $params[] = $tokens;
2119
-                }
2120
-                $output = call_user_func_array($funcCompiler, $params);
2121
-            } else {
2122
-                array_unshift($params, '$this');
2123
-                $params = self::implode_r($params);
2124
-                if ($pluginType & Core::CUSTOM_PLUGIN) {
2125
-                    $callback = $this->customPlugins[$func]['callback'];
2126
-                    if ($callback instanceof Closure) {
2127
-                        $output = 'call_user_func($this->getCustomPlugin(\'' . $func . '\'), ' . $params . ')';
2128
-                    } else {
2129
-                        $output = 'call_user_func(\'' . $callback . '\', ' . $params . ')';
2130
-                    }
2131
-                } else {
2132
-                    // Custom plugin
2133
-                    if (function_exists('Plugin' . Core::toCamelCase($func)) !== false) {
2134
-                        $output = 'Plugin' . Core::toCamelCase($func) . '(' . $params .
2135
-                            ')';
2136
-                    } // Builtin helper plugin
2137
-                    elseif(function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !==
2138
-                        false) {
2139
-                        $output = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . '(' .
2140
-                            $params . ')';
2141
-                    } // Builtin function plugin
2142
-                    else {
2143
-                        $output = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '(' .
2144
-                            $params . ')';
2145
-                    }
2146
-                }
2147
-            }
2148
-        } // Proxy plugin
2149
-        elseif ($pluginType & Core::PROXY_PLUGIN) {
2150
-            $output = call_user_func(array($this->getDwoo()->getPluginProxy(), 'getCode'), $func, $params);
2151
-        } // Smarty function (@deprecated)
2152
-        elseif ($pluginType & Core::SMARTY_FUNCTION) {
2153
-            $params = '';
2154
-            if (isset($params['*'])) {
2155
-                $params = self::implode_r($params['*'], true);
2156
-            }
2157
-
2158
-            if ($pluginType & Core::CUSTOM_PLUGIN) {
2159
-                $callback = $this->customPlugins[$func]['callback'];
2160
-                if (is_array($callback)) {
2161
-                    if (is_object($callback[0])) {
2162
-                        $output = 'call_user_func_array(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(array(' . $params . '), $this))';
2163
-                    } else {
2164
-                        $output = 'call_user_func_array(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(array(' . $params . '), $this))';
2165
-                    }
2166
-                } else {
2167
-                    $output = $callback . '(array(' . $params . '), $this)';
2168
-                }
2169
-            } else {
2170
-                $output = 'smarty_function_' . $func . '(array(' . $params . '), $this)';
2171
-            }
2172
-        } // Template plugin
2173
-        elseif ($pluginType & Core::TEMPLATE_PLUGIN) {
2174
-            array_unshift($params, '$this');
2175
-            $params                                 = self::implode_r($params);
2176
-            $output                                 = 'Plugin' . Core::toCamelCase($func) .
2177
-                $this->templatePlugins[$func]['uuid'] . '(' . $params . ')';
2178
-            $this->templatePlugins[$func]['called'] = true;
2179
-        }
2180
-
2181
-        if (is_array($parsingParams)) {
2182
-            $parsingParams[] = array($output, $output);
2183
-
2184
-            return $parsingParams;
2185
-        } elseif ($curBlock === 'namedparam') {
2186
-            return array($output, $output);
2187
-        }
2188
-
2189
-        return $output;
2190
-    }
2191
-
2192
-    /**
2193
-     * Parses a string.
2194
-     *
2195
-     * @param string $in            the string within which we must parse something
2196
-     * @param int    $from          the starting offset of the parsed area
2197
-     * @param int    $to            the ending offset of the parsed area
2198
-     * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
2199
-     *                              default
2200
-     * @param string $curBlock      the current parser-block being processed
2201
-     * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
2202
-     *                              or null by default
2203
-     *
2204
-     * @return string parsed values
2205
-     * @throws CompilationException
2206
-     */
2207
-    protected function parseString($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2208
-    {
2209
-        $substr = substr($in, $from, $to - $from);
2210
-        $first  = $substr[0];
2211
-
2212
-        if ($this->debug) {
2213
-            echo 'STRING FOUND (in ' . htmlentities(substr($in, $from, min($to - $from, 50))) . (($to - $from) > 50 ? '...' : '') . ')' . "\n";
2214
-        }
2215
-        $strend = false;
2216
-        $o      = $from + 1;
2217
-        while ($strend === false) {
2218
-            $strend = strpos($in, $first, $o);
2219
-            if ($strend === false) {
2220
-                throw new CompilationException($this, 'Unfinished string, started with ' . substr($in, $from, $to - $from));
2221
-            }
2222
-            if (substr($in, $strend - 1, 1) === '\\') {
2223
-                $o      = $strend + 1;
2224
-                $strend = false;
2225
-            }
2226
-        }
2227
-        if ($this->debug) {
2228
-            echo 'STRING DELIMITED: ' . substr($in, $from, $strend + 1 - $from) . "\n";
2229
-        }
2230
-
2231
-        $srcOutput = substr($in, $from, $strend + 1 - $from);
2232
-
2233
-        if ($pointer !== null) {
2234
-            $pointer += strlen($srcOutput);
2235
-        }
2236
-
2237
-        $output = $this->replaceStringVars($srcOutput, $first);
2238
-
2239
-        // handle modifiers
2240
-        if ($curBlock !== 'modifier' && preg_match('#^((?:\|(?:@?[a-z0-9_]+(?::.*)*))+)#i', substr($substr, $strend + 1 - $from), $match)) {
2241
-            $modstr = $match[1];
2242
-
2243
-            if ($curBlock === 'root' && substr($modstr, - 1) === '}') {
2244
-                $modstr = substr($modstr, 0, - 1);
2245
-            }
2246
-            $modstr = str_replace('\\' . $first, $first, $modstr);
2247
-            $ptr    = 0;
2248
-            $output = $this->replaceModifiers(array(null, null, $output, $modstr), 'string', $ptr);
2249
-
2250
-            $strend += $ptr;
2251
-            if ($pointer !== null) {
2252
-                $pointer += $ptr;
2253
-            }
2254
-            $srcOutput .= substr($substr, $strend + 1 - $from, $ptr);
2255
-        }
2256
-
2257
-        if (is_array($parsingParams)) {
2258
-            $parsingParams[] = array($output, substr($srcOutput, 1, - 1));
2259
-
2260
-            return $parsingParams;
2261
-        } elseif ($curBlock === 'namedparam') {
2262
-            return array($output, substr($srcOutput, 1, - 1));
2263
-        }
2264
-
2265
-        return $output;
2266
-    }
2267
-
2268
-    /**
2269
-     * Parses a constant.
2270
-     *
2271
-     * @param string $in            the string within which we must parse something
2272
-     * @param int    $from          the starting offset of the parsed area
2273
-     * @param int    $to            the ending offset of the parsed area
2274
-     * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
2275
-     *                              default
2276
-     * @param string $curBlock      the current parser-block being processed
2277
-     * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
2278
-     *                              or null by default
2279
-     *
2280
-     * @return string parsed values
2281
-     * @throws CompilationException
2282
-     */
2283
-    protected function parseConst($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2284
-    {
2285
-        $substr = substr($in, $from, $to - $from);
2286
-
2287
-        if ($this->debug) {
2288
-            echo 'CONST FOUND : ' . $substr . "\n";
2289
-        }
2290
-
2291
-        if (!preg_match('#^%([\\\\a-z0-9_:]+)#i', $substr, $m)) {
2292
-            throw new CompilationException($this, 'Invalid constant');
2293
-        }
2294
-
2295
-        if ($pointer !== null) {
2296
-            $pointer += strlen($m[0]);
2297
-        }
2298
-
2299
-        $output = $this->parseConstKey($m[1], $curBlock);
2300
-
2301
-        if (is_array($parsingParams)) {
2302
-            $parsingParams[] = array($output, $m[1]);
2303
-
2304
-            return $parsingParams;
2305
-        } elseif ($curBlock === 'namedparam') {
2306
-            return array($output, $m[1]);
2307
-        }
2308
-
2309
-        return $output;
2310
-    }
2311
-
2312
-    /**
2313
-     * Parses a constant.
2314
-     *
2315
-     * @param string $key      the constant to parse
2316
-     * @param string $curBlock the current parser-block being processed
2317
-     *
2318
-     * @return string parsed constant
2319
-     */
2320
-    protected function parseConstKey($key, $curBlock)
2321
-    {
2322
-        $key = str_replace('\\\\', '\\', $key);
2323
-
2324
-        if ($this->securityPolicy !== null && $this->securityPolicy->getConstantHandling() === SecurityPolicy::CONST_DISALLOW) {
2325
-            return 'null';
2326
-        }
2327
-
2328
-        if ($curBlock !== 'root') {
2329
-            return '(defined("' . $key . '") ? ' . $key . ' : null)';
2330
-        }
2331
-
2332
-        return $key;
2333
-    }
2334
-
2335
-    /**
2336
-     * Parses a variable.
2337
-     *
2338
-     * @param string $in            the string within which we must parse something
2339
-     * @param int    $from          the starting offset of the parsed area
2340
-     * @param int    $to            the ending offset of the parsed area
2341
-     * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
2342
-     *                              default
2343
-     * @param string $curBlock      the current parser-block being processed
2344
-     * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
2345
-     *                              or null by default
2346
-     *
2347
-     * @return string parsed values
2348
-     * @throws CompilationException
2349
-     */
2350
-    protected function parseVar($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2351
-    {
2352
-        $substr = substr($in, $from, $to - $from);
2353
-
2354
-        // var key
2355
-        $varRegex = '(\\$?\\.?[a-z0-9\\\\_:]*(?:(?:(?:\\.|->)(?:[a-z0-9\\\\_:]+|(?R))|\\[(?:[a-z0-9\\\\_:]+|(?R)|(["\'])[^\\2]*?\\2)\\]))*)';
2356
-        // method call
2357
-        $methodCall = ($curBlock === 'root' || $curBlock === 'function' || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'variable' || $curBlock === 'expression' || $curBlock === 'delimited_string' ? '(\(.*)?' : '()');
2358
-        // simple math expressions
2359
-        $simpleMathExpressions = ($curBlock === 'root' || $curBlock === 'function' || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'variable' || $curBlock === 'delimited_string' ? '((?:(?:[+\/*%=-])(?:(?<!=)=?-?[$%][a-z0-9\\\\.[\]>_:-]+(?:\([^)]*\))?|(?<!=)=?-?[0-9\.,]*|[+-]))*)' : '()');
2360
-        // modifiers
2361
-        $modifiers = $curBlock !== 'modifier' ? '((?:\|(?:@?[a-z0-9\\\\_]+(?:(?::("|\').*?\5|:[^`]*))*))+)?' : '(())';
2362
-
2363
-        $regex = '#';
2364
-        $regex .= $varRegex;
2365
-        $regex .= $methodCall;
2366
-        $regex .= $simpleMathExpressions;
2367
-        $regex .= $modifiers;
2368
-        $regex .= '#i';
2369
-
2370
-        if (preg_match($regex, $substr, $match)) {
2371
-            $key = substr($match[1], 1);
2372
-
2373
-            $matchedLength = strlen($match[0]);
2374
-            $hasModifiers  = !empty($match[5]);
2375
-            $hasExpression = !empty($match[4]);
2376
-            $hasMethodCall = !empty($match[3]);
2377
-
2378
-            if (substr($key, - 1) == '.') {
2379
-                $key = substr($key, 0, - 1);
2380
-                -- $matchedLength;
2381
-            }
2382
-
2383
-            if ($hasMethodCall) {
2384
-                $matchedLength -= strlen($match[3]) + strlen(substr($match[1], strrpos($match[1], '->')));
2385
-                $key        = substr($match[1], 1, strrpos($match[1], '->') - 1);
2386
-                $methodCall = substr($match[1], strrpos($match[1], '->')) . $match[3];
2387
-            }
2388
-
2389
-            if ($hasModifiers) {
2390
-                $matchedLength -= strlen($match[5]);
2391
-            }
2392
-
2393
-            if ($pointer !== null) {
2394
-                $pointer += $matchedLength;
2395
-            }
2396
-
2397
-            // replace useless brackets by dot accessed vars and strip enclosing quotes if present
2398
-            $key = preg_replace('#\[(["\']?)([^$%\[.>-]+)\1\]#', '.$2', $key);
2399
-
2400
-            if ($this->debug) {
2401
-                if ($hasMethodCall) {
2402
-                    echo 'METHOD CALL FOUND : $' . $key . substr($methodCall, 0, 30) . "\n";
2403
-                } else {
2404
-                    echo 'VAR FOUND : $' . $key . "\n";
2405
-                }
2406
-            }
2407
-
2408
-            $key = str_replace('"', '\\"', $key);
2409
-
2410
-            $cnt = substr_count($key, '$');
2411
-            if ($cnt > 0) {
2412
-                $uid           = 0;
2413
-                $parsed        = array($uid => '');
2414
-                $current       = &$parsed;
2415
-                $curTxt        = &$parsed[$uid ++];
2416
-                $tree          = array();
2417
-                $chars         = str_split($key, 1);
2418
-                $inSplittedVar = false;
2419
-                $bracketCount  = 0;
2420
-
2421
-                while (($char = array_shift($chars)) !== null) {
2422
-                    if ($char === '[') {
2423
-                        if (count($tree) > 0) {
2424
-                            ++ $bracketCount;
2425
-                        } else {
2426
-                            $tree[]        = &$current;
2427
-                            $current[$uid] = array($uid + 1 => '');
2428
-                            $current       = &$current[$uid ++];
2429
-                            $curTxt        = &$current[$uid ++];
2430
-                            continue;
2431
-                        }
2432
-                    } elseif ($char === ']') {
2433
-                        if ($bracketCount > 0) {
2434
-                            -- $bracketCount;
2435
-                        } else {
2436
-                            $current = &$tree[count($tree) - 1];
2437
-                            array_pop($tree);
2438
-                            if (current($chars) !== '[' && current($chars) !== false && current($chars) !== ']') {
2439
-                                $current[$uid] = '';
2440
-                                $curTxt        = &$current[$uid ++];
2441
-                            }
2442
-                            continue;
2443
-                        }
2444
-                    } elseif ($char === '$') {
2445
-                        if (count($tree) == 0) {
2446
-                            $curTxt        = &$current[$uid ++];
2447
-                            $inSplittedVar = true;
2448
-                        }
2449
-                    } elseif (($char === '.' || $char === '-') && count($tree) == 0 && $inSplittedVar) {
2450
-                        $curTxt        = &$current[$uid ++];
2451
-                        $inSplittedVar = false;
2452
-                    }
2453
-
2454
-                    $curTxt .= $char;
2455
-                }
2456
-                unset($uid, $current, $curTxt, $tree, $chars);
2457
-
2458
-                if ($this->debug) {
2459
-                    echo 'RECURSIVE VAR REPLACEMENT : ' . $key . "\n";
2460
-                }
2461
-
2462
-                $key = $this->flattenVarTree($parsed);
2463
-
2464
-                if ($this->debug) {
2465
-                    echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n";
2466
-                }
2467
-
2468
-                $output = preg_replace('#(^""\.|""\.|\.""$|(\()""\.|\.""(\)))#', '$2$3', '$this->readVar("' . $key . '")');
2469
-            } else {
2470
-                $output = $this->parseVarKey($key, $hasModifiers ? 'modifier' : $curBlock);
2471
-            }
2472
-
2473
-
2474
-            // methods
2475
-            if ($hasMethodCall) {
2476
-                $ptr = 0;
2477
-
2478
-                $output = $this->parseMethodCall($output, $methodCall, $curBlock, $ptr);
2479
-
2480
-                if ($pointer !== null) {
2481
-                    $pointer += $ptr;
2482
-                }
2483
-                $matchedLength += $ptr;
2484
-            }
2485
-
2486
-            if ($hasExpression) {
2487
-                // expressions
2488
-                preg_match_all('#(?:([+/*%=-])(=?-?[%$][a-z0-9\\\\.[\]>_:-]+(?:\([^)]*\))?|=?-?[0-9.,]+|\1))#i', $match[4], $expMatch);
2489
-                foreach ($expMatch[1] as $k => $operator) {
2490
-                    if (substr($expMatch[2][$k], 0, 1) === '=') {
2491
-                        $assign = true;
2492
-                        if ($operator === '=') {
2493
-                            throw new CompilationException($this, 'Invalid expression <em>' . $substr . '</em>, can not use "==" in expressions');
2494
-                        }
2495
-                        if ($curBlock !== 'root') {
2496
-                            throw new CompilationException($this, 'Invalid expression <em>' . $substr . '</em>, assignments can only be used in top level expressions like {$foo+=3} or {$foo="bar"}');
2497
-                        }
2498
-                        $operator .= '=';
2499
-                        $expMatch[2][$k] = substr($expMatch[2][$k], 1);
2500
-                    }
2501
-
2502
-                    if (substr($expMatch[2][$k], 0, 1) === '-' && strlen($expMatch[2][$k]) > 1) {
2503
-                        $operator .= '-';
2504
-                        $expMatch[2][$k] = substr($expMatch[2][$k], 1);
2505
-                    }
2506
-                    if (($operator === '+' || $operator === '-') && $expMatch[2][$k] === $operator) {
2507
-                        $output = '(' . $output . $operator . $operator . ')';
2508
-                        break;
2509
-                    } elseif (substr($expMatch[2][$k], 0, 1) === '$') {
2510
-                        $output = '(' . $output . ' ' . $operator . ' ' . $this->parseVar($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')';
2511
-                    } elseif (substr($expMatch[2][$k], 0, 1) === '%') {
2512
-                        $output = '(' . $output . ' ' . $operator . ' ' . $this->parseConst($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')';
2513
-                    } elseif (!empty($expMatch[2][$k])) {
2514
-                        $output = '(' . $output . ' ' . $operator . ' ' . str_replace(',', '.', $expMatch[2][$k]) . ')';
2515
-                    } else {
2516
-                        throw new CompilationException($this, 'Unfinished expression <em>' . $substr . '</em>, missing var or number after math operator');
2517
-                    }
2518
-                }
2519
-            }
2520
-
2521
-            if ($this->autoEscape === true && $curBlock !== 'condition') {
2522
-                $output = '(is_string($tmp=' . $output . ') ? htmlspecialchars($tmp, ENT_QUOTES, $this->charset) : $tmp)';
2523
-            }
2524
-
2525
-            // handle modifiers
2526
-            if ($curBlock !== 'modifier' && $hasModifiers) {
2527
-                $ptr    = 0;
2528
-                $output = $this->replaceModifiers(array(null, null, $output, $match[5]), 'var', $ptr);
2529
-                if ($pointer !== null) {
2530
-                    $pointer += $ptr;
2531
-                }
2532
-                $matchedLength += $ptr;
2533
-            }
2534
-
2535
-            if (is_array($parsingParams)) {
2536
-                $parsingParams[] = array($output, $key);
2537
-
2538
-                return $parsingParams;
2539
-            } elseif ($curBlock === 'namedparam') {
2540
-                return array($output, $key);
2541
-            } elseif ($curBlock === 'string' || $curBlock === 'delimited_string') {
2542
-                return array($matchedLength, $output);
2543
-            } elseif ($curBlock === 'expression' || $curBlock === 'variable') {
2544
-                return $output;
2545
-            } elseif (isset($assign)) {
2546
-                return self::PHP_OPEN . $output . ';' . self::PHP_CLOSE;
2547
-            }
2548
-
2549
-            return $output;
2550
-        } else {
2551
-            if ($curBlock === 'string' || $curBlock === 'delimited_string') {
2552
-                return array(0, '');
2553
-            }
2554
-            throw new CompilationException($this, 'Invalid variable name <em>' . $substr . '</em>');
2555
-        }
2556
-    }
2557
-
2558
-    /**
2559
-     * Parses any number of chained method calls/property reads.
2560
-     *
2561
-     * @param string $output     the variable or whatever upon which the method are called
2562
-     * @param string $methodCall method call source, starting at "->"
2563
-     * @param string $curBlock   the current parser-block being processed
2564
-     * @param int    $pointer    a reference to a pointer that will be increased by the amount of characters parsed
2565
-     *
2566
-     * @return string parsed call(s)/read(s)
2567
-     */
2568
-    protected function parseMethodCall($output, $methodCall, $curBlock, &$pointer)
2569
-    {
2570
-        $ptr = 0;
2571
-        $len = strlen($methodCall);
2572
-
2573
-        while ($ptr < $len) {
2574
-            if (strpos($methodCall, '->', $ptr) === $ptr) {
2575
-                $ptr += 2;
2576
-            }
2577
-
2578
-            if (in_array(
2579
-                $methodCall[$ptr], array(
2580
-                    ';',
2581
-                    ',',
2582
-                    '/',
2583
-                    ' ',
2584
-                    "\t",
2585
-                    "\r",
2586
-                    "\n",
2587
-                    ')',
2588
-                    '+',
2589
-                    '*',
2590
-                    '%',
2591
-                    '=',
2592
-                    '-',
2593
-                    '|'
2594
-                )
2595
-            ) || substr($methodCall, $ptr, strlen($this->rd)) === $this->rd
2596
-            ) {
2597
-                // break char found
2598
-                break;
2599
-            }
2600
-
2601
-            if (!preg_match('/^([a-z0-9_]+)(\(.*?\))?/i', substr($methodCall, $ptr), $methMatch)) {
2602
-                break;
2603
-            }
2604
-
2605
-            if (empty($methMatch[2])) {
2606
-                // property
2607
-                if ($curBlock === 'root') {
2608
-                    $output .= '->' . $methMatch[1];
2609
-                } else {
2610
-                    $output = '(($tmp = ' . $output . ') ? $tmp->' . $methMatch[1] . ' : null)';
2611
-                }
2612
-                $ptr += strlen($methMatch[1]);
2613
-            } else {
2614
-                // method
2615
-                if (substr($methMatch[2], 0, 2) === '()') {
2616
-                    $parsedCall = $methMatch[1] . '()';
2617
-                    $ptr += strlen($methMatch[1]) + 2;
2618
-                } else {
2619
-                    $parsedCall = $this->parseFunction($methodCall, $ptr, strlen($methodCall), false, 'method', $ptr);
2620
-                }
2621
-                if ($this->securityPolicy !== null) {
2622
-                    $argPos = strpos($parsedCall, '(');
2623
-                    $method = strtolower(substr($parsedCall, 0, $argPos));
2624
-                    $args   = substr($parsedCall, $argPos);
2625
-                    if ($curBlock === 'root') {
2626
-                        $output = '$this->getSecurityPolicy()->callMethod($this, ' . $output . ', ' . var_export($method, true) . ', array' . $args . ')';
2627
-                    } else {
2628
-                        $output = '(($tmp = ' . $output . ') ? $this->getSecurityPolicy()->callMethod($this, $tmp, ' . var_export($method, true) . ', array' . $args . ') : null)';
2629
-                    }
2630
-                } else {
2631
-                    if ($curBlock === 'root') {
2632
-                        $output .= '->' . $parsedCall;
2633
-                    } else {
2634
-                        $output = '(($tmp = ' . $output . ') ? $tmp->' . $parsedCall . ' : null)';
2635
-                    }
2636
-                }
2637
-            }
2638
-        }
2639
-
2640
-        $pointer += $ptr;
2641
-
2642
-        return $output;
2643
-    }
2644
-
2645
-    /**
2646
-     * Parses a constant variable (a variable that doesn't contain another variable) and preprocesses it to save
2647
-     * runtime processing time.
2648
-     *
2649
-     * @param string $key      the variable to parse
2650
-     * @param string $curBlock the current parser-block being processed
2651
-     *
2652
-     * @return string parsed variable
2653
-     */
2654
-    protected function parseVarKey($key, $curBlock)
2655
-    {
2656
-        if ($key === '') {
2657
-            return '$this->scope';
2658
-        }
2659
-        if (substr($key, 0, 1) === '.') {
2660
-            $key = 'dwoo' . $key;
2661
-        }
2662
-        if (preg_match('#dwoo\.(get|post|server|cookies|session|env|request)((?:\.[a-z0-9_-]+)+)#i', $key, $m)) {
2663
-            $global = strtoupper($m[1]);
2664
-            if ($global === 'COOKIES') {
2665
-                $global = 'COOKIE';
2666
-            }
2667
-            $key = '$_' . $global;
2668
-            foreach (explode('.', ltrim($m[2], '.')) as $part) {
2669
-                $key .= '[' . var_export($part, true) . ']';
2670
-            }
2671
-            if ($curBlock === 'root') {
2672
-                $output = $key;
2673
-            } else {
2674
-                $output = '(isset(' . $key . ')?' . $key . ':null)';
2675
-            }
2676
-        } elseif (preg_match('#dwoo\\.const\\.([a-z0-9\\\\_:]+)#i', $key, $m)) {
2677
-            return $this->parseConstKey($m[1], $curBlock);
2678
-        } elseif ($this->scope !== null) {
2679
-            if (strstr($key, '.') === false && strstr($key, '[') === false && strstr($key, '->') === false) {
2680
-                if ($key === 'dwoo') {
2681
-                    $output = '$this->globals';
2682
-                } elseif ($key === '_root' || $key === '__') {
2683
-                    $output = '$this->data';
2684
-                } elseif ($key === '_parent' || $key === '_') {
2685
-                    $output = '$this->readParentVar(1)';
2686
-                } elseif ($key === '_key') {
2687
-                    $output = '$tmp_key';
2688
-                } else {
2689
-                    if ($curBlock === 'root') {
2690
-                        $output = '$this->scope["' . $key . '"]';
2691
-                    } else {
2692
-                        $output = '(isset($this->scope["' . $key . '"]) ? $this->scope["' . $key . '"] : null)';
2693
-                    }
2694
-                }
2695
-            } else {
2696
-                preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+|(\\\?[\'"])[^\3]*?\3)\]?#i', $key, $m);
2697
-
2698
-                $i = $m[2][0];
2699
-                if ($i === '_parent' || $i === '_') {
2700
-                    $parentCnt = 0;
2701
-
2702
-                    while (true) {
2703
-                        ++ $parentCnt;
2704
-                        array_shift($m[2]);
2705
-                        array_shift($m[1]);
2706
-                        if (current($m[2]) === '_parent') {
2707
-                            continue;
2708
-                        }
2709
-                        break;
2710
-                    }
2711
-
2712
-                    $output = '$this->readParentVar(' . $parentCnt . ')';
2713
-                } else {
2714
-                    if ($i === 'dwoo') {
2715
-                        $output = '$this->globals';
2716
-                        array_shift($m[2]);
2717
-                        array_shift($m[1]);
2718
-                    } elseif ($i === '_root' || $i === '__') {
2719
-                        $output = '$this->data';
2720
-                        array_shift($m[2]);
2721
-                        array_shift($m[1]);
2722
-                    } elseif ($i === '_key') {
2723
-                        $output = '$tmp_key';
2724
-                    } else {
2725
-                        $output = '$this->scope';
2726
-                    }
2727
-
2728
-                    while (count($m[1]) && $m[1][0] !== '->') {
2729
-                        $m[2][0] = preg_replace('/(^\\\([\'"])|\\\([\'"])$)/x', '$2$3', $m[2][0]);
2730
-                        if (substr($m[2][0], 0, 1) == '"' || substr($m[2][0], 0, 1) == "'") {
2731
-                            $output .= '[' . $m[2][0] . ']';
2732
-                        } else {
2733
-                            $output .= '["' . $m[2][0] . '"]';
2734
-                        }
2735
-                        array_shift($m[2]);
2736
-                        array_shift($m[1]);
2737
-                    }
2738
-
2739
-                    if ($curBlock !== 'root') {
2740
-                        $output = '(isset(' . $output . ') ? ' . $output . ':null)';
2741
-                    }
2742
-                }
2743
-
2744
-                if (count($m[2])) {
2745
-                    unset($m[0]);
2746
-                    $output = '$this->readVarInto(' . str_replace("\n", '', var_export($m, true)) . ', ' . $output . ', ' . ($curBlock == 'root' ? 'false' : 'true') . ')';
2747
-                }
2748
-            }
2749
-        } else {
2750
-            preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+)\]?#i', $key, $m);
2751
-            unset($m[0]);
2752
-            $output = '$this->readVar(' . str_replace("\n", '', var_export($m, true)) . ')';
2753
-        }
2754
-
2755
-        return $output;
2756
-    }
2757
-
2758
-    /**
2759
-     * Flattens a variable tree, this helps in parsing very complex variables such as $var.foo[$foo.bar->baz].baz,
2760
-     * it computes the contents of the brackets first and works out from there.
2761
-     *
2762
-     * @param array $tree     the variable tree parsed by he parseVar() method that must be flattened
2763
-     * @param bool  $recursed leave that to false by default, it is only for internal use
2764
-     *
2765
-     * @return string flattened tree
2766
-     */
2767
-    protected function flattenVarTree(array $tree, $recursed = false)
2768
-    {
2769
-        $out = $recursed ? '".$this->readVarInto(' : '';
2770
-        foreach ($tree as $bit) {
2771
-            if (is_array($bit)) {
2772
-                $out .= '.' . $this->flattenVarTree($bit, false);
2773
-            } else {
2774
-                $key = str_replace('"', '\\"', $bit);
2775
-
2776
-                if (substr($key, 0, 1) === '$') {
2777
-                    $out .= '".' . $this->parseVar($key, 0, strlen($key), false, 'variable') . '."';
2778
-                } else {
2779
-                    $cnt = substr_count($key, '$');
2780
-
2781
-                    if ($this->debug) {
2782
-                        echo 'PARSING SUBVARS IN : ' . $key . "\n";
2783
-                    }
2784
-                    if ($cnt > 0) {
2785
-                        while (-- $cnt >= 0) {
2786
-                            if (isset($last)) {
2787
-                                $last = strrpos($key, '$', - (strlen($key) - $last + 1));
2788
-                            } else {
2789
-                                $last = strrpos($key, '$');
2790
-                            }
2791
-                            preg_match('#\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', substr($key, $last), $submatch);
2792
-
2793
-                            $len = strlen($submatch[0]);
2794
-                            $key = substr_replace(
2795
-                                $key, preg_replace_callback(
2796
-                                    '#(\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*)' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', array(
2797
-                                        $this,
2798
-                                        'replaceVarKeyHelper'
2799
-                                    ), substr($key, $last, $len)
2800
-                                ), $last, $len
2801
-                            );
2802
-                            if ($this->debug) {
2803
-                                echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n";
2804
-                            }
2805
-                        }
2806
-                        unset($last);
2807
-
2808
-                        $out .= $key;
2809
-                    } else {
2810
-                        $out .= $key;
2811
-                    }
2812
-                }
2813
-            }
2814
-        }
2815
-        $out .= $recursed ? ', true)."' : '';
2816
-
2817
-        return $out;
2818
-    }
2819
-
2820
-    /**
2821
-     * Helper function that parses a variable.
2822
-     *
2823
-     * @param array $match the matched variable, array(1=>"string match")
2824
-     *
2825
-     * @return string parsed variable
2826
-     */
2827
-    protected function replaceVarKeyHelper($match)
2828
-    {
2829
-        return '".' . $this->parseVar($match[0], 0, strlen($match[0]), false, 'variable') . '."';
2830
-    }
2831
-
2832
-    /**
2833
-     * Parses various constants, operators or non-quoted strings.
2834
-     *
2835
-     * @param string $in            the string within which we must parse something
2836
-     * @param int    $from          the starting offset of the parsed area
2837
-     * @param int    $to            the ending offset of the parsed area
2838
-     * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
2839
-     *                              default
2840
-     * @param string $curBlock      the current parser-block being processed
2841
-     * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
2842
-     *                              or null by default
2843
-     *
2844
-     * @return string parsed values
2845
-     * @throws Exception
2846
-     */
2847
-    protected function parseOthers($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2848
-    {
2849
-        $substr = substr($in, $from, $to - $from);
2850
-
2851
-        $end = strlen($substr);
2852
-
2853
-        if ($curBlock === 'condition') {
2854
-            $breakChars = array(
2855
-                '(',
2856
-                ')',
2857
-                ' ',
2858
-                '||',
2859
-                '&&',
2860
-                '|',
2861
-                '&',
2862
-                '>=',
2863
-                '<=',
2864
-                '===',
2865
-                '==',
2866
-                '=',
2867
-                '!==',
2868
-                '!=',
2869
-                '<<',
2870
-                '<',
2871
-                '>>',
2872
-                '>',
2873
-                '^',
2874
-                '~',
2875
-                ',',
2876
-                '+',
2877
-                '-',
2878
-                '*',
2879
-                '/',
2880
-                '%',
2881
-                '!',
2882
-                '?',
2883
-                ':',
2884
-                $this->rd,
2885
-                ';'
2886
-            );
2887
-        } elseif ($curBlock === 'modifier') {
2888
-            $breakChars = array(' ', ',', ')', ':', '|', "\r", "\n", "\t", ';', $this->rd);
2889
-        } elseif ($curBlock === 'expression') {
2890
-            $breakChars = array('/', '%', '+', '-', '*', ' ', ',', ')', "\r", "\n", "\t", ';', $this->rd);
2891
-        } else {
2892
-            $breakChars = array(' ', ',', ')', "\r", "\n", "\t", ';', $this->rd);
2893
-        }
2894
-
2895
-        $breaker = false;
2896
-        foreach ($breakChars as $k => $char) {
2897
-            $test = strpos($substr, $char);
2898
-            if ($test !== false && $test < $end) {
2899
-                $end     = $test;
2900
-                $breaker = $k;
2901
-            }
2902
-        }
2903
-
2904
-        if ($curBlock === 'condition') {
2905
-            if ($end === 0 && $breaker !== false) {
2906
-                $end = strlen($breakChars[$breaker]);
2907
-            }
2908
-        }
2909
-
2910
-        if ($end !== false) {
2911
-            $substr = substr($substr, 0, $end);
2912
-        }
2913
-
2914
-        if ($pointer !== null) {
2915
-            $pointer += strlen($substr);
2916
-        }
2917
-
2918
-        $src    = $substr;
2919
-        $substr = trim($substr);
2920
-
2921
-        if (strtolower($substr) === 'false' || strtolower($substr) === 'no' || strtolower($substr) === 'off') {
2922
-            if ($this->debug) {
2923
-                echo 'BOOLEAN(FALSE) PARSED' . "\n";
2924
-            }
2925
-            $substr = 'false';
2926
-            $type   = self::T_BOOL;
2927
-        } elseif (strtolower($substr) === 'true' || strtolower($substr) === 'yes' || strtolower($substr) === 'on') {
2928
-            if ($this->debug) {
2929
-                echo 'BOOLEAN(TRUE) PARSED' . "\n";
2930
-            }
2931
-            $substr = 'true';
2932
-            $type   = self::T_BOOL;
2933
-        } elseif ($substr === 'null' || $substr === 'NULL') {
2934
-            if ($this->debug) {
2935
-                echo 'NULL PARSED' . "\n";
2936
-            }
2937
-            $substr = 'null';
2938
-            $type   = self::T_NULL;
2939
-        } elseif (is_numeric($substr)) {
2940
-            $substr = (float)$substr;
2941
-            if ((int)$substr == $substr) {
2942
-                $substr = (int)$substr;
2943
-            }
2944
-            $type = self::T_NUMERIC;
2945
-            if ($this->debug) {
2946
-                echo 'NUMBER (' . $substr . ') PARSED' . "\n";
2947
-            }
2948
-        } elseif (preg_match('{^-?(\d+|\d*(\.\d+))\s*([/*%+-]\s*-?(\d+|\d*(\.\d+)))+$}', $substr)) {
2949
-            if ($this->debug) {
2950
-                echo 'SIMPLE MATH PARSED . "\n"';
2951
-            }
2952
-            $type   = self::T_MATH;
2953
-            $substr = '(' . $substr . ')';
2954
-        } elseif ($curBlock === 'condition' && array_search($substr, $breakChars, true) !== false) {
2955
-            if ($this->debug) {
2956
-                echo 'BREAKCHAR (' . $substr . ') PARSED' . "\n";
2957
-            }
2958
-            $type = self::T_BREAKCHAR;
2959
-            //$substr = '"'.$substr.'"';
2960
-        } else {
2961
-            $substr = $this->replaceStringVars('\'' . str_replace('\'', '\\\'', $substr) . '\'', '\'', $curBlock);
2962
-            $type   = self::T_UNQUOTED_STRING;
2963
-            if ($this->debug) {
2964
-                echo 'BLABBER (' . $substr . ') CASTED AS STRING' . "\n";
2965
-            }
2966
-        }
2967
-
2968
-        if (is_array($parsingParams)) {
2969
-            $parsingParams[] = array($substr, $src, $type);
2970
-
2971
-            return $parsingParams;
2972
-        } elseif ($curBlock === 'namedparam') {
2973
-            return array($substr, $src, $type);
2974
-        } elseif ($curBlock === 'expression') {
2975
-            return $substr;
2976
-        } else {
2977
-            throw new Exception('Something went wrong');
2978
-        }
2979
-    }
2980
-
2981
-    /**
2982
-     * Replaces variables within a parsed string.
2983
-     *
2984
-     * @param string $string   the parsed string
2985
-     * @param string $first    the first character parsed in the string, which is the string delimiter (' or ")
2986
-     * @param string $curBlock the current parser-block being processed
2987
-     *
2988
-     * @return string the original string with variables replaced
2989
-     */
2990
-    protected function replaceStringVars($string, $first, $curBlock = '')
2991
-    {
2992
-        $pos = 0;
2993
-        if ($this->debug) {
2994
-            echo 'STRING VAR REPLACEMENT : ' . $string . "\n";
2995
-        }
2996
-        // replace vars
2997
-        while (($pos = strpos($string, '$', $pos)) !== false) {
2998
-            $prev = substr($string, $pos - 1, 1);
2999
-            if ($prev === '\\') {
3000
-                ++ $pos;
3001
-                continue;
3002
-            }
3003
-
3004
-            $var = $this->parse($string, $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string')));
3005
-            $len = $var[0];
3006
-            $var = $this->parse(str_replace('\\' . $first, $first, $string), $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string')));
3007
-
3008
-            if ($prev === '`' && substr($string, $pos + $len, 1) === '`') {
3009
-                $string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos - 1, $len + 2);
3010
-            } else {
3011
-                $string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos, $len);
3012
-            }
3013
-            $pos += strlen($var[1]) + 2;
3014
-            if ($this->debug) {
3015
-                echo 'STRING VAR REPLACEMENT DONE : ' . $string . "\n";
3016
-            }
3017
-        }
3018
-
3019
-        // handle modifiers
3020
-        // TODO Obsolete?
3021
-        $string = preg_replace_callback(
3022
-            '#("|\')\.(.+?)\.\1((?:\|(?:@?[a-z0-9_]+(?:(?::("|\').+?\4|:[^`]*))*))+)#i', array(
3023
-            $this,
3024
-            'replaceModifiers'
3025
-            ), $string
3026
-        );
3027
-
3028
-        // replace escaped dollar operators by unescaped ones if required
3029
-        if ($first === "'") {
3030
-            $string = str_replace('\\$', '$', $string);
3031
-        }
3032
-
3033
-        return $string;
3034
-    }
3035
-
3036
-    /**
3037
-     * Replaces the modifiers applied to a string or a variable.
3038
-     *
3039
-     * @param array  $m        the regex matches that must be array(1=>"double or single quotes enclosing a string,
3040
-     *                         when applicable", 2=>"the string or var", 3=>"the modifiers matched")
3041
-     * @param string $curBlock the current parser-block being processed
3042
-     * @param null   $pointer
3043
-     *
3044
-     * @return string the input enclosed with various function calls according to the modifiers found
3045
-     * @throws CompilationException
3046
-     * @throws Exception
3047
-     */
3048
-    protected function replaceModifiers(array $m, $curBlock = null, &$pointer = null)
3049
-    {
3050
-        if ($this->debug) {
3051
-            echo 'PARSING MODIFIERS : ' . $m[3] . "\n";
3052
-        }
3053
-
3054
-        if ($pointer !== null) {
3055
-            $pointer += strlen($m[3]);
3056
-        }
3057
-        // remove first pipe
3058
-        $cmdstrsrc = substr($m[3], 1);
3059
-        // remove last quote if present
3060
-        if (substr($cmdstrsrc, - 1, 1) === $m[1]) {
3061
-            $cmdstrsrc = substr($cmdstrsrc, 0, - 1);
3062
-            $add       = $m[1];
3063
-        }
3064
-
3065
-        $output = $m[2];
3066
-
3067
-        $continue = true;
3068
-        while (strlen($cmdstrsrc) > 0 && $continue) {
3069
-            if ($cmdstrsrc[0] === '|') {
3070
-                $cmdstrsrc = substr($cmdstrsrc, 1);
3071
-                continue;
3072
-            }
3073
-            if ($cmdstrsrc[0] === ' ' || $cmdstrsrc[0] === ';' || substr($cmdstrsrc, 0, strlen($this->rd)) === $this->rd) {
3074
-                if ($this->debug) {
3075
-                    echo 'MODIFIER PARSING ENDED, RIGHT DELIMITER or ";" FOUND' . "\n";
3076
-                }
3077
-                $continue = false;
3078
-                if ($pointer !== null) {
3079
-                    $pointer -= strlen($cmdstrsrc);
3080
-                }
3081
-                break;
3082
-            }
3083
-            $cmdstr   = $cmdstrsrc;
3084
-            $paramsep = ':';
3085
-            if (!preg_match('/^(@{0,2}[a-z_][a-z0-9_]*)(:)?/i', $cmdstr, $match)) {
3086
-                throw new CompilationException($this, 'Invalid modifier name, started with : ' . substr($cmdstr, 0, 10));
3087
-            }
3088
-            $paramspos = !empty($match[2]) ? strlen($match[1]) : false;
3089
-            $func      = $match[1];
3090
-
3091
-            $state = 0;
3092
-            if ($paramspos === false) {
3093
-                $cmdstrsrc = substr($cmdstrsrc, strlen($func));
3094
-                $params    = array();
3095
-                if ($this->debug) {
3096
-                    echo 'MODIFIER (' . $func . ') CALLED WITH NO PARAMS' . "\n";
3097
-                }
3098
-            } else {
3099
-                $paramstr = substr($cmdstr, $paramspos + 1);
3100
-                if (substr($paramstr, - 1, 1) === $paramsep) {
3101
-                    $paramstr = substr($paramstr, 0, - 1);
3102
-                }
3103
-
3104
-                $ptr    = 0;
3105
-                $params = array();
3106
-                while ($ptr < strlen($paramstr)) {
3107
-                    if ($this->debug) {
3108
-                        echo 'MODIFIER (' . $func . ') START PARAM PARSING WITH POINTER AT ' . $ptr . "\n";
3109
-                    }
3110
-                    if ($this->debug) {
3111
-                        echo $paramstr . '--' . $ptr . '--' . strlen($paramstr) . '--modifier' . "\n";
3112
-                    }
3113
-                    $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'modifier', $ptr);
3114
-                    if ($this->debug) {
3115
-                        echo 'PARAM PARSED, POINTER AT ' . $ptr . "\n";
3116
-                    }
3117
-
3118
-                    if ($ptr >= strlen($paramstr)) {
3119
-                        if ($this->debug) {
3120
-                            echo 'PARAM PARSING ENDED, PARAM STRING CONSUMED' . "\n";
3121
-                        }
3122
-                        break;
3123
-                    }
3124
-
3125
-                    if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === '|' || $paramstr[$ptr] === ';' || substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) {
3126
-                        if ($this->debug) {
3127
-                            echo 'PARAM PARSING ENDED, " ", "|", RIGHT DELIMITER or ";" FOUND, POINTER AT ' . $ptr . "\n";
3128
-                        }
3129
-                        if ($paramstr[$ptr] !== '|') {
3130
-                            $continue = false;
3131
-                            if ($pointer !== null) {
3132
-                                $pointer -= strlen($paramstr) - $ptr;
3133
-                            }
3134
-                        }
3135
-                        ++ $ptr;
3136
-                        break;
3137
-                    }
3138
-                    if ($ptr < strlen($paramstr) && $paramstr[$ptr] === ':') {
3139
-                        ++ $ptr;
3140
-                    }
3141
-                }
3142
-                $cmdstrsrc = substr($cmdstrsrc, strlen($func) + 1 + $ptr);
3143
-                foreach ($params as $k => $p) {
3144
-                    if (is_array($p) && is_array($p[1])) {
3145
-                        $state |= 2;
3146
-                    } else {
3147
-                        if (($state & 2) && preg_match('#^(["\'])(.+?)\1$#', $p[0], $m)) {
3148
-                            $params[$k] = array($m[2], array('true', 'true'));
3149
-                        } else {
3150
-                            if ($state & 2) {
3151
-                                throw new CompilationException($this, 'You can not use an unnamed parameter after a named one');
3152
-                            }
3153
-                            $state |= 1;
3154
-                        }
3155
-                    }
3156
-                }
3157
-            }
3158
-
3159
-            // check if we must use array_map with this plugin or not
3160
-            $mapped = false;
3161
-            if (substr($func, 0, 1) === '@') {
3162
-                $func   = substr($func, 1);
3163
-                $mapped = true;
3164
-            }
3165
-
3166
-            $pluginType = $this->getPluginType($func);
3167
-
3168
-            if ($state & 2) {
3169
-                array_unshift($params, array('value', is_array($output) ? $output : array($output, $output)));
3170
-            } else {
3171
-                array_unshift($params, is_array($output) ? $output : array($output, $output));
3172
-            }
3173
-
3174
-            if ($pluginType & Core::NATIVE_PLUGIN) {
3175
-                $params = $this->mapParams($params, null, $state);
3176
-
3177
-                $params = $params['*'][0];
3178
-
3179
-                $params = self::implode_r($params);
3180
-
3181
-                if ($mapped) {
3182
-                    $output = '$this->arrayMap(\'' . $func . '\', array(' . $params . '))';
3183
-                } else {
3184
-                    $output = $func . '(' . $params . ')';
3185
-                }
3186
-            } elseif ($pluginType & Core::PROXY_PLUGIN) {
3187
-                $params = $this->mapParams($params, $this->getDwoo()->getPluginProxy()->getCallback($func), $state);
3188
-                foreach ($params as &$p) {
3189
-                    $p = $p[0];
3190
-                }
3191
-                $output = call_user_func(array($this->getDwoo()->getPluginProxy(), 'getCode'), $func, $params);
3192
-            } elseif ($pluginType & Core::SMARTY_MODIFIER) {
3193
-                $params = $this->mapParams($params, null, $state);
3194
-                $params = $params['*'][0];
3195
-
3196
-                $params = self::implode_r($params);
3197
-
3198
-                if ($pluginType & Core::CUSTOM_PLUGIN) {
3199
-                    $callback = $this->customPlugins[$func]['callback'];
3200
-                    if (is_array($callback)) {
3201
-                        if (is_object($callback[0])) {
3202
-                            $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(' . $params . '))';
3203
-                        } else {
3204
-                            $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))';
3205
-                        }
3206
-                    } elseif ($mapped) {
3207
-                        $output = '$this->arrayMap(\'' . $callback . '\', array(' . $params . '))';
3208
-                    } else {
3209
-                        $output = $callback . '(' . $params . ')';
3210
-                    }
3211
-                } elseif ($mapped) {
3212
-                    $output = '$this->arrayMap(\'smarty_modifier_' . $func . '\', array(' . $params . '))';
3213
-                } else {
3214
-                    $output = 'smarty_modifier_' . $func . '(' . $params . ')';
3215
-                }
3216
-            } else {
3217
-                if ($pluginType & Core::CUSTOM_PLUGIN) {
3218
-                    $callback   = $this->customPlugins[$func]['callback'];
3219
-                    $pluginName = $callback;
3220
-                } else {
3221
-                    if (class_exists('Plugin' . Core::toCamelCase($func)) !== false || function_exists('Plugin' .
3222
-                            Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''))
3223
-                        !== false) {
3224
-                        $pluginName = 'Plugin' . Core::toCamelCase($func);
3225
-                    } else {
3226
-                        $pluginName = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func);
3227
-                    }
3228
-                    if ($pluginType & Core::CLASS_PLUGIN) {
3229
-                        $callback = array($pluginName, ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process');
3230
-                    } else {
3231
-                        $callback = $pluginName . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '');
3232
-                    }
3233
-                }
3234
-                $params = $this->mapParams($params, $callback, $state);
3235
-
3236
-                foreach ($params as &$p) {
3237
-                    $p = $p[0];
3238
-                }
3239
-
3240
-                // Only for PHP function, who is not a PHP class
3241
-                if ($pluginType & Core::FUNC_PLUGIN && !($pluginType & Core::CLASS_PLUGIN)) {
3242
-                    if ($pluginType & Core::COMPILABLE_PLUGIN) {
3243
-                        if ($mapped) {
3244
-                            throw new CompilationException($this, 'The @ operator can not be used on compiled plugins.');
3245
-                        }
3246
-                        if ($pluginType & Core::CUSTOM_PLUGIN) {
3247
-                            $funcCompiler = $this->customPlugins[$func]['callback'];
3248
-                        } else {
3249
-                            if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
3250
-                                $funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile';
3251
-                            } else {
3252
-                                $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) .
3253
-                                    'Compile';
3254
-                            }
3255
-                        }
3256
-                        array_unshift($params, $this);
3257
-                        $output = call_user_func_array($funcCompiler, $params);
3258
-                    } else {
3259
-                        array_unshift($params, '$this');
3260
-
3261
-                        $params = self::implode_r($params);
3262
-                        if ($mapped) {
3263
-                            $output = '$this->arrayMap(\'' . $pluginName . '\', array(' . $params . '))';
3264
-                        } else {
3265
-                            $output = $pluginName . '(' . $params . ')';
3266
-                        }
3267
-                    }
3268
-                } else {
3269
-                    if ($pluginType & Core::COMPILABLE_PLUGIN) {
3270
-                        if ($mapped) {
3271
-                            throw new CompilationException($this, 'The @ operator can not be used on compiled plugins.');
3272
-                        }
3273
-                        if ($pluginType & Core::CUSTOM_PLUGIN) {
3274
-                            $callback = $this->customPlugins[$func]['callback'];
3275
-                            if (!is_array($callback)) {
3276
-                                if (!method_exists($callback, 'compile')) {
3277
-                                    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');
3278
-                                }
3279
-                                if (($ref = new ReflectionMethod($callback, 'compile')) && $ref->isStatic()) {
3280
-                                    $funcCompiler = array($callback, 'compile');
3281
-                                } else {
3282
-                                    $funcCompiler = array(new $callback(), 'compile');
3283
-                                }
3284
-                            } else {
3285
-                                $funcCompiler = $callback;
3286
-                            }
3287
-                        } else {
3288
-                            if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
3289
-                                $funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile');
3290
-                            } else {
3291
-                                $funcCompiler = array(
3292
-                                    Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
3293
-                                    'compile'
3294
-                                );
3295
-                            }
3296
-                            array_unshift($params, $this);
3297
-                        }
3298
-                        $output = call_user_func_array($funcCompiler, $params);
3299
-                    } else {
3300
-                        $params = self::implode_r($params);
3301
-
3302
-                        if ($pluginType & Core::CUSTOM_PLUGIN) {
3303
-                            if (is_object($callback[0])) {
3304
-                                $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(' . $params . '))';
3305
-                            } else {
3306
-                                $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))';
3307
-                            }
3308
-                        } elseif ($mapped) {
3309
-                            $output = '$this->arrayMap(array($this->getObjectPlugin(\''.
3310
-                                Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '\'), 
2091
+					} else {
2092
+						$output = '$this->classCall(\'' . $func . '\', array(' . $params . '))';
2093
+					}
2094
+				}
2095
+			}
2096
+		} // Function plugin only (cannot be a class)
2097
+		elseif ($pluginType & Core::FUNC_PLUGIN) {
2098
+			if ($pluginType & Core::COMPILABLE_PLUGIN) {
2099
+				if ($pluginType & Core::CUSTOM_PLUGIN) {
2100
+					$funcCompiler = $this->customPlugins[$func]['callback'];
2101
+				} else {
2102
+					// Custom plugin
2103
+					if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
2104
+						$funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile';
2105
+					} // Builtin helper plugin
2106
+					elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
2107
+						$funcCompiler = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) .
2108
+							'Compile';
2109
+					} // Builtin function plugin
2110
+					else {
2111
+						$funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) .
2112
+							'Compile';
2113
+					}
2114
+				}
2115
+				array_unshift($params, $this);
2116
+				// @TODO: Is it a real fix ?
2117
+				if ($func === 'tif') {
2118
+					$params[] = $tokens;
2119
+				}
2120
+				$output = call_user_func_array($funcCompiler, $params);
2121
+			} else {
2122
+				array_unshift($params, '$this');
2123
+				$params = self::implode_r($params);
2124
+				if ($pluginType & Core::CUSTOM_PLUGIN) {
2125
+					$callback = $this->customPlugins[$func]['callback'];
2126
+					if ($callback instanceof Closure) {
2127
+						$output = 'call_user_func($this->getCustomPlugin(\'' . $func . '\'), ' . $params . ')';
2128
+					} else {
2129
+						$output = 'call_user_func(\'' . $callback . '\', ' . $params . ')';
2130
+					}
2131
+				} else {
2132
+					// Custom plugin
2133
+					if (function_exists('Plugin' . Core::toCamelCase($func)) !== false) {
2134
+						$output = 'Plugin' . Core::toCamelCase($func) . '(' . $params .
2135
+							')';
2136
+					} // Builtin helper plugin
2137
+					elseif(function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !==
2138
+						false) {
2139
+						$output = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . '(' .
2140
+							$params . ')';
2141
+					} // Builtin function plugin
2142
+					else {
2143
+						$output = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '(' .
2144
+							$params . ')';
2145
+					}
2146
+				}
2147
+			}
2148
+		} // Proxy plugin
2149
+		elseif ($pluginType & Core::PROXY_PLUGIN) {
2150
+			$output = call_user_func(array($this->getDwoo()->getPluginProxy(), 'getCode'), $func, $params);
2151
+		} // Smarty function (@deprecated)
2152
+		elseif ($pluginType & Core::SMARTY_FUNCTION) {
2153
+			$params = '';
2154
+			if (isset($params['*'])) {
2155
+				$params = self::implode_r($params['*'], true);
2156
+			}
2157
+
2158
+			if ($pluginType & Core::CUSTOM_PLUGIN) {
2159
+				$callback = $this->customPlugins[$func]['callback'];
2160
+				if (is_array($callback)) {
2161
+					if (is_object($callback[0])) {
2162
+						$output = 'call_user_func_array(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(array(' . $params . '), $this))';
2163
+					} else {
2164
+						$output = 'call_user_func_array(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(array(' . $params . '), $this))';
2165
+					}
2166
+				} else {
2167
+					$output = $callback . '(array(' . $params . '), $this)';
2168
+				}
2169
+			} else {
2170
+				$output = 'smarty_function_' . $func . '(array(' . $params . '), $this)';
2171
+			}
2172
+		} // Template plugin
2173
+		elseif ($pluginType & Core::TEMPLATE_PLUGIN) {
2174
+			array_unshift($params, '$this');
2175
+			$params                                 = self::implode_r($params);
2176
+			$output                                 = 'Plugin' . Core::toCamelCase($func) .
2177
+				$this->templatePlugins[$func]['uuid'] . '(' . $params . ')';
2178
+			$this->templatePlugins[$func]['called'] = true;
2179
+		}
2180
+
2181
+		if (is_array($parsingParams)) {
2182
+			$parsingParams[] = array($output, $output);
2183
+
2184
+			return $parsingParams;
2185
+		} elseif ($curBlock === 'namedparam') {
2186
+			return array($output, $output);
2187
+		}
2188
+
2189
+		return $output;
2190
+	}
2191
+
2192
+	/**
2193
+	 * Parses a string.
2194
+	 *
2195
+	 * @param string $in            the string within which we must parse something
2196
+	 * @param int    $from          the starting offset of the parsed area
2197
+	 * @param int    $to            the ending offset of the parsed area
2198
+	 * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
2199
+	 *                              default
2200
+	 * @param string $curBlock      the current parser-block being processed
2201
+	 * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
2202
+	 *                              or null by default
2203
+	 *
2204
+	 * @return string parsed values
2205
+	 * @throws CompilationException
2206
+	 */
2207
+	protected function parseString($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2208
+	{
2209
+		$substr = substr($in, $from, $to - $from);
2210
+		$first  = $substr[0];
2211
+
2212
+		if ($this->debug) {
2213
+			echo 'STRING FOUND (in ' . htmlentities(substr($in, $from, min($to - $from, 50))) . (($to - $from) > 50 ? '...' : '') . ')' . "\n";
2214
+		}
2215
+		$strend = false;
2216
+		$o      = $from + 1;
2217
+		while ($strend === false) {
2218
+			$strend = strpos($in, $first, $o);
2219
+			if ($strend === false) {
2220
+				throw new CompilationException($this, 'Unfinished string, started with ' . substr($in, $from, $to - $from));
2221
+			}
2222
+			if (substr($in, $strend - 1, 1) === '\\') {
2223
+				$o      = $strend + 1;
2224
+				$strend = false;
2225
+			}
2226
+		}
2227
+		if ($this->debug) {
2228
+			echo 'STRING DELIMITED: ' . substr($in, $from, $strend + 1 - $from) . "\n";
2229
+		}
2230
+
2231
+		$srcOutput = substr($in, $from, $strend + 1 - $from);
2232
+
2233
+		if ($pointer !== null) {
2234
+			$pointer += strlen($srcOutput);
2235
+		}
2236
+
2237
+		$output = $this->replaceStringVars($srcOutput, $first);
2238
+
2239
+		// handle modifiers
2240
+		if ($curBlock !== 'modifier' && preg_match('#^((?:\|(?:@?[a-z0-9_]+(?::.*)*))+)#i', substr($substr, $strend + 1 - $from), $match)) {
2241
+			$modstr = $match[1];
2242
+
2243
+			if ($curBlock === 'root' && substr($modstr, - 1) === '}') {
2244
+				$modstr = substr($modstr, 0, - 1);
2245
+			}
2246
+			$modstr = str_replace('\\' . $first, $first, $modstr);
2247
+			$ptr    = 0;
2248
+			$output = $this->replaceModifiers(array(null, null, $output, $modstr), 'string', $ptr);
2249
+
2250
+			$strend += $ptr;
2251
+			if ($pointer !== null) {
2252
+				$pointer += $ptr;
2253
+			}
2254
+			$srcOutput .= substr($substr, $strend + 1 - $from, $ptr);
2255
+		}
2256
+
2257
+		if (is_array($parsingParams)) {
2258
+			$parsingParams[] = array($output, substr($srcOutput, 1, - 1));
2259
+
2260
+			return $parsingParams;
2261
+		} elseif ($curBlock === 'namedparam') {
2262
+			return array($output, substr($srcOutput, 1, - 1));
2263
+		}
2264
+
2265
+		return $output;
2266
+	}
2267
+
2268
+	/**
2269
+	 * Parses a constant.
2270
+	 *
2271
+	 * @param string $in            the string within which we must parse something
2272
+	 * @param int    $from          the starting offset of the parsed area
2273
+	 * @param int    $to            the ending offset of the parsed area
2274
+	 * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
2275
+	 *                              default
2276
+	 * @param string $curBlock      the current parser-block being processed
2277
+	 * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
2278
+	 *                              or null by default
2279
+	 *
2280
+	 * @return string parsed values
2281
+	 * @throws CompilationException
2282
+	 */
2283
+	protected function parseConst($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2284
+	{
2285
+		$substr = substr($in, $from, $to - $from);
2286
+
2287
+		if ($this->debug) {
2288
+			echo 'CONST FOUND : ' . $substr . "\n";
2289
+		}
2290
+
2291
+		if (!preg_match('#^%([\\\\a-z0-9_:]+)#i', $substr, $m)) {
2292
+			throw new CompilationException($this, 'Invalid constant');
2293
+		}
2294
+
2295
+		if ($pointer !== null) {
2296
+			$pointer += strlen($m[0]);
2297
+		}
2298
+
2299
+		$output = $this->parseConstKey($m[1], $curBlock);
2300
+
2301
+		if (is_array($parsingParams)) {
2302
+			$parsingParams[] = array($output, $m[1]);
2303
+
2304
+			return $parsingParams;
2305
+		} elseif ($curBlock === 'namedparam') {
2306
+			return array($output, $m[1]);
2307
+		}
2308
+
2309
+		return $output;
2310
+	}
2311
+
2312
+	/**
2313
+	 * Parses a constant.
2314
+	 *
2315
+	 * @param string $key      the constant to parse
2316
+	 * @param string $curBlock the current parser-block being processed
2317
+	 *
2318
+	 * @return string parsed constant
2319
+	 */
2320
+	protected function parseConstKey($key, $curBlock)
2321
+	{
2322
+		$key = str_replace('\\\\', '\\', $key);
2323
+
2324
+		if ($this->securityPolicy !== null && $this->securityPolicy->getConstantHandling() === SecurityPolicy::CONST_DISALLOW) {
2325
+			return 'null';
2326
+		}
2327
+
2328
+		if ($curBlock !== 'root') {
2329
+			return '(defined("' . $key . '") ? ' . $key . ' : null)';
2330
+		}
2331
+
2332
+		return $key;
2333
+	}
2334
+
2335
+	/**
2336
+	 * Parses a variable.
2337
+	 *
2338
+	 * @param string $in            the string within which we must parse something
2339
+	 * @param int    $from          the starting offset of the parsed area
2340
+	 * @param int    $to            the ending offset of the parsed area
2341
+	 * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
2342
+	 *                              default
2343
+	 * @param string $curBlock      the current parser-block being processed
2344
+	 * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
2345
+	 *                              or null by default
2346
+	 *
2347
+	 * @return string parsed values
2348
+	 * @throws CompilationException
2349
+	 */
2350
+	protected function parseVar($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2351
+	{
2352
+		$substr = substr($in, $from, $to - $from);
2353
+
2354
+		// var key
2355
+		$varRegex = '(\\$?\\.?[a-z0-9\\\\_:]*(?:(?:(?:\\.|->)(?:[a-z0-9\\\\_:]+|(?R))|\\[(?:[a-z0-9\\\\_:]+|(?R)|(["\'])[^\\2]*?\\2)\\]))*)';
2356
+		// method call
2357
+		$methodCall = ($curBlock === 'root' || $curBlock === 'function' || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'variable' || $curBlock === 'expression' || $curBlock === 'delimited_string' ? '(\(.*)?' : '()');
2358
+		// simple math expressions
2359
+		$simpleMathExpressions = ($curBlock === 'root' || $curBlock === 'function' || $curBlock === 'namedparam' || $curBlock === 'condition' || $curBlock === 'variable' || $curBlock === 'delimited_string' ? '((?:(?:[+\/*%=-])(?:(?<!=)=?-?[$%][a-z0-9\\\\.[\]>_:-]+(?:\([^)]*\))?|(?<!=)=?-?[0-9\.,]*|[+-]))*)' : '()');
2360
+		// modifiers
2361
+		$modifiers = $curBlock !== 'modifier' ? '((?:\|(?:@?[a-z0-9\\\\_]+(?:(?::("|\').*?\5|:[^`]*))*))+)?' : '(())';
2362
+
2363
+		$regex = '#';
2364
+		$regex .= $varRegex;
2365
+		$regex .= $methodCall;
2366
+		$regex .= $simpleMathExpressions;
2367
+		$regex .= $modifiers;
2368
+		$regex .= '#i';
2369
+
2370
+		if (preg_match($regex, $substr, $match)) {
2371
+			$key = substr($match[1], 1);
2372
+
2373
+			$matchedLength = strlen($match[0]);
2374
+			$hasModifiers  = !empty($match[5]);
2375
+			$hasExpression = !empty($match[4]);
2376
+			$hasMethodCall = !empty($match[3]);
2377
+
2378
+			if (substr($key, - 1) == '.') {
2379
+				$key = substr($key, 0, - 1);
2380
+				-- $matchedLength;
2381
+			}
2382
+
2383
+			if ($hasMethodCall) {
2384
+				$matchedLength -= strlen($match[3]) + strlen(substr($match[1], strrpos($match[1], '->')));
2385
+				$key        = substr($match[1], 1, strrpos($match[1], '->') - 1);
2386
+				$methodCall = substr($match[1], strrpos($match[1], '->')) . $match[3];
2387
+			}
2388
+
2389
+			if ($hasModifiers) {
2390
+				$matchedLength -= strlen($match[5]);
2391
+			}
2392
+
2393
+			if ($pointer !== null) {
2394
+				$pointer += $matchedLength;
2395
+			}
2396
+
2397
+			// replace useless brackets by dot accessed vars and strip enclosing quotes if present
2398
+			$key = preg_replace('#\[(["\']?)([^$%\[.>-]+)\1\]#', '.$2', $key);
2399
+
2400
+			if ($this->debug) {
2401
+				if ($hasMethodCall) {
2402
+					echo 'METHOD CALL FOUND : $' . $key . substr($methodCall, 0, 30) . "\n";
2403
+				} else {
2404
+					echo 'VAR FOUND : $' . $key . "\n";
2405
+				}
2406
+			}
2407
+
2408
+			$key = str_replace('"', '\\"', $key);
2409
+
2410
+			$cnt = substr_count($key, '$');
2411
+			if ($cnt > 0) {
2412
+				$uid           = 0;
2413
+				$parsed        = array($uid => '');
2414
+				$current       = &$parsed;
2415
+				$curTxt        = &$parsed[$uid ++];
2416
+				$tree          = array();
2417
+				$chars         = str_split($key, 1);
2418
+				$inSplittedVar = false;
2419
+				$bracketCount  = 0;
2420
+
2421
+				while (($char = array_shift($chars)) !== null) {
2422
+					if ($char === '[') {
2423
+						if (count($tree) > 0) {
2424
+							++ $bracketCount;
2425
+						} else {
2426
+							$tree[]        = &$current;
2427
+							$current[$uid] = array($uid + 1 => '');
2428
+							$current       = &$current[$uid ++];
2429
+							$curTxt        = &$current[$uid ++];
2430
+							continue;
2431
+						}
2432
+					} elseif ($char === ']') {
2433
+						if ($bracketCount > 0) {
2434
+							-- $bracketCount;
2435
+						} else {
2436
+							$current = &$tree[count($tree) - 1];
2437
+							array_pop($tree);
2438
+							if (current($chars) !== '[' && current($chars) !== false && current($chars) !== ']') {
2439
+								$current[$uid] = '';
2440
+								$curTxt        = &$current[$uid ++];
2441
+							}
2442
+							continue;
2443
+						}
2444
+					} elseif ($char === '$') {
2445
+						if (count($tree) == 0) {
2446
+							$curTxt        = &$current[$uid ++];
2447
+							$inSplittedVar = true;
2448
+						}
2449
+					} elseif (($char === '.' || $char === '-') && count($tree) == 0 && $inSplittedVar) {
2450
+						$curTxt        = &$current[$uid ++];
2451
+						$inSplittedVar = false;
2452
+					}
2453
+
2454
+					$curTxt .= $char;
2455
+				}
2456
+				unset($uid, $current, $curTxt, $tree, $chars);
2457
+
2458
+				if ($this->debug) {
2459
+					echo 'RECURSIVE VAR REPLACEMENT : ' . $key . "\n";
2460
+				}
2461
+
2462
+				$key = $this->flattenVarTree($parsed);
2463
+
2464
+				if ($this->debug) {
2465
+					echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n";
2466
+				}
2467
+
2468
+				$output = preg_replace('#(^""\.|""\.|\.""$|(\()""\.|\.""(\)))#', '$2$3', '$this->readVar("' . $key . '")');
2469
+			} else {
2470
+				$output = $this->parseVarKey($key, $hasModifiers ? 'modifier' : $curBlock);
2471
+			}
2472
+
2473
+
2474
+			// methods
2475
+			if ($hasMethodCall) {
2476
+				$ptr = 0;
2477
+
2478
+				$output = $this->parseMethodCall($output, $methodCall, $curBlock, $ptr);
2479
+
2480
+				if ($pointer !== null) {
2481
+					$pointer += $ptr;
2482
+				}
2483
+				$matchedLength += $ptr;
2484
+			}
2485
+
2486
+			if ($hasExpression) {
2487
+				// expressions
2488
+				preg_match_all('#(?:([+/*%=-])(=?-?[%$][a-z0-9\\\\.[\]>_:-]+(?:\([^)]*\))?|=?-?[0-9.,]+|\1))#i', $match[4], $expMatch);
2489
+				foreach ($expMatch[1] as $k => $operator) {
2490
+					if (substr($expMatch[2][$k], 0, 1) === '=') {
2491
+						$assign = true;
2492
+						if ($operator === '=') {
2493
+							throw new CompilationException($this, 'Invalid expression <em>' . $substr . '</em>, can not use "==" in expressions');
2494
+						}
2495
+						if ($curBlock !== 'root') {
2496
+							throw new CompilationException($this, 'Invalid expression <em>' . $substr . '</em>, assignments can only be used in top level expressions like {$foo+=3} or {$foo="bar"}');
2497
+						}
2498
+						$operator .= '=';
2499
+						$expMatch[2][$k] = substr($expMatch[2][$k], 1);
2500
+					}
2501
+
2502
+					if (substr($expMatch[2][$k], 0, 1) === '-' && strlen($expMatch[2][$k]) > 1) {
2503
+						$operator .= '-';
2504
+						$expMatch[2][$k] = substr($expMatch[2][$k], 1);
2505
+					}
2506
+					if (($operator === '+' || $operator === '-') && $expMatch[2][$k] === $operator) {
2507
+						$output = '(' . $output . $operator . $operator . ')';
2508
+						break;
2509
+					} elseif (substr($expMatch[2][$k], 0, 1) === '$') {
2510
+						$output = '(' . $output . ' ' . $operator . ' ' . $this->parseVar($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')';
2511
+					} elseif (substr($expMatch[2][$k], 0, 1) === '%') {
2512
+						$output = '(' . $output . ' ' . $operator . ' ' . $this->parseConst($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')';
2513
+					} elseif (!empty($expMatch[2][$k])) {
2514
+						$output = '(' . $output . ' ' . $operator . ' ' . str_replace(',', '.', $expMatch[2][$k]) . ')';
2515
+					} else {
2516
+						throw new CompilationException($this, 'Unfinished expression <em>' . $substr . '</em>, missing var or number after math operator');
2517
+					}
2518
+				}
2519
+			}
2520
+
2521
+			if ($this->autoEscape === true && $curBlock !== 'condition') {
2522
+				$output = '(is_string($tmp=' . $output . ') ? htmlspecialchars($tmp, ENT_QUOTES, $this->charset) : $tmp)';
2523
+			}
2524
+
2525
+			// handle modifiers
2526
+			if ($curBlock !== 'modifier' && $hasModifiers) {
2527
+				$ptr    = 0;
2528
+				$output = $this->replaceModifiers(array(null, null, $output, $match[5]), 'var', $ptr);
2529
+				if ($pointer !== null) {
2530
+					$pointer += $ptr;
2531
+				}
2532
+				$matchedLength += $ptr;
2533
+			}
2534
+
2535
+			if (is_array($parsingParams)) {
2536
+				$parsingParams[] = array($output, $key);
2537
+
2538
+				return $parsingParams;
2539
+			} elseif ($curBlock === 'namedparam') {
2540
+				return array($output, $key);
2541
+			} elseif ($curBlock === 'string' || $curBlock === 'delimited_string') {
2542
+				return array($matchedLength, $output);
2543
+			} elseif ($curBlock === 'expression' || $curBlock === 'variable') {
2544
+				return $output;
2545
+			} elseif (isset($assign)) {
2546
+				return self::PHP_OPEN . $output . ';' . self::PHP_CLOSE;
2547
+			}
2548
+
2549
+			return $output;
2550
+		} else {
2551
+			if ($curBlock === 'string' || $curBlock === 'delimited_string') {
2552
+				return array(0, '');
2553
+			}
2554
+			throw new CompilationException($this, 'Invalid variable name <em>' . $substr . '</em>');
2555
+		}
2556
+	}
2557
+
2558
+	/**
2559
+	 * Parses any number of chained method calls/property reads.
2560
+	 *
2561
+	 * @param string $output     the variable or whatever upon which the method are called
2562
+	 * @param string $methodCall method call source, starting at "->"
2563
+	 * @param string $curBlock   the current parser-block being processed
2564
+	 * @param int    $pointer    a reference to a pointer that will be increased by the amount of characters parsed
2565
+	 *
2566
+	 * @return string parsed call(s)/read(s)
2567
+	 */
2568
+	protected function parseMethodCall($output, $methodCall, $curBlock, &$pointer)
2569
+	{
2570
+		$ptr = 0;
2571
+		$len = strlen($methodCall);
2572
+
2573
+		while ($ptr < $len) {
2574
+			if (strpos($methodCall, '->', $ptr) === $ptr) {
2575
+				$ptr += 2;
2576
+			}
2577
+
2578
+			if (in_array(
2579
+				$methodCall[$ptr], array(
2580
+					';',
2581
+					',',
2582
+					'/',
2583
+					' ',
2584
+					"\t",
2585
+					"\r",
2586
+					"\n",
2587
+					')',
2588
+					'+',
2589
+					'*',
2590
+					'%',
2591
+					'=',
2592
+					'-',
2593
+					'|'
2594
+				)
2595
+			) || substr($methodCall, $ptr, strlen($this->rd)) === $this->rd
2596
+			) {
2597
+				// break char found
2598
+				break;
2599
+			}
2600
+
2601
+			if (!preg_match('/^([a-z0-9_]+)(\(.*?\))?/i', substr($methodCall, $ptr), $methMatch)) {
2602
+				break;
2603
+			}
2604
+
2605
+			if (empty($methMatch[2])) {
2606
+				// property
2607
+				if ($curBlock === 'root') {
2608
+					$output .= '->' . $methMatch[1];
2609
+				} else {
2610
+					$output = '(($tmp = ' . $output . ') ? $tmp->' . $methMatch[1] . ' : null)';
2611
+				}
2612
+				$ptr += strlen($methMatch[1]);
2613
+			} else {
2614
+				// method
2615
+				if (substr($methMatch[2], 0, 2) === '()') {
2616
+					$parsedCall = $methMatch[1] . '()';
2617
+					$ptr += strlen($methMatch[1]) + 2;
2618
+				} else {
2619
+					$parsedCall = $this->parseFunction($methodCall, $ptr, strlen($methodCall), false, 'method', $ptr);
2620
+				}
2621
+				if ($this->securityPolicy !== null) {
2622
+					$argPos = strpos($parsedCall, '(');
2623
+					$method = strtolower(substr($parsedCall, 0, $argPos));
2624
+					$args   = substr($parsedCall, $argPos);
2625
+					if ($curBlock === 'root') {
2626
+						$output = '$this->getSecurityPolicy()->callMethod($this, ' . $output . ', ' . var_export($method, true) . ', array' . $args . ')';
2627
+					} else {
2628
+						$output = '(($tmp = ' . $output . ') ? $this->getSecurityPolicy()->callMethod($this, $tmp, ' . var_export($method, true) . ', array' . $args . ') : null)';
2629
+					}
2630
+				} else {
2631
+					if ($curBlock === 'root') {
2632
+						$output .= '->' . $parsedCall;
2633
+					} else {
2634
+						$output = '(($tmp = ' . $output . ') ? $tmp->' . $parsedCall . ' : null)';
2635
+					}
2636
+				}
2637
+			}
2638
+		}
2639
+
2640
+		$pointer += $ptr;
2641
+
2642
+		return $output;
2643
+	}
2644
+
2645
+	/**
2646
+	 * Parses a constant variable (a variable that doesn't contain another variable) and preprocesses it to save
2647
+	 * runtime processing time.
2648
+	 *
2649
+	 * @param string $key      the variable to parse
2650
+	 * @param string $curBlock the current parser-block being processed
2651
+	 *
2652
+	 * @return string parsed variable
2653
+	 */
2654
+	protected function parseVarKey($key, $curBlock)
2655
+	{
2656
+		if ($key === '') {
2657
+			return '$this->scope';
2658
+		}
2659
+		if (substr($key, 0, 1) === '.') {
2660
+			$key = 'dwoo' . $key;
2661
+		}
2662
+		if (preg_match('#dwoo\.(get|post|server|cookies|session|env|request)((?:\.[a-z0-9_-]+)+)#i', $key, $m)) {
2663
+			$global = strtoupper($m[1]);
2664
+			if ($global === 'COOKIES') {
2665
+				$global = 'COOKIE';
2666
+			}
2667
+			$key = '$_' . $global;
2668
+			foreach (explode('.', ltrim($m[2], '.')) as $part) {
2669
+				$key .= '[' . var_export($part, true) . ']';
2670
+			}
2671
+			if ($curBlock === 'root') {
2672
+				$output = $key;
2673
+			} else {
2674
+				$output = '(isset(' . $key . ')?' . $key . ':null)';
2675
+			}
2676
+		} elseif (preg_match('#dwoo\\.const\\.([a-z0-9\\\\_:]+)#i', $key, $m)) {
2677
+			return $this->parseConstKey($m[1], $curBlock);
2678
+		} elseif ($this->scope !== null) {
2679
+			if (strstr($key, '.') === false && strstr($key, '[') === false && strstr($key, '->') === false) {
2680
+				if ($key === 'dwoo') {
2681
+					$output = '$this->globals';
2682
+				} elseif ($key === '_root' || $key === '__') {
2683
+					$output = '$this->data';
2684
+				} elseif ($key === '_parent' || $key === '_') {
2685
+					$output = '$this->readParentVar(1)';
2686
+				} elseif ($key === '_key') {
2687
+					$output = '$tmp_key';
2688
+				} else {
2689
+					if ($curBlock === 'root') {
2690
+						$output = '$this->scope["' . $key . '"]';
2691
+					} else {
2692
+						$output = '(isset($this->scope["' . $key . '"]) ? $this->scope["' . $key . '"] : null)';
2693
+					}
2694
+				}
2695
+			} else {
2696
+				preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+|(\\\?[\'"])[^\3]*?\3)\]?#i', $key, $m);
2697
+
2698
+				$i = $m[2][0];
2699
+				if ($i === '_parent' || $i === '_') {
2700
+					$parentCnt = 0;
2701
+
2702
+					while (true) {
2703
+						++ $parentCnt;
2704
+						array_shift($m[2]);
2705
+						array_shift($m[1]);
2706
+						if (current($m[2]) === '_parent') {
2707
+							continue;
2708
+						}
2709
+						break;
2710
+					}
2711
+
2712
+					$output = '$this->readParentVar(' . $parentCnt . ')';
2713
+				} else {
2714
+					if ($i === 'dwoo') {
2715
+						$output = '$this->globals';
2716
+						array_shift($m[2]);
2717
+						array_shift($m[1]);
2718
+					} elseif ($i === '_root' || $i === '__') {
2719
+						$output = '$this->data';
2720
+						array_shift($m[2]);
2721
+						array_shift($m[1]);
2722
+					} elseif ($i === '_key') {
2723
+						$output = '$tmp_key';
2724
+					} else {
2725
+						$output = '$this->scope';
2726
+					}
2727
+
2728
+					while (count($m[1]) && $m[1][0] !== '->') {
2729
+						$m[2][0] = preg_replace('/(^\\\([\'"])|\\\([\'"])$)/x', '$2$3', $m[2][0]);
2730
+						if (substr($m[2][0], 0, 1) == '"' || substr($m[2][0], 0, 1) == "'") {
2731
+							$output .= '[' . $m[2][0] . ']';
2732
+						} else {
2733
+							$output .= '["' . $m[2][0] . '"]';
2734
+						}
2735
+						array_shift($m[2]);
2736
+						array_shift($m[1]);
2737
+					}
2738
+
2739
+					if ($curBlock !== 'root') {
2740
+						$output = '(isset(' . $output . ') ? ' . $output . ':null)';
2741
+					}
2742
+				}
2743
+
2744
+				if (count($m[2])) {
2745
+					unset($m[0]);
2746
+					$output = '$this->readVarInto(' . str_replace("\n", '', var_export($m, true)) . ', ' . $output . ', ' . ($curBlock == 'root' ? 'false' : 'true') . ')';
2747
+				}
2748
+			}
2749
+		} else {
2750
+			preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+)\]?#i', $key, $m);
2751
+			unset($m[0]);
2752
+			$output = '$this->readVar(' . str_replace("\n", '', var_export($m, true)) . ')';
2753
+		}
2754
+
2755
+		return $output;
2756
+	}
2757
+
2758
+	/**
2759
+	 * Flattens a variable tree, this helps in parsing very complex variables such as $var.foo[$foo.bar->baz].baz,
2760
+	 * it computes the contents of the brackets first and works out from there.
2761
+	 *
2762
+	 * @param array $tree     the variable tree parsed by he parseVar() method that must be flattened
2763
+	 * @param bool  $recursed leave that to false by default, it is only for internal use
2764
+	 *
2765
+	 * @return string flattened tree
2766
+	 */
2767
+	protected function flattenVarTree(array $tree, $recursed = false)
2768
+	{
2769
+		$out = $recursed ? '".$this->readVarInto(' : '';
2770
+		foreach ($tree as $bit) {
2771
+			if (is_array($bit)) {
2772
+				$out .= '.' . $this->flattenVarTree($bit, false);
2773
+			} else {
2774
+				$key = str_replace('"', '\\"', $bit);
2775
+
2776
+				if (substr($key, 0, 1) === '$') {
2777
+					$out .= '".' . $this->parseVar($key, 0, strlen($key), false, 'variable') . '."';
2778
+				} else {
2779
+					$cnt = substr_count($key, '$');
2780
+
2781
+					if ($this->debug) {
2782
+						echo 'PARSING SUBVARS IN : ' . $key . "\n";
2783
+					}
2784
+					if ($cnt > 0) {
2785
+						while (-- $cnt >= 0) {
2786
+							if (isset($last)) {
2787
+								$last = strrpos($key, '$', - (strlen($key) - $last + 1));
2788
+							} else {
2789
+								$last = strrpos($key, '$');
2790
+							}
2791
+							preg_match('#\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', substr($key, $last), $submatch);
2792
+
2793
+							$len = strlen($submatch[0]);
2794
+							$key = substr_replace(
2795
+								$key, preg_replace_callback(
2796
+									'#(\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*)' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', array(
2797
+										$this,
2798
+										'replaceVarKeyHelper'
2799
+									), substr($key, $last, $len)
2800
+								), $last, $len
2801
+							);
2802
+							if ($this->debug) {
2803
+								echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n";
2804
+							}
2805
+						}
2806
+						unset($last);
2807
+
2808
+						$out .= $key;
2809
+					} else {
2810
+						$out .= $key;
2811
+					}
2812
+				}
2813
+			}
2814
+		}
2815
+		$out .= $recursed ? ', true)."' : '';
2816
+
2817
+		return $out;
2818
+	}
2819
+
2820
+	/**
2821
+	 * Helper function that parses a variable.
2822
+	 *
2823
+	 * @param array $match the matched variable, array(1=>"string match")
2824
+	 *
2825
+	 * @return string parsed variable
2826
+	 */
2827
+	protected function replaceVarKeyHelper($match)
2828
+	{
2829
+		return '".' . $this->parseVar($match[0], 0, strlen($match[0]), false, 'variable') . '."';
2830
+	}
2831
+
2832
+	/**
2833
+	 * Parses various constants, operators or non-quoted strings.
2834
+	 *
2835
+	 * @param string $in            the string within which we must parse something
2836
+	 * @param int    $from          the starting offset of the parsed area
2837
+	 * @param int    $to            the ending offset of the parsed area
2838
+	 * @param mixed  $parsingParams must be an array if we are parsing a function or modifier's parameters, or false by
2839
+	 *                              default
2840
+	 * @param string $curBlock      the current parser-block being processed
2841
+	 * @param mixed  $pointer       a reference to a pointer that will be increased by the amount of characters parsed,
2842
+	 *                              or null by default
2843
+	 *
2844
+	 * @return string parsed values
2845
+	 * @throws Exception
2846
+	 */
2847
+	protected function parseOthers($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2848
+	{
2849
+		$substr = substr($in, $from, $to - $from);
2850
+
2851
+		$end = strlen($substr);
2852
+
2853
+		if ($curBlock === 'condition') {
2854
+			$breakChars = array(
2855
+				'(',
2856
+				')',
2857
+				' ',
2858
+				'||',
2859
+				'&&',
2860
+				'|',
2861
+				'&',
2862
+				'>=',
2863
+				'<=',
2864
+				'===',
2865
+				'==',
2866
+				'=',
2867
+				'!==',
2868
+				'!=',
2869
+				'<<',
2870
+				'<',
2871
+				'>>',
2872
+				'>',
2873
+				'^',
2874
+				'~',
2875
+				',',
2876
+				'+',
2877
+				'-',
2878
+				'*',
2879
+				'/',
2880
+				'%',
2881
+				'!',
2882
+				'?',
2883
+				':',
2884
+				$this->rd,
2885
+				';'
2886
+			);
2887
+		} elseif ($curBlock === 'modifier') {
2888
+			$breakChars = array(' ', ',', ')', ':', '|', "\r", "\n", "\t", ';', $this->rd);
2889
+		} elseif ($curBlock === 'expression') {
2890
+			$breakChars = array('/', '%', '+', '-', '*', ' ', ',', ')', "\r", "\n", "\t", ';', $this->rd);
2891
+		} else {
2892
+			$breakChars = array(' ', ',', ')', "\r", "\n", "\t", ';', $this->rd);
2893
+		}
2894
+
2895
+		$breaker = false;
2896
+		foreach ($breakChars as $k => $char) {
2897
+			$test = strpos($substr, $char);
2898
+			if ($test !== false && $test < $end) {
2899
+				$end     = $test;
2900
+				$breaker = $k;
2901
+			}
2902
+		}
2903
+
2904
+		if ($curBlock === 'condition') {
2905
+			if ($end === 0 && $breaker !== false) {
2906
+				$end = strlen($breakChars[$breaker]);
2907
+			}
2908
+		}
2909
+
2910
+		if ($end !== false) {
2911
+			$substr = substr($substr, 0, $end);
2912
+		}
2913
+
2914
+		if ($pointer !== null) {
2915
+			$pointer += strlen($substr);
2916
+		}
2917
+
2918
+		$src    = $substr;
2919
+		$substr = trim($substr);
2920
+
2921
+		if (strtolower($substr) === 'false' || strtolower($substr) === 'no' || strtolower($substr) === 'off') {
2922
+			if ($this->debug) {
2923
+				echo 'BOOLEAN(FALSE) PARSED' . "\n";
2924
+			}
2925
+			$substr = 'false';
2926
+			$type   = self::T_BOOL;
2927
+		} elseif (strtolower($substr) === 'true' || strtolower($substr) === 'yes' || strtolower($substr) === 'on') {
2928
+			if ($this->debug) {
2929
+				echo 'BOOLEAN(TRUE) PARSED' . "\n";
2930
+			}
2931
+			$substr = 'true';
2932
+			$type   = self::T_BOOL;
2933
+		} elseif ($substr === 'null' || $substr === 'NULL') {
2934
+			if ($this->debug) {
2935
+				echo 'NULL PARSED' . "\n";
2936
+			}
2937
+			$substr = 'null';
2938
+			$type   = self::T_NULL;
2939
+		} elseif (is_numeric($substr)) {
2940
+			$substr = (float)$substr;
2941
+			if ((int)$substr == $substr) {
2942
+				$substr = (int)$substr;
2943
+			}
2944
+			$type = self::T_NUMERIC;
2945
+			if ($this->debug) {
2946
+				echo 'NUMBER (' . $substr . ') PARSED' . "\n";
2947
+			}
2948
+		} elseif (preg_match('{^-?(\d+|\d*(\.\d+))\s*([/*%+-]\s*-?(\d+|\d*(\.\d+)))+$}', $substr)) {
2949
+			if ($this->debug) {
2950
+				echo 'SIMPLE MATH PARSED . "\n"';
2951
+			}
2952
+			$type   = self::T_MATH;
2953
+			$substr = '(' . $substr . ')';
2954
+		} elseif ($curBlock === 'condition' && array_search($substr, $breakChars, true) !== false) {
2955
+			if ($this->debug) {
2956
+				echo 'BREAKCHAR (' . $substr . ') PARSED' . "\n";
2957
+			}
2958
+			$type = self::T_BREAKCHAR;
2959
+			//$substr = '"'.$substr.'"';
2960
+		} else {
2961
+			$substr = $this->replaceStringVars('\'' . str_replace('\'', '\\\'', $substr) . '\'', '\'', $curBlock);
2962
+			$type   = self::T_UNQUOTED_STRING;
2963
+			if ($this->debug) {
2964
+				echo 'BLABBER (' . $substr . ') CASTED AS STRING' . "\n";
2965
+			}
2966
+		}
2967
+
2968
+		if (is_array($parsingParams)) {
2969
+			$parsingParams[] = array($substr, $src, $type);
2970
+
2971
+			return $parsingParams;
2972
+		} elseif ($curBlock === 'namedparam') {
2973
+			return array($substr, $src, $type);
2974
+		} elseif ($curBlock === 'expression') {
2975
+			return $substr;
2976
+		} else {
2977
+			throw new Exception('Something went wrong');
2978
+		}
2979
+	}
2980
+
2981
+	/**
2982
+	 * Replaces variables within a parsed string.
2983
+	 *
2984
+	 * @param string $string   the parsed string
2985
+	 * @param string $first    the first character parsed in the string, which is the string delimiter (' or ")
2986
+	 * @param string $curBlock the current parser-block being processed
2987
+	 *
2988
+	 * @return string the original string with variables replaced
2989
+	 */
2990
+	protected function replaceStringVars($string, $first, $curBlock = '')
2991
+	{
2992
+		$pos = 0;
2993
+		if ($this->debug) {
2994
+			echo 'STRING VAR REPLACEMENT : ' . $string . "\n";
2995
+		}
2996
+		// replace vars
2997
+		while (($pos = strpos($string, '$', $pos)) !== false) {
2998
+			$prev = substr($string, $pos - 1, 1);
2999
+			if ($prev === '\\') {
3000
+				++ $pos;
3001
+				continue;
3002
+			}
3003
+
3004
+			$var = $this->parse($string, $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string')));
3005
+			$len = $var[0];
3006
+			$var = $this->parse(str_replace('\\' . $first, $first, $string), $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string')));
3007
+
3008
+			if ($prev === '`' && substr($string, $pos + $len, 1) === '`') {
3009
+				$string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos - 1, $len + 2);
3010
+			} else {
3011
+				$string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos, $len);
3012
+			}
3013
+			$pos += strlen($var[1]) + 2;
3014
+			if ($this->debug) {
3015
+				echo 'STRING VAR REPLACEMENT DONE : ' . $string . "\n";
3016
+			}
3017
+		}
3018
+
3019
+		// handle modifiers
3020
+		// TODO Obsolete?
3021
+		$string = preg_replace_callback(
3022
+			'#("|\')\.(.+?)\.\1((?:\|(?:@?[a-z0-9_]+(?:(?::("|\').+?\4|:[^`]*))*))+)#i', array(
3023
+			$this,
3024
+			'replaceModifiers'
3025
+			), $string
3026
+		);
3027
+
3028
+		// replace escaped dollar operators by unescaped ones if required
3029
+		if ($first === "'") {
3030
+			$string = str_replace('\\$', '$', $string);
3031
+		}
3032
+
3033
+		return $string;
3034
+	}
3035
+
3036
+	/**
3037
+	 * Replaces the modifiers applied to a string or a variable.
3038
+	 *
3039
+	 * @param array  $m        the regex matches that must be array(1=>"double or single quotes enclosing a string,
3040
+	 *                         when applicable", 2=>"the string or var", 3=>"the modifiers matched")
3041
+	 * @param string $curBlock the current parser-block being processed
3042
+	 * @param null   $pointer
3043
+	 *
3044
+	 * @return string the input enclosed with various function calls according to the modifiers found
3045
+	 * @throws CompilationException
3046
+	 * @throws Exception
3047
+	 */
3048
+	protected function replaceModifiers(array $m, $curBlock = null, &$pointer = null)
3049
+	{
3050
+		if ($this->debug) {
3051
+			echo 'PARSING MODIFIERS : ' . $m[3] . "\n";
3052
+		}
3053
+
3054
+		if ($pointer !== null) {
3055
+			$pointer += strlen($m[3]);
3056
+		}
3057
+		// remove first pipe
3058
+		$cmdstrsrc = substr($m[3], 1);
3059
+		// remove last quote if present
3060
+		if (substr($cmdstrsrc, - 1, 1) === $m[1]) {
3061
+			$cmdstrsrc = substr($cmdstrsrc, 0, - 1);
3062
+			$add       = $m[1];
3063
+		}
3064
+
3065
+		$output = $m[2];
3066
+
3067
+		$continue = true;
3068
+		while (strlen($cmdstrsrc) > 0 && $continue) {
3069
+			if ($cmdstrsrc[0] === '|') {
3070
+				$cmdstrsrc = substr($cmdstrsrc, 1);
3071
+				continue;
3072
+			}
3073
+			if ($cmdstrsrc[0] === ' ' || $cmdstrsrc[0] === ';' || substr($cmdstrsrc, 0, strlen($this->rd)) === $this->rd) {
3074
+				if ($this->debug) {
3075
+					echo 'MODIFIER PARSING ENDED, RIGHT DELIMITER or ";" FOUND' . "\n";
3076
+				}
3077
+				$continue = false;
3078
+				if ($pointer !== null) {
3079
+					$pointer -= strlen($cmdstrsrc);
3080
+				}
3081
+				break;
3082
+			}
3083
+			$cmdstr   = $cmdstrsrc;
3084
+			$paramsep = ':';
3085
+			if (!preg_match('/^(@{0,2}[a-z_][a-z0-9_]*)(:)?/i', $cmdstr, $match)) {
3086
+				throw new CompilationException($this, 'Invalid modifier name, started with : ' . substr($cmdstr, 0, 10));
3087
+			}
3088
+			$paramspos = !empty($match[2]) ? strlen($match[1]) : false;
3089
+			$func      = $match[1];
3090
+
3091
+			$state = 0;
3092
+			if ($paramspos === false) {
3093
+				$cmdstrsrc = substr($cmdstrsrc, strlen($func));
3094
+				$params    = array();
3095
+				if ($this->debug) {
3096
+					echo 'MODIFIER (' . $func . ') CALLED WITH NO PARAMS' . "\n";
3097
+				}
3098
+			} else {
3099
+				$paramstr = substr($cmdstr, $paramspos + 1);
3100
+				if (substr($paramstr, - 1, 1) === $paramsep) {
3101
+					$paramstr = substr($paramstr, 0, - 1);
3102
+				}
3103
+
3104
+				$ptr    = 0;
3105
+				$params = array();
3106
+				while ($ptr < strlen($paramstr)) {
3107
+					if ($this->debug) {
3108
+						echo 'MODIFIER (' . $func . ') START PARAM PARSING WITH POINTER AT ' . $ptr . "\n";
3109
+					}
3110
+					if ($this->debug) {
3111
+						echo $paramstr . '--' . $ptr . '--' . strlen($paramstr) . '--modifier' . "\n";
3112
+					}
3113
+					$params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'modifier', $ptr);
3114
+					if ($this->debug) {
3115
+						echo 'PARAM PARSED, POINTER AT ' . $ptr . "\n";
3116
+					}
3117
+
3118
+					if ($ptr >= strlen($paramstr)) {
3119
+						if ($this->debug) {
3120
+							echo 'PARAM PARSING ENDED, PARAM STRING CONSUMED' . "\n";
3121
+						}
3122
+						break;
3123
+					}
3124
+
3125
+					if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === '|' || $paramstr[$ptr] === ';' || substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) {
3126
+						if ($this->debug) {
3127
+							echo 'PARAM PARSING ENDED, " ", "|", RIGHT DELIMITER or ";" FOUND, POINTER AT ' . $ptr . "\n";
3128
+						}
3129
+						if ($paramstr[$ptr] !== '|') {
3130
+							$continue = false;
3131
+							if ($pointer !== null) {
3132
+								$pointer -= strlen($paramstr) - $ptr;
3133
+							}
3134
+						}
3135
+						++ $ptr;
3136
+						break;
3137
+					}
3138
+					if ($ptr < strlen($paramstr) && $paramstr[$ptr] === ':') {
3139
+						++ $ptr;
3140
+					}
3141
+				}
3142
+				$cmdstrsrc = substr($cmdstrsrc, strlen($func) + 1 + $ptr);
3143
+				foreach ($params as $k => $p) {
3144
+					if (is_array($p) && is_array($p[1])) {
3145
+						$state |= 2;
3146
+					} else {
3147
+						if (($state & 2) && preg_match('#^(["\'])(.+?)\1$#', $p[0], $m)) {
3148
+							$params[$k] = array($m[2], array('true', 'true'));
3149
+						} else {
3150
+							if ($state & 2) {
3151
+								throw new CompilationException($this, 'You can not use an unnamed parameter after a named one');
3152
+							}
3153
+							$state |= 1;
3154
+						}
3155
+					}
3156
+				}
3157
+			}
3158
+
3159
+			// check if we must use array_map with this plugin or not
3160
+			$mapped = false;
3161
+			if (substr($func, 0, 1) === '@') {
3162
+				$func   = substr($func, 1);
3163
+				$mapped = true;
3164
+			}
3165
+
3166
+			$pluginType = $this->getPluginType($func);
3167
+
3168
+			if ($state & 2) {
3169
+				array_unshift($params, array('value', is_array($output) ? $output : array($output, $output)));
3170
+			} else {
3171
+				array_unshift($params, is_array($output) ? $output : array($output, $output));
3172
+			}
3173
+
3174
+			if ($pluginType & Core::NATIVE_PLUGIN) {
3175
+				$params = $this->mapParams($params, null, $state);
3176
+
3177
+				$params = $params['*'][0];
3178
+
3179
+				$params = self::implode_r($params);
3180
+
3181
+				if ($mapped) {
3182
+					$output = '$this->arrayMap(\'' . $func . '\', array(' . $params . '))';
3183
+				} else {
3184
+					$output = $func . '(' . $params . ')';
3185
+				}
3186
+			} elseif ($pluginType & Core::PROXY_PLUGIN) {
3187
+				$params = $this->mapParams($params, $this->getDwoo()->getPluginProxy()->getCallback($func), $state);
3188
+				foreach ($params as &$p) {
3189
+					$p = $p[0];
3190
+				}
3191
+				$output = call_user_func(array($this->getDwoo()->getPluginProxy(), 'getCode'), $func, $params);
3192
+			} elseif ($pluginType & Core::SMARTY_MODIFIER) {
3193
+				$params = $this->mapParams($params, null, $state);
3194
+				$params = $params['*'][0];
3195
+
3196
+				$params = self::implode_r($params);
3197
+
3198
+				if ($pluginType & Core::CUSTOM_PLUGIN) {
3199
+					$callback = $this->customPlugins[$func]['callback'];
3200
+					if (is_array($callback)) {
3201
+						if (is_object($callback[0])) {
3202
+							$output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(' . $params . '))';
3203
+						} else {
3204
+							$output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))';
3205
+						}
3206
+					} elseif ($mapped) {
3207
+						$output = '$this->arrayMap(\'' . $callback . '\', array(' . $params . '))';
3208
+					} else {
3209
+						$output = $callback . '(' . $params . ')';
3210
+					}
3211
+				} elseif ($mapped) {
3212
+					$output = '$this->arrayMap(\'smarty_modifier_' . $func . '\', array(' . $params . '))';
3213
+				} else {
3214
+					$output = 'smarty_modifier_' . $func . '(' . $params . ')';
3215
+				}
3216
+			} else {
3217
+				if ($pluginType & Core::CUSTOM_PLUGIN) {
3218
+					$callback   = $this->customPlugins[$func]['callback'];
3219
+					$pluginName = $callback;
3220
+				} else {
3221
+					if (class_exists('Plugin' . Core::toCamelCase($func)) !== false || function_exists('Plugin' .
3222
+							Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''))
3223
+						!== false) {
3224
+						$pluginName = 'Plugin' . Core::toCamelCase($func);
3225
+					} else {
3226
+						$pluginName = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func);
3227
+					}
3228
+					if ($pluginType & Core::CLASS_PLUGIN) {
3229
+						$callback = array($pluginName, ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process');
3230
+					} else {
3231
+						$callback = $pluginName . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '');
3232
+					}
3233
+				}
3234
+				$params = $this->mapParams($params, $callback, $state);
3235
+
3236
+				foreach ($params as &$p) {
3237
+					$p = $p[0];
3238
+				}
3239
+
3240
+				// Only for PHP function, who is not a PHP class
3241
+				if ($pluginType & Core::FUNC_PLUGIN && !($pluginType & Core::CLASS_PLUGIN)) {
3242
+					if ($pluginType & Core::COMPILABLE_PLUGIN) {
3243
+						if ($mapped) {
3244
+							throw new CompilationException($this, 'The @ operator can not be used on compiled plugins.');
3245
+						}
3246
+						if ($pluginType & Core::CUSTOM_PLUGIN) {
3247
+							$funcCompiler = $this->customPlugins[$func]['callback'];
3248
+						} else {
3249
+							if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
3250
+								$funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile';
3251
+							} else {
3252
+								$funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) .
3253
+									'Compile';
3254
+							}
3255
+						}
3256
+						array_unshift($params, $this);
3257
+						$output = call_user_func_array($funcCompiler, $params);
3258
+					} else {
3259
+						array_unshift($params, '$this');
3260
+
3261
+						$params = self::implode_r($params);
3262
+						if ($mapped) {
3263
+							$output = '$this->arrayMap(\'' . $pluginName . '\', array(' . $params . '))';
3264
+						} else {
3265
+							$output = $pluginName . '(' . $params . ')';
3266
+						}
3267
+					}
3268
+				} else {
3269
+					if ($pluginType & Core::COMPILABLE_PLUGIN) {
3270
+						if ($mapped) {
3271
+							throw new CompilationException($this, 'The @ operator can not be used on compiled plugins.');
3272
+						}
3273
+						if ($pluginType & Core::CUSTOM_PLUGIN) {
3274
+							$callback = $this->customPlugins[$func]['callback'];
3275
+							if (!is_array($callback)) {
3276
+								if (!method_exists($callback, 'compile')) {
3277
+									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');
3278
+								}
3279
+								if (($ref = new ReflectionMethod($callback, 'compile')) && $ref->isStatic()) {
3280
+									$funcCompiler = array($callback, 'compile');
3281
+								} else {
3282
+									$funcCompiler = array(new $callback(), 'compile');
3283
+								}
3284
+							} else {
3285
+								$funcCompiler = $callback;
3286
+							}
3287
+						} else {
3288
+							if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
3289
+								$funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile');
3290
+							} else {
3291
+								$funcCompiler = array(
3292
+									Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
3293
+									'compile'
3294
+								);
3295
+							}
3296
+							array_unshift($params, $this);
3297
+						}
3298
+						$output = call_user_func_array($funcCompiler, $params);
3299
+					} else {
3300
+						$params = self::implode_r($params);
3301
+
3302
+						if ($pluginType & Core::CUSTOM_PLUGIN) {
3303
+							if (is_object($callback[0])) {
3304
+								$output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(' . $params . '))';
3305
+							} else {
3306
+								$output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))';
3307
+							}
3308
+						} elseif ($mapped) {
3309
+							$output = '$this->arrayMap(array($this->getObjectPlugin(\''.
3310
+								Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '\'), 
3311 3311
                             \'process\'), array(' . $params . '))';
3312
-                        } else {
3313
-                            if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
3314
-                                $output = '$this->classCall(\'Plugin' . Core::toCamelCase($func) . '\', array(' . $params . '))';
3315
-                            } elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($func)) !== false) {
3316
-                                $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . $func . '\', array(' . $params . '))';
3317
-                            } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) {
3318
-                                $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . $func . '\', array(' . $params . '))';
3319
-                            } else {
3320
-                                $output = '$this->classCall(\'' . $func . '\', array(' . $params . '))';
3321
-                            }
3322
-                        }
3323
-                    }
3324
-                }
3325
-            }
3326
-        }
3327
-
3328
-        if ($curBlock === 'namedparam') {
3329
-            return array($output, $output);
3330
-        } elseif ($curBlock === 'var' || $m[1] === null) {
3331
-            return $output;
3332
-        } elseif ($curBlock === 'string' || $curBlock === 'root') {
3333
-            return $m[1] . '.' . $output . '.' . $m[1] . (isset($add) ? $add : null);
3334
-        }
3335
-
3336
-        return '';
3337
-    }
3338
-
3339
-    /**
3340
-     * Recursively implodes an array in a similar manner as var_export() does but with some tweaks
3341
-     * to handle pre-compiled values and the fact that we do not need to enclose everything with
3342
-     * "array" and do not require top-level keys to be displayed.
3343
-     *
3344
-     * @param array $params        the array to implode
3345
-     * @param bool  $recursiveCall if set to true, the function outputs key names for the top level
3346
-     *
3347
-     * @return string the imploded array
3348
-     */
3349
-    public static function implode_r(array $params, $recursiveCall = false)
3350
-    {
3351
-        $out = '';
3352
-        foreach ($params as $k => $p) {
3353
-            if (is_array($p)) {
3354
-                $out2 = 'array(';
3355
-                foreach ($p as $k2 => $v) {
3356
-                    $out2 .= var_export($k2, true) . ' => ' . (is_array($v) ? 'array(' . self::implode_r($v, true) . ')' : $v) . ', ';
3357
-                }
3358
-                $p = rtrim($out2, ', ') . ')';
3359
-            }
3360
-            if ($recursiveCall) {
3361
-                $out .= var_export($k, true) . ' => ' . $p . ', ';
3362
-            } else {
3363
-                $out .= $p . ', ';
3364
-            }
3365
-        }
3366
-
3367
-        return rtrim($out, ', ');
3368
-    }
3369
-
3370
-    /**
3371
-     * Returns the plugin type of a plugin and adds it to the used plugins array if required.
3372
-     *
3373
-     * @param string $name plugin name, as found in the template
3374
-     *
3375
-     * @return int type as a multi bit flag composed of the Dwoo plugin types constants
3376
-     * @throws Exception
3377
-     * @throws SecurityException
3378
-     * @throws Exception
3379
-     */
3380
-    protected function getPluginType($name)
3381
-    {
3382
-        $pluginType = - 1;
3383
-
3384
-        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)) {
3385
-            $phpFunc = true;
3386
-        } elseif ($this->securityPolicy !== null && function_exists($name) && array_key_exists(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) === false) {
3387
-            throw new SecurityException('Call to a disallowed php function : ' . $name);
3388
-        }
3389
-
3390
-        while ($pluginType <= 0) {
3391
-            // Template plugin compilable
3392
-            if (isset($this->templatePlugins[$name])) {
3393
-                $pluginType = Core::TEMPLATE_PLUGIN | Core::COMPILABLE_PLUGIN;
3394
-            } // Custom plugin
3395
-            elseif (isset($this->customPlugins[$name])) {
3396
-                $pluginType = $this->customPlugins[$name]['type'] | Core::CUSTOM_PLUGIN;
3397
-            } // Class blocks plugin
3398
-            elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name)) !== false) {
3399
-                $pluginType = Core::CLASS_PLUGIN;
3400
-                if (is_subclass_of(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name), 'Dwoo\Block\Plugin')) {
3401
-                    $pluginType += Core::BLOCK_PLUGIN;
3402
-                }
3403
-                $interfaces = class_implements(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name));
3404
-                if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
3405
-                    $pluginType |= Core::COMPILABLE_PLUGIN;
3406
-                }
3407
-            } // Class functions plugin
3408
-            elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name)) !== false) {
3409
-                $pluginType = Core::FUNC_PLUGIN + Core::CLASS_PLUGIN;
3410
-                $interfaces = class_implements(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name));
3411
-                if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
3412
-                    $pluginType |= Core::COMPILABLE_PLUGIN;
3413
-                }
3414
-            } // Class without namespace
3415
-            elseif (class_exists('Plugin' . Core::toCamelCase($name)) !== false) {
3416
-                $pluginType = Core::CLASS_PLUGIN;
3417
-                $interfaces = class_implements('Plugin' . Core::toCamelCase($name));
3418
-                if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
3419
-                    $pluginType |= Core::COMPILABLE_PLUGIN;
3420
-                }
3421
-            } // Function plugin (with/without namespaces)
3422
-            elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase ($name)) !==
3423
-                false || function_exists('Plugin' . Core::toCamelCase($name)) !== false) {
3424
-                $pluginType = Core::FUNC_PLUGIN;
3425
-            } // Function plugin compile (with/without namespaces)
3426
-            elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name) .
3427
-                    'Compile') !== false || function_exists('Plugin' . Core::toCamelCase($name) . 'Compile') !==
3428
-                false) {
3429
-                $pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN;
3430
-            } // Helper plugin class compile
3431
-            elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name)) !== false) {
3432
-                $pluginType = Core::CLASS_PLUGIN | Core::COMPILABLE_PLUGIN;
3433
-            } // Helper plugin function compile
3434
-            elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name) . 'Compile') !== false) {
3435
-                $pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN;
3436
-            } // Smarty modifier
3437
-            elseif (function_exists('smarty_modifier_' . $name) !== false) {
3438
-                $pluginType = Core::SMARTY_MODIFIER;
3439
-            } // Smarty function
3440
-            elseif (function_exists('smarty_function_' . $name) !== false) {
3441
-                $pluginType = Core::SMARTY_FUNCTION;
3442
-            } // Smarty block
3443
-            elseif (function_exists('smarty_block_' . $name) !== false) {
3444
-                $pluginType = Core::SMARTY_BLOCK;
3445
-            } // Everything else
3446
-            else {
3447
-                if ($pluginType === - 1) {
3448
-                    try {
3449
-                        $this->getDwoo()->getLoader()->loadPlugin('Plugin' . Core::toCamelCase($name));
3450
-                    }
3451
-                    catch (Exception $e) {
3452
-                        if (isset($phpFunc)) {
3453
-                            $pluginType = Core::NATIVE_PLUGIN;
3454
-                        } elseif (is_object($this->getDwoo()->getPluginProxy()) && $this->getDwoo()->getPluginProxy()->handles($name)) {
3455
-                            $pluginType = Core::PROXY_PLUGIN;
3456
-                            break;
3457
-                        } else {
3458
-                            throw $e;
3459
-                        }
3460
-                    }
3461
-                } else {
3462
-                    throw new Exception('Plugin "' . $name . '" could not be found, type:' . $pluginType);
3463
-                }
3464
-                ++ $pluginType;
3465
-            }
3466
-        }
3467
-
3468
-        if (($pluginType & Core::COMPILABLE_PLUGIN) === 0 && ($pluginType & Core::NATIVE_PLUGIN) === 0 && ($pluginType & Core::PROXY_PLUGIN) === 0) {
3469
-            $this->addUsedPlugin(Core::toCamelCase($name), $pluginType);
3470
-        }
3471
-
3472
-        return $pluginType;
3473
-    }
3474
-
3475
-    /**
3476
-     * Allows a plugin to load another one at compile time, this will also mark
3477
-     * it as used by this template so it will be loaded at runtime (which can be
3478
-     * useful for compiled plugins that rely on another plugin when their compiled
3479
-     * code runs).
3480
-     *
3481
-     * @param string $name the plugin name
3482
-     *
3483
-     * @return void
3484
-     */
3485
-    public function loadPlugin($name)
3486
-    {
3487
-        $this->getPluginType($name);
3488
-    }
3489
-
3490
-    /**
3491
-     * Runs htmlentities over the matched <?php ?> blocks when the security policy enforces that.
3492
-     *
3493
-     * @param array $match matched php block
3494
-     *
3495
-     * @return string the htmlentities-converted string
3496
-     */
3497
-    protected function phpTagEncodingHelper($match)
3498
-    {
3499
-        return htmlspecialchars($match[0]);
3500
-    }
3501
-
3502
-    /**
3503
-     * Maps the parameters received from the template onto the parameters required by the given callback.
3504
-     *
3505
-     * @param array    $params   the array of parameters
3506
-     * @param callback $callback the function or method to reflect on to find out the required parameters
3507
-     * @param int      $callType the type of call in the template, 0 = no params, 1 = php-style call, 2 = named
3508
-     *                           parameters call
3509
-     * @param array    $map      the parameter map to use, if not provided it will be built from the callback
3510
-     *
3511
-     * @return array parameters sorted in the correct order with missing optional parameters filled
3512
-     * @throws CompilationException
3513
-     */
3514
-    protected function mapParams(array $params, $callback, $callType = 2, $map = null)
3515
-    {
3516
-        if (!$map) {
3517
-            $map = $this->getParamMap($callback);
3518
-        }
3519
-
3520
-        $paramlist = array();
3521
-
3522
-        // transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values))
3523
-        $ps = array();
3524
-        foreach ($params as $p) {
3525
-            if (is_array($p[1])) {
3526
-                $ps[$p[0]] = $p[1];
3527
-            } else {
3528
-                $ps[] = $p;
3529
-            }
3530
-        }
3531
-
3532
-        // loops over the param map and assigns values from the template or default value for unset optional params
3533
-        foreach ($map as $k => $v){
3534
-            if ($v[0] === '*') {
3535
-                // "rest" array parameter, fill every remaining params in it and then break
3536
-                if (count($ps) === 0) {
3537
-                    if ($v[1] === false) {
3538
-                        throw new CompilationException(
3539
-                            $this, 'Rest argument missing for ' . str_replace(
3540
-                                array(
3541
-                                    Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin',
3542
-                                'Compile'
3543
-                                ), '', (is_array($callback) ? $callback[0] : $callback)
3544
-                            )
3545
-                        );
3546
-                    } else {
3547
-                        break;
3548
-                    }
3549
-                }
3550
-                $tmp  = array();
3551
-                $tmp2 = array();
3552
-                $tmp3 = array();
3553
-                foreach ($ps as $i => $p) {
3554
-                    $tmp[$i]  = $p[0];
3555
-                    $tmp2[$i] = $p[1];
3556
-                    $tmp3[$i] = isset($p[2]) ? $p[2] : 0;
3557
-                    unset($ps[$i]);
3558
-                }
3559
-                $paramlist[$v[0]] = array($tmp, $tmp2, $tmp3);
3560
-                unset($tmp, $tmp2, $i, $p);
3561
-                break;
3562
-            } elseif (isset($ps[$v[0]])) {
3563
-                // parameter is defined as named param
3564
-                $paramlist[$v[0]] = $ps[$v[0]];
3565
-                unset($ps[$v[0]]);
3566
-            } elseif (isset($ps[$k])) {
3567
-                // parameter is defined as ordered param
3568
-                $paramlist[$v[0]] = $ps[$k];
3569
-                unset($ps[$k]);
3570
-            } elseif ($v[1] === false) {
3571
-                // parameter is not defined and not optional, throw error
3572
-                if (is_array($callback)) {
3573
-                    if (is_object($callback[0])) {
3574
-                        $name = get_class($callback[0]) . '::' . $callback[1];
3575
-                    } else {
3576
-                        $name = $callback[0];
3577
-                    }
3578
-                } else {
3579
-                    $name = $callback;
3580
-                }
3581
-
3582
-                throw new CompilationException(
3583
-                    $this, 'Argument ' . $k . '/' . $v[0] . ' missing for ' . str_replace(
3584
-                        array(
3585
-                            Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin',
3586
-                        'Compile'
3587
-                        ), '', $name
3588
-                    )
3589
-                );
3590
-            } elseif ($v[2] === null) {
3591
-                // enforce lowercased null if default value is null (php outputs NULL with var export)
3592
-                $paramlist[$v[0]] = array('null', null, self::T_NULL);
3593
-            } else {
3594
-                // outputs default value with var_export
3595
-                $paramlist[$v[0]] = array(var_export($v[2], true), $v[2]);
3596
-            }
3597
-        }
3598
-
3599
-        if (count($ps)) {
3600
-            foreach ($ps as $i => $p) {
3601
-                array_push($paramlist, $p);
3602
-            }
3603
-        }
3604
-
3605
-        return $paramlist;
3606
-    }
3607
-
3608
-    /**
3609
-     * Returns the parameter map of the given callback, it filters out entries typed as Dwoo and Compiler and turns the
3610
-     * rest parameter into a "*".
3611
-     *
3612
-     * @param callback $callback the function/method to reflect on
3613
-     *
3614
-     * @return array processed parameter map
3615
-     */
3616
-    protected function getParamMap($callback)
3617
-    {
3618
-        if (is_null($callback)) {
3619
-            return array(array('*', true));
3620
-        }
3621
-        if (is_array($callback)) {
3622
-            $ref = new ReflectionMethod($callback[0], $callback[1]);
3623
-        } else {
3624
-            $ref = new ReflectionFunction($callback);
3625
-        }
3626
-
3627
-        $out = array();
3628
-        foreach ($ref->getParameters() as $param) {
3629
-            if (($class = $param->getClass()) !== null && $class->name === 'Dwoo\Core') {
3630
-                continue;
3631
-            }
3632
-            if (($class = $param->getClass()) !== null && $class->name === 'Dwoo\Compiler') {
3633
-                continue;
3634
-            }
3635
-            if ($param->getName() === 'rest' && $param->isArray() === true) {
3636
-                $out[] = array('*', $param->isOptional(), null);
3637
-                continue;
3638
-            }
3639
-            $out[] = array(
3640
-                $param->getName(),
3641
-                $param->isOptional(),
3642
-                $param->isOptional() ? $param->getDefaultValue() : null
3643
-            );
3644
-        }
3645
-
3646
-        return $out;
3647
-    }
3648
-
3649
-    /**
3650
-     * Returns a default instance of this compiler, used by default by all Dwoo templates that do not have a
3651
-     * specific compiler assigned and when you do not override the default compiler factory function.
3652
-     *
3653
-     * @see    Core::setDefaultCompilerFactory()
3654
-     * @return Compiler
3655
-     */
3656
-    public static function compilerFactory()
3657
-    {
3658
-        if (self::$instance === null) {
3659
-            self::$instance = new self();
3660
-        }
3661
-
3662
-        return self::$instance;
3663
-    }
3312
+						} else {
3313
+							if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
3314
+								$output = '$this->classCall(\'Plugin' . Core::toCamelCase($func) . '\', array(' . $params . '))';
3315
+							} elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($func)) !== false) {
3316
+								$output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . $func . '\', array(' . $params . '))';
3317
+							} elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) {
3318
+								$output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . $func . '\', array(' . $params . '))';
3319
+							} else {
3320
+								$output = '$this->classCall(\'' . $func . '\', array(' . $params . '))';
3321
+							}
3322
+						}
3323
+					}
3324
+				}
3325
+			}
3326
+		}
3327
+
3328
+		if ($curBlock === 'namedparam') {
3329
+			return array($output, $output);
3330
+		} elseif ($curBlock === 'var' || $m[1] === null) {
3331
+			return $output;
3332
+		} elseif ($curBlock === 'string' || $curBlock === 'root') {
3333
+			return $m[1] . '.' . $output . '.' . $m[1] . (isset($add) ? $add : null);
3334
+		}
3335
+
3336
+		return '';
3337
+	}
3338
+
3339
+	/**
3340
+	 * Recursively implodes an array in a similar manner as var_export() does but with some tweaks
3341
+	 * to handle pre-compiled values and the fact that we do not need to enclose everything with
3342
+	 * "array" and do not require top-level keys to be displayed.
3343
+	 *
3344
+	 * @param array $params        the array to implode
3345
+	 * @param bool  $recursiveCall if set to true, the function outputs key names for the top level
3346
+	 *
3347
+	 * @return string the imploded array
3348
+	 */
3349
+	public static function implode_r(array $params, $recursiveCall = false)
3350
+	{
3351
+		$out = '';
3352
+		foreach ($params as $k => $p) {
3353
+			if (is_array($p)) {
3354
+				$out2 = 'array(';
3355
+				foreach ($p as $k2 => $v) {
3356
+					$out2 .= var_export($k2, true) . ' => ' . (is_array($v) ? 'array(' . self::implode_r($v, true) . ')' : $v) . ', ';
3357
+				}
3358
+				$p = rtrim($out2, ', ') . ')';
3359
+			}
3360
+			if ($recursiveCall) {
3361
+				$out .= var_export($k, true) . ' => ' . $p . ', ';
3362
+			} else {
3363
+				$out .= $p . ', ';
3364
+			}
3365
+		}
3366
+
3367
+		return rtrim($out, ', ');
3368
+	}
3369
+
3370
+	/**
3371
+	 * Returns the plugin type of a plugin and adds it to the used plugins array if required.
3372
+	 *
3373
+	 * @param string $name plugin name, as found in the template
3374
+	 *
3375
+	 * @return int type as a multi bit flag composed of the Dwoo plugin types constants
3376
+	 * @throws Exception
3377
+	 * @throws SecurityException
3378
+	 * @throws Exception
3379
+	 */
3380
+	protected function getPluginType($name)
3381
+	{
3382
+		$pluginType = - 1;
3383
+
3384
+		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)) {
3385
+			$phpFunc = true;
3386
+		} elseif ($this->securityPolicy !== null && function_exists($name) && array_key_exists(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) === false) {
3387
+			throw new SecurityException('Call to a disallowed php function : ' . $name);
3388
+		}
3389
+
3390
+		while ($pluginType <= 0) {
3391
+			// Template plugin compilable
3392
+			if (isset($this->templatePlugins[$name])) {
3393
+				$pluginType = Core::TEMPLATE_PLUGIN | Core::COMPILABLE_PLUGIN;
3394
+			} // Custom plugin
3395
+			elseif (isset($this->customPlugins[$name])) {
3396
+				$pluginType = $this->customPlugins[$name]['type'] | Core::CUSTOM_PLUGIN;
3397
+			} // Class blocks plugin
3398
+			elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name)) !== false) {
3399
+				$pluginType = Core::CLASS_PLUGIN;
3400
+				if (is_subclass_of(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name), 'Dwoo\Block\Plugin')) {
3401
+					$pluginType += Core::BLOCK_PLUGIN;
3402
+				}
3403
+				$interfaces = class_implements(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name));
3404
+				if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
3405
+					$pluginType |= Core::COMPILABLE_PLUGIN;
3406
+				}
3407
+			} // Class functions plugin
3408
+			elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name)) !== false) {
3409
+				$pluginType = Core::FUNC_PLUGIN + Core::CLASS_PLUGIN;
3410
+				$interfaces = class_implements(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name));
3411
+				if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
3412
+					$pluginType |= Core::COMPILABLE_PLUGIN;
3413
+				}
3414
+			} // Class without namespace
3415
+			elseif (class_exists('Plugin' . Core::toCamelCase($name)) !== false) {
3416
+				$pluginType = Core::CLASS_PLUGIN;
3417
+				$interfaces = class_implements('Plugin' . Core::toCamelCase($name));
3418
+				if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
3419
+					$pluginType |= Core::COMPILABLE_PLUGIN;
3420
+				}
3421
+			} // Function plugin (with/without namespaces)
3422
+			elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase ($name)) !==
3423
+				false || function_exists('Plugin' . Core::toCamelCase($name)) !== false) {
3424
+				$pluginType = Core::FUNC_PLUGIN;
3425
+			} // Function plugin compile (with/without namespaces)
3426
+			elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name) .
3427
+					'Compile') !== false || function_exists('Plugin' . Core::toCamelCase($name) . 'Compile') !==
3428
+				false) {
3429
+				$pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN;
3430
+			} // Helper plugin class compile
3431
+			elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name)) !== false) {
3432
+				$pluginType = Core::CLASS_PLUGIN | Core::COMPILABLE_PLUGIN;
3433
+			} // Helper plugin function compile
3434
+			elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name) . 'Compile') !== false) {
3435
+				$pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN;
3436
+			} // Smarty modifier
3437
+			elseif (function_exists('smarty_modifier_' . $name) !== false) {
3438
+				$pluginType = Core::SMARTY_MODIFIER;
3439
+			} // Smarty function
3440
+			elseif (function_exists('smarty_function_' . $name) !== false) {
3441
+				$pluginType = Core::SMARTY_FUNCTION;
3442
+			} // Smarty block
3443
+			elseif (function_exists('smarty_block_' . $name) !== false) {
3444
+				$pluginType = Core::SMARTY_BLOCK;
3445
+			} // Everything else
3446
+			else {
3447
+				if ($pluginType === - 1) {
3448
+					try {
3449
+						$this->getDwoo()->getLoader()->loadPlugin('Plugin' . Core::toCamelCase($name));
3450
+					}
3451
+					catch (Exception $e) {
3452
+						if (isset($phpFunc)) {
3453
+							$pluginType = Core::NATIVE_PLUGIN;
3454
+						} elseif (is_object($this->getDwoo()->getPluginProxy()) && $this->getDwoo()->getPluginProxy()->handles($name)) {
3455
+							$pluginType = Core::PROXY_PLUGIN;
3456
+							break;
3457
+						} else {
3458
+							throw $e;
3459
+						}
3460
+					}
3461
+				} else {
3462
+					throw new Exception('Plugin "' . $name . '" could not be found, type:' . $pluginType);
3463
+				}
3464
+				++ $pluginType;
3465
+			}
3466
+		}
3467
+
3468
+		if (($pluginType & Core::COMPILABLE_PLUGIN) === 0 && ($pluginType & Core::NATIVE_PLUGIN) === 0 && ($pluginType & Core::PROXY_PLUGIN) === 0) {
3469
+			$this->addUsedPlugin(Core::toCamelCase($name), $pluginType);
3470
+		}
3471
+
3472
+		return $pluginType;
3473
+	}
3474
+
3475
+	/**
3476
+	 * Allows a plugin to load another one at compile time, this will also mark
3477
+	 * it as used by this template so it will be loaded at runtime (which can be
3478
+	 * useful for compiled plugins that rely on another plugin when their compiled
3479
+	 * code runs).
3480
+	 *
3481
+	 * @param string $name the plugin name
3482
+	 *
3483
+	 * @return void
3484
+	 */
3485
+	public function loadPlugin($name)
3486
+	{
3487
+		$this->getPluginType($name);
3488
+	}
3489
+
3490
+	/**
3491
+	 * Runs htmlentities over the matched <?php ?> blocks when the security policy enforces that.
3492
+	 *
3493
+	 * @param array $match matched php block
3494
+	 *
3495
+	 * @return string the htmlentities-converted string
3496
+	 */
3497
+	protected function phpTagEncodingHelper($match)
3498
+	{
3499
+		return htmlspecialchars($match[0]);
3500
+	}
3501
+
3502
+	/**
3503
+	 * Maps the parameters received from the template onto the parameters required by the given callback.
3504
+	 *
3505
+	 * @param array    $params   the array of parameters
3506
+	 * @param callback $callback the function or method to reflect on to find out the required parameters
3507
+	 * @param int      $callType the type of call in the template, 0 = no params, 1 = php-style call, 2 = named
3508
+	 *                           parameters call
3509
+	 * @param array    $map      the parameter map to use, if not provided it will be built from the callback
3510
+	 *
3511
+	 * @return array parameters sorted in the correct order with missing optional parameters filled
3512
+	 * @throws CompilationException
3513
+	 */
3514
+	protected function mapParams(array $params, $callback, $callType = 2, $map = null)
3515
+	{
3516
+		if (!$map) {
3517
+			$map = $this->getParamMap($callback);
3518
+		}
3519
+
3520
+		$paramlist = array();
3521
+
3522
+		// transforms the parameter array from (x=>array('paramname'=>array(values))) to (paramname=>array(values))
3523
+		$ps = array();
3524
+		foreach ($params as $p) {
3525
+			if (is_array($p[1])) {
3526
+				$ps[$p[0]] = $p[1];
3527
+			} else {
3528
+				$ps[] = $p;
3529
+			}
3530
+		}
3531
+
3532
+		// loops over the param map and assigns values from the template or default value for unset optional params
3533
+		foreach ($map as $k => $v){
3534
+			if ($v[0] === '*') {
3535
+				// "rest" array parameter, fill every remaining params in it and then break
3536
+				if (count($ps) === 0) {
3537
+					if ($v[1] === false) {
3538
+						throw new CompilationException(
3539
+							$this, 'Rest argument missing for ' . str_replace(
3540
+								array(
3541
+									Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin',
3542
+								'Compile'
3543
+								), '', (is_array($callback) ? $callback[0] : $callback)
3544
+							)
3545
+						);
3546
+					} else {
3547
+						break;
3548
+					}
3549
+				}
3550
+				$tmp  = array();
3551
+				$tmp2 = array();
3552
+				$tmp3 = array();
3553
+				foreach ($ps as $i => $p) {
3554
+					$tmp[$i]  = $p[0];
3555
+					$tmp2[$i] = $p[1];
3556
+					$tmp3[$i] = isset($p[2]) ? $p[2] : 0;
3557
+					unset($ps[$i]);
3558
+				}
3559
+				$paramlist[$v[0]] = array($tmp, $tmp2, $tmp3);
3560
+				unset($tmp, $tmp2, $i, $p);
3561
+				break;
3562
+			} elseif (isset($ps[$v[0]])) {
3563
+				// parameter is defined as named param
3564
+				$paramlist[$v[0]] = $ps[$v[0]];
3565
+				unset($ps[$v[0]]);
3566
+			} elseif (isset($ps[$k])) {
3567
+				// parameter is defined as ordered param
3568
+				$paramlist[$v[0]] = $ps[$k];
3569
+				unset($ps[$k]);
3570
+			} elseif ($v[1] === false) {
3571
+				// parameter is not defined and not optional, throw error
3572
+				if (is_array($callback)) {
3573
+					if (is_object($callback[0])) {
3574
+						$name = get_class($callback[0]) . '::' . $callback[1];
3575
+					} else {
3576
+						$name = $callback[0];
3577
+					}
3578
+				} else {
3579
+					$name = $callback;
3580
+				}
3581
+
3582
+				throw new CompilationException(
3583
+					$this, 'Argument ' . $k . '/' . $v[0] . ' missing for ' . str_replace(
3584
+						array(
3585
+							Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin',
3586
+						'Compile'
3587
+						), '', $name
3588
+					)
3589
+				);
3590
+			} elseif ($v[2] === null) {
3591
+				// enforce lowercased null if default value is null (php outputs NULL with var export)
3592
+				$paramlist[$v[0]] = array('null', null, self::T_NULL);
3593
+			} else {
3594
+				// outputs default value with var_export
3595
+				$paramlist[$v[0]] = array(var_export($v[2], true), $v[2]);
3596
+			}
3597
+		}
3598
+
3599
+		if (count($ps)) {
3600
+			foreach ($ps as $i => $p) {
3601
+				array_push($paramlist, $p);
3602
+			}
3603
+		}
3604
+
3605
+		return $paramlist;
3606
+	}
3607
+
3608
+	/**
3609
+	 * Returns the parameter map of the given callback, it filters out entries typed as Dwoo and Compiler and turns the
3610
+	 * rest parameter into a "*".
3611
+	 *
3612
+	 * @param callback $callback the function/method to reflect on
3613
+	 *
3614
+	 * @return array processed parameter map
3615
+	 */
3616
+	protected function getParamMap($callback)
3617
+	{
3618
+		if (is_null($callback)) {
3619
+			return array(array('*', true));
3620
+		}
3621
+		if (is_array($callback)) {
3622
+			$ref = new ReflectionMethod($callback[0], $callback[1]);
3623
+		} else {
3624
+			$ref = new ReflectionFunction($callback);
3625
+		}
3626
+
3627
+		$out = array();
3628
+		foreach ($ref->getParameters() as $param) {
3629
+			if (($class = $param->getClass()) !== null && $class->name === 'Dwoo\Core') {
3630
+				continue;
3631
+			}
3632
+			if (($class = $param->getClass()) !== null && $class->name === 'Dwoo\Compiler') {
3633
+				continue;
3634
+			}
3635
+			if ($param->getName() === 'rest' && $param->isArray() === true) {
3636
+				$out[] = array('*', $param->isOptional(), null);
3637
+				continue;
3638
+			}
3639
+			$out[] = array(
3640
+				$param->getName(),
3641
+				$param->isOptional(),
3642
+				$param->isOptional() ? $param->getDefaultValue() : null
3643
+			);
3644
+		}
3645
+
3646
+		return $out;
3647
+	}
3648
+
3649
+	/**
3650
+	 * Returns a default instance of this compiler, used by default by all Dwoo templates that do not have a
3651
+	 * specific compiler assigned and when you do not override the default compiler factory function.
3652
+	 *
3653
+	 * @see    Core::setDefaultCompilerFactory()
3654
+	 * @return Compiler
3655
+	 */
3656
+	public static function compilerFactory()
3657
+	{
3658
+		if (self::$instance === null) {
3659
+			self::$instance = new self();
3660
+		}
3661
+
3662
+		return self::$instance;
3663
+	}
3664 3664
 }
Please login to merge, or discard this patch.
Spacing   +380 added lines, -382 removed lines patch added patch discarded remove patch
@@ -294,7 +294,7 @@  discard block
 block discarded – undo
294 294
      */
295 295
     public function setNestedCommentsHandling($allow = true)
296 296
     {
297
-        $this->allowNestedComments = (bool)$allow;
297
+        $this->allowNestedComments = (bool) $allow;
298 298
     }
299 299
 
300 300
     /**
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
      */
320 320
     public function setLooseOpeningHandling($allow = false)
321 321
     {
322
-        $this->allowLooseOpenings = (bool)$allow;
322
+        $this->allowLooseOpenings = (bool) $allow;
323 323
     }
324 324
 
325 325
     /**
@@ -344,7 +344,7 @@  discard block
 block discarded – undo
344 344
      */
345 345
     public function setAutoEscape($enabled)
346 346
     {
347
-        $this->autoEscape = (bool)$enabled;
347
+        $this->autoEscape = (bool) $enabled;
348 348
     }
349 349
 
350 350
     /**
@@ -371,7 +371,7 @@  discard block
 block discarded – undo
371 371
     {
372 372
         if ($autoload) {
373 373
             $name  = str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', Core::toCamelCase($callback));
374
-            $class = Core::NAMESPACE_PLUGINS_PROCESSORS . $name;
374
+            $class = Core::NAMESPACE_PLUGINS_PROCESSORS.$name;
375 375
 
376 376
             if (class_exists($class)) {
377 377
                 $callback = array(new $class($this), 'process');
@@ -396,12 +396,12 @@  discard block
 block discarded – undo
396 396
     {
397 397
         if (($index = array_search($callback, $this->processors['pre'], true)) !== false) {
398 398
             unset($this->processors['pre'][$index]);
399
-        } elseif (($index = array_search(Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '',
399
+        } elseif (($index = array_search(Core::NAMESPACE_PLUGINS_PROCESSORS.str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '',
400 400
                     $callback),
401 401
                 $this->processors['pre'], true)) !== false) {
402 402
             unset($this->processors['pre'][$index]);
403 403
         } else {
404
-            $class = Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $callback);
404
+            $class = Core::NAMESPACE_PLUGINS_PROCESSORS.str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $callback);
405 405
             foreach ($this->processors['pre'] as $index => $proc) {
406 406
                 if (is_array($proc) && ($proc[0] instanceof $class) || (isset($proc['class']) && $proc['class'] == $class)) {
407 407
                     unset($this->processors['pre'][$index]);
@@ -424,7 +424,7 @@  discard block
 block discarded – undo
424 424
     {
425 425
         if ($autoload) {
426 426
             $name  = str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $callback);
427
-            $class = Core::NAMESPACE_PLUGINS_PROCESSORS . Core::toCamelCase($name);
427
+            $class = Core::NAMESPACE_PLUGINS_PROCESSORS.Core::toCamelCase($name);
428 428
 
429 429
             if (class_exists($class)) {
430 430
                 $callback = array(new $class($this), 'process');
@@ -449,12 +449,12 @@  discard block
 block discarded – undo
449 449
     {
450 450
         if (($index = array_search($callback, $this->processors['post'], true)) !== false) {
451 451
             unset($this->processors['post'][$index]);
452
-        } elseif (($index = array_search(Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '',
452
+        } elseif (($index = array_search(Core::NAMESPACE_PLUGINS_PROCESSORS.str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '',
453 453
                     $callback),
454 454
                 $this->processors['post'], true)) !== false) {
455 455
             unset($this->processors['post'][$index]);
456 456
         } else {
457
-            $class = Core::NAMESPACE_PLUGINS_PROCESSORS . str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $callback);
457
+            $class = Core::NAMESPACE_PLUGINS_PROCESSORS.str_replace(Core::NAMESPACE_PLUGINS_PROCESSORS, '', $callback);
458 458
             foreach ($this->processors['post'] as $index => $proc) {
459 459
                 if (is_array($proc) && ($proc[0] instanceof $class) || (isset($proc['class']) && $proc['class'] == $class)) {
460 460
                     unset($this->processors['post'][$index]);
@@ -480,7 +480,7 @@  discard block
 block discarded – undo
480 480
                 $this->getDwoo()->getLoader()->loadPlugin($name);
481 481
             }
482 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');
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 484
             }
485 485
         }
486 486
 
@@ -666,7 +666,7 @@  discard block
 block discarded – undo
666 666
     public function setTemplateSource($newSource, $fromPointer = false)
667 667
     {
668 668
         if ($fromPointer === true) {
669
-            $this->templateSource = substr($this->templateSource, 0, $this->pointer) . $newSource;
669
+            $this->templateSource = substr($this->templateSource, 0, $this->pointer).$newSource;
670 670
         } else {
671 671
             $this->templateSource = $newSource;
672 672
         }
@@ -737,11 +737,11 @@  discard block
 block discarded – undo
737 737
 
738 738
                 if ($this->debug) {
739 739
                     echo "\n";
740
-                    echo 'COMPILER INIT' . "\n";
740
+                    echo 'COMPILER INIT'."\n";
741 741
                 }
742 742
 
743 743
                 if ($this->debug) {
744
-                    echo 'PROCESSING PREPROCESSORS (' . count($this->processors['pre']) . ')' . "\n";
744
+                    echo 'PROCESSING PREPROCESSORS ('.count($this->processors['pre']).')'."\n";
745 745
                 }
746 746
 
747 747
                 // runs preprocessors
@@ -785,23 +785,23 @@  discard block
 block discarded – undo
785 785
             if ($pos === false) {
786 786
                 $this->push(substr($tpl, $ptr), 0);
787 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)) {
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 793
                     throw new CompilationException($this, 'The {literal} blocks must be closed explicitly with {/literal}');
794 794
                 }
795 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]);
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 798
             } else {
799
-                if (substr($tpl, $pos - 2, 1) === '\\' && substr($tpl, $pos - 1, 1) === '\\') {
800
-                    $this->push(substr($tpl, $ptr, $pos - $ptr - 1));
799
+                if (substr($tpl, $pos-2, 1) === '\\' && substr($tpl, $pos-1, 1) === '\\') {
800
+                    $this->push(substr($tpl, $ptr, $pos-$ptr-1));
801 801
                     $ptr = $pos;
802 802
                 }
803 803
 
804
-                $this->push(substr($tpl, $ptr, $pos - $ptr));
804
+                $this->push(substr($tpl, $ptr, $pos-$ptr));
805 805
                 $ptr = $pos;
806 806
 
807 807
                 $pos += strlen($this->ld);
@@ -819,7 +819,7 @@  discard block
 block discarded – undo
819 819
 
820 820
                 // check that there is an end tag present
821 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) . '"');
822
+                    throw new CompilationException($this, 'A template tag was not closed, started with "'.substr($tpl, $ptr, 30).'"');
823 823
                 }
824 824
 
825 825
                 $ptr += strlen($this->ld);
@@ -833,7 +833,7 @@  discard block
 block discarded – undo
833 833
                         continue 2;
834 834
                     }
835 835
 
836
-                    $len = $subptr - $ptr;
836
+                    $len = $subptr-$ptr;
837 837
                     $this->push($parsed, substr_count(substr($tpl, $ptr, $len), "\n"));
838 838
                     $ptr += $len;
839 839
 
@@ -847,7 +847,7 @@  discard block
 block discarded – undo
847 847
         $compiled .= $this->removeBlock('TopLevelBlock');
848 848
 
849 849
         if ($this->debug) {
850
-            echo 'PROCESSING POSTPROCESSORS' . "\n";
850
+            echo 'PROCESSING POSTPROCESSORS'."\n";
851 851
         }
852 852
 
853 853
         foreach ($this->processors['post'] as $postProc) {
@@ -863,7 +863,7 @@  discard block
 block discarded – undo
863 863
         unset($postProc);
864 864
 
865 865
         if ($this->debug) {
866
-            echo 'COMPILATION COMPLETE : MEM USAGE : ' . memory_get_usage() . "\n";
866
+            echo 'COMPILATION COMPLETE : MEM USAGE : '.memory_get_usage()."\n";
867 867
         }
868 868
 
869 869
         $output = "<?php\n/* template head */\n";
@@ -876,30 +876,30 @@  discard block
 block discarded – undo
876 876
 
877 877
             switch ($type) {
878 878
                 case Core::CLASS_PLUGIN:
879
-                case Core::CLASS_PLUGIN + Core::BLOCK_PLUGIN:
880
-                    if (class_exists('Plugin' . $plugin) !== false) {
881
-                        $output .= "if (class_exists('" . "Plugin" . $plugin . "')===false)".
879
+                case Core::CLASS_PLUGIN+Core::BLOCK_PLUGIN:
880
+                    if (class_exists('Plugin'.$plugin) !== false) {
881
+                        $output .= "if (class_exists('"."Plugin".$plugin."')===false)".
882 882
                         "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
883 883
                     } else {
884
-                        $output .= "if (class_exists('" . Core::NAMESPACE_PLUGINS_BLOCKS . "Plugin" . $plugin . "')===false)".
884
+                        $output .= "if (class_exists('".Core::NAMESPACE_PLUGINS_BLOCKS."Plugin".$plugin."')===false)".
885 885
                         "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
886 886
                     }
887 887
                     break;
888
-                case Core::CLASS_PLUGIN + Core::FUNC_PLUGIN:
889
-                    if (class_exists('Plugin' . $plugin) !== false) {
890
-                        $output .= "if (class_exists('" . "Plugin" . $plugin . "')===false)".
888
+                case Core::CLASS_PLUGIN+Core::FUNC_PLUGIN:
889
+                    if (class_exists('Plugin'.$plugin) !== false) {
890
+                        $output .= "if (class_exists('"."Plugin".$plugin."')===false)".
891 891
                             "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
892 892
                     } else {
893
-                        $output .= "if (class_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)".
893
+                        $output .= "if (class_exists('".Core::NAMESPACE_PLUGINS_FUNCTIONS."Plugin".$plugin."')===false)".
894 894
                             "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
895 895
                     }
896 896
                     break;
897 897
                 case Core::FUNC_PLUGIN:
898
-                    if (function_exists('Plugin' . $plugin) !== false) {
899
-                        $output .= "if (function_exists('" . "Plugin" . $plugin . "')===false)".
898
+                    if (function_exists('Plugin'.$plugin) !== false) {
899
+                        $output .= "if (function_exists('"."Plugin".$plugin."')===false)".
900 900
                         "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
901 901
                     } else {
902
-                        $output .= "if (function_exists('" . Core::NAMESPACE_PLUGINS_FUNCTIONS . "Plugin" . $plugin . "')===false)".
902
+                        $output .= "if (function_exists('".Core::NAMESPACE_PLUGINS_FUNCTIONS."Plugin".$plugin."')===false)".
903 903
                         "\n\t\$this->getLoader()->loadPlugin('Plugin$plugin');\n";
904 904
                     }
905 905
                     break;
@@ -919,7 +919,7 @@  discard block
 block discarded – undo
919 919
                     $output .= $this->getDwoo()->getPluginProxy()->getLoader($plugin);
920 920
                     break;
921 921
                 default:
922
-                    throw new CompilationException($this, 'Type error for ' . $plugin . ' with type' . $type);
922
+                    throw new CompilationException($this, 'Type error for '.$plugin.' with type'.$type);
923 923
             }
924 924
         }
925 925
 
@@ -930,30 +930,30 @@  discard block
 block discarded – undo
930 930
         }
931 931
         foreach ($this->templatePlugins as $function) {
932 932
             if (isset($function['called']) && $function['called'] === true) {
933
-                $output .= $function['body'] . PHP_EOL;
933
+                $output .= $function['body'].PHP_EOL;
934 934
             }
935 935
         }
936 936
 
937
-        $output .= $compiled . "\n?>";
937
+        $output .= $compiled."\n?>";
938 938
 
939
-        $output = preg_replace('/(?<!;|\}|\*\/|\n|\{)(\s*' . preg_quote(self::PHP_CLOSE, '/') . preg_quote(self::PHP_OPEN, '/') . ')/', ";\n", $output);
940
-        $output = str_replace(self::PHP_CLOSE . self::PHP_OPEN, "\n", $output);
939
+        $output = preg_replace('/(?<!;|\}|\*\/|\n|\{)(\s*'.preg_quote(self::PHP_CLOSE, '/').preg_quote(self::PHP_OPEN, '/').')/', ";\n", $output);
940
+        $output = str_replace(self::PHP_CLOSE.self::PHP_OPEN, "\n", $output);
941 941
 
942 942
         // handle <?xml tag at the beginning
943 943
         $output = preg_replace('#(/\* template body \*/ \?>\s*)<\?xml#is', '$1<?php echo \'<?xml\'; ?>', $output);
944 944
 
945 945
         // add another line break after PHP closing tags that have a line break following,
946 946
         // as we do not know whether it's intended, and PHP will strip it otherwise
947
-        $output = preg_replace('/(?<!"|<\?xml)\s*\?>\n/', '$0' . "\n", $output);
947
+        $output = preg_replace('/(?<!"|<\?xml)\s*\?>\n/', '$0'."\n", $output);
948 948
 
949 949
         if ($this->debug) {
950
-            echo '=============================================================================================' . "\n";
950
+            echo '============================================================================================='."\n";
951 951
             $lines = preg_split('{\r\n|\n|<br />}', $output);
952 952
             array_shift($lines);
953 953
             foreach ($lines as $i => $line) {
954
-                echo ($i + 1) . '. ' . $line . "\r\n";
954
+                echo ($i+1).'. '.$line."\r\n";
955 955
             }
956
-            echo '=============================================================================================' . "\n";
956
+            echo '============================================================================================='."\n";
957 957
         }
958 958
 
959 959
         $this->template = $this->dwoo = null;
@@ -970,13 +970,13 @@  discard block
 block discarded – undo
970 970
     protected function resolveSubTemplateDependencies($function)
971 971
     {
972 972
         if ($this->debug) {
973
-            echo 'Compiler::' . __FUNCTION__ . "\n";
973
+            echo 'Compiler::'.__FUNCTION__."\n";
974 974
         }
975 975
 
976 976
         $body = $this->templatePlugins[$function]['body'];
977 977
         foreach ($this->templatePlugins as $func => $attr) {
978
-            if ($func !== $function && !isset($attr['called']) && strpos($body, Core::NAMESPACE_PLUGINS_FUNCTIONS .
979
-            'Plugin' . Core::toCamelCase($func)) !== false) {
978
+            if ($func !== $function && !isset($attr['called']) && strpos($body, Core::NAMESPACE_PLUGINS_FUNCTIONS.
979
+            'Plugin'.Core::toCamelCase($func)) !== false) {
980 980
                 $this->templatePlugins[$func]['called'] = true;
981 981
                 $this->resolveSubTemplateDependencies($func);
982 982
             }
@@ -1000,14 +1000,14 @@  discard block
 block discarded – undo
1000 1000
 
1001 1001
         if ($this->curBlock['buffer'] === null && count($this->stack) > 1) {
1002 1002
             // buffer is not initialized yet (the block has just been created)
1003
-            $this->stack[count($this->stack) - 2]['buffer'] .= (string)$content;
1003
+            $this->stack[count($this->stack)-2]['buffer'] .= (string) $content;
1004 1004
             $this->curBlock['buffer'] = '';
1005 1005
         } else {
1006 1006
             if (!isset($this->curBlock['buffer'])) {
1007 1007
                 throw new CompilationException($this, 'The template has been closed too early, you probably have an extra block-closing tag somewhere');
1008 1008
             }
1009 1009
             // append current content to current block's buffer
1010
-            $this->curBlock['buffer'] .= (string)$content;
1010
+            $this->curBlock['buffer'] .= (string) $content;
1011 1011
         }
1012 1012
         $this->line += $lineCount;
1013 1013
     }
@@ -1077,23 +1077,23 @@  discard block
 block discarded – undo
1077 1077
     public function addBlock($type, array $params, $paramtype)
1078 1078
     {
1079 1079
         if ($this->debug) {
1080
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1080
+            echo 'Compiler::'.__FUNCTION__."\n";
1081 1081
         }
1082 1082
 
1083
-        $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type);
1083
+        $class = Core::NAMESPACE_PLUGINS_BLOCKS.'Plugin'.Core::toCamelCase($type);
1084 1084
         if (class_exists($class) === false) {
1085 1085
             $this->getDwoo()->getLoader()->loadPlugin($type);
1086 1086
         }
1087 1087
         $params = $this->mapParams($params, array($class, 'init'), $paramtype);
1088 1088
 
1089
-        $this->stack[]  = array(
1089
+        $this->stack[] = array(
1090 1090
             'type'   => $type,
1091 1091
             'params' => $params,
1092 1092
             'custom' => false,
1093 1093
             'class'  => $class,
1094 1094
             'buffer' => null
1095 1095
         );
1096
-        $this->curBlock = &$this->stack[count($this->stack) - 1];
1096
+        $this->curBlock = &$this->stack[count($this->stack)-1];
1097 1097
 
1098 1098
         return call_user_func(array($class, 'preProcessing'), $this, $params, '', '', $type);
1099 1099
     }
@@ -1118,14 +1118,14 @@  discard block
 block discarded – undo
1118 1118
 
1119 1119
         $params = $this->mapParams($params, array($class, 'init'), $paramtype);
1120 1120
 
1121
-        $this->stack[]  = array(
1121
+        $this->stack[] = array(
1122 1122
             'type'   => $type,
1123 1123
             'params' => $params,
1124 1124
             'custom' => true,
1125 1125
             'class'  => $class,
1126 1126
             'buffer' => null
1127 1127
         );
1128
-        $this->curBlock = &$this->stack[count($this->stack) - 1];
1128
+        $this->curBlock = &$this->stack[count($this->stack)-1];
1129 1129
 
1130 1130
         return call_user_func(array($class, 'preProcessing'), $this, $params, '', '', $type);
1131 1131
     }
@@ -1140,21 +1140,21 @@  discard block
 block discarded – undo
1140 1140
     public function injectBlock($type, array $params)
1141 1141
     {
1142 1142
         if ($this->debug) {
1143
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1143
+            echo 'Compiler::'.__FUNCTION__."\n";
1144 1144
         }
1145 1145
 
1146
-        $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($type);
1146
+        $class = Core::NAMESPACE_PLUGINS_BLOCKS.'Plugin'.Core::toCamelCase($type);
1147 1147
         if (class_exists($class) === false) {
1148 1148
             $this->getDwoo()->getLoader()->loadPlugin($type);
1149 1149
         }
1150
-        $this->stack[]  = array(
1150
+        $this->stack[] = array(
1151 1151
             'type'   => $type,
1152 1152
             'params' => $params,
1153 1153
             'custom' => false,
1154 1154
             'class'  => $class,
1155 1155
             'buffer' => null
1156 1156
         );
1157
-        $this->curBlock = &$this->stack[count($this->stack) - 1];
1157
+        $this->curBlock = &$this->stack[count($this->stack)-1];
1158 1158
     }
1159 1159
 
1160 1160
     /**
@@ -1169,7 +1169,7 @@  discard block
 block discarded – undo
1169 1169
     public function removeBlock($type)
1170 1170
     {
1171 1171
         if ($this->debug) {
1172
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1172
+            echo 'Compiler::'.__FUNCTION__."\n";
1173 1173
         }
1174 1174
 
1175 1175
         $output = '';
@@ -1183,10 +1183,10 @@  discard block
 block discarded – undo
1183 1183
                 if ($top['custom']) {
1184 1184
                     $class = $top['class'];
1185 1185
                 } else {
1186
-                    $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($top['type']);
1186
+                    $class = Core::NAMESPACE_PLUGINS_BLOCKS.'Plugin'.Core::toCamelCase($top['type']);
1187 1187
                 }
1188 1188
                 if (count($this->stack)) {
1189
-                    $this->curBlock = &$this->stack[count($this->stack) - 1];
1189
+                    $this->curBlock = &$this->stack[count($this->stack)-1];
1190 1190
                     $this->push(call_user_func(array(
1191 1191
                         $class,
1192 1192
                         'postProcessing'
@@ -1207,7 +1207,7 @@  discard block
 block discarded – undo
1207 1207
                 }
1208 1208
             }
1209 1209
 
1210
-            throw new CompilationException($this, 'Syntax malformation, a block of type "' . $type . '" was closed but was not opened');
1210
+            throw new CompilationException($this, 'Syntax malformation, a block of type "'.$type.'" was closed but was not opened');
1211 1211
         }
1212 1212
 
1213 1213
         return $output;
@@ -1245,7 +1245,7 @@  discard block
 block discarded – undo
1245 1245
             }
1246 1246
         }
1247 1247
 
1248
-        throw new CompilationException($this, 'A parent block of type "' . $type . '" is required and can not be found');
1248
+        throw new CompilationException($this, 'A parent block of type "'.$type.'" is required and can not be found');
1249 1249
     }
1250 1250
 
1251 1251
     /**
@@ -1268,7 +1268,7 @@  discard block
 block discarded – undo
1268 1268
     public function removeTopBlock()
1269 1269
     {
1270 1270
         if ($this->debug) {
1271
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1271
+            echo 'Compiler::'.__FUNCTION__."\n";
1272 1272
         }
1273 1273
 
1274 1274
         $o = array_pop($this->stack);
@@ -1278,10 +1278,10 @@  discard block
 block discarded – undo
1278 1278
         if ($o['custom']) {
1279 1279
             $class = $o['class'];
1280 1280
         } else {
1281
-            $class = Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($o['type']);
1281
+            $class = Core::NAMESPACE_PLUGINS_BLOCKS.'Plugin'.Core::toCamelCase($o['type']);
1282 1282
         }
1283 1283
 
1284
-        $this->curBlock = &$this->stack[count($this->stack) - 1];
1284
+        $this->curBlock = &$this->stack[count($this->stack)-1];
1285 1285
 
1286 1286
         return call_user_func(array($class, 'postProcessing'), $this, $o['params'], '', '', $o['buffer']);
1287 1287
     }
@@ -1360,7 +1360,7 @@  discard block
 block discarded – undo
1360 1360
     protected function parse($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
1361 1361
     {
1362 1362
         if ($this->debug) {
1363
-            echo 'Compiler::' . __FUNCTION__ . "\n";
1363
+            echo 'Compiler::'.__FUNCTION__."\n";
1364 1364
         }
1365 1365
 
1366 1366
         if ($to === null) {
@@ -1377,14 +1377,14 @@  discard block
 block discarded – undo
1377 1377
                 // end template tag
1378 1378
                 $pointer += strlen($this->rd);
1379 1379
                 if ($this->debug) {
1380
-                    echo 'TEMPLATE PARSING ENDED' . "\n";
1380
+                    echo 'TEMPLATE PARSING ENDED'."\n";
1381 1381
                 }
1382 1382
 
1383 1383
                 return false;
1384 1384
             }
1385
-            ++ $from;
1385
+            ++$from;
1386 1386
             if ($pointer !== null) {
1387
-                ++ $pointer;
1387
+                ++$pointer;
1388 1388
             }
1389 1389
             if ($from >= $to) {
1390 1390
                 if (is_array($parsingParams)) {
@@ -1396,22 +1396,22 @@  discard block
 block discarded – undo
1396 1396
             $first = $in[$from];
1397 1397
         }
1398 1398
 
1399
-        $substr = substr($in, $from, $to - $from);
1399
+        $substr = substr($in, $from, $to-$from);
1400 1400
 
1401 1401
         if ($this->debug) {
1402
-            echo 'PARSE CALL : PARSING "' . htmlentities(substr($in, $from, min($to - $from, 50))) . (($to - $from) > 50 ? '...' : '') . '" @ ' . $from . ':' . $to . ' in ' . $curBlock . ' : pointer=' . $pointer . "\n";
1402
+            echo 'PARSE CALL : PARSING "'.htmlentities(substr($in, $from, min($to-$from, 50))).(($to-$from) > 50 ? '...' : '').'" @ '.$from.':'.$to.' in '.$curBlock.' : pointer='.$pointer."\n";
1403 1403
         }
1404 1404
         $parsed = '';
1405 1405
 
1406 1406
         if ($curBlock === 'root' && $first === '*') {
1407 1407
             $src      = $this->getTemplateSource();
1408
-            $startpos = $this->getPointer() - strlen($this->ld);
1408
+            $startpos = $this->getPointer()-strlen($this->ld);
1409 1409
             if (substr($src, $startpos, strlen($this->ld)) === $this->ld) {
1410 1410
                 if ($startpos > 0) {
1411 1411
                     do {
1412 1412
                         $char = substr($src, -- $startpos, 1);
1413 1413
                         if ($char == "\n") {
1414
-                            ++ $startpos;
1414
+                            ++$startpos;
1415 1415
                             $whitespaceStart = true;
1416 1416
                             break;
1417 1417
                         }
@@ -1422,12 +1422,12 @@  discard block
 block discarded – undo
1422 1422
                 if (!isset($whitespaceStart)) {
1423 1423
                     $startpos = $this->getPointer();
1424 1424
                 } else {
1425
-                    $pointer -= $this->getPointer() - $startpos;
1425
+                    $pointer -= $this->getPointer()-$startpos;
1426 1426
                 }
1427 1427
 
1428
-                if ($this->allowNestedComments && strpos($src, $this->ld . '*', $this->getPointer()) !== false) {
1429
-                    $comOpen  = $this->ld . '*';
1430
-                    $comClose = '*' . $this->rd;
1428
+                if ($this->allowNestedComments && strpos($src, $this->ld.'*', $this->getPointer()) !== false) {
1429
+                    $comOpen  = $this->ld.'*';
1430
+                    $comClose = '*'.$this->rd;
1431 1431
                     $level    = 1;
1432 1432
                     $ptr      = $this->getPointer();
1433 1433
 
@@ -1437,33 +1437,33 @@  discard block
 block discarded – undo
1437 1437
 
1438 1438
                         if ($open !== false && $close !== false) {
1439 1439
                             if ($open < $close) {
1440
-                                $ptr = $open + strlen($comOpen);
1441
-                                ++ $level;
1440
+                                $ptr = $open+strlen($comOpen);
1441
+                                ++$level;
1442 1442
                             } else {
1443
-                                $ptr = $close + strlen($comClose);
1444
-                                -- $level;
1443
+                                $ptr = $close+strlen($comClose);
1444
+                                --$level;
1445 1445
                             }
1446 1446
                         } elseif ($open !== false) {
1447
-                            $ptr = $open + strlen($comOpen);
1448
-                            ++ $level;
1447
+                            $ptr = $open+strlen($comOpen);
1448
+                            ++$level;
1449 1449
                         } elseif ($close !== false) {
1450
-                            $ptr = $close + strlen($comClose);
1451
-                            -- $level;
1450
+                            $ptr = $close+strlen($comClose);
1451
+                            --$level;
1452 1452
                         } else {
1453 1453
                             $ptr = strlen($src);
1454 1454
                         }
1455 1455
                     }
1456
-                    $endpos = $ptr - strlen('*' . $this->rd);
1456
+                    $endpos = $ptr-strlen('*'.$this->rd);
1457 1457
                 } else {
1458
-                    $endpos = strpos($src, '*' . $this->rd, $startpos);
1458
+                    $endpos = strpos($src, '*'.$this->rd, $startpos);
1459 1459
                     if ($endpos == false) {
1460 1460
                         throw new CompilationException($this, 'Un-ended comment');
1461 1461
                     }
1462 1462
                 }
1463
-                $pointer += $endpos - $startpos + strlen('*' . $this->rd);
1464
-                if (isset($whitespaceStart) && preg_match('#^[\t ]*\r?\n#', substr($src, $endpos + strlen('*' . $this->rd)), $m)) {
1463
+                $pointer += $endpos-$startpos+strlen('*'.$this->rd);
1464
+                if (isset($whitespaceStart) && preg_match('#^[\t ]*\r?\n#', substr($src, $endpos+strlen('*'.$this->rd)), $m)) {
1465 1465
                     $pointer += strlen($m[0]);
1466
-                    $this->curBlock['buffer'] = substr($this->curBlock['buffer'], 0, strlen($this->curBlock['buffer']) - ($this->getPointer() - $startpos - strlen($this->ld)));
1466
+                    $this->curBlock['buffer'] = substr($this->curBlock['buffer'], 0, strlen($this->curBlock['buffer'])-($this->getPointer()-$startpos-strlen($this->ld)));
1467 1467
                 }
1468 1468
 
1469 1469
                 return false;
@@ -1480,20 +1480,20 @@  discard block
 block discarded – undo
1480 1480
         } elseif (($first === '"' || $first === "'") && !(is_array($parsingParams) && preg_match('#^([\'"])[a-z0-9_]+\1\s*=>?(?:\s+|[^=])#i', $substr))) {
1481 1481
             // string
1482 1482
             $out = $this->parseString($in, $from, $to, $parsingParams, $curBlock, $pointer);
1483
-        } 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)) {
1483
+        } 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)) {
1484 1484
             // func
1485 1485
             $out    = $this->parseFunction($in, $from, $to, $parsingParams, $curBlock, $pointer);
1486 1486
             $parsed = 'func';
1487 1487
         } elseif ($first === ';') {
1488 1488
             // instruction end
1489 1489
             if ($this->debug) {
1490
-                echo 'END OF INSTRUCTION' . "\n";
1490
+                echo 'END OF INSTRUCTION'."\n";
1491 1491
             }
1492 1492
             if ($pointer !== null) {
1493
-                ++ $pointer;
1493
+                ++$pointer;
1494 1494
             }
1495 1495
 
1496
-            return $this->parse($in, $from + 1, $to, false, 'root', $pointer);
1496
+            return $this->parse($in, $from+1, $to, false, 'root', $pointer);
1497 1497
         } elseif ($curBlock === 'root' && preg_match('#^/([a-z_][a-z0-9_]*)?#i', $substr, $match)) {
1498 1498
             // close block
1499 1499
             if (!empty($match[1]) && $match[1] == 'else') {
@@ -1510,13 +1510,13 @@  discard block
 block discarded – undo
1510 1510
                     $pointer -= strlen($match[0]);
1511 1511
                 }
1512 1512
                 if ($this->debug) {
1513
-                    echo 'TOP BLOCK CLOSED' . "\n";
1513
+                    echo 'TOP BLOCK CLOSED'."\n";
1514 1514
                 }
1515 1515
 
1516 1516
                 return $this->removeTopBlock();
1517 1517
             } else {
1518 1518
                 if ($this->debug) {
1519
-                    echo 'BLOCK OF TYPE ' . $match[1] . ' CLOSED' . "\n";
1519
+                    echo 'BLOCK OF TYPE '.$match[1].' CLOSED'."\n";
1520 1520
                 }
1521 1521
 
1522 1522
                 return $this->removeBlock($match[1]);
@@ -1524,19 +1524,19 @@  discard block
 block discarded – undo
1524 1524
         } elseif ($curBlock === 'root' && substr($substr, 0, strlen($this->rd)) === $this->rd) {
1525 1525
             // end template tag
1526 1526
             if ($this->debug) {
1527
-                echo 'TAG PARSING ENDED' . "\n";
1527
+                echo 'TAG PARSING ENDED'."\n";
1528 1528
             }
1529 1529
             $pointer += strlen($this->rd);
1530 1530
 
1531 1531
             return false;
1532
-        } elseif (is_array($parsingParams) && preg_match('#^(([\'"]?)[a-z0-9_]+\2\s*=' . ($curBlock === 'array' ? '>?' : '') . ')(?:\s+|[^=]).*#i', $substr, $match)) {
1532
+        } elseif (is_array($parsingParams) && preg_match('#^(([\'"]?)[a-z0-9_]+\2\s*='.($curBlock === 'array' ? '>?' : '').')(?:\s+|[^=]).*#i', $substr, $match)) {
1533 1533
             // named parameter
1534 1534
             if ($this->debug) {
1535
-                echo 'NAMED PARAM FOUND' . "\n";
1535
+                echo 'NAMED PARAM FOUND'."\n";
1536 1536
             }
1537 1537
             $len = strlen($match[1]);
1538
-            while (substr($in, $from + $len, 1) === ' ') {
1539
-                ++ $len;
1538
+            while (substr($in, $from+$len, 1) === ' ') {
1539
+                ++$len;
1540 1540
             }
1541 1541
             if ($pointer !== null) {
1542 1542
                 $pointer += $len;
@@ -1544,7 +1544,7 @@  discard block
 block discarded – undo
1544 1544
 
1545 1545
             $output = array(
1546 1546
                 trim($match[1], " \t\r\n=>'\""),
1547
-                $this->parse($in, $from + $len, $to, false, 'namedparam', $pointer)
1547
+                $this->parse($in, $from+$len, $to, false, 'namedparam', $pointer)
1548 1548
             );
1549 1549
 
1550 1550
             $parsingParams[] = $output;
@@ -1565,31 +1565,31 @@  discard block
 block discarded – undo
1565 1565
             $out = $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
1566 1566
         } else {
1567 1567
             // parse error
1568
-            throw new CompilationException($this, 'Parse error in "' . substr($in, $from, $to - $from) . '"');
1568
+            throw new CompilationException($this, 'Parse error in "'.substr($in, $from, $to-$from).'"');
1569 1569
         }
1570 1570
 
1571 1571
         if (empty($out)) {
1572 1572
             return '';
1573 1573
         }
1574 1574
 
1575
-        $substr = substr($in, $pointer, $to - $pointer);
1575
+        $substr = substr($in, $pointer, $to-$pointer);
1576 1576
 
1577 1577
         // var parsed, check if any var-extension applies
1578 1578
         if ($parsed === 'var') {
1579 1579
             if (preg_match('#^\s*([/%+*-])\s*([a-z0-9]|\$)#i', $substr, $match)) {
1580 1580
                 if ($this->debug) {
1581
-                    echo 'PARSING POST-VAR EXPRESSION ' . $substr . "\n";
1581
+                    echo 'PARSING POST-VAR EXPRESSION '.$substr."\n";
1582 1582
                 }
1583 1583
                 // parse expressions
1584
-                $pointer += strlen($match[0]) - 1;
1584
+                $pointer += strlen($match[0])-1;
1585 1585
                 if (is_array($parsingParams)) {
1586 1586
                     if ($match[2] == '$') {
1587 1587
                         $expr = $this->parseVar($in, $pointer, $to, array(), $curBlock, $pointer);
1588 1588
                     } else {
1589 1589
                         $expr = $this->parse($in, $pointer, $to, array(), 'expression', $pointer);
1590 1590
                     }
1591
-                    $out[count($out) - 1][0] .= $match[1] . $expr[0][0];
1592
-                    $out[count($out) - 1][1] .= $match[1] . $expr[0][1];
1591
+                    $out[count($out)-1][0] .= $match[1].$expr[0][0];
1592
+                    $out[count($out)-1][1] .= $match[1].$expr[0][1];
1593 1593
                 } else {
1594 1594
                     if ($match[2] == '$') {
1595 1595
                         $expr = $this->parseVar($in, $pointer, $to, false, $curBlock, $pointer);
@@ -1597,26 +1597,26 @@  discard block
 block discarded – undo
1597 1597
                         $expr = $this->parse($in, $pointer, $to, false, 'expression', $pointer);
1598 1598
                     }
1599 1599
                     if (is_array($out) && is_array($expr)) {
1600
-                        $out[0] .= $match[1] . $expr[0];
1601
-                        $out[1] .= $match[1] . $expr[1];
1600
+                        $out[0] .= $match[1].$expr[0];
1601
+                        $out[1] .= $match[1].$expr[1];
1602 1602
                     } elseif (is_array($out)) {
1603
-                        $out[0] .= $match[1] . $expr;
1604
-                        $out[1] .= $match[1] . $expr;
1603
+                        $out[0] .= $match[1].$expr;
1604
+                        $out[1] .= $match[1].$expr;
1605 1605
                     } elseif (is_array($expr)) {
1606
-                        $out .= $match[1] . $expr[0];
1606
+                        $out .= $match[1].$expr[0];
1607 1607
                     } else {
1608
-                        $out .= $match[1] . $expr;
1608
+                        $out .= $match[1].$expr;
1609 1609
                     }
1610 1610
                 }
1611 1611
             } elseif ($curBlock === 'root' && preg_match('#^(\s*(?:[+/*%-.]=|=|\+\+|--)\s*)(.*)#s', $substr, $match)) {
1612 1612
                 if ($this->debug) {
1613
-                    echo 'PARSING POST-VAR ASSIGNMENT ' . $substr . "\n";
1613
+                    echo 'PARSING POST-VAR ASSIGNMENT '.$substr."\n";
1614 1614
                 }
1615 1615
                 // parse assignment
1616 1616
                 $value    = $match[2];
1617 1617
                 $operator = trim($match[1]);
1618 1618
                 if (substr($value, 0, 1) == '=') {
1619
-                    throw new CompilationException($this, 'Unexpected "=" in <em>' . $substr . '</em>');
1619
+                    throw new CompilationException($this, 'Unexpected "=" in <em>'.$substr.'</em>');
1620 1620
                 }
1621 1621
 
1622 1622
                 if ($pointer !== null) {
@@ -1637,7 +1637,7 @@  discard block
 block discarded – undo
1637 1637
                         throw new CompilationException($this, 'Assignments require the "if" plugin to be accessible');
1638 1638
                     }
1639 1639
 
1640
-                    $parts  = $this->mapParams($parts, array(Core::NAMESPACE_PLUGINS_BLOCKS . 'PluginIf', 'init'), 1);
1640
+                    $parts  = $this->mapParams($parts, array(Core::NAMESPACE_PLUGINS_BLOCKS.'PluginIf', 'init'), 1);
1641 1641
                     $tokens = $this->getParamTokens($parts);
1642 1642
                     $parts  = $this->getCompiledParams($parts);
1643 1643
 
@@ -1651,14 +1651,14 @@  discard block
 block discarded – undo
1651 1651
                 if ($this->autoEscape) {
1652 1652
                     $out = preg_replace('#\(is_string\(\$tmp=(.+?)\) \? htmlspecialchars\(\$tmp, ENT_QUOTES, \$this->charset\) : \$tmp\)#', '$1', $out);
1653 1653
                 }
1654
-                $out = self::PHP_OPEN . $echo . $out . $operator . implode(' ', $value) . self::PHP_CLOSE;
1654
+                $out = self::PHP_OPEN.$echo.$out.$operator.implode(' ', $value).self::PHP_CLOSE;
1655 1655
             } elseif ($curBlock === 'array' && is_array($parsingParams) && preg_match('#^(\s*=>?\s*)#', $substr, $match)) {
1656 1656
                 // parse namedparam with var as name (only for array)
1657 1657
                 if ($this->debug) {
1658
-                    echo 'VARIABLE NAMED PARAM (FOR ARRAY) FOUND' . "\n";
1658
+                    echo 'VARIABLE NAMED PARAM (FOR ARRAY) FOUND'."\n";
1659 1659
                 }
1660 1660
                 $len = strlen($match[1]);
1661
-                $var = $out[count($out) - 1];
1661
+                $var = $out[count($out)-1];
1662 1662
                 $pointer += $len;
1663 1663
 
1664 1664
                 $output = array($var[0], $this->parse($substr, $len, null, false, 'namedparam', $pointer));
@@ -1673,16 +1673,16 @@  discard block
 block discarded – undo
1673 1673
             // parse modifier on funcs or vars
1674 1674
             $srcPointer = $pointer;
1675 1675
             if (is_array($parsingParams)) {
1676
-                $tmp                     = $this->replaceModifiers(
1676
+                $tmp = $this->replaceModifiers(
1677 1677
                     array(
1678 1678
                     null,
1679 1679
                     null,
1680
-                    $out[count($out) - 1][0],
1680
+                    $out[count($out)-1][0],
1681 1681
                     $match[0]
1682 1682
                     ), $curBlock, $pointer
1683 1683
                 );
1684
-                $out[count($out) - 1][0] = $tmp;
1685
-                $out[count($out) - 1][1] .= substr($substr, $srcPointer, $srcPointer - $pointer);
1684
+                $out[count($out)-1][0] = $tmp;
1685
+                $out[count($out)-1][1] .= substr($substr, $srcPointer, $srcPointer-$pointer);
1686 1686
             } else {
1687 1687
                 $out = $this->replaceModifiers(array(null, null, $out, $match[0]), $curBlock, $pointer);
1688 1688
             }
@@ -1694,10 +1694,10 @@  discard block
 block discarded – undo
1694 1694
             $ptr = 0;
1695 1695
 
1696 1696
             if (is_array($parsingParams)) {
1697
-                $output = $this->parseMethodCall($out[count($out) - 1][1], $match[0], $curBlock, $ptr);
1697
+                $output = $this->parseMethodCall($out[count($out)-1][1], $match[0], $curBlock, $ptr);
1698 1698
 
1699
-                $out[count($out) - 1][0] = $output;
1700
-                $out[count($out) - 1][1] .= substr($match[0], 0, $ptr);
1699
+                $out[count($out)-1][0] = $output;
1700
+                $out[count($out)-1][1] .= substr($match[0], 0, $ptr);
1701 1701
             } else {
1702 1702
                 $out = $this->parseMethodCall($out, $match[0], $curBlock, $ptr);
1703 1703
             }
@@ -1706,7 +1706,7 @@  discard block
 block discarded – undo
1706 1706
         }
1707 1707
 
1708 1708
         if ($curBlock === 'root' && substr($out, 0, strlen(self::PHP_OPEN)) !== self::PHP_OPEN) {
1709
-            return self::PHP_OPEN . 'echo ' . $out . ';' . self::PHP_CLOSE;
1709
+            return self::PHP_OPEN.'echo '.$out.';'.self::PHP_CLOSE;
1710 1710
         }
1711 1711
 
1712 1712
         return $out;
@@ -1732,11 +1732,11 @@  discard block
 block discarded – undo
1732 1732
     protected function parseFunction($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
1733 1733
     {
1734 1734
         $output = '';
1735
-        $cmdstr = substr($in, $from, $to - $from);
1736
-        preg_match('/^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*(?:::[a-z_][a-z0-9_]*)?)(\s*' . $this->rdr . '|\s*;)?/i', $cmdstr, $match);
1735
+        $cmdstr = substr($in, $from, $to-$from);
1736
+        preg_match('/^(\\\\?[a-z_](?:\\\\?[a-z0-9_]+)*(?:::[a-z_][a-z0-9_]*)?)(\s*'.$this->rdr.'|\s*;)?/i', $cmdstr, $match);
1737 1737
 
1738 1738
         if (empty($match[1])) {
1739
-            throw new CompilationException($this, 'Parse error, invalid function name : ' . substr($cmdstr, 0, 15));
1739
+            throw new CompilationException($this, 'Parse error, invalid function name : '.substr($cmdstr, 0, 15));
1740 1740
         }
1741 1741
 
1742 1742
         $func = $match[1];
@@ -1746,7 +1746,7 @@  discard block
 block discarded – undo
1746 1746
         }
1747 1747
 
1748 1748
         if ($this->debug) {
1749
-            echo 'FUNC FOUND (' . $func . ')' . "\n";
1749
+            echo 'FUNC FOUND ('.$func.')'."\n";
1750 1750
         }
1751 1751
 
1752 1752
         $paramsep = '';
@@ -1758,11 +1758,11 @@  discard block
 block discarded – undo
1758 1758
             $paramspos = $match[1][0][1];
1759 1759
             $paramsep  = substr($match[1][0][0], - 1) === '(' ? ')' : '';
1760 1760
             if ($paramsep === ')') {
1761
-                $paramspos += strlen($match[1][0][0]) - 1;
1761
+                $paramspos += strlen($match[1][0][0])-1;
1762 1762
                 if (substr($cmdstr, 0, 2) === 'if' || substr($cmdstr, 0, 6) === 'elseif') {
1763 1763
                     $paramsep = '';
1764 1764
                     if (strlen($match[1][0][0]) > 1) {
1765
-                        -- $paramspos;
1765
+                        --$paramspos;
1766 1766
                     }
1767 1767
                 }
1768 1768
             }
@@ -1787,8 +1787,8 @@  discard block
 block discarded – undo
1787 1787
                     return $this->parseOthers($in, $from, $to, $parsingParams, $curBlock, $pointer);
1788 1788
                 }
1789 1789
             }
1790
-            $whitespace = strlen(substr($cmdstr, strlen($func), $paramspos - strlen($func)));
1791
-            $paramstr   = substr($cmdstr, $paramspos + 1);
1790
+            $whitespace = strlen(substr($cmdstr, strlen($func), $paramspos-strlen($func)));
1791
+            $paramstr   = substr($cmdstr, $paramspos+1);
1792 1792
             if (substr($paramstr, - 1, 1) === $paramsep) {
1793 1793
                 $paramstr = substr($paramstr, 0, - 1);
1794 1794
             }
@@ -1810,36 +1810,36 @@  discard block
 block discarded – undo
1810 1810
 
1811 1811
                             if ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === ')') {
1812 1812
                                 if ($this->debug) {
1813
-                                    echo 'PARAM PARSING ENDED, ")" FOUND, POINTER AT ' . $ptr . "\n";
1813
+                                    echo 'PARAM PARSING ENDED, ")" FOUND, POINTER AT '.$ptr."\n";
1814 1814
                                 }
1815 1815
                                 break 2;
1816 1816
                             } elseif ($paramstr[$ptr] === ';') {
1817
-                                ++ $ptr;
1817
+                                ++$ptr;
1818 1818
                                 if ($this->debug) {
1819
-                                    echo 'PARAM PARSING ENDED, ";" FOUND, POINTER AT ' . $ptr . "\n";
1819
+                                    echo 'PARAM PARSING ENDED, ";" FOUND, POINTER AT '.$ptr."\n";
1820 1820
                                 }
1821 1821
                                 break 2;
1822 1822
                             } elseif ($func !== 'if' && $func !== 'elseif' && $paramstr[$ptr] === '/') {
1823 1823
                                 if ($this->debug) {
1824
-                                    echo 'PARAM PARSING ENDED, "/" FOUND, POINTER AT ' . $ptr . "\n";
1824
+                                    echo 'PARAM PARSING ENDED, "/" FOUND, POINTER AT '.$ptr."\n";
1825 1825
                                 }
1826 1826
                                 break 2;
1827 1827
                             } elseif (substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) {
1828 1828
                                 if ($this->debug) {
1829
-                                    echo 'PARAM PARSING ENDED, RIGHT DELIMITER FOUND, POINTER AT ' . $ptr . "\n";
1829
+                                    echo 'PARAM PARSING ENDED, RIGHT DELIMITER FOUND, POINTER AT '.$ptr."\n";
1830 1830
                                 }
1831 1831
                                 break 2;
1832 1832
                             }
1833 1833
 
1834 1834
                             if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === ',' || $paramstr[$ptr] === "\r" || $paramstr[$ptr] === "\n" || $paramstr[$ptr] === "\t") {
1835
-                                ++ $ptr;
1835
+                                ++$ptr;
1836 1836
                             } else {
1837 1837
                                 break;
1838 1838
                             }
1839 1839
                         }
1840 1840
 
1841 1841
                         if ($this->debug) {
1842
-                            echo 'FUNC START PARAM PARSING WITH POINTER AT ' . $ptr . "\n";
1842
+                            echo 'FUNC START PARAM PARSING WITH POINTER AT '.$ptr."\n";
1843 1843
                         }
1844 1844
 
1845 1845
                         if ($func === 'if' || $func === 'elseif' || $func === 'tif') {
@@ -1851,7 +1851,7 @@  discard block
 block discarded – undo
1851 1851
                         }
1852 1852
 
1853 1853
                         if ($this->debug) {
1854
-                            echo 'PARAM PARSED, POINTER AT ' . $ptr . ' (' . substr($paramstr, $ptr - 1, 3) . ')' . "\n";
1854
+                            echo 'PARAM PARSED, POINTER AT '.$ptr.' ('.substr($paramstr, $ptr-1, 3).')'."\n";
1855 1855
                         }
1856 1856
                     }
1857 1857
                 }
@@ -1875,16 +1875,16 @@  discard block
 block discarded – undo
1875 1875
         }
1876 1876
 
1877 1877
         if ($pointer !== null) {
1878
-            $pointer += (isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func) + (isset($whitespace) ? $whitespace : 0);
1878
+            $pointer += (isset($paramstr) ? strlen($paramstr) : 0)+(')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1))+strlen($func)+(isset($whitespace) ? $whitespace : 0);
1879 1879
             if ($this->debug) {
1880
-                echo 'FUNC ADDS ' . ((isset($paramstr) ? strlen($paramstr) : 0) + (')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1)) + strlen($func)) . ' TO POINTER' . "\n";
1880
+                echo 'FUNC ADDS '.((isset($paramstr) ? strlen($paramstr) : 0)+(')' === $paramsep ? 2 : ($paramspos === false ? 0 : 1))+strlen($func)).' TO POINTER'."\n";
1881 1881
             }
1882 1882
         }
1883 1883
 
1884 1884
         if ($curBlock === 'method' || $func === 'do' || strstr($func, '::') !== false) {
1885 1885
             // handle static method calls with security policy
1886 1886
             if (strstr($func, '::') !== false && $this->securityPolicy !== null && $this->securityPolicy->isMethodAllowed(explode('::', strtolower($func))) !== true) {
1887
-                throw new SecurityException('Call to a disallowed php function : ' . $func);
1887
+                throw new SecurityException('Call to a disallowed php function : '.$func);
1888 1888
             }
1889 1889
             $pluginType = Core::NATIVE_PLUGIN;
1890 1890
         } else {
@@ -1929,19 +1929,19 @@  discard block
 block discarded – undo
1929 1929
                     $this->customPlugins[$func]['function']
1930 1930
                 ), $state);
1931 1931
             } else {
1932
-                if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
1932
+                if (class_exists('Plugin'.Core::toCamelCase($func)) !== false) {
1933 1933
                     $params = $this->mapParams($params, array(
1934
-                        'Plugin' . Core::toCamelCase($func),
1934
+                        'Plugin'.Core::toCamelCase($func),
1935 1935
                         ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
1936 1936
                     ), $state);
1937
-                } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) {
1937
+                } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func)) !== false) {
1938 1938
                     $params = $this->mapParams($params, array(
1939
-                        Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func),
1939
+                        Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func),
1940 1940
                         ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
1941 1941
                     ), $state);
1942 1942
                 } else {
1943 1943
                     $params = $this->mapParams($params, array(
1944
-                        Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
1944
+                        Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func),
1945 1945
                         ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process'
1946 1946
                     ), $state);
1947 1947
                 }
@@ -1952,24 +1952,22 @@  discard block
 block discarded – undo
1952 1952
                 $params = $this->mapParams($params, $this->customPlugins[$func]['callback'], $state);
1953 1953
             } else {
1954 1954
                 // Custom plugin
1955
-                if (function_exists('Plugin' . Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ?
1955
+                if (function_exists('Plugin'.Core::toCamelCase($func).(($pluginType & Core::COMPILABLE_PLUGIN) ?
1956 1956
                         'Compile' : '')) !== false) {
1957
-                    $params = $this->mapParams($params, 'Plugin' . Core::toCamelCase($func) . (($pluginType &
1957
+                    $params = $this->mapParams($params, 'Plugin'.Core::toCamelCase($func).(($pluginType &
1958 1958
                             Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1959 1959
                 } // Builtin helper plugin
1960
-                elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . (
1960
+                elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func).(
1961 1961
                     ($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '')) !== false) {
1962
-                    $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase
1963
-                        ($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1962
+                    $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func).(($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1964 1963
                 } // Builtin function plugin
1965 1964
                 else {
1966
-                    $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase
1967
-                        ($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1965
+                    $params = $this->mapParams($params, Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func).(($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''), $state);
1968 1966
                 }
1969 1967
             }
1970 1968
         } // Smarty modifier
1971 1969
         elseif ($pluginType & Core::SMARTY_MODIFIER) {
1972
-            $output = 'smarty_modifier_' . $func . '(' . implode(', ', $params) . ')';
1970
+            $output = 'smarty_modifier_'.$func.'('.implode(', ', $params).')';
1973 1971
         } // Proxy plugin
1974 1972
         elseif ($pluginType & Core::PROXY_PLUGIN) {
1975 1973
             $params = $this->mapParams($params, $this->getDwoo()->getPluginProxy()->getCallback($func), $state);
@@ -2009,19 +2007,19 @@  discard block
 block discarded – undo
2009 2007
             if ($func === 'do') {
2010 2008
                 $output = '';
2011 2009
                 if (isset($params['*'])) {
2012
-                    $output = implode(';', $params['*']) . ';';
2010
+                    $output = implode(';', $params['*']).';';
2013 2011
                 }
2014 2012
 
2015 2013
                 if (is_array($parsingParams) || $curBlock !== 'root') {
2016 2014
                     throw new CompilationException($this, 'Do can not be used inside another function or block');
2017 2015
                 }
2018 2016
 
2019
-                return self::PHP_OPEN . $output . self::PHP_CLOSE;
2017
+                return self::PHP_OPEN.$output.self::PHP_CLOSE;
2020 2018
             } else {
2021 2019
                 if (isset($params['*'])) {
2022
-                    $output = $func . '(' . implode(', ', $params['*']) . ')';
2020
+                    $output = $func.'('.implode(', ', $params['*']).')';
2023 2021
                 } else {
2024
-                    $output = $func . '()';
2022
+                    $output = $func.'()';
2025 2023
                 }
2026 2024
             }
2027 2025
         } // Block class OR Function class
@@ -2031,7 +2029,7 @@  discard block
 block discarded – undo
2031 2029
                     $callback = $this->customPlugins[$func]['callback'];
2032 2030
                     if (!is_array($callback)) {
2033 2031
                         if (!method_exists($callback, 'compile')) {
2034
-                            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');
2032
+                            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');
2035 2033
                         }
2036 2034
                         if (($ref = new ReflectionMethod($callback, 'compile')) && $ref->isStatic()) {
2037 2035
                             $funcCompiler = array($callback, 'compile');
@@ -2042,13 +2040,13 @@  discard block
 block discarded – undo
2042 2040
                         $funcCompiler = $callback;
2043 2041
                     }
2044 2042
                 } else {
2045
-                    if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
2046
-                        $funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile');
2047
-                    } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !== false) {
2048
-                        $funcCompiler = array(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func), 'compile');
2043
+                    if (class_exists('Plugin'.Core::toCamelCase($func)) !== false) {
2044
+                        $funcCompiler = array('Plugin'.Core::toCamelCase($func), 'compile');
2045
+                    } elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func)) !== false) {
2046
+                        $funcCompiler = array(Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func), 'compile');
2049 2047
                     } else {
2050 2048
                         $funcCompiler = array(
2051
-                            Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
2049
+                            Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func),
2052 2050
                             'compile'
2053 2051
                         );
2054 2052
                     }
@@ -2065,31 +2063,31 @@  discard block
 block discarded – undo
2065 2063
                     $callback = $this->customPlugins[$func]['callback'];
2066 2064
                     if (!is_array($callback)) {
2067 2065
                         if (!method_exists($callback, 'process')) {
2068
-                            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');
2066
+                            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');
2069 2067
                         }
2070 2068
                         if (($ref = new ReflectionMethod($callback, 'process')) && $ref->isStatic()) {
2071
-                            $output = 'call_user_func(array(\'' . $callback . '\', \'process\'), ' . $params . ')';
2069
+                            $output = 'call_user_func(array(\''.$callback.'\', \'process\'), '.$params.')';
2072 2070
                         } else {
2073
-                            $output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback . '\'), \'process\'), ' . $params . ')';
2071
+                            $output = 'call_user_func(array($this->getObjectPlugin(\''.$callback.'\'), \'process\'), '.$params.')';
2074 2072
                         }
2075 2073
                     } elseif (is_object($callback[0])) {
2076
-                        $output = 'call_user_func(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), ' . $params . ')';
2074
+                        $output = 'call_user_func(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), '.$params.')';
2077 2075
                     } elseif (($ref = new ReflectionMethod($callback[0], $callback[1])) && $ref->isStatic()) {
2078
-                        $output = 'call_user_func(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), ' . $params . ')';
2076
+                        $output = 'call_user_func(array(\''.$callback[0].'\', \''.$callback[1].'\'), '.$params.')';
2079 2077
                     } else {
2080
-                        $output = 'call_user_func(array($this->getObjectPlugin(\'' . $callback[0] . '\'), \'' . $callback[1] . '\'), ' . $params . ')';
2078
+                        $output = 'call_user_func(array($this->getObjectPlugin(\''.$callback[0].'\'), \''.$callback[1].'\'), '.$params.')';
2081 2079
                     }
2082 2080
                     if (empty($params)) {
2083
-                        $output = substr($output, 0, - 3) . ')';
2081
+                        $output = substr($output, 0, - 3).')';
2084 2082
                     }
2085 2083
                 } else {
2086
-                    if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
2087
-                        $output = '$this->classCall(\'Plugin' . $func . '\', array(' . $params . '))';
2088
-                    } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) {
2089
-                        $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . $func . '\', 
2090
-                        array(' . $params . '))';
2084
+                    if (class_exists('Plugin'.Core::toCamelCase($func)) !== false) {
2085
+                        $output = '$this->classCall(\'Plugin'.$func.'\', array('.$params.'))';
2086
+                    } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func)) !== false) {
2087
+                        $output = '$this->classCall(\''.Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.$func.'\', 
2088
+                        array(' . $params.'))';
2091 2089
                     } else {
2092
-                        $output = '$this->classCall(\'' . $func . '\', array(' . $params . '))';
2090
+                        $output = '$this->classCall(\''.$func.'\', array('.$params.'))';
2093 2091
                     }
2094 2092
                 }
2095 2093
             }
@@ -2100,15 +2098,15 @@  discard block
 block discarded – undo
2100 2098
                     $funcCompiler = $this->customPlugins[$func]['callback'];
2101 2099
                 } else {
2102 2100
                     // Custom plugin
2103
-                    if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
2104
-                        $funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile';
2101
+                    if (function_exists('Plugin'.Core::toCamelCase($func).'Compile') !== false) {
2102
+                        $funcCompiler = 'Plugin'.Core::toCamelCase($func).'Compile';
2105 2103
                     } // Builtin helper plugin
2106
-                    elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
2107
-                        $funcCompiler = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) .
2104
+                    elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func).'Compile') !== false) {
2105
+                        $funcCompiler = Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func).
2108 2106
                             'Compile';
2109 2107
                     } // Builtin function plugin
2110 2108
                     else {
2111
-                        $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) .
2109
+                        $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func).
2112 2110
                             'Compile';
2113 2111
                     }
2114 2112
                 }
@@ -2124,24 +2122,24 @@  discard block
 block discarded – undo
2124 2122
                 if ($pluginType & Core::CUSTOM_PLUGIN) {
2125 2123
                     $callback = $this->customPlugins[$func]['callback'];
2126 2124
                     if ($callback instanceof Closure) {
2127
-                        $output = 'call_user_func($this->getCustomPlugin(\'' . $func . '\'), ' . $params . ')';
2125
+                        $output = 'call_user_func($this->getCustomPlugin(\''.$func.'\'), '.$params.')';
2128 2126
                     } else {
2129
-                        $output = 'call_user_func(\'' . $callback . '\', ' . $params . ')';
2127
+                        $output = 'call_user_func(\''.$callback.'\', '.$params.')';
2130 2128
                     }
2131 2129
                 } else {
2132 2130
                     // Custom plugin
2133
-                    if (function_exists('Plugin' . Core::toCamelCase($func)) !== false) {
2134
-                        $output = 'Plugin' . Core::toCamelCase($func) . '(' . $params .
2131
+                    if (function_exists('Plugin'.Core::toCamelCase($func)) !== false) {
2132
+                        $output = 'Plugin'.Core::toCamelCase($func).'('.$params.
2135 2133
                             ')';
2136 2134
                     } // Builtin helper plugin
2137
-                    elseif(function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func)) !==
2135
+                    elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func)) !==
2138 2136
                         false) {
2139
-                        $output = Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($func) . '(' .
2140
-                            $params . ')';
2137
+                        $output = Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($func).'('.
2138
+                            $params.')';
2141 2139
                     } // Builtin function plugin
2142 2140
                     else {
2143
-                        $output = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '(' .
2144
-                            $params . ')';
2141
+                        $output = Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func).'('.
2142
+                            $params.')';
2145 2143
                     }
2146 2144
                 }
2147 2145
             }
@@ -2159,22 +2157,22 @@  discard block
 block discarded – undo
2159 2157
                 $callback = $this->customPlugins[$func]['callback'];
2160 2158
                 if (is_array($callback)) {
2161 2159
                     if (is_object($callback[0])) {
2162
-                        $output = 'call_user_func_array(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(array(' . $params . '), $this))';
2160
+                        $output = 'call_user_func_array(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), array(array('.$params.'), $this))';
2163 2161
                     } else {
2164
-                        $output = 'call_user_func_array(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(array(' . $params . '), $this))';
2162
+                        $output = 'call_user_func_array(array(\''.$callback[0].'\', \''.$callback[1].'\'), array(array('.$params.'), $this))';
2165 2163
                     }
2166 2164
                 } else {
2167
-                    $output = $callback . '(array(' . $params . '), $this)';
2165
+                    $output = $callback.'(array('.$params.'), $this)';
2168 2166
                 }
2169 2167
             } else {
2170
-                $output = 'smarty_function_' . $func . '(array(' . $params . '), $this)';
2168
+                $output = 'smarty_function_'.$func.'(array('.$params.'), $this)';
2171 2169
             }
2172 2170
         } // Template plugin
2173 2171
         elseif ($pluginType & Core::TEMPLATE_PLUGIN) {
2174 2172
             array_unshift($params, '$this');
2175 2173
             $params                                 = self::implode_r($params);
2176
-            $output                                 = 'Plugin' . Core::toCamelCase($func) .
2177
-                $this->templatePlugins[$func]['uuid'] . '(' . $params . ')';
2174
+            $output                                 = 'Plugin'.Core::toCamelCase($func).
2175
+                $this->templatePlugins[$func]['uuid'].'('.$params.')';
2178 2176
             $this->templatePlugins[$func]['called'] = true;
2179 2177
         }
2180 2178
 
@@ -2206,29 +2204,29 @@  discard block
 block discarded – undo
2206 2204
      */
2207 2205
     protected function parseString($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2208 2206
     {
2209
-        $substr = substr($in, $from, $to - $from);
2207
+        $substr = substr($in, $from, $to-$from);
2210 2208
         $first  = $substr[0];
2211 2209
 
2212 2210
         if ($this->debug) {
2213
-            echo 'STRING FOUND (in ' . htmlentities(substr($in, $from, min($to - $from, 50))) . (($to - $from) > 50 ? '...' : '') . ')' . "\n";
2211
+            echo 'STRING FOUND (in '.htmlentities(substr($in, $from, min($to-$from, 50))).(($to-$from) > 50 ? '...' : '').')'."\n";
2214 2212
         }
2215 2213
         $strend = false;
2216
-        $o      = $from + 1;
2214
+        $o      = $from+1;
2217 2215
         while ($strend === false) {
2218 2216
             $strend = strpos($in, $first, $o);
2219 2217
             if ($strend === false) {
2220
-                throw new CompilationException($this, 'Unfinished string, started with ' . substr($in, $from, $to - $from));
2218
+                throw new CompilationException($this, 'Unfinished string, started with '.substr($in, $from, $to-$from));
2221 2219
             }
2222
-            if (substr($in, $strend - 1, 1) === '\\') {
2223
-                $o      = $strend + 1;
2220
+            if (substr($in, $strend-1, 1) === '\\') {
2221
+                $o      = $strend+1;
2224 2222
                 $strend = false;
2225 2223
             }
2226 2224
         }
2227 2225
         if ($this->debug) {
2228
-            echo 'STRING DELIMITED: ' . substr($in, $from, $strend + 1 - $from) . "\n";
2226
+            echo 'STRING DELIMITED: '.substr($in, $from, $strend+1-$from)."\n";
2229 2227
         }
2230 2228
 
2231
-        $srcOutput = substr($in, $from, $strend + 1 - $from);
2229
+        $srcOutput = substr($in, $from, $strend+1-$from);
2232 2230
 
2233 2231
         if ($pointer !== null) {
2234 2232
             $pointer += strlen($srcOutput);
@@ -2237,13 +2235,13 @@  discard block
 block discarded – undo
2237 2235
         $output = $this->replaceStringVars($srcOutput, $first);
2238 2236
 
2239 2237
         // handle modifiers
2240
-        if ($curBlock !== 'modifier' && preg_match('#^((?:\|(?:@?[a-z0-9_]+(?::.*)*))+)#i', substr($substr, $strend + 1 - $from), $match)) {
2238
+        if ($curBlock !== 'modifier' && preg_match('#^((?:\|(?:@?[a-z0-9_]+(?::.*)*))+)#i', substr($substr, $strend+1-$from), $match)) {
2241 2239
             $modstr = $match[1];
2242 2240
 
2243 2241
             if ($curBlock === 'root' && substr($modstr, - 1) === '}') {
2244 2242
                 $modstr = substr($modstr, 0, - 1);
2245 2243
             }
2246
-            $modstr = str_replace('\\' . $first, $first, $modstr);
2244
+            $modstr = str_replace('\\'.$first, $first, $modstr);
2247 2245
             $ptr    = 0;
2248 2246
             $output = $this->replaceModifiers(array(null, null, $output, $modstr), 'string', $ptr);
2249 2247
 
@@ -2251,7 +2249,7 @@  discard block
 block discarded – undo
2251 2249
             if ($pointer !== null) {
2252 2250
                 $pointer += $ptr;
2253 2251
             }
2254
-            $srcOutput .= substr($substr, $strend + 1 - $from, $ptr);
2252
+            $srcOutput .= substr($substr, $strend+1-$from, $ptr);
2255 2253
         }
2256 2254
 
2257 2255
         if (is_array($parsingParams)) {
@@ -2282,10 +2280,10 @@  discard block
 block discarded – undo
2282 2280
      */
2283 2281
     protected function parseConst($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2284 2282
     {
2285
-        $substr = substr($in, $from, $to - $from);
2283
+        $substr = substr($in, $from, $to-$from);
2286 2284
 
2287 2285
         if ($this->debug) {
2288
-            echo 'CONST FOUND : ' . $substr . "\n";
2286
+            echo 'CONST FOUND : '.$substr."\n";
2289 2287
         }
2290 2288
 
2291 2289
         if (!preg_match('#^%([\\\\a-z0-9_:]+)#i', $substr, $m)) {
@@ -2326,7 +2324,7 @@  discard block
 block discarded – undo
2326 2324
         }
2327 2325
 
2328 2326
         if ($curBlock !== 'root') {
2329
-            return '(defined("' . $key . '") ? ' . $key . ' : null)';
2327
+            return '(defined("'.$key.'") ? '.$key.' : null)';
2330 2328
         }
2331 2329
 
2332 2330
         return $key;
@@ -2349,7 +2347,7 @@  discard block
 block discarded – undo
2349 2347
      */
2350 2348
     protected function parseVar($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2351 2349
     {
2352
-        $substr = substr($in, $from, $to - $from);
2350
+        $substr = substr($in, $from, $to-$from);
2353 2351
 
2354 2352
         // var key
2355 2353
         $varRegex = '(\\$?\\.?[a-z0-9\\\\_:]*(?:(?:(?:\\.|->)(?:[a-z0-9\\\\_:]+|(?R))|\\[(?:[a-z0-9\\\\_:]+|(?R)|(["\'])[^\\2]*?\\2)\\]))*)';
@@ -2377,13 +2375,13 @@  discard block
 block discarded – undo
2377 2375
 
2378 2376
             if (substr($key, - 1) == '.') {
2379 2377
                 $key = substr($key, 0, - 1);
2380
-                -- $matchedLength;
2378
+                --$matchedLength;
2381 2379
             }
2382 2380
 
2383 2381
             if ($hasMethodCall) {
2384
-                $matchedLength -= strlen($match[3]) + strlen(substr($match[1], strrpos($match[1], '->')));
2385
-                $key        = substr($match[1], 1, strrpos($match[1], '->') - 1);
2386
-                $methodCall = substr($match[1], strrpos($match[1], '->')) . $match[3];
2382
+                $matchedLength -= strlen($match[3])+strlen(substr($match[1], strrpos($match[1], '->')));
2383
+                $key        = substr($match[1], 1, strrpos($match[1], '->')-1);
2384
+                $methodCall = substr($match[1], strrpos($match[1], '->')).$match[3];
2387 2385
             }
2388 2386
 
2389 2387
             if ($hasModifiers) {
@@ -2399,9 +2397,9 @@  discard block
 block discarded – undo
2399 2397
 
2400 2398
             if ($this->debug) {
2401 2399
                 if ($hasMethodCall) {
2402
-                    echo 'METHOD CALL FOUND : $' . $key . substr($methodCall, 0, 30) . "\n";
2400
+                    echo 'METHOD CALL FOUND : $'.$key.substr($methodCall, 0, 30)."\n";
2403 2401
                 } else {
2404
-                    echo 'VAR FOUND : $' . $key . "\n";
2402
+                    echo 'VAR FOUND : $'.$key."\n";
2405 2403
                 }
2406 2404
             }
2407 2405
 
@@ -2412,7 +2410,7 @@  discard block
 block discarded – undo
2412 2410
                 $uid           = 0;
2413 2411
                 $parsed        = array($uid => '');
2414 2412
                 $current       = &$parsed;
2415
-                $curTxt        = &$parsed[$uid ++];
2413
+                $curTxt        = &$parsed[$uid++];
2416 2414
                 $tree          = array();
2417 2415
                 $chars         = str_split($key, 1);
2418 2416
                 $inSplittedVar = false;
@@ -2421,33 +2419,33 @@  discard block
 block discarded – undo
2421 2419
                 while (($char = array_shift($chars)) !== null) {
2422 2420
                     if ($char === '[') {
2423 2421
                         if (count($tree) > 0) {
2424
-                            ++ $bracketCount;
2422
+                            ++$bracketCount;
2425 2423
                         } else {
2426 2424
                             $tree[]        = &$current;
2427
-                            $current[$uid] = array($uid + 1 => '');
2428
-                            $current       = &$current[$uid ++];
2429
-                            $curTxt        = &$current[$uid ++];
2425
+                            $current[$uid] = array($uid+1 => '');
2426
+                            $current       = &$current[$uid++];
2427
+                            $curTxt        = &$current[$uid++];
2430 2428
                             continue;
2431 2429
                         }
2432 2430
                     } elseif ($char === ']') {
2433 2431
                         if ($bracketCount > 0) {
2434
-                            -- $bracketCount;
2432
+                            --$bracketCount;
2435 2433
                         } else {
2436
-                            $current = &$tree[count($tree) - 1];
2434
+                            $current = &$tree[count($tree)-1];
2437 2435
                             array_pop($tree);
2438 2436
                             if (current($chars) !== '[' && current($chars) !== false && current($chars) !== ']') {
2439 2437
                                 $current[$uid] = '';
2440
-                                $curTxt        = &$current[$uid ++];
2438
+                                $curTxt        = &$current[$uid++];
2441 2439
                             }
2442 2440
                             continue;
2443 2441
                         }
2444 2442
                     } elseif ($char === '$') {
2445 2443
                         if (count($tree) == 0) {
2446
-                            $curTxt        = &$current[$uid ++];
2444
+                            $curTxt        = &$current[$uid++];
2447 2445
                             $inSplittedVar = true;
2448 2446
                         }
2449 2447
                     } elseif (($char === '.' || $char === '-') && count($tree) == 0 && $inSplittedVar) {
2450
-                        $curTxt        = &$current[$uid ++];
2448
+                        $curTxt        = &$current[$uid++];
2451 2449
                         $inSplittedVar = false;
2452 2450
                     }
2453 2451
 
@@ -2456,16 +2454,16 @@  discard block
 block discarded – undo
2456 2454
                 unset($uid, $current, $curTxt, $tree, $chars);
2457 2455
 
2458 2456
                 if ($this->debug) {
2459
-                    echo 'RECURSIVE VAR REPLACEMENT : ' . $key . "\n";
2457
+                    echo 'RECURSIVE VAR REPLACEMENT : '.$key."\n";
2460 2458
                 }
2461 2459
 
2462 2460
                 $key = $this->flattenVarTree($parsed);
2463 2461
 
2464 2462
                 if ($this->debug) {
2465
-                    echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n";
2463
+                    echo 'RECURSIVE VAR REPLACEMENT DONE : '.$key."\n";
2466 2464
                 }
2467 2465
 
2468
-                $output = preg_replace('#(^""\.|""\.|\.""$|(\()""\.|\.""(\)))#', '$2$3', '$this->readVar("' . $key . '")');
2466
+                $output = preg_replace('#(^""\.|""\.|\.""$|(\()""\.|\.""(\)))#', '$2$3', '$this->readVar("'.$key.'")');
2469 2467
             } else {
2470 2468
                 $output = $this->parseVarKey($key, $hasModifiers ? 'modifier' : $curBlock);
2471 2469
             }
@@ -2490,10 +2488,10 @@  discard block
 block discarded – undo
2490 2488
                     if (substr($expMatch[2][$k], 0, 1) === '=') {
2491 2489
                         $assign = true;
2492 2490
                         if ($operator === '=') {
2493
-                            throw new CompilationException($this, 'Invalid expression <em>' . $substr . '</em>, can not use "==" in expressions');
2491
+                            throw new CompilationException($this, 'Invalid expression <em>'.$substr.'</em>, can not use "==" in expressions');
2494 2492
                         }
2495 2493
                         if ($curBlock !== 'root') {
2496
-                            throw new CompilationException($this, 'Invalid expression <em>' . $substr . '</em>, assignments can only be used in top level expressions like {$foo+=3} or {$foo="bar"}');
2494
+                            throw new CompilationException($this, 'Invalid expression <em>'.$substr.'</em>, assignments can only be used in top level expressions like {$foo+=3} or {$foo="bar"}');
2497 2495
                         }
2498 2496
                         $operator .= '=';
2499 2497
                         $expMatch[2][$k] = substr($expMatch[2][$k], 1);
@@ -2504,22 +2502,22 @@  discard block
 block discarded – undo
2504 2502
                         $expMatch[2][$k] = substr($expMatch[2][$k], 1);
2505 2503
                     }
2506 2504
                     if (($operator === '+' || $operator === '-') && $expMatch[2][$k] === $operator) {
2507
-                        $output = '(' . $output . $operator . $operator . ')';
2505
+                        $output = '('.$output.$operator.$operator.')';
2508 2506
                         break;
2509 2507
                     } elseif (substr($expMatch[2][$k], 0, 1) === '$') {
2510
-                        $output = '(' . $output . ' ' . $operator . ' ' . $this->parseVar($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')';
2508
+                        $output = '('.$output.' '.$operator.' '.$this->parseVar($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression').')';
2511 2509
                     } elseif (substr($expMatch[2][$k], 0, 1) === '%') {
2512
-                        $output = '(' . $output . ' ' . $operator . ' ' . $this->parseConst($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression') . ')';
2510
+                        $output = '('.$output.' '.$operator.' '.$this->parseConst($expMatch[2][$k], 0, strlen($expMatch[2][$k]), false, 'expression').')';
2513 2511
                     } elseif (!empty($expMatch[2][$k])) {
2514
-                        $output = '(' . $output . ' ' . $operator . ' ' . str_replace(',', '.', $expMatch[2][$k]) . ')';
2512
+                        $output = '('.$output.' '.$operator.' '.str_replace(',', '.', $expMatch[2][$k]).')';
2515 2513
                     } else {
2516
-                        throw new CompilationException($this, 'Unfinished expression <em>' . $substr . '</em>, missing var or number after math operator');
2514
+                        throw new CompilationException($this, 'Unfinished expression <em>'.$substr.'</em>, missing var or number after math operator');
2517 2515
                     }
2518 2516
                 }
2519 2517
             }
2520 2518
 
2521 2519
             if ($this->autoEscape === true && $curBlock !== 'condition') {
2522
-                $output = '(is_string($tmp=' . $output . ') ? htmlspecialchars($tmp, ENT_QUOTES, $this->charset) : $tmp)';
2520
+                $output = '(is_string($tmp='.$output.') ? htmlspecialchars($tmp, ENT_QUOTES, $this->charset) : $tmp)';
2523 2521
             }
2524 2522
 
2525 2523
             // handle modifiers
@@ -2543,7 +2541,7 @@  discard block
 block discarded – undo
2543 2541
             } elseif ($curBlock === 'expression' || $curBlock === 'variable') {
2544 2542
                 return $output;
2545 2543
             } elseif (isset($assign)) {
2546
-                return self::PHP_OPEN . $output . ';' . self::PHP_CLOSE;
2544
+                return self::PHP_OPEN.$output.';'.self::PHP_CLOSE;
2547 2545
             }
2548 2546
 
2549 2547
             return $output;
@@ -2551,7 +2549,7 @@  discard block
 block discarded – undo
2551 2549
             if ($curBlock === 'string' || $curBlock === 'delimited_string') {
2552 2550
                 return array(0, '');
2553 2551
             }
2554
-            throw new CompilationException($this, 'Invalid variable name <em>' . $substr . '</em>');
2552
+            throw new CompilationException($this, 'Invalid variable name <em>'.$substr.'</em>');
2555 2553
         }
2556 2554
     }
2557 2555
 
@@ -2605,16 +2603,16 @@  discard block
 block discarded – undo
2605 2603
             if (empty($methMatch[2])) {
2606 2604
                 // property
2607 2605
                 if ($curBlock === 'root') {
2608
-                    $output .= '->' . $methMatch[1];
2606
+                    $output .= '->'.$methMatch[1];
2609 2607
                 } else {
2610
-                    $output = '(($tmp = ' . $output . ') ? $tmp->' . $methMatch[1] . ' : null)';
2608
+                    $output = '(($tmp = '.$output.') ? $tmp->'.$methMatch[1].' : null)';
2611 2609
                 }
2612 2610
                 $ptr += strlen($methMatch[1]);
2613 2611
             } else {
2614 2612
                 // method
2615 2613
                 if (substr($methMatch[2], 0, 2) === '()') {
2616
-                    $parsedCall = $methMatch[1] . '()';
2617
-                    $ptr += strlen($methMatch[1]) + 2;
2614
+                    $parsedCall = $methMatch[1].'()';
2615
+                    $ptr += strlen($methMatch[1])+2;
2618 2616
                 } else {
2619 2617
                     $parsedCall = $this->parseFunction($methodCall, $ptr, strlen($methodCall), false, 'method', $ptr);
2620 2618
                 }
@@ -2623,15 +2621,15 @@  discard block
 block discarded – undo
2623 2621
                     $method = strtolower(substr($parsedCall, 0, $argPos));
2624 2622
                     $args   = substr($parsedCall, $argPos);
2625 2623
                     if ($curBlock === 'root') {
2626
-                        $output = '$this->getSecurityPolicy()->callMethod($this, ' . $output . ', ' . var_export($method, true) . ', array' . $args . ')';
2624
+                        $output = '$this->getSecurityPolicy()->callMethod($this, '.$output.', '.var_export($method, true).', array'.$args.')';
2627 2625
                     } else {
2628
-                        $output = '(($tmp = ' . $output . ') ? $this->getSecurityPolicy()->callMethod($this, $tmp, ' . var_export($method, true) . ', array' . $args . ') : null)';
2626
+                        $output = '(($tmp = '.$output.') ? $this->getSecurityPolicy()->callMethod($this, $tmp, '.var_export($method, true).', array'.$args.') : null)';
2629 2627
                     }
2630 2628
                 } else {
2631 2629
                     if ($curBlock === 'root') {
2632
-                        $output .= '->' . $parsedCall;
2630
+                        $output .= '->'.$parsedCall;
2633 2631
                     } else {
2634
-                        $output = '(($tmp = ' . $output . ') ? $tmp->' . $parsedCall . ' : null)';
2632
+                        $output = '(($tmp = '.$output.') ? $tmp->'.$parsedCall.' : null)';
2635 2633
                     }
2636 2634
                 }
2637 2635
             }
@@ -2657,21 +2655,21 @@  discard block
 block discarded – undo
2657 2655
             return '$this->scope';
2658 2656
         }
2659 2657
         if (substr($key, 0, 1) === '.') {
2660
-            $key = 'dwoo' . $key;
2658
+            $key = 'dwoo'.$key;
2661 2659
         }
2662 2660
         if (preg_match('#dwoo\.(get|post|server|cookies|session|env|request)((?:\.[a-z0-9_-]+)+)#i', $key, $m)) {
2663 2661
             $global = strtoupper($m[1]);
2664 2662
             if ($global === 'COOKIES') {
2665 2663
                 $global = 'COOKIE';
2666 2664
             }
2667
-            $key = '$_' . $global;
2665
+            $key = '$_'.$global;
2668 2666
             foreach (explode('.', ltrim($m[2], '.')) as $part) {
2669
-                $key .= '[' . var_export($part, true) . ']';
2667
+                $key .= '['.var_export($part, true).']';
2670 2668
             }
2671 2669
             if ($curBlock === 'root') {
2672 2670
                 $output = $key;
2673 2671
             } else {
2674
-                $output = '(isset(' . $key . ')?' . $key . ':null)';
2672
+                $output = '(isset('.$key.')?'.$key.':null)';
2675 2673
             }
2676 2674
         } elseif (preg_match('#dwoo\\.const\\.([a-z0-9\\\\_:]+)#i', $key, $m)) {
2677 2675
             return $this->parseConstKey($m[1], $curBlock);
@@ -2687,9 +2685,9 @@  discard block
 block discarded – undo
2687 2685
                     $output = '$tmp_key';
2688 2686
                 } else {
2689 2687
                     if ($curBlock === 'root') {
2690
-                        $output = '$this->scope["' . $key . '"]';
2688
+                        $output = '$this->scope["'.$key.'"]';
2691 2689
                     } else {
2692
-                        $output = '(isset($this->scope["' . $key . '"]) ? $this->scope["' . $key . '"] : null)';
2690
+                        $output = '(isset($this->scope["'.$key.'"]) ? $this->scope["'.$key.'"] : null)';
2693 2691
                     }
2694 2692
                 }
2695 2693
             } else {
@@ -2700,7 +2698,7 @@  discard block
 block discarded – undo
2700 2698
                     $parentCnt = 0;
2701 2699
 
2702 2700
                     while (true) {
2703
-                        ++ $parentCnt;
2701
+                        ++$parentCnt;
2704 2702
                         array_shift($m[2]);
2705 2703
                         array_shift($m[1]);
2706 2704
                         if (current($m[2]) === '_parent') {
@@ -2709,7 +2707,7 @@  discard block
 block discarded – undo
2709 2707
                         break;
2710 2708
                     }
2711 2709
 
2712
-                    $output = '$this->readParentVar(' . $parentCnt . ')';
2710
+                    $output = '$this->readParentVar('.$parentCnt.')';
2713 2711
                 } else {
2714 2712
                     if ($i === 'dwoo') {
2715 2713
                         $output = '$this->globals';
@@ -2728,28 +2726,28 @@  discard block
 block discarded – undo
2728 2726
                     while (count($m[1]) && $m[1][0] !== '->') {
2729 2727
                         $m[2][0] = preg_replace('/(^\\\([\'"])|\\\([\'"])$)/x', '$2$3', $m[2][0]);
2730 2728
                         if (substr($m[2][0], 0, 1) == '"' || substr($m[2][0], 0, 1) == "'") {
2731
-                            $output .= '[' . $m[2][0] . ']';
2729
+                            $output .= '['.$m[2][0].']';
2732 2730
                         } else {
2733
-                            $output .= '["' . $m[2][0] . '"]';
2731
+                            $output .= '["'.$m[2][0].'"]';
2734 2732
                         }
2735 2733
                         array_shift($m[2]);
2736 2734
                         array_shift($m[1]);
2737 2735
                     }
2738 2736
 
2739 2737
                     if ($curBlock !== 'root') {
2740
-                        $output = '(isset(' . $output . ') ? ' . $output . ':null)';
2738
+                        $output = '(isset('.$output.') ? '.$output.':null)';
2741 2739
                     }
2742 2740
                 }
2743 2741
 
2744 2742
                 if (count($m[2])) {
2745 2743
                     unset($m[0]);
2746
-                    $output = '$this->readVarInto(' . str_replace("\n", '', var_export($m, true)) . ', ' . $output . ', ' . ($curBlock == 'root' ? 'false' : 'true') . ')';
2744
+                    $output = '$this->readVarInto('.str_replace("\n", '', var_export($m, true)).', '.$output.', '.($curBlock == 'root' ? 'false' : 'true').')';
2747 2745
                 }
2748 2746
             }
2749 2747
         } else {
2750 2748
             preg_match_all('#(\[|->|\.)?((?:[a-z0-9_]|-(?!>))+)\]?#i', $key, $m);
2751 2749
             unset($m[0]);
2752
-            $output = '$this->readVar(' . str_replace("\n", '', var_export($m, true)) . ')';
2750
+            $output = '$this->readVar('.str_replace("\n", '', var_export($m, true)).')';
2753 2751
         }
2754 2752
 
2755 2753
         return $output;
@@ -2769,38 +2767,38 @@  discard block
 block discarded – undo
2769 2767
         $out = $recursed ? '".$this->readVarInto(' : '';
2770 2768
         foreach ($tree as $bit) {
2771 2769
             if (is_array($bit)) {
2772
-                $out .= '.' . $this->flattenVarTree($bit, false);
2770
+                $out .= '.'.$this->flattenVarTree($bit, false);
2773 2771
             } else {
2774 2772
                 $key = str_replace('"', '\\"', $bit);
2775 2773
 
2776 2774
                 if (substr($key, 0, 1) === '$') {
2777
-                    $out .= '".' . $this->parseVar($key, 0, strlen($key), false, 'variable') . '."';
2775
+                    $out .= '".'.$this->parseVar($key, 0, strlen($key), false, 'variable').'."';
2778 2776
                 } else {
2779 2777
                     $cnt = substr_count($key, '$');
2780 2778
 
2781 2779
                     if ($this->debug) {
2782
-                        echo 'PARSING SUBVARS IN : ' . $key . "\n";
2780
+                        echo 'PARSING SUBVARS IN : '.$key."\n";
2783 2781
                     }
2784 2782
                     if ($cnt > 0) {
2785
-                        while (-- $cnt >= 0) {
2783
+                        while (--$cnt >= 0) {
2786 2784
                             if (isset($last)) {
2787
-                                $last = strrpos($key, '$', - (strlen($key) - $last + 1));
2785
+                                $last = strrpos($key, '$', - (strlen($key)-$last+1));
2788 2786
                             } else {
2789 2787
                                 $last = strrpos($key, '$');
2790 2788
                             }
2791
-                            preg_match('#\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', substr($key, $last), $submatch);
2789
+                            preg_match('#\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*'.'((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', substr($key, $last), $submatch);
2792 2790
 
2793 2791
                             $len = strlen($submatch[0]);
2794 2792
                             $key = substr_replace(
2795 2793
                                 $key, preg_replace_callback(
2796
-                                    '#(\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*)' . '((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', array(
2794
+                                    '#(\$[a-z0-9_]+((?:(?:\.|->)(?:[a-z0-9_]+|(?R))|\[(?:[a-z0-9_]+|(?R))\]))*)'.'((?:(?:[+/*%-])(?:\$[a-z0-9.[\]>_:-]+(?:\([^)]*\))?|[0-9.,]*))*)#i', array(
2797 2795
                                         $this,
2798 2796
                                         'replaceVarKeyHelper'
2799 2797
                                     ), substr($key, $last, $len)
2800 2798
                                 ), $last, $len
2801 2799
                             );
2802 2800
                             if ($this->debug) {
2803
-                                echo 'RECURSIVE VAR REPLACEMENT DONE : ' . $key . "\n";
2801
+                                echo 'RECURSIVE VAR REPLACEMENT DONE : '.$key."\n";
2804 2802
                             }
2805 2803
                         }
2806 2804
                         unset($last);
@@ -2826,7 +2824,7 @@  discard block
 block discarded – undo
2826 2824
      */
2827 2825
     protected function replaceVarKeyHelper($match)
2828 2826
     {
2829
-        return '".' . $this->parseVar($match[0], 0, strlen($match[0]), false, 'variable') . '."';
2827
+        return '".'.$this->parseVar($match[0], 0, strlen($match[0]), false, 'variable').'."';
2830 2828
     }
2831 2829
 
2832 2830
     /**
@@ -2846,7 +2844,7 @@  discard block
 block discarded – undo
2846 2844
      */
2847 2845
     protected function parseOthers($in, $from, $to, $parsingParams = false, $curBlock = '', &$pointer = null)
2848 2846
     {
2849
-        $substr = substr($in, $from, $to - $from);
2847
+        $substr = substr($in, $from, $to-$from);
2850 2848
 
2851 2849
         $end = strlen($substr);
2852 2850
 
@@ -2920,48 +2918,48 @@  discard block
 block discarded – undo
2920 2918
 
2921 2919
         if (strtolower($substr) === 'false' || strtolower($substr) === 'no' || strtolower($substr) === 'off') {
2922 2920
             if ($this->debug) {
2923
-                echo 'BOOLEAN(FALSE) PARSED' . "\n";
2921
+                echo 'BOOLEAN(FALSE) PARSED'."\n";
2924 2922
             }
2925 2923
             $substr = 'false';
2926 2924
             $type   = self::T_BOOL;
2927 2925
         } elseif (strtolower($substr) === 'true' || strtolower($substr) === 'yes' || strtolower($substr) === 'on') {
2928 2926
             if ($this->debug) {
2929
-                echo 'BOOLEAN(TRUE) PARSED' . "\n";
2927
+                echo 'BOOLEAN(TRUE) PARSED'."\n";
2930 2928
             }
2931 2929
             $substr = 'true';
2932 2930
             $type   = self::T_BOOL;
2933 2931
         } elseif ($substr === 'null' || $substr === 'NULL') {
2934 2932
             if ($this->debug) {
2935
-                echo 'NULL PARSED' . "\n";
2933
+                echo 'NULL PARSED'."\n";
2936 2934
             }
2937 2935
             $substr = 'null';
2938 2936
             $type   = self::T_NULL;
2939 2937
         } elseif (is_numeric($substr)) {
2940
-            $substr = (float)$substr;
2941
-            if ((int)$substr == $substr) {
2942
-                $substr = (int)$substr;
2938
+            $substr = (float) $substr;
2939
+            if ((int) $substr == $substr) {
2940
+                $substr = (int) $substr;
2943 2941
             }
2944 2942
             $type = self::T_NUMERIC;
2945 2943
             if ($this->debug) {
2946
-                echo 'NUMBER (' . $substr . ') PARSED' . "\n";
2944
+                echo 'NUMBER ('.$substr.') PARSED'."\n";
2947 2945
             }
2948 2946
         } elseif (preg_match('{^-?(\d+|\d*(\.\d+))\s*([/*%+-]\s*-?(\d+|\d*(\.\d+)))+$}', $substr)) {
2949 2947
             if ($this->debug) {
2950 2948
                 echo 'SIMPLE MATH PARSED . "\n"';
2951 2949
             }
2952 2950
             $type   = self::T_MATH;
2953
-            $substr = '(' . $substr . ')';
2951
+            $substr = '('.$substr.')';
2954 2952
         } elseif ($curBlock === 'condition' && array_search($substr, $breakChars, true) !== false) {
2955 2953
             if ($this->debug) {
2956
-                echo 'BREAKCHAR (' . $substr . ') PARSED' . "\n";
2954
+                echo 'BREAKCHAR ('.$substr.') PARSED'."\n";
2957 2955
             }
2958 2956
             $type = self::T_BREAKCHAR;
2959 2957
             //$substr = '"'.$substr.'"';
2960 2958
         } else {
2961
-            $substr = $this->replaceStringVars('\'' . str_replace('\'', '\\\'', $substr) . '\'', '\'', $curBlock);
2959
+            $substr = $this->replaceStringVars('\''.str_replace('\'', '\\\'', $substr).'\'', '\'', $curBlock);
2962 2960
             $type   = self::T_UNQUOTED_STRING;
2963 2961
             if ($this->debug) {
2964
-                echo 'BLABBER (' . $substr . ') CASTED AS STRING' . "\n";
2962
+                echo 'BLABBER ('.$substr.') CASTED AS STRING'."\n";
2965 2963
             }
2966 2964
         }
2967 2965
 
@@ -2991,28 +2989,28 @@  discard block
 block discarded – undo
2991 2989
     {
2992 2990
         $pos = 0;
2993 2991
         if ($this->debug) {
2994
-            echo 'STRING VAR REPLACEMENT : ' . $string . "\n";
2992
+            echo 'STRING VAR REPLACEMENT : '.$string."\n";
2995 2993
         }
2996 2994
         // replace vars
2997 2995
         while (($pos = strpos($string, '$', $pos)) !== false) {
2998
-            $prev = substr($string, $pos - 1, 1);
2996
+            $prev = substr($string, $pos-1, 1);
2999 2997
             if ($prev === '\\') {
3000
-                ++ $pos;
2998
+                ++$pos;
3001 2999
                 continue;
3002 3000
             }
3003 3001
 
3004 3002
             $var = $this->parse($string, $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string')));
3005 3003
             $len = $var[0];
3006
-            $var = $this->parse(str_replace('\\' . $first, $first, $string), $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string')));
3004
+            $var = $this->parse(str_replace('\\'.$first, $first, $string), $pos, null, false, ($curBlock === 'modifier' ? 'modifier' : ($prev === '`' ? 'delimited_string' : 'string')));
3007 3005
 
3008
-            if ($prev === '`' && substr($string, $pos + $len, 1) === '`') {
3009
-                $string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos - 1, $len + 2);
3006
+            if ($prev === '`' && substr($string, $pos+$len, 1) === '`') {
3007
+                $string = substr_replace($string, $first.'.'.$var[1].'.'.$first, $pos-1, $len+2);
3010 3008
             } else {
3011
-                $string = substr_replace($string, $first . '.' . $var[1] . '.' . $first, $pos, $len);
3009
+                $string = substr_replace($string, $first.'.'.$var[1].'.'.$first, $pos, $len);
3012 3010
             }
3013
-            $pos += strlen($var[1]) + 2;
3011
+            $pos += strlen($var[1])+2;
3014 3012
             if ($this->debug) {
3015
-                echo 'STRING VAR REPLACEMENT DONE : ' . $string . "\n";
3013
+                echo 'STRING VAR REPLACEMENT DONE : '.$string."\n";
3016 3014
             }
3017 3015
         }
3018 3016
 
@@ -3048,7 +3046,7 @@  discard block
 block discarded – undo
3048 3046
     protected function replaceModifiers(array $m, $curBlock = null, &$pointer = null)
3049 3047
     {
3050 3048
         if ($this->debug) {
3051
-            echo 'PARSING MODIFIERS : ' . $m[3] . "\n";
3049
+            echo 'PARSING MODIFIERS : '.$m[3]."\n";
3052 3050
         }
3053 3051
 
3054 3052
         if ($pointer !== null) {
@@ -3072,7 +3070,7 @@  discard block
 block discarded – undo
3072 3070
             }
3073 3071
             if ($cmdstrsrc[0] === ' ' || $cmdstrsrc[0] === ';' || substr($cmdstrsrc, 0, strlen($this->rd)) === $this->rd) {
3074 3072
                 if ($this->debug) {
3075
-                    echo 'MODIFIER PARSING ENDED, RIGHT DELIMITER or ";" FOUND' . "\n";
3073
+                    echo 'MODIFIER PARSING ENDED, RIGHT DELIMITER or ";" FOUND'."\n";
3076 3074
                 }
3077 3075
                 $continue = false;
3078 3076
                 if ($pointer !== null) {
@@ -3083,7 +3081,7 @@  discard block
 block discarded – undo
3083 3081
             $cmdstr   = $cmdstrsrc;
3084 3082
             $paramsep = ':';
3085 3083
             if (!preg_match('/^(@{0,2}[a-z_][a-z0-9_]*)(:)?/i', $cmdstr, $match)) {
3086
-                throw new CompilationException($this, 'Invalid modifier name, started with : ' . substr($cmdstr, 0, 10));
3084
+                throw new CompilationException($this, 'Invalid modifier name, started with : '.substr($cmdstr, 0, 10));
3087 3085
             }
3088 3086
             $paramspos = !empty($match[2]) ? strlen($match[1]) : false;
3089 3087
             $func      = $match[1];
@@ -3093,10 +3091,10 @@  discard block
 block discarded – undo
3093 3091
                 $cmdstrsrc = substr($cmdstrsrc, strlen($func));
3094 3092
                 $params    = array();
3095 3093
                 if ($this->debug) {
3096
-                    echo 'MODIFIER (' . $func . ') CALLED WITH NO PARAMS' . "\n";
3094
+                    echo 'MODIFIER ('.$func.') CALLED WITH NO PARAMS'."\n";
3097 3095
                 }
3098 3096
             } else {
3099
-                $paramstr = substr($cmdstr, $paramspos + 1);
3097
+                $paramstr = substr($cmdstr, $paramspos+1);
3100 3098
                 if (substr($paramstr, - 1, 1) === $paramsep) {
3101 3099
                     $paramstr = substr($paramstr, 0, - 1);
3102 3100
                 }
@@ -3105,41 +3103,41 @@  discard block
 block discarded – undo
3105 3103
                 $params = array();
3106 3104
                 while ($ptr < strlen($paramstr)) {
3107 3105
                     if ($this->debug) {
3108
-                        echo 'MODIFIER (' . $func . ') START PARAM PARSING WITH POINTER AT ' . $ptr . "\n";
3106
+                        echo 'MODIFIER ('.$func.') START PARAM PARSING WITH POINTER AT '.$ptr."\n";
3109 3107
                     }
3110 3108
                     if ($this->debug) {
3111
-                        echo $paramstr . '--' . $ptr . '--' . strlen($paramstr) . '--modifier' . "\n";
3109
+                        echo $paramstr.'--'.$ptr.'--'.strlen($paramstr).'--modifier'."\n";
3112 3110
                     }
3113 3111
                     $params = $this->parse($paramstr, $ptr, strlen($paramstr), $params, 'modifier', $ptr);
3114 3112
                     if ($this->debug) {
3115
-                        echo 'PARAM PARSED, POINTER AT ' . $ptr . "\n";
3113
+                        echo 'PARAM PARSED, POINTER AT '.$ptr."\n";
3116 3114
                     }
3117 3115
 
3118 3116
                     if ($ptr >= strlen($paramstr)) {
3119 3117
                         if ($this->debug) {
3120
-                            echo 'PARAM PARSING ENDED, PARAM STRING CONSUMED' . "\n";
3118
+                            echo 'PARAM PARSING ENDED, PARAM STRING CONSUMED'."\n";
3121 3119
                         }
3122 3120
                         break;
3123 3121
                     }
3124 3122
 
3125 3123
                     if ($paramstr[$ptr] === ' ' || $paramstr[$ptr] === '|' || $paramstr[$ptr] === ';' || substr($paramstr, $ptr, strlen($this->rd)) === $this->rd) {
3126 3124
                         if ($this->debug) {
3127
-                            echo 'PARAM PARSING ENDED, " ", "|", RIGHT DELIMITER or ";" FOUND, POINTER AT ' . $ptr . "\n";
3125
+                            echo 'PARAM PARSING ENDED, " ", "|", RIGHT DELIMITER or ";" FOUND, POINTER AT '.$ptr."\n";
3128 3126
                         }
3129 3127
                         if ($paramstr[$ptr] !== '|') {
3130 3128
                             $continue = false;
3131 3129
                             if ($pointer !== null) {
3132
-                                $pointer -= strlen($paramstr) - $ptr;
3130
+                                $pointer -= strlen($paramstr)-$ptr;
3133 3131
                             }
3134 3132
                         }
3135
-                        ++ $ptr;
3133
+                        ++$ptr;
3136 3134
                         break;
3137 3135
                     }
3138 3136
                     if ($ptr < strlen($paramstr) && $paramstr[$ptr] === ':') {
3139
-                        ++ $ptr;
3137
+                        ++$ptr;
3140 3138
                     }
3141 3139
                 }
3142
-                $cmdstrsrc = substr($cmdstrsrc, strlen($func) + 1 + $ptr);
3140
+                $cmdstrsrc = substr($cmdstrsrc, strlen($func)+1+$ptr);
3143 3141
                 foreach ($params as $k => $p) {
3144 3142
                     if (is_array($p) && is_array($p[1])) {
3145 3143
                         $state |= 2;
@@ -3179,9 +3177,9 @@  discard block
 block discarded – undo
3179 3177
                 $params = self::implode_r($params);
3180 3178
 
3181 3179
                 if ($mapped) {
3182
-                    $output = '$this->arrayMap(\'' . $func . '\', array(' . $params . '))';
3180
+                    $output = '$this->arrayMap(\''.$func.'\', array('.$params.'))';
3183 3181
                 } else {
3184
-                    $output = $func . '(' . $params . ')';
3182
+                    $output = $func.'('.$params.')';
3185 3183
                 }
3186 3184
             } elseif ($pluginType & Core::PROXY_PLUGIN) {
3187 3185
                 $params = $this->mapParams($params, $this->getDwoo()->getPluginProxy()->getCallback($func), $state);
@@ -3199,36 +3197,36 @@  discard block
 block discarded – undo
3199 3197
                     $callback = $this->customPlugins[$func]['callback'];
3200 3198
                     if (is_array($callback)) {
3201 3199
                         if (is_object($callback[0])) {
3202
-                            $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(' . $params . '))';
3200
+                            $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array').'(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), array('.$params.'))';
3203 3201
                         } else {
3204
-                            $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))';
3202
+                            $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array').'(array(\''.$callback[0].'\', \''.$callback[1].'\'), array('.$params.'))';
3205 3203
                         }
3206 3204
                     } elseif ($mapped) {
3207
-                        $output = '$this->arrayMap(\'' . $callback . '\', array(' . $params . '))';
3205
+                        $output = '$this->arrayMap(\''.$callback.'\', array('.$params.'))';
3208 3206
                     } else {
3209
-                        $output = $callback . '(' . $params . ')';
3207
+                        $output = $callback.'('.$params.')';
3210 3208
                     }
3211 3209
                 } elseif ($mapped) {
3212
-                    $output = '$this->arrayMap(\'smarty_modifier_' . $func . '\', array(' . $params . '))';
3210
+                    $output = '$this->arrayMap(\'smarty_modifier_'.$func.'\', array('.$params.'))';
3213 3211
                 } else {
3214
-                    $output = 'smarty_modifier_' . $func . '(' . $params . ')';
3212
+                    $output = 'smarty_modifier_'.$func.'('.$params.')';
3215 3213
                 }
3216 3214
             } else {
3217 3215
                 if ($pluginType & Core::CUSTOM_PLUGIN) {
3218 3216
                     $callback   = $this->customPlugins[$func]['callback'];
3219 3217
                     $pluginName = $callback;
3220 3218
                 } else {
3221
-                    if (class_exists('Plugin' . Core::toCamelCase($func)) !== false || function_exists('Plugin' .
3222
-                            Core::toCamelCase($func) . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''))
3219
+                    if (class_exists('Plugin'.Core::toCamelCase($func)) !== false || function_exists('Plugin'.
3220
+                            Core::toCamelCase($func).(($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : ''))
3223 3221
                         !== false) {
3224
-                        $pluginName = 'Plugin' . Core::toCamelCase($func);
3222
+                        $pluginName = 'Plugin'.Core::toCamelCase($func);
3225 3223
                     } else {
3226
-                        $pluginName = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func);
3224
+                        $pluginName = Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func);
3227 3225
                     }
3228 3226
                     if ($pluginType & Core::CLASS_PLUGIN) {
3229 3227
                         $callback = array($pluginName, ($pluginType & Core::COMPILABLE_PLUGIN) ? 'compile' : 'process');
3230 3228
                     } else {
3231
-                        $callback = $pluginName . (($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '');
3229
+                        $callback = $pluginName.(($pluginType & Core::COMPILABLE_PLUGIN) ? 'Compile' : '');
3232 3230
                     }
3233 3231
                 }
3234 3232
                 $params = $this->mapParams($params, $callback, $state);
@@ -3246,10 +3244,10 @@  discard block
 block discarded – undo
3246 3244
                         if ($pluginType & Core::CUSTOM_PLUGIN) {
3247 3245
                             $funcCompiler = $this->customPlugins[$func]['callback'];
3248 3246
                         } else {
3249
-                            if (function_exists('Plugin' . Core::toCamelCase($func) . 'Compile') !== false) {
3250
-                                $funcCompiler = 'Plugin' . Core::toCamelCase($func) . 'Compile';
3247
+                            if (function_exists('Plugin'.Core::toCamelCase($func).'Compile') !== false) {
3248
+                                $funcCompiler = 'Plugin'.Core::toCamelCase($func).'Compile';
3251 3249
                             } else {
3252
-                                $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) .
3250
+                                $funcCompiler = Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func).
3253 3251
                                     'Compile';
3254 3252
                             }
3255 3253
                         }
@@ -3260,9 +3258,9 @@  discard block
 block discarded – undo
3260 3258
 
3261 3259
                         $params = self::implode_r($params);
3262 3260
                         if ($mapped) {
3263
-                            $output = '$this->arrayMap(\'' . $pluginName . '\', array(' . $params . '))';
3261
+                            $output = '$this->arrayMap(\''.$pluginName.'\', array('.$params.'))';
3264 3262
                         } else {
3265
-                            $output = $pluginName . '(' . $params . ')';
3263
+                            $output = $pluginName.'('.$params.')';
3266 3264
                         }
3267 3265
                     }
3268 3266
                 } else {
@@ -3274,7 +3272,7 @@  discard block
 block discarded – undo
3274 3272
                             $callback = $this->customPlugins[$func]['callback'];
3275 3273
                             if (!is_array($callback)) {
3276 3274
                                 if (!method_exists($callback, 'compile')) {
3277
-                                    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');
3275
+                                    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');
3278 3276
                                 }
3279 3277
                                 if (($ref = new ReflectionMethod($callback, 'compile')) && $ref->isStatic()) {
3280 3278
                                     $funcCompiler = array($callback, 'compile');
@@ -3285,11 +3283,11 @@  discard block
 block discarded – undo
3285 3283
                                 $funcCompiler = $callback;
3286 3284
                             }
3287 3285
                         } else {
3288
-                            if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
3289
-                                $funcCompiler = array('Plugin' . Core::toCamelCase($func), 'compile');
3286
+                            if (class_exists('Plugin'.Core::toCamelCase($func)) !== false) {
3287
+                                $funcCompiler = array('Plugin'.Core::toCamelCase($func), 'compile');
3290 3288
                             } else {
3291 3289
                                 $funcCompiler = array(
3292
-                                    Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func),
3290
+                                    Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func),
3293 3291
                                     'compile'
3294 3292
                                 );
3295 3293
                             }
@@ -3301,23 +3299,23 @@  discard block
 block discarded – undo
3301 3299
 
3302 3300
                         if ($pluginType & Core::CUSTOM_PLUGIN) {
3303 3301
                             if (is_object($callback[0])) {
3304
-                                $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array($this->plugins[\'' . $func . '\'][\'callback\'][0], \'' . $callback[1] . '\'), array(' . $params . '))';
3302
+                                $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array').'(array($this->plugins[\''.$func.'\'][\'callback\'][0], \''.$callback[1].'\'), array('.$params.'))';
3305 3303
                             } else {
3306
-                                $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array') . '(array(\'' . $callback[0] . '\', \'' . $callback[1] . '\'), array(' . $params . '))';
3304
+                                $output = ($mapped ? '$this->arrayMap' : 'call_user_func_array').'(array(\''.$callback[0].'\', \''.$callback[1].'\'), array('.$params.'))';
3307 3305
                             }
3308 3306
                         } elseif ($mapped) {
3309 3307
                             $output = '$this->arrayMap(array($this->getObjectPlugin(\''.
3310
-                                Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func) . '\'), 
3311
-                            \'process\'), array(' . $params . '))';
3308
+                                Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func).'\'), 
3309
+                            \'process\'), array(' . $params.'))';
3312 3310
                         } else {
3313
-                            if (class_exists('Plugin' . Core::toCamelCase($func)) !== false) {
3314
-                                $output = '$this->classCall(\'Plugin' . Core::toCamelCase($func) . '\', array(' . $params . '))';
3315
-                            } elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($func)) !== false) {
3316
-                                $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . $func . '\', array(' . $params . '))';
3317
-                            } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($func)) !== false) {
3318
-                                $output = '$this->classCall(\'' . Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . $func . '\', array(' . $params . '))';
3311
+                            if (class_exists('Plugin'.Core::toCamelCase($func)) !== false) {
3312
+                                $output = '$this->classCall(\'Plugin'.Core::toCamelCase($func).'\', array('.$params.'))';
3313
+                            } elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS.'Plugin'.Core::toCamelCase($func)) !== false) {
3314
+                                $output = '$this->classCall(\''.Core::NAMESPACE_PLUGINS_BLOCKS.'Plugin'.$func.'\', array('.$params.'))';
3315
+                            } elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($func)) !== false) {
3316
+                                $output = '$this->classCall(\''.Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.$func.'\', array('.$params.'))';
3319 3317
                             } else {
3320
-                                $output = '$this->classCall(\'' . $func . '\', array(' . $params . '))';
3318
+                                $output = '$this->classCall(\''.$func.'\', array('.$params.'))';
3321 3319
                             }
3322 3320
                         }
3323 3321
                     }
@@ -3330,7 +3328,7 @@  discard block
 block discarded – undo
3330 3328
         } elseif ($curBlock === 'var' || $m[1] === null) {
3331 3329
             return $output;
3332 3330
         } elseif ($curBlock === 'string' || $curBlock === 'root') {
3333
-            return $m[1] . '.' . $output . '.' . $m[1] . (isset($add) ? $add : null);
3331
+            return $m[1].'.'.$output.'.'.$m[1].(isset($add) ? $add : null);
3334 3332
         }
3335 3333
 
3336 3334
         return '';
@@ -3353,14 +3351,14 @@  discard block
 block discarded – undo
3353 3351
             if (is_array($p)) {
3354 3352
                 $out2 = 'array(';
3355 3353
                 foreach ($p as $k2 => $v) {
3356
-                    $out2 .= var_export($k2, true) . ' => ' . (is_array($v) ? 'array(' . self::implode_r($v, true) . ')' : $v) . ', ';
3354
+                    $out2 .= var_export($k2, true).' => '.(is_array($v) ? 'array('.self::implode_r($v, true).')' : $v).', ';
3357 3355
                 }
3358
-                $p = rtrim($out2, ', ') . ')';
3356
+                $p = rtrim($out2, ', ').')';
3359 3357
             }
3360 3358
             if ($recursiveCall) {
3361
-                $out .= var_export($k, true) . ' => ' . $p . ', ';
3359
+                $out .= var_export($k, true).' => '.$p.', ';
3362 3360
             } else {
3363
-                $out .= $p . ', ';
3361
+                $out .= $p.', ';
3364 3362
             }
3365 3363
         }
3366 3364
 
@@ -3384,7 +3382,7 @@  discard block
 block discarded – undo
3384 3382
         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)) {
3385 3383
             $phpFunc = true;
3386 3384
         } elseif ($this->securityPolicy !== null && function_exists($name) && array_key_exists(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) === false) {
3387
-            throw new SecurityException('Call to a disallowed php function : ' . $name);
3385
+            throw new SecurityException('Call to a disallowed php function : '.$name);
3388 3386
         }
3389 3387
 
3390 3388
         while ($pluginType <= 0) {
@@ -3395,58 +3393,58 @@  discard block
 block discarded – undo
3395 3393
             elseif (isset($this->customPlugins[$name])) {
3396 3394
                 $pluginType = $this->customPlugins[$name]['type'] | Core::CUSTOM_PLUGIN;
3397 3395
             } // Class blocks plugin
3398
-            elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name)) !== false) {
3396
+            elseif (class_exists(Core::NAMESPACE_PLUGINS_BLOCKS.'Plugin'.Core::toCamelCase($name)) !== false) {
3399 3397
                 $pluginType = Core::CLASS_PLUGIN;
3400
-                if (is_subclass_of(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name), 'Dwoo\Block\Plugin')) {
3398
+                if (is_subclass_of(Core::NAMESPACE_PLUGINS_BLOCKS.'Plugin'.Core::toCamelCase($name), 'Dwoo\Block\Plugin')) {
3401 3399
                     $pluginType += Core::BLOCK_PLUGIN;
3402 3400
                 }
3403
-                $interfaces = class_implements(Core::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . Core::toCamelCase($name));
3401
+                $interfaces = class_implements(Core::NAMESPACE_PLUGINS_BLOCKS.'Plugin'.Core::toCamelCase($name));
3404 3402
                 if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
3405 3403
                     $pluginType |= Core::COMPILABLE_PLUGIN;
3406 3404
                 }
3407 3405
             } // Class functions plugin
3408
-            elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name)) !== false) {
3409
-                $pluginType = Core::FUNC_PLUGIN + Core::CLASS_PLUGIN;
3410
-                $interfaces = class_implements(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name));
3406
+            elseif (class_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($name)) !== false) {
3407
+                $pluginType = Core::FUNC_PLUGIN+Core::CLASS_PLUGIN;
3408
+                $interfaces = class_implements(Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($name));
3411 3409
                 if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
3412 3410
                     $pluginType |= Core::COMPILABLE_PLUGIN;
3413 3411
                 }
3414 3412
             } // Class without namespace
3415
-            elseif (class_exists('Plugin' . Core::toCamelCase($name)) !== false) {
3413
+            elseif (class_exists('Plugin'.Core::toCamelCase($name)) !== false) {
3416 3414
                 $pluginType = Core::CLASS_PLUGIN;
3417
-                $interfaces = class_implements('Plugin' . Core::toCamelCase($name));
3415
+                $interfaces = class_implements('Plugin'.Core::toCamelCase($name));
3418 3416
                 if (in_array('Dwoo\ICompilable', $interfaces) !== false || in_array('Dwoo\ICompilable\Block', $interfaces) !== false) {
3419 3417
                     $pluginType |= Core::COMPILABLE_PLUGIN;
3420 3418
                 }
3421 3419
             } // Function plugin (with/without namespaces)
3422
-            elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase ($name)) !==
3423
-                false || function_exists('Plugin' . Core::toCamelCase($name)) !== false) {
3420
+            elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($name)) !==
3421
+                false || function_exists('Plugin'.Core::toCamelCase($name)) !== false) {
3424 3422
                 $pluginType = Core::FUNC_PLUGIN;
3425 3423
             } // Function plugin compile (with/without namespaces)
3426
-            elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin' . Core::toCamelCase($name) .
3427
-                    'Compile') !== false || function_exists('Plugin' . Core::toCamelCase($name) . 'Compile') !==
3424
+            elseif (function_exists(Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin'.Core::toCamelCase($name).
3425
+                    'Compile') !== false || function_exists('Plugin'.Core::toCamelCase($name).'Compile') !==
3428 3426
                 false) {
3429 3427
                 $pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN;
3430 3428
             } // Helper plugin class compile
3431
-            elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name)) !== false) {
3429
+            elseif (class_exists(Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($name)) !== false) {
3432 3430
                 $pluginType = Core::CLASS_PLUGIN | Core::COMPILABLE_PLUGIN;
3433 3431
             } // Helper plugin function compile
3434
-            elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS . 'Plugin' . Core::toCamelCase($name) . 'Compile') !== false) {
3432
+            elseif (function_exists(Core::NAMESPACE_PLUGINS_HELPERS.'Plugin'.Core::toCamelCase($name).'Compile') !== false) {
3435 3433
                 $pluginType = Core::FUNC_PLUGIN | Core::COMPILABLE_PLUGIN;
3436 3434
             } // Smarty modifier
3437
-            elseif (function_exists('smarty_modifier_' . $name) !== false) {
3435
+            elseif (function_exists('smarty_modifier_'.$name) !== false) {
3438 3436
                 $pluginType = Core::SMARTY_MODIFIER;
3439 3437
             } // Smarty function
3440
-            elseif (function_exists('smarty_function_' . $name) !== false) {
3438
+            elseif (function_exists('smarty_function_'.$name) !== false) {
3441 3439
                 $pluginType = Core::SMARTY_FUNCTION;
3442 3440
             } // Smarty block
3443
-            elseif (function_exists('smarty_block_' . $name) !== false) {
3441
+            elseif (function_exists('smarty_block_'.$name) !== false) {
3444 3442
                 $pluginType = Core::SMARTY_BLOCK;
3445 3443
             } // Everything else
3446 3444
             else {
3447 3445
                 if ($pluginType === - 1) {
3448 3446
                     try {
3449
-                        $this->getDwoo()->getLoader()->loadPlugin('Plugin' . Core::toCamelCase($name));
3447
+                        $this->getDwoo()->getLoader()->loadPlugin('Plugin'.Core::toCamelCase($name));
3450 3448
                     }
3451 3449
                     catch (Exception $e) {
3452 3450
                         if (isset($phpFunc)) {
@@ -3459,9 +3457,9 @@  discard block
 block discarded – undo
3459 3457
                         }
3460 3458
                     }
3461 3459
                 } else {
3462
-                    throw new Exception('Plugin "' . $name . '" could not be found, type:' . $pluginType);
3460
+                    throw new Exception('Plugin "'.$name.'" could not be found, type:'.$pluginType);
3463 3461
                 }
3464
-                ++ $pluginType;
3462
+                ++$pluginType;
3465 3463
             }
3466 3464
         }
3467 3465
 
@@ -3530,15 +3528,15 @@  discard block
 block discarded – undo
3530 3528
         }
3531 3529
 
3532 3530
         // loops over the param map and assigns values from the template or default value for unset optional params
3533
-        foreach ($map as $k => $v){
3531
+        foreach ($map as $k => $v) {
3534 3532
             if ($v[0] === '*') {
3535 3533
                 // "rest" array parameter, fill every remaining params in it and then break
3536 3534
                 if (count($ps) === 0) {
3537 3535
                     if ($v[1] === false) {
3538 3536
                         throw new CompilationException(
3539
-                            $this, 'Rest argument missing for ' . str_replace(
3537
+                            $this, 'Rest argument missing for '.str_replace(
3540 3538
                                 array(
3541
-                                    Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin',
3539
+                                    Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin',
3542 3540
                                 'Compile'
3543 3541
                                 ), '', (is_array($callback) ? $callback[0] : $callback)
3544 3542
                             )
@@ -3571,7 +3569,7 @@  discard block
 block discarded – undo
3571 3569
                 // parameter is not defined and not optional, throw error
3572 3570
                 if (is_array($callback)) {
3573 3571
                     if (is_object($callback[0])) {
3574
-                        $name = get_class($callback[0]) . '::' . $callback[1];
3572
+                        $name = get_class($callback[0]).'::'.$callback[1];
3575 3573
                     } else {
3576 3574
                         $name = $callback[0];
3577 3575
                     }
@@ -3580,9 +3578,9 @@  discard block
 block discarded – undo
3580 3578
                 }
3581 3579
 
3582 3580
                 throw new CompilationException(
3583
-                    $this, 'Argument ' . $k . '/' . $v[0] . ' missing for ' . str_replace(
3581
+                    $this, 'Argument '.$k.'/'.$v[0].' missing for '.str_replace(
3584 3582
                         array(
3585
-                            Core::NAMESPACE_PLUGINS_FUNCTIONS . 'Plugin',
3583
+                            Core::NAMESPACE_PLUGINS_FUNCTIONS.'Plugin',
3586 3584
                         'Compile'
3587 3585
                         ), '', $name
3588 3586
                     )
Please login to merge, or discard this patch.