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) { |
||
0 ignored issues
–
show
|
|||
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
It seems like
$data of type object<ArrayAccess> is incompatible with the declared type array of property $data .
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..
Loading history...
|
|||
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) |
||
0 ignored issues
–
show
|
|||
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; |
||
0 ignored issues
–
show
$varstr is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the
Loading history...
|
|||
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)); |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
69% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them.
Loading history...
|
|||
1792 | } |
||
1793 | } |
||
1794 |
This check looks for the bodies of
if
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.