Query Monitor - Version 2.8.0

Version Description

  • A new Languages component for debugging languages and text domains. Thanks, @MPolleke!
  • REST API debugging in the form of HTTP headers when performing an authenticated REST API request. Shows PHP errors when relevant, along with an overview of memory usage, processing time, database query number, and database query time.
  • Various visual improvements, including displaying the relevant file name below stack trace functions, and a more visible stack trace expansion toggle.
  • Add is_embed(), is_comment_feed(), and is_user_admin() to the list of conditional functions.
  • Add HHVM, SAPI, and MySQL client info to the Environment component.
  • QM is now not loaded at all on the CLI.
  • Avoid an issue with the CloudFlare Flexible SSL plugin.
  • Improve the output of Multisite's $current_blog and $current_site in the Request component.
  • Fully handle Windows paths when detecting a file component.
  • Don't display the symlink warning when using a secondary instance of WPDB.
  • Whole bunch of internal structure refactoring, escaping, and other misc tweaks.
Download this release

Release Info

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

Code changes from version 2.7.4 to 2.8.0

assets/query-monitor.css CHANGED
@@ -192,7 +192,7 @@ body.wp-admin.folded #qm {
192
  }
193
 
194
  .qm table {
195
- color: #555 !important;
196
  border-collapse: collapse !important;
197
  box-shadow: none !important;
198
  width: 100% !important;
@@ -215,6 +215,7 @@ body.wp-admin.folded #qm {
215
  vertical-align: top !important;
216
  text-shadow: none !important;
217
  text-transform: none !important;
 
218
  }
219
 
220
  .qm .qm-totally-legit-spacer td {
@@ -264,7 +265,7 @@ body.wp-admin.folded #qm {
264
  border-collapse: collapse !important;
265
  margin: 0 !important;
266
  background: #fff !important;
267
- color: #555 !important;
268
  border-style: hidden !important;
269
  width: 100% !important;
270
  }
@@ -367,8 +368,13 @@ body.wp-admin.folded #qm {
367
 
368
  .qm a.qm-toggle {
369
  color: #bbb !important;
370
- padding: 0 3px !important;
371
- border: 1px solid transparent !important;
 
 
 
 
 
372
  }
373
 
374
  .qm a.qm-toggle:focus,
192
  }
193
 
194
  .qm table {
195
+ color: #444 !important;
196
  border-collapse: collapse !important;
197
  box-shadow: none !important;
198
  width: 100% !important;
215
  vertical-align: top !important;
216
  text-shadow: none !important;
217
  text-transform: none !important;
218
+ position: relative !important;
219
  }
220
 
221
  .qm .qm-totally-legit-spacer td {
265
  border-collapse: collapse !important;
266
  margin: 0 !important;
267
  background: #fff !important;
268
+ color: #444 !important;
269
  border-style: hidden !important;
270
  width: 100% !important;
271
  }
368
 
369
  .qm a.qm-toggle {
370
  color: #bbb !important;
371
+ padding: 2px 7px !important;
372
+ border-left: 1px solid #e1e1e1 !important;
373
+ border-bottom: 1px solid #e1e1e1 !important;
374
+ position: absolute !important;
375
+ top: 0 !important;
376
+ right: 0 !important;
377
+ background: #fff !important;
378
  }
379
 
380
  .qm a.qm-toggle:focus,
assets/query-monitor.js CHANGED
@@ -150,7 +150,7 @@ jQuery( function($) {
150
 
151
  $('#qm').find('.qm-toggle').on('click',function(e){
152
  var el = $(this);
153
- $(this).closest('td').find('.qm-toggled').toggle(0,function(){
154
  if ( el.attr('data-off') == el.text() )
155
  el.text(el.attr('data-on'));
156
  else
@@ -183,7 +183,7 @@ jQuery( function($) {
183
 
184
  $( document ).ajaxSuccess( function( event, response, options ) {
185
 
186
- var errors = response.getResponseHeader( 'X-QM-Errors' );
187
 
188
  if ( !errors )
189
  return event;
@@ -192,7 +192,7 @@ jQuery( function($) {
192
 
193
  for ( var key = 1; key <= errors; key++ ) {
194
 
195
- error = $.parseJSON( response.getResponseHeader( 'X-QM-Error-' + key ) );
196
 
197
  if ( window.console ) {
198
  console.debug( '=== ' + qm_l10n.ajax_error + ' ===' );
150
 
151
  $('#qm').find('.qm-toggle').on('click',function(e){
152
  var el = $(this);
153
+ $(this).closest('td').find('.qm-toggled').slideToggle(100,function(){
154
  if ( el.attr('data-off') == el.text() )
155
  el.text(el.attr('data-on'));
156
  else
183
 
184
  $( document ).ajaxSuccess( function( event, response, options ) {
185
 
186
+ var errors = response.getResponseHeader( 'X-QM-error-count' );
187
 
188
  if ( !errors )
189
  return event;
192
 
193
  for ( var key = 1; key <= errors; key++ ) {
194
 
195
+ error = $.parseJSON( response.getResponseHeader( 'X-QM-error-' + key ) );
196
 
197
  if ( window.console ) {
198
  console.debug( '=== ' + qm_l10n.ajax_error + ' ===' );
classes/Collectors.php CHANGED
@@ -18,6 +18,7 @@ if ( ! class_exists( 'QM_Collectors' ) ) {
18
  class QM_Collectors implements IteratorAggregate {
19
 
20
  private $items = array();
 
21
 
22
  public function getIterator() {
23
  return new ArrayIterator( $this->items );
@@ -47,5 +48,16 @@ class QM_Collectors implements IteratorAggregate {
47
 
48
  }
49
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
  }
18
  class QM_Collectors implements IteratorAggregate {
19
 
20
  private $items = array();
21
+ private $processed = false;
22
 
23
  public function getIterator() {
24
  return new ArrayIterator( $this->items );
48
 
49
  }
50
 
51
+ public function process() {
52
+ if ( $this->processed ) {
53
+ return;
54
+ }
55
+ foreach ( $this as $collector ) {
56
+ $collector->tear_down();
57
+ $collector->process();
58
+ }
59
+ $this->processed = true;
60
+ }
61
+
62
  }
63
  }
classes/Dispatcher.php CHANGED
@@ -24,19 +24,58 @@ abstract class QM_Dispatcher {
24
  define( 'QM_COOKIE', 'query_monitor_' . COOKIEHASH );
25
  }
26
 
 
 
27
  }
28
 
29
  abstract public function is_active();
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  public function init() {
 
32
  // nothing
33
  }
34
 
35
- public function before_output() {
36
  // nothing
37
  }
38
 
39
- public function after_output() {
40
  // nothing
41
  }
42
 
@@ -50,13 +89,13 @@ abstract class QM_Dispatcher {
50
  return true;
51
  }
52
 
53
- return $this->user_verified();
54
 
55
  }
56
 
57
- public function user_verified() {
58
  if ( isset( $_COOKIE[QM_COOKIE] ) ) {
59
- return $this->verify_cookie( stripslashes( $_COOKIE[QM_COOKIE] ) );
60
  }
61
  return false;
62
  }
24
  define( 'QM_COOKIE', 'query_monitor_' . COOKIEHASH );
25
  }
26
 
27
+ add_action( 'init', array( $this, 'init' ) );
28
+
29
  }
30
 
31
  abstract public function is_active();
32
 
33
+ final public function should_dispatch() {
34
+
35
+ $e = error_get_last();
36
+
37
+ # Don't dispatch if a fatal has occurred:
38
+ if ( ! empty( $e ) and ( $e['type'] & ( E_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR ) ) ) {
39
+ return false;
40
+ }
41
+
42
+ # Allow users to disable this dispatcher
43
+ if ( ! apply_filters( "qm/dispatch/{$this->id}", true ) ) {
44
+ return false;
45
+ }
46
+
47
+ return $this->is_active();
48
+
49
+ }
50
+
51
+ public function get_outputters( $outputter_id ) {
52
+
53
+ $out = array();
54
+
55
+ $collectors = QM_Collectors::init();
56
+ $collectors->process();
57
+
58
+ $this->outputters = apply_filters( "qm/outputter/{$outputter_id}", array(), $collectors );
59
+
60
+ /* @var QM_Output[] */
61
+ foreach ( $this->outputters as $id => $outputter ) {
62
+ $out[ $id ] = $outputter;
63
+ }
64
+
65
+ return $out;
66
+
67
+ }
68
+
69
  public function init() {
70
+ // @TODO should be abstract?
71
  // nothing
72
  }
73
 
74
+ protected function before_output() {
75
  // nothing
76
  }
77
 
78
+ protected function after_output() {
79
  // nothing
80
  }
81
 
89
  return true;
90
  }
91
 
92
+ return self::user_verified();
93
 
94
  }
95
 
96
+ public static function user_verified() {
97
  if ( isset( $_COOKIE[QM_COOKIE] ) ) {
98
+ return self::verify_cookie( stripslashes( $_COOKIE[QM_COOKIE] ) );
99
  }
100
  return false;
101
  }
classes/Output.php CHANGED
@@ -14,12 +14,24 @@ GNU General Public License for more details.
14
 
15
  */
16
 
17
- if ( ! interface_exists( 'QM_Output' ) ) {
18
- interface QM_Output {
19
 
20
- public function __construct( QM_Collector $collector );
21
 
22
- public function output();
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  }
25
  }
14
 
15
  */
16
 
17
+ if ( ! class_exists( 'QM_Output' ) ) {
18
+ abstract class QM_Output {
19
 
20
+ protected $collector;
21
 
22
+ public function __construct( QM_Collector $collector ) {
23
+ $this->collector = $collector;
24
+ }
25
+
26
+ abstract public function get_output();
27
+
28
+ public function output() {
29
+ // nothing
30
+ }
31
+
32
+ public function get_collector() {
33
+ return $this->collector;
34
+ }
35
 
36
  }
37
  }
classes/Plugin.php CHANGED
@@ -17,6 +17,8 @@ GNU General Public License for more details.
17
  if ( ! class_exists( 'QM_Plugin' ) ) {
18
  abstract class QM_Plugin {
19
 
 
 
20
  /**
21
  * Class constructor
22
  *
@@ -74,15 +76,21 @@ abstract class QM_Plugin {
74
  *
75
  * @author John Blackbourn
76
  **/
77
- final protected function _plugin( $item, $file = '' ) {
78
- if ( !isset( $this->plugin ) ) {
79
- $this->plugin = array(
80
- 'url' => plugin_dir_url( $this->file ),
81
- 'path' => plugin_dir_path( $this->file ),
82
- 'base' => plugin_basename( $this->file )
83
- );
 
 
 
 
 
 
84
  }
85
- return $this->plugin[$item] . ltrim( $file, '/' );
86
  }
87
 
88
  }
17
  if ( ! class_exists( 'QM_Plugin' ) ) {
18
  abstract class QM_Plugin {
19
 
20
+ private $plugin = array();
21
+
22
  /**
23
  * Class constructor
24
  *
76
  *
77
  * @author John Blackbourn
78
  **/
79
+ final private function _plugin( $item, $file = '' ) {
80
+ if ( ! array_key_exists( $item, $this->plugin ) ) {
81
+ switch ( $item ) {
82
+ case 'url':
83
+ $this->plugin[ $item ] = plugin_dir_url( $this->file );
84
+ break;
85
+ case 'path':
86
+ $this->plugin[ $item ] = plugin_dir_path( $this->file );
87
+ break;
88
+ case 'base':
89
+ $this->plugin[ $item ] = plugin_basename( $this->file );
90
+ break;
91
+ }
92
  }
93
+ return $this->plugin[ $item ] . ltrim( $file, '/' );
94
  }
95
 
96
  }
classes/Util.php CHANGED
@@ -78,6 +78,8 @@ class QM_Util {
78
 
79
  # @TODO turn this into a class (eg QM_File_Component)
80
 
 
 
81
  if ( isset( self::$file_components[$file] ) ) {
82
  return self::$file_components[$file];
83
  }
@@ -167,7 +169,7 @@ class QM_Util {
167
 
168
  if ( is_a( $callback['function'], 'Closure' ) ) {
169
  $ref = new ReflectionFunction( $callback['function'] );
170
- $file = trim( QM_Util::standard_dir( $ref->getFileName(), '' ), '/' );
171
  $callback['name'] = sprintf( __( 'Closure on line %1$d of %2$s', 'query-monitor' ), $ref->getStartLine(), $file );
172
  } else {
173
  // the object should have a __invoke() method
@@ -186,16 +188,26 @@ class QM_Util {
186
  $callback['file'] = $ref->getFileName();
187
  $callback['line'] = $ref->getStartLine();
188
 
189
- if ( '__lambda_func' === $ref->getName() ) {
 
 
 
190
  if ( preg_match( '|(?P<file>.*)\((?P<line>[0-9]+)\)|', $callback['file'], $matches ) ) {
191
  $callback['file'] = $matches['file'];
192
  $callback['line'] = $matches['line'];
193
  $file = trim( QM_Util::standard_dir( $callback['file'], '' ), '/' );
194
  $callback['name'] = sprintf( __( 'Anonymous function on line %1$d of %2$s', 'query-monitor' ), $callback['line'], $file );
 
 
 
 
 
195
  }
196
  }
197
 
198
- $callback['component'] = self::get_file_component( $callback['file'] );
 
 
199
 
200
  } catch ( ReflectionException $e ) {
201
 
@@ -236,31 +248,13 @@ class QM_Util {
236
  return ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
237
  }
238
 
239
- public static function include_files( $path ) {
240
-
241
- if ( class_exists( 'DirectoryIterator' ) and method_exists( 'DirectoryIterator', 'getExtension' ) ) {
242
-
243
- $output_iterator = new DirectoryIterator( $path );
244
-
245
- foreach ( $output_iterator as $output ) {
246
- if ( $output->getExtension() === 'php' ) {
247
- include $output->getPathname();
248
- }
249
- }
250
-
251
- } else {
252
-
253
- foreach ( glob( sprintf( '%s/*.php', $path ) ) as $file ) {
254
- include $file;
255
- }
256
-
257
- }
258
-
259
- }
260
-
261
  public static function is_multi_network() {
262
  global $wpdb;
263
 
 
 
 
 
264
  if ( ! is_multisite() ) {
265
  return false;
266
  }
@@ -273,5 +267,19 @@ class QM_Util {
273
  return ( $num_sites > 1 );
274
  }
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  }
277
  }
78
 
79
  # @TODO turn this into a class (eg QM_File_Component)
80
 
81
+ $file = self::standard_dir( $file );
82
+
83
  if ( isset( self::$file_components[$file] ) ) {
84
  return self::$file_components[$file];
85
  }
169
 
170
  if ( is_a( $callback['function'], 'Closure' ) ) {
171
  $ref = new ReflectionFunction( $callback['function'] );
172
+ $file = QM_Util::standard_dir( $ref->getFileName(), '' );
173
  $callback['name'] = sprintf( __( 'Closure on line %1$d of %2$s', 'query-monitor' ), $ref->getStartLine(), $file );
174
  } else {
175
  // the object should have a __invoke() method
188
  $callback['file'] = $ref->getFileName();
189
  $callback['line'] = $ref->getStartLine();
190
 
191
+ // https://github.com/facebook/hhvm/issues/5856
192
+ $name = trim( $ref->getName() );
193
+
194
+ if ( '__lambda_func' === $name || 0 === strpos( $name, 'lambda_' ) ) {
195
  if ( preg_match( '|(?P<file>.*)\((?P<line>[0-9]+)\)|', $callback['file'], $matches ) ) {
196
  $callback['file'] = $matches['file'];
197
  $callback['line'] = $matches['line'];
198
  $file = trim( QM_Util::standard_dir( $callback['file'], '' ), '/' );
199
  $callback['name'] = sprintf( __( 'Anonymous function on line %1$d of %2$s', 'query-monitor' ), $callback['line'], $file );
200
+ } else {
201
+ // https://github.com/facebook/hhvm/issues/5807
202
+ unset( $callback['line'], $callback['file'] );
203
+ $callback['name'] = $name . '()';
204
+ $callback['error'] = new WP_Error( 'unknown_lambda', __( 'Unable to determine source of lambda function', 'query-monitor' ) );
205
  }
206
  }
207
 
208
+ if ( ! empty( $callback['file'] ) ) {
209
+ $callback['component'] = self::get_file_component( $callback['file'] );
210
+ }
211
 
212
  } catch ( ReflectionException $e ) {
213
 
248
  return ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
249
  }
250
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  public static function is_multi_network() {
252
  global $wpdb;
253
 
254
+ if ( function_exists( 'is_multi_network' ) ) {
255
+ return is_multi_network();
256
+ }
257
+
258
  if ( ! is_multisite() ) {
259
  return false;
260
  }
267
  return ( $num_sites > 1 );
268
  }
269
 
270
+ public static function get_client_version( $client ) {
271
+
272
+ $client = intval( $client );
273
+
274
+ $hello = $client % 10000;
275
+
276
+ $major = intval( floor( $client / 10000 ) );
277
+ $minor = intval( floor( $hello / 100 ) );
278
+ $patch = intval( $hello % 100 );
279
+
280
+ return compact( 'major', 'minor', 'patch' );
281
+
282
+ }
283
+
284
  }
285
  }
collectors/conditionals.php CHANGED
@@ -25,11 +25,45 @@ class QM_Collector_Conditionals extends QM_Collector {
25
  public function process() {
26
 
27
  $conds = apply_filters( 'qm/collect/conditionals', array(
28
- 'is_404', 'is_archive', 'is_admin', 'is_attachment', 'is_author', 'is_blog_admin', 'is_category', 'is_comments_popup', 'is_customize_preview', 'is_date',
29
- 'is_day', 'is_feed', 'is_front_page', 'is_home', 'is_main_network', 'is_main_site', 'is_month', 'is_network_admin',
30
- 'is_page', 'is_page_template', 'is_paged', 'is_post_type_archive', 'is_preview', 'is_robots', 'is_rtl', 'is_search', 'is_single',
31
- 'is_singular', 'is_ssl', 'is_sticky', 'is_tag', 'is_tax', 'is_time', 'is_trackback', 'is_year'
32
- ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  $conds = apply_filters( 'query_monitor_conditionals', $conds );
34
 
35
  $true = $false = $na = array();
25
  public function process() {
26
 
27
  $conds = apply_filters( 'qm/collect/conditionals', array(
28
+ 'is_404',
29
+ 'is_admin',
30
+ 'is_archive',
31
+ 'is_attachment',
32
+ 'is_author',
33
+ 'is_blog_admin',
34
+ 'is_category',
35
+ 'is_comment_feed',
36
+ 'is_comments_popup',
37
+ 'is_customize_preview',
38
+ 'is_date',
39
+ 'is_day',
40
+ 'is_embed',
41
+ 'is_feed',
42
+ 'is_front_page',
43
+ 'is_home',
44
+ 'is_main_network',
45
+ 'is_main_site',
46
+ 'is_month',
47
+ 'is_network_admin',
48
+ 'is_page',
49
+ 'is_page_template',
50
+ 'is_paged',
51
+ 'is_post_type_archive',
52
+ 'is_preview',
53
+ 'is_robots',
54
+ 'is_rtl',
55
+ 'is_search',
56
+ 'is_single',
57
+ 'is_singular',
58
+ 'is_ssl',
59
+ 'is_sticky',
60
+ 'is_tag',
61
+ 'is_tax',
62
+ 'is_time',
63
+ 'is_trackback',
64
+ 'is_user_admin',
65
+ 'is_year',
66
+ ) );
67
  $conds = apply_filters( 'query_monitor_conditionals', $conds );
68
 
69
  $true = $false = $na = array();
collectors/debug_bar.php CHANGED
@@ -25,7 +25,7 @@ final class QM_Collector_Debug_Bar extends QM_Collector {
25
 
26
  public function name() {
27
  $title = $this->get_panel()->title();
28
- return sprintf( 'Debug Bar: %s', $title );
29
  }
30
 
31
  public function set_panel( Debug_Bar_Panel $panel ) {
25
 
26
  public function name() {
27
  $title = $this->get_panel()->title();
28
+ return sprintf( __( 'Debug Bar: %s', 'query-monitor' ), $title );
29
  }
30
 
31
  public function set_panel( Debug_Bar_Panel $panel ) {
collectors/environment.php CHANGED
@@ -109,28 +109,50 @@ class QM_Collector_Environment extends QM_Collector {
109
  " );
110
 
111
  if ( is_resource( $db->dbh ) ) {
112
- # Standard mysql extension
113
- $driver = 'mysql';
114
  } else if ( is_object( $db->dbh ) ) {
115
  # mysqli or PDO
116
- $driver = get_class( $db->dbh );
117
  } else {
118
  # Who knows?
119
- $driver = '<span class="qm-warn">' . __( 'Unknown', 'query-monitor' ) . '</span>';
120
  }
121
 
122
  if ( method_exists( $db, 'db_version' ) ) {
123
- $version = $db->db_version();
124
  } else {
125
- $version = '<span class="qm-warn">' . __( 'Unknown', 'query-monitor' ) . '</span>';
126
  }
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  $this->data['db'][$id] = array(
129
- 'version' => $version,
130
- 'driver' => $driver,
131
- 'user' => $db->dbuser,
132
- 'host' => $db->dbhost,
133
- 'name' => $db->dbname,
134
  'vars' => $mysql_vars,
135
  'variables' => $variables
136
  );
@@ -140,8 +162,13 @@ class QM_Collector_Environment extends QM_Collector {
140
  }
141
 
142
  $this->data['php']['version'] = phpversion();
 
143
  $this->data['php']['user'] = self::get_current_user();
144
 
 
 
 
 
145
  foreach ( $this->php_vars as $setting ) {
146
  $this->data['php']['variables'][$setting]['after'] = ini_get( $setting );
147
  }
109
  " );
110
 
111
  if ( is_resource( $db->dbh ) ) {
112
+ # Old mysql extension
113
+ $extension = 'mysql';
114
  } else if ( is_object( $db->dbh ) ) {
115
  # mysqli or PDO
116
+ $extension = get_class( $db->dbh );
117
  } else {
118
  # Who knows?
119
+ $extension = null;
120
  }
121
 
122
  if ( method_exists( $db, 'db_version' ) ) {
123
+ $server = $db->db_version();
124
  } else {
125
+ $server = null;
126
  }
127
 
128
+ if ( isset( $db->use_mysqli ) && $db->use_mysqli ) {
129
+ $client = mysqli_get_client_version( $db->dbh );
130
+ } else {
131
+ if ( preg_match( '|[0-9]{1,2}\.[0-9]{1,2}\.[0-9]{1,2}|', mysql_get_client_info(), $matches ) ) {
132
+ $client = $matches[0];
133
+ } else {
134
+ $client = null;
135
+ }
136
+ }
137
+
138
+ if ( $client ) {
139
+ $client_version = implode( '.', QM_Util::get_client_version( $client ) );
140
+ $client_version = sprintf( '%s (%s)', $client, $client_version );
141
+ } else {
142
+ $client_version = null;
143
+ }
144
+
145
+ $info = array(
146
+ 'extension' => $extension,
147
+ 'server version' => $server,
148
+ 'client version' => $client_version,
149
+ 'user' => $db->dbuser,
150
+ 'host' => $db->dbhost,
151
+ 'database' => $db->dbname,
152
+ );
153
+
154
  $this->data['db'][$id] = array(
155
+ 'info' => $info,
 
 
 
 
156
  'vars' => $mysql_vars,
157
  'variables' => $variables
158
  );
162
  }
163
 
164
  $this->data['php']['version'] = phpversion();
165
+ $this->data['php']['sapi'] = php_sapi_name();
166
  $this->data['php']['user'] = self::get_current_user();
167
 
168
+ if ( defined( 'HHVM_VERSION' ) ) {
169
+ $this->data['php']['hhvm'] = HHVM_VERSION;
170
+ }
171
+
172
  foreach ( $this->php_vars as $setting ) {
173
  $this->data['php']['variables'][$setting]['after'] = ini_get( $setting );
174
  }
collectors/hooks.php CHANGED
@@ -37,10 +37,7 @@ class QM_Collector_Hooks extends QM_Collector {
37
  $hooks = $all_parts = $components = array();
38
 
39
  if ( has_filter( 'all' ) ) {
40
-
41
  $hooks['all'] = $this->process_action( 'all', $wp_filter );
42
- $this->data['warnings']['all_hooked'] = $hooks['all'];
43
-
44
  }
45
 
46
  foreach ( $wp_actions as $name => $count ) {
37
  $hooks = $all_parts = $components = array();
38
 
39
  if ( has_filter( 'all' ) ) {
 
40
  $hooks['all'] = $this->process_action( 'all', $wp_filter );
 
 
41
  }
42
 
43
  foreach ( $wp_actions as $name => $count ) {
collectors/languages.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Collector_Languages extends QM_Collector {
18
+
19
+ public $id = 'languages';
20
+
21
+ public function name() {
22
+ return __( 'Languages', 'query-monitor' );
23
+ }
24
+
25
+ public function __construct() {
26
+
27
+ parent::__construct();
28
+
29
+ add_filter( 'override_load_textdomain', array( $this, 'log_file_load' ), 99, 3 );
30
+
31
+ }
32
+
33
+
34
+ /**
35
+ * Store log data.
36
+ *
37
+ * @param bool $override Whether to override the text domain. Default false.
38
+ * @param string $domain Text domain. Unique identifier for retrieving translated strings.
39
+ * @param string $mofile Path to the MO file.
40
+ * @return bool
41
+ */
42
+ public function log_file_load( $override, $domain, $mofile ) {
43
+
44
+ $trace = new QM_Backtrace;
45
+ $filtered = $trace->get_filtered_trace();
46
+ $caller = array();
47
+
48
+ foreach ( $filtered as $i => $item ) {
49
+
50
+ if ( in_array( $item['function'], array(
51
+ 'load_plugin_textdomain',
52
+ 'load_theme_textdomain',
53
+ 'load_default_textdomain',
54
+ ), true ) ) {
55
+ $caller = $item;
56
+ $display = $i + 1;
57
+ if ( isset( $filtered[ $display ] ) ) {
58
+ $caller['display'] = $filtered[ $display ]['display'];
59
+ }
60
+ break;
61
+ }
62
+
63
+ }
64
+
65
+ if ( empty( $caller ) ) {
66
+ $caller = $filtered[1];
67
+ }
68
+
69
+ $this->data['languages'][] = array(
70
+ 'caller' => $caller,
71
+ 'domain' => $domain,
72
+ 'mofile' => $mofile,
73
+ 'found' => file_exists( $mofile ) ? filesize( $mofile ): false,
74
+ );
75
+
76
+ return $override;
77
+
78
+ }
79
+
80
+
81
+ }
82
+
83
+ function register_qm_collector_languages( array $collectors, QueryMonitor $qm ) {
84
+ $collectors['languages'] = new QM_Collector_Languages;
85
+ return $collectors;
86
+ }
87
+
88
+ add_filter( 'qm/collectors', 'register_qm_collector_languages', 21, 2 );
collectors/request.php CHANGED
@@ -29,11 +29,17 @@ class QM_Collector_Request extends QM_Collector {
29
  $qo = get_queried_object();
30
 
31
  if ( is_multisite() ) {
32
- $this->data['multisite']['current_blog'] = $current_blog;
 
 
 
33
  }
34
 
35
  if ( QM_Util::is_multi_network() ) {
36
- $this->data['multisite']['current_site'] = $current_site;
 
 
 
37
  }
38
 
39
  if ( is_admin() ) {
@@ -88,8 +94,7 @@ class QM_Collector_Request extends QM_Collector {
88
 
89
  case is_a( $qo, 'WP_Post' ):
90
  // Single post
91
- $this->data['queried_object_type'] = 'post';
92
- $this->data['queried_object_title'] = sprintf( __( 'Single %s: #%d', 'query-monitor' ),
93
  get_post_type_object( $qo->post_type )->labels->singular_name,
94
  $qo->ID
95
  );
@@ -97,37 +102,36 @@ class QM_Collector_Request extends QM_Collector {
97
 
98
  case is_a( $qo, 'WP_User' ):
99
  // Author archive
100
- $this->data['queried_object_type'] = 'user';
101
- $this->data['queried_object_title'] = sprintf( __( 'Author archive: %s', 'query-monitor' ),
102
  $qo->user_nicename
103
  );
104
  break;
105
 
 
106
  case property_exists( $qo, 'term_id' ):
107
  // Term archive
108
- $this->data['queried_object_type'] = 'term';
109
- $this->data['queried_object_title'] = sprintf( __( 'Term archive: %s', 'query-monitor' ),
110
  $qo->slug
111
  );
112
  break;
113
 
114
  case property_exists( $qo, 'has_archive' ):
115
  // Post type archive
116
- $this->data['queried_object_type'] = 'archive';
117
- $this->data['queried_object_title'] = sprintf( __( 'Post type archive: %s', 'query-monitor' ),
118
  $qo->name
119
  );
120
  break;
121
 
122
  default:
123
  // Unknown, but we have a queried object
124
- $this->data['queried_object_type'] = 'unknown';
125
- $this->data['queried_object_title'] = __( 'Unknown queried object', 'query-monitor' );
126
  break;
127
 
128
  }
129
 
130
- $this->data['queried_object'] = $qo;
 
 
131
 
132
  }
133
 
29
  $qo = get_queried_object();
30
 
31
  if ( is_multisite() ) {
32
+ $this->data['multisite']['current_blog'] = array(
33
+ 'title' => sprintf( 'Current blog: #%d', $current_blog->blog_id ),
34
+ 'data' => $current_blog,
35
+ );
36
  }
37
 
38
  if ( QM_Util::is_multi_network() ) {
39
+ $this->data['multisite']['current_site'] = array(
40
+ 'title' => sprintf( 'Current site: #%d', $current_site->id ),
41
+ 'data' => $current_site,
42
+ );
43
  }
44
 
45
  if ( is_admin() ) {
94
 
95
  case is_a( $qo, 'WP_Post' ):
96
  // Single post
97
+ $this->data['queried_object']['title'] = sprintf( __( 'Single %s: #%d', 'query-monitor' ),
 
98
  get_post_type_object( $qo->post_type )->labels->singular_name,
99
  $qo->ID
100
  );
102
 
103
  case is_a( $qo, 'WP_User' ):
104
  // Author archive
105
+ $this->data['queried_object']['title'] = sprintf( __( 'Author archive: %s', 'query-monitor' ),
 
106
  $qo->user_nicename
107
  );
108
  break;
109
 
110
+ case is_a( $qo, 'WP_Term' ):
111
  case property_exists( $qo, 'term_id' ):
112
  // Term archive
113
+ $this->data['queried_object']['title'] = sprintf( __( 'Term archive: %s', 'query-monitor' ),
 
114
  $qo->slug
115
  );
116
  break;
117
 
118
  case property_exists( $qo, 'has_archive' ):
119
  // Post type archive
120
+ $this->data['queried_object']['title'] = sprintf( __( 'Post type archive: %s', 'query-monitor' ),
 
121
  $qo->name
122
  );
123
  break;
124
 
125
  default:
126
  // Unknown, but we have a queried object
127
+ $this->data['queried_object']['title'] = __( 'Unknown queried object', 'query-monitor' );
 
128
  break;
129
 
130
  }
131
 
132
+ if ( ! is_null( $qo ) ) {
133
+ $this->data['queried_object']['data'] = $qo;
134
+ }
135
 
136
  }
137
 
collectors/theme.php CHANGED
@@ -24,7 +24,8 @@ class QM_Collector_Theme extends QM_Collector {
24
 
25
  public function __construct() {
26
  parent::__construct();
27
- add_filter( 'body_class', array( $this, 'filter_body_class' ), 99 );
 
28
  }
29
 
30
  public function filter_body_class( $class ) {
@@ -32,25 +33,33 @@ class QM_Collector_Theme extends QM_Collector {
32
  return $class;
33
  }
34
 
 
 
 
 
 
35
  public function process() {
36
 
37
- global $template;
 
 
 
 
 
38
 
39
- $template_path = QM_Util::standard_dir( $template );
40
- $stylesheet_directory = QM_Util::standard_dir( get_stylesheet_directory() );
41
- $template_directory = QM_Util::standard_dir( get_template_directory() );
42
- $theme_directory = QM_Util::standard_dir( get_theme_root() );
43
 
44
- $template_file = str_replace( array( $stylesheet_directory, $template_directory ), '', $template_path );
45
- $template_file = ltrim( $template_file, '/' );
46
- $theme_template = str_replace( $theme_directory, '', $template_path );
47
- $theme_template = ltrim( $theme_template, '/' );
 
48
 
49
- $this->data['template_path'] = $template_path;
50
- $this->data['template_file'] = $template_file;
51
- $this->data['theme_template'] = $theme_template;
52
- $this->data['stylesheet'] = get_stylesheet();
53
- $this->data['template'] = get_template();
54
 
55
  if ( isset( $this->data['body_class'] ) ) {
56
  asort( $this->data['body_class'] );
24
 
25
  public function __construct() {
26
  parent::__construct();
27
+ add_filter( 'body_class', array( $this, 'filter_body_class' ), 999 );
28
+ add_filter( 'template_include', array( $this, 'filter_template_include' ), 999 );
29
  }
30
 
31
  public function filter_body_class( $class ) {
33
  return $class;
34
  }
35
 
36
+ public function filter_template_include( $template_path ) {
37
+ $this->data['template_path'] = $template_path;
38
+ return $template_path;
39
+ }
40
+
41
  public function process() {
42
 
43
+ if ( ! empty( $this->data['template_path'] ) ) {
44
+
45
+ $template_path = QM_Util::standard_dir( $this->data['template_path'] );
46
+ $stylesheet_directory = QM_Util::standard_dir( get_stylesheet_directory() );
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 = str_replace( $theme_directory, '', $template_path );
53
+ $theme_template = ltrim( $theme_template, '/' );
54
 
55
+ $this->data['template_path'] = $template_path;
56
+ $this->data['template_file'] = $template_file;
57
+ $this->data['theme_template'] = $theme_template;
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'] );
composer.json CHANGED
@@ -10,7 +10,18 @@
10
  "homepage": "https://johnblackbourn.com/"
11
  }
12
  ],
 
 
 
 
 
13
  "require": {
14
  "composer/installers": "~1.0"
15
- }
 
 
 
 
 
 
16
  }
10
  "homepage": "https://johnblackbourn.com/"
11
  }
12
  ],
13
+ "support": {
14
+ "issues": "https://github.com/johnbillion/query-monitor/issues",
15
+ "forum": "https://wordpress.org/support/plugin/query-monitor",
16
+ "source": "https://github.com/johnbillion/query-monitor"
17
+ },
18
  "require": {
19
  "composer/installers": "~1.0"
20
+ },
21
+ "require-dev" : {
22
+ "phpunit/phpunit": ">=3.7@stable"
23
+ },
24
+ "config": {
25
+ "bin-dir": "bin/"
26
+ }
27
  }
dispatchers/{Headers.php → AJAX.php} RENAMED
@@ -14,12 +14,15 @@ GNU General Public License for more details.
14
 
15
  */
16
 
17
- class QM_Dispatcher_Headers extends QM_Dispatcher {
18
 
19
- public $id = 'headers';
20
 
21
  public function __construct( QM_Plugin $qm ) {
22
  parent::__construct( $qm );
 
 
 
23
  }
24
 
25
  public function init() {
@@ -34,18 +37,36 @@ class QM_Dispatcher_Headers extends QM_Dispatcher {
34
 
35
  }
36
 
37
- public function before_output() {
38
 
39
- require_once $this->qm->plugin_path( 'output/Headers.php' );
 
 
40
 
41
- QM_Util::include_files( $this->qm->plugin_path( 'output/headers' ) );
 
 
 
 
 
 
 
42
 
43
  }
44
 
45
- public function after_output() {
 
 
 
 
 
 
 
 
 
46
 
47
  # flush once, because we're nice
48
- if ( QM_Util::is_ajax() and ob_get_length() ) {
49
  ob_flush();
50
  }
51
 
@@ -53,6 +74,10 @@ class QM_Dispatcher_Headers extends QM_Dispatcher {
53
 
54
  public function is_active() {
55
 
 
 
 
 
56
  if ( ! $this->user_can_view() ) {
57
  return false;
58
  }
@@ -62,15 +87,26 @@ class QM_Dispatcher_Headers extends QM_Dispatcher {
62
  return false;
63
  }
64
 
 
 
 
 
 
 
 
 
 
 
 
65
  return true;
66
 
67
  }
68
 
69
  }
70
 
71
- function register_qm_dispatcher_headers( array $dispatchers, QM_Plugin $qm ) {
72
- $dispatchers['headers'] = new QM_Dispatcher_Headers( $qm );
73
  return $dispatchers;
74
  }
75
 
76
- add_filter( 'qm/dispatchers', 'register_qm_dispatcher_headers', 10, 2 );
14
 
15
  */
16
 
17
+ class QM_Dispatcher_AJAX extends QM_Dispatcher {
18
 
19
+ public $id = 'ajax';
20
 
21
  public function __construct( QM_Plugin $qm ) {
22
  parent::__construct( $qm );
23
+
24
+ add_action( 'shutdown', array( $this, 'dispatch' ), 0 );
25
+
26
  }
27
 
28
  public function init() {
37
 
38
  }
39
 
40
+ public function dispatch() {
41
 
42
+ if ( ! $this->should_dispatch() ) {
43
+ return;
44
+ }
45
 
46
+ $this->before_output();
47
+
48
+ /* @var QM_Output_Headers[] */
49
+ foreach ( $this->get_outputters( 'headers' ) as $id => $output ) {
50
+ $output->output();
51
+ }
52
+
53
+ $this->after_output();
54
 
55
  }
56
 
57
+ protected function before_output() {
58
+
59
+ require_once $this->qm->plugin_path( 'output/Headers.php' );
60
+
61
+ foreach ( glob( $this->qm->plugin_path( 'output/headers/*.php' ) ) as $file ) {
62
+ require_once $file;
63
+ }
64
+ }
65
+
66
+ protected function after_output() {
67
 
68
  # flush once, because we're nice
69
+ if ( ob_get_length() ) {
70
  ob_flush();
71
  }
72
 
74
 
75
  public function is_active() {
76
 
77
+ if ( ! QM_Util::is_ajax() ) {
78
+ return false;
79
+ }
80
+
81
  if ( ! $this->user_can_view() ) {
82
  return false;
83
  }
87
  return false;
88
  }
89
 
90
+ # Don't process if the minimum required actions haven't fired:
91
+ if ( is_admin() ) {
92
+ if ( ! did_action( 'admin_init' ) ) {
93
+ return false;
94
+ }
95
+ } else {
96
+ if ( ! did_action( 'wp' ) ) {
97
+ return false;
98
+ }
99
+ }
100
+
101
  return true;
102
 
103
  }
104
 
105
  }
106
 
107
+ function register_qm_dispatcher_ajax( array $dispatchers, QM_Plugin $qm ) {
108
+ $dispatchers['ajax'] = new QM_Dispatcher_AJAX( $qm );
109
  return $dispatchers;
110
  }
111
 
112
+ add_filter( 'qm/dispatchers', 'register_qm_dispatcher_ajax', 10, 2 );
dispatchers/Html.php CHANGED
@@ -17,6 +17,7 @@ GNU General Public License for more details.
17
  class QM_Dispatcher_Html extends QM_Dispatcher {
18
 
19
  public $id = 'html';
 
20
 
21
  public function __construct( QM_Plugin $qm ) {
22
 
@@ -25,10 +26,20 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
25
  add_action( 'wp_ajax_qm_auth_off', array( $this, 'ajax_off' ) );
26
  add_action( 'wp_ajax_nopriv_qm_auth_off', array( $this, 'ajax_off' ) );
27
 
 
 
 
 
 
 
28
  parent::__construct( $qm );
29
 
30
  }
31
 
 
 
 
 
32
  /**
33
  * Helper function. Should the authentication cookie be secure?
34
  *
@@ -44,7 +55,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
44
  wp_send_json_error( __( 'Could not set authentication cookie.', 'query-monitor' ) );
45
  }
46
 
47
- $expiration = time() + 172800; # 48 hours
48
  $secure = self::secure_cookie();
49
  $cookie = wp_generate_auth_cookie( get_current_user_id(), $expiration, 'logged_in' );
50
 
@@ -58,7 +69,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
58
 
59
  public function ajax_off() {
60
 
61
- if ( ! $this->user_verified() or ! check_ajax_referer( 'qm-auth-off', 'nonce', false ) ) {
62
  wp_send_json_error( __( 'Could not clear authentication cookie.', 'query-monitor' ) );
63
  }
64
 
@@ -78,23 +89,22 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
78
  return;
79
  }
80
 
81
- $class = implode( ' ', array( 'hide-if-js' ) );
82
  $title = __( 'Query Monitor', 'query-monitor' );
83
 
84
  $wp_admin_bar->add_menu( array(
85
  'id' => 'query-monitor',
86
- 'title' => $title,
87
  'href' => '#qm-overview',
88
  'meta' => array(
89
- 'classname' => $class
90
- )
91
  ) );
92
 
93
  $wp_admin_bar->add_menu( array(
94
  'parent' => 'query-monitor',
95
  'id' => 'query-monitor-placeholder',
96
- 'title' => $title,
97
- 'href' => '#qm-overview'
98
  ) );
99
 
100
  }
@@ -162,11 +172,30 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
162
 
163
  }
164
 
165
- public function before_output() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
  require_once $this->qm->plugin_path( 'output/Html.php' );
168
 
169
- QM_Util::include_files( $this->qm->plugin_path( 'output/html' ) );
 
 
170
 
171
  $class = array(
172
  'qm-no-js',
@@ -183,13 +212,13 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
183
  $class[] = 'qm-show';
184
  }
185
 
186
- echo '<div id="qm" class="' . implode( ' ', $class ) . '">';
187
  echo '<div id="qm-wrapper">';
188
- echo '<p>' . __( 'Query Monitor', 'query-monitor' ) . '</p>';
189
 
190
  }
191
 
192
- public function after_output() {
193
 
194
  echo '<div class="qm qm-half" id="qm-authentication">';
195
  echo '<table cellspacing="0">';
@@ -200,22 +229,22 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
200
  echo '</thead>';
201
  echo '<tbody>';
202
 
203
- if ( !$this->user_verified() ) {
204
 
205
  echo '<tr>';
206
- echo '<td>' . __( 'You can set an authentication cookie which allows you to view Query Monitor output when you&rsquo;re not logged in.', 'query-monitor' ) . '</td>';
207
  echo '</tr>';
208
  echo '<tr>';
209
- echo '<td><a href="#" class="qm-auth" data-action="on">' . __( 'Set authentication cookie', 'query-monitor' ) . '</a></td>';
210
  echo '</tr>';
211
 
212
  } else {
213
 
214
  echo '<tr>';
215
- echo '<td>' . __( 'You currently have an authentication cookie which allows you to view Query Monitor output.', 'query-monitor' ) . '</td>';
216
  echo '</tr>';
217
  echo '<tr>';
218
- echo '<td><a href="#" class="qm-auth" data-action="off">' . __( 'Clear authentication cookie', 'query-monitor' ) . '</a></td>';
219
  echo '</tr>';
220
 
221
  }
@@ -249,7 +278,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
249
  $title = implode( '&nbsp;&nbsp;&nbsp;', apply_filters( 'qm/output/title', array() ) );
250
 
251
  if ( empty( $title ) ) {
252
- $title = __( 'Query Monitor', 'query-monitor' );
253
  }
254
 
255
  $admin_bar_menu = array(
@@ -261,7 +290,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
261
  );
262
 
263
  foreach ( apply_filters( 'qm/output/menus', array() ) as $menu ) {
264
- $admin_bar_menu['sub'][] = $menu;
265
  }
266
 
267
  return $admin_bar_menu;
@@ -274,7 +303,7 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
274
  return false;
275
  }
276
 
277
- if ( ! ( did_action( 'wp_footer' ) or did_action( 'admin_footer' ) or did_action( 'login_footer' ) ) ) {
278
  return false;
279
  }
280
 
@@ -282,6 +311,22 @@ class QM_Dispatcher_Html extends QM_Dispatcher {
282
  return false;
283
  }
284
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
  return true;
286
 
287
  }
17
  class QM_Dispatcher_Html extends QM_Dispatcher {
18
 
19
  public $id = 'html';
20
+ public $did_footer = false;
21
 
22
  public function __construct( QM_Plugin $qm ) {
23
 
26
  add_action( 'wp_ajax_qm_auth_off', array( $this, 'ajax_off' ) );
27
  add_action( 'wp_ajax_nopriv_qm_auth_off', array( $this, 'ajax_off' ) );
28
 
29
+ add_action( 'shutdown', array( $this, 'dispatch' ), 0 );
30
+
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
 
37
  }
38
 
39
+ public function action_footer() {
40
+ $this->did_footer = true;
41
+ }
42
+
43
  /**
44
  * Helper function. Should the authentication cookie be secure?
45
  *
55
  wp_send_json_error( __( 'Could not set authentication cookie.', 'query-monitor' ) );
56
  }
57
 
58
+ $expiration = time() + ( 2 * DAY_IN_SECONDS );
59
  $secure = self::secure_cookie();
60
  $cookie = wp_generate_auth_cookie( get_current_user_id(), $expiration, 'logged_in' );
61
 
69
 
70
  public function ajax_off() {
71
 
72
+ if ( ! self::user_verified() or ! check_ajax_referer( 'qm-auth-off', 'nonce', false ) ) {
73
  wp_send_json_error( __( 'Could not clear authentication cookie.', 'query-monitor' ) );
74
  }
75
 
89
  return;
90
  }
91
 
 
92
  $title = __( 'Query Monitor', 'query-monitor' );
93
 
94
  $wp_admin_bar->add_menu( array(
95
  'id' => 'query-monitor',
96
+ 'title' => esc_html( $title ),
97
  'href' => '#qm-overview',
98
  'meta' => array(
99
+ 'classname' => 'hide-if-js',
100
+ ),
101
  ) );
102
 
103
  $wp_admin_bar->add_menu( array(
104
  'parent' => 'query-monitor',
105
  'id' => 'query-monitor-placeholder',
106
+ 'title' => esc_html( $title ),
107
+ 'href' => '#qm-overview',
108
  ) );
109
 
110
  }
172
 
173
  }
174
 
175
+ public function dispatch() {
176
+
177
+ if ( ! $this->should_dispatch() ) {
178
+ return;
179
+ }
180
+
181
+ $this->before_output();
182
+
183
+ /* @var QM_Output_Html[] */
184
+ foreach ( $this->get_outputters( 'html' ) as $id => $output ) {
185
+ $output->output();
186
+ }
187
+
188
+ $this->after_output();
189
+
190
+ }
191
+
192
+ protected function before_output() {
193
 
194
  require_once $this->qm->plugin_path( 'output/Html.php' );
195
 
196
+ foreach ( glob( $this->qm->plugin_path( 'output/html/*.php' ) ) as $file ) {
197
+ require_once $file;
198
+ }
199
 
200
  $class = array(
201
  'qm-no-js',
212
  $class[] = 'qm-show';
213
  }
214
 
215
+ echo '<div id="qm" class="' . implode( ' ', array_map( 'esc_attr', $class ) ) . '">';
216
  echo '<div id="qm-wrapper">';
217
+ echo '<p>' . esc_html__( 'Query Monitor', 'query-monitor' ) . '</p>';
218
 
219
  }
220
 
221
+ protected function after_output() {
222
 
223
  echo '<div class="qm qm-half" id="qm-authentication">';
224
  echo '<table cellspacing="0">';
229
  echo '</thead>';
230
  echo '<tbody>';
231
 
232
+ if ( ! self::user_verified() ) {
233
 
234
  echo '<tr>';
235
+ echo '<td>' . esc_html__( 'You can set an authentication cookie which allows you to view Query Monitor output when you&rsquo;re not logged in.', 'query-monitor' ) . '</td>';
236
  echo '</tr>';
237
  echo '<tr>';
238
+ echo '<td><a href="#" class="qm-auth" data-action="on">' . esc_html__( 'Set authentication cookie', 'query-monitor' ) . '</a></td>';
239
  echo '</tr>';
240
 
241
  } else {
242
 
243
  echo '<tr>';
244
+ echo '<td>' . esc_html__( 'You currently have an authentication cookie which allows you to view Query Monitor output.', 'query-monitor' ) . '</td>';
245
  echo '</tr>';
246
  echo '<tr>';
247
+ echo '<td><a href="#" class="qm-auth" data-action="off">' . esc_html__( 'Clear authentication cookie', 'query-monitor' ) . '</a></td>';
248
  echo '</tr>';
249
 
250
  }
278
  $title = implode( '&nbsp;&nbsp;&nbsp;', apply_filters( 'qm/output/title', array() ) );
279
 
280
  if ( empty( $title ) ) {
281
+ $title = esc_html__( 'Query Monitor', 'query-monitor' );
282
  }
283
 
284
  $admin_bar_menu = array(
290
  );
291
 
292
  foreach ( apply_filters( 'qm/output/menus', array() ) as $menu ) {
293
+ $admin_bar_menu['sub'][ $menu['id'] ] = $menu;
294
  }
295
 
296
  return $admin_bar_menu;
303
  return false;
304
  }
305
 
306
+ if ( ! $this->did_footer ) {
307
  return false;
308
  }
309
 
311
  return false;
312
  }
313
 
314
+ # Don't process if the minimum required actions haven't fired:
315
+ if ( is_admin() ) {
316
+ if ( ! did_action( 'admin_init' ) ) {
317
+ return false;
318
+ }
319
+ } else {
320
+ if ( ! ( did_action( 'wp' ) || did_action( 'login_init' ) ) ) {
321
+ return false;
322
+ }
323
+ }
324
+
325
+ # Back-compat filter. Please use `qm/dispatch/html` instead
326
+ if ( ! apply_filters( 'qm/process', true, is_admin_bar_showing() ) ) {
327
+ return false;
328
+ }
329
+
330
  return true;
331
 
332
  }
dispatchers/REST.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Dispatcher_REST extends QM_Dispatcher {
18
+
19
+ public $id = 'rest';
20
+
21
+ public function __construct( QM_Plugin $qm ) {
22
+ parent::__construct( $qm );
23
+
24
+ add_filter( 'rest_post_dispatch', array( $this, 'filter_rest_post_dispatch' ), 1, 3 );
25
+
26
+ }
27
+
28
+ /**
29
+ *
30
+ * @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response.
31
+ * @param WP_REST_Server $server Server instance.
32
+ * @param WP_REST_Request $request Request used to generate the response.
33
+ * @return WP_HTTP_Response Result to send to the client.
34
+ */
35
+ public function filter_rest_post_dispatch( WP_HTTP_Response $result, WP_REST_Server $server, WP_REST_Request $request ) {
36
+
37
+ if ( ! $this->should_dispatch() ) {
38
+ return $result;
39
+ }
40
+
41
+ $this->before_output();
42
+
43
+ /* @var QM_Output_Headers[] */
44
+ foreach ( $this->get_outputters( 'headers' ) as $id => $output ) {
45
+ $output->output();
46
+ }
47
+
48
+ $this->after_output();
49
+
50
+ return $result;
51
+
52
+ }
53
+
54
+ public function before_output() {
55
+
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
+
63
+ public function is_active() {
64
+
65
+ # If the headers have already been sent then we can't do anything about it
66
+ if ( headers_sent() ) {
67
+ return false;
68
+ }
69
+
70
+ if ( ! $this->user_can_view() ) {
71
+ return false;
72
+ }
73
+
74
+ return true;
75
+
76
+ }
77
+
78
+ }
79
+
80
+ function register_qm_dispatcher_rest( array $dispatchers, QM_Plugin $qm ) {
81
+ $dispatchers['rest'] = new QM_Dispatcher_REST( $qm );
82
+ return $dispatchers;
83
+ }
84
+
85
+ add_filter( 'qm/dispatchers', 'register_qm_dispatcher_rest', 10, 2 );
dispatchers/Redirect.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Dispatcher_Redirect extends QM_Dispatcher {
18
+
19
+ public $id = 'redirect';
20
+
21
+ public function __construct( QM_Plugin $qm ) {
22
+ parent::__construct( $qm );
23
+
24
+ add_filter( 'wp_redirect', array( $this, 'filter_wp_redirect' ), 999, 2 );
25
+
26
+ }
27
+
28
+ /**
29
+ *
30
+ * @param string $location The path to redirect to.
31
+ * @param int $status Status code to use.
32
+ */
33
+ public function filter_wp_redirect( $location, $status ) {
34
+
35
+ if ( ! $this->should_dispatch() ) {
36
+ return $location;
37
+ }
38
+
39
+ $this->before_output();
40
+
41
+ /* @var QM_Output_Headers[] */
42
+ foreach ( $this->get_outputters( 'headers' ) as $id => $output ) {
43
+ $output->output();
44
+ }
45
+
46
+ $this->after_output();
47
+
48
+ return $location;
49
+
50
+ }
51
+
52
+ protected function before_output() {
53
+
54
+ require_once $this->qm->plugin_path( 'output/Headers.php' );
55
+
56
+ foreach ( glob( $this->qm->plugin_path( 'output/headers/*.php' ) ) as $file ) {
57
+ require_once $file;
58
+ }
59
+ }
60
+
61
+ public function is_active() {
62
+
63
+ if ( ! $this->user_can_view() ) {
64
+ return false;
65
+ }
66
+
67
+ # If the headers have already been sent then we can't do anything about it
68
+ if ( headers_sent() ) {
69
+ return false;
70
+ }
71
+
72
+ # Don't process if the minimum required actions haven't fired:
73
+ if ( is_admin() ) {
74
+ if ( ! did_action( 'admin_init' ) ) {
75
+ return false;
76
+ }
77
+ } else {
78
+ if ( ! ( did_action( 'wp' ) || did_action( 'login_init' ) ) ) {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ return true;
84
+
85
+ }
86
+
87
+ }
88
+
89
+ function register_qm_dispatcher_redirect( array $dispatchers, QM_Plugin $qm ) {
90
+ $dispatchers['redirect'] = new QM_Dispatcher_Redirect( $qm );
91
+ return $dispatchers;
92
+ }
93
+
94
+ add_filter( 'qm/dispatchers', 'register_qm_dispatcher_redirect', 10, 2 );
output/Headers.php CHANGED
@@ -14,10 +14,20 @@ GNU General Public License for more details.
14
 
15
  */
16
 
17
- abstract class QM_Output_Headers implements QM_Output {
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- public function __construct( QM_Collector $collector ) {
20
- $this->collector = $collector;
21
  }
22
 
23
  }
14
 
15
  */
16
 
17
+ abstract class QM_Output_Headers extends QM_Output {
18
+
19
+ public function output() {
20
+
21
+ $id = $this->collector->id;
22
+
23
+ foreach ( $this->get_output() as $key => $value ) {
24
+ if ( is_scalar( $value ) ) {
25
+ header( sprintf( 'X-QM-%s-%s: %s', $id, $key, $value ) );
26
+ } else {
27
+ header( sprintf( 'X-QM-%s-%s: %s', $id, $key, json_encode( $value ) ) );
28
+ }
29
+ }
30
 
 
 
31
  }
32
 
33
  }
output/Html.php CHANGED
@@ -14,46 +14,50 @@ GNU General Public License for more details.
14
 
15
  */
16
 
17
- abstract class QM_Output_Html implements QM_Output {
18
 
19
  protected static $file_link_format = null;
20
 
21
- public function __construct( QM_Collector $collector ) {
22
- $this->collector = $collector;
23
- }
24
-
25
  public function admin_menu( array $menu ) {
26
 
27
  $menu[] = $this->menu( array(
28
- 'title' => $this->collector->name(),
29
  ) );
30
  return $menu;
31
 
32
  }
33
 
 
 
 
 
 
 
 
 
34
  public static function output_inner( $vars ) {
35
 
36
  echo '<table cellspacing="0" class="qm-inner">';
37
 
38
  foreach ( $vars as $key => $value ) {
39
  echo '<tr>';
40
- echo '<td valign="top">' . esc_html( $key ) . '</td>';
41
  if ( is_array( $value ) ) {
42
- echo '<td valign="top" class="qm-has-inner">';
43
  self::output_inner( $value );
44
  echo '</td>';
45
  } else if ( is_object( $value ) ) {
46
- echo '<td valign="top" class="qm-has-inner">';
47
  self::output_inner( get_object_vars( $value ) );
48
  echo '</td>';
49
  } else if ( is_bool( $value ) ) {
50
  if ( $value ) {
51
- echo '<td valign="top" class="qm-true">true</td>';
52
  } else {
53
- echo '<td valign="top" class="qm-false">false</td>';
54
  }
55
  } else {
56
- echo '<td valign="top">';
57
  echo nl2br( esc_html( $value ) );
58
  echo '</td>';
59
  }
@@ -64,6 +68,14 @@ abstract class QM_Output_Html implements QM_Output {
64
 
65
  }
66
 
 
 
 
 
 
 
 
 
67
  protected function build_filter( $name, array $values, $highlight = '' ) {
68
 
69
  if ( empty( $values ) ) {
@@ -73,7 +85,7 @@ abstract class QM_Output_Html implements QM_Output {
73
  usort( $values, 'strcasecmp' );
74
 
75
  $out = '<select id="qm-filter-' . esc_attr( $this->collector->id . '-' . $name ) . '" class="qm-filter" data-filter="' . esc_attr( $name ) . '" data-highlight="' . esc_attr( $highlight ) . '">';
76
- $out .= '<option value="">' . _x( 'All', '"All" option for filters', 'query-monitor' ) . '</option>';
77
 
78
  foreach ( $values as $value ) {
79
  $out .= '<option value="' . esc_attr( $value ) . '">' . esc_html( $value ) . '</option>';
@@ -85,6 +97,11 @@ abstract class QM_Output_Html implements QM_Output {
85
 
86
  }
87
 
 
 
 
 
 
88
  protected function build_sorter() {
89
  $out = '<span class="qm-sort-controls">';
90
  $out .= '<a href="#" class="qm-sort qm-sort-asc">&#9650;</a>';
@@ -96,12 +113,18 @@ abstract class QM_Output_Html implements QM_Output {
96
  protected function menu( array $args ) {
97
 
98
  return array_merge( array(
99
- 'id' => "query-monitor-{$this->collector->id}",
100
- 'href' => '#' . $this->collector->id()
101
  ), $args );
102
 
103
  }
104
 
 
 
 
 
 
 
105
  public static function format_sql( $sql ) {
106
 
107
  $sql = str_replace( array( "\r\n", "\r", "\n", "\t" ), ' ', $sql );
@@ -113,6 +136,7 @@ abstract class QM_Output_Html implements QM_Output {
113
  'HAVING', 'INNER', 'INSERT', 'LEFT', 'LIMIT', 'ON', 'OR', 'ORDER', 'OUTER', 'REPLACE', 'RIGHT', 'ROLLBACK', 'SELECT', 'SET',
114
  'SHOW', 'START', 'THEN', 'TRUNCATE', 'UPDATE', 'VALUES', 'WHEN', 'WHERE'
115
  ) as $cmd ) {
 
116
  $sql = trim( str_replace( " $cmd ", "<br>$cmd ", $sql ) );
117
  }
118
 
@@ -123,27 +147,41 @@ abstract class QM_Output_Html implements QM_Output {
123
 
124
  }
125
 
 
 
 
 
 
 
126
  public static function format_url( $url ) {
127
- $url = str_replace( array(
128
- '=',
129
- '&amp;',
130
- '?',
131
- ), array(
132
- '<span class="qm-equals">=</span>',
133
- '<br><span class="qm-param">&amp;</span>',
134
- '<br><span class="qm-param">?</span>',
135
- ), esc_html( $url ) );
136
- return $url;
137
-
138
  }
139
 
140
- public static function output_filename( $text, $file, $line = 1 ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
  # Further reading:
143
  # http://simonwheatley.co.uk/2012/07/clickable-stack-traces/
144
  # https://github.com/grych/subl-handler
145
 
146
- if ( !isset( self::$file_link_format ) ) {
 
 
147
  $format = ini_get( 'xdebug.file_link_format' );
148
  $format = apply_filters( 'qm/output/file_link_format', $format );
149
  if ( empty( $format ) ) {
@@ -154,11 +192,19 @@ abstract class QM_Output_Html implements QM_Output {
154
  }
155
 
156
  if ( false === self::$file_link_format ) {
157
- return $text;
 
 
 
 
 
 
 
 
158
  }
159
 
160
- $link = sprintf( self::$file_link_format, urlencode( $file ), $line );
161
- return sprintf( '<a href="%s">%s</a>', $link, $text );
162
 
163
  }
164
 
14
 
15
  */
16
 
17
+ abstract class QM_Output_Html extends QM_Output {
18
 
19
  protected static $file_link_format = null;
20
 
 
 
 
 
21
  public function admin_menu( array $menu ) {
22
 
23
  $menu[] = $this->menu( array(
24
+ 'title' => esc_html( $this->collector->name() ),
25
  ) );
26
  return $menu;
27
 
28
  }
29
 
30
+ public function get_output() {
31
+ ob_start();
32
+ // compat until I convert all the existing outputters to use `get_output()`
33
+ $this->output();
34
+ $out = ob_get_clean();
35
+ return $out;
36
+ }
37
+
38
  public static function output_inner( $vars ) {
39
 
40
  echo '<table cellspacing="0" class="qm-inner">';
41
 
42
  foreach ( $vars as $key => $value ) {
43
  echo '<tr>';
44
+ echo '<td>' . esc_html( $key ) . '</td>';
45
  if ( is_array( $value ) ) {
46
+ echo '<td class="qm-has-inner">';
47
  self::output_inner( $value );
48
  echo '</td>';
49
  } else if ( is_object( $value ) ) {
50
+ echo '<td class="qm-has-inner">';
51
  self::output_inner( get_object_vars( $value ) );
52
  echo '</td>';
53
  } else if ( is_bool( $value ) ) {
54
  if ( $value ) {
55
+ echo '<td class="qm-true">true</td>';
56
  } else {
57
+ echo '<td class="qm-false">false</td>';
58
  }
59
  } else {
60
+ echo '<td>';
61
  echo nl2br( esc_html( $value ) );
62
  echo '</td>';
63
  }
68
 
69
  }
70
 
71
+ /**
72
+ * Returns the table filter controls. Safe for output.
73
+ *
74
+ * @param string $name The name for the `data-` attributes that get filtered by this control.
75
+ * @param array $values Possible values for this control.
76
+ * @param string $highlight Optional. The name for the `data-` attributes that get highlighted by this control.
77
+ * @return string Markup for the table filter controls.
78
+ */
79
  protected function build_filter( $name, array $values, $highlight = '' ) {
80
 
81
  if ( empty( $values ) ) {
85
  usort( $values, 'strcasecmp' );
86
 
87
  $out = '<select id="qm-filter-' . esc_attr( $this->collector->id . '-' . $name ) . '" class="qm-filter" data-filter="' . esc_attr( $name ) . '" data-highlight="' . esc_attr( $highlight ) . '">';
88
+ $out .= '<option value="">' . esc_html_x( 'All', '"All" option for filters', 'query-monitor' ) . '</option>';
89
 
90
  foreach ( $values as $value ) {
91
  $out .= '<option value="' . esc_attr( $value ) . '">' . esc_html( $value ) . '</option>';
97
 
98
  }
99
 
100
+ /**
101
+ * Returns the column sorter controls. Safe for output.
102
+ *
103
+ * @return string Markup for the column sorter controls.
104
+ */
105
  protected function build_sorter() {
106
  $out = '<span class="qm-sort-controls">';
107
  $out .= '<a href="#" class="qm-sort qm-sort-asc">&#9650;</a>';
113
  protected function menu( array $args ) {
114
 
115
  return array_merge( array(
116
+ 'id' => esc_attr( "query-monitor-{$this->collector->id}" ),
117
+ 'href' => esc_attr( '#' . $this->collector->id() )
118
  ), $args );
119
 
120
  }
121
 
122
+ /**
123
+ * Returns the given SQL string in a nicely presented format. Safe for output.
124
+ *
125
+ * @param string $sql An SQL query string.
126
+ * @return string The SQL formatted with markup.
127
+ */
128
  public static function format_sql( $sql ) {
129
 
130
  $sql = str_replace( array( "\r\n", "\r", "\n", "\t" ), ' ', $sql );
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
 
147
 
148
  }
149
 
150
+ /**
151
+ * Returns the given URL in a nicely presented format. Safe for output.
152
+ *
153
+ * @param string $url A URL.
154
+ * @return string The URL formatted with markup.
155
+ */
156
  public static function format_url( $url ) {
157
+ return str_replace( '&amp;', '<br>&amp;', esc_html( $url ) );
 
 
 
 
 
 
 
 
 
 
158
  }
159
 
160
+ /**
161
+ * Returns a file path, name, and line number. Safe for output.
162
+ *
163
+ * If clickable file links are enabled, a link such as this is returned:
164
+ *
165
+ * <a href="subl://open/?line={line}&url={file}">{text}</a>
166
+ *
167
+ * Otherwise, the display text and file details such as this is returned:
168
+ *
169
+ * {text}<br>{file}:{line}
170
+ *
171
+ * @param string $text The display text, such as a function name or file name.
172
+ * @param string $file The full file path and name.
173
+ * @param int $line Optional. A line number, if appropriate.
174
+ * @return string The fully formatted file link or file name, safe for output.
175
+ */
176
+ public static function output_filename( $text, $file, $line = 0 ) {
177
 
178
  # Further reading:
179
  # http://simonwheatley.co.uk/2012/07/clickable-stack-traces/
180
  # https://github.com/grych/subl-handler
181
 
182
+ $link_line = ( $line ) ? $line : 1;
183
+
184
+ if ( ! isset( self::$file_link_format ) ) {
185
  $format = ini_get( 'xdebug.file_link_format' );
186
  $format = apply_filters( 'qm/output/file_link_format', $format );
187
  if ( empty( $format ) ) {
192
  }
193
 
194
  if ( false === self::$file_link_format ) {
195
+ $fallback = QM_Util::standard_dir( $file, '' );
196
+ if ( $line ) {
197
+ $fallback .= ':' . $line;
198
+ }
199
+ $return = esc_html( $text );
200
+ if ( $fallback != $text ) {
201
+ $return .= '<br><span class="qm-info">&nbsp;' . esc_html( $fallback ) . '</span>';
202
+ }
203
+ return $return;
204
  }
205
 
206
+ $link = sprintf( self::$file_link_format, urlencode( $file ), intval( $link_line ) );
207
+ return sprintf( '<a href="%s">%s</a>', esc_attr( $link ), esc_html( $text ) );
208
 
209
  }
210
 
output/headers/overview.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Headers_Overview extends QM_Output_Headers {
18
+
19
+ public function get_output() {
20
+
21
+ $data = $this->collector->get_data();
22
+ $headers = array();
23
+
24
+ $headers['time'] = number_format_i18n( $data['time'], 4 );
25
+ $headers['time_usage'] = sprintf(
26
+ __( '%1$s%% of %2$ss limit', 'query-monitor' ),
27
+ number_format_i18n( $data['time_usage'], 1 ),
28
+ number_format_i18n( $data['time_limit'] )
29
+ );
30
+
31
+ if ( ! empty( $data['memory'] ) ) {
32
+ $headers['memory'] = sprintf(
33
+ __( '%s kB', 'query-monitor' ),
34
+ number_format_i18n( $data['memory'] / 1024 )
35
+ );
36
+ $headers['memory_usage'] = sprintf(
37
+ __( '%1$s%% of %2$s kB limit', 'query-monitor' ),
38
+ number_format_i18n( $data['memory_usage'], 1 ),
39
+ number_format_i18n( $data['memory_limit'] / 1024 )
40
+ );
41
+ }
42
+
43
+ return $headers;
44
+
45
+ }
46
+
47
+ }
48
+
49
+ function register_qm_output_headers_overview( array $output, QM_Collectors $collectors ) {
50
+ if ( $collector = QM_Collectors::get( 'overview' ) ) {
51
+ $output['overview'] = new QM_Output_Headers_Overview( $collector );
52
+ }
53
+ return $output;
54
+ }
55
+
56
+ add_filter( 'qm/outputter/headers', 'register_qm_output_headers_overview', 10, 2 );
output/headers/php_errors.php CHANGED
@@ -16,16 +16,13 @@ GNU General Public License for more details.
16
 
17
  class QM_Output_Headers_PHP_Errors extends QM_Output_Headers {
18
 
19
- public function output() {
20
-
21
- if ( ! QM_Util::is_ajax() ) {
22
- return;
23
- }
24
 
25
  $data = $this->collector->get_data();
 
26
 
27
  if ( empty( $data['errors'] ) ) {
28
- return;
29
  }
30
 
31
  $count = 0;
@@ -48,18 +45,16 @@ class QM_Output_Headers_PHP_Errors extends QM_Output_Headers {
48
  'component' => $component->name,
49
  );
50
 
51
- header( sprintf( 'X-QM-Error-%d: %s',
52
- $count,
53
- json_encode( $output_error )
54
- ) );
55
 
56
  }
57
 
58
  }
59
 
60
- header( sprintf( 'X-QM-Errors: %d',
61
- $count
62
- ) );
63
 
64
  }
65
 
16
 
17
  class QM_Output_Headers_PHP_Errors extends QM_Output_Headers {
18
 
19
+ public function get_output() {
 
 
 
 
20
 
21
  $data = $this->collector->get_data();
22
+ $headers = array();
23
 
24
  if ( empty( $data['errors'] ) ) {
25
+ return array();
26
  }
27
 
28
  $count = 0;
45
  'component' => $component->name,
46
  );
47
 
48
+ $key = sprintf( 'error-%d', $count );
49
+ $headers[ $key ] = json_encode( $output_error );
 
 
50
 
51
  }
52
 
53
  }
54
 
55
+ return array_merge( array(
56
+ 'error-count' => $count,
57
+ ), $headers );
58
 
59
  }
60
 
output/headers/redirects.php CHANGED
@@ -16,17 +16,17 @@ GNU General Public License for more details.
16
 
17
  class QM_Output_Headers_Redirects extends QM_Output_Headers {
18
 
19
- public function output() {
20
 
21
  $data = $this->collector->get_data();
 
22
 
23
  if ( empty( $data['trace'] ) ) {
24
- return;
25
  }
26
 
27
- header( sprintf( 'X-QM-Redirect-Trace: %s',
28
- implode( ', ', $data['trace']->get_stack() )
29
- ) );
30
 
31
  }
32
 
16
 
17
  class QM_Output_Headers_Redirects extends QM_Output_Headers {
18
 
19
+ public function get_output() {
20
 
21
  $data = $this->collector->get_data();
22
+ $headers = array();
23
 
24
  if ( empty( $data['trace'] ) ) {
25
+ return array();
26
  }
27
 
28
+ $headers['Redirect-Trace'] = implode( ', ', $data['trace']->get_stack() );
29
+ return $headers;
 
30
 
31
  }
32
 
output/html/admin.php CHANGED
@@ -58,7 +58,7 @@ class QM_Output_Html_Admin extends QM_Output_Html {
58
 
59
  echo '<tr>';
60
  echo '<td class="qm-ltr">$pagenow</td>';
61
- echo "<td>{$data['pagenow']}</td>";
62
  echo '</tr>';
63
 
64
  $screens = array(
@@ -75,6 +75,7 @@ class QM_Output_Html_Admin extends QM_Output_Html {
75
  'users-network' => true,
76
  );
77
 
 
78
  if ( !empty( $data['current_screen'] ) and isset( $screens[$data['current_screen']->base] ) ) {
79
 
80
  # And now, WordPress' legendary inconsistency comes into play:
@@ -102,16 +103,16 @@ class QM_Output_Html_Admin extends QM_Output_Html {
102
  }
103
 
104
  echo '<tr>';
105
- echo '<td rowspan="2">' . __( 'Column Filters', 'query-monitor' ) . '</td>';
106
- echo "<td colspan='2'>manage_<span class='qm-current'>{$cols}</span>_columns</td>";
107
  echo '</tr>';
108
  echo '<tr>';
109
- echo "<td colspan='2'>manage_<span class='qm-current'>{$data['current_screen']->id}</span>_sortable_columns</td>";
110
  echo '</tr>';
111
 
112
  echo '<tr>';
113
- echo '<td rowspan="1">' . __( 'Column Action', 'query-monitor' ) . '</td>';
114
- echo "<td colspan='2'>manage_<span class='qm-current'>{$col}</span>_custom_column</td>";
115
  echo '</tr>';
116
 
117
  }
58
 
59
  echo '<tr>';
60
  echo '<td class="qm-ltr">$pagenow</td>';
61
+ echo '<td>' . esc_html( $data['pagenow'] ) . '</td>';
62
  echo '</tr>';
63
 
64
  $screens = array(
75
  'users-network' => true,
76
  );
77
 
78
+ // @TODO a lot of this logic can move to the collector
79
  if ( !empty( $data['current_screen'] ) and isset( $screens[$data['current_screen']->base] ) ) {
80
 
81
  # And now, WordPress' legendary inconsistency comes into play:
103
  }
104
 
105
  echo '<tr>';
106
+ echo '<td rowspan="2">' . esc_html__( 'Column Filters', 'query-monitor' ) . '</td>';
107
+ echo '<td colspan="2">manage_<span class="qm-current">' . esc_html( $cols ) . '</span>_columns</td>';
108
  echo '</tr>';
109
  echo '<tr>';
110
+ echo '<td colspan="2">manage_<span class="qm-current">' . esc_html( $data['current_screen']->id ) . '</span>_sortable_columns</td>';
111
  echo '</tr>';
112
 
113
  echo '<tr>';
114
+ echo '<td rowspan="1">' . esc_html__( 'Column Action', 'query-monitor' ) . '</td>';
115
+ echo '<td colspan="2">manage_<span class="qm-current">' . esc_html( $col ) . '</span>_custom_column</td>';
116
  echo '</tr>';
117
 
118
  }
output/html/assets.php CHANGED
@@ -40,17 +40,17 @@ class QM_Output_Html_Assets extends QM_Output_Html {
40
 
41
  echo '<thead>';
42
 
43
- if ( 'scripts' != $type ) {
44
  echo '<tr class="qm-totally-legit-spacer">';
45
  echo '<td colspan="6"></td>';
46
  echo '</tr>';
47
  }
48
 
49
  echo '<tr>';
50
- echo '<th colspan="2">' . $type_label . '</th>';
51
- echo '<th>' . __( 'Dependencies', 'query-monitor' ) . '</th>';
52
- echo '<th>' . __( 'Dependents', 'query-monitor' ) . '</th>';
53
- echo '<th>' . __( 'Version', 'query-monitor' ) . '</th>';
54
  echo '</tr>';
55
  echo '</thead>';
56
  echo '<tbody>';
@@ -83,8 +83,8 @@ class QM_Output_Html_Assets extends QM_Output_Html {
83
 
84
  if ( empty( $handles ) ) {
85
  echo '<tr>';
86
- echo '<td valign="top" class="qm-nowrap">' . $label . '</td>';
87
- echo '<td valign="top" colspan="5"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
88
  echo '</tr>';
89
  return;
90
  }
@@ -92,14 +92,14 @@ 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="' . $handle . '">';
96
  } else {
97
- echo '<tr data-qm-subject="' . $handle . '" class="qm-warn">';
98
  }
99
 
100
  if ( $first ) {
101
  $rowspan = count( $handles );
102
- echo "<th valign='top' rowspan='{$rowspan}' class='qm-nowrap'>" . $label . "</th>";
103
  }
104
 
105
  $this->dependency_row( $dependencies->query( $handle ), $dependencies );
@@ -113,13 +113,13 @@ class QM_Output_Html_Assets extends QM_Output_Html {
113
  protected function dependency_row( _WP_Dependency $script, WP_Dependencies $dependencies ) {
114
 
115
  if ( empty( $script->ver ) ) {
116
- $ver = '&nbsp;';
117
  } else {
118
- $ver = esc_html( $script->ver );
119
  }
120
 
121
  if ( empty( $script->src ) ) {
122
- $src = '&nbsp;';
123
  } else {
124
  $src = $script->src;
125
  }
@@ -134,10 +134,10 @@ class QM_Output_Html_Assets extends QM_Output_Html {
134
  }
135
  }
136
 
137
- echo '<td valign="top" class="qm-wrap">' . $script->handle . '<br><span class="qm-info">' . $src . '</span></td>';
138
- echo '<td valign="top" class="qm-nowrap qm-highlighter" data-qm-highlight="' . implode( ' ', $deps ) . '">' . implode( '<br>', $deps ) . '</td>';
139
- echo '<td valign="top" class="qm-nowrap qm-highlighter" data-qm-highlight="' . implode( ' ', $dependents ) . '">' . implode( '<br>', $dependents ) . '</td>';
140
- echo '<td valign="top">' . $ver . '</td>';
141
 
142
  }
143
 
@@ -177,7 +177,7 @@ class QM_Output_Html_Assets extends QM_Output_Html {
177
 
178
  $data = $this->collector->get_data();
179
  $args = array(
180
- 'title' => $this->collector->name()
181
  );
182
 
183
  if ( !empty( $data['broken'] ) or !empty( $data['missing'] ) ) {
40
 
41
  echo '<thead>';
42
 
43
+ if ( 'scripts' !== $type ) {
44
  echo '<tr class="qm-totally-legit-spacer">';
45
  echo '<td colspan="6"></td>';
46
  echo '</tr>';
47
  }
48
 
49
  echo '<tr>';
50
+ echo '<th colspan="2">' . esc_html( $type_label ) . '</th>';
51
+ echo '<th>' . esc_html__( 'Dependencies', 'query-monitor' ) . '</th>';
52
+ echo '<th>' . esc_html__( 'Dependents', 'query-monitor' ) . '</th>';
53
+ echo '<th>' . esc_html__( 'Version', 'query-monitor' ) . '</th>';
54
  echo '</tr>';
55
  echo '</thead>';
56
  echo '<tbody>';
83
 
84
  if ( empty( $handles ) ) {
85
  echo '<tr>';
86
+ echo '<td class="qm-nowrap">' . esc_html( $label ) . '</td>';
87
+ echo '<td colspan="5"><em>' . esc_html__( 'none', 'query-monitor' ) . '</em></td>';
88
  echo '</tr>';
89
  return;
90
  }
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 ) {
101
  $rowspan = count( $handles );
102
+ echo '<th rowspan="' . esc_attr( $rowspan ) . '" class="qm-nowrap">' . esc_html( $label ) . '</th>';
103
  }
104
 
105
  $this->dependency_row( $dependencies->query( $handle ), $dependencies );
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
  if ( empty( $script->src ) ) {
122
+ $src = '';
123
  } else {
124
  $src = $script->src;
125
  }
134
  }
135
  }
136
 
137
+ echo '<td class="qm-wrap">' . esc_html( $script->handle ) . '<br><span class="qm-info">&nbsp;' . esc_html( $src ) . '</span></td>';
138
+ echo '<td class="qm-nowrap qm-highlighter" data-qm-highlight="' . esc_attr( implode( ' ', $deps ) ) . '">' . implode( '<br>', array_map( 'esc_html', $deps ) ) . '</td>';
139
+ echo '<td class="qm-nowrap qm-highlighter" data-qm-highlight="' . esc_attr( implode( ' ', $dependents ) ) . '">' . implode( '<br>', array_map( 'esc_html', $dependents ) ) . '</td>';
140
+ echo '<td>' . esc_html( $ver ) . '</td>';
141
 
142
  }
143
 
177
 
178
  $data = $this->collector->get_data();
179
  $args = array(
180
+ 'title' => esc_html( $this->collector->name() ),
181
  );
182
 
183
  if ( !empty( $data['broken'] ) or !empty( $data['missing'] ) ) {
output/html/conditionals.php CHANGED
@@ -33,7 +33,7 @@ class QM_Output_Html_Conditionals extends QM_Output_Html {
33
  echo '<table cellspacing="0">';
34
  echo '<thead>';
35
  echo '<tr>';
36
- echo '<th colspan="' . $cols . '">' . esc_html( $this->collector->name() ) . '</th>';
37
  echo '</tr>';
38
  echo '</thead>';
39
  echo '<tbody>';
@@ -43,7 +43,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="' . $w . '%">' . $cond . '()</td>';
47
  if ( 0 === $i%$cols ) {
48
  echo '</tr>';
49
  }
@@ -54,7 +54,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="' . $w . '%">' . $cond . '()</td>';
58
  if ( 0 === $i%$cols ) {
59
  echo '</tr>';
60
  }
@@ -62,7 +62,7 @@ class QM_Output_Html_Conditionals extends QM_Output_Html {
62
 
63
  $fill = ( $cols - ( $i % $cols ) );
64
  if ( $fill and ( $fill != $cols ) ) {
65
- echo '<td colspan="' . $fill . '">&nbsp;</td>';
66
  echo '</tr>';
67
  }
68
 
@@ -78,8 +78,8 @@ class QM_Output_Html_Conditionals extends QM_Output_Html {
78
 
79
  foreach ( $data['conds']['true'] as $cond ) {
80
  $menu[] = $this->menu( array(
81
- 'title' => $cond . '()',
82
- 'id' => 'query-monitor-' . $cond,
83
  'meta' => array( 'classname' => 'qm-true qm-ltr' )
84
  ) );
85
  }
33
  echo '<table cellspacing="0">';
34
  echo '<thead>';
35
  echo '<tr>';
36
+ echo '<th colspan="' . absint( $cols ) . '">' . esc_html( $this->collector->name() ) . '</th>';
37
  echo '</tr>';
38
  echo '</thead>';
39
  echo '<tbody>';
43
  if ( 1 === $i%$cols ) {
44
  echo '<tr>';
45
  }
46
+ echo '<td class="qm-ltr qm-true" width="' . absint( $w ) . '%">' . esc_html( $cond ) . '()</td>';
47
  if ( 0 === $i%$cols ) {
48
  echo '</tr>';
49
  }
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
  }
62
 
63
  $fill = ( $cols - ( $i % $cols ) );
64
  if ( $fill and ( $fill != $cols ) ) {
65
+ echo '<td colspan="' . absint( $fill ) . '">&nbsp;</td>';
66
  echo '</tr>';
67
  }
68
 
78
 
79
  foreach ( $data['conds']['true'] as $cond ) {
80
  $menu[] = $this->menu( array(
81
+ 'title' => esc_html( $cond . '()' ),
82
+ 'id' => 'query-monitor-conditionals-' . esc_attr( $cond ),
83
  'meta' => array( 'classname' => 'qm-true qm-ltr' )
84
  ) );
85
  }
output/html/db_callers.php CHANGED
@@ -36,16 +36,22 @@ class QM_Output_Html_DB_Callers extends QM_Output_Html {
36
  echo '<table cellspacing="0" class="qm-sortable">';
37
  echo '<thead>';
38
  echo '<tr>';
39
- echo '<th colspan="' . $span . '">' . esc_html( $this->collector->name() ) . '</th>';
40
  echo '</tr>';
41
  echo '<tr>';
42
- echo '<th>' . _x( 'Caller', 'Query caller', 'query-monitor' ) . '</th>';
43
 
44
  foreach ( $data['types'] as $type_name => $type_count ) {
45
- echo '<th class="qm-num">' . $type_name . $this->build_sorter() . '</th>';
 
 
 
46
  }
47
 
48
- echo '<th class="qm-num qm-sorted-desc">' . __( 'Time', 'query-monitor' ) . $this->build_sorter() . '</th>';
 
 
 
49
  echo '</tr>';
50
  echo '</thead>';
51
 
@@ -58,17 +64,17 @@ class QM_Output_Html_DB_Callers extends QM_Output_Html {
58
  $stime = number_format_i18n( $row['ltime'], 4 );
59
 
60
  echo '<tr>';
61
- echo "<td valign='top' class='qm-ltr'>{$row['caller']}</td>";
62
 
63
  foreach ( $data['types'] as $type_name => $type_count ) {
64
  if ( isset( $row['types'][$type_name] ) ) {
65
- echo "<td valign='top' class='qm-num'>" . number_format_i18n( $row['types'][$type_name] ) . "</td>";
66
  } else {
67
- echo "<td valign='top' class='qm-num'>&nbsp;</td>";
68
  }
69
  }
70
 
71
- echo "<td valign='top' class='qm-num'>{$stime}</td>";
72
  echo '</tr>';
73
 
74
  }
@@ -82,10 +88,10 @@ class QM_Output_Html_DB_Callers extends QM_Output_Html {
82
  echo '<td>&nbsp;</td>';
83
 
84
  foreach ( $data['types'] as $type_name => $type_count ) {
85
- echo '<td class="qm-num">' . number_format_i18n( $type_count ) . '</td>';
86
  }
87
 
88
- echo "<td class='qm-num'>{$total_stime}</td>";
89
  echo '</tr>';
90
 
91
  echo '</tfoot>';
@@ -94,7 +100,7 @@ class QM_Output_Html_DB_Callers extends QM_Output_Html {
94
 
95
  echo '<tbody>';
96
  echo '<tr>';
97
- echo '<td colspan="3" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
98
  echo '</tr>';
99
  echo '</tbody>';
100
 
@@ -111,7 +117,7 @@ class QM_Output_Html_DB_Callers extends QM_Output_Html {
111
  $dbq_data = $dbq->get_data();
112
  if ( isset( $dbq_data['times'] ) ) {
113
  $menu[] = $this->menu( array(
114
- 'title' => __( 'Queries by Caller', 'query-monitor' )
115
  ) );
116
  }
117
  }
36
  echo '<table cellspacing="0" class="qm-sortable">';
37
  echo '<thead>';
38
  echo '<tr>';
39
+ echo '<th colspan="' . absint( $span ) . '">' . esc_html( $this->collector->name() ) . '</th>';
40
  echo '</tr>';
41
  echo '<tr>';
42
+ echo '<th>' . esc_html_x( 'Caller', 'Query caller', 'query-monitor' ) . '</th>';
43
 
44
  foreach ( $data['types'] as $type_name => $type_count ) {
45
+ echo '<th class="qm-num">';
46
+ echo esc_html( $type_name );
47
+ echo $this->build_sorter(); // WPCS: XSS ok;
48
+ echo '</th>';
49
  }
50
 
51
+ echo '<th class="qm-num qm-sorted-desc">';
52
+ esc_html_e( 'Time', 'query-monitor' );
53
+ echo $this->build_sorter(); // WPCS: XSS ok;
54
+ echo '</th>';
55
  echo '</tr>';
56
  echo '</thead>';
57
 
64
  $stime = number_format_i18n( $row['ltime'], 4 );
65
 
66
  echo '<tr>';
67
+ echo '<td class="qm-ltr">' . esc_html( $row['caller'] ) . '</td>';
68
 
69
  foreach ( $data['types'] as $type_name => $type_count ) {
70
  if ( isset( $row['types'][$type_name] ) ) {
71
+ echo "<td class='qm-num'>" . esc_html( number_format_i18n( $row['types'][$type_name] ) ) . '</td>';
72
  } else {
73
+ echo "<td class='qm-num'>&nbsp;</td>";
74
  }
75
  }
76
 
77
+ echo '<td class="qm-num">' . esc_html( $stime ) . '</td>';
78
  echo '</tr>';
79
 
80
  }
88
  echo '<td>&nbsp;</td>';
89
 
90
  foreach ( $data['types'] as $type_name => $type_count ) {
91
+ echo '<td class="qm-num">' . esc_html( number_format_i18n( $type_count ) ) . '</td>';
92
  }
93
 
94
+ echo '<td class="qm-num">' . esc_html( $total_stime ) . '</td>';
95
  echo '</tr>';
96
 
97
  echo '</tfoot>';
100
 
101
  echo '<tbody>';
102
  echo '<tr>';
103
+ echo '<td colspan="3" style="text-align:center !important"><em>' . esc_html__( 'none', 'query-monitor' ) . '</em></td>';
104
  echo '</tr>';
105
  echo '</tbody>';
106
 
117
  $dbq_data = $dbq->get_data();
118
  if ( isset( $dbq_data['times'] ) ) {
119
  $menu[] = $this->menu( array(
120
+ 'title' => esc_html__( 'Queries by Caller', 'query-monitor' )
121
  ) );
122
  }
123
  }
output/html/db_components.php CHANGED
@@ -37,16 +37,22 @@ class QM_Output_Html_DB_Components extends QM_Output_Html {
37
  echo '<table cellspacing="0" class="qm-sortable">';
38
  echo '<thead>';
39
  echo '<tr>';
40
- echo '<th colspan="' . $span . '">' . esc_html( $this->collector->name() ) . '</th>';
41
  echo '</tr>';
42
  echo '<tr>';
43
- echo '<th>' . _x( 'Component', 'Query component', 'query-monitor' ) . '</th>';
44
 
45
  foreach ( $data['types'] as $type_name => $type_count ) {
46
- echo '<th class="qm-num">' . $type_name . $this->build_sorter() . '</th>';
 
 
 
47
  }
48
 
49
- echo '<th class="qm-num qm-sorted-desc">' . __( 'Time', 'query-monitor' ) . $this->build_sorter() . '</th>';
 
 
 
50
  echo '</tr>';
51
  echo '</thead>';
52
 
@@ -57,20 +63,19 @@ class QM_Output_Html_DB_Components extends QM_Output_Html {
57
  foreach ( $data['times'] as $row ) {
58
  $total_time += $row['ltime'];
59
  $total_calls += $row['calls'];
60
- $stime = number_format_i18n( $row['ltime'], 4 );
61
 
62
  echo '<tr>';
63
- echo "<td valign='top'>{$row['component']}</td>";
64
 
65
  foreach ( $data['types'] as $type_name => $type_count ) {
66
  if ( isset( $row['types'][$type_name] ) ) {
67
- echo "<td valign='top' class='qm-num'>" . number_format_i18n( $row['types'][$type_name] ) . "</td>";
68
  } else {
69
- echo "<td valign='top' class='qm-num'>&nbsp;</td>";
70
  }
71
  }
72
 
73
- echo "<td valign='top' class='qm-num'>{$stime}</td>";
74
  echo '</tr>';
75
 
76
  }
@@ -84,10 +89,10 @@ class QM_Output_Html_DB_Components extends QM_Output_Html {
84
  echo '<td>&nbsp;</td>';
85
 
86
  foreach ( $data['types'] as $type_name => $type_count ) {
87
- echo '<td class="qm-num">' . number_format_i18n( $type_count ) . '</td>';
88
  }
89
 
90
- echo "<td class='qm-num'>{$total_stime}</td>";
91
  echo '</tr>';
92
  echo '</tfoot>';
93
 
@@ -95,7 +100,7 @@ class QM_Output_Html_DB_Components extends QM_Output_Html {
95
 
96
  echo '<tbody>';
97
  echo '<tr>';
98
- echo '<td colspan="' . $span . '" style="text-align:center !important"><em>' . __( 'Unknown', 'query-monitor' ) . '</em></td>';
99
  echo '</tr>';
100
  echo '</tbody>';
101
 
@@ -112,7 +117,7 @@ class QM_Output_Html_DB_Components extends QM_Output_Html {
112
  $dbq_data = $dbq->get_data();
113
  if ( isset( $dbq_data['component_times'] ) ) {
114
  $menu[] = $this->menu( array(
115
- 'title' => __( 'Queries by Component', 'query-monitor' )
116
  ) );
117
  }
118
  }
37
  echo '<table cellspacing="0" class="qm-sortable">';
38
  echo '<thead>';
39
  echo '<tr>';
40
+ echo '<th colspan="' . esc_attr( $span ) . '">' . esc_html( $this->collector->name() ) . '</th>';
41
  echo '</tr>';
42
  echo '<tr>';
43
+ echo '<th>' . esc_html_x( 'Component', 'Query component', 'query-monitor' ) . '</th>';
44
 
45
  foreach ( $data['types'] as $type_name => $type_count ) {
46
+ echo '<th class="qm-num">';
47
+ echo esc_html( $type_name );
48
+ echo $this->build_sorter(); // WPCS: XSS ok;
49
+ echo '</th>';
50
  }
51
 
52
+ echo '<th class="qm-num qm-sorted-desc">';
53
+ esc_html_e( 'Time', 'query-monitor' );
54
+ echo $this->build_sorter(); // WPCS: XSS ok;
55
+ echo '</th>';
56
  echo '</tr>';
57
  echo '</thead>';
58
 
63
  foreach ( $data['times'] as $row ) {
64
  $total_time += $row['ltime'];
65
  $total_calls += $row['calls'];
 
66
 
67
  echo '<tr>';
68
+ echo '<td>' . esc_html( $row['component'] ) . '</td>';
69
 
70
  foreach ( $data['types'] as $type_name => $type_count ) {
71
  if ( isset( $row['types'][$type_name] ) ) {
72
+ echo '<td class="qm-num">' . esc_html( number_format_i18n( $row['types'][ $type_name ] ) ) . '</td>';
73
  } else {
74
+ echo '<td class="qm-num">&nbsp;</td>';
75
  }
76
  }
77
 
78
+ echo '<td class="qm-num">' . esc_html( number_format_i18n( $row['ltime'], 4 ) ) . '</td>';
79
  echo '</tr>';
80
 
81
  }
89
  echo '<td>&nbsp;</td>';
90
 
91
  foreach ( $data['types'] as $type_name => $type_count ) {
92
+ echo '<td class="qm-num">' . esc_html( number_format_i18n( $type_count ) ) . '</td>';
93
  }
94
 
95
+ echo '<td class="qm-num">' . esc_html( $total_stime ) . '</td>';
96
  echo '</tr>';
97
  echo '</tfoot>';
98
 
100
 
101
  echo '<tbody>';
102
  echo '<tr>';
103
+ echo '<td colspan="' . esc_attr( $span ) . '" style="text-align:center !important"><em>' . esc_html__( 'Unknown', 'query-monitor' ) . '</em></td>';
104
  echo '</tr>';
105
  echo '</tbody>';
106
 
117
  $dbq_data = $dbq->get_data();
118
  if ( isset( $dbq_data['component_times'] ) ) {
119
  $menu[] = $this->menu( array(
120
+ 'title' => esc_html__( 'Queries by Component', 'query-monitor' )
121
  ) );
122
  }
123
  }
output/html/db_queries.php CHANGED
@@ -54,13 +54,16 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
54
  echo '<table cellspacing="0">';
55
  echo '<thead>';
56
  echo '<tr>';
57
- echo '<th>' . __( 'Database Queries', 'query-monitor' ) . '</th>';
58
  echo '</tr>';
59
  echo '</thead>';
60
  echo '<tbody>';
61
  echo '<tr>';
62
  echo '<td class="qm-warn">';
63
- _e( 'No database queries were logged because <code>SAVEQUERIES</code> is set to <code>false</code>', 'query-monitor' );
 
 
 
64
  echo '</td>';
65
  echo '</tr>';
66
  echo '</tbody>';
@@ -75,13 +78,13 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
75
  echo '<table cellspacing="0">';
76
  echo '<thead>';
77
  echo '<tr>';
78
- echo '<th colspan="4">' . __( 'Database Errors', 'query-monitor' ) . '</th>';
79
  echo '</tr>';
80
  echo '<tr>';
81
- echo '<th>' . __( 'Query', 'query-monitor' ) . '</th>';
82
- echo '<th>' . __( 'Call Stack', 'query-monitor' ) . '</th>';
83
- echo '<th>' . __( 'Component', 'query-monitor' ) . '</th>';
84
- echo '<th>' . __( 'Error', 'query-monitor' ) . '</th>';
85
  echo '</tr>';
86
  echo '</thead>';
87
  echo '<tbody>';
@@ -104,21 +107,25 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
104
  echo '<table cellspacing="0">';
105
  echo '<thead>';
106
  echo '<tr>';
107
- echo '<th colspan="5" class="qm-expensive">' . sprintf( __( 'Slow Database Queries (above %ss)', 'query-monitor' ), '<span class="qm-expensive">' . number_format_i18n( QM_DB_EXPENSIVE, $dp ) . '</span>' ) . '</th>';
 
 
 
 
108
  echo '</tr>';
109
  echo '<tr>';
110
- echo '<th scope="col">' . __( 'Query', 'query-monitor' ) . '</th>';
111
- echo '<th scope="col">' . __( 'Caller', 'query-monitor' ) . '</th>';
112
 
113
  if ( isset( $expensive[0]['component'] ) ) {
114
- echo '<th scope="col">' . __( 'Component', 'query-monitor' ) . '</th>';
115
  }
116
 
117
  if ( isset( $expensive[0]['result'] ) ) {
118
- echo '<th scope="col" class="qm-num">' . __( 'Affected Rows', 'query-monitor' ) . '</th>';
119
  }
120
 
121
- echo '<th class="qm-num">' . __( 'Time', 'query-monitor' ) . '</th>';
122
  echo '</tr>';
123
  echo '</thead>';
124
  echo '<tbody>';
@@ -144,30 +151,50 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
144
  $span++;
145
  }
146
 
147
- echo '<div class="qm qm-queries" id="' . esc_attr( $this->collector->id() ) . '-' . sanitize_title( $name ) . '">';
148
  echo '<table cellspacing="0" class="qm-sortable">';
149
  echo '<thead>';
150
  echo '<tr>';
151
- echo '<th colspan="' . $span . '">' . sprintf( __( '%s Queries', 'query-monitor' ), $name ) . '</th>';
152
  echo '</tr>';
153
 
154
  if ( !empty( $db->rows ) ) {
155
 
156
- if ( ! $db->has_trace ) {
157
  echo '<tr>';
158
- echo '<td colspan="' . $span . '" class="qm-warn">' . sprintf( __( 'Extended query information such as the component and affected rows is not available. Query Monitor was unable to symlink its <code>db.php</code> file into place. <a href="%s" target="_blank">See this wiki page for more information.</a>', 'query-monitor' ),
 
 
 
159
  'https://github.com/johnbillion/query-monitor/wiki/db.php-Symlink'
160
- ) . '</td>';
 
 
 
 
 
 
161
  echo '</tr>';
162
  }
163
 
164
  echo '<tr>';
165
- echo '<th scope="col" class="qm-sorted-asc">&nbsp;' . $this->build_sorter() . '</th>';
166
- echo '<th scope="col">' . __( 'Query', 'query-monitor' ) . $this->build_filter( 'type', array_keys( $db->types ) ) . '</th>';
167
- echo '<th scope="col">' . __( 'Caller', 'query-monitor' ) . $this->build_filter( 'caller', wp_list_pluck( $data['times'], 'caller' ) ) . '</th>';
 
 
 
 
 
 
 
 
168
 
169
  if ( $db->has_trace ) {
170
- echo '<th scope="col">' . __( 'Component', 'query-monitor' ) . $this->build_filter( 'component', wp_list_pluck( $data['component_times'], 'component' ) ) . '</th>';
 
 
 
171
  }
172
 
173
  if ( $db->has_result ) {
@@ -176,10 +203,16 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
176
  } else {
177
  $class = '';
178
  }
179
- echo '<th scope="col" class="' . $class . '">' . __( 'Rows', 'query-monitor' ) . $this->build_sorter() . '</th>';
 
 
 
180
  }
181
 
182
- echo '<th scope="col" class="qm-num">' . __( 'Time', 'query-monitor' ) . $this->build_sorter() . '</th>';
 
 
 
183
  echo '</tr>';
184
 
185
  }
@@ -200,13 +233,23 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
200
  $total_stime = number_format_i18n( $db->total_time, 4 );
201
 
202
  echo '<tr class="qm-items-shown qm-hide">';
203
- echo '<td valign="top" colspan="' . ( $span - 1 ) . '">' . sprintf( __( 'Queries in filter: %s', 'query-monitor' ), '<span class="qm-items-number">' . number_format_i18n( $db->total_qs ) . '</span>' ) . '</td>';
204
- echo "<td valign='top' class='qm-items-time qm-num'>{$total_stime}</td>";
 
 
 
 
 
205
  echo '</tr>';
206
 
207
  echo '<tr>';
208
- echo '<td valign="top" colspan="' . ( $span - 1 ) . '">' . sprintf( __( 'Total Queries: %s', 'query-monitor' ), number_format_i18n( $db->total_qs ) ) . '</td>';
209
- echo "<td valign='top' class='qm-num'>{$total_stime}</td>";
 
 
 
 
 
210
  echo '</tr>';
211
  echo '</tfoot>';
212
 
@@ -214,7 +257,7 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
214
 
215
  echo '<tbody>';
216
  echo '<tr>';
217
- echo '<td colspan="' . $span . '" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
218
  echo '</tr>';
219
  echo '</tbody>';
220
 
@@ -239,7 +282,6 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
239
  unset( $cols['stack'] );
240
  }
241
 
242
- $row_attr = array();
243
  $stime = number_format_i18n( $row['ltime'], 4 );
244
  $td = $this->collector->is_expensive( $row ) ? ' qm-expensive' : '';
245
 
@@ -249,14 +291,6 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
249
  $sql = "<span class='qm-nonselectsql'>{$sql}</span>";
250
  }
251
 
252
- if ( is_wp_error( $row['result'] ) ) {
253
- $error = $row['result']->get_error_message();
254
- $result = "<td valign='top' class='qm-row-result qm-row-error'>{$error}</td>\n";
255
- $row_attr['class'] = 'qm-warn';
256
- } else {
257
- $result = "<td valign='top' class='qm-row-result qm-num'>{$row['result']}</td>\n";
258
- }
259
-
260
  if ( isset( $row['trace'] ) ) {
261
 
262
  $caller = $row['trace']->get_caller();
@@ -271,13 +305,19 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
271
 
272
  } else {
273
 
274
- $caller_name = $row['caller'];
275
  $stack = explode( ', ', $row['stack'] );
276
  $stack = array_reverse( $stack );
277
  array_shift( $stack );
 
278
 
279
  }
280
 
 
 
 
 
 
281
  if ( isset( $cols['sql'] ) ) {
282
  $row_attr['data-qm-type'] = $row['type'];
283
  }
@@ -297,43 +337,52 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
297
  $attr .= ' ' . $a . '="' . esc_attr( $v ) . '"';
298
  }
299
 
300
- echo "<tr{$attr}>";
301
 
302
  if ( isset( $cols['row'] ) ) {
303
- echo "<td valign='top' class='qm-row-num qm-num'>" . ++$this->query_row . "</td>";
304
  }
305
 
306
  if ( isset( $cols['sql'] ) ) {
307
- echo "<td valign='top' class='qm-row-sql qm-ltr qm-wrap'>{$sql}</td>";
 
 
308
  }
309
 
310
  if ( isset( $cols['caller'] ) ) {
311
- echo "<td valign='top' class='qm-row-caller qm-ltr qm-has-toggle'>";
312
 
313
- echo $caller_name;
314
 
315
- if ( !empty( $stack ) ) {
316
  echo '<a href="#" class="qm-toggle" data-on="+" data-off="-">+</a>';
317
- echo '<div class="qm-toggled">' . implode( '<br>', $stack ) . '</div>';
318
  }
319
 
320
- echo "</td>";
321
  }
322
 
323
  if ( isset( $cols['stack'] ) ) {
324
- echo '<td valign="top" class="qm-row-caller qm-row-stack qm-nowrap qm-ltr">' . $caller_name . '<br>' . implode( '<br>', $stack ) . '</td>';
 
 
325
  }
326
 
327
  if ( isset( $cols['component'] ) ) {
328
- echo "<td valign='top' class='qm-row-component qm-nowrap'>{$row['component']->name}</td>\n";
329
  }
330
 
331
  if ( isset( $cols['result'] ) ) {
332
- echo $result;
 
 
 
 
 
333
  }
334
 
335
  if ( isset( $cols['time'] ) ) {
336
- echo "<td valign='top' class='qm-num qm-row-time{$td}'>{$stime}</td>\n";
337
  }
338
 
339
  echo '</tr>';
@@ -379,28 +428,38 @@ class QM_Output_Html_DB_Queries extends QM_Output_Html {
379
  $menu[] = $this->menu( array(
380
  'id' => 'query-monitor-errors',
381
  'href' => '#qm-query-errors',
382
- 'title' => sprintf( __( 'Database Errors (%s)', 'query-monitor' ), number_format_i18n( count( $errors ) ) )
 
 
 
383
  ) );
384
  }
385
  if ( $expensive = $this->collector->get_expensive() ) {
386
  $menu[] = $this->menu( array(
387
  'id' => 'query-monitor-expensive',
388
  'href' => '#qm-query-expensive',
389
- 'title' => sprintf( __( 'Slow Queries (%s)', 'query-monitor' ), number_format_i18n( count( $expensive ) ) )
 
 
 
390
  ) );
391
  }
392
 
393
  if ( isset( $data['dbs'] ) and count( $data['dbs'] ) > 1 ) {
394
  foreach ( $data['dbs'] as $name => $db ) {
395
  $menu[] = $this->menu( array(
396
- 'title' => sprintf( __( 'Queries (%s)', 'query-monitor' ), esc_html( $name ) ),
397
- 'href' => sprintf( '#%s-%s', $this->collector->id(), sanitize_title( $name ) ),
 
 
 
 
398
  ) );
399
  }
400
  } else {
401
  $menu[] = $this->menu( array(
402
- 'title' => __( 'Queries', 'query-monitor' ),
403
- 'href' => sprintf( '#%s-wpdb', $this->collector->id() ),
404
  ) );
405
  }
406
 
54
  echo '<table cellspacing="0">';
55
  echo '<thead>';
56
  echo '<tr>';
57
+ echo '<th>' . esc_html__( 'Database Queries', 'query-monitor' ) . '</th>';
58
  echo '</tr>';
59
  echo '</thead>';
60
  echo '<tbody>';
61
  echo '<tr>';
62
  echo '<td class="qm-warn">';
63
+ printf( esc_html__( 'No database queries were logged because the %s constant is set to %s', 'query-monitor' ),
64
+ '<code>SAVEQUERIES</code>',
65
+ '<code>false</code>'
66
+ );
67
  echo '</td>';
68
  echo '</tr>';
69
  echo '</tbody>';
78
  echo '<table cellspacing="0">';
79
  echo '<thead>';
80
  echo '<tr>';
81
+ echo '<th colspan="4">' . esc_html__( 'Database Errors', 'query-monitor' ) . '</th>';
82
  echo '</tr>';
83
  echo '<tr>';
84
+ echo '<th>' . esc_html__( 'Query', 'query-monitor' ) . '</th>';
85
+ echo '<th>' . esc_html__( 'Call Stack', 'query-monitor' ) . '</th>';
86
+ echo '<th>' . esc_html__( 'Component', 'query-monitor' ) . '</th>';
87
+ echo '<th>' . esc_html__( 'Error', 'query-monitor' ) . '</th>';
88
  echo '</tr>';
89
  echo '</thead>';
90
  echo '<tbody>';
107
  echo '<table cellspacing="0">';
108
  echo '<thead>';
109
  echo '<tr>';
110
+ echo '<th colspan="5" class="qm-expensive">';
111
+ printf( esc_html__( 'Slow Database Queries (above %ss)', 'query-monitor' ),
112
+ '<span class="qm-expensive">' . esc_html( number_format_i18n( QM_DB_EXPENSIVE, $dp ) ) . '</span>'
113
+ );
114
+ echo '</th>';
115
  echo '</tr>';
116
  echo '<tr>';
117
+ echo '<th scope="col">' . esc_html__( 'Query', 'query-monitor' ) . '</th>';
118
+ echo '<th scope="col">' . esc_html__( 'Caller', 'query-monitor' ) . '</th>';
119
 
120
  if ( isset( $expensive[0]['component'] ) ) {
121
+ echo '<th scope="col">' . esc_html__( 'Component', 'query-monitor' ) . '</th>';
122
  }
123
 
124
  if ( isset( $expensive[0]['result'] ) ) {
125
+ echo '<th scope="col" class="qm-num">' . esc_html__( 'Affected Rows', 'query-monitor' ) . '</th>';
126
  }
127
 
128
+ echo '<th class="qm-num">' . esc_html__( 'Time', 'query-monitor' ) . '</th>';
129
  echo '</tr>';
130
  echo '</thead>';
131
  echo '<tbody>';
151
  $span++;
152
  }
153
 
154
+ echo '<div class="qm qm-queries" id="' . esc_attr( $this->collector->id() . '-' . sanitize_title_with_dashes( $name ) ) . '">';
155
  echo '<table cellspacing="0" class="qm-sortable">';
156
  echo '<thead>';
157
  echo '<tr>';
158
+ echo '<th colspan="' . absint( $span ) . '">' . esc_html( sprintf( __( '%s Queries', 'query-monitor' ), $name ) ) . '</th>';
159
  echo '</tr>';
160
 
161
  if ( !empty( $db->rows ) ) {
162
 
163
+ if ( ! $db->has_trace && ( '$wpdb' === $name ) ) {
164
  echo '<tr>';
165
+ echo '<td colspan="' . absint( $span ) . '" class="qm-warn">';
166
+ echo wp_kses( sprintf(
167
+ __( 'Extended query information such as the component and affected rows is not available. Query Monitor was unable to symlink its %1$s file into place. <a href="%2$s" target="_blank">See this wiki page for more information.</a>', 'query-monitor' ),
168
+ '<code>db.php</code>',
169
  'https://github.com/johnbillion/query-monitor/wiki/db.php-Symlink'
170
+ ), array(
171
+ 'a' => array(
172
+ 'href' => array(),
173
+ 'target' => array(),
174
+ ),
175
+ ) );
176
+ echo '</td>';
177
  echo '</tr>';
178
  }
179
 
180
  echo '<tr>';
181
+ echo '<th scope="col" class="qm-sorted-asc">&nbsp;';
182
+ echo $this->build_sorter(); // WPCS: XSS ok;
183
+ echo '</th>';
184
+ echo '<th scope="col">';
185
+ esc_html_e( 'Query', 'query-monitor' );
186
+ echo $this->build_filter( 'type', array_keys( $db->types ) ); // WPCS: XSS ok;
187
+ echo '</th>';
188
+ echo '<th scope="col">';
189
+ esc_html_e( 'Caller', 'query-monitor' );
190
+ echo $this->build_filter( 'caller', wp_list_pluck( $data['times'], 'caller' ) ); // WPCS: XSS ok;
191
+ echo '</th>';
192
 
193
  if ( $db->has_trace ) {
194
+ echo '<th scope="col">';
195
+ esc_html_e( 'Component', 'query-monitor' );
196
+ echo $this->build_filter( 'component', wp_list_pluck( $data['component_times'], 'component' ) ); // WPCS: XSS ok.
197
+ echo '</th>';
198
  }
199
 
200
  if ( $db->has_result ) {
203
  } else {
204
  $class = '';
205
  }
206
+ echo '<th scope="col" class="' . esc_attr( $class ) . '">';
207
+ esc_html_e( 'Rows', 'query-monitor' );
208
+ echo $this->build_sorter(); // WPCS: XSS ok.
209
+ echo '</th>';
210
  }
211
 
212
+ echo '<th scope="col" class="qm-num">';
213
+ esc_html_e( 'Time', 'query-monitor' );
214
+ echo $this->build_sorter(); // WPCS: XSS ok.
215
+ echo '</th>';
216
  echo '</tr>';
217
 
218
  }
233
  $total_stime = number_format_i18n( $db->total_time, 4 );
234
 
235
  echo '<tr class="qm-items-shown qm-hide">';
236
+ echo '<td colspan="' . absint( $span - 1 ) . '">';
237
+ printf(
238
+ esc_html__( 'Queries in filter: %s', 'query-monitor' ),
239
+ '<span class="qm-items-number">' . esc_html( number_format_i18n( $db->total_qs ) ) . '</span>'
240
+ );
241
+ echo '</td>';
242
+ echo '<td class="qm-items-time qm-num">' . esc_html( $total_stime ) . '</td>';
243
  echo '</tr>';
244
 
245
  echo '<tr>';
246
+ echo '<td colspan="' . absint( $span - 1 ) . '">';
247
+ echo esc_html( sprintf(
248
+ __( 'Total Queries: %s', 'query-monitor' ),
249
+ number_format_i18n( $db->total_qs )
250
+ ) );
251
+ echo '</td>';
252
+ echo '<td class="qm-num">' . esc_html( $total_stime ) . '</td>';
253
  echo '</tr>';
254
  echo '</tfoot>';
255
 
257
 
258
  echo '<tbody>';
259
  echo '<tr>';
260
+ echo '<td colspan="' . absint( $span ) . '" style="text-align:center !important"><em>' . esc_html__( 'none', 'query-monitor' ) . '</em></td>';
261
  echo '</tr>';
262
  echo '</tbody>';
263
 
282
  unset( $cols['stack'] );
283
  }
284
 
 
285
  $stime = number_format_i18n( $row['ltime'], 4 );
286
  $td = $this->collector->is_expensive( $row ) ? ' qm-expensive' : '';
287
 
291
  $sql = "<span class='qm-nonselectsql'>{$sql}</span>";
292
  }
293
 
 
 
 
 
 
 
 
 
294
  if ( isset( $row['trace'] ) ) {
295
 
296
  $caller = $row['trace']->get_caller();
305
 
306
  } else {
307
 
308
+ $caller_name = esc_html( $row['caller'] );
309
  $stack = explode( ', ', $row['stack'] );
310
  $stack = array_reverse( $stack );
311
  array_shift( $stack );
312
+ $stack = array_map( 'esc_html', $stack );
313
 
314
  }
315
 
316
+ $row_attr = array();
317
+
318
+ if ( is_wp_error( $row['result'] ) ) {
319
+ $row_attr['class'] = 'qm-warn';
320
+ }
321
  if ( isset( $cols['sql'] ) ) {
322
  $row_attr['data-qm-type'] = $row['type'];
323
  }
337
  $attr .= ' ' . $a . '="' . esc_attr( $v ) . '"';
338
  }
339
 
340
+ echo "<tr{$attr}>"; // WPCS: XSS ok.
341
 
342
  if ( isset( $cols['row'] ) ) {
343
+ echo '<td class="qm-row-num qm-num">' . absint( ++$this->query_row ) . '</td>';
344
  }
345
 
346
  if ( isset( $cols['sql'] ) ) {
347
+ printf( '<td class="qm-row-sql qm-ltr qm-wrap">%s</td>',
348
+ $sql
349
+ ); // WPCS: XSS ok.
350
  }
351
 
352
  if ( isset( $cols['caller'] ) ) {
353
+ echo "<td class='qm-row-caller qm-ltr qm-has-toggle qm-nowrap'>";
354
 
355
+ echo $caller_name; // WPCS: XSS ok.
356
 
357
+ if ( ! empty( $stack ) ) {
358
  echo '<a href="#" class="qm-toggle" data-on="+" data-off="-">+</a>';
359
+ echo '<div class="qm-toggled">' . implode( '<br>', $stack ) . '</div>'; // WPCS: XSS ok.
360
  }
361
 
362
+ echo '</td>';
363
  }
364
 
365
  if ( isset( $cols['stack'] ) ) {
366
+ echo '<td class="qm-row-caller qm-row-stack qm-nowrap qm-ltr">';
367
+ echo $caller_name; // WPCS: XSS ok.
368
+ echo '<br>' . implode( '<br>', $stack ) . '</td>'; // WPCS: XSS ok.
369
  }
370
 
371
  if ( isset( $cols['component'] ) ) {
372
+ echo "<td class='qm-row-component qm-nowrap'>" . esc_html( $row['component']->name ) . "</td>\n";
373
  }
374
 
375
  if ( isset( $cols['result'] ) ) {
376
+ if ( is_wp_error( $row['result'] ) ) {
377
+ echo "<td class='qm-row-result qm-row-error'>" . esc_html( $row['result']->get_error_message() ) . "</td>\n";
378
+ } else {
379
+ echo "<td class='qm-row-result qm-num'>" . esc_html( $row['result'] ) . "</td>\n";
380
+ }
381
+
382
  }
383
 
384
  if ( isset( $cols['time'] ) ) {
385
+ echo '<td class="qm-num qm-row-time"' . esc_attr( $td ) . '">'. esc_html( $stime ) . "</td>\n";
386
  }
387
 
388
  echo '</tr>';
428
  $menu[] = $this->menu( array(
429
  'id' => 'query-monitor-errors',
430
  'href' => '#qm-query-errors',
431
+ 'title' => esc_html( sprintf(
432
+ __( 'Database Errors (%s)', 'query-monitor' ),
433
+ number_format_i18n( count( $errors ) )
434
+ ) ),
435
  ) );
436
  }
437
  if ( $expensive = $this->collector->get_expensive() ) {
438
  $menu[] = $this->menu( array(
439
  'id' => 'query-monitor-expensive',
440
  'href' => '#qm-query-expensive',
441
+ 'title' => esc_html( sprintf(
442
+ __( 'Slow Queries (%s)', 'query-monitor' ),
443
+ number_format_i18n( count( $expensive ) )
444
+ ) ),
445
  ) );
446
  }
447
 
448
  if ( isset( $data['dbs'] ) and count( $data['dbs'] ) > 1 ) {
449
  foreach ( $data['dbs'] as $name => $db ) {
450
  $menu[] = $this->menu( array(
451
+ 'id' => esc_attr( sprintf( 'query-monitor-%s-db-%s', $this->collector->id(), sanitize_title_with_dashes( $name ) ) ),
452
+ 'title' => esc_html( sprintf(
453
+ __( 'Queries - %s', 'query-monitor' ),
454
+ $name
455
+ ) ),
456
+ 'href' => esc_attr( sprintf( '#%s-%s', $this->collector->id(), sanitize_title_with_dashes( $name ) ) ),
457
  ) );
458
  }
459
  } else {
460
  $menu[] = $this->menu( array(
461
+ 'title' => esc_html__( 'Queries', 'query-monitor' ),
462
+ 'href' => esc_attr( sprintf( '#%s-wpdb', $this->collector->id() ) ),
463
  ) );
464
  }
465
 
output/html/debug_bar.php CHANGED
@@ -35,7 +35,7 @@ class QM_Output_Html_Debug_Bar extends QM_Output_Html {
35
  echo '<tbody>';
36
 
37
  echo '<tr>';
38
- echo '<td valign="top">';
39
  echo '<div id="debug-menu-target-' . esc_attr( $target ) . '" class="debug-menu-target qm-debug-bar-output">';
40
 
41
  $this->collector->render();
35
  echo '<tbody>';
36
 
37
  echo '<tr>';
38
+ echo '<td>';
39
  echo '<div id="debug-menu-target-' . esc_attr( $target ) . '" class="debug-menu-target qm-debug-bar-output">';
40
 
41
  $this->collector->render();
output/html/environment.php CHANGED
@@ -38,36 +38,58 @@ class QM_Output_Html_Environment extends QM_Output_Html {
38
 
39
  echo '<tr>';
40
  echo '<td>version</td>';
41
- echo "<td>{$data['php']['version']}</td>";
42
  echo '</tr>';
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  echo '<tr>';
44
  echo '<td>user</td>';
45
  if ( !empty( $data['php']['user'] ) ) {
46
  echo '<td>' . esc_html( $data['php']['user'] ) . '</td>';
47
  } else {
48
- echo '<td><em>' . __( 'Unknown', 'query-monitor' ) . '</em></td>';
49
  }
50
  echo '</tr>';
51
 
52
  foreach ( $data['php']['variables'] as $key => $val ) {
53
 
54
- $append = '';
 
 
 
55
 
56
  if ( $val['after'] != $val['before'] ) {
57
- $append .= '<br><span class="qm-info">' . sprintf( __( 'Overridden at runtime from %s', 'query-monitor' ), $val['before'] ) . '</span>';
 
 
 
 
 
 
58
  }
59
 
60
- echo '<tr>';
61
- echo "<td>{$key}</td>";
62
- echo "<td>{$val['after']}{$append}</td>";
63
  echo '</tr>';
64
  }
65
 
66
- $error_levels = implode( '<br>', $this->collector->get_error_levels( $data['php']['error_reporting'] ) );
67
 
68
  echo '<tr>';
69
  echo '<td>error_reporting</td>';
70
- echo "<td>{$data['php']['error_reporting']}<br><span class='qm-info'>{$error_levels}</span></td>";
 
 
71
  echo '</tr>';
72
 
73
  echo '</tbody>';
@@ -79,9 +101,9 @@ class QM_Output_Html_Environment extends QM_Output_Html {
79
  foreach ( $data['db'] as $id => $db ) {
80
 
81
  if ( 1 == count( $data['db'] ) ) {
82
- $name = 'Database';
83
  } else {
84
- $name = 'Database: ' . $id;
85
  }
86
 
87
  echo '<div class="qm qm-half">';
@@ -93,42 +115,31 @@ class QM_Output_Html_Environment extends QM_Output_Html {
93
  echo '</thead>';
94
  echo '<tbody>';
95
 
96
- echo '<tr>';
97
- echo '<td>driver</td>';
98
- echo '<td>' . $db['driver'] . '</td>';
99
- echo '</tr>';
100
 
101
- echo '<tr>';
102
- echo '<td>version</td>';
103
- echo '<td>' . $db['version'] . '</td>';
104
- echo '</tr>';
105
 
106
- echo '<tr>';
107
- echo '<td>user</td>';
108
- echo '<td>' . $db['user'] . '</td>';
109
- echo '</tr>';
 
110
 
111
- echo '<tr>';
112
- echo '<td>host</td>';
113
- echo '<td>' . $db['host'] . '</td>';
114
- echo '</tr>';
115
 
116
- echo '<tr>';
117
- echo '<td>database</td>';
118
- echo '<td>' . $db['name'] . '</td>';
119
- echo '</tr>';
120
 
121
  echo '<tr>';
122
 
123
  $first = true;
124
- $warn = __( "This value may not be optimal. Check the recommended configuration for '%s'.", 'query-monitor' );
125
  $search = __( 'https://www.google.com/search?q=mysql+performance+%s', 'query-monitor' );
126
 
127
  foreach ( $db['variables'] as $setting ) {
128
 
129
  $key = $setting->Variable_name;
130
  $val = $setting->Value;
131
- $prepend = '';
132
  $show_warning = false;
133
 
134
  if ( ( true === $db['vars'][$key] ) and empty( $val ) ) {
@@ -138,24 +149,31 @@ class QM_Output_Html_Environment extends QM_Output_Html {
138
  }
139
 
140
  if ( $show_warning ) {
141
- $prepend .= '&nbsp;<span class="qm-info">(<a href="' . esc_url( sprintf( $search, $key ) ) . '" target="_blank" title="' . esc_attr( sprintf( $warn, $key ) ) . '">' . __( 'Help', 'query-monitor' ) . '</a>)</span>';
 
 
 
 
142
  }
143
 
144
  if ( is_numeric( $val ) and ( $val >= ( 1024*1024 ) ) ) {
145
- $prepend .= '<br><span class="qm-info">~' . size_format( $val ) . '</span>';
 
 
 
146
  }
147
 
148
  $class = ( $show_warning ) ? 'qm-warn' : '';
149
 
150
  if ( !$first ) {
151
- echo "<tr class='{$class}'>";
152
  }
153
 
154
- $key = esc_html( $key );
155
- $val = esc_html( $val );
156
-
157
- echo "<td>{$key}</td>";
158
- echo "<td>{$val}{$prepend}</td>";
159
 
160
  echo '</tr>';
161
 
@@ -183,8 +201,8 @@ class QM_Output_Html_Environment extends QM_Output_Html {
183
  foreach ( $data['wp'] as $key => $val ) {
184
 
185
  echo '<tr>';
186
- echo "<td>{$key}</td>";
187
- echo "<td>{$val}</td>";
188
  echo '</tr>';
189
 
190
  }
@@ -197,37 +215,37 @@ class QM_Output_Html_Environment extends QM_Output_Html {
197
  echo '<table cellspacing="0">';
198
  echo '<thead>';
199
  echo '<tr>';
200
- echo '<th colspan="2">' . __( 'Server', 'query-monitor' ) . '</th>';
201
  echo '</tr>';
202
  echo '</thead>';
203
  echo '<tbody>';
204
 
205
  echo '<tr>';
206
- echo '<td>software</td>';
207
- echo "<td>{$data['server']['name']}</td>";
208
  echo '</tr>';
209
 
210
  echo '<tr>';
211
- echo '<td>version</td>';
212
  if ( !empty( $data['server']['version'] ) ) {
213
  echo '<td>' . esc_html( $data['server']['version'] ) . '</td>';
214
  } else {
215
- echo '<td><em>' . __( 'Unknown', 'query-monitor' ) . '</em></td>';
216
  }
217
  echo '</tr>';
218
 
219
  echo '<tr>';
220
- echo '<td>address</td>';
221
  if ( !empty( $data['server']['address'] ) ) {
222
  echo '<td>' . esc_html( $data['server']['address'] ) . '</td>';
223
  } else {
224
- echo '<td><em>' . __( 'Unknown', 'query-monitor' ) . '</em></td>';
225
  }
226
  echo '</tr>';
227
 
228
  echo '<tr>';
229
- echo '<td>host</td>';
230
- echo "<td>{$data['server']['host']}</td>";
231
  echo '</tr>';
232
 
233
  echo '</tbody>';
38
 
39
  echo '<tr>';
40
  echo '<td>version</td>';
41
+ echo '<td>' . esc_html( $data['php']['version'] ) . '</td>';
42
  echo '</tr>';
43
+
44
+ echo '<tr>';
45
+ echo '<td>sapi</td>';
46
+ echo '<td>' . esc_html( $data['php']['sapi'] ) . '</td>';
47
+ echo '</tr>';
48
+
49
+ if ( isset( $data['php']['hhvm'] ) ) {
50
+ echo '<tr>';
51
+ echo '<td>hhvm</td>';
52
+ echo '<td>' . esc_html( $data['php']['hhvm'] ) . '</td>';
53
+ echo '</tr>';
54
+ }
55
+
56
  echo '<tr>';
57
  echo '<td>user</td>';
58
  if ( !empty( $data['php']['user'] ) ) {
59
  echo '<td>' . esc_html( $data['php']['user'] ) . '</td>';
60
  } else {
61
+ echo '<td><em>' . esc_html__( 'Unknown', 'query-monitor' ) . '</em></td>';
62
  }
63
  echo '</tr>';
64
 
65
  foreach ( $data['php']['variables'] as $key => $val ) {
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'] ) {
73
+ printf(
74
+ '<br><span class="qm-info">&nbsp;%s</span>',
75
+ esc_html( sprintf(
76
+ __( 'Overridden at runtime from %s', 'query-monitor' ),
77
+ $val['before']
78
+ ) )
79
+ );
80
  }
81
 
82
+ echo '</td>';
 
 
83
  echo '</tr>';
84
  }
85
 
86
+ $error_levels = implode( '<br>&nbsp;', array_map( 'esc_html', $this->collector->get_error_levels( $data['php']['error_reporting'] ) ) );
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>';
94
 
95
  echo '</tbody>';
101
  foreach ( $data['db'] as $id => $db ) {
102
 
103
  if ( 1 == count( $data['db'] ) ) {
104
+ $name = __( 'Database', 'query-monitor' );
105
  } else {
106
+ $name = sprintf( __( 'Database: %s', 'query-monitor' ), $id );
107
  }
108
 
109
  echo '<div class="qm qm-half">';
115
  echo '</thead>';
116
  echo '<tbody>';
117
 
118
+ foreach ( $db['info'] as $key => $value ) {
 
 
 
119
 
120
+ echo '<tr>';
121
+ echo '<td>' . esc_html( $key ) . '</td>';
 
 
122
 
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>';
 
 
 
130
 
131
+ }
 
 
 
132
 
133
  echo '<tr>';
134
 
135
  $first = true;
 
136
  $search = __( 'https://www.google.com/search?q=mysql+performance+%s', 'query-monitor' );
137
 
138
  foreach ( $db['variables'] as $setting ) {
139
 
140
  $key = $setting->Variable_name;
141
  $val = $setting->Value;
142
+ $append = '';
143
  $show_warning = false;
144
 
145
  if ( ( true === $db['vars'][$key] ) and empty( $val ) ) {
149
  }
150
 
151
  if ( $show_warning ) {
152
+ $append .= sprintf(
153
+ '&nbsp;<span class="qm-info">(<a href="%s" target="_blank">%s</a>)</span>',
154
+ esc_url( sprintf( $search, $key ) ),
155
+ esc_html__( 'Help', 'query-monitor' )
156
+ );
157
  }
158
 
159
  if ( is_numeric( $val ) and ( $val >= ( 1024*1024 ) ) ) {
160
+ $append .= sprintf(
161
+ '<br><span class="qm-info">&nbsp;~%s</span>',
162
+ esc_html( size_format( $val ) )
163
+ );
164
  }
165
 
166
  $class = ( $show_warning ) ? 'qm-warn' : '';
167
 
168
  if ( !$first ) {
169
+ echo '<tr class="' . esc_attr( $class ) . '"">';
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>';
177
 
178
  echo '</tr>';
179
 
201
  foreach ( $data['wp'] as $key => $val ) {
202
 
203
  echo '<tr>';
204
+ echo '<td>' . esc_html( $key ) . '</td>';
205
+ echo '<td>' . esc_html( $val ) . '</td>';
206
  echo '</tr>';
207
 
208
  }
215
  echo '<table cellspacing="0">';
216
  echo '<thead>';
217
  echo '<tr>';
218
+ echo '<th colspan="2">' . esc_html__( 'Server', 'query-monitor' ) . '</th>';
219
  echo '</tr>';
220
  echo '</thead>';
221
  echo '<tbody>';
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
  }
235
  echo '</tr>';
236
 
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
  }
244
  echo '</tr>';
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>';
output/html/hooks.php CHANGED
@@ -21,7 +21,6 @@ class QM_Output_Html_Hooks extends QM_Output_Html {
21
  public function __construct( QM_Collector $collector ) {
22
  parent::__construct( $collector );
23
  add_filter( 'qm/output/menus', array( $this, 'admin_menu' ), 80 );
24
- add_filter( 'qm/output/menu_class', array( $this, 'admin_class' ) );
25
  }
26
 
27
  public function output() {
@@ -32,8 +31,6 @@ class QM_Output_Html_Hooks extends QM_Output_Html {
32
  return;
33
  }
34
 
35
- $row_attr = array();
36
-
37
  if ( is_multisite() and is_network_admin() ) {
38
  $screen = preg_replace( '|-network$|', '', $data['screen'] );
39
  } else {
@@ -44,8 +41,14 @@ class QM_Output_Html_Hooks extends QM_Output_Html {
44
  echo '<table cellspacing="0">';
45
  echo '<thead>';
46
  echo '<tr>';
47
- echo '<th>' . __( 'Hook', 'query-monitor' ) . $this->build_filter( 'name', $data['parts'] ) . '</th>';
48
- echo '<th colspan="3">' . __( 'Actions', 'query-monitor' ) . $this->build_filter( 'component', $data['components'], 'subject' ) . '</th>';
 
 
 
 
 
 
49
  echo '</tr>';
50
  echo '</thead>';
51
  echo '<tbody>';
@@ -55,13 +58,16 @@ class QM_Output_Html_Hooks extends QM_Output_Html {
55
  if ( !empty( $screen ) ) {
56
 
57
  if ( false !== strpos( $hook['name'], $screen . '.php' ) ) {
58
- $hook['name'] = str_replace( '-' . $screen . '.php', '-<span class="qm-current">' . $screen . '.php</span>', $hook['name'] );
59
  } else {
60
- $hook['name'] = str_replace( '-' . $screen, '-<span class="qm-current">' . $screen . '</span>', $hook['name'] );
61
  }
62
 
 
 
63
  }
64
 
 
65
  $row_attr['data-qm-name'] = implode( ' ', $hook['parts'] );
66
  $row_attr['data-qm-component'] = implode( ' ', $hook['components'] );
67
 
@@ -89,42 +95,48 @@ class QM_Output_Html_Hooks extends QM_Output_Html {
89
  $component = '';
90
  }
91
 
92
- $trattr = $attr . ' data-qm-subject="' . esc_attr( $component ) . '"';
93
-
94
- echo "<tr{$trattr}>";
 
 
95
 
96
  if ( $first ) {
97
 
98
- echo "<th valign='top' rowspan='{$rowspan}'>";
99
- echo $hook['name'];
100
  if ( 'all' === $hook['name'] ) {
101
  echo '<br><span class="qm-warn">';
102
- _e( 'Warning: The <code>all</code> action is extremely resource intensive. Try to avoid using it.', 'query-monitor' );
 
 
 
103
  echo '<span>';
104
  }
105
  echo '</th>';
106
 
107
  }
108
 
109
- echo '<td valign="top" class="qm-num">' . $action['priority'] . '</td>';
110
- echo '<td valign="top" class="qm-ltr">';
111
 
112
  if ( isset( $action['callback']['file'] ) ) {
113
- echo self::output_filename( esc_html( $action['callback']['name'] ), $action['callback']['file'], $action['callback']['line'] );
114
  } else {
115
  echo esc_html( $action['callback']['name'] );
116
  }
117
 
118
  if ( isset( $action['callback']['error'] ) ) {
119
  echo '<br><span class="qm-warn">';
120
- printf( __( 'Error: %s', 'query-monitor' ),
121
- esc_html( $action['callback']['error']->get_error_message() )
122
- );
 
123
  echo '<span>';
124
  }
125
 
126
  echo '</td>';
127
- echo '<td valign="top" class="qm-nowrap">';
128
  echo esc_html( $component );
129
  echo '</td>';
130
  echo '</tr>';
@@ -132,9 +144,9 @@ class QM_Output_Html_Hooks extends QM_Output_Html {
132
  }
133
 
134
  } else {
135
- echo "<tr{$attr}>";
136
- echo "<th valign='top'>";
137
- echo $hook['name'];
138
  echo '</th>';
139
  echo '<td colspan="3">&nbsp;</td>';
140
  echo '</tr>';
@@ -148,35 +160,6 @@ class QM_Output_Html_Hooks extends QM_Output_Html {
148
 
149
  }
150
 
151
- public function admin_class( array $class ) {
152
-
153
- $data = $this->collector->get_data();
154
-
155
- if ( isset( $data['warnings'] ) ) {
156
- $class[] = 'qm-warning';
157
- }
158
-
159
- return $class;
160
-
161
- }
162
-
163
- public function admin_menu( array $menu ) {
164
-
165
- $data = $this->collector->get_data();
166
- $args = array(
167
- 'title' => $this->collector->name(),
168
- );
169
-
170
- if ( isset( $data['warnings'] ) ) {
171
- $args['meta']['classname'] = 'qm-warning';
172
- }
173
-
174
- $menu[] = $this->menu( $args );
175
-
176
- return $menu;
177
-
178
- }
179
-
180
  }
181
 
182
  function register_qm_output_html_hooks( array $output, QM_Collectors $collectors ) {
21
  public function __construct( QM_Collector $collector ) {
22
  parent::__construct( $collector );
23
  add_filter( 'qm/output/menus', array( $this, 'admin_menu' ), 80 );
 
24
  }
25
 
26
  public function output() {
31
  return;
32
  }
33
 
 
 
34
  if ( is_multisite() and is_network_admin() ) {
35
  $screen = preg_replace( '|-network$|', '', $data['screen'] );
36
  } else {
41
  echo '<table cellspacing="0">';
42
  echo '<thead>';
43
  echo '<tr>';
44
+ echo '<th>';
45
+ esc_html_e( 'Hook', 'query-monitor' );
46
+ echo $this->build_filter( 'name', $data['parts'] ); // WPCS: XSS ok.
47
+ echo '</th>';
48
+ echo '<th colspan="3">';
49
+ esc_html_e( 'Actions', 'query-monitor' );
50
+ echo $this->build_filter( 'component', $data['components'], 'subject' ); // WPCS: XSS ok.
51
+ echo '</th>';
52
  echo '</tr>';
53
  echo '</thead>';
54
  echo '<tbody>';
58
  if ( !empty( $screen ) ) {
59
 
60
  if ( false !== strpos( $hook['name'], $screen . '.php' ) ) {
61
+ $hook_name = str_replace( '-' . $screen . '.php', '-<span class="qm-current">' . $screen . '.php</span>', esc_html( $hook['name'] ) );
62
  } else {
63
+ $hook_name = str_replace( '-' . $screen, '-<span class="qm-current">' . $screen . '</span>', esc_html( $hook['name'] ) );
64
  }
65
 
66
+ } else {
67
+ $hook_name = esc_html( $hook['name'] );
68
  }
69
 
70
+ $row_attr = array();
71
  $row_attr['data-qm-name'] = implode( ' ', $hook['parts'] );
72
  $row_attr['data-qm-component'] = implode( ' ', $hook['components'] );
73
 
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
 
106
+ echo "<th rowspan='" . absint( $rowspan ) . "'>";
107
+ echo $hook_name; // WPCS: XSS ok.
108
  if ( 'all' === $hook['name'] ) {
109
  echo '<br><span class="qm-warn">';
110
+ printf(
111
+ esc_html__( 'Warning: The %s action is extremely resource intensive. Try to avoid using it.', 'query-monitor' ),
112
+ '<code>all</code>'
113
+ );
114
  echo '<span>';
115
  }
116
  echo '</th>';
117
 
118
  }
119
 
120
+ echo '<td class="qm-num">' . intval( $action['priority'] ) . '</td>';
121
+ echo '<td class="qm-ltr qm-nowrap">';
122
 
123
  if ( isset( $action['callback']['file'] ) ) {
124
+ echo self::output_filename( $action['callback']['name'], $action['callback']['file'], $action['callback']['line'] ); // WPCS: XSS ok.
125
  } else {
126
  echo esc_html( $action['callback']['name'] );
127
  }
128
 
129
  if ( isset( $action['callback']['error'] ) ) {
130
  echo '<br><span class="qm-warn">';
131
+ echo esc_html( sprintf(
132
+ __( 'Error: %s', 'query-monitor' ),
133
+ $action['callback']['error']->get_error_message()
134
+ ) );
135
  echo '<span>';
136
  }
137
 
138
  echo '</td>';
139
+ echo '<td class="qm-nowrap">';
140
  echo esc_html( $component );
141
  echo '</td>';
142
  echo '</tr>';
144
  }
145
 
146
  } else {
147
+ echo "<tr{$attr}>"; // WPCS: XSS ok.
148
+ echo "<th>";
149
+ echo $hook_name; // WPCS: XSS ok.
150
  echo '</th>';
151
  echo '<td colspan="3">&nbsp;</td>';
152
  echo '</tr>';
160
 
161
  }
162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  }
164
 
165
  function register_qm_output_html_hooks( array $output, QM_Collectors $collectors ) {
output/html/http.php CHANGED
@@ -32,25 +32,33 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
32
  echo '<table cellspacing="0" class="qm-sortable">';
33
  echo '<thead>';
34
  echo '<tr>';
35
- echo '<th class="qm-sorted-asc">&nbsp;' . $this->build_sorter() . '</th>';
36
- echo '<th scope="col">' . __( 'HTTP Request', 'query-monitor' ) . '</th>';
37
- echo '<th scope="col">' . __( 'Response', 'query-monitor' ) . $this->build_filter( 'type', array_keys( $data['types'] ) ) . '</th>';
38
- echo '<th scope="col">' . __( 'Transport', 'query-monitor' ) . '</th>';
39
- echo '<th scope="col">' . __( 'Call Stack', 'query-monitor' ) . '</th>';
40
- echo '<th scope="col">' . __( 'Component', 'query-monitor' ) . $this->build_filter( 'component', wp_list_pluck( $data['component_times'], 'component' ) ) . '</th>';
41
- echo '<th scope="col" class="qm-num">' . __( 'Timeout', 'query-monitor' ) . $this->build_sorter() . '</th>';
42
- echo '<th scope="col" class="qm-num">' . __( 'Time', 'query-monitor' ) . $this->build_sorter() . '</th>';
 
 
 
 
 
 
 
 
 
 
43
  echo '</tr>';
44
  echo '</thead>';
45
 
46
- $vars = '';
47
 
48
  if ( !empty( $data['vars'] ) ) {
49
- $vars = array();
50
  foreach ( $data['vars'] as $key => $value ) {
51
- $vars[] = $key . ': ' . esc_html( $value );
52
  }
53
- $vars = implode( '<br>', $vars );
54
  }
55
 
56
  if ( !empty( $data['http'] ) ) {
@@ -64,14 +72,8 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
64
 
65
  $row_attr = array();
66
 
67
- if ( empty( $ltime ) ) {
68
- $stime = '';
69
- } else {
70
- $stime = number_format_i18n( $ltime, 4 );
71
- }
72
-
73
  if ( is_wp_error( $row['response'] ) ) {
74
- $response = esc_html( $row['response']->get_error_message() );
75
  $css = 'qm-warn';
76
  } else {
77
  $response = wp_remote_retrieve_response_code( $row['response'] );
@@ -81,7 +83,7 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
81
  if ( empty( $response ) ) {
82
  $response = __( 'n/a', 'query-monitor' );
83
  } else {
84
- $response = esc_html( $response . ' ' . $msg );
85
  }
86
 
87
  if ( intval( $response ) >= 400 ) {
@@ -102,9 +104,16 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
102
  $transport = '';
103
  }
104
 
105
- $stack = $row['trace']->get_stack();
106
  $component = $row['component'];
107
 
 
 
 
 
 
 
 
 
108
  $row_attr['data-qm-component'] = $component->name;
109
  $row_attr['data-qm-type'] = $row['type'];
110
 
@@ -113,19 +122,52 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
113
  $attr .= ' ' . $a . '="' . esc_attr( $v ) . '"';
114
  }
115
 
116
- $stack = implode( '<br>', $stack );
117
- echo "
118
- <tr{$attr} class='{$css}'>\n
119
- <td valign='top' class='qm-num'>{$i}</td>
120
- <td valign='top' class='qm-url qm-ltr qm-wrap'>{$method}<br>{$url}</td>\n
121
- <td valign='top'>{$response}</td>\n
122
- <td valign='top'>{$transport}</td>\n
123
- <td valign='top' class='qm-nowrap qm-ltr'>{$stack}</td>\n
124
- <td valign='top' class='qm-nowrap'>{$component->name}</td>\n
125
- <td valign='top' class='qm-num'>{$row['args']['timeout']}</td>\n
126
- <td valign='top' class='qm-num'>{$stime}</td>\n
127
- </tr>\n
128
- ";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
 
131
  echo '</tbody>';
@@ -134,8 +176,11 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
134
  $total_stime = number_format_i18n( $data['ltime'], 4 );
135
 
136
  echo '<tr>';
137
- echo '<td colspan="7">' . $vars . '</td>';
138
- echo "<td class='qm-num'>{$total_stime}</td>";
 
 
 
139
  echo '</tr>';
140
  echo '</tfoot>';
141
 
@@ -143,11 +188,14 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
143
 
144
  echo '<tbody>';
145
  echo '<tr>';
146
- echo '<td colspan="8" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
147
  echo '</tr>';
148
  if ( !empty( $vars ) ) {
149
  echo '<tr>';
150
- echo '<td colspan="8">' . $vars . '</td>';
 
 
 
151
  echo '</tr>';
152
  }
153
  echo '</tbody>';
@@ -184,7 +232,10 @@ class QM_Output_Html_HTTP extends QM_Output_Html {
184
  : __( 'HTTP Requests (%s)', 'query-monitor' );
185
 
186
  $args = array(
187
- 'title' => sprintf( $title, number_format_i18n( $count ) ),
 
 
 
188
  );
189
 
190
  if ( isset( $data['errors']['error'] ) ) {
32
  echo '<table cellspacing="0" class="qm-sortable">';
33
  echo '<thead>';
34
  echo '<tr>';
35
+ echo '<th class="qm-sorted-asc">&nbsp;';
36
+ echo $this->build_sorter(); // WPCS: XSS ok.
37
+ echo '</th>';
38
+ echo '<th scope="col">' . esc_html__( 'HTTP Request', 'query-monitor' ) . '</th>';
39
+ echo '<th scope="col">' . esc_html__( 'Response', 'query-monitor' );
40
+ echo $this->build_filter( 'type', array_keys( $data['types'] ) ); // WPCS: XSS ok.
41
+ echo '</th>';
42
+ echo '<th scope="col">' . esc_html__( 'Transport', 'query-monitor' ) . '</th>';
43
+ echo '<th scope="col">' . esc_html__( 'Call Stack', 'query-monitor' ) . '</th>';
44
+ echo '<th scope="col">' . esc_html__( 'Component', 'query-monitor' );
45
+ echo $this->build_filter( 'component', wp_list_pluck( $data['component_times'], 'component' ) ); // WPCS: XSS ok.
46
+ echo '</th>';
47
+ echo '<th scope="col" class="qm-num">' . esc_html__( 'Timeout', 'query-monitor' );
48
+ echo $this->build_sorter(); // WPCS: XSS ok.
49
+ echo '</th>';
50
+ echo '<th scope="col" class="qm-num">' . esc_html__( 'Time', 'query-monitor' );
51
+ echo $this->build_sorter(); // WPCS: XSS ok.
52
+ echo '</th>';
53
  echo '</tr>';
54
  echo '</thead>';
55
 
56
+ $vars = array();
57
 
58
  if ( !empty( $data['vars'] ) ) {
 
59
  foreach ( $data['vars'] as $key => $value ) {
60
+ $vars[] = $key . ': ' . $value;
61
  }
 
62
  }
63
 
64
  if ( !empty( $data['http'] ) ) {
72
 
73
  $row_attr = array();
74
 
 
 
 
 
 
 
75
  if ( is_wp_error( $row['response'] ) ) {
76
+ $response = $row['response']->get_error_message();
77
  $css = 'qm-warn';
78
  } else {
79
  $response = wp_remote_retrieve_response_code( $row['response'] );
83
  if ( empty( $response ) ) {
84
  $response = __( 'n/a', 'query-monitor' );
85
  } else {
86
+ $response = $response . ' ' . $msg;
87
  }
88
 
89
  if ( intval( $response ) >= 400 ) {
104
  $transport = '';
105
  }
106
 
 
107
  $component = $row['component'];
108
 
109
+ $stack = array();
110
+ $filtered_trace = $row['trace']->get_filtered_trace();
111
+ array_shift( $filtered_trace );
112
+
113
+ foreach ( $filtered_trace as $item ) {
114
+ $stack[] = self::output_filename( $item['display'], $item['calling_file'], $item['calling_line'] );
115
+ }
116
+
117
  $row_attr['data-qm-component'] = $component->name;
118
  $row_attr['data-qm-type'] = $row['type'];
119
 
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 )
142
+ );
143
+ printf(
144
+ '<td>%s</td>',
145
+ esc_html( $transport )
146
+ );
147
+ printf(
148
+ '<td class="qm-nowrap qm-ltr">%s</td>',
149
+ implode( '<br>', $stack ) // WPCS: XSS ok.
150
+ );
151
+ printf(
152
+ '<td class="qm-nowrap">%s</td>',
153
+ esc_html( $component->name )
154
+ );
155
+ printf(
156
+ '<td class="qm-num">%s</td>',
157
+ esc_html( $row['args']['timeout'] )
158
+ );
159
+
160
+ if ( empty( $ltime ) ) {
161
+ $stime = '';
162
+ } else {
163
+ $stime = number_format_i18n( $ltime, 4 );
164
+ }
165
+
166
+ printf(
167
+ '<td class="qm-num">%s</td>',
168
+ esc_html( $stime )
169
+ );
170
+ echo '</tr>';
171
  }
172
 
173
  echo '</tbody>';
176
  $total_stime = number_format_i18n( $data['ltime'], 4 );
177
 
178
  echo '<tr>';
179
+ printf(
180
+ '<td colspan="7">%s</td>',
181
+ implode( '<br>', array_map( 'esc_html', $vars ) )
182
+ );
183
+ echo '<td class="qm-num">' . esc_html( $total_stime ) . '</td>';
184
  echo '</tr>';
185
  echo '</tfoot>';
186
 
188
 
189
  echo '<tbody>';
190
  echo '<tr>';
191
+ echo '<td colspan="8" style="text-align:center !important"><em>' . esc_html__( 'none', 'query-monitor' ) . '</em></td>';
192
  echo '</tr>';
193
  if ( !empty( $vars ) ) {
194
  echo '<tr>';
195
+ printf(
196
+ '<td colspan="8">%s</td>',
197
+ implode( '<br>', array_map( 'esc_html', $vars ) )
198
+ );
199
  echo '</tr>';
200
  }
201
  echo '</tbody>';
232
  : __( 'HTTP Requests (%s)', 'query-monitor' );
233
 
234
  $args = array(
235
+ 'title' => esc_html( sprintf(
236
+ $title,
237
+ number_format_i18n( $count )
238
+ ) ),
239
  );
240
 
241
  if ( isset( $data['errors']['error'] ) ) {
output/html/languages.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_Languages extends QM_Output_Html {
18
+
19
+ public $id = 'languages';
20
+
21
+ public function __construct( QM_Collector $collector ) {
22
+ parent::__construct( $collector );
23
+ add_filter( 'qm/output/menus', array( $this, 'admin_menu' ), 80 );
24
+ }
25
+
26
+ public function output() {
27
+
28
+ $data = $this->collector->get_data();
29
+
30
+ if ( empty( $data['languages'] ) ) {
31
+ return;
32
+ }
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>' . esc_html__( 'Languages', 'query-monitor' ) . '</th>';
39
+ echo '<th colspan="3">' . esc_html__( 'Language Setting:', 'query-monitor' ) . ' ' . esc_html( get_locale() ) . '</th>';
40
+ echo '</tr>';
41
+ echo '<tr>';
42
+ echo '<td>' . esc_html__( 'Text Domain', 'query-monitor' ) . '</td>';
43
+ echo '<td>' . esc_html__( 'Caller', 'query-monitor' ) . '</td>';
44
+ echo '<td>' . esc_html__( 'MO File', 'query-monitor' ) . '</td>';
45
+ echo '<td>' . esc_html__( 'Loaded', 'query-monitor' ) . '</td>';
46
+ echo '</tr>';
47
+ echo '</thead>';
48
+ echo '<tbody>';
49
+
50
+ foreach ( $data['languages'] as $mofile ) {
51
+
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
+
62
+ if ( $mofile['found'] ) {
63
+ echo '<td class="qm-nowrap">';
64
+ echo esc_html__( 'Found', 'query-monitor' ) . '<br />';
65
+ echo esc_html( size_format( $mofile['found'] ) );
66
+ echo '</td>';
67
+ } else {
68
+ echo '<td class="qm-warn">';
69
+ echo esc_html__( 'Not Found', 'query-monitor' );
70
+ echo '</td>';
71
+ }
72
+
73
+ echo '</tr>';
74
+
75
+ }
76
+
77
+ echo '</tbody>';
78
+ echo '</table>';
79
+ echo '</div>';
80
+
81
+ }
82
+
83
+ public function admin_menu( array $menu ) {
84
+
85
+ $data = $this->collector->get_data();
86
+ $args = array(
87
+ 'title' => esc_html( $this->collector->name() ),
88
+ );
89
+
90
+ $menu[] = $this->menu( $args );
91
+
92
+ return $menu;
93
+
94
+ }
95
+
96
+ }
97
+
98
+ function register_qm_output_html_languages( array $output, QM_Collectors $collectors ) {
99
+ if ( $collector = QM_Collectors::get( 'languages' ) ) {
100
+ $output['languages'] = new QM_Output_Html_Languages( $collector );
101
+ }
102
+ return $output;
103
+ }
104
+
105
+ add_filter( 'qm/outputter/html', 'register_qm_output_html_languages', 81, 2 );
output/html/overview.php CHANGED
@@ -34,49 +34,65 @@ class QM_Output_Html_Overview extends QM_Output_Html {
34
  $db_queries_data = $db_queries->get_data();
35
  if ( isset( $db_queries_data['types'] ) ) {
36
  $db_query_num = $db_queries_data['types'];
37
- $db_stime = number_format_i18n( $db_queries_data['total_time'], 4 );
38
  }
39
  }
40
 
41
- $total_stime = number_format_i18n( $data['time'], 4 );
42
-
43
  echo '<div class="qm" id="' . esc_attr( $this->collector->id() ) . '">';
44
  echo '<table cellspacing="0">';
45
 
46
- $memory_usage = '<br><span class="qm-info">' . sprintf( __( '%1$s%% of %2$s kB limit', 'query-monitor' ), number_format_i18n( $data['memory_usage'], 1 ), number_format_i18n( $data['memory_limit'] / 1024 ) ) . '</span>';
47
-
48
- $time_usage = '<br><span class="qm-info">' . sprintf( __( '%1$s%% of %2$ss limit', 'query-monitor' ), number_format_i18n( $data['time_usage'], 1 ), number_format_i18n( $data['time_limit'] ) ) . '</span>';
49
-
50
  echo '<thead>';
51
  echo '<tr>';
52
- echo '<th scope="col">' . __( 'Page generation time', 'query-monitor' ) . '</th>';
53
- echo '<th scope="col">' . __( 'Peak memory usage', 'query-monitor' ) . '</th>';
54
  if ( isset( $db_query_num ) ) {
55
- echo '<th scope="col">' . __( 'Database query time', 'query-monitor' ) . '</th>';
56
- echo '<th scope="col">' . __( 'Database queries', 'query-monitor' ) . '</th>';
57
  }
58
  echo '</tr>';
59
  echo '</thead>';
60
 
61
  echo '<tbody>';
62
  echo '<tr>';
63
- echo "<td>{$total_stime}{$time_usage}</td>";
 
 
 
 
 
 
 
 
 
64
 
65
  if ( empty( $data['memory'] ) ) {
66
- echo '<td><em>' . __( 'Unknown', 'query-monitor' ) . '</em><br><span class="qm-info">' . __( 'Neither memory_get_peak_usage() nor memory_get_usage() are available. Speak to your host and get them to sort it out.', 'query-monitor' ) . '</span></td>';
67
  } else {
68
- echo '<td>' . sprintf( __( '%s kB', 'query-monitor' ), number_format_i18n( $data['memory'] / 1024 ) ) . $memory_usage . '</td>';
 
 
 
 
 
 
 
 
 
 
 
 
69
  }
70
 
71
  if ( isset( $db_query_num ) ) {
72
- echo "<td>{$db_stime}</td>";
 
 
73
  echo '<td>';
74
 
75
  foreach ( $db_query_num as $type_name => $type_count ) {
76
  $db_query_types[] = sprintf( '%1$s: %2$s', $type_name, number_format_i18n( $type_count ) );
77
  }
78
 
79
- echo implode( '<br>', $db_query_types );
80
 
81
  echo '</td>';
82
  }
34
  $db_queries_data = $db_queries->get_data();
35
  if ( isset( $db_queries_data['types'] ) ) {
36
  $db_query_num = $db_queries_data['types'];
 
37
  }
38
  }
39
 
 
 
40
  echo '<div class="qm" id="' . esc_attr( $this->collector->id() ) . '">';
41
  echo '<table cellspacing="0">';
42
 
 
 
 
 
43
  echo '<thead>';
44
  echo '<tr>';
45
+ echo '<th scope="col">' . esc_html__( 'Page generation time', 'query-monitor' ) . '</th>';
46
+ echo '<th scope="col">' . esc_html__( 'Peak memory usage', 'query-monitor' ) . '</th>';
47
  if ( isset( $db_query_num ) ) {
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
 
54
  echo '<tbody>';
55
  echo '<tr>';
56
+ echo '<td>';
57
+ echo esc_html( number_format_i18n( $data['time'], 4 ) );
58
+ echo '<br><span class="qm-info">';
59
+ echo esc_html( sprintf(
60
+ __( '%1$s%% of %2$ss limit', 'query-monitor' ),
61
+ number_format_i18n( $data['time_usage'], 1 ),
62
+ number_format_i18n( $data['time_limit'] )
63
+ ) );
64
+ echo '</span>';
65
+ echo '</td>';
66
 
67
  if ( empty( $data['memory'] ) ) {
68
+ echo '<td><em>' . esc_html__( 'Unknown', 'query-monitor' ) . '</em></td>';
69
  } else {
70
+ echo '<td>';
71
+ echo esc_html( sprintf(
72
+ __( '%s kB', 'query-monitor' ),
73
+ number_format_i18n( $data['memory'] / 1024 )
74
+ ) );
75
+ echo '<br><span class="qm-info">';
76
+ echo esc_html( sprintf(
77
+ __( '%1$s%% of %2$s kB limit', 'query-monitor' ),
78
+ number_format_i18n( $data['memory_usage'], 1 ),
79
+ number_format_i18n( $data['memory_limit'] / 1024 )
80
+ ) );
81
+ echo '</span>';
82
+ echo '</td>';
83
  }
84
 
85
  if ( isset( $db_query_num ) ) {
86
+ echo '<td>';
87
+ echo esc_html( number_format_i18n( $db_queries_data['total_time'], 4 ) );
88
+ echo '</td>';
89
  echo '<td>';
90
 
91
  foreach ( $db_query_num as $type_name => $type_count ) {
92
  $db_query_types[] = sprintf( '%1$s: %2$s', $type_name, number_format_i18n( $type_count ) );
93
  }
94
 
95
+ echo implode( '<br>', array_map( 'esc_html', $db_query_types ) );
96
 
97
  echo '</td>';
98
  }
output/html/php_errors.php CHANGED
@@ -34,11 +34,11 @@ class QM_Output_Html_PHP_Errors extends QM_Output_Html {
34
  echo '<table cellspacing="0">';
35
  echo '<thead>';
36
  echo '<tr>';
37
- echo '<th colspan="2">' . __( 'PHP Error', 'query-monitor' ) . '</th>';
38
- echo '<th class="qm-num">' . __( 'Count', 'query-monitor' ) . '</th>';
39
- echo '<th>' . __( 'Location', 'query-monitor' ) . '</th>';
40
- echo '<th>' . __( 'Call Stack', 'query-monitor' ) . '</th>';
41
- echo '<th>' . __( 'Component', 'query-monitor' ) . '</th>';
42
  echo '</tr>';
43
  echo '</thead>';
44
  echo '<tbody>';
@@ -55,7 +55,7 @@ class QM_Output_Html_PHP_Errors extends QM_Output_Html {
55
  if ( isset( $data['errors'][$type] ) ) {
56
 
57
  echo '<tr>';
58
- echo '<td rowspan="' . count( $data['errors'][$type] ) . '">' . $title . '</td>';
59
  $first = true;
60
 
61
  foreach ( $data['errors'][$type] as $error ) {
@@ -64,25 +64,25 @@ class QM_Output_Html_PHP_Errors extends QM_Output_Html {
64
  echo '<tr>';
65
  }
66
 
67
- $stack = $error->trace->get_stack();
68
  $component = $error->trace->get_component();
 
 
 
 
 
 
 
 
 
 
 
 
69
  if ( $component ) {
70
- $name = $component->name;
71
  } else {
72
- $name = '<em>' . __( 'Unknown', 'query-monitor' ) . '</em>';
73
  }
74
- $stack = implode( '<br>', $stack );
75
- $message = str_replace( "href='function.", "target='_blank' href='http://php.net/function.", $error->message );
76
 
77
- $output = esc_html( $error->filename ) . ':' . $error->line;
78
-
79
- echo '<td>' . $message . '</td>';
80
- echo '<td>' . number_format_i18n( $error->calls ) . '</td>';
81
- echo '<td>';
82
- echo self::output_filename( $output, $error->file, $error->line );
83
- echo '</td>';
84
- echo '<td class="qm-nowrap qm-ltr">' . $stack . '</td>';
85
- echo '<td class="qm-nowrap">' . $name . '</td>';
86
  echo '</tr>';
87
 
88
  $first = false;
@@ -124,25 +124,37 @@ class QM_Output_Html_PHP_Errors extends QM_Output_Html {
124
  if ( isset( $data['errors']['warning'] ) ) {
125
  $menu[] = $this->menu( array(
126
  'id' => 'query-monitor-warnings',
127
- 'title' => sprintf( __( 'PHP Warnings (%s)', 'query-monitor' ), number_format_i18n( count( $data['errors']['warning'] ) ) )
 
 
 
128
  ) );
129
  }
130
  if ( isset( $data['errors']['notice'] ) ) {
131
  $menu[] = $this->menu( array(
132
  'id' => 'query-monitor-notices',
133
- 'title' => sprintf( __( 'PHP Notices (%s)', 'query-monitor' ), number_format_i18n( count( $data['errors']['notice'] ) ) )
 
 
 
134
  ) );
135
  }
136
  if ( isset( $data['errors']['strict'] ) ) {
137
  $menu[] = $this->menu( array(
138
  'id' => 'query-monitor-stricts',
139
- 'title' => sprintf( __( 'PHP Stricts (%s)', 'query-monitor' ), number_format_i18n( count( $data['errors']['strict'] ) ) )
 
 
 
140
  ) );
141
  }
142
  if ( isset( $data['errors']['deprecated'] ) ) {
143
  $menu[] = $this->menu( array(
144
  'id' => 'query-monitor-deprecated',
145
- 'title' => sprintf( __( 'PHP Deprecated (%s)', 'query-monitor' ), number_format_i18n( count( $data['errors']['deprecated'] ) ) )
 
 
 
146
  ) );
147
  }
148
  return $menu;
34
  echo '<table cellspacing="0">';
35
  echo '<thead>';
36
  echo '<tr>';
37
+ echo '<th colspan="2">' . esc_html__( 'PHP Error', 'query-monitor' ) . '</th>';
38
+ echo '<th class="qm-num">' . esc_html__( 'Count', 'query-monitor' ) . '</th>';
39
+ echo '<th>' . esc_html__( 'Location', 'query-monitor' ) . '</th>';
40
+ echo '<th>' . esc_html__( 'Call Stack', 'query-monitor' ) . '</th>';
41
+ echo '<th>' . esc_html__( 'Component', 'query-monitor' ) . '</th>';
42
  echo '</tr>';
43
  echo '</thead>';
44
  echo '<tbody>';
55
  if ( isset( $data['errors'][$type] ) ) {
56
 
57
  echo '<tr>';
58
+ echo '<td rowspan="' . count( $data['errors'][$type] ) . '">' . esc_html( $title ) . '</td>';
59
  $first = true;
60
 
61
  foreach ( $data['errors'][$type] as $error ) {
64
  echo '<tr>';
65
  }
66
 
 
67
  $component = $error->trace->get_component();
68
+ $message = wp_strip_all_tags( $error->message );
69
+
70
+ echo '<td>' . esc_html( $message ) . '</td>';
71
+ echo '<td>' . esc_html( number_format_i18n( $error->calls ) ) . '</td>';
72
+ echo '<td>';
73
+ echo self::output_filename( $error->filename . ':' . $error->line, $error->file, $error->line ); // WPCS: XSS ok.
74
+ echo '</td>';
75
+ printf(
76
+ '<td class="qm-nowrap qm-ltr">%s</td>',
77
+ implode( '<br>', array_map( 'esc_html', $error->trace->get_stack() ) )
78
+ );
79
+
80
  if ( $component ) {
81
+ echo '<td class="qm-nowrap">' . esc_html( $component->name ) . '</td>';
82
  } else {
83
+ echo '<td><em>' . esc_html__( 'Unknown', 'query-monitor' ) . '</em></td>';
84
  }
 
 
85
 
 
 
 
 
 
 
 
 
 
86
  echo '</tr>';
87
 
88
  $first = false;
124
  if ( isset( $data['errors']['warning'] ) ) {
125
  $menu[] = $this->menu( array(
126
  'id' => 'query-monitor-warnings',
127
+ 'title' => esc_html( sprintf(
128
+ __( 'PHP Warnings (%s)', 'query-monitor' ),
129
+ number_format_i18n( count( $data['errors']['warning'] ) )
130
+ ) )
131
  ) );
132
  }
133
  if ( isset( $data['errors']['notice'] ) ) {
134
  $menu[] = $this->menu( array(
135
  'id' => 'query-monitor-notices',
136
+ 'title' => esc_html( sprintf(
137
+ __( 'PHP Notices (%s)', 'query-monitor' ),
138
+ number_format_i18n( count( $data['errors']['notice'] ) )
139
+ ) )
140
  ) );
141
  }
142
  if ( isset( $data['errors']['strict'] ) ) {
143
  $menu[] = $this->menu( array(
144
  'id' => 'query-monitor-stricts',
145
+ 'title' => esc_html( sprintf(
146
+ __( 'PHP Stricts (%s)', 'query-monitor' ),
147
+ number_format_i18n( count( $data['errors']['strict'] ) )
148
+ ) )
149
  ) );
150
  }
151
  if ( isset( $data['errors']['deprecated'] ) ) {
152
  $menu[] = $this->menu( array(
153
  'id' => 'query-monitor-deprecated',
154
+ 'title' => esc_html( sprintf(
155
+ __( 'PHP Deprecated (%s)', 'query-monitor' ),
156
+ number_format_i18n( count( $data['errors']['deprecated'] ) )
157
+ ) )
158
  ) );
159
  }
160
  return $menu;
output/html/request.php CHANGED
@@ -41,25 +41,25 @@ class QM_Output_Html_Request extends QM_Output_Html {
41
  }
42
 
43
  if ( ! empty( $data['request'][$item] ) ) {
44
- if ( in_array( $item, array( 'request', 'matched_query', 'query_string' ) ) ) {
45
  $value = self::format_url( $data['request'][$item] );
46
  } else {
47
  $value = esc_html( $data['request'][$item] );
48
  }
49
  } else {
50
- $value = '<em>' . __( 'none', 'query-monitor' ) . '</em>';
51
  }
52
 
53
  echo '<tr>';
54
- echo '<td valign="top">' . $name . '</td>';
55
- echo '<td valign="top" colspan="2">' . $value . '</td>';
56
  echo '</tr>';
57
  }
58
 
59
  $rowspan = isset( $data['qvars'] ) ? count( $data['qvars'] ) : 1;
60
 
61
  echo '<tr>';
62
- echo '<td rowspan="' . $rowspan . '">' . __( 'Query Vars', 'query-monitor' ) . '</td>';
63
 
64
  if ( !empty( $data['qvars'] ) ) {
65
 
@@ -72,18 +72,17 @@ class QM_Output_Html_Request extends QM_Output_Html {
72
  }
73
 
74
  if ( isset( $data['plugin_qvars'][$var] ) ) {
75
- echo "<td valign='top'><span class='qm-current'>{$var}</span></td>";
76
  } else {
77
- echo "<td valign='top'>{$var}</td>";
78
  }
79
 
80
  if ( is_array( $value ) or is_object( $value ) ) {
81
- echo '<td valign="top"><pre>';
82
- print_r( $value );
83
  echo '</pre></td>';
84
  } else {
85
- $value = esc_html( $value );
86
- echo "<td valign='top'>{$value}</td>";
87
  }
88
 
89
  echo '</tr>';
@@ -94,7 +93,28 @@ class QM_Output_Html_Request extends QM_Output_Html {
94
 
95
  } else {
96
 
97
- echo '<td colspan="2"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  echo '</tr>';
99
 
100
  }
@@ -104,7 +124,7 @@ class QM_Output_Html_Request extends QM_Output_Html {
104
  $rowspan = count( $data['multisite'] );
105
 
106
  echo '<tr>';
107
- echo '<td rowspan="' . $rowspan . '">' . __( 'Multisite', 'query-monitor' ) . '</td>';
108
 
109
  $first = true;
110
 
@@ -114,37 +134,24 @@ class QM_Output_Html_Request extends QM_Output_Html {
114
  echo '<tr>';
115
  }
116
 
117
- echo "<td valign='top'>{$var}</td>";
118
 
119
- echo '<td valign="top"><pre>';
120
- print_r( $value );
121
- echo '</pre></td>';
 
122
 
123
- echo '</tr>';
 
 
 
 
124
 
125
  $first = false;
126
 
127
  }
128
  }
129
 
130
- if ( !empty( $data['queried_object'] ) ) {
131
-
132
- $vars = get_object_vars( $data['queried_object'] );
133
-
134
- echo '<tr>';
135
- echo '<td valign="top">' . __( 'Queried Object', 'query-monitor' ) . '</td>';
136
- echo '<td valign="top" colspan="2" class="qm-has-inner">';
137
- echo '<div class="qm-inner-toggle">' . $data['queried_object_title'] . ' (' . get_class( $data['queried_object'] ) . ' object) (<a href="#" class="qm-toggle" data-on="' . esc_attr__( 'Show', 'query-monitor' ) . '" data-off="' . esc_attr__( 'Hide', 'query-monitor' ) . '">' . __( 'Show', 'query-monitor' ) . '</a>)</div>';
138
-
139
- echo '<div class="qm-toggled">';
140
- self::output_inner( $vars );
141
- echo '</div>';
142
-
143
- echo '</td>';
144
- echo '</tr>';
145
-
146
- }
147
-
148
  echo '</tbody>';
149
  echo '</table>';
150
  echo '</div>';
@@ -161,7 +168,10 @@ class QM_Output_Html_Request extends QM_Output_Html {
161
  : __( 'Request (+%s)', 'query-monitor' );
162
 
163
  $menu[] = $this->menu( array(
164
- 'title' => sprintf( $title, number_format_i18n( $count ) )
 
 
 
165
  ) );
166
  return $menu;
167
 
41
  }
42
 
43
  if ( ! empty( $data['request'][$item] ) ) {
44
+ if ( in_array( $item, array( 'request', 'matched_query', 'query_string' ), true ) ) {
45
  $value = self::format_url( $data['request'][$item] );
46
  } else {
47
  $value = esc_html( $data['request'][$item] );
48
  }
49
  } else {
50
+ $value = '<em>' . esc_html__( 'none', 'query-monitor' ) . '</em>';
51
  }
52
 
53
  echo '<tr>';
54
+ echo '<td>' . esc_html( $name ) . '</td>';
55
+ echo '<td colspan="2">' . $value . '</td>'; // WPCS: XSS ok.
56
  echo '</tr>';
57
  }
58
 
59
  $rowspan = isset( $data['qvars'] ) ? count( $data['qvars'] ) : 1;
60
 
61
  echo '<tr>';
62
+ echo '<td rowspan="' . absint( $rowspan ) . '">' . esc_html__( 'Query Vars', 'query-monitor' ) . '</td>';
63
 
64
  if ( !empty( $data['qvars'] ) ) {
65
 
72
  }
73
 
74
  if ( isset( $data['plugin_qvars'][$var] ) ) {
75
+ echo "<td><span class='qm-current'>" . esc_html( $var ) . "</span></td>";
76
  } else {
77
+ echo "<td>" . esc_html( $var ) . "</td>";
78
  }
79
 
80
  if ( is_array( $value ) or is_object( $value ) ) {
81
+ echo '<td><pre>';
82
+ echo esc_html( print_r( $value, true ) );
83
  echo '</pre></td>';
84
  } else {
85
+ echo "<td>" . esc_html( $value ) . "</td>";
 
86
  }
87
 
88
  echo '</tr>';
93
 
94
  } else {
95
 
96
+ echo '<td colspan="2"><em>' . esc_html__( 'none', 'query-monitor' ) . '</em></td>';
97
+ echo '</tr>';
98
+
99
+ }
100
+
101
+ if ( ! empty( $data['queried_object'] ) ) {
102
+
103
+ echo '<tr>';
104
+ echo '<td>' . esc_html__( 'Queried Object', 'query-monitor' ) . '</td>';
105
+ echo '<td colspan="2" class="qm-has-inner">';
106
+
107
+ printf(
108
+ '<div class="qm-inner-toggle">%1$s (%2$s) <a href="#" class="qm-toggle" data-on="+" data-off="-">+</a></div>',
109
+ esc_html( $data['queried_object']['title'] ),
110
+ esc_html( get_class( $data['queried_object']['data'] ) )
111
+ );
112
+
113
+ echo '<div class="qm-toggled">';
114
+ self::output_inner( $data['queried_object']['data'] );
115
+ echo '</div>';
116
+
117
+ echo '</td>';
118
  echo '</tr>';
119
 
120
  }
124
  $rowspan = count( $data['multisite'] );
125
 
126
  echo '<tr>';
127
+ echo '<td rowspan="' . absint( $rowspan ) . '">' . esc_html__( 'Multisite', 'query-monitor' ) . '</td>';
128
 
129
  $first = true;
130
 
134
  echo '<tr>';
135
  }
136
 
137
+ echo '<td colspan="2" class="qm-has-inner">';
138
 
139
+ printf(
140
+ '<div class="qm-inner-toggle">%1$s <a href="#" class="qm-toggle" data-on="+" data-off="-">+</a></div>',
141
+ esc_html( $value['title'] )
142
+ );
143
 
144
+ echo '<div class="qm-toggled">';
145
+ self::output_inner( $value['data'] );
146
+ echo '</div>';
147
+
148
+ echo '</td>';
149
 
150
  $first = false;
151
 
152
  }
153
  }
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  echo '</tbody>';
156
  echo '</table>';
157
  echo '</div>';
168
  : __( 'Request (+%s)', 'query-monitor' );
169
 
170
  $menu[] = $this->menu( array(
171
+ 'title' => esc_html( sprintf(
172
+ $title,
173
+ number_format_i18n( $count )
174
+ ) ),
175
  ) );
176
  return $menu;
177
 
output/html/theme.php CHANGED
@@ -35,27 +35,31 @@ class QM_Output_Html_Theme extends QM_Output_Html {
35
  echo '<table cellspacing="0">';
36
  echo '<tbody>';
37
 
38
- echo '<tr>';
39
- echo '<td>' . __( 'Template File', 'query-monitor' ) . '</td>';
40
- if ( $child_theme ) {
41
- echo '<td>' . self::output_filename( $data['theme_template'], $data['template_path'] ) . '</td>';
42
- } else {
43
- echo '<td>' . self::output_filename( $data['template_file'], $data['template_path'] ) . '</td>';
 
 
 
 
 
44
  }
45
- echo '</tr>';
46
 
47
  echo '<tr>';
48
  if ( $child_theme ) {
49
- echo '<td>' . __( 'Child Theme', 'query-monitor' ) . '</td>';
50
  } else {
51
- echo '<td>' . __( 'Theme', 'query-monitor' ) . '</td>';
52
  }
53
  echo '<td>' . esc_html( $data['stylesheet'] ) . '</td>';
54
  echo '</tr>';
55
 
56
  if ( $child_theme ) {
57
  echo '<tr>';
58
- echo '<td>' . __( 'Parent Theme', 'query-monitor' ) . '</td>';
59
  echo '<td>' . esc_html( $data['template'] ) . '</td>';
60
  echo '</tr>';
61
  }
@@ -63,7 +67,7 @@ class QM_Output_Html_Theme extends QM_Output_Html {
63
  if ( !empty( $data['body_class'] ) ) {
64
 
65
  echo '<tr>';
66
- echo '<td rowspan="' . count( $data['body_class'] ) . '">' . __( 'Body Classes', 'query-monitor' ) . '</td>';
67
  $first = true;
68
 
69
  foreach ( $data['body_class'] as $class ) {
@@ -93,7 +97,10 @@ class QM_Output_Html_Theme extends QM_Output_Html {
93
 
94
  if ( isset( $data['template_file'] ) ) {
95
  $menu[] = $this->menu( array(
96
- 'title' => sprintf( __( 'Template: %s', 'query-monitor' ), $data['template_file'] )
 
 
 
97
  ) );
98
  }
99
  return $menu;
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'], $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>';
56
  }
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>';
64
  echo '</tr>';
65
  }
67
  if ( !empty( $data['body_class'] ) ) {
68
 
69
  echo '<tr>';
70
+ echo '<td rowspan="' . count( $data['body_class'] ) . '">' . esc_html__( 'Body Classes', 'query-monitor' ) . '</td>';
71
  $first = true;
72
 
73
  foreach ( $data['body_class'] as $class ) {
97
 
98
  if ( isset( $data['template_file'] ) ) {
99
  $menu[] = $this->menu( array(
100
+ 'title' => esc_html( sprintf(
101
+ __( 'Template: %s', 'query-monitor' ),
102
+ $data['template_file']
103
+ ) ),
104
  ) );
105
  }
106
  return $menu;
output/html/transients.php CHANGED
@@ -29,15 +29,15 @@ class QM_Output_Html_Transients extends QM_Output_Html {
29
  echo '<table cellspacing="0">';
30
  echo '<thead>';
31
  echo '<tr>';
32
- echo '<th>' . __( 'Transient Set', 'query-monitor' ) . '</th>';
33
  if ( is_multisite() ) {
34
- echo '<th>' . __( 'Type', 'query-monitor' ) . '</th>';
35
  }
36
  if ( !empty( $data['trans'] ) and isset( $data['trans'][0]['expiration'] ) ) {
37
- echo '<th>' . __( 'Expiration', 'query-monitor' ) . '</th>';
38
  }
39
- echo '<th>' . __( 'Call Stack', 'query-monitor' ) . '</th>';
40
- echo '<th>' . __( 'Component', 'query-monitor' ) . '</th>';
41
  echo '</tr>';
42
  echo '</thead>';
43
 
@@ -46,29 +46,58 @@ class QM_Output_Html_Transients extends QM_Output_Html {
46
  echo '<tbody>';
47
 
48
  foreach ( $data['trans'] as $row ) {
49
- $stack = $row['trace']->get_stack();
50
  $transient = str_replace( array(
51
  '_site_transient_',
52
  '_transient_'
53
  ), '', $row['transient'] );
54
- $type = ( is_multisite() ) ? "<td valign='top'>{$row['type']}</td>\n" : '';
55
- if ( 0 === $row['expiration'] ) {
56
- $row['expiration'] = '<em>' . __( 'none', 'query-monitor' ) . '</em>';
57
- }
58
- $expiration = ( isset( $row['expiration'] ) ) ? "<td valign='top'>{$row['expiration']}</td>\n" : '';
59
 
60
  $component = $row['trace']->get_component();
61
 
62
- $stack = implode( '<br>', $stack );
63
- echo "
64
- <tr>\n
65
- <td valign='top'>{$transient}</td>\n
66
- {$type}
67
- {$expiration}
68
- <td valign='top' class='qm-nowrap qm-ltr'>{$stack}</td>\n
69
- <td valign='top' class='qm-nowrap'>{$component->name}</td>\n
70
- </tr>\n
71
- ";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
 
74
  echo '</tbody>';
@@ -77,7 +106,7 @@ class QM_Output_Html_Transients extends QM_Output_Html {
77
 
78
  echo '<tbody>';
79
  echo '<tr>';
80
- echo '<td colspan="4" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
81
  echo '</tr>';
82
  echo '</tbody>';
83
 
@@ -98,7 +127,10 @@ class QM_Output_Html_Transients extends QM_Output_Html {
98
  : __( 'Transients Set (%s)', 'query-monitor' );
99
 
100
  $menu[] = $this->menu( array(
101
- 'title' => sprintf( $title, number_format_i18n( $count ) )
 
 
 
102
  ) );
103
  return $menu;
104
 
29
  echo '<table cellspacing="0">';
30
  echo '<thead>';
31
  echo '<tr>';
32
+ echo '<th>' . esc_html__( 'Transient Set', 'query-monitor' ) . '</th>';
33
  if ( is_multisite() ) {
34
+ echo '<th>' . esc_html__( 'Type', 'query-monitor' ) . '</th>';
35
  }
36
  if ( !empty( $data['trans'] ) and isset( $data['trans'][0]['expiration'] ) ) {
37
+ echo '<th>' . esc_html__( 'Expiration', 'query-monitor' ) . '</th>';
38
  }
39
+ echo '<th>' . esc_html__( 'Call Stack', 'query-monitor' ) . '</th>';
40
+ echo '<th>' . esc_html__( 'Component', 'query-monitor' ) . '</th>';
41
  echo '</tr>';
42
  echo '</thead>';
43
 
46
  echo '<tbody>';
47
 
48
  foreach ( $data['trans'] as $row ) {
 
49
  $transient = str_replace( array(
50
  '_site_transient_',
51
  '_transient_'
52
  ), '', $row['transient'] );
 
 
 
 
 
53
 
54
  $component = $row['trace']->get_component();
55
 
56
+ echo '<tr>';
57
+ printf(
58
+ '<td>%s</td>',
59
+ esc_html( $transient )
60
+ );
61
+ if ( is_multisite() ) {
62
+ printf(
63
+ '<td>%s</td>',
64
+ esc_html( $row['type'] )
65
+ );
66
+ }
67
+
68
+ if ( isset( $row['expiration'] ) ) {
69
+ if ( 0 === $row['expiration'] ) {
70
+ printf(
71
+ '<td><em>%s</em></td>',
72
+ esc_html__( 'none', 'query-monitor' )
73
+ );
74
+ } else {
75
+ printf(
76
+ '<td>%s</td>',
77
+ esc_html( $row['expiration'] )
78
+ );
79
+ }
80
+ }
81
+
82
+ $stack = array();
83
+ $filtered_trace = $row['trace']->get_filtered_trace();
84
+ array_shift( $filtered_trace );
85
+
86
+ foreach ( $filtered_trace as $item ) {
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 ) // WPCS: XSS ok.
93
+ );
94
+ printf(
95
+ '<td class="qm-nowrap">%s</td>',
96
+ esc_html( $component->name )
97
+ );
98
+
99
+ echo '</tr>';
100
+
101
  }
102
 
103
  echo '</tbody>';
106
 
107
  echo '<tbody>';
108
  echo '<tr>';
109
+ echo '<td colspan="4" style="text-align:center !important"><em>' . esc_html__( 'none', 'query-monitor' ) . '</em></td>';
110
  echo '</tr>';
111
  echo '</tbody>';
112
 
127
  : __( 'Transients Set (%s)', 'query-monitor' );
128
 
129
  $menu[] = $this->menu( array(
130
+ 'title' => esc_html( sprintf(
131
+ $title,
132
+ number_format_i18n( $count )
133
+ ) ),
134
  ) );
135
  return $menu;
136
 
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.7.4
6
  Plugin URI: https://querymonitor.com/
7
  Author: John Blackbourn
8
  Author URI: https://johnblackbourn.com/
@@ -30,9 +30,9 @@ if ( defined( 'QM_DISABLED' ) and QM_DISABLED ) {
30
  return;
31
  }
32
 
33
- if ( defined( 'WP_CLI' ) and WP_CLI ) {
34
- # For the time being, let's not load QM when using WP-CLI because we've no persistent storage and no means of
35
- # outputting collected data on the CLI. This will change in a future version of QM.
36
  return;
37
  }
38
 
@@ -49,7 +49,6 @@ class QueryMonitor extends QM_Plugin {
49
  # Actions
50
  add_action( 'plugins_loaded', array( $this, 'action_plugins_loaded' ) );
51
  add_action( 'init', array( $this, 'action_init' ) );
52
- add_action( 'shutdown', array( $this, 'action_shutdown' ), 0 );
53
 
54
  # Filters
55
  add_filter( 'pre_update_option_active_plugins', array( $this, 'filter_active_plugins' ) );
@@ -63,7 +62,9 @@ class QueryMonitor extends QM_Plugin {
63
  parent::__construct( $file );
64
 
65
  # Load and register built-in collectors:
66
- QM_Util::include_files( $this->plugin_path( 'collectors' ) );
 
 
67
 
68
  }
69
 
@@ -74,8 +75,10 @@ class QueryMonitor extends QM_Plugin {
74
  QM_Collectors::add( $collector );
75
  }
76
 
77
- # Dispatchers:
78
- QM_Util::include_files( $this->plugin_path( 'dispatchers' ) );
 
 
79
 
80
  # Register built-in and additional dispatchers:
81
  foreach ( apply_filters( 'qm/dispatchers', array(), $this ) as $dispatcher ) {
@@ -115,100 +118,10 @@ class QueryMonitor extends QM_Plugin {
115
 
116
  }
117
 
118
- public function should_process() {
119
-
120
- # @TODO this decision should be moved to each dispatcher
121
-
122
- # Don't process if the minimum required actions haven't fired:
123
-
124
- if ( is_admin() ) {
125
-
126
- if ( ! did_action( 'admin_init' ) ) {
127
- return false;
128
- }
129
-
130
- } else {
131
-
132
- if ( ! ( did_action( 'wp' ) or did_action( 'login_init' ) ) ) {
133
- return false;
134
- }
135
-
136
- }
137
-
138
- $e = error_get_last();
139
-
140
- # Don't process if a fatal has occurred:
141
- if ( ! empty( $e ) and ( $e['type'] & ( E_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR ) ) ) {
142
- return false;
143
- }
144
-
145
- # Allow users to disable the processing and output
146
- if ( ! apply_filters( 'qm/process', true, is_admin_bar_showing() ) ) {
147
- return false;
148
- }
149
-
150
- $dispatchers = QM_Dispatchers::init();
151
-
152
- foreach ( $dispatchers as $dispatcher ) {
153
-
154
- # At least one dispatcher is active, so we need to process:
155
- if ( $dispatcher->is_active() ) {
156
- return true;
157
- }
158
-
159
- }
160
-
161
- return false;
162
-
163
- }
164
-
165
- public function action_shutdown() {
166
-
167
- # @TODO this should move to each dispatcher so it can decide when it wants to do its output
168
- # eg. the JSON dispatcher needs to output inside the 'json_post_dispatch' filter, not on shutdown
169
-
170
- if ( ! $this->should_process() ) {
171
- return;
172
- }
173
-
174
- $collectors = QM_Collectors::init();
175
- $dispatchers = QM_Dispatchers::init();
176
-
177
- foreach ( $collectors as $collector ) {
178
- $collector->tear_down();
179
- $collector->process();
180
- }
181
-
182
- foreach ( $dispatchers as $dispatcher ) {
183
-
184
- if ( ! $dispatcher->is_active() ) {
185
- continue;
186
- }
187
-
188
- $dispatcher->before_output();
189
-
190
- $outputters = apply_filters( "qm/outputter/{$dispatcher->id}", array(), $collectors );
191
-
192
- foreach ( $outputters as $outputter ) {
193
- $outputter->output();
194
- }
195
-
196
- $dispatcher->after_output();
197
-
198
- }
199
-
200
- }
201
-
202
  public function action_init() {
203
 
204
  load_plugin_textdomain( 'query-monitor', false, dirname( $this->plugin_base() ) . '/languages' );
205
 
206
- $dispatchers = QM_Dispatchers::init();
207
-
208
- foreach ( $dispatchers as $dispatcher ) {
209
- $dispatcher->init();
210
- }
211
-
212
  }
213
 
214
  public function filter_active_plugins( $plugins ) {
@@ -250,7 +163,10 @@ class QueryMonitor extends QM_Plugin {
250
 
251
  public static function symlink_warning() {
252
  $db = WP_CONTENT_DIR . '/db.php';
253
- trigger_error( sprintf( __( 'The symlink at %s is no longer pointing to the correct location. Please remove the symlink, then deactivate and reactivate Query Monitor.', 'query-monitor' ), "<code>{$db}</code>" ), E_USER_WARNING );
 
 
 
254
  }
255
 
256
  public static function init( $file = null ) {
2
  /*
3
  Plugin Name: Query Monitor
4
  Description: Monitoring of database queries, hooks, conditionals and more.
5
+ Version: 2.8.0
6
  Plugin URI: https://querymonitor.com/
7
  Author: John Blackbourn
8
  Author URI: https://johnblackbourn.com/
30
  return;
31
  }
32
 
33
+ if ( 'cli' === php_sapi_name() && ! defined( 'QM_TESTS' ) ) {
34
+ # For the time being, let's not load QM when using the CLI because we've no persistent storage and no means of
35
+ # outputting collected data on the CLI. This will hopefully change in a future version of QM.
36
  return;
37
  }
38
 
49
  # Actions
50
  add_action( 'plugins_loaded', array( $this, 'action_plugins_loaded' ) );
51
  add_action( 'init', array( $this, 'action_init' ) );
 
52
 
53
  # Filters
54
  add_filter( 'pre_update_option_active_plugins', array( $this, 'filter_active_plugins' ) );
62
  parent::__construct( $file );
63
 
64
  # Load and register built-in collectors:
65
+ foreach ( glob( $this->plugin_path( 'collectors/*.php' ) ) as $file ) {
66
+ include $file;
67
+ }
68
 
69
  }
70
 
75
  QM_Collectors::add( $collector );
76
  }
77
 
78
+ # Load dispatchers:
79
+ foreach ( glob( $this->plugin_path( 'dispatchers/*.php' ) ) as $file ) {
80
+ include $file;
81
+ }
82
 
83
  # Register built-in and additional dispatchers:
84
  foreach ( apply_filters( 'qm/dispatchers', array(), $this ) as $dispatcher ) {
118
 
119
  }
120
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  public function action_init() {
122
 
123
  load_plugin_textdomain( 'query-monitor', false, dirname( $this->plugin_base() ) . '/languages' );
124
 
 
 
 
 
 
 
125
  }
126
 
127
  public function filter_active_plugins( $plugins ) {
163
 
164
  public static function symlink_warning() {
165
  $db = WP_CONTENT_DIR . '/db.php';
166
+ trigger_error( sprintf(
167
+ esc_html__( 'The symlink at %s is no longer pointing to the correct location. Please remove the symlink, then deactivate and reactivate Query Monitor.', 'query-monitor' ),
168
+ '<code>' . esc_html( $db ) . '</code>'
169
+ ), E_USER_WARNING );
170
  }
171
 
172
  public static function init( $file = null ) {
readme.txt CHANGED
@@ -1,16 +1,16 @@
1
  === Query Monitor ===
2
  Contributors: johnbillion
3
- Tags: debug, debug-bar, debugging, development, developer, performance, profiler, profiling, queries, query monitor
4
- Requires at least: 3.5
5
  Tested up to: 4.3
6
- Stable tag: 2.7.4
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 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
 
@@ -25,7 +25,7 @@ Here's an overview of what's shown:
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
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
 
@@ -39,7 +39,7 @@ Filtering queries by component or calling function makes it easy to see which pl
39
 
40
  * Shows the **template filename** for the current page
41
  * Shows the available **body classes** for the current page
42
- * Shows the active parent and child theme name
43
 
44
  = PHP Errors =
45
 
@@ -50,7 +50,7 @@ Filtering queries by component or calling function makes it easy to see which pl
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 (collapsed by default)
54
  * Shows details of the **current blog** (multisite only) and **current site** (multi-network only)
55
 
56
  = Scripts & Styles =
@@ -58,25 +58,34 @@ Filtering queries by component or calling function makes it easy to see which pl
58
  * Shows all **enqueued scripts and styles** on the current page, along with their URL and version
59
  * Shows their **dependencies and dependents**, and alerts you to any **broken dependencies**
60
 
 
 
 
 
 
61
  = HTTP Requests =
62
 
63
  * Shows all HTTP requests performed on the current page (as long as they use WordPress' HTTP API)
64
- * Shows the response code, call stack, transport, timeout, and time taken
65
  * Highlights **erroneous responses**, such as failed requests and anything without a `200` response code
66
 
67
  = Redirects =
68
 
69
- * Whenever a redirect occurs, Query Monitor adds an `X-QM-Redirect` HTTP header containing the call stack, so you can use your favourite HTTP inspector to easily trace where a redirect has come from
70
 
71
  = AJAX =
72
 
73
- The response from any jQuery AJAX request on the page will contain various debugging information in its header that gets output to the developer console. **No hooking required**.
74
 
75
- AJAX debugging is in its early stages. Currently it only includes PHP errors, but this will be built upon in future versions.
76
 
77
- = Admin Screen =
 
 
 
 
78
 
79
- Hands up who can remember the correct names for the filters and actions for custom admin screen columns?
80
 
81
  * Shows the correct names for **custom column filters and actions** on all admin screens that have a listing table
82
  * Shows the state of `get_current_screen()` and a few variables
@@ -94,8 +103,7 @@ Hands up who can remember the correct names for the filters and actions for cust
94
 
95
  * Shows any **transients that were set**, along with their timeout, component, and call stack
96
  * Shows all **WordPress conditionals** on the current page, highlighted nicely
97
- * Shows an overview including page generation time and memory limit as absolute values and as % of their respective limits
98
- * Shows all *scripts and styles* which were enqueued on the current page, along with their URL, dependencies, dependents, and version number
99
 
100
  = Authentication =
101
 
@@ -105,14 +113,9 @@ In addition to this, you can set an authentication cookie which allows you to vi
105
 
106
  == Installation ==
107
 
108
- You can install this plugin directly from your WordPress dashboard:
109
-
110
- 1. Go to the *Plugins* menu and click *Add New*.
111
- 2. Search for *Query Monitor*.
112
- 3. Click *Install Now* next to the Query Monitor plugin.
113
- 4. Activate the plugin.
114
 
115
- Alternatively, see the guide to [Manually Installing Plugins](https://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation).
116
 
117
  == Screenshots ==
118
 
@@ -150,12 +153,29 @@ In addition, Query Monitor transparently supports add-ons for the Debug Bar plug
150
 
151
  Please use [the issue tracker on Query Monitor's GitHub repo](https://github.com/johnbillion/query-monitor/issues) as it's easier to keep track of issues there, rather than on the wordpress.org support forums.
152
 
 
 
 
 
153
  = Do you accept donations? =
154
 
155
  No, I do not accept donations. If you like the plugin, I'd love for you to [leave a review](https://wordpress.org/support/view/plugin-reviews/query-monitor). Tell all your friends about the plugin too!
156
 
157
  == Changelog ==
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  = 2.7.4 =
160
  * An unknown component now gets marked as such, not as Core.
161
  * Support for invokable objects in action and filter callbacks.
1
  === Query Monitor ===
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.3
6
+ Stable tag: 2.8.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
 
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
 
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 =
45
 
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
  = Scripts & Styles =
58
  * Shows all **enqueued scripts and styles** on the current page, along with their URL and version
59
  * Shows their **dependencies and dependents**, and alerts you to any **broken dependencies**
60
 
61
+ = Languages =
62
+
63
+ * Shows you **language settings** and text domains
64
+ * Shows you the **MO files** for each text domain and which ones were loaded or not
65
+
66
  = HTTP Requests =
67
 
68
  * Shows all HTTP requests performed on the current page (as long as they use WordPress' HTTP API)
69
+ * Shows the response code, call stack, transport, component, timeout, and time taken
70
  * Highlights **erroneous responses**, such as failed requests and anything without a `200` response code
71
 
72
  = Redirects =
73
 
74
+ * Whenever a redirect occurs, Query Monitor adds an `X-QM-Redirect` HTTP header containing the call stack, so you can use your favourite HTTP inspector or browser developer tools to easily trace where a redirect has come from
75
 
76
  = AJAX =
77
 
78
+ 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**.
79
 
80
+ Currently this includes PHP errors and some overview information such as memory usage, but this will be built upon in future versions.
81
 
82
+ = REST API =
83
+
84
+ The response from an authenticated WordPress REST API (v2 or later) request will contain various debugging information in its headers, as long as the authenticated user has permission to view Query Monitor's output.
85
+
86
+ Currently this includes PHP errors and some overview information such as memory usage, but this will be built upon in future versions.
87
 
88
+ = Admin Screen =
89
 
90
  * Shows the correct names for **custom column filters and actions** on all admin screens that have a listing table
91
  * Shows the state of `get_current_screen()` and a few variables
103
 
104
  * Shows any **transients that were set**, along with their timeout, component, and call stack
105
  * Shows all **WordPress conditionals** on the current page, highlighted nicely
106
+ * Shows an overview at the top, including page generation time and memory limit as absolute values and as % of their respective limits
 
107
 
108
  = Authentication =
109
 
113
 
114
  == Installation ==
115
 
116
+ Install Query Monitor just like any other plugin.
 
 
 
 
 
117
 
118
+ If you don't know how to do that, then Query Monitor is not for you.
119
 
120
  == Screenshots ==
121
 
153
 
154
  Please use [the issue tracker on Query Monitor's GitHub repo](https://github.com/johnbillion/query-monitor/issues) as it's easier to keep track of issues there, rather than on the wordpress.org support forums.
155
 
156
+ = I'm using multiple instances of `wpdb`. How do I get my additional instances to show up in Query Monitor? =
157
+
158
+ You'll need to hook into the `qm/collect/db_objects` filter and add an item to the array with your connection name as the key and the `wpdb` instance as the value. Your `wpdb` instance will then show up as a separate panel, and the query time and query count will show up separately in the admin toolbar menu. Aggregate information (queries by caller and component) will not be separated.
159
+
160
  = Do you accept donations? =
161
 
162
  No, I do not accept donations. If you like the plugin, I'd love for you to [leave a review](https://wordpress.org/support/view/plugin-reviews/query-monitor). Tell all your friends about the plugin too!
163
 
164
  == Changelog ==
165
 
166
+ = 2.8.0 =
167
+ * A new Languages component for debugging languages and text domains. Thanks, @MPolleke!
168
+ * REST API debugging in the form of HTTP headers when performing an authenticated REST API request. Shows PHP errors when relevant, along with an overview of memory usage, processing time, database query number, and database query time.
169
+ * Various visual improvements, including displaying the relevant file name below stack trace functions, and a more visible stack trace expansion toggle.
170
+ * Add `is_embed()`, `is_comment_feed()`, and `is_user_admin()` to the list of conditional functions.
171
+ * Add HHVM, SAPI, and MySQL client info to the Environment component.
172
+ * QM is now not loaded at all on the CLI.
173
+ * Avoid an issue with the CloudFlare Flexible SSL plugin.
174
+ * Improve the output of Multisite's `$current_blog` and `$current_site` in the Request component.
175
+ * Fully handle Windows paths when detecting a file component.
176
+ * Don't display the symlink warning when using a secondary instance of WPDB.
177
+ * Whole bunch of internal structure refactoring, escaping, and other misc tweaks.
178
+
179
  = 2.7.4 =
180
  * An unknown component now gets marked as such, not as Core.
181
  * Support for invokable objects in action and filter callbacks.
wp-content/db.php CHANGED
@@ -29,9 +29,9 @@ if ( defined( 'QM_DISABLED' ) and QM_DISABLED ) {
29
  return;
30
  }
31
 
32
- if ( defined( 'WP_CLI' ) and WP_CLI ) {
33
- # For the time being, let's not load QM when using WP-CLI because we've no persistent storage and no means of
34
- # outputting collected data on the CLI. This will change in a future version of QM.
35
  return;
36
  }
37
 
29
  return;
30
  }
31
 
32
+ if ( 'cli' === php_sapi_name() && ! defined( 'QM_TESTS' ) ) {
33
+ # For the time being, let's not load QM when using the CLI because we've no persistent storage and no means of
34
+ # outputting collected data on the CLI. This will hopefully change in a future version of QM.
35
  return;
36
  }
37