Query Monitor - Version 2.5.2

Version Description

  • Prevent uncaught exceptions with static method actions
  • Misc formatting tweaks
Download this release

Release Info

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

Version 2.5.2

Backtrace.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Backtrace {
18
+
19
+ protected static $ignore_class = array(
20
+ 'wpdb' => true,
21
+ 'QueryMonitor' => true,
22
+ 'QueryMonitorDB' => true,
23
+ 'ExtQuery' => true,
24
+ 'W3_Db' => true,
25
+ 'Debug_Bar_PHP' => true,
26
+ );
27
+ protected static $ignore_method = array();
28
+ protected static $ignore_func = array(
29
+ 'include_once' => true,
30
+ 'require_once' => true,
31
+ 'include' => true,
32
+ 'require' => true,
33
+ 'call_user_func_array' => true,
34
+ 'call_user_func' => true,
35
+ 'trigger_error' => true,
36
+ '_doing_it_wrong' => true,
37
+ '_deprecated_argument' => true,
38
+ '_deprecated_file' => true,
39
+ '_deprecated_function' => true,
40
+ );
41
+ protected static $show_args = array(
42
+ 'do_action' => 1,
43
+ 'apply_filters' => 1,
44
+ 'do_action_ref_array' => 1,
45
+ 'apply_filters_ref_array' => 1,
46
+ 'get_template_part' => 2,
47
+ 'section_template' => 2,
48
+ 'load_template' => 'dir',
49
+ 'get_header' => 1,
50
+ 'get_sidebar' => 1,
51
+ 'get_footer' => 1,
52
+ );
53
+ protected static $filtered = false;
54
+
55
+ public function __construct( array $args = array() ) {
56
+ $args = array_merge( array(
57
+ 'ignore_current_filter' => true,
58
+ 'ignore_items' => 0,
59
+ ), $args );
60
+ $this->trace = debug_backtrace( false );
61
+ $this->ignore( 1 ); # Self-awareness
62
+
63
+ if ( $args['ignore_items'] )
64
+ $this->ignore( $args['ignore_items'] );
65
+ if ( $args['ignore_current_filter'] )
66
+ $this->ignore_current_filter();
67
+
68
+ }
69
+
70
+ public function get_stack() {
71
+ $trace = array_map( 'QM_Backtrace::filter_trace', $this->trace );
72
+ $trace = array_values( array_filter( $trace ) );
73
+ return $trace;
74
+ }
75
+
76
+ public function get_trace() {
77
+ return $this->trace;
78
+ }
79
+
80
+ public function ignore( $num ) {
81
+ for ( $i = 0; $i < absint( $num ); $i++ )
82
+ unset( $this->trace[$i] );
83
+ $this->trace = array_values( $this->trace );
84
+ return $this;
85
+ }
86
+
87
+ public function ignore_current_filter() {
88
+
89
+ if ( isset( $this->trace[2] ) and isset( $this->trace[2]['function'] ) ) {
90
+ if ( in_array( $this->trace[2]['function'], array( 'apply_filters', 'do_action' ) ) )
91
+ $this->ignore( 3 ); # Ignore filter and action callbacks
92
+ }
93
+
94
+ }
95
+
96
+ public static function filter_trace( array $trace ) {
97
+
98
+ if ( !self::$filtered and function_exists( 'did_action' ) and did_action( 'plugins_loaded' ) ) {
99
+
100
+ # Only run apply_filters on these once
101
+ self::$ignore_class = apply_filters( 'query_monitor_ignore_class', self::$ignore_class );
102
+ self::$ignore_method = apply_filters( 'query_monitor_ignore_method', self::$ignore_method );
103
+ self::$ignore_func = apply_filters( 'query_monitor_ignore_func', self::$ignore_func );
104
+ self::$show_args = apply_filters( 'query_monitor_show_args', self::$show_args );
105
+ self::$filtered = true;
106
+
107
+ }
108
+
109
+ if ( isset( $trace['class'] ) ) {
110
+
111
+ if ( isset( self::$ignore_class[$trace['class']] ) )
112
+ return null;
113
+ else if ( isset( self::$ignore_method[$trace['class']][$trace['function']] ) )
114
+ return null;
115
+ else if ( 0 === strpos( $trace['class'], 'QM_' ) )
116
+ return null;
117
+ else
118
+ return $trace['class'] . $trace['type'] . $trace['function'] . '()';
119
+
120
+ } else {
121
+
122
+ if ( isset( self::$ignore_func[$trace['function']] ) ) {
123
+
124
+ return null;
125
+
126
+ } else if ( isset( self::$show_args[$trace['function']] ) ) {
127
+
128
+ $show = self::$show_args[$trace['function']];
129
+ if ( 'dir' === $show ) {
130
+ if ( isset( $trace['args'][0] ) ) {
131
+ $arg = QM_Util::standard_dir( $trace['args'][0], '&hellip;/' );
132
+ return $trace['function'] . "('{$arg}')";
133
+ }
134
+ } else {
135
+ $args = array();
136
+ for ( $i = 0; $i < $show; $i++ ) {
137
+ if ( isset( $trace['args'][$i] ) )
138
+ $args[] = sprintf( "'%s'", $trace['args'][$i] );
139
+ }
140
+ return $trace['function'] . '(' . implode( ',', $args ) . ')';
141
+ }
142
+
143
+ }
144
+
145
+ return $trace['function'] . '()';
146
+
147
+ }
148
+
149
+ }
150
+
151
+ }
Component.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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
+ abstract class QM_Component {
18
+
19
+ protected $data = array();
20
+
21
+ protected function __construct() {}
22
+
23
+ final public function id() {
24
+ return "qm-{$this->id}";
25
+ }
26
+
27
+ final protected function menu( array $args ) {
28
+
29
+ return wp_parse_args( $args, array(
30
+ 'id' => "query-monitor-{$this->id}",
31
+ 'href' => '#' . $this->id()
32
+ ) );
33
+
34
+ }
35
+
36
+ final protected function get_component( $id ) {
37
+ # @TODO use singleton?
38
+ global $querymonitor;
39
+ return $querymonitor->get_component( $id );
40
+ }
41
+
42
+ protected function build_filter( $name, array $values ) {
43
+
44
+ usort( $values, 'strcasecmp' );
45
+
46
+ $out = '<select id="qm-filter-' . esc_attr( $this->id . '-' . $name ) . '" class="qm-filter" data-filter="' . esc_attr( $this->id . '-' . $name ) . '">';
47
+ $out .= '<option value="">' . _x( 'All', '"All" option for filters', 'query-monitor' ) . '</option>';
48
+
49
+ foreach ( $values as $value )
50
+ $out .= '<option value="' . esc_attr( $value ) . '">' . esc_html( $value ) . '</option>';
51
+
52
+ $out .= '</select>';
53
+
54
+ return $out;
55
+
56
+ }
57
+
58
+ final public function get_data() {
59
+ return $this->data;
60
+ }
61
+
62
+ public function process() {}
63
+
64
+ public function output_html( array $args, array $data ) {}
65
+
66
+ public function output_headers( array $args, array $data ) {}
67
+
68
+ }
Plugin.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Plugin {
18
+
19
+ /**
20
+ * Class constructor
21
+ *
22
+ * @author John Blackbourn
23
+ **/
24
+ public function __construct( $file ) {
25
+ $this->file = $file;
26
+ }
27
+
28
+ /**
29
+ * Returns the URL for for a file/dir within this plugin.
30
+ *
31
+ * @param $file string The path within this plugin, e.g. '/js/clever-fx.js'
32
+ * @return string URL
33
+ * @author John Blackbourn
34
+ **/
35
+ final public function plugin_url( $file = '' ) {
36
+ return $this->_plugin( 'url', $file );
37
+ }
38
+
39
+ /**
40
+ * Returns the filesystem path for a file/dir within this plugin.
41
+ *
42
+ * @param $file string The path within this plugin, e.g. '/js/clever-fx.js'
43
+ * @return string Filesystem path
44
+ * @author John Blackbourn
45
+ **/
46
+ final public function plugin_path( $file = '' ) {
47
+ return $this->_plugin( 'path', $file );
48
+ }
49
+
50
+ /**
51
+ * Returns a version number for the given plugin file.
52
+ *
53
+ * @param $file string The path within this plugin, e.g. '/js/clever-fx.js'
54
+ * @return string Version
55
+ * @author John Blackbourn
56
+ **/
57
+ final public function plugin_ver( $file ) {
58
+ return filemtime( $this->plugin_path( $file ) );
59
+ }
60
+
61
+ /**
62
+ * Returns the current plugin's basename, eg. 'my_plugin/my_plugin.php'.
63
+ *
64
+ * @return string Basename
65
+ * @author John Blackbourn
66
+ **/
67
+ final public function plugin_base() {
68
+ return $this->_plugin( 'base' );
69
+ }
70
+
71
+ /**
72
+ * Populates and returns the current plugin info.
73
+ *
74
+ * @author John Blackbourn
75
+ **/
76
+ final protected function _plugin( $item, $file = '' ) {
77
+ if ( !isset( $this->plugin ) ) {
78
+ $this->plugin = array(
79
+ 'url' => plugin_dir_url( $this->file ),
80
+ 'path' => plugin_dir_path( $this->file ),
81
+ 'base' => plugin_basename( $this->file )
82
+ );
83
+ }
84
+ return $this->plugin[$item] . ltrim( $file, '/' );
85
+ }
86
+
87
+ }
Util.php ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+
4
+ © 2013 John Blackbourn
5
+
6
+ This program is free software; you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation; either version 2 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ */
17
+
18
+ class QM_Util {
19
+
20
+ protected static $file_components = array();
21
+ protected static $file_dirs = array();
22
+
23
+ private function __construct() {}
24
+
25
+ public static function timer_stop_float() {
26
+ global $timestart;
27
+ return microtime( true ) - $timestart;
28
+ }
29
+
30
+ public static function sort( $a, $b ) {
31
+ if ( $a['ltime'] == $b['ltime'] )
32
+ return 0;
33
+ else
34
+ return ( $a['ltime'] > $b['ltime'] ) ? -1 : 1;
35
+ }
36
+
37
+ public static function convert_hr_to_bytes( $size ) {
38
+
39
+ # Annoyingly, wp_convert_hr_to_bytes() is defined in a file that's only
40
+ # loaded in the admin area, so we'll use our own version.
41
+ # See also http://core.trac.wordpress.org/ticket/17725
42
+
43
+ $bytes = (float) $size;
44
+
45
+ if ( $bytes ) {
46
+ $last = strtolower( substr( $size, -1 ) );
47
+ $pos = strpos( ' kmg', $last, 1);
48
+ if ( $pos )
49
+ $bytes *= pow( 1024, $pos );
50
+ $bytes = round( $bytes );
51
+ }
52
+
53
+ return $bytes;
54
+
55
+ }
56
+
57
+ public static function standard_dir( $dir, $abspath_replace = null ) {
58
+
59
+ $dir = str_replace( '\\', '/', $dir );
60
+ $dir = preg_replace( '|/+|', '/', $dir );
61
+
62
+ if ( is_string( $abspath_replace ) )
63
+ $dir = str_replace( self::standard_dir( ABSPATH ), $abspath_replace, $dir );
64
+
65
+ return $dir;
66
+
67
+ }
68
+
69
+ public static function get_file_component( $file ) {
70
+
71
+ # @TODO turn this into a class (eg QM_File_Component)
72
+
73
+ if ( isset( self::$file_components[$file] ) )
74
+ return self::$file_components[$file];
75
+
76
+ if ( empty( self::$file_dirs ) ) {
77
+ self::$file_dirs['plugin'] = self::standard_dir( WP_PLUGIN_DIR );
78
+ self::$file_dirs['muplugin'] = self::standard_dir( WPMU_PLUGIN_DIR );
79
+ self::$file_dirs['stylesheet'] = self::standard_dir( get_stylesheet_directory() );
80
+ self::$file_dirs['template'] = self::standard_dir( get_template_directory() );
81
+ self::$file_dirs['other'] = self::standard_dir( WP_CONTENT_DIR );
82
+ self::$file_dirs['core'] = self::standard_dir( ABSPATH );
83
+ }
84
+
85
+ foreach ( self::$file_dirs as $type => $dir ) {
86
+ if ( 0 === strpos( $file, $dir ) )
87
+ break;
88
+ }
89
+
90
+ switch ( $type ) {
91
+ case 'plugin':
92
+ case 'muplugin':
93
+ $plug = plugin_basename( $file );
94
+ if ( strpos( $plug, '/' ) ) {
95
+ $plug = explode( '/', $plug );
96
+ $plug = reset( $plug );
97
+ } else {
98
+ $plug = basename( $plug );
99
+ }
100
+ $name = sprintf( __( 'Plugin: %s', 'query-monitor' ), $plug );
101
+ break;
102
+ case 'stylesheet':
103
+ $name = __( 'Theme', 'query-monitor' );
104
+ break;
105
+ case 'template':
106
+ $name = __( 'Parent Theme', 'query-monitor' );
107
+ break;
108
+ case 'other':
109
+ $name = self::standard_dir( $file, '' );
110
+ break;
111
+ case 'core':
112
+ default:
113
+ $name = __( 'Core', 'query-monitor' );
114
+ break;
115
+ }
116
+
117
+ return self::$file_components[$file] = (object) compact( 'type', 'name' );
118
+
119
+ }
120
+
121
+ public static function get_backtrace_component( QM_Backtrace $backtrace ) {
122
+
123
+ # @TODO turn this into a class (eg QM_Trace_Component)
124
+
125
+ $components = array();
126
+
127
+ foreach ( $backtrace->get_trace() as $item ) {
128
+
129
+ try {
130
+
131
+ if ( isset( $item['file'] ) ) {
132
+ $file = $item['file'];
133
+ } else if ( isset( $item['class'] ) ) {
134
+ $ref = new ReflectionMethod( $item['class'], $item['function'] );
135
+ $file = $ref->getFileName();
136
+ } else {
137
+ $ref = new ReflectionFunction( $item['function'] );
138
+ $file = $ref->getFileName();
139
+ }
140
+
141
+ $comp = self::get_file_component( $file );
142
+ $components[$comp->type] = $comp;
143
+ } catch ( ReflectionException $e ) {
144
+ # nothing
145
+ }
146
+
147
+ }
148
+
149
+ foreach ( self::$file_dirs as $type => $dir ) {
150
+ if ( isset( $components[$type] ) )
151
+ return $components[$type];
152
+ }
153
+
154
+ # This should not happen
155
+
156
+ }
157
+
158
+ public static function populate_callback( array $callback ) {
159
+
160
+ $access = '->';
161
+
162
+ if ( is_string( $callback['function'] ) and ( false !== strpos( $callback['function'], '::' ) ) ) {
163
+ $callback['function'] = explode( '::', $callback['function'] );
164
+ $access = '::';
165
+ }
166
+
167
+ try {
168
+
169
+ if ( is_array( $callback['function'] ) ) {
170
+
171
+ if ( is_object( $callback['function'][0] ) )
172
+ $class = get_class( $callback['function'][0] );
173
+ else
174
+ $class = $callback['function'][0];
175
+
176
+ $callback['name'] = $class . $access . $callback['function'][1] . '()';
177
+ $ref = new ReflectionMethod( $class, $callback['function'][1] );
178
+
179
+ } else if ( is_object( $callback['function'] ) and is_a( $callback['function'], 'Closure' ) ) {
180
+
181
+ $ref = new ReflectionFunction( $callback['function'] );
182
+ $file = trim( QM_Util::standard_dir( $ref->getFileName(), '' ), '/' );
183
+ $callback['name'] = sprintf( __( '{closure}() on line %1$d of %2$s', 'query-monitor' ), $ref->getEndLine(), $file );
184
+
185
+ } else {
186
+
187
+ $callback['name'] = $callback['function'] . '()';
188
+ $ref = new ReflectionFunction( $callback['function'] );
189
+
190
+ }
191
+
192
+ $callback['component'] = self::get_file_component( $ref->getFileName() );
193
+
194
+ } catch ( ReflectionException $e ) {
195
+
196
+ # Nothing
197
+
198
+ }
199
+
200
+ return $callback;
201
+
202
+ }
203
+
204
+ public static function is_ajax() {
205
+ if ( defined( 'DOING_AJAX' ) and DOING_AJAX )
206
+ return true;
207
+ if ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) and 'xmlhttprequest' == strtolower( $_SERVER['HTTP_X_REQUESTED_WITH'] ) )
208
+ return true;
209
+ return false;
210
+ }
211
+
212
+ public static function wpv() {
213
+ return 'qm-wp-' . ( floatval( $GLOBALS['wp_version'] ) * 10 );
214
+ }
215
+
216
+ public static function get_admins() {
217
+ if ( is_multisite() )
218
+ return false;
219
+ else
220
+ return get_role( 'administrator' );
221
+ }
222
+
223
+ public static function format_sql( $sql ) {
224
+
225
+ $sql = str_replace( array( "\r\n", "\r", "\n", "\t" ), ' ', $sql );
226
+ $sql = esc_html( $sql );
227
+ $sql = trim( $sql );
228
+
229
+ foreach( array(
230
+ 'ALTER', 'AND', 'COMMIT', 'CREATE', 'DESCRIBE', 'DELETE', 'DROP', 'ELSE', 'END', 'FROM', 'GROUP',
231
+ 'HAVING', 'INNER', 'INSERT', 'LIMIT', 'ON', 'OR', 'ORDER', 'REPLACE', 'ROLLBACK', 'SELECT', 'SET',
232
+ 'SHOW', 'START', 'THEN', 'TRUNCATE', 'UPDATE', 'VALUES', 'WHEN', 'WHERE'
233
+ ) as $cmd )
234
+ $sql = trim( str_replace( " $cmd ", "<br/>$cmd ", $sql ) );
235
+
236
+ return $sql;
237
+
238
+ }
239
+
240
+ public static function format_bool_constant( $constant ) {
241
+ if ( !defined( $constant ) or !constant( $constant ) )
242
+ return 'false';
243
+ else
244
+ return 'true';
245
+ }
246
+
247
+ }
assets/query-monitor.css ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+
3
+ © 2013 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
+
18
+ #wpadminbar .quicklinks .menupop ul li.qm-true > a {
19
+ color: #4a4 !important;
20
+ }
21
+ body.mp6 #wpadminbar .quicklinks .menupop ul li.qm-true > a {
22
+ color: #8c8 !important;
23
+ }
24
+ body.mp6 #wpadminbar .quicklinks .menupop ul li.qm-true > a:hover {
25
+ color: #4a4 !important;
26
+ }
27
+
28
+ #wp-admin-bar-query-monitor-notices {
29
+ background-color: #740 !important;
30
+ }
31
+
32
+ #wpadminbar .qm-notice {
33
+ background-color: #740;
34
+ }
35
+
36
+ #wp-admin-bar-query-monitor-stricts {
37
+ background-color: #eee !important;
38
+ }
39
+
40
+ #wpadminbar .qm-strict {
41
+ background-color: #000;
42
+ }
43
+
44
+ #wp-admin-bar-query-monitor-expensive {
45
+ background-color: #b60 !important;
46
+ }
47
+
48
+ #wpadminbar .qm-expensive {
49
+ background-color: #b60;
50
+ }
51
+
52
+ #wp-admin-bar-query-monitor-warnings {
53
+ background-color: #c00 !important;
54
+ }
55
+
56
+ #wpadminbar .qm-warning {
57
+ background-color: #c00;
58
+ }
59
+
60
+ #wp-admin-bar-query-monitor-errors {
61
+ background-color: #c00 !important;
62
+ }
63
+
64
+ #wpadminbar .qm-error {
65
+ background-color: #c00 !important;
66
+ }
67
+
68
+ #wp-admin-bar-query-monitor-stricts a {
69
+ color: #555 !important;
70
+ }
71
+
72
+ #wp-admin-bar-query-monitor-notices a,
73
+ #wp-admin-bar-query-monitor-expensive a,
74
+ #wp-admin-bar-query-monitor-warnings a,
75
+ #wp-admin-bar-query-monitor-errors a {
76
+ color: #eee !important;
77
+ }
78
+
79
+ #wp-admin-bar-query-monitor small {
80
+ font-size: 11px !important;
81
+ }
82
+
83
+ #wp-admin-bar-query-monitor.hover a small,
84
+ #wp-admin-bar-query-monitor.hover a .ab-label {
85
+ text-shadow: none !important;
86
+ color: #333 !important;
87
+ }
88
+ body.mp6 #wp-admin-bar-query-monitor.hover a small,
89
+ body.mp6 #wp-admin-bar-query-monitor.hover a .ab-label {
90
+ color: #2ea2cc !important;
91
+ }
92
+
93
+ #wp-admin-bar-query-monitor-placeholder,
94
+ #wp-admin-bar-query-monitor-default {
95
+ display: none;
96
+ }
97
+
98
+ #wpadminbar #wp-admin-bar-query-monitor .ab-icon {
99
+ font: 16px/16px 'Open Sans', sans-serif !important;
100
+ width: auto !important;
101
+ padding: 0 2px !important;
102
+ color: #999 !important;
103
+ line-height: 32px !important;
104
+ display: none !important;
105
+ }
106
+
107
+ @media screen and (max-width: 782px) {
108
+ body.mp6 #wpadminbar #wp-admin-bar-query-monitor .ab-icon {
109
+ display: block !important;
110
+ }
111
+ }
112
+
113
+ #qm {
114
+ clear: both !important;
115
+ background: #eeeeee !important;
116
+ margin: 25px 0 0 !important;
117
+ border-top: 1px solid #ccc !important;
118
+ padding: 0 0 35px !important;
119
+ text-align: left !important;
120
+ display: none;
121
+ }
122
+
123
+ #qm.qm-show,
124
+ .no-js #qm,
125
+ .nojs #qm {
126
+ display: block;
127
+ }
128
+
129
+ #qm:after {
130
+ display: block !important;
131
+ content: '' !important;
132
+ clear: both !important;
133
+ height: 0 !important;
134
+ }
135
+
136
+ body.wp-admin #qm {
137
+ margin: 0 !important;
138
+ }
139
+
140
+ body.sticky-menu #qm {
141
+ margin-left: 150px !important;
142
+ }
143
+
144
+ body.sticky-menu.folded #qm {
145
+ margin-left: 36px !important;
146
+ }
147
+
148
+ #qm-wrapper {
149
+ margin: 0 auto;
150
+ max-width: 85em;
151
+ }
152
+
153
+ #qm-wrapper > p {
154
+ color: #777 !important;
155
+ font: 13px/15px 'Open Sans', Arial !important;
156
+ margin: 20px 1% -15px !important;
157
+ font-style: italic !important;
158
+ }
159
+
160
+ .qm {
161
+ margin: 0 1% !important;
162
+ padding: 40px 0 0 !important;
163
+ clear: both !important;
164
+ }
165
+
166
+ .qm-half {
167
+ float: left !important;
168
+ width: 48% !important;
169
+ clear: none !important;
170
+ }
171
+
172
+ .qm>table {
173
+ border-collapse: collapse !important;
174
+ color: #555 !important;
175
+ border-style: hidden !important;
176
+ box-shadow: 0 1px 1px -1px rgba(0,0,0,0.1) !important;
177
+ width: 100% !important;
178
+ border: 1px solid #dedede !important;
179
+ }
180
+
181
+ .qm td,
182
+ .qm th {
183
+ background: #fff !important;
184
+ text-align: left !important;
185
+ font-family: Menlo, Monaco, Consolas, 'Courier New', monospace !important;
186
+ font-size: 11px !important;
187
+ font-weight: normal !important;
188
+ font-style: normal !important;
189
+ line-height: 16px !important;
190
+ border: 1px solid #e1e1e1 !important;
191
+ padding: 5px 8px 4px !important;
192
+ vertical-align: top !important;
193
+ text-shadow: none !important;
194
+ text-transform: none !important;
195
+ }
196
+
197
+ .qm tbody tr:hover th,
198
+ .qm tbody tr:hover td {
199
+ background: #f5f5f5 !important;
200
+ }
201
+
202
+ #qm-conditionals tbody tr:hover td,
203
+ #qm-overview tbody tr:hover td,
204
+ #qm-authentication tbody tr:hover td,
205
+ .qm tbody tr:hover th[rowspan],
206
+ .qm tbody tr:hover td[rowspan] {
207
+ background: #fff !important;
208
+ }
209
+
210
+ .qm th {
211
+ color: #444 !important;
212
+ }
213
+
214
+ .qm-inner {
215
+ border-collapse: collapse !important;
216
+ margin: 0 !important;
217
+ background: #fff !important;
218
+ color: #555 !important;
219
+ border: none !important;
220
+ }
221
+
222
+ .qm-inner td,
223
+ .qm-inner th {
224
+ text-align: left !important;
225
+ font-family: Menlo, Monaco, Consolas, 'Courier New', monospace !important;
226
+ font-size: 11px !important;
227
+ line-height: 16px !important;
228
+ border: none !important;
229
+ padding: 1px 0 !important;
230
+ }
231
+
232
+ .qm ul {
233
+ margin: 0 !important;
234
+ padding: 0 0 0 10px !important;
235
+ list-style: none !important;
236
+ }
237
+
238
+ .qm li {
239
+ margin-bottom: 2px !important;
240
+ }
241
+
242
+ .qm span.qm-true,
243
+ .qm td.qm-true {
244
+ color: #4a4 !important;
245
+ }
246
+
247
+ .qm span.qm-false,
248
+ .qm td.qm-false {
249
+ color: #ccc !important;
250
+ }
251
+
252
+ .qm span.qm-na,
253
+ .qm tr.qm-na {
254
+ color: #ccc !important;
255
+ }
256
+
257
+ .qm .qm-sql {
258
+ word-wrap: break-word !important;
259
+ word-break: break-all !important;
260
+ }
261
+
262
+ .qm .qm-nonselectsql {
263
+ color: #a0a !important;
264
+ }
265
+
266
+ .qm .qm-current {
267
+ color: #a0a !important;
268
+ }
269
+
270
+ .qm .qm-url span {
271
+ color: #ccc !important;
272
+ }
273
+
274
+ .qm .qm-info {
275
+ color: #999 !important;
276
+ }
277
+
278
+ .qm a {
279
+ color: #0074a2 !important;
280
+ text-decoration: none !important;
281
+ text-shadow: none !important;
282
+ font-weight: normal !important;
283
+ }
284
+ .qm a:hover {
285
+ text-decoration: underline !important;
286
+ color: #2ea2cc !important;
287
+ }
288
+
289
+ .qm a.qm-warn {
290
+ text-decoration: underline !important;
291
+ }
292
+
293
+ .qm .qm-warn,
294
+ .qm .qm-warn span.qm-na {
295
+ color: #f00 !important;
296
+ }
297
+
298
+ .qm span.qm-expensive,
299
+ .qm td.qm-expensive {
300
+ color: #f44 !important;
301
+ }
302
+
303
+ .qm td.qm-priority {
304
+ text-align: right !important;
305
+ }
306
+
307
+ .qm td.qm-var {
308
+ text-align: right !important;
309
+ padding: 1px 1.5em 1px 0 !important;
310
+ }
311
+
312
+ .qm-param {
313
+ padding: 0 6px !important;
314
+ }
315
+
316
+ /* Filters */
317
+
318
+ select.qm-filter {
319
+ display: block !important;
320
+ font-family: Menlo, Monaco, Consolas, 'Courier New', monospace !important;
321
+ font-weight: normal !important;
322
+ font-size: 11px !important;
323
+ margin: 4px 0 2px !important;
324
+ border: 1px solid #e6e6e6 !important;
325
+ padding: 1px !important;
326
+ background: #fff !important;
327
+ color: #444 !important;
328
+ height: 2em !important;
329
+ width: auto !important;
330
+ border-radius: 0 !important;
331
+ float: none !important;
332
+ cursor: pointer !important;
333
+ text-transform: none !important;
334
+ -webkit-appearance: menulist !important;
335
+ -moz-appearance: menulist !important;
336
+ }
337
+
338
+ .qm-hide,
339
+ .qm-hide-hooks-component,
340
+ .qm-hide-hooks-name,
341
+ .qm-hide-db_queries-type,
342
+ .qm-hide-db_queries-caller,
343
+ .qm-hide-db_queries-component {
344
+ display: none !important;
345
+ }
346
+
347
+ /* RTL */
348
+
349
+ html[dir="rtl"] .qm-ltr {
350
+ direction: ltr !important;
351
+ text-align: right !important;
352
+ }
353
+
354
+ html[dir="rtl"] .qm td.qm-priority {
355
+ padding: 1px 0 1px 0.75em !important;
356
+ }
357
+
358
+ html[dir="rtl"] .qm td.qm-var {
359
+ padding: 1px 0 1px 1.5em !important;
360
+ text-align: left !important;
361
+ }
362
+
363
+ html[dir="rtl"] body.sticky-menu #qm {
364
+ margin-right: 150px !important;
365
+ margin-left: 0 !important;
366
+ }
assets/query-monitor.js ADDED
@@ -0,0 +1,162 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+
3
+ © 2013 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
+ var QM_i18n = {
18
+
19
+ // http://core.trac.wordpress.org/ticket/20491
20
+
21
+ number_format : function( number, decimals ) {
22
+
23
+ if ( isNaN( number ) )
24
+ return;
25
+
26
+ if ( !decimals )
27
+ decimals = 0;
28
+
29
+ number = parseFloat( number );
30
+
31
+ var num_float = number.toFixed( decimals ),
32
+ num_int = Math.floor( number ),
33
+ num_str = num_int.toString(),
34
+ fraction = num_float.substring( num_float.indexOf( '.' ) + 1, num_float.length ),
35
+ o = '';
36
+
37
+ if ( num_str.length > 3 ) {
38
+ for ( i = num_str.length; i > 3; i -= 3 )
39
+ o = qm_locale.number_format.thousands_sep + num_str.slice( i - 3, i ) + o;
40
+ o = num_str.slice( 0, i ) + o;
41
+ } else {
42
+ o = num_str;
43
+ }
44
+
45
+ if ( decimals )
46
+ o = o + qm_locale.number_format.decimal_point + fraction;
47
+
48
+ return o;
49
+
50
+ }
51
+
52
+ };
53
+
54
+ jQuery( function($) {
55
+
56
+ if ( !window.qm )
57
+ return;
58
+
59
+ if ( $('#wp-admin-bar-query-monitor').length ) {
60
+
61
+ var container = document.createDocumentFragment();
62
+
63
+ $('#wp-admin-bar-query-monitor')
64
+ .addClass(qm.menu.top.classname)
65
+ .find('a').eq(0)
66
+ .html(qm.menu.top.title)
67
+ ;
68
+
69
+ $.each( qm.menu.sub, function( i, el ) {
70
+
71
+ var new_menu = $('#wp-admin-bar-query-monitor-placeholder')
72
+ .clone()
73
+ .attr('id','wp-admin-bar-'+el.id)
74
+ ;
75
+ new_menu
76
+ .find('a').eq(0)
77
+ .html(el.title)
78
+ .attr('href',el.href)
79
+ ;
80
+
81
+ if ( ( typeof el.meta != 'undefined' ) && ( typeof el.meta.classname != 'undefined' ) )
82
+ new_menu.addClass(el.meta.classname);
83
+
84
+ container.appendChild( new_menu.get(0) );
85
+
86
+ } );
87
+
88
+ $('#wp-admin-bar-query-monitor ul').append(container);
89
+
90
+ $('#wp-admin-bar-query-monitor').find('a').on('click',function(e){
91
+ $('#qm').show();
92
+ });
93
+
94
+ $('#wp-admin-bar-query-monitor,#wp-admin-bar-query-monitor-default').show();
95
+
96
+ }
97
+
98
+ $('#qm').find('select.qm-filter').on('change',function(e){
99
+
100
+ var filter = $(this).attr('data-filter'),
101
+ table = $(this).closest('table'),
102
+ tr = table.find('tbody tr[data-qm-' + filter + ']'),
103
+ val = $(this).val().replace(/[[\]()'"]/g, "\\$&"),
104
+ total = tr.removeClass('qm-hide-' + filter).length,
105
+ time = 0;
106
+
107
+ if ( $(this).val() !== '' )
108
+ tr.not('[data-qm-' + filter + '*="' + val + '"]').addClass('qm-hide-' + filter);
109
+
110
+ var matches = tr.filter(':visible');
111
+ matches.each(function(i){
112
+ var row_time = $(this).attr('data-qm-time');
113
+ if ( row_time )
114
+ time += parseFloat( row_time );
115
+ });
116
+ if ( time )
117
+ time = QM_i18n.number_format( time, 4 );
118
+
119
+ var results = table.find('.qm-items-shown').removeClass('qm-hide');
120
+ results.find('.qm-items-number').text(matches.length);
121
+ results.find('.qm-items-time').text(time);
122
+
123
+ $(this).blur();
124
+
125
+ });
126
+
127
+ $( document ).ajaxSuccess( function( event, response, options ) {
128
+
129
+ var errors = response.getResponseHeader( 'X-QM-Errors' );
130
+
131
+ if ( !errors )
132
+ return event;
133
+
134
+ errors = $.parseJSON( errors );
135
+
136
+ for ( var key in errors ) {
137
+
138
+ error = $.parseJSON( response.getResponseHeader( 'X-QM-Error-' + errors[key] ) );
139
+
140
+ if ( window.console ) {
141
+ console.debug( '=== PHP Error in AJAX Response ===' ); // @TODO i18n
142
+ console.debug( error );
143
+ }
144
+
145
+ if ( $('#wp-admin-bar-query-monitor').length ) {
146
+ if ( ! qm.ajax_errors[error.type] ) {
147
+ $('#wp-admin-bar-query-monitor')
148
+ .addClass('qm-'+error.type)
149
+ .find('a').first().append('<span class="qm-ajax-'+ error.type +'"> / AJAX: '+ error.type +'</span>')
150
+ ;
151
+ }
152
+ }
153
+
154
+ qm.ajax_errors[error.type] = true;
155
+
156
+ }
157
+
158
+ return event;
159
+
160
+ } );
161
+
162
+ } );
autoloader.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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
+ function qm_autoloader( $class ) {
18
+
19
+ if ( 0 !== strpos( $class, 'QM_' ) )
20
+ return;
21
+
22
+ $name = preg_replace( '|^QM_|', '', $class );
23
+ $name = str_replace( '_', '/', $name );
24
+
25
+ $file = sprintf( '%1$s/%2$s.php',
26
+ dirname( __FILE__ ),
27
+ $name
28
+ );
29
+
30
+ if ( is_readable( $file ) )
31
+ include $file;
32
+
33
+ }
34
+
35
+ spl_autoload_register( 'qm_autoloader' );
components/admin.php ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Admin extends QM_Component {
18
+
19
+ var $id = 'admin';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'current_screen', array( $this, 'current_screen' ), 99 );
24
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 100 );
25
+ }
26
+
27
+ function current_screen( WP_Screen $screen ) {
28
+ if ( empty( $this->data['admin'] ) )
29
+ $this->data['admin'] = wp_clone( $screen );
30
+ return $screen;
31
+ }
32
+
33
+ function process() {
34
+
35
+ global $pagenow;
36
+
37
+ if ( isset( $_GET['page'] ) )
38
+ $this->data['base'] = get_current_screen()->base;
39
+ else
40
+ $this->data['base'] = $pagenow;
41
+
42
+ if ( !isset( $this->data['admin'] ) )
43
+ $this->data['admin'] = __( 'n/a', 'query-monitor' );
44
+
45
+ $this->data['pagenow'] = $pagenow;
46
+ $this->data['current_screen'] = get_current_screen();
47
+
48
+ }
49
+
50
+ function admin_menu( array $menu ) {
51
+
52
+ if ( isset( $this->data['base'] ) ) {
53
+ $menu[] = $this->menu( array(
54
+ 'title' => sprintf( __( 'Admin Screen: %s', 'query-monitor' ), $this->data['base'] )
55
+ ) );
56
+ }
57
+ return $menu;
58
+
59
+ }
60
+
61
+ function output_html( array $args, array $data ) {
62
+
63
+ if ( empty( $data ) )
64
+ return;
65
+
66
+ echo '<div class="qm qm-half" id="' . $args['id'] . '">';
67
+ echo '<table cellspacing="0">';
68
+ echo '<thead>';
69
+ echo '<tr>';
70
+ echo '<th colspan="3">' . __( 'Admin', 'query-monitor' ) . '</th>';
71
+ echo '</tr>';
72
+ echo '</thead>';
73
+ echo '<tbody>';
74
+
75
+ echo '<tr>';
76
+ echo '<td class="qm-ltr">get_current_screen()</td>';
77
+ echo '<td>';
78
+
79
+ if ( is_object( $data['admin'] ) ) {
80
+ echo '<table class="qm-inner" cellspacing="0">';
81
+ echo '<tbody>';
82
+ foreach ( $data['admin'] as $key => $value ) {
83
+ echo '<tr>';
84
+ echo "<td class='qm-var'>{$key}:</td>";
85
+ echo '<td>';
86
+ echo $value;
87
+ if ( !empty( $value ) and ( $data['current_screen']->$key != $value ) )
88
+ echo '&nbsp;(<a href="http://core.trac.wordpress.org/ticket/14886" class="qm-warn" title="' . esc_attr__( 'This value may not be as expected. Please see WordPress bug #14886.', 'query-monitor' ) . '" target="_blank">!</a>)';
89
+ echo '</td>';
90
+ echo '</tr>';
91
+ }
92
+ echo '</tbody>';
93
+ echo '</table>';
94
+ } else {
95
+ echo $data['admin'];
96
+ }
97
+
98
+ echo '</td>';
99
+ echo '</tr>';
100
+
101
+ echo '<tr>';
102
+ echo '<td class="qm-ltr">$pagenow</td>';
103
+ echo "<td>{$data['pagenow']}</td>";
104
+ echo '</tr>';
105
+
106
+ $screens = array(
107
+ 'edit' => true,
108
+ 'edit-comments' => true,
109
+ 'edit-tags' => true,
110
+ 'link-manager' => true,
111
+ 'plugins' => true,
112
+ 'plugins-network' => true,
113
+ 'sites-network' => true,
114
+ 'themes-network' => true,
115
+ 'upload' => true,
116
+ 'users' => true,
117
+ 'users-network' => true,
118
+ );
119
+
120
+ if ( !empty( $data['current_screen'] ) and isset( $screens[$data['current_screen']->base] ) ) {
121
+
122
+ # And now, WordPress' legendary inconsistency comes into play:
123
+
124
+ if ( !empty( $data['current_screen']->taxonomy ) )
125
+ $col = $data['current_screen']->taxonomy;
126
+ else if ( !empty( $data['current_screen']->post_type ) )
127
+ $col = $data['current_screen']->post_type . '_posts';
128
+ else
129
+ $col = $data['current_screen']->base;
130
+
131
+ if ( !empty( $data['current_screen']->post_type ) and empty( $data['current_screen']->taxonomy ) )
132
+ $cols = $data['current_screen']->post_type . '_posts';
133
+ else
134
+ $cols = $data['current_screen']->id;
135
+
136
+ if ( 'edit-comments' == $col )
137
+ $col = 'comments';
138
+ else if ( 'upload' == $col )
139
+ $col = 'media';
140
+ else if ( 'link-manager' == $col )
141
+ $col = 'link';
142
+
143
+ echo '<tr>';
144
+ echo '<td rowspan="2">' . __( 'Columns', 'query-monitor' ) . '</td>';
145
+ echo "<td colspan='2'>manage_<span class='qm-current'>{$cols}</span>_columns</td>";
146
+ echo '</tr>';
147
+ echo '<tr>';
148
+ echo "<td colspan='2'>manage_<span class='qm-current'>{$data['current_screen']->id}</span>_sortable_columns</td>";
149
+ echo '</tr>';
150
+
151
+ echo '<tr>';
152
+ echo '<td rowspan="1">' . __( 'Column', 'query-monitor' ) . '</td>';
153
+ echo "<td colspan='2'>manage_<span class='qm-current'>{$col}</span>_custom_column</td>";
154
+ echo '</tr>';
155
+
156
+ }
157
+
158
+ echo '</tbody>';
159
+ echo '</table>';
160
+ echo '</div>';
161
+
162
+ }
163
+
164
+ }
165
+
166
+ function register_qm_admin( array $qm ) {
167
+ if ( is_admin() )
168
+ $qm['admin'] = new QM_Component_Admin;
169
+ return $qm;
170
+ }
171
+
172
+ add_filter( 'query_monitor_components', 'register_qm_admin', 50 );
components/authentication.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Authentication extends QM_Component {
18
+
19
+ var $id = 'authentication';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'plugins_loaded', array( $this, 'action_plugins_loaded' ) );
24
+ }
25
+
26
+ function action_plugins_loaded() {
27
+
28
+ if ( !defined( 'QM_COOKIE' ) )
29
+ define( 'QM_COOKIE', 'qm_' . COOKIEHASH );
30
+
31
+ }
32
+
33
+ function show_query_monitor() {
34
+ if ( isset( $_COOKIE[QM_COOKIE] ) )
35
+ return self::verify_nonce( $_COOKIE[QM_COOKIE], 'view_query_monitor' );
36
+ return false;
37
+ }
38
+
39
+ function output_html( array $args, array $data ) {
40
+
41
+ echo '<div class="qm" id="' . $args['id'] . '">';
42
+ echo '<table cellspacing="0">';
43
+ echo '<thead>';
44
+ echo '<tr>';
45
+ echo '<th>' . __( 'Authentication', 'query-monitor' ) . '</th>';
46
+ echo '</tr>';
47
+ echo '</thead>';
48
+ echo '<tbody>';
49
+
50
+ $name = QM_COOKIE;
51
+ $domain = COOKIE_DOMAIN;
52
+ $path = COOKIEPATH;
53
+
54
+ if ( !isset( $_COOKIE[$name] ) or !self::verify_nonce( $_COOKIE[$name], 'view_query_monitor' ) ) {
55
+
56
+ $value = self::create_nonce( 'view_query_monitor' );
57
+ $text = esc_js( __( 'Authentication cookie set. You can now view Query Monitor output while logged out or while logged in as a different user.', 'query-monitor' ) );
58
+ $link = "document.cookie='{$name}={$value}; domain={$domain}; path={$path}'; alert('{$text}'); return false;";
59
+
60
+ echo '<tr>';
61
+ 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>';
62
+ echo '</tr>';
63
+ echo '<tr>';
64
+ echo '<td><a href="#" onclick="' . $link . '">' . __( 'Set authentication cookie', 'query-monitor' ) . '</a></td>';
65
+ echo '</tr>';
66
+
67
+ } else {
68
+
69
+ $text = esc_js( __( 'Authentication cookie cleared.', 'query-monitor' ) );
70
+ $link = "document.cookie='{$name}=; expires=' + new Date(0).toUTCString() + '; domain={$domain}; path={$path}'; alert('{$text}'); return false;";
71
+
72
+ echo '<tr>';
73
+ echo '<td>' . __( 'You currently have an authentication cookie which allows you to view Query Monitor output.', 'query-monitor' ) . '</td>';
74
+ echo '</tr>';
75
+ echo '<tr>';
76
+ echo '<td><a href="#" onclick="' . $link . '">' . __( 'Clear authentication cookie', 'query-monitor' ) . '</a></td>';
77
+ echo '</tr>';
78
+
79
+ }
80
+
81
+ echo '</tbody>';
82
+ echo '</table>';
83
+ echo '</div>';
84
+
85
+ }
86
+
87
+ public static function create_nonce( $action ) {
88
+ # This is just WordPress' nonce implementation minus the user ID
89
+ # check so a nonce can be set in a cookie and used cross-user
90
+ $i = wp_nonce_tick();
91
+ return substr( wp_hash( $i . $action, 'nonce' ), -12, 10 );
92
+ }
93
+
94
+ public static function verify_nonce( $nonce, $action ) {
95
+
96
+ $i = wp_nonce_tick();
97
+
98
+ if ( substr( wp_hash( $i . $action, 'nonce' ), -12, 10 ) === $nonce )
99
+ return true;
100
+ if ( substr( wp_hash( ( $i - 1 ) . $action, 'nonce' ), -12, 10 ) === $nonce )
101
+ return true;
102
+
103
+ return false;
104
+
105
+ }
106
+
107
+ }
108
+
109
+ function register_qm_authentication( array $qm ) {
110
+ $qm['authentication'] = new QM_Component_Authentication;
111
+ return $qm;
112
+ }
113
+
114
+ add_filter( 'query_monitor_components', 'register_qm_authentication', 130 );
components/conditionals.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Conditionals extends QM_Component {
18
+
19
+ var $id = 'conditionals';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 120 );
24
+ }
25
+
26
+ function admin_menu( array $menu ) {
27
+
28
+ foreach ( $this->data['conds']['true'] as $cond ) {
29
+ $menu[] = $this->menu( array(
30
+ 'title' => $cond . '()',
31
+ 'id' => 'query-monitor-' . $cond,
32
+ 'meta' => array( 'classname' => 'qm-true qm-ltr' )
33
+ ) );
34
+ }
35
+
36
+ return $menu;
37
+
38
+ }
39
+
40
+ function output_html( array $args, array $data ) {
41
+
42
+ $cols = 5;
43
+ $i = 0;
44
+ $w = floor( 100 / $cols );
45
+
46
+ echo '<div class="qm" id="' . $args['id'] . '">';
47
+ echo '<table cellspacing="0">';
48
+ echo '<thead>';
49
+ echo '<tr>';
50
+ echo '<th colspan="' . $cols . '">' . __( 'Conditionals', 'query-monitor' ) . '</th>';
51
+ echo '</tr>';
52
+ echo '</thead>';
53
+ echo '<tbody>';
54
+
55
+ foreach ( $data['conds']['true'] as $cond ) {
56
+ $i++;
57
+ if ( 1 === $i%$cols )
58
+ echo '<tr>';
59
+ echo '<td class="qm-ltr qm-true" width="' . $w . '%">' . $cond . '()</td>';
60
+ if ( 0 === $i%$cols )
61
+ echo '</tr>';
62
+ }
63
+
64
+ foreach ( $data['conds']['false'] as $cond ) {
65
+ $i++;
66
+ if ( 1 === $i%$cols )
67
+ echo '<tr>';
68
+ echo '<td class="qm-ltr qm-false" width="' . $w . '%">' . $cond . '()</td>';
69
+ if ( 0 === $i%$cols )
70
+ echo '</tr>';
71
+ }
72
+ $fill = ($cols-($i%$cols));
73
+ if ( $fill ) {
74
+ echo '<td colspan="' . $fill . '">&nbsp;</td>';
75
+ echo '</tr>';
76
+ }
77
+
78
+ echo '</tbody>';
79
+ echo '</table>';
80
+ echo '</div>';
81
+
82
+ }
83
+
84
+ function process() {
85
+
86
+ $conds = apply_filters( 'query_monitor_conditionals', array(
87
+ 'is_404', 'is_archive', 'is_admin', 'is_attachment', 'is_author', 'is_blog_admin', 'is_category', 'is_comments_popup', 'is_date',
88
+ 'is_day', 'is_feed', 'is_front_page', 'is_home', 'is_main_network', 'is_main_site', 'is_month', 'is_multitax', 'is_network_admin',
89
+ 'is_page', 'is_page_template', 'is_paged', 'is_post_type_archive', 'is_preview', 'is_robots', 'is_rtl', 'is_search', 'is_single',
90
+ 'is_singular', 'is_ssl', 'is_sticky', 'is_tag', 'is_tax', 'is_time', 'is_trackback', 'is_year'
91
+ ) );
92
+
93
+ $true = $false = $na = array();
94
+
95
+ foreach ( $conds as $cond ) {
96
+ if ( function_exists( $cond ) ) {
97
+
98
+ if ( ( 'is_sticky' == $cond ) and !get_post( $id = null ) ) {
99
+ # Special case for is_sticky to prevent PHP notices
100
+ $false[] = $cond;
101
+ } else if ( ( 'is_main_site' == $cond ) and !is_multisite() ) {
102
+ # Special case for is_main_site to prevent it from being annoying on single site installs
103
+ $na[] = $cond;
104
+ } else {
105
+ if ( call_user_func( $cond ) )
106
+ $true[] = $cond;
107
+ else
108
+ $false[] = $cond;
109
+ }
110
+
111
+ } else {
112
+ $na[] = $cond;
113
+ }
114
+ }
115
+ $this->data['conds'] = compact( 'true', 'false', 'na' );
116
+
117
+ }
118
+
119
+ }
120
+
121
+ function register_qm_conditionals( array $qm ) {
122
+ $qm['conditionals'] = new QM_Component_Conditionals;
123
+ return $qm;
124
+ }
125
+
126
+ add_filter( 'query_monitor_components', 'register_qm_conditionals', 40 );
components/db_callers.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_DB_Callers extends QM_Component {
18
+
19
+ var $id = 'db_callers';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 30 );
24
+ }
25
+
26
+ function process() {
27
+
28
+ if ( $dbq = $this->get_component( 'db_queries' ) ) {
29
+ if ( isset( $dbq->data['times'] ) ) {
30
+ $this->data['times'] = $dbq->data['times'];
31
+ }
32
+ if ( isset( $dbq->data['types'] ) ) {
33
+ $this->data['types'] = $dbq->data['types'];
34
+ }
35
+ }
36
+
37
+ }
38
+
39
+ function admin_menu( array $menu ) {
40
+
41
+ if ( $dbq = $this->get_component( 'db_queries' ) and isset( $dbq->data['times'] ) ) {
42
+ $menu[] = $this->menu( array(
43
+ 'title' => __( 'Queries by Caller', 'query-monitor' )
44
+ ) );
45
+ }
46
+ return $menu;
47
+
48
+ }
49
+
50
+ function output_html( array $args, array $data ) {
51
+
52
+ if ( empty( $data ) )
53
+ return;
54
+
55
+ $total_time = 0;
56
+ $total_calls = 0;
57
+
58
+ echo '<div class="qm qm-half" id="' . $args['id'] . '">';
59
+ echo '<table cellspacing="0">';
60
+ echo '<thead>';
61
+ echo '<tr>';
62
+ echo '<th>' . _x( 'Caller', 'Query caller', 'query-monitor' ) . '</th>';
63
+
64
+ if ( !empty( $data['types'] ) ) {
65
+ foreach ( $data['types'] as $type_name => $type_count )
66
+ echo '<th>' . $type_name . '</th>';
67
+ }
68
+
69
+ echo '<th>' . __( 'Time', 'query-monitor' ) . '</th>';
70
+ echo '</tr>';
71
+ echo '</thead>';
72
+
73
+ if ( !empty( $data['times'] ) ) {
74
+
75
+ echo '<tbody>';
76
+
77
+ usort( $data['times'], 'QM_Util::sort' );
78
+
79
+ foreach ( $data['times'] as $caller => $row ) {
80
+ $total_time += $row['ltime'];
81
+ $total_calls += $row['calls'];
82
+ $stime = number_format_i18n( $row['ltime'], 4 );
83
+ $ltime = number_format_i18n( $row['ltime'], 10 );
84
+
85
+ echo '<tr>';
86
+ echo "<td valign='top' class='qm-ltr'>{$row['caller']}</td>";
87
+
88
+ foreach ( $data['types'] as $type_name => $type_count ) {
89
+ if ( isset( $row['types'][$type_name] ) )
90
+ echo "<td valign='top'>{$row['types'][$type_name]}</td>";
91
+ else
92
+ echo "<td valign='top'>&nbsp;</td>";
93
+ }
94
+
95
+ echo "<td valign='top' title='{$ltime}'>{$stime}</td>";
96
+ echo '</tr>';
97
+
98
+ }
99
+
100
+ echo '</tbody>';
101
+ echo '<tfoot>';
102
+
103
+ $total_stime = number_format_i18n( $total_time, 4 );
104
+ $total_ltime = number_format_i18n( $total_time, 10 );
105
+
106
+ echo '<tr>';
107
+ echo '<td>&nbsp;</td>';
108
+
109
+ foreach ( $data['types'] as $type_name => $type_count )
110
+ echo '<td>' . number_format_i18n( $type_count ) . '</td>';
111
+
112
+ echo "<td title='{$total_ltime}'>{$total_stime}</td>";
113
+ echo '</tr>';
114
+
115
+ echo '</tfoot>';
116
+
117
+ } else {
118
+
119
+ echo '<tbody>';
120
+ echo '<tr>';
121
+ echo '<td colspan="3" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
122
+ echo '</tr>';
123
+ echo '</tbody>';
124
+
125
+ }
126
+
127
+ echo '</table>';
128
+ echo '</div>';
129
+
130
+ }
131
+
132
+ }
133
+
134
+ function register_qm_db_callers( array $qm ) {
135
+ $qm['db_callers'] = new QM_Component_DB_Callers;
136
+ return $qm;
137
+ }
138
+
139
+ add_filter( 'query_monitor_components', 'register_qm_db_callers', 30 );
components/db_components.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_DB_Components extends QM_Component {
18
+
19
+ var $id = 'db_components';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 40 );
24
+ }
25
+
26
+ function process() {
27
+
28
+ if ( $dbq = $this->get_component( 'db_queries' ) ) {
29
+ if ( isset( $dbq->data['component_times'] ) ) {
30
+ $this->data['times'] = $dbq->data['component_times'];
31
+ }
32
+ if ( isset( $dbq->data['types'] ) ) {
33
+ $this->data['types'] = $dbq->data['types'];
34
+ }
35
+ }
36
+
37
+ }
38
+
39
+ function admin_menu( array $menu ) {
40
+
41
+ if ( $dbq = $this->get_component( 'db_queries' ) and isset( $dbq->data['component_times'] ) ) {
42
+ $menu[] = $this->menu( array(
43
+ 'title' => __( 'Queries by Component', 'query-monitor' )
44
+ ) );
45
+ }
46
+ return $menu;
47
+
48
+ }
49
+
50
+ function output_html( array $args, array $data ) {
51
+
52
+ if ( empty( $data ) )
53
+ return;
54
+
55
+ $total_time = 0;
56
+ $total_calls = 0;
57
+
58
+ echo '<div class="qm qm-half" id="' . $args['id'] . '">';
59
+ echo '<table cellspacing="0">';
60
+ echo '<thead>';
61
+ echo '<tr>';
62
+ echo '<th>' . _x( 'Component', 'Query component', 'query-monitor' ) . '</th>';
63
+
64
+ if ( !empty( $data['types'] ) ) {
65
+ foreach ( $data['types'] as $type_name => $type_count )
66
+ echo '<th>' . $type_name . '</th>';
67
+ }
68
+
69
+ echo '<th>' . __( 'Time', 'query-monitor' ) . '</th>';
70
+ echo '</tr>';
71
+ echo '</thead>';
72
+
73
+ if ( !empty( $data['times'] ) ) {
74
+
75
+ echo '<tbody>';
76
+
77
+ usort( $data['times'], 'QM_Util::sort' );
78
+
79
+ foreach ( $data['times'] as $component => $row ) {
80
+ $total_time += $row['ltime'];
81
+ $total_calls += $row['calls'];
82
+ $stime = number_format_i18n( $row['ltime'], 4 );
83
+ $ltime = number_format_i18n( $row['ltime'], 10 );
84
+
85
+ echo '<tr>';
86
+ echo "<td valign='top' class='qm-ltr'>{$row['component']}</td>";
87
+
88
+ foreach ( $data['types'] as $type_name => $type_count ) {
89
+ if ( isset( $row['types'][$type_name] ) )
90
+ echo "<td valign='top'>{$row['types'][$type_name]}</td>";
91
+ else
92
+ echo "<td valign='top'>&nbsp;</td>";
93
+ }
94
+
95
+ echo "<td valign='top' title='{$ltime}'>{$stime}</td>";
96
+ echo '</tr>';
97
+
98
+ }
99
+
100
+ echo '</tbody>';
101
+ echo '<tfoot>';
102
+
103
+ $total_stime = number_format_i18n( $total_time, 4 );
104
+ $total_ltime = number_format_i18n( $total_time, 10 );
105
+
106
+ echo '<tr>';
107
+ echo '<td>&nbsp;</td>';
108
+
109
+ foreach ( $data['types'] as $type_name => $type_count )
110
+ echo '<td>' . number_format_i18n( $type_count ) . '</td>';
111
+
112
+ echo "<td title='{$total_ltime}'>{$total_stime}</td>";
113
+ echo '</tr>';
114
+ echo '</tfoot>';
115
+
116
+ } else {
117
+
118
+ echo '<tbody>';
119
+ echo '<tr>';
120
+ echo '<td colspan="3" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
121
+ echo '</tr>';
122
+ echo '</tbody>';
123
+
124
+ }
125
+
126
+ echo '</table>';
127
+ echo '</div>';
128
+
129
+ }
130
+
131
+ }
132
+
133
+ function register_qm_db_components( array $qm ) {
134
+ $qm['db_components'] = new QM_Component_DB_Components;
135
+ return $qm;
136
+ }
137
+
138
+ add_filter( 'query_monitor_components', 'register_qm_db_components', 35 );
components/db_queries.php ADDED
@@ -0,0 +1,506 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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
+ if ( !defined( 'SAVEQUERIES' ) )
18
+ define( 'SAVEQUERIES', true );
19
+ if ( !defined( 'QM_DB_EXPENSIVE' ) )
20
+ define( 'QM_DB_EXPENSIVE', 0.05 );
21
+
22
+ # QM_DB_LIMIT used to be a hard limit but proved to be more of an annoyance than anything. It now
23
+ # just adds a nag to the top of the query table. I might remove it altogether at some point.
24
+ if ( !defined( 'QM_DB_LIMIT' ) )
25
+ define( 'QM_DB_LIMIT', 100 );
26
+
27
+ class QM_Component_DB_Queries extends QM_Component {
28
+
29
+ public $id = 'db_queries';
30
+ public $db_objects = array();
31
+
32
+ function __construct() {
33
+ parent::__construct();
34
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 20 );
35
+ add_filter( 'query_monitor_title', array( $this, 'admin_title' ), 20 );
36
+ add_filter( 'query_monitor_class', array( $this, 'admin_class' ) );
37
+ }
38
+
39
+ function admin_title( array $title ) {
40
+ if ( isset( $this->data['dbs'] ) ) {
41
+ foreach ( $this->data['dbs'] as $db ) {
42
+ $title[] = sprintf(
43
+ _x( '%s<small>S</small>', 'database query time', 'query-monitor' ),
44
+ number_format_i18n( $db->total_time, 4 )
45
+ );
46
+ $title[] = sprintf(
47
+ _x( '%s<small>Q</small>', 'database query number', 'query-monitor' ),
48
+ number_format_i18n( $db->total_qs )
49
+ );
50
+ }
51
+ }
52
+ return $title;
53
+ }
54
+
55
+ function admin_class( array $class ) {
56
+
57
+ if ( $this->get_errors() )
58
+ $class[] = 'qm-error';
59
+ if ( $this->get_expensive() )
60
+ $class[] = 'qm-expensive';
61
+ return $class;
62
+
63
+ }
64
+
65
+ function admin_menu( array $menu ) {
66
+
67
+ if ( $errors = $this->get_errors() ) {
68
+ $menu[] = $this->menu( array(
69
+ 'id' => 'query-monitor-errors',
70
+ 'href' => '#qm-query-errors',
71
+ 'title' => sprintf( __( 'Database Errors (%s)', 'query-monitor' ), number_format_i18n( count( $errors ) ) )
72
+ ) );
73
+ }
74
+ if ( $expensive = $this->get_expensive() ) {
75
+ $menu[] = $this->menu( array(
76
+ 'id' => 'query-monitor-expensive',
77
+ 'href' => '#qm-query-expensive',
78
+ 'title' => sprintf( __( 'Slow Queries (%s)', 'query-monitor' ), number_format_i18n( count( $expensive ) ) )
79
+ ) );
80
+ }
81
+
82
+ if ( count( $this->data['dbs'] ) > 1 ) {
83
+ foreach ( $this->data['dbs'] as $name => $db ) {
84
+ $menu[] = $this->menu( array(
85
+ 'title' => sprintf( __( 'Queries (%s)', 'query-monitor' ), esc_html( $name ) ),
86
+ 'href' => sprintf( '#%s-%s', $this->id(), sanitize_title( $name ) ),
87
+ ) );
88
+ }
89
+ } else {
90
+ $menu[] = $this->menu( array(
91
+ 'title' => __( 'Queries', 'query-monitor' ),
92
+ 'href' => sprintf( '#%s-wpdb', $this->id() ),
93
+ ) );
94
+ }
95
+
96
+ return $menu;
97
+
98
+ }
99
+
100
+ function get_errors() {
101
+ if ( !empty( $this->data['errors'] ) )
102
+ return $this->data['errors'];
103
+ return false;
104
+ }
105
+
106
+ function get_expensive() {
107
+ if ( !empty( $this->data['expensive'] ) )
108
+ return $this->data['expensive'];
109
+ return false;
110
+ }
111
+
112
+ protected static function is_expensive( array $row ) {
113
+ return $row['ltime'] > QM_DB_EXPENSIVE;
114
+ }
115
+
116
+ function process() {
117
+
118
+ if ( !SAVEQUERIES )
119
+ return;
120
+
121
+ $this->data['total_qs'] = 0;
122
+ $this->data['total_time'] = 0;
123
+ $this->data['errors'] = array();
124
+
125
+ $this->db_objects = apply_filters( 'query_monitor_db_objects', array(
126
+ '$wpdb' => $GLOBALS['wpdb']
127
+ ) );
128
+
129
+ foreach ( $this->db_objects as $name => $db ) {
130
+ if ( is_a( $db, 'wpdb' ) )
131
+ $this->process_db_object( $name, $db );
132
+ }
133
+
134
+ }
135
+
136
+ function output_html( array $args, array $data ) {
137
+
138
+ if ( empty( $data['dbs'] ) )
139
+ return;
140
+
141
+ if ( !empty( $data['errors'] ) ) {
142
+
143
+ echo '<div class="qm qm-queries" id="qm-query-errors">';
144
+ echo '<table cellspacing="0">';
145
+ echo '<thead>';
146
+ echo '<tr>';
147
+ echo '<th colspan="4">' . __( 'Database Errors', 'query-monitor' ) . '</th>';
148
+ echo '</tr>';
149
+ echo '<tr>';
150
+ echo '<th>' . __( 'Query', 'query-monitor' ) . '</th>';
151
+ echo '<th>' . __( 'Call Stack', 'query-monitor' ) . '</th>';
152
+ echo '<th>' . __( 'Component', 'query-monitor' ) . '</th>';
153
+ echo '<th>' . __( 'Error', 'query-monitor' ) . '</th>';
154
+ echo '</tr>';
155
+ echo '</thead>';
156
+ echo '<tbody>';
157
+
158
+ foreach ( $data['errors'] as $row )
159
+ $this->output_query_row( $row, array( 'sql', 'stack', 'component', 'result' ) );
160
+
161
+ echo '</tbody>';
162
+ echo '</table>';
163
+ echo '</div>';
164
+
165
+ }
166
+
167
+ if ( !empty( $data['expensive'] ) ) {
168
+
169
+ $dp = strlen( substr( strrchr( QM_DB_EXPENSIVE, '.' ), 1 ) );
170
+
171
+ echo '<div class="qm qm-queries" id="qm-query-expensive">';
172
+ echo '<table cellspacing="0">';
173
+ echo '<thead>';
174
+ echo '<tr>';
175
+ 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>';
176
+ echo '</tr>';
177
+ echo '<tr>';
178
+ echo '<th scope="col">' . __( 'Query', 'query-monitor' ) . '</th>';
179
+ echo '<th scope="col">' . __( 'Caller', 'query-monitor' ) . '</th>';
180
+
181
+ if ( isset( $data['expensive'][0]['component'] ) )
182
+ echo '<th scope="col">' . __( 'Component', 'query-monitor' ) . '</th>';
183
+
184
+ if ( isset( $data['expensive'][0]['result'] ) )
185
+ echo '<th scope="col">' . __( 'Affected Rows', 'query-monitor' ) . '</th>';
186
+
187
+ echo '<th>' . __( 'Time', 'query-monitor' ) . '</th>';
188
+ echo '</tr>';
189
+ echo '</thead>';
190
+ echo '<tbody>';
191
+
192
+ foreach ( $data['expensive'] as $row )
193
+ $this->output_query_row( $row, array( 'sql', 'caller', 'component', 'result', 'time' ) );
194
+
195
+ echo '</tbody>';
196
+ echo '</table>';
197
+ echo '</div>';
198
+
199
+ }
200
+
201
+ foreach ( $data['dbs'] as $name => $db )
202
+ $this->output_queries( $name, $db, $data );
203
+
204
+ }
205
+
206
+ function log_type( $type ) {
207
+
208
+ if ( isset( $this->data['types'][$type] ) )
209
+ $this->data['types'][$type]++;
210
+ else
211
+ $this->data['types'][$type] = 1;
212
+
213
+ }
214
+
215
+ function log_caller( $caller, $ltime, $type ) {
216
+
217
+ if ( !isset( $this->data['times'][$caller] ) ) {
218
+ $this->data['times'][$caller] = array(
219
+ 'caller' => $caller,
220
+ 'calls' => 0,
221
+ 'ltime' => 0,
222
+ 'types' => array()
223
+ );
224
+ }
225
+
226
+ $this->data['times'][$caller]['calls']++;
227
+ $this->data['times'][$caller]['ltime'] += $ltime;
228
+
229
+ if ( isset( $this->data['times'][$caller]['types'][$type] ) )
230
+ $this->data['times'][$caller]['types'][$type]++;
231
+ else
232
+ $this->data['times'][$caller]['types'][$type] = 1;
233
+
234
+ }
235
+
236
+ function log_component( $component, $ltime, $type ) {
237
+
238
+ if ( !isset( $this->data['component_times'][$component->name] ) ) {
239
+ $this->data['component_times'][$component->name] = array(
240
+ 'component' => $component->name,
241
+ 'calls' => 0,
242
+ 'ltime' => 0,
243
+ 'types' => array()
244
+ );
245
+ }
246
+
247
+ $this->data['component_times'][$component->name]['calls']++;
248
+ $this->data['component_times'][$component->name]['ltime'] += $ltime;
249
+
250
+ if ( isset( $this->data['component_times'][$component->name]['types'][$type] ) )
251
+ $this->data['component_times'][$component->name]['types'][$type]++;
252
+ else
253
+ $this->data['component_times'][$component->name]['types'][$type] = 1;
254
+
255
+ }
256
+
257
+ protected static function query_compat( array & $query ) {
258
+
259
+ list( $query['sql'], $query['ltime'], $query['stack'] ) = $query;
260
+
261
+ }
262
+
263
+ function process_db_object( $id, wpdb $db ) {
264
+
265
+ $rows = array();
266
+ $types = array();
267
+ $total_time = 0;
268
+
269
+ foreach ( (array) $db->queries as $query ) {
270
+
271
+ if ( ! isset( $query['sql'] ) )
272
+ self::query_compat( $query );
273
+
274
+ if ( false !== strpos( $query['stack'], 'wp_admin_bar' ) and !isset( $_REQUEST['qm_display_admin_bar'] ) )
275
+ continue;
276
+
277
+ $sql = $query['sql'];
278
+ $ltime = $query['ltime'];
279
+ $stack = $query['stack'];
280
+ $has_component = isset( $query['trace'] );
281
+ $has_results = isset( $query['result'] );
282
+
283
+ if ( $has_results )
284
+ $result = $query['result'];
285
+ else
286
+ $result = null;
287
+
288
+ $total_time += $ltime;
289
+
290
+ if ( isset( $query['trace'] ) )
291
+ $component = QM_Util::get_backtrace_component( $query['trace'] );
292
+ else
293
+ $component = null;
294
+
295
+ # @TODO we should grab this from the trace instead for increased accuracy in case
296
+ # the caller contains multiple comma separated arguments (see QM_Backtrace::$show_args)
297
+ $callers = explode( ',', $stack );
298
+ $caller = trim( end( $callers ) );
299
+
300
+ if ( false !== strpos( $caller, '(' ) )
301
+ $caller_name = strstr( $caller, '(', true ) . '()';
302
+ else
303
+ $caller_name = $caller;
304
+
305
+ # @TODO this formatting should move to JIT when outputting as html
306
+ $sql = QM_Util::format_sql( $sql );
307
+ $type = preg_split( '/\b/', $sql );
308
+ $type = strtoupper( $type[1] );
309
+
310
+ $this->log_type( $type );
311
+ $this->log_caller( $caller_name, $ltime, $type );
312
+
313
+ if ( $component )
314
+ $this->log_component( $component, $ltime, $type );
315
+
316
+ if ( !isset( $types[$type]['total'] ) )
317
+ $types[$type]['total'] = 1;
318
+ else
319
+ $types[$type]['total']++;
320
+
321
+ if ( !isset( $types[$type]['callers'][$caller] ) )
322
+ $types[$type]['callers'][$caller] = 1;
323
+ else
324
+ $types[$type]['callers'][$caller]++;
325
+
326
+ $row = compact( 'caller', 'caller_name', 'stack', 'sql', 'ltime', 'result', 'type', 'component' );
327
+
328
+ if ( is_wp_error( $result ) )
329
+ $this->data['errors'][] = $row;
330
+
331
+ if ( self::is_expensive( $row ) )
332
+ $this->data['expensive'][] = $row;
333
+
334
+ $rows[] = $row;
335
+
336
+ }
337
+
338
+ if ( isset( $_REQUEST['qm_sort'] ) and ( 'time' == $_REQUEST['qm_sort'] ) )
339
+ usort( $rows, 'QM_Util::sort' );
340
+
341
+ $total_qs = count( $rows );
342
+
343
+ $this->data['total_qs'] += $total_qs;
344
+ $this->data['total_time'] += $total_time;
345
+
346
+ # @TODO put errors in here too:
347
+ # @TODO proper class instead of (object)
348
+ $this->data['dbs'][$id] = (object) compact( 'rows', 'types', 'has_results', 'has_component', 'total_time', 'total_qs' );
349
+
350
+ }
351
+
352
+ function output_queries( $name, stdClass $db, array $data ) {
353
+
354
+ $max_exceeded = $db->total_qs > QM_DB_LIMIT;
355
+
356
+ $span = 3;
357
+
358
+ if ( $db->has_results )
359
+ $span++;
360
+ if ( $db->has_component )
361
+ $span++;
362
+
363
+ echo '<div class="qm qm-queries" id="' . $this->id() . '-' . sanitize_title( $name ) . '">';
364
+ echo '<table cellspacing="0">';
365
+ echo '<thead>';
366
+ echo '<tr>';
367
+ echo '<th colspan="' . $span . '" class="qm-ltr">' . $name . '</th>';
368
+ echo '</tr>';
369
+
370
+ if ( $max_exceeded ) {
371
+ echo '<tr>';
372
+ echo '<td colspan="' . $span . '" class="qm-expensive">' . sprintf( __( '%1$s %2$s queries were performed on this page load. Crikey!', 'query-monitor' ),
373
+ number_format_i18n( $db->total_qs ),
374
+ $name,
375
+ number_format_i18n( QM_DB_LIMIT )
376
+ ) . '</td>';
377
+ echo '</tr>';
378
+ }
379
+
380
+ echo '<tr>';
381
+ echo '<th scope="col">' . __( 'Query', 'query-monitor' ) . $this->build_filter( 'type', array_keys( $db->types ) ) . '</th>';
382
+ echo '<th scope="col">' . __( 'Caller', 'query-monitor' ) . $this->build_filter( 'caller', array_keys( $data['times'] ) ) . '</th>';
383
+
384
+ if ( $db->has_component )
385
+ echo '<th scope="col">' . __( 'Component', 'query-monitor' ) . $this->build_filter( 'component', array_keys( $data['component_times'] ) ) . '</th>';
386
+
387
+ if ( $db->has_results )
388
+ echo '<th scope="col">' . __( 'Affected Rows', 'query-monitor' ) . '</th>';
389
+
390
+ echo '<th scope="col">' . __( 'Time', 'query-monitor' ) . '</th>';
391
+ echo '</tr>';
392
+ echo '</thead>';
393
+
394
+ if ( !empty( $db->rows ) ) {
395
+
396
+ echo '<tbody>';
397
+
398
+ foreach ( $db->rows as $i => $row )
399
+ $this->output_query_row( $row, array( 'sql', 'caller', 'component', 'result', 'time' ) );
400
+
401
+ echo '</tbody>';
402
+ echo '<tfoot>';
403
+
404
+ $total_stime = number_format_i18n( $db->total_time, 4 );
405
+ $total_ltime = number_format_i18n( $db->total_time, 10 );
406
+
407
+ echo '<tr>';
408
+ echo '<td valign="top" colspan="' . ( $span - 1 ) . '">' . sprintf( __( 'Total Queries: %s', 'query-monitor' ), number_format_i18n( $db->total_qs ) ) . '</td>';
409
+ echo "<td valign='top' title='{$total_ltime}'>{$total_stime}</td>";
410
+ echo '</tr>';
411
+
412
+ echo '<tr class="qm-items-shown qm-hide">';
413
+ 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>';
414
+ echo "<td valign='top' class='qm-items-time'>{$total_stime}</td>";
415
+ echo '</tr>';
416
+ echo '</tfoot>';
417
+
418
+ } else {
419
+
420
+ echo '<tbody>';
421
+ echo '<tr>';
422
+ echo '<td colspan="' . $span . '" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
423
+ echo '</tr>';
424
+ echo '</tbody>';
425
+
426
+ }
427
+
428
+ echo '</table>';
429
+ echo '</div>';
430
+
431
+ }
432
+
433
+ function output_query_row( array $row, array $cols ) {
434
+
435
+ $cols = array_flip( $cols );
436
+
437
+ if ( is_null( $row['component'] ) )
438
+ unset( $cols['component'] );
439
+ if ( is_null( $row['result'] ) )
440
+ unset( $cols['result'] );
441
+
442
+ $row_attr = array();
443
+ $stime = number_format_i18n( $row['ltime'], 4 );
444
+ $ltime = number_format_i18n( $row['ltime'], 10 );
445
+ $td = self::is_expensive( $row ) ? ' qm-expensive' : '';
446
+
447
+ if ( 'SELECT' != $row['type'] )
448
+ $row['sql'] = "<span class='qm-nonselectsql'>{$row['sql']}</span>";
449
+
450
+ if ( is_wp_error( $row['result'] ) ) {
451
+ $error = $row['result']->get_error_message();
452
+ $result = "<td valign='top' class='qm-row-result qm-row-error'>{$error}</td>\n";
453
+ $row_attr['class'] = 'qm-warn';
454
+ } else {
455
+ $result = "<td valign='top' class='qm-row-result'>{$row['result']}</td>\n";
456
+ }
457
+
458
+ if ( isset( $cols['sql'] ) )
459
+ $row_attr['data-qm-db_queries-type'] = $row['type'];
460
+ if ( isset( $cols['component'] ) )
461
+ $row_attr['data-qm-db_queries-component'] = $row['component']->name;
462
+ if ( isset( $cols['caller'] ) )
463
+ $row_attr['data-qm-db_queries-caller'] = $row['caller_name'];
464
+ if ( isset( $cols['time'] ) )
465
+ $row_attr['data-qm-db_queries-time'] = $row['ltime'];
466
+
467
+ $stack = esc_attr( $row['stack'] );
468
+ $attr = '';
469
+
470
+ foreach ( $row_attr as $a => $v )
471
+ $attr .= ' ' . $a . '="' . esc_attr( $v ) . '"';
472
+
473
+ echo "<tr{$attr}>";
474
+
475
+ if ( isset( $cols['sql'] ) )
476
+ echo "<td valign='top' class='qm-row-sql qm-ltr qm-sql'>{$row['sql']}</td>";
477
+
478
+ if ( isset( $cols['caller'] ) )
479
+ echo "<td valign='top' class='qm-row-caller qm-ltr' title='{$stack}'>{$row['caller']}</td>";
480
+
481
+ if ( isset( $cols['stack'] ) ) {
482
+ $stack = implode( '<br/>', $row['stack'] );
483
+ echo "<td valign='top' class='qm-row-caller qm-row-stack qm-ltr'>{$stack}</td>";
484
+ }
485
+
486
+ if ( isset( $cols['component'] ) )
487
+ echo "<td valign='top' class='qm-row-component'>{$row['component']->name}</td>\n";
488
+
489
+ if ( isset( $cols['result'] ) )
490
+ echo $result;
491
+
492
+ if ( isset( $cols['time'] ) )
493
+ echo "<td valign='top' title='{$ltime}' class='qm-row-time{$td}'>{$stime}</td>\n";
494
+
495
+ echo '</tr>';
496
+
497
+ }
498
+
499
+ }
500
+
501
+ function register_qm_db_queries( array $qm ) {
502
+ $qm['db_queries'] = new QM_Component_DB_Queries;
503
+ return $qm;
504
+ }
505
+
506
+ add_filter( 'query_monitor_components', 'register_qm_db_queries', 20 );
components/environment.php ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Environment extends QM_Component {
18
+
19
+ var $id = 'environment';
20
+ var $php_vars = array(
21
+ 'max_execution_time',
22
+ 'memory_limit',
23
+ 'upload_max_filesize',
24
+ 'post_max_size',
25
+ 'display_errors',
26
+ 'log_errors',
27
+ # 'error_log',
28
+ );
29
+
30
+ function __construct() {
31
+
32
+ global $wpdb;
33
+
34
+ parent::__construct();
35
+
36
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 110 );
37
+
38
+ # If QueryMonitorDB is in place then we'll use the values which were
39
+ # caught early before any plugins had a chance to alter them
40
+
41
+ foreach ( $this->php_vars as $setting ) {
42
+ if ( isset( $wpdb->qm_php_vars ) and isset( $wpdb->qm_php_vars[$setting] ) )
43
+ $val = $wpdb->qm_php_vars[$setting];
44
+ else
45
+ $val = ini_get( $setting );
46
+ $this->data['php']['variables'][$setting]['before'] = $val;
47
+ }
48
+
49
+ if ( isset( $wpdb->qm_php_vars ) and isset( $wpdb->qm_php_vars['error_reporting'] ) )
50
+ $val = $wpdb->qm_php_vars['error_reporting'];
51
+ else
52
+ $val = implode( '<br/>', $this->get_error_reporting() );
53
+
54
+ $this->data['php']['variables']['error_reporting']['before'] = $val;
55
+
56
+ }
57
+
58
+ function get_error_reporting() {
59
+
60
+ # @TODO move this into QM_Util and call it in QueryMonitorDB too
61
+
62
+ $error_reporting = error_reporting();
63
+ $levels = array();
64
+
65
+ $constants = array(
66
+ 'E_ERROR',
67
+ 'E_WARNING',
68
+ 'E_PARSE',
69
+ 'E_NOTICE',
70
+ 'E_USER_ERROR',
71
+ 'E_USER_WARNING',
72
+ 'E_USER_NOTICE',
73
+ 'E_STRICT',
74
+ 'E_RECOVERABLE_ERROR',
75
+ 'E_DEPRECATED',
76
+ 'E_USER_DEPRECATED',
77
+ 'E_ALL'
78
+ );
79
+
80
+ foreach ( $constants as $level ) {
81
+ if ( defined( $level ) ) {
82
+ $c = constant( $level );
83
+ if ( $error_reporting & $c )
84
+ $levels[$c] = $level;
85
+ }
86
+ }
87
+
88
+ return $levels;
89
+
90
+ }
91
+
92
+ function admin_menu( array $menu ) {
93
+
94
+ $menu[] = $this->menu( array(
95
+ 'title' => __( 'Environment', 'query-monitor' )
96
+ ) );
97
+ return $menu;
98
+
99
+ }
100
+
101
+ function process() {
102
+
103
+ global $wp_version, $blog_id;
104
+
105
+ $mysql_vars = array(
106
+ 'key_buffer_size' => true, # Key cache size limit
107
+ 'max_allowed_packet' => false, # Individual query size limit
108
+ 'max_connections' => false, # Max number of client connections
109
+ 'query_cache_limit' => true, # Individual query cache size limit
110
+ 'query_cache_size' => true, # Total cache size limit
111
+ 'query_cache_type' => 'ON' # Query cache on or off
112
+ );
113
+ $php_u = '';
114
+
115
+ if ( $dbq = $this->get_component( 'db_queries' ) ) {
116
+
117
+ foreach ( $dbq->db_objects as $id => $db ) {
118
+
119
+ if ( !is_a( $db, 'wpdb' ) )
120
+ continue;
121
+
122
+ $variables = $db->get_results( "
123
+ SHOW VARIABLES
124
+ WHERE Variable_name IN ( '" . implode( "', '", array_keys( $mysql_vars ) ) . "' )
125
+ " );
126
+
127
+ $this->data['db'][$id] = array(
128
+ 'version' => mysql_get_server_info( $db->dbh ),
129
+ 'user' => $db->dbuser,
130
+ 'host' => $db->dbhost,
131
+ 'name' => $db->dbname,
132
+ 'vars' => $mysql_vars,
133
+ 'variables' => $variables
134
+ );
135
+
136
+ }
137
+
138
+ }
139
+
140
+ if ( function_exists( 'posix_getpwuid' ) ) {
141
+
142
+ $u = posix_getpwuid( posix_getuid() );
143
+ $g = posix_getgrgid( $u['gid'] );
144
+ $php_u = esc_html( $u['name'] . ':' . $g['name'] );
145
+
146
+ } else if ( isset( $_SERVER['USER'] ) ) {
147
+
148
+ $php_u = esc_html( $_SERVER['USER'] );
149
+
150
+ } else if ( function_exists( 'exec' ) ) {
151
+
152
+ $php_u = esc_html( exec( 'whoami' ) );
153
+
154
+ }
155
+
156
+ if ( empty( $php_u ) )
157
+ $php_u = '<em>' . __( 'Unknown', 'query-monitor' ) . '</em>';
158
+
159
+ $this->data['php']['version'] = phpversion();
160
+ $this->data['php']['user'] = $php_u;
161
+
162
+ foreach ( $this->php_vars as $setting )
163
+ $this->data['php']['variables'][$setting]['after'] = ini_get( $setting );
164
+
165
+ $this->data['php']['variables']['error_reporting']['after'] = implode( '<br/>', $this->get_error_reporting() );
166
+
167
+ $this->data['wp'] = array(
168
+ 'version' => $wp_version,
169
+ 'WP_DEBUG' => QM_Util::format_bool_constant( 'WP_DEBUG' ),
170
+ 'WP_LOCAL_DEV' => QM_Util::format_bool_constant( 'WP_LOCAL_DEV' ),
171
+ );
172
+
173
+ if ( is_multisite() )
174
+ $this->data['wp']['blog_id'] = $blog_id;
175
+
176
+ $server = explode( ' ', $_SERVER['SERVER_SOFTWARE'] );
177
+ $server = explode( '/', reset( $server ) );
178
+
179
+ if ( isset( $server[1] ) )
180
+ $server_version = $server[1];
181
+ else
182
+ $server_version = '<em>' . __( 'Unknown', 'query-monitor' ) . '</em>';
183
+
184
+ $this->data['server'] = array(
185
+ 'name' => $server[0],
186
+ 'version' => $server_version,
187
+ 'address' => $_SERVER['SERVER_ADDR'],
188
+ 'host' => php_uname( 'n' )
189
+ );
190
+
191
+ }
192
+
193
+ function output_html( array $args, array $data ) {
194
+
195
+ echo '<div class="qm" id="' . $args['id'] . '">';
196
+ echo '<table cellspacing="0">';
197
+ echo '<thead>';
198
+ echo '<tr>';
199
+ echo '<th colspan="3">' . __( 'Environment', 'query-monitor' ) . '</th>';
200
+ echo '</tr>';
201
+ echo '</thead>';
202
+ echo '<tbody>';
203
+
204
+ echo '<tr>';
205
+ echo '<td rowspan="' . ( 2 + count( $data['php']['variables'] ) ) . '">PHP</td>';
206
+ echo '<td>version</td>';
207
+ echo "<td>{$data['php']['version']}</td>";
208
+ echo '</tr>';
209
+ echo '<tr>';
210
+ echo '<td>user</td>';
211
+ echo "<td>{$data['php']['user']}</td>";
212
+ echo '</tr>';
213
+
214
+ foreach ( $data['php']['variables'] as $key => $val ) {
215
+
216
+ $append = '';
217
+
218
+ if ( $val['after'] != $val['before'] )
219
+ $append .= '<br /><span class="qm-info">' . sprintf( __( 'Overridden at runtime from %s', 'query-monitor' ), $val['before'] ) . '</span>';
220
+
221
+ echo '<tr>';
222
+ echo "<td>{$key}</td>";
223
+ echo "<td>{$val['after']}{$append}</td>";
224
+ echo '</tr>';
225
+ }
226
+
227
+ if ( isset( $data['db'] ) ) {
228
+
229
+ foreach ( $data['db'] as $id => $db ) {
230
+
231
+ if ( 1 == count( $data['db'] ) )
232
+ $name = 'MySQL';
233
+ else
234
+ $name = $id . '<br />MySQL';
235
+
236
+ echo '<tr>';
237
+ echo '<td rowspan="' . ( 4 + count( $db['variables'] ) ) . '">' . $name . '</td>';
238
+ echo '<td>version</td>';
239
+ echo '<td>' . $db['version'] . '</td>';
240
+ echo '</tr>';
241
+
242
+ echo '<tr>';
243
+ echo '<td>user</td>';
244
+ echo '<td>' . $db['user'] . '</td>';
245
+ echo '</tr>';
246
+
247
+ echo '<tr>';
248
+ echo '<td>host</td>';
249
+ echo '<td>' . $db['host'] . '</td>';
250
+ echo '</tr>';
251
+
252
+ echo '<tr>';
253
+ echo '<td>database</td>';
254
+ echo '<td>' . $db['name'] . '</td>';
255
+ echo '</tr>';
256
+
257
+ echo '<tr>';
258
+
259
+ $first = true;
260
+ $warn = __( "This value may not be optimal. Check the recommended configuration for '%s'.", 'query-monitor' );
261
+ $search = __( 'https://www.google.com/search?q=mysql+performance+%s', 'query-monitor' );
262
+
263
+ foreach ( $db['variables'] as $setting ) {
264
+
265
+ $key = $setting->Variable_name;
266
+ $val = $setting->Value;
267
+ $prepend = '';
268
+ $show_warning = false;
269
+
270
+ if ( ( true === $db['vars'][$key] ) and empty( $val ) )
271
+ $show_warning = true;
272
+ else if ( is_string( $db['vars'][$key] ) and ( $val !== $db['vars'][$key] ) )
273
+ $show_warning = true;
274
+
275
+ if ( $show_warning )
276
+ $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>';
277
+
278
+ if ( is_numeric( $val ) and ( $val >= ( 1024*1024 ) ) )
279
+ $prepend .= '<br /><span class="qm-info">~' . size_format( $val ) . '</span>';
280
+
281
+ $class = ( $show_warning ) ? 'qm-warn' : '';
282
+
283
+ if ( !$first )
284
+ echo "<tr class='{$class}'>";
285
+
286
+ $key = esc_html( $key );
287
+ $val = esc_html( $val );
288
+
289
+ echo "<td>{$key}</td>";
290
+ echo "<td>{$val}{$prepend}</td>";
291
+
292
+ echo '</tr>';
293
+
294
+ $first = false;
295
+
296
+ }
297
+
298
+ }
299
+
300
+ }
301
+
302
+ echo '<tr>';
303
+ echo '<td rowspan="' . count( $data['wp'] ). '">WordPress</td>';
304
+
305
+ $first = true;
306
+
307
+ foreach ( $data['wp'] as $key => $val ) {
308
+
309
+ if ( !$first )
310
+ echo "<tr>";
311
+
312
+ echo "<td>{$key}</td>";
313
+ echo "<td>{$val}</td>";
314
+ echo '</tr>';
315
+
316
+ $first = false;
317
+
318
+ }
319
+
320
+ echo '<tr>';
321
+ echo '<td rowspan="4">' . __( 'Server', 'query-monitor' ) . '</td>';
322
+ echo '<td>software</td>';
323
+ echo "<td>{$data['server']['name']}</td>";
324
+ echo '</tr>';
325
+
326
+ echo '<tr>';
327
+ echo '<td>version</td>';
328
+ echo "<td>{$data['server']['version']}</td>";
329
+ echo '</tr>';
330
+
331
+ echo '<tr>';
332
+ echo '<td>address</td>';
333
+ echo "<td>{$data['server']['address']}</td>";
334
+ echo '</tr>';
335
+
336
+ echo '<tr>';
337
+ echo '<td>host</td>';
338
+ echo "<td>{$data['server']['host']}</td>";
339
+ echo '</tr>';
340
+
341
+ echo '</tbody>';
342
+ echo '</table>';
343
+ echo '</div>';
344
+
345
+ }
346
+
347
+ }
348
+
349
+ function register_qm_environment( array $qm ) {
350
+ $qm['environment'] = new QM_Component_Environment;
351
+ return $qm;
352
+ }
353
+
354
+ add_filter( 'query_monitor_components', 'register_qm_environment', 90 );
components/hooks.php ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Hooks extends QM_Component {
18
+
19
+ var $id = 'hooks';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 80 );
24
+ }
25
+
26
+ function admin_menu( array $menu ) {
27
+
28
+ $menu[] = $this->menu( array(
29
+ 'title' => __( 'Hooks', 'query-monitor' )
30
+ ) );
31
+ return $menu;
32
+
33
+ }
34
+
35
+ function process() {
36
+
37
+ global $wp_actions, $wp_filter;
38
+
39
+ if ( is_admin() and ( $admin = $this->get_component( 'admin' ) ) )
40
+ $this->data['screen'] = $admin->data['base'];
41
+ else
42
+ $this->data['screen'] = '';
43
+
44
+ $hooks = $parts = $components = array();
45
+
46
+ # @TODO why am i doing this here?:
47
+ if ( is_multisite() and is_network_admin() )
48
+ $this->data['screen'] = preg_replace( '|-network$|', '', $this->data['screen'] );
49
+
50
+ foreach ( $wp_actions as $name => $count ) {
51
+
52
+ $actions = array();
53
+ # @TODO better variable name:
54
+ $c = array();
55
+
56
+ if ( isset( $wp_filter[$name] ) ) {
57
+
58
+ foreach( $wp_filter[$name] as $priority => $callbacks ) {
59
+
60
+ foreach ( $callbacks as $callback ) {
61
+
62
+ $callback = QM_Util::populate_callback( $callback );
63
+
64
+ if ( isset( $callback['component'] ) )
65
+ $c[$callback['component']->name] = $callback['component']->name;
66
+
67
+ $actions[] = array(
68
+ 'priority' => $priority,
69
+ 'callback' => $callback,
70
+ );
71
+
72
+ }
73
+
74
+ }
75
+
76
+ }
77
+
78
+ # @TODO better variable name:
79
+ $p = array_filter( preg_split( '/[_\/-]/', $name ) );
80
+ $parts = array_merge( $parts, $p );
81
+ $components = array_merge( $components, $c );
82
+
83
+ $hooks[$name] = array(
84
+ 'name' => $name,
85
+ 'actions' => $actions,
86
+ 'parts' => $p,
87
+ 'components' => $c,
88
+ );
89
+
90
+ }
91
+
92
+ $this->data['hooks'] = $hooks;
93
+ $this->data['parts'] = array_unique( array_filter( $parts ) );
94
+ $this->data['components'] = array_unique( array_filter( $components ) );
95
+
96
+ }
97
+
98
+ function output_html( array $args, array $data ) {
99
+
100
+ $row_attr = array();
101
+
102
+ echo '<div class="qm" id="' . $args['id'] . '">';
103
+ echo '<table cellspacing="0">';
104
+ echo '<thead>';
105
+ echo '<tr>';
106
+ echo '<th>' . __( 'Hook', 'query-monitor' ) . $this->build_filter( 'name', $data['parts'] ) . '</th>';
107
+ echo '<th colspan="2">' . __( 'Actions', 'query-monitor' ) . '</th>';
108
+ echo '<th>' . __( 'Action Component', 'query-monitor' ) . $this->build_filter( 'component', $data['components'] ) . '</th>';
109
+ echo '</tr>';
110
+ echo '</thead>';
111
+ echo '<tbody>';
112
+
113
+ foreach ( $data['hooks'] as $hook ) {
114
+
115
+ if ( !empty( $data['screen'] ) ) {
116
+
117
+ if ( false !== strpos( $hook['name'], $data['screen'] . '.php' ) )
118
+ $hook['name'] = str_replace( '-' . $data['screen'] . '.php', '-<span class="qm-current">' . $data['screen'] . '.php</span>', $hook['name'] );
119
+ else
120
+ $hook['name'] = str_replace( '-' . $data['screen'], '-<span class="qm-current">' . $data['screen'] . '</span>', $hook['name'] );
121
+
122
+ }
123
+
124
+ $row_attr['data-qm-hooks-name'] = implode( ' ', $hook['parts'] );
125
+ $row_attr['data-qm-hooks-component'] = implode( ' ', $hook['components'] );
126
+
127
+ $attr = '';
128
+
129
+ if ( !empty( $hook['actions'] ) )
130
+ $rowspan = count( $hook['actions'] );
131
+ else
132
+ $rowspan = 1;
133
+
134
+ foreach ( $row_attr as $a => $v )
135
+ $attr .= ' ' . $a . '="' . esc_attr( $v ) . '"';
136
+
137
+ echo "<tr{$attr}>";
138
+
139
+ echo "<td valign='top' rowspan='{$rowspan}'>{$hook['name']}</td>";
140
+ if ( !empty( $hook['actions'] ) ) {
141
+
142
+ $first = true;
143
+
144
+ foreach ( $hook['actions'] as $action ) {
145
+
146
+ if ( isset( $action['callback']['component'] ) )
147
+ $component = $action['callback']['component']->name;
148
+ else
149
+ $component = '';
150
+
151
+ if ( !$first )
152
+ echo "<tr{$attr}>";
153
+
154
+ echo '<td valign="top" class="qm-priority">' . $action['priority'] . '</td>';
155
+ echo '<td valign="top" class="qm-ltr">';
156
+ echo esc_html( $action['callback']['name'] );
157
+ echo '</td>';
158
+ echo '<td valign="top">';
159
+ echo esc_html( $component );
160
+ echo '</td>';
161
+ echo '</tr>';
162
+ $first = false;
163
+ }
164
+
165
+ } else {
166
+ echo '<td colspan="2">&nbsp;</td>';
167
+ echo '<td>&nbsp;</td>';
168
+ }
169
+ echo '</tr>';
170
+ }
171
+
172
+ echo '</tbody>';
173
+ echo '</table>';
174
+ echo '</div>';
175
+
176
+ }
177
+
178
+ }
179
+
180
+ function register_qm_hooks( array $qm ) {
181
+ $qm['hooks'] = new QM_Component_Hooks;
182
+ return $qm;
183
+ }
184
+
185
+ add_filter( 'query_monitor_components', 'register_qm_hooks', 80 );
components/http.php ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_HTTP extends QM_Component {
18
+
19
+ var $id = 'http';
20
+ var $http = array();
21
+
22
+ function __construct() {
23
+
24
+ parent::__construct();
25
+
26
+ add_action( 'http_api_debug', array( $this, 'http_debug' ), 99, 5 );
27
+ add_filter( 'http_request_args', array( $this, 'http_request' ), 99, 2 );
28
+ add_filter( 'http_response', array( $this, 'http_response' ), 99, 3 );
29
+ # http://core.trac.wordpress.org/ticket/25747
30
+ add_filter( 'pre_http_request', array( $this, 'http_response' ), 99, 3 );
31
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 60 );
32
+
33
+ }
34
+
35
+ function http_request( array $args, $url ) {
36
+ $m_start = microtime( true );
37
+ $key = $m_start . $url;
38
+ $this->data['http'][$key] = array(
39
+ 'url' => $url,
40
+ 'args' => $args,
41
+ 'start' => $m_start,
42
+ 'trace' => new QM_Backtrace
43
+ );
44
+ $args['_qm_key'] = $key;
45
+ return $args;
46
+ }
47
+
48
+ function http_debug( $param, $action ) {
49
+
50
+ switch ( $action ) {
51
+
52
+ case 'response':
53
+
54
+ $fga = func_get_args();
55
+
56
+ list( $response, $action, $class ) = $fga;
57
+
58
+ # http://core.trac.wordpress.org/ticket/18732
59
+ if ( isset( $fga[3] ) )
60
+ $args = $fga[3];
61
+ if ( isset( $fga[4] ) )
62
+ $url = $fga[4];
63
+ if ( !isset( $args['_qm_key'] ) )
64
+ return;
65
+
66
+ if ( !empty( $class ) )
67
+ $this->data['http'][$args['_qm_key']]['transport'] = str_replace( 'wp_http_', '', strtolower( $class ) );
68
+ else
69
+ $this->data['http'][$args['_qm_key']]['transport'] = false;
70
+
71
+ if ( is_wp_error( $response ) )
72
+ $this->http_response( $response, $args, $url );
73
+
74
+ break;
75
+
76
+ case 'transports_list':
77
+ # Nothing
78
+ break;
79
+
80
+ }
81
+
82
+ }
83
+
84
+ function http_response( $response, array $args, $url ) {
85
+ $this->data['http'][$args['_qm_key']]['end'] = microtime( true );
86
+ $this->data['http'][$args['_qm_key']]['response'] = $response;
87
+ return $response;
88
+ }
89
+
90
+ function admin_menu( array $menu ) {
91
+
92
+ $count = isset( $this->data['http'] ) ? count( $this->data['http'] ) : 0;
93
+
94
+ $title = ( empty( $count ) )
95
+ ? __( 'HTTP Requests', 'query-monitor' )
96
+ : __( 'HTTP Requests (%s)', 'query-monitor' );
97
+
98
+ $menu[] = $this->menu( array(
99
+ 'title' => sprintf( $title, number_format_i18n( $count ) )
100
+ ) );
101
+ return $menu;
102
+
103
+ }
104
+
105
+ function output_html( array $args, array $data ) {
106
+
107
+ $total_time = 0;
108
+
109
+ echo '<div class="qm" id="' . $args['id'] . '">';
110
+ echo '<table cellspacing="0">';
111
+ echo '<thead>';
112
+ echo '<tr>';
113
+ echo '<th>' . __( 'HTTP Request', 'query-monitor' ) . '</th>';
114
+ echo '<th>' . __( 'Response', 'query-monitor' ) . '</th>';
115
+ echo '<th>' . __( 'Transport', 'query-monitor' ) . '</th>';
116
+ echo '<th>' . __( 'Call Stack', 'query-monitor' ) . '</th>';
117
+ echo '<th>' . __( 'Component', 'query-monitor' ) . '</th>';
118
+ echo '<th>' . __( 'Timeout', 'query-monitor' ) . '</th>';
119
+ echo '<th>' . __( 'Time', 'query-monitor' ) . '</th>';
120
+ echo '</tr>';
121
+ echo '</thead>';
122
+
123
+ if ( !empty( $data['http'] ) ) {
124
+
125
+ echo '<tbody>';
126
+
127
+ foreach ( $data['http'] as $row ) {
128
+ $funcs = array();
129
+
130
+ if ( isset( $row['response'] ) ) {
131
+
132
+ $ltime = ( $row['end'] - $row['start'] );
133
+ $total_time += $ltime;
134
+ $stime = number_format_i18n( $ltime, 4 );
135
+ $ltime = number_format_i18n( $ltime, 10 );
136
+
137
+ if ( is_wp_error( $row['response'] ) ) {
138
+ $response = $row['response']->get_error_message();
139
+ $css = 'qm-warn';
140
+ } else {
141
+ $response = wp_remote_retrieve_response_code( $row['response'] );
142
+ $msg = wp_remote_retrieve_response_message( $row['response'] );
143
+ $css = '';
144
+
145
+ if ( empty( $response ) )
146
+ $response = __( 'n/a', 'query-monitor' );
147
+ else
148
+ $response = esc_html( $response . ' ' . $msg );
149
+
150
+ if ( intval( $response ) >= 400 )
151
+ $css = 'qm-warn';
152
+
153
+ }
154
+
155
+ } else {
156
+
157
+ # @TODO test if the timeout has actually passed. if not, the request was erroneous rather than timed out
158
+
159
+ $total_time += $row['args']['timeout'];
160
+
161
+ $ltime = '';
162
+ $stime = number_format_i18n( $row['args']['timeout'], 4 );
163
+ $response = __( 'Request timed out', 'query-monitor' );
164
+ $css = 'qm-warn';
165
+
166
+ }
167
+
168
+ $method = $row['args']['method'];
169
+ if ( !$row['args']['blocking'] )
170
+ $method .= '&nbsp;' . _x( '(non-blocking)', 'non-blocking HTTP transport', 'query-monitor' );
171
+ $url = str_replace( array(
172
+ '=',
173
+ '&',
174
+ '?',
175
+ ), array(
176
+ '<span class="qm-param">=</span>',
177
+ '<br /><span class="qm-param">&amp;</span>',
178
+ '<br /><span class="qm-param">?</span>',
179
+ ), $row['url'] );
180
+
181
+ if ( isset( $row['transport'] ) )
182
+ $transport = $row['transport'];
183
+ else
184
+ $transport = '';
185
+
186
+ $stack = $row['trace']->get_stack();
187
+
188
+ foreach ( $stack as & $trace ) {
189
+ foreach ( array( 'WP_Http', 'wp_remote_', 'fetch_rss', 'fetch_feed', 'SimplePie', 'download_url' ) as $skip ) {
190
+ if ( 0 === strpos( $trace, $skip ) ) {
191
+ $trace = sprintf( '<span class="qm-na">%s</span>', $trace );
192
+ break;
193
+ }
194
+ }
195
+ }
196
+
197
+ $component = QM_Util::get_backtrace_component( $row['trace'] );
198
+
199
+ $stack = implode( '<br />', $stack );
200
+ echo "
201
+ <tr class='{$css}'>\n
202
+ <td valign='top' class='qm-url qm-ltr'>{$method}<br/>{$url}</td>\n
203
+ <td valign='top'>{$response}</td>\n
204
+ <td valign='top'>{$transport}</td>\n
205
+ <td valign='top' class='qm-ltr'>{$stack}</td>\n
206
+ <td valign='top'>{$component->name}</td>\n
207
+ <td valign='top'>{$row['args']['timeout']}</td>\n
208
+ <td valign='top' title='{$ltime}'>{$stime}</td>\n
209
+ </tr>\n
210
+ ";
211
+ }
212
+
213
+ echo '</tbody>';
214
+ echo '<tfoot>';
215
+
216
+ $total_stime = number_format_i18n( $total_time, 4 );
217
+ $total_ltime = number_format_i18n( $total_time, 10 );
218
+
219
+ echo '<tr>';
220
+ echo '<td colspan="6">&nbsp;</td>';
221
+ echo "<td title='{$total_ltime}'>{$total_stime}</td>";
222
+ echo '</tr>';
223
+ echo '</tfoot>';
224
+
225
+ } else {
226
+
227
+ echo '<tbody>';
228
+ echo '<tr>';
229
+ echo '<td colspan="7" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
230
+ echo '</tr>';
231
+ echo '</tbody>';
232
+
233
+ }
234
+
235
+ echo '</table>';
236
+ echo '</div>';
237
+
238
+ }
239
+
240
+ }
241
+
242
+ function register_qm_http( array $qm ) {
243
+ $qm['http'] = new QM_Component_HTTP;
244
+ return $qm;
245
+ }
246
+
247
+ add_filter( 'query_monitor_components', 'register_qm_http', 110 );
components/overview.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Overview extends QM_Component {
18
+
19
+ var $id = 'overview';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'query_monitor_title', array( $this, 'admin_title' ), 10 );
24
+ }
25
+
26
+ function admin_title( array $title ) {
27
+ $title[] = sprintf(
28
+ _x( '%s<small>S</small>', 'page load time', 'query-monitor' ),
29
+ number_format_i18n( $this->data['time'], 2 )
30
+ );
31
+ $title[] = sprintf(
32
+ _x( '%s<small>MB</small>', 'memory usage', 'query-monitor' ),
33
+ number_format_i18n( ( $this->data['memory'] / 1024 / 1024 ), 2 )
34
+ );
35
+ return $title;
36
+ }
37
+
38
+ function output_html( array $args, array $data ) {
39
+
40
+ $http_time = null;
41
+ $db_query_num = null;
42
+ $db_query_types = array();
43
+ $http = $this->get_component( 'http' );
44
+ $db_queries = $this->get_component( 'db_queries' );
45
+ $time_usage = '';
46
+ $memory_usage = '';
47
+
48
+ if ( $http and isset( $http->data['http'] ) ) {
49
+ foreach ( $http->data['http'] as $row ) {
50
+ if ( isset( $row['response'] ) )
51
+ $http_time += ( $row['end'] - $row['start'] );
52
+ else
53
+ $http_time += $row['args']['timeout'];
54
+ }
55
+ }
56
+
57
+ if ( $db_queries and isset( $db_queries->data['types'] ) ) {
58
+ $db_query_num = $db_queries->data['types'];
59
+ $db_stime = number_format_i18n( $db_queries->data['total_time'], 4 );
60
+ $db_ltime = number_format_i18n( $db_queries->data['total_time'], 10 );
61
+ }
62
+
63
+ $total_stime = number_format_i18n( $data['time'], 4 );
64
+ $total_ltime = number_format_i18n( $data['time'], 10 );
65
+
66
+ echo '<div class="qm" id="' . $this->id() . '">';
67
+ echo '<table cellspacing="0">';
68
+
69
+ $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>';
70
+
71
+ $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>';
72
+
73
+ echo '<thead>';
74
+ echo '<tr>';
75
+ echo '<th scope="col">' . __( 'Page generation time', 'query-monitor' ) . '</th>';
76
+ echo '<th scope="col">' . __( 'Peak memory usage', 'query-monitor' ) . '</th>';
77
+ if ( isset( $db_query_num ) ) {
78
+ echo '<th scope="col">' . __( 'Database query time', 'query-monitor' ) . '</th>';
79
+ echo '<th scope="col">' . __( 'Database queries', 'query-monitor' ) . '</th>';
80
+ }
81
+ echo '</tr>';
82
+ echo '</thead>';
83
+
84
+ echo '<tbody>';
85
+ echo '<tr>';
86
+ echo "<td><span title='{$total_ltime}'>{$total_stime}</span>{$time_usage}</td>";
87
+ echo '<td><span title="' . esc_attr( sprintf( __( '%s bytes', 'query-monitor' ), number_format_i18n( $data['memory'] ) ) ) . '">' . sprintf( __( '%s kB', 'query-monitor' ), number_format_i18n( $data['memory'] / 1024 ) ) . '</span>' . $memory_usage . '</td>';
88
+ if ( isset( $db_query_num ) ) {
89
+ echo "<td title='{$db_ltime}'>{$db_stime}</td>";
90
+ echo '<td>';
91
+
92
+ foreach ( $db_query_num as $type_name => $type_count )
93
+ $db_query_types[] = sprintf( '%1$s: %2$s', $type_name, number_format_i18n( $type_count ) );
94
+
95
+ echo implode( '<br />', $db_query_types );
96
+
97
+ echo '</td>';
98
+ }
99
+ echo '</tr>';
100
+ echo '</tbody>';
101
+
102
+ echo '</table>';
103
+ echo '</div>';
104
+
105
+ }
106
+
107
+ function process() {
108
+
109
+ $this->data['time'] = QM_Util::timer_stop_float();
110
+ $this->data['time_limit'] = ini_get( 'max_execution_time' );
111
+
112
+ if ( !empty( $this->data['time_limit'] ) )
113
+ $this->data['time_usage'] = ( 100 / $this->data['time_limit'] ) * $this->data['time'];
114
+ else
115
+ $this->data['time_usage'] = 0;
116
+
117
+ if ( function_exists( 'memory_get_peak_usage' ) )
118
+ $this->data['memory'] = memory_get_peak_usage();
119
+ else
120
+ $this->data['memory'] = memory_get_usage();
121
+
122
+ $this->data['memory_limit'] = QM_Util::convert_hr_to_bytes( ini_get( 'memory_limit' ) );
123
+ $this->data['memory_usage'] = ( 100 / $this->data['memory_limit'] ) * $this->data['memory'];
124
+
125
+ }
126
+
127
+ }
128
+
129
+ function register_qm_overview( array $qm ) {
130
+ $qm['overview'] = new QM_Component_Overview;
131
+ return $qm;
132
+ }
133
+
134
+ add_filter( 'query_monitor_components', 'register_qm_overview', 10 );
components/php_errors.php ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_PHP_Errors extends QM_Component {
18
+
19
+ var $id = 'php_errors';
20
+
21
+ function __construct() {
22
+
23
+ parent::__construct();
24
+ set_error_handler( array( $this, 'error_handler' ) );
25
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 10 );
26
+ add_filter( 'query_monitor_class', array( $this, 'admin_class' ) );
27
+
28
+ }
29
+
30
+ function admin_class( array $class ) {
31
+
32
+ if ( isset( $this->data['errors']['warning'] ) )
33
+ $class[] = 'qm-warning';
34
+ else if ( isset( $this->data['errors']['notice'] ) )
35
+ $class[] = 'qm-notice';
36
+ else if ( isset( $this->data['errors']['strict'] ) )
37
+ $class[] = 'qm-strict';
38
+
39
+ return $class;
40
+
41
+ }
42
+
43
+ function admin_menu( array $menu ) {
44
+
45
+ if ( isset( $this->data['errors']['warning'] ) ) {
46
+ $menu[] = $this->menu( array(
47
+ 'id' => 'query-monitor-warnings',
48
+ 'title' => sprintf( __( 'PHP Warnings (%s)', 'query-monitor' ), number_format_i18n( count( $this->data['errors']['warning'] ) ) )
49
+ ) );
50
+ }
51
+ if ( isset( $this->data['errors']['notice'] ) ) {
52
+ $menu[] = $this->menu( array(
53
+ 'id' => 'query-monitor-notices',
54
+ 'title' => sprintf( __( 'PHP Notices (%s)', 'query-monitor' ), number_format_i18n( count( $this->data['errors']['notice'] ) ) )
55
+ ) );
56
+ }
57
+ if ( isset( $this->data['errors']['strict'] ) ) {
58
+ $menu[] = $this->menu( array(
59
+ 'id' => 'query-monitor-stricts',
60
+ 'title' => sprintf( __( 'PHP Stricts (%s)', 'query-monitor' ), number_format_i18n( count( $this->data['errors']['strict'] ) ) )
61
+ ) );
62
+ }
63
+ return $menu;
64
+
65
+ }
66
+
67
+ function output_headers( array $args, array $data ) {
68
+
69
+ if ( empty( $data['errors'] ) )
70
+ return;
71
+
72
+ $all_errors = array();
73
+
74
+ foreach ( $data['errors'] as $type => $errors ) {
75
+
76
+ foreach ( $errors as $key => $error ) {
77
+
78
+ $all_errors[$key] = $key;
79
+
80
+ header( sprintf( 'X-QM-Error-%s: %s',
81
+ $key,
82
+ json_encode( $error )
83
+ ) );
84
+
85
+ }
86
+
87
+ }
88
+
89
+ header( sprintf( 'X-QM-Errors: %s',
90
+ json_encode( $all_errors )
91
+ ) );
92
+
93
+ }
94
+
95
+ function output_html( array $args, array $data ) {
96
+
97
+ if ( empty( $data['errors'] ) )
98
+ return;
99
+
100
+ echo '<div class="qm" id="' . $args['id'] . '">';
101
+ echo '<table cellspacing="0">';
102
+ echo '<thead>';
103
+ echo '<tr>';
104
+ echo '<th colspan="2">' . __( 'PHP Error', 'query-monitor' ) . '</th>';
105
+ echo '<th>' . __( 'File', 'query-monitor' ) . '</th>';
106
+ echo '<th>' . __( 'Line', 'query-monitor' ) . '</th>';
107
+ echo '<th>' . __( 'Call Stack', 'query-monitor' ) . '</th>';
108
+ echo '<th>' . __( 'Component', 'query-monitor' ) . '</th>';
109
+ echo '</tr>';
110
+ echo '</thead>';
111
+ echo '<tbody>';
112
+
113
+ $types = array(
114
+ 'warning' => __( 'Warning', 'query-monitor' ),
115
+ 'notice' => __( 'Notice', 'query-monitor' ),
116
+ 'strict' => __( 'Strict', 'query-monitor' ),
117
+ );
118
+
119
+ foreach ( $types as $type => $title ) {
120
+
121
+ if ( isset( $data['errors'][$type] ) ) {
122
+
123
+ echo '<tr>';
124
+ if ( count( $data['errors'][$type] ) > 1 )
125
+ echo '<td rowspan="' . count( $data['errors'][$type] ) . '">' . $title . '</td>';
126
+ else
127
+ echo '<td>' . $title . '</td>';
128
+ $first = true;
129
+
130
+ foreach ( $data['errors'][$type] as $error ) {
131
+
132
+ if ( !$first )
133
+ echo '<tr>';
134
+
135
+ $stack = $error->trace->get_stack();
136
+ $component = QM_Util::get_backtrace_component( $error->trace );
137
+
138
+ if ( empty( $stack ) )
139
+ $stack = '<em>' . __( 'none', 'query-monitor' ) . '</em>';
140
+ else
141
+ $stack = implode( '<br />', $stack );
142
+
143
+ $message = str_replace( "href='function.", "target='_blank' href='http://php.net/function.", $error->message );
144
+
145
+ echo '<td>' . $message . '</td>';
146
+ echo '<td title="' . esc_attr( $error->file ) . '">' . esc_html( $error->filename ) . '</td>';
147
+ echo '<td>' . esc_html( $error->line ) . '</td>';
148
+ echo '<td class="qm-ltr">' . $stack . '</td>';
149
+ echo '<td>' . $component->name . '</td>';
150
+ echo '</tr>';
151
+
152
+ $first = false;
153
+
154
+ }
155
+
156
+ }
157
+
158
+ }
159
+
160
+ echo '</tbody>';
161
+ echo '</table>';
162
+ echo '</div>';
163
+
164
+ }
165
+
166
+ function error_handler( $errno, $message, $file = null, $line = null ) {
167
+
168
+ #if ( !( error_reporting() & $errno ) )
169
+ # return false;
170
+
171
+ switch ( $errno ) {
172
+
173
+ case E_WARNING:
174
+ case E_USER_WARNING:
175
+ $type = 'warning';
176
+ break;
177
+
178
+ case E_NOTICE:
179
+ case E_USER_NOTICE:
180
+ $type = 'notice';
181
+ break;
182
+
183
+ case E_STRICT:
184
+ $type = 'strict';
185
+ break;
186
+
187
+ default:
188
+ return false;
189
+ break;
190
+
191
+ }
192
+
193
+ if ( error_reporting() > 0 ) {
194
+
195
+ $trace = new QM_Backtrace;
196
+ $func = reset( $trace->get_stack() );
197
+ $key = md5( $message . $file . $line . $func );
198
+
199
+ $filename = QM_Util::standard_dir( $file, '' );
200
+
201
+ if ( isset( $this->data['errors'][$type][$key] ) ) {
202
+ $this->data['errors'][$type][$key]->calls++;
203
+ } else {
204
+ $this->data['errors'][$type][$key] = (object) array(
205
+ 'errno' => $errno,
206
+ 'type' => $type,
207
+ 'message' => $message,
208
+ 'file' => $file,
209
+ 'filename' => $filename,
210
+ 'line' => $line,
211
+ 'trace' => $trace,
212
+ 'calls' => 1
213
+ );
214
+ }
215
+
216
+ }
217
+
218
+ return apply_filters( 'query_monitor_php_errors_return_value', true );
219
+
220
+ }
221
+
222
+ }
223
+
224
+ function register_qm_php_errors( array $qm ) {
225
+ $qm['php_errors'] = new QM_Component_PHP_Errors;
226
+ return $qm;
227
+ }
228
+
229
+ function qm_php_errors_return_value( $return ) {
230
+ # If Xdebug is enabled we'll return false so Xdebug's error handler can do its thing.
231
+ if ( function_exists( 'xdebug_is_enabled' ) and xdebug_is_enabled() )
232
+ return false;
233
+ else
234
+ return $return;
235
+ }
236
+
237
+ add_filter( 'query_monitor_components', 'register_qm_php_errors', 120 );
238
+ add_filter( 'query_monitor_php_errors_return_value', 'qm_php_errors_return_value' );
components/query_vars.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Query_Vars extends QM_Component {
18
+
19
+ var $id = 'query_vars';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 90 );
24
+ }
25
+
26
+ function process() {
27
+
28
+ $plugin_qvars = array_flip( apply_filters( 'query_vars', array() ) );
29
+ $qvars = $GLOBALS['wp_query']->query_vars;
30
+ $query_vars = array();
31
+
32
+ foreach ( $qvars as $k => $v ) {
33
+ if ( isset( $plugin_qvars[$k] ) ) {
34
+ if ( '' !== $v )
35
+ $query_vars[$k] = $v;
36
+ } else {
37
+ if ( !empty( $v ) )
38
+ $query_vars[$k] = $v;
39
+ }
40
+ }
41
+
42
+ ksort( $query_vars );
43
+
44
+ # First add plugin vars to $this->data['qvars']:
45
+ foreach ( $query_vars as $k => $v ) {
46
+ if ( isset( $plugin_qvars[$k] ) ) {
47
+ $this->data['qvars'][$k] = $v;
48
+ $this->data['plugin_qvars'][$k] = $v;
49
+ }
50
+ }
51
+
52
+ # Now add all other vars to $this->data['qvars']:
53
+ foreach ( $query_vars as $k => $v ) {
54
+ if ( !isset( $plugin_qvars[$k] ) )
55
+ $this->data['qvars'][$k] = $v;
56
+ }
57
+
58
+ }
59
+
60
+ function output_html( array $args, array $data ) {
61
+
62
+ echo '<div class="qm qm-half" id="' . $args['id'] . '">';
63
+ echo '<table cellspacing="0">';
64
+ echo '<thead>';
65
+ echo '<tr>';
66
+ echo '<th colspan="2">' . __( 'Query Vars', 'query-monitor' ) . '</th>';
67
+ echo '</tr>';
68
+ echo '</thead>';
69
+ echo '<tbody>';
70
+
71
+ if ( !empty( $data['qvars'] ) ) {
72
+
73
+ foreach( $data['qvars'] as $var => $value ) {
74
+ echo '<tr>';
75
+ if ( isset( $data['plugin_qvars'][$var] ) )
76
+ echo "<td valign='top'><span class='qm-current'>{$var}</span></td>";
77
+ else
78
+ echo "<td valign='top'>{$var}</td>";
79
+ if ( is_array( $value ) or is_object( $value ) ) {
80
+ echo '<td valign="top"><pre>';
81
+ print_r( $value );
82
+ echo '</pre></td>';
83
+ } else {
84
+ $value = esc_html( $value );
85
+ echo "<td valign='top'>{$value}</td>";
86
+ }
87
+ echo '</tr>';
88
+ }
89
+
90
+ } else {
91
+
92
+ echo '<tr>';
93
+ echo '<td colspan="2" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
94
+ echo '</tr>';
95
+
96
+ }
97
+
98
+ echo '</tbody>';
99
+ echo '</table>';
100
+ echo '</div>';
101
+
102
+ }
103
+
104
+ function admin_menu( array $menu ) {
105
+
106
+ $count = isset( $this->data['plugin_qvars'] ) ? count( $this->data['plugin_qvars'] ) : 0;
107
+
108
+ $title = ( empty( $count ) )
109
+ ? __( 'Query Vars', 'query-monitor' )
110
+ : __( 'Query Vars (+%s)', 'query-monitor' );
111
+
112
+ $menu[] = $this->menu( array(
113
+ 'title' => sprintf( $title, number_format_i18n( $count ) )
114
+ ) );
115
+ return $menu;
116
+
117
+ }
118
+
119
+ }
120
+
121
+ function register_qm_query_vars( array $qm ) {
122
+ $qm['query_vars'] = new QM_Component_Query_Vars;
123
+ return $qm;
124
+ }
125
+
126
+ add_filter( 'query_monitor_components', 'register_qm_query_vars', 70 );
components/redirects.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Redirects extends QM_Component {
18
+
19
+ var $id = 'redirects';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'wp_redirect', array( $this, 'filter_wp_redirect' ), 999, 2 );
24
+ }
25
+
26
+ public function filter_wp_redirect( $location, $status ) {
27
+
28
+ global $querymonitor;
29
+
30
+ if ( !$location )
31
+ return $location;
32
+ if ( !$querymonitor->show_query_monitor() )
33
+ return $location;
34
+ if ( headers_sent() )
35
+ return $location;
36
+
37
+ $trace = new QM_Backtrace;
38
+
39
+ header( sprintf( 'X-QM-Redirect-Trace: %s',
40
+ implode( ', ', $trace->get_stack() )
41
+ ) );
42
+
43
+ return $location;
44
+
45
+ }
46
+
47
+ }
48
+
49
+ function register_qm_redirects( array $qm ) {
50
+ $qm['redirects'] = new QM_Component_Redirects;
51
+ return $qm;
52
+ }
53
+
54
+ add_filter( 'query_monitor_components', 'register_qm_redirects', 140 );
components/theme.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Theme extends QM_Component {
18
+
19
+ var $id = 'theme';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ add_filter( 'body_class', array( $this, 'body_class' ), 99 );
24
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 100 );
25
+ }
26
+
27
+ function body_class( $class ) {
28
+ $this->data['body_class'] = $class;
29
+ return $class;
30
+ }
31
+
32
+ function process() {
33
+
34
+ global $template;
35
+
36
+ $template_file = QM_Util::standard_dir( $template );
37
+ $stylesheet_directory = QM_Util::standard_dir( get_stylesheet_directory() );
38
+ $template_directory = QM_Util::standard_dir( get_template_directory() );
39
+
40
+ $template_file = str_replace( array( $stylesheet_directory, $template_directory ), '', $template_file );
41
+ $template_file = ltrim( $template_file, '/' );
42
+
43
+ $this->data['template_file'] = apply_filters( 'query_monitor_template', $template_file, $template );
44
+ $this->data['stylesheet'] = get_stylesheet();
45
+ $this->data['template'] = get_template();
46
+
47
+ if ( isset( $this->data['body_class'] ) )
48
+ asort( $this->data['body_class'] );
49
+
50
+ }
51
+
52
+ function output_html( array $args, array $data ) {
53
+
54
+ if ( empty( $data ) )
55
+ return;
56
+
57
+ echo '<div class="qm qm-half" id="' . $args['id'] . '">';
58
+ echo '<table cellspacing="0">';
59
+ echo '<thead>';
60
+ echo '<tr>';
61
+ echo '<th colspan="2">' . __( 'Theme', 'query-monitor' ) . '</th>';
62
+ echo '</tr>';
63
+ echo '</thead>';
64
+
65
+ echo '<tbody>';
66
+ echo '<tr>';
67
+ echo '<td>' . __( 'Template', 'query-monitor' ) . '</td>';
68
+ echo "<td>{$this->data['template_file']}</td>";
69
+ echo '</tr>';
70
+
71
+ if ( !empty( $data['body_class'] ) ) {
72
+
73
+ echo '<tr>';
74
+ echo '<td rowspan="' . count( $data['body_class'] ) . '">' . __( 'Body Classes', 'query-monitor' ) . '</td>';
75
+ $first = true;
76
+
77
+ foreach ( $data['body_class'] as $class ) {
78
+
79
+ if ( !$first )
80
+ echo '<tr>';
81
+
82
+ echo "<td>{$class}</td>";
83
+ echo '</tr>';
84
+
85
+ $first = false;
86
+
87
+ }
88
+
89
+ }
90
+
91
+ echo '<tr>';
92
+ echo '<td>' . __( 'Theme', 'query-monitor' ) . '</td>';
93
+ echo "<td>{$this->data['stylesheet']}</td>";
94
+ echo '</tr>';
95
+
96
+ if ( $this->data['stylesheet'] != $this->data['template'] ) {
97
+ echo '<tr>';
98
+ echo '<td>' . __( 'Parent Theme', 'query-monitor' ) . '</td>';
99
+ echo "<td>{$this->data['template']}</td>";
100
+ echo '</tr>';
101
+ }
102
+
103
+ echo '</tbody>';
104
+ echo '</table>';
105
+ echo '</div>';
106
+
107
+ }
108
+
109
+ function admin_menu( array $menu ) {
110
+
111
+ if ( isset( $this->data['template_file'] ) ) {
112
+ $menu[] = $this->menu( array(
113
+ 'title' => sprintf( __( 'Template: %s', 'query-monitor' ), $this->data['template_file'] )
114
+ ) );
115
+ }
116
+ return $menu;
117
+
118
+ }
119
+
120
+ }
121
+
122
+ function register_qm_theme( array $qm ) {
123
+ if ( !is_admin() )
124
+ $qm['theme'] = new QM_Component_Theme;
125
+ return $qm;
126
+ }
127
+
128
+ add_filter( 'query_monitor_components', 'register_qm_theme', 60 );
components/transients.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright 2013 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_Component_Transients extends QM_Component {
18
+
19
+ var $id = 'transients';
20
+
21
+ function __construct() {
22
+ parent::__construct();
23
+ # See http://core.trac.wordpress.org/ticket/24583
24
+ add_action( 'setted_site_transient', array( $this, 'setted_site_transient' ), 10, 3 );
25
+ add_action( 'setted_transient', array( $this, 'setted_blog_transient' ), 10, 3 );
26
+ add_filter( 'query_monitor_menus', array( $this, 'admin_menu' ), 70 );
27
+ }
28
+
29
+ function setted_site_transient( $transient, $value = null, $expiration = null ) {
30
+ $this->setted_transient( $transient, 'site', $value, $expiration );
31
+ }
32
+
33
+ function setted_blog_transient( $transient, $value = null, $expiration = null ) {
34
+ $this->setted_transient( $transient, 'blog', $value, $expiration );
35
+ }
36
+
37
+ function setted_transient( $transient, $type, $value = null, $expiration = null ) {
38
+ $trace = new QM_Backtrace( array(
39
+ 'ignore_items' => 1 # Ignore the setted_(site|blog)_transient method
40
+ ) );
41
+ $this->data['trans'][] = array(
42
+ 'transient' => $transient,
43
+ 'trace' => $trace,
44
+ 'type' => $type,
45
+ 'value' => $value,
46
+ 'expiration' => $expiration,
47
+ );
48
+ }
49
+
50
+ function output_html( array $args, array $data ) {
51
+
52
+ echo '<div class="qm" id="' . $args['id'] . '">';
53
+ echo '<table cellspacing="0">';
54
+ echo '<thead>';
55
+ echo '<tr>';
56
+ echo '<th>' . __( 'Transient Set', 'query-monitor' ) . '</th>';
57
+ if ( is_multisite() )
58
+ echo '<th>' . __( 'Type', 'query-monitor' ) . '</th>';
59
+ if ( !empty( $data['trans'] ) and !is_null( $data['trans'][0]['expiration'] ) )
60
+ echo '<th>' . __( 'Expiration', 'query-monitor' ) . '</th>';
61
+ echo '<th>' . __( 'Call Stack', 'query-monitor' ) . '</th>';
62
+ echo '<th>' . __( 'Component', 'query-monitor' ) . '</th>';
63
+ echo '</tr>';
64
+ echo '</thead>';
65
+
66
+ if ( !empty( $data['trans'] ) ) {
67
+
68
+ echo '<tbody>';
69
+
70
+ foreach ( $data['trans'] as $row ) {
71
+ $stack = $row['trace']->get_stack();
72
+ $transient = str_replace( array(
73
+ '_site_transient_',
74
+ '_transient_'
75
+ ), '', $row['transient'] );
76
+ $type = ( is_multisite() ) ? "<td valign='top'>{$row['type']}</td>\n" : '';
77
+ if ( 0 === $row['expiration'] )
78
+ $row['expiration'] = '<em>' . __( 'none', 'query-monitor' ) . '</em>';
79
+ $expiration = ( !is_null( $row['expiration'] ) ) ? "<td valign='top'>{$row['expiration']}</td>\n" : '';
80
+
81
+ foreach ( $stack as & $trace ) {
82
+ foreach ( array( 'set_transient', 'set_site_transient' ) as $skip ) {
83
+ if ( 0 === strpos( $trace, $skip ) ) {
84
+ $trace = sprintf( '<span class="qm-na">%s</span>', $trace );
85
+ break;
86
+ }
87
+ }
88
+ }
89
+
90
+ $component = QM_Util::get_backtrace_component( $row['trace'] );
91
+
92
+ $stack = implode( '<br />', $stack );
93
+ echo "
94
+ <tr>\n
95
+ <td valign='top'>{$transient}</td>\n
96
+ {$type}
97
+ {$expiration}
98
+ <td valign='top' class='qm-ltr'>{$stack}</td>\n
99
+ <td valign='top'>{$component->name}</td>\n
100
+ </tr>\n
101
+ ";
102
+ }
103
+
104
+ echo '</tbody>';
105
+
106
+ } else {
107
+
108
+ echo '<tbody>';
109
+ echo '<tr>';
110
+ echo '<td colspan="4" style="text-align:center !important"><em>' . __( 'none', 'query-monitor' ) . '</em></td>';
111
+ echo '</tr>';
112
+ echo '</tbody>';
113
+
114
+ }
115
+
116
+ echo '</table>';
117
+ echo '</div>';
118
+
119
+ }
120
+
121
+ function admin_menu( array $menu ) {
122
+
123
+ $count = isset( $this->data['trans'] ) ? count( $this->data['trans'] ) : 0;
124
+
125
+ $title = ( empty( $count ) )
126
+ ? __( 'Transients Set', 'query-monitor' )
127
+ : __( 'Transients Set (%s)', 'query-monitor' );
128
+
129
+ $menu[] = $this->menu( array(
130
+ 'title' => sprintf( $title, number_format_i18n( $count ) )
131
+ ) );
132
+ return $menu;
133
+
134
+ }
135
+
136
+
137
+ }
138
+
139
+ function register_qm_transients( array $qm ) {
140
+ $qm['transients'] = new QM_Component_Transients;
141
+ return $qm;
142
+ }
143
+
144
+ add_filter( 'query_monitor_components', 'register_qm_transients', 100 );
composer.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name" : "johnbillion/query-monitor",
3
+ "description": "WordPress plugin for monitoring database queries, hooks, conditionals, HTTP requests, query vars, environment, redirects, and more.",
4
+ "homepage" : "https://github.com/johnbillion/QueryMonitor/",
5
+ "type" : "wordpress-plugin",
6
+ "license" : "GPL-2.0+",
7
+ "authors" : [
8
+ {
9
+ "name" : "John Blackbourn",
10
+ "homepage": "https://johnblackbourn.com/"
11
+ }
12
+ ],
13
+ "require": {
14
+ "composer/installers": "~1.0"
15
+ }
16
+ }
query-monitor.php ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: Query Monitor
4
+ Description: Monitoring of database queries, hooks, conditionals and more.
5
+ Version: 2.5.2
6
+ Plugin URI: https://github.com/johnbillion/QueryMonitor
7
+ Author: John Blackbourn
8
+ Author URI: https://johnblackbourn.com/
9
+ Text Domain: query-monitor
10
+ Domain Path: /languages/
11
+ License: GPL v2 or later
12
+
13
+ Copyright 2013 John Blackbourn
14
+
15
+ This program is free software; you can redistribute it and/or modify
16
+ it under the terms of the GNU General Public License as published by
17
+ the Free Software Foundation; either version 2 of the License, or
18
+ (at your option) any later version.
19
+
20
+ This program is distributed in the hope that it will be useful,
21
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
22
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
+ GNU General Public License for more details.
24
+
25
+ */
26
+
27
+ defined( 'ABSPATH' ) or die();
28
+
29
+ require_once dirname( __FILE__ ) . '/autoloader.php';
30
+
31
+ class QueryMonitor extends QM_Plugin {
32
+
33
+ protected $components = array();
34
+ protected $did_footer = false;
35
+
36
+ public function __construct( $file ) {
37
+
38
+ # Actions
39
+ add_action( 'init', array( $this, 'action_init' ) );
40
+ add_action( 'admin_footer', array( $this, 'action_footer' ), 999 );
41
+ add_action( 'wp_footer', array( $this, 'action_footer' ), 999 );
42
+ add_action( 'login_footer', array( $this, 'action_footer' ), 999 );
43
+ add_action( 'admin_bar_menu', array( $this, 'action_admin_bar_menu' ), 999 );
44
+ add_action( 'shutdown', array( $this, 'action_shutdown' ), 0 );
45
+
46
+ # Filters
47
+ add_filter( 'pre_update_option_active_plugins', array( $this, 'filter_active_plugins' ) );
48
+ add_filter( 'pre_update_site_option_active_sitewide_plugins', array( $this, 'filter_active_sitewide_plugins' ) );
49
+
50
+ # [Dea|A]ctivation
51
+ register_activation_hook( __FILE__, array( $this, 'activate' ) );
52
+ register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
53
+
54
+ # Parent setup:
55
+ parent::__construct( $file );
56
+
57
+ foreach ( glob( $this->plugin_path( 'components/*.php' ) ) as $component )
58
+ include $component;
59
+
60
+ foreach ( apply_filters( 'query_monitor_components', array() ) as $component )
61
+ $this->add_component( $component );
62
+
63
+ }
64
+
65
+ public function add_component( QM_Component $component ) {
66
+ $this->components[$component->id] = $component;
67
+ }
68
+
69
+ public function get_component( $id ) {
70
+ if ( isset( $this->components[$id] ) )
71
+ return $this->components[$id];
72
+ return false;
73
+ }
74
+
75
+ public function get_components() {
76
+ return $this->components;
77
+ }
78
+
79
+ public function activate( $sitewide = false ) {
80
+
81
+ if ( $admins = QM_Util::get_admins() )
82
+ $admins->add_cap( 'view_query_monitor' );
83
+
84
+ if ( !file_exists( $db = WP_CONTENT_DIR . '/db.php' ) and function_exists( 'symlink' ) )
85
+ @symlink( $this->plugin_path( 'wp-content/db.php' ), $db );
86
+
87
+ if ( $sitewide )
88
+ update_site_option( 'active_sitewide_plugins', $this->filter_active_sitewide_plugins( get_site_option( 'active_sitewide_plugins' ) ) );
89
+ else
90
+ update_option( 'active_plugins', $this->filter_active_plugins( get_option( 'active_plugins' ) ) );
91
+
92
+ }
93
+
94
+ public function deactivate() {
95
+
96
+ if ( $admins = QM_Util::get_admins() )
97
+ $admins->remove_cap( 'view_query_monitor' );
98
+
99
+ # Only delete db.php if it belongs to Query Monitor
100
+ if ( class_exists( 'QueryMonitorDB' ) )
101
+ unlink( WP_CONTENT_DIR . '/db.php' );
102
+
103
+ }
104
+
105
+ public function action_admin_bar_menu( WP_Admin_Bar $wp_admin_bar ) {
106
+
107
+ if ( !$this->show_query_monitor() )
108
+ return;
109
+
110
+ $class = implode( ' ', array( 'hide-if-js', QM_Util::wpv() ) );
111
+ $title = __( 'Query Monitor', 'query-monitor' );
112
+
113
+ $wp_admin_bar->add_menu( array(
114
+ 'id' => 'query-monitor',
115
+ 'title' => $title,
116
+ 'href' => '#qm-overview',
117
+ 'meta' => array(
118
+ 'classname' => $class
119
+ )
120
+ ) );
121
+
122
+ $wp_admin_bar->add_menu( array(
123
+ 'parent' => 'query-monitor',
124
+ 'id' => 'query-monitor-placeholder',
125
+ 'title' => $title,
126
+ 'href' => '#qm-overview'
127
+ ) );
128
+
129
+ }
130
+
131
+ public function js_admin_bar_menu() {
132
+
133
+ $class = implode( ' ', apply_filters( 'query_monitor_class', array( QM_Util::wpv() ) ) );
134
+ $title = implode( ' / ', apply_filters( 'query_monitor_title', array() ) );
135
+
136
+ if ( empty( $title ) )
137
+ $title = __( 'Query Monitor', 'query-monitor' );
138
+
139
+ $admin_bar_menu = array(
140
+ 'top' => array(
141
+ 'title' => sprintf( '<span class="ab-icon">QM</span><span class="ab-label">%s</span>', $title ),
142
+ 'classname' => $class
143
+ ),
144
+ 'sub' => array()
145
+ );
146
+
147
+ foreach ( apply_filters( 'query_monitor_menus', array() ) as $menu )
148
+ $admin_bar_menu['sub'][] = $menu;
149
+
150
+ return $admin_bar_menu;
151
+
152
+ }
153
+
154
+ public function show_query_monitor() {
155
+
156
+ if ( !did_action( 'plugins_loaded' ) )
157
+ return false;
158
+
159
+ if ( isset( $this->show_query_monitor ) )
160
+ return $this->show_query_monitor;
161
+
162
+ if ( isset( $_REQUEST['wp_customize'] ) and 'on' == $_REQUEST['wp_customize'] )
163
+ return $this->show_query_monitor = false;
164
+
165
+ if ( is_multisite() ) {
166
+ if ( current_user_can( 'manage_network_options' ) )
167
+ return $this->show_query_monitor = true;
168
+ } else if ( current_user_can( 'view_query_monitor' ) ) {
169
+ return $this->show_query_monitor = true;
170
+ }
171
+
172
+ if ( $auth = $this->get_component( 'authentication' ) )
173
+ return $this->show_query_monitor = $auth->show_query_monitor();
174
+
175
+ return $this->show_query_monitor = false;
176
+
177
+ }
178
+
179
+ public function action_footer() {
180
+
181
+ $this->did_footer = true;
182
+
183
+ }
184
+
185
+ public function action_shutdown() {
186
+
187
+ if ( QM_Util::is_ajax() )
188
+ $this->output_ajax();
189
+ else if ( $this->did_footer )
190
+ $this->output_footer();
191
+
192
+ }
193
+
194
+ public function action_init() {
195
+
196
+ global $wp_locale;
197
+
198
+ load_plugin_textdomain( 'query-monitor', false, dirname( $this->plugin_base() ) . '/languages' );
199
+
200
+ if ( !$this->show_query_monitor() )
201
+ return;
202
+
203
+ if ( QM_Util::is_ajax() )
204
+ ob_start();
205
+
206
+ if ( !defined( 'DONOTCACHEPAGE' ) )
207
+ define( 'DONOTCACHEPAGE', 1 );
208
+
209
+ wp_enqueue_style(
210
+ 'query-monitor',
211
+ $this->plugin_url( 'assets/query-monitor.css' ),
212
+ null,
213
+ $this->plugin_ver( 'assets/query-monitor.css' )
214
+ );
215
+ wp_enqueue_script(
216
+ 'query-monitor',
217
+ $this->plugin_url( 'assets/query-monitor.js' ),
218
+ array( 'jquery' ),
219
+ $this->plugin_ver( 'assets/query-monitor.js' ),
220
+ true
221
+ );
222
+ wp_localize_script(
223
+ 'query-monitor',
224
+ 'qm_locale',
225
+ (array) $wp_locale
226
+ );
227
+
228
+ }
229
+
230
+ public function output_footer() {
231
+
232
+ if ( !$this->show_query_monitor() )
233
+ return;
234
+
235
+ # Flush the output buffer to avoid crashes
236
+ if ( !is_feed() ) {
237
+ while ( ob_get_length() )
238
+ ob_flush();
239
+ }
240
+
241
+ foreach ( $this->get_components() as $component )
242
+ $component->process();
243
+
244
+ if ( !function_exists( 'is_admin_bar_showing' ) or !is_admin_bar_showing() )
245
+ $class = 'qm-show';
246
+ else
247
+ $class = '';
248
+
249
+ $qm = array(
250
+ 'menu' => $this->js_admin_bar_menu(),
251
+ 'ajax_errors' => array() # @TODO move this into the php_errors component
252
+ );
253
+
254
+ echo '<script type="text/javascript">' . "\n\n";
255
+ echo 'var qm = ' . json_encode( $qm ) . ';' . "\n\n";
256
+ echo '</script>' . "\n\n";
257
+
258
+ echo '<div id="qm" class="' . $class . '">';
259
+ echo '<div id="qm-wrapper">';
260
+ echo '<p>' . __( 'Query Monitor', 'query-monitor' ) . '</p>';
261
+
262
+ foreach ( $this->get_components() as $component ) {
263
+ $component->output_html( array(
264
+ 'id' => $component->id()
265
+ ), $component->get_data() );
266
+ }
267
+
268
+ echo '</div>';
269
+ echo '</div>';
270
+
271
+ }
272
+
273
+ public function output_ajax() {
274
+
275
+ # if the headers have already been sent then we can't do anything about it
276
+ if ( headers_sent() )
277
+ return;
278
+
279
+ if ( !$this->show_query_monitor() )
280
+ return;
281
+
282
+ foreach ( $this->get_components() as $component )
283
+ $component->process();
284
+
285
+ foreach ( $this->get_components() as $component ) {
286
+ $component->output_headers( array(
287
+ 'id' => $component->id()
288
+ ), $component->get_data() );
289
+ }
290
+
291
+ # flush once, because we're nice
292
+ if ( ob_get_length() )
293
+ ob_flush();
294
+
295
+ }
296
+
297
+ public function filter_active_plugins( array $plugins ) {
298
+
299
+ $f = preg_quote( basename( __FILE__ ) );
300
+
301
+ return array_merge(
302
+ preg_grep( '/' . $f . '$/', $plugins ),
303
+ preg_grep( '/' . $f . '$/', $plugins, PREG_GREP_INVERT )
304
+ );
305
+
306
+ }
307
+
308
+ public function filter_active_sitewide_plugins( array $plugins ) {
309
+
310
+ $f = plugin_basename( __FILE__ );
311
+
312
+ if ( isset( $plugins[$f] ) ) {
313
+
314
+ unset( $plugins[$f] );
315
+
316
+ return array_merge( array(
317
+ $f => time(),
318
+ ), $plugins );
319
+
320
+ } else {
321
+ return $plugins;
322
+ }
323
+
324
+ }
325
+
326
+ }
327
+
328
+ $GLOBALS['querymonitor'] = new QueryMonitor( __FILE__ );
readme.txt ADDED
@@ -0,0 +1,291 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Query Monitor ===
2
+ Contributors: johnbillion
3
+ Tags: debug, debugging, development, developer, performance, profiler, profiling, queries
4
+ Requires at least: 3.5
5
+ Tested up to: 3.7
6
+ Stable tag: 2.5.2
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 unique features not yet seen 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/QueryMonitor).
16
+
17
+ Here's an overview of what's shown:
18
+
19
+ = Database Queries =
20
+
21
+ * Shows all database queries performed on the current page
22
+ * Shows **affected rows** and time for all queries
23
+ * Show notifications for **slow queries** and **queries with errors**
24
+ * Filter queries by **query type** (`SELECT`, `UPDATE`, `DELETE`, etc)
25
+ * Filter queries by **component** (WordPress core, Plugin X, Plugin Y, theme)
26
+ * Filter queries by **calling function**
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. Query Monitor can easily tell you if your "premium" theme is doing a premium number of database queries.
31
+
32
+ = Hooks =
33
+
34
+ * Shows all hooks fired on the current page, along with hooked actions and their priorities
35
+ * Filter hooks by **part of their name**
36
+ * Filter actions by **component** (WordPress core, Plugin X, Plugin Y, theme)
37
+
38
+ = Theme =
39
+
40
+ * Shows the **template filename** for the current page
41
+ * Shows the available **body classes** for the current page
42
+ * Shows the active theme name
43
+
44
+ = PHP Errors =
45
+
46
+ * PHP errors (warnings, notices and stricts) are presented nicely along with their component and call stack
47
+ * Shows an easily visible warning in the admin toolbar
48
+ * Plays nicely with Xdebug
49
+
50
+ = HTTP Requests =
51
+
52
+ * Shows all HTTP requests performed on the current page (as long as they use WordPress' HTTP API)
53
+ * Shows the response code, call stack, transport, timeout, and time taken
54
+ * Highlights **erroneous responses**, such as failed requests and anything without a `200` response code
55
+
56
+ = Redirects =
57
+
58
+ * 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
59
+
60
+ = AJAX =
61
+
62
+ 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**.
63
+
64
+ AJAX debugging is in its early stages. Currently it only includes PHP errors (warnings, notices and stricts), but this will be built upon in future versions.
65
+
66
+ = Admin Screen =
67
+
68
+ Hands up who can remember the correct names for the filters and hooks for custom admin screen columns?
69
+
70
+ * Shows the correct names for **custom column hooks and filters** on all admin screens that have a listing table
71
+ * Shows the state of `get_current_screen()` and a few variables
72
+
73
+ = Environment Information =
74
+
75
+ * Shows **various PHP information** such as memory limit and error reporting levels
76
+ * Highlights the fact when any of these are overridden at runtime
77
+ * Shows **various MySQL information**, including caching and performance related configuration
78
+ * Highlights the fact when any performance related configurations are not optimal
79
+ * Shows various details about **WordPress** and the **web server**
80
+ * Shows version numbers for everything
81
+
82
+ = Everything Else =
83
+
84
+ * Shows the names and values for **query vars** on the current page, and highlights **custom query vars**
85
+ * Shows any **transients that were set**, along with their timeout, component, and call stack
86
+ * Shows all **WordPress conditionals** on the current page, highlighted nicely
87
+ * Shows an overview at the top, including page generation time and memory limit as absolute values and as % of their respective limits
88
+ * You can set an authentication cookie which allows you to view Query Monitor output when you're not logged in (or if you're logged in as a non-administrator). See the bottom of Query Monitor's output for details
89
+
90
+ == Installation ==
91
+
92
+ You can install this plugin directly from your WordPress dashboard:
93
+
94
+ 1. Go to the *Plugins* menu and click *Add New*.
95
+ 2. Search for *Query Monitor*.
96
+ 3. Click *Install Now* next to the Query Monitor plugin.
97
+ 4. Activate the plugin.
98
+
99
+ Alternatively, see the guide to [Manually Installing Plugins](http://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation).
100
+
101
+ == Screenshots ==
102
+
103
+ 1. An example of Query Monitor's output
104
+
105
+ == Frequently Asked Questions ==
106
+
107
+ = There's nothing here =
108
+
109
+ I know!
110
+
111
+ == Changelog ==
112
+
113
+ = 2.5.2 =
114
+ * Prevent uncaught exceptions with static method actions
115
+ * Misc formatting tweaks
116
+
117
+ = 2.5.1 =
118
+ * Un-break query filtering
119
+ * Performance improvements
120
+
121
+ = 2.5 =
122
+ * Display the component for HTTP requests, transients, PHP errors, and hook actions
123
+ * Improved visual appearance and layout
124
+ * Add an action component filter to the Hooks panel
125
+ * Log errors returned in the `pre_http_request` filter
126
+ * `QM_DB_LIMIT` is now a soft limit
127
+ * Performance improvements
128
+
129
+ = 2.4.2 =
130
+ * Add a hook name filter to the Hooks panel
131
+ * Update db.php to match latest wp-db.php
132
+ * Avoid fatal error if the plugin is manually deleted
133
+ * Add the new `is_main_network()` conditional
134
+ * Lots more tweaks
135
+
136
+ = 2.4.1 =
137
+ * Un-break all the things
138
+
139
+ = 2.4 =
140
+ * New Redirect component
141
+ * Add support for strict errors
142
+ * Display the call stack for HTTP requests
143
+ * Display the call stack for transients
144
+ * Remove pre-3.0 back-compat code
145
+ * Many other bugfixes and tweaks
146
+
147
+ = 2.3.1 =
148
+ * Compat with Xdebug
149
+ * Display the call stack for PHP errors
150
+
151
+ = 2.3 =
152
+ * Introduce AJAX debugging (just PHP errors for now)
153
+ * Visual refresh
154
+ * Add theme and stylesheet into to the Theme panel
155
+
156
+ = 2.2.8 =
157
+ * Add error reporting to the Environment panel
158
+
159
+ = 2.2.7 =
160
+ * Don't output QM in the theme customizer
161
+
162
+ = 2.2.6 =
163
+ * Add the database query time to the admin toolbar
164
+ * Various trace and JavaScript errors
165
+
166
+ = 2.2.5 =
167
+ * Load QM before other plugins
168
+ * Show QM output on the log in screen
169
+
170
+ = 2.2.4 =
171
+ * Add filtering to the qyer panel
172
+
173
+ = 2.2.3 =
174
+ * Show component information indicating whether a plugin, theme or core was responsible for each database query
175
+ * New Query Component panel showing components ordered by total query time
176
+
177
+ = 2.2.2 =
178
+ * Show memory usage as a percentage of the memory limit
179
+ * Show page generation time as percentage of the limit, if it's high
180
+ * Show a few bits of server information in the Environment panel
181
+ * Log PHP settings as early as possible and highlight when the values have been altered at runtime
182
+
183
+ = 2.2.1 =
184
+ * A few formatting and layout tweaks
185
+
186
+ = 2.2 =
187
+ * Breakdown queries by type in the Overview and Query Functions panels
188
+ * Show the HTTP transport order of preference in the HTTP panel
189
+ * Highlight database errors and slow database queries in their own panels
190
+ * Add a few PHP enviroment variables to the Environment panel (more to come)
191
+
192
+ = 2.1.8 =
193
+ * Change i18n text domain
194
+ * Hide Authentication panel for non-JS
195
+ * Show database info in Overview panel
196
+
197
+ = 2.1.7 =
198
+ * Full WordPress 3.4 compatibility
199
+
200
+ = 2.1.6 =
201
+ * Small tweaks to conditionals and HTTP components
202
+ * Allow filtering of ignore_class, ignore_func and show_arg on QM and QM DB
203
+
204
+ = 2.1.5 =
205
+ * Tweak a few conditional outputs
206
+ * Full support for all WPDB instances
207
+ * Tweak query var output
208
+ * Initial code for data logging before redirects (incomplete)
209
+
210
+ = 2.1.4 =
211
+ * Add full support for multiple DB instances to the Environment component
212
+ * Improve PHP error function stack
213
+
214
+ = 2.1.3 =
215
+ * Fix display of wp_admin_bar instantiated queries
216
+ * Fix function trace for HTTP calls and transients
217
+
218
+ = 2.1.2 =
219
+ * Lots more behind the scenes improvements
220
+ * Better future-proof CSS
221
+ * Complete separation of data/presentation in db_queries
222
+ * Complete support for multiple database connections
223
+
224
+ = 2.1.1 =
225
+ * Lots of behind the scenes improvements
226
+ * More separation of data from presentation
227
+ * Fewer cross-component dependencies
228
+ * Nicer way of doing menu items, classes & title
229
+
230
+ = 2.1 =
231
+ * Let's split everything up into components. Lots of optimisations to come.
232
+
233
+ = 2.0.3 =
234
+ * Localisation improvements
235
+
236
+ = 2.0.2 =
237
+ * Admin bar tweaks for WordPress 3.3
238
+ * Add some missing l10n
239
+ * Prevent some PHP notices
240
+
241
+ = 2.0.1 =
242
+ * Just a few rearrangements
243
+
244
+ = 2.0 =
245
+ * Show warnings next to MySQL variables with sub-optimal values
246
+
247
+ = 1.9.3 =
248
+ * Fix list of non-default query vars
249
+ * Fix list of admin screen column names in 3.3
250
+ * Lots of other misc tweaks
251
+ * Add RTL support
252
+
253
+ = 1.9.2 =
254
+ * Lots of interface improvements
255
+ * Show counts for transients, HTTP requests and custom query vars in the admin menu
256
+ * Add backtrace to PHP error output
257
+ * Hide repeated identical PHP errors
258
+ * Filter out calls to _deprecated_*() and trigger_error() in backtraces
259
+ * Show do_action_ref_array() and apply_filters_ref_array() parameter in backtraces
260
+ * Remove the 'component' code
261
+ * Remove the object cache output
262
+ * Add a 'qm_template' filter so themes that do crazy things can report the correct template file
263
+
264
+ = 1.9.1 =
265
+ * Display all custom column filter names on admin screens that contain columns
266
+
267
+ = 1.9 =
268
+ * Display more accurate $current_screen values
269
+ * Display a warning message about bug with $typenow and $current_screen values
270
+ * Improve PHP error backtrace
271
+
272
+ = 1.8 =
273
+ * Introduce a 'view_query_monitor' capability for finer grained permissions control
274
+
275
+ = 1.7.11 =
276
+ * List body classes with the template output
277
+ * Display calling function in PHP warnings and notices
278
+ * Fix admin bar CSS when displaying notices
279
+ * Remove pointless non-existant filter code
280
+
281
+ = 1.7.10.1 =
282
+ * Fix a formatting error in the transient table
283
+
284
+ = 1.7.10 =
285
+ * Tweaks to counts, HTTP output and transient output
286
+ * Upgrade routine which adds a symlink to db.php in wp-content/db.php
287
+
288
+ = 1.7.9 =
289
+ * PHP warning and notice handling
290
+ * Add some new template conditionals
291
+ * Tweaks to counts, HTTP output and transient output
wp-content/db.php ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: Query Monitor
4
+
5
+ *********************************************************************
6
+
7
+ Ensure this file is symlinked to your wp-content directory to provide
8
+ additional database query information in Query Monitor's output.
9
+
10
+ *********************************************************************
11
+
12
+ Copyright 2013 John Blackbourn
13
+
14
+ This program is free software; you can redistribute it and/or modify
15
+ it under the terms of the GNU General Public License as published by
16
+ the Free Software Foundation; either version 2 of the License, or
17
+ (at your option) any later version.
18
+
19
+ This program is distributed in the hope that it will be useful,
20
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
+ GNU General Public License for more details.
23
+
24
+ */
25
+
26
+ defined( 'ABSPATH' ) or die();
27
+
28
+ if ( ! is_readable( $autoloader = dirname( __FILE__ ) . '/../autoloader.php' ) )
29
+ return;
30
+
31
+ include_once $autoloader;
32
+
33
+ if ( !defined( 'SAVEQUERIES' ) )
34
+ define( 'SAVEQUERIES', true );
35
+
36
+ class QueryMonitorDB extends wpdb {
37
+
38
+ public $qm_php_vars = array(
39
+ 'max_execution_time' => null,
40
+ 'memory_limit' => null,
41
+ 'upload_max_filesize' => null,
42
+ 'post_max_size' => null,
43
+ 'display_errors' => null,
44
+ 'log_errors' => null,
45
+ );
46
+
47
+ /**
48
+ * Class constructor
49
+ */
50
+ function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) {
51
+
52
+ foreach ( $this->qm_php_vars as $setting => &$val )
53
+ $val = ini_get( $setting );
54
+
55
+ parent::__construct( $dbuser, $dbpassword, $dbname, $dbhost );
56
+
57
+ }
58
+
59
+ /**
60
+ * Perform a MySQL database query, using current database connection.
61
+ *
62
+ * More information can be found on the codex page.
63
+ *
64
+ * @since 0.71
65
+ *
66
+ * @param string $query Database query
67
+ * @return int|false Number of rows affected/selected or false on error
68
+ */
69
+ function query( $query ) {
70
+ if ( ! $this->ready )
71
+ return false;
72
+
73
+ if ( $this->show_errors and class_exists( 'QM_Component_DB_Queries' ) )
74
+ $this->hide_errors();
75
+
76
+ // some queries are made before the plugins have been loaded, and thus cannot be filtered with this method
77
+ $query = apply_filters( 'query', $query );
78
+
79
+ $return_val = 0;
80
+ $this->flush();
81
+
82
+ // Log how the function was called
83
+ $this->func_call = "\$db->query(\"$query\")";
84
+
85
+ // Keep track of the last query for debug..
86
+ $this->last_query = $query;
87
+
88
+ if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES )
89
+ $this->timer_start();
90
+
91
+ $this->result = @mysql_query( $query, $this->dbh );
92
+ $this->num_queries++;
93
+
94
+ if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
95
+ $trace = new QM_Backtrace;
96
+ $q = array(
97
+ 'sql' => $query,
98
+ 'ltime' => $this->timer_stop(),
99
+ 'stack' => implode( ', ', array_reverse( $trace->get_stack() ) ),
100
+ 'trace' => $trace,
101
+ 'result' => null,
102
+ );
103
+ # Numeric indices are for compatibility for anything else using saved queries
104
+ $q[0] = $q['sql'];
105
+ $q[1] = $q['ltime'];
106
+ $q[2] = $q['stack'];
107
+ $this->queries[$this->num_queries] = $q;
108
+ }
109
+
110
+ // If there is an error then take note of it..
111
+ if ( $this->last_error = mysql_error( $this->dbh ) ) {
112
+ if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES )
113
+ $this->queries[$this->num_queries]['result'] = new WP_Error( 'qmdb', $this->last_error );
114
+ // Clear insert_id on a subsequent failed insert.
115
+ if ( $this->insert_id && preg_match( '/^\s*(insert|replace)\s/i', $query ) )
116
+ $this->insert_id = 0;
117
+
118
+ $this->print_error();
119
+ return false;
120
+ }
121
+
122
+ if ( preg_match( '/^\s*(create|alter|truncate|drop)\s/i', $query ) ) {
123
+ $return_val = $this->result;
124
+ } elseif ( preg_match( '/^\s*(insert|delete|update|replace)\s/i', $query ) ) {
125
+ $this->rows_affected = mysql_affected_rows( $this->dbh );
126
+ // Take note of the insert_id
127
+ if ( preg_match( '/^\s*(insert|replace)\s/i', $query ) ) {
128
+ $this->insert_id = mysql_insert_id($this->dbh);
129
+ }
130
+ // Return number of rows affected
131
+ $return_val = $this->rows_affected;
132
+ } else {
133
+ $num_rows = 0;
134
+ while ( $row = @mysql_fetch_object( $this->result ) ) {
135
+ $this->last_result[$num_rows] = $row;
136
+ $num_rows++;
137
+ }
138
+
139
+ // Log number of rows the query returned
140
+ // and return number of rows selected
141
+ $this->num_rows = $num_rows;
142
+ $return_val = $num_rows;
143
+ }
144
+
145
+ if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES )
146
+ $this->queries[$this->num_queries]['result'] = $return_val;
147
+
148
+ return $return_val;
149
+ }
150
+
151
+ }
152
+
153
+ $wpdb = new QueryMonitorDB( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );