Completed
Push — master ( fafa6b...d42f0d )
by Frederik
03:02
created

AlternativeText::updateLinks()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 42
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 3.0013

Importance

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