TruncateHtml::execute()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 8
c 2
b 0
f 0
nc 2
nop 3
dl 0
loc 15
ccs 9
cts 9
cp 1
crap 2
rs 10
1
<?php
2
/**
3
 * This file is part of the sauls/helpers package.
4
 *
5
 * @author    Saulius Vaičeliūnas <[email protected]>
6
 * @link      http://saulius.vaiceliunas.lt
7
 * @copyright 2018
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Sauls\Component\Helper\Operation\StringOperation;
14
15
16
class TruncateHtml implements TruncateHtmlInterface
17
{
18
    private $truncateOperation;
19
20
    private $truncateOperationMethod = 'truncate';
21
22
    private $truncateOperationMethods = [
23
        self::TRUNCATE_HTML_LETTER,
24
        self::TRUNCATE_HTML_WORD,
25
        self::TRUNCATE_HTML_SENTENCE,
26
    ];
27
28
    private $encoding = 'utf-8';
29
30
    private $truncateWordsOperation;
31
32
    private $truncateSentencesOperation;
33
    /**
34
     * @var CountWordsInterface
35
     */
36
    private $countWordsOperation;
37
    /**
38
     * @var CountSentencesInterface
39
     */
40
    private $countSentencesOperation;
41
42
    private $openTokens = [];
43
44
    private $totalCount = 0;
45
46
    private $depth = 0;
47
48
    private $truncated = [];
49
50 1
    public function __construct(
51
        TruncateInterface $truncateOperation,
52
        TruncateWordsInterface $truncateWordsOperation,
53
        TruncateSentencesInterface $truncateSentencesOperation,
54
        CountWordsInterface $countWordsOperation,
55
        CountSentencesInterface $countSentencesOperation
56
    ) {
57 1
        $this->truncateOperation = $truncateOperation;
58 1
        $this->truncateWordsOperation = $truncateWordsOperation;
59 1
        $this->truncateSentencesOperation = $truncateSentencesOperation;
60 1
        $this->countWordsOperation = $countWordsOperation;
61 1
        $this->countSentencesOperation = $countSentencesOperation;
62 1
    }
63
64 5
    public function execute(string $value, int $length, string $suffix = '...'): string
65
    {
66 5
        $this->resetVariables();
67
68 5
        $config = \HTMLPurifier_Config::create(null);
69
70 5
        $lexer = \HTMLPurifier_Lexer::create($config);
71 5
        $tokens = $lexer->tokenizeHTML($value, $config, new \HTMLPurifier_Context());
72
73 5
        $this->processTokens($tokens, $length, $suffix);
74
75 5
        $context = new \HTMLPurifier_Context();
76 5
        $generator = new \HTMLPurifier_Generator($config, $context);
77
78 5
        return $generator->generateFromTokens($this->truncated).($this->totalCount >= $length ? $suffix : '');
79
    }
80
81 5
    private function resetVariables(): void
82
    {
83 5
        $this->openTokens = [];
84 5
        $this->depth = 0;
85 5
        $this->totalCount = 0;
86 5
        $this->truncated = [];
87 5
    }
88
89 5
    private function processTokens(array $tokens, int $length, string $suffix): void
90
    {
91 5
        foreach ($tokens as $token) {
92
93 5
            $this->processTokenStart($token);
94 5
            $this->processTokenText($token, $length, $suffix);
95 5
            $this->processTokenEnd($token);
96 5
            $this->processTokenEmpty($token);
97
98 5
            if ($this->totalCount >= $length) {
99 5
                $this->processOpenTokens();
100 5
                break;
101
            }
102
        }
103 5
    }
104
105 5
    private function processTokenStart($token): void
106
    {
107 5
        if ($token instanceof \HTMLPurifier_Token_Start) {
108 5
            $this->openTokens[$this->depth] = $token->name;
109 5
            $this->truncated[] = $token;
110 5
            ++$this->depth;
111
        }
112 5
    }
113
114 5
    private function processTokenText($token, int $length, string $suffix): void
115
    {
116 5
        if ($token instanceof \HTMLPurifier_Token_Text && $this->totalCount <= $length) {
117 5
            $currentLength = $this->truncateByMethod($token->data, $length - $this->totalCount, $suffix);
118 5
            $this->totalCount += $currentLength;
119 5
            $this->truncated[] = $token;
120
        }
121 5
    }
122
123 5
    private function truncateByMethod(string &$value, int $length, string $suffix): int
124
    {
125 5
        return $this->{$this->truncateOperationMethod}($value, $length, $suffix);
126
    }
127
128 5
    private function processTokenEnd($token): void
129
    {
130 5
        if ($token instanceof \HTMLPurifier_Token_End) {
131 3
            if ($token->name === $this->openTokens[$this->depth - 1]) {
132 3
                --$this->depth;
133 3
                unset($this->openTokens[$this->depth]);
134 3
                $this->truncated[] = $token;
135
            }
136
        }
137 5
    }
138
139 5
    private function processTokenEmpty($token)
140
    {
141 5
        if ($token instanceof \HTMLPurifier_Token_Empty) {
142 1
            $this->truncated[] = $token;
143
        }
144 5
    }
145
146 5
    private function processOpenTokens(): void
147
    {
148 5
        if (0 < \count($this->openTokens)) {
149 5
            \krsort($this->openTokens);
150 5
            foreach ($this->openTokens as $name) {
151 5
                $this->truncated[] = new \HTMLPurifier_Token_End($name);
152
            }
153
        }
154 5
    }
155
156
    /**
157
     * @param string $truncateOperationMethod
158
     *
159
     * @return TruncateHtml
160
     */
161 5
    public function setTruncateOperationMethod(string $truncateOperationMethod): void
162
    {
163 5
        if (!in_array($truncateOperationMethod, $this->truncateOperationMethods)) {
164 1
            throw new \InvalidArgumentException(\sprintf('`%s` truncate operation method is not supported.',
165 1
                $truncateOperationMethod));
166
        }
167
168 4
        $this->truncateOperationMethod = $truncateOperationMethod;
169 4
    }
170
171 1
    private function truncate(string &$value, int $length, string $suffix): int
172
    {
173 1
        $value = $this->truncateOperation->execute($value, $length, $suffix);
174
175 1
        return \mb_strlen($value, $this->encoding);
176
    }
177
178 1
    private function truncateWords(string &$value, int $count, string $suffix): int
179
    {
180 1
        $value = $this->truncateWordsOperation->execute($value, $count, $suffix);
181
182 1
        return $this->countWordsOperation->execute($value);
183
    }
184
185 3
    private function truncateSentences(string &$value, int $count, string $suffix): int
186
    {
187 3
        $value = $this->truncateSentencesOperation->execute($value, $count, $suffix);
188
189 3
        return $this->countSentencesOperation->execute($value);
190
    }
191
192
193
}
194