Completed
Push — master ( 3fbc02...498696 )
by Alex
04:33
created

FeedIo   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 364
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 17

Test Coverage

Coverage 91.35%

Importance

Changes 0
Metric Value
wmc 33
lcom 2
cbo 17
dl 0
loc 364
ccs 95
cts 104
cp 0.9135
rs 9.76
c 0
b 0
f 0

26 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A loadCommonStandards() 0 9 2
A addFilter() 0 6 1
A getCommonStandards() 0 6 1
A addStandard() 0 9 1
A newParser() 0 10 2
A getFixerSet() 0 4 1
A loadFixerSet() 0 11 2
A addFixer() 0 7 1
A getBaseFixers() 0 9 1
A getDateTimeBuilder() 0 4 1
A getReader() 0 4 1
A setReader() 0 6 1
A discover() 0 6 1
A readAsync() 0 6 1
A read() 0 18 3
A readSince() 0 4 1
A resetFilters() 0 6 1
A getPsrResponse() 0 9 1
A format() 0 8 1
A toRss() 0 4 1
A toAtom() 0 4 1
A toJson() 0 4 1
A getStandard() 0 9 2
A logAction() 0 7 1
A addDateFormats() 0 8 2
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of the feed-io package.
4
 *
5
 * (c) Alexandre Debril <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace FeedIo;
12
13
use FeedIo\Filter\ModifiedSince;
14
use FeedIo\Reader\Result;
15
use FeedIo\Reader\FixerSet;
16
use FeedIo\Reader\FixerAbstract;
17
use FeedIo\Rule\DateTimeBuilder;
18
use FeedIo\Rule\DateTimeBuilderInterface;
19
use FeedIo\Adapter\ClientInterface;
20
use FeedIo\Standard\Loader;
21
use FeedIo\Async\Reader as AsyncReader;
22
use FeedIo\Async\CallbackInterface;
23
use FeedIo\FeedInterface;
24
use Psr\Log\LoggerInterface;
25
use FeedIo\Http\ResponseBuilder;
26
use Psr\Http\Message\ResponseInterface;
27
28
/**
29
 * This class acts as a facade. It provides methods to access feed-io main features
30
 *
31
 * <code>
32
 *   // $client is a \FeedIo\Adapter\ClientInterface instance, $logger a \Psr\Log\LoggerInterface
33
 *   $feedIo = new FeedIo($client, $logger);
34
 *
35
 *   // read a feed. Output is a Result instance
36
 *   $result = $feedIo->read('http://somefeed.org/feed.rss');
37
 *
38
 *   // use the feed
39
 *   $feed = $result->getFeed();
40
 *   echo $feed->getTitle();
41
 *
42
 *   // and its items
43
 *   foreach ( $feed as $item ) {
44
 *       echo $item->getTitle();
45
 *       echo $item->getDescription();
46
 *   }
47
 *
48
 * </code>
49
 *
50
 * <code>
51
 *   // build the feed to publish
52
 *   $feed = new \FeedIo\Feed;
53
 *   $feed->setTitle('title');
54
 *   // ...
55
 *
56
 *   // add items to it
57
 *   $item = new \FeedIo\Feed\Item
58
 *   $item->setTitle('my great post');
59
 *
60
 *   // want to publish a media ? no problem
61
 *   $media = new \FeedIo\Feed\Item\Media
62
 *   $media->setUrl('http://yourdomain.tld/medias/some-podcast.mp3');
63
 *   $media->setType('audio/mpeg');
64
 *
65
 *   // add it to the item
66
 *   $item->addMedia($media);
67
 *
68
 *   // add the item to the feed (almost there)
69
 *   $feed->add($item);
70
 *
71
 *   // format it in atom
72
 *   $feedIo->toAtom($feed);
73
 * </code>
74
 *
75
 */
76
class FeedIo
77
{
78
79
    /**
80
     * @var \FeedIo\Reader
81
     */
82
    protected $reader;
83
84
    /**
85
     * @var \FeedIo\Rule\DateTimeBuilder
86
     */
87
    protected $dateTimeBuilder;
88
89
    /**
90
     * @var \FeedIo\Adapter\ClientInterface;
91
     */
92
    protected $client;
93
94
    /**
95
     * @var \Psr\Log\LoggerInterface
96
     */
97
    protected $logger;
98
99
    /**
100
     * @var array
101
     */
102
    protected $standards;
103
104
    /**
105
     * @var \FeedIo\Reader\FixerSet
106
     */
107
    protected $fixerSet;
108
109
    /**
110
     * @param \FeedIo\Adapter\ClientInterface $client
111
     * @param \Psr\Log\LoggerInterface        $logger
112
     */
113 12
    public function __construct(ClientInterface $client, LoggerInterface $logger, DateTimeBuilderInterface $dateTimeBuilder = null)
114
    {
115 12
        $this->client = $client;
116 12
        $this->logger = $logger;
117 12
        $this->dateTimeBuilder = $dateTimeBuilder ?? new DateTimeBuilder($logger);
0 ignored issues
show
Documentation Bug introduced by
$dateTimeBuilder ?? new ...ateTimeBuilder($logger) is of type object<FeedIo\Rule\DateTimeBuilderInterface>, but the property $dateTimeBuilder was declared to be of type object<FeedIo\Rule\DateTimeBuilder>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
118 12
        $this->setReader(new Reader($client, $logger));
119 12
        $this->loadCommonStandards();
120 12
        $this->loadFixerSet();
121 12
    }
122
123
    /**
124
     * Loads main standards (RSS, RDF, Atom) in current object's attributes
125
     *
126
     * @return FeedIo
127
     */
128 12
    protected function loadCommonStandards() : FeedIo
129
    {
130 12
        $standards = $this->getCommonStandards();
131 12
        foreach ($standards as $name => $standard) {
132 12
            $this->addStandard($name, $standard);
133
        }
134
135 12
        return $this;
136
    }
137
138
    /**
139
     * adds a filter to the reader
140
     *
141
     * @param \FeedIo\FilterInterface $filter
142
     * @return FeedIo
143
     */
144 2
    public function addFilter(FilterInterface $filter) : FeedIo
145
    {
146 2
        $this->getReader()->addFilter($filter);
147
148 2
        return $this;
149
    }
150
151
    /**
152
     * Returns main standards
153
     *
154
     * @return array
155
     */
156 12
    public function getCommonStandards() : array
157
    {
158 12
        $loader = new Loader();
159
160 12
        return $loader->getCommonStandards($this->getDateTimeBuilder());
161
    }
162
163
    /**
164
     * @param  string                   $name
165
     * @param  \FeedIo\StandardAbstract $standard
166
     * @return FeedIo
167
     */
168 12
    public function addStandard(string $name, StandardAbstract $standard) : FeedIo
169
    {
170 12
        $name = strtolower($name);
171 12
        $this->standards[$name] = $standard;
172 12
        $parser = $this->newParser($standard->getSyntaxFormat(), $standard);
173 12
        $this->reader->addParser($parser);
174
175 12
        return $this;
176
    }
177
178
    /**
179
     * @param string $format
180
     * @param StandardAbstract $standard
181
     * @return ParserAbstract
182
     */
183 11
    public function newParser(string $format, StandardAbstract $standard) : ParserAbstract
184
    {
185 11
        $reflection = new \ReflectionClass("FeedIo\\Parser\\{$format}Parser");
186
187 11
        if (! $reflection->isSubclassOf('FeedIo\ParserAbstract')) {
188
            throw new \InvalidArgumentException();
189
        }
190
191 11
        return $reflection->newInstanceArgs([$standard, $this->logger]);
192
    }
193
194
    /**
195
     * @return \FeedIo\Reader\FixerSet
196
     */
197 1
    public function getFixerSet() : FixerSet
198
    {
199 1
        return $this->fixerSet;
200
    }
201
202
    /**
203
     * @return FeedIo
204
     */
205 11
    protected function loadFixerSet() : FeedIo
206
    {
207 11
        $this->fixerSet = new FixerSet();
208 11
        $fixers = $this->getBaseFixers();
209
210 11
        foreach ($fixers as $fixer) {
211 11
            $this->addFixer($fixer);
212
        }
213
214 11
        return $this;
215
    }
216
217
    /**
218
     * @param  FixerAbstract $fixer
219
     * @return FeedIo
220
     */
221 11
    public function addFixer(FixerAbstract $fixer) : FeedIo
222
    {
223 11
        $fixer->setLogger($this->logger);
224 11
        $this->fixerSet->add($fixer);
225
226 11
        return $this;
227
    }
228
229
    /**
230
     * @return array
231
     */
232 11
    public function getBaseFixers() : array
233
    {
234
        return array(
235 11
            new Reader\Fixer\LastModified(),
236 11
            new Reader\Fixer\LastModifiedSince(),
237 11
            new Reader\Fixer\HttpLastModified(),
238 11
            new Reader\Fixer\PublicId(),
239
        );
240
    }
241
242
    /**
243
     * @param array $formats
244
     * @return FeedIo
245
     */
246
    public function addDateFormats(array $formats) : FeedIo
247
    {
248
        foreach ($formats as $format) {
249
            $this->getDateTimeBuilder()->addDateFormat($format);
250
        }
251
252
        return $this;
253
    }
254
255
    /**
256
     * @return \FeedIo\Rule\DateTimeBuilder
257
     */
258 12
    public function getDateTimeBuilder() : DateTimeBuilder
259
    {
260 12
        return $this->dateTimeBuilder;
261
    }
262
263
    /**
264
     * @return \FeedIo\Reader
265
     */
266 4
    public function getReader() : Reader
267
    {
268 4
        return $this->reader;
269
    }
270
271
    /**
272
     * @param \FeedIo\Reader $reader
273
     * @return FeedIo
274
     */
275 12
    public function setReader(Reader $reader) : FeedIo
276
    {
277 12
        $this->reader = $reader;
278
279 12
        return $this;
280
    }
281
282
    /**
283
     * Discover feeds from the webpage's headers
284
     * @param  string $url
285
     * @return array
286
     */
287 1
    public function discover(string $url) : array
288
    {
289 1
        $explorer = new Explorer($this->client, $this->logger);
290
291 1
        return $explorer->discover($url);
292
    }
293
294
    /**
295
     * @param iterable $requests
296
     * @param CallbackInterface $callback
297
     * @param string $feedClass
298
     */
299
    public function readAsync(iterable $requests, CallbackInterface $callback, string $feedClass = '\FeedIo\Feed') : void
300
    {
301
        $reader = new AsyncReader($this->reader, $this->reader->getClient(), $callback, $feedClass);
0 ignored issues
show
Compatibility introduced by
$this->reader->getClient() of type object<FeedIo\Adapter\ClientInterface> is not a sub-type of object<FeedIo\Adapter\Guzzle\Client>. It seems like you assume a concrete implementation of the interface FeedIo\Adapter\ClientInterface 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...
302
303
        $reader->process($requests);
304
    }
305
306
    /**
307
     * @param  string                $url
308
     * @param  FeedInterface         $feed
309
     * @param  \DateTime             $modifiedSince
310
     * @return \FeedIo\Reader\Result
311
     */
312 2
    public function read(string $url, FeedInterface $feed = null, \DateTime $modifiedSince = null) : Result
313
    {
314 2
        if (is_null($feed)) {
315 1
            $feed = new Feed();
316
        }
317
318 2
        if ($modifiedSince instanceof \DateTime) {
319 1
            $this->addFilter(new ModifiedSince($modifiedSince));
320
        }
321
322 2
        $this->logAction($feed, "read access : $url into a feed instance");
323 2
        $result = $this->reader->read($url, $feed, $modifiedSince);
324
325 2
        $this->fixerSet->correct($result);
326 2
        $this->resetFilters();
327
328 2
        return $result;
329
    }
330
331
    /**
332
     * @param  string                $url
333
     * @param  \DateTime             $modifiedSince
334
     * @return \FeedIo\Reader\Result
335
     */
336 1
    public function readSince(string $url, \DateTime $modifiedSince) : Result
337
    {
338 1
        return $this->read($url, new Feed(), $modifiedSince);
339
    }
340
341
    /**
342
     * @return FeedIo
343
     */
344 2
    public function resetFilters() : FeedIo
345
    {
346 2
        $this->getReader()->resetFilters();
347
348 2
        return $this;
349
    }
350
351
    /**
352
     * Get a PSR-7 compliant response for the given feed
353
     *
354
     * @param \FeedIo\FeedInterface $feed
355
     * @param string $standard
356
     * @param int $maxAge
357
     * @param bool $public
358
     * @return ResponseInterface
359
     */
360 1
    public function getPsrResponse(FeedInterface $feed, string $standard, int $maxAge = 600, bool $public = true) : ResponseInterface
361
    {
362 1
        $this->logAction($feed, "creating a PSR 7 Response in $standard format");
363
364 1
        $formatter = $this->getStandard($standard)->getFormatter();
365 1
        $responseBuilder = new ResponseBuilder($maxAge, $public);
366
367 1
        return $responseBuilder->createResponse($standard, $formatter, $feed);
368
    }
369
370
    /**
371
     * @param  FeedInterface $feed
372
     * @param  string        $standard Standard's name
373
     * @return string
374
     */
375 1
    public function format(FeedInterface $feed, string $standard) : string
376
    {
377 1
        $this->logAction($feed, "formatting a feed in $standard format");
378
379 1
        $formatter = $this->getStandard($standard)->getFormatter();
380
381 1
        return $formatter->toString($feed);
382
    }
383
384
    /**
385
     * @param  \FeedIo\FeedInterface $feed
386
     * @return string
387
     */
388 1
    public function toRss(FeedInterface $feed) : string
389
    {
390 1
        return $this->format($feed, 'rss');
391
    }
392
393
    /**
394
     * @param  \FeedIo\FeedInterface $feed
395
     * @return string
396
     */
397 1
    public function toAtom(FeedInterface $feed) : string
398
    {
399 1
        return $this->format($feed, 'atom');
400
    }
401
402
    /**
403
     * @param  \FeedIo\FeedInterface $feed
404
     * @return string
405
     */
406 1
    public function toJson(FeedInterface $feed) : string
407
    {
408 1
        return $this->format($feed, 'json');
409
    }
410
411
412
    /**
413
     * @param  string                   $name
414
     * @return \FeedIo\StandardAbstract
415
     * @throws \OutOfBoundsException
416
     */
417 3
    public function getStandard(string $name) : StandardAbstract
418
    {
419 3
        $name = strtolower($name);
420 3
        if (array_key_exists($name, $this->standards)) {
421 2
            return $this->standards[$name];
422
        }
423
424 1
        throw new \OutOfBoundsException("no standard found for $name");
425
    }
426
427
    /**
428
     * @param  \FeedIo\FeedInterface $feed
429
     * @param  string                $message
430
     * @return FeedIo
431
     */
432 3
    protected function logAction(FeedInterface $feed, string $message) : FeedIo
433
    {
434 3
        $class = get_class($feed);
435 3
        $this->logger->debug("$message (feed class : $class)");
436
437 3
        return $this;
438
    }
439
}
440