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

446 lines 12.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 // 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:104.0) Gecko/20100101 Firefox/104.0',
33 'Chrome' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)'.
34 ' Chrome/105.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/105.0.0.0 Safari/537.36 Edg/105.0.1343.27'
37 ],
38 esc_html__('Mobile', 'code-profiler') => [
39 'Android Phone' => 'Mozilla/5.0 (Android 12; Mobile; rv:68.0) Gecko/68.0 Firefox/104.0',
40 'Android Tablet' => 'Mozilla/5.0 (Linux; Android 12.0; SAMSUNG-SM-T377A Build/NMF26X)'.
41 ' AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Mobile Safari/537.36',
42 'iPhone' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_5 like Mac OS X) AppleWebKit/605.1.15'.
43 ' (KHTML, like Gecko) Version/15.4 Mobile/15E148 Safari/604.1',
44 'iPad' => 'Mozilla/5.0 (iPad; CPU OS 15_5 like Mac OS X) AppleWebKit/605.1.15'.
45 ' (KHTML, like Gecko) GSA/213.0.449417121 Mobile/15E148 Safari/604.1'
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 (! file_exists( __DIR__ .'/mu.plugin') ) {
133 code_profiler_log_error( sprintf(
134 esc_html__('Cannot find the MU plugin: %s', 'code-profiler'),
135 __DIR__ .'/mu.plugin'
136 ));
137 } else {
138 $res = copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
139 if ( $res === false ) {
140 code_profiler_log_error( sprintf(
141 esc_html__('Cannot copy the MU plugin to %s', 'code-profiler'),
142 WPMU_PLUGIN_DIR
143 ));
144 }
145 }
146 } else {
147 // Update MU plugin it if needed
148 if ( md5_file( WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN ) !== md5_file( __DIR__ .'/mu.plugin') ) {
149 copy( __DIR__ .'/mu.plugin', WPMU_PLUGIN_DIR .'/'. CODE_PROFILER_MUPLUGIN );
150 }
151 }
152
153 // Log file
154 if (! file_exists( CODE_PROFILER_LOG ) ) {
155 file_put_contents( CODE_PROFILER_LOG, "<?php exit; ?>\n" );
156 }
157
158 }
159
160 // =====================================================================
161 // Create the default options.
162
163 function code_profiler_default_options() {
164
165 $cp_options = [
166 'show_paths' => 'relative',
167 'display_name' => 'full',
168 'truncate_name' => 30,
169 'chart_type' => 'x',
170 'chart_max_plugins' => 25,
171 'hide_empty_value' => 1,
172 'table_max_rows' => 30,
173 'warn_composer' => 1,
174 'enable_wpcli' => 1,
175 'disable_wpcron' => 1,
176 'http_response' => '^(?:3|4|5)\d{2}$',
177 'accuracy' => 1
178 ];
179
180 update_option('code-profiler', $cp_options );
181
182 }
183
184 // =====================================================================
185 // Create a profile name.
186
187 function code_profiler_profile_name() {
188
189 return date('Y-m-d_') . substr( time(), 4 );
190
191 }
192
193 // =====================================================================
194 // Disable opcode cache.
195
196 function code_profiler_disable_opcode() {
197
198 try {
199 if (extension_loaded('Zend OPcache')) {
200 ini_set('opcache.enable', 0);
201 } elseif ( extension_loaded('wincache') ) {
202 ini_set('wincache.fcenabled', 0);
203 }
204 set_time_limit(0);
205
206 } catch ( Exception $e ) { }
207
208 $cp_options = get_option('code-profiler');
209 if (! empty( $cp_options['disable_wpcron'] ) ) {
210 if (! defined('DISABLE_WP_CRON') ) {
211 define('DISABLE_WP_CRON', true );
212 }
213 }
214
215 }
216 // =====================================================================
217 // Verify the security key when running the profile.
218
219 function code_profiler_verify_key() {
220
221 $response = [
222 'status' => 'error',
223 'message' => __('Security keys do not match. Reload the page and try again (%s)', 'code-profiler')
224 ];
225
226 $cp_options = get_option('code-profiler');
227
228 if ( empty( $cp_options['hash'] ) ) {
229 $response['message'] = sprintf( $response['message'], '#1');
230
231 } else {
232 if ( empty( $_REQUEST['profiler_key'] ) ) {
233 $response['message'] = sprintf( $response['message'], '#2');
234
235 } else {
236 if ( $cp_options['hash'] == sha1( $_REQUEST['profiler_key'] ) ) {
237 return;
238 } else {
239 $response['message'] = sprintf( $response['message'], '#3');
240 }
241 }
242 }
243 wp_send_json( $response );
244
245 }
246
247 // =====================================================================
248 // Write message to the log.
249 // Log level can be a combination of INFO (1), WARN (2) and ERROR (4).
250
251 function code_profiler_write2log( $message, $level = 1 ) {
252
253 file_put_contents( CODE_PROFILER_LOG, time() ."~~$level~~$message\n", FILE_APPEND );
254 }
255
256 function code_profiler_log_info( $string ) { code_profiler_write2log( $string, 1 ); }
257 function code_profiler_log_warn( $string ) { code_profiler_write2log( $string, 2 ); }
258 function code_profiler_log_error( $string ) { code_profiler_write2log( $string, 4 ); }
259
260 // =====================================================================
261 // Verify if a profile exists and return its full path.
262
263 function code_profiler_get_profile_path( $id, $type = 'slugs') {
264
265 $return = false;
266
267 if (! empty( $id ) && preg_match('/^\d{10}\.\d+$/', $id ) ) {
268 $glob = glob( CODE_PROFILER_UPLOAD_DIR ."/$id.*.$type.profile" );
269 if ( is_array( $glob ) && ! empty( $glob[0] ) ) {
270 if ( preg_match( "`/$id\.(.+?).$type.profile$`", $glob[0], $match ) ) {
271
272 return CODE_PROFILER_UPLOAD_DIR. "/$id.{$match[1]}";
273 }
274 }
275 }
276 return false;
277 }
278
279 // =====================================================================
280 // Open, parse and return the content of a profile file.
281
282 function code_profiler_get_profile_data( $file, $type = 'slugs') {
283
284 $buffer = [];
285
286 $fh = fopen( "$file.$type.profile", 'rb');
287
288 if ( $fh === false ) {
289 $err = sprintf(
290 CODE_PROFILER_ERROR_NOTICE,
291 sprintf(
292 esc_html__('Unable to open profile file: %s.', 'code-profiler'),
293 esc_html( "$file.$type.profile" )
294 )
295 );
296 $buffer['error'] = $err;
297 return $buffer;
298 }
299
300 while (! feof( $fh ) ) {
301 $buffer[] = fgetcsv( $fh, 1000, "\t" );
302 }
303
304 fclose( $fh );
305
306 if ( empty( $buffer ) ) {
307 $err = sprintf(
308 CODE_PROFILER_ERROR_NOTICE,
309 sprintf(
310 esc_html__('Profile file is empty or corrupted: %s.', 'code-profiler'),
311 esc_html( "$file.$type.profile" )
312 )
313 );
314 $buffer['error'] = $err;
315 return $buffer;
316 }
317
318 // Get rid of the empty field created by fgetcsv
319 return array_filter( $buffer );
320 }
321
322 // =====================================================================
323 // Exit with a json-encoded error message except if we're running
324 // from WP CLI
325
326 function code_profiler_wp_send_json( $response ) {
327
328 if ( defined('WP_CLI') && WP_CLI ) {
329 WP_CLI::error( $response['message'] );
330 }
331 wp_send_json( $response );
332 }
333
334 // =====================================================================
335 // Retrieve the summary stats for a given profile.
336
337 function code_profiler_getsummarystats( $profile_path, $type = 'html') {
338
339 if ( $type == 'html') {
340 $open = '<ul><li>&#10148; ';
341 $div = '</li><li>&#10148; ';
342 $close = '</li></ul>';
343 } else {
344 // WP-CLI
345 $open = " \u{27A4} ";
346 $div = "\n \u{27A4} ";
347 $close = "\n\n";
348 }
349
350 if (! file_exists( "$profile_path.summary.profile" ) ) {
351 return false;
352 }
353 $decode = json_decode( file_get_contents( "$profile_path.summary.profile" ) );
354
355 $string = $open;
356
357 if ( empty( $decode->items ) ) {
358 return false;
359 }
360 $decode->items--;
361 $string .= sprintf(
362 __('%s plugins and 1 theme', 'code-profiler'),
363 (int) $decode->items
364 );
365
366 $string .= $div;
367
368 if ( empty( $decode->time ) ) {
369 return false;
370 }
371 $string .= sprintf(
372 __('Execution time: %ss', 'code-profiler'),
373 number_format( $decode->time, 4 )
374 );
375
376 $string .= $div;
377
378 if ( empty( $decode->memory ) ) {
379 return false;
380 }
381 $string .= sprintf(
382 __('Peak memory: %s MB', 'code-profiler'),
383 number_format( $decode->memory, 2)
384 );
385
386 $string .= $div;
387
388 if ( empty( $decode->io ) ) {
389 return false;
390 }
391 $string .= sprintf(
392 __('File I/O operations: %s', 'code-profiler'),
393 number_format( $decode->io )
394 );
395
396 $string .= $div;
397
398 if ( empty( $decode->queries ) ) {
399 return false;
400 }
401 $string .= sprintf(
402 __('SQL queries: %s', 'code-profiler'),
403 number_format( $decode->queries )
404 );
405
406 $string .= $close;
407
408 return $string;
409 }
410
411 // =====================================================================
412 // Remove *tmp files left after an HTTP error (4xx or 5xx).
413
414 function code_profiler_cleantmpfiles() {
415
416 $glob = glob( CODE_PROFILER_UPLOAD_DIR .'/*.tmp');
417 if ( is_array( $glob ) ) {
418 $count = 0;
419 foreach( $glob as $file ) {
420 $count++;
421 unlink( $file );
422 }
423 if ( $count ) {
424 code_profiler_log_info( sprintf(
425 __('Deleting %s temporary files found in the profiles folder', 'code-profiler'),
426 (int) $count
427 ) );
428 }
429 }
430 }
431
432 // =====================================================================
433 // We don't want to be bothered by other themes/plugins' admin notices.
434
435 add_action('admin_head', 'code_profiler_hide_admin_notices');
436
437 function code_profiler_hide_admin_notices() {
438 if ( isset( $_GET['page'] ) && $_GET['page'] == 'code-profiler') {
439 remove_all_actions('admin_notices');
440 remove_all_actions('all_admin_notices');
441 }
442 }
443
444 // =====================================================================
445 // EOF
446