Passed
Pull Request — master (#38)
by Ryan
25:32 queued 10:46
created

Atom::getSingleComplexTypes()   B

Complexity

Conditions 6
Paths 18

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 18
nc 18
nop 4
dl 0
loc 34
rs 8.439
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
9
declare(strict_types=1);
10
11
namespace SimplePie\Middleware\Xml;
12
13
use DOMXPath;
14
use ReflectionClass;
15
use SimplePie\Configuration as C;
16
use SimplePie\Mixin as Tr;
17
use SimplePie\Type as T;
18
use stdClass;
19
20
/**
21
 * Support for the Atom 1.0 grammar.
22
 *
23
 * @see https://tools.ietf.org/html/rfc4287
24
 * @see https://www.w3.org/wiki/Atom
25
 */
26
class Atom extends AbstractXmlMiddleware implements XmlInterface, C\SetLoggerInterface
27
{
28
    use Tr\LoggerTrait;
29
30
    /**
31
     * {@inheritdoc}
32
     */
33
    public function __invoke(stdClass $feedRoot, string $namespaceAlias, DOMXPath $xpath): void
34
    {
35
        $path = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $path is dead and can be removed.
Loading history...
36
37
        // lang (single, scalar)
38
        $this->addArrayProperty($feedRoot, 'lang');
39
        $xq = $xpath->query($this->applyNsToQuery('/%s:feed[attribute::xml:lang][1]/@xml:lang', $namespaceAlias));
40
41
        $feedRoot->lang[$namespaceAlias] = ($xq->length > 0)
42
            ? T\Node::factory((string) $xq->item(0)->nodeValue)
43
            : null;
44
45
        // Top-level feed
46
        $path = ['feed'];
47
        $this->getSingleScalarTypes($feedRoot, $namespaceAlias, $xpath, $path);
48
        $this->getSingleComplexTypes($feedRoot, $namespaceAlias, $xpath, $path);
49
        $this->getMultipleComplexTypes($feedRoot, $namespaceAlias, $xpath, $path);
50
51
        // <entry> element
52
        $path = ['feed', 'entry'];
53
54
        foreach ($feedRoot->entry[$namespaceAlias] as $i => &$entry) {
55
            $cpath   = $path;
56
            $cpath[] = $i;
57
58
            $this->getSingleScalarTypes($entry, $namespaceAlias, $xpath, $cpath);
59
            $this->getSingleComplexTypes($entry, $namespaceAlias, $xpath, $cpath);
60
            $this->getMultipleComplexTypes($entry, $namespaceAlias, $xpath, $cpath);
61
        }
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     *
67
     * Supports valid and invalid variations.
68
     *
69
     * * http://www.w3.org/2005/Atom
70
     * * http://www.w3.org/2005/Atom/
71
     * * https://www.w3.org/2005/Atom
72
     * * https://www.w3.org/2005/Atom/
73
     */
74
    public function getSupportedNamespaces(): array
75
    {
76
        return [
77
            'http://www.w3.org/2005/Atom'              => 'atom10',
78
            '/https?:\/\/www\.w3\.org\/2005\/Atom\/?/' => 'atom10',
79
        ];
80
    }
81
82
    /**
83
     * Fetches elements with a single, scalar value.
84
     *
85
     * @param stdClass $feedRoot       The root of the feed. This will be written-to when the parsing middleware runs.
86
     * @param string   $namespaceAlias The preferred namespace alias for a given XML namespace URI. Should be the result
87
     *                                 of a call to `SimplePie\Util\Ns`.
88
     * @param DOMXPath $xpath          The `DOMXPath` object with this middleware's namespace alias applied.
89
     * @param array    $path           The path of the XML traversal. Should begin with `<feed>` or `<channel>`,
90
     *                                 then `<entry>` or `<item>`.
91
     *
92
     * phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
93
     */
94
    protected function getSingleScalarTypes(
95
        object $feedRoot,
96
        string $namespaceAlias,
97
        DOMXPath $xpath,
98
        array $path
99
    ): void {
100
        // phpcs:enable
101
102
        $cpath = $path;
103
104
        if (\is_int(\end($cpath))) {
105
            \array_pop($cpath);
106
        }
107
108
        if ('feed' === \end($cpath)) {
109
            $nodes = [
110
                'icon',
111
                'id',
112
                'logo',
113
                'published',
114
                'rights',
115
                'subtitle',
116
                'summary',
117
                'title',
118
                'updated',
119
            ];
120
        } elseif ('entry' === \end($cpath)) {
121
            $nodes = [
122
                'content',
123
                'id',
124
                'published',
125
                'rights',
126
                'summary',
127
                'title',
128
                'updated',
129
            ];
130
        }
131
132
        // & atomContent?
133
134
        foreach ($nodes as $nodeName) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $nodes does not seem to be defined for all execution paths leading up to this point.
Loading history...
135
            $query = $this->generateQuery($namespaceAlias, \array_merge($path, [$nodeName]));
136
            $this->addArrayProperty($feedRoot, $nodeName);
137
            $this->getLogger()->debug(\sprintf('%s is running an XPath query:', __CLASS__), [$query]);
138
139
            $feedRoot->{$nodeName}[$namespaceAlias] = $this->handleSingleNode(
140
                static function () use ($xpath, $query) {
141
                    return $xpath->query($query);
142
                }
143
            );
144
        }
145
    }
146
147
    /**
148
     * Fetches elements with a single, complex value.
149
     *
150
     * @param stdClass $feedRoot       The root of the feed. This will be written-to when the parsing middleware runs.
151
     * @param string   $namespaceAlias The preferred namespace alias for a given XML namespace URI. Should be the result
152
     *                                 of a call to `SimplePie\Util\Ns`.
153
     * @param DOMXPath $xpath          The `DOMXPath` object with this middleware's namespace alias applied.
154
     * @param array    $path           The path of the XML traversal. Should begin with `<feed>` or `<channel>`,
155
     *                                 then `<entry>` or `<item>`.
156
     *
157
     * phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
158
     */
159
    protected function getSingleComplexTypes(
160
        object $feedRoot,
161
        string $namespaceAlias,
162
        DOMXPath $xpath,
163
        array $path
164
    ): void {
165
        // phpcs:enable
166
167
        $cpath = $path;
168
169
        if (\is_int(\end($cpath))) {
170
            \array_pop($cpath);
171
        }
172
173
        if ('feed' === \end($cpath)) {
174
            $nodes = [
175
                'author'    => T\Person::class,
176
                'generator' => T\Generator::class,
177
            ];
178
        } elseif ('entry' === \end($cpath)) {
179
            $nodes = [
180
                'author' => T\Person::class,
181
            ];
182
        }
183
184
        foreach ($nodes as $name => $class) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $nodes does not seem to be defined for all execution paths leading up to this point.
Loading history...
185
            $query = $this->generateQuery($namespaceAlias, \array_merge($path, [$name]));
186
            $xq    = $xpath->query($query);
187
            $this->addArrayProperty($feedRoot, $name);
188
            $this->getLogger()->debug(\sprintf('%s is running an XPath query:', __CLASS__), [$query]);
189
190
            $feedRoot->{$name}[$namespaceAlias] = ($xq->length > 0)
191
                ? new $class($xq->item(0), $this->getLogger())
192
                : null;
193
        }
194
    }
195
196
    /**
197
     * Fetches elements with a multiple, complex values.
198
     *
199
     * @param stdClass $feedRoot       The root of the feed. This will be written-to when the parsing middleware runs.
200
     * @param string   $namespaceAlias The preferred namespace alias for a given XML namespace URI. Should be the result
201
     *                                 of a call to `SimplePie\Util\Ns`.
202
     * @param DOMXPath $xpath          The `DOMXPath` object with this middleware's namespace alias applied.
203
     * @param array    $path           The path of the XML traversal. Should begin with `<feed>` or `<channel>`,
204
     *                                 then `<entry>` or `<item>`.
205
     *
206
     * phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
207
     */
208
    protected function getMultipleComplexTypes(
209
        object $feedRoot,
210
        string $namespaceAlias,
211
        DOMXPath $xpath,
212
        array $path
213
    ): void {
214
        // phpcs:enable
215
216
        $cpath = $path;
217
218
        if (\is_int(\end($cpath))) {
219
            \array_pop($cpath);
220
        }
221
222
        if ('feed' === \end($cpath)) {
223
            $nodes = [
224
                'category'    => T\Category::class,
225
                'contributor' => T\Person::class,
226
                'link'        => T\Link::class,
227
                'entry'       => T\Entry::class,
228
            ];
229
        } elseif ('entry' === \end($cpath)) {
230
            $nodes = [
231
                'category'    => T\Category::class,
232
                'contributor' => T\Person::class,
233
                'link'        => T\Link::class,
234
            ];
235
        }
236
237
        foreach ($nodes as $name => $class) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $nodes does not seem to be defined for all execution paths leading up to this point.
Loading history...
238
            $query = $this->generateQuery($namespaceAlias, \array_merge($path, [$name]));
239
            $xq    = $xpath->query($query);
240
            $this->addArrayProperty($feedRoot, $name);
241
            $this->getLogger()->debug(\sprintf('%s is running an XPath query:', __CLASS__), [$query]);
242
243
            $feedRoot->{$name}[$namespaceAlias] = [];
244
245
            foreach ($xq as $result) {
246
                // What kind of class is this?
247
                $rclass = (new ReflectionClass($class))
248
                    ->newInstanceWithoutConstructor();
249
250
                if ($rclass instanceof T\BranchInterface) {
251
                    $feedRoot->{$name}[$namespaceAlias][] = new $class(
252
                        $namespaceAlias,
253
                        $result,
254
                        $this->getLogger()
255
                    );
256
                } else {
257
                    $feedRoot->{$name}[$namespaceAlias][] = new $class(
258
                        $result,
259
                        $this->getLogger()
260
                    );
261
                }
262
            }
263
        }
264
    }
265
}
266