Completed
Push — master ( f9f913...137204 )
by Alec
02:37
created

AbstractTimer::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

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