Completed
Push — master ( bd5947...6becd7 )
by Alex
12s
created

FeedReader::getResponse()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 2
crap 2
1
<?php
2
3
/**
4
 * Rss/Atom Bundle for Symfony.
5
 *
6
 *
7
 * @license http://opensource.org/licenses/lgpl-3.0.html LGPL
8
 * @copyright (c) 2013, Alexandre Debril
9
 */
10
namespace Debril\RssAtomBundle\Protocol;
11
12
use Debril\RssAtomBundle\Protocol\Filter\ModifiedSince;
13
use Debril\RssAtomBundle\Protocol\Parser\XmlParser;
14
use SimpleXMLElement;
15
use Debril\RssAtomBundle\Driver\HttpDriverInterface;
16
use Debril\RssAtomBundle\Driver\HttpDriverResponse;
17
use Debril\RssAtomBundle\Protocol\Parser\Factory;
18
use Debril\RssAtomBundle\Exception\ParserException;
19
use Debril\RssAtomBundle\Exception\FeedException\FeedCannotBeReadException;
20
use Debril\RssAtomBundle\Exception\FeedException\FeedNotFoundException;
21
use Debril\RssAtomBundle\Exception\FeedException\FeedNotModifiedException;
22
use Debril\RssAtomBundle\Exception\FeedException\FeedServerErrorException;
23
use Debril\RssAtomBundle\Exception\FeedException\FeedForbiddenException;
24
25
/**
26
 * Class to read any kind of supported feeds (RSS, ATOM, and more if you need).
27
 *
28
 * FeedReader uses an HttpDriverInterface to pull feeds and one more Parser instances to
29
 * parse them. For each feed, FeedReader automatically chooses the accurate
30
 * Parser and use it to return a FeedContent instance.
31
 *
32
 * <code>
33
 * // HttpDriverInterface and Factory instances are required to construct a FeedReader.
34
 * // Here we use the HttpCurlDriver (recommanded)
35
 * $reader = new FeedReader(new HttpCurlDriver(), new Factory());
36
 *
37
 * // now we add the parsers
38
 * $reader->addParser(new AtomParser());
39
 * $reader->addParser(new RssParser());
40
 *
41
 * // $url is obviously the feed you want to read
42
 * // $dateTime is the last moment you read the feed
43
 * $content = $reader->getFeedContent($url, $dateTime);
44
 *
45
 * // now we can display the feed's content
46
 * echo $feed->getTitle();
47
 *
48
 * // each
49
 * foreach( $content->getItems() as $item )
50
 * {
51
 *      echo $item->getTitle();
52
 *      echo $item->getSummary();
53
 * }
54
 * </code>
55
 */
56
class FeedReader
57
{
58
    /**
59
     * @var Parser[]
60
     */
61
    protected $parsers = array();
62
63
    /**
64
     * @var HttpDriverInterface
65
     */
66
    protected $driver = null;
67
68
    /**
69
     * @var Factory
70
     */
71
    protected $factory = null;
72
73
    /**
74
     * @var XmlParser
75
     */
76
    protected $xmlParser = null;
77
78
    /**
79
     * @param HttpDriverInterface $driver
80
     * @param Factory             $factory
81
     */
82 2
    public function __construct(HttpDriverInterface $driver, Factory $factory, XmlParser $xmlParser)
83
    {
84 2
        $this->driver = $driver;
85 2
        $this->factory = $factory;
86 2
        $this->xmlParser = $xmlParser;
87 2
    }
88
89
    /**
90
     * Add a Parser.
91
     *
92
     * @param Parser $parser
93
     *
94
     * @return FeedReader
95
     */
96 2
    public function addParser(Parser $parser)
97
    {
98 2
        $parser->setFactory($this->factory);
99 2
        $this->parsers[] = $parser;
100
101 2
        return $this;
102
    }
103
104
    /**
105
     * @return HttpDriverInterface
106
     */
107 2
    public function getDriver()
108
    {
109 2
        return $this->driver;
110
    }
111
112
    /**
113
     * Read a feed using its url and create a FeedInInterface instance
114
     * Second parameter can be either a \DateTime instance or a numeric limit.
115
     *
116
     * @param string    $url
117
     * @param \DateTime $arg
118
     *
119
     * @return FeedInInterface|FeedContent
120
     */
121 4
    public function getFeedContent($url, $arg = null)
122
    {
123 4
        if (is_numeric($arg)) {
124
            return $this->getFilteredContent($url, array(
125
                        new Filter\Limit($arg),
126
            ));
127
        }
128 4
        if ($arg instanceof \DateTime) {
129 4
            return $this->getFeedContentSince($url, $arg);
130
        }
131
132
        return $this->getFilteredContent($url, array());
133
    }
134
135
    /**
136
     * @param string    $url
137
     * @param array     $filters
138
     * @param \DateTime $modifiedSince
139
     *
140
     * @return FeedInInterface
141
     */
142 1
    public function getFilteredContent($url, array $filters, \DateTime $modifiedSince = null)
143
    {
144 1
        $response = $this->getResponse($url, $modifiedSince);
145
146 1
        return $this->parseBody($response, $this->factory->newFeed(), $filters);
147
    }
148
149
    /**
150
     * @param string    $url
151
     * @param \DateTime $modifiedSince
152
     *
153
     * @return FeedInInterface
154
     */
155 1
    public function getFeedContentSince($url, \DateTime $modifiedSince)
156
    {
157
        $filters = array(
158 1
            new Filter\ModifiedSince($modifiedSince),
159 1
        );
160
161 1
        return $this->getFilteredContent($url, $filters);
162
    }
163
164
    /**
165
     * Read a feed using its url and hydrate the given FeedInInterface instance.
166
     *
167
     * @param string          $url
168
     * @param FeedInInterface $feed
169
     * @param \DateTime       $modifiedSince
170
     *
171
     * @return FeedInInterface
172
     */
173 1
    public function readFeed($url, FeedInInterface $feed, \DateTime $modifiedSince)
174
    {
175 1
        $response = $this->getResponse($url, $modifiedSince);
176
177
        $filters = array(
178 1
            new ModifiedSince($modifiedSince),
179 1
        );
180
181 1
        return $this->parseBody($response, $feed, $filters);
182
    }
183
184
    /**
185
     * Read the XML stream hosted at $url.
186
     *
187
     * @param $url
188
     * @param \Datetime $modifiedSince
189
     *
190
     * @return HttpDriverResponse
191
     */
192 2
    public function getResponse($url, \Datetime $modifiedSince = null)
193
    {
194 2
        if (is_null($modifiedSince)) {
195 1
            $modifiedSince = new \DateTime('@0');
196 1
        }
197
198 2
        return $this->getDriver()->getResponse($url, $modifiedSince);
199
    }
200
201
    /**
202
     * Parse the body of a feed and write it into the FeedInInterface instance.
203
     *
204
     * @param HttpDriverResponse $response
205
     * @param FeedInInterface    $feed
206
     * @param array              $filters
207
     *
208
     * @return FeedInInterface
209
     */
210 10
    public function parseBody(HttpDriverResponse $response, FeedInInterface $feed, array $filters = array())
211
    {
212 10
        if ($response->getHttpCodeIsOk()
213 10
            || $response->getHttpCodeIsRedirection()) {
214 5
            $xmlBody = $this->xmlParser->parseString($response->getBody());
215 5
            $parser = $this->getAccurateParser($xmlBody);
216
217 5
            return $parser->parse($xmlBody, $feed, $filters);
0 ignored issues
show
Compatibility introduced by
$feed of type object<Debril\RssAtomBun...otocol\FeedInInterface> is not a sub-type of object<Debril\RssAtomBun...Protocol\FeedInterface>. It seems like you assume a child interface of the interface Debril\RssAtomBundle\Protocol\FeedInInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
218
        }
219
220 5
        switch ($response->getHttpCode()) {
221 5
            case HttpDriverResponse::HTTP_CODE_NOT_FOUND:
222 1
                throw new FeedNotFoundException($response->getHttpMessage());
223 4
            case HttpDriverResponse::HTTP_CODE_NOT_MODIFIED:
224 1
                throw new FeedNotModifiedException($response->getHttpMessage());
225 3
            case HttpDriverResponse::HTTP_CODE_SERVER_ERROR:
226 1
                throw new FeedServerErrorException($response->getHttpMessage());
227 2
            case HttpDriverResponse::HTTP_CODE_FORBIDDEN:
228 1
                throw new FeedForbiddenException($response->getHttpMessage());
229 1
            default:
230 1
                throw new FeedCannotBeReadException($response->getHttpMessage(), $response->getHttpCode());
231 1
        }
232
    }
233
234
    /**
235
     * Choose the accurate Parser for the XML stream.
236
     *
237
     * @param SimpleXMLElement $xmlBody
238
     *
239
     * @throws ParserException
240
     *
241
     * @return Parser
242
     */
243 3
    public function getAccurateParser(SimpleXMLElement $xmlBody)
244
    {
245 3
        foreach ($this->parsers as $parser) {
246 2
            if ($parser->canHandle($xmlBody)) {
247 2
                return $parser;
248
            }
249 3
        }
250
251 1
        throw new ParserException('No parser can handle this stream');
252
    }
253
}
254