Completed
Push — master ( 1909f4...1cc48d )
by Alex
18s queued 11s
created

FeedIo::format()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 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\LastModifiedSince(),
237 12
            new Reader\Fixer\HttpLastModified(),
238 12
            new Reader\Fixer\PublicId(),
239
        );
240
    }
241
242
    /**
243
     * @param array $formats
244
     * @return FeedIo
245
     */
246 1
    public function addDateFormats(array $formats) : FeedIo
247
    {
248 1
        foreach ($formats as $format) {
249 1
            $this->getDateTimeBuilder()->addDateFormat($format);
250
        }
251
252 1
        return $this;
253
    }
254
255
    /**
256
     * @return \FeedIo\Rule\DateTimeBuilder
257
     */
258 13
    public function getDateTimeBuilder() : DateTimeBuilder
259
    {
260 13
        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 13
    public function setReader(Reader $reader) : FeedIo
276
    {
277 13
        $this->reader = $reader;
278
279 13
        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
327 2
        return $result;
328
    }
329
330
    /**
331
     * @param  string                $url
332
     * @param  \DateTime             $modifiedSince
333
     * @return \FeedIo\Reader\Result
334
     */
335 1
    public function readSince(string $url, \DateTime $modifiedSince) : Result
336
    {
337 1
        return $this->read($url, new Feed(), $modifiedSince);
338
    }
339
340
    /**
341
     * @return FeedIo
342
     */
343 1
    public function resetFilters() : FeedIo
344
    {
345 1
        $this->getReader()->resetFilters();
346
347 1
        return $this;
348
    }
349
350
    /**
351
     * Get a PSR-7 compliant response for the given feed
352
     *
353
     * @param \FeedIo\FeedInterface $feed
354
     * @param string $standard
355
     * @param int $maxAge
356
     * @param bool $public
357
     * @return ResponseInterface
358
     */
359 1
    public function getPsrResponse(FeedInterface $feed, string $standard, int $maxAge = 600, bool $public = true) : ResponseInterface
360
    {
361 1
        $this->logAction($feed, "creating a PSR 7 Response in $standard format");
362
363 1
        $formatter = $this->getStandard($standard)->getFormatter();
364 1
        $responseBuilder = new ResponseBuilder($maxAge, $public);
365
366 1
        return $responseBuilder->createResponse($standard, $formatter, $feed);
367
    }
368
369
    /**
370
     * @param  FeedInterface $feed
371
     * @param  string        $standard Standard's name
372
     * @return string
373
     */
374 1
    public function format(FeedInterface $feed, string $standard) : string
375
    {
376 1
        $this->logAction($feed, "formatting a feed in $standard format");
377
378 1
        $formatter = $this->getStandard($standard)->getFormatter();
379
380 1
        return $formatter->toString($feed);
381
    }
382
383
    /**
384
     * @param  \FeedIo\FeedInterface $feed
385
     * @return string
386
     */
387 1
    public function toRss(FeedInterface $feed) : string
388
    {
389 1
        return $this->format($feed, 'rss');
390
    }
391
392
    /**
393
     * @param  \FeedIo\FeedInterface $feed
394
     * @return string
395
     */
396 1
    public function toAtom(FeedInterface $feed) : string
397
    {
398 1
        return $this->format($feed, 'atom');
399
    }
400
401
    /**
402
     * @param  \FeedIo\FeedInterface $feed
403
     * @return string
404
     */
405 1
    public function toJson(FeedInterface $feed) : string
406
    {
407 1
        return $this->format($feed, 'json');
408
    }
409
410
411
    /**
412
     * @param  string                   $name
413
     * @return \FeedIo\StandardAbstract
414
     * @throws \OutOfBoundsException
415
     */
416 3
    public function getStandard(string $name) : StandardAbstract
417
    {
418 3
        $name = strtolower($name);
419 3
        if (array_key_exists($name, $this->standards)) {
420 2
            return $this->standards[$name];
421
        }
422
423 1
        throw new \OutOfBoundsException("no standard found for $name");
424
    }
425
426
    /**
427
     * @param  \FeedIo\FeedInterface $feed
428
     * @param  string                $message
429
     * @return FeedIo
430
     */
431 3
    protected function logAction(FeedInterface $feed, string $message) : FeedIo
432
    {
433 3
        $class = get_class($feed);
434 3
        $this->logger->debug("$message (feed class : $class)");
435
436 3
        return $this;
437
    }
438
}
439