PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.4
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.4
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, at lib/ajax.php

301 lines 10.4 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 update_option( 'code-profiler', $cp_options );
117
118 // Build query
119 $url = add_query_arg( [
120 'CODE_PROFILER_ON' => $microtime,
121 'profiler_key' => $profiler_key,
122 ], $url );
123
124 global $wp_version;
125 $headers = [
126 'Cache-Control' => 'no-cache, no-store, must-revalidate',
127 'Pragma' => 'no-cache',
128 'Expires' => '0',
129 'httpversion' => '1.1',
130 // Devs must be allowed to use it on localhost over TLS too
131 'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
132 'timeout' => 180, // 180-second timeout instead of the default 5s
133 'redirection' => 0, // We don't want to be redirected
134 'headers' => [
135 'code-profiler-key' => $profiler_key,
136 'Accept-Language' => 'en-US,en;q=0.5',
137 'User-Agent' => $ua_signature
138 ]
139 ];
140
141 // Forward basic authentication if any (not available from WP CLI)
142 if ( function_exists('apache_request_headers') ) {
143 $apache_headers = apache_request_headers();
144 if ( isset( $apache_headers['Authorization'] ) ) {
145 $headers['headers']['Authorization'] = $apache_headers['Authorization'];
146 }
147 // WP-CLI (wp code-profiler run --u=FOO --p=BAR)
148 } elseif ( defined('WP_CLI') && ! empty( $_POST['Authorization'] ) ) {
149 $headers['headers']['Authorization'] = $_POST['Authorization'];
150 }
151
152 if ( $_POST['user'] == 'authenticated' ) {
153 // Used for authentication
154 if ( is_ssl() ) {
155 $cookie_auth = SECURE_AUTH_COOKIE;
156 $scheme = 'secure_auth';
157 } else {
158 $cookie_auth = AUTH_COOKIE;
159 $scheme = 'auth';
160 }
161 $id = get_current_user_id();
162 $headers['cookies'][ $cookie_auth ] = wp_generate_auth_cookie( $id, time() + 180, $scheme );
163 $headers['cookies'][ LOGGED_IN_COOKIE ] = wp_generate_auth_cookie( $id, time() + 180, 'logged_in' );
164 $session_id = session_id();
165 if ( $session_id !== false ) {
166 $session_name = session_name();
167 $headers['cookies'][ $session_name ] = $session_id;
168 }
169 }
170
171 if ( function_exists( 'opcache_reset' ) ) {
172 opcache_reset();
173 }
174
175 // We must allow developers to run the profiler
176 // on a local IP (e.g, http://127.0.0.1/)
177 add_filter( 'http_request_host_is_external', '__return_true' );
178 $res = wp_safe_remote_get( $url, $headers );
179
180 // Connection error
181 if ( is_wp_error( $res ) ) {
182 $msg = esc_html__('Cannot connect to the requested page: %s', 'code-profiler');
183 $response['message'] = sprintf(
184 $msg,
185 esc_html( $res->get_error_message() )
186 );
187 code_profiler_log_error( sprintf( $msg, $res->get_error_message() ) );
188 code_profiler_wp_send_json( $response );
189 }
190
191 // HTTP status code
192 if (! empty( $cp_options['http_response'] ) ) {
193 if ( preg_match( "/{$cp_options['http_response']}/", $res['response']['code'] ) ) {
194 $msg = esc_html__('The website returned the following HTTP status code: %s %s.', 'code-profiler').
195 ' '.
196 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');
197 $response['message'] = sprintf(
198 $msg,
199 (int) $res['response']['code'],
200 esc_html( $res['response']['message'] )
201 );
202 code_profiler_log_error( sprintf( $msg, $res['response']['code'], $res['response']['message'] ) );
203 code_profiler_wp_send_json( $response );
204 }
205 }
206 // Check response
207 $message = json_decode( $res['body'], true );
208 if ( isset( $message['status'] ) && isset( $message['message'] ) ) {
209 $response['status'] = $message['status'];
210 $response['message'] = $message['message'];
211 code_profiler_wp_send_json( $response );
212 }
213 code_profiler_log_info( esc_html__('Collecting data to analyze', 'code-profiler' ) );
214 // Return success
215 $response = ['status' => 'success'];
216 $response['message'] = 'success';
217 $response['microtime'] = $microtime;
218
219 // AJAX action?
220 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
221 code_profiler_wp_send_json( $response );
222 } else {
223 return json_encode( $response );
224 }
225
226 }
227
228 // =====================================================================
229
230 add_action( 'wp_ajax_codeprofiler_prepare_report', 'codeprofiler_prepare_report' );
231
232 function codeprofiler_prepare_report() {
233
234 $response = ['status' => 'error'];
235
236 // If this is an AJAX call, make sure it comes from an admin/superadmin.
237 if ( isset( $_REQUEST['action'] ) && $_REQUEST['action'] == 'codeprofiler_prepare_report' ) {
238 // Admin/Superadmin only
239 if (! is_super_admin() ) {
240 $msg = esc_html__('You are not allowed to performed this action', 'code-profiler');
241 $response['message'] = $msg;
242 code_profiler_log_error( $msg );
243 code_profiler_wp_send_json( $response );
244 }
245 }
246
247 // Verify the security nonce
248 if ( empty( $_POST['cp_nonce'] ) || ! wp_verify_nonce( $_POST['cp_nonce'], 'start_profiler_nonce' ) ) {
249 $msg = esc_html__('Missing or wrong security nonce. Reload the page and try again', 'code-profiler');
250 $response['message'] = $msg;
251 code_profiler_log_error( $msg );
252 code_profiler_wp_send_json( $response );
253 }
254
255 if ( empty( $_POST['microtime'] ) || ! preg_match( '/^\d{10}\.\d+$/', $_POST['microtime'] ) ) {
256 $msg = esc_html__('Missing parameter (microtime).', 'code-profiler');
257 $response['message'] = $msg;
258 code_profiler_log_error( $msg );
259 code_profiler_wp_send_json( $response );
260 }
261 $microtime = sanitize_text_field( $_POST['microtime'] );
262
263 $profile = sanitize_file_name( $_POST['profile'] );
264 if ( empty( $profile ) ) {
265 $msg = esc_html__('Missing profile name.', 'code-profiler');
266 $response['message'] = $msg;
267 code_profiler_log_error( $msg );
268 code_profiler_wp_send_json( $response );
269 }
270
271 code_profiler_log_info( esc_html__('Preparing report', 'code-profiler') );
272 require 'class-report.php';
273 $report = new CodeProfiler_Report( $profile, $microtime );
274 $report->prepare_report();
275
276 // Take a 1s break so that we can spot any potential error
277 // in the backend before AJAX refresh the page
278 usleep(1000000);
279
280 // Clear hash
281 $cp_options = get_option( 'code-profiler' );
282 unset( $cp_options['hash'] );
283 update_option( 'code-profiler', $cp_options );
284
285 code_profiler_log_info( esc_html__('All done, exiting profiler', 'code-profiler') );
286 $response['cp_profile'] = $microtime;
287 $response['status'] = 'success';
288 $response['message'] = 'success';
289
290 // AJAX action?
291 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
292 code_profiler_wp_send_json( $response );
293 } else {
294 return json_encode( $response );
295 }
296
297 }
298
299 // =====================================================================
300 // EOF
301