Completed
Push — master ( 772f65...48ab5c )
by Kirill
02:32
created

Parser::parseCode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
/**
3
 * This file is part of Platform package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Karma\System\Gitter\Message;
11
12
use Karma\Platform\Io\UserInterface;
13
use Karma\Platform\Transformer\ParserInterface;
14
15
/**
16
 * Class Parser
17
 * @package Karma\System\Gitter\Message
18
 */
19
class Parser implements ParserInterface
20
{
21
    /**
22
     * Inline code template
23
     */
24
    private const CODE_INLINE = '<code>%s</code>';
25
26
    /**
27
     * Block code template
28
     */
29
    private const CODE_BLOCK  = '<code language="%s">%s</code>';
30
31
    /**
32
     * User without avatar
33
     */
34
    private const USER_SHORT  = '<user id="%s" name="%s">%s</user>';
35
36
    /**
37
     * User with avatar
38
     */
39
    private const USER_FULL   = '<user id="%s" name="%s" avatar="%s">%s</user>';
40
41
    /**
42
     * @var array|UserInterface[]
43
     */
44
    private $mentions = [];
45
46
    /**
47
     * @param string $html
48
     * @param array|UserInterface[] $mentions
49
     * @return string
50
     */
51
    public function parse(string $html, array $mentions): string
52
    {
53
        foreach ($mentions as $mention) {
54
            $this->mentions[$mention->getName()] = $mention;
55
        }
56
57
        $html = $this->removeUnusedTags($html);
58
59
        $html = $this->transformXml($this->createRoot($html));
60
61
        return $html;
62
    }
63
64
    /**
65
     * @param string $html
66
     * @return string
67
     */
68
    private function removeUnusedTags(string $html): string
69
    {
70
        return strip_tags($html, '<code><span>');
71
    }
72
73
    /**
74
     * @param string $html
75
     * @return \DOMNode
76
     */
77
    private function createRoot(string $html): \DOMNode
78
    {
79
        $html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
80
81
        $dom = new \DOMDocument('1.0', 'UTF-8');
82
        $dom->loadHTML('<html>' . $html . '</html>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
83
84
        return $dom;
85
    }
86
87
    /**
88
     * @param \DOMNode $root
89
     * @return string
90
     */
91
    private function transformXml(\DOMNode $root): string
92
    {
93
        if ($root->hasChildNodes()) {
94
            $result = '';
95
96
            /** @var \DOMElement|\DOMText $child */
97
            foreach ($root->childNodes as $child) {
98
                $result .= $this->transformChildren($child);
99
            }
100
101
            return $result;
102
        }
103
104
        return $root->textContent;
105
    }
106
107
    /**
108
     * @param \DOMElement|\DOMText $child
109
     * @return string
110
     */
111
    private function transformChildren($child): string
112
    {
113
        if ($child instanceof \DOMText) {
114
            return $child->textContent;
115
        }
116
117
        if ($child->tagName === 'span') {
118
            return $this->parseUser($child);
119
        }
120
121
        if ($child->tagName === 'code') {
122
            return $this->parseCode($child);
123
        }
124
125
        return $this->transformXml($child);
126
    }
127
128
    /**
129
     * @param \DOMElement $user
130
     * @return string
131
     */
132
    private function parseUser(\DOMElement $user): string
133
    {
134
        $body = $user->textContent;
135
        $name = $user->getAttribute('data-screen-name');
136
137
        if (!isset($this->mentions[$name])) {
138
            return $body;
139
        }
140
141
        /** @var UserInterface $mention */
142
        $mention = $this->mentions[$name];
143
144
        return $mention->getAvatar()
145
            ? sprintf(self::USER_FULL, $mention->getId(), $mention->getName(), $mention->getAvatar(), $body)
146
            : sprintf(self::USER_SHORT, $mention->getId(), $mention->getName(), $body);
147
    }
148
149
    /**
150
     * @param \DOMElement $code
151
     * @return string
152
     */
153
    private function parseCode(\DOMElement $code): string
154
    {
155
        $body = $code->textContent;
156
157
        if ($class = $code->getAttribute('class')) {
158
            return sprintf(self::CODE_BLOCK, $class, htmlspecialchars($body));
159
        }
160
161
        return sprintf(self::CODE_INLINE, htmlspecialchars($body));
162
    }
163
}
164