Passed
Push — master ( 652cf6...e1aaee )
by Thomas
02:20
created

Benchmark::benchmark()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 47
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 29
c 1
b 0
f 0
nc 10
nop 2
dl 0
loc 47
rs 8.8337
1
<?php
2
3
namespace LeKoala\DevToolkit;
4
5
use Psr\Log\LoggerInterface;
6
use LeKoala\Base\Helpers\ClassHelper;
0 ignored issues
show
Bug introduced by
The type LeKoala\Base\Helpers\ClassHelper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use SilverStripe\Core\Injector\Injector;
8
9
class Benchmark
10
{
11
    /**
12
     * @var array<string,array<mixed>>
13
     */
14
    protected static array $metrics = [];
15
16
    /**
17
     * For example 0.001
18
     *
19
     * @var float
20
     */
21
    public static float $slow_threshold = 0;
22
23
    /**
24
     * @return \Monolog\Logger
25
     */
26
    public static function getLogger()
27
    {
28
        $parts = explode("\\", get_called_class());
29
        $class = array_pop($parts);
30
        return Injector::inst()->get(LoggerInterface::class)->withName(ClassHelper::getClassWithoutNamespace($class));
31
    }
32
33
    /**
34
     * A dead simple benchmark function
35
     *
36
     * Usage : bm(function() { // Insert here the code to benchmark });
37
     * Alternative usage : bm() ; // Code to test ; bm();
38
     *
39
     * @param callable $cb
40
     * @return void
41
     */
42
    public static function run($cb = null)
43
    {
44
        $data = self::benchmark($cb);
45
        if (!$data) {
0 ignored issues
show
introduced by
The condition $data is always false.
Loading history...
46
            return;
47
        }
48
        printf("It took %s seconds and used %s memory", $data['time'], $data['memory']);
49
        die();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
50
    }
51
52
    /**
53
     * @param null|callable $cb
54
     * @param string|null $name
55
     * @return false|array{'time': string, 'memory': string}
56
     */
57
    protected static function benchmark($cb = null, $name = null)
58
    {
59
        static $_data = null;
60
61
        if ($name) {
62
            $data = self::$metrics[$name] ?? null;
63
        } else {
64
            $data = $_data;
65
        }
66
67
        // No callback scenario
68
        if ($cb === null) {
69
            if ($data === null) {
70
                $data = [
71
                    'startTime' => microtime(true),
72
                    'startMemory' => memory_get_usage(),
73
                ];
74
                if ($name) {
75
                    self::$metrics[$name] = $data;
76
                }
77
                // Allow another call
78
                return false;
79
            } else {
80
                $startTime = $data['startTime'];
81
                $startMemory = $data['startMemory'];
82
83
                // Clear for future calls
84
                if (!$name) {
85
                    $_data = null;
86
                }
87
            }
88
        } else {
89
            $startTime = microtime(true);
90
            $startMemory = memory_get_usage();
91
92
            $cb();
93
        }
94
95
        $endTime = microtime(true);
96
        $endMemory = memory_get_usage();
97
98
        $time = sprintf("%.6f", $endTime - $startTime);
99
        $memory = self::bytesToHuman($endMemory - $startMemory);
100
101
        return [
102
            'time' => $time,
103
            'memory' => $memory,
104
        ];
105
    }
106
107
    protected static function bytesToHuman(float $bytes, int $decimals = 2): string
0 ignored issues
show
Unused Code introduced by
The parameter $decimals is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

107
    protected static function bytesToHuman(float $bytes, /** @scrutinizer ignore-unused */ int $decimals = 2): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
108
    {
109
        if ($bytes == 0) {
110
            return "0.00 B";
111
        }
112
        $e = floor(log($bytes, 1024));
113
        return round($bytes / pow(1024, $e), 2) . ['B', 'KB', 'MB', 'GB', 'TB', 'PB'][$e];
114
    }
115
116
    /**
117
     * @param string $name
118
     * @param null|callable $cb
119
     * @return void
120
     */
121
    public static function log(string $name, $cb = null): void
122
    {
123
        // Helps dealing with nasty ajax calls in the admin
124
        $ignoredPaths = [
125
            'schema/'
126
        ];
127
        $requestUri = $_SERVER['REQUEST_URI'] ?? '';
128
        foreach ($ignoredPaths as $ignoredPath) {
129
            if (str_contains($requestUri, $ignoredPath)) {
130
                return;
131
            }
132
        }
133
        $data = self::benchmark($cb, $name);
134
        if (!$data) {
0 ignored issues
show
introduced by
The condition $data is always false.
Loading history...
135
            return;
136
        }
137
138
        $time = $data['time'];
139
        $memory = $data['memory'];
140
141
        if (self::$slow_threshold && $time < self::$slow_threshold) {
142
            return;
143
        }
144
145
        $bt = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
146
        $line = $bt[1]['line'] ?? 0;
147
        $file = basename($bt[1]['file'] ?? "unknown");
148
149
        self::getLogger()->debug("$name: $time seconds | $memory memory.", [$requestUri, "$file:$line"]);
150
    }
151
}
152