PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.18.5
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.18.5
1.19.8 1.19.7 1.19.6 1.19.5 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.12.0 1.13.0 1.14.0 1.15.0 1.15.1 1.15.2 1.15.3 1.16.0 1.16.1 1.16.2 1.16.3 1.16.4 1.16.5 1.16.6 1.16.7 1.16.8 1.17.0 1.17.6 1.17.7 1.17.8 1.17.9 1.18.0 1.18.1 1.18.2 1.18.3 1.18.4 1.18.5 1.18.6 1.18.7 1.18.8 1.18.9 1.19.0 1.19.1 1.19.2 1.19.3 1.19.4 1.3.19 1.3.20 1.4.0 1.4.1 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.16 1.5.17 1.5.18 1.5.19 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.8.1 1.8.3 1.9.0 1.9.1 1.9.2
nitropack / classes / CLI.php
nitropack / classes Last commit date
Feature 10 months ago Integration 11 months ago Interfaces 1 year ago Util 1 year ago WordPress 10 months ago CLI.php 10 months ago ModuleHandler.php 11 months ago PluginStateHandler.php 11 months ago
CLI.php
665 lines
1 <?php
2
3 namespace NitroPack;
4
5 use \WP_CLI;
6
7 defined( 'ABSPATH' ) || die( 'No script kiddies please!' );
8
9 class CLI {
10 /**
11 * Pair of public and private keys
12 *
13 * @var null|object
14 */
15 protected $keys;
16 private $logger;
17 public function init() {
18 add_action( 'init', [ $this, 'register_hooks' ] );
19 $this->logger = WordPress\NitroPack::getInstance()->getLogger();
20 }
21 public function register_hooks() {
22
23 $is_wp_cli = nitropack_is_wp_cli();
24
25 if ( ! is_admin() && ! $is_wp_cli && ! is_user_logged_in() )
26 return;
27
28 WP_CLI::add_command( "nitropack connect", [ $this, "nitropack_cli_connect" ] );
29 WP_CLI::add_command( "nitropack disconnect", [ $this, "nitropack_cli_disconnect" ] );
30 WP_CLI::add_command( "nitropack purge", [ $this, "nitropack_cli_purge" ] );
31 WP_CLI::add_command( "nitropack invalidate", [ $this, "nitropack_cli_invalidate" ] );
32 WP_CLI::add_command( "nitropack mode", [ $this, "nitropack_mode" ] );
33 WP_CLI::add_command( 'nitropack preview', [ $this, 'nitropack_preview' ] );
34 WP_CLI::add_command( "nitropack warmup", [ $this, "nitropack_warmup" ] );
35 WP_CLI::add_command( 'nitropack urls', [ $this, 'nitropack_urls' ] );
36 WP_CLI::add_command( 'nitropack excludedurls', [ $this, 'nitropack_excluded_urls' ] );
37 WP_CLI::add_command( 'nitropack excludes', [ $this, 'nitropack_excludes' ] );
38 }
39 /**
40 * Get site configuration.
41 *
42 * @return null|array Returns site configuration on success or exits with an error.
43 */
44 protected function get_site_config() {
45 $site_config = nitropack_get_site_config();
46 if ( empty( $site_config['siteId'] ) || empty( $site_config['siteSecret'] ) ) {
47 $this->logger->error( 'Cannot connect. The Site ID or Site Secret is missing' );
48 WP_CLI::error( 'Cannot connect. The Site ID or Site Secret is missing!' );
49 return;
50 }
51 return $site_config;
52 }
53 /**
54 * Generate and get public and private keys.
55 *
56 * @return object
57 */
58 protected function keys_instance() {
59 // This must be executed only once per request.
60 if ( empty( $this->keys ) ) {
61 $this->keys = \NitroPack\SDK\Crypto::generateKeyPair();
62 }
63
64 return $this->keys;
65 }
66 /**
67 * Get vendor API.
68 *
69 * @return \NitroPack\SDK\Api API.
70 */
71 protected function get_vendor_api() {
72 $nitro = get_nitropack_sdk();
73 return $nitro->getApi();
74 }
75 /**
76 * Connects a website to NitroPack
77 *
78 * ## OPTIONS
79 *
80 * <siteID>
81 * : The API Key obtained from https://nitropack.io/user/connect
82 *
83 * <siteSecret>
84 * : The API Secret Key obtained from https://nitropack.io/user/connect
85 *
86 * Example: wp nitropack connect siteID siteSecret
87 */
88
89 public function nitropack_cli_connect( $args, $assocArgs ) {
90 $siteId = ! empty( $args[0] ) ? $args[0] : "";
91 $siteSecret = ! empty( $args[1] ) ? $args[1] : "";
92 nitropack_verify_connect( $siteId, $siteSecret );
93 }
94
95 /**
96 * Disconnects a website from NitroPack
97 * Example: wp nitropack disconnect
98 */
99
100 public function nitropack_cli_disconnect( $args, $assocArgs ) {
101 nitropack_disconnect();
102 }
103
104 /**
105 * Purges a website's cache
106 * Example: wp nitropack purge
107 */
108
109 public function nitropack_cli_purge( $args, $assocArgs ) {
110 $host = ! empty( $assocArgs["purge-host"] ) ? $assocArgs["purge-host"] : NULL;
111 $url = ! empty( $assocArgs["purge-url"] ) ? $assocArgs["purge-url"] : NULL;
112 $tag = ! empty( $assocArgs["purge-tag"] ) ? $assocArgs["purge-tag"] : NULL;
113 $reason = ! empty( $assocArgs["purge-reason"] ) ? $assocArgs["purge-reason"] . ' via WP-CLI' : 'Light purge of all caches via WP-CLI';
114
115 if ( ! empty( $host ) ) {
116 /**
117 * Override the site url by the purge-host parameter
118 *
119 * @param string $host
120 * @return string
121 */
122 add_filter(
123 'nitropack_current_host',
124 function () use ($host) {
125 if ( ! preg_match( '#^http(s)?://#', $host ) ) {
126 $host = 'https://' . $host;
127 }
128 return $host;
129 }
130 );
131 }
132
133 if ( $url || $tag || $reason ) {
134 try {
135 if ( nitropack_sdk_purge( $url, $tag, $reason ) ) {
136 $this->logger->notice( 'Cache has been purged' );
137 WP_CLI::success( 'Cache has been purged.' );
138 }
139 } catch (\Exception $e) {
140 $this->logger->error( 'Cannot purge cache. Error: ' . $e );
141 WP_CLI::error( sprintf( 'Cannot purge cache. Error: %s', $e ) );
142 }
143 }
144 }
145
146 /**
147 * Invalidate a website's cache
148 * Example: wp nitropack invalidate
149 */
150
151 public function nitropack_cli_invalidate( $args, $assocArgs ) {
152 $url = ! empty( $assocArgs["purge-url"] ) ? $assocArgs["purge-url"] : NULL;
153 $tag = ! empty( $assocArgs["purge-tag"] ) ? $assocArgs["purge-tag"] : NULL;
154 $reason = ! empty( $assocArgs["purge-reason"] ) ? $assocArgs["purge-reason"] . ' via WP-CLI' : 'Manual invalidation of all pages via WP-CLI';
155 if ( $url || $tag || $reason ) {
156 try {
157 if ( nitropack_sdk_invalidate( $url, $tag, $reason ) ) {
158 $this->logger->notice( 'Cache has been invalidated' );
159 WP_CLI::success( 'Cache has been invalidated.' );
160 }
161 } catch (\Exception $e) {
162 $this->logger->error( 'Cannot invalidate cache. Error: ' . $e );
163 WP_CLI::error( sprintf( 'Error, cannot invalidate cache. %s', $e ) );
164 }
165 }
166 }
167 /**
168 * Start of PSB commands from WPEngine below.
169 *
170 * Set NitroPack mode.
171 *
172 * ## OPTIONS
173 *
174 * [<mode>]
175 * : Read or change mode.
176 *
177 * options:
178 * - 0 - off - readable only
179 * - 1 - standard
180 * - 2 - medium
181 * - 3 - strong
182 * - 4 - ludicrous
183 * - 5 - custom - readable only
184 *
185 * Example: wp nitropack mode 3
186 * @when before_wp_load
187 *
188 * @param array $args Command arguments.
189 * @param array $assoc_args Command parameters.
190 */
191 private function nitropack_modes( $mode ) {
192 $modes = [ 0 => 'Off', 1 => 'Standard', 2 => 'Medium', 3 => 'Strong', 4 => 'Ludicrous', 5 => 'Custom' ];
193 if ( $mode !== null ) {
194 return $modes[ $mode ];
195 }
196 return $modes;
197 }
198 public function nitropack_mode( $args ) {
199 $mode = isset( $args[0] ) ? intval( $args[0] ) : null;
200 if ( $mode !== null && ( 1 > $mode || 4 < $mode ) ) {
201 $this->logger->error( 'The mode is invalid! Valid modes are from 1-4' );
202 WP_CLI::error( 'The mode is invalid! Valid modes are from 1-4.' );
203 return;
204 }
205 $change = $mode > 1 && $mode < 4;
206 $site_config = $this->get_site_config();
207 $keys = $this->keys_instance();
208 $url = new \NitroPack\SDK\IntegrationUrl( $change ? 'quicksetup' : 'quicksetup_json', $site_config['siteId'], $site_config['siteSecret'] );
209 $headers = [
210 'X-Nitro-Public-Key' => base64_encode( $keys->publicKey ), // phpcs:ignore
211 ];
212
213 if ( $change ) {
214 $response = \wp_remote_post(
215 $url->getUrl(),
216 [
217 'headers' => $headers,
218 'body' => [
219 'setting' => $mode,
220 ],
221 ]
222 );
223 } else {
224 $response = \wp_remote_get( $url->getUrl(), [ 'headers' => $headers ] );
225 }
226
227 if ( is_wp_error( $response ) ) {
228 /**
229 * Response error.
230 *
231 * @var WP_Error $error
232 */
233 $error = $response;
234 $this->logger->error( 'Optimization mode failed. Error: ' . $error->get_error_message() );
235 WP_CLI::error( $error->get_error_message() );
236 return;
237 }
238
239 if ( 200 !== $response['response']['code'] ) {
240 $this->logger->error( 'Optimization mode failed. Response: ' . $response['response']['code'] );
241 WP_CLI::debug( sprintf( 'Response body: %s.', $response['body'] ) );
242 WP_CLI::error( sprintf( 'Request has failed with %d %s.', $response['response']['code'], $response['response']['message'] ) );
243 return;
244 }
245
246 if ( $change ) {
247 $this->logger->notice( 'Mode has been changed to ' . $this->nitropack_modes( $mode ) );
248 WP_CLI::success( 'Mode has been changed to ' . $this->nitropack_modes( $mode ) );
249 return;
250 }
251
252 $body = @json_decode( $response['body'], true ); // phpcs:ignore
253 if ( empty( $body['optimization_level'] ) ) {
254 $this->logger->error( 'Mode is missing in the response body' );
255 WP_CLI::error( 'Mode is missing in the response body!' );
256 return;
257 }
258
259 $this->logger->notice( 'Mode is: ' . $this->nitropack_modes( $body['optimization_level'] ) );
260 WP_CLI::success( sprintf( 'Mode is: %s.', $this->nitropack_modes( $body['optimization_level'] ) ) );
261 }
262 /**
263 * NitroPack test mode.
264 *
265 * ## OPTIONS
266 *
267 * [<command>]
268 * : Get test mode status or change it.
269 * ---
270 * default: status
271 * options:
272 * - status
273 * - disable
274 * - enable
275 * ---
276 *
277 * @when before_wp_load
278 *
279 * @param array $args Command arguments.
280 * @param array $assoc_args Command parameters.
281 */
282 public function nitropack_preview( $args, $assoc_args ) {
283 $this->get_site_config();
284
285 /**
286 * SDK.
287 *
288 * @var \NitroPack\SDK\NitroPack $sdk
289 */
290 $sdk = get_nitropack()->getSdk();
291 try {
292 $command = empty( $args[0] ) ? 'status' : $args[0];
293 if ( 'enable' === $command ) {
294 $sdk->enableSafeMode();
295 } elseif ( 'disable' === $command ) {
296 $sdk->disableSafeMode();
297 } else {
298 $api = $this->get_vendor_api();
299 $status = $api->isSafeModeEnabled();
300 $this->logger->notice( 'Test mode is ' . ( $status ? 'enabled' : 'disabled' ) );
301 WP_CLI::success( sprintf( 'Test mode is %s.', $status ? 'enabled' : 'disabled' ) );
302 return;
303 }
304 } catch (\Exception $e) {
305 $this->logger->error( 'Fail to ' . $command . ' test mode. Error: ' . $e->getMessage() );
306 WP_CLI::error( $e->getMessage(), false );
307 WP_CLI::error( sprintf( 'Failed to %s test mode.', $command ) );
308 return;
309 }
310 $this->logger->notice( 'Test mode has been ' . $command . 'd' );
311 WP_CLI::success( sprintf( 'Test mode has been %sd.', $command ) );
312 }
313 /**
314 * NitroPack cache warmup.
315 *
316 * ## OPTIONS
317 *
318 * [<command>]
319 * : Get cache warmup status or change it.
320 * ---
321 * default: status
322 * options:
323 * - status
324 * - disable
325 * - enable
326 * ---
327 *
328 * @when before_wp_load
329 *
330 * @param array $args Command arguments.
331 * @param array $assoc_args Command parameters.
332 */
333 public function nitropack_warmup( $args, $assoc_args ) {
334 $this->get_site_config();
335
336 $command = empty( $args[0] ) ? 'status' : $args[0];
337
338 /* Starts a warmup process for a website */
339 if ( 'run' === $command ) {
340 nitropack_run_warmup();
341 return;
342 }
343 /* Enables AND runs a warmup process for a website */
344 if ( 'enable' === $command ) {
345 nitropack_enable_warmup();
346 return;
347 }
348 /* Disables the warmup process for a website */
349 if ( 'disable' === $command ) {
350 nitropack_disable_warmup();
351 return;
352 }
353
354 try {
355 $api = $this->get_vendor_api();
356 $stats = $api->getWarmupStats();
357 if ( isset( $stats['status'] ) ) {
358 $stats['status'] = $stats['status'] ? 'enabled' : 'disabled';
359 }
360 WP_CLI\Utils\format_items(
361 'yaml',
362 array(
363 array(
364 'warmup' => $stats,
365 ),
366 ),
367 array(
368 'warmup',
369 )
370 );
371 $this->logger->notice( 'Get warmup status: ' . $stats['status'] );
372 } catch (\Exception $e) {
373 $this->logger->error( 'Failed to fetch warmup status. Error: ' . $e->getMessage() );
374 WP_CLI::error( $e->getMessage(), false );
375 WP_CLI::error( 'Failed to fetch warmup stats.' );
376 return;
377 }
378 }
379 /**
380 * NitroPack optimized URLs.
381 *
382 * ## OPTIONS
383 *
384 * [<command>]
385 * : Get optimized URLs or pending optimization.
386 * ---
387 * default: optimized
388 * options:
389 * - optimized
390 * - pending
391 * ---
392 *
393 * @when before_wp_load
394 *
395 * @param array $args Command arguments.
396 * @param array $assoc_args Command parameters.
397 */
398 public function nitropack_urls( $args, $assoc_args ) {
399 $this->get_site_config();
400
401 $command = empty( $args[0] ) ? 'optimized' : $args[0];
402
403 try {
404 $api = $this->get_vendor_api();
405 $result = array();
406 if ( 'optimized' === $command ) {
407 $result = $api->getUrls();
408 $this->logger->notice( 'Get optimized URLs' );
409 } elseif ( 'pending' === $command ) {
410 $result = $api->getPendingUrls();
411 $this->logger->notice( 'Get pending for optimization URLs' );
412 }
413
414 WP_CLI\Utils\format_items(
415 'yaml',
416 array(
417 array(
418 'urls' => $result,
419 ),
420 ),
421 array(
422 'urls',
423 )
424 );
425 } catch (\Exception $e) {
426 $this->logger->error( 'Get Optimized URLs. Error - ' . $e->getMessage() );
427 WP_CLI::error( $e->getMessage(), false );
428 WP_CLI::error( 'Failed to fetch URLs.' );
429 return;
430 }
431 }
432 /**
433 * NitroPack excluded URLs.
434 *
435 * ## OPTIONS
436 *
437 * [<command>]
438 * : Control excluded URLs.
439 * ---
440 * default: get
441 * options:
442 * - enable
443 * - disable
444 * - get
445 * - add
446 * - remove
447 * ---
448 *
449 * [<url_pattern>]
450 * : URL pattern.
451 *
452 * ## EXAMPLES
453 *
454 * # Enable feature to exclude any URL
455 * $ wp psb excludedurls enable
456 *
457 * # Exclude a contact page
458 * $ wp psb excludedurls add *\/contact
459 *
460 * @when before_wp_load
461 *
462 * @param array $args Command arguments.
463 * @param array $assoc_args Command parameters.
464 */
465 public function nitropack_excluded_urls( $args, $assoc_args ) {
466 $site_config = $this->get_site_config();
467
468 $command = empty( $args[0] ) ? '' : $args[0];
469 $url_pattern = empty( $args[1] ) ? '' : $args[1];
470
471 if ( ( 'add' === $command || 'remove' === $command ) && ! $url_pattern ) {
472 $this->logger->error( 'Enter valid URL' );
473 WP_CLI::error( 'Enter valid URL.' );
474 return;
475 }
476
477 try {
478 $api = $this->get_vendor_api();
479 switch ( $command ) {
480 case 'enable':
481 $api->enableExcludedUrls();
482 break;
483 case 'disable':
484 $api->disableExcludedUrls();
485 break;
486 case 'get':
487 $fetcher = new \NitroPack\SDK\Api\RemoteConfigFetcher( $site_config['siteId'], $site_config['siteSecret'] );
488 $response = $fetcher->get();
489 $config = @json_decode( $response, true ); // phpcs:ignore
490 if ( ! array_key_exists( 'DisabledURLs', $config ) ) {
491 $this->logger->error( 'Disabled URLs are not present in the respone. Vendor API might changed' );
492 WP_CLI::error( 'Disabled URLs are not present in the respone. Vendor API might changed.' );
493 return;
494 }
495 WP_CLI\Utils\format_items(
496 'yaml',
497 array(
498 array(
499 'excludes' => $config['DisabledURLs'],
500 ),
501 ),
502 array(
503 'excludes',
504 )
505 );
506 $this->logger->notice( 'Get excluded URLs' );
507 return;
508 case 'add':
509 $api->addExcludedUrl( $url_pattern );
510 $this->logger->notice( 'Add excluded URLs' );
511 break;
512 case 'remove':
513 $api->removeExcludedUrl( $url_pattern );
514 $this->logger->notice( 'Remove excluded URLs' );
515 break;
516 default:
517 $this->logger->error( 'Excluded URLs - enter valid sub-command.' );
518 WP_CLI::error( 'Enter valid sub-command.' );
519 return;
520 }
521 } catch (\Exception $e) {
522 $this->logger->error( "Failed to '" . $command . "' excluded URLs. Error: " . $e->getMessage() );
523 WP_CLI::error( $e->getMessage(), false );
524 WP_CLI::error( sprintf( 'Failed in %s excluded URL(s).', $command ) );
525 return;
526 }
527 $this->logger->notice( "Succeeded to '" . $command . "' excluded URL(s)" );
528 WP_CLI::success( sprintf( 'Succeeded to %s excluded URL(s).', $command ) );
529 }
530 /**
531 * NitroPack - JS, CSS, image, font excludes.
532 *
533 * ## OPTIONS
534 *
535 * [<command>]
536 * : Control excluded asset.
537 * ---
538 * default: get
539 * options:
540 * - enable
541 * - disable
542 * - get
543 * - add
544 * - remove
545 * ---
546 *
547 * [<url_pattern>]
548 * : URL pattern.
549 *
550 * [--resource=<type>]
551 * : Limit exclusion to a specific resource type.
552 * ---
553 * default: any
554 * options:
555 * - any
556 * - css
557 * - js
558 * - font
559 * - image
560 * ---
561 *
562 * [--device=<type>]
563 * : Limit exclusion to a specific device.
564 * ---
565 * default: any
566 * options:
567 * - any
568 * - desktop
569 * - tablet
570 * - mobile
571 * ---
572 *
573 * ## EXAMPLES
574 *
575 * # Enable feature to exclude any resource
576 * $ wp psb excludes enable
577 *
578 * # Exclude a contact page
579 * $ wp psb excludes add *\/script.js --resource=js
580 *
581 * @when before_wp_load
582 *
583 * @param array $args Command arguments.
584 * @param array $assoc_args Command parameters.
585 */
586 public function nitropack_excludes( $args, $assoc_args ) {
587 $this->get_site_config();
588
589 if ( ! class_exists( '\NitroPack\SDK\ExcludeEntry' ) ) {
590 $this->logger->error( 'The dependent plugin is incompatible' );
591 WP_CLI::error( 'The dependent plugin is incompatible.' );
592 return;
593 }
594
595 $command = empty( $args[0] ) ? '' : $args[0];
596 $url_pattern = empty( $args[1] ) ? '' : $args[1];
597
598 if ( ( 'add' === $command || 'remove' === $command ) && ! $url_pattern ) {
599 $this->logger->error( 'Enter valid URL' );
600 WP_CLI::error( 'Enter valid URL.' );
601 return;
602 }
603
604 try {
605 $api = $this->get_vendor_api();
606 switch ( $command ) {
607 case 'enable':
608 $api->enableExcludes();
609 break;
610 case 'disable':
611 $api->disableExcludes();
612 break;
613 case 'get':
614 $all_excludes = $api->getExcludes();
615 WP_CLI\Utils\format_items(
616 'yaml',
617 array(
618 array(
619 'excludes' => $all_excludes,
620 ),
621 ),
622 array(
623 'excludes',
624 )
625 );
626 $this->logger->notice( 'Get excludes' );
627 return;
628 case 'add':
629 $all_excludes = $api->getExcludes();
630
631 $new_exclude = new \NitroPack\SDK\ExcludeEntry();
632 $new_exclude->string = $url_pattern;
633 $new_exclude->device = empty( $assoc_args['device'] ) || 'any' === $assoc_args['device'] ? null : $assoc_args['device'];
634 $new_exclude->resourceType = empty( $assoc_args['resource'] ) || 'any' === $assoc_args['resource'] ? null : $assoc_args['resource']; // phpcs:ignore
635 $new_exclude->operation->all = true;
636
637 $all_excludes[] = $new_exclude;
638 $api->setExcludes( $all_excludes );
639 break;
640 case 'remove':
641 $all_excludes = $api->getExcludes();
642 $all_excludes = array_filter(
643 $all_excludes,
644 function ($exclusion) use ($url_pattern) {
645 return $exclusion->string !== $url_pattern;
646 }
647 );
648 $api->setExcludes( $all_excludes );
649 break;
650 default:
651 $this->logger->error( 'Enter valid sub-command' );
652 WP_CLI::error( 'Enter valid sub-command.' );
653 return;
654 }
655 } catch (\Exception $e) {
656 $this->logger->error( "Failed to '" . $command . "' exclude. Error: " . $e->getMessage() );
657 WP_CLI::error( $e->getMessage(), false );
658 WP_CLI::error( sprintf( 'Failed to %s exclude(s).', $command ) );
659 return;
660 }
661 $this->logger->notice( "Succeeded to '" . $command . "' exclude(s)" );
662 WP_CLI::success( sprintf( 'Succeeded to %s exclude(s).', $command ) );
663 }
664 }
665