PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.7.4
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.7.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
← All changes | lib/class-report.php +139 -39 1.51.7.4 View file →
@@ -6,13 +6,13 @@
6 6 | | | / _ \ / _` |/ _ \ | |_) | '__/ _ \| |_| | |/ _ \ '__| |
7 7 | | |__| (_) | (_| | __/ | __/| | | (_) | _| | | __/ | |
8 8 | \____\___/ \__,_|\___| |_| |_| \___/|_| |_|_|\___|_| |
9 9 | |
10 - | (c) Jerome Bruandet ~ https://code-profiler.com/ |
10 + | (c) Jerome Bruandet ~ https://nintechnet.com/codeprofiler/ |
11 11 +=====================================================================+
12 12 */
13 13
14 -if (! defined( 'ABSPATH' ) ) { die( 'Forbidden' ); }
14 +if (! defined('ABSPATH') ) { die('Forbidden'); }
15 15
16 16
17 17 class CodeProfiler_Report {
18 18
@@ -22,8 +22,9 @@
22 22 private $themes = [];
23 23 private $plugins = [];
24 24 private $composer = [];
25 25 private $summary_list = [];
26 + private $cx_buffer = [];
26 27 private $parsed_data = 0;
27 28 private $plugins_dir;
28 29 private $themes_dir;
29 30 private $mu_dir;
@@ -33,9 +34,10 @@
33 34 private $microtime;
34 35 private $tmp_iostats;
35 36 private $tmp_summary;
36 37 private $tmp_diskio;
37 - private $tmp_ticks;
38 + private $tmp_connections;
39 + private $tmp_calls;
38 40
39 41
40 42 /**
41 43 * Initialize
@@ -41,41 +43,44 @@
41 43 * Initialize
42 44 */
43 45 public function __construct( $profile_name, $microtime ) {
44 46
45 - $this->tmp_summary = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
46 - CODE_PROFILER_TMP_SUMMARY_LOG;
47 - $this->tmp_iostats = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
48 - CODE_PROFILER_TMP_IOSTATS_LOG;
49 - $this->tmp_ticks = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
50 - CODE_PROFILER_TMP_TICKS_LOG;
51 - $this->tmp_diskio = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
52 - CODE_PROFILER_TMP_DISKIO_LOG;
47 + $this->tmp_summary = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
48 + CODE_PROFILER_TMP_SUMMARY_LOG;
49 + $this->tmp_iostats = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
50 + CODE_PROFILER_TMP_IOSTATS_LOG;
51 + $this->tmp_calls = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
52 + CODE_PROFILER_TMP_CALLS_LOG;
53 + $this->tmp_diskio = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
54 + CODE_PROFILER_TMP_DISKIO_LOG;
55 + $this->tmp_connections = CODE_PROFILER_UPLOAD_DIR ."/$microtime." .
56 + CODE_PROFILER_TMP_CONNECTIONS_LOG;
53 57 // Used for naming, not metrics
54 58 $this->microtime = $microtime;
55 59
56 60 // Make sure all files are there
57 - if (! file_exists( $this->tmp_ticks ) ) {
58 - $cp_error = 1;
61 + if (! file_exists( $this->tmp_calls ) ) {
62 + $cp_error = 'calls';
59 63 } elseif (! file_exists( $this->tmp_iostats ) ) {
60 - $cp_error = 2;
64 + $cp_error = 'iostats';
61 65 }
62 66 if (! empty( $cp_error ) ) {
63 67 $error = sprintf(
64 - esc_html__('Cannot create the report: the profiler did not generate a data file (#%s). Make sure the following directory is writable: %s. If you are using a caching plugin or an opcode cache, try to disable it. You may also find more details about the error in the Log page.', 'code-profiler'),
65 - $cp_error,
66 - CODE_PROFILER_UPLOAD_DIR .'/'
67 - );
68 + esc_html__('Cannot create the report: the profiler did not generate a data file (%s). '.
69 + 'You may find more details about this error in the "Log" tab or your PHP error log.',
70 + 'code-profiler'),
71 + $cp_error
72 + );
68 73 $this->return_error( $error );
69 74 }
70 75
71 76 // Total data analyzed
72 77 $this->parsed_data += filesize( $this->tmp_iostats );
73 - $this->parsed_data += filesize( $this->tmp_ticks );
78 + $this->parsed_data += filesize( $this->tmp_calls );
74 79
75 80 $this->profile_name = $profile_name;
76 81 $this->plugins_dir = preg_quote( realpath( WP_PLUGIN_DIR ) );
77 - $this->themes_dir = preg_quote( realpath( WP_CONTENT_DIR .'/themes' ) );
82 + $this->themes_dir = preg_quote( realpath( WP_CONTENT_DIR .'/themes') );
78 83 $this->mu_dir = preg_quote( realpath( WPMU_PLUGIN_DIR ) );
79 84 }
80 85
81 86
@@ -83,20 +88,23 @@
83 88 * Filter and save the profiler's data
84 89 */
85 90 public function prepare_report() {
86 91
87 - $this->parse_ticks();
92 + $this->parse_calls();
88 93 $this->get_plugins_theme_name();
89 94 $this->save_iostats();
95 + $this->save_connections();
90 96 $this->save_data();
91 97 $this->save_diskio();
92 98 $this->save_composer();
93 99
94 100 code_profiler_log_info( sprintf(
95 - __('Volume of PHP code analyzed and processed: %s MB (%s plugins and 1 theme) - Memory used: %s MB', 'code-profiler'),
101 + __('Volume of code and data analyzed: %1$sMB (%2$s plugins and '.
102 + '1 theme) in %4$ss - Memory used: %3$sMB', 'code-profiler'),
96 103 number_format( $this->parsed_data / 1024 / 1024, 2 ),
97 - $this->total_plugins,
98 - number_format( memory_get_peak_usage( false ) / 1024 / 1024, 2 )
104 + (int) $this->total_plugins,
105 + number_format( memory_get_peak_usage( false ) / 1024 / 1024, 2 ),
106 + number_format( microtime( true ) - $this->microtime, 2 )
99 107 ));
100 108
101 109 return $this->microtime;
102 110 }
@@ -133,9 +141,16 @@
133 141 foreach( $this->plugins as $content => $content_array ) {
134 142 $this->total_plugins++;
135 143 // Slugs
136 144 $this->summary_list['time'] += $content_array['time'];
137 - $time = $this->convert_2_seconds( $content_array['time'] );
145 + if ( isset( $this->cx_buffer['plugin'][ $content ] ) ) {
146 + $time = $this->convert_2_seconds(
147 + $content_array['time'] + $this->cx_buffer['plugin'][ $content ]
148 + );
149 + $this->summary_list['time'] += $this->cx_buffer['plugin'][ $content ];
150 + } else {
151 + $time = $this->convert_2_seconds( $content_array['time'] );
152 + }
138 153 if ( empty( $content_array['name'] ) ) {
139 154 // E.g., a plugin without a folder
140 155 $content_array['name'] = $content;
141 156 }
@@ -148,15 +163,22 @@
148 163 }
149 164 foreach( $this->themes as $content => $content_array ) {
150 165 // Slugs
151 166 $this->summary_list['time'] += $content_array['time'];
152 - $time = $this->convert_2_seconds( $content_array['time'] );
167 + if ( isset( $this->cx_buffer['theme'][ $content ] ) ) {
168 + $time = $this->convert_2_seconds(
169 + $content_array['time'] + $this->cx_buffer['theme'][ $content ]
170 + );
171 + $this->summary_list['time'] += $this->cx_buffer['theme'][ $content ];
172 + } else {
173 + $time = $this->convert_2_seconds( $content_array['time'] );
174 + }
153 175 $slugs_buffer .= "$content\t$time\t{$content_array['name']}\ttheme\n";
154 176 }
155 177
156 178 $error = '';
157 179 if ( empty( $slugs_buffer ) ) {
158 - $error = esc_html__( 'Data is empty: no plugins or themes found', 'code-profiler' );
180 + $error = esc_html__('Data is empty: no plugins or themes found', 'code-profiler');
159 181 }
160 182 if ( $error ) {
161 183 $this->return_error( $error );
162 184 }
@@ -167,25 +189,37 @@
167 189 // Summary
168 190 $s = json_decode( file_get_contents( $this->tmp_summary ), true );
169 191 unlink( $this->tmp_summary );
170 192 if ( empty( $s['memory'] ) ) {
171 - $this->summary_list['memory'] = '-';
193 + $this->summary_list['memory'] = 0;
172 194 } else {
173 195 $this->summary_list['memory'] = $s['memory'] / 1024 / 1024;
174 196 }
175 197 if ( empty( $s['queries'] ) ) {
176 - $this->summary_list['queries']= '-';
198 + $this->summary_list['queries']= 0;
177 199 } else {
178 200 $this->summary_list['queries']= $s['queries'];
179 201 }
180 202 if ( empty( $this->total_io ) ) {
181 - $this->summary_list['io'] = '-';
203 + $this->summary_list['io'] = 0;
182 204 } else {
183 205 $this->summary_list['io'] = $this->total_io;
184 206 }
207 + if (! empty( $s['precision'] ) ) {
208 + $this->summary_list['precision'] = $s['precision'];
209 + }
185 210 $this->summary_list['items'] = $this->total_plugins + 1; // Add the theme
186 211 $this->summary_list['time'] = $this->convert_2_seconds( $this->summary_list['time'] );
187 212 $this->summary_list['time'] = number_format( $this->summary_list['time'], 4 );
213 +
214 + // Save Code Profiler, PHP and WordPress' versions
215 + global $wp_version;
216 + $this->summary_list['versions'] = [
217 + 'wp' => $wp_version,
218 + 'cp' => CODE_PROFILER_VERSION,
219 + 'php' => PHP_VERSION
220 + ];
221 +
188 222 file_put_contents( $summary_json, json_encode( $this->summary_list ) );
189 223
190 224 }
191 225
@@ -194,9 +228,11 @@
194 228 * Convert microtime (PHP<7.3) or hrtime (PHP>=7.3) to seconds
195 229 */
196 230 private function convert_2_seconds( $time ) {
197 231
198 - if ( empty( $time ) ) { return '0'; }
232 + if ( $time < 0 || empty( $time ) ) {
233 + return '0';
234 + }
199 235 if ( strpos( $time, '.' ) !== false ) {
200 236 // Looks like microtime
201 237 $time = number_format( $time, 6 );
202 238 } else {
@@ -211,9 +247,13 @@
211 247 * Check if a slug is from a plugin or theme.
212 248 */
213 249 private function plugin_or_theme( $script ) {
214 250
215 - $res = [ 'theme' => '', 'plugin' => '', 'script' => '' ];
251 + $res = [
252 + 'theme' => '',
253 + 'plugin' => '',
254 + 'script' => ''
255 + ];
216 256
217 257 if ( preg_match("`^{$this->plugins_dir}[\\\/](?:(.+?)[\\\/]|([^\\\/]+\.php)$)`", $script, $slug ) ) {
218 258 if (! empty( $slug[1] ) ) {
219 259 $res['plugin'] = $slug[1];
@@ -275,15 +315,15 @@
275 315
276 316 /**
277 317 * Parse, filter and sort the data
278 318 */
279 - private function parse_ticks() {
319 + private function parse_calls() {
280 320
281 - $fh = fopen( $this->tmp_ticks, 'rb' );
321 + $fh = fopen( $this->tmp_calls, 'rb');
282 322 if ( $fh === false ) {
283 323 $error = sprintf(
284 324 esc_html__('Cannot open file for reading: %s', 'code-profiler'),
285 - $this->tmp_ticks
325 + $this->tmp_calls
286 326 );
287 327 $this->return_error( $error );
288 328 }
289 329 while(! feof( $fh ) ) {
@@ -386,12 +426,12 @@
386 426 }
387 427 }
388 428
389 429 fclose( $fh );
390 - unlink( $this->tmp_ticks );
430 + unlink( $this->tmp_calls );
391 431 }
392 432
393 - /**
433 + /********************************************************************
394 434 * Save IO stats
395 435 */
396 436 private function save_iostats() {
397 437
@@ -422,9 +462,9 @@
422 462
423 463 /**
424 464 * Save Read/Write stats
425 465 */
426 - function save_diskio() {
466 + private function save_diskio() {
427 467
428 468 rename(
429 469 $this->tmp_diskio,
430 470 CODE_PROFILER_UPLOAD_DIR ."/{$this->microtime}.{$this->profile_name}.diskio.profile"
@@ -434,9 +474,9 @@
434 474
435 475 /**
436 476 * Save list of plugins using composer
437 477 */
438 - function save_composer() {
478 + private function save_composer() {
439 479
440 480 if (! empty( $this->composer ) ) {
441 481 // Try to get the plugin's name
442 482 foreach( $this->composer as $slug => $v ) {
@@ -447,8 +487,68 @@
447 487 file_put_contents(
448 488 CODE_PROFILER_UPLOAD_DIR ."/{$this->microtime}.{$this->profile_name}.composer.profile",
449 489 json_encode( $this->composer )
450 490 );
491 + }
492 + }
493 +
494 + /**
495 + * External connections.
496 + */
497 + private function save_connections() {
498 +
499 + if (! file_exists( $this->tmp_connections ) ) {
500 + return;
501 + }
502 +
503 + $connections = json_decode( file_get_contents( $this->tmp_connections ), true );
504 + unlink( $this->tmp_connections );
505 +
506 + if ( empty( $connections ) ) {
507 + return;
508 + }
509 +
510 + foreach( $connections as $connection => $array ) {
511 + $found_call = 0;
512 + $found_caller = 0;
513 +
514 + foreach( $array['bt'] as $k =>$v ) {
515 + if ( isset( $v['file'] ) && isset( $v['function'] ) && isset( $v['line'] ) ) {
516 +
517 + if (! $found_caller && ( $found_call ||
518 + in_array( $v['function'], [
519 + 'wp_remote_get',
520 + 'wp_safe_remote_get',
521 + 'wp_remote_post',
522 + 'wp_safe_remote_post'
523 + ] )
524 + ) ) {
525 +
526 + $found_call = 1;
527 +
528 + $type = $this->plugin_or_theme( $v['file'] );
529 +
530 + if (! empty( $type['theme'] ) ) {
531 + // Save it for later use
532 + if ( isset( $this->cx_buffer['theme'][ $type['theme'] ] ) ) {
533 + $this->cx_buffer['theme'][ $type['theme'] ] += $array['stop'] - $connection;
534 + } else {
535 + $this->cx_buffer['theme'][ $type['theme'] ] = $array['stop'] - $connection;
536 + }
537 + $found_caller = 1;
538 +
539 + } elseif (! empty( $type['plugin'] ) ) {
540 + // Save it for later use
541 + if ( isset( $this->cx_buffer['plugin'][ $type['plugin'] ] ) ) {
542 + $this->cx_buffer['plugin'][ $type['plugin'] ] += $array['stop'] - $connection;
543 + } else {
544 + $this->cx_buffer['plugin'][ $type['plugin'] ] = $array['stop'] - $connection;
545 + }
546 + $found_caller = 1;
547 + }
548 + }
549 + }
550 + }
451 551 }
452 552 }
453 553
454 554 }