Stopwatch   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 375
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 150
dl 0
loc 375
rs 9.52
c 1
b 0
f 0
wmc 36

18 Methods

Rating   Name   Duplication   Size   Complexity  
C _addEvent() 0 70 11
A getJs() 0 15 3
A reset() 0 14 1
A _timeFromCakeToStopwatch() 0 3 1
B getString() 0 69 6
A printStatistic() 0 6 1
A disable() 0 3 1
A enable() 0 3 1
A init() 0 4 1
A getInstance() 0 7 2
A getWallTime() 0 8 1
A start() 0 3 1
A _timeToStopwatch() 0 3 1
A __construct() 0 2 1
A __clone() 0 2 1
A end() 0 3 1
A _timeToCake() 0 3 1
A stop() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Saito - The Threaded Web Forum
7
 *
8
 * @copyright Copyright (c) the Saito Project Developers
9
 * @link https://github.com/Schlaefer/Saito
10
 * @license http://opensource.org/licenses/MIT
11
 */
12
13
namespace Stopwatch\Lib;
14
15
class Stopwatch
16
{
17
18
    protected static $_startupTime = 0;
19
20
    protected static $_instance = null;
21
22
    protected static $_enableTimer = false;
23
24
    protected static $_wallStart = 0;
25
26
    protected static $_userStart = 0;
27
28
    protected static $_wallLast = 0;
29
30
    protected static $_userLast = 0;
31
32
    protected static $_events;
33
34
    protected static $_sums = [];
35
36
    protected static $_starts = [];
37
38
    protected static $_stopwatchTime = 0;
39
40
    protected static $_stopwatchCalls = 0;
41
42
    /**
43
     * get instance
44
     *
45
     * @return null|Stopwatch
46
     */
47
    public static function getInstance()
48
    {
49
        if (self::$_instance === null) {
50
            self::$_instance = new Stopwatch();
51
        }
52
53
        return self::$_instance;
54
    }
55
56
    /**
57
     * {@inheritDoc}
58
     */
59
    protected function __construct()
60
    {
61
    }
62
63
    /**
64
     * {@inheritDoc}
65
     */
66
    private function __clone()
67
    {
68
    }
69
70
    /**
71
     * reset
72
     *
73
     * @return void
74
     */
75
    public static function reset()
76
    {
77
        self::$_startupTime = 0;
78
        self::$_instance = null;
79
        self::$_enableTimer = false;
80
        self::$_wallStart = 0;
81
        self::$_userStart = 0;
82
        self::$_wallLast = 0;
83
        self::$_userLast = 0;
84
        self::$_events = [];
85
        self::$_sums = [];
86
        self::$_starts = [];
87
        self::$_stopwatchTime = 0;
88
        self::$_stopwatchCalls = 0;
89
    }
90
91
    /**
92
     * add event
93
     *
94
     * @param string $x event-name
95
     * @param string|null $event type
96
     * @return void
97
     */
98
    protected static function _addEvent($x, $event = null)
99
    {
100
        if (self::$_enableTimer === false) {
101
            return;
102
        }
103
104
        list($usec, $sec) = explode(' ', microtime());
105
        $wtime = ((float)$sec + (float)$usec);
106
        if (!self::$_wallStart) {
107
            self::$_wallStart = $wtime;
108
        }
109
110
        // phpcs:disable Generic.PHP.NoSilencedErrors.Discouraged
111
        $dat = @getrusage();
112
        // phpcs:enable Generic.PHP.NoSilencedErrors.Discouraged
113
        if (empty($dat)) {
114
            // some hosters disable getrusage() while hardening their PHP
115
            $utime = 0;
116
        } else {
117
            $utime = ($dat['ru_utime.tv_sec'] + $dat['ru_utime.tv_usec'] / 1000000);
118
        }
119
120
        if (!self::$_userStart) {
121
            self::$_userStart = $utime;
122
        }
123
124
        $udiff = ($wtime - self::$_wallStart == 0) ? 0 : $utime - self::$_userLast;
125
        self::$_userLast = $utime;
126
127
        $wdiff = ($wtime - self::$_wallStart == 0) ? 0 : $wtime - self::$_wallLast;
128
        self::$_wallLast = $wtime;
129
130
        if (!isset(self::$_starts[$x])) {
131
            self::$_starts[$x]['wtime'] = $wtime;
132
            self::$_starts[$x]['utime'] = $utime;
133
        } else {
134
            if (!isset(self::$_sums[$x]['wtime'])) {
135
                self::$_sums[$x]['wtime'] = 0;
136
                self::$_sums[$x]['utime'] = 0;
137
                self::$_sums[$x]['times'] = 0;
138
            }
139
            self::$_sums[$x]['wtime'] = self::$_sums[$x]['wtime'] + $wtime - self::$_starts[$x]['wtime'];
140
            self::$_sums[$x]['utime'] = self::$_sums[$x]['utime'] + $utime - self::$_starts[$x]['utime'];
141
            self::$_sums[$x]['times'] = self::$_sums[$x]['times'] + 1;
142
            unset(self::$_starts[$x]);
143
        }
144
145
        switch ($event) {
146
            case 'start':
147
                $x = '* ' . $x;
148
                break;
149
            case 'stop':
150
                $x = '† ' . $x;
151
                break;
152
        }
153
154
        self::$_events[] = [
155
            'title' => $x,
156
            'wtime' => $wtime - self::$_wallStart,
157
            'utime' => $utime - self::$_userStart,
158
            'wdiff' => $wdiff,
159
            'udiff' => $udiff,
160
            'mem' => memory_get_usage(),
161
        ];
162
163
        // endtime
164
        list($eusec, $esec) = explode(' ', microtime());
165
        $ewtime = ((float)$esec + (float)$eusec);
166
        self::$_stopwatchTime += ($ewtime - $wtime);
167
        self::$_stopwatchCalls++;
168
    }
169
170
    /**
171
     * time to Cake start
172
     *
173
     * @return mixed
174
     */
175
    protected static function _timeToCake()
176
    {
177
        return TIME_START - $_SERVER['REQUEST_TIME_FLOAT'];
178
    }
179
180
    /**
181
     * time from cake to stopwatch
182
     *
183
     * @return float
184
     */
185
    protected static function _timeFromCakeToStopwatch(): float
186
    {
187
        return self::$_startupTime - TIME_START;
188
    }
189
190
    /**
191
     * time until stopwatch start
192
     *
193
     * @return int
194
     */
195
    protected static function _timeToStopwatch()
196
    {
197
        return self::$_startupTime - $_SERVER['REQUEST_TIME_FLOAT'];
198
    }
199
200
    /**
201
     * Get output
202
     *
203
     * @return string|void
204
     */
205
    public static function getString()
206
    {
207
        if (self::$_enableTimer === false) {
208
            return;
209
        }
210
211
        self::start('now');
212
213
        $out = "";
214
        $out .= 'Time to Cake: ' . sprintf('%05.3f', self::_timeToCake()) . " s\n";
215
        $out .= 'Cake bootstrap: ' . sprintf('%05.3f', self::_timeFromCakeToStopwatch()) . " s\n";
216
217
        $out .= "W\tU\tW_delta\tU_delta\tMem [MB]\n";
218
        $_seriesIndex = 1;
219
        foreach (self::$_events as $k => $v) {
220
            $out .= '<span id="stopwatch-' . $_seriesIndex++ . '" class="stopwatch-row">';
221
            $out .= sprintf(
222
                "%05.3f\t%05.3f\t%05.3f\t%05.3f\t%5.1f\t%s\n",
223
                $v['wtime'],
224
                $v['utime'],
225
                $v['wdiff'],
226
                $v['udiff'],
227
                $v['mem'] / 1048576,
228
                $v['title']
229
            );
230
            $out .= '</span>';
231
        }
232
233
        $out .= "\n\n";
234
235
        for ($i = 0; $i < 100; $i++) {
236
            Stopwatch::start('e');
237
            Stopwatch::stop('e');
238
        }
239
        $_e = array_pop(self::$_sums);
240
        $_eW = $_e['wtime'] / 100;
241
        $_eU = $_e['utime'] / 100;
0 ignored issues
show
Unused Code introduced by
The assignment to $_eU is dead and can be removed.
Loading history...
242
243
        self::$_events = array_slice(self::$_events, 0, -200);
244
245
        $out .= "W_sum\tU_sum\tW_%\tU_%\t#\tW_ø\n";
246
247
        $_lastTimestamp = end(self::$_events);
248
        $wlast = $_lastTimestamp['wtime'] / 100;
249
        $ulast = $_lastTimestamp['utime'] / 100;
250
        foreach (self::$_sums as $k => $v) {
251
            // on vagrant $ulast may be 0 for unknown reason when running test cases
252
            // ugly hack to suppress output in test-cases, where it isn't read anyway
253
            if (empty($ulast)) {
254
                break;
255
            }
256
            $v['wtime'] = $v['wtime'] - ($_eW * $v['times']);
257
            $v['utime'] = $v['utime'] - ($_eW * $v['times']);
258
259
            $out .= sprintf(
260
                "%05.3f\t%05.3f\t%04.1f\t%04.1f\t%u\t%05.3f\t%s\n",
261
                $v['wtime'],
262
                $v['utime'],
263
                $v['wtime'] / $wlast,
264
                $v['utime'] / $ulast,
265
                $v['times'],
266
                $v['wtime'] / $v['times'],
267
                $k
268
            );
269
        }
270
271
        $out .= "\n\n" . self::printStatistic();
272
273
        return $out;
274
    }
275
276
    /**
277
     * get json encoded
278
     *
279
     * @return string|void
280
     */
281
    public static function getJs()
282
    {
283
        if (self::$_enableTimer === false) {
284
            return;
285
        }
286
        $data = [];
287
        foreach (self::$_events as $v) {
288
            $data[] = [
289
                'label' => $v['title'],
290
                'data' => [[1, $v['wdiff']], [2, $v['udiff']]],
291
            ];
292
        }
293
        $out = json_encode($data);
294
295
        return $out;
296
    }
297
298
    /**
299
     * Init
300
     *
301
     * @return void
302
     */
303
    public static function init()
304
    {
305
        self::reset();
306
        self::$_startupTime = microtime(true);
307
    }
308
309
    /**
310
     * enable
311
     *
312
     * @return void
313
     */
314
    public static function enable()
315
    {
316
        self::$_enableTimer = true;
317
    }
318
319
    /**
320
     * disable
321
     *
322
     * @return void
323
     */
324
    public static function disable()
325
    {
326
        self::$_enableTimer = false;
327
    }
328
329
    /**
330
     * Start
331
     *
332
     * @param string $text id
333
     *
334
     * @return void
335
     */
336
    public static function start($text)
337
    {
338
        self::_addEvent($text, 'start');
339
    }
340
341
    /**
342
     * Stop.
343
     *
344
     * @param string $text id
345
     *
346
     * @return void
347
     */
348
    public static function stop($text)
349
    {
350
        self::_addEvent($text, 'stop');
351
    }
352
353
    /**
354
     * Print static
355
     *
356
     * @return string
357
     */
358
    public static function printStatistic()
359
    {
360
        return self::$_stopwatchCalls . " calls with ca " . sprintf(
361
            "%05.3f",
362
            self::$_stopwatchTime
363
        ) . ' sec overhead.';
364
    }
365
366
    /**
367
     * Gets current accumulated wall time
368
     *
369
     * @return float
370
     */
371
    public static function getWallTime(): float
372
    {
373
        self::start('getWallTime()');
374
        self::end('getWallTime()');
375
        $time = self::$_events[count(self::$_events) - 1]['wtime'] +
376
            self::_timeToStopwatch();
377
378
        return round($time, 3);
379
    }
380
381
    /**
382
     * Alias for self::stop
383
     *
384
     * @param string $text key
385
     * @return void
386
     */
387
    public static function end($text)
388
    {
389
        self::stop($text);
390
    }
391
}
392