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