Passed
Push — master ( e26094...28949d )
by Paul
02:50
created

Actions::callbacksForHook()   B

Complexity

Conditions 9
Paths 12

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 18
nc 12
nop 2
dl 0
loc 28
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
namespace GeminiLabs\BlackBar\Modules;
4
5
use GeminiLabs\BlackBar\Application;
6
7
class Actions implements Module
8
{
9
    /**
10
     * @var Application
11
     */
12
    protected $app;
13
    /**
14
     * @var array
15
     */
16
    protected $entries;
17
    /**
18
     * @var array
19
     */
20
    protected $hooks;
21
    /**
22
     * @var int
23
     */
24
    protected $totalActions;
25
    /**
26
     * @var float
27
     */
28
    protected $totalTime;
29
30
    public function __construct(Application $app)
31
    {
32
        $this->app = $app;
33
        $this->entries = [];
34
        $this->hooks = [];
35
        $this->totalActions = 0;
36
        $this->totalTime = (float) 0;
37
    }
38
39
    public function callbacksForHook(string $action, array $data): void
0 ignored issues
show
Unused Code introduced by
The parameter $data 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

39
    public function callbacksForHook(string $action, /** @scrutinizer ignore-unused */ array $data): void

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...
40
    {
41
        global $wp_filter;
42
        if (!array_key_exists($action, $this->hooks)) {
43
            return;
44
        }
45
        $this->hooks[$action]['callbacks_count'] = 0;
46
        foreach ($wp_filter[$action] as $priority => $callbacks) {
47
            if (!array_key_exists($priority, $this->hooks[$action]['callbacks'])) {
48
                $this->hooks[$action]['callbacks'][$priority] = [];
49
            }
50
            foreach ($callbacks as $callback) {
51
                if (is_array($callback['function']) && 2 === count($callback['function'])) {
52
                    list($object, $method) = $callback['function'];
53
                    if (is_object($object)) {
54
                        $object = get_class($object);
55
                        // $reflection = new \ReflectionClass($object);
56
                        // if (str_starts_with($reflection->getNamespaceName(), 'GeminiLabs\BlackBar')) {
57
                        //     continue; // skip blackbar callbacks
58
                        // }
59
                    }
60
                    $this->hooks[$action]['callbacks'][$priority][] = sprintf('%s::%s', $object, $method);
61
                } elseif (is_object($callback['function'])) {
62
                    $this->hooks[$action]['callbacks'][$priority][] = get_class($callback['function']);
63
                } else {
64
                    $this->hooks[$action]['callbacks'][$priority][] = $callback['function'];
65
                }
66
                ++$this->hooks[$action]['callbacks_count'];
67
            }
68
        }
69
    }
70
71
    public function entries(): array
72
    {
73
        if (class_exists('Debug_Bar_Slow_Actions')) {
74
            return [];
75
        }
76
        if (!empty($this->entries) || empty($this->hooks)) {
77
            return $this->entries;
78
        }
79
        foreach ($this->hooks as $action => $data) {
80
            $total = $this->totalTimeForHook($data);
81
            $this->hooks[$action]['total'] = $total;
82
            $this->totalTime += $total;
83
            $this->totalActions += $data['count'];
84
            $this->callbacksForHook($action, $data);
85
        }
86
        uasort($this->hooks, [$this, 'sortByTime']);
87
        $this->entries = array_slice($this->hooks, 0, 50); // return the 50 slowest actions
88
        return $this->entries;
89
    }
90
91
    public function hasEntries(): bool
92
    {
93
        return !empty($this->entries());
94
    }
95
96
    public function id(): string
97
    {
98
        return 'glbb-actions';
99
    }
100
101
    public function isVisible(): bool
102
    {
103
        return true;
104
    }
105
106
    public function label(): string
107
    {
108
        $label = __('Hooks', 'blackbar');
109
        if (class_exists('Debug_Bar_Slow_Actions')) {
110
            return $label;
111
        }
112
        $this->entries(); // calculates the totalTime
113
        if ($this->totalTime > 0) {
114
            $label = sprintf('%s (%.2f %s)', $label, $this->totalTime, __('ms', 'blackbar'));
115
        }
116
        return $label;
117
    }
118
119
    public function render(): void
120
    {
121
        $this->app->render('panels/actions', ['actions' => $this]);
122
    }
123
124
    public function startTimer(): void
125
    {
126
        if (!isset($this->hooks[current_filter()])) {
127
            $this->hooks[current_filter()] = [
128
                'callbacks' => [],
129
                'count' => 0,
130
                'stack' => [],
131
                'time' => [],
132
            ];
133
            add_action(current_filter(), [$this, 'stopTimer'], 9999); // @phpstan-ignore-line
134
        }
135
        ++$this->hooks[current_filter()]['count'];
136
        array_push($this->hooks[current_filter()]['stack'], ['start' => microtime(true)]);
137
    }
138
139
    /**
140
     * @param mixed $filteredValue
141
     * @return mixed
142
     */
143
    public function stopTimer($filteredValue = null)
144
    {
145
        $time = array_pop($this->hooks[current_filter()]['stack']);
146
        $time['stop'] = microtime(true);
147
        array_push($this->hooks[current_filter()]['time'], $time);
148
        return $filteredValue; // In case this was a filter.
149
    }
150
151
    public function totalTimeForHook(array $data): float
152
    {
153
        $total = 0;
154
        foreach ($data['time'] as $time) {
155
            $total += ($time['stop'] - $time['start']) * 1000;
156
        }
157
        return (float) $total;
158
    }
159
160
    protected function sortByTime(array $a, array $b): int
161
    {
162
        if ($a['total'] !== $b['total']) {
163
            return ($a['total'] > $b['total']) ? -1 : 1;
164
        }
165
        return 0;
166
    }
167
}
168