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