RssReader::__construct()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 5
Bugs 0 Features 1
Metric Value
eloc 4
c 5
b 0
f 1
dl 0
loc 11
ccs 0
cts 5
cp 0
rs 10
cc 3
nc 4
nop 3
crap 12
1
<?php declare(strict_types=1);
2
3
namespace Compolomus\RssReader;
4
5
use DateTime;
6
use DOMDocument;
7
use DOMXPath;
8
use function array_column;
9
use function array_merge;
10
use function count;
11
use function crc32;
12
use function in_array;
13
use function strip_tags;
14
use function trim;
15
16
class RssReader
17
{
18
    public function __construct(
19
        public array           $channels = [],
20
        public ?CacheInterface $cache = null,
21
        public int             $limit = 0,
22
    ) {
23
        if (!empty($_ENV['RSSREADER_LIMIT'])) {
24
            $this->limit = (int) $_ENV['RSSREADER_LIMIT'];
25
        }
26
27
        if (null === $this->cache) {
28
            $this->cache = new FileCache();
29
        }
30
    }
31
32
    /**
33
     * @throws \Exception
34
     */
35
    protected function getPostsFromChannel(string $chanel): ?array
36
    {
37
        // Load document
38
        $dom = new DOMDocument();
39
        $dom->load($chanel);
40
41
        // Extract all items
42
        $items = $dom->getElementsByTagName('item');
43
44
        // Build XPath object
45
        $xpath = new DOMXpath($dom);
46
47
        // Parse all items in loop
48
        $result = [];
49
        foreach ($items as $item) {
50
            $link      = $item->getElementsByTagName('link')->item(0)->nodeValue;
51
            $itemId    = crc32($link);
52
            $timestamp = (new DateTime($item->getElementsByTagName('pubDate')->item(0)->nodeValue))->getTimestamp();
53
54
            // Extract posts which is not in a cache
55
            if (!in_array($itemId, $this->cache->getIds(), false)) {
0 ignored issues
show
Bug introduced by
The method getIds() does not exist on null. ( Ignorable by Annotation )

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

55
            if (!in_array($itemId, $this->cache->/** @scrutinizer ignore-call */ getIds(), false)) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
56
                $result[] = [
57
                    'id'        => $itemId,
58
                    'title'     => $item->getElementsByTagName('title')->item(0)->nodeValue,
59
                    'desc'      => trim(strip_tags($item->getElementsByTagName('description')->item(0)->nodeValue)),
60
                    'link'      => $link,
61
                    'timestamp' => $timestamp,
62
                    'img'       => $xpath->query('//enclosure/@url')->item(0)->nodeValue,
63
                ];
64
            }
65
        }
66
67
        return $result;
68
    }
69
70
    /**
71
     * Return array with only unique elements
72
     *
73
     * @param array $array
74
     *
75
     * @return array
76
     */
77
    public function getUnique(array $array): array
78
    {
79
        $serialized = array_map('serialize', $array);
80
        $unique     = array_unique($serialized);
81
82
        return array_intersect_key($array, $unique);
83
    }
84
85
    /**
86
     * @throws \Exception
87
     */
88
    public function getAll(): array
89
    {
90
        $result = [];
91
        foreach ($this->channels as $chanel) {
92
            $result[] = $this->getPostsFromChannel($chanel);
93
        }
94
95
        // Merge results from all sources
96
        $result = array_merge(...$result);
97
98
        // Only unique records
99
        $result = $this->getUnique($result);
100
101
        // Sort array by timestamp column
102
        usort($result, static function ($a, $b) {
103
            if ($a['timestamp'] === $b['timestamp']) {
104
                return 0;
105
            }
106
            // ASC order
107
            return ($a['timestamp'] > $b['timestamp']) ? 1 : -1;
108
        });
109
110
        // Extract IDs and save them all
111
        $ids = array_column($result, 'id');
112
        if (count($ids)) {
113
            $this->cache->saveIds($ids);
114
        }
115
116
        // Slice last few posts if limit is set
117
        if (!empty($this->limit)) {
118
            $result = array_slice($result, -$this->limit, $this->limit);
119
        }
120
121
        return $result;
122
    }
123
}
124