Passed
Push — master ( 115a96...ff3cf0 )
by Ryan
13:16
created

Xml::getFeed()   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
9
declare(strict_types=1);
10
11
namespace SimplePie\Parser;
12
13
use DOMDocument;
14
use DOMXPath;
15
use Psr\Http\Message\StreamInterface;
16
use Psr\Log\LoggerInterface;
17
use SimplePie\Enum\FeedType;
18
use SimplePie\HandlerStackInterface;
19
use SimplePie\Mixin as T;
20
use SimplePie\SimplePie;
21
use SimplePie\Type\Feed;
22
use SimplePie\Util\Ns;
23
24
class Xml extends AbstractParser
25
{
26
    use T\DomDocumentTrait;
27
    use T\LoggerTrait;
28
    use T\RawDocumentTrait;
29
30
    /**
31
     * The object which contains the parsed results.
32
     *
33
     * @var Feed
34
     */
35
    protected $feed;
36
37
    /**
38
     * Bitwise libxml options to use for parsing XML.
39
     *
40
     * @var int
41
     */
42
    protected $libxml;
43
44
    /**
45
     * The handler stack which contains registered middleware.
46
     *
47
     * @var HandlerStackInterface
48
     */
49
    protected $middleware;
50
51
    /**
52
     * The namespace discoverer.
53
     *
54
     * @var Ns
55
     */
56
    protected $ns;
57
58
    /**
59
     * Constructs a new instance of this class.
60
     *
61
     * @param StreamInterface       $stream                  A PSR-7 `StreamInterface` which is typically returned by
62
     *                                                       the `getBody()` method of a `ResponseInterface` class.
63
     * @param LoggerInterface       $logger                  The PSR-3 logger.
64
     * @param HandlerStackInterface $handlerStack            The handler stack which contains registered middleware.
65
     * @param int                   $libxml                  The libxml value to use for parsing XML.
66
     * @param bool                  $handleHtmlEntitiesInXml Whether or not SimplePie should pre-parse the XML as HTML
67
     *                                                       to resolve the entities. A value of `true` means that
68
     *                                                       SimplePie should inject the entity definitions. A value of
69
     *                                                       `false` means that SimplePie should NOT inject the entity
70
     *                                                       definitions. The default value is `false`.
71
     *
72
     * @throws Error
73
     * @throws TypeError
74
     *
75
     * phpcs:disable Generic.Functions.OpeningFunctionBraceBsdAllman.BraceOnSameLine
76
     */
77
    public function __construct(
78
        StreamInterface $stream,
79
        LoggerInterface $logger,
80
        HandlerStackInterface $handlerStack,
81
        int $libxml,
82
        bool $handleHtmlEntitiesInXml
83
    ) {
84
        // phpcs:enable
85
86
        // Logger
87
        $this->logger = $logger;
88
89
        // Middleware
90
        $this->middleware = $handlerStack;
91
92
        // Libxml2
93
        $this->libxml = $libxml;
94
95
        // Raw stream
96
        $this->rawDocument = $this->readStream($stream);
97
98
        // DOMDocument
99
        $this->domDocument = new DOMDocument();
0 ignored issues
show
Bug introduced by
The call to DOMDocument::__construct() has too few arguments starting with version. ( Ignorable by Annotation )

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

99
        $this->domDocument = /** @scrutinizer ignore-call */ new DOMDocument();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
100
101
        // Don't barf errors all over the output
102
        \libxml_use_internal_errors(true);
103
104
        // DOMDocument configuration
105
        $this->domDocument->recover             = true;
106
        $this->domDocument->formatOutput        = true;
107
        $this->domDocument->preserveWhiteSpace  = false;
108
        $this->domDocument->resolveExternals    = true;
109
        $this->domDocument->substituteEntities  = true;
110
        $this->domDocument->strictErrorChecking = false;
111
        $this->domDocument->validateOnParse     = false;
112
113
        // If enabled, force-inject the contents of `entities.dtd` into the feed.
114
        if ($handleHtmlEntitiesInXml) {
115
            $this->getLogger()->debug('Enabled handing HTML entities in XML.');
116
            $this->domDocument->loadXML($this->rawDocument, $this->libxml);
117
118
            // <feed, <rss, etc.
119
            $rootElementStart = \sprintf('<%s', (string) $this->domDocument->firstChild->nodeName);
120
121
            // Read the entity definition file, and force-inject it into the XML document
122
            $this->rawDocument = \str_replace(
123
                $rootElementStart,
124
                \sprintf('%s%s', \trim(\file_get_contents(SIMPLEPIE_ROOT . '/entities.dtd')), $rootElementStart),
125
                $this->rawDocument
126
            );
127
        }
128
129
        // Parse the XML document with the configured libxml options
130
        $this->domDocument->loadXML($this->rawDocument, $this->libxml);
131
132
        // Register the namespace handler.
133
        $this->ns = (new Ns($this->domDocument))
134
            ->setLogger($this->getLogger());
135
136
        // Look at which namespaces the registered middleware understands.
137
        $this->middleware->registerNamespaces($this->ns);
0 ignored issues
show
Bug introduced by
The method registerNamespaces() does not exist on SimplePie\HandlerStackInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to SimplePie\HandlerStackInterface. ( Ignorable by Annotation )

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

137
        $this->middleware->/** @scrutinizer ignore-call */ 
138
                           registerNamespaces($this->ns);
Loading history...
138
139
        // Instantiate a new write-to feed object.
140
        $this->feed = (new Feed($this->getNamespaceAlias()))
141
            ->setLogger($this->getLogger());
142
143
        // Invoke the registered middleware.
144
        $this->middleware->invoke(
145
            FeedType::XML,
146
            $this->getFeed()->getRoot(),
147
            $this->getNamespaceAlias(),
148
            $this->xpath()
149
        );
150
151
        // Clear the libxml errors to avoid excessive memory usage
152
        \libxml_clear_errors();
153
    }
154
155
    /**
156
     * Get the namespace handler.
157
     *
158
     * @return Ns
159
     */
160
    public function getNs(): Ns
161
    {
162
        return $this->ns;
163
    }
164
165
    /**
166
     * Get the preferred namespace alias.
167
     *
168
     * @return string|null
169
     */
170
    public function getNamespaceAlias(): ?string
171
    {
172
        $namespace = $this->getNs();
173
174
        $alias = $namespace->getPreferredNamespaceAlias(
175
            $this->domDocument->documentElement->namespaceURI
176
        );
177
178
        return $alias;
179
    }
180
181
    /**
182
     * Gets a reference to the `DOMXPath` object, with the default namespace
183
     * already registered.
184
     *
185
     * @return DOMXPath
186
     */
187
    public function xpath()
188
    {
189
        $ns    = $this->getNamespaceAlias();
190
        $xpath = new DOMXPath($this->domDocument);
191
192
        // Register the namespace alias with the XPath instance
193
        if (null !== $ns) {
194
            $xpath->registerNamespace(
195
                $ns,
196
                $this->domDocument->documentElement->namespaceURI ?? ''
197
            );
198
        }
199
200
        return $xpath;
201
    }
202
203
    /**
204
     * Retrieves the object which represents the top-level feed.
205
     *
206
     * @return Feed
207
     */
208
    public function getFeed(): Feed
209
    {
210
        return $this->feed;
211
    }
212
}
213