Completed
Push — master ( 9bba17...b2b071 )
by Colin
09:14 queued 05:11
created

Element   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 248
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 94.23%

Importance

Changes 4
Bugs 0 Features 1
Metric Value
wmc 55
c 4
b 0
f 1
lcom 1
cbo 0
dl 0
loc 248
ccs 98
cts 104
cp 0.9423
rs 6.8

18 Methods

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

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