assigning incompatible types to properties.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Copyright (c) 2013-2017 |
||
4 | * |
||
5 | * @category Library |
||
6 | * @package Dwoo |
||
7 | * @author Jordi Boggiano <[email protected]> |
||
8 | * @author David Sanchez <[email protected]> |
||
9 | * @copyright 2008-2013 Jordi Boggiano |
||
10 | * @copyright 2013-2017 David Sanchez |
||
11 | * @license http://dwoo.org/LICENSE Modified BSD License |
||
12 | * @version 1.3.2 |
||
13 | * @date 2017-01-04 |
||
14 | * @link http://dwoo.org/ |
||
15 | */ |
||
16 | |||
17 | namespace Dwoo; |
||
18 | |||
19 | use ArrayAccess; |
||
20 | use Closure; |
||
21 | use Countable; |
||
22 | use Dwoo\Plugins\Blocks\PluginDynamic; |
||
23 | use Dwoo\Security\Policy as SecurityPolicy; |
||
24 | use Dwoo\Block\Plugin as BlockPlugin; |
||
25 | use Dwoo\Template\File as TemplateFile; |
||
26 | use Iterator; |
||
27 | use stdClass; |
||
28 | use Traversable; |
||
29 | |||
30 | /** |
||
31 | * Main dwoo class, allows communication between the compiler, template and data classes. |
||
32 | * <pre> |
||
33 | * requirements : |
||
34 | * php 5.3.0 or above (might work below, it's a rough estimate) |
||
35 | * SPL and PCRE extensions (for php versions prior to 5.3.0) |
||
36 | * mbstring extension for some string manipulation plugins (especially if you intend to use UTF-8) |
||
37 | * recommended : |
||
38 | * hash extension (for Dwoo\Template\Str - minor performance boost) |
||
39 | * project created : |
||
40 | * 2008-01-05 |
||
41 | * </pre> |
||
42 | * This software is provided 'as-is', without any express or implied warranty. |
||
43 | * In no event will the authors be held liable for any damages arising from the use of this software. |
||
44 | */ |
||
45 | class Core |
||
46 | { |
||
47 | /** |
||
48 | * Current version number. |
||
49 | * |
||
50 | * @var string |
||
51 | */ |
||
52 | const VERSION = '1.3.2'; |
||
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 = 132; |
||
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( |
||
333 | 'Dwoo->get\'s first argument must be a ITemplate (i.e. TemplateFile) or |
||
334 | a valid path to a template file', E_USER_NOTICE |
||
335 | ); |
||
336 | } |
||
337 | |||
338 | // save the current template, enters render mode at the same time |
||
339 | // if another rendering is requested it will be proxied to a new Core(instance |
||
340 | $this->template = $_tpl; |
||
341 | |||
342 | // load data |
||
343 | if ($data instanceof IDataProvider) { |
||
344 | $this->data = $data->getData(); |
||
345 | } elseif (is_array($data)) { |
||
346 | $this->data = $data; |
||
347 | } elseif ($data instanceof ArrayAccess) { |
||
348 | $this->data = $data; |
||
0 ignored issues
–
show
|
|||
349 | } else { |
||
350 | throw new Exception( |
||
351 | 'Dwoo->get/Dwoo->output\'s data argument must be a IDataProvider object (i.e. Data) or |
||
352 | an associative array', E_USER_NOTICE |
||
353 | ); |
||
354 | } |
||
355 | |||
356 | $this->addGlobal('template', $_tpl->getName()); |
||
357 | $this->initRuntimeVars($_tpl); |
||
358 | |||
359 | // try to get cached template |
||
360 | $file = $_tpl->getCachedTemplate($this); |
||
361 | $doCache = $file === true; |
||
362 | $cacheLoaded = is_string($file); |
||
363 | |||
364 | if ($cacheLoaded === true) { |
||
365 | // cache is present, run it |
||
366 | ob_start(); |
||
367 | include $file; |
||
368 | $this->template = null; |
||
369 | |||
370 | return ob_get_clean(); |
||
371 | } else { |
||
372 | $dynamicId = uniqid(); |
||
373 | |||
374 | // render template |
||
375 | $compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler); |
||
376 | $out = include $compiledTemplate; |
||
377 | |||
378 | // template returned false so it needs to be recompiled |
||
379 | if ($out === false) { |
||
380 | $_tpl->forceCompilation(); |
||
381 | $compiledTemplate = $_tpl->getCompiledTemplate($this, $_compiler); |
||
382 | $out = include $compiledTemplate; |
||
383 | } |
||
384 | |||
385 | if ($doCache === true) { |
||
386 | $out = preg_replace('/(<%|%>|<\?php|<\?|\?>)/', '<?php /*' . $dynamicId . '*/ echo \'$1\'; ?>', $out); |
||
387 | if (!class_exists(self::NAMESPACE_PLUGINS_BLOCKS . 'PluginDynamic')) { |
||
388 | $this->getLoader()->loadPlugin('PluginDynamic'); |
||
389 | } |
||
390 | $out = PluginDynamic::unescape($out, $dynamicId, $compiledTemplate); |
||
391 | } |
||
392 | |||
393 | // process filters |
||
394 | foreach ($this->filters as $filter) { |
||
395 | if (is_array($filter) && $filter[0] instanceof Filter) { |
||
396 | $out = call_user_func($filter, $out); |
||
397 | } else { |
||
398 | $out = call_user_func($filter, $this, $out); |
||
399 | } |
||
400 | } |
||
401 | |||
402 | if ($doCache === true) { |
||
403 | // building cache |
||
404 | $file = $_tpl->cache($this, $out); |
||
405 | |||
406 | // run it from the cache to be sure dynamics are rendered |
||
407 | ob_start(); |
||
408 | include $file; |
||
409 | // exit render mode |
||
410 | $this->template = null; |
||
411 | |||
412 | return ob_get_clean(); |
||
413 | } else { |
||
414 | // no need to build cache |
||
415 | // exit render mode |
||
416 | $this->template = null; |
||
417 | |||
418 | return $out; |
||
419 | } |
||
420 | } |
||
421 | } |
||
422 | |||
423 | /** |
||
424 | * Registers a Global. |
||
425 | * New globals can be added before compiling or rendering a template |
||
426 | * but after, you can only update existing globals. |
||
427 | * |
||
428 | * @param string $name |
||
429 | * @param mixed $value |
||
430 | * |
||
431 | * @return $this |
||
432 | * @throws Exception |
||
433 | */ |
||
434 | public function addGlobal($name, $value) |
||
435 | { |
||
436 | if (null === $this->globals) { |
||
437 | $this->initGlobals(); |
||
438 | } |
||
439 | |||
440 | $this->globals[$name] = $value; |
||
441 | |||
442 | return $this; |
||
443 | } |
||
444 | |||
445 | /** |
||
446 | * Gets the registered Globals. |
||
447 | * |
||
448 | * @return array |
||
449 | */ |
||
450 | public function getGlobals() |
||
451 | { |
||
452 | return $this->globals; |
||
453 | } |
||
454 | |||
455 | /** |
||
456 | * Re-initializes the globals array before each template run. |
||
457 | * this method is only callede once when the Dwoo object is created |
||
458 | * |
||
459 | * @return void |
||
460 | */ |
||
461 | protected function initGlobals() |
||
462 | { |
||
463 | $this->globals = array( |
||
464 | 'version' => self::VERSION, |
||
465 | 'ad' => '<a href="http://dwoo.org/">Powered by Dwoo</a>', |
||
466 | 'now' => $_SERVER['REQUEST_TIME'], |
||
467 | 'charset' => $this->getCharset(), |
||
468 | ); |
||
469 | } |
||
470 | |||
471 | /** |
||
472 | * Re-initializes the runtime variables before each template run. |
||
473 | * override this method to inject data in the globals array if needed, this |
||
474 | * method is called before each template execution |
||
475 | * |
||
476 | * @param ITemplate $tpl the template that is going to be rendered |
||
477 | * |
||
478 | * @return void |
||
479 | */ |
||
480 | protected function initRuntimeVars(ITemplate $tpl) |
||
481 | { |
||
482 | $this->runtimePlugins = array(); |
||
483 | $this->scope = &$this->data; |
||
484 | $this->scopeTree = array(); |
||
485 | $this->stack = array(); |
||
486 | $this->curBlock = null; |
||
487 | $this->buffer = ''; |
||
488 | $this->returnData = array(); |
||
489 | } |
||
490 | |||
491 | /** |
||
492 | * Adds a custom plugin that is not in one of the plugin directories. |
||
493 | * |
||
494 | * @param string $name the plugin name to be used in the templates |
||
495 | * @param callback $callback the plugin callback, either a function name, |
||
496 | * a class name or an array containing an object |
||
497 | * or class name and a method name |
||
498 | * @param bool $compilable if set to true, the plugin is assumed to be compilable |
||
499 | * |
||
500 | * @return void |
||
501 | * @throws Exception |
||
502 | */ |
||
503 | public function addPlugin($name, $callback, $compilable = false) |
||
504 | { |
||
505 | $compilable = $compilable ? self::COMPILABLE_PLUGIN : 0; |
||
506 | if (is_array($callback)) { |
||
507 | if (is_subclass_of(is_object($callback[0]) ? get_class($callback[0]) : $callback[0], 'Dwoo\Block\Plugin')) { |
||
508 | $this->plugins[$name] = array( |
||
509 | 'type' => self::BLOCK_PLUGIN | $compilable, |
||
510 | 'callback' => $callback, |
||
511 | 'class' => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0]) |
||
512 | ); |
||
513 | } else { |
||
514 | $this->plugins[$name] = array( |
||
515 | 'type' => self::CLASS_PLUGIN | $compilable, |
||
516 | 'callback' => $callback, |
||
517 | 'class' => (is_object($callback[0]) ? get_class($callback[0]) : $callback[0]), |
||
518 | 'function' => $callback[1] |
||
519 | ); |
||
520 | } |
||
521 | } elseif (is_string($callback)) { |
||
522 | if (class_exists($callback)) { |
||
523 | if (is_subclass_of($callback, 'Dwoo\Block\Plugin')) { |
||
524 | $this->plugins[$name] = array( |
||
525 | 'type' => self::BLOCK_PLUGIN | $compilable, |
||
526 | 'callback' => $callback, |
||
527 | 'class' => $callback |
||
528 | ); |
||
529 | } else { |
||
530 | $this->plugins[$name] = array( |
||
531 | 'type' => self::CLASS_PLUGIN | $compilable, |
||
532 | 'callback' => $callback, |
||
533 | 'class' => $callback, |
||
534 | 'function' => ($compilable ? 'compile' : 'process') |
||
535 | ); |
||
536 | } |
||
537 | } elseif (function_exists($callback)) { |
||
538 | $this->plugins[$name] = array( |
||
539 | 'type' => self::FUNC_PLUGIN | $compilable, |
||
540 | 'callback' => $callback |
||
541 | ); |
||
542 | } else { |
||
543 | throw new Exception( |
||
544 | 'Callback could not be processed correctly, please check that the function/class |
||
545 | you used exists' |
||
546 | ); |
||
547 | } |
||
548 | } elseif ($callback instanceof Closure) { |
||
549 | $this->plugins[$name] = array( |
||
550 | 'type' => self::FUNC_PLUGIN | $compilable, |
||
551 | 'callback' => $callback |
||
552 | ); |
||
553 | } else { |
||
554 | throw new Exception( |
||
555 | 'Callback could not be processed correctly, please check that the function/class you |
||
556 | used exists' |
||
557 | ); |
||
558 | } |
||
559 | } |
||
560 | |||
561 | /** |
||
562 | * Removes a custom plugin. |
||
563 | * |
||
564 | * @param string $name the plugin name |
||
565 | * |
||
566 | * @return void |
||
567 | */ |
||
568 | public function removePlugin($name) |
||
569 | { |
||
570 | if (isset($this->plugins[$name])) { |
||
571 | unset($this->plugins[$name]); |
||
572 | } |
||
573 | } |
||
574 | |||
575 | /** |
||
576 | * Adds a filter to this Dwoo instance, it will be used to filter the output of all the templates rendered by this |
||
577 | * instance. |
||
578 | * |
||
579 | * @param mixed $callback a callback or a filter name if it is autoloaded from a plugin directory |
||
580 | * @param bool $autoload if true, the first parameter must be a filter name from one of the plugin directories |
||
581 | * |
||
582 | * @return void |
||
583 | * @throws Exception |
||
584 | */ |
||
585 | public function addFilter($callback, $autoload = false) |
||
586 | { |
||
587 | if ($autoload) { |
||
588 | $class = self::NAMESPACE_PLUGINS_FILTERS . self::toCamelCase($callback); |
||
589 | if (!class_exists($class) && !function_exists($class)) { |
||
590 | try { |
||
591 | $this->getLoader()->loadPlugin($callback); |
||
592 | } |
||
593 | catch (Exception $e) { |
||
594 | if (strstr($callback, self::NAMESPACE_PLUGINS_FILTERS)) { |
||
595 | throw new Exception( |
||
596 | 'Wrong filter name : ' . $callback . ', the "Dwoo_Filter_" prefix should |
||
597 | not be used, please only use "' . str_replace('Dwoo_Filter_', '', $callback) . '"' |
||
598 | ); |
||
599 | } else { |
||
600 | throw new Exception( |
||
601 | 'Wrong filter name : ' . $callback . ', when using autoload the filter must |
||
602 | be in one of your plugin dir as "name.php" containig a class or function named |
||
603 | "Dwoo_Filter_name"' |
||
604 | ); |
||
605 | } |
||
606 | } |
||
607 | } |
||
608 | |||
609 | if (class_exists($class)) { |
||
610 | $callback = array(new $class($this), 'process'); |
||
611 | } elseif (function_exists($class)) { |
||
612 | $callback = $class; |
||
613 | } else { |
||
614 | throw new Exception( |
||
615 | 'Wrong filter name : ' . $callback . ', when using autoload the filter must be in |
||
616 | one of your plugin dir as "name.php" containig a class or function named "Dwoo_Filter_name"' |
||
617 | ); |
||
618 | } |
||
619 | |||
620 | $this->filters[] = $callback; |
||
621 | } else { |
||
622 | $this->filters[] = $callback; |
||
623 | } |
||
624 | } |
||
625 | |||
626 | /** |
||
627 | * Removes a filter. |
||
628 | * |
||
629 | * @param mixed $callback callback or filter name if it was autoloaded |
||
630 | * |
||
631 | * @return void |
||
632 | */ |
||
633 | public function removeFilter($callback) |
||
634 | { |
||
635 | if (($index = array_search(self::NAMESPACE_PLUGINS_FILTERS. 'Filter' . self::toCamelCase($callback), $this->filters, |
||
636 | true)) !== |
||
637 | false) { |
||
638 | unset($this->filters[$index]); |
||
639 | } elseif (($index = array_search($callback, $this->filters, true)) !== false) { |
||
640 | unset($this->filters[$index]); |
||
641 | } else { |
||
642 | $class = self::NAMESPACE_PLUGINS_FILTERS . 'Filter' . $callback; |
||
643 | foreach ($this->filters as $index => $filter) { |
||
644 | if (is_array($filter) && $filter[0] instanceof $class) { |
||
645 | unset($this->filters[$index]); |
||
646 | break; |
||
647 | } |
||
648 | } |
||
649 | } |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * Adds a resource or overrides a default one. |
||
654 | * |
||
655 | * @param string $name the resource name |
||
656 | * @param string $class the resource class (which must implement ITemplate) |
||
657 | * @param callback $compilerFactory the compiler factory callback, a function that must return a compiler instance |
||
658 | * used to compile this resource, if none is provided. by default it will produce |
||
659 | * a Compiler object |
||
660 | * |
||
661 | * @return void |
||
662 | * @throws Exception |
||
663 | */ |
||
664 | public function addResource($name, $class, $compilerFactory = null) |
||
665 | { |
||
666 | if (strlen($name) < 2) { |
||
667 | throw new Exception('Resource names must be at least two-character long to avoid conflicts with Windows paths'); |
||
668 | } |
||
669 | |||
670 | if (!class_exists($class)) { |
||
671 | throw new Exception(sprintf('Resource class %s does not exist', $class)); |
||
672 | } |
||
673 | |||
674 | $interfaces = class_implements($class); |
||
675 | if (in_array('Dwoo\ITemplate', $interfaces) === false) { |
||
676 | throw new Exception('Resource class must implement ITemplate'); |
||
677 | } |
||
678 | |||
679 | $this->resources[$name] = array( |
||
680 | 'class' => $class, |
||
681 | 'compiler' => $compilerFactory |
||
682 | ); |
||
683 | } |
||
684 | |||
685 | /** |
||
686 | * Removes a custom resource. |
||
687 | * |
||
688 | * @param string $name the resource name |
||
689 | * |
||
690 | * @return void |
||
691 | */ |
||
692 | public function removeResource($name) |
||
693 | { |
||
694 | unset($this->resources[$name]); |
||
695 | if ($name === 'file') { |
||
696 | $this->resources['file'] = array( |
||
697 | 'class' => 'Dwoo\Template\File', |
||
698 | 'compiler' => null |
||
699 | ); |
||
700 | } |
||
701 | } |
||
702 | |||
703 | /** |
||
704 | * Sets the loader object to use to load plugins. |
||
705 | * |
||
706 | * @param ILoader $loader loader |
||
707 | * |
||
708 | * @return void |
||
709 | */ |
||
710 | public function setLoader(ILoader $loader) |
||
711 | { |
||
712 | $this->loader = $loader; |
||
713 | } |
||
714 | |||
715 | /** |
||
716 | * Returns the current loader object or a default one if none is currently found. |
||
717 | * |
||
718 | * @return ILoader|Loader |
||
719 | */ |
||
720 | public function getLoader() |
||
721 | { |
||
722 | if ($this->loader === null) { |
||
723 | $this->loader = new Loader($this->getCompileDir()); |
||
724 | } |
||
725 | |||
726 | return $this->loader; |
||
727 | } |
||
728 | |||
729 | /** |
||
730 | * Returns the custom plugins loaded. |
||
731 | * Used by the ITemplate classes to pass the custom plugins to their ICompiler instance. |
||
732 | * |
||
733 | * @return array |
||
734 | */ |
||
735 | public function getCustomPlugins() |
||
736 | { |
||
737 | return $this->plugins; |
||
738 | } |
||
739 | |||
740 | /** |
||
741 | * Return a specified custom plugin loaded by his name. |
||
742 | * Used by the compiler, for executing a Closure. |
||
743 | * |
||
744 | * @param string $name |
||
745 | * |
||
746 | * @return mixed|null |
||
747 | */ |
||
748 | public function getCustomPlugin($name) |
||
749 | { |
||
750 | if (isset($this->plugins[$name])) { |
||
751 | return $this->plugins[$name]['callback']; |
||
752 | } |
||
753 | |||
754 | return null; |
||
755 | } |
||
756 | |||
757 | /** |
||
758 | * Returns the cache directory with a trailing DIRECTORY_SEPARATOR. |
||
759 | * |
||
760 | * @return string |
||
761 | */ |
||
762 | public function getCacheDir() |
||
763 | { |
||
764 | if ($this->cacheDir === null) { |
||
765 | $this->setCacheDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR); |
||
766 | } |
||
767 | |||
768 | return $this->cacheDir; |
||
769 | } |
||
770 | |||
771 | /** |
||
772 | * Sets the cache directory and automatically appends a DIRECTORY_SEPARATOR. |
||
773 | * |
||
774 | * @param string $dir the cache directory |
||
775 | * |
||
776 | * @return void |
||
777 | * @throws Exception |
||
778 | */ |
||
779 | View Code Duplication | public function setCacheDir($dir) |
|
780 | { |
||
781 | $this->cacheDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR; |
||
782 | if (is_writable($this->cacheDir) === false) { |
||
783 | throw new Exception('The cache directory must be writable, chmod "' . $this->cacheDir . '" to make it writable'); |
||
784 | } |
||
785 | } |
||
786 | |||
787 | /** |
||
788 | * Returns the compile directory with a trailing DIRECTORY_SEPARATOR. |
||
789 | * |
||
790 | * @return string |
||
791 | */ |
||
792 | public function getCompileDir() |
||
793 | { |
||
794 | if ($this->compileDir === null) { |
||
795 | $this->setCompileDir(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'compiled' . DIRECTORY_SEPARATOR); |
||
796 | } |
||
797 | |||
798 | return $this->compileDir; |
||
799 | } |
||
800 | |||
801 | /** |
||
802 | * Sets the compile directory and automatically appends a DIRECTORY_SEPARATOR. |
||
803 | * |
||
804 | * @param string $dir the compile directory |
||
805 | * |
||
806 | * @return void |
||
807 | * @throws Exception |
||
808 | */ |
||
809 | View Code Duplication | public function setCompileDir($dir) |
|
810 | { |
||
811 | $this->compileDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR; |
||
812 | if (is_writable($this->compileDir) === false) { |
||
813 | throw new Exception('The compile directory must be writable, chmod "' . $this->compileDir . '" to make it writable'); |
||
814 | } |
||
815 | } |
||
816 | |||
817 | /** |
||
818 | * Returns an array of the template directory with a trailing DIRECTORY_SEPARATOR |
||
819 | * |
||
820 | * @return array |
||
821 | */ |
||
822 | public function getTemplateDir() |
||
823 | { |
||
824 | return $this->templateDir; |
||
825 | } |
||
826 | |||
827 | /** |
||
828 | * sets the template directory and automatically appends a DIRECTORY_SEPARATOR |
||
829 | * template directory is stored in an array |
||
830 | * |
||
831 | * @param string $dir |
||
832 | * |
||
833 | * @throws Exception |
||
834 | */ |
||
835 | public function setTemplateDir($dir) |
||
836 | { |
||
837 | $tmpDir = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR; |
||
838 | if (is_dir($tmpDir) === false) { |
||
839 | throw new Exception('The template directory: "' . $tmpDir . '" does not exists, create the directory or specify an other location !'); |
||
840 | } |
||
841 | $this->templateDir[] = $tmpDir; |
||
842 | } |
||
843 | |||
844 | /** |
||
845 | * Returns the default cache time that is used with templates that do not have a cache time set. |
||
846 | * |
||
847 | * @return int the duration in seconds |
||
848 | */ |
||
849 | public function getCacheTime() |
||
850 | { |
||
851 | return $this->cacheTime; |
||
852 | } |
||
853 | |||
854 | /** |
||
855 | * Sets the default cache time to use with templates that do not have a cache time set. |
||
856 | * |
||
857 | * @param int $seconds the duration in seconds |
||
858 | * |
||
859 | * @return void |
||
860 | */ |
||
861 | public function setCacheTime($seconds) |
||
862 | { |
||
863 | $this->cacheTime = (int)$seconds; |
||
864 | } |
||
865 | |||
866 | /** |
||
867 | * Returns the character set used by the string manipulation plugins. |
||
868 | * the charset is automatically lowercased |
||
869 | * |
||
870 | * @return string |
||
871 | */ |
||
872 | public function getCharset() |
||
873 | { |
||
874 | return $this->charset; |
||
875 | } |
||
876 | |||
877 | /** |
||
878 | * Sets the character set used by the string manipulation plugins. |
||
879 | * the charset will be automatically lowercased |
||
880 | * |
||
881 | * @param string $charset the character set |
||
882 | * |
||
883 | * @return void |
||
884 | */ |
||
885 | public function setCharset($charset) |
||
886 | { |
||
887 | $this->charset = strtolower((string)$charset); |
||
888 | } |
||
889 | |||
890 | /** |
||
891 | * Returns the current template being rendered, when applicable, or null. |
||
892 | * |
||
893 | * @return ITemplate|null |
||
894 | */ |
||
895 | public function getTemplate() |
||
896 | { |
||
897 | return $this->template; |
||
898 | } |
||
899 | |||
900 | /** |
||
901 | * Sets the current template being rendered. |
||
902 | * |
||
903 | * @param ITemplate $tpl template object |
||
904 | * |
||
905 | * @return void |
||
906 | */ |
||
907 | public function setTemplate(ITemplate $tpl) |
||
908 | { |
||
909 | $this->template = $tpl; |
||
910 | } |
||
911 | |||
912 | /** |
||
913 | * Sets the default compiler factory function for the given resource name. |
||
914 | * a compiler factory must return a ICompiler object pre-configured to fit your needs |
||
915 | * |
||
916 | * @param string $resourceName the resource name (i.e. file, string) |
||
917 | * @param callback $compilerFactory the compiler factory callback |
||
918 | * |
||
919 | * @return void |
||
920 | */ |
||
921 | public function setDefaultCompilerFactory($resourceName, $compilerFactory) |
||
922 | { |
||
923 | $this->resources[$resourceName]['compiler'] = $compilerFactory; |
||
924 | } |
||
925 | |||
926 | /** |
||
927 | * Returns the default compiler factory function for the given resource name. |
||
928 | * |
||
929 | * @param string $resourceName the resource name |
||
930 | * |
||
931 | * @return callback the compiler factory callback |
||
932 | */ |
||
933 | public function getDefaultCompilerFactory($resourceName) |
||
934 | { |
||
935 | return $this->resources[$resourceName]['compiler']; |
||
936 | } |
||
937 | |||
938 | /** |
||
939 | * Sets the security policy object to enforce some php security settings. |
||
940 | * use this if untrusted persons can modify templates |
||
941 | * |
||
942 | * @param SecurityPolicy $policy the security policy object |
||
943 | * |
||
944 | * @return void |
||
945 | */ |
||
946 | public function setSecurityPolicy(SecurityPolicy $policy = null) |
||
947 | { |
||
948 | $this->securityPolicy = $policy; |
||
949 | } |
||
950 | |||
951 | /** |
||
952 | * Returns the current security policy object or null by default. |
||
953 | * |
||
954 | * @return SecurityPolicy|null the security policy object if any |
||
955 | */ |
||
956 | public function getSecurityPolicy() |
||
957 | { |
||
958 | return $this->securityPolicy; |
||
959 | } |
||
960 | |||
961 | /** |
||
962 | * Sets the object that must be used as a plugin proxy when plugin can't be found |
||
963 | * by dwoo's loader. |
||
964 | * |
||
965 | * @param IPluginProxy $pluginProxy the proxy object |
||
966 | * |
||
967 | * @return void |
||
968 | */ |
||
969 | public function setPluginProxy(IPluginProxy $pluginProxy) |
||
970 | { |
||
971 | $this->pluginProxy = $pluginProxy; |
||
972 | } |
||
973 | |||
974 | /** |
||
975 | * Returns the current plugin proxy object or null by default. |
||
976 | * |
||
977 | * @return IPluginProxy |
||
978 | */ |
||
979 | public function getPluginProxy() |
||
980 | { |
||
981 | return $this->pluginProxy; |
||
982 | } |
||
983 | |||
984 | /** |
||
985 | * Checks whether the given template is cached or not. |
||
986 | * |
||
987 | * @param ITemplate $tpl the template object |
||
988 | * |
||
989 | * @return bool |
||
990 | */ |
||
991 | public function isCached(ITemplate $tpl) |
||
992 | { |
||
993 | return is_string($tpl->getCachedTemplate($this)); |
||
994 | } |
||
995 | |||
996 | /** |
||
997 | * Clear templates inside the compiled directory. |
||
998 | * |
||
999 | * @return int |
||
1000 | */ |
||
1001 | public function clearCompiled() |
||
1002 | { |
||
1003 | $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCompileDir()), \RecursiveIteratorIterator::SELF_FIRST); |
||
1004 | $count = 0; |
||
1005 | foreach ($iterator as $file) { |
||
1006 | if ($file->isFile()) { |
||
1007 | $count += unlink($file->__toString()) ? 1 : 0; |
||
1008 | } |
||
1009 | } |
||
1010 | |||
1011 | return $count; |
||
1012 | } |
||
1013 | |||
1014 | /** |
||
1015 | * Clears the cached templates if they are older than the given time. |
||
1016 | * |
||
1017 | * @param int $olderThan minimum time (in seconds) required for a cached template to be cleared |
||
1018 | * |
||
1019 | * @return int the amount of templates cleared |
||
1020 | */ |
||
1021 | public function clearCache($olderThan = - 1) |
||
1022 | { |
||
1023 | $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->getCacheDir()), \RecursiveIteratorIterator::SELF_FIRST); |
||
1024 | $expired = time() - $olderThan; |
||
1025 | $count = 0; |
||
1026 | foreach ($iterator as $file) { |
||
1027 | if ($file->isFile() && $file->getCTime() < $expired) { |
||
1028 | $count += unlink((string)$file) ? 1 : 0; |
||
1029 | } |
||
1030 | } |
||
1031 | |||
1032 | return $count; |
||
1033 | } |
||
1034 | |||
1035 | /** |
||
1036 | * Fetches a template object of the given resource. |
||
1037 | * |
||
1038 | * @param string $resourceName the resource name (i.e. file, string) |
||
1039 | * @param string $resourceId the resource identifier (i.e. file path) |
||
1040 | * @param int $cacheTime the cache time setting for this resource |
||
1041 | * @param string $cacheId the unique cache identifier |
||
1042 | * @param string $compileId the unique compiler identifier |
||
1043 | * @param ITemplate $parentTemplate the parent template |
||
1044 | * |
||
1045 | * @return ITemplate |
||
1046 | * @throws Exception |
||
1047 | */ |
||
1048 | public function templateFactory($resourceName, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, ITemplate $parentTemplate = null) |
||
1049 | { |
||
1050 | if (isset($this->resources[$resourceName])) { |
||
1051 | /** |
||
1052 | * Interface ITemplate |
||
1053 | * |
||
1054 | * @var ITemplate $class |
||
1055 | */ |
||
1056 | $class = $this->resources[$resourceName]['class']; |
||
1057 | |||
1058 | return $class::templateFactory($this, $resourceId, $cacheTime, $cacheId, $compileId, $parentTemplate); |
||
1059 | } |
||
1060 | |||
1061 | throw new Exception('Unknown resource type : ' . $resourceName); |
||
1062 | } |
||
1063 | |||
1064 | /** |
||
1065 | * Checks if the input is an array or arrayaccess object, optionally it can also check if it's |
||
1066 | * empty. |
||
1067 | * |
||
1068 | * @param mixed $value the variable to check |
||
1069 | * @param bool $checkIsEmpty if true, the function will also check if the array|arrayaccess is empty, |
||
1070 | * and return true only if it's not empty |
||
1071 | * |
||
1072 | * @return int|bool true if it's an array|arrayaccess (or the item count if $checkIsEmpty is true) or false if it's |
||
1073 | * not an array|arrayaccess (or 0 if $checkIsEmpty is true) |
||
1074 | */ |
||
1075 | public function isArray($value, $checkIsEmpty = false) |
||
1076 | { |
||
1077 | if (is_array($value) === true || $value instanceof ArrayAccess) { |
||
1078 | if ($checkIsEmpty === false) { |
||
1079 | return true; |
||
1080 | } |
||
1081 | |||
1082 | return $this->count($value); |
||
1083 | } |
||
1084 | |||
1085 | return false; |
||
1086 | } |
||
1087 | |||
1088 | /** |
||
1089 | * Checks if the input is an array or a traversable object, optionally it can also check if it's |
||
1090 | * empty. |
||
1091 | * |
||
1092 | * @param mixed $value the variable to check |
||
1093 | * @param bool $checkIsEmpty if true, the function will also check if the array|traversable is empty, |
||
1094 | * and return true only if it's not empty |
||
1095 | * |
||
1096 | * @return int|bool true if it's an array|traversable (or the item count if $checkIsEmpty is true) or false if it's |
||
1097 | * not an array|traversable (or 0 if $checkIsEmpty is true) |
||
1098 | */ |
||
1099 | public function isTraversable($value, $checkIsEmpty = false) |
||
1100 | { |
||
1101 | if (is_array($value) === true) { |
||
1102 | if ($checkIsEmpty === false) { |
||
1103 | return true; |
||
1104 | } else { |
||
1105 | return count($value) > 0; |
||
1106 | } |
||
1107 | } elseif ($value instanceof Traversable) { |
||
1108 | if ($checkIsEmpty === false) { |
||
1109 | return true; |
||
1110 | } else { |
||
1111 | return $this->count($value); |
||
1112 | } |
||
1113 | } |
||
1114 | |||
1115 | return false; |
||
1116 | } |
||
1117 | |||
1118 | /** |
||
1119 | * Counts an array or arrayaccess/traversable object. |
||
1120 | * |
||
1121 | * @param mixed $value the value to count |
||
1122 | * |
||
1123 | * @return int|bool the count for arrays and objects that implement countable, true for other objects that don't, |
||
1124 | * and 0 for empty elements |
||
1125 | */ |
||
1126 | public function count($value) |
||
1127 | { |
||
1128 | if (is_array($value) === true || $value instanceof Countable) { |
||
1129 | return count($value); |
||
1130 | } elseif ($value instanceof ArrayAccess) { |
||
1131 | if ($value->offsetExists(0)) { |
||
1132 | return true; |
||
1133 | } |
||
1134 | } elseif ($value instanceof Iterator) { |
||
1135 | $value->rewind(); |
||
1136 | if ($value->valid()) { |
||
1137 | return true; |
||
1138 | } |
||
1139 | } elseif ($value instanceof Traversable) { |
||
1140 | foreach ($value as $dummy) { |
||
1141 | return true; |
||
1142 | } |
||
1143 | } |
||
1144 | |||
1145 | return 0; |
||
1146 | } |
||
1147 | |||
1148 | /** |
||
1149 | * Triggers a dwoo error. |
||
1150 | * |
||
1151 | * @param string $message the error message |
||
1152 | * @param int $level the error level, one of the PHP's E_* constants |
||
1153 | * |
||
1154 | * @return void |
||
1155 | */ |
||
1156 | public function triggerError($message, $level = E_USER_NOTICE) |
||
1157 | { |
||
1158 | if (!($tplIdentifier = $this->template->getResourceIdentifier())) { |
||
1159 | $tplIdentifier = $this->template->getResourceName(); |
||
1160 | } |
||
1161 | trigger_error('Dwoo error (in ' . $tplIdentifier . ') : ' . $message, $level); |
||
1162 | } |
||
1163 | |||
1164 | /** |
||
1165 | * Adds a block to the block stack. |
||
1166 | * |
||
1167 | * @param string $blockName the block name (without Dwoo_Plugin_ prefix) |
||
1168 | * @param array $args the arguments to be passed to the block's init() function |
||
1169 | * |
||
1170 | * @return BlockPlugin the newly created block |
||
1171 | */ |
||
1172 | public function addStack($blockName, array $args = array()) |
||
1173 | { |
||
1174 | View Code Duplication | if (isset($this->plugins[$blockName])) { |
|
1175 | $class = $this->plugins[$blockName]['class']; |
||
1176 | } else { |
||
1177 | $class = self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin' . self::toCamelCase($blockName); |
||
1178 | } |
||
1179 | |||
1180 | if ($this->curBlock !== null) { |
||
1181 | $this->curBlock->buffer(ob_get_contents()); |
||
1182 | ob_clean(); |
||
1183 | } else { |
||
1184 | $this->buffer .= ob_get_contents(); |
||
1185 | ob_clean(); |
||
1186 | } |
||
1187 | |||
1188 | $block = new $class($this); |
||
1189 | |||
1190 | call_user_func_array(array($block, 'init'), $args); |
||
1191 | |||
1192 | $this->stack[] = $this->curBlock = $block; |
||
1193 | |||
1194 | return $block; |
||
1195 | } |
||
1196 | |||
1197 | /** |
||
1198 | * Removes the plugin at the top of the block stack. |
||
1199 | * Calls the block buffer() function, followed by a call to end() and finally a call to process() |
||
1200 | * |
||
1201 | * @return void |
||
1202 | */ |
||
1203 | public function delStack() |
||
1204 | { |
||
1205 | $args = func_get_args(); |
||
1206 | |||
1207 | $this->curBlock->buffer(ob_get_contents()); |
||
1208 | ob_clean(); |
||
1209 | |||
1210 | call_user_func_array(array($this->curBlock, 'end'), $args); |
||
1211 | |||
1212 | $tmp = array_pop($this->stack); |
||
1213 | |||
1214 | if (count($this->stack) > 0) { |
||
1215 | $this->curBlock = end($this->stack); |
||
1216 | $this->curBlock->buffer($tmp->process()); |
||
1217 | } else { |
||
1218 | if ($this->buffer !== '') { |
||
1219 | echo $this->buffer; |
||
1220 | $this->buffer = ''; |
||
1221 | } |
||
1222 | $this->curBlock = null; |
||
1223 | echo $tmp->process(); |
||
1224 | } |
||
1225 | |||
1226 | unset($tmp); |
||
1227 | } |
||
1228 | |||
1229 | /** |
||
1230 | * Returns the parent block of the given block. |
||
1231 | * |
||
1232 | * @param BlockPlugin $block the block class plugin |
||
1233 | * |
||
1234 | * @return BlockPlugin|false if the given block isn't in the stack |
||
1235 | */ |
||
1236 | public function getParentBlock(BlockPlugin $block) |
||
1237 | { |
||
1238 | $index = array_search($block, $this->stack, true); |
||
1239 | if ($index !== false && $index > 0) { |
||
1240 | return $this->stack[$index - 1]; |
||
1241 | } |
||
1242 | |||
1243 | return false; |
||
1244 | } |
||
1245 | |||
1246 | /** |
||
1247 | * Finds the closest block of the given type, starting at the top of the stack. |
||
1248 | * |
||
1249 | * @param string $type the type of plugin you want to find |
||
1250 | * |
||
1251 | * @return BlockPlugin|false if no plugin of such type is in the stack |
||
1252 | */ |
||
1253 | public function findBlock($type) |
||
1254 | { |
||
1255 | View Code Duplication | if (isset($this->plugins[$type])) { |
|
1256 | $type = $this->plugins[$type]['class']; |
||
1257 | } else { |
||
1258 | $type = self::NAMESPACE_PLUGINS_BLOCKS . 'Plugin_' . str_replace(self::NAMESPACE_PLUGINS_BLOCKS.'Plugin', |
||
1259 | '', $type); |
||
1260 | } |
||
1261 | |||
1262 | $keys = array_keys($this->stack); |
||
1263 | while (($key = array_pop($keys)) !== false) { |
||
1264 | if ($this->stack[$key] instanceof $type) { |
||
1265 | return $this->stack[$key]; |
||
1266 | } |
||
1267 | } |
||
1268 | |||
1269 | return false; |
||
1270 | } |
||
1271 | |||
1272 | /** |
||
1273 | * Returns a Plugin of the given class. |
||
1274 | * this is so a single instance of every class plugin is created at each template run, |
||
1275 | * allowing class plugins to have "per-template-run" static variables |
||
1276 | * |
||
1277 | * @param string $class the class name |
||
1278 | * |
||
1279 | * @return mixed an object of the given class |
||
1280 | */ |
||
1281 | public function getObjectPlugin($class) |
||
1282 | { |
||
1283 | if (isset($this->runtimePlugins[$class])) { |
||
1284 | return $this->runtimePlugins[$class]; |
||
1285 | } |
||
1286 | |||
1287 | return $this->runtimePlugins[$class] = new $class($this); |
||
1288 | } |
||
1289 | |||
1290 | /** |
||
1291 | * Calls the process() method of the given class-plugin name. |
||
1292 | * |
||
1293 | * @param string $plugName the class plugin name (without Dwoo_Plugin_ prefix) |
||
1294 | * @param array $params an array of parameters to send to the process() method |
||
1295 | * |
||
1296 | * @return string the process() return value |
||
1297 | */ |
||
1298 | public function classCall($plugName, array $params = array()) |
||
1299 | { |
||
1300 | $class = self::toCamelCase($plugName); |
||
1301 | $plugin = $this->getObjectPlugin($class); |
||
1302 | |||
1303 | return call_user_func_array(array($plugin, 'process'), $params); |
||
1304 | } |
||
1305 | |||
1306 | /** |
||
1307 | * Calls a php function. |
||
1308 | * |
||
1309 | * @param string $callback the function to call |
||
1310 | * @param array $params an array of parameters to send to the function |
||
1311 | * |
||
1312 | * @return mixed the return value of the called function |
||
1313 | */ |
||
1314 | public function arrayMap($callback, array $params) |
||
1315 | { |
||
1316 | if ($params[0] === $this) { |
||
1317 | $addThis = true; |
||
1318 | array_shift($params); |
||
1319 | } |
||
1320 | if ((is_array($params[0]) || ($params[0] instanceof Iterator && $params[0] instanceof ArrayAccess))) { |
||
1321 | if (empty($params[0])) { |
||
1322 | return $params[0]; |
||
1323 | } |
||
1324 | |||
1325 | // array map |
||
1326 | $out = array(); |
||
1327 | $cnt = count($params); |
||
1328 | |||
1329 | if (isset($addThis)) { |
||
1330 | array_unshift($params, $this); |
||
1331 | $items = $params[1]; |
||
1332 | $keys = array_keys($items); |
||
1333 | |||
1334 | if (is_string($callback) === false) { |
||
1335 | while (($i = array_shift($keys)) !== null) { |
||
1336 | $out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params); |
||
1337 | } |
||
1338 | } elseif ($cnt === 1) { |
||
1339 | while (($i = array_shift($keys)) !== null) { |
||
1340 | $out[] = $callback($this, $items[$i]); |
||
1341 | } |
||
1342 | View Code Duplication | } elseif ($cnt === 2) { |
|
1343 | while (($i = array_shift($keys)) !== null) { |
||
1344 | $out[] = $callback($this, $items[$i], $params[2]); |
||
1345 | } |
||
1346 | } elseif ($cnt === 3) { |
||
1347 | while (($i = array_shift($keys)) !== null) { |
||
1348 | $out[] = $callback($this, $items[$i], $params[2], $params[3]); |
||
1349 | } |
||
1350 | } else { |
||
1351 | while (($i = array_shift($keys)) !== null) { |
||
1352 | $out[] = call_user_func_array($callback, array(1 => $items[$i]) + $params); |
||
1353 | } |
||
1354 | } |
||
1355 | } else { |
||
1356 | $items = $params[0]; |
||
1357 | $keys = array_keys($items); |
||
1358 | |||
1359 | if (is_string($callback) === false) { |
||
1360 | while (($i = array_shift($keys)) !== null) { |
||
1361 | $out[] = call_user_func_array($callback, array($items[$i]) + $params); |
||
1362 | } |
||
1363 | } elseif ($cnt === 1) { |
||
1364 | while (($i = array_shift($keys)) !== null) { |
||
1365 | $out[] = $callback($items[$i]); |
||
1366 | } |
||
1367 | View Code Duplication | } elseif ($cnt === 2) { |
|
1368 | while (($i = array_shift($keys)) !== null) { |
||
1369 | $out[] = $callback($items[$i], $params[1]); |
||
1370 | } |
||
1371 | } elseif ($cnt === 3) { |
||
1372 | while (($i = array_shift($keys)) !== null) { |
||
1373 | $out[] = $callback($items[$i], $params[1], $params[2]); |
||
1374 | } |
||
1375 | View Code Duplication | } elseif ($cnt === 4) { |
|
1376 | while (($i = array_shift($keys)) !== null) { |
||
1377 | $out[] = $callback($items[$i], $params[1], $params[2], $params[3]); |
||
1378 | } |
||
1379 | } else { |
||
1380 | while (($i = array_shift($keys)) !== null) { |
||
1381 | $out[] = call_user_func_array($callback, array($items[$i]) + $params); |
||
1382 | } |
||
1383 | } |
||
1384 | } |
||
1385 | |||
1386 | return $out; |
||
1387 | } else { |
||
1388 | return $params[0]; |
||
1389 | } |
||
1390 | } |
||
1391 | |||
1392 | /** |
||
1393 | * Reads a variable into the given data array. |
||
1394 | * |
||
1395 | * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property") |
||
1396 | * @param mixed $data the data array or object to read from |
||
1397 | * @param bool $safeRead if true, the function will check whether the index exists to prevent any notices from |
||
1398 | * being output |
||
1399 | * |
||
1400 | * @return mixed |
||
1401 | */ |
||
1402 | public function readVarInto($varstr, $data, $safeRead = false) |
||
1403 | { |
||
1404 | if ($data === null) { |
||
1405 | return null; |
||
1406 | } |
||
1407 | |||
1408 | if (is_array($varstr) === false) { |
||
1409 | preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m); |
||
1410 | } else { |
||
1411 | $m = $varstr; |
||
1412 | } |
||
1413 | unset($varstr); |
||
1414 | |||
1415 | while (list($k, $sep) = each($m[1])) { |
||
1416 | if ($sep === '.' || $sep === '[' || $sep === '') { |
||
1417 | // strip enclosing quotes if present |
||
1418 | $m[2][$k] = preg_replace('#^(["\']?)(.*?)\1$#', '$2', $m[2][$k]); |
||
1419 | |||
1420 | View Code Duplication | if ((is_array($data) || $data instanceof ArrayAccess) && ($safeRead === false || isset($data[$m[2][$k]]))) { |
|
1421 | $data = $data[$m[2][$k]]; |
||
1422 | } else { |
||
1423 | return null; |
||
1424 | } |
||
1425 | } else { |
||
1426 | if (is_object($data) && ($safeRead === false || isset($data->{$m[2][$k]}))) { |
||
1427 | $data = $data->{$m[2][$k]}; |
||
1428 | } else { |
||
1429 | return null; |
||
1430 | } |
||
1431 | } |
||
1432 | } |
||
1433 | |||
1434 | return $data; |
||
1435 | } |
||
1436 | |||
1437 | /** |
||
1438 | * Reads a variable into the parent scope. |
||
1439 | * |
||
1440 | * @param int $parentLevels the amount of parent levels to go from the current scope |
||
1441 | * @param string $varstr the variable string, using dwoo variable syntax (i.e. |
||
1442 | * "var.subvar[subsubvar]->property") |
||
1443 | * |
||
1444 | * @return mixed |
||
1445 | */ |
||
1446 | public function readParentVar($parentLevels, $varstr = null) |
||
1447 | { |
||
1448 | $tree = $this->scopeTree; |
||
1449 | $cur = $this->data; |
||
1450 | |||
1451 | while ($parentLevels -- !== 0) { |
||
1452 | array_pop($tree); |
||
1453 | } |
||
1454 | |||
1455 | View Code Duplication | while (($i = array_shift($tree)) !== null) { |
|
1456 | if (is_object($cur)) { |
||
1457 | $cur = $cur->{$i}; |
||
1458 | } else { |
||
1459 | $cur = $cur[$i]; |
||
1460 | } |
||
1461 | } |
||
1462 | |||
1463 | if ($varstr !== null) { |
||
1464 | return $this->readVarInto($varstr, $cur); |
||
1465 | } else { |
||
1466 | return $cur; |
||
1467 | } |
||
1468 | } |
||
1469 | |||
1470 | /** |
||
1471 | * Reads a variable into the current scope. |
||
1472 | * |
||
1473 | * @param string $varstr the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property") |
||
1474 | * |
||
1475 | * @return mixed |
||
1476 | */ |
||
1477 | public function readVar($varstr) |
||
1478 | { |
||
1479 | if (is_array($varstr) === true) { |
||
1480 | $m = $varstr; |
||
1481 | unset($varstr); |
||
1482 | } else { |
||
1483 | if (strstr($varstr, '.') === false && strstr($varstr, '[') === false && strstr($varstr, '->') === false) { |
||
1484 | if ($varstr === 'dwoo') { |
||
1485 | return $this->getGlobals(); |
||
1486 | } elseif ($varstr === '__' || $varstr === '_root') { |
||
1487 | return $this->data; |
||
1488 | } elseif ($varstr === '_' || $varstr === '_parent') { |
||
1489 | $varstr = '.' . $varstr; |
||
1490 | $tree = $this->scopeTree; |
||
1491 | $cur = $this->data; |
||
1492 | array_pop($tree); |
||
1493 | |||
1494 | View Code Duplication | while (($i = array_shift($tree)) !== null) { |
|
1495 | if (is_object($cur)) { |
||
1496 | $cur = $cur->{$i}; |
||
1497 | } else { |
||
1498 | $cur = $cur[$i]; |
||
1499 | } |
||
1500 | } |
||
1501 | |||
1502 | return $cur; |
||
1503 | } |
||
1504 | |||
1505 | $cur = $this->scope; |
||
1506 | |||
1507 | if (isset($cur[$varstr])) { |
||
1508 | return $cur[$varstr]; |
||
1509 | } else { |
||
1510 | return null; |
||
1511 | } |
||
1512 | } |
||
1513 | |||
1514 | if (substr($varstr, 0, 1) === '.') { |
||
1515 | $varstr = 'dwoo' . $varstr; |
||
1516 | } |
||
1517 | |||
1518 | preg_match_all('#(\[|->|\.)?((?:[^.[\]-]|-(?!>))+)\]?#i', $varstr, $m); |
||
1519 | } |
||
1520 | |||
1521 | $i = $m[2][0]; |
||
1522 | if ($i === 'dwoo') { |
||
1523 | $cur = $this->getGlobals(); |
||
1524 | array_shift($m[2]); |
||
1525 | array_shift($m[1]); |
||
1526 | switch ($m[2][0]) { |
||
1527 | case 'get': |
||
1528 | $cur = $_GET; |
||
1529 | break; |
||
1530 | case 'post': |
||
1531 | $cur = $_POST; |
||
1532 | break; |
||
1533 | case 'session': |
||
1534 | $cur = $_SESSION; |
||
1535 | break; |
||
1536 | case 'cookies': |
||
1537 | case 'cookie': |
||
1538 | $cur = $_COOKIE; |
||
1539 | break; |
||
1540 | case 'server': |
||
1541 | $cur = $_SERVER; |
||
1542 | break; |
||
1543 | case 'env': |
||
1544 | $cur = $_ENV; |
||
1545 | break; |
||
1546 | case 'request': |
||
1547 | $cur = $_REQUEST; |
||
1548 | break; |
||
1549 | case 'const': |
||
1550 | array_shift($m[2]); |
||
1551 | if (defined($m[2][0])) { |
||
1552 | return constant($m[2][0]); |
||
1553 | } else { |
||
1554 | return null; |
||
1555 | } |
||
1556 | } |
||
1557 | if ($cur !== $this->getGlobals()) { |
||
1558 | array_shift($m[2]); |
||
1559 | array_shift($m[1]); |
||
1560 | } |
||
1561 | View Code Duplication | } elseif ($i === '__' || $i === '_root') { |
|
1562 | $cur = $this->data; |
||
1563 | array_shift($m[2]); |
||
1564 | array_shift($m[1]); |
||
1565 | } elseif ($i === '_' || $i === '_parent') { |
||
1566 | $tree = $this->scopeTree; |
||
1567 | $cur = $this->data; |
||
1568 | |||
1569 | while (true) { |
||
1570 | array_pop($tree); |
||
1571 | array_shift($m[2]); |
||
1572 | array_shift($m[1]); |
||
1573 | if (current($m[2]) === '_' || current($m[2]) === '_parent') { |
||
1574 | continue; |
||
1575 | } |
||
1576 | |||
1577 | View Code Duplication | while (($i = array_shift($tree)) !== null) { |
|
1578 | if (is_object($cur)) { |
||
1579 | $cur = $cur->{$i}; |
||
1580 | } else { |
||
1581 | $cur = $cur[$i]; |
||
1582 | } |
||
1583 | } |
||
1584 | break; |
||
1585 | } |
||
1586 | } else { |
||
1587 | $cur = $this->scope; |
||
1588 | } |
||
1589 | |||
1590 | while (list($k, $sep) = each($m[1])) { |
||
1591 | if ($sep === '.' || $sep === '[' || $sep === '') { |
||
1592 | View Code Duplication | if ((is_array($cur) || $cur instanceof ArrayAccess) && isset($cur[$m[2][$k]])) { |
|
1593 | $cur = $cur[$m[2][$k]]; |
||
1594 | } else { |
||
1595 | return null; |
||
1596 | } |
||
1597 | View Code Duplication | } elseif ($sep === '->') { |
|
1598 | if (is_object($cur)) { |
||
1599 | $cur = $cur->{$m[2][$k]}; |
||
1600 | } else { |
||
1601 | return null; |
||
1602 | } |
||
1603 | } else { |
||
1604 | return null; |
||
1605 | } |
||
1606 | } |
||
1607 | |||
1608 | return $cur; |
||
1609 | } |
||
1610 | |||
1611 | /** |
||
1612 | * Assign the value to the given variable. |
||
1613 | * |
||
1614 | * @param mixed $value the value to assign |
||
1615 | * @param string $scope the variable string, using dwoo variable syntax (i.e. "var.subvar[subsubvar]->property") |
||
1616 | * |
||
1617 | * @return bool true if assigned correctly or false if a problem occured while parsing the var string |
||
1618 | */ |
||
1619 | public function assignInScope($value, $scope) |
||
1620 | { |
||
1621 | if (!is_string($scope)) { |
||
1622 | $this->triggerError('Assignments must be done into strings, (' . gettype($scope) . ') ' . var_export($scope, true) . ' given', E_USER_ERROR); |
||
1623 | } |
||
1624 | if (strstr($scope, '.') === false && strstr($scope, '->') === false) { |
||
1625 | $this->scope[$scope] = $value; |
||
1626 | } else { |
||
1627 | // TODO handle _root/_parent scopes ? |
||
1628 | preg_match_all('#(\[|->|\.)?([^.[\]-]+)\]?#i', $scope, $m); |
||
1629 | |||
1630 | $cur = &$this->scope; |
||
1631 | $last = array( |
||
1632 | array_pop($m[1]), |
||
1633 | array_pop($m[2]) |
||
1634 | ); |
||
1635 | |||
1636 | while (list($k, $sep) = each($m[1])) { |
||
1637 | if ($sep === '.' || $sep === '[' || $sep === '') { |
||
1638 | if (is_array($cur) === false) { |
||
1639 | $cur = array(); |
||
1640 | } |
||
1641 | $cur = &$cur[$m[2][$k]]; |
||
1642 | View Code Duplication | } elseif ($sep === '->') { |
|
1643 | if (is_object($cur) === false) { |
||
1644 | $cur = new stdClass(); |
||
1645 | } |
||
1646 | $cur = &$cur->{$m[2][$k]}; |
||
1647 | } else { |
||
1648 | return false; |
||
1649 | } |
||
1650 | } |
||
1651 | |||
1652 | if ($last[0] === '.' || $last[0] === '[' || $last[0] === '') { |
||
1653 | if (is_array($cur) === false) { |
||
1654 | $cur = array(); |
||
1655 | } |
||
1656 | $cur[$last[1]] = $value; |
||
1657 | } elseif ($last[0] === '->') { |
||
1658 | if (is_object($cur) === false) { |
||
1659 | $cur = new stdClass(); |
||
1660 | } |
||
1661 | $cur->{$last[1]} = $value; |
||
1662 | } else { |
||
1663 | return false; |
||
1664 | } |
||
1665 | } |
||
1666 | } |
||
1667 | |||
1668 | /** |
||
1669 | * Sets the scope to the given scope string or array. |
||
1670 | * |
||
1671 | * @param mixed $scope a string i.e. "level1.level2" or an array i.e. array("level1", "level2") |
||
1672 | * @param bool $absolute if true, the scope is set from the top level scope and not from the current scope |
||
1673 | * |
||
1674 | * @return array the current scope tree |
||
1675 | */ |
||
1676 | public function setScope($scope, $absolute = false) |
||
1677 | { |
||
1678 | $old = $this->scopeTree; |
||
1679 | |||
1680 | if (is_string($scope) === true) { |
||
1681 | $scope = explode('.', $scope); |
||
1682 | } |
||
1683 | |||
1684 | if ($absolute === true) { |
||
1685 | $this->scope = &$this->data; |
||
1686 | $this->scopeTree = array(); |
||
1687 | } |
||
1688 | |||
1689 | while (($bit = array_shift($scope)) !== null) { |
||
1690 | if ($bit === '_' || $bit === '_parent') { |
||
1691 | array_pop($this->scopeTree); |
||
1692 | $this->scope = &$this->data; |
||
1693 | $cnt = count($this->scopeTree); |
||
1694 | View Code Duplication | for ($i = 0; $i < $cnt; ++ $i) { |
|
1695 | $this->scope = &$this->scope[$this->scopeTree[$i]]; |
||
1696 | } |
||
1697 | View Code Duplication | } elseif ($bit === '__' || $bit === '_root') { |
|
1698 | $this->scope = &$this->data; |
||
1699 | $this->scopeTree = array(); |
||
1700 | } elseif (isset($this->scope[$bit])) { |
||
1701 | if ($this->scope instanceof ArrayAccess) { |
||
1702 | $tmp = $this->scope[$bit]; |
||
1703 | $this->scope = &$tmp; |
||
1704 | } else { |
||
1705 | $this->scope = &$this->scope[$bit]; |
||
1706 | } |
||
1707 | $this->scopeTree[] = $bit; |
||
1708 | } else { |
||
1709 | unset($this->scope); |
||
1710 | $this->scope = null; |
||
1711 | } |
||
1712 | } |
||
1713 | |||
1714 | return $old; |
||
1715 | } |
||
1716 | |||
1717 | /** |
||
1718 | * Returns the entire data array. |
||
1719 | * |
||
1720 | * @return array |
||
1721 | */ |
||
1722 | public function getData() |
||
1723 | { |
||
1724 | return $this->data; |
||
1725 | } |
||
1726 | |||
1727 | /** |
||
1728 | * Sets a return value for the currently running template. |
||
1729 | * |
||
1730 | * @param string $name var name |
||
1731 | * @param mixed $value var value |
||
1732 | * |
||
1733 | * @return void |
||
1734 | */ |
||
1735 | public function setReturnValue($name, $value) |
||
1736 | { |
||
1737 | $this->returnData[$name] = $value; |
||
1738 | } |
||
1739 | |||
1740 | /** |
||
1741 | * Retrieves the return values set by the template. |
||
1742 | * |
||
1743 | * @return array |
||
1744 | */ |
||
1745 | public function getReturnValues() |
||
1746 | { |
||
1747 | return $this->returnData; |
||
1748 | } |
||
1749 | |||
1750 | /** |
||
1751 | * Returns a reference to the current scope. |
||
1752 | * |
||
1753 | * @return mixed |
||
1754 | */ |
||
1755 | public function &getScope() |
||
1756 | { |
||
1757 | return $this->scope; |
||
1758 | } |
||
1759 | |||
1760 | /** |
||
1761 | * Redirects all calls to unexisting to plugin proxy. |
||
1762 | * |
||
1763 | * @param string $method the method name |
||
1764 | * @param array $args array of arguments |
||
1765 | * |
||
1766 | * @return mixed |
||
1767 | * @throws Exception |
||
1768 | */ |
||
1769 | public function __call($method, $args) |
||
1770 | { |
||
1771 | $proxy = $this->getPluginProxy(); |
||
1772 | if (!$proxy) { |
||
1773 | throw new Exception('Call to undefined method ' . __CLASS__ . '::' . $method . '()'); |
||
1774 | } |
||
1775 | |||
1776 | return call_user_func_array($proxy->getCallback($method), $args); |
||
1777 | } |
||
1778 | |||
1779 | /** |
||
1780 | * Convert plugin name from `auto_escape` to `AutoEscape`. |
||
1781 | * @param string $input |
||
1782 | * @param string $separator |
||
1783 | * |
||
1784 | * @return mixed |
||
1785 | */ |
||
1786 | public static function toCamelCase($input, $separator = '_') |
||
1787 | { |
||
1788 | return join(array_map('ucfirst', explode($separator, $input))); |
||
1789 | |||
1790 | // TODO >= PHP5.4.32 |
||
1791 | //return str_replace($separator, '', ucwords($input, $separator)); |
||
1792 | } |
||
1793 | } |
||
1794 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..