Test Setup Failed
Push — develop ( 7cd2b2...750858 )
by Alec
03:42
created

AbstractTimer::updateCurrentAndPrevious()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace AlecRabbit\Tools;
4
5
use AlecRabbit\Tools\Contracts\TimerInterface;
6
use AlecRabbit\Tools\Reports\Contracts\ReportableInterface;
7
use AlecRabbit\Tools\Reports\Factory;
8
use AlecRabbit\Tools\Reports\TimerReport;
9
use AlecRabbit\Tools\Reports\Traits\HasReport;
10
use AlecRabbit\Tools\Traits\TimerFields;
11
use function AlecRabbit\typeOf;
12
13
abstract class AbstractTimer implements TimerInterface, ReportableInterface
14
{
15
    use TimerFields, HasReport;
16
17
    /**
18
     * Timer constructor.
19
     * @param null|string $name
20
     * @param bool $start
21
     * @throws \Exception
22
     */
23
    public function __construct(?string $name = null, bool $start = true)
24
    {
25
        $this->checkEnvironment();
26
        $this->name = $this->defaultName($name);
27
        $this->creationTime = new \DateTimeImmutable();
28
        $this->computeElapsed();
29
        $this->report = (new TimerReport())->buildOn($this);
30
        if ($start) {
31
            $this->start();
32
        }
33
    }
34
35
    protected function checkEnvironment(): void
36
    {
37
    }
38
39
    /**
40
     * Starts the timer.
41
     *
42
     * @return void
43
     */
44
    public function start(): void
45
    {
46
        if ($this->isNotStarted()) {
47
            $this->previous = $this->current();
48
        }
49
        $this->started = true;
50
    }
51
52
    abstract public function current();
53
54
    /**
55
     * Marks the time.
56
     * If timer was not started starts the timer.
57
     * @param int|null $iterationNumber
58
     * @return self
59
     */
60
    public function check(?int $iterationNumber = null): self
61
    {
62
        if ($this->isStopped()) {
63
            throw new \RuntimeException('Timer[' . $this->name . '] is already stopped.');
64
        }
65
        if ($this->isNotStarted()) {
66
            $this->start();
67
        } else {
68
            $this->mark($iterationNumber);
69
        }
70
        return $this;
71
    }
72
73
    /**
74
     * @param int|null $iterationNumber
75
     */
76
    protected function mark(?int $iterationNumber = null): void
77
    {
78
        $current = $this->current();
79
        $this->currentValue = $current - $this->previous;
80
        $this->previous = $current;
81
82
        $this->compute($iterationNumber);
83
    }
84
85
    /**
86
     * @param null|int $iterationNumber
87
     */
88
    protected function compute(?int $iterationNumber): void
89
    {
90
        if (0 !== $this->count) {
91
            ++$this->count;
92
            $this->checkMinValue($iterationNumber);
93
            $this->checkMaxValue($iterationNumber);
94
            $this->computeAverage();
95
        } else {
96
            $this->initValues();
97
        }
98
    }
99
100
    /**
101
     * @param null|int $iterationNumber
102
     */
103
    protected function checkMinValue(?int $iterationNumber): void
104
    {
105
        if ($this->currentValue < $this->minValue) {
106
            $this->minValue = $this->currentValue;
107
            $this->minValueIteration = $iterationNumber ?? $this->count;
108
        }
109
    }
110
111
    /**
112
     * @param null|int $iterationNumber
113
     */
114
    protected function checkMaxValue(?int $iterationNumber): void
115
    {
116
        if ($this->currentValue > $this->maxValue) {
117
            $this->maxValue = $this->currentValue;
118
            $this->maxValueIteration = $iterationNumber ?? $this->count;
119
        }
120
    }
121
122
    protected function computeAverage(): void
123
    {
124
        $this->avgValue = (($this->avgValue * ($this->count - 1)) + $this->currentValue) / $this->count;
125
    }
126
127
    protected function initValues(): void
128
    {
129
        $this->maxValueIteration = $this->minValueIteration = $this->count = 1;
130
        $this->maxValue = $this->currentValue;
131
        $this->minValue = $this->currentValue;
132
        $this->avgValue = $this->currentValue;
133
    }
134
135
    /**
136
     * Stops the timer and returns elapsed time string.
137
     * @return string
138
     * @throws \Exception
139
     */
140
    public function elapsed(): string
141
    {
142
//        if ($this->isNotStarted()) { // todo investigate if it's needed here
143
//            $this->start();
144
//        }
145
        if ($this->isNotStopped()) {
146
            $this->stop();
147
        }
148
        return
149
            $this->formattedElapsed();
150
    }
151
152
    /**
153
     * Stops the timer.
154
     * @throws \Exception
155
     */
156
    public function stop(): void
157
    {
158
        $this->computeElapsed();
159
        $this->stopped = true;
160
    }
161
162
    /**
163
     * @throws \Exception
164
     */
165
    protected function computeElapsed(): void
166
    {
167
        $this->elapsed = (new \DateTimeImmutable())->diff($this->creationTime);
168
    }
169
170
    /**
171
     * @return string
172
     * @throws \Exception
173
     */
174
    protected function formattedElapsed(): string
175
    {
176
        return
177
            Factory::getTimerReportFormatter()::formatElapsed($this->elapsed);
178
    }
179
180
    /**
181
     * @return \DateInterval
182
     * @throws \Exception
183
     */
184
    public function getElapsed(): \DateInterval
185
    {
186
        if ($this->isNotStopped()) {
187
            $this->computeElapsed();
188
        }
189
        return $this->elapsed;
190
    }
191
192
    /**
193
     * @param int|float $start
194
     * @param int|float $stop
195
     * @param null|int $iterationNumber
196
     * @return self
197
     */
198
    public function bounds($start, $stop, ?int $iterationNumber = null): self
199
    {
200
        $this->assertStartAndStop($start, $stop);
201
        if ($this->isNotStarted()) {
202
            $this->start();
203
        }
204
        $this->updateCurrentAndPrevious($start, $stop);
205
206
        $this->compute($iterationNumber);
207
        return $this;
208
    }
209
210
    /**
211
     * @param int|float $start
212
     * @param int|float $stop
213
     */
214
    protected function assertStartAndStop($start, $stop): void
215
    {
216
        // todo optimize (make abstract here?)
217
        $start_ok = false;
218
        $stop_ok = false;
219
        if (is_int($start) || is_float($start)) {
0 ignored issues
show
introduced by
The condition is_float($start) is always true.
Loading history...
220
            $start_ok = true;
221
        }
222
        if (is_int($stop) || is_float($stop)) {
0 ignored issues
show
introduced by
The condition is_float($stop) is always true.
Loading history...
223
            $stop_ok = true;
224
        }
225
        if (!$start_ok) {
0 ignored issues
show
introduced by
The condition $start_ok is always true.
Loading history...
226
            throw new \RuntimeException('Start value is NOT ok. [' . typeOf($start) . ']');
227
        }
228
        if (!$stop_ok) {
0 ignored issues
show
introduced by
The condition $stop_ok is always true.
Loading history...
229
            throw new \RuntimeException('Stop value is NOT ok. [' . typeOf($stop) . ']');
230
        }
231
    }
232
233
    /**
234
     * @param float $start
235
     * @param float $stop
236
     */
237
    protected function updateCurrentAndPrevious($start, $stop): void
238
    {
239
        $this->currentValue = $stop - $start;
240
        $this->previous = $stop;
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     * @throws \Exception
246
     */
247
    protected function beforeReport(): void
248
    {
249
        if ($this->isNotStarted()) {
250
            $this->start();
251
            $this->mark();
252
        }
253
        $this->stop();
254
    }
255
}
256