Smarty::clearCompiledTemplate()   F
last analyzed

Complexity

Conditions 34
Paths 709

Size

Total Lines 92
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 34
eloc 69
nc 709
nop 3
dl 0
loc 92
rs 0.4041
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Smarty;
4
5
use FilesystemIterator;
6
use RecursiveDirectoryIterator;
7
use RecursiveIteratorIterator;
8
use Smarty\Cacheresource\File;
9
use Smarty\Extension\Base;
10
use Smarty\Extension\BCPluginsAdapter;
11
use Smarty\Extension\CallbackWrapper;
12
use Smarty\Extension\CoreExtension;
13
use Smarty\Extension\DefaultExtension;
14
use Smarty\Extension\ExtensionInterface;
15
use Smarty\Filter\Output\TrimWhitespace;
16
use Smarty\Runtime\CaptureRuntime;
17
use Smarty\Runtime\DefaultPluginHandlerRuntime;
18
use Smarty\Runtime\ForeachRuntime;
19
use Smarty\Runtime\InheritanceRuntime;
20
use Smarty\Runtime\TplFunctionRuntime;
21
22
23
/**
24
 * Project:     Smarty: the PHP compiling template engine
25
 *
26
 * This library is free software; you can redistribute it and/or
27
 * modify it under the terms of the GNU Lesser General Public
28
 * License as published by the Free Software Foundation; either
29
 * version 3.0 of the License, or (at your option) any later version.
30
 *
31
 * This library is distributed in the hope that it will be useful,
32
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
34
 * Lesser General Public License for more details.
35
 *
36
 * You should have received a copy of the GNU Lesser General Public
37
 * License along with this library; if not, write to the Free Software
38
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
39
 * For questions, help, comments, discussion, etc., please join the
40
 * Smarty mailing list. Send a blank e-mail to
41
 * [email protected]
42
 *
43
 * @author    Monte Ohrt <monte at ohrt dot com>
44
 * @author    Uwe Tews   <uwe dot tews at gmail dot com>
45
 * @author    Rodney Rehm
46
 * @author    Simon Wisselink
47
 */
48
49
/**
50
 * This is the main Smarty class
51
 */
52
class Smarty extends \Smarty\TemplateBase {
53
54
	/**
55
	 * smarty version
56
	 */
57
	const SMARTY_VERSION = '5.4.3';
58
59
	/**
60
	 * define caching modes
61
	 */
62
	const CACHING_OFF = 0;
63
	const CACHING_LIFETIME_CURRENT = 1;
64
	const CACHING_LIFETIME_SAVED = 2;
65
	/**
66
	 * define constant for clearing cache files be saved expiration dates
67
	 */
68
	const CLEAR_EXPIRED = -1;
69
	/**
70
	 * define compile check modes
71
	 */
72
	const COMPILECHECK_OFF = 0;
73
	const COMPILECHECK_ON = 1;
74
	/**
75
	 * filter types
76
	 */
77
	const FILTER_POST = 'post';
78
	const FILTER_PRE = 'pre';
79
	const FILTER_OUTPUT = 'output';
80
	const FILTER_VARIABLE = 'variable';
81
	/**
82
	 * plugin types
83
	 */
84
	const PLUGIN_FUNCTION = 'function';
85
	const PLUGIN_BLOCK = 'block';
86
	const PLUGIN_COMPILER = 'compiler';
87
	const PLUGIN_MODIFIER = 'modifier';
88
	const PLUGIN_MODIFIERCOMPILER = 'modifiercompiler';
89
90
	/**
91
	 * The character set to adhere to (defaults to "UTF-8")
92
	 */
93
	public static $_CHARSET = 'UTF-8';
94
95
	/**
96
	 * The date format to be used internally
97
	 * (accepts date() and strftime())
98
	 */
99
	public static $_DATE_FORMAT = '%b %e, %Y';
100
101
	/**
102
	 * Flag denoting if PCRE should run in UTF-8 mode
103
	 */
104
	public static $_UTF8_MODIFIER = 'u';
105
106
	/**
107
	 * Flag denoting if operating system is windows
108
	 */
109
	public static $_IS_WINDOWS = false;
110
111
	/**
112
	 * auto literal on delimiters with whitespace
113
	 *
114
	 * @var boolean
115
	 */
116
	public $auto_literal = true;
117
118
	/**
119
	 * display error on not assigned variables
120
	 *
121
	 * @var boolean
122
	 */
123
	public $error_unassigned = false;
124
125
	/**
126
	 * flag if template_dir is normalized
127
	 *
128
	 * @var bool
129
	 */
130
	public $_templateDirNormalized = false;
131
132
	/**
133
	 * joined template directory string used in cache keys
134
	 *
135
	 * @var string
136
	 */
137
	public $_joined_template_dir = null;
138
139
	/**
140
	 * flag if config_dir is normalized
141
	 *
142
	 * @var bool
143
	 */
144
	public $_configDirNormalized = false;
145
146
	/**
147
	 * joined config directory string used in cache keys
148
	 *
149
	 * @var string
150
	 */
151
	public $_joined_config_dir = null;
152
153
	/**
154
	 * default template handler
155
	 *
156
	 * @var callable
157
	 */
158
	public $default_template_handler_func = null;
159
160
	/**
161
	 * default config handler
162
	 *
163
	 * @var callable
164
	 */
165
	public $default_config_handler_func = null;
166
167
	/**
168
	 * default plugin handler
169
	 *
170
	 * @var callable
171
	 */
172
	private $default_plugin_handler_func = null;
173
174
	/**
175
	 * flag if template_dir is normalized
176
	 *
177
	 * @var bool
178
	 */
179
	public $_compileDirNormalized = false;
180
181
	/**
182
	 * flag if template_dir is normalized
183
	 *
184
	 * @var bool
185
	 */
186
	public $_cacheDirNormalized = false;
187
188
	/**
189
	 * force template compiling?
190
	 *
191
	 * @var boolean
192
	 */
193
	public $force_compile = false;
194
195
	/**
196
	 * use sub dirs for compiled/cached files?
197
	 *
198
	 * @var boolean
199
	 */
200
	public $use_sub_dirs = false;
201
202
	/**
203
	 * merge compiled includes
204
	 *
205
	 * @var boolean
206
	 */
207
	public $merge_compiled_includes = false;
208
209
	/**
210
	 * force cache file creation
211
	 *
212
	 * @var boolean
213
	 */
214
	public $force_cache = false;
215
216
	/**
217
	 * template left-delimiter
218
	 *
219
	 * @var string
220
	 */
221
	private $left_delimiter = "{";
222
223
	/**
224
	 * template right-delimiter
225
	 *
226
	 * @var string
227
	 */
228
	private $right_delimiter = "}";
229
230
	/**
231
	 * array of strings which shall be treated as literal by compiler
232
	 *
233
	 * @var array string
234
	 */
235
	public $literals = [];
236
237
	/**
238
	 * class name
239
	 * This should be instance of \Smarty\Security.
240
	 *
241
	 * @var string
242
	 * @see \Smarty\Security
243
	 */
244
	public $security_class = \Smarty\Security::class;
245
246
	/**
247
	 * implementation of security class
248
	 *
249
	 * @var \Smarty\Security
250
	 */
251
	public $security_policy = null;
252
253
	/**
254
	 * debug mode
255
	 * Setting this to true enables the debug-console. Setting it to 2 enables individual Debug Console window by
256
	 * template name.
257
	 *
258
	 * @var boolean|int
259
	 */
260
	public $debugging = false;
261
262
	/**
263
	 * This determines if debugging is enable-able from the browser.
264
	 * <ul>
265
	 *  <li>NONE => no debugging control allowed</li>
266
	 *  <li>URL => enable debugging when SMARTY_DEBUG is found in the URL.</li>
267
	 * </ul>
268
	 *
269
	 * @var string
270
	 */
271
	public $debugging_ctrl = 'NONE';
272
273
	/**
274
	 * Name of debugging URL-param.
275
	 * Only used when $debugging_ctrl is set to 'URL'.
276
	 * The name of the URL-parameter that activates debugging.
277
	 *
278
	 * @var string
279
	 */
280
	public $smarty_debug_id = 'SMARTY_DEBUG';
281
282
	/**
283
	 * Path of debug template.
284
	 *
285
	 * @var string
286
	 */
287
	public $debug_tpl = null;
288
289
	/**
290
	 * When set, smarty uses this value as error_reporting-level.
291
	 *
292
	 * @var int
293
	 */
294
	public $error_reporting = null;
295
296
	/**
297
	 * Controls whether variables with the same name overwrite each other.
298
	 *
299
	 * @var boolean
300
	 */
301
	public $config_overwrite = true;
302
303
	/**
304
	 * Controls whether config values of on/true/yes and off/false/no get converted to boolean.
305
	 *
306
	 * @var boolean
307
	 */
308
	public $config_booleanize = true;
309
310
	/**
311
	 * Controls whether hidden config sections/vars are read from the file.
312
	 *
313
	 * @var boolean
314
	 */
315
	public $config_read_hidden = false;
316
317
	/**
318
	 * locking concurrent compiles
319
	 *
320
	 * @var boolean
321
	 */
322
	public $compile_locking = true;
323
324
	/**
325
	 * Controls whether cache resources should use locking mechanism
326
	 *
327
	 * @var boolean
328
	 */
329
	public $cache_locking = false;
330
331
	/**
332
	 * seconds to wait for acquiring a lock before ignoring the write lock
333
	 *
334
	 * @var float
335
	 */
336
	public $locking_timeout = 10;
337
338
	/**
339
	 * resource type used if none given
340
	 * Must be a valid key of $registered_resources.
341
	 *
342
	 * @var string
343
	 */
344
	public $default_resource_type = 'file';
345
346
	/**
347
	 * cache resource
348
	 * Must be a subclass of \Smarty\Cacheresource\Base
349
	 *
350
	 * @var \Smarty\Cacheresource\Base
351
	 */
352
	private $cacheResource;
353
354
	/**
355
	 * config type
356
	 *
357
	 * @var string
358
	 */
359
	public $default_config_type = 'file';
360
361
	/**
362
	 * check If-Modified-Since headers
363
	 *
364
	 * @var boolean
365
	 */
366
	public $cache_modified_check = false;
367
368
	/**
369
	 * registered plugins
370
	 *
371
	 * @var array
372
	 */
373
	public $registered_plugins = [];
374
375
	/**
376
	 * registered objects
377
	 *
378
	 * @var array
379
	 */
380
	public $registered_objects = [];
381
382
	/**
383
	 * registered classes
384
	 *
385
	 * @var array
386
	 */
387
	public $registered_classes = [];
388
389
	/**
390
	 * registered resources
391
	 *
392
	 * @var array
393
	 */
394
	public $registered_resources = [];
395
396
	/**
397
	 * registered cache resources
398
	 *
399
	 * @var array
400
	 * @deprecated since 5.0
401
	 */
402
	private $registered_cache_resources = [];
403
404
	/**
405
	 * default modifier
406
	 *
407
	 * @var array
408
	 */
409
	public $default_modifiers = [];
410
411
	/**
412
	 * autoescape variable output
413
	 *
414
	 * @var boolean
415
	 */
416
	public $escape_html = false;
417
418
	/**
419
	 * start time for execution time calculation
420
	 *
421
	 * @var int
422
	 */
423
	public $start_time = 0;
424
425
	/**
426
	 * internal flag to enable parser debugging
427
	 *
428
	 * @var bool
429
	 */
430
	public $_parserdebug = false;
431
432
	/**
433
	 * Debug object
434
	 *
435
	 * @var \Smarty\Debug
436
	 */
437
	public $_debug = null;
438
439
	/**
440
	 * template directory
441
	 *
442
	 * @var array
443
	 */
444
	protected $template_dir = ['./templates/'];
445
446
	/**
447
	 * flags for normalized template directory entries
448
	 *
449
	 * @var array
450
	 */
451
	protected $_processedTemplateDir = [];
452
453
	/**
454
	 * config directory
455
	 *
456
	 * @var array
457
	 */
458
	protected $config_dir = ['./configs/'];
459
460
	/**
461
	 * flags for normalized template directory entries
462
	 *
463
	 * @var array
464
	 */
465
	protected $_processedConfigDir = [];
466
467
	/**
468
	 * compile directory
469
	 *
470
	 * @var string
471
	 */
472
	protected $compile_dir = './templates_c/';
473
474
	/**
475
	 * cache directory
476
	 *
477
	 * @var string
478
	 */
479
	protected $cache_dir = './cache/';
480
481
	/**
482
	 * PHP7 Compatibility mode
483
	 *
484
	 * @var bool
485
	 */
486
	private $isMutingUndefinedOrNullWarnings = false;
487
488
	/**
489
	 * Cache of loaded resource handlers.
490
	 *
491
	 * @var array
492
	 */
493
	public $_resource_handlers = [];
494
495
	/**
496
	 * Cache of loaded cacheresource handlers.
497
	 *
498
	 * @var array
499
	 */
500
	public $_cacheresource_handlers = [];
501
502
	/**
503
	 * List of extensions
504
	 *
505
	 * @var ExtensionInterface[]
506
	 */
507
	private $extensions = [];
508
	/**
509
	 * @var BCPluginsAdapter
510
	 */
511
	private $BCPluginsAdapter;
512
513
	/**
514
	 * Initialize new Smarty object
515
	 */
516
	public function __construct() {
517
518
		$this->start_time = microtime(true);
0 ignored issues
show
Documentation Bug introduced by
It seems like microtime(true) can also be of type string. However, the property $start_time is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
519
		// Check if we're running on Windows
520
		\Smarty\Smarty::$_IS_WINDOWS = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
521
		// let PCRE (preg_*) treat strings as ISO-8859-1 if we're not dealing with UTF-8
522
		if (\Smarty\Smarty::$_CHARSET !== 'UTF-8') {
523
			\Smarty\Smarty::$_UTF8_MODIFIER = '';
524
		}
525
526
		$this->BCPluginsAdapter = new BCPluginsAdapter($this);
527
528
		$this->extensions[] = new CoreExtension();
529
		$this->extensions[] = new DefaultExtension();
530
		$this->extensions[] = $this->BCPluginsAdapter;
531
532
		$this->cacheResource = new File();
533
	}
534
535
	/**
536
	 * Load an additional extension.
537
	 *
538
	 * @return void
539
	 */
540
	public function addExtension(ExtensionInterface $extension) {
541
		$this->extensions[] = $extension;
542
	}
543
544
	/**
545
	 * Returns all loaded extensions
546
	 *
547
	 * @return array|ExtensionInterface[]
548
	 */
549
	public function getExtensions(): array {
550
		return $this->extensions;
551
	}
552
553
	/**
554
	 * Replace the entire list extensions, allowing you to determine the exact order of the extensions.
555
	 *
556
	 * @param ExtensionInterface[] $extensions
557
	 *
558
	 * @return void
559
	 */
560
	public function setExtensions(array $extensions): void {
561
		$this->extensions = $extensions;
562
	}
563
564
	/**
565
	 * Check if a template resource exists
566
	 *
567
	 * @param string $resource_name template name
568
	 *
569
	 * @return bool status
570
	 * @throws \Smarty\Exception
571
	 */
572
	public function templateExists($resource_name) {
573
		// create source object
574
		$source = Template\Source::load(null, $this, $resource_name);
575
		return $source->exists;
576
	}
577
578
	/**
579
	 * Loads security class and enables security
580
	 *
581
	 * @param string|\Smarty\Security $security_class if a string is used, it must be class-name
582
	 *
583
	 * @return static                 current Smarty instance for chaining
584
	 * @throws \Smarty\Exception
585
	 */
586
	public function enableSecurity($security_class = null) {
587
		\Smarty\Security::enableSecurity($this, $security_class);
588
		return $this;
589
	}
590
591
	/**
592
	 * Disable security
593
	 *
594
	 * @return static current Smarty instance for chaining
595
	 */
596
	public function disableSecurity() {
597
		$this->security_policy = null;
598
		return $this;
599
	}
600
601
	/**
602
	 * Add template directory(s)
603
	 *
604
	 * @param string|array $template_dir directory(s) of template sources
605
	 * @param string $key of the array element to assign the template dir to
606
	 * @param bool $isConfig true for config_dir
607
	 *
608
	 * @return static current Smarty instance for chaining
609
	 */
610
	public function addTemplateDir($template_dir, $key = null, $isConfig = false) {
611
		if ($isConfig) {
612
			$processed = &$this->_processedConfigDir;
613
			$dir = &$this->config_dir;
614
			$this->_configDirNormalized = false;
615
		} else {
616
			$processed = &$this->_processedTemplateDir;
617
			$dir = &$this->template_dir;
618
			$this->_templateDirNormalized = false;
619
		}
620
		if (is_array($template_dir)) {
621
			foreach ($template_dir as $k => $v) {
622
				if (is_int($k)) {
623
					// indexes are not merged but appended
624
					$dir[] = $v;
625
				} else {
626
					// string indexes are overridden
627
					$dir[$k] = $v;
628
					unset($processed[$key]);
629
				}
630
			}
631
		} else {
632
			if ($key !== null) {
633
				// override directory at specified index
634
				$dir[$key] = $template_dir;
635
				unset($processed[$key]);
636
			} else {
637
				// append new directory
638
				$dir[] = $template_dir;
639
			}
640
		}
641
		return $this;
642
	}
643
644
	/**
645
	 * Get template directories
646
	 *
647
	 * @param mixed $index index of directory to get, null to get all
648
	 * @param bool $isConfig true for config_dir
649
	 *
650
	 * @return array|string list of template directories, or directory of $index
651
	 */
652
	public function getTemplateDir($index = null, $isConfig = false) {
653
		if ($isConfig) {
654
			$dir = &$this->config_dir;
655
		} else {
656
			$dir = &$this->template_dir;
657
		}
658
		if ($isConfig ? !$this->_configDirNormalized : !$this->_templateDirNormalized) {
659
			$this->_normalizeTemplateConfig($isConfig);
660
		}
661
		if ($index !== null) {
662
			return isset($dir[$index]) ? $dir[$index] : null;
663
		}
664
		return $dir;
665
	}
666
667
	/**
668
	 * Set template directory
669
	 *
670
	 * @param string|array $template_dir directory(s) of template sources
671
	 * @param bool $isConfig true for config_dir
672
	 *
673
	 * @return static current Smarty instance for chaining
674
	 */
675
	public function setTemplateDir($template_dir, $isConfig = false) {
676
		if ($isConfig) {
677
			$this->config_dir = [];
678
			$this->_processedConfigDir = [];
679
		} else {
680
			$this->template_dir = [];
681
			$this->_processedTemplateDir = [];
682
		}
683
		$this->addTemplateDir($template_dir, null, $isConfig);
684
		return $this;
685
	}
686
687
	/**
688
	 * Adds a template directory before any existing directoires
689
	 *
690
	 * @param string $new_template_dir directory of template sources
691
	 * @param bool $is_config true for config_dir
692
	 *
693
	 * @return static current Smarty instance for chaining
694
	 */
695
	public function prependTemplateDir($new_template_dir, $is_config = false) {
696
		$current_template_dirs = $is_config ? $this->config_dir : $this->template_dir;
697
		array_unshift($current_template_dirs, $new_template_dir);
698
		$this->setTemplateDir($current_template_dirs, $is_config);
699
		return $this;
700
	}
701
702
	/**
703
	 * Add config directory(s)
704
	 *
705
	 * @param string|array $config_dir directory(s) of config sources
706
	 * @param mixed $key key of the array element to assign the config dir to
707
	 *
708
	 * @return static current Smarty instance for chaining
709
	 */
710
	public function addConfigDir($config_dir, $key = null) {
711
		return $this->addTemplateDir($config_dir, $key, true);
712
	}
713
714
	/**
715
	 * Get config directory
716
	 *
717
	 * @param mixed $index index of directory to get, null to get all
718
	 *
719
	 * @return array configuration directory
720
	 */
721
	public function getConfigDir($index = null) {
722
		return $this->getTemplateDir($index, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getTemplateDir($index, true) also could return the type string which is incompatible with the documented return type array.
Loading history...
723
	}
724
725
	/**
726
	 * Set config directory
727
	 *
728
	 * @param $config_dir
729
	 *
730
	 * @return static current Smarty instance for chaining
731
	 */
732
	public function setConfigDir($config_dir) {
733
		return $this->setTemplateDir($config_dir, true);
734
	}
735
736
	/**
737
	 * Registers plugin to be used in templates
738
	 *
739
	 * @param string $type plugin type
740
	 * @param string $name name of template tag
741
	 * @param callable $callback PHP callback to register
742
	 * @param bool $cacheable if true (default) this function is cache able
743
	 *
744
	 * @return $this
745
	 * @throws \Smarty\Exception
746
	 *
747
	 * @api  Smarty::registerPlugin()
748
	 */
749
	public function registerPlugin($type, $name, $callback, $cacheable = true) {
750
		if (isset($this->registered_plugins[$type][$name])) {
751
			throw new Exception("Plugin tag '{$name}' already registered");
752
		} elseif (!is_callable($callback) && !class_exists($callback)) {
0 ignored issues
show
Bug introduced by
$callback of type callable is incompatible with the type string expected by parameter $class of class_exists(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

752
		} elseif (!is_callable($callback) && !class_exists(/** @scrutinizer ignore-type */ $callback)) {
Loading history...
753
			throw new Exception("Plugin '{$name}' not callable");
754
		} else {
755
			$this->registered_plugins[$type][$name] = [$callback, (bool)$cacheable];
756
		}
757
		return $this;
758
	}
759
760
	/**
761
	 * Returns plugin previously registered using ::registerPlugin as a numerical array as follows or null if not found:
762
	 * [
763
	 *  0 => the callback
764
	 *  1 => (bool) $cacheable
765
	 *  2 => (array) $cache_attr
766
	 * ]
767
	 *
768
	 * @param string $type plugin type
769
	 * @param string $name name of template tag
770
	 *
771
	 * @return array|null
772
	 *
773
	 * @api  Smarty::unregisterPlugin()
774
	 */
775
	public function getRegisteredPlugin($type, $name): ?array {
776
		if (isset($this->registered_plugins[$type][$name])) {
777
			return $this->registered_plugins[$type][$name];
778
		}
779
		return null;
780
	}
781
782
	/**
783
	 * Unregisters plugin previously registered using ::registerPlugin
784
	 *
785
	 * @param string $type plugin type
786
	 * @param string $name name of template tag
787
	 *
788
	 * @return $this
789
	 *
790
	 * @api  Smarty::unregisterPlugin()
791
	 */
792
	public function unregisterPlugin($type, $name) {
793
		if (isset($this->registered_plugins[$type][$name])) {
794
			unset($this->registered_plugins[$type][$name]);
795
		}
796
		return $this;
797
	}
798
799
	/**
800
	 * Adds directory of plugin files
801
	 *
802
	 * @param null|array|string $plugins_dir
803
	 *
804
	 * @return static current Smarty instance for chaining
805
	 * @deprecated since 5.0
806
	 */
807
	public function addPluginsDir($plugins_dir) {
808
		trigger_error('Using Smarty::addPluginsDir() to load plugins is deprecated and will be ' .
809
			'removed in a future release. Use Smarty::addExtension() to add an extension or Smarty::registerPlugin to ' .
810
			'quickly register a plugin using a callback function.', E_USER_DEPRECATED);
811
812
		foreach ((array)$plugins_dir as $v) {
813
			$path = $this->_realpath(rtrim($v ?? '', '/\\') . DIRECTORY_SEPARATOR, true);
814
			$this->BCPluginsAdapter->loadPluginsFromDir($path);
815
		}
816
817
		return $this;
818
	}
819
820
	/**
821
	 * Get plugin directories
822
	 *
823
	 * @return array list of plugin directories
824
	 * @deprecated since 5.0
825
	 */
826
	public function getPluginsDir() {
827
		trigger_error('Using Smarty::getPluginsDir() is deprecated and will be ' .
828
			'removed in a future release. It will always return an empty array.', E_USER_DEPRECATED);
829
		return [];
830
	}
831
832
	/**
833
	 * Set plugins directory
834
	 *
835
	 * @param string|array $plugins_dir directory(s) of plugins
836
	 *
837
	 * @return static current Smarty instance for chaining
838
	 * @deprecated since 5.0
839
	 */
840
	public function setPluginsDir($plugins_dir) {
841
		trigger_error('Using Smarty::getPluginsDir() is deprecated and will be ' .
842
			'removed in a future release. For now, it will remove the DefaultExtension from the extensions list and ' .
843
			'proceed to call Smartyy::addPluginsDir..', E_USER_DEPRECATED);
844
845
		$this->extensions = array_filter(
846
			$this->extensions,
847
			function ($extension) {
848
				return !($extension instanceof DefaultExtension);
849
			}
850
		);
851
852
		return $this->addPluginsDir($plugins_dir);
0 ignored issues
show
Deprecated Code introduced by
The function Smarty\Smarty::addPluginsDir() has been deprecated: since 5.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

852
		return /** @scrutinizer ignore-deprecated */ $this->addPluginsDir($plugins_dir);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
853
	}
854
855
	/**
856
	 * Registers a default plugin handler
857
	 *
858
	 * @param callable $callback class/method name
859
	 *
860
	 * @return $this
861
	 * @throws Exception              if $callback is not callable
862
	 *
863
	 * @api  Smarty::registerDefaultPluginHandler()
864
	 *
865
	 * @deprecated since 5.0
866
	 */
867
	public function registerDefaultPluginHandler($callback) {
868
869
		trigger_error('Using Smarty::registerDefaultPluginHandler() is deprecated and will be ' .
870
			'removed in a future release. Please rewrite your plugin handler as an extension.',
871
			E_USER_DEPRECATED);
872
873
		if (is_callable($callback)) {
874
			$this->default_plugin_handler_func = $callback;
875
		} else {
876
			throw new Exception("Default plugin handler '$callback' not callable");
877
		}
878
		return $this;
879
	}
880
881
	/**
882
	 * Get compiled directory
883
	 *
884
	 * @return string path to compiled templates
885
	 */
886
	public function getCompileDir() {
887
		if (!$this->_compileDirNormalized) {
888
			$this->_normalizeDir('compile_dir', $this->compile_dir);
889
			$this->_compileDirNormalized = true;
890
		}
891
		return $this->compile_dir;
892
	}
893
894
	/**
895
	 *
896
	 * @param string $compile_dir directory to store compiled templates in
897
	 *
898
	 * @return static current Smarty instance for chaining
899
	 */
900
	public function setCompileDir($compile_dir) {
901
		$this->_normalizeDir('compile_dir', $compile_dir);
902
		$this->_compileDirNormalized = true;
903
		return $this;
904
	}
905
906
	/**
907
	 * Get cache directory
908
	 *
909
	 * @return string path of cache directory
910
	 */
911
	public function getCacheDir() {
912
		if (!$this->_cacheDirNormalized) {
913
			$this->_normalizeDir('cache_dir', $this->cache_dir);
914
			$this->_cacheDirNormalized = true;
915
		}
916
		return $this->cache_dir;
917
	}
918
919
	/**
920
	 * Set cache directory
921
	 *
922
	 * @param string $cache_dir directory to store cached templates in
923
	 *
924
	 * @return static current Smarty instance for chaining
925
	 */
926
	public function setCacheDir($cache_dir) {
927
		$this->_normalizeDir('cache_dir', $cache_dir);
928
		$this->_cacheDirNormalized = true;
929
		return $this;
930
	}
931
932
	private $templates = [];
933
934
	/**
935
	 * Creates a template object
936
	 *
937
	 * @param string $template_name
938
	 * @param mixed $cache_id cache id to be used with this template
939
	 * @param mixed $compile_id compile id to be used with this template
940
	 * @param null $parent next higher level of Smarty variables
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $parent is correct as it would always require null to be passed?
Loading history...
941
	 *
942
	 * @return Template template object
943
	 * @throws Exception
944
	 */
945
	public function createTemplate($template_name, $cache_id = null, $compile_id = null, $parent = null): Template {
946
947
		$data = [];
948
949
		// Shuffle params for backward compatibility: if 2nd param is an object, it's the parent
950
		if (is_object($cache_id)) {
951
			$parent = $cache_id;
952
			$cache_id = null;
953
		}
954
955
		// Shuffle params for backward compatibility: if 2nd param is an array, it's data
956
		if (is_array($cache_id)) {
957
			$data = $cache_id;
958
			$cache_id = null;
959
		}
960
961
		return $this->doCreateTemplate($template_name, $cache_id, $compile_id, $parent, null, null, false, $data);
962
	}
963
964
	/**
965
	 * Get unique template id
966
	 *
967
	 * @param string $resource_name
968
	 * @param null|mixed $cache_id
969
	 * @param null|mixed $compile_id
970
	 * @param null $caching
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $caching is correct as it would always require null to be passed?
Loading history...
971
	 *
972
	 * @return string
973
	 */
974
	private function generateUniqueTemplateId(
975
		$resource_name,
976
		$cache_id = null,
977
		$compile_id = null,
978
		$caching = null
979
	): string {
980
		// defaults for optional params
981
		$cache_id = $cache_id ?? $this->cache_id;
982
		$compile_id = $compile_id ?? $this->compile_id;
983
		$caching = (int)$caching ?? $this->caching;
984
985
		// Add default resource type to resource name if it is missing
986
		if (strpos($resource_name, ':') === false) {
987
			$resource_name = "{$this->default_resource_type}:{$resource_name}";
988
		}
989
990
		$_templateId = $resource_name . '#' . $cache_id . '#' . $compile_id . '#' . $caching;
991
992
		// hash very long IDs to prevent problems with filename length
993
		// do not hash shorter IDs, so they remain recognizable
994
		if (strlen($_templateId) > 150) {
995
			$_templateId = sha1($_templateId);
996
		}
997
998
		return $_templateId;
999
	}
1000
1001
	/**
1002
	 * Normalize path
1003
	 *  - remove /./ and /../
1004
	 *  - make it absolute if required
1005
	 *
1006
	 * @param string $path file path
1007
	 * @param bool $realpath if true - convert to absolute
1008
	 *                         false - convert to relative
1009
	 *                         null - keep as it is but
1010
	 *                         remove /./ /../
1011
	 *
1012
	 * @return string
1013
	 */
1014
	public function _realpath($path, $realpath = null) {
1015
		$nds = ['/' => '\\', '\\' => '/'];
1016
		preg_match(
1017
			'%^(?<root>(?:[[:alpha:]]:[\\\\/]|/|[\\\\]{2}[[:alpha:]]+|[[:print:]]{2,}:[/]{2}|[\\\\])?)(?<path>(.*))$%u',
1018
			$path,
1019
			$parts
1020
		);
1021
		$path = $parts['path'];
1022
		if ($parts['root'] === '\\') {
1023
			$parts['root'] = substr(getcwd(), 0, 2) . $parts['root'];
1024
		} else {
1025
			if ($realpath !== null && !$parts['root']) {
1026
				$path = getcwd() . DIRECTORY_SEPARATOR . $path;
1027
			}
1028
		}
1029
		// normalize DIRECTORY_SEPARATOR
1030
		$path = str_replace($nds[DIRECTORY_SEPARATOR], DIRECTORY_SEPARATOR, $path);
1031
		$parts['root'] = str_replace($nds[DIRECTORY_SEPARATOR], DIRECTORY_SEPARATOR, $parts['root']);
1032
		do {
1033
			$path = preg_replace(
1034
				['#[\\\\/]{2}#', '#[\\\\/][.][\\\\/]#', '#[\\\\/]([^\\\\/.]+)[\\\\/][.][.][\\\\/]#'],
1035
				DIRECTORY_SEPARATOR,
1036
				$path,
1037
				-1,
1038
				$count
1039
			);
1040
		} while ($count > 0);
1041
		return $realpath !== false ? $parts['root'] . $path : str_ireplace(getcwd(), '.', $parts['root'] . $path);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $realpath !== fal...$parts['root'] . $path) also could return the type array which is incompatible with the documented return type string.
Loading history...
1042
	}
1043
1044
	/**
1045
	 * @param boolean $use_sub_dirs
1046
	 */
1047
	public function setUseSubDirs($use_sub_dirs) {
1048
		$this->use_sub_dirs = $use_sub_dirs;
1049
	}
1050
1051
	/**
1052
	 * @param int $error_reporting
1053
	 */
1054
	public function setErrorReporting($error_reporting) {
1055
		$this->error_reporting = $error_reporting;
1056
	}
1057
1058
	/**
1059
	 * @param boolean $escape_html
1060
	 */
1061
	public function setEscapeHtml($escape_html) {
1062
		$this->escape_html = $escape_html;
1063
	}
1064
1065
	/**
1066
	 * Return auto_literal flag
1067
	 *
1068
	 * @return boolean
1069
	 */
1070
	public function getAutoLiteral() {
1071
		return $this->auto_literal;
1072
	}
1073
1074
	/**
1075
	 * Set auto_literal flag
1076
	 *
1077
	 * @param boolean $auto_literal
1078
	 */
1079
	public function setAutoLiteral($auto_literal = true) {
1080
		$this->auto_literal = $auto_literal;
1081
	}
1082
1083
	/**
1084
	 * @param boolean $force_compile
1085
	 */
1086
	public function setForceCompile($force_compile) {
1087
		$this->force_compile = $force_compile;
1088
	}
1089
1090
	/**
1091
	 * @param boolean $merge_compiled_includes
1092
	 */
1093
	public function setMergeCompiledIncludes($merge_compiled_includes) {
1094
		$this->merge_compiled_includes = $merge_compiled_includes;
1095
	}
1096
1097
	/**
1098
	 * Get left delimiter
1099
	 *
1100
	 * @return string
1101
	 */
1102
	public function getLeftDelimiter() {
1103
		return $this->left_delimiter;
1104
	}
1105
1106
	/**
1107
	 * Set left delimiter
1108
	 *
1109
	 * @param string $left_delimiter
1110
	 */
1111
	public function setLeftDelimiter($left_delimiter) {
1112
		$this->left_delimiter = $left_delimiter;
1113
	}
1114
1115
	/**
1116
	 * Get right delimiter
1117
	 *
1118
	 * @return string $right_delimiter
1119
	 */
1120
	public function getRightDelimiter() {
1121
		return $this->right_delimiter;
1122
	}
1123
1124
	/**
1125
	 * Set right delimiter
1126
	 *
1127
	 * @param string
1128
	 */
1129
	public function setRightDelimiter($right_delimiter) {
1130
		$this->right_delimiter = $right_delimiter;
1131
	}
1132
1133
	/**
1134
	 * @param boolean $debugging
1135
	 */
1136
	public function setDebugging($debugging) {
1137
		$this->debugging = $debugging;
1138
	}
1139
1140
	/**
1141
	 * @param boolean $config_overwrite
1142
	 */
1143
	public function setConfigOverwrite($config_overwrite) {
1144
		$this->config_overwrite = $config_overwrite;
1145
	}
1146
1147
	/**
1148
	 * @param boolean $config_booleanize
1149
	 */
1150
	public function setConfigBooleanize($config_booleanize) {
1151
		$this->config_booleanize = $config_booleanize;
1152
	}
1153
1154
	/**
1155
	 * @param boolean $config_read_hidden
1156
	 */
1157
	public function setConfigReadHidden($config_read_hidden) {
1158
		$this->config_read_hidden = $config_read_hidden;
1159
	}
1160
1161
	/**
1162
	 * @param boolean $compile_locking
1163
	 */
1164
	public function setCompileLocking($compile_locking) {
1165
		$this->compile_locking = $compile_locking;
1166
	}
1167
1168
	/**
1169
	 * @param string $default_resource_type
1170
	 */
1171
	public function setDefaultResourceType($default_resource_type) {
1172
		$this->default_resource_type = $default_resource_type;
1173
	}
1174
1175
	/**
1176
	 * Test install
1177
	 *
1178
	 * @param null $errors
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $errors is correct as it would always require null to be passed?
Loading history...
1179
	 */
1180
	public function testInstall(&$errors = null) {
1181
		\Smarty\TestInstall::testInstall($this, $errors);
1182
	}
1183
1184
	/**
1185
	 * Get Smarty object
1186
	 *
1187
	 * @return static
1188
	 */
1189
	public function getSmarty() {
1190
		return $this;
1191
	}
1192
1193
	/**
1194
	 * Normalize and set directory string
1195
	 *
1196
	 * @param string $dirName cache_dir or compile_dir
1197
	 * @param string $dir filepath of folder
1198
	 */
1199
	private function _normalizeDir($dirName, $dir) {
1200
		$this->{$dirName} = $this->_realpath(rtrim($dir ?? '', "/\\") . DIRECTORY_SEPARATOR, true);
1201
	}
1202
1203
	/**
1204
	 * Normalize template_dir or config_dir
1205
	 *
1206
	 * @param bool $isConfig true for config_dir
1207
	 */
1208
	private function _normalizeTemplateConfig($isConfig) {
1209
		if ($isConfig) {
1210
			$processed = &$this->_processedConfigDir;
1211
			$dir = &$this->config_dir;
1212
		} else {
1213
			$processed = &$this->_processedTemplateDir;
1214
			$dir = &$this->template_dir;
1215
		}
1216
		if (!is_array($dir)) {
0 ignored issues
show
introduced by
The condition is_array($dir) is always true.
Loading history...
1217
			$dir = (array)$dir;
1218
		}
1219
		foreach ($dir as $k => $v) {
1220
			if (!isset($processed[$k])) {
1221
				$dir[$k] = $this->_realpath(rtrim($v ?? '', "/\\") . DIRECTORY_SEPARATOR, true);
1222
				$processed[$k] = true;
1223
			}
1224
		}
1225
1226
		if ($isConfig) {
1227
			$this->_configDirNormalized = true;
1228
			$this->_joined_config_dir = join('#', $this->config_dir);
1229
		} else {
1230
			$this->_templateDirNormalized = true;
1231
			$this->_joined_template_dir = join('#', $this->template_dir);
1232
		}
1233
1234
	}
1235
1236
	/**
1237
	 * Mutes errors for "undefined index", "undefined array key" and "trying to read property of null".
1238
	 *
1239
	 * @void
1240
	 */
1241
	public function muteUndefinedOrNullWarnings(): void {
1242
		$this->isMutingUndefinedOrNullWarnings = true;
1243
	}
1244
1245
	/**
1246
	 * Indicates if Smarty will mute errors for "undefined index", "undefined array key" and "trying to read property of null".
1247
	 *
1248
	 * @return bool
1249
	 */
1250
	public function isMutingUndefinedOrNullWarnings(): bool {
1251
		return $this->isMutingUndefinedOrNullWarnings;
1252
	}
1253
1254
	/**
1255
	 * Empty cache for a specific template
1256
	 *
1257
	 * @param string $template_name template name
1258
	 * @param string $cache_id cache id
1259
	 * @param string $compile_id compile id
1260
	 * @param integer $exp_time expiration time
1261
	 * @param string $type resource type
1262
	 *
1263
	 * @return int number of cache files deleted
1264
	 * @throws \Smarty\Exception
1265
	 *
1266
	 * @api  Smarty::clearCache()
1267
	 */
1268
	public function clearCache(
1269
		$template_name,
1270
		$cache_id = null,
1271
		$compile_id = null,
1272
		$exp_time = null
1273
	) {
1274
		return $this->getCacheResource()->clear($this, $template_name, $cache_id, $compile_id, $exp_time);
1275
	}
1276
1277
	/**
1278
	 * Empty cache folder
1279
	 *
1280
	 * @param integer $exp_time expiration time
1281
	 * @param string $type resource type
1282
	 *
1283
	 * @return int number of cache files deleted
1284
	 *
1285
	 * @api  Smarty::clearAllCache()
1286
	 */
1287
	public function clearAllCache($exp_time = null) {
1288
		return $this->getCacheResource()->clearAll($this, $exp_time);
1289
	}
1290
1291
	/**
1292
	 * Delete compiled template file
1293
	 *
1294
	 * @param string $resource_name template name
1295
	 * @param string $compile_id compile id
1296
	 * @param integer $exp_time expiration time
1297
	 *
1298
	 * @return int number of template files deleted
1299
	 * @throws \Smarty\Exception
1300
	 *
1301
	 * @api  Smarty::clearCompiledTemplate()
1302
	 */
1303
	public function clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null) {
1304
		$_compile_dir = $this->getCompileDir();
1305
		if ($_compile_dir === '/') { //We should never want to delete this!
1306
			return 0;
1307
		}
1308
		$_compile_id = isset($compile_id) ? preg_replace('![^\w]+!', '_', $compile_id) : null;
1309
		$_dir_sep = $this->use_sub_dirs ? DIRECTORY_SEPARATOR : '^';
1310
		if (isset($resource_name)) {
1311
			$_save_stat = $this->caching;
1312
			$this->caching = \Smarty\Smarty::CACHING_OFF;
1313
			/* @var Template $tpl */
1314
			$tpl = $this->doCreateTemplate($resource_name);
1315
			$this->caching = $_save_stat;
1316
			if (!$tpl->getSource()->handler->recompiled && $tpl->getSource()->exists) {
1317
				$_resource_part_1 = basename(str_replace('^', DIRECTORY_SEPARATOR, $tpl->getCompiled()->filepath));
1318
				$_resource_part_1_length = strlen($_resource_part_1);
1319
			} else {
1320
				return 0;
1321
			}
1322
			$_resource_part_2 = str_replace('.php', '.cache.php', $_resource_part_1);
1323
			$_resource_part_2_length = strlen($_resource_part_2);
1324
		}
1325
		$_dir = $_compile_dir;
1326
		if ($this->use_sub_dirs && isset($_compile_id)) {
1327
			$_dir .= $_compile_id . $_dir_sep;
1328
		}
1329
		if (isset($_compile_id)) {
1330
			$_compile_id_part = $_compile_dir . $_compile_id . $_dir_sep;
1331
			$_compile_id_part_length = strlen($_compile_id_part);
1332
		}
1333
		$_count = 0;
1334
		try {
1335
			$_compileDirs = new RecursiveDirectoryIterator($_dir);
1336
		} catch (\UnexpectedValueException $e) {
1337
			// path not found / not a dir
1338
			return 0;
1339
		}
1340
		$_compile = new RecursiveIteratorIterator($_compileDirs, RecursiveIteratorIterator::CHILD_FIRST);
1341
		foreach ($_compile as $_file) {
1342
			if (substr(basename($_file->getPathname()), 0, 1) === '.') {
1343
				continue;
1344
			}
1345
			$_filepath = (string)$_file;
1346
			if ($_file->isDir()) {
1347
				if (!$_compile->isDot()) {
0 ignored issues
show
Bug introduced by
The method isDot() does not exist on RecursiveIteratorIterator. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1347
				if (!$_compile->/** @scrutinizer ignore-call */ isDot()) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1348
					// delete folder if empty
1349
					@rmdir($_file->getPathname());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for rmdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1349
					/** @scrutinizer ignore-unhandled */ @rmdir($_file->getPathname());

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1350
				}
1351
			} else {
1352
				// delete only php files
1353
				if (substr($_filepath, -4) !== '.php') {
1354
					continue;
1355
				}
1356
				$unlink = false;
1357
				if ((!isset($_compile_id) ||
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (! IssetNode || IssetNod...ce_part_2_length) === 0, Probably Intended Meaning: ! IssetNode || (IssetNod...e_part_2_length) === 0)
Loading history...
1358
						(isset($_filepath[$_compile_id_part_length]) &&
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_compile_id_part_length does not seem to be defined for all execution paths leading up to this point.
Loading history...
1359
							$a = !strncmp($_filepath, $_compile_id_part, $_compile_id_part_length)))
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_compile_id_part does not seem to be defined for all execution paths leading up to this point.
Loading history...
Unused Code introduced by
The assignment to $a is dead and can be removed.
Loading history...
1360
					&& (!isset($resource_name) || (isset($_filepath[$_resource_part_1_length])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_resource_part_1_length does not seem to be defined for all execution paths leading up to this point.
Loading history...
1361
							&& substr_compare(
1362
								$_filepath,
1363
								$_resource_part_1,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_resource_part_1 does not seem to be defined for all execution paths leading up to this point.
Loading history...
1364
								-$_resource_part_1_length,
1365
								$_resource_part_1_length
1366
							) === 0) || (isset($_filepath[$_resource_part_2_length])
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_resource_part_2_length does not seem to be defined for all execution paths leading up to this point.
Loading history...
1367
							&& substr_compare(
1368
								$_filepath,
1369
								$_resource_part_2,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $_resource_part_2 does not seem to be defined for all execution paths leading up to this point.
Loading history...
1370
								-$_resource_part_2_length,
1371
								$_resource_part_2_length
1372
							) === 0))
1373
				) {
1374
					if (isset($exp_time)) {
1375
						if (is_file($_filepath) && time() - filemtime($_filepath) >= $exp_time) {
1376
							$unlink = true;
1377
						}
1378
					} else {
1379
						$unlink = true;
1380
					}
1381
				}
1382
				if ($unlink && is_file($_filepath) && @unlink($_filepath)) {
1383
					$_count++;
1384
					if (function_exists('opcache_invalidate')
1385
						&& (!function_exists('ini_get') || strlen(ini_get('opcache.restrict_api')) < 1)
1386
					) {
1387
						opcache_invalidate($_filepath, true);
1388
					} elseif (function_exists('apc_delete_file')) {
1389
						apc_delete_file($_filepath);
1390
					}
1391
				}
1392
			}
1393
		}
1394
		return $_count;
1395
	}
1396
1397
	/**
1398
	 * Compile all template files
1399
	 *
1400
	 * @param string $extension file extension
1401
	 * @param bool $force_compile force all to recompile
1402
	 * @param int $time_limit
1403
	 * @param int $max_errors
1404
	 *
1405
	 * @return integer number of template files recompiled
1406
	 * @api Smarty::compileAllTemplates()
1407
	 *
1408
	 */
1409
	public function compileAllTemplates(
1410
		$extension = '.tpl',
1411
		$force_compile = false,
1412
		$time_limit = 0,
1413
		$max_errors = null
1414
	) {
1415
		return $this->compileAll($extension, $force_compile, $time_limit, $max_errors);
1416
	}
1417
1418
	/**
1419
	 * Compile all config files
1420
	 *
1421
	 * @param string $extension file extension
1422
	 * @param bool $force_compile force all to recompile
1423
	 * @param int $time_limit
1424
	 * @param int $max_errors
1425
	 *
1426
	 * @return int number of template files recompiled
1427
	 * @api Smarty::compileAllConfig()
1428
	 *
1429
	 */
1430
	public function compileAllConfig(
1431
		$extension = '.conf',
1432
		$force_compile = false,
1433
		$time_limit = 0,
1434
		$max_errors = null
1435
	) {
1436
		return $this->compileAll($extension, $force_compile, $time_limit, $max_errors, true);
1437
	}
1438
1439
	/**
1440
	 * Compile all template or config files
1441
	 *
1442
	 * @param string $extension template file name extension
1443
	 * @param bool $force_compile force all to recompile
1444
	 * @param int $time_limit set maximum execution time
1445
	 * @param int $max_errors set maximum allowed errors
1446
	 * @param bool $isConfig flag true if called for config files
1447
	 *
1448
	 * @return int number of template files compiled
1449
	 */
1450
	protected function compileAll(
1451
		$extension,
1452
		$force_compile,
1453
		$time_limit,
1454
		$max_errors,
1455
		$isConfig = false
1456
	) {
1457
		// switch off time limit
1458
		if (function_exists('set_time_limit')) {
1459
			@set_time_limit($time_limit);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for set_time_limit(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1459
			/** @scrutinizer ignore-unhandled */ @set_time_limit($time_limit);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1460
		}
1461
		$_count = 0;
1462
		$_error_count = 0;
1463
		$sourceDir = $isConfig ? $this->getConfigDir() : $this->getTemplateDir();
1464
		// loop over array of source directories
1465
		foreach ($sourceDir as $_dir) {
1466
			$_dir_1 = new RecursiveDirectoryIterator(
1467
				$_dir,
1468
				defined('FilesystemIterator::FOLLOW_SYMLINKS') ?
1469
					FilesystemIterator::FOLLOW_SYMLINKS : 0
1470
			);
1471
			$_dir_2 = new RecursiveIteratorIterator($_dir_1);
1472
			foreach ($_dir_2 as $_fileinfo) {
1473
				$_file = $_fileinfo->getFilename();
1474
				if (substr(basename($_fileinfo->getPathname()), 0, 1) === '.' || strpos($_file, '.svn') !== false) {
1475
					continue;
1476
				}
1477
				if (substr_compare($_file, $extension, -strlen($extension)) !== 0) {
1478
					continue;
1479
				}
1480
				if ($_fileinfo->getPath() !== substr($_dir, 0, -1)) {
1481
					$_file = substr($_fileinfo->getPath(), strlen($_dir)) . DIRECTORY_SEPARATOR . $_file;
1482
				}
1483
				echo "\n", $_dir, '---', $_file;
1484
				flush();
1485
				$_start_time = microtime(true);
1486
				$_smarty = clone $this;
1487
				//
1488
				$_smarty->force_compile = $force_compile;
1489
				try {
1490
					$_tpl = $this->doCreateTemplate($_file);
1491
					$_tpl->caching = self::CACHING_OFF;
1492
					$_tpl->setSource(
1493
						$isConfig ? \Smarty\Template\Config::load($_tpl) : \Smarty\Template\Source::load($_tpl)
1494
					);
1495
					if ($_tpl->mustCompile()) {
1496
						$_tpl->compileTemplateSource();
1497
						$_count++;
1498
						echo ' compiled in  ', microtime(true) - $_start_time, ' seconds';
1499
						flush();
1500
					} else {
1501
						echo ' is up to date';
1502
						flush();
1503
					}
1504
				} catch (\Exception $e) {
1505
					echo "\n        ------>Error: ", $e->getMessage(), "\n";
1506
					$_error_count++;
1507
				}
1508
				// free memory
1509
				unset($_tpl);
1510
				if ($max_errors !== null && $_error_count === $max_errors) {
1511
					echo "\ntoo many errors\n";
1512
					exit(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1513
				}
1514
			}
1515
		}
1516
		echo "\n";
1517
		return $_count;
1518
	}
1519
1520
	/**
1521
	 * check client side cache
1522
	 *
1523
	 * @param \Smarty\Template\Cached $cached
1524
	 * @param Template $_template
1525
	 * @param string $content
1526
	 *
1527
	 * @throws \Exception
1528
	 * @throws \Smarty\Exception
1529
	 */
1530
	public function cacheModifiedCheck(Template\Cached $cached, Template $_template, $content) {
1531
		$_isCached = $_template->isCached() && !$_template->getCompiled()->getNocacheCode();
1532
		$_last_modified_date =
1533
			@substr($_SERVER['HTTP_IF_MODIFIED_SINCE'], 0, strpos($_SERVER['HTTP_IF_MODIFIED_SINCE'], 'GMT') + 3);
1534
		if ($_isCached && $cached->timestamp <= strtotime($_last_modified_date)) {
1535
			switch (PHP_SAPI) {
1536
				case 'cgi': // php-cgi < 5.3
1537
				case 'cgi-fcgi': // php-cgi >= 5.3
1538
				case 'fpm-fcgi': // php-fpm >= 5.3.3
1539
					header('Status: 304 Not Modified');
1540
					break;
1541
				case 'cli':
1542
					if (/* ^phpunit */
1543
					!empty($_SERVER['SMARTY_PHPUNIT_DISABLE_HEADERS']) /* phpunit$ */
1544
					) {
1545
						$_SERVER['SMARTY_PHPUNIT_HEADERS'][] = '304 Not Modified';
1546
					}
1547
					break;
1548
				default:
1549
					if (/* ^phpunit */
1550
					!empty($_SERVER['SMARTY_PHPUNIT_DISABLE_HEADERS']) /* phpunit$ */
1551
					) {
1552
						$_SERVER['SMARTY_PHPUNIT_HEADERS'][] = '304 Not Modified';
1553
					} else {
1554
						header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
1555
					}
1556
					break;
1557
			}
1558
		} else {
1559
			switch (PHP_SAPI) {
1560
				case 'cli':
1561
					if (/* ^phpunit */
1562
					!empty($_SERVER['SMARTY_PHPUNIT_DISABLE_HEADERS']) /* phpunit$ */
1563
					) {
1564
						$_SERVER['SMARTY_PHPUNIT_HEADERS'][] =
1565
							'Last-Modified: ' . gmdate('D, d M Y H:i:s', $cached->timestamp) . ' GMT';
0 ignored issues
show
Bug introduced by
It seems like $cached->timestamp can also be of type boolean; however, parameter $timestamp of gmdate() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1565
							'Last-Modified: ' . gmdate('D, d M Y H:i:s', /** @scrutinizer ignore-type */ $cached->timestamp) . ' GMT';
Loading history...
1566
					}
1567
					break;
1568
				default:
1569
					header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $cached->timestamp) . ' GMT');
1570
					break;
1571
			}
1572
			echo $content;
1573
		}
1574
	}
1575
1576
	public function getModifierCallback(string $modifierName) {
1577
		foreach ($this->getExtensions() as $extension) {
1578
			if ($callback = $extension->getModifierCallback($modifierName)) {
1579
				return [new CallbackWrapper($modifierName, $callback), 'handle'];
1580
			}
1581
		}
1582
		return null;
1583
	}
1584
1585
	public function getFunctionHandler(string $functionName): ?\Smarty\FunctionHandler\FunctionHandlerInterface {
1586
		foreach ($this->getExtensions() as $extension) {
1587
			if ($handler = $extension->getFunctionHandler($functionName)) {
1588
				return $handler;
1589
			}
1590
		}
1591
		return null;
1592
	}
1593
1594
	public function getBlockHandler(string $blockTagName): ?\Smarty\BlockHandler\BlockHandlerInterface {
1595
		foreach ($this->getExtensions() as $extension) {
1596
			if ($handler = $extension->getBlockHandler($blockTagName)) {
1597
				return $handler;
1598
			}
1599
		}
1600
		return null;
1601
	}
1602
1603
	public function getModifierCompiler(string $modifier): ?\Smarty\Compile\Modifier\ModifierCompilerInterface {
1604
		foreach ($this->getExtensions() as $extension) {
1605
			if ($handler = $extension->getModifierCompiler($modifier)) {
1606
				return $handler;
1607
			}
1608
		}
1609
		return null;
1610
	}
1611
1612
	/**
1613
	 * Run pre-filters over template source
1614
	 *
1615
	 * @param string $source the content which shall be processed by the filters
1616
	 * @param Template $template template object
1617
	 *
1618
	 * @return string                   the filtered source
1619
	 */
1620
	public function runPreFilters($source, Template $template) {
1621
1622
		foreach ($this->getExtensions() as $extension) {
1623
			/** @var \Smarty\Filter\FilterInterface $filter */
1624
			foreach ($extension->getPreFilters() as $filter) {
1625
				$source = $filter->filter($source, $template);
1626
			}
1627
		}
1628
1629
		// return filtered output
1630
		return $source;
1631
	}
1632
1633
	/**
1634
	 * Run post-filters over template's compiled code
1635
	 *
1636
	 * @param string $code the content which shall be processed by the filters
1637
	 * @param Template $template template object
1638
	 *
1639
	 * @return string                   the filtered code
1640
	 */
1641
	public function runPostFilters($code, Template $template) {
1642
1643
		foreach ($this->getExtensions() as $extension) {
1644
			/** @var \Smarty\Filter\FilterInterface $filter */
1645
			foreach ($extension->getPostFilters() as $filter) {
1646
				$code = $filter->filter($code, $template);
1647
			}
1648
		}
1649
1650
		// return filtered output
1651
		return $code;
1652
	}
1653
1654
	/**
1655
	 * Run filters over template output
1656
	 *
1657
	 * @param string $content the content which shall be processed by the filters
1658
	 * @param Template $template template object
1659
	 *
1660
	 * @return string                   the filtered (modified) output
1661
	 */
1662
	public function runOutputFilters($content, Template $template) {
1663
1664
		foreach ($this->getExtensions() as $extension) {
1665
			/** @var \Smarty\Filter\FilterInterface $filter */
1666
			foreach ($extension->getOutputFilters() as $filter) {
1667
				$content = $filter->filter($content, $template);
1668
			}
1669
		}
1670
1671
		// return filtered output
1672
		return $content;
1673
	}
1674
1675
	/**
1676
	 * Writes file in a safe way to disk
1677
	 *
1678
	 * @param string $_filepath complete filepath
1679
	 * @param string $_contents file content
1680
	 *
1681
	 * @return boolean true
1682
	 * @throws Exception
1683
	 */
1684
	public function writeFile($_filepath, $_contents) {
1685
		$_error_reporting = error_reporting();
1686
		error_reporting($_error_reporting & ~E_NOTICE & ~E_WARNING);
1687
		$_dirpath = dirname($_filepath);
1688
		// if subdirs, create dir structure
1689
		if ($_dirpath !== '.') {
1690
			$i = 0;
1691
			// loop if concurrency problem occurs
1692
			// see https://bugs.php.net/bug.php?id=35326
1693
			while (!is_dir($_dirpath)) {
1694
				if (@mkdir($_dirpath, 0777, true)) {
1695
					break;
1696
				}
1697
				clearstatcache();
1698
				if (++$i === 3) {
1699
					error_reporting($_error_reporting);
1700
					throw new Exception("unable to create directory {$_dirpath}");
1701
				}
1702
				sleep(1);
1703
			}
1704
		}
1705
		// write to tmp file, then move to overt file lock race condition
1706
		$_tmp_file = $_dirpath . DIRECTORY_SEPARATOR . str_replace(['.', ','], '_', uniqid('wrt', true));
1707
		if (!file_put_contents($_tmp_file, $_contents)) {
1708
			error_reporting($_error_reporting);
1709
			throw new Exception("unable to write file {$_tmp_file}");
1710
		}
1711
		/*
1712
		 * Windows' rename() fails if the destination exists,
1713
		 * Linux' rename() properly handles the overwrite.
1714
		 * Simply unlink()ing a file might cause other processes
1715
		 * currently reading that file to fail, but linux' rename()
1716
		 * seems to be smart enough to handle that for us.
1717
		 */
1718
		if (\Smarty\Smarty::$_IS_WINDOWS) {
1719
			// remove original file
1720
			if (is_file($_filepath)) {
1721
				@unlink($_filepath);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1721
				/** @scrutinizer ignore-unhandled */ @unlink($_filepath);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1722
			}
1723
			// rename tmp file
1724
			$success = @rename($_tmp_file, $_filepath);
1725
		} else {
1726
			// rename tmp file
1727
			$success = @rename($_tmp_file, $_filepath);
1728
			if (!$success) {
1729
				// remove original file
1730
				if (is_file($_filepath)) {
1731
					@unlink($_filepath);
1732
				}
1733
				// rename tmp file
1734
				$success = @rename($_tmp_file, $_filepath);
1735
			}
1736
		}
1737
		if (!$success) {
1738
			error_reporting($_error_reporting);
1739
			throw new Exception("unable to write file {$_filepath}");
1740
		}
1741
		// set file permissions
1742
		@chmod($_filepath, 0666 & ~umask());
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1742
		/** @scrutinizer ignore-unhandled */ @chmod($_filepath, 0666 & ~umask());

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1743
		error_reporting($_error_reporting);
1744
		return true;
1745
	}
1746
1747
	private $runtimes = [];
1748
1749
	/**
1750
	 * Loads and returns a runtime extension or null if not found
1751
	 *
1752
	 * @param string $type
1753
	 *
1754
	 * @return object|null
1755
	 */
1756
	public function getRuntime(string $type) {
1757
1758
		if (isset($this->runtimes[$type])) {
1759
			return $this->runtimes[$type];
1760
		}
1761
1762
		// Lazy load runtimes when/if needed
1763
		switch ($type) {
1764
			case 'Capture':
1765
				return $this->runtimes[$type] = new CaptureRuntime();
1766
			case 'Foreach':
1767
				return $this->runtimes[$type] = new ForeachRuntime();
1768
			case 'Inheritance':
1769
				return $this->runtimes[$type] = new InheritanceRuntime();
1770
			case 'TplFunction':
1771
				return $this->runtimes[$type] = new TplFunctionRuntime();
1772
			case 'DefaultPluginHandler':
1773
				return $this->runtimes[$type] = new DefaultPluginHandlerRuntime(
1774
					$this->getDefaultPluginHandlerFunc()
1775
				);
1776
		}
1777
1778
		throw new \Smarty\Exception('Trying to load invalid runtime ' . $type);
1779
	}
1780
1781
	/**
1782
	 * Indicates if a runtime is available.
1783
	 *
1784
	 * @param string $type
1785
	 *
1786
	 * @return bool
1787
	 */
1788
	public function hasRuntime(string $type): bool {
1789
		try {
1790
			$this->getRuntime($type);
1791
			return true;
1792
		} catch (\Smarty\Exception $e) {
1793
			return false;
1794
		}
1795
	}
1796
1797
	/**
1798
	 * @return callable|null
1799
	 */
1800
	public function getDefaultPluginHandlerFunc(): ?callable {
1801
		return $this->default_plugin_handler_func;
1802
	}
1803
1804
	/**
1805
	 * load a filter of specified type and name
1806
	 *
1807
	 * @param string $type filter type
1808
	 * @param string $name filter name
1809
	 *
1810
	 * @return bool
1811
	 * @throws \Smarty\Exception
1812
	 * @api  Smarty::loadFilter()
1813
	 *
1814
	 * @deprecated since 5.0
1815
	 */
1816
	public function loadFilter($type, $name) {
1817
1818
		if ($type == \Smarty\Smarty::FILTER_VARIABLE) {
1819
			foreach ($this->getExtensions() as $extension) {
1820
				if ($extension->getModifierCallback($name)) {
1821
1822
					trigger_error('Using Smarty::loadFilter() to load variable filters is deprecated and will ' .
1823
						'be removed in a future release. Use Smarty::addDefaultModifiers() to add a modifier.',
1824
						E_USER_DEPRECATED);
1825
1826
					$this->addDefaultModifiers([$name]);
1827
					return true;
1828
				}
1829
			}
1830
		}
1831
1832
		trigger_error('Using Smarty::loadFilter() to load filters is deprecated and will be ' .
1833
			'removed in a future release. Use Smarty::addExtension() to add an extension or Smarty::registerFilter to ' .
1834
			'quickly register a filter using a callback function.', E_USER_DEPRECATED);
1835
1836
		if ($type == \Smarty\Smarty::FILTER_OUTPUT && $name == 'trimwhitespace') {
1837
			$this->BCPluginsAdapter->addOutputFilter(new TrimWhitespace());
1838
			return true;
1839
		}
1840
1841
		$_plugin = "smarty_{$type}filter_{$name}";
1842
		if (!is_callable($_plugin) && class_exists($_plugin, false)) {
1843
			$_plugin = [$_plugin, 'execute'];
1844
		}
1845
1846
		if (is_callable($_plugin)) {
1847
			$this->registerFilter($type, $_plugin, $name);
1848
			return true;
1849
		}
1850
1851
		throw new Exception("{$type}filter '{$name}' not found or callable");
1852
	}
1853
1854
	/**
1855
	 * load a filter of specified type and name
1856
	 *
1857
	 * @param string $type filter type
1858
	 * @param string $name filter name
1859
	 *
1860
	 * @return static
1861
	 * @throws \Smarty\Exception
1862
	 * @api  Smarty::unloadFilter()
1863
	 *
1864
	 *
1865
	 * @deprecated since 5.0
1866
	 */
1867
	public function unloadFilter($type, $name) {
1868
		trigger_error('Using Smarty::unloadFilter() to unload filters is deprecated and will be ' .
1869
			'removed in a future release. Use Smarty::addExtension() to add an extension or Smarty::(un)registerFilter to ' .
1870
			'quickly (un)register a filter using a callback function.', E_USER_DEPRECATED);
1871
1872
		return $this->unregisterFilter($type, $name);
1873
	}
1874
1875
	private $_caching_type = 'file';
1876
1877
	/**
1878
	 * @param $type
1879
	 *
1880
	 * @return void
1881
	 * @deprecated since 5.0
1882
	 */
1883
	public function setCachingType($type) {
1884
		trigger_error('Using Smarty::setCachingType() is deprecated and will be ' .
1885
			'removed in a future release. Use Smarty::setCacheResource() instead.', E_USER_DEPRECATED);
1886
		$this->_caching_type = $type;
1887
		$this->activateBCCacheResource();
1888
	}
1889
1890
	/**
1891
	 * @return string
1892
	 * @deprecated since 5.0
1893
	 */
1894
	public function getCachingType(): string {
1895
		trigger_error('Using Smarty::getCachingType() is deprecated and will be ' .
1896
			'removed in a future release.', E_USER_DEPRECATED);
1897
		return $this->_caching_type;
1898
	}
1899
1900
	/**
1901
	 * Registers a resource to fetch a template
1902
	 *
1903
	 * @param string $name name of resource type
1904
	 * @param Base $resource_handler
1905
	 *
1906
	 * @return static
1907
	 *
1908
	 * @api  Smarty::registerCacheResource()
1909
	 *
1910
	 * @deprecated since 5.0
1911
	 */
1912
	public function registerCacheResource($name, \Smarty\Cacheresource\Base $resource_handler) {
1913
1914
		trigger_error('Using Smarty::registerCacheResource() is deprecated and will be ' .
1915
			'removed in a future release. Use Smarty::setCacheResource() instead.', E_USER_DEPRECATED);
1916
1917
		$this->registered_cache_resources[$name] = $resource_handler;
0 ignored issues
show
Deprecated Code introduced by
The property Smarty\Smarty::$registered_cache_resources has been deprecated: since 5.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1917
		/** @scrutinizer ignore-deprecated */ $this->registered_cache_resources[$name] = $resource_handler;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1918
		$this->activateBCCacheResource();
1919
		return $this;
1920
	}
1921
1922
	/**
1923
	 * Unregisters a resource to fetch a template
1924
	 *
1925
	 * @param                                                                 $name
1926
	 *
1927
	 * @return static
1928
	 * @api  Smarty::unregisterCacheResource()
1929
	 *
1930
	 * @deprecated since 5.0
1931
	 *
1932
	 */
1933
	public function unregisterCacheResource($name) {
1934
1935
		trigger_error('Using Smarty::unregisterCacheResource() is deprecated and will be ' .
1936
			'removed in a future release.', E_USER_DEPRECATED);
1937
1938
		if (isset($this->registered_cache_resources[$name])) {
0 ignored issues
show
Deprecated Code introduced by
The property Smarty\Smarty::$registered_cache_resources has been deprecated: since 5.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1938
		if (isset(/** @scrutinizer ignore-deprecated */ $this->registered_cache_resources[$name])) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1939
			unset($this->registered_cache_resources[$name]);
1940
		}
1941
		return $this;
1942
	}
1943
1944
	private function activateBCCacheResource() {
1945
		if ($this->_caching_type == 'file') {
1946
			$this->setCacheResource(new File());
1947
		}
1948
		if (isset($this->registered_cache_resources[$this->_caching_type])) {
0 ignored issues
show
Deprecated Code introduced by
The property Smarty\Smarty::$registered_cache_resources has been deprecated: since 5.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1948
		if (isset(/** @scrutinizer ignore-deprecated */ $this->registered_cache_resources[$this->_caching_type])) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1949
			$this->setCacheResource($this->registered_cache_resources[$this->_caching_type]);
1950
		}
1951
	}
1952
1953
	/**
1954
	 * Registers a filter function
1955
	 *
1956
	 * @param string $type filter type
1957
	 * @param callable $callback
1958
	 * @param string|null $name optional filter name
1959
	 *
1960
	 * @return static
1961
	 * @throws \Smarty\Exception
1962
	 *
1963
	 * @api  Smarty::registerFilter()
1964
	 */
1965
	public function registerFilter($type, $callback, $name = null) {
1966
		$name = $name ?? $this->_getFilterName($callback);
1967
		if (!is_callable($callback)) {
1968
			throw new Exception("{$type}filter '{$name}' not callable");
1969
		}
1970
		switch ($type) {
1971
			case 'variable':
1972
				$this->registerPlugin(self::PLUGIN_MODIFIER, $name, $callback);
1973
				trigger_error('Using Smarty::registerFilter() to register variable filters is deprecated and ' .
1974
					'will be removed in a future release. Use Smarty::addDefaultModifiers() to add a modifier.',
1975
					E_USER_DEPRECATED);
1976
1977
				$this->addDefaultModifiers([$name]);
1978
				break;
1979
			case 'output':
1980
				$this->BCPluginsAdapter->addCallableAsOutputFilter($callback, $name);
1981
				break;
1982
			case 'pre':
1983
				$this->BCPluginsAdapter->addCallableAsPreFilter($callback, $name);
1984
				break;
1985
			case 'post':
1986
				$this->BCPluginsAdapter->addCallableAsPostFilter($callback, $name);
1987
				break;
1988
			default:
1989
				throw new Exception("Illegal filter type '{$type}'");
1990
		}
1991
1992
		return $this;
1993
	}
1994
1995
	/**
1996
	 * Return internal filter name
1997
	 *
1998
	 * @param callback $callable
1999
	 *
2000
	 * @return string|null   internal filter name or null if callable cannot be serialized
2001
	 */
2002
	private function _getFilterName($callable) {
2003
		if (is_array($callable)) {
2004
			$_class_name = is_object($callable[0]) ? get_class($callable[0]) : $callable[0];
2005
			return $_class_name . '_' . $callable[1];
2006
		} elseif (is_string($callable)) {
2007
			return $callable;
2008
		}
2009
		return null;
2010
	}
2011
2012
	/**
2013
	 * Unregisters a filter function. Smarty cannot unregister closures/anonymous functions if
2014
	 * no name was given in ::registerFilter.
2015
	 *
2016
	 * @param string $type filter type
2017
	 * @param callback|string $name the name previously used in ::registerFilter
2018
	 *
2019
	 * @return static
2020
	 * @throws \Smarty\Exception
2021
	 * @api  Smarty::unregisterFilter()
2022
	 *
2023
	 *
2024
	 */
2025
	public function unregisterFilter($type, $name) {
2026
2027
		if (!is_string($name)) {
2028
			$name = $this->_getFilterName($name);
2029
		}
2030
2031
		if ($name) {
2032
			switch ($type) {
2033
				case 'output':
2034
					$this->BCPluginsAdapter->removeOutputFilter($name);
2035
					break;
2036
				case 'pre':
2037
					$this->BCPluginsAdapter->removePreFilter($name);
2038
					break;
2039
				case 'post':
2040
					$this->BCPluginsAdapter->removePostFilter($name);
2041
					break;
2042
				default:
2043
					throw new Exception("Illegal filter type '{$type}'");
2044
			}
2045
		}
2046
2047
		return $this;
2048
	}
2049
2050
	/**
2051
	 * Add default modifiers
2052
	 *
2053
	 * @param array|string $modifiers modifier or list of modifiers
2054
	 *                                                                                   to add
2055
	 *
2056
	 * @return static
2057
	 * @api Smarty::addDefaultModifiers()
2058
	 *
2059
	 */
2060
	public function addDefaultModifiers($modifiers) {
2061
		if (is_array($modifiers)) {
2062
			$this->default_modifiers = array_merge($this->default_modifiers, $modifiers);
2063
		} else {
2064
			$this->default_modifiers[] = $modifiers;
2065
		}
2066
		return $this;
2067
	}
2068
2069
	/**
2070
	 * Get default modifiers
2071
	 *
2072
	 * @return array list of default modifiers
2073
	 * @api Smarty::getDefaultModifiers()
2074
	 *
2075
	 */
2076
	public function getDefaultModifiers() {
2077
		return $this->default_modifiers;
2078
	}
2079
2080
	/**
2081
	 * Set default modifiers
2082
	 *
2083
	 * @param array|string $modifiers modifier or list of modifiers
2084
	 *                                                                                   to set
2085
	 *
2086
	 * @return static
2087
	 * @api Smarty::setDefaultModifiers()
2088
	 *
2089
	 */
2090
	public function setDefaultModifiers($modifiers) {
2091
		$this->default_modifiers = (array)$modifiers;
2092
		return $this;
2093
	}
2094
2095
	/**
2096
	 * @return Cacheresource\Base
2097
	 */
2098
	public function getCacheResource(): Cacheresource\Base {
2099
		return $this->cacheResource;
2100
	}
2101
2102
	/**
2103
	 * @param Cacheresource\Base $cacheResource
2104
	 */
2105
	public function setCacheResource(Cacheresource\Base $cacheResource): void {
2106
		$this->cacheResource = $cacheResource;
2107
	}
2108
2109
	/**
2110
	 * fetches a rendered Smarty template
2111
	 *
2112
	 * @param string $template the resource handle of the template file or template object
2113
	 * @param mixed $cache_id cache id to be used with this template
2114
	 * @param mixed $compile_id compile id to be used with this template
2115
	 *
2116
	 * @return string rendered template output
2117
	 * @throws Exception
2118
	 * @throws Exception
2119
	 */
2120
	public function fetch($template = null, $cache_id = null, $compile_id = null) {
2121
		return $this->returnOrCreateTemplate($template, $cache_id, $compile_id)->fetch();
2122
	}
2123
2124
	/**
2125
	 * displays a Smarty template
2126
	 *
2127
	 * @param string $template the resource handle of the template file or template object
2128
	 * @param mixed $cache_id cache id to be used with this template
2129
	 * @param mixed $compile_id compile id to be used with this template
2130
	 *
2131
	 * @throws \Exception
2132
	 * @throws \Smarty\Exception
2133
	 */
2134
	public function display($template = null, $cache_id = null, $compile_id = null) {
2135
		$this->returnOrCreateTemplate($template, $cache_id, $compile_id)->display();
2136
	}
2137
2138
	/**
2139
	 * @param $resource_name
2140
	 * @param $cache_id
2141
	 * @param $compile_id
2142
	 * @param $parent
2143
	 * @param $caching
2144
	 * @param $cache_lifetime
2145
	 * @param bool $isConfig
2146
	 * @param array $data
2147
	 *
2148
	 * @return Template
2149
	 * @throws Exception
2150
	 */
2151
	public function doCreateTemplate(
2152
		$resource_name,
2153
		$cache_id = null,
2154
		$compile_id = null,
2155
		$parent = null,
2156
		$caching = null,
2157
		$cache_lifetime = null,
2158
		bool $isConfig = false,
2159
		array $data = []): Template {
2160
2161
		if (!$this->_templateDirNormalized) {
2162
			$this->_normalizeTemplateConfig(false);
2163
		}
2164
2165
		$_templateId = $this->generateUniqueTemplateId($resource_name, $cache_id, $compile_id, $caching);
2166
2167
		if (!isset($this->templates[$_templateId])) {
2168
			$newTemplate = new Template($resource_name, $this, $parent ?: $this, $cache_id, $compile_id, $caching, $isConfig);
2169
			$newTemplate->templateId = $_templateId; // @TODO this could go in constructor ^?
2170
			$this->templates[$_templateId] = $newTemplate;
2171
		}
2172
2173
		$tpl = clone $this->templates[$_templateId];
2174
2175
		$tpl->setParent($parent ?: $this);
2176
2177
		if ($cache_lifetime) {
2178
			$tpl->setCacheLifetime($cache_lifetime);
2179
		}
2180
2181
		// fill data if present
2182
		foreach ($data as $_key => $_val) {
2183
			$tpl->assign($_key, $_val);
2184
		}
2185
2186
		$tpl->tplFunctions = array_merge($parent->tplFunctions ?? [], $tpl->tplFunctions ?? []);
2187
2188
		if (!$this->debugging && $this->debugging_ctrl === 'URL') {
2189
			$tpl->getSmarty()->getDebug()->debugUrl($tpl->getSmarty());
2190
		}
2191
		return $tpl;
2192
	}
2193
2194
	/**
2195
	 * test if cache is valid
2196
	 *
2197
	 * @param null|string|Template $template the resource handle of the template file or template
2198
	 *                                                          object
2199
	 * @param mixed $cache_id cache id to be used with this template
2200
	 * @param mixed $compile_id compile id to be used with this template
2201
	 *
2202
	 * @return bool cache status
2203
	 * @throws \Exception
2204
	 * @throws \Smarty\Exception
2205
	 *
2206
	 * @api  Smarty::isCached()
2207
	 */
2208
	public function isCached($template = null, $cache_id = null, $compile_id = null) {
2209
		return $this->returnOrCreateTemplate($template, $cache_id, $compile_id)->isCached();
2210
	}
2211
2212
	/**
2213
	 * @param $template
2214
	 * @param $cache_id
2215
	 * @param $compile_id
2216
	 * @param $parent
2217
	 *
2218
	 * @return Template
2219
	 * @throws Exception
2220
	 */
2221
	private function returnOrCreateTemplate($template, $cache_id = null, $compile_id = null) {
2222
		if (!($template instanceof Template)) {
2223
			$template = $this->createTemplate($template, $cache_id, $compile_id, $this);
2224
			$template->caching = $this->caching;
2225
		}
2226
		return $template;
2227
	}
2228
2229
	/**
2230
	 * Sets if Smarty should check If-Modified-Since headers to determine cache validity.
2231
	 * @param bool $cache_modified_check
2232
	 * @return void
2233
	 */
2234
	public function setCacheModifiedCheck($cache_modified_check): void {
2235
		$this->cache_modified_check = (bool) $cache_modified_check;
2236
	}
2237
2238
}
2239
2240