PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.4.4
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.4.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 / helper.php

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

536 lines 15.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 // Various constants
18
19 $upload_dir = wp_upload_dir();
20 define('CODE_PROFILER_UPLOAD_DIR', $upload_dir['basedir'] .'/code-profiler' );
21 define('CODE_PROFILER_LOG', CODE_PROFILER_UPLOAD_DIR .'/log.php' );
22 define('CODE_PROFILER_TMP_IOSTATS_LOG', 'iostats.tmp' );
23 define('CODE_PROFILER_TMP_SUMMARY_LOG', 'summary.tmp' );
24 define('CODE_PROFILER_TMP_TICKS_LOG', 'ticks.tmp' );
25 define('CODE_PROFILER_TMP_DISKIO_LOG', 'diskio.tmp' );
26 define('CODE_PROFILER_UPDATE_NOTICE', '<div class="updated notice is-dismissible"><p>%s</p></div>' );
27 define('CODE_PROFILER_ERROR_NOTICE', '<div class="error notice is-dismissible"><p>%s</p></div>' );
28 define('CODE_PROFILER_UPDATE_URI', 'https://code-profiler.com/update/' );
29 global $wp_version;
30 if (! defined('CODE_PROFILER_UA') ) { // UA signatures can be user-defined in the wp-config.php
31 define ('CODE_PROFILER_UA', [
32 esc_html__('Desktop', 'code-profiler') => [
33 'Firefox' => 'Mozilla/5.0 (Linux x86_64; rv:100.0) Gecko/20100101 Firefox/100.0',
34 'Chrome' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)'.
35 ' Chrome/102.0.5005.63 Safari/537.36',
36 'Edge' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,'.
37 ' like Gecko) Chrome/102.0.5005.63 Safari/537.36 Edg/100.0.1185.39'
38 ],
39 esc_html__('Mobile', 'code-profiler') => [
40 'Android Phone' => 'Mozilla/5.0 (Android 12; Mobile; rv:68.0) Gecko/68.0 Firefox/100.0',
41 'Android Tablet' => 'Mozilla/5.0 (Linux; Android 12.0; SAMSUNG-SM-T377A Build/NMF26X)'.
42 ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.58 Mobile Safari/537.36',
43 'iPhone' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X) AppleWebKit/605.1.15'.
44 ' (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
45 'iPad' => 'Mozilla/5.0 (iPad; CPU OS 15_5 like Mac OS X) AppleWebKit/605.1.15'.
46 ' (KHTML, like Gecko) GSA/213.0.449417121 Mobile/15E148 Safari/604.1'
47 ],
48 esc_html__('Bot', 'code-profiler') => [
49 'Google Bot' => 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
50 'WordPress' => 'Mozilla/5.0 (compatible; CodeProfiler for WordPress/'. $wp_version .
51 '; https://code-profiler.com/)'
52 ]
53
54 ] );
55 }
56 if (! defined('CODE_PROFILER_MUPLUGIN') ) {
57 define('CODE_PROFILER_MUPLUGIN', '0----code-profiler.php');
58 }
59 // =====================================================================
60 // Update version in the DB and check if options must be updated.
61
62 function code_profiler_init_update() {
63
64 $cp_options = get_option( 'code-profiler' );
65 if ( empty( $cp_options['version'] ) ||
66 version_compare( $cp_options['version'], CODE_PROFILER_VERSION, '<' ) ) {
67
68 $cp_options['version'] = CODE_PROFILER_VERSION;
69
70 // Version 1.1
71 if (! isset( $cp_options['enable_wpcli'] ) ) {
72 $cp_options['enable_wpcli'] = 1;
73 }
74
75 // Version 1.2
76 if (! isset( $cp_options['disable_wpcron'] ) ) {
77 $cp_options['disable_wpcron'] = 1;
78 }
79
80 // Version 1.3.1
81 if (! isset( $cp_options['http_response'] ) ) {
82 $cp_options['http_response'] = '^(?:3|4|5)\d{2}$';
83 }
84
85 // Update version in the DB
86 update_option( 'code-profiler', $cp_options );
87 }
88
89 }
90 add_action('admin_init', 'code_profiler_init_update' );
91
92 // =====================================================================
93 // Verify or create our storage folder in the uploads directory.
94 // Call during activation and access to the plugin.
95
96 function code_profiler_check_uploadsdir() {
97
98 if (! file_exists( CODE_PROFILER_UPLOAD_DIR ) ) {
99 mkdir( CODE_PROFILER_UPLOAD_DIR, 0755 );
100 }
101 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/index.html' ) ) {
102 touch( CODE_PROFILER_UPLOAD_DIR .'/index.html' );
103 }
104 // For Apache & Litespeed
105 if (! file_exists( CODE_PROFILER_UPLOAD_DIR .'/.htaccess' ) ) {
106 file_put_contents(
107 CODE_PROFILER_UPLOAD_DIR .'/.htaccess',
108 'Require all denied'
109 );
110 }
111 // Make sure there's a MU plugin directory
112 if (! is_dir( WPMU_PLUGIN_DIR ) ) {
113 wp_mkdir_p( WPMU_PLUGIN_DIR );
114 }
115
116
117 if (! file_exists( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) ) {
118
119 if (! file_exists( __DIR__ .'/mu.plugin' ) ) {
120 code_profiler_log_error( sprintf(
121 esc_html__('Cannot find the MU plugin: %s', 'code-profiler'),
122 __DIR__ .'/mu.plugin'
123 ));
124 } else {
125 $res = copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
126 if ( $res === false ) {
127 code_profiler_log_error( sprintf(
128 esc_html__('Cannot copy the MU plugin to %s', 'code-profiler'),
129 WPMU_PLUGIN_DIR
130 ));
131 }
132 }
133 } else {
134 // Update MU plugin it if needed
135 if ( md5_file( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) !== md5_file( __DIR__ .'/mu.plugin' ) ) {
136 copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
137 }
138 }
139
140 // Log file
141 if (! file_exists( CODE_PROFILER_LOG ) ) {
142 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
143 }
144
145 }
146
147 // =====================================================================
148 // Create the default options.
149
150 function code_profiler_default_options() {
151
152 $cp_options = [
153 'show_paths' => 'relative',
154 'display_name' => 'full',
155 'truncate_name' => 30,
156 'chart_type' => 'x',
157 'chart_max_plugins' => 25,
158 'hide_empty_value' => 1,
159 'table_max_rows' => 30,
160 'warn_composer' => 1,
161 'enable_wpcli' => 1,
162 'disable_wpcron' => 1,
163 'http_response' => '^(?:3|4|5)\d{2}$'
164 ];
165
166 update_option( 'code-profiler', $cp_options );
167
168 }
169
170 // =====================================================================
171 // Create a profile name.
172
173 function code_profiler_profile_name() {
174
175 return date('Y-m-d_') . substr( time(), 4 );
176
177 }
178
179 // =====================================================================
180 // Disable opcode cache.
181
182 function code_profiler_disable_opcode() {
183
184 try {
185 if (extension_loaded('Zend OPcache')) {
186 ini_set('opcache.enable', 0);
187 } elseif ( extension_loaded('wincache') ) {
188 ini_set('wincache.fcenabled', 0);
189 }
190 set_time_limit(0);
191
192 } catch ( Exception $e ) { }
193
194 $cp_options = get_option( 'code-profiler' );
195 if (! empty( $cp_options['disable_wpcron'] ) ) {
196 if (! defined( 'DISABLE_WP_CRON' ) ) {
197 define('DISABLE_WP_CRON', true );
198 }
199 }
200
201 }
202 // =====================================================================
203 // Verify the security key when running the profile.
204
205 function code_profiler_verify_key() {
206
207 $response = [
208 'status' => 'error',
209 'message' => __('Security keys do not match. Reload the page and try again (%s)', 'code-profiler' )
210 ];
211
212 $cp_options = get_option( 'code-profiler' );
213
214 if ( empty( $cp_options['hash'] ) ) {
215 $response['message'] = sprintf( $response['message'], '#1' );
216
217 } else {
218 if ( empty( $_REQUEST['profiler_key'] ) ) {
219 $response['message'] = sprintf( $response['message'], '#2' );
220
221 } else {
222 if ( $cp_options['hash'] == sha1( $_REQUEST['profiler_key'] ) ) {
223 return;
224 } else {
225 $response['message'] = sprintf( $response['message'], '#3' );
226 }
227 }
228 }
229 wp_send_json( $response );
230
231 }
232
233 // =====================================================================
234 // Write message to the log.
235 // Log level can be a combination of INFO (1), WARN (2) and ERROR (4).
236
237 function code_profiler_write2log( $message, $level = 1 ) {
238
239 file_put_contents( CODE_PROFILER_LOG, time() ."~~$level~~$message\n", FILE_APPEND );
240 }
241
242 function code_profiler_log_info( $string ) { code_profiler_write2log( $string, 1 ); }
243 function code_profiler_log_warn( $string ) { code_profiler_write2log( $string, 2 ); }
244 function code_profiler_log_error( $string ) { code_profiler_write2log( $string, 4 ); }
245
246 // =====================================================================
247 // Verify if a profile exists and return its full path.
248
249 function code_profiler_get_profile_path( $id, $type = 'slugs' ) {
250
251 $return = false;
252
253 if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
254 $glob = glob( CODE_PROFILER_UPLOAD_DIR ."/$id.*.$type.profile" );
255 if ( is_array( $glob ) && ! empty( $glob[0] ) ) {
256 if ( preg_match( "`/$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
257
258 return CODE_PROFILER_UPLOAD_DIR. "/$id.{$match[1]}";
259 }
260 }
261 }
262 return false;
263 }
264
265 // =====================================================================
266 // Open, parse and return the content of a profile file.
267
268 function code_profiler_get_profile_data( $file, $type = 'slugs' ) {
269
270 $buffer = [];
271
272 $fh = fopen( "$file.$type.profile", 'rb' );
273
274 if ( $fh === false ) {
275 $err = sprintf(
276 CODE_PROFILER_ERROR_NOTICE,
277 sprintf(
278 esc_html__('Unable to open profile file: %s.', 'code-profiler' ),
279 esc_html( "$file.$type.profile" )
280 )
281 );
282 $buffer['error'] = $err;
283 return $buffer;
284 }
285
286 while (! feof( $fh ) ) {
287 $buffer[] = fgetcsv( $fh, 1000, "\t" );
288 }
289
290 fclose( $fh );
291
292 if ( empty( $buffer ) ) {
293 $err = sprintf(
294 CODE_PROFILER_ERROR_NOTICE,
295 sprintf(
296 esc_html__('Profile file is empty or corrupted: %s.', 'code-profiler' ),
297 esc_html( "$file.$type.profile" )
298 )
299 );
300 $buffer['error'] = $err;
301 return $buffer;
302 }
303
304 // Get rid of the empty field created by fgetcsv
305 return array_filter( $buffer );
306 }
307
308 // =====================================================================
309 // Exit with a json-encoded error message except if we're running
310 // from WP CLI
311
312 function code_profiler_wp_send_json( $response ) {
313
314 if ( defined( 'WP_CLI' ) && WP_CLI ) {
315 WP_CLI::error( $response['message'] );
316 }
317 wp_send_json( $response );
318 }
319
320 // =====================================================================
321 // Retrieve the summary stats for a given profile.
322
323 function code_profiler_getsummarystats( $profile_path, $type = 'html' ) {
324
325 if ( $type == 'html' ) {
326 $open = '<ul><li>&#10148; ';
327 $div = '</li><li>&#10148; ';
328 $close = '</li></ul>';
329 } else {
330 // WP-CLI
331 $open = " \u{27A4} ";
332 $div = "\n \u{27A4} ";
333 $close = "\n\n";
334 }
335
336 if (! file_exists( "$profile_path.summary.profile" ) ) {
337 return false;
338 }
339 $decode = json_decode( file_get_contents( "$profile_path.summary.profile" ) );
340
341 $string = $open;
342
343 if ( empty( $decode->items ) ) {
344 return false;
345 }
346 $decode->items--;
347 $string .= sprintf(
348 __('%s plugins and 1 theme', 'code-profiler'),
349 (int) $decode->items
350 );
351
352 $string .= $div;
353
354 if ( empty( $decode->time ) ) {
355 return false;
356 }
357 $string .= sprintf(
358 __('Execution time: %ss', 'code-profiler'),
359 number_format( $decode->time, 4 )
360 );
361
362 $string .= $div;
363
364 if ( empty( $decode->memory ) ) {
365 return false;
366 }
367 $string .= sprintf(
368 __('Peak memory: %s MB', 'code-profiler'),
369 number_format( $decode->memory, 2)
370 );
371
372 $string .= $div;
373
374 if ( empty( $decode->io ) ) {
375 return false;
376 }
377 $string .= sprintf(
378 __('File I/O operations: %s', 'code-profiler'),
379 number_format( $decode->io )
380 );
381
382 $string .= $div;
383
384 if ( empty( $decode->queries ) ) {
385 return false;
386 }
387 $string .= sprintf(
388 __('SQL queries: %s', 'code-profiler'),
389 number_format( $decode->queries )
390 );
391
392 $string .= $close;
393
394 return $string;
395 }
396
397 // =====================================================================
398 // Remove *tmp files left after an HTTP error (4xx or 5xx).
399
400 function code_profiler_cleantmpfiles() {
401
402 $glob = glob( CODE_PROFILER_UPLOAD_DIR .'/*.tmp' );
403 if ( is_array( $glob ) ) {
404 $count = 0;
405 foreach( $glob as $file ) {
406 $count++;
407 unlink( $file );
408 }
409 if ( $count ) {
410 code_profiler_log_info( sprintf(
411 __('Deleting %s temporary files found in the profiles folder', 'code-profiler' ),
412 (int) $count
413 ) );
414 }
415 }
416 }
417
418 // =====================================================================
419 // Check and inform if there's an update.
420
421 function code_profiler_check_update( $transient ) {
422
423 if ( empty( $transient->checked['code-profiler/index.php'] ) ) {
424 $version = CODE_PROFILER_VERSION;
425 } else {
426 $version = $transient->checked['code-profiler/index.php'];
427 }
428
429 $args = [
430 'slug' => 'code-profiler',
431 'version' => $version
432 ];
433
434 $domain = strtolower( parse_url( network_home_url(), PHP_URL_HOST ) );
435 global $wp_version;
436 $request_string = array(
437 'body' => array(
438 'action' => 'version',
439 'request' => serialize( $args ),
440 'version' => CODE_PROFILER_VERSION,
441 ),
442 'user-agent' => "WordPress/$wp_version;$domain; ".
443 CODE_PROFILER_VERSION .'; version'
444 );
445
446 $res = wp_remote_post( CODE_PROFILER_UPDATE_URI, $request_string);
447
448 if (! is_wp_error( $res ) && $res['response']['code'] == 200 ) {
449 $response = unserialize( $res['body'] );
450 }
451 if ( ! empty( $response ) && is_object( $response ) ) {
452 $transient->response['code-profiler/index.php'] = $response;
453 } else {
454 // No update
455 $item = (object) array(
456 'id' => 'code-profiler/index.php',
457 'slug' => 'code-profiler',
458 'plugin' => 'code-profiler/index.php',
459 'new_version' => CODE_PROFILER_VERSION,
460 'url' => '',
461 'package' => '',
462 'icons' => array(),
463 'banners' => array(),
464 'banners_rtl' => array(),
465 'tested' => '',
466 'requires_php' => '',
467 'compatibility' => new stdClass(),
468 );
469 // Needed for the enable/disable auto-updates link.
470 $transient->no_update['code-profiler/index.php'] = $item;
471 }
472
473 return $transient;
474 }
475
476 add_filter('pre_set_site_transient_update_plugins', 'code_profiler_check_update');
477
478 // =====================================================================
479 // We return the new version's changelog.
480
481 function code_profiler_check_plugin_info( $def, $action, $args ) {
482
483 if (! isset($args->slug) || $args->slug != 'code-profiler' ||
484 $action != 'plugin_information' ) {
485
486 return $def;
487 }
488 $plugin_info = get_site_transient('update_plugins');
489 if ( isset( $plugin_info->checked['code-profiler/index.php'] ) ) {
490 $current_version = $plugin_info->checked['code-profiler/index.php'];
491 } else {
492 $current_version = CODE_PROFILER_VERSION;
493 }
494 $args->version = $current_version;
495
496 $domain = strtolower( parse_url( network_home_url(), PHP_URL_HOST ) );
497 global $wp_version;
498
499 $request_string = array(
500 'body' => array(
501 'action' => 'plugin_information',
502 'request' => serialize( $args ),
503 'version' => CODE_PROFILER_VERSION
504 ),
505 'user-agent' => "WordPress/$wp_version;$domain; ".
506 CODE_PROFILER_VERSION .'; plugin_information'
507 );
508
509 $res = wp_remote_post( CODE_PROFILER_UPDATE_URI, $request_string );
510
511 if (! is_wp_error( $res ) && $res['response']['code'] == 200 ) {
512 // Server can return either serialized data or a json-encoded error
513 if ( is_serialized( $res['body'] ) ) {
514 return unserialize( $res['body'] );
515 }
516 }
517 return false;
518 }
519
520 add_filter('plugins_api', 'code_profiler_check_plugin_info', 10, 3);
521
522 // =====================================================================
523 // We don't want to be bothered by other themes/plugins' admin notices.
524
525 add_action('admin_head', 'code_profiler_hide_admin_notices');
526
527 function code_profiler_hide_admin_notices() {
528 if ( isset( $_GET['page'] ) && $_GET['page'] == 'code-profiler' ) {
529 remove_all_actions('admin_notices');
530 remove_all_actions('all_admin_notices');
531 }
532 }
533
534 // =====================================================================
535 // EOF
536