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 / helper.php

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

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