diff --git a/plugins/block.textformat.php b/plugins/block.textformat.php deleted file mode 100644 index 398d66bc..00000000 --- a/plugins/block.textformat.php +++ /dev/null @@ -1,110 +0,0 @@ - - * @throws \Smarty\Exception - */ -function smarty_block_textformat($params, $content, Template $template, &$repeat) -{ - if (is_null($content)) { - return; - } - $style = null; - $indent = 0; - $indent_first = 0; - $indent_char = ' '; - $wrap = 80; - $wrap_char = "\n"; - $wrap_cut = false; - $assign = null; - foreach ($params as $_key => $_val) { - switch ($_key) { - case 'style': - case 'indent_char': - case 'wrap_char': - case 'assign': - $$_key = (string)$_val; - break; - case 'indent': - case 'indent_first': - case 'wrap': - $$_key = (int)$_val; - break; - case 'wrap_cut': - $$_key = (bool)$_val; - break; - default: - trigger_error("textformat: unknown attribute '{$_key}'"); - } - } - if ($style === 'email') { - $wrap = 72; - } - // split into paragraphs - $_paragraphs = preg_split('![\r\n]{2}!', $content); - foreach ($_paragraphs as &$_paragraph) { - if (!$_paragraph) { - continue; - } - // convert mult. spaces & special chars to single space - $_paragraph = - preg_replace( - array( - '!\s+!' . \Smarty::$_UTF8_MODIFIER, - '!(^\s+)|(\s+$)!' . \Smarty::$_UTF8_MODIFIER - ), - array( - ' ', - '' - ), - $_paragraph - ); - // indent first line - if ($indent_first > 0) { - $_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph; - } - // wordwrap sentences - $_paragraph = smarty_modifier_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); - // indent lines - if ($indent > 0) { - $_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph); - } - } - $_output = implode($wrap_char . $wrap_char, $_paragraphs); - if ($assign) { - $template->assign($assign, $_output); - } else { - return $_output; - } -} diff --git a/plugins/outputfilter.trimwhitespace.php b/plugins/outputfilter.trimwhitespace.php deleted file mode 100644 index 2f747ad5..00000000 --- a/plugins/outputfilter.trimwhitespace.php +++ /dev/null @@ -1,89 +0,0 @@ -.*?#is', - $source, - $matches, - PREG_OFFSET_CAPTURE | PREG_SET_ORDER - ) - ) { - foreach ($matches as $match) { - $store[] = $match[ 0 ][ 0 ]; - $_length = strlen($match[ 0 ][ 0 ]); - $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; - $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] - $_offset, $_length); - $_offset += $_length - strlen($replace); - $_store++; - } - } - // Strip all HTML-Comments - // yes, even the ones in ]*>)|(]*>)|(
]*>.*?]*>)#is', - $source, - $matches, - PREG_OFFSET_CAPTURE | PREG_SET_ORDER - ) - ) { - foreach ($matches as $match) { - $store[] = $match[ 0 ][ 0 ]; - $_length = strlen($match[ 0 ][ 0 ]); - $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; - $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] - $_offset, $_length); - $_offset += $_length - strlen($replace); - $_store++; - } - } - $expressions = array(// replace multiple spaces between tags by a single space - // can't remove them entirely, becaue that might break poorly implemented CSS display:inline-block elements - '#(:SMARTY@!@|>)\s+(?=@!@SMARTY:|<)#s' => '\1 \2', - // remove spaces between attributes (but not in attribute values!) - '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5', - // note: for some very weird reason trim() seems to remove spaces inside attributes. - // maybe a \0 byte or something is interfering? - '#^\s+<#Ss' => '<', - '#>\s+$#Ss' => '>', - ); - $source = preg_replace(array_keys($expressions), array_values($expressions), $source); - // note: for some very weird reason trim() seems to remove spaces inside attributes. - // maybe a \0 byte or something is interfering? - // $source = trim( $source ); - $_offset = 0; - if (preg_match_all('#@!@SMARTY:([0-9]+):SMARTY@!@#is', $source, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { - foreach ($matches as $match) { - $_length = strlen($match[ 0 ][ 0 ]); - $replace = $store[ $match[ 1 ][ 0 ] ]; - $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] + $_offset, $_length); - $_offset += strlen($replace) - $_length; - $_store++; - } - } - return $source; -} diff --git a/src/BlockHandler/BlockHandlerInterface.php b/src/BlockHandler/BlockHandlerInterface.php new file mode 100644 index 00000000..918a0bb6 --- /dev/null +++ b/src/BlockHandler/BlockHandlerInterface.php @@ -0,0 +1,9 @@ + + * @throws \Smarty\Exception + */ +class TextFormat implements BlockHandlerInterface { + + public function handle($params, $content, Template $template, &$repeat) { + if (is_null($content)) { + return; + } + $style = null; + $indent = 0; + $indent_first = 0; + $indent_char = ' '; + $wrap = 80; + $wrap_char = "\n"; + $wrap_cut = false; + $assign = null; + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'style': + case 'indent_char': + case 'wrap_char': + case 'assign': + $$_key = (string)$_val; + break; + case 'indent': + case 'indent_first': + case 'wrap': + $$_key = (int)$_val; + break; + case 'wrap_cut': + $$_key = (bool)$_val; + break; + default: + trigger_error("textformat: unknown attribute '{$_key}'"); + } + } + if ($style === 'email') { + $wrap = 72; + } + // split into paragraphs + $_paragraphs = preg_split('![\r\n]{2}!', $content); + foreach ($_paragraphs as &$_paragraph) { + if (!$_paragraph) { + continue; + } + // convert mult. spaces & special chars to single space + $_paragraph = + preg_replace( + array( + '!\s+!' . \Smarty::$_UTF8_MODIFIER, + '!(^\s+)|(\s+$)!' . \Smarty::$_UTF8_MODIFIER + ), + array( + ' ', + '' + ), + $_paragraph + ); + // indent first line + if ($indent_first > 0) { + $_paragraph = str_repeat($indent_char, $indent_first) . $_paragraph; + } + // wordwrap sentences + $_paragraph = smarty_mb_wordwrap($_paragraph, $wrap - $indent, $wrap_char, $wrap_cut); + // indent lines + if ($indent > 0) { + $_paragraph = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraph); + } + } + $_output = implode($wrap_char . $wrap_char, $_paragraphs); + if ($assign) { + $template->assign($assign, $_output); + } else { + return $_output; + } + } + +} \ No newline at end of file diff --git a/src/Compile/Modifier/WordWrapModifierCompiler.php b/src/Compile/Modifier/WordWrapModifierCompiler.php index a20f2da3..4a6d84b0 100644 --- a/src/Compile/Modifier/WordWrapModifierCompiler.php +++ b/src/Compile/Modifier/WordWrapModifierCompiler.php @@ -22,8 +22,7 @@ class WordWrapModifierCompiler extends Base { if (!isset($params[ 3 ])) { $params[ 3 ] = 'false'; } - $function = $compiler->getPlugin('mb_wordwrap', 'modifier'); - return $function . '(' . $params[ 0 ] . ',' . $params[ 1 ] . ',' . $params[ 2 ] . ',' . $params[ 3 ] . ')'; + return 'smarty_mb_wordwrap(' . $params[ 0 ] . ',' . $params[ 1 ] . ',' . $params[ 2 ] . ',' . $params[ 3 ] . ')'; } } \ No newline at end of file diff --git a/src/Extension/Base.php b/src/Extension/Base.php index 9265b68b..b5fa73af 100644 --- a/src/Extension/Base.php +++ b/src/Extension/Base.php @@ -18,7 +18,16 @@ class Base implements ExtensionInterface { return null; } + public function getBlockHandler(string $functionName): ?\Smarty\FunctionHandler\BlockHandlerInterface { + return null; + } + public function getModifierCallback(string $modifierName) { return null; } + + public function getOutputFilters(): array { + return []; + } + } \ No newline at end of file diff --git a/src/Extension/Core.php b/src/Extension/Core.php index a6f2978e..aaffb717 100644 --- a/src/Extension/Core.php +++ b/src/Extension/Core.php @@ -124,6 +124,19 @@ class Core extends Base { return null; } + public function getBlockHandler(string $blockTagName): ?\Smarty\FunctionHandler\BlockHandlerInterface { + switch ($blockTagName) { + case 'textformat': return new \Smarty\BlockHandler\TextFormat(); + } + return null; + } + + public function getOutputFilters(): array { + return [ + new \Smarty\Filter\Output\TrimWhitespace(), + ]; + } + /** * Smarty spacify modifier plugin * Type: modifier @@ -560,51 +573,7 @@ class Core extends Base { */ private function smarty_modifier_mb_wordwrap($str, $width = 75, $break = "\n", $cut = false) { - // break words into tokens using white space as a delimiter - $tokens = preg_split('!(\s)!S' . \Smarty\Smarty::$_UTF8_MODIFIER, $str, -1, PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE); - $length = 0; - $t = ''; - $_previous = false; - $_space = false; - foreach ($tokens as $_token) { - $token_length = mb_strlen($_token, \Smarty\Smarty::$_CHARSET); - $_tokens = array($_token); - if ($token_length > $width) { - if ($cut) { - $_tokens = preg_split( - '!(.{' . $width . '})!S' . \Smarty\Smarty::$_UTF8_MODIFIER, - $_token, - -1, - PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE - ); - } - } - foreach ($_tokens as $token) { - $_space = !!preg_match('!^\s$!S' . \Smarty\Smarty::$_UTF8_MODIFIER, $token); - $token_length = mb_strlen($token, \Smarty\Smarty::$_CHARSET); - $length += $token_length; - if ($length > $width) { - // remove space before inserted break - if ($_previous) { - $t = mb_substr($t, 0, -1, \Smarty\Smarty::$_CHARSET); - } - if (!$_space) { - // add the break before the token - if (!empty($t)) { - $t .= $break; - } - $length = $token_length; - } - } elseif ($token === "\n") { - // hard break must reset counters - $length = 0; - } - $_previous = $_space; - // add the token - $t .= $token; - } - } - return $t; + return smarty_mb_wordwrap($str, $width, $break, $cut); } /** diff --git a/src/Extension/ExtensionInterface.php b/src/Extension/ExtensionInterface.php index 4213c073..b8a65c57 100644 --- a/src/Extension/ExtensionInterface.php +++ b/src/Extension/ExtensionInterface.php @@ -10,6 +10,10 @@ interface ExtensionInterface { public function getFunctionHandler(string $functionName): ?\Smarty\FunctionHandler\FunctionHandlerInterface; + public function getBlockHandler(string $blockTagName): ?\Smarty\FunctionHandler\BlockHandlerInterface; + public function getModifierCallback(string $modifierName); + public function getOutputFilters(): array; + } \ No newline at end of file diff --git a/src/Filter/FilterInterface.php b/src/Filter/FilterInterface.php new file mode 100644 index 00000000..42280e25 --- /dev/null +++ b/src/Filter/FilterInterface.php @@ -0,0 +1,9 @@ +.*?#is', + $source, + $matches, + PREG_OFFSET_CAPTURE | PREG_SET_ORDER + ) + ) { + foreach ($matches as $match) { + $store[] = $match[ 0 ][ 0 ]; + $_length = strlen($match[ 0 ][ 0 ]); + $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; + $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] - $_offset, $_length); + $_offset += $_length - strlen($replace); + $_store++; + } + } + // Strip all HTML-Comments + // yes, even the ones in ]*>)|(]*>)|(
]*>.*?]*>)#is', + $source, + $matches, + PREG_OFFSET_CAPTURE | PREG_SET_ORDER + ) + ) { + foreach ($matches as $match) { + $store[] = $match[ 0 ][ 0 ]; + $_length = strlen($match[ 0 ][ 0 ]); + $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; + $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] - $_offset, $_length); + $_offset += $_length - strlen($replace); + $_store++; + } + } + $expressions = array(// replace multiple spaces between tags by a single space + // can't remove them entirely, becaue that might break poorly implemented CSS display:inline-block elements + '#(:SMARTY@!@|>)\s+(?=@!@SMARTY:|<)#s' => '\1 \2', + // remove spaces between attributes (but not in attribute values!) + '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5', + // note: for some very weird reason trim() seems to remove spaces inside attributes. + // maybe a \0 byte or something is interfering? + '#^\s+<#Ss' => '<', + '#>\s+$#Ss' => '>', + ); + $source = preg_replace(array_keys($expressions), array_values($expressions), $source); + // note: for some very weird reason trim() seems to remove spaces inside attributes. + // maybe a \0 byte or something is interfering? + // $source = trim( $source ); + $_offset = 0; + if (preg_match_all('#@!@SMARTY:([0-9]+):SMARTY@!@#is', $source, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { + foreach ($matches as $match) { + $_length = strlen($match[ 0 ][ 0 ]); + $replace = $store[ $match[ 1 ][ 0 ] ]; + $source = substr_replace($source, $replace, $match[ 0 ][ 1 ] + $_offset, $_length); + $_offset += strlen($replace) - $_length; + $_store++; + } + } + return $source; + } + +} \ No newline at end of file diff --git a/src/functions.php b/src/functions.php index 1c0c975e..b647335b 100644 --- a/src/functions.php +++ b/src/functions.php @@ -187,3 +187,67 @@ function smarty_function_escape_special_chars($string) } return $string; } + +/** + * Smarty wordwrap supporting multibyte + * Name: smarty_mb_wordwrap + * Purpose: Wrap a string to a given number of characters + * + * @link https://php.net/manual/en/function.wordwrap.php for similarity + * + * @param string $str the string to wrap + * @param int $width the width of the output + * @param string $break the character used to break the line + * @param boolean $cut ignored parameter, just for the sake of + * + * @return string wrapped string + * @author Rodney Rehm + */ +function smarty_mb_wordwrap($str, $width = 75, $break = "\n", $cut = false) +{ + // break words into tokens using white space as a delimiter + $tokens = preg_split('!(\s)!S' . \Smarty\Smarty::$_UTF8_MODIFIER, $str, -1, PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE); + $length = 0; + $t = ''; + $_previous = false; + $_space = false; + foreach ($tokens as $_token) { + $token_length = mb_strlen($_token, \Smarty\Smarty::$_CHARSET); + $_tokens = array($_token); + if ($token_length > $width) { + if ($cut) { + $_tokens = preg_split( + '!(.{' . $width . '})!S' . \Smarty\Smarty::$_UTF8_MODIFIER, + $_token, + -1, + PREG_SPLIT_NO_EMPTY + PREG_SPLIT_DELIM_CAPTURE + ); + } + } + foreach ($_tokens as $token) { + $_space = !!preg_match('!^\s$!S' . \Smarty\Smarty::$_UTF8_MODIFIER, $token); + $token_length = mb_strlen($token, \Smarty\Smarty::$_CHARSET); + $length += $token_length; + if ($length > $width) { + // remove space before inserted break + if ($_previous) { + $t = mb_substr($t, 0, -1, \Smarty\Smarty::$_CHARSET); + } + if (!$_space) { + // add the break before the token + if (!empty($t)) { + $t .= $break; + } + $length = $token_length; + } + } elseif ($token === "\n") { + // hard break must reset counters + $length = 0; + } + $_previous = $_space; + // add the token + $t .= $token; + } + } + return $t; +}