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