Query Monitor - Version 2.6.5

Version Description

  • Avoid the "Class 'QM_Backtrace' not found" error
  • Correct the layout of the Slow Queries and Query Errors panels
  • Move back-compat CSS into its own file
  • Huge simplification of code in db.php by using parent::query()
  • Misc visual tweaks
Download this release

Release Info

Developer johnbillion
Plugin Icon 128x128 Query Monitor
Version 2.6.5
Comparing to
See all releases

Code changes from version 2.6.4 to 2.6.5

Collector.php CHANGED
@@ -42,6 +42,13 @@ abstract class QM_Collector {
42
  return $this->data;
43
  }
44
 
 
 
 
 
 
 
 
45
  public function process() {}
46
 
47
  }
42
  return $this->data;
43
  }
44
 
45
+ public static function sort_ltime( $a, $b ) {
46
+ if ( $a['ltime'] == $b['ltime'] )
47
+ return 0;
48
+ else
49
+ return ( $a['ltime'] > $b['ltime'] ) ? -1 : 1;
50
+ }
51
+
52
  public function process() {}
53
 
54
  }
Output.php CHANGED
@@ -20,6 +20,4 @@ interface QM_Output {
20
 
21
  public function output();
22
 
23
- public function get_type();
24
-
25
  }
20
 
21
  public function output();
22
 
 
 
23
  }
Util.php CHANGED
@@ -22,13 +22,6 @@ class QM_Util {
22
 
23
  private function __construct() {}
24
 
25
- public static function sort( $a, $b ) {
26
- if ( $a['ltime'] == $b['ltime'] )
27
- return 0;
28
- else
29
- return ( $a['ltime'] > $b['ltime'] ) ? -1 : 1;
30
- }
31
-
32
  public static function convert_hr_to_bytes( $size ) {
33
 
34
  # Annoyingly, wp_convert_hr_to_bytes() is defined in a file that's only
22
 
23
  private function __construct() {}
24
 
 
 
 
 
 
 
 
25
  public static function convert_hr_to_bytes( $size ) {
26
 
27
  # Annoyingly, wp_convert_hr_to_bytes() is defined in a file that's only
assets/compat.css ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+
3
+ Copyright 2014 John Blackbourn
4
+
5
+ This program is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ */
16
+
17
+ body:not(.mp6) #wpadminbar .quicklinks .menupop ul li.qm-true > a {
18
+ color: #4a4 !important;
19
+ }
20
+
21
+ body:not(.mp6) #wp-admin-bar-query-monitor-deprecated,
22
+ body:not(.mp6) #wp-admin-bar-query-monitor-stricts {
23
+ background-color: #eee !important;
24
+ }
25
+
26
+ body:not(.mp6) #wpadminbar .qm-deprecated,
27
+ body:not(.mp6) #wpadminbar .qm-strict {
28
+ background-color: #000;
29
+ }
30
+
31
+ body:not(.mp6) #wp-admin-bar-query-monitor-deprecated a,
32
+ body:not(.mp6) #wp-admin-bar-query-monitor-stricts a {
33
+ color: #555 !important;
34
+ }
35
+
36
+ body:not(.mp6) #wp-admin-bar-query-monitor.hover a small,
37
+ body:not(.mp6) #wp-admin-bar-query-monitor.hover a .ab-label {
38
+ color: #333 !important;
39
+ }
40
+
41
+ /* for mp6 on 3.7, reduce the menu icon line height */
42
+ body.mp6 #wpadminbar #wp-admin-bar-query-monitor .ab-icon {
43
+ line-height: 30px !important;
44
+ }
45
+
46
+ @media screen and (max-width: 782px) {
47
+ /* for default 3.7, hide the menu icon */
48
+ body:not(.mp6) #wpadminbar #wp-admin-bar-query-monitor .ab-icon {
49
+ display: none !important;
50
+ }
51
+ /* for default 3.7, show the menu text */
52
+ body:not(.mp6) #wpadminbar #wp-admin-bar-query-monitor .ab-label {
53
+ display: inline-block !important;
54
+ }
55
+ }
56
+
57
+ /* remove the label margin */
58
+ #wpadminbar #wp-admin-bar-query-monitor .ab-label {
59
+ margin: 0 !important;
60
+ }
assets/query-monitor.css CHANGED
@@ -17,9 +17,6 @@ GNU General Public License for more details.
17
  #wpadminbar .quicklinks .menupop ul li.qm-true > a {
18
  color: #8c8 !important;
19
  }
20
- body:not(.mp6) #wpadminbar .quicklinks .menupop.qm-wp-37 ul li.qm-true > a {
21
- color: #4a4 !important;
22
- }
23
 
24
  #wpadminbar .quicklinks .menupop ul li.qm-true > a:hover {
25
  color: #4a4 !important;
@@ -37,19 +34,11 @@ body:not(.mp6) #wpadminbar .quicklinks .menupop.qm-wp-37 ul li.qm-true > a {
37
  #wp-admin-bar-query-monitor-stricts {
38
  background-color: #555 !important;
39
  }
40
- body:not(.mp6) .qm-wp-37 #wp-admin-bar-query-monitor-deprecated,
41
- body:not(.mp6) .qm-wp-37 #wp-admin-bar-query-monitor-stricts {
42
- background-color: #eee !important;
43
- }
44
 
45
  #wpadminbar .qm-deprecated,
46
  #wpadminbar .qm-strict {
47
  background-color: #3c3c3c;
48
  }
49
- body:not(.mp6) #wpadminbar .qm-wp-37 .qm-deprecated,
50
- body:not(.mp6) #wpadminbar .qm-wp-37 .qm-strict {
51
- background-color: #000;
52
- }
53
 
54
  #wp-admin-bar-query-monitor-expensive {
55
  background-color: #b60 !important;
@@ -92,20 +81,10 @@ body:not(.mp6) #wpadminbar .qm-wp-37 .qm-strict {
92
  color: #eee !important;
93
  }
94
 
95
- body:not(.mp6) .qm-wp-37 #wp-admin-bar-query-monitor-deprecated a,
96
- body:not(.mp6) .qm-wp-37 #wp-admin-bar-query-monitor-stricts a {
97
- color: #555 !important;
98
- }
99
-
100
  #wp-admin-bar-query-monitor small {
101
  font-size: 11px !important;
102
  }
103
 
104
- body:not(.mp6) #wp-admin-bar-query-monitor.qm-wp-37.hover a small,
105
- body:not(.mp6) #wp-admin-bar-query-monitor.qm-wp-37.hover a .ab-label {
106
- text-shadow: none !important;
107
- color: #333 !important;
108
- }
109
  #wp-admin-bar-query-monitor.hover a small,
110
  #wp-admin-bar-query-monitor.hover a .ab-label {
111
  text-shadow: none !important;
@@ -123,10 +102,6 @@ body:not(.mp6) #wp-admin-bar-query-monitor.qm-wp-37.hover a .ab-label {
123
  color: #aaa !important;
124
  display: none !important;
125
  }
126
- /* for mp6 on 3.7, reduce the menu icon line height */
127
- body.mp6 #wpadminbar #wp-admin-bar-query-monitor.qm-wp-37 .ab-icon {
128
- line-height: 30px !important;
129
- }
130
 
131
  @media screen and (max-width: 782px) {
132
  /* force menu icon to show up */
@@ -137,19 +112,6 @@ body.mp6 #wpadminbar #wp-admin-bar-query-monitor.qm-wp-37 .ab-icon {
137
  #wpadminbar #wp-admin-bar-query-monitor .ab-label {
138
  display: none !important;
139
  }
140
- /* for default 3.7, override our rule above and hide the menu icon */
141
- body:not(.mp6) #wpadminbar #wp-admin-bar-query-monitor.qm-wp-37 .ab-icon {
142
- display: none !important;
143
- }
144
- /* for default 3.7, override our rule above and show the menu text */
145
- body:not(.mp6) #wpadminbar #wp-admin-bar-query-monitor.qm-wp-37 .ab-label {
146
- display: inline-block !important;
147
- }
148
- }
149
-
150
- /* for 3.7, fix the label margin */
151
- #wpadminbar #wp-admin-bar-query-monitor.qm-wp-37 .ab-label {
152
- margin: 0 !important;
153
  }
154
 
155
  #qm {
@@ -366,7 +328,7 @@ body.js #qm-wrapper {
366
  }
367
 
368
  .qm a.qm-toggle {
369
- color: #ccc !important;
370
  padding: 0 3px !important;
371
  border: 1px solid transparent !important;
372
  }
@@ -374,7 +336,7 @@ body.js #qm-wrapper {
374
  .qm a.qm-toggle:hover {
375
  text-decoration: none !important;
376
  border-color: #e1e1e1 !important;
377
- color: #999 !important;
378
  background: #eee !important;
379
  }
380
 
17
  #wpadminbar .quicklinks .menupop ul li.qm-true > a {
18
  color: #8c8 !important;
19
  }
 
 
 
20
 
21
  #wpadminbar .quicklinks .menupop ul li.qm-true > a:hover {
22
  color: #4a4 !important;
34
  #wp-admin-bar-query-monitor-stricts {
35
  background-color: #555 !important;
36
  }
 
 
 
 
37
 
38
  #wpadminbar .qm-deprecated,
39
  #wpadminbar .qm-strict {
40
  background-color: #3c3c3c;
41
  }
 
 
 
 
42
 
43
  #wp-admin-bar-query-monitor-expensive {
44
  background-color: #b60 !important;
81
  color: #eee !important;
82
  }
83
 
 
 
 
 
 
84
  #wp-admin-bar-query-monitor small {
85
  font-size: 11px !important;
86
  }
87
 
 
 
 
 
 
88
  #wp-admin-bar-query-monitor.hover a small,
89
  #wp-admin-bar-query-monitor.hover a .ab-label {
90
  text-shadow: none !important;
102
  color: #aaa !important;
103
  display: none !important;
104
  }
 
 
 
 
105
 
106
  @media screen and (max-width: 782px) {
107
  /* force menu icon to show up */
112
  #wpadminbar #wp-admin-bar-query-monitor .ab-label {
113
  display: none !important;
114
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  }
116
 
117
  #qm {
328
  }
329
 
330
  .qm a.qm-toggle {
331
+ color: #bbb !important;
332
  padding: 0 3px !important;
333
  border: 1px solid transparent !important;
334
  }
336
  .qm a.qm-toggle:hover {
337
  text-decoration: none !important;
338
  border-color: #e1e1e1 !important;
339
+ color: #888 !important;
340
  background: #eee !important;
341
  }
342
 
collectors/db_callers.php CHANGED
@@ -31,6 +31,7 @@ class QM_Collector_DB_Callers extends QM_Collector {
31
  if ( $dbq = QueryMonitor::get_collector( 'db_queries' ) ) {
32
  if ( isset( $dbq->data['times'] ) ) {
33
  $this->data['times'] = $dbq->data['times'];
 
34
  }
35
  if ( isset( $dbq->data['types'] ) ) {
36
  $this->data['types'] = $dbq->data['types'];
31
  if ( $dbq = QueryMonitor::get_collector( 'db_queries' ) ) {
32
  if ( isset( $dbq->data['times'] ) ) {
33
  $this->data['times'] = $dbq->data['times'];
34
+ usort( $this->data['times'], 'QM_Collector::sort_ltime' );
35
  }
36
  if ( isset( $dbq->data['types'] ) ) {
37
  $this->data['types'] = $dbq->data['types'];
collectors/db_components.php CHANGED
@@ -31,6 +31,7 @@ class QM_Collector_DB_Components extends QM_Collector {
31
  if ( $dbq = QueryMonitor::get_collector( 'db_queries' ) ) {
32
  if ( isset( $dbq->data['component_times'] ) ) {
33
  $this->data['times'] = $dbq->data['component_times'];
 
34
  }
35
  if ( isset( $dbq->data['types'] ) ) {
36
  $this->data['types'] = $dbq->data['types'];
31
  if ( $dbq = QueryMonitor::get_collector( 'db_queries' ) ) {
32
  if ( isset( $dbq->data['component_times'] ) ) {
33
  $this->data['times'] = $dbq->data['component_times'];
34
+ usort( $this->data['times'], 'QM_Collector::sort_ltime' );
35
  }
36
  if ( isset( $dbq->data['types'] ) ) {
37
  $this->data['types'] = $dbq->data['types'];
collectors/db_queries.php CHANGED
@@ -19,11 +19,6 @@ if ( !defined( 'SAVEQUERIES' ) )
19
  if ( !defined( 'QM_DB_EXPENSIVE' ) )
20
  define( 'QM_DB_EXPENSIVE', 0.05 );
21
 
22
- # QM_DB_LIMIT used to be a hard limit but proved to be more of an annoyance than anything. It now
23
- # just adds a nag to the top of the query table. I might remove it altogether at some point.
24
- if ( !defined( 'QM_DB_LIMIT' ) )
25
- define( 'QM_DB_LIMIT', 100 );
26
-
27
  class QM_Collector_DB_Queries extends QM_Collector {
28
 
29
  public $id = 'db_queries';
@@ -124,12 +119,6 @@ class QM_Collector_DB_Queries extends QM_Collector {
124
 
125
  }
126
 
127
- protected static function query_compat( array & $query ) {
128
-
129
- list( $query['sql'], $query['ltime'], $query['stack'] ) = $query;
130
-
131
- }
132
-
133
  public function process_db_object( $id, wpdb $db ) {
134
 
135
  $rows = array();
@@ -138,19 +127,15 @@ class QM_Collector_DB_Queries extends QM_Collector {
138
 
139
  foreach ( (array) $db->queries as $query ) {
140
 
141
- if ( ! isset( $query['sql'] ) )
142
- self::query_compat( $query );
143
-
144
- if ( false !== strpos( $query['stack'], 'wp_admin_bar' ) and !isset( $_REQUEST['qm_display_admin_bar'] ) )
145
  continue;
146
 
147
- $sql = $query['sql'];
148
- $ltime = $query['ltime'];
149
- $stack = $query['stack'];
150
- $has_component = isset( $query['trace'] );
151
- $has_results = isset( $query['result'] );
152
- $trace = null;
153
- $component = null;
154
 
155
  if ( isset( $query['result'] ) )
156
  $result = $query['result'];
@@ -169,8 +154,10 @@ class QM_Collector_DB_Queries extends QM_Collector {
169
 
170
  } else {
171
 
172
- $callers = explode( ',', $stack );
173
- $caller = trim( end( $callers ) );
 
 
174
 
175
  if ( false !== strpos( $caller, '(' ) )
176
  $caller_name = substr( $caller, 0, strpos( $caller, '(' ) ) . '()';
@@ -180,8 +167,8 @@ class QM_Collector_DB_Queries extends QM_Collector {
180
  }
181
 
182
  $sql = trim( $sql );
183
- $type = preg_split( '/\b/', $sql );
184
- $type = strtoupper( $type[1] );
185
 
186
  $this->log_type( $type );
187
  $this->log_caller( $caller_name, $ltime, $type );
@@ -218,7 +205,7 @@ class QM_Collector_DB_Queries extends QM_Collector {
218
 
219
  # @TODO put errors in here too:
220
  # @TODO proper class instead of (object)
221
- $this->data['dbs'][$id] = (object) compact( 'rows', 'types', 'has_results', 'has_component', 'total_time', 'total_qs' );
222
 
223
  }
224
 
19
  if ( !defined( 'QM_DB_EXPENSIVE' ) )
20
  define( 'QM_DB_EXPENSIVE', 0.05 );
21
 
 
 
 
 
 
22
  class QM_Collector_DB_Queries extends QM_Collector {
23
 
24
  public $id = 'db_queries';
119
 
120
  }
121
 
 
 
 
 
 
 
122
  public function process_db_object( $id, wpdb $db ) {
123
 
124
  $rows = array();
127
 
128
  foreach ( (array) $db->queries as $query ) {
129
 
130
+ # @TODO: decide what I want to do with this:
131
+ if ( false !== strpos( $query[2], 'wp_admin_bar' ) and !isset( $_REQUEST['qm_display_admin_bar'] ) )
 
 
132
  continue;
133
 
134
+ $sql = $query[0];
135
+ $ltime = $query[1];
136
+ $stack = $query[2];
137
+ $has_trace = isset( $query['trace'] );
138
+ $has_result = isset( $query['result'] );
 
 
139
 
140
  if ( isset( $query['result'] ) )
141
  $result = $query['result'];
154
 
155
  } else {
156
 
157
+ $trace = null;
158
+ $component = null;
159
+ $callers = explode( ',', $stack );
160
+ $caller = trim( end( $callers ) );
161
 
162
  if ( false !== strpos( $caller, '(' ) )
163
  $caller_name = substr( $caller, 0, strpos( $caller, '(' ) ) . '()';
167
  }
168
 
169
  $sql = trim( $sql );
170
+ $type = preg_split( '/\b/', $sql, 2, PREG_SPLIT_NO_EMPTY );
171
+ $type = strtoupper( $type[0] );
172
 
173
  $this->log_type( $type );
174
  $this->log_caller( $caller_name, $ltime, $type );
205
 
206
  # @TODO put errors in here too:
207
  # @TODO proper class instead of (object)
208
+ $this->data['dbs'][$id] = (object) compact( 'rows', 'types', 'has_result', 'has_trace', 'total_time', 'total_qs' );
209
 
210
  }
211
 
collectors/php_errors.php CHANGED
@@ -74,6 +74,10 @@ class QM_Collector_PHP_Errors extends QM_Collector {
74
 
75
  if ( error_reporting() > 0 ) {
76
 
 
 
 
 
77
  $trace = new QM_Backtrace;
78
  $caller = $trace->get_caller();
79
  $key = md5( $message . $file . $line . $caller['id'] );
74
 
75
  if ( error_reporting() > 0 ) {
76
 
77
+ if ( ! class_exists( 'QM_Backtrace' ) ) {
78
+ return false;
79
+ }
80
+
81
  $trace = new QM_Backtrace;
82
  $caller = $trace->get_caller();
83
  $key = md5( $message . $file . $line . $caller['id'] );
collectors/request.php CHANGED
@@ -112,6 +112,12 @@ class QM_Collector_Request extends QM_Collector {
112
  );
113
  break;
114
 
 
 
 
 
 
 
115
  }
116
 
117
  $this->data['queried_object'] = $qo;
112
  );
113
  break;
114
 
115
+ default:
116
+ // Unknown, but we have a queried object
117
+ $this->data['queried_object_type'] = 'unknown';
118
+ $this->data['queried_object_title'] = __( 'Unknown queried object', 'query-monitor' );
119
+ break;
120
+
121
  }
122
 
123
  $this->data['queried_object'] = $qo;
dispatchers/Html.php CHANGED
@@ -72,7 +72,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
72
 
73
  public function enqueue_assets() {
74
 
75
- global $wp_locale;
76
 
77
  wp_enqueue_style(
78
  'query-monitor',
@@ -100,6 +100,15 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
100
  )
101
  );
102
 
 
 
 
 
 
 
 
 
 
103
  }
104
 
105
  public function before_output() {
72
 
73
  public function enqueue_assets() {
74
 
75
+ global $wp_locale, $wp_version;
76
 
77
  wp_enqueue_style(
78
  'query-monitor',
100
  )
101
  );
102
 
103
+ if ( floatval( $wp_version ) <= 3.7 ) {
104
+ wp_enqueue_style(
105
+ 'query-monitor-compat',
106
+ $this->qm->plugin_url( 'assets/compat.css' ),
107
+ null,
108
+ $this->qm->plugin_ver( 'assets/compat.css' )
109
+ );
110
+ }
111
+
112
  }
113
 
114
  public function before_output() {
output/Headers.php CHANGED
@@ -26,8 +26,4 @@ class QM_Output_Headers implements QM_Output {
26
  return false;
27
  }
28
 
29
- final public function get_type() {
30
- return 'headers';
31
- }
32
-
33
  }
26
  return false;
27
  }
28
 
 
 
 
 
29
  }
output/Html.php CHANGED
@@ -180,8 +180,4 @@ class QM_Output_Html implements QM_Output {
180
 
181
  }
182
 
183
- final public function get_type() {
184
- return 'html';
185
- }
186
-
187
  }
180
 
181
  }
182
 
 
 
 
 
183
  }
output/html/db_callers.php CHANGED
@@ -54,9 +54,7 @@ class QM_Output_Html_DB_Callers extends QM_Output_Html {
54
 
55
  echo '<tbody>';
56
 
57
- usort( $data['times'], 'QM_Util::sort' );
58
-
59
- foreach ( $data['times'] as $caller => $row ) {
60
  $total_time += $row['ltime'];
61
  $stime = number_format_i18n( $row['ltime'], 4 );
62
 
54
 
55
  echo '<tbody>';
56
 
57
+ foreach ( $data['times'] as $row ) {
 
 
58
  $total_time += $row['ltime'];
59
  $stime = number_format_i18n( $row['ltime'], 4 );
60
 
output/html/db_components.php CHANGED
@@ -55,9 +55,7 @@ class QM_Output_Html_DB_Components extends QM_Output_Html {
55
 
56
  echo '<tbody>';
57
 
58
- usort( $data['times'], 'QM_Util::sort' );
59
-
60
- foreach ( $data['times'] as $component => $row ) {
61
  $total_time += $row['ltime'];
62
  $total_calls += $row['calls'];
63
  $stime = number_format_i18n( $row['ltime'], 4 );
55
 
56
  echo '<tbody>';
57
 
58
+ foreach ( $data['times'] as $row ) {
 
 
59
  $total_time += $row['ltime'];
60
  $total_calls += $row['calls'];
61
  $stime = number_format_i18n( $row['ltime'], 4 );
output/html/db_queries.php CHANGED
@@ -132,13 +132,11 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
132
 
133
  protected function output_queries( $name, stdClass $db, array $data ) {
134
 
135
- $max_exceeded = $db->total_qs > QM_DB_LIMIT;
136
-
137
  $span = 4;
138
 
139
- if ( $db->has_results )
140
  $span++;
141
- if ( $db->has_component )
142
  $span++;
143
 
144
  echo '<div class="qm qm-queries" id="' . $this->collector->id() . '-' . sanitize_title( $name ) . '">';
@@ -148,7 +146,7 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
148
  echo '<th colspan="' . $span . '">' . sprintf( __( '%s Queries', 'query-monitor' ), $name ) . '</th>';
149
  echo '</tr>';
150
 
151
- if ( ! $db->has_component ) {
152
  echo '<tr>';
153
  echo '<td colspan="' . $span . '" class="qm-warn">' . sprintf( __( 'Extended query information such as the component and affected rows is not available. Query Monitor was unable to symlink its <code>db.php</code> file into place. <a href="%s" target="_blank">See this wiki page for more information.</a>', 'query-monitor' ),
154
  'https://github.com/johnbillion/query-monitor/wiki/db.php-Symlink'
@@ -156,25 +154,15 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
156
  echo '</tr>';
157
  }
158
 
159
- if ( $max_exceeded ) {
160
- echo '<tr>';
161
- echo '<td colspan="' . $span . '" class="qm-expensive">' . sprintf( __( '%1$s %2$s queries were performed on this page load. Crikey!', 'query-monitor' ),
162
- number_format_i18n( $db->total_qs ),
163
- $name,
164
- number_format_i18n( QM_DB_LIMIT )
165
- ) . '</td>';
166
- echo '</tr>';
167
- }
168
-
169
  echo '<tr>';
170
  echo '<th scope="col" class="qm-sorted-asc">&nbsp;' . $this->build_sorter() . '</th>';
171
  echo '<th scope="col">' . __( 'Query', 'query-monitor' ) . $this->build_filter( 'type', array_keys( $db->types ) ) . '</th>';
172
- echo '<th scope="col">' . __( 'Caller', 'query-monitor' ) . $this->build_filter( 'caller', array_keys( $data['times'] ) ) . '</th>';
173
 
174
- if ( $db->has_component )
175
- echo '<th scope="col">' . __( 'Component', 'query-monitor' ) . $this->build_filter( 'component', array_keys( $data['component_times'] ) ) . '</th>';
176
 
177
- if ( $db->has_results )
178
  echo '<th scope="col">' . __( 'Rows', 'query-monitor' ) . $this->build_sorter() . '</th>';
179
 
180
  echo '<th scope="col" class="qm-num">' . __( 'Time', 'query-monitor' ) . $this->build_sorter() . '</th>';
@@ -186,7 +174,7 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
186
  echo '<tbody>';
187
 
188
  foreach ( $db->rows as $row )
189
- $this->output_query_row( $row, array( 'sql', 'caller', 'component', 'result', 'time' ) );
190
 
191
  echo '</tbody>';
192
  echo '<tfoot>';
@@ -247,6 +235,27 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
247
  $result = "<td valign='top' class='qm-row-result'>{$row['result']}</td>\n";
248
  }
249
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  if ( isset( $cols['sql'] ) )
251
  $row_attr['data-qm-db_queries-type'] = $row['type'];
252
  if ( isset( $cols['component'] ) )
@@ -265,7 +274,9 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
265
 
266
  echo "<tr{$attr}>";
267
 
268
- echo "<td valign='top' class='qm-'>" . ++$this->query_row . "</td>";
 
 
269
 
270
  if ( isset( $cols['sql'] ) )
271
  echo "<td valign='top' class='qm-row-sql qm-ltr qm-sql'>{$sql}</td>";
@@ -273,36 +284,18 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
273
  if ( isset( $cols['caller'] ) ) {
274
  echo "<td valign='top' class='qm-row-caller qm-ltr qm-has-toggle'>";
275
 
276
- $caller_name = $row['caller'];
277
-
278
- if ( isset( $row['trace'] ) ) {
279
- $caller = $row['trace']->get_caller();
280
- $caller_name = self::output_filename( $row['caller'], $caller['calling_file'], $caller['calling_line'] );
281
- }
282
-
283
  echo $caller_name;
284
 
285
- # This isn't optimal...
286
- # @TODO convert this to use our new filtered trace array when present
287
- $stack = explode( ', ', $row['stack'] );
288
- $stack = array_reverse( $stack );
289
- array_shift( $stack );
290
- $stack = implode( '<br>', $stack );
291
-
292
  if ( !empty( $stack ) ) {
293
  echo '<a href="#" class="qm-toggle" data-on="+" data-off="-">+</a>';
294
- echo '<div class="qm-toggled">' . $stack . '</div>';
295
  }
296
 
297
  echo "</td>";
298
  }
299
 
300
  if ( isset( $cols['stack'] ) ) {
301
- # This isn't optimal...
302
- $stack = explode( ', ', $row['stack'] );
303
- $stack = array_reverse( $stack );
304
- $stack = implode( '<br>', $stack );
305
- echo "<td valign='top' class='qm-row-caller qm-row-stack qm-ltr'>{$stack}</td>";
306
  }
307
 
308
  if ( isset( $cols['component'] ) )
132
 
133
  protected function output_queries( $name, stdClass $db, array $data ) {
134
 
 
 
135
  $span = 4;
136
 
137
+ if ( $db->has_result )
138
  $span++;
139
+ if ( $db->has_trace )
140
  $span++;
141
 
142
  echo '<div class="qm qm-queries" id="' . $this->collector->id() . '-' . sanitize_title( $name ) . '">';
146
  echo '<th colspan="' . $span . '">' . sprintf( __( '%s Queries', 'query-monitor' ), $name ) . '</th>';
147
  echo '</tr>';
148
 
149
+ if ( ! $db->has_trace ) {
150
  echo '<tr>';
151
  echo '<td colspan="' . $span . '" class="qm-warn">' . sprintf( __( 'Extended query information such as the component and affected rows is not available. Query Monitor was unable to symlink its <code>db.php</code> file into place. <a href="%s" target="_blank">See this wiki page for more information.</a>', 'query-monitor' ),
152
  'https://github.com/johnbillion/query-monitor/wiki/db.php-Symlink'
154
  echo '</tr>';
155
  }
156
 
 
 
 
 
 
 
 
 
 
 
157
  echo '<tr>';
158
  echo '<th scope="col" class="qm-sorted-asc">&nbsp;' . $this->build_sorter() . '</th>';
159
  echo '<th scope="col">' . __( 'Query', 'query-monitor' ) . $this->build_filter( 'type', array_keys( $db->types ) ) . '</th>';
160
+ echo '<th scope="col">' . __( 'Caller', 'query-monitor' ) . $this->build_filter( 'caller', wp_list_pluck( $data['times'], 'caller' ) ) . '</th>';
161
 
162
+ if ( $db->has_trace )
163
+ echo '<th scope="col">' . __( 'Component', 'query-monitor' ) . $this->build_filter( 'component', wp_list_pluck( $data['component_times'], 'component' ) ) . '</th>';
164
 
165
+ if ( $db->has_result )
166
  echo '<th scope="col">' . __( 'Rows', 'query-monitor' ) . $this->build_sorter() . '</th>';
167
 
168
  echo '<th scope="col" class="qm-num">' . __( 'Time', 'query-monitor' ) . $this->build_sorter() . '</th>';
174
  echo '<tbody>';
175
 
176
  foreach ( $db->rows as $row )
177
+ $this->output_query_row( $row, array( 'row', 'sql', 'caller', 'component', 'result', 'time' ) );
178
 
179
  echo '</tbody>';
180
  echo '<tfoot>';
235
  $result = "<td valign='top' class='qm-row-result'>{$row['result']}</td>\n";
236
  }
237
 
238
+ if ( isset( $row['trace'] ) ) {
239
+
240
+ $caller = $row['trace']->get_caller();
241
+ $caller_name = self::output_filename( $row['caller'], $caller['calling_file'], $caller['calling_line'] );
242
+ $stack = array();
243
+ $filtered_trace = $row['trace']->get_filtered_trace();
244
+ array_shift( $filtered_trace );
245
+
246
+ foreach ( $filtered_trace as $item ) {
247
+ $stack[] = self::output_filename( $item['display'], $item['calling_file'], $item['calling_line'] );
248
+ }
249
+
250
+ } else {
251
+
252
+ $caller_name = $row['caller'];
253
+ $stack = explode( ', ', $row['stack'] );
254
+ $stack = array_reverse( $stack );
255
+ array_shift( $stack );
256
+
257
+ }
258
+
259
  if ( isset( $cols['sql'] ) )
260
  $row_attr['data-qm-db_queries-type'] = $row['type'];
261
  if ( isset( $cols['component'] ) )
274
 
275
  echo "<tr{$attr}>";
276
 
277
+ if ( isset( $cols['row'] ) ) {
278
+ echo "<td valign='top'>" . ++$this->query_row . "</td>";
279
+ }
280
 
281
  if ( isset( $cols['sql'] ) )
282
  echo "<td valign='top' class='qm-row-sql qm-ltr qm-sql'>{$sql}</td>";
284
  if ( isset( $cols['caller'] ) ) {
285
  echo "<td valign='top' class='qm-row-caller qm-ltr qm-has-toggle'>";
286
 
 
 
 
 
 
 
 
287
  echo $caller_name;
288
 
 
 
 
 
 
 
 
289
  if ( !empty( $stack ) ) {
290
  echo '<a href="#" class="qm-toggle" data-on="+" data-off="-">+</a>';
291
+ echo '<div class="qm-toggled">' . implode( '<br>', $stack ) . '</div>';
292
  }
293
 
294
  echo "</td>";
295
  }
296
 
297
  if ( isset( $cols['stack'] ) ) {
298
+ echo '<td valign="top" class="qm-row-caller qm-row-stack qm-ltr">' . implode( '<br>', $stack ) . '</td>';
 
 
 
 
299
  }
300
 
301
  if ( isset( $cols['component'] ) )
output/html/request.php CHANGED
@@ -28,13 +28,34 @@ class QM_Output_Html_Request extends QM_Output_Html {
28
 
29
  echo '<div class="qm qm-half" id="' . $this->collector->id() . '">';
30
  echo '<table cellspacing="0">';
31
- echo '<thead>';
32
- echo '<tr>';
33
- echo '<th colspan="3">' . $this->collector->name() . '</th>';
34
- echo '</tr>';
35
- echo '</thead>';
36
  echo '<tbody>';
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  $rowspan = isset( $data['qvars'] ) ? count( $data['qvars'] ) : 1;
39
 
40
  echo '<tr>';
@@ -76,32 +97,6 @@ class QM_Output_Html_Request extends QM_Output_Html {
76
 
77
  }
78
 
79
- foreach ( array(
80
- 'request' => __( 'Request', 'query-monitor' ),
81
- 'matched_rule' => __( 'Matched Rule', 'query-monitor' ),
82
- 'matched_query' => __( 'Matched Query', 'query-monitor' ),
83
- 'query_string' => __( 'Query String', 'query-monitor' ),
84
- ) as $item => $name ) {
85
-
86
- if ( !isset( $data['request'][$item] ) )
87
- continue;
88
-
89
- if ( ! empty( $data['request'][$item] ) ) {
90
- if ( in_array( $item, array( 'request', 'matched_query', 'query_string' ) ) ) {
91
- $value = self::format_url( $data['request'][$item] );
92
- } else {
93
- $value = esc_html( $data['request'][$item] );
94
- }
95
- } else {
96
- $value = '<em>' . __( 'none', 'query-monitor' ) . '</em>';
97
- }
98
-
99
- echo '<tr>';
100
- echo '<td valign="top">' . $name . '</td>';
101
- echo '<td valign="top" colspan="2">' . $value . '</td>';
102
- echo '</tr>';
103
- }
104
-
105
  if ( !empty( $data['queried_object'] ) ) {
106
 
107
  $vars = get_object_vars( $data['queried_object'] );
28
 
29
  echo '<div class="qm qm-half" id="' . $this->collector->id() . '">';
30
  echo '<table cellspacing="0">';
 
 
 
 
 
31
  echo '<tbody>';
32
 
33
+ foreach ( array(
34
+ 'request' => __( 'Request', 'query-monitor' ),
35
+ 'matched_rule' => __( 'Matched Rule', 'query-monitor' ),
36
+ 'matched_query' => __( 'Matched Query', 'query-monitor' ),
37
+ 'query_string' => __( 'Query String', 'query-monitor' ),
38
+ ) as $item => $name ) {
39
+
40
+ if ( !isset( $data['request'][$item] ) )
41
+ continue;
42
+
43
+ if ( ! empty( $data['request'][$item] ) ) {
44
+ if ( in_array( $item, array( 'request', 'matched_query', 'query_string' ) ) ) {
45
+ $value = self::format_url( $data['request'][$item] );
46
+ } else {
47
+ $value = esc_html( $data['request'][$item] );
48
+ }
49
+ } else {
50
+ $value = '<em>' . __( 'none', 'query-monitor' ) . '</em>';
51
+ }
52
+
53
+ echo '<tr>';
54
+ echo '<td valign="top">' . $name . '</td>';
55
+ echo '<td valign="top" colspan="2">' . $value . '</td>';
56
+ echo '</tr>';
57
+ }
58
+
59
  $rowspan = isset( $data['qvars'] ) ? count( $data['qvars'] ) : 1;
60
 
61
  echo '<tr>';
97
 
98
  }
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  if ( !empty( $data['queried_object'] ) ) {
101
 
102
  $vars = get_object_vars( $data['queried_object'] );
output/html/theme.php CHANGED
@@ -31,15 +31,22 @@ class QM_Output_Html_Theme extends QM_Output_Html {
31
 
32
  echo '<div class="qm qm-half" id="' . $this->collector->id() . '">';
33
  echo '<table cellspacing="0">';
34
- echo '<thead>';
 
35
  echo '<tr>';
36
- echo '<th colspan="2">' . $this->collector->name() . '</th>';
 
37
  echo '</tr>';
38
- echo '</thead>';
39
 
40
- echo '<tbody>';
 
 
 
 
 
 
41
  echo '<tr>';
42
- echo '<td>' . __( 'Template', 'query-monitor' ) . '</td>';
43
  echo '<td>' . self::output_filename( $data['template_file'], $data['template_path'] ) . '</td>';
44
  echo '</tr>';
45
 
@@ -54,7 +61,7 @@ class QM_Output_Html_Theme extends QM_Output_Html {
54
  if ( !$first )
55
  echo '<tr>';
56
 
57
- echo "<td>{$class}</td>";
58
  echo '</tr>';
59
 
60
  $first = false;
@@ -63,18 +70,6 @@ class QM_Output_Html_Theme extends QM_Output_Html {
63
 
64
  }
65
 
66
- echo '<tr>';
67
- echo '<td>' . __( 'Theme', 'query-monitor' ) . '</td>';
68
- echo "<td>{$data['stylesheet']}</td>";
69
- echo '</tr>';
70
-
71
- if ( $data['stylesheet'] != $data['template'] ) {
72
- echo '<tr>';
73
- echo '<td>' . __( 'Parent Theme', 'query-monitor' ) . '</td>';
74
- echo "<td>{$data['template']}</td>";
75
- echo '</tr>';
76
- }
77
-
78
  echo '</tbody>';
79
  echo '</table>';
80
  echo '</div>';
31
 
32
  echo '<div class="qm qm-half" id="' . $this->collector->id() . '">';
33
  echo '<table cellspacing="0">';
34
+ echo '<tbody>';
35
+
36
  echo '<tr>';
37
+ echo '<td>' . __( 'Theme', 'query-monitor' ) . '</td>';
38
+ echo '<td>' . esc_html( $data['stylesheet'] ) . '</td>';
39
  echo '</tr>';
 
40
 
41
+ if ( $data['stylesheet'] != $data['template'] ) {
42
+ echo '<tr>';
43
+ echo '<td>' . __( 'Parent Theme', 'query-monitor' ) . '</td>';
44
+ echo '<td>' . esc_html( $data['template'] ) . '</td>';
45
+ echo '</tr>';
46
+ }
47
+
48
  echo '<tr>';
49
+ echo '<td>' . __( 'Template File', 'query-monitor' ) . '</td>';
50
  echo '<td>' . self::output_filename( $data['template_file'], $data['template_path'] ) . '</td>';
51
  echo '</tr>';
52
 
61
  if ( !$first )
62
  echo '<tr>';
63
 
64
+ echo '<td>' . esc_html( $class ) . '</td>';
65
  echo '</tr>';
66
 
67
  $first = false;
70
 
71
  }
72
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  echo '</tbody>';
74
  echo '</table>';
75
  echo '</div>';
query-monitor.php CHANGED
@@ -2,7 +2,7 @@
2
  /*
3
  Plugin Name: Query Monitor
4
  Description: Monitoring of database queries, hooks, conditionals and more.
5
- Version: 2.6.4
6
  Plugin URI: https://github.com/johnbillion/query-monitor
7
  Author: John Blackbourn
8
  Author URI: https://johnblackbourn.com/
2
  /*
3
  Plugin Name: Query Monitor
4
  Description: Monitoring of database queries, hooks, conditionals and more.
5
+ Version: 2.6.5
6
  Plugin URI: https://github.com/johnbillion/query-monitor
7
  Author: John Blackbourn
8
  Author URI: https://johnblackbourn.com/
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: johnbillion
3
  Tags: debug, debugging, development, developer, performance, profiler, profiling, queries
4
  Requires at least: 3.5
5
  Tested up to: 3.9
6
- Stable tag: 2.6.4
7
  License: GPLv2 or later
8
 
9
  View debugging and performance information on database queries, hooks, conditionals, HTTP requests, redirects and more.
@@ -143,6 +143,13 @@ No, I do not accept donations. If you like the plugin, I'd love for you to [leav
143
 
144
  == Changelog ==
145
 
 
 
 
 
 
 
 
146
  = 2.6.4 =
147
  * Introduce sortable columns for database query times and numbers
148
  * Display the queried object in the Request panel
3
  Tags: debug, debugging, development, developer, performance, profiler, profiling, queries
4
  Requires at least: 3.5
5
  Tested up to: 3.9
6
+ Stable tag: 2.6.5
7
  License: GPLv2 or later
8
 
9
  View debugging and performance information on database queries, hooks, conditionals, HTTP requests, redirects and more.
143
 
144
  == Changelog ==
145
 
146
+ = 2.6.5 =
147
+ * Avoid the "Class 'QM_Backtrace' not found" error
148
+ * Correct the layout of the Slow Queries and Query Errors panels
149
+ * Move back-compat CSS into its own file
150
+ * Huge simplification of code in `db.php` by using `parent::query()`
151
+ * Misc visual tweaks
152
+
153
  = 2.6.4 =
154
  * Introduce sortable columns for database query times and numbers
155
  * Display the queried object in the Request panel
wp-content/db.php CHANGED
@@ -65,9 +65,7 @@ class QueryMonitorDB extends wpdb {
65
  /**
66
  * Perform a MySQL database query, using current database connection.
67
  *
68
- * More information can be found on the codex page.
69
- *
70
- * @since 0.71
71
  *
72
  * @param string $query Database query
73
  * @return int|false Number of rows affected/selected or false on error
@@ -79,80 +77,20 @@ class QueryMonitorDB extends wpdb {
79
  if ( $this->show_errors )
80
  $this->hide_errors();
81
 
82
- // some queries are made before the plugins have been loaded, and thus cannot be filtered with this method
83
- $query = apply_filters( 'query', $query );
84
-
85
- $return_val = 0;
86
- $this->flush();
87
-
88
- // Log how the function was called
89
- $this->func_call = "\$db->query(\"$query\")";
90
-
91
- // Keep track of the last query for debug..
92
- $this->last_query = $query;
93
-
94
- if ( SAVEQUERIES )
95
- $this->timer_start();
96
-
97
- $this->result = @mysql_query( $query, $this->dbh );
98
- $this->num_queries++;
99
-
100
- if ( SAVEQUERIES ) {
101
- $ltime = $this->timer_stop();
102
- $trace = new QM_Backtrace;
103
- $q = array(
104
- 'sql' => $query,
105
- 'ltime' => $ltime,
106
- 'stack' => implode( ', ', array_reverse( $trace->get_stack() ) ),
107
- 'trace' => $trace,
108
- 'result' => null,
109
- );
110
- # Numeric indices are for compatibility for anything else using saved queries
111
- $q[0] = $q['sql'];
112
- $q[1] = $q['ltime'];
113
- $q[2] = $q['stack'];
114
- $this->queries[$this->num_queries] = $q;
115
- }
116
-
117
- // If there is an error then take note of it..
118
- if ( $this->last_error = mysql_error( $this->dbh ) ) {
119
- if ( SAVEQUERIES )
120
- $this->queries[$this->num_queries]['result'] = new WP_Error( 'qmdb', $this->last_error );
121
- // Clear insert_id on a subsequent failed insert.
122
- if ( $this->insert_id && preg_match( '/^\s*(insert|replace)\s/i', $query ) )
123
- $this->insert_id = 0;
124
-
125
- $this->print_error();
126
- return false;
127
- }
128
-
129
- if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) {
130
- $return_val = $this->result;
131
- } elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) {
132
- $this->rows_affected = mysql_affected_rows( $this->dbh );
133
- // Take note of the insert_id
134
- if ( preg_match( '/^\s*(insert|replace)\s/i', $query ) ) {
135
- $this->insert_id = mysql_insert_id($this->dbh);
136
- }
137
- // Return number of rows affected
138
- $return_val = $this->rows_affected;
139
- } else {
140
- $num_rows = 0;
141
- while ( $row = @mysql_fetch_object( $this->result ) ) {
142
- $this->last_result[$num_rows] = $row;
143
- $num_rows++;
144
- }
145
-
146
- // Log number of rows the query returned
147
- // and return number of rows selected
148
- $this->num_rows = $num_rows;
149
- $return_val = $num_rows;
150
- }
151
-
152
- if ( SAVEQUERIES )
153
- $this->queries[$this->num_queries]['result'] = $return_val;
154
-
155
- return $return_val;
156
  }
157
 
158
  }
65
  /**
66
  * Perform a MySQL database query, using current database connection.
67
  *
68
+ * @see wpdb::query()
 
 
69
  *
70
  * @param string $query Database query
71
  * @return int|false Number of rows affected/selected or false on error
77
  if ( $this->show_errors )
78
  $this->hide_errors();
79
 
80
+ $result = parent::query( $query );
81
+
82
+ if ( ! SAVEQUERIES )
83
+ return $result;
84
+
85
+ $i = $this->num_queries - 1;
86
+ $this->queries[$i]['trace'] = new QM_Backtrace;
87
+
88
+ if ( $this->last_error )
89
+ $this->queries[$i]['result'] = new WP_Error( 'qmdb', $this->last_error );
90
+ else
91
+ $this->queries[$i]['result'] = $result;
92
+
93
+ return $result;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  }
95
 
96
  }