1
0
mirror of https://github.com/postfixadmin/postfixadmin.git synced 2025-08-07 17:42:53 +03:00

update smarty (4.1.1 -> 4.3.0) (add PHP 8.2 support, fixes for PHP 8.1; also fixes #541 where using a Turkish locale broke smarty's autoloader)

This commit is contained in:
David Goodwin
2022-12-04 19:35:33 +00:00
parent 58e9538e6a
commit b76260d3d3
45 changed files with 514 additions and 285 deletions

View File

@@ -5,6 +5,11 @@
* @package Smarty * @package Smarty
*/ */
if (!defined('SMARTY_HELPER_FUNCTIONS_LOADED')) {
include __DIR__ . '/functions.php';
}
/** /**
* Smarty Autoloader * Smarty Autoloader
* *
@@ -73,7 +78,7 @@ class Smarty_Autoloader
*/ */
public static function register($prepend = false) public static function register($prepend = false)
{ {
self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : dirname(__FILE__) . DIRECTORY_SEPARATOR; self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : __DIR__ . DIRECTORY_SEPARATOR;
self::$SMARTY_SYSPLUGINS_DIR = defined('SMARTY_SYSPLUGINS_DIR') ? SMARTY_SYSPLUGINS_DIR : self::$SMARTY_SYSPLUGINS_DIR = defined('SMARTY_SYSPLUGINS_DIR') ? SMARTY_SYSPLUGINS_DIR :
self::$SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR; self::$SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR;
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend); spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
@@ -89,7 +94,7 @@ class Smarty_Autoloader
if ($class[ 0 ] !== 'S' || strpos($class, 'Smarty') !== 0) { if ($class[ 0 ] !== 'S' || strpos($class, 'Smarty') !== 0) {
return; return;
} }
$_class = strtolower($class); $_class = smarty_strtolower_ascii($class);
if (isset(self::$rootClasses[ $_class ])) { if (isset(self::$rootClasses[ $_class ])) {
$file = self::$SMARTY_DIR . self::$rootClasses[ $_class ]; $file = self::$SMARTY_DIR . self::$rootClasses[ $_class ];
if (is_file($file)) { if (is_file($file)) {

View File

@@ -36,7 +36,7 @@ if (!defined('SMARTY_DIR')) {
/** /**
* *
*/ */
define('SMARTY_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR); define('SMARTY_DIR', __DIR__ . DIRECTORY_SEPARATOR);
} }
/** /**
* set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins. * set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins.
@@ -60,12 +60,21 @@ if (!defined('SMARTY_MBSTRING')) {
*/ */
define('SMARTY_MBSTRING', function_exists('mb_get_info')); define('SMARTY_MBSTRING', function_exists('mb_get_info'));
} }
/**
* Load helper functions
*/
if (!defined('SMARTY_HELPER_FUNCTIONS_LOADED')) {
include __DIR__ . '/functions.php';
}
/** /**
* Load Smarty_Autoloader * Load Smarty_Autoloader
*/ */
if (!class_exists('Smarty_Autoloader')) { if (!class_exists('Smarty_Autoloader')) {
include dirname(__FILE__) . '/bootstrap.php'; include __DIR__ . '/bootstrap.php';
} }
/** /**
* Load always needed external class files * Load always needed external class files
*/ */
@@ -98,7 +107,7 @@ class Smarty extends Smarty_Internal_TemplateBase
/** /**
* smarty version * smarty version
*/ */
const SMARTY_VERSION = '4.1.1'; const SMARTY_VERSION = '4.3.0';
/** /**
* define variable scopes * define variable scopes
*/ */

View File

@@ -11,6 +11,6 @@
* Load and register Smarty Autoloader * Load and register Smarty Autoloader
*/ */
if (!class_exists('Smarty_Autoloader')) { if (!class_exists('Smarty_Autoloader')) {
include dirname(__FILE__) . '/Autoloader.php'; include __DIR__ . '/Autoloader.php';
} }
Smarty_Autoloader::register(true); Smarty_Autoloader::register(true);

View File

@@ -1,9 +1,9 @@
{capture name='_smarty_debug' assign=debug_output} {capture name='_smarty_debug' assign=debug_output}
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <html lang="en">
<head> <head>
<title>Smarty Debug Console</title> <title>Smarty Debug Console</title>
<style type="text/css"> <style>
{literal} {literal}
body, h1, h2, h3, td, th, p { body, h1, h2, h3, td, th, p {
font-family: sans-serif; font-family: sans-serif;
@@ -31,6 +31,7 @@
padding: 2px; padding: 2px;
border-top: 1px solid black; border-top: 1px solid black;
} }
h3 { h3 {
text-align: left; text-align: left;
font-weight: bold; font-weight: bold;
@@ -67,11 +68,11 @@
color: green; color: green;
} }
.odd { tr:nth-child(odd) {
background-color: #eeeeee; background-color: #eeeeee;
} }
.even { tr:nth-child(even) {
background-color: #fafafa; background-color: #fafafa;
} }
@@ -84,13 +85,16 @@
color: black; color: black;
font-weight: bold; font-weight: bold;
} }
#blue h3 { #blue h3 {
color: blue; color: blue;
} }
#normal div { #normal div {
color: black; color: black;
font-weight: normal; font-weight: normal;
} }
#table_assigned_vars th { #table_assigned_vars th {
color: blue; color: blue;
font-weight: bold; font-weight: bold;
@@ -99,7 +103,6 @@
#table_config_vars th { #table_config_vars th {
color: maroon; color: maroon;
} }
{/literal} {/literal}
</style> </style>
</head> </head>
@@ -112,11 +115,11 @@
<h2>included templates &amp; config files (load time in seconds)</h2> <h2>included templates &amp; config files (load time in seconds)</h2>
<div> <div>
{foreach $template_data as $template} {foreach $template_data as $template}
<font color=brown>{$template.name}</font> <span style="color: brown;">{$template.name}</span>
<br />&nbsp;&nbsp;<span class="exectime"> <br>&nbsp;&nbsp;<span class="exectime">
(compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"}) (compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"})
</span> </span>
<br /> <br>
{/foreach} {/foreach}
</div> </div>
{/if} {/if}
@@ -125,13 +128,22 @@
<table id="table_assigned_vars"> <table id="table_assigned_vars">
{foreach $assigned_vars as $vars} {foreach $assigned_vars as $vars}
<tr class="{if $vars@iteration % 2 eq 0}odd{else}even{/if}"> <tr>
<td><h3><font color=blue>${$vars@key}</font></h3> <td>
{if isset($vars['nocache'])}<b>Nocache</b><br />{/if} <h3 style="color: blue;">${$vars@key}</h3>
{if isset($vars['scope'])}<b>Origin:</b> {$vars['scope']|debug_print_var nofilter}{/if} {if isset($vars['nocache'])}<strong>Nocache</strong><br>{/if}
{if isset($vars['scope'])}<strong>Origin:</strong> {$vars['scope']|debug_print_var nofilter}{/if}
</td>
<td>
<h3>Value</h3>
{$vars['value']|debug_print_var:10:80 nofilter}
</td>
<td>
{if isset($vars['attributes'])}
<h3>Attributes</h3>
{$vars['attributes']|debug_print_var nofilter}
{/if}
</td> </td>
<td><h3>Value</h3>{$vars['value']|debug_print_var:10:80 nofilter}</td>
<td>{if isset($vars['attributes'])}<h3>Attributes</h3>{$vars['attributes']|debug_print_var nofilter} {/if}</td>
{/foreach} {/foreach}
</table> </table>
@@ -139,11 +151,14 @@
<table id="table_config_vars"> <table id="table_config_vars">
{foreach $config_vars as $vars} {foreach $config_vars as $vars}
<tr class="{if $vars@iteration % 2 eq 0}odd{else}even{/if}"> <tr>
<td><h3><font color=blue>#{$vars@key}#</font></h3> <td>
{if isset($vars['scope'])}<b>Origin:</b> {$vars['scope']|debug_print_var nofilter}{/if} <h3 style="color: blue;">#{$vars@key}#</h3>
{if isset($vars['scope'])}<strong>Origin:</strong> {$vars['scope']|debug_print_var nofilter}{/if}
</td>
<td>
{$vars['value']|debug_print_var:10:80 nofilter}
</td> </td>
<td>{$vars['value']|debug_print_var:10:80 nofilter}</td>
</tr> </tr>
{/foreach} {/foreach}

View File

@@ -0,0 +1,51 @@
<?php
/**
* This file is part of the Smarty package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Registers some helper/polyfill functions.
*/
const SMARTY_HELPER_FUNCTIONS_LOADED = true;
/**
* Converts the first characters in $string to uppercase (A-Z) if it is an ASCII lowercase character (a-z).
*
* May not be required when running PHP8.2+: https://wiki.php.net/rfc/strtolower-ascii
*
* @param $string
*
* @return string
*/
function smarty_ucfirst_ascii($string): string {
return smarty_strtoupper_ascii(substr($string, 0, 1)) . substr($string, 1);
}
/**
* Converts all uppercase ASCII characters (A-Z) in $string to lowercase (a-z).
*
* May not be required when running PHP8.2+: https://wiki.php.net/rfc/strtolower-ascii
*
* @param $string
*
* @return string
*/
function smarty_strtolower_ascii($string): string {
return strtr($string, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz');
}
/**
* Converts all lowercase ASCII characters (a-z) in $string to uppercase (A-Z).
*
* May not be required when running PHP8.2+: https://wiki.php.net/rfc/strtolower-ascii
*
* @param $string
*
* @return string
*/
function smarty_strtoupper_ascii($string): string {
return strtr($string, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
}

View File

@@ -101,6 +101,7 @@ function smarty_function_html_select_date($params, Smarty_Internal_Template $tem
$field_separator = "\n"; $field_separator = "\n";
$option_separator = "\n"; $option_separator = "\n";
$time = null; $time = null;
// $all_empty = null; // $all_empty = null;
// $day_empty = null; // $day_empty = null;
// $month_empty = null; // $month_empty = null;
@@ -113,17 +114,7 @@ function smarty_function_html_select_date($params, Smarty_Internal_Template $tem
foreach ($params as $_key => $_value) { foreach ($params as $_key => $_value) {
switch ($_key) { switch ($_key) {
case 'time': case 'time':
if (!is_array($_value) && $_value !== null) { $$_key = $_value; // we'll handle conversion below
$template->_checkPlugins(
array(
array(
'function' => 'smarty_make_timestamp',
'file' => SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php'
)
)
);
$time = smarty_make_timestamp($_value);
}
break; break;
case 'month_names': case 'month_names':
if (is_array($_value) && count($_value) === 12) { if (is_array($_value) && count($_value) === 12) {
@@ -178,43 +169,59 @@ function smarty_function_html_select_date($params, Smarty_Internal_Template $tem
} }
// Note: date() is faster than strftime() // Note: date() is faster than strftime()
// Note: explode(date()) is faster than date() date() date() // Note: explode(date()) is faster than date() date() date()
if (isset($params[ 'time' ]) && is_array($params[ 'time' ])) {
if (isset($params[ 'time' ][ $prefix . 'Year' ])) { if (isset($time) && is_array($time)) {
if (isset($time[$prefix . 'Year'])) {
// $_REQUEST[$field_array] given // $_REQUEST[$field_array] given
foreach (array( foreach ([
'Y' => 'Year', 'Y' => 'Year',
'm' => 'Month', 'm' => 'Month',
'd' => 'Day' 'd' => 'Day'
) as $_elementKey => $_elementName) { ] as $_elementKey => $_elementName) {
$_variableName = '_' . strtolower($_elementName); $_variableName = '_' . strtolower($_elementName);
$$_variableName = $$_variableName =
isset($params[ 'time' ][ $prefix . $_elementName ]) ? $params[ 'time' ][ $prefix . $_elementName ] : isset($time[$prefix . $_elementName]) ? $time[$prefix . $_elementName] :
date($_elementKey); date($_elementKey);
} }
} elseif (isset($params[ 'time' ][ $field_array ][ $prefix . 'Year' ])) { } elseif (isset($time[$field_array][$prefix . 'Year'])) {
// $_REQUEST given // $_REQUEST given
foreach (array( foreach ([
'Y' => 'Year', 'Y' => 'Year',
'm' => 'Month', 'm' => 'Month',
'd' => 'Day' 'd' => 'Day'
) as $_elementKey => $_elementName) { ] as $_elementKey => $_elementName) {
$_variableName = '_' . strtolower($_elementName); $_variableName = '_' . strtolower($_elementName);
$$_variableName = isset($params[ 'time' ][ $field_array ][ $prefix . $_elementName ]) ? $$_variableName = isset($time[$field_array][$prefix . $_elementName]) ?
$params[ 'time' ][ $field_array ][ $prefix . $_elementName ] : date($_elementKey); $time[$field_array][$prefix . $_elementName] : date($_elementKey);
} }
} else { } else {
// no date found, use NOW // no date found, use NOW
list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d')); [$_year, $_month, $_day] = explode('-', date('Y-m-d'));
} }
} elseif (isset($time) && preg_match("/(\d*)-(\d*)-(\d*)/", $time, $matches)) {
$_year = $_month = $_day = null;
if ($matches[1] > '') $_year = (int) $matches[1];
if ($matches[2] > '') $_month = (int) $matches[2];
if ($matches[3] > '') $_day = (int) $matches[3];
} elseif ($time === null) { } elseif ($time === null) {
if (array_key_exists('time', $params)) { if (array_key_exists('time', $params)) {
$_year = $_month = $_day = $time = null; $_year = $_month = $_day = null;
} else { } else {
list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d')); [$_year, $_month, $_day] = explode('-', date('Y-m-d'));
} }
} else { } else {
list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d', $time)); $template->_checkPlugins(
array(
array(
'function' => 'smarty_make_timestamp',
'file' => SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php'
)
)
);
$time = smarty_make_timestamp($time);
[$_year, $_month, $_day] = explode('-', date('Y-m-d', $time));
} }
// make syntax "+N" or "-N" work with $start_year and $end_year // make syntax "+N" or "-N" work with $start_year and $end_year
// Note preg_match('!^(\+|\-)\s*(\d+)$!', $end_year, $match) is slower than trim+substr // Note preg_match('!^(\+|\-)\s*(\d+)$!', $end_year, $match) is slower than trim+substr
foreach (array( foreach (array(
@@ -309,8 +316,8 @@ function smarty_function_html_select_date($params, Smarty_Internal_Template $tem
for ($i = 1; $i <= 12; $i++) { for ($i = 1; $i <= 12; $i++) {
$_val = sprintf('%02d', $i); $_val = sprintf('%02d', $i);
$_text = isset($month_names) ? smarty_function_escape_special_chars($month_names[ $i ]) : $_text = isset($month_names) ? smarty_function_escape_special_chars($month_names[ $i ]) :
($month_format === '%m' ? $_val : strftime($month_format, $_month_timestamps[ $i ])); ($month_format === '%m' ? $_val : @strftime($month_format, $_month_timestamps[ $i ]));
$_value = $month_value_format === '%m' ? $_val : strftime($month_value_format, $_month_timestamps[ $i ]); $_value = $month_value_format === '%m' ? $_val : @strftime($month_value_format, $_month_timestamps[ $i ]);
$_html_months .= '<option value="' . $_value . '"' . ($_val == $_month ? ' selected="selected"' : '') . $_html_months .= '<option value="' . $_value . '"' . ($_val == $_month ? ' selected="selected"' : '') .
'>' . $_text . '</option>' . $option_separator; '>' . $_text . '</option>' . $option_separator;
} }

View File

@@ -48,8 +48,13 @@
*/ */
function smarty_function_mailto($params) function smarty_function_mailto($params)
{ {
static $_allowed_encoding = static $_allowed_encoding = [
array('javascript' => true, 'javascript_charcode' => true, 'hex' => true, 'none' => true); 'javascript' => true,
'javascript_charcode' => true,
'hex' => true,
'none' => true
];
$extra = ''; $extra = '';
if (empty($params[ 'address' ])) { if (empty($params[ 'address' ])) {
trigger_error("mailto: missing 'address' parameter", E_USER_WARNING); trigger_error("mailto: missing 'address' parameter", E_USER_WARNING);
@@ -57,19 +62,19 @@ function smarty_function_mailto($params)
} else { } else {
$address = $params[ 'address' ]; $address = $params[ 'address' ];
} }
$text = $address; $text = $address;
// netscape and mozilla do not decode %40 (@) in BCC field (bug?) // netscape and mozilla do not decode %40 (@) in BCC field (bug?)
// so, don't encode it. // so, don't encode it.
$search = array('%40', '%2C'); $mail_parms = [];
$replace = array('@', ',');
$mail_parms = array();
foreach ($params as $var => $value) { foreach ($params as $var => $value) {
switch ($var) { switch ($var) {
case 'cc': case 'cc':
case 'bcc': case 'bcc':
case 'followupto': case 'followupto':
if (!empty($value)) { if (!empty($value)) {
$mail_parms[] = $var . '=' . str_replace($search, $replace, rawurlencode($value)); $mail_parms[] = $var . '=' . str_replace(['%40', '%2C'], ['@', ','], rawurlencode($value));
} }
break; break;
case 'subject': case 'subject':
@@ -83,6 +88,7 @@ function smarty_function_mailto($params)
default: default:
} }
} }
if ($mail_parms) { if ($mail_parms) {
$address .= '?' . join('&', $mail_parms); $address .= '?' . join('&', $mail_parms);
} }
@@ -94,19 +100,21 @@ function smarty_function_mailto($params)
); );
return; return;
} }
$string = '<a href="mailto:' . htmlspecialchars($address, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, Smarty::$_CHARSET) .
'" ' . $extra . '>' . htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, Smarty::$_CHARSET) . '</a>';
if ($encode === 'javascript') { if ($encode === 'javascript') {
$string = '<a href="mailto:' . $address . '" ' . $extra . '>' . $text . '</a>';
$js_encode = ''; $js_encode = '';
for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { for ($x = 0, $_length = strlen($string); $x < $_length; $x++) {
$js_encode .= '%' . bin2hex($string[ $x ]); $js_encode .= '%' . bin2hex($string[ $x ]);
} }
return '<script type="text/javascript">document.write(unescape(\'' . $js_encode . '\'))</script>'; return '<script type="text/javascript">document.write(unescape(\'' . $js_encode . '\'))</script>';
} elseif ($encode === 'javascript_charcode') { } elseif ($encode === 'javascript_charcode') {
$string = '<a href="mailto:' . $address . '" ' . $extra . '>' . $text . '</a>';
for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { for ($x = 0, $_length = strlen($string); $x < $_length; $x++) {
$ord[] = ord($string[ $x ]); $ord[] = ord($string[ $x ]);
} }
return '<script type="text/javascript">document.write(String.fromCharCode(' . implode(',', $ord) . '))</script>'; return '<script type="text/javascript">document.write(String.fromCharCode(' . implode(',', $ord) . '))</script>';
} elseif ($encode === 'hex') { } elseif ($encode === 'hex') {
preg_match('!^(.*)(\?.*)$!', $address, $match); preg_match('!^(.*)(\?.*)$!', $address, $match);
if (!empty($match[ 2 ])) { if (!empty($match[ 2 ])) {
@@ -129,6 +137,6 @@ function smarty_function_mailto($params)
return '<a href="' . $mailto . $address_encode . '" ' . $extra . '>' . $text_encode . '</a>'; return '<a href="' . $mailto . $address_encode . '" ' . $extra . '>' . $text_encode . '</a>';
} else { } else {
// no encoding // no encoding
return '<a href="mailto:' . $address . '" ' . $extra . '>' . $text . '</a>'; return $string;
} }
} }

View File

@@ -70,7 +70,7 @@ function smarty_function_math($params, $template)
$number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number $number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number
$functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))'; $functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))';
$operators = '[,+\/*\^%-]'; // Allowed math operators $operators = '[,+\/*\^%-]'; // Allowed math operators
$regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)+\)|\((?1)+\)))(?:'.$operators.'(?1))?)+$/'; $regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)*\)|\((?1)*\)))(?:'.$operators.'(?1))?)+$/';
if (!preg_match($regexp, $equation)) { if (!preg_match($regexp, $equation)) {
trigger_error("math: illegal characters", E_USER_WARNING); trigger_error("math: illegal characters", E_USER_WARNING);

View File

@@ -22,6 +22,8 @@
*/ */
function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false) function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = false)
{ {
$string = (string) $string;
if (Smarty::$_MBSTRING) { if (Smarty::$_MBSTRING) {
if ($lc_rest) { if ($lc_rest) {
// uppercase (including hyphenated words) // uppercase (including hyphenated words)

View File

@@ -0,0 +1,36 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty count modifier plugin
* Type: modifier
* Name: count
* Purpose: counts all elements in an array or in a Countable object
* Input:
* - Countable|array: array or object to count
* - mode: int defaults to 0 for normal count mode, if set to 1 counts recursive
*
* @param mixed $arrayOrObject input array/object
* @param int $mode count mode
*
* @return int
*/
function smarty_modifier_count($arrayOrObject, $mode = 0)
{
/*
* @see https://www.php.net/count
* > Prior to PHP 8.0.0, if the parameter was neither an array nor an object that implements the Countable interface,
* > 1 would be returned, unless value was null, in which case 0 would be returned.
*/
if ($arrayOrObject instanceof Countable || is_array($arrayOrObject)) {
return count($arrayOrObject, (int) $mode);
} elseif ($arrayOrObject === null) {
return 0;
}
return 1;
}

View File

@@ -78,7 +78,8 @@ function smarty_modifier_date_format($string, $format = null, $default_date = ''
} }
$format = str_replace($_win_from, $_win_to, $format); $format = str_replace($_win_from, $_win_to, $format);
} }
return strftime($format, $timestamp); // @ to suppress deprecation errors when running in PHP8.1 or higher.
return @strftime($format, $timestamp);
} else { } else {
return date($format, $timestamp); return date($format, $timestamp);
} }

View File

@@ -23,7 +23,6 @@
*/ */
function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true) function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true)
{ {
static $_double_encode = true;
static $is_loaded_1 = false; static $is_loaded_1 = false;
static $is_loaded_2 = false; static $is_loaded_2 = false;
if (!$char_set) { if (!$char_set) {
@@ -34,87 +33,15 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $
switch ($esc_type) { switch ($esc_type) {
case 'html': case 'html':
if ($_double_encode) { return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
// php >=5.3.2 - go native
return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
} else {
if ($double_encode) {
// php <5.2.3 - only handle double encoding
return htmlspecialchars($string, ENT_QUOTES, $char_set);
} else {
// php <5.2.3 - prevent double encoding
$string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
$string = htmlspecialchars($string, ENT_QUOTES, $char_set);
$string = str_replace(
array(
'%%%SMARTY_START%%%',
'%%%SMARTY_END%%%'
),
array(
'&',
';'
),
$string
);
return $string;
}
}
// no break // no break
case 'htmlall': case 'htmlall':
if (Smarty::$_MBSTRING) { if (Smarty::$_MBSTRING) {
// mb_convert_encoding ignores htmlspecialchars() $string = mb_convert_encoding($string, 'UTF-8', $char_set);
if ($_double_encode) { return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode);
// php >=5.3.2 - go native
$string = htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
} else {
if ($double_encode) {
// php <5.2.3 - only handle double encoding
$string = htmlspecialchars($string, ENT_QUOTES, $char_set);
} else {
// php <5.2.3 - prevent double encoding
$string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
$string = htmlspecialchars($string, ENT_QUOTES, $char_set);
$string =
str_replace(
array(
'%%%SMARTY_START%%%',
'%%%SMARTY_END%%%'
),
array(
'&',
';'
),
$string
);
return $string;
}
}
// htmlentities() won't convert everything, so use mb_convert_encoding
return mb_convert_encoding($string, 'HTML-ENTITIES', $char_set);
} }
// no MBString fallback // no MBString fallback
if ($_double_encode) { return htmlentities($string, ENT_QUOTES, $char_set, $double_encode);
return htmlentities($string, ENT_QUOTES, $char_set, $double_encode);
} else {
if ($double_encode) {
return htmlentities($string, ENT_QUOTES, $char_set);
} else {
$string = preg_replace('!&(#?\w+);!', '%%%SMARTY_START%%%\\1%%%SMARTY_END%%%', $string);
$string = htmlentities($string, ENT_QUOTES, $char_set);
$string = str_replace(
array(
'%%%SMARTY_START%%%',
'%%%SMARTY_END%%%'
),
array(
'&',
';'
),
$string
);
return $string;
}
}
// no break // no break
case 'url': case 'url':
return rawurlencode($string); return rawurlencode($string);

View File

@@ -0,0 +1,25 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty explode modifier plugin
* Type: modifier
* Name: explode
* Purpose: split a string by a string
*
* @param string $separator
* @param string $string
* @param int|null $limit
*
* @return array
*/
function smarty_modifier_explode($separator, $string, ?int $limit = null)
{
// provide $string default to prevent deprecation errors in PHP >=8.1
return explode($separator, $string ?? '', $limit ?? PHP_INT_MAX);
}

View File

@@ -0,0 +1,26 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifier
*/
/**
* Smarty number_format modifier plugin
* Type: modifier
* Name: number_format
* Purpose: Format a number with grouped thousands
*
* @param float|null $num
* @param int $decimals
* @param string|null $decimal_separator
* @param string|null $thousands_separator
*
* @return string
*/
function smarty_modifier_number_format(?float $num, int $decimals = 0, ?string $decimal_separator = ".", ?string $thousands_separator = ",")
{
// provide $num default to prevent deprecation errors in PHP >=8.1
return number_format($num ?? 0.0, $decimals, $decimal_separator, $thousands_separator);
}

View File

@@ -18,12 +18,10 @@
* @param Smarty_Internal_TemplateCompilerBase $compiler * @param Smarty_Internal_TemplateCompilerBase $compiler
* *
* @return string with compiled code * @return string with compiled code
* @throws \SmartyException * @throws SmartyException
*/ */
function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompilerBase $compiler) function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompilerBase $compiler)
{ {
static $_double_encode = true;
static $is_loaded = false;
$compiler->template->_checkPlugins( $compiler->template->_checkPlugins(
array( array(
array( array(
@@ -41,41 +39,18 @@ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompile
} }
switch ($esc_type) { switch ($esc_type) {
case 'html': case 'html':
if ($_double_encode) { return 'htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
return 'htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . var_export($double_encode, true) . ')';
var_export($double_encode, true) . ')';
} elseif ($double_encode) {
return 'htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ')';
} else {
// fall back to modifier.escape.php
}
// no break // no break
case 'htmlall': case 'htmlall':
if (Smarty::$_MBSTRING) { if (Smarty::$_MBSTRING) {
if ($_double_encode) { return 'htmlentities(mb_convert_encoding((string)' . $params[ 0 ] . ', \'UTF-8\', ' .
// php >=5.2.3 - go native var_export($char_set, true) . '), ENT_QUOTES, \'UTF-8\', ' .
return 'mb_convert_encoding(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($double_encode, true) . ')';
var_export($char_set, true) . ', ' . var_export($double_encode, true) .
'), "HTML-ENTITIES", ' . var_export($char_set, true) . ')';
} elseif ($double_encode) {
// php <5.2.3 - only handle double encoding
return 'mb_convert_encoding(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' .
var_export($char_set, true) . '), "HTML-ENTITIES", ' . var_export($char_set, true) . ')';
} else {
// fall back to modifier.escape.php
}
} }
// no MBString fallback // no MBString fallback
if ($_double_encode) { return 'htmlentities((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
// php >=5.2.3 - go native var_export($double_encode, true) . ')';
return 'htmlentities((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
var_export($double_encode, true) . ')';
} elseif ($double_encode) {
// php <5.2.3 - only handle double encoding
return 'htmlentities((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ')';
} else {
// fall back to modifier.escape.php
}
// no break // no break
case 'url': case 'url':
return 'rawurlencode((string)' . $params[ 0 ] . ')'; return 'rawurlencode((string)' . $params[ 0 ] . ')';

View File

@@ -0,0 +1,23 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifierCompiler
*/
/**
* Smarty nl2br modifier plugin
* Type: modifier
* Name: nl2br
* Purpose: insert HTML line breaks before all newlines in a string
*
* @link https://www.smarty.net/docs/en/language.modifier.nl2br.tpl nl2br (Smarty online manual)
*
* @param array $params parameters
*
* @return string with compiled code
*/
function smarty_modifiercompiler_nl2br($params) {
return 'nl2br((string) ' . $params[0] . ', (bool) ' . ($params[1] ?? true) . ')';
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifierCompiler
*/
/**
* Smarty round modifier plugin
* Type: modifier
* Name: round
* Purpose: Returns the rounded value of num to specified precision (number of digits after the decimal point)
*
* @link https://www.smarty.net/docs/en/language.modifier.round.tpl round (Smarty online manual)
*
* @param array $params parameters
*
* @return string with compiled code
*/
function smarty_modifiercompiler_round($params) {
return 'round((float) ' . $params[0] . ', (int) ' . ($params[1] ?? 0) . ', (int) ' . ($params[2] ?? PHP_ROUND_HALF_UP) . ')';
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifierCompiler
*/
/**
* Smarty str_repeat modifier plugin
* Type: modifier
* Name: str_repeat
* Purpose: returns string repeated times times
*
* @link https://www.smarty.net/docs/en/language.modifier.str_repeat.tpl str_repeat (Smarty online manual)
*
* @param array $params parameters
*
* @return string with compiled code
*/
function smarty_modifiercompiler_str_repeat($params) {
return 'str_repeat((string) ' . $params[0] . ', (int) ' . $params[1] . ')';
}

View File

@@ -21,8 +21,8 @@
function smarty_modifiercompiler_strip_tags($params) function smarty_modifiercompiler_strip_tags($params)
{ {
if (!isset($params[ 1 ]) || $params[ 1 ] === true || trim($params[ 1 ], '"') === 'true') { if (!isset($params[ 1 ]) || $params[ 1 ] === true || trim($params[ 1 ], '"') === 'true') {
return "preg_replace('!<[^>]*?>!', ' ', {$params[0]})"; return "preg_replace('!<[^>]*?>!', ' ', {$params[0]} ?: '')";
} else { } else {
return 'strip_tags(' . $params[ 0 ] . ')'; return 'strip_tags((string) ' . $params[ 0 ] . ')';
} }
} }

View File

@@ -0,0 +1,23 @@
<?php
/**
* Smarty plugin
*
* @package Smarty
* @subpackage PluginsModifierCompiler
*/
/**
* Smarty strlen modifier plugin
* Type: modifier
* Name: strlen
* Purpose: return the length of the given string
*
* @link https://www.smarty.net/docs/en/language.modifier.strlen.tpl strlen (Smarty online manual)
*
* @param array $params parameters
*
* @return string with compiled code
*/
function smarty_modifiercompiler_strlen($params) {
return 'strlen((string) ' . $params[0] . ')';
}

View File

@@ -14,26 +14,34 @@
* @author Rodney Rehm * @author Rodney Rehm
* *
* @param array $params parameters * @param array $params parameters
* @param Smarty_Internal_TemplateCompilerBase $compiler
* *
* @return string with compiled code * @return string with compiled code
*/ */
function smarty_modifiercompiler_unescape($params) function smarty_modifiercompiler_unescape($params, Smarty_Internal_TemplateCompilerBase $compiler)
{ {
if (!isset($params[ 1 ])) { $compiler->template->_checkPlugins(
$params[ 1 ] = 'html'; array(
} array(
'function' => 'smarty_literal_compiler_param',
'file' => SMARTY_PLUGINS_DIR . 'shared.literal_compiler_param.php'
)
)
);
$esc_type = smarty_literal_compiler_param($params, 1, 'html');
if (!isset($params[ 2 ])) { if (!isset($params[ 2 ])) {
$params[ 2 ] = '\'' . addslashes(Smarty::$_CHARSET) . '\''; $params[ 2 ] = '\'' . addslashes(Smarty::$_CHARSET) . '\'';
} else {
$params[ 2 ] = "'{$params[ 2 ]}'";
} }
switch (trim($params[ 1 ], '"\'')) {
switch ($esc_type) {
case 'entity': case 'entity':
case 'htmlall': case 'htmlall':
if (Smarty::$_MBSTRING) { if (Smarty::$_MBSTRING) {
return 'mb_convert_encoding(' . $params[ 0 ] . ', ' . $params[ 2 ] . ', \'HTML-ENTITIES\')'; return 'html_entity_decode(mb_convert_encoding(' . $params[ 0 ] . ', ' . $params[ 2 ] . ', \'UTF-8\'), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ' . $params[ 2 ] . ')';
} }
return 'html_entity_decode(' . $params[ 0 ] . ', ENT_NOQUOTES, ' . $params[ 2 ] . ')'; return 'html_entity_decode(' . $params[ 0 ] . ', ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ' . $params[ 2 ] . ')';
case 'html': case 'html':
return 'htmlspecialchars_decode(' . $params[ 0 ] . ', ENT_QUOTES)'; return 'htmlspecialchars_decode(' . $params[ 0 ] . ', ENT_QUOTES)';
case 'url': case 'url':

View File

@@ -21,8 +21,8 @@
function smarty_modifiercompiler_upper($params) function smarty_modifiercompiler_upper($params)
{ {
if (Smarty::$_MBSTRING) { if (Smarty::$_MBSTRING) {
return 'mb_strtoupper(' . $params[ 0 ] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')'; return 'mb_strtoupper(' . $params[ 0 ] . ' ?? \'\', \'' . addslashes(Smarty::$_CHARSET) . '\')';
} }
// no MBString fallback // no MBString fallback
return 'strtoupper(' . $params[ 0 ] . ')'; return 'strtoupper(' . $params[ 0 ] . ' ?? \'\')';
} }

View File

@@ -44,9 +44,43 @@ if (!function_exists('smarty_mb_str_replace')) {
} }
} }
} else { } else {
$parts = mb_split(preg_quote($search), $subject) ?: array(); $mb_reg_charset = mb_regex_encoding();
// Check if mbstring regex is using UTF-8
$reg_is_unicode = !strcasecmp($mb_reg_charset, "UTF-8");
if(!$reg_is_unicode) {
// ...and set to UTF-8 if not
mb_regex_encoding("UTF-8");
}
// See if charset used by Smarty is matching one used by regex...
$current_charset = mb_regex_encoding();
$convert_result = (bool)strcasecmp(Smarty::$_CHARSET, $current_charset);
if($convert_result) {
// ...convert to it if not.
$subject = mb_convert_encoding($subject, $current_charset, Smarty::$_CHARSET);
$search = mb_convert_encoding($search, $current_charset, Smarty::$_CHARSET);
$replace = mb_convert_encoding($replace, $current_charset, Smarty::$_CHARSET);
}
$parts = mb_split(preg_quote($search), $subject ?? "") ?: array();
// If original regex encoding was not unicode...
if(!$reg_is_unicode) {
// ...restore original regex encoding to avoid breaking the system.
mb_regex_encoding($mb_reg_charset);
}
if($parts === false) {
// This exception is thrown if call to mb_split failed.
// Usually it happens, when $search or $replace are not valid for given mb_regex_encoding().
// There may be other cases for it to fail, please file an issue if you find a reproducible one.
throw new SmartyException("Source string is not a valid $current_charset sequence (probably)");
}
$count = count($parts) - 1; $count = count($parts) - 1;
$subject = implode($replace, $parts); $subject = implode($replace, $parts);
// Convert results back to charset used by Smarty, if needed.
if($convert_result) {
$subject = mb_convert_encoding($subject, Smarty::$_CHARSET, $current_charset);
}
} }
return $subject; return $subject;
} }

View File

@@ -205,11 +205,11 @@ abstract class Smarty_CacheResource
} }
// try sysplugins dir // try sysplugins dir
if (isset(self::$sysplugins[ $type ])) { if (isset(self::$sysplugins[ $type ])) {
$cache_resource_class = 'Smarty_Internal_CacheResource_' . ucfirst($type); $cache_resource_class = 'Smarty_Internal_CacheResource_' . smarty_ucfirst_ascii($type);
return $smarty->_cache[ 'cacheresource_handlers' ][ $type ] = new $cache_resource_class(); return $smarty->_cache[ 'cacheresource_handlers' ][ $type ] = new $cache_resource_class();
} }
// try plugins dir // try plugins dir
$cache_resource_class = 'Smarty_CacheResource_' . ucfirst($type); $cache_resource_class = 'Smarty_CacheResource_' . smarty_ucfirst_ascii($type);
if ($smarty->loadPlugin($cache_resource_class)) { if ($smarty->loadPlugin($cache_resource_class)) {
return $smarty->_cache[ 'cacheresource_handlers' ][ $type ] = new $cache_resource_class(); return $smarty->_cache[ 'cacheresource_handlers' ][ $type ] = new $cache_resource_class();
} }

View File

@@ -244,7 +244,7 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource
*/ */
protected function sanitize($string) protected function sanitize($string)
{ {
$string = trim($string, '|'); $string = trim((string)$string, '|');
if (!$string) { if (!$string) {
return ''; return '';
} }
@@ -428,7 +428,7 @@ abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource
$t[] = 'IVK#COMPILE' . $_compile; $t[] = 'IVK#COMPILE' . $_compile;
} }
$_name .= '#'; $_name .= '#';
$cid = trim($cache_id, '|'); $cid = trim((string)$cache_id, '|');
if (!$cid) { if (!$cid) {
return $t; return $t;
} }

View File

@@ -143,7 +143,7 @@ class Smarty_Internal_Compile_Private_ForeachSection extends Smarty_Internal_Com
foreach ($this->resultOffsets as $key => $offset) { foreach ($this->resultOffsets as $key => $offset) {
foreach ($match[ $offset ] as $m) { foreach ($match[ $offset ] as $m) {
if (!empty($m)) { if (!empty($m)) {
$this->matchResults[ $key ][ strtolower($m) ] = true; $this->matchResults[ $key ][ smarty_strtolower_ascii($m) ] = true;
} }
} }
} }
@@ -213,12 +213,12 @@ class Smarty_Internal_Compile_Private_ForeachSection extends Smarty_Internal_Com
*/ */
public function compileSpecialVariable($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter) public function compileSpecialVariable($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
{ {
$tag = strtolower(trim($parameter[ 0 ], '"\'')); $tag = smarty_strtolower_ascii(trim($parameter[ 0 ], '"\''));
$name = isset($parameter[ 1 ]) ? $compiler->getId($parameter[ 1 ]) : false; $name = isset($parameter[ 1 ]) ? $compiler->getId($parameter[ 1 ]) : false;
if (!$name) { if (!$name) {
$compiler->trigger_template_error("missing or illegal \$smarty.{$tag} name attribute", null, true); $compiler->trigger_template_error("missing or illegal \$smarty.{$tag} name attribute", null, true);
} }
$property = isset($parameter[ 2 ]) ? strtolower($compiler->getId($parameter[ 2 ])) : false; $property = isset($parameter[ 2 ]) ? smarty_strtolower_ascii($compiler->getId($parameter[ 2 ])) : false;
if (!$property || !in_array($property, $this->nameProperties)) { if (!$property || !in_array($property, $this->nameProperties)) {
$compiler->trigger_template_error("missing or illegal \$smarty.{$tag} property attribute", null, true); $compiler->trigger_template_error("missing or illegal \$smarty.{$tag} property attribute", null, true);
} }

View File

@@ -109,6 +109,9 @@ class Smarty_Internal_Compile_Private_Modifier extends Smarty_Internal_CompileBa
if (!is_object($compiler->smarty->security_policy) if (!is_object($compiler->smarty->security_policy)
|| $compiler->smarty->security_policy->isTrustedPhpModifier($modifier, $compiler) || $compiler->smarty->security_policy->isTrustedPhpModifier($modifier, $compiler)
) { ) {
trigger_error('Using php-function "' . $modifier . '" as a modifier is deprecated and will be ' .
'removed in a future release. Use Smarty::registerPlugin to explicitly register ' .
'a custom modifier.', E_USER_DEPRECATED);
$output = "{$modifier}({$params})"; $output = "{$modifier}({$params})";
} }
$compiler->known_modifier_type[ $modifier ] = $type; $compiler->known_modifier_type[ $modifier ] = $type;

View File

@@ -93,7 +93,7 @@ class Smarty_Internal_Compile_Private_Print_Expression extends Smarty_Internal_C
} }
// autoescape html // autoescape html
if ($compiler->template->smarty->escape_html) { if ($compiler->template->smarty->escape_html) {
$output = "htmlspecialchars({$output}, ENT_QUOTES, '" . addslashes(Smarty::$_CHARSET) . "')"; $output = "htmlspecialchars((string) {$output}, ENT_QUOTES, '" . addslashes(Smarty::$_CHARSET) . "')";
} }
// loop over registered filters // loop over registered filters
if (!empty($compiler->template->smarty->registered_filters[ Smarty::FILTER_VARIABLE ])) { if (!empty($compiler->template->smarty->registered_filters[ Smarty::FILTER_VARIABLE ])) {

View File

@@ -29,7 +29,7 @@ class Smarty_Internal_Compile_Private_Special_Variable extends Smarty_Internal_C
public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter) public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
{ {
$_index = preg_split("/\]\[/", substr($parameter, 1, strlen($parameter) - 2)); $_index = preg_split("/\]\[/", substr($parameter, 1, strlen($parameter) - 2));
$variable = strtolower($compiler->getId($_index[ 0 ])); $variable = smarty_strtolower_ascii($compiler->getId($_index[ 0 ]));
if ($variable === false) { if ($variable === false) {
$compiler->trigger_template_error("special \$Smarty variable name index can not be variable", null, true); $compiler->trigger_template_error("special \$Smarty variable name index can not be variable", null, true);
} }
@@ -40,7 +40,7 @@ class Smarty_Internal_Compile_Private_Special_Variable extends Smarty_Internal_C
case 'foreach': case 'foreach':
case 'section': case 'section':
if (!isset(Smarty_Internal_TemplateCompilerBase::$_tag_objects[ $variable ])) { if (!isset(Smarty_Internal_TemplateCompilerBase::$_tag_objects[ $variable ])) {
$class = 'Smarty_Internal_Compile_' . ucfirst($variable); $class = 'Smarty_Internal_Compile_' . smarty_ucfirst_ascii($variable);
Smarty_Internal_TemplateCompilerBase::$_tag_objects[ $variable ] = new $class; Smarty_Internal_TemplateCompilerBase::$_tag_objects[ $variable ] = new $class;
} }
return Smarty_Internal_TemplateCompilerBase::$_tag_objects[ $variable ]->compileSpecialVariable( return Smarty_Internal_TemplateCompilerBase::$_tag_objects[ $variable ]->compileSpecialVariable(
@@ -76,7 +76,7 @@ class Smarty_Internal_Compile_Private_Special_Variable extends Smarty_Internal_C
$compiler->trigger_template_error("(secure mode) super globals not permitted"); $compiler->trigger_template_error("(secure mode) super globals not permitted");
break; break;
} }
$compiled_ref = '$_' . strtoupper($variable); $compiled_ref = '$_' . smarty_strtoupper_ascii($variable);
break; break;
case 'template': case 'template':
return 'basename($_smarty_tpl->source->filepath)'; return 'basename($_smarty_tpl->source->filepath)';

View File

@@ -210,7 +210,7 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data
// copy the working dirs from application // copy the working dirs from application
$debObj->setCompileDir($smarty->getCompileDir()); $debObj->setCompileDir($smarty->getCompileDir());
// init properties by hand as user may have edited the original Smarty class // init properties by hand as user may have edited the original Smarty class
$debObj->setPluginsDir(is_dir(dirname(__FILE__) . '/../plugins') ? dirname(__FILE__) . $debObj->setPluginsDir(is_dir(__DIR__ . '/../plugins') ? __DIR__ .
'/../plugins' : $smarty->getPluginsDir()); '/../plugins' : $smarty->getPluginsDir());
$debObj->force_compile = false; $debObj->force_compile = false;
$debObj->compile_check = Smarty::COMPILECHECK_ON; $debObj->compile_check = Smarty::COMPILECHECK_ON;
@@ -221,7 +221,7 @@ class Smarty_Internal_Debug extends Smarty_Internal_Data
$debObj->debugging_ctrl = 'NONE'; $debObj->debugging_ctrl = 'NONE';
$debObj->error_reporting = E_ALL & ~E_NOTICE; $debObj->error_reporting = E_ALL & ~E_NOTICE;
$debObj->debug_tpl = $debObj->debug_tpl =
isset($smarty->debug_tpl) ? $smarty->debug_tpl : 'file:' . dirname(__FILE__) . '/../debug.tpl'; isset($smarty->debug_tpl) ? $smarty->debug_tpl : 'file:' . __DIR__ . '/../debug.tpl';
$debObj->registered_plugins = array(); $debObj->registered_plugins = array();
$debObj->registered_resources = array(); $debObj->registered_resources = array();
$debObj->registered_filters = array(); $debObj->registered_filters = array();

View File

@@ -36,6 +36,7 @@
* @property Smarty_Internal_Method_RegisterPlugin $registerPlugin * @property Smarty_Internal_Method_RegisterPlugin $registerPlugin
* @property mixed|\Smarty_Template_Cached configLoad * @property mixed|\Smarty_Template_Cached configLoad
*/ */
#[\AllowDynamicProperties]
class Smarty_Internal_Extension_Handler class Smarty_Internal_Extension_Handler
{ {
public $objType = null; public $objType = null;
@@ -88,20 +89,19 @@ class Smarty_Internal_Extension_Handler
$objType = $data->_objType; $objType = $data->_objType;
$propertyType = false; $propertyType = false;
if (!isset($this->resolvedProperties[ $match[ 0 ] ][ $objType ])) { if (!isset($this->resolvedProperties[ $match[ 0 ] ][ $objType ])) {
$property = isset($this->resolvedProperties[ 'property' ][ $basename ]) ? $property = $this->resolvedProperties['property'][$basename] ??
$this->resolvedProperties[ 'property' ][ $basename ] : $this->resolvedProperties['property'][$basename] = smarty_strtolower_ascii(
$property = $this->resolvedProperties[ 'property' ][ $basename ] = strtolower( join(
join( '_',
'_', preg_split(
preg_split( '/([A-Z][^A-Z]*)/',
'/([A-Z][^A-Z]*)/', $basename,
$basename, -1,
-1, PREG_SPLIT_NO_EMPTY |
PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
PREG_SPLIT_DELIM_CAPTURE
)
) )
); )
);
if ($property !== false) { if ($property !== false) {
if (property_exists($data, $property)) { if (property_exists($data, $property)) {
$propertyType = $this->resolvedProperties[ $match[ 0 ] ][ $objType ] = 1; $propertyType = $this->resolvedProperties[ $match[ 0 ] ][ $objType ] = 1;
@@ -145,7 +145,7 @@ class Smarty_Internal_Extension_Handler
public function upperCase($name) public function upperCase($name)
{ {
$_name = explode('_', $name); $_name = explode('_', $name);
$_name = array_map('ucfirst', $_name); $_name = array_map('smarty_ucfirst_ascii', $_name);
return implode('_', $_name); return implode('_', $_name);
} }

View File

@@ -40,7 +40,7 @@ class Smarty_Internal_Method_LoadPlugin
throw new SmartyException("plugin {$plugin_name} is not a valid name format"); throw new SmartyException("plugin {$plugin_name} is not a valid name format");
} }
if (!empty($match[ 2 ])) { if (!empty($match[ 2 ])) {
$file = SMARTY_SYSPLUGINS_DIR . strtolower($plugin_name) . '.php'; $file = SMARTY_SYSPLUGINS_DIR . smarty_strtolower_ascii($plugin_name) . '.php';
if (isset($this->plugin_files[ $file ])) { if (isset($this->plugin_files[ $file ])) {
if ($this->plugin_files[ $file ] !== false) { if ($this->plugin_files[ $file ] !== false) {
return $this->plugin_files[ $file ]; return $this->plugin_files[ $file ];
@@ -60,7 +60,7 @@ class Smarty_Internal_Method_LoadPlugin
} }
// plugin filename is expected to be: [type].[name].php // plugin filename is expected to be: [type].[name].php
$_plugin_filename = "{$match[1]}.{$match[4]}.php"; $_plugin_filename = "{$match[1]}.{$match[4]}.php";
$_lower_filename = strtolower($_plugin_filename); $_lower_filename = smarty_strtolower_ascii($_plugin_filename);
if (isset($this->plugin_files)) { if (isset($this->plugin_files)) {
if (isset($this->plugin_files[ 'plugins_dir' ][ $_lower_filename ])) { if (isset($this->plugin_files[ 'plugins_dir' ][ $_lower_filename ])) {
if (!$smarty->use_include_path || $this->plugin_files[ 'plugins_dir' ][ $_lower_filename ] !== false) { if (!$smarty->use_include_path || $this->plugin_files[ 'plugins_dir' ][ $_lower_filename ] !== false) {

View File

@@ -32,7 +32,7 @@ class Smarty_Internal_Method_MustCompile
{ {
if (!$_template->source->exists) { if (!$_template->source->exists) {
if ($_template->_isSubTpl()) { if ($_template->_isSubTpl()) {
$parent_resource = " in '$_template->parent->template_resource}'"; $parent_resource = " in '{$_template->parent->template_resource}'";
} else { } else {
$parent_resource = ''; $parent_resource = '';
} }

View File

@@ -22,7 +22,7 @@ class Smarty_Internal_Runtime_Make_Nocache
{ {
if (isset($tpl->tpl_vars[ $var ])) { if (isset($tpl->tpl_vars[ $var ])) {
$export = $export =
preg_replace('/^Smarty_Variable::__set_state[(]|[)]$/', '', var_export($tpl->tpl_vars[ $var ], true)); preg_replace('/^\\\\?Smarty_Variable::__set_state[(]|[)]$/', '', var_export($tpl->tpl_vars[ $var ], true));
if (preg_match('/(\w+)::__set_state/', $export, $match)) { if (preg_match('/(\w+)::__set_state/', $export, $match)) {
throw new SmartyException("{make_nocache \${$var}} in template '{$tpl->source->name}': variable does contain object '{$match[1]}' not implementing method '__set_state'"); throw new SmartyException("{make_nocache \${$var}} in template '{$tpl->source->name}': variable does contain object '{$match[1]}' not implementing method '__set_state'");
} }

View File

@@ -29,12 +29,6 @@ class Smarty_Internal_Runtime_WriteFile
{ {
$_error_reporting = error_reporting(); $_error_reporting = error_reporting();
error_reporting($_error_reporting & ~E_NOTICE & ~E_WARNING); error_reporting($_error_reporting & ~E_NOTICE & ~E_WARNING);
$_file_perms = property_exists($smarty, '_file_perms') ? $smarty->_file_perms : 0644;
$_dir_perms =
property_exists($smarty, '_dir_perms') ? (isset($smarty->_dir_perms) ? $smarty->_dir_perms : 0777) : 0771;
if ($_file_perms !== null) {
$old_umask = umask(0);
}
$_dirpath = dirname($_filepath); $_dirpath = dirname($_filepath);
// if subdirs, create dir structure // if subdirs, create dir structure
if ($_dirpath !== '.') { if ($_dirpath !== '.') {
@@ -42,7 +36,7 @@ class Smarty_Internal_Runtime_WriteFile
// loop if concurrency problem occurs // loop if concurrency problem occurs
// see https://bugs.php.net/bug.php?id=35326 // see https://bugs.php.net/bug.php?id=35326
while (!is_dir($_dirpath)) { while (!is_dir($_dirpath)) {
if (@mkdir($_dirpath, $_dir_perms, true)) { if (@mkdir($_dirpath, 0777, true)) {
break; break;
} }
clearstatcache(); clearstatcache();
@@ -89,11 +83,8 @@ class Smarty_Internal_Runtime_WriteFile
error_reporting($_error_reporting); error_reporting($_error_reporting);
throw new SmartyException("unable to write file {$_filepath}"); throw new SmartyException("unable to write file {$_filepath}");
} }
if ($_file_perms !== null) { // set file permissions
// set file permissions @chmod($_filepath, 0666 & ~umask());
chmod($_filepath, $_file_perms);
umask($old_umask);
}
error_reporting($_error_reporting); error_reporting($_error_reporting);
return true; return true;
} }

View File

@@ -24,6 +24,7 @@
* *
* @method bool mustCompile() * @method bool mustCompile()
*/ */
#[\AllowDynamicProperties]
class Smarty_Internal_Template extends Smarty_Internal_TemplateBase class Smarty_Internal_Template extends Smarty_Internal_TemplateBase
{ {
/** /**
@@ -292,7 +293,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase
$smarty = &$this->smarty; $smarty = &$this->smarty;
$_templateId = $smarty->_getTemplateId($template, $cache_id, $compile_id, $caching, $tpl); $_templateId = $smarty->_getTemplateId($template, $cache_id, $compile_id, $caching, $tpl);
// recursive call ? // recursive call ?
if (isset($tpl->templateId) ? $tpl->templateId : $tpl->_getTemplateId() !== $_templateId) { if ((isset($tpl->templateId) ? $tpl->templateId : $tpl->_getTemplateId()) !== $_templateId) {
// already in template cache? // already in template cache?
if (isset(self::$tplObjCache[ $_templateId ])) { if (isset(self::$tplObjCache[ $_templateId ])) {
// copy data from cached object // copy data from cached object
@@ -358,7 +359,7 @@ class Smarty_Internal_Template extends Smarty_Internal_TemplateBase
} }
if ($tpl->caching === 9999) { if ($tpl->caching === 9999) {
if (!isset($tpl->compiled)) { if (!isset($tpl->compiled)) {
$this->loadCompiled(true); $tpl->loadCompiled(true);
} }
if ($tpl->compiled->has_nocache_code) { if ($tpl->compiled->has_nocache_code) {
$this->cached->hashes[ $tpl->compiled->nocache_hash ] = true; $this->cached->hashes[ $tpl->compiled->nocache_hash ] = true;

View File

@@ -257,7 +257,7 @@ abstract class Smarty_Internal_TemplateBase extends Smarty_Internal_Data
error_reporting($_smarty_old_error_level); error_reporting($_smarty_old_error_level);
} }
return $result; return $result;
} catch (Exception $e) { } catch (Throwable $e) {
while (ob_get_level() > $level) { while (ob_get_level() > $level) {
ob_end_clean(); ob_end_clean();
} }

View File

@@ -422,9 +422,6 @@ abstract class Smarty_Internal_TemplateCompilerBase
try { try {
// save template object in compiler class // save template object in compiler class
$this->template = $template; $this->template = $template;
if (property_exists($this->template->smarty, 'plugin_search_order')) {
$this->plugin_search_order = $this->template->smarty->plugin_search_order;
}
if ($this->smarty->debugging) { if ($this->smarty->debugging) {
if (!isset($this->smarty->_debug)) { if (!isset($this->smarty->_debug)) {
$this->smarty->_debug = new Smarty_Internal_Debug(); $this->smarty->_debug = new Smarty_Internal_Debug();
@@ -608,7 +605,7 @@ abstract class Smarty_Internal_TemplateCompilerBase
if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0 if (strcasecmp($name, 'isset') === 0 || strcasecmp($name, 'empty') === 0
|| strcasecmp($name, 'array') === 0 || is_callable($name) || strcasecmp($name, 'array') === 0 || is_callable($name)
) { ) {
$func_name = strtolower($name); $func_name = smarty_strtolower_ascii($name);
if ($func_name === 'isset') { if ($func_name === 'isset') {
if (count($parameter) === 0) { if (count($parameter) === 0) {
@@ -768,7 +765,7 @@ abstract class Smarty_Internal_TemplateCompilerBase
if (!isset(self::$_tag_objects[ $tag ])) { if (!isset(self::$_tag_objects[ $tag ])) {
// lazy load internal compiler plugin // lazy load internal compiler plugin
$_tag = explode('_', $tag); $_tag = explode('_', $tag);
$_tag = array_map('ucfirst', $_tag); $_tag = array_map('smarty_ucfirst_ascii', $_tag);
$class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag); $class_name = 'Smarty_Internal_Compile_' . implode('_', $_tag);
if (class_exists($class_name) if (class_exists($class_name)
&& (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this)) && (!isset($this->smarty->security_policy) || $this->smarty->security_policy->isTrustedTag($tag, $this))
@@ -1134,8 +1131,12 @@ abstract class Smarty_Internal_TemplateCompilerBase
echo ob_get_clean(); echo ob_get_clean();
flush(); flush();
} }
$e = new SmartyCompilerException($error_text); $e = new SmartyCompilerException(
$e->setLine($line); $error_text,
0,
$this->template->source->filepath,
$line
);
$e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ])); $e->source = trim(preg_replace('![\t\r\n]+!', ' ', $match[ $line - 1 ]));
$e->desc = $args; $e->desc = $args;
$e->template = $this->template->source->filepath; $e->template = $this->template->source->filepath;

View File

@@ -144,7 +144,7 @@ class Smarty_Internal_TestInstall
} }
// test if all registered plugins_dir are accessible // test if all registered plugins_dir are accessible
// and if core plugins directory is still registered // and if core plugins directory is still registered
$_core_plugins_dir = realpath(dirname(__FILE__) . '/../plugins'); $_core_plugins_dir = realpath(__DIR__ . '/../plugins');
$_core_plugins_available = false; $_core_plugins_available = false;
foreach ($smarty->getPluginsDir() as $plugin_dir) { foreach ($smarty->getPluginsDir() as $plugin_dir) {
$_plugin_dir = $plugin_dir; $_plugin_dir = $plugin_dir;

View File

@@ -76,11 +76,11 @@ abstract class Smarty_Resource
} }
// try sysplugins dir // try sysplugins dir
if (isset(self::$sysplugins[ $type ])) { if (isset(self::$sysplugins[ $type ])) {
$_resource_class = 'Smarty_Internal_Resource_' . ucfirst($type); $_resource_class = 'Smarty_Internal_Resource_' . smarty_ucfirst_ascii($type);
return $smarty->_cache[ 'resource_handlers' ][ $type ] = new $_resource_class(); return $smarty->_cache[ 'resource_handlers' ][ $type ] = new $_resource_class();
} }
// try plugins dir // try plugins dir
$_resource_class = 'Smarty_Resource_' . ucfirst($type); $_resource_class = 'Smarty_Resource_' . smarty_ucfirst_ascii($type);
if ($smarty->loadPlugin($_resource_class)) { if ($smarty->loadPlugin($_resource_class)) {
if (class_exists($_resource_class, false)) { if (class_exists($_resource_class, false)) {
return $smarty->_cache[ 'resource_handlers' ][ $type ] = new $_resource_class(); return $smarty->_cache[ 'resource_handlers' ][ $type ] = new $_resource_class();

View File

@@ -47,7 +47,7 @@ abstract class Smarty_Resource_Custom extends Smarty_Resource
*/ */
public function populate(Smarty_Template_Source $source, Smarty_Internal_Template $_template = null) public function populate(Smarty_Template_Source $source, Smarty_Internal_Template $_template = null)
{ {
$source->filepath = $source->type . ':' . substr(preg_replace('/[^A-Za-z0-9.]/', '', $source->name), 0, 25); $source->filepath = $source->type . ':' . $this->generateSafeName($source->name);
$source->uid = sha1($source->type . ':' . $source->name); $source->uid = sha1($source->type . ':' . $source->name);
$mtime = $this->fetchTimestamp($source->name); $mtime = $this->fetchTimestamp($source->name);
if ($mtime !== null) { if ($mtime !== null) {
@@ -88,6 +88,17 @@ abstract class Smarty_Resource_Custom extends Smarty_Resource
*/ */
public function getBasename(Smarty_Template_Source $source) public function getBasename(Smarty_Template_Source $source)
{ {
return basename(substr(preg_replace('/[^A-Za-z0-9.]/', '', $source->name), 0, 25)); return basename($this->generateSafeName($source->name));
}
/**
* Removes special characters from $name and limits its length to 127 characters.
*
* @param $name
*
* @return string
*/
private function generateSafeName($name): string {
return substr(preg_replace('/[^A-Za-z0-9._]/', '', (string) $name), 0, 127);
} }
} }

View File

@@ -19,6 +19,7 @@
/** /**
* This class does contain the security settings * This class does contain the security settings
*/ */
#[\AllowDynamicProperties]
class Smarty_Security class Smarty_Security
{ {
@@ -105,7 +106,7 @@ class Smarty_Security
* *
* @var array * @var array
*/ */
public $php_modifiers = array('escape', 'count', 'nl2br',); public $php_modifiers = array('escape', 'count', 'sizeof', 'nl2br',);
/** /**
* This is an array of allowed tags. * This is an array of allowed tags.
@@ -328,7 +329,7 @@ class Smarty_Security
* *
* @param string $modifier_name * @param string $modifier_name
* @param object $compiler compiler object * @param object $compiler compiler object
* * @deprecated
* @return boolean true if modifier is trusted * @return boolean true if modifier is trusted
*/ */
public function isTrustedPhpModifier($modifier_name, $compiler) public function isTrustedPhpModifier($modifier_name, $compiler)
@@ -555,35 +556,6 @@ class Smarty_Security
throw new SmartyException("URI '{$uri}' not allowed by security setting"); throw new SmartyException("URI '{$uri}' not allowed by security setting");
} }
/**
* Check if directory of file resource is trusted.
*
* @param string $filepath
*
* @return boolean true if directory is trusted
* @throws SmartyException if PHP directory is not trusted
*/
public function isTrustedPHPDir($filepath)
{
if (empty($this->trusted_dir)) {
throw new SmartyException("directory '{$filepath}' not allowed by security setting (no trusted_dir specified)");
}
// check if index is outdated
if (!$this->_trusted_dir || $this->_trusted_dir !== $this->trusted_dir) {
$this->_php_resource_dir = array();
$this->_trusted_dir = $this->trusted_dir;
foreach ((array)$this->trusted_dir as $directory) {
$directory = $this->smarty->_realpath($directory . '/', true);
$this->_php_resource_dir[ $directory ] = true;
}
}
$addPath = $this->_checkDir($filepath, $this->_php_resource_dir);
if ($addPath !== false) {
$this->_php_resource_dir = array_merge($this->_php_resource_dir, $addPath);
}
return true;
}
/** /**
* Remove old directories and its sub folders, add new directories * Remove old directories and its sub folders, add new directories
* *

View File

@@ -7,6 +7,7 @@
* @package Smarty * @package Smarty
* @subpackage Template * @subpackage Template
*/ */
#[\AllowDynamicProperties]
class Smarty_Variable class Smarty_Variable
{ {
/** /**

View File

@@ -7,6 +7,33 @@
*/ */
class SmartyCompilerException extends SmartyException class SmartyCompilerException extends SmartyException
{ {
/**
* The constructor of the exception
*
* @param string $message The Exception message to throw.
* @param int $code The Exception code.
* @param string|null $filename The filename where the exception is thrown.
* @param int|null $line The line number where the exception is thrown.
* @param Throwable|null $previous The previous exception used for the exception chaining.
*/
public function __construct(
string $message = "",
int $code = 0,
?string $filename = null,
?int $line = null,
Throwable $previous = null
) {
parent::__construct($message, $code, $previous);
// These are optional parameters, should be be overridden only when present!
if ($filename) {
$this->file = $filename;
}
if ($line) {
$this->line = $line;
}
}
/** /**
* @return string * @return string
*/ */
@@ -22,6 +49,7 @@ class SmartyCompilerException extends SmartyException
{ {
$this->line = $line; $this->line = $line;
} }
/** /**
* The template source snippet relating to the error * The template source snippet relating to the error
* *

View File

@@ -1,5 +1,5 @@
$Id$ $Id$
Smarty version: 4.1.1 Smarty version: 4.3.0 (released: 2022/11/22)
https://github.com/smarty-php/smarty/archive/refs/tags/v4.1.1.tar.gz https://github.com/smarty-php/smarty/archive/refs/tags/v4.3.0.tar.gz