Passed
Push — master ( 77527d...a5b99b )
by Alec
03:07
created

Timer   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 267
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 104
dl 0
loc 267
ccs 106
cts 106
cp 1
rs 9.68
c 0
b 0
f 0
wmc 34

16 Methods

Rating   Name   Duplication   Size   Complexity  
A elapsed() 0 9 3
A forceStart() 0 4 1
A __construct() 0 3 1
A start() 0 3 1
B format() 0 36 6
A current() 0 4 1
A mark() 0 19 4
A getMinValue() 0 3 1
A getName() 0 3 1
A getCurrentValue() 0 3 1
A getMaxValue() 0 3 1
A getTimerValues() 0 21 4
A check() 0 8 2
A report() 0 21 5
A getCount() 0 3 1
A getAvgValue() 0 3 1
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 13
    public function __construct(?string $name = null)
41
    {
42 13
        $this->name = $name ?? static::_DEFAULT;
43 13
    }
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 7
    public function start(): void
61
    {
62 7
        $this->previous = $this->start = $this->current();
63 7
    }
64
65
    /**
66
     * @return float
67
     */
68 7
    private function current(): float
69
    {
70
        return
71 7
            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|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.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(
143
        ?bool $formatted = null,
144
        ?bool $extended = null,
145
        ?int $units = null,
146
        ?int $precision = null
147
    ): iterable {
148 2
        if (!$this->count) {
149 1
            $this->check();
150
        }
151 2
        $formatted = $formatted ?? false;
152 2
        $current = $formatted ? $this->format($this->currentValue, $units, $precision) : $this->currentValue;
153 2
        $report = [];
154 2
        if ($current) {
155 1
            $name = $this->getName();
156
            $report = [
157 1
                static::_NAME => $name,
158 1
                static::_LAST => $current,
159 1
                static::_EXTENDED => $extended ? $this->getTimerValues($formatted) : null
160
            ];
161
        }
162 2
        return $report;
163
    }
164
165
    /**
166
     * Marks the elapsed time.
167
     * If timer was not started starts the timer.
168
     */
169 6
    public function check(): Timer
170
    {
171 6
        if (null !== $this->previous) {
172 5
            $this->mark();
173
        } else {
174 1
            $this->start();
175
        }
176 6
        return $this;
177
    }
178
179 5
    private function mark(): void
180
    {
181 5
        $current = $this->current();
182 5
        $this->currentValue = $current - $this->previous;
183 5
        $this->previous = $current;
184
185 5
        if ($this->count) {
186 3
            if ($this->currentValue < $this->minValue) {
187 2
                $this->minValue = $this->currentValue;
188
            }
189 3
            if ($this->currentValue > $this->maxValue) {
190 2
                $this->maxValue = $this->currentValue;
191
            }
192 3
            $this->avgValue = (($this->avgValue * $this->count) + $this->currentValue) / ++$this->count;
193
        } else {
194 5
            $this->count = 1;
195 5
            $this->maxValue = $this->currentValue;
196 5
            $this->minValue = $this->currentValue;
197 5
            $this->avgValue = $this->currentValue;
198
        }
199 5
    }
200
201
    /**
202
     * @return string
203
     */
204 5
    public function getName(): string
205
    {
206 5
        return $this->name;
207
    }
208
209
    /**
210
     * @param bool $formatted
211
     * @param int|null $units
212
     * @param int|null $precision
213
     * @return iterable
214
     */
215 3
    public function getTimerValues(bool $formatted = true, ?int $units = null, ?int $precision = null): iterable
216
    {
217 3
        if (!$count = $this->getCount()) {
218 1
            throw new \RuntimeException('Timer has not been started.');
219
        }
220 2
        $minValue = ($count === 1) ? $this->getCurrentValue() : $this->getMinValue();
221
        return
222 2
            $formatted ?
223
                [
224 1
                    static::_LAST => $this->format($this->getCurrentValue(), $units, $precision),
225 1
                    static::_AVG => $this->format($this->getAvgValue(), $units, $precision),
226 1
                    static::_MIN => $this->format($minValue, $units, $precision),
227 1
                    static::_MAX => $this->format($this->getMaxValue(), $units, $precision),
228 1
                    static::_COUNT => $count,
229
                ] :
230
                [
231 1
                    static::_LAST => $this->getCurrentValue(),
232 1
                    static::_AVG => $this->getAvgValue(),
233 1
                    static::_MIN => $minValue,
234 1
                    static::_MAX => $this->getMaxValue(),
235 2
                    static::_COUNT => $count,
236
                ];
237
    }
238
239
    /**
240
     * @return int|null
241
     */
242 4
    public function getCount(): ?int
243
    {
244 4
        return $this->count;
245
    }
246
247
    /**
248
     * @return float|null
249
     */
250 3
    public function getCurrentValue(): ?float
251
    {
252 3
        return $this->currentValue;
253
    }
254
255
    /**
256
     * @return float|null
257
     */
258 3
    public function getMinValue(): ?float
259
    {
260 3
        return $this->minValue;
261
    }
262
263
    /**
264
     * @return float|null
265
     */
266 3
    public function getAvgValue(): ?float
267
    {
268 3
        return $this->avgValue;
269
    }
270
271
    /**
272
     * @return float|null
273
     */
274 3
    public function getMaxValue(): ?float
275
    {
276 3
        return $this->maxValue;
277
    }
278
}
279