1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
<?php declare(strict_types = 1); /** * Duplicate database query collector. * * @package query-monitor */
if ( ! defined( 'ABSPATH' ) ) { exit; }
/** * @extends QM_DataCollector<QM_Data_DB_Dupes> */ class QM_Collector_DB_Dupes extends QM_DataCollector {
public $id = 'db_dupes';
public function get_storage(): QM_Data { return new QM_Data_DB_Dupes(); }
/** * @return void */ public function process() { /** @var QM_Collector_DB_Queries|null $dbq */ $dbq = QM_Collectors::get( 'db_queries' );
if ( ! $dbq ) { return; }
/** @var QM_Data_DB_Queries $dbq_data */ $dbq_data = $dbq->get_data();
if ( empty( $dbq_data->dupes ) ) { return; }
// Filter out SQL queries that do not have dupes $this->data->dupes = array_filter( $dbq_data->dupes, array( $this, 'filter_dupe_items' ) );
// Ignore dupes from `WP_Query->set_found_posts()` unset( $this->data->dupes['SELECT FOUND_ROWS()'] );
$stacks = array(); $callers = array(); $components = array(); $times = array();
// Loop over all SQL queries that have dupes foreach ( $this->data->dupes as $sql => $query_ids ) {
// Loop over each query foreach ( $query_ids as $query_id ) {
if ( isset( $dbq_data->rows[ $query_id ]['trace'] ) ) { /** @var QM_Backtrace */ $trace = $dbq_data->rows[ $query_id ]['trace']; $stack = array_column( $trace->get_filtered_trace(), 'id' ); $component = $trace->get_component();
// Populate the component counts for this query if ( isset( $components[ $sql ][ $component->name ] ) ) { $components[ $sql ][ $component->name ]++; } else { $components[ $sql ][ $component->name ] = 1; } } else { /** @var array<int, string> */ $stack = $dbq_data->rows[ $query_id ]['stack']; }
// Populate the caller counts for this query if ( isset( $callers[ $sql ][ $stack[0] ] ) ) { $callers[ $sql ][ $stack[0] ]++; } else { $callers[ $sql ][ $stack[0] ] = 1; }
// Populate the stack for this query $stacks[ $sql ][] = $stack;
// Populate the time for this query if ( isset( $times[ $sql ] ) ) { $times[ $sql ] += $dbq->data->rows[ $query_id ]['ltime']; } else { $times[ $sql ] = $dbq->data->rows[ $query_id ]['ltime']; } }
// Get the callers which are common to all stacks for this query $common = call_user_func_array( 'array_intersect', $stacks[ $sql ] );
// Remove callers which are common to all stacks for this query foreach ( $stacks[ $sql ] as $i => $stack ) { $stacks[ $sql ][ $i ] = array_values( array_diff( $stack, $common ) );
// No uncommon callers within the stack? Just use the topmost caller. if ( empty( $stacks[ $sql ][ $i ] ) ) { $stacks[ $sql ][ $i ] = array_keys( $callers[ $sql ] ); } }
// Wave a magic wand $sources[ $sql ] = array_count_values( array_column( $stacks[ $sql ], 0 ) );
}
if ( ! empty( $sources ) ) { $this->data->dupe_sources = $sources; $this->data->dupe_callers = $callers; $this->data->dupe_components = $components; $this->data->dupe_times = $times; }
} }
/** * @param array<string, QM_Collector> $collectors * @param QueryMonitor $qm * @return array<string, QM_Collector> */ function register_qm_collector_db_dupes( array $collectors, QueryMonitor $qm ) { $collectors['db_dupes'] = new QM_Collector_DB_Dupes(); return $collectors; }
add_filter( 'qm/collectors', 'register_qm_collector_db_dupes', 25, 2 );
|