Completed
Push — master ( 8146b2...061ea8 )
by Ryan
03:02
created

Node::getBase()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
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
    public function __construct(?DOMNode $node = null, array $fallback = [])
69
    {
70
        if ($node) {
71
            $this->node  = $node;
72
            $this->value = $node->nodeValue;
73
74
            if (isset($fallback['base'])) {
75
                $this->base = $fallback['base']->nodeValue;
76
            }
77
78
            if (isset($fallback['lang'])) {
79
                $this->lang = $fallback['lang']->nodeValue;
80
            }
81
82
            if (XML_ELEMENT_NODE === $node->nodeType && $node->attributes->length > 0) {
83
                foreach ($node->attributes as $attribute) {
84
                    if ('xml:base' === $attribute->nodeName) {
85
                        $this->base = $attribute->nodeValue;
86
                    } elseif ('xml:lang' === $attribute->nodeName) {
87
                        $this->lang = $attribute->nodeValue;
88
                    } elseif ('src' === $attribute->name) {
89
                        $this->handleAsSource($attribute);
90
                    } elseif ('type' === $attribute->name && Serialization::TEXT === $attribute->value) {
91
                        $this->handleAsText($node, $attribute);
92
                    } elseif ('type' === $attribute->name && Serialization::HTML === $attribute->value) {
93
                        $this->handleAsHtml($node, $attribute);
94
                    } elseif ('type' === $attribute->name && Serialization::XHTML === $attribute->value) {
95
                        $this->handleAsXhtml($node, $attribute);
96
                    } elseif ('type' === $attribute->name && 'application/octet-stream' === $attribute->value) {
97
                        $this->handleAsBase64($node);
98
                    } else {
99
                        $this->serialization = Serialization::TEXT;
100
                        $this->value         = $node->nodeValue;
101
                    }
102
                }
103
            }
104
        }
105
    }
106
107
    // @phpcs:enable
108
109
    /**
110
     * Casting this Node element to a string with return the _value_ of the Node.
111
     *
112
     * @return string
113
     */
114
    public function __toString(): string
115
    {
116
        return $this->getValue() ?? '';
117
    }
118
119
    /**
120
     * Creates a new `Node` object from a string of text (such as from an XML attribute).
121
     *
122
     * @param string $value The string of text to convert to a `Node` object.
123
     *
124
     * @return Node
125
     */
126
    public static function factory(string $value): self
127
    {
128
        return new self(
129
            new DOMText($value)
130
        );
131
    }
132
133
    /**
134
     * Gets the raw `DOMNode` element.
135
     *
136
     * @return DOMNode|null
137
     */
138
    public function getNode(): ?DOMNode
139
    {
140
        return $this->node;
141
    }
142
143
    /**
144
     * Gets the content of the node, serialized appropriately.
145
     *
146
     * @return string|null
147
     */
148
    public function getValue(): ?string
149
    {
150
        return $this->value;
151
    }
152
153
    /**
154
     * Gets the serialization of the content.
155
     *
156
     * Will always be one of the enums from `SimplePie\Enum\Serialization`.
157
     *
158
     * @return string
159
     */
160
    public function getSerialization(): string
161
    {
162
        return $this->serialization;
163
    }
164
165
    /**
166
     * Gets the language of the content.
167
     *
168
     * @return string|null
169
     */
170
    public function getLang(): self
171
    {
172
        return self::factory($this->lang);
0 ignored issues
show
Bug introduced by
It seems like $this->lang can also be of type null; however, parameter $value of SimplePie\Type\Node::factory() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

172
        return self::factory(/** @scrutinizer ignore-type */ $this->lang);
Loading history...
173
    }
174
175
    /**
176
     * Gets the xml:base of the content.
177
     *
178
     * @return string|null
179
     */
180
    public function getBase(): self
181
    {
182
        return self::factory($this->base);
0 ignored issues
show
Bug introduced by
It seems like $this->base can also be of type null; however, parameter $value of SimplePie\Type\Node::factory() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

182
        return self::factory(/** @scrutinizer ignore-type */ $this->base);
Loading history...
183
    }
184
185
    /**
186
     * {@inheritdoc}
187
     */
188
    public function getAlias(string $nodeName): string
189
    {
190
        switch ($nodeName) {
191
            case 'language':
192
                return 'lang';
193
194
            default:
195
                return $nodeName;
196
        }
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202
    public function getHandler(string $nodeName, array $args = []): self
203
    {
204
        throw new SimplePieException(
205
            $this->getUnresolvableMessage($nodeName)
206
        );
207
    }
208
209
    /**
210
     * Handle the content as source.
211
     *
212
     * @param DOMAttr $attribute The DOMAttr element.
213
     */
214
    private function handleAsSource(DOMAttr $attribute): void
215
    {
216
        $this->serialization = Serialization::TEXT;
217
        $this->value         = $attribute->nodeValue;
218
    }
219
220
    /**
221
     * Handle the content as plain text.
222
     *
223
     * @param DOMNode $node      The DOMNode element.
224
     * @param DOMAttr $attribute The DOMAttr element.
225
     */
226
    private function handleAsText(DOMNode $node, DOMAttr $attribute): void
227
    {
228
        $this->serialization = $attribute->nodeValue;
229
        $this->value         = \html_entity_decode(
230
            $node->nodeValue,
231
            ENT_COMPAT,
232
            CharacterSet::UTF_8
233
        );
234
    }
235
236
    /**
237
     * Handle the content as HTML.
238
     *
239
     * @param DOMNode $node      The DOMNode element.
240
     * @param DOMAttr $attribute The DOMAttr element.
241
     */
242
    private function handleAsHtml(DOMNode $node, DOMAttr $attribute): void
243
    {
244
        $this->serialization = $attribute->nodeValue;
245
        $this->value         = \html_entity_decode(
246
            $node->nodeValue,
247
            ENT_COMPAT | ENT_HTML5,
248
            CharacterSet::UTF_8
249
        );
250
    }
251
252
    /**
253
     * Handle the content as XHTML.
254
     *
255
     * @param DOMNode $node      The DOMNode element.
256
     * @param DOMAttr $attribute The DOMAttr element.
257
     */
258
    private function handleAsXhtml(DOMNode $node, DOMAttr $attribute): void
259
    {
260
        $this->serialization = $attribute->nodeValue;
261
262
        // We can't just grab the content. We need to stringify it, then remove the wrapper element.
263
        $content = \preg_replace(
264
            '/^<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">(.*)<\/div>$/ims',
265
            '$1',
266
            $node->ownerDocument->saveXML(
267
                $node->getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'div')[0]
268
            )
269
        );
270
271
        $this->value = \trim($content);
272
    }
273
274
    /**
275
     * Handle the content as Base64-encoded text.
276
     *
277
     * @param DOMNode $node The DOMNode element.
278
     */
279
    private function handleAsBase64(DOMNode $node): void
280
    {
281
        $this->serialization = Serialization::TEXT;
282
        $this->value         = \base64_decode(\trim($node->nodeValue), true);
283
    }
284
}
285