Query Monitor - Version 2.10.0

Version Description

  • Add a new panel which lists duplicated database queries.
  • Add support for displaying QM's output when viewing an embed.
  • Differentiate regular plugins from mu-plugins when displaying components.
  • Ensure early errors are always reported regardless of system level error reporting.
  • Ensure that script and style dependency highlighting is restricted to the scripts and styles tables, respectively.
  • Rearrange the Environment section output a little.
  • Various minor tweaks.
Download this release

Release Info

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

Code changes from version 2.9.1 to 2.10.0

assets/query-monitor.css CHANGED
@@ -146,11 +146,19 @@ body.wp-admin #qm {
146
  margin: 0 0 0 160px !important;
147
  }
148
 
 
 
 
 
 
 
 
149
  body.wp-admin.folded #qm {
150
  margin-left: 36px !important;
151
  }
152
 
153
  @media screen and (max-width: 782px) {
 
154
  body.wp-admin #qm {
155
  margin-left: 0 !important;
156
  }
@@ -180,6 +188,12 @@ body.wp-admin.folded #qm {
180
  clear: none !important;
181
  }
182
 
 
 
 
 
 
 
183
  .qm:focus {
184
  outline: 0 !important;
185
  }
@@ -194,6 +208,7 @@ body.wp-admin.folded #qm {
194
  }
195
 
196
  @media all and (max-width: 900px) {
 
197
  .qm-half {
198
  float: none !important;
199
  width: 100% !important;
@@ -375,7 +390,7 @@ body.wp-admin.folded #qm {
375
  }
376
 
377
  .qm a {
378
- color: #2ea2cc !important;
379
  text-decoration: none !important;
380
  text-shadow: none !important;
381
  font-weight: normal !important;
@@ -383,7 +398,7 @@ body.wp-admin.folded #qm {
383
  .qm a:focus,
384
  .qm a:hover {
385
  text-decoration: underline !important;
386
- color: #2ea2cc !important;
387
  }
388
 
389
  .qm a.qm-warn {
@@ -391,7 +406,7 @@ body.wp-admin.folded #qm {
391
  }
392
 
393
  .qm a.qm-toggle {
394
- color: #999 !important;
395
  padding: 4px 8px 3px !important;
396
  border-left: 1px solid #e8e8e8 !important;
397
  border-bottom: 1px solid #e8e8e8 !important;
@@ -405,7 +420,7 @@ body.wp-admin.folded #qm {
405
  .qm a.qm-toggle:hover {
406
  text-decoration: none !important;
407
  border-color: #e8e8e8 !important;
408
- color: #444 !important;
409
  background: #eee !important;
410
  }
411
 
@@ -501,6 +516,13 @@ html[dir="rtl"] body.wp-admin #qm {
501
  }
502
  }
503
 
 
 
 
 
 
 
 
504
  html[dir="rtl"] body.wp-admin.folded #qm {
505
  margin-right: 36px !important;
506
  margin-left: 0 !important;
@@ -515,6 +537,13 @@ html[dir="rtl"] .qm th {
515
  text-align: right !important;
516
  }
517
 
 
 
 
 
 
 
 
518
  /* No-JS tweaks */
519
 
520
  .qm-no-js #qm-authentication,
146
  margin: 0 0 0 160px !important;
147
  }
148
 
149
+ @media only screen and (max-width: 960px) {
150
+ body.wp-admin.auto-fold #qm {
151
+ margin-left: 36px !important;
152
+ margin-right: 0 !important;
153
+ }
154
+ }
155
+
156
  body.wp-admin.folded #qm {
157
  margin-left: 36px !important;
158
  }
159
 
160
  @media screen and (max-width: 782px) {
161
+ body.wp-admin.auto-fold #qm,
162
  body.wp-admin #qm {
163
  margin-left: 0 !important;
164
  }
188
  clear: none !important;
189
  }
190
 
191
+ .qm-third {
192
+ float: left !important;
193
+ width: 33.33% !important;
194
+ clear: none !important;
195
+ }
196
+
197
  .qm:focus {
198
  outline: 0 !important;
199
  }
208
  }
209
 
210
  @media all and (max-width: 900px) {
211
+ .qm-third,
212
  .qm-half {
213
  float: none !important;
214
  width: 100% !important;
390
  }
391
 
392
  .qm a {
393
+ color: #00a0d2 !important;
394
  text-decoration: none !important;
395
  text-shadow: none !important;
396
  font-weight: normal !important;
398
  .qm a:focus,
399
  .qm a:hover {
400
  text-decoration: underline !important;
401
+ color: #00a0d2 !important;
402
  }
403
 
404
  .qm a.qm-warn {
406
  }
407
 
408
  .qm a.qm-toggle {
409
+ color: #00a0d2 !important;
410
  padding: 4px 8px 3px !important;
411
  border-left: 1px solid #e8e8e8 !important;
412
  border-bottom: 1px solid #e8e8e8 !important;
420
  .qm a.qm-toggle:hover {
421
  text-decoration: none !important;
422
  border-color: #e8e8e8 !important;
423
+ color: #00a0d2 !important;
424
  background: #eee !important;
425
  }
426
 
516
  }
517
  }
518
 
519
+ @media only screen and (max-width: 960px) {
520
+ html[dir="rtl"] body.wp-admin.auto-fold #qm {
521
+ margin-right: 36px !important;
522
+ margin-left: 0 !important;
523
+ }
524
+ }
525
+
526
  html[dir="rtl"] body.wp-admin.folded #qm {
527
  margin-right: 36px !important;
528
  margin-left: 0 !important;
537
  text-align: right !important;
538
  }
539
 
540
+ html[dir="rtl"] .qm a.qm-toggle {
541
+ border-left: none !important;
542
+ border-right: 1px solid #e8e8e8 !important;
543
+ right: auto !important;
544
+ left: 1px !important;
545
+ }
546
+
547
  /* No-JS tweaks */
548
 
549
  .qm-no-js #qm-authentication,
assets/query-monitor.js CHANGED
@@ -91,6 +91,8 @@ jQuery( function($) {
91
  $('#wp-admin-bar-query-monitor ul').append(container);
92
 
93
  $('#wp-admin-bar-query-monitor').find('a').on('click',function(e){
 
 
94
  if ( is_admin ) {
95
  $('#wpfooter').css('position','relative');
96
  }
@@ -99,10 +101,6 @@ jQuery( function($) {
99
 
100
  $( infinite_scroll.contentSelector ).infinitescroll('pause');
101
 
102
- if ( window.console ) {
103
- console.debug( qm_l10n.infinitescroll_paused );
104
- }
105
-
106
  } else if ( window.infiniteScroll && infiniteScroll.scroller ) {
107
  // Jetpack Infinite Scroll module
108
 
@@ -110,11 +108,20 @@ jQuery( function($) {
110
  return false;
111
  };
112
 
113
- if ( window.console ) {
114
- console.debug( qm_l10n.infinitescroll_paused );
115
- }
116
 
 
 
 
 
 
 
 
 
 
117
  }
 
118
  $('#qm').show();
119
  });
120
 
91
  $('#wp-admin-bar-query-monitor ul').append(container);
92
 
93
  $('#wp-admin-bar-query-monitor').find('a').on('click',function(e){
94
+ var paused = true;
95
+
96
  if ( is_admin ) {
97
  $('#wpfooter').css('position','relative');
98
  }
101
 
102
  $( infinite_scroll.contentSelector ).infinitescroll('pause');
103
 
 
 
 
 
104
  } else if ( window.infiniteScroll && infiniteScroll.scroller ) {
105
  // Jetpack Infinite Scroll module
106
 
108
  return false;
109
  };
110
 
111
+ } else if ( window.wp && wp.themes && wp.themes.RunInstaller ) {
112
+ // Infinite scrolling on Appearance -> Add New screens
 
113
 
114
+ var view = wp.themes.RunInstaller.view.view;
115
+ view.stopListening( view.parent, 'theme:scroll' );
116
+
117
+ } else {
118
+ paused = false;
119
+ }
120
+
121
+ if ( paused && window.console ) {
122
+ console.debug( qm_l10n.infinitescroll_paused );
123
  }
124
+
125
  $('#qm').show();
126
  });
127
 
classes/Backtrace.php CHANGED
@@ -40,15 +40,18 @@ class QM_Backtrace {
40
  'dbDelta' => true,
41
  );
42
  protected static $show_args = array(
43
- 'do_action' => 1,
44
- 'apply_filters' => 1,
45
- 'do_action_ref_array' => 1,
46
- 'apply_filters_ref_array' => 1,
47
- 'get_template_part' => 2,
48
- 'load_template' => 'dir',
49
- 'get_header' => 1,
50
- 'get_sidebar' => 1,
51
- 'get_footer' => 1,
 
 
 
52
  );
53
  protected static $filtered = false;
54
  protected $trace = null;
40
  'dbDelta' => true,
41
  );
42
  protected static $show_args = array(
43
+ 'do_action' => 1,
44
+ 'apply_filters' => 1,
45
+ 'do_action_ref_array' => 1,
46
+ 'apply_filters_ref_array' => 1,
47
+ 'get_template_part' => 2,
48
+ 'get_extended_template_part' => 2,
49
+ 'load_template' => 'dir',
50
+ 'dynamic_sidebar' => 1,
51
+ 'get_header' => 1,
52
+ 'get_sidebar' => 1,
53
+ 'get_footer' => 1,
54
+ 'get_site_by_path' => 3,
55
  );
56
  protected static $filtered = false;
57
  protected $trace = null;
classes/Collector.php CHANGED
@@ -40,6 +40,17 @@ abstract class QM_Collector {
40
 
41
  }
42
 
 
 
 
 
 
 
 
 
 
 
 
43
  protected function log_component( $component, $ltime, $type ) {
44
 
45
  if ( !isset( $this->data['component_times'][$component->name] ) ) {
40
 
41
  }
42
 
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
+
50
+ $this->data['dupes'][ $sql ][] = $i;
51
+
52
+ }
53
+
54
  protected function log_component( $component, $ltime, $type ) {
55
 
56
  if ( !isset( $this->data['component_times'][$component->name] ) ) {
classes/Util.php CHANGED
@@ -102,7 +102,11 @@ class QM_Util {
102
  } else {
103
  $plug = basename( $plug );
104
  }
105
- $name = sprintf( __( 'Plugin: %s', 'query-monitor' ), $plug );
 
 
 
 
106
  $context = $plug;
107
  break;
108
  case 'go-plugin':
102
  } else {
103
  $plug = basename( $plug );
104
  }
105
+ if ( 'mu-plugin' === $type ) {
106
+ $name = sprintf( __( 'MU Plugin: %s', 'query-monitor' ), $plug );
107
+ } else {
108
+ $name = sprintf( __( 'Plugin: %s', 'query-monitor' ), $plug );
109
+ }
110
  $context = $plug;
111
  break;
112
  case 'go-plugin':
collectors/assets.php CHANGED
@@ -25,6 +25,7 @@ class QM_Collector_Assets extends QM_Collector {
25
  add_action( 'admin_head', array( $this, 'action_head' ), 999 );
26
  add_action( 'wp_head', array( $this, 'action_head' ), 999 );
27
  add_action( 'login_head', array( $this, 'action_head' ), 999 );
 
28
  }
29
 
30
  public function action_head() {
25
  add_action( 'admin_head', array( $this, 'action_head' ), 999 );
26
  add_action( 'wp_head', array( $this, 'action_head' ), 999 );
27
  add_action( 'login_head', array( $this, 'action_head' ), 999 );
28
+ add_action( 'embed_head', array( $this, 'action_head' ), 999 );
29
  }
30
 
31
  public function action_head() {
collectors/db_dupes.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_DB_Dupes extends QM_Collector {
18
+
19
+ public $id = 'db_dupes';
20
+
21
+ public function name() {
22
+ return __( 'Duplicate Queries', 'query-monitor' );
23
+ }
24
+
25
+ public function process() {
26
+
27
+ if ( ! $dbq = QM_Collectors::get( 'db_queries' ) ) {
28
+ return;
29
+ }
30
+ if ( ! isset( $dbq->data['dupes'] ) ) {
31
+ return;
32
+ }
33
+
34
+ // Filter out SQL queries that do not have dupes
35
+ $this->data['dupes'] = array_filter( $dbq->data['dupes'], array( $this, '_filter_dupe_queries' ) );
36
+
37
+ // Ignore dupes from `WP_Query->set_found_posts()`
38
+ unset( $this->data['dupes']['SELECT FOUND_ROWS()'] );
39
+
40
+ $stacks = array();
41
+ $tops = array();
42
+ $callers = array();
43
+ $components = array();
44
+
45
+ // Loop over all SQL queries that have dupes
46
+ foreach ( $this->data['dupes'] as $sql => $query_ids ) {
47
+
48
+ // Loop over each query
49
+ foreach ( $query_ids as $query_id ) {
50
+
51
+ if ( isset( $dbq->data['dbs']['$wpdb']->rows[ $query_id ]['trace'] ) ) {
52
+
53
+ $trace = $dbq->data['dbs']['$wpdb']->rows[ $query_id ]['trace'];
54
+ $stack = wp_list_pluck( $trace->get_filtered_trace(), 'id' );
55
+ $component = $trace->get_component();
56
+
57
+ // Populate the component counts for this query
58
+ if ( isset( $components[ $sql ][ $component->name ] ) ) {
59
+ $components[ $sql ][ $component->name ]++;
60
+ } else {
61
+ $components[ $sql ][ $component->name ] = 1;
62
+ }
63
+
64
+ } else {
65
+ $stack = array_reverse( explode( ', ', $dbq->data['dbs']['$wpdb']->rows[ $query_id ]['stack'] ) );
66
+ }
67
+
68
+ // Populate the caller counts for this query
69
+ if ( isset( $callers[ $sql ][ $stack[0] ] ) ) {
70
+ $callers[ $sql ][ $stack[0] ]++;
71
+ } else {
72
+ $callers[ $sql ][ $stack[0] ] = 1;
73
+ }
74
+
75
+ // Populate the stack for this query
76
+ $stacks[ $sql ][] = $stack;
77
+
78
+ }
79
+
80
+ // Get the callers which are common to all stacks for this query
81
+ $common = call_user_func_array( 'array_intersect', $stacks[ $sql ] );
82
+
83
+ // Remove callers which are common to all stacks for this query
84
+ foreach ( $stacks[ $sql ] as $i => $stack ) {
85
+ $stacks[ $sql ][ $i ] = array_values( array_diff( $stack, $common ) );
86
+
87
+ // No uncommon callers within the stack? Just use the topmost caller.
88
+ if ( empty( $stacks[ $sql ][ $i ] ) ) {
89
+ $stacks[ $sql ][ $i ] = array_keys( $callers[ $sql ] );
90
+ }
91
+ }
92
+
93
+ // Wave a magic wand
94
+ $sources[ $sql ] = array_count_values( wp_list_pluck( $stacks[ $sql ], 0 ) );
95
+
96
+ }
97
+
98
+ if ( ! empty( $sources ) ) {
99
+ $this->data['dupe_sources'] = $sources;
100
+ $this->data['dupe_callers'] = $callers;
101
+ $this->data['dupe_components'] = $components;
102
+ }
103
+
104
+ }
105
+
106
+ public function _filter_dupe_queries( $queries ) {
107
+ return ( count( $queries ) > 1 );
108
+ }
109
+
110
+ }
111
+
112
+ function register_qm_collector_db_dupes( array $collectors, QueryMonitor $qm ) {
113
+ $collectors['db_dupes'] = new QM_Collector_DB_Dupes;
114
+ return $collectors;
115
+ }
116
+
117
+ add_filter( 'qm/collectors', 'register_qm_collector_db_dupes', 25, 2 );
collectors/db_queries.php CHANGED
@@ -101,6 +101,7 @@ class QM_Collector_DB_Queries extends QM_Collector {
101
  $total_time = 0;
102
  $has_result = false;
103
  $has_trace = false;
 
104
 
105
  foreach ( (array) $db->queries as $query ) {
106
 
@@ -159,6 +160,8 @@ class QM_Collector_DB_Queries extends QM_Collector {
159
  $this->log_type( $type );
160
  $this->log_caller( $caller_name, $ltime, $type );
161
 
 
 
162
  if ( $component ) {
163
  $this->log_component( $component, $ltime, $type );
164
  }
@@ -185,7 +188,8 @@ class QM_Collector_DB_Queries extends QM_Collector {
185
  $this->data['expensive'][] = $row;
186
  }
187
 
188
- $rows[] = $row;
 
189
 
190
  }
191
 
101
  $total_time = 0;
102
  $has_result = false;
103
  $has_trace = false;
104
+ $i = 0;
105
 
106
  foreach ( (array) $db->queries as $query ) {
107
 
160
  $this->log_type( $type );
161
  $this->log_caller( $caller_name, $ltime, $type );
162
 
163
+ $this->maybe_log_dupe( $sql, $i );
164
+
165
  if ( $component ) {
166
  $this->log_component( $component, $ltime, $type );
167
  }
188
  $this->data['expensive'][] = $row;
189
  }
190
 
191
+ $rows[ $i ] = $row;
192
+ $i++;
193
 
194
  }
195
 
collectors/overview.php CHANGED
@@ -44,6 +44,7 @@ class QM_Collector_Overview extends QM_Collector {
44
  $this->data['memory_limit'] = QM_Util::convert_hr_to_bytes( ini_get( 'memory_limit' ) );
45
  $this->data['memory_usage'] = ( 100 / $this->data['memory_limit'] ) * $this->data['memory'];
46
 
 
47
  }
48
 
49
  }
44
  $this->data['memory_limit'] = QM_Util::convert_hr_to_bytes( ini_get( 'memory_limit' ) );
45
  $this->data['memory_usage'] = ( 100 / $this->data['memory_limit'] ) * $this->data['memory'];
46
 
47
+ $this->data['is_admin'] = is_admin();
48
  }
49
 
50
  }
collectors/php_errors.php CHANGED
@@ -49,9 +49,6 @@ class QM_Collector_PHP_Errors extends QM_Collector {
49
 
50
  public function error_handler( $errno, $message, $file = null, $line = null ) {
51
 
52
- #if ( !( error_reporting() & $errno ) )
53
- # return false;
54
-
55
  switch ( $errno ) {
56
 
57
  case E_WARNING:
@@ -79,35 +76,31 @@ class QM_Collector_PHP_Errors extends QM_Collector {
79
 
80
  }
81
 
82
- if ( error_reporting() > 0 ) {
 
 
83
 
84
- if ( ! class_exists( 'QM_Backtrace' ) ) {
85
- return false;
86
- }
 
 
87
 
88
- $trace = new QM_Backtrace( array(
89
- 'ignore_current_filter' => false,
90
- ) );
91
- $caller = $trace->get_caller();
92
- $key = md5( $message . $file . $line . $caller['id'] );
93
-
94
- $filename = QM_Util::standard_dir( $file, '' );
95
-
96
- if ( isset( $this->data['errors'][$type][$key] ) ) {
97
- $this->data['errors'][$type][$key]->calls++;
98
- } else {
99
- $this->data['errors'][$type][$key] = (object) array(
100
- 'errno' => $errno,
101
- 'type' => $type,
102
- 'message' => $message,
103
- 'file' => $file,
104
- 'filename' => $filename,
105
- 'line' => $line,
106
- 'trace' => $trace,
107
- 'calls' => 1
108
- );
109
- }
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  }
112
 
113
  return apply_filters( 'qm/collect/php_errors_return_value', false );
49
 
50
  public function error_handler( $errno, $message, $file = null, $line = null ) {
51
 
 
 
 
52
  switch ( $errno ) {
53
 
54
  case E_WARNING:
76
 
77
  }
78
 
79
+ if ( ! class_exists( 'QM_Backtrace' ) ) {
80
+ return false;
81
+ }
82
 
83
+ $trace = new QM_Backtrace( array(
84
+ 'ignore_current_filter' => false,
85
+ ) );
86
+ $caller = $trace->get_caller();
87
+ $key = md5( $message . $file . $line . $caller['id'] );
88
 
89
+ $filename = QM_Util::standard_dir( $file, '' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
+ if ( isset( $this->data['errors'][$type][$key] ) ) {
92
+ $this->data['errors'][$type][$key]->calls++;
93
+ } else {
94
+ $this->data['errors'][$type][$key] = (object) array(
95
+ 'errno' => $errno,
96
+ 'type' => $type,
97
+ 'message' => $message,
98
+ 'file' => $file,
99
+ 'filename' => $filename,
100
+ 'line' => $line,
101
+ 'trace' => $trace,
102
+ 'calls' => 1
103
+ );
104
  }
105
 
106
  return apply_filters( 'qm/collect/php_errors_return_value', false );
collectors/request.php CHANGED
@@ -133,6 +133,8 @@ class QM_Collector_Request extends QM_Collector {
133
  $this->data['queried_object']['data'] = $qo;
134
  }
135
 
 
 
136
  }
137
 
138
  }
133
  $this->data['queried_object']['data'] = $qo;
134
  }
135
 
136
+ $this->data['request_method'] = strtoupper( $_SERVER['REQUEST_METHOD'] );
137
+
138
  }
139
 
140
  }
collectors/theme.php CHANGED
@@ -49,7 +49,7 @@ class QM_Collector_Theme extends QM_Collector {
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( $theme_directory, '', $template_path );
53
  $theme_template_file = ltrim( $theme_template_file, '/' );
54
 
55
  $this->data['template_path'] = $template_path;
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, '/' );
54
 
55
  $this->data['template_path'] = $template_path;
dispatchers/Html.php CHANGED
@@ -31,6 +31,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
31
  add_action( 'wp_footer', array( $this, 'action_footer' ) );
32
  add_action( 'admin_footer', array( $this, 'action_footer' ) );
33
  add_action( 'login_footer', array( $this, 'action_footer' ) );
 
34
 
35
  parent::__construct( $qm );
36
 
@@ -122,6 +123,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
122
  add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
123
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
124
  add_action( 'login_enqueue_scripts', array( $this, 'enqueue_assets' ) );
 
125
  add_action( 'send_headers', 'nocache_headers' );
126
 
127
  }
@@ -202,7 +204,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
202
  'qm-no-js',
203
  );
204
 
205
- if ( !is_admin() ) {
206
  $absolute = function_exists( 'twentyfifteen_setup' );
207
  if ( apply_filters( 'qm/output/absolute_position', $absolute ) ) {
208
  $class[] = 'qm-absolute';
@@ -224,7 +226,9 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
224
 
225
  protected function after_output() {
226
 
227
- echo '<div class="qm qm-half" id="qm-authentication">';
 
 
228
  echo '<table cellspacing="0">';
229
  echo '<thead>';
230
  echo '<tr>';
31
  add_action( 'wp_footer', array( $this, 'action_footer' ) );
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
 
123
  add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
124
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
125
  add_action( 'login_enqueue_scripts', array( $this, 'enqueue_assets' ) );
126
+ add_action( 'enqueue_embed_scripts', array( $this, 'enqueue_assets' ) );
127
  add_action( 'send_headers', 'nocache_headers' );
128
 
129
  }
204
  'qm-no-js',
205
  );
206
 
207
+ if ( did_action( 'wp_head' ) ) {
208
  $absolute = function_exists( 'twentyfifteen_setup' );
209
  if ( apply_filters( 'qm/output/absolute_position', $absolute ) ) {
210
  $class[] = 'qm-absolute';
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>';
234
  echo '<tr>';
output/html/assets.php CHANGED
@@ -63,7 +63,7 @@ class QM_Output_Html_Assets extends QM_Output_Html {
63
  ) as $position => $position_label ) {
64
 
65
  if ( isset( $data[ $position ][ $type ] ) ) {
66
- $this->dependency_rows( $data[ $position ][ $type ], $data['raw'][ $type ], sprintf( $position_label, $type_label ) );
67
  }
68
 
69
  }
@@ -77,7 +77,7 @@ class QM_Output_Html_Assets extends QM_Output_Html {
77
 
78
  }
79
 
80
- protected function dependency_rows( array $handles, WP_Dependencies $dependencies, $label ) {
81
 
82
  $first = true;
83
 
@@ -92,9 +92,9 @@ class QM_Output_Html_Assets extends QM_Output_Html {
92
  foreach ( $handles as $handle ) {
93
 
94
  if ( in_array( $handle, $dependencies->done ) ) {
95
- echo '<tr data-qm-subject="' . esc_attr( $handle ) . '">';
96
  } else {
97
- echo '<tr data-qm-subject="' . esc_attr( $handle ) . '" class="qm-warn">';
98
  }
99
 
100
  if ( $first ) {
@@ -102,7 +102,7 @@ class QM_Output_Html_Assets extends QM_Output_Html {
102
  echo '<th rowspan="' . esc_attr( $rowspan ) . '" class="qm-nowrap">' . esc_html( $label ) . '</th>';
103
  }
104
 
105
- $this->dependency_row( $dependencies->query( $handle ), $dependencies );
106
 
107
  echo '</tr>';
108
  $first = false;
@@ -110,12 +110,12 @@ class QM_Output_Html_Assets extends QM_Output_Html {
110
 
111
  }
112
 
113
- protected function dependency_row( _WP_Dependency $script, WP_Dependencies $dependencies ) {
114
 
115
- if ( empty( $script->ver ) ) {
116
  $ver = '';
117
  } else {
118
- $ver = $script->ver;
119
  }
120
 
121
  /**
@@ -124,7 +124,7 @@ class QM_Output_Html_Assets extends QM_Output_Html {
124
  * @param string $src Script loader source path.
125
  * @param string $handle Script handle.
126
  */
127
- $source = apply_filters( 'script_loader_src', $script->src, $script->handle );
128
 
129
  if ( is_wp_error( $source ) ) {
130
  $src = $source->get_error_message();
@@ -137,8 +137,8 @@ class QM_Output_Html_Assets extends QM_Output_Html {
137
  $src = $source;
138
  }
139
 
140
- $dependents = self::get_dependents( $script, $dependencies );
141
- $deps = $script->deps;
142
  sort( $deps );
143
 
144
  foreach ( $deps as & $dep ) {
@@ -147,7 +147,12 @@ class QM_Output_Html_Assets extends QM_Output_Html {
147
  }
148
  }
149
 
150
- echo '<td class="qm-wrap">' . esc_html( $script->handle ) . '<br><span class="qm-info">&nbsp;';
 
 
 
 
 
151
  if ( is_wp_error( $source ) ) {
152
  printf( '<span class="qm-warn">%s</span>',
153
  esc_html( $src )
@@ -156,13 +161,17 @@ class QM_Output_Html_Assets extends QM_Output_Html {
156
  echo esc_html( $src );
157
  }
158
  echo '</span></td>';
159
- echo '<td class="qm-nowrap qm-highlighter" data-qm-highlight="' . esc_attr( implode( ' ', $deps ) ) . '">' . implode( '<br>', array_map( 'esc_html', $deps ) ) . '</td>';
160
- echo '<td class="qm-nowrap qm-highlighter" data-qm-highlight="' . esc_attr( implode( ' ', $dependents ) ) . '">' . implode( '<br>', array_map( 'esc_html', $dependents ) ) . '</td>';
161
  echo '<td>' . esc_html( $ver ) . '</td>';
162
 
163
  }
164
 
165
- protected static function get_dependents( _WP_Dependency $script, WP_Dependencies $dependencies ) {
 
 
 
 
166
 
167
  // @TODO move this into the collector
168
  $dependents = array();
@@ -170,7 +179,7 @@ class QM_Output_Html_Assets extends QM_Output_Html {
170
 
171
  foreach ( $handles as $handle ) {
172
  if ( $item = $dependencies->query( $handle ) ) {
173
- if ( in_array( $script->handle, $item->deps ) ) {
174
  $dependents[] = $handle;
175
  }
176
  }
63
  ) as $position => $position_label ) {
64
 
65
  if ( isset( $data[ $position ][ $type ] ) ) {
66
+ $this->dependency_rows( $data[ $position ][ $type ], $data['raw'][ $type ], sprintf( $position_label, $type_label ), $type );
67
  }
68
 
69
  }
77
 
78
  }
79
 
80
+ protected function dependency_rows( array $handles, WP_Dependencies $dependencies, $label, $type ) {
81
 
82
  $first = true;
83
 
92
  foreach ( $handles as $handle ) {
93
 
94
  if ( in_array( $handle, $dependencies->done ) ) {
95
+ echo '<tr data-qm-subject="' . esc_attr( $type . '-' . $handle ) . '">';
96
  } else {
97
+ echo '<tr data-qm-subject="' . esc_attr( $type . '-' . $handle ) . '" class="qm-warn">';
98
  }
99
 
100
  if ( $first ) {
102
  echo '<th rowspan="' . esc_attr( $rowspan ) . '" class="qm-nowrap">' . esc_html( $label ) . '</th>';
103
  }
104
 
105
+ $this->dependency_row( $dependencies->query( $handle ), $dependencies, $type );
106
 
107
  echo '</tr>';
108
  $first = false;
110
 
111
  }
112
 
113
+ protected function dependency_row( _WP_Dependency $dependency, WP_Dependencies $dependencies, $type ) {
114
 
115
+ if ( empty( $dependency->ver ) ) {
116
  $ver = '';
117
  } else {
118
+ $ver = $dependency->ver;
119
  }
120
 
121
  /**
124
  * @param string $src Script loader source path.
125
  * @param string $handle Script handle.
126
  */
127
+ $source = apply_filters( 'script_loader_src', $dependency->src, $dependency->handle );
128
 
129
  if ( is_wp_error( $source ) ) {
130
  $src = $source->get_error_message();
137
  $src = $source;
138
  }
139
 
140
+ $dependents = self::get_dependents( $dependency, $dependencies, $type );
141
+ $deps = $dependency->deps;
142
  sort( $deps );
143
 
144
  foreach ( $deps as & $dep ) {
147
  }
148
  }
149
 
150
+ $this->type = $type;
151
+
152
+ $highlight_deps = array_map( array( $this, '_prefix_type' ), $deps );
153
+ $highlight_dependents = array_map( array( $this, '_prefix_type' ), $dependents );
154
+
155
+ echo '<td class="qm-wrap">' . esc_html( $dependency->handle ) . '<br><span class="qm-info">&nbsp;';
156
  if ( is_wp_error( $source ) ) {
157
  printf( '<span class="qm-warn">%s</span>',
158
  esc_html( $src )
161
  echo esc_html( $src );
162
  }
163
  echo '</span></td>';
164
+ echo '<td class="qm-nowrap qm-highlighter" data-qm-highlight="' . esc_attr( implode( ' ', $highlight_deps ) ) . '">' . implode( '<br>', array_map( 'esc_html', $deps ) ) . '</td>';
165
+ echo '<td class="qm-nowrap qm-highlighter" data-qm-highlight="' . esc_attr( implode( ' ', $highlight_dependents ) ) . '">' . implode( '<br>', array_map( 'esc_html', $dependents ) ) . '</td>';
166
  echo '<td>' . esc_html( $ver ) . '</td>';
167
 
168
  }
169
 
170
+ public function _prefix_type( $val ) {
171
+ return $this->type . '-' . $val;
172
+ }
173
+
174
+ protected static function get_dependents( _WP_Dependency $dependency, WP_Dependencies $dependencies, $type ) {
175
 
176
  // @TODO move this into the collector
177
  $dependents = array();
179
 
180
  foreach ( $handles as $handle ) {
181
  if ( $item = $dependencies->query( $handle ) ) {
182
+ if ( in_array( $dependency->handle, $item->deps ) ) {
183
  $dependents[] = $handle;
184
  }
185
  }
output/html/db_dupes.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2009-2015 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_Output_Html_DB_Dupes extends QM_Output_Html {
18
+
19
+ public function __construct( QM_Collector $collector ) {
20
+ parent::__construct( $collector );
21
+ add_filter( 'qm/output/menus', array( $this, 'admin_menu' ), 45 );
22
+ }
23
+
24
+ public function output() {
25
+
26
+ $data = $this->collector->get_data();
27
+
28
+ if ( empty( $data['dupes'] ) ) {
29
+ return;
30
+ }
31
+
32
+ $colspan = empty( $data['dupe_components'] ) ? 4 : 5;
33
+
34
+ echo '<div class="qm" id="' . esc_attr( $this->collector->id() ) . '">';
35
+ echo '<table cellspacing="0">';
36
+ echo '<thead>';
37
+ echo '<tr>';
38
+ echo '<th colspan="' . absint( $colspan ) . '">' . esc_html( $this->collector->name() ) . '</th>';
39
+ echo '</tr>';
40
+
41
+ echo '<tr>';
42
+ echo '<th>' . esc_html__( 'Query', 'query-monitor' ) . '</th>';
43
+ echo '<th class="qm-num">' . esc_html__( 'Count', 'query-monitor' ) . '</th>';
44
+ echo '<th>' . esc_html__( 'Callers', 'query-monitor' ) . '</th>';
45
+ if ( ! empty( $data['dupe_components'] ) ) {
46
+ echo '<th>' . esc_html__( 'Components', 'query-monitor' ) . '</th>';
47
+ }
48
+ echo '<th>' . esc_html__( 'Potential Troublemakers', 'query-monitor' ) . '</th>';
49
+ echo '</tr>';
50
+
51
+ echo '</thead>';
52
+
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>',
67
+ esc_attr( $caller ),
68
+ esc_html( $caller ),
69
+ esc_html( sprintf(
70
+ _n( '%s call', '%s calls', $calls, 'query-monitor' ),
71
+ number_format_i18n( $calls )
72
+ ) )
73
+ );
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>',
81
+ esc_html( $component ),
82
+ esc_html( sprintf(
83
+ _n( '%s call', '%s calls', $calls, 'query-monitor' ),
84
+ number_format_i18n( $calls )
85
+ ) )
86
+ );
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>',
94
+ esc_html( $source ),
95
+ esc_html( sprintf(
96
+ _n( '%s call', '%s calls', $calls, 'query-monitor' ),
97
+ number_format_i18n( $calls )
98
+ ) )
99
+ );
100
+ }
101
+ echo '</td>';
102
+ echo '</tr>';
103
+ }
104
+ echo '</tbody>';
105
+
106
+ echo '</table>';
107
+ echo '</div>';
108
+
109
+ }
110
+
111
+ public function admin_menu( array $menu ) {
112
+
113
+ if ( $dbq = QM_Collectors::get( 'db_dupes' ) ) {
114
+ $dbq_data = $dbq->get_data();
115
+ if ( isset( $dbq_data['dupes'] ) && count( $dbq_data['dupes'] ) ) {
116
+ $menu[] = $this->menu( array(
117
+ 'title' => esc_html( sprintf(
118
+ __( 'Duplicate Queries (%s)', 'query-monitor' ),
119
+ count( $dbq_data['dupes'] )
120
+ ) ),
121
+ ) );
122
+ }
123
+ }
124
+ return $menu;
125
+
126
+ }
127
+
128
+ }
129
+
130
+ function register_qm_output_html_db_dupes( array $output, QM_Collectors $collectors ) {
131
+ if ( $collector = QM_Collectors::get( 'db_dupes' ) ) {
132
+ $output['db_dupes'] = new QM_Output_Html_DB_Dupes( $collector );
133
+ }
134
+ return $output;
135
+ }
136
+
137
+ add_filter( 'qm/outputter/html', 'register_qm_output_html_db_dupes', 45, 2 );
output/html/db_queries.php CHANGED
@@ -352,9 +352,10 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
352
  }
353
 
354
  if ( isset( $cols['sql'] ) ) {
355
- printf( '<td class="qm-row-sql qm-ltr qm-wrap">%s</td>',
 
356
  $sql
357
- ); // WPCS: XSS ok.
358
  }
359
 
360
  if ( isset( $cols['caller'] ) ) {
@@ -402,9 +403,10 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
402
  $data = $this->collector->get_data();
403
 
404
  if ( isset( $data['dbs'] ) ) {
405
- foreach ( $data['dbs'] as $db ) {
406
  $title[] = sprintf(
407
- _x( '%s<small>S</small>', 'database query time', 'query-monitor' ),
 
408
  number_format_i18n( $db->total_time, 4 )
409
  );
410
  $title[] = sprintf(
352
  }
353
 
354
  if ( isset( $cols['sql'] ) ) {
355
+ printf( // WPCS: XSS ok.
356
+ '<td class="qm-row-sql qm-ltr qm-wrap">%s</td>',
357
  $sql
358
+ );
359
  }
360
 
361
  if ( isset( $cols['caller'] ) ) {
403
  $data = $this->collector->get_data();
404
 
405
  if ( isset( $data['dbs'] ) ) {
406
+ foreach ( $data['dbs'] as $key => $db ) {
407
  $title[] = sprintf(
408
+ _x( '%s%s<small>S</small>', 'database query time', 'query-monitor' ),
409
+ ( count( $data['dbs'] ) > 1 ? '&bull;&nbsp;&nbsp;&nbsp;' : '' ),
410
  number_format_i18n( $db->total_time, 4 )
411
  );
412
  $title[] = sprintf(
output/html/environment.php CHANGED
@@ -27,7 +27,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
27
 
28
  echo '<div id="' . esc_attr( $this->collector->id() ) . '">';
29
 
30
- echo '<div class="qm qm-half">';
31
  echo '<table cellspacing="0">';
32
  echo '<thead>';
33
  echo '<tr>';
@@ -66,7 +66,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
66
 
67
  echo '<tr>';
68
  echo '<td>' . esc_html( $key ) . '</td>';
69
- echo '<td>';
70
  echo esc_html( $val['after'] );
71
 
72
  if ( $val['after'] !== $val['before'] ) {
@@ -87,7 +87,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
87
 
88
  echo '<tr>';
89
  echo '<td>error_reporting</td>';
90
- echo '<td>' . esc_html( $data['php']['error_reporting'] ) . '<br><span class="qm-info">&nbsp;';
91
  echo $error_levels; // WPCS: XSS ok.
92
  echo '</span></td>';
93
  echo '</tr>';
@@ -106,7 +106,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
106
  $name = sprintf( __( 'Database: %s', 'query-monitor' ), $id );
107
  }
108
 
109
- echo '<div class="qm qm-half">';
110
  echo '<table cellspacing="0">';
111
  echo '<thead>';
112
  echo '<tr>';
@@ -123,7 +123,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
123
  if ( ! isset( $value ) ) {
124
  echo '<td><span class="qm-warn">' . esc_html__( 'Unknown', 'query-monitor' ) . '</span></td>';
125
  } else {
126
- echo '<td>' . esc_html( $value ) . '</td>';
127
  }
128
 
129
  echo '</tr>';
@@ -170,7 +170,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
170
  }
171
 
172
  echo '<td>' . esc_html( $key ) . '</td>';
173
- echo '<td>';
174
  echo esc_html( $val );
175
  echo $append; // WPCS: XSS ok.
176
  echo '</td>';
@@ -189,7 +189,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
189
 
190
  }
191
 
192
- echo '<div class="qm qm-half qm-clear">';
193
  echo '<table cellspacing="0">';
194
  echo '<thead>';
195
  echo '<tr>';
@@ -202,7 +202,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
202
 
203
  echo '<tr>';
204
  echo '<td>' . esc_html( $key ) . '</td>';
205
- echo '<td>' . esc_html( $val ) . '</td>';
206
  echo '</tr>';
207
 
208
  }
@@ -211,7 +211,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
211
  echo '</table>';
212
  echo '</div>';
213
 
214
- echo '<div class="qm qm-half">';
215
  echo '<table cellspacing="0">';
216
  echo '<thead>';
217
  echo '<tr>';
@@ -222,13 +222,13 @@ class QM_Output_Html_Environment extends QM_Output_Html {
222
 
223
  echo '<tr>';
224
  echo '<td>' . esc_html__( 'software', 'query-monitor' ) . '</td>';
225
- echo '<td>' . esc_html( $data['server']['name'] ) . '</td>';
226
  echo '</tr>';
227
 
228
  echo '<tr>';
229
  echo '<td>' . esc_html__( 'version', 'query-monitor' ) . '</td>';
230
  if ( !empty( $data['server']['version'] ) ) {
231
- echo '<td>' . esc_html( $data['server']['version'] ) . '</td>';
232
  } else {
233
  echo '<td><em>' . esc_html__( 'Unknown', 'query-monitor' ) . '</em></td>';
234
  }
@@ -237,7 +237,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
237
  echo '<tr>';
238
  echo '<td>' . esc_html__( 'address', 'query-monitor' ) . '</td>';
239
  if ( !empty( $data['server']['address'] ) ) {
240
- echo '<td>' . esc_html( $data['server']['address'] ) . '</td>';
241
  } else {
242
  echo '<td><em>' . esc_html__( 'Unknown', 'query-monitor' ) . '</em></td>';
243
  }
@@ -245,7 +245,7 @@ class QM_Output_Html_Environment extends QM_Output_Html {
245
 
246
  echo '<tr>';
247
  echo '<td>' . esc_html__( 'host', 'query-monitor' ) . '</td>';
248
- echo '<td>' . esc_html( $data['server']['host'] ) . '</td>';
249
  echo '</tr>';
250
 
251
  echo '</tbody>';
27
 
28
  echo '<div id="' . esc_attr( $this->collector->id() ) . '">';
29
 
30
+ echo '<div class="qm qm-third">';
31
  echo '<table cellspacing="0">';
32
  echo '<thead>';
33
  echo '<tr>';
66
 
67
  echo '<tr>';
68
  echo '<td>' . esc_html( $key ) . '</td>';
69
+ echo '<td class="qm-wrap">';
70
  echo esc_html( $val['after'] );
71
 
72
  if ( $val['after'] !== $val['before'] ) {
87
 
88
  echo '<tr>';
89
  echo '<td>error_reporting</td>';
90
+ echo '<td class="qm-wrap">' . esc_html( $data['php']['error_reporting'] ) . '<br><span class="qm-info">&nbsp;';
91
  echo $error_levels; // WPCS: XSS ok.
92
  echo '</span></td>';
93
  echo '</tr>';
106
  $name = sprintf( __( 'Database: %s', 'query-monitor' ), $id );
107
  }
108
 
109
+ echo '<div class="qm qm-third">';
110
  echo '<table cellspacing="0">';
111
  echo '<thead>';
112
  echo '<tr>';
123
  if ( ! isset( $value ) ) {
124
  echo '<td><span class="qm-warn">' . esc_html__( 'Unknown', 'query-monitor' ) . '</span></td>';
125
  } else {
126
+ echo '<td class="qm-wrap">' . esc_html( $value ) . '</td>';
127
  }
128
 
129
  echo '</tr>';
170
  }
171
 
172
  echo '<td>' . esc_html( $key ) . '</td>';
173
+ echo '<td class="qm-wrap">';
174
  echo esc_html( $val );
175
  echo $append; // WPCS: XSS ok.
176
  echo '</td>';
189
 
190
  }
191
 
192
+ echo '<div class="qm qm-third" style="float:right !important">';
193
  echo '<table cellspacing="0">';
194
  echo '<thead>';
195
  echo '<tr>';
202
 
203
  echo '<tr>';
204
  echo '<td>' . esc_html( $key ) . '</td>';
205
+ echo '<td class="qm-wrap">' . esc_html( $val ) . '</td>';
206
  echo '</tr>';
207
 
208
  }
211
  echo '</table>';
212
  echo '</div>';
213
 
214
+ echo '<div class="qm qm-third">';
215
  echo '<table cellspacing="0">';
216
  echo '<thead>';
217
  echo '<tr>';
222
 
223
  echo '<tr>';
224
  echo '<td>' . esc_html__( 'software', 'query-monitor' ) . '</td>';
225
+ echo '<td class="qm-wrap">' . esc_html( $data['server']['name'] ) . '</td>';
226
  echo '</tr>';
227
 
228
  echo '<tr>';
229
  echo '<td>' . esc_html__( 'version', 'query-monitor' ) . '</td>';
230
  if ( !empty( $data['server']['version'] ) ) {
231
+ echo '<td class="qm-wrap">' . esc_html( $data['server']['version'] ) . '</td>';
232
  } else {
233
  echo '<td><em>' . esc_html__( 'Unknown', 'query-monitor' ) . '</em></td>';
234
  }
237
  echo '<tr>';
238
  echo '<td>' . esc_html__( 'address', 'query-monitor' ) . '</td>';
239
  if ( !empty( $data['server']['address'] ) ) {
240
+ echo '<td class="qm-wrap">' . esc_html( $data['server']['address'] ) . '</td>';
241
  } else {
242
  echo '<td><em>' . esc_html__( 'Unknown', 'query-monitor' ) . '</em></td>';
243
  }
245
 
246
  echo '<tr>';
247
  echo '<td>' . esc_html__( 'host', 'query-monitor' ) . '</td>';
248
+ echo '<td class="qm-wrap">' . esc_html( $data['server']['host'] ) . '</td>';
249
  echo '</tr>';
250
 
251
  echo '</tbody>';
output/html/hooks.php CHANGED
@@ -95,11 +95,11 @@ class QM_Output_Html_Hooks extends QM_Output_Html {
95
  $component = '';
96
  }
97
 
98
- printf(
99
  '<tr data-qm-subject="%s" %s>',
100
  esc_attr( $component ),
101
  $attr
102
- ); // WPCS: XSS ok.
103
 
104
  if ( $first ) {
105
 
95
  $component = '';
96
  }
97
 
98
+ printf( // WPCS: XSS ok.
99
  '<tr data-qm-subject="%s" %s>',
100
  esc_attr( $component ),
101
  $attr
102
+ );
103
 
104
  if ( $first ) {
105
 
output/html/http.php CHANGED
@@ -122,20 +122,20 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
122
  $attr .= ' ' . $a . '="' . esc_attr( $v ) . '"';
123
  }
124
 
125
- printf(
126
  '<tr %s class="%s">',
127
  $attr,
128
  esc_attr( $css )
129
- ); // WPCS:: XSS ok.
130
  printf(
131
  '<td class="qm-num">%s</td>',
132
  intval( $i )
133
  );
134
- printf(
135
  '<td class="qm-url qm-ltr qm-wrap">%s<br>%s</td>',
136
  esc_html( $method ),
137
  $url
138
- ); // WPCS:: XSS ok.
139
  printf(
140
  '<td>%s</td>',
141
  esc_html( $response )
@@ -144,10 +144,10 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
144
  '<td>%s</td>',
145
  esc_html( $transport )
146
  );
147
- printf(
148
  '<td class="qm-nowrap qm-ltr">%s</td>',
149
  implode( '<br>', $stack )
150
- ); // WPCS: XSS ok.
151
  printf(
152
  '<td class="qm-nowrap">%s</td>',
153
  esc_html( $component->name )
122
  $attr .= ' ' . $a . '="' . esc_attr( $v ) . '"';
123
  }
124
 
125
+ printf( // WPCS: XSS ok.
126
  '<tr %s class="%s">',
127
  $attr,
128
  esc_attr( $css )
129
+ );
130
  printf(
131
  '<td class="qm-num">%s</td>',
132
  intval( $i )
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(
140
  '<td>%s</td>',
141
  esc_html( $response )
144
  '<td>%s</td>',
145
  esc_html( $transport )
146
  );
147
+ printf( // WPCS: XSS ok.
148
  '<td class="qm-nowrap qm-ltr">%s</td>',
149
  implode( '<br>', $stack )
150
+ );
151
  printf(
152
  '<td class="qm-nowrap">%s</td>',
153
  esc_html( $component->name )
output/html/languages.php CHANGED
@@ -52,10 +52,10 @@ class QM_Output_Html_Languages extends QM_Output_Html {
52
  echo '<tr>';
53
 
54
  echo '<td>' . esc_html( $mofile['domain'] ) . '</td>';
55
- echo '<td class="qm-nowrap">';
56
  echo self::output_filename( $mofile['caller']['display'], $mofile['caller']['file'], $mofile['caller']['line'] ); // WPCS: XSS ok.
57
  echo '</td>';
58
- echo '<td>';
59
  echo esc_html( QM_Util::standard_dir( $mofile['mofile'], '' ) );
60
  echo '</td>';
61
 
52
  echo '<tr>';
53
 
54
  echo '<td>' . esc_html( $mofile['domain'] ) . '</td>';
55
+ echo '<td class="qm-nowrap qm-ltr">';
56
  echo self::output_filename( $mofile['caller']['display'], $mofile['caller']['file'], $mofile['caller']['line'] ); // WPCS: XSS ok.
57
  echo '</td>';
58
+ echo '<td class="qm-ltr">';
59
  echo esc_html( QM_Util::standard_dir( $mofile['mofile'], '' ) );
60
  echo '</td>';
61
 
output/html/transients.php CHANGED
@@ -87,10 +87,10 @@ class QM_Output_Html_Transients extends QM_Output_Html {
87
  $stack[] = self::output_filename( $item['display'], $item['calling_file'], $item['calling_line'] );
88
  }
89
 
90
- printf(
91
  '<td class="qm-nowrap qm-ltr">%s</td>',
92
  implode( '<br>', $stack )
93
- ); // WPCS: XSS ok.
94
  printf(
95
  '<td class="qm-nowrap">%s</td>',
96
  esc_html( $component->name )
87
  $stack[] = self::output_filename( $item['display'], $item['calling_file'], $item['calling_line'] );
88
  }
89
 
90
+ printf( // WPCS: XSS ok.
91
  '<td class="qm-nowrap qm-ltr">%s</td>',
92
  implode( '<br>', $stack )
93
+ );
94
  printf(
95
  '<td class="qm-nowrap">%s</td>',
96
  esc_html( $component->name )
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.9.1
6
  Plugin URI: https://querymonitor.com/
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.10.0
6
  Plugin URI: https://querymonitor.com/
7
  Author: John Blackbourn
8
  Author URI: https://johnblackbourn.com/
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  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.4
6
- Stable tag: 2.9.1
7
  License: GPLv2 or later
8
 
9
  View debugging and performance information on database queries, hooks, conditionals, HTTP requests, redirects and more.
@@ -20,7 +20,7 @@ Here's an overview of what's shown:
20
 
21
  * Shows all database queries performed on the current page
22
  * Shows **affected rows** and time for all queries
23
- * Show notifications for **slow 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**
@@ -173,6 +173,16 @@ No, I do not accept donations. If you like the plugin, I'd love for you to [leav
173
 
174
  == Changelog ==
175
 
 
 
 
 
 
 
 
 
 
 
176
  = 2.9.1 =
177
 
178
  * Query callers and query components can now be clicked to filter the main query list by that caller or component.
2
  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.
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**
173
 
174
  == Changelog ==
175
 
176
+ = 2.10.0 =
177
+
178
+ * Add a new panel which lists duplicated database queries.
179
+ * Add support for displaying QM's output when viewing an embed.
180
+ * Differentiate regular plugins from mu-plugins when displaying components.
181
+ * Ensure early errors are always reported regardless of system level error reporting.
182
+ * Ensure that script and style dependency highlighting is restricted to the scripts and styles tables, respectively.
183
+ * Rearrange the Environment section output a little.
184
+ * Various minor tweaks.
185
+
186
  = 2.9.1 =
187
 
188
  * Query callers and query components can now be clicked to filter the main query list by that caller or component.