Passed
Push — master ( ec953f...c33ef4 )
by Ryan
03:43
created

Node::overrideXmlAttrs()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 6
ccs 0
cts 3
cp 0
crap 12
rs 9.4285
c 0
b 0
f 0
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
     * @phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded
67
     */
68 484
    public function __construct(?DOMNode $node = null, array $fallback = [])
69
    {
70 484
        if ($node) {
71 484
            $this->node  = $node;
72 484
            $this->value = $node->nodeValue;
73
74 484
            // Set some default values
75 2
            $this->handleFallback($fallback);
76
77
            if (XML_ELEMENT_NODE === $node->nodeType && $node->attributes->length > 0) {
78 484
                foreach ($node->attributes as $attribute) {
79 64
                    if ('xml:base' === $attribute->nodeName) {
80
                        $this->base = $attribute->nodeValue;
81
                    } elseif ('xml:lang' === $attribute->nodeName) {
82 484
                        $this->lang = $attribute->nodeValue;
83 136
                    } elseif ('src' === $attribute->name) {
84 136
                        $this->handleAsSource($attribute);
85 41
                    } elseif ('type' === $attribute->name && Serialization::TEXT === $attribute->value) {
86 136
                        $this->handleAsText($node, $attribute);
87 65
                    } elseif ('type' === $attribute->name && Serialization::HTML === $attribute->value) {
88 128
                        $this->handleAsHtml($node, $attribute);
89 1
                    } elseif ('type' === $attribute->name && Serialization::XHTML === $attribute->value) {
90 127
                        $this->handleAsXhtml($node, $attribute);
91 11
                    } elseif ('type' === $attribute->name && 'application/octet-stream' === $attribute->value) {
92 116
                        $this->handleAsBase64($node);
93 50
                    } else {
94 105
                        $this->serialization = Serialization::TEXT;
95 25
                        $this->value         = $node->nodeValue;
96 80
                    }
97 12
                }
98
            }
99 68
        }
100 136
    }
101
102
    // @phpcs:enable
103
104
    /**
105 484
     * Casting this Node element to a string with return the _value_ of the Node.
106
     *
107
     * @return string
108
     */
109
    public function __toString(): string
110
    {
111
        return $this->getValue() ?? '';
112
    }
113
114 470
    /**
115
     * Creates a new `Node` object from a string of text (such as from an XML attribute).
116 470
     *
117
     * @param string $value The string of text to convert to a `Node` object.
118
     *
119
     * @return Node
120
     */
121
    public static function factory(string $value): self
122
    {
123
        return new self(
124
            new DOMText($value)
125
        );
126 38
    }
127
128 38
    /**
129 38
     * Gets the raw `DOMNode` element.
130
     *
131
     * @return DOMNode|null
132
     */
133
    public function getNode(): ?DOMNode
134
    {
135
        return $this->node;
136
    }
137
138 484
    /**
139
     * Gets the content of the node, serialized appropriately.
140 484
     *
141
     * @return string|null
142
     */
143
    public function getValue(): ?string
144
    {
145
        return $this->value;
146
    }
147
148 476
    /**
149
     * Gets the serialization of the content.
150 476
     *
151
     * Will always be one of the enums from `SimplePie\Enum\Serialization`.
152
     *
153
     * @return string
154
     */
155
    public function getSerialization(): string
156
    {
157
        return $this->serialization;
158
    }
159
160 374
    /**
161
     * Gets the language of the content.
162 374
     *
163
     * @return string|null
164
     */
165
    public function getLang(): self
166
    {
167
        return self::factory($this->lang ?? '');
168
    }
169
170 30
    /**
171
     * Gets the xml:base of the content.
172 30
     *
173
     * @return string|null
174
     */
175
    public function getBase(): self
176
    {
177
        return self::factory($this->base ?? '');
178
    }
179
180 3
    /**
181
     * {@inheritdoc}
182 3
     */
183
    public function getAlias(string $nodeName): string
184
    {
185
        switch ($nodeName) {
186
            case 'language':
187
                return 'lang';
188
189
            default:
190
                return $nodeName;
191
        }
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197
    public function getHandler(string $nodeName, array $args = []): self
198
    {
199
        throw new SimplePieException(
200
            $this->getUnresolvableMessage($nodeName)
201
        );
202
    }
203
204
    /**
205
     * Handle the default "fallback" state of certain properties.
206
     *
207
     * @param array $fallback An array of attributes for default XML attributes. The default value is an empty array.
208
     */
209
    private function handleFallback(array $fallback = []): void
210
    {
211
        if (isset($fallback['base'])) {
212
            $this->base = $fallback['base']->nodeValue;
213
        }
214 1
215
        if (isset($fallback['lang'])) {
216 1
            $this->lang = $fallback['lang']->nodeValue;
217 1
        }
218 1
    }
219
220
    /**
221
     * Handle the content as source.
222
     *
223
     * @param DOMAttr $attribute The DOMAttr element.
224
     */
225
    private function handleAsSource(DOMAttr $attribute): void
226 11
    {
227
        $this->serialization = Serialization::TEXT;
228 11
        $this->value         = $attribute->nodeValue;
229 11
    }
230 11
231 11
    /**
232 11
     * Handle the content as plain text.
233
     *
234 11
     * @param DOMNode $node      The DOMNode element.
235
     * @param DOMAttr $attribute The DOMAttr element.
236
     */
237
    private function handleAsText(DOMNode $node, DOMAttr $attribute): void
238
    {
239
        $this->serialization = $attribute->nodeValue;
240
        $this->value         = \html_entity_decode(
241
            $node->nodeValue,
242 50
            ENT_COMPAT,
243
            CharacterSet::UTF_8
244 50
        );
245 50
    }
246 50
247 50
    /**
248 50
     * Handle the content as HTML.
249
     *
250 50
     * @param DOMNode $node      The DOMNode element.
251
     * @param DOMAttr $attribute The DOMAttr element.
252
     */
253
    private function handleAsHtml(DOMNode $node, DOMAttr $attribute): void
254
    {
255
        $this->serialization = $attribute->nodeValue;
256
        $this->value         = \html_entity_decode(
257
            $node->nodeValue,
258 25
            ENT_COMPAT | ENT_HTML5,
259
            CharacterSet::UTF_8
260 25
        );
261
    }
262
263 25
    /**
264 25
     * Handle the content as XHTML.
265 25
     *
266 25
     * @param DOMNode $node      The DOMNode element.
267 25
     * @param DOMAttr $attribute The DOMAttr element.
268
     */
269
    private function handleAsXhtml(DOMNode $node, DOMAttr $attribute): void
270
    {
271 25
        $this->serialization = $attribute->nodeValue;
272 25
273
        // We can't just grab the content. We need to stringify it, then remove the wrapper element.
274
        $content = \preg_replace(
275
            '/^<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">(.*)<\/div>$/ims',
276
            '$1',
277
            $node->ownerDocument->saveXML(
278
                $node->getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'div')[0]
279 12
            )
280
        );
281 12
282 12
        $this->value = \trim($content);
283 12
    }
284
285
    /**
286
     * Handle the content as Base64-encoded text.
287
     *
288
     * @param DOMNode $node The DOMNode element.
289
     */
290
    private function handleAsBase64(DOMNode $node): void
291
    {
292
        $this->serialization = Serialization::TEXT;
293
        $this->value         = \base64_decode(\trim($node->nodeValue), true);
294
    }
295
}
296