Query Monitor - Version 2.11.0

Version Description

  • Template parts used in the current request are now listed along with the template file.
  • Fix the REST API output for embedded requests and internal API calls.
  • Enable QM's output to appear in customiser preview responses.
  • Add support for the AMP plugin by Automattic, which short-circuits template output.
  • Highlight the fact when an HTTP request has disabled certificate verification.
  • Take into account custom content directory locations that are outside of ABSPATH when removing leading paths from file names.
  • Even more fallback support for when jQuery is broken or isn't available.
  • Introduce a collector for the object cache. Only outputs an overview at the moment.
  • Better formatting in the Duplicate Queries panel.
  • Introduce a fallback method of detecting errors in queries when QM_DB is not in use.
  • Improve the initial state of QM's output when the admin toolbar is not in use.
Download this release

Release Info

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

Code changes from version 2.10.0 to 2.11.0

assets/query-monitor.css CHANGED
@@ -130,11 +130,16 @@ GNU General Public License for more details.
130
  }
131
 
132
  #qm.qm-show,
 
133
  .no-js #qm,
134
  .nojs #qm {
135
  display: block;
136
  }
137
 
 
 
 
 
138
  #qm:after {
139
  display: block !important;
140
  content: '' !important;
@@ -168,11 +173,43 @@ body.wp-admin.folded #qm {
168
  margin: 0 auto 60px !important;
169
  }
170
 
171
- #qm-wrapper > p {
 
 
 
 
172
  color: #777 !important;
173
  font: 13px/15px 'Open Sans', Arial !important;
174
- margin: 20px 20px -15px !important;
 
 
 
175
  font-style: italic !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  }
177
 
178
  .qm {
@@ -203,7 +240,7 @@ body.wp-admin.folded #qm {
203
  margin-bottom: -45px !important;
204
  }
205
 
206
- #qm.qm-theme-twentysixteen #qm-wrapper > p {
207
  margin-bottom: -45px !important;
208
  }
209
 
@@ -229,6 +266,11 @@ body.wp-admin.folded #qm {
229
  margin: 0 !important;
230
  }
231
 
 
 
 
 
 
232
  .qm td,
233
  .qm th {
234
  background: #fff !important;
@@ -389,12 +431,16 @@ body.wp-admin.folded #qm {
389
  display: none;
390
  }
391
 
 
392
  .qm a {
393
  color: #00a0d2 !important;
394
  text-decoration: none !important;
395
  text-shadow: none !important;
396
  font-weight: normal !important;
397
  }
 
 
 
398
  .qm a:focus,
399
  .qm a:hover {
400
  text-decoration: underline !important;
130
  }
131
 
132
  #qm.qm-show,
133
+ #qm.qm-peek,
134
  .no-js #qm,
135
  .nojs #qm {
136
  display: block;
137
  }
138
 
139
+ #qm.qm-peek .qm {
140
+ display: none;
141
+ }
142
+
143
  #qm:after {
144
  display: block !important;
145
  content: '' !important;
173
  margin: 0 auto 60px !important;
174
  }
175
 
176
+ #qm.qm-peek #qm-wrapper {
177
+ margin: 0 auto !important;
178
+ }
179
+
180
+ #qm-title {
181
  color: #777 !important;
182
  font: 13px/15px 'Open Sans', Arial !important;
183
+ margin: 35px 0 -15px !important;
184
+ }
185
+
186
+ #qm-title p {
187
  font-style: italic !important;
188
+ margin: 0 20px !important;
189
+ }
190
+
191
+ #qm-title ul {
192
+ margin: 10px 10px 0 !important;
193
+ font-family: Menlo, Monaco, Consolas, monospace !important;
194
+ font-size: 11px !important;
195
+ font-weight: normal !important;
196
+ font-style: normal !important;
197
+ line-height: 16px !important;
198
+ list-style: none !important;
199
+ padding: 0 !important;
200
+ }
201
+
202
+ #qm-title li {
203
+ display: inline-block !important;
204
+ width: 24em !important;
205
+ background: #fff !important;
206
+ border: 1px solid #e8e8e8 !important;
207
+ margin: 0 5px 5px 0 !important;
208
+ }
209
+
210
+ #qm-title li a {
211
+ display: block !important;
212
+ padding: 4px 7px !important;
213
  }
214
 
215
  .qm {
240
  margin-bottom: -45px !important;
241
  }
242
 
243
+ #qm.qm-theme-twentysixteen #qm-title {
244
  margin-bottom: -45px !important;
245
  }
246
 
266
  margin: 0 !important;
267
  }
268
 
269
+ #qm-conditionals table,
270
+ #qm-overview table {
271
+ table-layout: fixed !important;
272
+ }
273
+
274
  .qm td,
275
  .qm th {
276
  background: #fff !important;
431
  display: none;
432
  }
433
 
434
+ #qm-title a,
435
  .qm a {
436
  color: #00a0d2 !important;
437
  text-decoration: none !important;
438
  text-shadow: none !important;
439
  font-weight: normal !important;
440
  }
441
+
442
+ #qm-title a:focus,
443
+ #qm-title a:hover,
444
  .qm a:focus,
445
  .qm a:hover {
446
  text-decoration: underline !important;
assets/query-monitor.js CHANGED
@@ -122,11 +122,31 @@ jQuery( function($) {
122
  console.debug( qm_l10n.infinitescroll_paused );
123
  }
124
 
125
- $('#qm').show();
126
  });
127
 
128
  $('#wp-admin-bar-query-monitor,#wp-admin-bar-query-monitor-default').show();
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  }
131
 
132
  $('#qm').find('.qm-filter').on('change',function(e){
122
  console.debug( qm_l10n.infinitescroll_paused );
123
  }
124
 
125
+ $('#qm').addClass('qm-show').removeClass('qm-hide');
126
  });
127
 
128
  $('#wp-admin-bar-query-monitor,#wp-admin-bar-query-monitor-default').show();
129
 
130
+ } else {
131
+
132
+ var container = document.createDocumentFragment();
133
+
134
+ $.each( qm.menu.sub, function( i, el ) {
135
+
136
+ var new_menu = $('<li><a/></li>');
137
+ new_menu
138
+ .find('a').eq(0)
139
+ .html(el.title)
140
+ .attr('href',el.href)
141
+ ;
142
+
143
+ container.appendChild( new_menu.get(0) );
144
+
145
+ } );
146
+
147
+ $('<ul/>').appendTo('#qm-title').append(container).find('a').on('click',function(e){
148
+ $('#qm').addClass('qm-show').removeClass('qm-hide qm-peek');
149
+ } );
150
  }
151
 
152
  $('#qm').find('.qm-filter').on('change',function(e){
classes/Collector.php CHANGED
@@ -43,7 +43,7 @@ abstract class QM_Collector {
43
  protected function maybe_log_dupe( $sql, $i ) {
44
 
45
  $sql = str_replace( array( "\r\n", "\r", "\n" ), ' ', $sql );
46
- $sql = str_replace( array( "\t" ), '', $sql );
47
  $sql = preg_replace( '/[ ]+/', ' ', $sql );
48
  $sql = trim( $sql );
49
 
43
  protected function maybe_log_dupe( $sql, $i ) {
44
 
45
  $sql = str_replace( array( "\r\n", "\r", "\n" ), ' ', $sql );
46
+ $sql = str_replace( array( "\t", '`' ), '', $sql );
47
  $sql = preg_replace( '/[ ]+/', ' ', $sql );
48
  $sql = trim( $sql );
49
 
classes/Util.php CHANGED
@@ -20,6 +20,7 @@ class QM_Util {
20
  protected static $file_components = array();
21
  protected static $file_dirs = array();
22
  protected static $abspath = null;
 
23
 
24
  private function __construct() {}
25
 
@@ -44,15 +45,19 @@ class QM_Util {
44
 
45
  }
46
 
47
- public static function standard_dir( $dir, $abspath_replace = null ) {
48
 
49
  $dir = wp_normalize_path( $dir );
50
 
51
- if ( is_string( $abspath_replace ) ) {
52
- if ( !self::$abspath ) {
53
- self::$abspath = wp_normalize_path( ABSPATH );
 
54
  }
55
- $dir = str_replace( self::$abspath, $abspath_replace, $dir );
 
 
 
56
  }
57
 
58
  return $dir;
@@ -282,5 +287,19 @@ class QM_Util {
282
 
283
  }
284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  }
286
  }
20
  protected static $file_components = array();
21
  protected static $file_dirs = array();
22
  protected static $abspath = null;
23
+ protected static $contentpath = null;
24
 
25
  private function __construct() {}
26
 
45
 
46
  }
47
 
48
+ public static function standard_dir( $dir, $path_replace = null ) {
49
 
50
  $dir = wp_normalize_path( $dir );
51
 
52
+ if ( is_string( $path_replace ) ) {
53
+ if ( ! self::$abspath ) {
54
+ self::$abspath = wp_normalize_path( ABSPATH );
55
+ self::$contentpath = wp_normalize_path( dirname( WP_CONTENT_DIR ) . '/' );
56
  }
57
+ $dir = str_replace( array(
58
+ self::$abspath,
59
+ self::$contentpath,
60
+ ), $path_replace, $dir );
61
  }
62
 
63
  return $dir;
287
 
288
  }
289
 
290
+ public static function get_query_type( $sql ) {
291
+ $sql = $type = trim( $sql );
292
+
293
+ if ( 0 === strpos( $sql, '/*' ) ) {
294
+ // Strip out leading comments such as `/*NO_SELECT_FOUND_ROWS*/` before calculating the query type
295
+ $type = preg_replace( '|^/\*[^\*/]+\*/|', '', $sql );
296
+ }
297
+
298
+ $type = preg_split( '/\b/', trim( $type ), 2, PREG_SPLIT_NO_EMPTY );
299
+ $type = strtoupper( $type[0] );
300
+
301
+ return $type;
302
+ }
303
+
304
  }
305
  }
collectors/cache.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2009-2016 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
+ class QM_Collector_Cache extends QM_Collector {
18
+
19
+ public $id = 'cache';
20
+
21
+ public function name() {
22
+ return __( 'Cache', 'query-monitor' );
23
+ }
24
+
25
+ public function process() {
26
+ global $wp_object_cache;
27
+
28
+ $this->data['ext_object_cache'] = (bool) wp_using_ext_object_cache();
29
+
30
+ if ( is_object( $wp_object_cache ) ) {
31
+
32
+ if ( isset( $wp_object_cache->cache_hits ) ) {
33
+ $this->data['stats']['cache_hits'] = $wp_object_cache->cache_hits;
34
+ }
35
+
36
+ if ( isset( $wp_object_cache->cache_misses ) ) {
37
+ $this->data['stats']['cache_misses'] = $wp_object_cache->cache_misses;
38
+ }
39
+
40
+ if ( isset( $wp_object_cache->stats ) ) {
41
+ foreach ( $wp_object_cache->stats as $key => $value ) {
42
+ if ( ! is_scalar( $value ) ) {
43
+ continue;
44
+ }
45
+ $this->data['stats'][ $key ] = $value;
46
+ }
47
+ }
48
+
49
+ }
50
+
51
+ if ( isset( $this->data['stats']['cache_hits'] ) && $this->data['stats']['cache_misses'] ) {
52
+ $total = $this->data['stats']['cache_misses'] + $this->data['stats']['cache_hits'];
53
+ $this->data['cache_hit_percentage'] = ( 100 / $total ) * $this->data['stats']['cache_hits'];
54
+ }
55
+
56
+ }
57
+
58
+ }
59
+
60
+ function register_qm_collector_cache( array $collectors, QueryMonitor $qm ) {
61
+ $collectors['cache'] = new QM_Collector_Cache;
62
+ return $collectors;
63
+ }
64
+
65
+ add_filter( 'qm/collectors', 'register_qm_collector_cache', 20, 2 );
collectors/db_queries.php CHANGED
@@ -95,6 +95,7 @@ class QM_Collector_DB_Queries extends QM_Collector {
95
  }
96
 
97
  public function process_db_object( $id, wpdb $db ) {
 
98
 
99
  $rows = array();
100
  $types = array();
@@ -147,15 +148,8 @@ class QM_Collector_DB_Queries extends QM_Collector {
147
 
148
  }
149
 
150
- $sql = $type = trim( $sql );
151
-
152
- if ( 0 === strpos( $sql, '/*' ) ) {
153
- // Strip out leading comments such as `/*NO_SELECT_FOUND_ROWS*/` before calculating the query type
154
- $type = preg_replace( '|^/\*[^\*/]+\*/|', '', $sql );
155
- }
156
-
157
- $type = preg_split( '/\b/', trim( $type ), 2, PREG_SPLIT_NO_EMPTY );
158
- $type = strtoupper( $type[0] );
159
 
160
  $this->log_type( $type );
161
  $this->log_caller( $caller_name, $ltime, $type );
@@ -193,6 +187,23 @@ class QM_Collector_DB_Queries extends QM_Collector {
193
 
194
  }
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  $total_qs = count( $rows );
197
 
198
  $this->data['total_qs'] += $total_qs;
95
  }
96
 
97
  public function process_db_object( $id, wpdb $db ) {
98
+ global $EZSQL_ERROR;
99
 
100
  $rows = array();
101
  $types = array();
148
 
149
  }
150
 
151
+ $sql = trim( $sql );
152
+ $type = QM_Util::get_query_type( $sql );
 
 
 
 
 
 
 
153
 
154
  $this->log_type( $type );
155
  $this->log_caller( $caller_name, $ltime, $type );
187
 
188
  }
189
 
190
+ if ( '$wpdb' === $id && ! $has_result && ! empty( $EZSQL_ERROR ) && is_array( $EZSQL_ERROR ) ) {
191
+ // Fallback for displaying database errors when wp-content/db.php isn't in place
192
+ foreach ( $EZSQL_ERROR as $error ) {
193
+ $row = array(
194
+ 'caller' => 'Unknown',
195
+ 'caller_name' => 'Unknown',
196
+ 'stack' => array(),
197
+ 'sql' => $error['query'],
198
+ 'result' => new WP_Error( 'qmdb', $error['error_str'] ),
199
+ 'type' => '',
200
+ 'component' => false,
201
+ 'trace' => null,
202
+ );
203
+ $this->data['errors'][] = $row;
204
+ }
205
+ }
206
+
207
  $total_qs = count( $rows );
208
 
209
  $this->data['total_qs'] += $total_qs;
collectors/http.php CHANGED
@@ -130,6 +130,7 @@ class QM_Collector_HTTP extends QM_Collector {
130
  public function log_http_response( $response, array $args, $url ) {
131
  $this->data['http'][$args['_qm_key']]['end'] = microtime( true );
132
  $this->data['http'][$args['_qm_key']]['response'] = $response;
 
133
  if ( isset( $args['_qm_original_key'] ) ) {
134
  $this->data['http'][$args['_qm_original_key']]['end'] = $this->data['http'][$args['_qm_original_key']]['start'];
135
  $this->data['http'][$args['_qm_original_key']]['response'] = new WP_Error( 'http_request_not_executed', __( 'Request not executed due to a filter on pre_http_request', 'query-monitor' ) );
130
  public function log_http_response( $response, array $args, $url ) {
131
  $this->data['http'][$args['_qm_key']]['end'] = microtime( true );
132
  $this->data['http'][$args['_qm_key']]['response'] = $response;
133
+ $this->data['http'][$args['_qm_key']]['args'] = $args;
134
  if ( isset( $args['_qm_original_key'] ) ) {
135
  $this->data['http'][$args['_qm_original_key']]['end'] = $this->data['http'][$args['_qm_original_key']]['start'];
136
  $this->data['http'][$args['_qm_original_key']]['response'] = new WP_Error( 'http_request_not_executed', __( 'Request not executed due to a filter on pre_http_request', 'query-monitor' ) );
collectors/theme.php CHANGED
@@ -28,7 +28,7 @@ class QM_Collector_Theme extends QM_Collector {
28
  add_filter( 'template_include', array( $this, 'filter_template_include' ), 999 );
29
  }
30
 
31
- public function filter_body_class( $class ) {
32
  $this->data['body_class'] = $class;
33
  return $class;
34
  }
@@ -47,7 +47,7 @@ class QM_Collector_Theme extends QM_Collector {
47
  $template_directory = QM_Util::standard_dir( get_template_directory() );
48
  $theme_directory = QM_Util::standard_dir( get_theme_root() );
49
 
50
- $template_file = str_replace( array( $stylesheet_directory, $template_directory ), '', $template_path );
51
  $template_file = ltrim( $template_file, '/' );
52
  $theme_template_file = str_replace( array( $theme_directory, ABSPATH ), '', $template_path );
53
  $theme_template_file = ltrim( $theme_template_file, '/' );
@@ -56,10 +56,33 @@ class QM_Collector_Theme extends QM_Collector {
56
  $this->data['template_file'] = $template_file;
57
  $this->data['theme_template_file'] = $theme_template_file;
58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  }
60
 
61
- $this->data['stylesheet'] = get_stylesheet();
62
- $this->data['template'] = get_template();
 
63
 
64
  if ( isset( $this->data['body_class'] ) ) {
65
  asort( $this->data['body_class'] );
28
  add_filter( 'template_include', array( $this, 'filter_template_include' ), 999 );
29
  }
30
 
31
+ public function filter_body_class( array $class ) {
32
  $this->data['body_class'] = $class;
33
  return $class;
34
  }
47
  $template_directory = QM_Util::standard_dir( get_template_directory() );
48
  $theme_directory = QM_Util::standard_dir( get_theme_root() );
49
 
50
+ $template_file = str_replace( array( $stylesheet_directory, $template_directory, ABSPATH ), '', $template_path );
51
  $template_file = ltrim( $template_file, '/' );
52
  $theme_template_file = str_replace( array( $theme_directory, ABSPATH ), '', $template_path );
53
  $theme_template_file = ltrim( $theme_template_file, '/' );
56
  $this->data['template_file'] = $template_file;
57
  $this->data['theme_template_file'] = $theme_template_file;
58
 
59
+ foreach ( get_included_files() as $file ) {
60
+ $filename = str_replace( array(
61
+ $stylesheet_directory,
62
+ $template_directory,
63
+ ), '', $file );
64
+ if ( $filename !== $file ) {
65
+ $slug = trim( str_replace( '.php', '', $filename ), '/' );
66
+ $display = trim( $filename, '/' );
67
+ $theme_display = trim( str_replace( $theme_directory, '', $file ), '/' );
68
+ if ( did_action( "get_template_part_{$slug}" ) ) {
69
+ $this->data['template_parts'][ $file ] = $display;
70
+ $this->data['theme_template_parts'][ $file ] = $theme_display;
71
+ } else {
72
+ $slug = trim( preg_replace( '|\-[^\-]+$|', '', $slug ), '/' );
73
+ if ( did_action( "get_template_part_{$slug}" ) ) {
74
+ $this->data['template_parts'][ $file ] = $display;
75
+ $this->data['theme_template_parts'][ $file ] = $theme_display;
76
+ }
77
+ }
78
+ }
79
+ }
80
+
81
  }
82
 
83
+ $this->data['stylesheet'] = get_stylesheet();
84
+ $this->data['template'] = get_template();
85
+ $this->data['is_child_theme'] = ( $this->data['stylesheet'] != $this->data['template'] );
86
 
87
  if ( isset( $this->data['body_class'] ) ) {
88
  asort( $this->data['body_class'] );
dispatchers/Html.php CHANGED
@@ -32,6 +32,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
32
  add_action( 'admin_footer', array( $this, 'action_footer' ) );
33
  add_action( 'login_footer', array( $this, 'action_footer' ) );
34
  add_action( 'embed_footer', array( $this, 'action_footer' ) );
 
35
 
36
  parent::__construct( $qm );
37
 
@@ -95,7 +96,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
95
  $wp_admin_bar->add_menu( array(
96
  'id' => 'query-monitor',
97
  'title' => esc_html( $title ),
98
- 'href' => '#qm-overview',
99
  'meta' => array(
100
  'classname' => 'hide-if-js',
101
  ),
@@ -105,7 +106,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
105
  'parent' => 'query-monitor',
106
  'id' => 'query-monitor-placeholder',
107
  'title' => esc_html( $title ),
108
- 'href' => '#qm-overview',
109
  ) );
110
 
111
  }
@@ -126,6 +127,18 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
126
  add_action( 'enqueue_embed_scripts', array( $this, 'enqueue_assets' ) );
127
  add_action( 'send_headers', 'nocache_headers' );
128
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
 
131
  public function enqueue_assets() {
@@ -215,19 +228,19 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
215
  }
216
 
217
  if ( !is_admin_bar_showing() ) {
218
- $class[] = 'qm-show';
219
  }
220
 
221
  echo '<div id="qm" class="' . implode( ' ', array_map( 'esc_attr', $class ) ) . '">';
222
  echo '<div id="qm-wrapper">';
 
223
  echo '<p>' . esc_html__( 'Query Monitor', 'query-monitor' ) . '</p>';
 
224
 
225
  }
226
 
227
  protected function after_output() {
228
 
229
- $collectors = QM_Collectors::init();
230
-
231
  echo '<div class="qm qm-half qm-clear" id="qm-authentication">';
232
  echo '<table cellspacing="0">';
233
  echo '<thead>';
@@ -272,8 +285,10 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
272
  echo '<script type="text/javascript">' . "\n\n";
273
  echo 'var qm = ' . json_encode( $json ) . ';' . "\n\n";
274
  ?>
275
- if ( 'undefined' === typeof QM_i18n ) {
276
  document.getElementById( 'qm' ).style.display = 'block';
 
 
277
  }
278
  <?php
279
  echo '</script>' . "\n\n";
@@ -315,7 +330,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
315
  return false;
316
  }
317
 
318
- if ( QM_Util::is_async() ) {
319
  return false;
320
  }
321
 
32
  add_action( 'admin_footer', array( $this, 'action_footer' ) );
33
  add_action( 'login_footer', array( $this, 'action_footer' ) );
34
  add_action( 'embed_footer', array( $this, 'action_footer' ) );
35
+ add_action( 'amp_post_template_footer', array( $this, 'action_footer' ) );
36
 
37
  parent::__construct( $qm );
38
 
96
  $wp_admin_bar->add_menu( array(
97
  'id' => 'query-monitor',
98
  'title' => esc_html( $title ),
99
+ 'href' => '#qm',
100
  'meta' => array(
101
  'classname' => 'hide-if-js',
102
  ),
106
  'parent' => 'query-monitor',
107
  'id' => 'query-monitor-placeholder',
108
  'title' => esc_html( $title ),
109
+ 'href' => '#qm',
110
  ) );
111
 
112
  }
127
  add_action( 'enqueue_embed_scripts', array( $this, 'enqueue_assets' ) );
128
  add_action( 'send_headers', 'nocache_headers' );
129
 
130
+ add_action( 'amp_post_template_head', array( $this, 'enqueue_assets' ) );
131
+ add_action( 'amp_post_template_head', array( $this, 'manually_print_assets' ), 11 );
132
+
133
+ }
134
+
135
+ public function manually_print_assets() {
136
+ wp_print_scripts( array(
137
+ 'query-monitor',
138
+ ) );
139
+ wp_print_styles( array(
140
+ 'query-monitor',
141
+ ) );
142
  }
143
 
144
  public function enqueue_assets() {
228
  }
229
 
230
  if ( !is_admin_bar_showing() ) {
231
+ $class[] = 'qm-peek';
232
  }
233
 
234
  echo '<div id="qm" class="' . implode( ' ', array_map( 'esc_attr', $class ) ) . '">';
235
  echo '<div id="qm-wrapper">';
236
+ echo '<div id="qm-title">';
237
  echo '<p>' . esc_html__( 'Query Monitor', 'query-monitor' ) . '</p>';
238
+ echo '</div>';
239
 
240
  }
241
 
242
  protected function after_output() {
243
 
 
 
244
  echo '<div class="qm qm-half qm-clear" id="qm-authentication">';
245
  echo '<table cellspacing="0">';
246
  echo '<thead>';
285
  echo '<script type="text/javascript">' . "\n\n";
286
  echo 'var qm = ' . json_encode( $json ) . ';' . "\n\n";
287
  ?>
288
+ if ( ( 'undefined' === typeof QM_i18n ) || ( 'undefined' === typeof jQuery ) || ! jQuery ) {
289
  document.getElementById( 'qm' ).style.display = 'block';
290
+ } else if ( ! document.getElementById( 'wpadminbar' ) ) {
291
+ document.getElementById( 'qm' ).className += ' qm-peek';
292
  }
293
  <?php
294
  echo '</script>' . "\n\n";
330
  return false;
331
  }
332
 
333
+ if ( QM_Util::is_async() && ! is_customize_preview() ) {
334
  return false;
335
  }
336
 
dispatchers/REST.php CHANGED
@@ -56,7 +56,7 @@ class QM_Dispatcher_REST extends QM_Dispatcher {
56
  require_once $this->qm->plugin_path( 'output/Headers.php' );
57
 
58
  foreach ( glob( $this->qm->plugin_path( 'output/headers/*.php' ) ) as $file ) {
59
- include $file;
60
  }
61
  }
62
 
@@ -67,6 +67,10 @@ class QM_Dispatcher_REST extends QM_Dispatcher {
67
  return false;
68
  }
69
 
 
 
 
 
70
  if ( ! $this->user_can_view() ) {
71
  return false;
72
  }
56
  require_once $this->qm->plugin_path( 'output/Headers.php' );
57
 
58
  foreach ( glob( $this->qm->plugin_path( 'output/headers/*.php' ) ) as $file ) {
59
+ include_once $file;
60
  }
61
  }
62
 
67
  return false;
68
  }
69
 
70
+ if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) {
71
+ return false;
72
+ }
73
+
74
  if ( ! $this->user_can_view() ) {
75
  return false;
76
  }
output/Html.php CHANGED
@@ -131,17 +131,8 @@ abstract class QM_Output_Html extends QM_Output {
131
  $sql = esc_html( $sql );
132
  $sql = trim( $sql );
133
 
134
- foreach( array(
135
- 'ALTER', 'AND', 'COMMIT', 'CREATE', 'DESCRIBE', 'DELETE', 'DROP', 'ELSE', 'END', 'FROM', 'GROUP',
136
- 'HAVING', 'INNER', 'INSERT', 'LEFT', 'LIMIT', 'ON', 'OR', 'ORDER', 'OUTER', 'REPLACE', 'RIGHT', 'ROLLBACK', 'SELECT', 'SET',
137
- 'SHOW', 'START', 'THEN', 'TRUNCATE', 'UPDATE', 'VALUES', 'WHEN', 'WHERE'
138
- ) as $cmd ) {
139
- // Why does this trim() every time?
140
- $sql = trim( str_replace( " $cmd ", "<br>$cmd ", $sql ) );
141
- }
142
-
143
- # @TODO profile this as an alternative:
144
- # $sql = preg_replace( '# (ALTER|AND|COMMIT|CREATE|DESCRIBE) #', '<br>$1 ', $sql );
145
 
146
  return $sql;
147
 
131
  $sql = esc_html( $sql );
132
  $sql = trim( $sql );
133
 
134
+ $regex = 'ADD|AFTER|ALTER|AND|BEGIN|COMMIT|CREATE|DESCRIBE|DELETE|DROP|ELSE|END|EXCEPT|FROM|GROUP|HAVING|INNER|INSERT|INTERSECT|LEFT|LIMIT|ON|OR|ORDER|OUTER|REPLACE|RIGHT|ROLLBACK|SELECT|SET|SHOW|START|THEN|TRUNCATE|UNION|UPDATE|USING|VALUES|WHEN|WHERE|XOR';
135
+ $sql = preg_replace( '# (' . $regex . ') #', '<br>$1 ', $sql );
 
 
 
 
 
 
 
 
 
136
 
137
  return $sql;
138
 
output/html/conditionals.php CHANGED
@@ -27,7 +27,6 @@ class QM_Output_Html_Conditionals extends QM_Output_Html {
27
 
28
  $cols = 6;
29
  $i = 0;
30
- $w = floor( 100 / $cols );
31
 
32
  echo '<div class="qm" id="' . esc_attr( $this->collector->id() ) . '">';
33
  echo '<table cellspacing="0">';
@@ -43,7 +42,7 @@ class QM_Output_Html_Conditionals extends QM_Output_Html {
43
  if ( 1 === $i % $cols ) {
44
  echo '<tr>';
45
  }
46
- echo '<td class="qm-ltr qm-true" width="' . absint( $w ) . '%">' . esc_html( $cond ) . '()&nbsp;&#x2713;</td>';
47
  if ( 0 === $i % $cols ) {
48
  echo '</tr>';
49
  }
@@ -54,7 +53,7 @@ class QM_Output_Html_Conditionals extends QM_Output_Html {
54
  if ( 1 === $i % $cols ) {
55
  echo '<tr>';
56
  }
57
- echo '<td class="qm-ltr qm-false" width="' . absint( $w ) . '%">' . esc_html( $cond ) . '()</td>';
58
  if ( 0 === $i % $cols ) {
59
  echo '</tr>';
60
  }
27
 
28
  $cols = 6;
29
  $i = 0;
 
30
 
31
  echo '<div class="qm" id="' . esc_attr( $this->collector->id() ) . '">';
32
  echo '<table cellspacing="0">';
42
  if ( 1 === $i % $cols ) {
43
  echo '<tr>';
44
  }
45
+ echo '<td class="qm-ltr qm-true">' . esc_html( $cond ) . '()&nbsp;&#x2713;</td>';
46
  if ( 0 === $i % $cols ) {
47
  echo '</tr>';
48
  }
53
  if ( 1 === $i % $cols ) {
54
  echo '<tr>';
55
  }
56
+ echo '<td class="qm-ltr qm-false">' . esc_html( $cond ) . '()</td>';
57
  if ( 0 === $i % $cols ) {
58
  echo '</tr>';
59
  }
output/html/db_dupes.php CHANGED
@@ -53,14 +53,23 @@ class QM_Output_Html_DB_Dupes extends QM_Output_Html {
53
  echo '<tbody>';
54
 
55
  foreach ( $data['dupes'] as $sql => $queries ) {
 
 
 
 
 
 
 
 
 
56
  echo '<tr>';
57
- echo '<td>';
58
- echo self::format_sql( $sql ); // WPCS: XSS ok;
59
  echo '</td>';
60
  echo '<td class="qm-num">';
61
  echo esc_html( number_format_i18n( count( $queries ), 0 ) );
62
  echo '</td>';
63
- echo '<td class="qm-nowrap qm-ltr">';
64
  foreach ( $data['dupe_callers'][ $sql ] as $caller => $calls ) {
65
  printf(
66
  '<a href="#" class="qm-filter-trigger" data-qm-target="db_queries-wpdb" data-qm-filter="caller" data-qm-value="%s">%s</a><br><span class="qm-info">&nbsp;%s</span><br>',
@@ -74,7 +83,7 @@ class QM_Output_Html_DB_Dupes extends QM_Output_Html {
74
  }
75
  echo '</td>';
76
  if ( isset( $data['dupe_components'][ $sql ] ) ) {
77
- echo '<td class="qm-nowrap">';
78
  foreach ( $data['dupe_components'][ $sql ] as $component => $calls ) {
79
  printf(
80
  '%s<br><span class="qm-info">&nbsp;%s</span><br>',
@@ -87,7 +96,7 @@ class QM_Output_Html_DB_Dupes extends QM_Output_Html {
87
  }
88
  echo '</td>';
89
  }
90
- echo '<td class="qm-nowrap qm-ltr">';
91
  foreach ( $data['dupe_sources'][ $sql ] as $source => $calls ) {
92
  printf(
93
  '%s<br><span class="qm-info">&nbsp;%s</span><br>',
53
  echo '<tbody>';
54
 
55
  foreach ( $data['dupes'] as $sql => $queries ) {
56
+
57
+ // This should probably happen in the collector's processor
58
+ $type = QM_Util::get_query_type( $sql );
59
+ $sql_out = self::format_sql( $sql );
60
+
61
+ if ( 'SELECT' !== $type ) {
62
+ $sql_out = "<span class='qm-nonselectsql'>{$sql_out}</span>";
63
+ }
64
+
65
  echo '<tr>';
66
+ echo '<td class="qm-row-sql qm-ltr qm-wrap">';
67
+ echo $sql_out; // WPCS: XSS ok;
68
  echo '</td>';
69
  echo '<td class="qm-num">';
70
  echo esc_html( number_format_i18n( count( $queries ), 0 ) );
71
  echo '</td>';
72
+ echo '<td class="qm-row-caller qm-nowrap qm-ltr">';
73
  foreach ( $data['dupe_callers'][ $sql ] as $caller => $calls ) {
74
  printf(
75
  '<a href="#" class="qm-filter-trigger" data-qm-target="db_queries-wpdb" data-qm-filter="caller" data-qm-value="%s">%s</a><br><span class="qm-info">&nbsp;%s</span><br>',
83
  }
84
  echo '</td>';
85
  if ( isset( $data['dupe_components'][ $sql ] ) ) {
86
+ echo '<td class="qm-row-component qm-nowrap">';
87
  foreach ( $data['dupe_components'][ $sql ] as $component => $calls ) {
88
  printf(
89
  '%s<br><span class="qm-info">&nbsp;%s</span><br>',
96
  }
97
  echo '</td>';
98
  }
99
+ echo '<td class="qm-row-caller qm-nowrap qm-ltr">';
100
  foreach ( $data['dupe_sources'][ $sql ] as $source => $calls ) {
101
  printf(
102
  '%s<br><span class="qm-info">&nbsp;%s</span><br>',
output/html/db_queries.php CHANGED
@@ -329,7 +329,7 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
329
  if ( isset( $cols['sql'] ) ) {
330
  $row_attr['data-qm-type'] = $row['type'];
331
  }
332
- if ( isset( $cols['component'] ) ) {
333
  $row_attr['data-qm-component'] = $row['component']->name;
334
  }
335
  if ( isset( $cols['caller'] ) ) {
@@ -378,7 +378,11 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
378
  }
379
 
380
  if ( isset( $cols['component'] ) ) {
 
381
  echo "<td class='qm-row-component qm-nowrap'>" . esc_html( $row['component']->name ) . "</td>\n";
 
 
 
382
  }
383
 
384
  if ( isset( $cols['result'] ) ) {
329
  if ( isset( $cols['sql'] ) ) {
330
  $row_attr['data-qm-type'] = $row['type'];
331
  }
332
+ if ( isset( $cols['component'] ) && $row['component'] ) {
333
  $row_attr['data-qm-component'] = $row['component']->name;
334
  }
335
  if ( isset( $cols['caller'] ) ) {
378
  }
379
 
380
  if ( isset( $cols['component'] ) ) {
381
+ if ( $row['component'] ) {
382
  echo "<td class='qm-row-component qm-nowrap'>" . esc_html( $row['component']->name ) . "</td>\n";
383
+ } else {
384
+ echo "<td class='qm-row-component qm-nowrap'>" . esc_html__( 'Unknown', 'query-monitor' ) . "</td>\n";
385
+ }
386
  }
387
 
388
  if ( isset( $cols['result'] ) ) {
output/html/http.php CHANGED
@@ -92,10 +92,22 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
92
 
93
  }
94
 
95
- $method = $row['args']['method'];
96
- if ( !$row['args']['blocking'] ) {
97
- $method .= '&nbsp;' . _x( '(non-blocking)', 'non-blocking HTTP transport', 'query-monitor' );
 
 
 
 
 
 
 
 
 
 
 
98
  }
 
99
  $url = self::format_url( $row['url'] );
100
 
101
  if ( isset( $row['transport'] ) ) {
@@ -133,7 +145,7 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
133
  );
134
  printf( // WPCS: XSS ok.
135
  '<td class="qm-url qm-ltr qm-wrap">%s<br>%s</td>',
136
- esc_html( $method ),
137
  $url
138
  );
139
  printf(
92
 
93
  }
94
 
95
+ $method = esc_html( $row['args']['method'] );
96
+
97
+ if ( empty( $row['args']['blocking'] ) ) {
98
+ $method .= '<br><span class="qm-info">' . esc_html( sprintf(
99
+ _x( '(Non-blocking request: %s)', 'non-blocking HTTP transport', 'query-monitor' ),
100
+ 'blocking=false'
101
+ ) ) . '</span>';
102
+ }
103
+
104
+ if ( ! empty( $row['args']['ssl'] ) && empty( $row['args']['sslverify'] ) && empty( $row['args']['local'] ) ) {
105
+ $method .= '<br><span class="qm-warn">' . esc_html( sprintf(
106
+ __( '(Certificate verification disabled: %s)', 'query-monitor' ),
107
+ 'sslverify=false'
108
+ ) ) . '</span>';
109
  }
110
+
111
  $url = self::format_url( $row['url'] );
112
 
113
  if ( isset( $row['transport'] ) ) {
145
  );
146
  printf( // WPCS: XSS ok.
147
  '<td class="qm-url qm-ltr qm-wrap">%s<br>%s</td>',
148
+ $method,
149
  $url
150
  );
151
  printf(
output/html/overview.php CHANGED
@@ -37,6 +37,15 @@ class QM_Output_Html_Overview extends QM_Output_Html {
37
  }
38
  }
39
 
 
 
 
 
 
 
 
 
 
40
  echo '<div class="qm" id="' . esc_attr( $this->collector->id() ) . '">';
41
  echo '<table cellspacing="0">';
42
 
@@ -48,6 +57,9 @@ class QM_Output_Html_Overview extends QM_Output_Html {
48
  echo '<th scope="col">' . esc_html__( 'Database query time', 'query-monitor' ) . '</th>';
49
  echo '<th scope="col">' . esc_html__( 'Database queries', 'query-monitor' ) . '</th>';
50
  }
 
 
 
51
  echo '</tr>';
52
  echo '</thead>';
53
 
@@ -96,6 +108,20 @@ class QM_Output_Html_Overview extends QM_Output_Html {
96
 
97
  echo '</td>';
98
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  echo '</tr>';
100
  echo '</tbody>';
101
 
37
  }
38
  }
39
 
40
+ $cache = QM_Collectors::get( 'cache' );
41
+
42
+ if ( $cache ) {
43
+ $cache_data = $cache->get_data();
44
+ if ( isset( $cache_data['stats'] ) && isset( $cache_data['cache_hit_percentage'] ) ) {
45
+ $cache_hit_percentage = $cache_data['cache_hit_percentage'];
46
+ }
47
+ }
48
+
49
  echo '<div class="qm" id="' . esc_attr( $this->collector->id() ) . '">';
50
  echo '<table cellspacing="0">';
51
 
57
  echo '<th scope="col">' . esc_html__( 'Database query time', 'query-monitor' ) . '</th>';
58
  echo '<th scope="col">' . esc_html__( 'Database queries', 'query-monitor' ) . '</th>';
59
  }
60
+ if ( isset( $cache_hit_percentage ) ) {
61
+ echo '<th scope="col">' . esc_html__( 'Object cache', 'query-monitor' ) . '</th>';
62
+ }
63
  echo '</tr>';
64
  echo '</thead>';
65
 
108
 
109
  echo '</td>';
110
  }
111
+
112
+ if ( isset( $cache_hit_percentage ) ) {
113
+ echo '<td>';
114
+ echo esc_html( sprintf(
115
+ '%s%% hit rate',
116
+ number_format_i18n( $cache_hit_percentage, 1 )
117
+ ) );
118
+ echo '<br>' . esc_html( sprintf(
119
+ __( 'External object cache: %s'),
120
+ ( $cache_data['ext_object_cache'] ? 'true' : 'false' )
121
+ ) );
122
+ echo '</td>';
123
+ }
124
+
125
  echo '</tr>';
126
  echo '</tbody>';
127
 
output/html/theme.php CHANGED
@@ -29,27 +29,61 @@ class QM_Output_Html_Theme extends QM_Output_Html {
29
  return;
30
  }
31
 
32
- $child_theme = ( $data['stylesheet'] !== $data['template'] );
33
-
34
  echo '<div class="qm qm-half" id="' . esc_attr( $this->collector->id() ) . '">';
35
  echo '<table cellspacing="0">';
36
  echo '<tbody>';
37
 
 
 
38
  if ( ! empty( $data['template_path'] ) ) {
39
 
40
- echo '<tr>';
41
- echo '<td>' . esc_html__( 'Template File', 'query-monitor' ) . '</td>';
42
- if ( $child_theme ) {
43
  echo '<td>' . self::output_filename( $data['theme_template_file'], $data['template_path'] ) . '</td>'; // WPCS: XSS ok.
44
  } else {
45
  echo '<td>' . self::output_filename( $data['template_file'], $data['template_path'] ) . '</td>'; // WPCS: XSS ok.
46
  }
47
- echo '</tr>';
48
 
 
 
49
  }
50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  echo '<tr>';
52
- if ( $child_theme ) {
53
  echo '<td>' . esc_html__( 'Child Theme', 'query-monitor' ) . '</td>';
54
  } else {
55
  echo '<td>' . esc_html__( 'Theme', 'query-monitor' ) . '</td>';
@@ -57,7 +91,7 @@ class QM_Output_Html_Theme extends QM_Output_Html {
57
  echo '<td>' . esc_html( $data['stylesheet'] ) . '</td>';
58
  echo '</tr>';
59
 
60
- if ( $child_theme ) {
61
  echo '<tr>';
62
  echo '<td>' . esc_html__( 'Parent Theme', 'query-monitor' ) . '</td>';
63
  echo '<td>' . esc_html( $data['template'] ) . '</td>';
@@ -99,7 +133,7 @@ class QM_Output_Html_Theme extends QM_Output_Html {
99
  $menu[] = $this->menu( array(
100
  'title' => esc_html( sprintf(
101
  __( 'Template: %s', 'query-monitor' ),
102
- $data['template_file']
103
  ) ),
104
  ) );
105
  }
29
  return;
30
  }
31
 
 
 
32
  echo '<div class="qm qm-half" id="' . esc_attr( $this->collector->id() ) . '">';
33
  echo '<table cellspacing="0">';
34
  echo '<tbody>';
35
 
36
+ echo '<tr>';
37
+ echo '<td>' . esc_html__( 'Template File', 'query-monitor' ) . '</td>';
38
  if ( ! empty( $data['template_path'] ) ) {
39
 
40
+ if ( $data['is_child_theme'] ) {
 
 
41
  echo '<td>' . self::output_filename( $data['theme_template_file'], $data['template_path'] ) . '</td>'; // WPCS: XSS ok.
42
  } else {
43
  echo '<td>' . self::output_filename( $data['template_file'], $data['template_path'] ) . '</td>'; // WPCS: XSS ok.
44
  }
 
45
 
46
+ } else {
47
+ echo '<td><em>' . esc_html__( 'Unknown', 'query-monitor' ) . '</em></td>';
48
  }
49
 
50
+ echo '</tr>';
51
+
52
+ if ( ! empty( $data['template_parts'] ) ) {
53
+
54
+ $count = count( $data['template_parts'] );
55
+ echo '<tr>';
56
+ echo '<td rowspan="' . absint( $count ) . '">' . esc_html__( 'Template Parts', 'query-monitor' ) . '</td>';
57
+ if ( $data['is_child_theme'] ) {
58
+ $parts = $data['theme_template_parts'];
59
+ } else {
60
+ $parts = $data['template_parts'];
61
+ }
62
+ $first = true;
63
+
64
+ foreach ( $parts as $filename => $display ) {
65
+
66
+ if ( ! $first ) {
67
+ echo '<tr>';
68
+ }
69
+
70
+ echo '<td>' . self::output_filename( $display, $filename ) . '</td>'; // WPCS: XSS ok.
71
+ echo '</tr>';
72
+
73
+ $first = false;
74
+
75
+ }
76
+
77
+ } else {
78
+ echo '<tr>';
79
+ echo '<td>' . esc_html__( 'Template Parts', 'query-monitor' ) . '</td>';
80
+ echo '<td><em>' . esc_html__( 'None', 'query-monitor' ) . '</em></td>';
81
+ echo '</tr>';
82
+ }
83
+
84
+
85
  echo '<tr>';
86
+ if ( $data['is_child_theme'] ) {
87
  echo '<td>' . esc_html__( 'Child Theme', 'query-monitor' ) . '</td>';
88
  } else {
89
  echo '<td>' . esc_html__( 'Theme', 'query-monitor' ) . '</td>';
91
  echo '<td>' . esc_html( $data['stylesheet'] ) . '</td>';
92
  echo '</tr>';
93
 
94
+ if ( $data['is_child_theme'] ) {
95
  echo '<tr>';
96
  echo '<td>' . esc_html__( 'Parent Theme', 'query-monitor' ) . '</td>';
97
  echo '<td>' . esc_html( $data['template'] ) . '</td>';
133
  $menu[] = $this->menu( array(
134
  'title' => esc_html( sprintf(
135
  __( 'Template: %s', 'query-monitor' ),
136
+ ( $data['is_child_theme'] ? $data['theme_template_file'] : $data['template_file'] )
137
  ) ),
138
  ) );
139
  }
query-monitor.php CHANGED
@@ -2,8 +2,8 @@
2
  /*
3
  Plugin Name: Query Monitor
4
  Description: Monitoring of database queries, hooks, conditionals and more.
5
- Version: 2.10.0
6
- Plugin URI: https://querymonitor.com/
7
  Author: John Blackbourn
8
  Author URI: https://johnblackbourn.com/
9
  Text Domain: query-monitor
2
  /*
3
  Plugin Name: Query Monitor
4
  Description: Monitoring of database queries, hooks, conditionals and more.
5
+ Version: 2.11.0
6
+ Plugin URI: https://github.com/johnbillion/querymonitor
7
  Author: John Blackbourn
8
  Author URI: https://johnblackbourn.com/
9
  Text Domain: query-monitor
readme.txt CHANGED
@@ -3,14 +3,14 @@ Contributors: johnbillion
3
  Tags: ajax, debug, debug-bar, debugging, development, developer, performance, profiler, profiling, queries, query monitor, rest-api
4
  Requires at least: 3.7
5
  Tested up to: 4.5
6
- Stable tag: 2.10.0
7
  License: GPLv2 or later
8
 
9
  View debugging and performance information on database queries, hooks, conditionals, HTTP requests, redirects and more.
10
 
11
  == Description ==
12
 
13
- Query Monitor is a debugging plugin for anyone developing with WordPress. It has some advanced features not available in other debugging plugins, including automatic AJAX debugging, REST API debugging, and the ability to narrow down things by plugin or theme.
14
 
15
  For complete information, please see [Query Monitor's GitHub repo](https://github.com/johnbillion/query-monitor).
16
 
@@ -18,27 +18,28 @@ Here's an overview of what's shown:
18
 
19
  = Database Queries =
20
 
21
- * Shows all database queries performed on the current page
22
- * Shows **affected rows** and time for all queries
23
- * Shows notifications for **slow queries**, **duplicate queries**, and **queries with errors**
24
- * Filter queries by **query type** (`SELECT`, `UPDATE`, `DELETE`, etc)
25
- * Filter queries by **component** (WordPress core, Plugin X, Plugin Y, theme)
26
- * Filter queries by **calling function**
27
- * View **aggregate query information** grouped by component, calling function, and type
28
- * Super advanced: Supports **multiple instances of wpdb** on one page (more info in the FAQ)
29
 
30
  Filtering queries by component or calling function makes it easy to see which plugins, themes, or functions on your site are making the most (or the slowest) database queries.
31
 
32
  = Hooks =
33
 
34
- * Shows all hooks fired on the current page, along with hooked actions, their priorities, and their components
35
- * Filter hooks by **part of their name**
36
- * Filter actions by **component** (WordPress core, Plugin X, Plugin Y, theme)
37
 
38
  = Theme =
39
 
40
- * Shows the **template filename** for the current page
41
- * Shows the available **body classes** for the current page
 
42
  * Shows the active theme name
43
 
44
  = PHP Errors =
@@ -48,30 +49,30 @@ Filtering queries by component or calling function makes it easy to see which pl
48
 
49
  = Request =
50
 
51
- * Shows **matched rewrite rules** and associated query strings
52
- * Shows **query vars** for the current request, and highlights **custom query vars**
53
- * Shows the **queried object** details
54
- * Shows details of the **current blog** (multisite only) and **current site** (multi-network only)
55
 
56
  = Rewrite Rules =
57
 
58
- * Shows **all matching rewrite rules** for a given request
59
 
60
  = Scripts & Styles =
61
 
62
- * Shows all **enqueued scripts and styles** on the current page, along with their URL and version
63
- * Shows their **dependencies and dependents**, and alerts you to any **broken dependencies**
64
 
65
  = Languages =
66
 
67
- * Shows you **language settings** and text domains
68
- * Shows you the **MO files** for each text domain and which ones were loaded or not
69
 
70
  = HTTP Requests =
71
 
72
- * Shows all HTTP requests performed on the current page (as long as they use WordPress' HTTP API)
73
  * Shows the response code, call stack, transport, component, timeout, and time taken
74
- * Highlights **erroneous responses**, such as failed requests and anything without a `200` response code
75
 
76
  = Redirects =
77
 
@@ -79,7 +80,7 @@ Filtering queries by component or calling function makes it easy to see which pl
79
 
80
  = AJAX =
81
 
82
- The response from any jQuery AJAX request on the page will contain various debugging information in its headers. Any errors also get output to the developer console. **No hooking required**.
83
 
84
  Currently this includes PHP errors and some overview information such as memory usage, but this will be built upon in future versions.
85
 
@@ -91,22 +92,22 @@ Currently this includes PHP errors and some overview information such as memory
91
 
92
  = Admin Screen =
93
 
94
- * Shows the correct names for **custom column filters and actions** on all admin screens that have a listing table
95
  * Shows the state of `get_current_screen()` and a few variables
96
 
97
  = Environment Information =
98
 
99
- * Shows **various PHP information** such as memory limit and error reporting levels
100
  * Highlights the fact when any of these are overridden at runtime
101
- * Shows **various MySQL information**, including caching and performance related configuration
102
  * Highlights the fact when any performance related configurations are not optimal
103
- * Shows various details about **WordPress** and the **web server**
104
  * Shows version numbers for all the things
105
 
106
  = Everything Else =
107
 
108
- * Shows any **transients that were set**, along with their timeout, component, and call stack
109
- * Shows all **WordPress conditionals** on the current page, highlighted nicely
110
  * Shows an overview at the top, including page generation time and memory limit as absolute values and as % of their respective limits
111
 
112
  = Authentication =
@@ -173,6 +174,20 @@ No, I do not accept donations. If you like the plugin, I'd love for you to [leav
173
 
174
  == Changelog ==
175
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  = 2.10.0 =
177
 
178
  * Add a new panel which lists duplicated database queries.
@@ -419,7 +434,7 @@ No, I do not accept donations. If you like the plugin, I'd love for you to [leav
419
  * Show QM output on the log in screen
420
 
421
  = 2.2.4 =
422
- * Add filtering to the qyer panel
423
 
424
  = 2.2.3 =
425
  * Show component information indicating whether a plugin, theme or core was responsible for each database query
3
  Tags: ajax, debug, debug-bar, debugging, development, developer, performance, profiler, profiling, queries, query monitor, rest-api
4
  Requires at least: 3.7
5
  Tested up to: 4.5
6
+ Stable tag: 2.11.0
7
  License: GPLv2 or later
8
 
9
  View debugging and performance information on database queries, hooks, conditionals, HTTP requests, redirects and more.
10
 
11
  == Description ==
12
 
13
+ Query Monitor is a debugging plugin for anyone developing with WordPress. It has some advanced features not available in other debugging plugins, including debugging of AJAX calls, REST API requests, redirects, and the ability to narrow down its output by plugin or theme.
14
 
15
  For complete information, please see [Query Monitor's GitHub repo](https://github.com/johnbillion/query-monitor).
16
 
18
 
19
  = Database Queries =
20
 
21
+ * Shows all database queries performed on the current request
22
+ * Shows affected rows and time for all queries
23
+ * Shows notifications for slow queries, duplicate queries, and queries with errors
24
+ * Filter queries by query type (`SELECT`, `UPDATE`, `DELETE`, etc)
25
+ * Filter queries by component (WordPress core, Plugin X, Plugin Y, theme)
26
+ * Filter queries by calling function
27
+ * View aggregate query information grouped by component, calling function, and type
28
+ * Super advanced: Supports multiple instances of wpdb on one page (more info in the FAQ)
29
 
30
  Filtering queries by component or calling function makes it easy to see which plugins, themes, or functions on your site are making the most (or the slowest) database queries.
31
 
32
  = Hooks =
33
 
34
+ * Shows all hooks fired on the current request, along with hooked actions, their priorities, and their components
35
+ * Filter hooks by part of their name
36
+ * Filter actions by component (WordPress core, Plugin X, Plugin Y, theme)
37
 
38
  = Theme =
39
 
40
+ * Shows the template filename for the current request
41
+ * Shows all template parts used on the current request
42
+ * Shows the available body classes for the current request
43
  * Shows the active theme name
44
 
45
  = PHP Errors =
49
 
50
  = Request =
51
 
52
+ * Shows matched rewrite rules and associated query strings
53
+ * Shows query vars for the current request, and highlights custom query vars
54
+ * Shows the queried object details
55
+ * Shows details of the current blog (multisite only) and current site (multi-network only)
56
 
57
  = Rewrite Rules =
58
 
59
+ * Shows all matching rewrite rules for a given request
60
 
61
  = Scripts & Styles =
62
 
63
+ * Shows all enqueued scripts and styles on the current request, along with their URL and version
64
+ * Shows their dependencies and dependents, and alerts you to any broken dependencies
65
 
66
  = Languages =
67
 
68
+ * Shows you language settings and text domains
69
+ * Shows you the MO files for each text domain and which ones were loaded or not
70
 
71
  = HTTP Requests =
72
 
73
+ * Shows all HTTP requests performed on the current request (as long as they use WordPress' HTTP API)
74
  * Shows the response code, call stack, transport, component, timeout, and time taken
75
+ * Highlights erroneous responses, such as failed requests and anything without a `200` response code
76
 
77
  = Redirects =
78
 
80
 
81
  = AJAX =
82
 
83
+ The response from any jQuery AJAX request on the page will contain various debugging information in its headers. Any errors also get output to the developer console. No hooking required.
84
 
85
  Currently this includes PHP errors and some overview information such as memory usage, but this will be built upon in future versions.
86
 
92
 
93
  = Admin Screen =
94
 
95
+ * Shows the correct names for custom column filters and actions on all admin screens that have a listing table
96
  * Shows the state of `get_current_screen()` and a few variables
97
 
98
  = Environment Information =
99
 
100
+ * Shows various PHP information such as memory limit and error reporting levels
101
  * Highlights the fact when any of these are overridden at runtime
102
+ * Shows various MySQL information, including caching and performance related configuration
103
  * Highlights the fact when any performance related configurations are not optimal
104
+ * Shows various details about WordPress and the web server
105
  * Shows version numbers for all the things
106
 
107
  = Everything Else =
108
 
109
+ * Shows any transients that were set, along with their timeout, component, and call stack
110
+ * Shows all WordPress conditionals on the current request, highlighted nicely
111
  * Shows an overview at the top, including page generation time and memory limit as absolute values and as % of their respective limits
112
 
113
  = Authentication =
174
 
175
  == Changelog ==
176
 
177
+ = 2.11.0 =
178
+
179
+ * Template parts used in the current request are now listed along with the template file.
180
+ * Fix the REST API output for embedded requests and internal API calls.
181
+ * Enable QM's output to appear in customiser preview responses.
182
+ * Add support for the AMP plugin by Automattic, which short-circuits template output.
183
+ * Highlight the fact when an HTTP request has disabled certificate verification.
184
+ * Take into account custom content directory locations that are outside of ABSPATH when removing leading paths from file names.
185
+ * Even more fallback support for when jQuery is broken or isn't available.
186
+ * Introduce a collector for the object cache. Only outputs an overview at the moment.
187
+ * Better formatting in the Duplicate Queries panel.
188
+ * Introduce a fallback method of detecting errors in queries when `QM_DB` is not in use.
189
+ * Improve the initial state of QM's output when the admin toolbar is not in use.
190
+
191
  = 2.10.0 =
192
 
193
  * Add a new panel which lists duplicated database queries.
434
  * Show QM output on the log in screen
435
 
436
  = 2.2.4 =
437
+ * Add filtering to the query panel
438
 
439
  = 2.2.3 =
440
  * Show component information indicating whether a plugin, theme or core was responsible for each database query