PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.17.6
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.17.6
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 1 year ago Integration 1 year ago Interfaces 2 years ago Util 2 years ago WordPress 1 year ago CLI.php 1 year ago ModuleHandler.php 1 year ago PluginStateHandler.php 1 year ago
CLI.php
669 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 } elseif ( 'disable' === $command ) {
300 $sdk->disableSafeMode();
301 } else {
302 $api = $this->get_vendor_api();
303 $status = $api->isSafeModeEnabled();
304 $this->logger->notice( 'Test mode is ' . ( $status ? 'enabled' : 'disabled' ) );
305 WP_CLI::success( sprintf( 'Test mode is %s.', $status ? 'enabled' : 'disabled' ) );
306 return;
307 }
308 } catch (\Exception $e) {
309 $this->logger->error( 'Fail to ' . $command . ' test mode. Error: ' . $e->getMessage() );
310 WP_CLI::error( $e->getMessage(), false );
311 WP_CLI::error( sprintf( 'Failed to %s test mode.', $command ) );
312 return;
313 }
314 $this->logger->notice( 'Test mode has been ' . $command . 'd' );
315 WP_CLI::success( sprintf( 'Test mode has been %sd.', $command ) );
316 }
317 /**
318 * NitroPack cache warmup.
319 *
320 * ## OPTIONS
321 *
322 * [<command>]
323 * : Get cache warmup status or change it.
324 * ---
325 * default: status
326 * options:
327 * - status
328 * - disable
329 * - enable
330 * ---
331 *
332 * @when before_wp_load
333 *
334 * @param array $args Command arguments.
335 * @param array $assoc_args Command parameters.
336 */
337 public function nitropack_warmup( $args, $assoc_args ) {
338 $this->get_site_config();
339
340 $command = empty( $args[0] ) ? 'status' : $args[0];
341
342 /* Starts a warmup process for a website */
343 if ( 'run' === $command ) {
344 nitropack_run_warmup();
345 return;
346 }
347 /* Enables AND runs a warmup process for a website */
348 if ( 'enable' === $command ) {
349 nitropack_enable_warmup();
350 return;
351 }
352 /* Disables the warmup process for a website */
353 if ( 'disable' === $command ) {
354 nitropack_disable_warmup();
355 return;
356 }
357
358 try {
359 $api = $this->get_vendor_api();
360 $stats = $api->getWarmupStats();
361 if ( isset( $stats['status'] ) ) {
362 $stats['status'] = $stats['status'] ? 'enabled' : 'disabled';
363 }
364 WP_CLI\Utils\format_items(
365 'yaml',
366 array(
367 array(
368 'warmup' => $stats,
369 ),
370 ),
371 array(
372 'warmup',
373 )
374 );
375 $this->logger->notice( 'Get warmup status: ' . $stats['status'] );
376 } catch (\Exception $e) {
377 $this->logger->error( 'Failed to fetch warmup status. Error: ' . $e->getMessage() );
378 WP_CLI::error( $e->getMessage(), false );
379 WP_CLI::error( 'Failed to fetch warmup stats.' );
380 return;
381 }
382 }
383 /**
384 * NitroPack optimized URLs.
385 *
386 * ## OPTIONS
387 *
388 * [<command>]
389 * : Get optimized URLs or pending optimization.
390 * ---
391 * default: optimized
392 * options:
393 * - optimized
394 * - pending
395 * ---
396 *
397 * @when before_wp_load
398 *
399 * @param array $args Command arguments.
400 * @param array $assoc_args Command parameters.
401 */
402 public function nitropack_urls( $args, $assoc_args ) {
403 $this->get_site_config();
404
405 $command = empty( $args[0] ) ? 'optimized' : $args[0];
406
407 try {
408 $api = $this->get_vendor_api();
409 $result = array();
410 if ( 'optimized' === $command ) {
411 $result = $api->getUrls();
412 $this->logger->notice( 'Get optimized URLs' );
413 } elseif ( 'pending' === $command ) {
414 $result = $api->getPendingUrls();
415 $this->logger->notice( 'Get pending for optimization URLs' );
416 }
417
418 WP_CLI\Utils\format_items(
419 'yaml',
420 array(
421 array(
422 'urls' => $result,
423 ),
424 ),
425 array(
426 'urls',
427 )
428 );
429 } catch (\Exception $e) {
430 $this->logger->error( 'Get Optimized URLs. Error - ' . $e->getMessage() );
431 WP_CLI::error( $e->getMessage(), false );
432 WP_CLI::error( 'Failed to fetch URLs.' );
433 return;
434 }
435 }
436 /**
437 * NitroPack excluded URLs.
438 *
439 * ## OPTIONS
440 *
441 * [<command>]
442 * : Control excluded URLs.
443 * ---
444 * default: get
445 * options:
446 * - enable
447 * - disable
448 * - get
449 * - add
450 * - remove
451 * ---
452 *
453 * [<url_pattern>]
454 * : URL pattern.
455 *
456 * ## EXAMPLES
457 *
458 * # Enable feature to exclude any URL
459 * $ wp psb excludedurls enable
460 *
461 * # Exclude a contact page
462 * $ wp psb excludedurls add *\/contact
463 *
464 * @when before_wp_load
465 *
466 * @param array $args Command arguments.
467 * @param array $assoc_args Command parameters.
468 */
469 public function nitropack_excluded_urls( $args, $assoc_args ) {
470 $site_config = $this->get_site_config();
471
472 $command = empty( $args[0] ) ? '' : $args[0];
473 $url_pattern = empty( $args[1] ) ? '' : $args[1];
474
475 if ( ( 'add' === $command || 'remove' === $command ) && ! $url_pattern ) {
476 $this->logger->error( 'Enter valid URL' );
477 WP_CLI::error( 'Enter valid URL.' );
478 return;
479 }
480
481 try {
482 $api = $this->get_vendor_api();
483 switch ( $command ) {
484 case 'enable':
485 $api->enableExcludedUrls();
486 break;
487 case 'disable':
488 $api->disableExcludedUrls();
489 break;
490 case 'get':
491 $fetcher = new \NitroPack\SDK\Api\RemoteConfigFetcher( $site_config['siteId'], $site_config['siteSecret'] );
492 $response = $fetcher->get();
493 $config = @json_decode( $response, true ); // phpcs:ignore
494 if ( ! array_key_exists( 'DisabledURLs', $config ) ) {
495 $this->logger->error( 'Disabled URLs are not present in the respone. Vendor API might changed' );
496 WP_CLI::error( 'Disabled URLs are not present in the respone. Vendor API might changed.' );
497 return;
498 }
499 WP_CLI\Utils\format_items(
500 'yaml',
501 array(
502 array(
503 'excludes' => $config['DisabledURLs'],
504 ),
505 ),
506 array(
507 'excludes',
508 )
509 );
510 $this->logger->notice( 'Get excluded URLs' );
511 return;
512 case 'add':
513 $api->addExcludedUrl( $url_pattern );
514 $this->logger->notice( 'Add excluded URLs' );
515 break;
516 case 'remove':
517 $api->removeExcludedUrl( $url_pattern );
518 $this->logger->notice( 'Remove excluded URLs' );
519 break;
520 default:
521 $this->logger->error( 'Excluded URLs - enter valid sub-command.' );
522 WP_CLI::error( 'Enter valid sub-command.' );
523 return;
524 }
525 } catch (\Exception $e) {
526 $this->logger->error( "Failed to '" . $command . "' excluded URLs. Error: " . $e->getMessage() );
527 WP_CLI::error( $e->getMessage(), false );
528 WP_CLI::error( sprintf( 'Failed in %s excluded URL(s).', $command ) );
529 return;
530 }
531 $this->logger->notice( "Succeeded to '" . $command . "' excluded URL(s)" );
532 WP_CLI::success( sprintf( 'Succeeded to %s excluded URL(s).', $command ) );
533 }
534 /**
535 * NitroPack - JS, CSS, image, font excludes.
536 *
537 * ## OPTIONS
538 *
539 * [<command>]
540 * : Control excluded asset.
541 * ---
542 * default: get
543 * options:
544 * - enable
545 * - disable
546 * - get
547 * - add
548 * - remove
549 * ---
550 *
551 * [<url_pattern>]
552 * : URL pattern.
553 *
554 * [--resource=<type>]
555 * : Limit exclusion to a specific resource type.
556 * ---
557 * default: any
558 * options:
559 * - any
560 * - css
561 * - js
562 * - font
563 * - image
564 * ---
565 *
566 * [--device=<type>]
567 * : Limit exclusion to a specific device.
568 * ---
569 * default: any
570 * options:
571 * - any
572 * - desktop
573 * - tablet
574 * - mobile
575 * ---
576 *
577 * ## EXAMPLES
578 *
579 * # Enable feature to exclude any resource
580 * $ wp psb excludes enable
581 *
582 * # Exclude a contact page
583 * $ wp psb excludes add *\/script.js --resource=js
584 *
585 * @when before_wp_load
586 *
587 * @param array $args Command arguments.
588 * @param array $assoc_args Command parameters.
589 */
590 public function nitropack_excludes( $args, $assoc_args ) {
591 $this->get_site_config();
592
593 if ( ! class_exists( '\NitroPack\SDK\ExcludeEntry' ) ) {
594 $this->logger->error( 'The dependent plugin is incompatible' );
595 WP_CLI::error( 'The dependent plugin is incompatible.' );
596 return;
597 }
598
599 $command = empty( $args[0] ) ? '' : $args[0];
600 $url_pattern = empty( $args[1] ) ? '' : $args[1];
601
602 if ( ( 'add' === $command || 'remove' === $command ) && ! $url_pattern ) {
603 $this->logger->error( 'Enter valid URL' );
604 WP_CLI::error( 'Enter valid URL.' );
605 return;
606 }
607
608 try {
609 $api = $this->get_vendor_api();
610 switch ( $command ) {
611 case 'enable':
612 $api->enableExcludes();
613 break;
614 case 'disable':
615 $api->disableExcludes();
616 break;
617 case 'get':
618 $all_excludes = $api->getExcludes();
619 WP_CLI\Utils\format_items(
620 'yaml',
621 array(
622 array(
623 'excludes' => $all_excludes,
624 ),
625 ),
626 array(
627 'excludes',
628 )
629 );
630 $this->logger->notice( 'Get excludes' );
631 return;
632 case 'add':
633 $all_excludes = $api->getExcludes();
634
635 $new_exclude = new \NitroPack\SDK\ExcludeEntry();
636 $new_exclude->string = $url_pattern;
637 $new_exclude->device = empty( $assoc_args['device'] ) || 'any' === $assoc_args['device'] ? null : $assoc_args['device'];
638 $new_exclude->resourceType = empty( $assoc_args['resource'] ) || 'any' === $assoc_args['resource'] ? null : $assoc_args['resource']; // phpcs:ignore
639 $new_exclude->operation->all = true;
640
641 $all_excludes[] = $new_exclude;
642 $api->setExcludes( $all_excludes );
643 break;
644 case 'remove':
645 $all_excludes = $api->getExcludes();
646 $all_excludes = array_filter(
647 $all_excludes,
648 function ($exclusion) use ($url_pattern) {
649 return $exclusion->string !== $url_pattern;
650 }
651 );
652 $api->setExcludes( $all_excludes );
653 break;
654 default:
655 $this->logger->error( 'Enter valid sub-command' );
656 WP_CLI::error( 'Enter valid sub-command.' );
657 return;
658 }
659 } catch (\Exception $e) {
660 $this->logger->error( "Failed to '" . $command . "' exclude. Error: " . $e->getMessage() );
661 WP_CLI::error( $e->getMessage(), false );
662 WP_CLI::error( sprintf( 'Failed to %s exclude(s).', $command ) );
663 return;
664 }
665 $this->logger->notice( "Succeeded to '" . $command . "' exclude(s)" );
666 WP_CLI::success( sprintf( 'Succeeded to %s exclude(s).', $command ) );
667 }
668 }
669