PluginProbe
Code Profiler – WordPress Performance Profiling and Debugging Made Easy / 1.8.2
Code Profiler – WordPress Performance Profiling and Debugging Made Easy v1.8.2
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 / class-cli.php

class-cli.php in Code Profiler – WordPress Performance Profiling and Debugging Made Easy 1.8.2, at lib/class-cli.php

355 lines 9.5 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://nintechnet.com/codeprofiler/ |
11 +=====================================================================+
12 */
13
14 if (! defined('ABSPATH') ) { die('Forbidden'); }
15
16 // =====================================================================
17
18 class CodeProfiler_CLI extends WP_CLI_Command {
19
20 private $file;
21 private $out = 'view';
22 private $cmd_view = 'wp code-profiler view';
23 private $cmd_run = 'wp code-profiler run';
24
25 /**
26 * Run the profiler.
27 *
28 */
29 public function run( $args, $assoc_args ) {
30
31 $this->is_enabled();
32
33 $_POST['cp_nonce'] = wp_create_nonce('start_profiler_nonce');
34
35 $_POST['post'] = home_url( '/' );
36 // Mark the connection as 'HTTPS' if needed.
37 if ( strtolower( parse_url( $_POST['post'], PHP_URL_SCHEME ) ) == 'https') {
38 $_SERVER['HTTPS'] = 'on';
39 }
40
41 $_POST['x_end'] = 'frontend';
42 // Detect if we're authenticated or not
43 if ( is_user_logged_in() === true ) {
44 $_POST['x_auth'] = 'authenticated';
45 } else {
46 $_POST['x_auth'] = 'unauthenticated';
47 }
48 $_POST['profile'] = 'WP-CLI_' . time();
49 $_POST['user_agent'] = 'Firefox';
50
51 if (! empty( $assoc_args['dest'] ) ) {
52 $message = sprintf(
53 __('The "%s" option is only available in the pro version of Code Profiler.', 'code-profiler'),
54 '--dest'
55 );
56 WP_CLI::error( $message );
57 exit;
58 }
59
60 if (! empty( $assoc_args['out'] ) && in_array( $assoc_args['out'], ['json', 'csv'] ) ) {
61 $this->out = $assoc_args['out'];
62 }
63
64 // HTTP basic authentication
65 if (! empty( $assoc_args['u'] ) ) {
66 _e('Enter your HTTP basic authentication password:', 'code-profiler');
67 echo ' ';
68 echo "\033[30;40m";
69 $password = trim( stream_get_line( STDIN, 255, PHP_EOL) );
70 echo "\033[0m";
71 $_POST['Authorization'] = 'Basic '. base64_encode( "{$assoc_args['u']}:{$password}" );
72 }
73
74 if ( $this->out == 'view') {
75 WP_CLI::log( sprintf(
76 __('Starting Code Profiler v%s (profile: %s)', 'code-profiler'),
77 CODE_PROFILER_VERSION, $_POST['profile'] ) . "\n"
78 );
79
80 $progress = \WP_CLI\Utils\make_progress_bar( '', 3 );
81 $progress->tick();
82 }
83
84 // Run the profiler
85 $response = json_decode( codeprofiler_start_profiler(), true );
86 if ( $response === false ) {
87 $message = __('Unknown error returned by AJAX', 'code-profiler');
88 WP_CLI::error( $message );
89 exit;
90 }
91 if ( $response['status'] == 'error' ) {
92 WP_CLI::error( $response['message'] );
93 exit;
94 }
95
96 if ( $this->out == 'view') {
97 $progress->tick();
98 }
99
100 // All good, run the parser
101 $_POST['microtime'] = $response['microtime'];
102 $response = json_decode( codeprofiler_prepare_report(), true );
103 if ( $response === false ) {
104 $message = __('Unknown error returned by AJAX', 'code-profiler');
105 WP_CLI::error( $message );
106 exit;
107 }
108 if ( $response['status'] == 'error' ) {
109 WP_CLI::error( $response['message'] );
110 exit;
111 }
112
113 if ( $this->out == 'view') {
114 $progress->tick();
115 $progress->finish();
116 }
117
118 // Run the parser and show the results
119 $this->view( $args, $assoc_args, $response['cp_profile'] );
120
121 exit;
122 }
123
124
125 /**
126 * Display last created profile.
127 *
128 */
129 public function view( $args = [], $assoc_args = [], $profile = '') {
130
131 $this->is_enabled();
132
133 if (! empty( $assoc_args['out'] ) && in_array( $assoc_args['out'], ['json', 'csv'] ) ) {
134 $this->out = $assoc_args['out'];
135 }
136
137 // The profile was just created (`wp code-profiler run`)...
138 if (! empty( $profile ) ) {
139 $this->file = code_profiler_get_profile_path( $profile );
140 if ( $this->file === false ) {
141 $message = __('Cannot find the profile file', 'code-profiler');
142 WP_CLI::error( $message );
143 exit;
144 }
145 $this->file .= '.slugs.profile';
146
147 // ... or we show last created one (`wp code-profiler view`)
148 } else {
149 $this->file = $this->find_last_profile();
150 if ( $this->file === false ) {
151 $message = sprintf(
152 __('No profile found. Run the profiler at least once to create a profile: %s', 'code-profiler'),
153 $this->cmd_run
154 );
155 WP_CLI::error( $message );
156 exit;
157 }
158 }
159
160 // Fetch and display the profile's name only
161 preg_match(
162 '`'. CODE_PROFILER_UPLOAD_DIR .'/\d{10}\.\d+\.(.+?)\.slugs\.profile$`',
163 $this->file,
164 $match
165 );
166
167 $cp_slug = __('Slug', 'code-profiler');
168 $cp_time = __('Execution time', 'code-profiler');
169 $cp_name = __('Name', 'code-profiler');
170 $cp_type = __('Type', 'code-profiler');
171
172 if ( $this->out == 'view' ) {
173 $buffer = '';
174 $message = sprintf( __('Viewing: %s', 'code-profiler'), $match[1] );
175 $date = date('Y/m/d \@ H:i:s', filemtime( $this->file ) );
176 echo WP_CLI::colorize("\n%Y$message ~ $date%n\n\n");
177 // Display stats
178 $summary_file = str_replace('.slugs.profile', '', $this->file );
179 echo code_profiler_getsummarystats( $summary_file, 'text');
180 /**
181 * CSV output.
182 */
183 } elseif ( $this->out == 'csv' ) {
184 $buffer = "$cp_slug,\"$cp_time\",$cp_name,$cp_type\n";
185 /**
186 * JSON-encoded output.
187 */
188 } else {
189 $buffer = [];
190 }
191
192 $slugs = $this->read_profile();
193
194 $cp_options = get_option('code-profiler');
195
196 // Get the total time and the slowest item
197 $total_time = 0;
198 foreach( $slugs as $k => $v ) {
199 $total_time += $v[1];
200 }
201
202 $coeff = number_format( $slugs[0][1] / $total_time * 100 );
203
204 foreach( $slugs as $k => $v ) {
205
206 if ( $this->out == 'csv') {
207 $buffer .= "{$v[0]},{$v[1]},{$v[2]},{$v[3]}\n";
208
209 } elseif( $this->out == 'json') {
210 $buffer[] = [
211 $cp_slug = $v[0],
212 $cp_time = $v[1],
213 $cp_name = $v[2],
214 $cp_type = $v[3]
215 ];
216
217 } else {
218 // Display name, time and %
219 if ( isset( $cp_options['display_name'] ) && $cp_options['display_name'] == 'slug' ) {
220 $name = $v[0];
221 } else {
222 $name = $v[2];
223 }
224 // Inform if it's the theme or a mu-plugin
225 if ( $v[3] == 'theme') {
226 $name .= ' (theme)';
227 } elseif ( $v[3] == 'mu-plugin') {
228 $name .= ' (mu-plugin)';
229 }
230
231 $time = number_format( $v[1], 3 );
232 $percent = number_format( $v[1] / $total_time * 100 );
233 $chars = number_format( $percent * 80 / $coeff );
234 // We use `echo` instead of `WP_CLI::log` because the layout could
235 // be all messed-up when some caching plugins such as LiteSpeed Cache
236 // are activated.
237 echo " $name | {$time}s | {$percent}%\n";
238 if (! $percent ) {
239 echo " \u{258C}\n\n";
240 } else {
241 $bar = '';
242 for ( $i = 0; $i < $chars; $i++ ) {
243 $bar .= ' ';
244 }
245 echo WP_CLI::colorize(" %8$bar%n\n\n");
246 }
247 }
248 }
249 if ( $this->out == 'csv') {
250 echo $buffer;
251
252 } elseif ( $this->out == 'json' ) {
253 echo json_encode( $buffer );
254 }
255
256 exit;
257 }
258
259
260 /**
261 * Retrieve the full path/name to the last created profile
262 *
263 */
264 private function find_last_profile() {
265
266 $profiles = code_profiler_glob( CODE_PROFILER_UPLOAD_DIR, '\.slugs\.profile$', true );
267
268 array_multisort(
269 array_map('filectime', $profiles ),
270 SORT_NUMERIC,
271 SORT_DESC,
272 $profiles
273 );
274
275 if (! empty( $profiles[0] ) ) {
276 return $profiles[0];
277 }
278
279 return false;
280 }
281
282
283 /**
284 * Parse and return the profile's data
285 *
286 */
287 private function read_profile() {
288
289 $profile = str_replace('.slugs.profile', '', $this->file );
290
291 $res = code_profiler_get_profile_data( $profile );
292 if ( isset( $res['error'] ) ) {
293 WP_CLI::error( $res['error'] );
294 exit;
295 }
296
297 // Sort data (slowest plugin/theme first)
298 usort( $res, function( $a, $b ) {
299 return $b[1] <=> $a[1];
300 } );
301
302 return $res;
303 }
304
305
306 /**
307 * Verify wether WP CLI integration is enabled or not
308 *
309 */
310 private function is_enabled() {
311
312 $cp_options = get_option('code-profiler');
313 if ( empty( $cp_options['enable_wpcli'] ) ) {
314 $message = __('WP-CLI integration is disabled. To enable it, log in to your admin dashboard and go to the Code Profiler settings page.', 'code-profiler');
315 WP_CLI::error( $message );
316 exit;
317 }
318 }
319
320
321 /**
322 * Display help screen and quit.
323 *
324 */
325 public function help() {
326
327 $this->is_enabled();
328
329 WP_CLI::log("\nCode Profiler v". CODE_PROFILER_VERSION .
330 " (c)". date('Y') ." Jerome Bruandet & NinTechNet Limited ~ https://nintechnet.com/codeprofiler/\n\n".
331 " {$this->cmd_view} ". __('View last created profile', 'code-profiler') ."\n".
332 " {$this->cmd_run} ". __('Run the profiler in the frontend', 'code-profiler') ."\n\n".
333 __('GLOBAL PARAMETERS', 'code-profiler') ."\n\n".
334 " --dest=<URL to profile> **". __('Pro version only', 'code-profiler') ."**\n".
335 " ". __('Path to the WordPress page or post to profile. If missing, profile the frontend.', 'code-profiler') ."\n\n".
336 " --user=<id|login|email> (optional)\n".
337 " ". __('Run the profiler as the corresponding WordPress user. If missing, run as an unauthenticated user.', 'code-profiler') ."\n\n".
338 " --u=<username> (optional)\n".
339 " ". __('HTTP Basic authentication username. You will be prompted to enter your password.', 'code-profiler') ."\n\n".
340 " --out=<json|csv> (optional)\n".
341 " ". __('Return the results in JSON-encoded or CSV format.', 'code-profiler') ."\n\n" );
342 exit;
343 }
344
345 }
346
347 WP_CLI::add_command(
348 'code-profiler',
349 'CodeProfiler_CLI',
350 ['shortdesc' => 'Profile your blog with Code Profiler.']
351 );
352
353 // =====================================================================
354 // EOF
355