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