Test Failed
Push — master ( 179aff...ec953f )
by Ryan
12:29
created

Node::handleTypes()   B

Complexity

Conditions 10
Paths 6

Size

Total Lines 15
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 16.4

Importance

Changes 0
Metric Value
cc 10
eloc 13
nc 6
nop 1
dl 0
loc 15
ccs 3
cts 5
cp 0.6
crap 16.4
rs 7.2765
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Copyright (c) 2017–2018 Ryan Parman <http://ryanparman.com>.
4
 * Copyright (c) 2017–2018 Contributors.
5
 *
6
 * http://opensource.org/licenses/Apache2.0
7
 */
8
declare(strict_types=1);
9
10
namespace SimplePie\Type;
11
12
use DOMAttr;
13
use DOMNode;
14
use DOMText;
15
use SimplePie\Enum\CharacterSet;
16
use SimplePie\Enum\Serialization;
17
use SimplePie\Exception\SimplePieException;
18
19
/**
20
 * A type model for a deep-level Node element.
21
 */
22
class Node extends AbstractType implements NodeInterface, TypeInterface
23
{
24
    /**
25
     * The raw `DOMNode` element.
26
     *
27
     * @var DOMNode|null
28
     */
29
    protected $node;
30
31
    /**
32
     * The content of the node, serialized appropriately.
33
     *
34
     * @var string|null
35
     */
36
    protected $value;
37
38
    /**
39
     * The language of the content.
40
     *
41
     * @var string|null
42
     */
43
    protected $lang;
44
45
    /**
46
     * The xml:base value of the content.
47
     *
48
     * @var string|null
49
     */
50
    protected $base;
51
52
    /**
53
     * The serialization of the content.
54
     *
55
     * @var string
56
     */
57
    protected $serialization = Serialization::TEXT;
58
59
    /**
60
     * Get the text node in multiple formats.
61
     *
62
     * @param DOMNode|null $node     A `DOMNode` element to read properties from.
63
     * @param array        $fallback An array of attributes for default XML attributes. The default value is an
64
     *                               empty array.
65
     */
66
    public function __construct(?DOMNode $node = null, array $fallback = [])
67
    {
68 484
        if ($node) {
69
            $this->node  = $node;
70 484
            $this->value = $node->nodeValue;
71 484
72 484
            // Set some default values
73
            $this->handleFallback($fallback);
74 484
75 2
            if (XML_ELEMENT_NODE === $node->nodeType && $node->attributes->length > 0) {
76
                foreach ($node->attributes as $attribute) {
77
                    $this->overrideXmlAttrs($attribute);
78 484
                    $this->handleTypes($attribute);
79 64
                }
80
            }
81
        }
82 484
    }
83 136
84 136
    /**
85 41
     * Casting this Node element to a string with return the _value_ of the Node.
86 136
     *
87 65
     * @return string
88 128
     */
89 1
    public function __toString(): string
90 127
    {
91 11
        return $this->getValue() ?? '';
92 116
    }
93 50
94 105
    /**
95 25
     * Creates a new `Node` object from a string of text (such as from an XML attribute).
96 80
     *
97 12
     * @param string $value The string of text to convert to a `Node` object.
98
     *
99 68
     * @return Node
100 136
     */
101
    public static function factory(string $value): self
102
    {
103
        return new self(
104
            new DOMText($value)
105 484
        );
106
    }
107
108
    /**
109
     * Gets the raw `DOMNode` element.
110
     *
111
     * @return DOMNode|null
112
     */
113
    public function getNode(): ?DOMNode
114 470
    {
115
        return $this->node;
116 470
    }
117
118
    /**
119
     * Gets the content of the node, serialized appropriately.
120
     *
121
     * @return string|null
122
     */
123
    public function getValue(): ?string
124
    {
125
        return $this->value;
126 38
    }
127
128 38
    /**
129 38
     * Gets the serialization of the content.
130
     *
131
     * Will always be one of the enums from `SimplePie\Enum\Serialization`.
132
     *
133
     * @return string
134
     */
135
    public function getSerialization(): string
136
    {
137
        return $this->serialization;
138 484
    }
139
140 484
    /**
141
     * Gets the language of the content.
142
     *
143
     * @return string|null
144
     */
145
    public function getLang(): self
146
    {
147
        return self::factory($this->lang ?? '');
148 476
    }
149
150 476
    /**
151
     * Gets the xml:base of the content.
152
     *
153
     * @return string|null
154
     */
155
    public function getBase(): self
156
    {
157
        return self::factory($this->base ?? '');
158
    }
159
160 374
    /**
161
     * {@inheritdoc}
162 374
     */
163
    public function getAlias(string $nodeName): string
164
    {
165
        switch ($nodeName) {
166
            case 'language':
167
                return 'lang';
168
169
            default:
170 30
                return $nodeName;
171
        }
172 30
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function getHandler(string $nodeName, array $args = []): self
178
    {
179
        throw new SimplePieException(
180 3
            $this->getUnresolvableMessage($nodeName)
181
        );
182 3
    }
183
184
    /**
185
     * Override the `xml:`-prefixed values inherited from the parent with local values.
186
     *
187
     * @param DOMAttr $attribute A `DOMAttr` element to read properties from.
188
     */
189
    private function overrideXmlAttrs(DOMAttr $attribute): void
190
    {
191
        if ('xml:base' === $attribute->nodeName) {
192
            $this->base = $attribute->nodeValue;
193
        } elseif ('xml:lang' === $attribute->nodeName) {
194
            $this->lang = $attribute->nodeValue;
195
        }
196
    }
197
198
    /**
199
     * Determine how a serialized node should be handled.
200
     *
201
     * @param DOMAttr $attribute A `DOMAttr` element to read properties from.
202
     */
203
    private function handleTypes(DOMAttr $attribute): void
204
    {
205
        if ('src' === $attribute->name) {
206
            $this->handleAsSource($attribute);
207
        } elseif ('type' === $attribute->name && Serialization::TEXT === $attribute->value) {
208
            $this->handleAsText($node, $attribute);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $node seems to be never defined.
Loading history...
209
        } elseif ('type' === $attribute->name && Serialization::HTML === $attribute->value) {
210
            $this->handleAsHtml($node, $attribute);
211
        } elseif ('type' === $attribute->name && Serialization::XHTML === $attribute->value) {
212
            $this->handleAsXhtml($node, $attribute);
213
        } elseif ('type' === $attribute->name && 'application/octet-stream' === $attribute->value) {
214 1
            $this->handleAsBase64($node);
215
        } else {
216 1
            $this->serialization = Serialization::TEXT;
217 1
            $this->value         = $node->nodeValue;
218 1
        }
219
    }
220
221
    /**
222
     * Handle the default "fallback" state of certain properties.
223
     *
224
     * @param array $fallback An array of attributes for default XML attributes. The default value is an empty array.
225
     */
226 11
    private function handleFallback(array $fallback = []): void
227
    {
228 11
        if (isset($fallback['base'])) {
229 11
            $this->base = $fallback['base']->nodeValue;
230 11
        }
231 11
232 11
        if (isset($fallback['lang'])) {
233
            $this->lang = $fallback['lang']->nodeValue;
234 11
        }
235
    }
236
237
    /**
238
     * Handle the content as source.
239
     *
240
     * @param DOMAttr $attribute The DOMAttr element.
241
     */
242 50
    private function handleAsSource(DOMAttr $attribute): void
243
    {
244 50
        $this->serialization = Serialization::TEXT;
245 50
        $this->value         = $attribute->nodeValue;
246 50
    }
247 50
248 50
    /**
249
     * Handle the content as plain text.
250 50
     *
251
     * @param DOMNode $node      The DOMNode element.
252
     * @param DOMAttr $attribute The DOMAttr element.
253
     */
254
    private function handleAsText(DOMNode $node, DOMAttr $attribute): void
255
    {
256
        $this->serialization = $attribute->nodeValue;
257
        $this->value         = \html_entity_decode(
258 25
            $node->nodeValue,
259
            ENT_COMPAT,
260 25
            CharacterSet::UTF_8
261
        );
262
    }
263 25
264 25
    /**
265 25
     * Handle the content as HTML.
266 25
     *
267 25
     * @param DOMNode $node      The DOMNode element.
268
     * @param DOMAttr $attribute The DOMAttr element.
269
     */
270
    private function handleAsHtml(DOMNode $node, DOMAttr $attribute): void
271 25
    {
272 25
        $this->serialization = $attribute->nodeValue;
273
        $this->value         = \html_entity_decode(
274
            $node->nodeValue,
275
            ENT_COMPAT | ENT_HTML5,
276
            CharacterSet::UTF_8
277
        );
278
    }
279 12
280
    /**
281 12
     * Handle the content as XHTML.
282 12
     *
283 12
     * @param DOMNode $node      The DOMNode element.
284
     * @param DOMAttr $attribute The DOMAttr element.
285
     */
286
    private function handleAsXhtml(DOMNode $node, DOMAttr $attribute): void
287
    {
288
        $this->serialization = $attribute->nodeValue;
289
290
        // We can't just grab the content. We need to stringify it, then remove the wrapper element.
291
        $content = \preg_replace(
292
            '/^<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">(.*)<\/div>$/ims',
293
            '$1',
294
            $node->ownerDocument->saveXML(
295
                $node->getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'div')[0]
296
            )
297
        );
298
299
        $this->value = \trim($content);
300
    }
301
302
    /**
303
     * Handle the content as Base64-encoded text.
304
     *
305
     * @param DOMNode $node The DOMNode element.
306
     */
307
    private function handleAsBase64(DOMNode $node): void
308
    {
309
        $this->serialization = Serialization::TEXT;
310
        $this->value         = \base64_decode(\trim($node->nodeValue), true);
311
    }
312
}
313