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

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