Parser   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 4
dl 0
loc 112
ccs 0
cts 56
cp 0
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A parse() 0 10 2
A removeUnusedTags() 0 4 1
A createRoot() 0 9 1
A transformXml() 0 11 3
A transformChildren() 0 15 4
A parseUser() 0 14 2
A parseCode() 0 6 2
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\Ast\CodeNode;
13
use Karma\Platform\Ast\NodeInterface;
14
use Karma\Platform\Ast\NodeList;
15
use Karma\Platform\Ast\TextNode;
16
use Karma\Platform\Ast\UserNode;
17
use Karma\Platform\Io\UserInterface;
18
use Karma\Platform\Ast\Transformer\ParserInterface;
19
20
/**
21
 * Class Parser
22
 * @package Karma\System\Gitter\Message
23
 */
24
class Parser implements ParserInterface
25
{
26
    /**
27
     * @var array|UserInterface[]
28
     */
29
    private $mentions = [];
30
31
    /**
32
     * @param string $html
33
     * @param array|UserInterface[] $mentions
34
     * @return NodeList
35
     */
36
    public function parse(string $html, array $mentions): NodeList
37
    {
38
        foreach ($mentions as $mention) {
39
            $this->mentions[$mention->getName()] = $mention;
40
        }
41
42
        $html = $this->removeUnusedTags($html);
43
44
        return new NodeList($this->transformXml($this->createRoot($html)));
0 ignored issues
show
Documentation introduced by
$this->transformXml($this->createRoot($html)) is of type object<Generator>, but the function expects a object<Karma\Platform\Ast\iterable>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
45
    }
46
47
    /**
48
     * @param string $html
49
     * @return string
50
     */
51
    private function removeUnusedTags(string $html): string
52
    {
53
        return strip_tags($html, '<code><span>');
54
    }
55
56
    /**
57
     * @param string $html
58
     * @return \DOMNode
59
     */
60
    private function createRoot(string $html): \DOMNode
61
    {
62
        $html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');
63
64
        $dom = new \DOMDocument('1.0', 'UTF-8');
65
        $dom->loadHTML('<html>' . $html . '</html>', LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
66
67
        return $dom;
68
    }
69
70
    /**
71
     * @param \DOMNode $root
72
     * @return \Generator
73
     */
74
    private function transformXml(\DOMNode $root): \Generator
75
    {
76
        if ($root->hasChildNodes()) {
77
            /** @var \DOMElement|\DOMText $child */
78
            foreach ($root->childNodes as $child) {
79
                yield from $this->transformChildren($child);
80
            }
81
        } else {
82
            yield new TextNode($root->textContent);
83
        }
84
    }
85
86
    /**
87
     * @param \DOMElement|\DOMText $child
88
     * @return \Generator
89
     */
90
    private function transformChildren($child): \Generator
91
    {
92
        if ($child instanceof \DOMText) {
93
            yield new TextNode($child->textContent);
94
95
        } else if ($child->tagName === 'span') {
96
            yield $this->parseUser($child);
97
98
        } else if ($child->tagName === 'code') {
99
            yield $this->parseCode($child);
100
101
        } else {
102
            yield from $this->transformXml($child);
103
        }
104
    }
105
106
    /**
107
     * @param \DOMElement $user
108
     * @return UserNode|TextNode|NodeInterface
109
     */
110
    private function parseUser(\DOMElement $user): NodeInterface
111
    {
112
        $body = $user->textContent;
113
        $name = $user->getAttribute('data-screen-name');
114
115
        if (!isset($this->mentions[$name])) {
116
            return new TextNode($body);
117
        }
118
119
        /** @var UserInterface $mention */
120
        $mention = $this->mentions[$name];
121
122
        return UserNode::fromUserInterface($body, $mention);
123
    }
124
125
    /**
126
     * @param \DOMElement $code
127
     * @return CodeNode
128
     */
129
    private function parseCode(\DOMElement $code): CodeNode
130
    {
131
        $body = htmlspecialchars($code->textContent);
132
133
        return new CodeNode($body, $code->getAttribute('class') ?: null);
134
    }
135
}
136