diff --git a/src/Operation/StringOperation/TruncateHtml.php b/src/Operation/StringOperation/TruncateHtml.php index bcfe8fd..388c90b 100644 --- a/src/Operation/StringOperation/TruncateHtml.php +++ b/src/Operation/StringOperation/TruncateHtml.php @@ -20,7 +20,9 @@ class TruncateHtml implements TruncateHtmlInterface private $truncateOperationMethod = 'truncate'; private $truncateOperationMethods = [ - self::TRUNCATE_HTML_LETTER, self::TRUNCATE_HTML_WORD, self::TRUNCATE_HTML_SENTENCE + self::TRUNCATE_HTML_LETTER, + self::TRUNCATE_HTML_WORD, + self::TRUNCATE_HTML_SENTENCE, ]; private $encoding = 'utf-8'; @@ -37,6 +39,14 @@ class TruncateHtml implements TruncateHtmlInterface */ private $countSentencesOperation; + private $openTokens = []; + + private $totalCount = 0; + + private $depth = 0; + + private $truncated = []; + public function __construct( TruncateInterface $truncateOperation, TruncateWordsInterface $truncateWordsOperation, @@ -53,48 +63,61 @@ public function __construct( public function execute(string $value, int $length, string $suffix = '...'): string { + $this->resetVariables(); + $config = \HTMLPurifier_Config::create(null); $lexer = \HTMLPurifier_Lexer::create($config); $tokens = $lexer->tokenizeHTML($value, $config, new \HTMLPurifier_Context()); - $openTokens = []; - $totalCount = 0; - $depth = 0; - $truncated = []; + + $this->processTokens($tokens, $length, $suffix); + + $context = new \HTMLPurifier_Context(); + $generator = new \HTMLPurifier_Generator($config, $context); + + return $generator->generateFromTokens($this->truncated).($this->totalCount >= $length ? $suffix : ''); + } + + private function resetVariables(): void + { + $this->openTokens = []; + $this->depth = 0; + $this->totalCount = 0; + $this->truncated = []; + } + + private function processTokens(array $tokens, int $length, string $suffix): void + { foreach ($tokens as $token) { - if ($token instanceof \HTMLPurifier_Token_Start) { - $openTokens[$depth] = $token->name; - $truncated[] = $token; - ++$depth; - } elseif ($token instanceof \HTMLPurifier_Token_Text && $totalCount <= $length) { - - $currentLength = $this->truncateByMethod($token->data, $length - $totalCount, $suffix); - - $totalCount += $currentLength; - $truncated[] = $token; - } elseif ($token instanceof \HTMLPurifier_Token_End) { - if ($token->name === $openTokens[$depth - 1]) { - --$depth; - unset($openTokens[$depth]); - $truncated[] = $token; - } - } elseif ($token instanceof \HTMLPurifier_Token_Empty) { - $truncated[] = $token; - } - if ($totalCount >= $length) { - if (0 < \count($openTokens)) { - \krsort($openTokens); - foreach ($openTokens as $name) { - $truncated[] = new \HTMLPurifier_Token_End($name); - } - } + + $this->processTokenStart($token); + $this->processTokenText($token, $length, $suffix); + $this->processTokenEnd($token); + $this->processTokenEmpty($token); + + if ($this->totalCount >= $length) { + $this->processOpenTokens(); break; } } - $context = new \HTMLPurifier_Context(); - $generator = new \HTMLPurifier_Generator($config, $context); + } + + private function processTokenStart($token): void + { + if ($token instanceof \HTMLPurifier_Token_Start) { + $this->openTokens[$this->depth] = $token->name; + $this->truncated[] = $token; + ++$this->depth; + } + } - return $generator->generateFromTokens($truncated).($totalCount >= $length ? $suffix : ''); + private function processTokenText($token, int $length, string $suffix): void + { + if ($token instanceof \HTMLPurifier_Token_Text && $this->totalCount <= $length) { + $currentLength = $this->truncateByMethod($token->data, $length - $this->totalCount, $suffix); + $this->totalCount += $currentLength; + $this->truncated[] = $token; + } } private function truncateByMethod(string &$value, int $length, string $suffix): int @@ -102,24 +125,32 @@ private function truncateByMethod(string &$value, int $length, string $suffix): return $this->{$this->truncateOperationMethod}($value, $length, $suffix); } - private function truncate(string &$value, int $length, string $suffix): int + private function processTokenEnd($token): void { - $value = $this->truncateOperation->execute($value, $length, $suffix); - - return \mb_strlen($value, $this->encoding); + if ($token instanceof \HTMLPurifier_Token_End) { + if ($token->name === $this->openTokens[$this->depth - 1]) { + --$this->depth; + unset($this->openTokens[$this->depth]); + $this->truncated[] = $token; + } + } } - private function truncateWords(string &$value, int $count, string $suffix): int + private function processTokenEmpty($token) { - $value = $this->truncateWordsOperation->execute($value, $count, $suffix); - - return $this->countWordsOperation->execute($value); + if ($token instanceof \HTMLPurifier_Token_Empty) { + $this->truncated[] = $token; + } } - private function truncateSentences(string &$value, int $count, string $suffix): int + private function processOpenTokens(): void { - $value = $this->truncateSentencesOperation->execute($value, $count, $suffix); - return $this->countSentencesOperation->execute($value); + if (0 < \count($this->openTokens)) { + \krsort($this->openTokens); + foreach ($this->openTokens as $name) { + $this->truncated[] = new \HTMLPurifier_Token_End($name); + } + } } /** @@ -130,11 +161,33 @@ private function truncateSentences(string &$value, int $count, string $suffix): public function setTruncateOperationMethod(string $truncateOperationMethod): void { if (!in_array($truncateOperationMethod, $this->truncateOperationMethods)) { - throw new \InvalidArgumentException(\sprintf('`%s` truncate operation method is not supported.', $truncateOperationMethod)); + throw new \InvalidArgumentException(\sprintf('`%s` truncate operation method is not supported.', + $truncateOperationMethod)); } $this->truncateOperationMethod = $truncateOperationMethod; } + private function truncate(string &$value, int $length, string $suffix): int + { + $value = $this->truncateOperation->execute($value, $length, $suffix); + + return \mb_strlen($value, $this->encoding); + } + + private function truncateWords(string &$value, int $count, string $suffix): int + { + $value = $this->truncateWordsOperation->execute($value, $count, $suffix); + + return $this->countWordsOperation->execute($value); + } + + private function truncateSentences(string &$value, int $count, string $suffix): int + { + $value = $this->truncateSentencesOperation->execute($value, $count, $suffix); + + return $this->countSentencesOperation->execute($value); + } + }