Completed
Push — master ( 0868ae...a5d47a )
by Colin
03:35
created

Element   C

Complexity

Total Complexity 54

Size/Duplication

Total Lines 252
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 94.57%

Importance

Changes 0
Metric Value
dl 0
loc 252
c 0
b 0
f 0
wmc 54
lcom 1
cbo 0
ccs 87
cts 92
cp 0.9457
rs 6.4799

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A isWhitespace() 0 4 2
A getTagName() 0 4 1
A getValue() 0 4 1
A getParent() 0 4 2
A hasChildren() 0 4 1
A getNextNode() 0 14 5
A setFinalMarkdown() 0 5 1
A getChildrenAsString() 0 4 1
A getAttribute() 0 8 2
A equals() 0 8 2
C isBlock() 0 23 16
A getChildren() 0 10 2
A getNext() 0 11 3
A isDescendantOf() 0 18 5
A getSiblingPosition() 0 19 4
A getListItemLevel() 0 14 4
A isText() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Element often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Element, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace League\HTMLToMarkdown;
4
5
class Element implements ElementInterface
6
{
7
    /**
8
     * @var \DOMNode
9
     */
10
    protected $node;
11
12
    /**
13
     * @var ElementInterface|null
14
     */
15
    private $nextCached;
16
17 90
    public function __construct(\DOMNode $node)
18
    {
19 90
        $this->node = $node;
20 90
    }
21
22
    /**
23
     * @return bool
24
     */
25 15
    public function isBlock()
26
    {
27 15
        switch ($this->getTagName()) {
28 15
            case 'blockquote':
29 15
            case 'body':
30 15
            case 'div':
31 15
            case 'h1':
32 15
            case 'h2':
33 15
            case 'h3':
34 15
            case 'h4':
35 15
            case 'h5':
36 15
            case 'h6':
37 15
            case 'hr':
38 15
            case 'html':
39 15
            case 'li':
40 12
            case 'p':
41 12
            case 'ol':
42 12
            case 'ul':
43 3
                return true;
44
            default:
45 12
                return false;
46
        }
47
    }
48
49
    /**
50
     * @return bool
51
     */
52 3
    public function isText()
53
    {
54 3
        return $this->getTagName() === '#text';
55
    }
56
57
    /**
58
     * @return bool
59
     */
60 6
    public function isWhitespace()
61
    {
62 6
        return $this->getTagName() === '#text' && trim($this->getValue()) === '';
63
    }
64
65
    /**
66
     * @return string
67
     */
68 90
    public function getTagName()
69
    {
70 90
        return $this->node->nodeName;
71
    }
72
73
    /**
74
     * @return string
75
     */
76 78
    public function getValue()
77
    {
78 78
        return $this->node->nodeValue;
79
    }
80
81
    /**
82
     * @return ElementInterface|null
83
     */
84 75
    public function getParent()
85
    {
86 75
        return new static($this->node->parentNode) ?: null;
87
    }
88
89
    /**
90
     * @return bool
91
     */
92 90
    public function hasChildren()
93
    {
94 90
        return $this->node->hasChildNodes();
95
    }
96
97
    /**
98
     * @return ElementInterface[]
99
     */
100 90
    public function getChildren()
101
    {
102 90
        $ret = array();
103
        /** @var \DOMNode $node */
104 90
        foreach ($this->node->childNodes as $node) {
105 90
            $ret[] = new static($node);
106
        }
107
108 90
        return $ret;
109
    }
110
111
    /**
112
     * @return ElementInterface|null
113
     */
114 21
    public function getNext()
115
    {
116 21
        if ($this->nextCached === null) {
117 21
            $nextNode = $this->getNextNode($this->node);
118 21
            if ($nextNode !== null) {
119 21
                $this->nextCached = new static($nextNode);
120
            }
121
        }
122
123 21
        return $this->nextCached;
124
    }
125
126
    /**
127
     * @param \DomNode $node
128
     * @param bool $checkChildren
129
     *
130
     * @return \DomNode|null
131
     */
132 21
    private function getNextNode($node, $checkChildren = true)
133
    {
134 21
        if ($checkChildren && $node->firstChild) {
135
            return $node->firstChild;
136
        }
137
138 21
        if ($node->nextSibling) {
139 21
            return $node->nextSibling;
140
        }
141
142 6
        if ($node->parentNode) {
143 6
            return $this->getNextNode($node->parentNode, false);
144
        }
145
    }
146
147
    /**
148
     * @param string[]|string $tagNames
149
     *
150
     * @return bool
151
     */
152 90
    public function isDescendantOf($tagNames)
153
    {
154 90
        if (!is_array($tagNames)) {
155 3
            $tagNames = array($tagNames);
156
        }
157
158 90
        for ($p = $this->node->parentNode; $p !== false; $p = $p->parentNode) {
159 90
            if (is_null($p)) {
160 90
                return false;
161
            }
162
163 90
            if (in_array($p->nodeName, $tagNames)) {
164 24
                return true;
165
            }
166
        }
167
168
        return false;
169
    }
170
171
    /**
172
     * @param string $markdown
173
     */
174 90
    public function setFinalMarkdown($markdown)
175
    {
176 90
        $markdown_node = $this->node->ownerDocument->createTextNode($markdown);
177 90
        $this->node->parentNode->replaceChild($markdown_node, $this->node);
178 90
    }
179
180
    /**
181
     * @return string
182
     */
183 81
    public function getChildrenAsString()
184
    {
185 81
        return $this->node->C14N();
186
    }
187
188
    /**
189
     * @return int
190
     */
191 6
    public function getSiblingPosition()
192
    {
193 6
        $position = 0;
194
195
        // Loop through all nodes and find the given $node
196 6
        foreach ($this->getParent()->getChildren() as $current_node) {
197 6
            if (!$current_node->isWhitespace()) {
198 6
                $position++;
199
            }
200
201
            // TODO: Need a less-buggy way of comparing these
202
            // Perhaps we can somehow ensure that we always have the exact same object and use === instead?
203 6
            if ($this->equals($current_node)) {
204 6
                break;
205
            }
206
        }
207
208 6
        return $position;
209
    }
210
211
    /**
212
     * @return int
213
     */
214 9
    public function getListItemLevel()
215
    {
216 9
        $level = 0;
217 9
        $parent = $this->getParent();
218
219 9
        while ($parent !== null && $parent->node->parentNode) {
220 9
            if ($parent->getTagName() === 'li') {
221 3
                $level++;
222
            }
223 9
            $parent = $parent->getParent();
224
        }
225
226 9
        return $level;
227
    }
228
229
    /**
230
     * @param string $name
231
     *
232
     * @return string
233
     */
234 30
    public function getAttribute($name)
235
    {
236 30
        if ($this->node instanceof \DOMElement) {
237 30
            return $this->node->getAttribute($name);
238
        }
239
240
        return '';
241
    }
242
243
    /**
244
     * @param ElementInterface $element
245
     *
246
     * @return bool
247
     */
248 6
    public function equals(ElementInterface $element)
249
    {
250 6
        if ($element instanceof self) {
251 6
            return $element->node === $this->node;
252
        }
253
254
        return $element === $this;
255
    }
256
}
257