Completed
Push — issue/261 ( 6b2f47 )
by Alex
03:29
created

FeedIo   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 361
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 15

Test Coverage

Coverage 95.05%

Importance

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