Completed
Push — master ( 369441...015527 )
by Alex
16s queued 11s
created

UpdateStats::getTimestamp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
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\Reader\Result;
12
13
use FeedIo\Feed\ItemInterface;
14
use FeedIo\FeedInterface;
15
16
class UpdateStats
17
{
18
    /**
19
     * default update delay applied when average or median intervals are outdated
20
     */
21
    const DEFAULT_MIN_DELAY = 3600;
22
23
    /**
24
     * default update delay applied when the feed is considered sleepy
25
     */
26
    const DEFAULT_SLEEPY_DELAY = 86400;
27
28
    /**
29
     * default duration after which the feed is considered sleepy
30
     */
31
    const DEFAULT_DURATION_BEFORE_BEING_SLEEPY = 7 * 86400;
32
33
    /**
34
     * default margin ratio applied to update time calculation
35
     */
36
    const DEFAULT_MARGIN_RATIO = 0.1;
37
38
    /**
39
     * @var FeedInterface
40
     */
41
    protected $feed;
42
43
    /**
44
     * @var array
45
     */
46
    protected $intervals;
47
48
    /**
49
     * UpdateStats constructor.
50
     * @param FeedInterface $feed
51
     */
52 2
    public function __construct(FeedInterface $feed)
53
    {
54 2
        $this->feed = $feed;
55 2
        $this->intervals = $this->computeIntervals($this->extractDates($feed));
56 2
    }
57
58
    /**
59
     * @param int $minDelay
60
     * @param int $sleepyDelay
61
     * @param int $sleepyDuration
62
     * @param float $marginRatio
63
     * @return \DateTime
64
     */
65 2
    public function computeNextUpdate(
66
        int $minDelay = self::DEFAULT_MIN_DELAY,
67
        int $sleepyDelay = self::DEFAULT_SLEEPY_DELAY,
68
        int $sleepyDuration = self::DEFAULT_DURATION_BEFORE_BEING_SLEEPY,
69
        float $marginRatio = self::DEFAULT_MARGIN_RATIO
70
    ): \DateTime {
71 2
        if ($this->isSleepy($sleepyDuration, $marginRatio)) {
72 1
            return (new \DateTime())->setTimestamp(time() + $sleepyDelay);
73
        }
74 1
        $feedTimeStamp = $this->getFeedTimestamp();
75 1
        $now = time();
76
        $intervals = [
77 1
            $this->getAverageInterval(),
78 1
            $this->getMedianInterval(),
79
        ];
80 1
        sort($intervals);
81 1
        $newTimestamp = $now + $minDelay;
82 1
        foreach ($intervals as $interval) {
83 1
            $computedTimestamp = $this->addInterval($feedTimeStamp, $interval, $marginRatio);
84 1
            if ($computedTimestamp > $now) {
85 1
                $newTimestamp = $computedTimestamp;
86 1
                break;
87
            }
88
        }
89 1
        return (new \DateTime())->setTimestamp($newTimestamp);
90
    }
91
92
    /**
93
     * @param int $sleepyDuration
94
     * @param float $marginRatio
95
     * @return bool
96
     */
97 2
    public function isSleepy(int $sleepyDuration, float $marginRatio): bool
98
    {
99 2
        return time() > $this->addInterval($this->getFeedTimestamp(), $sleepyDuration, $marginRatio);
100
    }
101
102
    /**
103
     * @param int $ts
104
     * @param int $interval
105
     * @param float $marginRatio
106
     * @return int
107
     */
108 2
    public function addInterval(int $ts, int $interval, float $marginRatio): int
109
    {
110 2
        return $ts + intval($interval + $marginRatio * $interval);
111
    }
112
113
    /**
114
     * @return array
115
     */
116 2
    public function getIntervals(): array
117
    {
118 2
        return $this->intervals;
119
    }
120
121
    /**
122
     * @return int
123
     */
124 1
    public function getMinInterval(): int
125
    {
126 1
        return min($this->intervals);
127
    }
128
129
    /**
130
     * @return int
131
     */
132
    public function getMaxInterval(): int
133
    {
134
        return max($this->intervals);
135
    }
136
137
    /**
138
     * @return int
139
     */
140 1
    public function getAverageInterval(): int
141
    {
142 1
        $total = array_sum($this->intervals);
143
144 1
        return intval(floor($total / count($this->intervals)));
145
    }
146
147
    /**
148
     * @return int
149
     */
150 1
    public function getMedianInterval(): int
151
    {
152 1
        sort($this->intervals);
153 1
        $num = floor(count($this->intervals) / 2);
154
155 1
        return $this->intervals[$num];
156
    }
157
158 2
    private function computeIntervals(array $dates): array
159
    {
160 2
        rsort($dates);
161 2
        $intervals = [];
162 2
        $current = 0;
163 2
        foreach ($dates as $date) {
164 2
            if ($current > 0) {
165 2
                $intervals[] = $current - $date;
166
            }
167 2
            $current = $date;
168
        }
169 2
        return $intervals;
170
    }
171
172 2
    private function extractDates(FeedInterface $feed): array
173
    {
174 2
        $dates = [];
175 2
        foreach ($feed as $item) {
176 2
            $dates[] = $this->getTimestamp($item);
177
        }
178 2
        return $dates;
179
    }
180
181 2
    private function getTimestamp(ItemInterface $item): ? int
182
    {
183 2
        return $item->getLastModified()->getTimestamp();
184
    }
185
186 2
    private function getFeedTimestamp(): int
187
    {
188 2
        return !! $this->feed->getLastModified() ? $this->feed->getLastModified()->getTimestamp():time();
189
    }
190
}
191