]> git.mxchange.org Git - friendica.git/blob - vendor/smarty/smarty/libs/Smarty.class.php
Add Smarty to Composer
[friendica.git] / vendor / smarty / smarty / libs / Smarty.class.php
1 <?php
2 /**
3  * Project:     Smarty: the PHP compiling template engine
4  * File:        Smarty.class.php
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * For questions, help, comments, discussion, etc., please join the
20  * Smarty mailing list. Send a blank e-mail to
21  * smarty-discussion-subscribe@googlegroups.com
22  *
23  * @link      http://www.smarty.net/
24  * @copyright 2016 New Digital Group, Inc.
25  * @copyright 2016 Uwe Tews
26  * @author    Monte Ohrt <monte at ohrt dot com>
27  * @author    Uwe Tews
28  * @author    Rodney Rehm
29  * @package   Smarty
30  * @version   3.1.31
31  */
32
33 /**
34  * set SMARTY_DIR to absolute path to Smarty library files.
35  * Sets SMARTY_DIR only if user application has not already defined it.
36  */
37 if (!defined('SMARTY_DIR')) {
38     define('SMARTY_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR);
39 }
40
41 /**
42  * set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins.
43  * Sets SMARTY_SYSPLUGINS_DIR only if user application has not already defined it.
44  */
45 if (!defined('SMARTY_SYSPLUGINS_DIR')) {
46     define('SMARTY_SYSPLUGINS_DIR', SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR);
47 }
48 if (!defined('SMARTY_PLUGINS_DIR')) {
49     define('SMARTY_PLUGINS_DIR', SMARTY_DIR . 'plugins' . DIRECTORY_SEPARATOR);
50 }
51 if (!defined('SMARTY_MBSTRING')) {
52     define('SMARTY_MBSTRING', function_exists('mb_get_info'));
53 }
54 if (!defined('SMARTY_RESOURCE_CHAR_SET')) {
55     // UTF-8 can only be done properly when mbstring is available!
56     /**
57      * @deprecated in favor of Smarty::$_CHARSET
58      */
59     define('SMARTY_RESOURCE_CHAR_SET', SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1');
60 }
61 if (!defined('SMARTY_RESOURCE_DATE_FORMAT')) {
62     /**
63      * @deprecated in favor of Smarty::$_DATE_FORMAT
64      */
65     define('SMARTY_RESOURCE_DATE_FORMAT', '%b %e, %Y');
66 }
67
68 /**
69  * Load Smarty_Autoloader
70  */
71 if (!class_exists('Smarty_Autoloader')) {
72     include __DIR__ . '/bootstrap.php';
73 }
74
75 /**
76  * Load always needed external class files
77  */
78 require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_data.php';
79 require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_extension_handler.php';
80 require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_templatebase.php';
81 require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_template.php';
82 require_once SMARTY_SYSPLUGINS_DIR . 'smarty_resource.php';
83 require_once SMARTY_SYSPLUGINS_DIR . 'smarty_variable.php';
84 require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_source.php';
85 require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_resource_base.php';
86 require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_resource_file.php';
87
88 /**
89  * This is the main Smarty class
90  *
91  * @package Smarty
92  *
93  * The following methods will be dynamically loaded by the extension handler when they are called.
94  * They are located in a corresponding Smarty_Internal_Method_xxxx class
95  *
96  * @method int clearAllCache(int $exp_time = null, string $type = null)
97  * @method int clearCache(string $template_name, string $cache_id = null, string $compile_id = null, int $exp_time = null, string $type = null)
98  * @method int compileAllTemplates(string $extension = '.tpl', bool $force_compile = false, int $time_limit = 0, int $max_errors = null)
99  * @method int compileAllConfig(string $extension = '.conf', bool $force_compile = false, int $time_limit = 0, int $max_errors = null)
100  * @method int clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null)
101  */
102 class Smarty extends Smarty_Internal_TemplateBase
103 {
104     /**#@+
105      * constant definitions
106      */
107
108     /**
109      * smarty version
110      */
111     const SMARTY_VERSION = '3.1.31';
112
113     /**
114      * define variable scopes
115      */
116     const SCOPE_LOCAL = 1;
117
118     const SCOPE_PARENT = 2;
119
120     const SCOPE_TPL_ROOT = 4;
121
122     const SCOPE_ROOT = 8;
123
124     const SCOPE_SMARTY = 16;
125
126     const SCOPE_GLOBAL = 32;
127
128     /**
129      * define caching modes
130      */
131     const CACHING_OFF = 0;
132
133     const CACHING_LIFETIME_CURRENT = 1;
134
135     const CACHING_LIFETIME_SAVED = 2;
136
137     /**
138      * define constant for clearing cache files be saved expiration dates
139      */
140     const CLEAR_EXPIRED = - 1;
141
142     /**
143      * define compile check modes
144      */
145     const COMPILECHECK_OFF = 0;
146
147     const COMPILECHECK_ON = 1;
148
149     const COMPILECHECK_CACHEMISS = 2;
150
151     /**
152      * define debug modes
153      */
154     const DEBUG_OFF = 0;
155
156     const DEBUG_ON = 1;
157
158     const DEBUG_INDIVIDUAL = 2;
159
160     /**
161      * modes for handling of "<?php ... ?>" tags in templates.
162      */
163     const PHP_PASSTHRU = 0; //-> print tags as plain text
164
165     const PHP_QUOTE = 1; //-> escape tags as entities
166
167     const PHP_REMOVE = 2; //-> escape tags as entities
168
169     const PHP_ALLOW = 3; //-> escape tags as entities
170
171     /**
172      * filter types
173      */
174     const FILTER_POST = 'post';
175
176     const FILTER_PRE = 'pre';
177
178     const FILTER_OUTPUT = 'output';
179
180     const FILTER_VARIABLE = 'variable';
181
182     /**
183      * plugin types
184      */
185     const PLUGIN_FUNCTION = 'function';
186
187     const PLUGIN_BLOCK = 'block';
188
189     const PLUGIN_COMPILER = 'compiler';
190
191     const PLUGIN_MODIFIER = 'modifier';
192
193     const PLUGIN_MODIFIERCOMPILER = 'modifiercompiler';
194
195     /**
196      * Resource caching modes
197      * (not used since 3.1.30)
198      */
199     const RESOURCE_CACHE_OFF = 0;
200
201     const RESOURCE_CACHE_AUTOMATIC = 1; // cache template objects by rules
202
203     const RESOURCE_CACHE_TEMPLATE = 2; // cache all template objects
204
205     const RESOURCE_CACHE_ON = 4;    // cache source and compiled resources
206
207     /**#@-*/
208
209     /**
210      * assigned global tpl vars
211      */
212     public static $global_tpl_vars = array();
213
214     /**
215      * error handler returned by set_error_handler() in Smarty::muteExpectedErrors()
216      */
217     public static $_previous_error_handler = null;
218
219     /**
220      * contains directories outside of SMARTY_DIR that are to be muted by muteExpectedErrors()
221      */
222     public static $_muted_directories = array();
223
224     /**
225      * Flag denoting if Multibyte String functions are available
226      */
227     public static $_MBSTRING = SMARTY_MBSTRING;
228
229     /**
230      * The character set to adhere to (e.g. "UTF-8")
231      */
232     public static $_CHARSET = SMARTY_RESOURCE_CHAR_SET;
233
234     /**
235      * The date format to be used internally
236      * (accepts date() and strftime())
237      */
238     public static $_DATE_FORMAT = SMARTY_RESOURCE_DATE_FORMAT;
239
240     /**
241      * Flag denoting if PCRE should run in UTF-8 mode
242      */
243     public static $_UTF8_MODIFIER = 'u';
244
245     /**
246      * Flag denoting if operating system is windows
247      */
248     public static $_IS_WINDOWS = false;
249
250     /**#@+
251      * variables
252      */
253
254     /**
255      * auto literal on delimiters with whitespace
256      *
257      * @var boolean
258      */
259     public $auto_literal = true;
260
261     /**
262      * display error on not assigned variables
263      *
264      * @var boolean
265      */
266     public $error_unassigned = false;
267
268     /**
269      * look up relative file path in include_path
270      *
271      * @var boolean
272      */
273     public $use_include_path = false;
274
275     /**
276      * template directory
277      *
278      * @var array
279      */
280     protected $template_dir = array('./templates/');
281
282     /**
283      * flags for normalized template directory entries
284      *
285      * @var array
286      */
287     protected $_processedTemplateDir = array();
288
289     /**
290      * flag if template_dir is normalized
291      *
292      * @var bool
293      */
294     public $_templateDirNormalized = false;
295
296     /**
297      * joined template directory string used in cache keys
298      *
299      * @var string
300      */
301     public $_joined_template_dir = null;
302
303     /**
304      * config directory
305      *
306      * @var array
307      */
308     protected $config_dir = array('./configs/');
309
310     /**
311      * flags for normalized template directory entries
312      *
313      * @var array
314      */
315     protected $_processedConfigDir = array();
316
317     /**
318      * flag if config_dir is normalized
319      *
320      * @var bool
321      */
322     public $_configDirNormalized = false;
323
324     /**
325      * joined config directory string used in cache keys
326      *
327      * @var string
328      */
329     public $_joined_config_dir = null;
330
331     /**
332      * default template handler
333      *
334      * @var callable
335      */
336     public $default_template_handler_func = null;
337
338     /**
339      * default config handler
340      *
341      * @var callable
342      */
343     public $default_config_handler_func = null;
344
345     /**
346      * default plugin handler
347      *
348      * @var callable
349      */
350     public $default_plugin_handler_func = null;
351
352     /**
353      * compile directory
354      *
355      * @var string
356      */
357     protected $compile_dir = './templates_c/';
358
359     /**
360      * flag if template_dir is normalized
361      *
362      * @var bool
363      */
364     public $_compileDirNormalized = false;
365
366     /**
367      * plugins directory
368      *
369      * @var array
370      */
371     protected $plugins_dir = array();
372
373     /**
374      * flag if plugins_dir is normalized
375      *
376      * @var bool
377      */
378     public $_pluginsDirNormalized = false;
379
380     /**
381      * cache directory
382      *
383      * @var string
384      */
385     protected $cache_dir = './cache/';
386
387     /**
388      * flag if template_dir is normalized
389      *
390      * @var bool
391      */
392     public $_cacheDirNormalized = false;
393
394     /**
395      * force template compiling?
396      *
397      * @var boolean
398      */
399     public $force_compile = false;
400
401     /**
402      * check template for modifications?
403      *
404      * @var boolean
405      */
406     public $compile_check = true;
407
408     /**
409      * use sub dirs for compiled/cached files?
410      *
411      * @var boolean
412      */
413     public $use_sub_dirs = false;
414
415     /**
416      * allow ambiguous resources (that are made unique by the resource handler)
417      *
418      * @var boolean
419      */
420     public $allow_ambiguous_resources = false;
421
422     /**
423      * merge compiled includes
424      *
425      * @var boolean
426      */
427     public $merge_compiled_includes = false;
428
429     /*
430     * flag for behaviour when extends: resource  and {extends} tag are used simultaneous
431     *   if false disable execution of {extends} in templates called by extends resource.
432     *   (behaviour as versions < 3.1.28)
433     *
434     * @var boolean
435     */
436     public $extends_recursion = true;
437
438     /**
439      * force cache file creation
440      *
441      * @var boolean
442      */
443     public $force_cache = false;
444
445     /**
446      * template left-delimiter
447      *
448      * @var string
449      */
450     public $left_delimiter = "{";
451
452     /**
453      * template right-delimiter
454      *
455      * @var string
456      */
457     public $right_delimiter = "}";
458
459     /**#@+
460      * security
461      */
462     /**
463      * class name
464      * This should be instance of Smarty_Security.
465      *
466      * @var string
467      * @see Smarty_Security
468      */
469     public $security_class = 'Smarty_Security';
470
471     /**
472      * implementation of security class
473      *
474      * @var Smarty_Security
475      */
476     public $security_policy = null;
477
478     /**
479      * controls handling of PHP-blocks
480      *
481      * @var integer
482      */
483     public $php_handling = self::PHP_PASSTHRU;
484
485     /**
486      * controls if the php template file resource is allowed
487      *
488      * @var bool
489      */
490     public $allow_php_templates = false;
491
492     /**#@-*/
493     /**
494      * debug mode
495      * Setting this to true enables the debug-console.
496      *
497      * @var boolean
498      */
499     public $debugging = false;
500
501     /**
502      * This determines if debugging is enable-able from the browser.
503      * <ul>
504      *  <li>NONE => no debugging control allowed</li>
505      *  <li>URL => enable debugging when SMARTY_DEBUG is found in the URL.</li>
506      * </ul>
507      *
508      * @var string
509      */
510     public $debugging_ctrl = 'NONE';
511
512     /**
513      * Name of debugging URL-param.
514      * Only used when $debugging_ctrl is set to 'URL'.
515      * The name of the URL-parameter that activates debugging.
516      *
517      * @var string
518      */
519     public $smarty_debug_id = 'SMARTY_DEBUG';
520
521     /**
522      * Path of debug template.
523      *
524      * @var string
525      */
526     public $debug_tpl = null;
527
528     /**
529      * When set, smarty uses this value as error_reporting-level.
530      *
531      * @var int
532      */
533     public $error_reporting = null;
534
535     /**#@+
536      * config var settings
537      */
538
539     /**
540      * Controls whether variables with the same name overwrite each other.
541      *
542      * @var boolean
543      */
544     public $config_overwrite = true;
545
546     /**
547      * Controls whether config values of on/true/yes and off/false/no get converted to boolean.
548      *
549      * @var boolean
550      */
551     public $config_booleanize = true;
552
553     /**
554      * Controls whether hidden config sections/vars are read from the file.
555      *
556      * @var boolean
557      */
558     public $config_read_hidden = false;
559
560     /**#@-*/
561
562     /**#@+
563      * resource locking
564      */
565
566     /**
567      * locking concurrent compiles
568      *
569      * @var boolean
570      */
571     public $compile_locking = true;
572
573     /**
574      * Controls whether cache resources should use locking mechanism
575      *
576      * @var boolean
577      */
578     public $cache_locking = false;
579
580     /**
581      * seconds to wait for acquiring a lock before ignoring the write lock
582      *
583      * @var float
584      */
585     public $locking_timeout = 10;
586
587     /**#@-*/
588
589     /**
590      * resource type used if none given
591      * Must be an valid key of $registered_resources.
592      *
593      * @var string
594      */
595     public $default_resource_type = 'file';
596
597     /**
598      * caching type
599      * Must be an element of $cache_resource_types.
600      *
601      * @var string
602      */
603     public $caching_type = 'file';
604
605     /**
606      * config type
607      *
608      * @var string
609      */
610     public $default_config_type = 'file';
611
612     /**
613      * check If-Modified-Since headers
614      *
615      * @var boolean
616      */
617     public $cache_modified_check = false;
618
619     /**
620      * registered plugins
621      *
622      * @var array
623      */
624     public $registered_plugins = array();
625
626     /**
627      * registered objects
628      *
629      * @var array
630      */
631     public $registered_objects = array();
632
633     /**
634      * registered classes
635      *
636      * @var array
637      */
638     public $registered_classes = array();
639
640     /**
641      * registered filters
642      *
643      * @var array
644      */
645     public $registered_filters = array();
646
647     /**
648      * registered resources
649      *
650      * @var array
651      */
652     public $registered_resources = array();
653
654     /**
655      * registered cache resources
656      *
657      * @var array
658      */
659     public $registered_cache_resources = array();
660
661     /**
662      * autoload filter
663      *
664      * @var array
665      */
666     public $autoload_filters = array();
667
668     /**
669      * default modifier
670      *
671      * @var array
672      */
673     public $default_modifiers = array();
674
675     /**
676      * autoescape variable output
677      *
678      * @var boolean
679      */
680     public $escape_html = false;
681
682     /**
683      * start time for execution time calculation
684      *
685      * @var int
686      */
687     public $start_time = 0;
688
689     /**
690      * required by the compiler for BC
691      *
692      * @var string
693      */
694     public $_current_file = null;
695
696     /**
697      * internal flag to enable parser debugging
698      *
699      * @var bool
700      */
701     public $_parserdebug = false;
702
703     /**
704      * This object type (Smarty = 1, template = 2, data = 4)
705      *
706      * @var int
707      */
708     public $_objType = 1;
709
710     /**
711      * Debug object
712      *
713      * @var Smarty_Internal_Debug
714      */
715     public $_debug = null;
716
717     /**
718      * Directory separator
719      *
720      * @var string
721      */
722     public $ds = DIRECTORY_SEPARATOR;
723
724     /**
725      * removed properties
726      *
727      * @var string[]
728      */
729     private $obsoleteProperties = array('resource_caching', 'template_resource_caching', 'direct_access_security',
730                                         '_dir_perms', '_file_perms', 'plugin_search_order',
731                                         'inheritance_merge_compiled_includes', 'resource_cache_mode',);
732
733     /**
734      * List of private properties which will call getter/setter on a direct access
735      *
736      * @var string[]
737      */
738     private $accessMap = array('template_dir' => 'TemplateDir', 'config_dir' => 'ConfigDir',
739                                'plugins_dir' => 'PluginsDir', 'compile_dir' => 'CompileDir',
740                                'cache_dir' => 'CacheDir',);
741
742     /**#@-*/
743
744     /**
745      * Initialize new Smarty object
746      */
747     public function __construct()
748     {
749         $this->_clearTemplateCache();
750         parent::__construct();
751         if (is_callable('mb_internal_encoding')) {
752             mb_internal_encoding(Smarty::$_CHARSET);
753         }
754         $this->start_time = microtime(true);
755
756         if (isset($_SERVER[ 'SCRIPT_NAME' ])) {
757             Smarty::$global_tpl_vars[ 'SCRIPT_NAME' ] = new Smarty_Variable($_SERVER[ 'SCRIPT_NAME' ]);
758         }
759
760         // Check if we're running on windows
761         Smarty::$_IS_WINDOWS = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
762         // let PCRE (preg_*) treat strings as ISO-8859-1 if we're not dealing with UTF-8
763         if (Smarty::$_CHARSET !== 'UTF-8') {
764             Smarty::$_UTF8_MODIFIER = '';
765         }
766     }
767
768     /**
769      * Check if a template resource exists
770      *
771      * @param  string $resource_name template name
772      *
773      * @return boolean status
774      */
775     public function templateExists($resource_name)
776     {
777         // create source object
778         $source = Smarty_Template_Source::load(null, $this, $resource_name);
779         return $source->exists;
780     }
781
782     /**
783      * Loads security class and enables security
784      *
785      * @param  string|Smarty_Security $security_class if a string is used, it must be class-name
786      *
787      * @return Smarty                 current Smarty instance for chaining
788      * @throws SmartyException        when an invalid class name is provided
789      */
790     public function enableSecurity($security_class = null)
791     {
792         Smarty_Security::enableSecurity($this, $security_class);
793         return $this;
794     }
795
796     /**
797      * Disable security
798      *
799      * @return Smarty current Smarty instance for chaining
800      */
801     public function disableSecurity()
802     {
803         $this->security_policy = null;
804
805         return $this;
806     }
807
808     /**
809      * Set template directory
810      *
811      * @param  string|array $template_dir directory(s) of template sources
812      * @param bool          $isConfig     true for config_dir
813      *
814      * @return \Smarty current Smarty instance for chaining
815      */
816     public function setTemplateDir($template_dir, $isConfig = false)
817     {
818         if ($isConfig) {
819             $this->config_dir = array();
820             $this->_processedConfigDir = array();
821         } else {
822             $this->template_dir = array();
823             $this->_processedTemplateDir = array();
824         }
825         $this->addTemplateDir($template_dir, null, $isConfig);
826         return $this;
827     }
828
829     /**
830      * Add template directory(s)
831      *
832      * @param  string|array $template_dir directory(s) of template sources
833      * @param  string       $key          of the array element to assign the template dir to
834      * @param bool          $isConfig     true for config_dir
835      *
836      * @return Smarty          current Smarty instance for chaining
837      */
838     public function addTemplateDir($template_dir, $key = null, $isConfig = false)
839     {
840         if ($isConfig) {
841             $processed = &$this->_processedConfigDir;
842             $dir = &$this->config_dir;
843             $this->_configDirNormalized = false;
844         } else {
845             $processed = &$this->_processedTemplateDir;
846             $dir = &$this->template_dir;
847             $this->_templateDirNormalized = false;
848         }
849         if (is_array($template_dir)) {
850             foreach ($template_dir as $k => $v) {
851                 if (is_int($k)) {
852                     // indexes are not merged but appended
853                     $dir[] = $v;
854                 } else {
855                     // string indexes are overridden
856                     $dir[ $k ] = $v;
857                     unset($processed[ $key ]);
858                 }
859             }
860         } else {
861             if ($key !== null) {
862                 // override directory at specified index
863                 $dir[ $key ] = $template_dir;
864                 unset($processed[ $key ]);
865             } else {
866                 // append new directory
867                 $dir[] = $template_dir;
868             }
869         }
870         return $this;
871     }
872
873     /**
874      * Get template directories
875      *
876      * @param mixed $index    index of directory to get, null to get all
877      * @param bool  $isConfig true for config_dir
878      *
879      * @return array list of template directories, or directory of $index
880      */
881     public function getTemplateDir($index = null, $isConfig = false)
882     {
883         if ($isConfig) {
884             $dir = &$this->config_dir;
885         } else {
886             $dir = &$this->template_dir;
887         }
888         if ($isConfig ? !$this->_configDirNormalized : !$this->_templateDirNormalized) {
889             $this->_nomalizeTemplateConfig($isConfig);
890         }
891         if ($index !== null) {
892             return isset($dir[ $index ]) ? $dir[ $index ] : null;
893         }
894         return $dir;
895     }
896
897     /**
898      * Set config directory
899      *
900      * @param $config_dir
901      *
902      * @return Smarty       current Smarty instance for chaining
903      */
904     public function setConfigDir($config_dir)
905     {
906         return $this->setTemplateDir($config_dir, true);
907     }
908
909     /**
910      * Add config directory(s)
911      *
912      * @param string|array $config_dir directory(s) of config sources
913      * @param mixed        $key        key of the array element to assign the config dir to
914      *
915      * @return Smarty current Smarty instance for chaining
916      */
917     public function addConfigDir($config_dir, $key = null)
918     {
919         return $this->addTemplateDir($config_dir, $key, true);
920     }
921
922     /**
923      * Get config directory
924      *
925      * @param mixed $index index of directory to get, null to get all
926      *
927      * @return array configuration directory
928      */
929     public function getConfigDir($index = null)
930     {
931         return $this->getTemplateDir($index, true);
932     }
933
934     /**
935      * Set plugins directory
936      *
937      * @param  string|array $plugins_dir directory(s) of plugins
938      *
939      * @return Smarty       current Smarty instance for chaining
940      */
941     public function setPluginsDir($plugins_dir)
942     {
943         $this->plugins_dir = (array) $plugins_dir;
944         $this->_pluginsDirNormalized = false;
945         return $this;
946     }
947
948     /**
949      * Adds directory of plugin files
950      *
951      * @param null|array $plugins_dir
952      *
953      * @return Smarty current Smarty instance for chaining
954      */
955     public function addPluginsDir($plugins_dir)
956     {
957         if (empty($this->plugins_dir)) {
958             $this->plugins_dir[] = SMARTY_PLUGINS_DIR;
959         }
960         $this->plugins_dir = array_merge($this->plugins_dir, (array) $plugins_dir);
961         $this->_pluginsDirNormalized = false;
962         return $this;
963     }
964
965     /**
966      * Get plugin directories
967      *
968      * @return array list of plugin directories
969      */
970     public function getPluginsDir()
971     {
972         if (empty($this->plugins_dir)) {
973             $this->plugins_dir[] = SMARTY_PLUGINS_DIR;
974             $this->_pluginsDirNormalized = false;
975         }
976         if (!$this->_pluginsDirNormalized) {
977             if (!is_array($this->plugins_dir)) {
978                 $this->plugins_dir = (array) $this->plugins_dir;
979             }
980             foreach ($this->plugins_dir as $k => $v) {
981                 $this->plugins_dir[ $k ] = $this->_realpath(rtrim($v, "/\\") . $this->ds, true);
982             }
983             $this->_cache[ 'plugin_files' ] = array();
984             $this->_pluginsDirNormalized = true;
985         }
986         return $this->plugins_dir;
987     }
988
989     /**
990      *
991      * @param  string $compile_dir directory to store compiled templates in
992      *
993      * @return Smarty current Smarty instance for chaining
994      */
995     public function setCompileDir($compile_dir)
996     {
997         $this->_normalizeDir('compile_dir', $compile_dir);
998         $this->_compileDirNormalized = true;
999         return $this;
1000     }
1001
1002     /**
1003      * Get compiled directory
1004      *
1005      * @return string path to compiled templates
1006      */
1007     public function getCompileDir()
1008     {
1009         if (!$this->_compileDirNormalized) {
1010             $this->_normalizeDir('compile_dir', $this->compile_dir);
1011             $this->_compileDirNormalized = true;
1012         }
1013         return $this->compile_dir;
1014     }
1015
1016     /**
1017      * Set cache directory
1018      *
1019      * @param  string $cache_dir directory to store cached templates in
1020      *
1021      * @return Smarty current Smarty instance for chaining
1022      */
1023     public function setCacheDir($cache_dir)
1024     {
1025         $this->_normalizeDir('cache_dir', $cache_dir);
1026         $this->_cacheDirNormalized = true;
1027         return $this;
1028     }
1029
1030     /**
1031      * Get cache directory
1032      *
1033      * @return string path of cache directory
1034      */
1035     public function getCacheDir()
1036     {
1037         if (!$this->_cacheDirNormalized) {
1038             $this->_normalizeDir('cache_dir', $this->cache_dir);
1039             $this->_cacheDirNormalized = true;
1040         }
1041         return $this->cache_dir;
1042     }
1043
1044     /**
1045      * Normalize and set directory string
1046      *
1047      * @param string $dirName cache_dir or compile_dir
1048      * @param string $dir     filepath of folder
1049      */
1050     private function _normalizeDir($dirName, $dir)
1051     {
1052         $this->{$dirName} = $this->_realpath(rtrim($dir, "/\\") . $this->ds, true);
1053         if (!isset(Smarty::$_muted_directories[ $this->{$dirName} ])) {
1054             Smarty::$_muted_directories[ $this->{$dirName} ] = null;
1055         }
1056     }
1057
1058     /**
1059      * Normalize template_dir or config_dir
1060      *
1061      * @param bool $isConfig true for config_dir
1062      *
1063      */
1064     private function _nomalizeTemplateConfig($isConfig)
1065     {
1066         if ($isConfig) {
1067             $processed = &$this->_processedConfigDir;
1068             $dir = &$this->config_dir;
1069         } else {
1070             $processed = &$this->_processedTemplateDir;
1071             $dir = &$this->template_dir;
1072         }
1073         if (!is_array($dir)) {
1074             $dir = (array) $dir;
1075         }
1076         foreach ($dir as $k => $v) {
1077             if (!isset($processed[ $k ])) {
1078                 $dir[ $k ] = $v = $this->_realpath(rtrim($v, "/\\") . $this->ds, true);
1079                 $processed[ $k ] = true;
1080             }
1081         }
1082         $isConfig ? $this->_configDirNormalized = true : $this->_templateDirNormalized = true;
1083         $isConfig ? $this->_joined_config_dir = join('#', $this->config_dir) :
1084             $this->_joined_template_dir = join('#', $this->template_dir);
1085     }
1086
1087     /**
1088      * creates a template object
1089      *
1090      * @param  string  $template   the resource handle of the template file
1091      * @param  mixed   $cache_id   cache id to be used with this template
1092      * @param  mixed   $compile_id compile id to be used with this template
1093      * @param  object  $parent     next higher level of Smarty variables
1094      * @param  boolean $do_clone   flag is Smarty object shall be cloned
1095      *
1096      * @return object  template object
1097      */
1098     public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true)
1099     {
1100         if ($cache_id !== null && (is_object($cache_id) || is_array($cache_id))) {
1101             $parent = $cache_id;
1102             $cache_id = null;
1103         }
1104         if ($parent !== null && is_array($parent)) {
1105             $data = $parent;
1106             $parent = null;
1107         } else {
1108             $data = null;
1109         }
1110         if (!$this->_templateDirNormalized) {
1111             $this->_nomalizeTemplateConfig(false);
1112         }
1113         $_templateId = $this->_getTemplateId($template, $cache_id, $compile_id);
1114         $tpl = null;
1115         if ($this->caching && isset(Smarty_Internal_Template::$isCacheTplObj[ $_templateId ])) {
1116             $tpl = $do_clone ? clone Smarty_Internal_Template::$isCacheTplObj[ $_templateId ] :
1117                 Smarty_Internal_Template::$isCacheTplObj[ $_templateId ];
1118             $tpl->inheritance = null;
1119             $tpl->tpl_vars = $tpl->config_vars = array();
1120         } else if (!$do_clone && isset(Smarty_Internal_Template::$tplObjCache[ $_templateId ])) {
1121             $tpl = clone Smarty_Internal_Template::$tplObjCache[ $_templateId ];
1122             $tpl->inheritance = null;
1123             $tpl->tpl_vars = $tpl->config_vars = array();
1124         } else {
1125             /* @var Smarty_Internal_Template $tpl */
1126             $tpl = new $this->template_class($template, $this, null, $cache_id, $compile_id, null, null);
1127             $tpl->templateId = $_templateId;
1128         }
1129         if ($do_clone) {
1130             $tpl->smarty = clone $tpl->smarty;
1131         }
1132         $tpl->parent = $parent ? $parent : $this;
1133         // fill data if present
1134         if (!empty($data) && is_array($data)) {
1135             // set up variable values
1136             foreach ($data as $_key => $_val) {
1137                 $tpl->tpl_vars[ $_key ] = new Smarty_Variable($_val);
1138             }
1139         }
1140         if ($this->debugging || $this->debugging_ctrl == 'URL') {
1141             $tpl->smarty->_debug = new Smarty_Internal_Debug();
1142             // check URL debugging control
1143             if (!$this->debugging && $this->debugging_ctrl == 'URL') {
1144                 $tpl->smarty->_debug->debugUrl($tpl->smarty);
1145             }
1146         }
1147         return $tpl;
1148     }
1149
1150     /**
1151      * Takes unknown classes and loads plugin files for them
1152      * class name format: Smarty_PluginType_PluginName
1153      * plugin filename format: plugintype.pluginname.php
1154      *
1155      * @param  string $plugin_name class plugin name to load
1156      * @param  bool   $check       check if already loaded
1157      *
1158      * @throws SmartyException
1159      * @return string |boolean filepath of loaded file or false
1160      */
1161     public function loadPlugin($plugin_name, $check = true)
1162     {
1163         return $this->ext->loadPlugin->loadPlugin($this, $plugin_name, $check);
1164     }
1165
1166     /**
1167      * Get unique template id
1168      *
1169      * @param string                    $template_name
1170      * @param null|mixed                $cache_id
1171      * @param null|mixed                $compile_id
1172      * @param null                      $caching
1173      * @param \Smarty_Internal_Template $template
1174      *
1175      * @return string
1176      */
1177     public function _getTemplateId($template_name, $cache_id = null, $compile_id = null, $caching = null,
1178                                    Smarty_Internal_Template $template = null)
1179     {
1180         $template_name = (strpos($template_name, ':') === false) ? "{$this->default_resource_type}:{$template_name}" :
1181             $template_name;
1182         $cache_id = $cache_id === null ? $this->cache_id : $cache_id;
1183         $compile_id = $compile_id === null ? $this->compile_id : $compile_id;
1184         $caching = (int) ($caching === null ? $this->caching : $caching);
1185
1186         if ((isset($template) && strpos($template_name, ':.') !== false) || $this->allow_ambiguous_resources) {
1187             $_templateId =
1188                 Smarty_Resource::getUniqueTemplateName((isset($template) ? $template : $this), $template_name) .
1189                 "#{$cache_id}#{$compile_id}#{$caching}";
1190         } else {
1191             $_templateId = $this->_joined_template_dir . "#{$template_name}#{$cache_id}#{$compile_id}#{$caching}";
1192         }
1193         if (isset($_templateId[ 150 ])) {
1194             $_templateId = sha1($_templateId);
1195         }
1196         return $_templateId;
1197     }
1198
1199     /**
1200      * Normalize path
1201      *  - remove /./ and /../
1202      *  - make it absolute if required
1203      *
1204      * @param string $path      file path
1205      * @param bool   $realpath  if true - convert to absolute
1206      *                          false - convert to relative
1207      *                          null - keep as it is but remove /./ /../
1208      *
1209      * @return string
1210      */
1211     public function _realpath($path, $realpath = null)
1212     {
1213         $nds = $this->ds == '/' ? '\\' : '/';
1214         // normalize $this->ds
1215         $path = str_replace($nds, $this->ds, $path);
1216         preg_match('%^(?<root>(?:[[:alpha:]]:[\\\\]|/|[\\\\]{2}[[:alpha:]]+|[[:print:]]{2,}:[/]{2}|[\\\\])?)(?<path>(?:[[:print:]]*))$%',
1217                    $path, $parts);
1218         $path = $parts[ 'path' ];
1219         if ($parts[ 'root' ] == '\\') {
1220             $parts[ 'root' ] = substr(getcwd(), 0, 2) . $parts[ 'root' ];
1221         } else {
1222             if ($realpath !== null && !$parts[ 'root' ]) {
1223                 $path = getcwd() . $this->ds . $path;
1224             }
1225         }
1226         // remove noop 'DIRECTORY_SEPARATOR DIRECTORY_SEPARATOR' and 'DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR' patterns
1227         $path = preg_replace('#([\\\\/]([.]?[\\\\/])+)#', $this->ds, $path);
1228         // resolve '..DIRECTORY_SEPARATOR' pattern, smallest first
1229         if (strpos($path, '..' . $this->ds) != false &&
1230             preg_match_all('#(([.]?[\\\\/])*([.][.])[\\\\/]([.]?[\\\\/])*)+#', $path, $match)
1231         ) {
1232             $counts = array();
1233             foreach ($match[ 0 ] as $m) {
1234                 $counts[] = (int) ((strlen($m) - 1) / 3);
1235             }
1236             sort($counts);
1237             foreach ($counts as $count) {
1238                 $path = preg_replace('#(([\\\\/]([.]?[\\\\/])*[^\\\\/.]+){' . $count .
1239                                      '}[\\\\/]([.]?[\\\\/])*([.][.][\\\\/]([.]?[\\\\/])*){' . $count . '})(?=[^.])#',
1240                                      $this->ds, $path);
1241             }
1242         }
1243
1244         return $parts[ 'root' ] . $path;
1245     }
1246
1247     /**
1248      * Empty template objects cache
1249      */
1250     public function _clearTemplateCache()
1251     {
1252         Smarty_Internal_Template::$isCacheTplObj = array();
1253         Smarty_Internal_Template::$tplObjCache = array();
1254     }
1255
1256     /**
1257      * Get Smarty object
1258      *
1259      * @return Smarty
1260      */
1261     public function _getSmartyObj()
1262     {
1263         return $this;
1264     }
1265
1266     /**
1267      * @param boolean $compile_check
1268      */
1269     public function setCompileCheck($compile_check)
1270     {
1271         $this->compile_check = $compile_check;
1272     }
1273
1274     /**
1275      * @param boolean $use_sub_dirs
1276      */
1277     public function setUseSubDirs($use_sub_dirs)
1278     {
1279         $this->use_sub_dirs = $use_sub_dirs;
1280     }
1281
1282     /**
1283      * @param int $error_reporting
1284      */
1285     public function setErrorReporting($error_reporting)
1286     {
1287         $this->error_reporting = $error_reporting;
1288     }
1289
1290     /**
1291      * @param boolean $escape_html
1292      */
1293     public function setEscapeHtml($escape_html)
1294     {
1295         $this->escape_html = $escape_html;
1296     }
1297
1298     /**
1299      * @param boolean $auto_literal
1300      */
1301     public function setAutoLiteral($auto_literal)
1302     {
1303         $this->auto_literal = $auto_literal;
1304     }
1305
1306     /**
1307      * @param boolean $force_compile
1308      */
1309     public function setForceCompile($force_compile)
1310     {
1311         $this->force_compile = $force_compile;
1312     }
1313
1314     /**
1315      * @param boolean $merge_compiled_includes
1316      */
1317     public function setMergeCompiledIncludes($merge_compiled_includes)
1318     {
1319         $this->merge_compiled_includes = $merge_compiled_includes;
1320     }
1321
1322     /**
1323      * @param string $left_delimiter
1324      */
1325     public function setLeftDelimiter($left_delimiter)
1326     {
1327         $this->left_delimiter = $left_delimiter;
1328     }
1329
1330     /**
1331      * @param string $right_delimiter
1332      */
1333     public function setRightDelimiter($right_delimiter)
1334     {
1335         $this->right_delimiter = $right_delimiter;
1336     }
1337
1338     /**
1339      * @param boolean $debugging
1340      */
1341     public function setDebugging($debugging)
1342     {
1343         $this->debugging = $debugging;
1344     }
1345
1346     /**
1347      * @param boolean $config_overwrite
1348      */
1349     public function setConfigOverwrite($config_overwrite)
1350     {
1351         $this->config_overwrite = $config_overwrite;
1352     }
1353
1354     /**
1355      * @param boolean $config_booleanize
1356      */
1357     public function setConfigBooleanize($config_booleanize)
1358     {
1359         $this->config_booleanize = $config_booleanize;
1360     }
1361
1362     /**
1363      * @param boolean $config_read_hidden
1364      */
1365     public function setConfigReadHidden($config_read_hidden)
1366     {
1367         $this->config_read_hidden = $config_read_hidden;
1368     }
1369
1370     /**
1371      * @param boolean $compile_locking
1372      */
1373     public function setCompileLocking($compile_locking)
1374     {
1375         $this->compile_locking = $compile_locking;
1376     }
1377
1378     /**
1379      * @param string $default_resource_type
1380      */
1381     public function setDefaultResourceType($default_resource_type)
1382     {
1383         $this->default_resource_type = $default_resource_type;
1384     }
1385
1386     /**
1387      * @param string $caching_type
1388      */
1389     public function setCachingType($caching_type)
1390     {
1391         $this->caching_type = $caching_type;
1392     }
1393
1394     /**
1395      * Test install
1396      *
1397      * @param null $errors
1398      */
1399     public function testInstall(&$errors = null)
1400     {
1401         Smarty_Internal_TestInstall::testInstall($this, $errors);
1402     }
1403
1404     /**
1405      * <<magic>> Generic getter.
1406      * Calls the appropriate getter function.
1407      * Issues an E_USER_NOTICE if no valid getter is found.
1408      *
1409      * @param  string $name property name
1410      *
1411      * @return mixed
1412      */
1413     public function __get($name)
1414     {
1415         if (isset($this->accessMap[ $name ])) {
1416             $method = 'get' . $this->accessMap[ $name ];
1417             return $this->{$method}();
1418         } elseif (isset($this->_cache[ $name ])) {
1419             return $this->_cache[ $name ];
1420         } elseif (in_array($name, $this->obsoleteProperties)) {
1421             return null;
1422         } else {
1423             trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE);
1424         }
1425         return null;
1426     }
1427
1428     /**
1429      * <<magic>> Generic setter.
1430      * Calls the appropriate setter function.
1431      * Issues an E_USER_NOTICE if no valid setter is found.
1432      *
1433      * @param string $name  property name
1434      * @param mixed  $value parameter passed to setter
1435      */
1436     public function __set($name, $value)
1437     {
1438         if (isset($this->accessMap[ $name ])) {
1439             $method = 'set' . $this->accessMap[ $name ];
1440             $this->{$method}($value);
1441         } elseif (in_array($name, $this->obsoleteProperties)) {
1442             return;
1443         } else {
1444             if (is_object($value) && method_exists($value, $name)) {
1445                 $this->$name = $value;
1446             } else {
1447                 trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE);
1448             }
1449         }
1450     }
1451
1452     /**
1453      * Error Handler to mute expected messages
1454      *
1455      * @link http://php.net/set_error_handler
1456      *
1457      * @param  integer $errno Error level
1458      * @param          $errstr
1459      * @param          $errfile
1460      * @param          $errline
1461      * @param          $errcontext
1462      *
1463      * @return bool|void
1464      */
1465     public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $errcontext)
1466     {
1467         $_is_muted_directory = false;
1468
1469         // add the SMARTY_DIR to the list of muted directories
1470         if (!isset(Smarty::$_muted_directories[ SMARTY_DIR ])) {
1471             $smarty_dir = realpath(SMARTY_DIR);
1472             if ($smarty_dir !== false) {
1473                 Smarty::$_muted_directories[ SMARTY_DIR ] =
1474                     array('file' => $smarty_dir, 'length' => strlen($smarty_dir),);
1475             }
1476         }
1477
1478         // walk the muted directories and test against $errfile
1479         foreach (Smarty::$_muted_directories as $key => &$dir) {
1480             if (!$dir) {
1481                 // resolve directory and length for speedy comparisons
1482                 $file = realpath($key);
1483                 if ($file === false) {
1484                     // this directory does not exist, remove and skip it
1485                     unset(Smarty::$_muted_directories[ $key ]);
1486                     continue;
1487                 }
1488                 $dir = array('file' => $file, 'length' => strlen($file),);
1489             }
1490             if (!strncmp($errfile, $dir[ 'file' ], $dir[ 'length' ])) {
1491                 $_is_muted_directory = true;
1492                 break;
1493             }
1494         }
1495         // pass to next error handler if this error did not occur inside SMARTY_DIR
1496         // or the error was within smarty but masked to be ignored
1497         if (!$_is_muted_directory || ($errno && $errno & error_reporting())) {
1498             if (Smarty::$_previous_error_handler) {
1499                 return call_user_func(Smarty::$_previous_error_handler, $errno, $errstr, $errfile, $errline,
1500                                       $errcontext);
1501             } else {
1502                 return false;
1503             }
1504         }
1505         return;
1506     }
1507
1508     /**
1509      * Enable error handler to mute expected messages
1510      *
1511      * @return void
1512      */
1513     public static function muteExpectedErrors()
1514     {
1515         /*
1516             error muting is done because some people implemented custom error_handlers using
1517             http://php.net/set_error_handler and for some reason did not understand the following paragraph:
1518
1519                 It is important to remember that the standard PHP error handler is completely bypassed for the
1520                 error types specified by error_types unless the callback function returns FALSE.
1521                 error_reporting() settings will have no effect and your error handler will be called regardless -
1522                 however you are still able to read the current value of error_reporting and act appropriately.
1523                 Of particular note is that this value will be 0 if the statement that caused the error was
1524                 prepended by the @ error-control operator.
1525
1526             Smarty deliberately uses @filemtime() over file_exists() and filemtime() in some places. Reasons include
1527                 - @filemtime() is almost twice as fast as using an additional file_exists()
1528                 - between file_exists() and filemtime() a possible race condition is opened,
1529                   which does not exist using the simple @filemtime() approach.
1530         */
1531         $error_handler = array('Smarty', 'mutingErrorHandler');
1532         $previous = set_error_handler($error_handler);
1533
1534         // avoid dead loops
1535         if ($previous !== $error_handler) {
1536             Smarty::$_previous_error_handler = $previous;
1537         }
1538     }
1539
1540     /**
1541      * Disable error handler muting expected messages
1542      *
1543      * @return void
1544      */
1545     public static function unmuteExpectedErrors()
1546     {
1547         restore_error_handler();
1548     }
1549 }