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