From d43fc158ee02f205f3753a8296c99f45e4239249 Mon Sep 17 00:00:00 2001 From: Simon Wisselink Date: Sun, 12 Apr 2020 01:19:07 +0200 Subject: [PATCH] Mark text sections as to be stripped, but do not strip them right away. Combine equivalent sections in chunks at the template level and strip combined chunks where possible. - simplified Smarty_Internal_TemplateCompilerBase::processText along the way Fixes !447 --- .../smarty_internal_parsetree_template.php | 116 ++++++++++++------ .../smarty_internal_parsetree_text.php | 29 ++++- .../smarty_internal_templatecompilerbase.php | 114 +++++++++-------- .../smarty_internal_templateparser.php | 9 +- 4 files changed, 163 insertions(+), 105 deletions(-) diff --git a/libs/sysplugins/smarty_internal_parsetree_template.php b/libs/sysplugins/smarty_internal_parsetree_template.php index 8e716bd5..477232ef 100644 --- a/libs/sysplugins/smarty_internal_parsetree_template.php +++ b/libs/sysplugins/smarty_internal_parsetree_template.php @@ -85,45 +85,85 @@ class Smarty_Internal_ParseTree_Template extends Smarty_Internal_ParseTree public function to_smarty_php(Smarty_Internal_Templateparser $parser) { $code = ''; - for ($key = 0, $cnt = count($this->subtrees); $key < $cnt; $key++) { - if ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Text) { - $subtree = $this->subtrees[ $key ]->to_smarty_php($parser); - while ($key + 1 < $cnt && ($this->subtrees[ $key + 1 ] instanceof Smarty_Internal_ParseTree_Text || - $this->subtrees[ $key + 1 ]->data === '')) { - $key++; - if ($this->subtrees[ $key ]->data === '') { - continue; - } - $subtree .= $this->subtrees[ $key ]->to_smarty_php($parser); - } - if ($subtree === '') { - continue; - } - $code .= preg_replace( - '/((<%)|(%>)|(<\?php)|(<\?)|(\?>)|(<\/?script))/', - "\n", - $subtree - ); - continue; - } - if ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Tag) { - $subtree = $this->subtrees[ $key ]->to_smarty_php($parser); - while ($key + 1 < $cnt && ($this->subtrees[ $key + 1 ] instanceof Smarty_Internal_ParseTree_Tag || - $this->subtrees[ $key + 1 ]->data === '')) { - $key++; - if ($this->subtrees[ $key ]->data === '') { - continue; - } - $subtree = $parser->compiler->appendCode($subtree, $this->subtrees[ $key ]->to_smarty_php($parser)); - } - if ($subtree === '') { - continue; - } - $code .= $subtree; - continue; - } - $code .= $this->subtrees[ $key ]->to_smarty_php($parser); + + foreach ($this->getChunkedSubtrees() as $chunk) { + $text = ''; + switch ($chunk['mode']) { + case 'textstripped': + foreach ($chunk['subtrees'] as $subtree) { + $text .= $subtree->to_smarty_php($parser); + } + $code .= preg_replace( + '/((<%)|(%>)|(<\?php)|(<\?)|(\?>)|(<\/?script))/', + "\n", + $parser->compiler->processText($text) + ); + break; + case 'text': + foreach ($chunk['subtrees'] as $subtree) { + $text .= $subtree->to_smarty_php($parser); + } + $code .= preg_replace( + '/((<%)|(%>)|(<\?php)|(<\?)|(\?>)|(<\/?script))/', + "\n", + $text + ); + break; + case 'tag': + foreach ($chunk['subtrees'] as $subtree) { + $text = $parser->compiler->appendCode($text, $subtree->to_smarty_php($parser)); + } + $code .= $text; + break; + default: + foreach ($chunk['subtrees'] as $subtree) { + $text = $subtree->to_smarty_php($parser); + } + $code .= $text; + + } } return $code; } + + private function getChunkedSubtrees() { + $chunks = []; + $currentMode = null; + $currentChunk = []; + for ($key = 0, $cnt = count($this->subtrees); $key < $cnt; $key++) { + + if ($this->subtrees[ $key ]->data === '' && in_array($currentMode, ['textstripped', 'text', 'tag'])) { + continue; + } + + if ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Text + && $this->subtrees[ $key ]->isToBeStripped()) { + $newMode = 'textstripped'; + } elseif ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Text) { + $newMode = 'text'; + } elseif ($this->subtrees[ $key ] instanceof Smarty_Internal_ParseTree_Tag) { + $newMode = 'tag'; + } else { + $newMode = 'other'; + } + + if ($newMode == $currentMode) { + $currentChunk[] = $this->subtrees[ $key ]; + } else { + $chunks[] = [ + 'mode' => $currentMode, + 'subtrees' => $currentChunk + ]; + $currentMode = $newMode; + $currentChunk = [$this->subtrees[ $key ]]; + } + } + if ($currentMode && $currentChunk) { + $chunks[] = [ + 'mode' => $currentMode, + 'subtrees' => $currentChunk + ]; + } + return $chunks; + } } diff --git a/libs/sysplugins/smarty_internal_parsetree_text.php b/libs/sysplugins/smarty_internal_parsetree_text.php index b3100fa0..399e8494 100644 --- a/libs/sysplugins/smarty_internal_parsetree_text.php +++ b/libs/sysplugins/smarty_internal_parsetree_text.php @@ -16,14 +16,31 @@ */ class Smarty_Internal_ParseTree_Text extends Smarty_Internal_ParseTree { - /** - * Create template text buffer - * - * @param string $data text - */ - public function __construct($data) + + /** + * Wether this section should be stripped on output to smarty php + * @var bool + */ + private $toBeStripped = false; + + /** + * Create template text buffer + * + * @param string $data text + * @param bool $toBeStripped wether this section should be stripped on output to smarty php + */ + public function __construct($data, $toBeStripped = false) { $this->data = $data; + $this->toBeStripped = $toBeStripped; + } + + /** + * Wether this section should be stripped on output to smarty php + * @return bool + */ + public function isToBeStripped() { + return $this->toBeStripped; } /** diff --git a/libs/sysplugins/smarty_internal_templatecompilerbase.php b/libs/sysplugins/smarty_internal_templatecompilerbase.php index a72f3a4a..bf50fa60 100644 --- a/libs/sysplugins/smarty_internal_templatecompilerbase.php +++ b/libs/sysplugins/smarty_internal_templatecompilerbase.php @@ -672,73 +672,69 @@ abstract class Smarty_Internal_TemplateCompilerBase } /** - * This method is called from parser to process a text content section + * This method is called from parser to process a text content section if strip is enabled * - remove text from inheritance child templates as they may generate output - * - strip text if strip is enabled * * @param string $text * - * @return null|\Smarty_Internal_ParseTree_Text + * @return string */ public function processText($text) { - if ((string)$text != '') { - $store = array(); - $_store = 0; - if ($this->parser->strip) { - if (strpos($text, '<') !== false) { - // capture html elements not to be messed with - $_offset = 0; - if (preg_match_all( - '#(]*>.*?]*>)|(]*>.*?]*>)|(]*>.*?]*>)#is', - $text, - $matches, - PREG_OFFSET_CAPTURE | PREG_SET_ORDER - ) - ) { - foreach ($matches as $match) { - $store[] = $match[ 0 ][ 0 ]; - $_length = strlen($match[ 0 ][ 0 ]); - $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; - $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length); - $_offset += $_length - strlen($replace); - $_store++; - } - } - $expressions = array(// replace multiple spaces between tags by a single space - '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s' => '\1 \2', - // remove newline between tags - '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s' => '\1\2', - // remove multiple spaces between attributes (but not in attribute values!) - '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5', - '#>[\040\011]+$#Ss' => '> ', - '#>[\040\011]*[\n]\s*$#Ss' => '>', - $this->stripRegEx => '', - ); - $text = preg_replace(array_keys($expressions), array_values($expressions), $text); - $_offset = 0; - if (preg_match_all( - '#@!@SMARTY:([0-9]+):SMARTY@!@#is', - $text, - $matches, - PREG_OFFSET_CAPTURE | PREG_SET_ORDER - ) - ) { - foreach ($matches as $match) { - $_length = strlen($match[ 0 ][ 0 ]); - $replace = $store[ $match[ 1 ][ 0 ] ]; - $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length); - $_offset += strlen($replace) - $_length; - $_store++; - } - } - } else { - $text = preg_replace($this->stripRegEx, '', $text); - } - } - return new Smarty_Internal_ParseTree_Text($text); + + if (strpos($text, '<') === false) { + return preg_replace($this->stripRegEx, '', $text); } - return null; + + $store = array(); + $_store = 0; + + // capture html elements not to be messed with + $_offset = 0; + if (preg_match_all( + '#(]*>.*?]*>)|(]*>.*?]*>)|(]*>.*?]*>)#is', + $text, + $matches, + PREG_OFFSET_CAPTURE | PREG_SET_ORDER + ) + ) { + foreach ($matches as $match) { + $store[] = $match[ 0 ][ 0 ]; + $_length = strlen($match[ 0 ][ 0 ]); + $replace = '@!@SMARTY:' . $_store . ':SMARTY@!@'; + $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] - $_offset, $_length); + $_offset += $_length - strlen($replace); + $_store++; + } + } + $expressions = array(// replace multiple spaces between tags by a single space + '#(:SMARTY@!@|>)[\040\011]+(?=@!@SMARTY:|<)#s' => '\1 \2', + // remove newline between tags + '#(:SMARTY@!@|>)[\040\011]*[\n]\s*(?=@!@SMARTY:|<)#s' => '\1\2', + // remove multiple spaces between attributes (but not in attribute values!) + '#(([a-z0-9]\s*=\s*("[^"]*?")|(\'[^\']*?\'))|<[a-z0-9_]+)\s+([a-z/>])#is' => '\1 \5', + '#>[\040\011]+$#Ss' => '> ', + '#>[\040\011]*[\n]\s*$#Ss' => '>', + $this->stripRegEx => '', + ); + $text = preg_replace(array_keys($expressions), array_values($expressions), $text); + $_offset = 0; + if (preg_match_all( + '#@!@SMARTY:([0-9]+):SMARTY@!@#is', + $text, + $matches, + PREG_OFFSET_CAPTURE | PREG_SET_ORDER + ) + ) { + foreach ($matches as $match) { + $_length = strlen($match[ 0 ][ 0 ]); + $replace = $store[ $match[ 1 ][ 0 ] ]; + $text = substr_replace($text, $replace, $match[ 0 ][ 1 ] + $_offset, $_length); + $_offset += strlen($replace) - $_length; + $_store++; + } + } + return $text; } /** diff --git a/libs/sysplugins/smarty_internal_templateparser.php b/libs/sysplugins/smarty_internal_templateparser.php index 80f9b884..aaeae63b 100644 --- a/libs/sysplugins/smarty_internal_templateparser.php +++ b/libs/sysplugins/smarty_internal_templateparser.php @@ -2169,8 +2169,13 @@ class Smarty_Internal_Templateparser // line 255 "../smarty/lexer/smarty_internal_templateparser.y" public function yy_r2() { - $this->current_buffer->append_subtree($this, - $this->compiler->processText($this->yystack[ $this->yyidx + 0 ]->minor)); + $text = $this->yystack[ $this->yyidx + 0 ]->minor; + + if ((string)$text == '') { + $this->current_buffer->append_subtree($this, null); + } + + $this->current_buffer->append_subtree($this, new Smarty_Internal_ParseTree_Text($text, $this->strip)); } // line 259 "../smarty/lexer/smarty_internal_templateparser.y"