Issues (46)

src/Middleware/Xml/Atom.php (2 issues)

Labels
1
<?php
2
/**
3
 * Copyright (c) 2017–2019 Ryan Parman <http://ryanparman.com>.
4
 * Copyright (c) 2017–2019 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 C\SetLoggerInterface, XmlInterface
27
{
28
    use Tr\LoggerTrait;
0 ignored issues
show
The type SimplePie\Mixin\LoggerTrait was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
30
    /**
31
     * {@inheritdoc}
32
     */
33 560
    public function __invoke(stdClass $feedRoot, string $namespaceAlias, DOMXPath $xpath): void
34
    {
35
        // Top-level feed
36 560
        $path = ['feed'];
37
38 560
        $this->getNodeAttributes($feedRoot, $namespaceAlias, $xpath, $path);
39
40
        $feedFallback = [
41 560
            'base' => $feedRoot->base[$namespaceAlias]->getNode(),
42 560
            'lang' => $feedRoot->lang[$namespaceAlias]->getNode(),
43
        ];
44
45 560
        $this->getSingleScalarTypes($feedRoot, $namespaceAlias, $xpath, $path, $feedFallback);
46 560
        $this->getSingleComplexTypes($feedRoot, $namespaceAlias, $xpath, $path);
47 560
        $this->getMultipleComplexTypes($feedRoot, $namespaceAlias, $xpath, $path);
48
49
        // <entry> element
50 560
        $path = ['feed', 'entry'];
51
52 560
        foreach ($feedRoot->entry[$namespaceAlias] as $i => &$entry) {
53 220
            $cpath   = $path;
54 220
            $cpath[] = $i;
55
56
            $feedFallback = [
57 220
                'base' => $feedRoot->base[$namespaceAlias]->getNode(),
58 220
                'lang' => $feedRoot->lang[$namespaceAlias]->getNode(),
59
            ];
60
61 220
            $this->getNodeAttributes($entry, $namespaceAlias, $xpath, $cpath, $feedFallback);
62
63
            $entryFallback = [
64 220
                'base' => $entry->base[$namespaceAlias]->getNode(),
65 220
                'lang' => $entry->lang[$namespaceAlias]->getNode(),
66
            ];
67
68 220
            $this->getSingleScalarTypes($entry, $namespaceAlias, $xpath, $cpath, $entryFallback);
69 220
            $this->getSingleComplexTypes($entry, $namespaceAlias, $xpath, $cpath);
70 220
            $this->getMultipleComplexTypes($entry, $namespaceAlias, $xpath, $cpath);
71
        }
72 560
    }
73
74
    /**
75
     * {@inheritdoc}
76
     *
77
     * Supports valid and invalid variations.
78
     *
79
     * * http://www.w3.org/2005/Atom
80
     * * http://www.w3.org/2005/Atom/
81
     * * https://www.w3.org/2005/Atom
82
     * * https://www.w3.org/2005/Atom/
83
     */
84 560
    public function getSupportedNamespaces(): array
85
    {
86
        return [
87 560
            'http://www.w3.org/2005/Atom'              => 'atom10',
88
            '/https?:\/\/www\.w3\.org\/2005\/Atom\/?/' => 'atom10',
89
        ];
90
    }
91
92
    /**
93
     * Fetches attributes with a single, scalar value, on elements.
94
     *
95
     * @param stdClass $feedRoot       The root of the feed. This will be written-to when the parsing middleware runs.
96
     * @param string   $namespaceAlias The preferred namespace alias for a given XML namespace URI. Should be the result
97
     *                                 of a call to `SimplePie\Util\Ns`.
98
     * @param DOMXPath $xpath          The `DOMXPath` object with this middleware's namespace alias applied.
99
     * @param array    $path           The path of the XML traversal. Should begin with `<feed>` or `<channel>`,
100
     *                                 then `<entry>` or `<item>`.
101
     * @param array    $fallback       An array of attributes for default XML attributes. The default value is an
102
     *                                 empty array.
103
     *
104
     * @phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
105
     */
106 560
    protected function getNodeAttributes(
107
        object $feedRoot,
108
        string $namespaceAlias,
109
        DOMXPath $xpath,
110
        array $path,
111
        array $fallback = []
112
    ): void {
113
        // @phpcs:enable
114
115
        $attrs = [
116 560
            'base' => '@xml:base',
117
            'lang' => '@xml:lang',
118
        ];
119
120
        // Used for traversing up the tree for inheritance
121 560
        $pathMinusLastBit = $path;
122 560
        \array_pop($pathMinusLastBit);
123
124 560
        foreach ($attrs as $nodeName => $searchName) {
125 560
            $query = $this->generateQuery($namespaceAlias, \array_merge($path, [$searchName]));
126 560
            $xq    = $xpath->query($query);
127 560
            $this->addArrayProperty($feedRoot, $nodeName);
128 560
            $this->getLogger()->debug(\sprintf('%s is running an XPath query:', __CLASS__), [$query]);
129
130 560
            $feedRoot->{$nodeName}[$namespaceAlias] = (false !== $xq && $xq->length > 0)
131 68
                ? new T\Node($xq->item(0))
132 560
                : new T\Node($this->get($fallback, $nodeName));
0 ignored issues
show
$this->get($fallback, $nodeName) of type string is incompatible with the type DOMNode|null expected by parameter $node of SimplePie\Type\Node::__construct(). ( Ignorable by Annotation )

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

132
                : new T\Node(/** @scrutinizer ignore-type */ $this->get($fallback, $nodeName));
Loading history...
133
        }
134 560
    }
135
136
    /**
137
     * Fetches elements with a single, scalar value.
138
     *
139
     * @param stdClass $feedRoot       The root of the feed. This will be written-to when the parsing middleware runs.
140
     * @param string   $namespaceAlias The preferred namespace alias for a given XML namespace URI. Should be the result
141
     *                                 of a call to `SimplePie\Util\Ns`.
142
     * @param DOMXPath $xpath          The `DOMXPath` object with this middleware's namespace alias applied.
143
     * @param array    $path           The path of the XML traversal. Should begin with `<feed>` or `<channel>`,
144
     *                                 then `<entry>` or `<item>`.
145
     * @param array    $fallback       An array of attributes for default XML attributes. The default value is an
146
     *                                 empty array.
147
     *
148
     * @phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
149
     */
150 560
    protected function getSingleScalarTypes(
151
        object $feedRoot,
152
        string $namespaceAlias,
153
        DOMXPath $xpath,
154
        array $path,
155
        array $fallback = []
156
    ): void {
157
        // @phpcs:enable
158
159 560
        $cpath = $path;
160 560
        $nodes = [];
161
162 560
        if (\is_int(\end($cpath))) {
163 220
            \array_pop($cpath);
164
        }
165
166 560
        if ('feed' === \end($cpath)) {
167
            $nodes = [
168 560
                'icon',
169
                'id',
170
                'logo',
171
                'published',
172
                'rights',
173
                'subtitle',
174
                'summary',
175
                'title',
176
                'updated',
177
            ];
178 220
        } elseif ('entry' === \end($cpath)) {
179
            $nodes = [
180 220
                'content',
181
                'id',
182
                'published',
183
                'rights',
184
                'summary',
185
                'title',
186
                'updated',
187
            ];
188
        }
189
190 560
        foreach ($nodes as $nodeName) {
191 560
            $query = $this->generateQuery($namespaceAlias, \array_merge($path, [$nodeName]));
192 560
            $xq    = $xpath->query($query);
193 560
            $this->addArrayProperty($feedRoot, $nodeName);
194 560
            $this->getLogger()->debug(\sprintf('%s is running an XPath query:', __CLASS__), [$query]);
195
196 560
            $feedRoot->{$nodeName}[$namespaceAlias] = (false !== $xq && $xq->length > 0)
197 479
                ? new T\Node($xq->item(0), $fallback)
198 560
                : new T\Node();
199
        }
200 560
    }
201
202
    /**
203
     * Fetches elements with a single, complex value.
204
     *
205
     * @param stdClass $feedRoot       The root of the feed. This will be written-to when the parsing middleware runs.
206
     * @param string   $namespaceAlias The preferred namespace alias for a given XML namespace URI. Should be the result
207
     *                                 of a call to `SimplePie\Util\Ns`.
208
     * @param DOMXPath $xpath          The `DOMXPath` object with this middleware's namespace alias applied.
209
     * @param array    $path           The path of the XML traversal. Should begin with `<feed>` or `<channel>`,
210
     *                                 then `<entry>` or `<item>`.
211
     *
212
     * @phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
213
     */
214 560
    protected function getSingleComplexTypes(
215
        object $feedRoot,
216
        string $namespaceAlias,
217
        DOMXPath $xpath,
218
        array $path
219
    ): void {
220
        // @phpcs:enable
221
222 560
        $cpath = $path;
223 560
        $nodes = [];
224
225 560
        if (\is_int(\end($cpath))) {
226 220
            \array_pop($cpath);
227
        }
228
229 560
        if ('feed' === \end($cpath)) {
230
            $nodes = [
231 560
                'generator' => T\Generator::class,
232
            ];
233
        }
234
235 560
        foreach ($nodes as $name => $class) {
236 560
            $query = $this->generateQuery($namespaceAlias, \array_merge($path, [$name]));
237 560
            $xq    = $xpath->query($query);
238 560
            $this->addArrayProperty($feedRoot, $name);
239 560
            $this->getLogger()->debug(\sprintf('%s is running an XPath query:', __CLASS__), [$query]);
240
241 560
            $feedRoot->{$name}[$namespaceAlias] = (false !== $xq && $xq->length > 0)
242 44
                ? new $class($xq->item(0), $this->getLogger())
243 516
                : null;
244
        }
245 560
    }
246
247
    /**
248
     * Fetches elements with a multiple, complex values.
249
     *
250
     * @param stdClass $feedRoot       The root of the feed. This will be written-to when the parsing middleware runs.
251
     * @param string   $namespaceAlias The preferred namespace alias for a given XML namespace URI. Should be the result
252
     *                                 of a call to `SimplePie\Util\Ns`.
253
     * @param DOMXPath $xpath          The `DOMXPath` object with this middleware's namespace alias applied.
254
     * @param array    $path           The path of the XML traversal. Should begin with `<feed>` or `<channel>`,
255
     *                                 then `<entry>` or `<item>`.
256
     *
257
     * @phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
258
     */
259 560
    protected function getMultipleComplexTypes(
260
        object $feedRoot,
261
        string $namespaceAlias,
262
        DOMXPath $xpath,
263
        array $path
264
    ): void {
265
        // @phpcs:enable
266
267 560
        $cpath = $path;
268 560
        $nodes = [];
269
270 560
        if (\is_int(\end($cpath))) {
271 220
            \array_pop($cpath);
272
        }
273
274 560
        if ('feed' === \end($cpath)) {
275
            $nodes = [
276 560
                'author'      => T\Person::class,
277
                'category'    => T\Category::class,
278
                'contributor' => T\Person::class,
279
                'entry'       => T\Entry::class,
280
                'link'        => T\Link::class,
281
            ];
282 220
        } elseif ('entry' === \end($cpath)) {
283
            $nodes = [
284 220
                'author'      => T\Person::class,
285
                'category'    => T\Category::class,
286
                'contributor' => T\Person::class,
287
                'link'        => T\Link::class,
288
            ];
289
        }
290
291 560
        foreach ($nodes as $name => $class) {
292 560
            $query = $this->generateQuery($namespaceAlias, \array_merge($path, [$name]));
293 560
            $xq    = $xpath->query($query);
294 560
            $this->addArrayProperty($feedRoot, $name);
295 560
            $this->getLogger()->debug(\sprintf('%s is running an XPath query:', __CLASS__), [$query]);
296
297 560
            $feedRoot->{$name}[$namespaceAlias] = [];
298
299 560
            foreach ($xq as $result) {
300
                // What kind of class is this?
301 253
                $rclass = (new ReflectionClass($class))
302 253
                    ->newInstanceWithoutConstructor();
303
304 253
                if ($rclass instanceof T\BranchInterface) {
305 220
                    $feedRoot->{$name}[$namespaceAlias][] = new $class(
306 220
                        $namespaceAlias,
307
                        $result,
308 220
                        $this->getLogger()
309
                    );
310
                } else {
311 115
                    $feedRoot->{$name}[$namespaceAlias][] = new $class(
312 115
                        $result,
313 115
                        $this->getLogger()
314
                    );
315
                }
316
            }
317
        }
318 560
    }
319
}
320