PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.4.1
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.4.1
1.9.5 1.9.4 1.9.3 trunk 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.6 1.6.1 1.6.10 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 All 40 releases
code-profiler / lib / ajax.php

ajax.php in Code Profiler – WordPress Performance Profiling and Debugging Made Easy 1.4.1, at lib/ajax.php

344 lines 11.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 +=====================================================================+
4 | ____ _ ____ __ _ _ |
5 | / ___|___ __| | ___ | _ \ _ __ ___ / _(_) | ___ _ __ |
6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
7 | | |__| (_) | (_| | __/ | __/| | | (_) | _| | | __/ | |
8 | \____\___/ \__,_|\___| |_| |_| \___/|_| |_|_|\___|_| |
9 | |
10 | (c) Jerome Bruandet ~ https://code-profiler.com/ |
11 +=====================================================================+
12 */
13
14 if (! defined( 'ABSPATH' ) ) { die( 'Forbidden' ); }
15
16 // =====================================================================
17 // Start the profiler.
18
19 add_action( 'wp_ajax_codeprofiler_start_profiler', 'codeprofiler_start_profiler' );
20
21 function codeprofiler_start_profiler() {
22
23 $response = ['status' => 'error'];
24
25 $cp_options = get_option( 'code-profiler' );
26
27 // If this is an AJAX call, make sure it comes from an admin/superadmin.
28 if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_start_profiler' ) {
29 // Admin/Superadmin only
30 if (! is_super_admin() ) {
31 $msg = esc_html__('You are not allowed to performed this action', 'code-profiler');
32 $response['message'] = $msg;
33 code_profiler_log_error( $msg );
34 code_profiler_wp_send_json( $response );
35 }
36 }
37
38 // Verify the security nonce
39 if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce' ) ) {
40 $msg = esc_html__('Missing or wrong security nonce. Reload the page and try again', 'code-profiler');
41 $response['message'] = $msg;
42 code_profiler_log_error( $msg );
43 code_profiler_wp_send_json( $response );
44 }
45
46 // Verify the MU plugin is loaded
47 if (! defined('CODE_PROFILER_MU_ON') ) {
48 $msg = esc_html__('The MU plugin is not loaded, please check the log', 'code-profiler');
49 $response['message'] = $msg;
50 code_profiler_log_error( $msg );
51 code_profiler_wp_send_json( $response );
52 }
53
54 // Frontend or backend
55 if ( empty( $_POST['where'] ) || ! in_array( $_POST['where'], [ 'frontend', 'backend', 'custom' ] ) ) {
56 $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'where' );
57 $response['message'] = $msg;
58 code_profiler_log_error( $msg );
59 code_profiler_wp_send_json( $response );
60 }
61 if ( empty( $_POST['post'] ) ) {
62 $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'post' );
63 $response['message'] = $msg;
64 code_profiler_log_error( $msg );
65 code_profiler_wp_send_json( $response );
66 }
67
68 // Make sure we have no more that 4 decimals, because when returning
69 // it via AJAX, it will display more decimals than that
70 $microtime = number_format( microtime( true ), 4, '.', '' );
71
72 // Authentication
73 if ( empty( $_POST['user'] ) || ! in_array( $_POST['user'], [ 'authenticated', 'unauthenticated' ] ) ) {
74 $msg = sprintf( esc_html__('Missing or incorrect parameter (%s)', 'code-profiler'), 'user' );
75 $response['message'] = $msg;
76 code_profiler_log_error( $msg );
77 code_profiler_wp_send_json( $response );
78 }
79
80 if ( empty( $_POST['profile'] ) || strlen( $_POST['profile'] ) > 100 ) {
81 $profile = code_profiler_profile_name();
82 } else {
83 $profile = sanitize_file_name( $_POST['profile'] );
84 }
85
86 // URI to profile
87 $url = esc_url_raw( $_POST['post'] );
88 code_profiler_log_info( sprintf(
89 esc_html__('Starting Code Profiler v%s for %s (profile: %s)', 'code-profiler' ),
90 CODE_PROFILER_VERSION,
91 $url,
92 $profile
93 ) );
94
95 // User-agent
96 if ( empty( $_POST['ua'] ) ) {
97 $ua = 'FireFox';
98 } else {
99 $ua = sanitize_text_field( $_POST['ua'] );
100 }
101 foreach( CODE_PROFILER_UA as $types => $types_array ) {
102 foreach( $types_array as $name => $value ) {
103 if ( $ua == $name ) {
104 $ua_signature = $value;
105 break;
106 }
107 }
108 }
109 if ( empty( $ua_signature ) ) {
110 $ua_signature = CODE_PROFILER_UA['Desktop']['FireFox'];
111 }
112
113 // Create security key
114 $profiler_key = bin2hex( random_bytes( 16 ) );
115 $cp_options['hash'] = sha1( $profiler_key );
116
117 // Build query
118 $url = add_query_arg( [
119 'CODE_PROFILER_ON' => $microtime,
120 'profiler_key' => $profiler_key,
121 ], $url );
122
123 global $wp_version;
124 $headers = [
125 'Cache-Control' => 'no-cache, no-store, must-revalidate',
126 'Pragma' => 'no-cache',
127 'Expires' => '0',
128 'httpversion' => '1.1',
129 // Devs must be allowed to use it on localhost over TLS too
130 'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
131 'timeout' => 180, // 180-second timeout instead of the default 5s
132 'redirection' => 0, // We don't want to be redirected
133 'headers' => [
134 'code-profiler-key' => $profiler_key,
135 'Accept-Language' => 'en-US,en;q=0.5',
136 'User-Agent' => $ua_signature
137 ]
138 ];
139
140 // Forward basic authentication if any (not available from WP CLI)
141 if ( function_exists('apache_request_headers') ) {
142 $apache_headers = apache_request_headers();
143 if ( isset( $apache_headers['Authorization'] ) ) {
144 $headers['headers']['Authorization'] = $apache_headers['Authorization'];
145 }
146 // WP-CLI (wp code-profiler run --u=FOO --p=BAR)
147 } elseif ( defined('WP_CLI') && ! empty( $_POST['Authorization'] ) ) {
148 $headers['headers']['Authorization'] = $_POST['Authorization'];
149 }
150
151 if ( $_POST['user'] == 'authenticated' ) {
152 // Used for authentication
153 if ( is_ssl() ) {
154 $cookie_auth = SECURE_AUTH_COOKIE;
155 $scheme = 'secure_auth';
156 } else {
157 $cookie_auth = AUTH_COOKIE;
158 $scheme = 'auth';
159 }
160 $id = get_current_user_id();
161 $headers['cookies'][ $cookie_auth ] = wp_generate_auth_cookie( $id, time() + 180, $scheme );
162 $headers['cookies'][ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( $id, time() + 180, 'logged_in' );
163 $session_id = session_id();
164 if ( $session_id !== false ) {
165 $session_name = session_name();
166 $headers['cookies'][ $session_name ] = $session_id;
167 }
168 }
169
170 if ( function_exists( 'opcache_reset' ) ) {
171 opcache_reset();
172 }
173
174 // GET or POST method
175 if (! empty( $_POST['method'] ) && $_POST['method'] == 'post' ) {
176 $safe_method = 'wp_safe_remote_post';
177 $cp_options['post'] = 1;
178
179 // Optional POST payload
180 if (! empty( $_POST['payload'] ) ) {
181 $payload_array = explode( PHP_EOL, trim( $_POST['payload'] ) );
182 foreach( $payload_array as $item ) {
183 $payload = explode( '=', trim( $item ), 2 );
184 if ( isset( $payload[1] ) ) {
185 $payload[0] = trim( $payload[0] );
186 $payload[1] = trim( $payload[1] );
187 $headers['body'][ $payload[0] ] = $payload[1];
188 }
189 }
190 $cp_options['payload'] = json_encode( $_POST['payload'] );
191 } else {
192 // POST request without a payload
193 unset( $cp_options['payload'] );
194 }
195 } else {
196 unset( $cp_options['post'] );
197 $safe_method = 'wp_safe_remote_get';
198 }
199
200 // Optional user-defined cookies
201 if (! empty( $_POST['cookies'] ) ) {
202 $cookies_array = explode( PHP_EOL, trim( $_POST['cookies'] ) );
203 foreach( $cookies_array as $item ) {
204 $cookie = explode( '=', trim( $item ), 2 );
205 if ( isset( $cookie[1] ) ) {
206 $cookie[0] = trim( $cookie[0] );
207 $cookie[1] = trim( $cookie[1] );
208 $headers['cookies'][ $cookie[0] ] = $cookie[1];
209 }
210 }
211 $cp_options['cookies'] = json_encode( $_POST['cookies'] );
212 } else {
213 unset( $cp_options['cookies'] );
214 }
215
216 update_option( 'code-profiler', $cp_options );
217
218 // We must allow developers to run the profiler
219 // on a local IP (e.g, http://127.0.0.1/)
220 add_filter( 'http_request_host_is_external', '__return_true' );
221 $res = $safe_method( $url, $headers );
222
223 // Connection error
224 if ( is_wp_error( $res ) ) {
225 $msg = esc_html__('Cannot connect to the requested page: %s', 'code-profiler');
226 $response['message'] = sprintf(
227 $msg,
228 esc_html( $res->get_error_message() )
229 );
230 code_profiler_log_error( sprintf( $msg, $res->get_error_message() ) );
231 code_profiler_wp_send_json( $response );
232 }
233
234 // HTTP status code
235 if (! empty( $cp_options['http_response'] ) ) {
236 if ( preg_match( "/{$cp_options['http_response']}/", $res['response']['code'] ) ) {
237 $msg = esc_html__('The website returned the following HTTP status code: %s %s.', 'code-profiler').
238 ' '.
239 esc_html__('By default, the profiler will always abort and throw an error if the server did not return a 200 HTTP status code. You can change that behaviour in the Settings section if the page you are profiling needs to return a different code (3xx, 4xx or 5xx).', 'code-profiler');
240 $response['message'] = sprintf(
241 $msg,
242 (int) $res['response']['code'],
243 esc_html( $res['response']['message'] )
244 );
245 code_profiler_log_error( sprintf( $msg, $res['response']['code'], $res['response']['message'] ) );
246 code_profiler_wp_send_json( $response );
247 }
248 }
249 // Check response
250 $message = json_decode( $res['body'], true );
251 if ( isset( $message['status'] ) && isset( $message['message'] ) ) {
252 $response['status'] = $message['status'];
253 $response['message'] = $message['message'];
254 code_profiler_wp_send_json( $response );
255 }
256 code_profiler_log_info( esc_html__('Collecting data to analyze', 'code-profiler' ) );
257 // Return success
258 $response = ['status' => 'success'];
259 $response['message'] = 'success';
260 $response['microtime'] = $microtime;
261
262 // AJAX action?
263 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
264 code_profiler_wp_send_json( $response );
265 } else {
266 return json_encode( $response );
267 }
268
269 }
270
271 // =====================================================================
272
273 add_action( 'wp_ajax_codeprofiler_prepare_report', 'codeprofiler_prepare_report' );
274
275 function codeprofiler_prepare_report() {
276
277 $response = ['status' => 'error'];
278
279 // If this is an AJAX call, make sure it comes from an admin/superadmin.
280 if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_prepare_report' ) {
281 // Admin/Superadmin only
282 if (! is_super_admin() ) {
283 $msg = esc_html__('You are not allowed to performed this action', 'code-profiler');
284 $response['message'] = $msg;
285 code_profiler_log_error( $msg );
286 code_profiler_wp_send_json( $response );
287 }
288 }
289
290 // Verify the security nonce
291 if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce' ) ) {
292 $msg = esc_html__('Missing or wrong security nonce. Reload the page and try again', 'code-profiler');
293 $response['message'] = $msg;
294 code_profiler_log_error( $msg );
295 code_profiler_wp_send_json( $response );
296 }
297
298 if ( empty( $_POST['microtime'] ) || ! preg_match( '/^\d{10}\.\d+$/', $_POST['microtime'] ) ) {
299 $msg = esc_html__('Missing parameter (microtime).', 'code-profiler');
300 $response['message'] = $msg;
301 code_profiler_log_error( $msg );
302 code_profiler_wp_send_json( $response );
303 }
304 $microtime = sanitize_text_field( $_POST['microtime'] );
305
306 $profile = sanitize_file_name( $_POST['profile'] );
307 if ( empty( $profile ) ) {
308 $msg = esc_html__('Missing profile name.', 'code-profiler');
309 $response['message'] = $msg;
310 code_profiler_log_error( $msg );
311 code_profiler_wp_send_json( $response );
312 }
313
314 code_profiler_log_info( esc_html__('Preparing report', 'code-profiler') );
315 require 'class-report.php';
316 $report = new CodeProfiler_Report( $profile, $microtime );
317 $report->prepare_report();
318
319 // Take a 1s break so that we can spot any potential error
320 // in the backend before AJAX refresh the page
321 usleep(1000000);
322
323 // Clear hash
324 $cp_options = get_option( 'code-profiler' );
325 unset( $cp_options['hash'] );
326 update_option( 'code-profiler', $cp_options );
327
328 code_profiler_log_info( esc_html__('All done, exiting profiler', 'code-profiler') );
329 $response['cp_profile'] = $microtime;
330 $response['status'] = 'success';
331 $response['message'] = 'success';
332
333 // AJAX action?
334 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
335 code_profiler_wp_send_json( $response );
336 } else {
337 return json_encode( $response );
338 }
339
340 }
341
342 // =====================================================================
343 // EOF
344