From d02ef6652aa6bf2e066f3b1320f4dbf558930aef Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Mon, 25 Jul 2022 13:55:53 +0200 Subject: [PATCH 1/8] PHP8.2 compatibility --- .github/workflows/ci.yml | 4 ++++ README.md | 2 +- docker-compose.yml | 5 +++++ docs/getting-started.md | 2 +- libs/plugins/modifiercompiler.escape.php | 10 +++++----- libs/sysplugins/smarty_internal_extension_handler.php | 1 + libs/sysplugins/smarty_internal_template.php | 1 + libs/sysplugins/smarty_security.php | 1 + libs/sysplugins/smarty_variable.php | 1 + utilities/testrunners/php82/Dockerfile | 10 ++++++++++ 10 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 utilities/testrunners/php82/Dockerfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 449146c9e..33224fcb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,7 @@ jobs: - "7.4" - "8.0" - "8.1" + - "8.2" compiler: - default @@ -42,6 +43,9 @@ jobs: - os: ubuntu-latest php-version: "8.1" compiler: jit + - os: ubuntu-latest + php-version: "8.2" + compiler: jit steps: - name: Checkout diff --git a/README.md b/README.md index 782f0b2cb..0ef3cfab6 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Smarty is a template engine for PHP, facilitating the separation of presentation Read the [documentation](https://smarty-php.github.io/smarty/) to find out how to use it. ## Requirements -Smarty can be run with PHP 7.1 to PHP 8.1. +Smarty can be run with PHP 7.1 to PHP 8.2. ## Installation Smarty versions 3.1.11 or later can be installed with [Composer](https://getcomposer.org/). diff --git a/docker-compose.yml b/docker-compose.yml index d46608bff..cc90fb8b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -38,3 +38,8 @@ services: service: base build: dockerfile: ./utilities/testrunners/php81/Dockerfile + php82: + extends: + service: base + build: + dockerfile: ./utilities/testrunners/php82/Dockerfile diff --git a/docs/getting-started.md b/docs/getting-started.md index de55ffe8e..2a1391f4b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -2,7 +2,7 @@ What is Smarty? ============== ## Requirements -Smarty can be run with PHP 7.1 to PHP 8.1. +Smarty can be run with PHP 7.1 to PHP 8.2. ## Installation Smarty versions 3.1.11 or later can be installed with [Composer](https://getcomposer.org/). diff --git a/libs/plugins/modifiercompiler.escape.php b/libs/plugins/modifiercompiler.escape.php index 1fc5e7817..c1469fabf 100644 --- a/libs/plugins/modifiercompiler.escape.php +++ b/libs/plugins/modifiercompiler.escape.php @@ -54,13 +54,13 @@ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompile if (Smarty::$_MBSTRING) { if ($_double_encode) { // php >=5.2.3 - go native - return 'mb_convert_encoding(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . - var_export($char_set, true) . ', ' . var_export($double_encode, true) . - '), "HTML-ENTITIES", ' . var_export($char_set, true) . ')'; + return 'htmlspecialchars_decode(mb_convert_encoding(htmlentities(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . + var_export($char_set, true) . ', ' . var_export($double_encode, true) . + '), ENT_COMPAT, ' . var_export($char_set, true) . '),' . 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) . ')'; + return 'htmlspecialchars_decode(mb_convert_encoding(htmlentities(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . + var_export($char_set, true) . '), ENT_COMPAT, ' . var_export($char_set, true) . '), ' . var_export($char_set, true) . '))'; } else { // fall back to modifier.escape.php } diff --git a/libs/sysplugins/smarty_internal_extension_handler.php b/libs/sysplugins/smarty_internal_extension_handler.php index 634ad8315..3ef040ab1 100644 --- a/libs/sysplugins/smarty_internal_extension_handler.php +++ b/libs/sysplugins/smarty_internal_extension_handler.php @@ -36,6 +36,7 @@ * @property Smarty_Internal_Method_RegisterPlugin $registerPlugin * @property mixed|\Smarty_Template_Cached configLoad */ +#[\AllowDynamicProperties] class Smarty_Internal_Extension_Handler { public $objType = null; diff --git a/libs/sysplugins/smarty_internal_template.php b/libs/sysplugins/smarty_internal_template.php index bf627ce72..72d1d52e0 100644 --- a/libs/sysplugins/smarty_internal_template.php +++ b/libs/sysplugins/smarty_internal_template.php @@ -24,6 +24,7 @@ * * @method bool mustCompile() */ +#[\AllowDynamicProperties] class Smarty_Internal_Template extends Smarty_Internal_TemplateBase { /** diff --git a/libs/sysplugins/smarty_security.php b/libs/sysplugins/smarty_security.php index 974c63529..97cd0521d 100644 --- a/libs/sysplugins/smarty_security.php +++ b/libs/sysplugins/smarty_security.php @@ -19,6 +19,7 @@ /** * This class does contain the security settings */ +#[\AllowDynamicProperties] class Smarty_Security { diff --git a/libs/sysplugins/smarty_variable.php b/libs/sysplugins/smarty_variable.php index 914d99bd7..6a534228b 100644 --- a/libs/sysplugins/smarty_variable.php +++ b/libs/sysplugins/smarty_variable.php @@ -7,6 +7,7 @@ * @package Smarty * @subpackage Template */ +#[\AllowDynamicProperties] class Smarty_Variable { /** diff --git a/utilities/testrunners/php82/Dockerfile b/utilities/testrunners/php82/Dockerfile new file mode 100644 index 000000000..6b7392ea8 --- /dev/null +++ b/utilities/testrunners/php82/Dockerfile @@ -0,0 +1,10 @@ +FROM php:8.2-cli + +## Basic utilities +RUN apt-get update -yqq && apt-get install -y curl apt-utils git zip unzip + +## Composer +COPY ./utilities/testrunners/shared/install-composer.sh /root/install-composer.sh +WORKDIR /root +RUN sh ./install-composer.sh +RUN mv ./composer.phar /usr/local/bin/composer From 892dc33907292a51bc6c3bdc52285751b3af7ae9 Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Thu, 15 Sep 2022 15:16:53 +0200 Subject: [PATCH 2/8] PHP8.2 compatibility : Fixed unit tests --- libs/sysplugins/smarty_internal_runtime_make_nocache.php | 2 +- tests/UnitTests/ResourceTests/Stream/StreamResourceTest.php | 1 + tests/UnitTests/SecurityTests/SecurityTest.php | 1 + .../RegisterObject/CompileRegisteredObjectFunctionTest.php | 5 +++++ 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/libs/sysplugins/smarty_internal_runtime_make_nocache.php b/libs/sysplugins/smarty_internal_runtime_make_nocache.php index 53069148d..7994aa048 100644 --- a/libs/sysplugins/smarty_internal_runtime_make_nocache.php +++ b/libs/sysplugins/smarty_internal_runtime_make_nocache.php @@ -22,7 +22,7 @@ public function save(Smarty_Internal_Template $tpl, $var) { if (isset($tpl->tpl_vars[ $var ])) { $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)) { throw new SmartyException("{make_nocache \${$var}} in template '{$tpl->source->name}': variable does contain object '{$match[1]}' not implementing method '__set_state'"); } diff --git a/tests/UnitTests/ResourceTests/Stream/StreamResourceTest.php b/tests/UnitTests/ResourceTests/Stream/StreamResourceTest.php index 57dbd9eb8..e5eedcc18 100644 --- a/tests/UnitTests/ResourceTests/Stream/StreamResourceTest.php +++ b/tests/UnitTests/ResourceTests/Stream/StreamResourceTest.php @@ -206,6 +206,7 @@ public function testSmartyIsCached() } } +#[AllowDynamicProperties] class ResourceStream { private $position; diff --git a/tests/UnitTests/SecurityTests/SecurityTest.php b/tests/UnitTests/SecurityTests/SecurityTest.php index e1469a8d6..7631a0a6b 100644 --- a/tests/UnitTests/SecurityTests/SecurityTest.php +++ b/tests/UnitTests/SecurityTests/SecurityTest.php @@ -391,6 +391,7 @@ class Security extends Smarty_Security { } +#[AllowDynamicProperties] class ResourceStreamSecurity { private $position; diff --git a/tests/UnitTests/SmartyMethodsTests/RegisterObject/CompileRegisteredObjectFunctionTest.php b/tests/UnitTests/SmartyMethodsTests/RegisterObject/CompileRegisteredObjectFunctionTest.php index 63a7b52dd..eaee13600 100644 --- a/tests/UnitTests/SmartyMethodsTests/RegisterObject/CompileRegisteredObjectFunctionTest.php +++ b/tests/UnitTests/SmartyMethodsTests/RegisterObject/CompileRegisteredObjectFunctionTest.php @@ -15,6 +15,11 @@ */ class CompileRegisteredObjectFunctionTest extends PHPUnit_Smarty { + /** + * @var RegObject + */ + private $object; + public function setUp(): void { $this->setUpSmarty(__DIR__); From 87c479c87e269c5e5d35a05acd1cc43cf05b1044 Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Mon, 19 Sep 2022 09:25:47 +0200 Subject: [PATCH 3/8] PHP8.2 compatibility : Replace ENT_COMPAT by ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 --- libs/plugins/modifiercompiler.escape.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/plugins/modifiercompiler.escape.php b/libs/plugins/modifiercompiler.escape.php index c1469fabf..c6ba88c76 100644 --- a/libs/plugins/modifiercompiler.escape.php +++ b/libs/plugins/modifiercompiler.escape.php @@ -56,11 +56,11 @@ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompile // php >=5.2.3 - go native return 'htmlspecialchars_decode(mb_convert_encoding(htmlentities(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . var_export($double_encode, true) . - '), ENT_COMPAT, ' . var_export($char_set, true) . '),' . var_export($char_set, true) . '))'; + '), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ' . var_export($char_set, true) . '),' . var_export($char_set, true) . '))'; } elseif ($double_encode) { // php <5.2.3 - only handle double encoding return 'htmlspecialchars_decode(mb_convert_encoding(htmlentities(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . - var_export($char_set, true) . '), ENT_COMPAT, ' . var_export($char_set, true) . '), ' . var_export($char_set, true) . '))'; + var_export($char_set, true) . '), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ' . var_export($char_set, true) . '), ' . var_export($char_set, true) . '))'; } else { // fall back to modifier.escape.php } From afbcf3250d3c1831a2b9dd6c92c80adb9e905ebe Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Mon, 19 Sep 2022 09:32:41 +0200 Subject: [PATCH 4/8] PHP8.2 compatibility : Remove deprecated utf8_decode --- .../TagTests/PluginModifier/PluginModifierCharsetTest.php | 8 ++++---- .../TagTests/PluginModifier/PluginModifierEscapeTest.php | 2 +- .../ValueTests/Variables/Stream/StreamVariableTest.php | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierCharsetTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierCharsetTest.php index 423b593c0..92ff95141 100644 --- a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierCharsetTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierCharsetTest.php @@ -23,7 +23,7 @@ public function setUp(): void public function testToLatin1() { $encoded = "hällö wörld 1"; - $result = utf8_decode($encoded); + $result = mb_convert_encoding($encoded, 'ISO-8859-1', 'UTF-8'); $tpl = $this->smarty->createTemplate('string:{"' . $encoded . '"|to_charset}'); $this->assertEquals(str_replace("\r", '', $result), $tpl->fetch()); } @@ -33,7 +33,7 @@ public function testToLatin1WithoutMbstring() Smarty::$_MBSTRING = false; $this->smarty->setCompileId('mb'); $encoded = "hällö wörld 2"; - $result = utf8_decode($encoded); + $result = mb_convert_encoding($encoded, 'ISO-8859-1', 'UTF-8'); $tpl = $this->smarty->createTemplate('string:{"' . $encoded . '"|to_charset}'); $this->assertEquals($encoded, $tpl->fetch()); Smarty::$_MBSTRING = true; @@ -42,7 +42,7 @@ public function testToLatin1WithoutMbstring() public function testFromLatin1() { $result = "hällö wörld 3"; - $encoded = utf8_decode($result); + $encoded = mb_convert_encoding($result, 'ISO-8859-1', 'UTF-8'); $tpl = $this->smarty->createTemplate('string:{"' . $encoded . '"|from_charset}'); $this->assertEquals(str_replace("\r", '', $result), $tpl->fetch()); } @@ -52,7 +52,7 @@ public function testFromLatin1WithoutMbstring() Smarty::$_MBSTRING = false; $this->smarty->setCompileId('mb'); $result = "hällö wörld 4"; - $encoded = utf8_decode($result); + $encoded = mb_convert_encoding($result, 'ISO-8859-1', 'UTF-8'); $tpl = $this->smarty->createTemplate('string:{"' . $encoded . '"|from_charset}'); $this->assertEquals($encoded, $tpl->fetch()); Smarty::$_MBSTRING = true; diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php index 0782a01cb..07c801d70 100644 --- a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php @@ -203,7 +203,7 @@ public function testNonstd() public function testNonstdWithoutMbstring() { Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb'); - $tpl = $this->smarty->createTemplate('string:{"' . utf8_decode('sma\'rty@»example«.com') . '"|escape:"nonstd"}'); + $tpl = $this->smarty->createTemplate('string:{"' . mb_convert_encoding('sma\'rty@»example«.com', 'ISO-8859-1', 'UTF-8') . '"|escape:"nonstd"}'); $this->assertEquals("sma'rty@»example«.com", $this->smarty->fetch($tpl)); Smarty::$_MBSTRING = true; } diff --git a/tests/UnitTests/TemplateSource/ValueTests/Variables/Stream/StreamVariableTest.php b/tests/UnitTests/TemplateSource/ValueTests/Variables/Stream/StreamVariableTest.php index feca410e6..c832fc70c 100644 --- a/tests/UnitTests/TemplateSource/ValueTests/Variables/Stream/StreamVariableTest.php +++ b/tests/UnitTests/TemplateSource/ValueTests/Variables/Stream/StreamVariableTest.php @@ -68,6 +68,7 @@ public function testStreamVariable3() // } } +#[AllowDynamicProperties] class VariableStream { private $position; From 9fee14e7afba3ad328fa6074cbb20ac75a4a1eba Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Fri, 18 Nov 2022 09:52:47 +0100 Subject: [PATCH 5/8] PHP8.2 compatibility : Remove HTML-ENTITIES parameter --- libs/plugins/modifier.escape.php | 4 +++- libs/plugins/modifiercompiler.unescape.php | 2 +- .../TagTests/PluginModifier/PluginModifierStripTest.php | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libs/plugins/modifier.escape.php b/libs/plugins/modifier.escape.php index 3ce483827..2470a1697 100644 --- a/libs/plugins/modifier.escape.php +++ b/libs/plugins/modifier.escape.php @@ -90,7 +90,9 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $ } } // htmlentities() won't convert everything, so use mb_convert_encoding - return mb_convert_encoding($string, 'HTML-ENTITIES', $char_set); + $string = mb_convert_encoding($string, 'UTF-8', $char_set); + $string = htmlentities($string); + return htmlspecialchars_decode($string); } // no MBString fallback if ($_double_encode) { diff --git a/libs/plugins/modifiercompiler.unescape.php b/libs/plugins/modifiercompiler.unescape.php index 3438fe3e0..9e67c75c8 100644 --- a/libs/plugins/modifiercompiler.unescape.php +++ b/libs/plugins/modifiercompiler.unescape.php @@ -39,7 +39,7 @@ function smarty_modifiercompiler_unescape($params, Smarty_Internal_TemplateCompi case 'entity': case 'htmlall': 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 ] . ')'; case 'html': diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierStripTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierStripTest.php index fbef16b0a..ab1a43849 100644 --- a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierStripTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierStripTest.php @@ -30,7 +30,7 @@ public function testUnicodeSpaces() { // Some Unicode Spaces $string = " hello spaced       words "; - $string = mb_convert_encoding($string, 'UTF-8', "HTML-ENTITIES"); + $string = html_entity_decode($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8'); $tpl = $this->smarty->createTemplate('string:{"' . $string . '"|strip}'); $this->assertEquals(" hello spaced words ", $this->smarty->fetch($tpl)); } From 4c39c543d42fd22e2fda7ea82d89f2de21d42fb7 Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Fri, 18 Nov 2022 09:58:47 +0100 Subject: [PATCH 6/8] Removed some unused code for clarity, updated the changelog. --- CHANGELOG.md | 3 + libs/plugins/modifier.escape.php | 78 +----------------------- libs/plugins/modifiercompiler.escape.php | 41 +++---------- 3 files changed, 14 insertions(+), 108 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 333035cac..0ccdfd6b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- PHP8.2 compatibility [#775](https://github.com/smarty-php/smarty/pull/775) + ### Changed - Include docs and demo in the releases [#799](https://github.com/smarty-php/smarty/issues/799) - Using PHP functions as modifiers now triggers a deprecation notice because we will drop support for this in the next major release [#813](https://github.com/smarty-php/smarty/issues/813) diff --git a/libs/plugins/modifier.escape.php b/libs/plugins/modifier.escape.php index 2470a1697..0ffbe0611 100644 --- a/libs/plugins/modifier.escape.php +++ b/libs/plugins/modifier.escape.php @@ -23,7 +23,6 @@ */ 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_2 = false; if (!$char_set) { @@ -34,89 +33,18 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $ switch ($esc_type) { case 'html': - if ($_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; - } - } + return htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); // no break case 'htmlall': if (Smarty::$_MBSTRING) { - // mb_convert_encoding ignores htmlspecialchars() - if ($_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; - } - } + $string = htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); // htmlentities() won't convert everything, so use mb_convert_encoding $string = mb_convert_encoding($string, 'UTF-8', $char_set); $string = htmlentities($string); return htmlspecialchars_decode($string); } // no MBString fallback - if ($_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; - } - } + return htmlentities($string, ENT_QUOTES, $char_set, $double_encode); // no break case 'url': return rawurlencode($string); diff --git a/libs/plugins/modifiercompiler.escape.php b/libs/plugins/modifiercompiler.escape.php index c6ba88c76..946de104f 100644 --- a/libs/plugins/modifiercompiler.escape.php +++ b/libs/plugins/modifiercompiler.escape.php @@ -18,12 +18,10 @@ * @param Smarty_Internal_TemplateCompilerBase $compiler * * @return string with compiled code - * @throws \SmartyException + * @throws SmartyException */ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompilerBase $compiler) { - static $_double_encode = true; - static $is_loaded = false; $compiler->template->_checkPlugins( array( array( @@ -41,41 +39,18 @@ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompile } switch ($esc_type) { case 'html': - if ($_double_encode) { - return 'htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, 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 - } + return 'htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . + var_export($double_encode, true) . ')'; // no break case 'htmlall': if (Smarty::$_MBSTRING) { - if ($_double_encode) { - // php >=5.2.3 - go native - return 'htmlspecialchars_decode(mb_convert_encoding(htmlentities(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . - var_export($char_set, true) . ', ' . var_export($double_encode, true) . - '), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ' . var_export($char_set, true) . '),' . var_export($char_set, true) . '))'; - } elseif ($double_encode) { - // php <5.2.3 - only handle double encoding - return 'htmlspecialchars_decode(mb_convert_encoding(htmlentities(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . - var_export($char_set, true) . '), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ' . var_export($char_set, true) . '), ' . var_export($char_set, true) . '))'; - } else { - // fall back to modifier.escape.php - } + return 'htmlspecialchars_decode(mb_convert_encoding(htmlentities(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . + var_export($char_set, true) . ', ' . var_export($double_encode, true) . + '), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ' . var_export($char_set, true) . '),' . var_export($char_set, true) . '))'; } // no MBString fallback - if ($_double_encode) { - // php >=5.2.3 - go native - 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 - } + return 'htmlentities((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . + var_export($double_encode, true) . ')'; // no break case 'url': return 'rawurlencode((string)' . $params[ 0 ] . ')'; From 6f4025b038c763499ff38405ade955e8a7083c0a Mon Sep 17 00:00:00 2001 From: Simon Wisselink Date: Fri, 18 Nov 2022 13:03:45 +0100 Subject: [PATCH 7/8] More concise escape implementation and unit test to cover both modifierplugin and modifiercompiler. --- libs/plugins/modifier.escape.php | 5 +-- libs/plugins/modifiercompiler.escape.php | 6 +-- .../PluginModifierEscapeTest.php | 38 +++++++++++++++++-- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/libs/plugins/modifier.escape.php b/libs/plugins/modifier.escape.php index 0ffbe0611..11e44682e 100644 --- a/libs/plugins/modifier.escape.php +++ b/libs/plugins/modifier.escape.php @@ -37,11 +37,8 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $ // no break case 'htmlall': if (Smarty::$_MBSTRING) { - $string = htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode); - // htmlentities() won't convert everything, so use mb_convert_encoding $string = mb_convert_encoding($string, 'UTF-8', $char_set); - $string = htmlentities($string); - return htmlspecialchars_decode($string); + return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode); } // no MBString fallback return htmlentities($string, ENT_QUOTES, $char_set, $double_encode); diff --git a/libs/plugins/modifiercompiler.escape.php b/libs/plugins/modifiercompiler.escape.php index 946de104f..602c3dbfc 100644 --- a/libs/plugins/modifiercompiler.escape.php +++ b/libs/plugins/modifiercompiler.escape.php @@ -44,9 +44,9 @@ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompile // no break case 'htmlall': if (Smarty::$_MBSTRING) { - return 'htmlspecialchars_decode(mb_convert_encoding(htmlentities(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . - var_export($char_set, true) . ', ' . var_export($double_encode, true) . - '), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ' . var_export($char_set, true) . '),' . var_export($char_set, true) . '))'; + return 'htmlentities(mb_convert_encoding((string)' . $params[ 0 ] . ', \'UTF-8\', ' . + var_export($char_set, true) . '), ENT_QUOTES, \'UTF-8\', ' . + var_export($double_encode, true) . ')'; } // no MBString fallback return 'htmlentities((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' . diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php index 07c801d70..309a71ab8 100644 --- a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php @@ -20,12 +20,19 @@ public function setUp(): void $this->setUpSmarty(__DIR__); } - public function testHtml() + public function testHtmlCompiled() { $tpl = $this->smarty->createTemplate('string:{"I\'m some to ä be \"escaped\" or ©"|escape:"html"}'); $this->assertEquals("I'm some <html> to ä be "escaped" or &copy;", $this->smarty->fetch($tpl)); } + public function testHtmlModifier() + { + $tpl = $this->smarty->createTemplate('string:{"I\'m some to ä be \"escaped\" or ©"|escape:$mode}'); + $this->smarty->assign('mode', 'html'); + $this->assertEquals("I'm some <html> to ä be "escaped" or &copy;", $this->smarty->fetch($tpl)); + } + public function testHtmlWithoutMbstring() { Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb'); @@ -48,13 +55,20 @@ public function testHtmlDoubleWithoutMbstring() Smarty::$_MBSTRING = true; } - public function testHtmlall() + public function testHtmlallCompiled() { $tpl = $this->smarty->createTemplate('string:{"I\'m some to ä be \"escaped\" or ©"|escape:"htmlall"}'); $this->assertEquals("I'm some <html> to ä be "escaped" or &copy;", $this->smarty->fetch($tpl)); } - public function testHtmlallWithoutMbstring() + public function testHtmlallModifier() + { + $tpl = $this->smarty->createTemplate('string:{"I\'m some to ä be \"escaped\" or ©"|escape:$mode}'); + $this->smarty->assign('mode', 'htmlall'); + $this->assertEquals("I'm some <html> to ä be "escaped" or &copy;", $this->smarty->fetch($tpl)); + } + + public function testHtmlallWithoutMbstringCompiled() { Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb'); $tpl = $this->smarty->createTemplate('string:{"I\'m some to ä be \"escaped\" or ©"|escape:"htmlall"}'); @@ -62,6 +76,15 @@ public function testHtmlallWithoutMbstring() Smarty::$_MBSTRING = true; } + public function testHtmlallWithoutMbstringModifier() + { + Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb'); + $tpl = $this->smarty->createTemplate('string:{"I\'m some to ä be \"escaped\" or ©"|escape:$mode}'); + $this->smarty->assign('mode', 'htmlall'); + $this->assertEquals("I'm some <html> to ä be "escaped" or &copy;", $this->smarty->fetch($tpl)); + Smarty::$_MBSTRING = true; + } + public function testHtmlallDouble() { $tpl = $this->smarty->createTemplate('string:{"I\'m some to ä be \"escaped\" or ©"|escape:"htmlall":null:false}'); @@ -76,12 +99,19 @@ public function testHtmlallDoubleWithoutMbstring() Smarty::$_MBSTRING = true; } - public function testUrl() + public function testUrlCompiled() { $tpl = $this->smarty->createTemplate('string:{"http://some.encoded.com/url?parts#foo"|escape:"url"}'); $this->assertEquals("http%3A%2F%2Fsome.encoded.com%2Furl%3Fparts%23foo", $this->smarty->fetch($tpl)); } + public function testUrlModifier() + { + $tpl = $this->smarty->createTemplate('string:{"http://some.encoded.com/url?parts#foo"|escape:$mode}'); + $this->smarty->assign('mode', 'url'); + $this->assertEquals("http%3A%2F%2Fsome.encoded.com%2Furl%3Fparts%23foo", $this->smarty->fetch($tpl)); + } + public function testUrlWithoutMbstring() { Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb'); From 665b216f61df8cdfac284db9c5a550b7d8dbdeec Mon Sep 17 00:00:00 2001 From: Simon Wisselink Date: Tue, 22 Nov 2022 00:47:03 +0100 Subject: [PATCH 8/8] Fix htmlall unescape of quotes without mbstring too --- libs/plugins/modifiercompiler.unescape.php | 2 +- .../PluginModifier/PluginModifierUnescapeTest.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/plugins/modifiercompiler.unescape.php b/libs/plugins/modifiercompiler.unescape.php index 9e67c75c8..9e1f06d67 100644 --- a/libs/plugins/modifiercompiler.unescape.php +++ b/libs/plugins/modifiercompiler.unescape.php @@ -41,7 +41,7 @@ function smarty_modifiercompiler_unescape($params, Smarty_Internal_TemplateCompi if (Smarty::$_MBSTRING) { 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': return 'htmlspecialchars_decode(' . $params[ 0 ] . ', ENT_QUOTES)'; case 'url': diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierUnescapeTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierUnescapeTest.php index e3e4ac6b2..2fdefb2eb 100644 --- a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierUnescapeTest.php +++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierUnescapeTest.php @@ -40,8 +40,8 @@ public function testHtmlWithoutMbstring() public function testHtmlall() { - $encoded = "aäЗдра><&amp;ääвсствуйте"; - $result = "aäЗдра><&ääвсствуйте"; + $encoded = "'"aäЗдра><&amp;ääвсствуйте"; + $result = "'\"aäЗдра><&ääвсствуйте"; $tpl = $this->smarty->createTemplate('string:{"' . $encoded . '"|unescape:"htmlall"}'); $this->assertEquals($result, $this->smarty->fetch($tpl)); } @@ -49,8 +49,8 @@ public function testHtmlall() public function testHtmlallWithoutMbstring() { Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb'); - $encoded = "aäЗдра><&amp;ääвсствуйте"; - $result = "aäЗдра><&ääвсствуйте"; + $encoded = "'"aäЗдра><&amp;ääвсствуйте"; + $result = "'\"aäЗдра><&ääвсствуйте"; $tpl = $this->smarty->createTemplate('string:{"' . $encoded . '"|unescape:"htmlall"}'); $this->assertEquals($result, $this->smarty->fetch($tpl)); Smarty::$_MBSTRING = true;