Completed
Push — master ( 66a93c...77527d )
by Alec
03:29
created

Timer::getTimerValues()   B

Complexity

Conditions 7
Paths 33

Size

Total Lines 20
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 17
nc 33
nop 3
dl 0
loc 20
ccs 17
cts 17
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * User: alec
4
 * Date: 14.10.18
5
 * Time: 2:19
6
 */
7
8
namespace AlecRabbit\Profiler;
9
10
class Timer implements Contracts\Timer
11
{
12
    /** @var string */
13
    private $name;
14
15
    /** @var float */
16
    private $previous;
17
18
    /** @var float */
19
    private $start;
20
21
    /** @var float */
22
    private $currentValue;
23
24
    /** @var float */
25
    private $avgValue;
26
27
    /** @var float */
28
    private $minValue;
29
30
    /** @var float */
31
    private $maxValue;
32
33
    /** @var int */
34
    private $count;
35
36
    /**
37
     * Timer constructor.
38
     * @param null|string $name
39
     */
40 12
    public function __construct(?string $name = null)
41
    {
42 12
        $this->name = $name ?? static::_DEFAULT;
43 12
    }
44
45
    /**
46
     * Starts the timer.
47
     * @return Timer
48
     */
49 2
    public function forceStart(): Timer
50
    {
51 2
        $this->start();
52 2
        return $this;
53
    }
54
55
    /**
56
     * Starts the timer.
57
     *
58
     * @return void
59
     */
60 6
    public function start(): void
61
    {
62 6
        $this->previous = $this->start = $this->current();
63 6
    }
64
65
    /**
66
     * @return float
67
     */
68 6
    private function current(): float
69
    {
70
        return
71 6
            microtime(true);
72
    }
73
74
    /**
75
     * @param bool $formatted
76
     * @return mixed
77
     */
78 4
    public function elapsed(bool $formatted = false)
79
    {
80 4
        if (!$this->start) {
81 1
            throw new \RuntimeException('Timer has not been started.');
82
        }
83 3
        $elapsed = $this->current() - $this->start;
84
85
        return
86 3
            $formatted ? $this->format($elapsed, self::UNIT_MILLISECONDS, 2) : $elapsed;
87
    }
88
89
    /**
90
     * @param float|int|null $value
91
     * @param int|null $units
92
     * @param int|null $precision
93
     * @return string
94
     */
95 4
    private function format(?float $value, ?int $units = null, int $precision = null): string
96
    {
97 4
        $units = $units ?? self::UNIT_MILLISECONDS;
98 4
        $precision = $precision ?? self::DEFAULT_PRECISION;
99 4
        $precision = (int)bounds($precision, 0, 6);
100 4
        $value = $value ?? 0;
101 4
        $suffix = 'ms';
102 4
        $coefficient = 1000;
103
104
        switch ($units) {
105 4
            case self::UNIT_HOURS:
106 1
                $suffix = 'h';
107 1
                $coefficient = 1 / 3600;
108 1
                break;
109 4
            case self::UNIT_MINUTES:
110 1
                $suffix = 'm';
111 1
                $coefficient = 1 / 60;
112 1
                break;
113 4
            case self::UNIT_SECONDS:
114 1
                $suffix = 's';
115 1
                $coefficient = 1;
116 1
                break;
117 4
            case self::UNIT_MILLISECONDS:
118 4
                $suffix = 'ms';
119 4
                $coefficient = 1000;
120 4
                break;
121 1
            case self::UNIT_MICROSECONDS:
122 1
                $suffix = 'μs';
123 1
                $coefficient = 1000000;
124 1
                break;
125
        }
126
        return
127 4
            sprintf(
128 4
                '%s%s',
129 4
                round($value * $coefficient, $precision),
130 4
                $suffix
131
            );
132
    }
133
134
    /**
135
     * @param bool|null $formatted
136
     * @param bool|null $extended
137
     * @param int|null $units
138
     * @param int|null $precision
139
     *
140
     * @return iterable
141
     */
142 2
    public function report(?bool $formatted = null, ?bool $extended = null, ?int $units = null, ?int $precision = null): iterable
143
    {
144 2
        if (!$this->count) {
145 1
            $this->check();
146
        }
147 2
        $formatted = $formatted ?? false;
148 2
        $current = $formatted ? $this->format($this->currentValue, $units, $precision) : $this->currentValue;
149 2
        $report = [];
150 2
        if ($current) {
151 1
            $name = $this->getName();
152
            $report = [
153 1
                static::_NAME => $name,
154 1
                static::_LAST => $current,
155 1
                static::_EXTENDED => $extended ? $this->getTimerValues($formatted) : null
156
            ];
157
        }
158 2
        return $report;
159
    }
160
161
    /**
162
     * Marks the elapsed time.
163
     * If timer was not started starts the timer.
164
     */
165 5
    public function check(): Timer
166
    {
167 5
        if (null !== $this->previous) {
168 4
            $this->mark();
169
        } else {
170 1
            $this->start();
171
        }
172 5
        return $this;
173
    }
174
175 4
    private function mark(): void
176
    {
177 4
        $current = $this->current();
178 4
        $this->currentValue = $current - $this->previous;
179 4
        $this->previous = $current;
180
181 4
        if ($this->count) {
182 2
            if ($this->currentValue < $this->minValue) {
183 1
                $this->minValue = $this->currentValue;
184
            }
185 2
            if ($this->currentValue > $this->maxValue) {
186 1
                $this->maxValue = $this->currentValue;
187
            }
188 2
            $this->avgValue = (($this->avgValue * $this->count) + $this->currentValue) / ++$this->count;
189
        } else {
190 4
            $this->count = 1;
191 4
            $this->maxValue = $this->currentValue;
192 4
            $this->minValue = $this->currentValue;
193 4
            $this->avgValue = $this->currentValue;
194
        }
195 4
    }
196
197
    /**
198
     * @return string
199
     */
200 5
    public function getName(): string
201
    {
202 5
        return $this->name;
203
    }
204
205
    /**
206
     * @param bool $formatted
207
     * @param int|null $units
208
     * @param int|null $precision
209
     * @return iterable
210
     */
211 2
    public function getTimerValues(bool $formatted = true, ?int $units = null, ?int $precision = null): iterable
212
    {
213 2
        if (!$count = $this->getCount()) {
214 1
            throw new \RuntimeException('Timer has not been started.');
215
        }
216 1
        $minValue = ($count === 1) ? $this->getCurrentValue() : $this->getMinValue();
217
        return [
218 1
            static::_LAST =>
219 1
                $formatted ?
220 1
                    $this->format($this->getCurrentValue(), $units, $precision) : $this->getCurrentValue(),
221 1
            static::_AVG =>
222 1
                $formatted ?
223 1
                    $this->format($this->getAvgValue(), $units, $precision) : $this->getAvgValue(),
224 1
            static::_MIN =>
225 1
                $formatted ?
226 1
                    $this->format($minValue, $units, $precision) : $minValue,
227 1
            static::_MAX =>
228 1
                $formatted ?
229 1
                    $this->format($this->getMaxValue(), $units, $precision) : $this->getMaxValue(),
230 1
            static::_COUNT => $count,
231
        ];
232
    }
233
234
    /**
235
     * @return int|null
236
     */
237 3
    public function getCount(): ?int
238
    {
239 3
        return $this->count;
240
    }
241
242
    /**
243
     * @return float|null
244
     */
245 2
    public function getCurrentValue(): ?float
246
    {
247 2
        return $this->currentValue;
248
    }
249
250
    /**
251
     * @return float|null
252
     */
253 2
    public function getMinValue(): ?float
254
    {
255 2
        return $this->minValue;
256
    }
257
258
    /**
259
     * @return float|null
260
     */
261 2
    public function getAvgValue(): ?float
262
    {
263 2
        return $this->avgValue;
264
    }
265
266
    /**
267
     * @return float|null
268
     */
269 2
    public function getMaxValue(): ?float
270
    {
271 2
        return $this->maxValue;
272
    }
273
}
274