Completed
Pull Request — master (#48)
by Frederik
02:13
created

AlternativeText::isEmpty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail;
5
6
final class AlternativeText
7
{
8
    /**
9
     * @var string
10
     */
11
    private $text;
12
13
    /**
14
     * @param string $text
15
     */
16 19
    public function __construct(string $text)
17
    {
18 19
        $this->text = $text;
19 19
    }
20
21
    /**
22
     * @return bool
23
     */
24 8
    public function isEmpty(): bool
25
    {
26 8
        return $this->text === '';
27
    }
28
29
    /**
30
     * @return string
31
     */
32 12
    public function __toString(): string
33
    {
34 12
        return $this->normalizeSpace($this->text);
35
    }
36
37
    /**
38
     * @param string $string
39
     * @return string
40
     */
41 12
    private function normalizeSpace(string $string): string
42
    {
43 12
        return \wordwrap(
44 12
            \str_replace(
45 12
                ["  ", "\n ", " \n", "\t"],
46 12
                [" ", "\n", "\n", "    "],
47 12
                \trim($string)
48
            )
49
        );
50
    }
51
52
    /**
53
     * @param string $html AlternativeText
54
     * @return AlternativeText
55
     */
56 19
    public static function fromHtml(string $html): AlternativeText
57
    {
58 19
        if ($html === '') {
59 17
            return new self($html);
60
        }
61
62 8
        $html = \preg_replace('/\h\h+/', ' ', $html);
63 8
        $html = \preg_replace('/\v/', '', $html);
64 8
        $text = new self($html);
65
66
        try {
67 8
            $document = new \DOMDocument();
68 8
            $result = @$document->loadHTML($text->text);
69 8
            if ($result === false) {
70
                throw new \DOMException('Incorrect HTML');
71
            }
72
73 8
            $text->wrapSymbols($document);
74 8
            $text->updateHorizontalRule($document);
75 8
            $text->updateLists($document);
76 8
            $text->updateImages($document);
77 8
            $text->updateLinks($document);
78 8
            $text->removeHead($document);
79 8
            $text->updateParagraphsAndBreaksToNewLine($document);
80
81 8
            $text->text = $document->textContent;
82
        } catch (\DOMException $e) {
83
            $text->text = \strip_tags($text->text);
84
        }
85
86 8
        return $text;
87
    }
88
89
    /**
90
     * @param \DOMDocument $document
91
     */
92 8
    private function updateParagraphsAndBreaksToNewLine(\DOMDocument $document): void
93
    {
94 8
        $xpath = new \DOMXPath($document);
95
        $break = [
96 8
            'br' => "\n",
97
            'ul' => "\n",
98
            'ol' => "\n",
99
            'dl' => "\n",
100
        ];
101
102
        /** @var \DOMElement $element */
103 8
        foreach ($xpath->query('//p|//br|//h1|//h2|//h3|//h4|//h5|//h6|//ul|//ol|//dl|//hr') as $element) {
104 6
            if (isset($break[$element->nodeName])) {
105 1
                $textNode = $document->createTextNode($break[$element->nodeName]);
106
            } else {
107 6
                $textNode = $document->createTextNode("\n\n");
108
            }
109
110 6
            $element->appendChild($textNode);
111
        }
112 8
    }
113
114
    /**
115
     * @param \DOMDocument $document
116
     */
117 8
    private function wrapSymbols(\DOMDocument $document): void
118
    {
119 8
        $xpath = new \DOMXPath($document);
120
        $wrap = [
121 8
            'h1' => "*",
122
            'h2' => "**",
123
            'h3' => "***",
124
            'h4' => "****",
125
            'h5' => "*****",
126
            'h6' => "******",
127
            'strong' => "*",
128
            'b' => "*",
129
            'em' => "*",
130
            'i' => "*",
131
        ];
132
133
        /** @var \DOMElement $element */
134 8
        foreach ($xpath->query('//h1|//h2|//h3|//h4|//h5|//h6|//strong|//b|//em|//i') as $element) {
135 1
            $element->appendChild(
136 1
                $document->createTextNode($wrap[$element->nodeName])
137
            );
138
139 1
            if ($element->firstChild !== null) {
140 1
                $element->insertBefore(
141 1
                    $document->createTextNode($wrap[$element->nodeName]),
142 1
                    $element->firstChild
143
                );
144
            }
145
        }
146 8
    }
147
148
    /**
149
     * @param \DOMDocument $document
150
     */
151 8
    private function updateLists(\DOMDocument $document): void
152
    {
153 8
        $xpath = new \DOMXPath($document);
154
155
        /** @var \DOMElement $element */
156 8
        foreach ($xpath->query('//ul/li') as $element) {
157 1
            if ($element->firstChild !== null) {
158 1
                $element->insertBefore(
159 1
                    $document->createTextNode("\t- "),
160 1
                    $element->firstChild
161
                );
162
            } else {
163
                $element->appendChild(
164
                    $document->createTextNode("\t- ")
165
                );
166
            }
167
168 1
            $element->appendChild(
169 1
                $document->createTextNode("\n")
170
            );
171
        }
172
173
        /** @var \DOMElement $element */
174 8
        foreach ($xpath->query('//ol/li') as $element) {
175 1
            $itemPath = new \DOMXPath($document);
176 1
            $itemNumber = (int)$itemPath->evaluate('string(count(preceding-sibling::li))', $element) + 1;
177 1
            $text = \sprintf("\t%d. ", $itemNumber);
178
179 1
            if ($element->firstChild !== null) {
180 1
                $element->insertBefore(
181 1
                    $document->createTextNode($text),
182 1
                    $element->firstChild
183
                );
184
            } else {
185
                $element->appendChild(
186
                    $document->createTextNode($text)
187
                );
188
            }
189
190 1
            $element->appendChild(
191 1
                $document->createTextNode("\n")
192
            );
193
        }
194
195
        /** @var \DOMElement $element */
196 8
        foreach ($xpath->query('//dl/dt') as $element) {
197 1
            $element->appendChild(
198 1
                $document->createTextNode(': ')
199
            );
200
        }
201
202
        /** @var \DOMElement $element */
203 8
        foreach ($xpath->query('//dl/dd') as $element) {
204 1
            $element->appendChild(
205 1
                $document->createTextNode("\n")
206
            );
207
        }
208 8
    }
209
210
    /**
211
     * @param \DOMDocument $document
212
     */
213 8
    private function updateImages(\DOMDocument $document): void
214
    {
215 8
        $xpath = new \DOMXPath($document);
216
217
        /** @var \DOMElement $element */
218 8
        foreach ($xpath->query('//img[@src and @alt]') as $element) {
219 1
            $link = $document->createElement('a');
220 1
            $link->setAttribute('href', $element->getAttribute('src'));
221 1
            $link->textContent = $element->getAttribute('alt');
222 1
            $element->parentNode->replaceChild($link, $element);
223
        }
224 8
    }
225
226
    /**
227
     * @param \DOMDocument $document
228
     */
229 8
    private function updateHorizontalRule(\DOMDocument $document): void
230
    {
231 8
        $xpath = new \DOMXPath($document);
232
233
        /** @var \DOMElement $element */
234 8
        foreach ($xpath->query('//hr') as $element) {
235 1
            $element->textContent = \str_repeat('=', 78);
236
        }
237 8
    }
238
239
    /**
240
     * @param \DOMDocument $document
241
     */
242 8
    private function updateLinks(\DOMDocument $document): void
243
    {
244 8
        $xpath = new \DOMXPath($document);
245 8
        $item = 1;
246
        $conversion = [
247 8
            '0' => "\u{2070}",
248
            '1' => "\u{2071}",
249
            '2' => "\u{00B2}",
250
            '3' => "\u{00B3}",
251
            '4' => "\u{2074}",
252
            '5' => "\u{2075}",
253
            '6' => "\u{2076}",
254
            '7' => "\u{2077}",
255
            '8' => "\u{2078}",
256
            '9' => "\u{2079}",
257
        ];
258
259
        /** @var \DOMElement $element */
260 8
        foreach ($xpath->query('//a[@href and @href != .]') as $element) {
261 1
            $itemString = (string) $item;
262 1
            $itemUnicode = '';
263 1
            for ($i = 0, $j = \strlen($itemString); $i < $j; $i++) {
264 1
                $itemUnicode .= $conversion[$itemString[$i]];
265
            }
266
267 1
            $document->documentElement->appendChild(
268 1
                $document->createTextNode(
269 1
                    \sprintf(
270 1
                        "[%s] %s\n",
271 1
                        $itemUnicode,
272 1
                        $element->getAttribute('href')
273
                    )
274
                )
275
            );
276
277 1
            $element->appendChild(
278 1
                $document->createTextNode($itemUnicode)
279
            );
280
281 1
            $item++;
282
        }
283 8
    }
284
285
    /**
286
     * @param \DOMDocument $document
287
     */
288 8
    private function removeHead(\DOMDocument $document): void
289
    {
290 8
        $heads = $document->getElementsByTagName('head');
291 8
        while ($heads->length > 0) {
292 2
            $head = $heads->item(0);
293 2
            $head->parentNode->removeChild($head);
294
        }
295 8
    }
296
}
297