PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.18.2
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.18.2
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 11 months ago Integration 11 months ago Interfaces 1 year ago Util 1 year ago WordPress 11 months ago CLI.php 1 year ago ModuleHandler.php 11 months ago PluginStateHandler.php 11 months ago
CLI.php
672 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"] : NULL;
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 } else {
144 nitropack_purge_cache();
145 }
146 }
147
148 /**
149 * Invalidate a website's cache
150 * Example: wp nitropack invalidate
151 */
152
153 public function nitropack_cli_invalidate( $args, $assocArgs ) {
154 $url = ! empty( $assocArgs["purge-url"] ) ? $assocArgs["purge-url"] : NULL;
155 $tag = ! empty( $assocArgs["purge-tag"] ) ? $assocArgs["purge-tag"] : NULL;
156 $reason = ! empty( $assocArgs["purge-reason"] ) ? $assocArgs["purge-reason"] : NULL;
157 if ( $url || $tag || $reason ) {
158 try {
159 if ( nitropack_sdk_invalidate( $url, $tag, $reason ) ) {
160 $this->logger->notice( 'Cache has been invalidated' );
161 WP_CLI::success( 'Cache has been invalidated.' );
162 }
163 } catch (\Exception $e) {
164 $this->logger->error( 'Cannot invalidate cache. Error: ' . $e );
165 WP_CLI::error( sprintf( 'Error, cannot invalidate cache. %s', $e ) );
166 }
167 } else {
168 nitropack_invalidate_cache();
169 }
170 }
171 /**
172 * Start of PSB commands from WPEngine below.
173 *
174 * Set NitroPack mode.
175 *
176 * ## OPTIONS
177 *
178 * [<mode>]
179 * : Read or change mode.
180 *
181 * options:
182 * - 0 - off - readable only
183 * - 1 - standard
184 * - 2 - medium
185 * - 3 - strong
186 * - 4 - ludicrous
187 * - 5 - custom - readable only
188 *
189 * Example: wp nitropack mode 3
190 * @when before_wp_load
191 *
192 * @param array $args Command arguments.
193 * @param array $assoc_args Command parameters.
194 */
195 private function nitropack_modes( $mode ) {
196 $modes = [ 0 => 'Off', 1 => 'Standard', 2 => 'Medium', 3 => 'Strong', 4 => 'Ludicrous', 5 => 'Custom' ];
197 if ( $mode !== null ) {
198 return $modes[ $mode ];
199 }
200 return $modes;
201 }
202 public function nitropack_mode( $args ) {
203 $mode = isset( $args[0] ) ? intval( $args[0] ) : null;
204 if ( $mode !== null && ( 1 > $mode || 4 < $mode ) ) {
205 $this->logger->error( 'The mode is invalid! Valid modes are from 1-4' );
206 WP_CLI::error( 'The mode is invalid! Valid modes are from 1-4.' );
207 return;
208 }
209 $change = $mode > 1 && $mode < 4;
210 $site_config = $this->get_site_config();
211 $keys = $this->keys_instance();
212 $url = new \NitroPack\SDK\IntegrationUrl( $change ? 'quicksetup' : 'quicksetup_json', $site_config['siteId'], $site_config['siteSecret'] );
213 $headers = [
214 'X-Nitro-Public-Key' => base64_encode( $keys->publicKey ), // phpcs:ignore
215 ];
216
217 if ( $change ) {
218 $response = \wp_remote_post(
219 $url->getUrl(),
220 [
221 'headers' => $headers,
222 'body' => [
223 'setting' => $mode,
224 ],
225 ]
226 );
227 } else {
228 $response = \wp_remote_get( $url->getUrl(), [ 'headers' => $headers ] );
229 }
230
231 if ( is_wp_error( $response ) ) {
232 /**
233 * Response error.
234 *
235 * @var WP_Error $error
236 */
237 $error = $response;
238 $this->logger->error( 'Optimization mode failed. Error: ' . $error->get_error_message() );
239 WP_CLI::error( $error->get_error_message() );
240 return;
241 }
242
243 if ( 200 !== $response['response']['code'] ) {
244 $this->logger->error( 'Optimization mode failed. Response: ' . $response['response']['code'] );
245 WP_CLI::debug( sprintf( 'Response body: %s.', $response['body'] ) );
246 WP_CLI::error( sprintf( 'Request has failed with %d %s.', $response['response']['code'], $response['response']['message'] ) );
247 return;
248 }
249
250 if ( $change ) {
251 $this->logger->notice( 'Mode has been changed to ' . $this->nitropack_modes( $mode ) );
252 WP_CLI::success( 'Mode has been changed to ' . $this->nitropack_modes( $mode ) );
253 return;
254 }
255
256 $body = @json_decode( $response['body'], true ); // phpcs:ignore
257 if ( empty( $body['optimization_level'] ) ) {
258 $this->logger->error( 'Mode is missing in the response body' );
259 WP_CLI::error( 'Mode is missing in the response body!' );
260 return;
261 }
262
263 $this->logger->notice( 'Mode is: ' . $this->nitropack_modes( $body['optimization_level'] ) );
264 WP_CLI::success( sprintf( 'Mode is: %s.', $this->nitropack_modes( $body['optimization_level'] ) ) );
265 }
266 /**
267 * NitroPack test mode.
268 *
269 * ## OPTIONS
270 *
271 * [<command>]
272 * : Get test mode status or change it.
273 * ---
274 * default: status
275 * options:
276 * - status
277 * - disable
278 * - enable
279 * ---
280 *
281 * @when before_wp_load
282 *
283 * @param array $args Command arguments.
284 * @param array $assoc_args Command parameters.
285 */
286 public function nitropack_preview( $args, $assoc_args ) {
287 $this->get_site_config();
288
289 /**
290 * SDK.
291 *
292 * @var \NitroPack\SDK\NitroPack $sdk
293 */
294 $sdk = get_nitropack()->getSdk();
295 try {
296 $command = empty( $args[0] ) ? 'status' : $args[0];
297 if ( 'enable' === $command ) {
298 $sdk->enableSafeMode();
299 nitropack_cache_safemode_status( 1 );
300 } elseif ( 'disable' === $command ) {
301 $sdk->disableSafeMode();
302 nitropack_cache_safemode_status( 0 );
303 } else {
304 $api = $this->get_vendor_api();
305 $status = $api->isSafeModeEnabled();
306 $this->logger->notice( 'Test mode is ' . ( $status ? 'enabled' : 'disabled' ) );
307 WP_CLI::success( sprintf( 'Test mode is %s.', $status ? 'enabled' : 'disabled' ) );
308 nitropack_cache_safemode_status( $status ? 1 : 0 );
309 return;
310 }
311 } catch (\Exception $e) {
312 $this->logger->error( 'Fail to ' . $command . ' test mode. Error: ' . $e->getMessage() );
313 WP_CLI::error( $e->getMessage(), false );
314 WP_CLI::error( sprintf( 'Failed to %s test mode.', $command ) );
315 return;
316 }
317 $this->logger->notice( 'Test mode has been ' . $command . 'd' );
318 WP_CLI::success( sprintf( 'Test mode has been %sd.', $command ) );
319 }
320 /**
321 * NitroPack cache warmup.
322 *
323 * ## OPTIONS
324 *
325 * [<command>]
326 * : Get cache warmup status or change it.
327 * ---
328 * default: status
329 * options:
330 * - status
331 * - disable
332 * - enable
333 * ---
334 *
335 * @when before_wp_load
336 *
337 * @param array $args Command arguments.
338 * @param array $assoc_args Command parameters.
339 */
340 public function nitropack_warmup( $args, $assoc_args ) {
341 $this->get_site_config();
342
343 $command = empty( $args[0] ) ? 'status' : $args[0];
344
345 /* Starts a warmup process for a website */
346 if ( 'run' === $command ) {
347 nitropack_run_warmup();
348 return;
349 }
350 /* Enables AND runs a warmup process for a website */
351 if ( 'enable' === $command ) {
352 nitropack_enable_warmup();
353 return;
354 }
355 /* Disables the warmup process for a website */
356 if ( 'disable' === $command ) {
357 nitropack_disable_warmup();
358 return;
359 }
360
361 try {
362 $api = $this->get_vendor_api();
363 $stats = $api->getWarmupStats();
364 if ( isset( $stats['status'] ) ) {
365 $stats['status'] = $stats['status'] ? 'enabled' : 'disabled';
366 }
367 WP_CLI\Utils\format_items(
368 'yaml',
369 array(
370 array(
371 'warmup' => $stats,
372 ),
373 ),
374 array(
375 'warmup',
376 )
377 );
378 $this->logger->notice( 'Get warmup status: ' . $stats['status'] );
379 } catch (\Exception $e) {
380 $this->logger->error( 'Failed to fetch warmup status. Error: ' . $e->getMessage() );
381 WP_CLI::error( $e->getMessage(), false );
382 WP_CLI::error( 'Failed to fetch warmup stats.' );
383 return;
384 }
385 }
386 /**
387 * NitroPack optimized URLs.
388 *
389 * ## OPTIONS
390 *
391 * [<command>]
392 * : Get optimized URLs or pending optimization.
393 * ---
394 * default: optimized
395 * options:
396 * - optimized
397 * - pending
398 * ---
399 *
400 * @when before_wp_load
401 *
402 * @param array $args Command arguments.
403 * @param array $assoc_args Command parameters.
404 */
405 public function nitropack_urls( $args, $assoc_args ) {
406 $this->get_site_config();
407
408 $command = empty( $args[0] ) ? 'optimized' : $args[0];
409
410 try {
411 $api = $this->get_vendor_api();
412 $result = array();
413 if ( 'optimized' === $command ) {
414 $result = $api->getUrls();
415 $this->logger->notice( 'Get optimized URLs' );
416 } elseif ( 'pending' === $command ) {
417 $result = $api->getPendingUrls();
418 $this->logger->notice( 'Get pending for optimization URLs' );
419 }
420
421 WP_CLI\Utils\format_items(
422 'yaml',
423 array(
424 array(
425 'urls' => $result,
426 ),
427 ),
428 array(
429 'urls',
430 )
431 );
432 } catch (\Exception $e) {
433 $this->logger->error( 'Get Optimized URLs. Error - ' . $e->getMessage() );
434 WP_CLI::error( $e->getMessage(), false );
435 WP_CLI::error( 'Failed to fetch URLs.' );
436 return;
437 }
438 }
439 /**
440 * NitroPack excluded URLs.
441 *
442 * ## OPTIONS
443 *
444 * [<command>]
445 * : Control excluded URLs.
446 * ---
447 * default: get
448 * options:
449 * - enable
450 * - disable
451 * - get
452 * - add
453 * - remove
454 * ---
455 *
456 * [<url_pattern>]
457 * : URL pattern.
458 *
459 * ## EXAMPLES
460 *
461 * # Enable feature to exclude any URL
462 * $ wp psb excludedurls enable
463 *
464 * # Exclude a contact page
465 * $ wp psb excludedurls add *\/contact
466 *
467 * @when before_wp_load
468 *
469 * @param array $args Command arguments.
470 * @param array $assoc_args Command parameters.
471 */
472 public function nitropack_excluded_urls( $args, $assoc_args ) {
473 $site_config = $this->get_site_config();
474
475 $command = empty( $args[0] ) ? '' : $args[0];
476 $url_pattern = empty( $args[1] ) ? '' : $args[1];
477
478 if ( ( 'add' === $command || 'remove' === $command ) && ! $url_pattern ) {
479 $this->logger->error( 'Enter valid URL' );
480 WP_CLI::error( 'Enter valid URL.' );
481 return;
482 }
483
484 try {
485 $api = $this->get_vendor_api();
486 switch ( $command ) {
487 case 'enable':
488 $api->enableExcludedUrls();
489 break;
490 case 'disable':
491 $api->disableExcludedUrls();
492 break;
493 case 'get':
494 $fetcher = new \NitroPack\SDK\Api\RemoteConfigFetcher( $site_config['siteId'], $site_config['siteSecret'] );
495 $response = $fetcher->get();
496 $config = @json_decode( $response, true ); // phpcs:ignore
497 if ( ! array_key_exists( 'DisabledURLs', $config ) ) {
498 $this->logger->error( 'Disabled URLs are not present in the respone. Vendor API might changed' );
499 WP_CLI::error( 'Disabled URLs are not present in the respone. Vendor API might changed.' );
500 return;
501 }
502 WP_CLI\Utils\format_items(
503 'yaml',
504 array(
505 array(
506 'excludes' => $config['DisabledURLs'],
507 ),
508 ),
509 array(
510 'excludes',
511 )
512 );
513 $this->logger->notice( 'Get excluded URLs' );
514 return;
515 case 'add':
516 $api->addExcludedUrl( $url_pattern );
517 $this->logger->notice( 'Add excluded URLs' );
518 break;
519 case 'remove':
520 $api->removeExcludedUrl( $url_pattern );
521 $this->logger->notice( 'Remove excluded URLs' );
522 break;
523 default:
524 $this->logger->error( 'Excluded URLs - enter valid sub-command.' );
525 WP_CLI::error( 'Enter valid sub-command.' );
526 return;
527 }
528 } catch (\Exception $e) {
529 $this->logger->error( "Failed to '" . $command . "' excluded URLs. Error: " . $e->getMessage() );
530 WP_CLI::error( $e->getMessage(), false );
531 WP_CLI::error( sprintf( 'Failed in %s excluded URL(s).', $command ) );
532 return;
533 }
534 $this->logger->notice( "Succeeded to '" . $command . "' excluded URL(s)" );
535 WP_CLI::success( sprintf( 'Succeeded to %s excluded URL(s).', $command ) );
536 }
537 /**
538 * NitroPack - JS, CSS, image, font excludes.
539 *
540 * ## OPTIONS
541 *
542 * [<command>]
543 * : Control excluded asset.
544 * ---
545 * default: get
546 * options:
547 * - enable
548 * - disable
549 * - get
550 * - add
551 * - remove
552 * ---
553 *
554 * [<url_pattern>]
555 * : URL pattern.
556 *
557 * [--resource=<type>]
558 * : Limit exclusion to a specific resource type.
559 * ---
560 * default: any
561 * options:
562 * - any
563 * - css
564 * - js
565 * - font
566 * - image
567 * ---
568 *
569 * [--device=<type>]
570 * : Limit exclusion to a specific device.
571 * ---
572 * default: any
573 * options:
574 * - any
575 * - desktop
576 * - tablet
577 * - mobile
578 * ---
579 *
580 * ## EXAMPLES
581 *
582 * # Enable feature to exclude any resource
583 * $ wp psb excludes enable
584 *
585 * # Exclude a contact page
586 * $ wp psb excludes add *\/script.js --resource=js
587 *
588 * @when before_wp_load
589 *
590 * @param array $args Command arguments.
591 * @param array $assoc_args Command parameters.
592 */
593 public function nitropack_excludes( $args, $assoc_args ) {
594 $this->get_site_config();
595
596 if ( ! class_exists( '\NitroPack\SDK\ExcludeEntry' ) ) {
597 $this->logger->error( 'The dependent plugin is incompatible' );
598 WP_CLI::error( 'The dependent plugin is incompatible.' );
599 return;
600 }
601
602 $command = empty( $args[0] ) ? '' : $args[0];
603 $url_pattern = empty( $args[1] ) ? '' : $args[1];
604
605 if ( ( 'add' === $command || 'remove' === $command ) && ! $url_pattern ) {
606 $this->logger->error( 'Enter valid URL' );
607 WP_CLI::error( 'Enter valid URL.' );
608 return;
609 }
610
611 try {
612 $api = $this->get_vendor_api();
613 switch ( $command ) {
614 case 'enable':
615 $api->enableExcludes();
616 break;
617 case 'disable':
618 $api->disableExcludes();
619 break;
620 case 'get':
621 $all_excludes = $api->getExcludes();
622 WP_CLI\Utils\format_items(
623 'yaml',
624 array(
625 array(
626 'excludes' => $all_excludes,
627 ),
628 ),
629 array(
630 'excludes',
631 )
632 );
633 $this->logger->notice( 'Get excludes' );
634 return;
635 case 'add':
636 $all_excludes = $api->getExcludes();
637
638 $new_exclude = new \NitroPack\SDK\ExcludeEntry();
639 $new_exclude->string = $url_pattern;
640 $new_exclude->device = empty( $assoc_args['device'] ) || 'any' === $assoc_args['device'] ? null : $assoc_args['device'];
641 $new_exclude->resourceType = empty( $assoc_args['resource'] ) || 'any' === $assoc_args['resource'] ? null : $assoc_args['resource']; // phpcs:ignore
642 $new_exclude->operation->all = true;
643
644 $all_excludes[] = $new_exclude;
645 $api->setExcludes( $all_excludes );
646 break;
647 case 'remove':
648 $all_excludes = $api->getExcludes();
649 $all_excludes = array_filter(
650 $all_excludes,
651 function ($exclusion) use ($url_pattern) {
652 return $exclusion->string !== $url_pattern;
653 }
654 );
655 $api->setExcludes( $all_excludes );
656 break;
657 default:
658 $this->logger->error( 'Enter valid sub-command' );
659 WP_CLI::error( 'Enter valid sub-command.' );
660 return;
661 }
662 } catch (\Exception $e) {
663 $this->logger->error( "Failed to '" . $command . "' exclude. Error: " . $e->getMessage() );
664 WP_CLI::error( $e->getMessage(), false );
665 WP_CLI::error( sprintf( 'Failed to %s exclude(s).', $command ) );
666 return;
667 }
668 $this->logger->notice( "Succeeded to '" . $command . "' exclude(s)" );
669 WP_CLI::success( sprintf( 'Succeeded to %s exclude(s).', $command ) );
670 }
671 }
672