From b9ecf115dd4c1bd3675fc9c34118956604b7e696 Mon Sep 17 00:00:00 2001 From: Simon Wisselink Date: Mon, 2 Jan 2023 23:26:36 +0100 Subject: [PATCH] fixed pre/post/output filters and removed some old todo-comments --- src/Extension/BCPluginsAdapter.php | 77 +++++++++ src/Extension/DefaultExtension.php | 6 - src/Filter/FilterPluginWrapper.php | 15 ++ src/Filter/Output/TrimWhitespace.php | 1 - src/Smarty.php | 154 ++++++++++++++++++ src/Template.php | 2 +- src/Template/Cached.php | 1 - src/TemplateBase.php | 149 ----------------- tests/UnitTests/A_Core/Filter/FilterTest.php | 10 ++ .../AppendByRef/AppendByRefTest.php | 2 - 10 files changed, 257 insertions(+), 160 deletions(-) create mode 100644 src/Filter/FilterPluginWrapper.php diff --git a/src/Extension/BCPluginsAdapter.php b/src/Extension/BCPluginsAdapter.php index 95c9538f..1a46389e 100644 --- a/src/Extension/BCPluginsAdapter.php +++ b/src/Extension/BCPluginsAdapter.php @@ -5,6 +5,7 @@ namespace Smarty\Extension; use Smarty\BlockHandler\BlockPluginWrapper; use Smarty\Compile\Modifier\BCPluginWrapper as ModifierCompilerPluginWrapper; use Smarty\Compile\Tag\BCPluginWrapper as TagPluginWrapper; +use Smarty\Filter\FilterPluginWrapper; use Smarty\FunctionHandler\BCPluginWrapper as FunctionPluginWrapper; class BCPluginsAdapter extends Base { @@ -80,6 +81,82 @@ class BCPluginsAdapter extends Base { return new ModifierCompilerPluginWrapper($callback); } + /** + * @var array + */ + private $preFilters = []; + + public function getPreFilters(): array { + return $this->preFilters; + } + + public function addPreFilter(\Smarty\Filter\FilterInterface $filter) { + $this->preFilters[] = $filter; + } + + public function addCallableAsPreFilter(callable $callable, ?string $name = null) { + if ($name === null) { + $this->preFilters[] = new FilterPluginWrapper($callable); + } else { + $this->preFilters[$name] = new FilterPluginWrapper($callable); + } + } + + public function removePrefilter(string $name) { + unset($this->preFilters[$name]); + } + + /** + * @var array + */ + private $postFilters = []; + + public function getPostFilters(): array { + return $this->postFilters; + } + + public function addPostFilter(\Smarty\Filter\FilterInterface $filter) { + $this->postFilters[] = $filter; + } + + public function addCallableAsPostFilter(callable $callable, ?string $name = null) { + if ($name === null) { + $this->postFilters[] = new FilterPluginWrapper($callable); + } else { + $this->postFilters[$name] = new FilterPluginWrapper($callable); + } + } + + public function removePostFilter(string $name) { + unset($this->postFilters[$name]); + } + + + /** + * @var array + */ + private $outputFilters = []; + + public function getOutputFilters(): array { + return $this->outputFilters; + } + + public function addOutputFilter(\Smarty\Filter\FilterInterface $filter) { + $this->outputFilters[] = $filter; + } + + public function addCallableAsOutputFilter(callable $callable, ?string $name = null) { + if ($name === null) { + $this->outputFilters[] = new FilterPluginWrapper($callable); + } else { + $this->outputFilters[$name] = new FilterPluginWrapper($callable); + } + } + + public function removeOutputFilter(string $name) { + unset($this->outputFilters[$name]); + } + public function loadPluginsFromDir(string $path) { foreach([ diff --git a/src/Extension/DefaultExtension.php b/src/Extension/DefaultExtension.php index 5c83ba0b..f5fa2566 100644 --- a/src/Extension/DefaultExtension.php +++ b/src/Extension/DefaultExtension.php @@ -74,12 +74,6 @@ class DefaultExtension extends Base { return null; } - public function getOutputFilters(): array { - return [ - new \Smarty\Filter\Output\TrimWhitespace(), - ]; - } - /** * Smarty spacify modifier plugin * Type: modifier diff --git a/src/Filter/FilterPluginWrapper.php b/src/Filter/FilterPluginWrapper.php new file mode 100644 index 00000000..665ee385 --- /dev/null +++ b/src/Filter/FilterPluginWrapper.php @@ -0,0 +1,15 @@ +callback = $callback; + } + public function filter($code, \Smarty\Template $template) { + return call_user_func($this->callback, $code, $template); + } +} \ No newline at end of file diff --git a/src/Filter/Output/TrimWhitespace.php b/src/Filter/Output/TrimWhitespace.php index 83887ca7..199d097f 100644 --- a/src/Filter/Output/TrimWhitespace.php +++ b/src/Filter/Output/TrimWhitespace.php @@ -11,7 +11,6 @@ namespace Smarty\Filter\Output; * @param string $source input string * * @return string filtered output - * @todo substr_replace() is not overloaded by mbstring.func_overload - so this function might fail! */ class TrimWhitespace implements \Smarty\Filter\FilterInterface { diff --git a/src/Smarty.php b/src/Smarty.php index c5a28ec5..77bbef58 100644 --- a/src/Smarty.php +++ b/src/Smarty.php @@ -9,6 +9,7 @@ use Smarty\Extension\BCPluginsAdapter; use Smarty\Extension\CoreExtension; use Smarty\Extension\DefaultExtension; use Smarty\Extension\ExtensionInterface; +use Smarty\Filter\Output\TrimWhitespace; use Smarty\Smarty\Runtime\CaptureRuntime; use Smarty\Smarty\Runtime\ForeachRuntime; use Smarty\Smarty\Runtime\InheritanceRuntime; @@ -2113,4 +2114,157 @@ class Smarty extends \Smarty\TemplateBase return $this->default_plugin_handler_func; } + + /** + * load a filter of specified type and name + * + * @param string $type filter type + * @param string $name filter name + * + * @return bool + * @throws \Smarty\Exception + * @api Smarty::loadFilter() + * @link https://www.smarty.net/docs/en/api.load.filter.tpl + * + * @deprecated since 5.0 + */ + public function loadFilter($type, $name) { + + trigger_error('Using Smarty::loadFilter() to load filters is deprecated and will be ' . + 'removed in a future release. Use Smarty::addExtension() to add an extension or Smarty::registerFilter to ' . + 'quickly register a filter using a callback function.', E_USER_DEPRECATED); + + if ($type == 'output' && $name == 'trimwhitespace') { + $this->BCPluginsAdapter->addOutputFilter(new TrimWhitespace()); + return true; + } else { + $_plugin = "smarty_{$type}filter_{$name}"; + if (!is_callable($_plugin) && class_exists($_plugin, false)) { + $_plugin = [$_plugin, 'execute']; + } + } + + if (is_callable($_plugin)) { + $this->registerFilter($type, $_plugin, $name); + return true; + } + + throw new Exception("{$type}filter '{$name}' not found or callable"); + } + + /** + * load a filter of specified type and name + * + * @param string $type filter type + * @param string $name filter name + * + * @return TemplateBase + * @throws \Smarty\Exception + * @api Smarty::unloadFilter() + * + * @link https://www.smarty.net/docs/en/api.unload.filter.tpl + * + * @deprecated since 5.0 + */ + public function unloadFilter($type, $name) { + trigger_error('Using Smarty::unloadFilter() to unload filters is deprecated and will be ' . + 'removed in a future release. Use Smarty::addExtension() to add an extension or Smarty::(un)registerFilter to ' . + 'quickly (un)register a filter using a callback function.', E_USER_DEPRECATED); + + return $this->unregisterFilter($type, $name); + } + + /** + * Registers a filter function + * + * @param string $type filter type + * @param callable $callback + * @param string|null $name optional filter name + * + * @return TemplateBase + * @throws \Smarty\Exception + * @link https://www.smarty.net/docs/en/api.register.filter.tpl + * + * @api Smarty::registerFilter() + */ + public function registerFilter($type, $callback, $name = null) { + $name = $name ?? $this->_getFilterName($callback); + if (!is_callable($callback)) { + throw new Exception("{$type}filter '{$name}' not callable"); + } + switch ($type) { + case 'output': + $this->BCPluginsAdapter->addCallableAsOutputFilter($callback, $name); + break; + case 'pre': + $this->BCPluginsAdapter->addCallableAsPreFilter($callback, $name); + break; + case 'post': + $this->BCPluginsAdapter->addCallableAsPostFilter($callback, $name); + break; + default: + throw new Exception("Illegal filter type '{$type}'"); + } + + return $this; + } + + /** + * Return internal filter name + * + * @param callback $callable + * + * @return string|null internal filter name or null if callable cannot be serialized + */ + private function _getFilterName($callable) + { + if (is_array($callable)) { + $_class_name = is_object($callable[0]) ? get_class($callable[0]) : $callable[0]; + return $_class_name . '_' . $callable[1]; + } elseif (is_string($callable)) { + return $callable; + } + return null; + } + + /** + * Unregisters a filter function. Smarty cannot unregister closures/anonymous functions if + * no name was given in ::registerFilter. + * + * @param string $type filter type + * @param callback|string $name the name previously used in ::registerFilter + * + * @return TemplateBase + * @throws \Smarty\Exception + * @api Smarty::unregisterFilter() + * + * @link https://www.smarty.net/docs/en/api.unregister.filter.tpl + * + */ + public function unregisterFilter($type, $name) { + + if (!is_string($name)) { + $name = $this->_getFilterName($name); + } + + if ($name) { + switch ($type) { + case 'output': + $this->BCPluginsAdapter->removeOutputFilter($name); + break; + case 'pre': + $this->BCPluginsAdapter->removePreFilter($name); + break; + case 'post': + $this->BCPluginsAdapter->removePostFilter($name); + break; + default: + throw new Exception("Illegal filter type '{$type}'"); + } + } + + return $this; + } + } + diff --git a/src/Template.php b/src/Template.php index c3129e91..d1aa12d8 100644 --- a/src/Template.php +++ b/src/Template.php @@ -244,8 +244,8 @@ class Template extends TemplateBase { if ( !$no_output_filter && (!$this->caching || $this->cached->has_nocache_code || $this->source->handler->recompiled) - && isset($this->smarty->registered_filters['output']) ) { + return $this->smarty->runOutputFilters(ob_get_clean(), $this); } // return cache content diff --git a/src/Template/Cached.php b/src/Template/Cached.php index 869e2ea1..b8377e26 100644 --- a/src/Template/Cached.php +++ b/src/Template/Cached.php @@ -368,7 +368,6 @@ class Cached extends ResourceBase { if ( !$no_output_filter && !$_template->cached->has_nocache_code - && isset($_template->smarty->registered_filters['output']) ) { $content = $_template->smarty->runOutputFilters($content, $_template); } diff --git a/src/TemplateBase.php b/src/TemplateBase.php index 0813be7f..a3864aad 100644 --- a/src/TemplateBase.php +++ b/src/TemplateBase.php @@ -76,13 +76,6 @@ abstract class TemplateBase extends Data { */ public $_var_stack = null; - /** - * Valid filter types - * - * @var array - */ - private $filterTypes = ['pre' => true, 'post' => true, 'output' => true, 'variable' => true]; - /** * fetches a rendered Smarty template * @@ -252,117 +245,6 @@ abstract class TemplateBase extends Data { } } - /** - * load a filter of specified type and name - * - * @param string $type filter type - * @param string $name filter name - * - * @return bool - * @throws \Smarty\Exception - * @api Smarty::loadFilter() - * @link https://www.smarty.net/docs/en/api.load.filter.tpl - * - */ - public function loadFilter($type, $name) { - $smarty = $this->_getSmartyObj(); - $this->_checkFilterType($type); - $_plugin = "smarty_{$type}filter_{$name}"; - $_filter_name = $_plugin; - if (is_callable($_plugin)) { - $smarty->registered_filters[$type][$_filter_name] = $_plugin; - return true; - } - if (class_exists($_plugin, false)) { - $_plugin = [$_plugin, 'execute']; - } - if (is_callable($_plugin)) { - $smarty->registered_filters[$type][$_filter_name] = $_plugin; - return true; - } - throw new Exception("{$type}filter '{$name}' not found or callable"); - } - - /** - * load a filter of specified type and name - * - * @param string $type filter type - * @param string $name filter name - * - * @return TemplateBase - * @throws \Smarty\Exception - * @api Smarty::unloadFilter() - * - * @link https://www.smarty.net/docs/en/api.unload.filter.tpl - * - */ - public function unloadFilter($type, $name) { - $smarty = $this->_getSmartyObj(); - $this->_checkFilterType($type); - if (isset($smarty->registered_filters[$type])) { - $_filter_name = "smarty_{$type}filter_{$name}"; - if (isset($smarty->registered_filters[$type][$_filter_name])) { - unset($smarty->registered_filters[$type][$_filter_name]); - if (empty($smarty->registered_filters[$type])) { - unset($smarty->registered_filters[$type]); - } - } - } - return $this; - } - - /** - * Registers a filter function - * - * @param string $type filter type - * @param callable $callback - * @param string|null $name optional filter name - * - * @return TemplateBase - * @throws \Smarty\Exception - * @link https://www.smarty.net/docs/en/api.register.filter.tpl - * - * @api Smarty::registerFilter() - */ - public function registerFilter($type, $callback, $name = null) { - $smarty = $this->_getSmartyObj(); - $this->_checkFilterType($type); - $name = isset($name) ? $name : $this->_getFilterName($callback); - if (!is_callable($callback)) { - throw new Exception("{$type}filter '{$name}' not callable"); - } - $smarty->registered_filters[$type][$name] = $callback; - return $this; - } - - /** - * Unregisters a filter function - * - * @param string $type filter type - * @param callback|string $callback - * - * @return TemplateBase - * @throws \Smarty\Exception - * @api Smarty::unregisterFilter() - * - * @link https://www.smarty.net/docs/en/api.unregister.filter.tpl - * - */ - public function unregisterFilter($type, $callback) { - $smarty = $this->_getSmartyObj(); - $this->_checkFilterType($type); - if (isset($smarty->registered_filters[$type])) { - $name = is_string($callback) ? $callback : $this->_getFilterName($callback); - if (isset($smarty->registered_filters[$type][$name])) { - unset($smarty->registered_filters[$type][$name]); - if (empty($smarty->registered_filters[$type])) { - unset($smarty->registered_filters[$type]); - } - } - } - return $this; - } - /** * Registers object to be used in templates * @@ -622,37 +504,6 @@ abstract class TemplateBase extends Data { $smarty->literals = array_merge((array)$smarty->literals, (array)$literals); } - /** - * Check if filter type is valid - * - * @param string $type - * - * @throws \Smarty\Exception - */ - private function _checkFilterType($type) { - if (!isset($this->filterTypes[$type])) { - throw new Exception("Illegal filter type '{$type}'"); - } - } - - /** - * Return internal filter name - * - * @param callback $function_name - * - * @return string internal filter name - */ - private function _getFilterName($function_name) { - if (is_array($function_name)) { - $_class_name = (is_object($function_name[0]) ? get_class($function_name[0]) : $function_name[0]); - return $_class_name . '_' . $function_name[1]; - } elseif (is_string($function_name)) { - return $function_name; - } else { - return 'closure'; - } - } - /** * Registers static classes to be used in templates * diff --git a/tests/UnitTests/A_Core/Filter/FilterTest.php b/tests/UnitTests/A_Core/Filter/FilterTest.php index 89409073..1fc49dee 100644 --- a/tests/UnitTests/A_Core/Filter/FilterTest.php +++ b/tests/UnitTests/A_Core/Filter/FilterTest.php @@ -26,6 +26,15 @@ class FilterTest extends PHPUnit_Smarty $this->cleanDirs(); } + /** + * test loaded filter + */ + public function testNoOutputFilter() + { + $tpl = $this->smarty->createTemplate('string:{"
hello world"}'); + $this->assertEquals("
hello world", $this->smarty->fetch($tpl)); + } + /** * test loaded filter */ @@ -87,6 +96,7 @@ class FilterTest extends PHPUnit_Smarty $this->smarty->assign('bar', 6); $this->smarty->registerFilter(\Smarty\Smarty::FILTER_OUTPUT, 'myoutputfilter2'); $this->assertEquals('5 filter 6', $this->smarty->fetch('output_001.tpl')); + } /** diff --git a/tests/UnitTests/SmartyMethodsTests/AppendByRef/AppendByRefTest.php b/tests/UnitTests/SmartyMethodsTests/AppendByRef/AppendByRefTest.php index 0f6abe5e..e97bb8d7 100644 --- a/tests/UnitTests/SmartyMethodsTests/AppendByRef/AppendByRefTest.php +++ b/tests/UnitTests/SmartyMethodsTests/AppendByRef/AppendByRefTest.php @@ -47,8 +47,6 @@ class AppendByRefTest extends PHPUnit_Smarty /** * test appendByRef merge - * - * @todo fix testAppendByRefMerge */ public function testAppendByRefMerge() {