Completed
Push — master ( cfa612...41c7dc )
by Colin
9s
created

Element   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 253
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 93.27%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 55
c 3
b 0
f 0
lcom 1
cbo 0
dl 0
loc 253
ccs 97
cts 104
cp 0.9327
rs 6.8

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
C isBlock() 0 24 17
A isText() 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 getChildren() 0 10 2
A getNext() 0 11 3
B isDescendantOf() 0 18 5
A setFinalMarkdown() 0 5 1
A getChildrenAsString() 0 4 1
A getSiblingPosition() 0 19 4
A getListItemLevel() 0 14 4
A getAttribute() 0 8 2
A equals() 0 8 2
B getNextNode() 0 14 5

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