PluginProbe
ElasticPress / 5.3.5
ElasticPress v5.3.5
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
← All changes | includes/classes/Command.php +429 -322 4.2.25.3.5 View file →
@@ -9,18 +9,21 @@
9 9 */
10 10
11 11 namespace ElasticPress;
12 12
13 -use \WP_CLI_Command as WP_CLI_Command;
14 -use \WP_CLI as WP_CLI;
15 -use \WP_Hook as WP_Hook;
16 -use ElasticPress\Features as Features;
17 -use ElasticPress\Utils as Utils;
18 -use ElasticPress\Elasticsearch as Elasticsearch;
19 -use ElasticPress\Indexables as Indexables;
13 +use WP_CLI_Command;
14 +use WP_CLI;
15 +use ElasticPress\Command\Utility;
16 +use ElasticPress\Elasticsearch;
17 +use ElasticPress\FeatureRequirementsStatus;
18 +use ElasticPress\Features;
19 +use ElasticPress\Indexables;
20 +use ElasticPress\Utils;
20 21
21 22 if ( ! defined( 'ABSPATH' ) ) {
23 + // @codeCoverageIgnoreStart
22 24 exit; // Exit if accessed directly.
25 + // @codeCoverageIgnoreEnd
23 26 }
24 27
25 28 /**
26 29 * CLI Commands for ElasticPress
@@ -26,8 +29,10 @@
26 29 * CLI Commands for ElasticPress
27 30 */
28 31 class Command extends WP_CLI_Command {
29 32
33 + use DeprecatedCommand;
34 +
30 35 /**
31 36 * Holds temporary wp_actions when indexing with pagination
32 37 *
33 38 * @since 2.2
@@ -69,9 +74,9 @@
69 74 *
70 75 * @since 3.5.2
71 76 */
72 77 public function __construct() {
73 - add_filter( 'pre_transient_ep_wpcli_sync_interrupted', [ $this, 'custom_get_transient' ], 10, 2 );
78 + add_filter( 'pre_transient_ep_wpcli_sync_interrupted', [ Utility::class, 'custom_get_transient' ], 10, 2 );
74 79 }
75 80
76 81 /**
77 82 * Activate a feature. If a re-indexing is required, you will need to do it manually.
@@ -100,12 +105,14 @@
100 105 }
101 106
102 107 $status = $feature->requirements_status();
103 108
104 - if ( 2 === $status->code ) {
105 - WP_CLI::error( sprintf( esc_html__( 'Feature requirements are not met: %s', 'elasticpress' ), implode( "\n\n", (array) $status->message ) ) );
106 - } elseif ( 1 === $status->code ) {
107 - WP_CLI::warning( sprintf( esc_html__( 'Feature is usable but there are warnings: %s', 'elasticpress' ), implode( "\n\n", (array) $status->message ) ) );
109 + if ( FeatureRequirementsStatus::FORCE_DISABLED === $status->get_code() ) {
110 + /* translators: Error message */
111 + WP_CLI::error( sprintf( esc_html__( 'Feature requirements are not met: %s', 'elasticpress' ), implode( "\n\n", (array) $status->get_message() ) ) );
112 + } elseif ( FeatureRequirementsStatus::MANUALLY_ENABLED === $status->get_code() && ! empty( $status->get_message() ) ) {
113 + /* translators: Warning message */
114 + WP_CLI::warning( sprintf( esc_html__( 'Feature is usable but there are warnings: %s', 'elasticpress' ), implode( "\n\n", (array) $status->get_message() ) ) );
108 115 }
109 116
110 117 Features::factory()->activate_feature( $feature->slug );
111 118
@@ -116,9 +123,9 @@
116 123 WP_CLI::success( esc_html__( 'Feature activated', 'elasticpress' ) );
117 124 }
118 125
119 126 /**
120 - * Dectivate a feature.
127 + * Deactivate a feature.
121 128 *
122 129 * ## OPTIONS
123 130 *
124 131 * <feature-slug>
@@ -137,17 +144,18 @@
137 144 if ( empty( $feature ) ) {
138 145 WP_CLI::error( esc_html__( 'No feature with that slug is registered', 'elasticpress' ) );
139 146 }
140 147
141 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
142 - $active_features = get_site_option( 'ep_feature_settings', [] );
143 - } else {
144 - $active_features = get_option( 'ep_feature_settings', [] );
145 - }
148 + $active_features = (array) Features::factory()->get_feature_settings();
149 + $active_features_draft = (array) Features::factory()->get_feature_settings_draft();
146 150
147 - $key = array_search( $feature->slug, array_keys( $active_features ), true );
151 + $key_current = array_search( $feature->slug, array_keys( $active_features ), true );
152 + $key_draft = array_search( $feature->slug, array_keys( $active_features_draft ), true );
148 153
149 - if ( false === $key || empty( $active_features[ $feature->slug ]['active'] ) ) {
154 + $in_current = false !== $key_current && ! empty( $active_features[ $feature->slug ]['active'] );
155 + $in_draft = false !== $key_draft && ! empty( $active_features_draft[ $feature->slug ]['active'] );
156 +
157 + if ( ! $in_current && ! $in_draft ) {
150 158 WP_CLI::error( esc_html__( 'Feature is not active', 'elasticpress' ) );
151 159 }
152 160
153 161 Features::factory()->deactivate_feature( $feature->slug );
@@ -168,15 +176,13 @@
168 176 * @param array $args Positional CLI args.
169 177 * @param array $assoc_args Associative CLI args.
170 178 */
171 179 public function list_features( $args, $assoc_args ) {
180 + $list_all = \WP_CLI\Utils\get_flag_value( $assoc_args, 'all', null );
172 181
173 - if ( empty( $assoc_args['all'] ) ) {
174 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
175 - $features = get_site_option( 'ep_feature_settings', [] );
176 - } else {
177 - $features = get_option( 'ep_feature_settings', [] );
178 - }
182 + if ( empty( $list_all ) ) {
183 + $features = Features::factory()->get_feature_settings();
184 +
179 185 WP_CLI::line( esc_html__( 'Active features:', 'elasticpress' ) );
180 186
181 187 foreach ( array_keys( $features ) as $feature_slug ) {
182 188 $feature = Features::factory()->get_registered_feature( $feature_slug );
@@ -202,9 +208,9 @@
202 208 *
203 209 * ## OPTIONS
204 210 *
205 211 * [--network-wide]
206 - * : Force mappings to be sent for every index in the network.
212 + * : Force mappings to be sent for every index in the network. `--network-wide` takes an optional argument to limit the number of mappings to be sent where 0 is no limit. For example, `--network-wide=5` would send mappings for only 5 blogs on the network.
207 213 *
208 214 * [--indexables=<indexables>]
209 215 * : List of indexables
210 216 *
@@ -223,12 +229,9 @@
223 229 $this->maybe_change_host( $assoc_args );
224 230 $this->maybe_change_index_prefix( $assoc_args );
225 231 $this->connect_check();
226 232 $this->index_occurring();
227 -
228 - if ( ! $this->put_mapping_helper( $args, $assoc_args ) ) {
229 - exit( 1 );
230 - }
233 + $this->put_mapping_helper( $args, $assoc_args );
231 234 }
232 235
233 236 /**
234 237 * Add document mappings for every indexable
@@ -247,20 +250,16 @@
247 250
248 251 $non_global_indexable_objects = Indexables::factory()->get_all( false );
249 252 $global_indexable_objects = Indexables::factory()->get_all( true );
250 253
251 - if ( isset( $assoc_args['network-wide'] ) && is_multisite() ) {
254 + if ( isset( $assoc_args['network-wide'] ) && defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
252 255 if ( ! is_numeric( $assoc_args['network-wide'] ) ) {
253 256 $assoc_args['network-wide'] = 0;
254 257 }
255 258
256 - $sites = Utils\get_sites( $assoc_args['network-wide'] );
259 + $sites = Utils\get_sites( $assoc_args['network-wide'], true );
257 260
258 261 foreach ( $sites as $site ) {
259 - if ( ! Utils\is_site_indexable( $site['blog_id'] ) ) {
260 - continue;
261 - }
262 -
263 262 switch_to_blog( $site['blog_id'] );
264 263
265 264 foreach ( $non_global_indexable_objects as $indexable ) {
266 265 /**
@@ -269,12 +268,13 @@
269 268 if ( null !== $indexables && ! in_array( $indexable->slug, $indexables, true ) ) {
270 269 continue;
271 270 }
272 271
272 + /* translators: 1. Indexable; 2. Site ID */
273 273 WP_CLI::line( sprintf( esc_html__( 'Adding %1$s mapping for site %2$d…', 'elasticpress' ), esc_html( strtolower( $indexable->labels['singular'] ) ), (int) $site['blog_id'] ) );
274 274
275 275 $indexable->delete_index();
276 - $result = $indexable->put_mapping();
276 + $result = $indexable->put_mapping( 'raw' );
277 277
278 278 /**
279 279 * Fires after CLI put mapping
280 280 *
@@ -284,14 +284,18 @@
284 284 * @param {array} $assoc_args CLI command associative args
285 285 */
286 286 do_action( 'ep_cli_put_mapping', $indexable, $args, $assoc_args );
287 287
288 - if ( $result ) {
288 + if ( ! is_wp_error( $result ) ) {
289 289 WP_CLI::success( esc_html__( 'Mapping sent', 'elasticpress' ) );
290 290 } else {
291 - WP_CLI::error( esc_html__( 'Mapping failed', 'elasticpress' ), false );
292 -
293 - return false;
291 + WP_CLI::error(
292 + sprintf(
293 + /* translators: Error message */
294 + esc_html__( 'Mapping failed: %s', 'elasticpress' ),
295 + Utils\get_elasticsearch_error_reason( $result->get_error_message() )
296 + )
297 + );
294 298 }
295 299 }
296 300
297 301 restore_current_blog();
@@ -304,12 +308,13 @@
304 308 if ( null !== $indexables && ! in_array( $indexable->slug, $indexables, true ) ) {
305 309 continue;
306 310 }
307 311
312 + /* translators: Indexable label */
308 313 WP_CLI::line( sprintf( esc_html__( 'Adding %s mapping…', 'elasticpress' ), esc_html( strtolower( $indexable->labels['singular'] ) ) ) );
309 314
310 315 $indexable->delete_index();
311 - $result = $indexable->put_mapping();
316 + $result = $indexable->put_mapping( 'raw' );
312 317
313 318 /**
314 319 * Fires after CLI put mapping
315 320 *
@@ -319,14 +324,18 @@
319 324 * @param {array} $assoc_args CLI command associative args
320 325 */
321 326 do_action( 'ep_cli_put_mapping', $indexable, $args, $assoc_args );
322 327
323 - if ( $result ) {
328 + if ( ! is_wp_error( $result ) ) {
324 329 WP_CLI::success( esc_html__( 'Mapping sent', 'elasticpress' ) );
325 330 } else {
326 - WP_CLI::error( esc_html__( 'Mapping failed', 'elasticpress' ), false );
327 -
328 - return false;
331 + WP_CLI::error(
332 + sprintf(
333 + /* translators: Error message */
334 + esc_html__( 'Mapping failed: %s', 'elasticpress' ),
335 + Utils\get_elasticsearch_error_reason( $result->get_error_message() )
336 + )
337 + );
329 338 }
330 339 }
331 340 }
332 341
@@ -340,12 +349,13 @@
340 349 if ( null !== $indexables && ! in_array( $indexable->slug, $indexables, true ) ) {
341 350 continue;
342 351 }
343 352
353 + /* translators: Indexable label */
344 354 WP_CLI::line( sprintf( esc_html__( 'Adding %s mapping…', 'elasticpress' ), esc_html( strtolower( $indexable->labels['singular'] ) ) ) );
345 355
346 356 $indexable->delete_index();
347 - $result = $indexable->put_mapping();
357 + $result = $indexable->put_mapping( 'raw' );
348 358
349 359 /**
350 360 * Fires after CLI put mapping
351 361 *
@@ -355,14 +365,18 @@
355 365 * @param {array} $assoc_args CLI command associative args
356 366 */
357 367 do_action( 'ep_cli_put_mapping', $indexable, $args, $assoc_args );
358 368
359 - if ( $result ) {
369 + if ( ! is_wp_error( $result ) ) {
360 370 WP_CLI::success( esc_html__( 'Mapping sent', 'elasticpress' ) );
361 371 } else {
362 - WP_CLI::error( esc_html__( 'Mapping failed', 'elasticpress' ), false );
363 -
364 - return false;
372 + WP_CLI::error(
373 + sprintf(
374 + /* translators: Error message */
375 + esc_html__( 'Mapping failed: %s', 'elasticpress' ),
376 + Utils\get_elasticsearch_error_reason( $result->get_error_message() )
377 + )
378 + );
365 379 }
366 380 }
367 381
368 382 return true;
@@ -384,19 +398,21 @@
384 398 * @param array $args Positional CLI args.
385 399 * @param array $assoc_args Associative CLI args.
386 400 */
387 401 public function get_mapping( $args, $assoc_args ) {
388 - $index_names = (array) ( isset( $assoc_args['index-name'] ) ? $assoc_args['index-name'] : $this->get_index_names() );
402 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
403 + $index_name = \WP_CLI\Utils\get_flag_value( $assoc_args, 'index-name' );
404 + $index_names = (array) ( ! empty( $index_name ) ? $index_name : $this->get_index_names() );
389 405
390 406 $path = join( ',', $index_names ) . '/_mapping';
391 407
392 408 $response = Elasticsearch::factory()->remote_request( $path );
393 409
394 - $this->print_json_response( $response, ! empty( $assoc_args['pretty'] ) );
410 + $this->print_json_response( $response, $pretty );
395 411 }
396 412
397 413 /**
398 - * Return all indexes from the cluster as a JSON object.
414 + * Return all indices from the cluster as a JSON object.
399 415 *
400 416 * ## OPTIONS
401 417 *
402 418 * [--pretty]
@@ -401,19 +417,19 @@
401 417 *
402 418 * [--pretty]
403 419 * : Use this flag to render a pretty-printed version of the JSON response.
404 420 *
405 - * @subcommand get-cluster-indexes
406 - * @since 3.2, `--pretty` introduced in 4.1.0
421 + * @subcommand get-cluster-indices
422 + * @since 4.4.0, `--pretty` introduced in 4.1.0
407 423 * @param array $args Positional CLI args.
408 424 * @param array $assoc_args Associative CLI args.
409 425 */
410 - public function get_cluster_indexes( $args, $assoc_args ) {
411 - $path = '_cat/indices?format=json';
426 + public function get_cluster_indices( $args, $assoc_args ) {
427 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
412 428
413 - $response = Elasticsearch::factory()->remote_request( $path );
429 + $cluster_indices = Elasticsearch::factory()->get_cluster_indices();
414 430
415 - $this->print_json_response( $response, ! empty( $assoc_args['pretty'] ) );
431 + $this->pretty_json_encode( $cluster_indices, $pretty );
416 432 }
417 433
418 434 /**
419 435 * Return all index names as a JSON object.
@@ -422,47 +438,37 @@
422 438 *
423 439 * [--pretty]
424 440 * : Use this flag to render a pretty-printed version of the JSON response.
425 441 *
426 - * @subcommand get-indexes
427 - * @since 3.2, `--pretty` introduced in 4.1.0
442 + * [--status=<status>]
443 + * : Use this flag to render a pretty-printed version of the JSON response.
444 + *
445 + * @subcommand get-indices
446 + * @since 4.4.0, `--pretty` introduced in 4.1.0, `--status` introduced in 4.5.0
428 447 * @param array $args Positional CLI args.
429 448 * @param array $assoc_args Associative CLI args.
430 449 */
431 - public function get_indexes( $args, $assoc_args ) {
432 - $index_names = $this->get_index_names();
450 + public function get_indices( $args, $assoc_args ) {
451 + $defaults = [
452 + 'status' => 'active',
453 + ];
433 454
434 - $this->pretty_json_encode( $index_names, ! empty( $assoc_args['pretty'] ) );
455 + $assoc_args = wp_parse_args( $assoc_args, $defaults );
456 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
457 + $index_names = $this->get_index_names( $assoc_args['status'] );
458 +
459 + $this->pretty_json_encode( $index_names, $pretty );
435 460 }
436 461
437 462 /**
438 463 * Get all index names.
439 464 *
440 - * @since 3.6.4
465 + * @param string $status Whether to return active indexables or all registered.
466 + * @since 3.6.4, 4.5.0 Added $status
441 467 * @return array
442 468 */
443 - protected function get_index_names() {
444 - $sites = ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ? Utils\get_sites() : array( array( 'blog_id' => get_current_blog_id() ) );
445 -
446 - $all_indexables = Indexables::factory()->get_all();
447 -
448 - $global_indexes = [];
449 - $non_global_indexes = [];
450 - foreach ( $all_indexables as $indexable ) {
451 - if ( $indexable->global ) {
452 - $global_indexes[] = $indexable->get_index_name();
453 - continue;
454 - }
455 -
456 - foreach ( $sites as $site ) {
457 - if ( ! Utils\is_site_indexable( $site['blog_id'] ) ) {
458 - continue;
459 - }
460 - $non_global_indexes[] = $indexable->get_index_name( $site['blog_id'] );
461 - }
462 - }
463 -
464 - return array_merge( $non_global_indexes, $global_indexes );
469 + protected function get_index_names( $status = 'active' ) {
470 + return Elasticsearch::factory()->get_index_names( $status );
465 471 }
466 472
467 473 /**
468 474 * Delete the index for each indexable. !!Warning!! This removes your elasticsearch index(s) for the entire site.
@@ -472,9 +478,9 @@
472 478 * [--index-name=<index_name>]
473 479 * : The name of the index to be deleted. If not passed, all indexes will be deleted
474 480 *
475 481 * [--network-wide]
476 - * : Force every index on the network to be deleted.
482 + * : Force every index on the network to be deleted. `--network-wide` takes an optional argument to limit the number of indices to be deleted where 0 is no limit. For example, `--network-wide=5` would limit to only 5 indices on the network to be deleted.
477 483 *
478 484 * [--yes]
479 485 * : Skip confirmation
480 486 *
@@ -504,19 +510,19 @@
504 510
505 511 $non_global_indexable_objects = Indexables::factory()->get_all( false );
506 512 $global_indexable_objects = Indexables::factory()->get_all( true );
507 513
508 - if ( isset( $assoc_args['network-wide'] ) && is_multisite() ) {
514 + if ( isset( $assoc_args['network-wide'] ) && defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
509 515 if ( ! is_numeric( $assoc_args['network-wide'] ) ) {
510 516 $assoc_args['network-wide'] = 0;
511 517 }
512 - $sites = Utils\get_sites( $assoc_args['network-wide'] );
518 + $sites = Utils\get_sites( $assoc_args['network-wide'], false );
513 519
514 520 foreach ( $sites as $site ) {
515 521 switch_to_blog( $site['blog_id'] );
516 522
517 523 foreach ( $non_global_indexable_objects as $indexable ) {
518 -
524 + /* translators: 1. Indexable label; 2. Site ID */
519 525 WP_CLI::line( sprintf( esc_html__( 'Deleting %1$s index for site %2$d…', 'elasticpress' ), esc_html( strtolower( $indexable->labels['singular'] ) ), (int) $site['blog_id'] ) );
520 526
521 527 $result = $indexable->delete_index();
522 528
@@ -530,8 +536,9 @@
530 536 restore_current_blog();
531 537 }
532 538 } else {
533 539 foreach ( $non_global_indexable_objects as $indexable ) {
540 + /* translators: Index Label (plural) */
534 541 WP_CLI::line( sprintf( esc_html__( 'Deleting index for %s…', 'elasticpress' ), esc_html( strtolower( $indexable->labels['plural'] ) ) ) );
535 542
536 543 $result = $indexable->delete_index();
537 544
@@ -543,8 +550,9 @@
543 550 }
544 551 }
545 552
546 553 foreach ( $global_indexable_objects as $indexable ) {
554 + /* translators: Index Label (plural) */
547 555 WP_CLI::line( sprintf( esc_html__( 'Deleting index for %s…', 'elasticpress' ), esc_html( strtolower( $indexable->labels['plural'] ) ) ) );
548 556
549 557 $result = $indexable->delete_index();
550 558
@@ -569,11 +577,16 @@
569 577 public function recreate_network_alias( $args, $assoc_args ) {
570 578 $this->connect_check();
571 579 $this->index_occurring();
572 580
581 + if ( ! defined( 'EP_IS_NETWORK' ) || ! EP_IS_NETWORK ) {
582 + WP_CLI::error( esc_html__( 'ElasticPress is not network activated.', 'elasticpress' ) );
583 + }
584 +
573 585 $indexables = Indexables::factory()->get_all( false );
574 586
575 587 foreach ( $indexables as $indexable ) {
588 + /* translators: Index Label */
576 589 WP_CLI::line( sprintf( esc_html__( 'Recreating %s network alias…', 'elasticpress' ), esc_html( strtolower( $indexable->labels['singular'] ) ) ) );
577 590
578 591 $indexable->delete_network_alias();
579 592
@@ -604,11 +617,25 @@
604 617
605 618 add_action( 'ep_epio_wp_cli_set_autosuggest', [ $autosuggest_feature, 'epio_send_autosuggest_public_request' ] );
606 619
607 620 do_action( 'ep_epio_wp_cli_set_autosuggest', $args, $assoc_args );
621 +
622 + WP_CLI::success( esc_html__( 'Done.', 'elasticpress' ) );
608 623 }
609 624
610 625 /**
626 + * A WP-CLI wrapper to run `Autosuggest::post_deactivation()`.
627 + *
628 + * @subcommand epio-reset-autosuggest
629 + * @since 5.3.2
630 + */
631 + public function epio_reset_autosuggest() {
632 + Features::factory()->get_registered_feature( 'autosuggest' )->post_deactivation();
633 +
634 + WP_CLI::success( esc_html__( 'Done.', 'elasticpress' ) );
635 + }
636 +
637 + /**
611 638 * Helper method for creating the network alias for an indexable
612 639 *
613 640 * @param Indexable $indexable Instance of indexable.
614 641 * @since 0.9
@@ -614,16 +641,12 @@
614 641 * @since 0.9
615 642 * @return array|bool
616 643 */
617 644 private function create_network_alias_helper( Indexable $indexable ) {
618 - $sites = Utils\get_sites();
645 + $sites = Utils\get_sites( 0, true );
619 646 $indexes = [];
620 647
621 648 foreach ( $sites as $site ) {
622 - if ( ! Utils\is_site_indexable( $site['blog_id'] ) ) {
623 - continue;
624 - }
625 -
626 649 switch_to_blog( $site['blog_id'] );
627 650
628 651 $indexes[] = $indexable->get_index_name();
629 652
@@ -639,13 +662,10 @@
639 662 * @param int $signal_no Signal number
640 663 * @since 3.3
641 664 */
642 665 public function delete_transient_on_int( $signal_no ) {
643 - if ( SIGINT === $signal_no ) {
644 - $this->delete_transient();
645 - WP_CLI::log( esc_html__( 'Indexing cleaned up.', 'elasticpress' ) );
646 - exit;
647 - }
666 + _deprecated_function( __METHOD__, '4.5.0', '\ElasticPress\Command\Utility::delete_transient_on_int' );
667 + Utility::delete_transient_on_int( $signal_no );
648 668 }
649 669
650 670 /**
651 671 * Index all posts for a site or network wide.
@@ -657,8 +677,11 @@
657 677 *
658 678 * [--setup]
659 679 * : Clear the index first and re-send the put mapping. Use `--yes` to skip the confirmation
660 680 *
681 + * [--force]
682 + * : Stop any ongoing sync
683 + *
661 684 * [--per-page=<per_page_number>]
662 685 * : Determine the amount of posts to be indexed per bulk index (or cycle)
663 686 *
664 687 * [--nobulk]
@@ -675,8 +698,11 @@
675 698 *
676 699 * [--show-nobulk-errors]
677 700 * : Display the error message returned from Elasticsearch when a post fails to index while not using the /_bulk endpoint
678 701 *
702 + * [--stop-on-error]
703 + * : Stop indexing if an error is encountered and display the error.
704 + *
679 705 * [--offset=<offset_number>]
680 706 * : Skip the first n posts (don't forget to remove the `--setup` flag when resuming or the index will be emptied before starting again).
681 707 *
682 708 * [--indexables=<indexables>]
@@ -706,32 +732,45 @@
706 732 * [--yes]
707 733 * : Skip confirmation needed by `--setup`
708 734 *
709 735 * @param array $args Positional CLI args.
710 - * @since 0.1.2
736 + * @since 4.4.0
711 737 * @param array $assoc_args Associative CLI args.
712 738 */
713 - public function index( $args, $assoc_args ) {
714 - global $wp_actions;
739 + public function sync( $args, $assoc_args ) {
740 + $setup_option = \WP_CLI\Utils\get_flag_value( $assoc_args, 'setup', false );
741 + $force_option = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false );
715 742
716 - $setup_option = isset( $assoc_args['setup'] ) ? $assoc_args['setup'] : false;
743 + if ( $setup_option ) {
744 + $message = sprintf(
745 + /* translators: ElasticPress.io or Elasticsearch */
746 + esc_html__( 'Syncing with the --setup option will delete your existing index in %s. Are you sure you want to delete your Elasticsearch index', 'elasticpress' ),
747 + Utils\is_epio() ? 'ElasticPress.io' : 'Elasticsearch'
748 + );
749 + WP_CLI::confirm( $message, $assoc_args );
750 + }
717 751
718 - if ( true === $setup_option ) {
719 - WP_CLI::confirm( esc_html__( 'Indexing with setup option needs to delete Elasticsearch index first, are you sure you want to delete your Elasticsearch index?', 'elasticpress' ), $assoc_args );
752 + if ( $force_option ) {
753 + WP_CLI::confirm( esc_html__( 'Are you sure you want to stop any other ongoing sync?', 'elasticpress' ), $assoc_args );
720 754 }
721 755
722 756 if ( ! function_exists( 'pcntl_signal' ) ) {
723 - WP_CLI::warning( esc_html__( 'Function pcntl_signal not available. Make sure to run `wp elasticpress clear-index` in case the process is killed.', 'elasticpress' ) );
757 + WP_CLI::warning( esc_html__( 'Function pcntl_signal not available. Make sure to run `wp elasticpress clear-sync` in case the process is killed.', 'elasticpress' ) );
724 758 } else {
725 759 declare( ticks = 1 );
726 - pcntl_signal( SIGINT, [ $this, 'delete_transient_on_int' ] );
760 + pcntl_signal( SIGINT, [ Utility::class, 'delete_transient_on_int' ] );
727 761 }
728 762
729 763 $this->maybe_change_host( $assoc_args );
730 764 $this->maybe_change_index_prefix( $assoc_args );
731 765 $this->connect_check();
732 - $this->index_occurring();
733 766
767 + if ( $force_option ) {
768 + $this->clear_sync();
769 + } else {
770 + $this->index_occurring();
771 + }
772 +
734 773 $indexables = null;
735 774
736 775 if ( ! empty( $assoc_args['indexables'] ) ) {
737 776 $indexables = explode( ',', str_replace( ' ', '', $assoc_args['indexables'] ) );
@@ -751,29 +790,38 @@
751 790 * @param {array} $assoc_args CLI command associative args
752 791 */
753 792 do_action( 'ep_wp_cli_pre_index', $args, $assoc_args );
754 793
755 - $this->timer_start();
794 + Utility::timer_start();
756 795
757 - add_action( 'ep_sync_put_mapping', [ $this, 'stop_on_failed_mapping' ], 10, 3 );
758 - add_action( 'ep_sync_put_mapping', [ $this, 'call_ep_cli_put_mapping' ], 10, 2 );
759 - add_action( 'ep_index_batch_new_attempt', [ $this, 'should_interrupt_sync' ] );
796 + add_action( 'ep_sync_put_mapping', [ Utility::class, 'stop_on_failed_mapping' ], 10, 3 );
797 + add_action( 'ep_sync_put_mapping', [ Utility::class, 'call_ep_cli_put_mapping' ], 10, 2 );
798 + add_action( 'ep_index_batch_new_attempt', [ Utility::class, 'should_interrupt_sync' ] );
760 799
761 - $no_bulk = ! empty( $assoc_args['nobulk'] );
800 + $no_bulk = ! empty( $assoc_args['nobulk'] );
801 + $static_bulk = \WP_CLI\Utils\get_flag_value( $assoc_args, 'static-bulk', null );
802 + $network_wide = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network-wide', null );
762 803
763 804 $index_args = [
764 805 'method' => 'cli',
765 806 'total_attempts' => 1,
766 807 'indexables' => $indexables,
767 - 'put_mapping' => ! empty( $setup_option ),
808 + 'put_mapping' => $setup_option,
768 809 'output_method' => [ $this, 'index_output' ],
769 - 'network_wide' => ( ! empty( $assoc_args['network-wide'] ) ) ? $assoc_args['network-wide'] : null,
810 + 'network_wide' => $network_wide,
770 811 'nobulk' => $no_bulk,
771 812 'offset' => ( ! empty( $assoc_args['offset'] ) ) ? absint( $assoc_args['offset'] ) : 0,
772 - 'static_bulk' => ( ! empty( $assoc_args['static-bulk'] ) ) ? $assoc_args['static-bulk'] : null,
813 + 'static_bulk' => $static_bulk,
773 814 ];
774 815
775 - if ( isset( $assoc_args['show-errors'] ) || ( isset( $assoc_args['show-bulk-errors'] ) && ! $no_bulk ) || ( isset( $assoc_args['show-nobulk-errors'] ) && $no_bulk ) ) {
816 + $index_args['stop_on_error'] = WP_CLI\Utils\get_flag_value( $assoc_args, 'stop-on-error', false );
817 +
818 + $show_errors = $index_args['stop_on_error'] ||
819 + WP_CLI\Utils\get_flag_value( $assoc_args, 'show-errors', false ) ||
820 + ( WP_CLI\Utils\get_flag_value( $assoc_args, 'show-bulk-errors', false ) && ! $no_bulk ) ||
821 + ( WP_CLI\Utils\get_flag_value( $assoc_args, 'show-nobulk-errors', false ) && $no_bulk );
822 +
823 + if ( $show_errors ) {
776 824 $index_args['show_errors'] = true;
777 825 }
778 826
779 827 if ( ! empty( $assoc_args['post-ids'] ) ) {
@@ -806,13 +854,13 @@
806 854 }
807 855
808 856 \ElasticPress\IndexHelper::factory()->full_index( $index_args );
809 857
810 - remove_action( 'ep_sync_put_mapping', [ $this, 'stop_on_failed_mapping' ] );
811 - remove_action( 'ep_sync_put_mapping', [ $this, 'call_ep_cli_put_mapping' ], 10, 2 );
812 - remove_action( 'ep_index_batch_new_attempt', [ $this, 'should_interrupt_sync' ] );
858 + remove_action( 'ep_sync_put_mapping', [ Utility::class, 'stop_on_failed_mapping' ] );
859 + remove_action( 'ep_sync_put_mapping', [ Utility::class, 'call_ep_cli_put_mapping' ], 10, 2 );
860 + remove_action( 'ep_index_batch_new_attempt', [ Utility::class, 'should_interrupt_sync' ] );
813 861
814 - $sync_time_in_ms = $this->timer_stop();
862 + $sync_time_in_ms = Utility::timer_stop();
815 863
816 864 /**
817 865 * Fires after executing a CLI index
818 866 *
@@ -823,11 +871,11 @@
823 871 * @since 3.5.5
824 872 */
825 873 do_action( 'ep_wp_cli_after_index', $args, $assoc_args );
826 874
827 - WP_CLI::log( WP_CLI::colorize( '%Y' . esc_html__( 'Total time elapsed: ', 'elasticpress' ) . '%N' . $this->timer_format( $sync_time_in_ms ) ) );
875 + WP_CLI::log( WP_CLI::colorize( '%Y' . esc_html__( 'Total time elapsed: ', 'elasticpress' ) . '%N' . Utility::timer_format( $sync_time_in_ms ) ) );
828 876
829 - $this->delete_transient();
877 + Utility::delete_transient();
830 878
831 879 WP_CLI::success( esc_html__( 'Done!', 'elasticpress' ) );
832 880 }
833 881
@@ -838,10 +886,8 @@
838 886 */
839 887 public function status() {
840 888 $this->connect_check();
841 889
842 - $request_args = [ 'headers' => Elasticsearch::factory()->format_request_headers() ];
843 -
844 890 $registered_index_names = $this->get_index_names();
845 891
846 892 $response_cat_indices = Elasticsearch::factory()->remote_request( '_cat/indices?format=json' );
847 893
@@ -860,9 +906,9 @@
860 906 }
861 907
862 908 $index_names_imploded = implode( ',', $index_names );
863 909
864 - $request = wp_remote_get( trailingslashit( Utils\get_host( true ) ) . $index_names_imploded . '/_recovery/?pretty', $request_args );
910 + $request = Elasticsearch::factory()->remote_request( $index_names_imploded . '/_recovery/?pretty' );
865 911
866 912 if ( is_wp_error( $request ) ) {
867 913 WP_CLI::error( implode( "\n", $request->get_error_messages() ) );
868 914 }
@@ -883,10 +929,8 @@
883 929 */
884 930 public function stats() {
885 931 $this->connect_check();
886 932
887 - $request_args = array( 'headers' => Elasticsearch::factory()->format_request_headers() );
888 -
889 933 $registered_index_names = $this->get_index_names();
890 934
891 935 $response_cat_indices = Elasticsearch::factory()->remote_request( '_cat/indices?format=json' );
892 936
@@ -905,9 +949,10 @@
905 949 }
906 950
907 951 $index_names_imploded = implode( ',', $index_names );
908 952
909 - $request = wp_remote_get( trailingslashit( Utils\get_host( true ) ) . $index_names_imploded . '/_stats/', $request_args );
953 + Elasticsearch::factory()->refresh_indices();
954 + $request = Elasticsearch::factory()->remote_request( $index_names_imploded . '/_stats/' );
910 955
911 956 if ( is_wp_error( $request ) ) {
912 957 WP_CLI::error( implode( "\n", $request->get_error_messages() ) );
913 958 }
@@ -949,17 +994,10 @@
949 994 *
950 995 * @since 3.1
951 996 */
952 997 private function delete_transient() {
953 - \ElasticPress\IndexHelper::factory()->clear_index_meta();
954 -
955 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
956 - delete_site_transient( 'ep_cli_sync_progress' );
957 - delete_site_transient( 'ep_wpcli_sync_interrupted' );
958 - } else {
959 - delete_transient( 'ep_cli_sync_progress' );
960 - delete_transient( 'ep_wpcli_sync_interrupted' );
961 - }
998 + _deprecated_function( __METHOD__, '4.5.0', '\ElasticPress\Command\Utility::delete_transient()' );
999 + Utility::delete_transient();
962 1000 }
963 1001
964 1002 /**
965 1003 * Clear a sync/index process.
@@ -965,15 +1003,15 @@
965 1003 * Clear a sync/index process.
966 1004 *
967 1005 * If an index was stopped prematurely and won't start again, this will clear this cached data such that a new index can start.
968 1006 *
969 - * @subcommand clear-index
1007 + * @subcommand clear-sync
970 1008 * @alias delete-transient
971 - * @since 3.4
1009 + * @since 4.4.0
972 1010 */
973 - public function clear_index() {
1011 + public function clear_sync() {
974 1012 /**
975 - * Fires before the CLI `clear-index` command is executed.
1013 + * Fires before the CLI `clear-sync` command is executed.
976 1014 *
977 1015 * @hook ep_cli_before_clear_index
978 1016 *
979 1017 * @since 3.5.5
@@ -979,12 +1017,12 @@
979 1017 * @since 3.5.5
980 1018 */
981 1019 do_action( 'ep_cli_before_clear_index' );
982 1020
983 - $this->delete_transient();
1021 + Utility::delete_transient();
984 1022
985 1023 /**
986 - * Fires after the CLI `clear-index` command is executed.
1024 + * Fires after the CLI `clear-sync` command is executed.
987 1025 *
988 1026 * @hook ep_cli_after_clear_index
989 1027 *
990 1028 * @since 3.5.5
@@ -990,9 +1028,9 @@
990 1028 * @since 3.5.5
991 1029 */
992 1030 do_action( 'ep_cli_after_clear_index' );
993 1031
994 - WP_CLI::log( esc_html__( 'Index cleared.', 'elasticpress' ) );
1032 + WP_CLI::log( esc_html__( 'Sync cleared.', 'elasticpress' ) );
995 1033 }
996 1034
997 1035 /**
998 1036 * Returns the status of an ongoing index operation in JSON array.
@@ -1007,14 +1045,15 @@
1007 1045 *
1008 1046 * [--pretty]
1009 1047 * : Use this flag to render a pretty-printed version of the JSON response.
1010 1048 *
1011 - * @subcommand get-indexing-status
1049 + * @subcommand get-ongoing-sync-status
1012 1050 * @since 3.5.1, `--pretty` introduced in 4.1.0
1013 1051 * @param array $args Positional CLI args.
1014 1052 * @param array $assoc_args Associative CLI args.
1015 1053 */
1016 - public function get_indexing_status( $args, $assoc_args ) {
1054 + public function get_ongoing_sync_status( $args, $assoc_args ) {
1055 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
1017 1056 $indexing_status = Utils\get_indexing_status();
1018 1057
1019 1058 if ( empty( $indexing_status ) ) {
1020 1059 $indexing_status = [
@@ -1024,9 +1063,9 @@
1024 1063 'total_items' => -1,
1025 1064 ];
1026 1065 }
1027 1066
1028 - $this->pretty_json_encode( $indexing_status, ! empty( $assoc_args['pretty'] ) );
1067 + $this->pretty_json_encode( $indexing_status, $pretty );
1029 1068 }
1030 1069
1031 1070 /**
1032 1071 * Returns a JSON array with the results of the last index (if present) or an empty array.
@@ -1042,15 +1081,16 @@
1042 1081 * @param array $args Positional CLI args.
1043 1082 * @param array $assoc_args Associative CLI args.
1044 1083 */
1045 1084 public function get_last_sync( $args, $assoc_args ) {
1046 - $last_sync = \ElasticPress\IndexHelper::factory()->get_last_index();
1085 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
1086 + $last_sync = \ElasticPress\IndexHelper::factory()->get_last_sync();
1047 1087
1048 - $this->pretty_json_encode( $last_sync, ! empty( $assoc_args['pretty'] ) );
1088 + $this->pretty_json_encode( $last_sync, $pretty );
1049 1089 }
1050 1090
1051 1091 /**
1052 - * Returns a JSON array with the results of the last CLI index (if present) or an empty array.
1092 + * Returns a JSON array with the results of the last CLI sync (if present) or an empty array.
1053 1093 *
1054 1094 * ## OPTIONS
1055 1095 *
1056 1096 * [--clear]
@@ -1058,14 +1098,15 @@
1058 1098 *
1059 1099 * [--pretty]
1060 1100 * : Use this flag to render a pretty-printed version of the JSON response.
1061 1101 *
1062 - * @subcommand get-last-cli-index
1063 - * @since 3.5.1, `--pretty` introduced in 4.1.0
1102 + * @subcommand get-last-cli-sync
1103 + * @since 4.4.0, `--pretty` introduced in 4.1.0
1064 1104 * @param array $args Positional CLI args.
1065 1105 * @param array $assoc_args Associative CLI args.
1066 1106 */
1067 - public function get_last_cli_index( $args, $assoc_args ) {
1107 + public function get_last_cli_sync( $args, $assoc_args ) {
1108 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
1068 1109 $last_sync = Utils\get_option( 'ep_last_cli_index', array() );
1069 1110
1070 1111 if ( isset( $assoc_args['clear'] ) ) {
1071 1112 Utils\delete_option( 'ep_last_cli_index' );
@@ -1070,9 +1111,9 @@
1070 1111 if ( isset( $assoc_args['clear'] ) ) {
1071 1112 Utils\delete_option( 'ep_last_cli_index' );
1072 1113 }
1073 1114
1074 - $this->pretty_json_encode( $last_sync, ! empty( $assoc_args['pretty'] ) );
1115 + $this->pretty_json_encode( $last_sync, $pretty );
1075 1116 }
1076 1117
1077 1118
1078 1119 /**
@@ -1085,9 +1126,9 @@
1085 1126 private function maybe_change_host( $assoc_args ) {
1086 1127 if ( isset( $assoc_args['ep-host'] ) ) {
1087 1128 add_filter(
1088 1129 'ep_host',
1089 - function ( $host ) use ( $assoc_args ) {
1130 + function () use ( $assoc_args ) {
1090 1131 return $assoc_args['ep-host'];
1091 1132 }
1092 1133 );
1093 1134 }
@@ -1092,9 +1133,8 @@
1092 1133 );
1093 1134 }
1094 1135 }
1095 1136
1096 -
1097 1137 /**
1098 1138 * maybe change index prefix on the fly
1099 1139 *
1100 1140 * @param array $assoc_args Associative CLI args.
@@ -1104,9 +1144,9 @@
1104 1144 private function maybe_change_index_prefix( $assoc_args ) {
1105 1145 if ( isset( $assoc_args['ep-prefix'] ) ) {
1106 1146 add_filter(
1107 1147 'ep_index_prefix',
1108 - function ( $prefix ) use ( $assoc_args ) {
1148 + function ( $prefix ) use ( $assoc_args ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
1109 1149 return $assoc_args['ep-prefix'];
1110 1150 }
1111 1151 );
1112 1152 }
@@ -1117,26 +1157,21 @@
1117 1157 *
1118 1158 * @since 3.5.2
1119 1159 */
1120 1160 public function should_interrupt_sync() {
1121 - $should_interrupt_sync = get_transient( 'ep_wpcli_sync_interrupted' );
1122 -
1123 - if ( $should_interrupt_sync ) {
1124 - WP_CLI::line( esc_html__( 'Sync was interrupted', 'elasticpress' ) );
1125 - $this->delete_transient_on_int( 2 );
1126 - WP_CLI::halt();
1127 - }
1161 + _deprecated_function( __METHOD__, '4.5.0', '\ElasticPress\Command\Utility::should_interrupt_sync' );
1162 + Utility::should_interrupt_sync();
1128 1163 }
1129 1164
1130 1165 /**
1131 - * Stop the indexing operation started from the dashboard.
1166 + * Stop the Sync operation started from the dashboard.
1132 1167 *
1133 - * @subcommand stop-indexing
1134 - * @since 3.5.2
1168 + * @subcommand stop-sync
1169 + * @since 4.4.0
1135 1170 * @param array $args Positional CLI args.
1136 1171 * @param array $assoc_args Associative CLI args.
1137 1172 */
1138 - public function stop_indexing( $args, $assoc_args ) {
1173 + public function stop_sync( $args, $assoc_args ) {
1139 1174 $indexing_status = \ElasticPress\Utils\get_indexing_status();
1140 1175
1141 1176 if ( empty( \ElasticPress\Utils\get_indexing_status() ) ) {
1142 1177 WP_CLI::warning( esc_html__( 'There is no indexing operation running.', 'elasticpress' ) );
@@ -1190,19 +1225,11 @@
1190 1225 WP_CLI::error( esc_html__( 'This command expects a version number or the --default flag.', 'elasticpress' ) );
1191 1226 }
1192 1227
1193 1228 if ( ! empty( $assoc_args['default'] ) ) {
1194 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
1195 - delete_site_option( 'ep_search_algorithm_version' );
1196 - } else {
1197 - delete_option( 'ep_search_algorithm_version' );
1198 - }
1229 + Utils\delete_option( 'ep_search_algorithm_version' );
1199 1230 } else {
1200 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
1201 - update_site_option( 'ep_search_algorithm_version', $assoc_args['version'] );
1202 - } else {
1203 - update_option( 'ep_search_algorithm_version', $assoc_args['version'], false );
1204 - }
1231 + Utils\update_option( 'ep_search_algorithm_version', $assoc_args['version'] );
1205 1232 }
1206 1233
1207 1234 /**
1208 1235 * Fires after the algorithm version is changed via WP-CLI.
@@ -1230,13 +1257,9 @@
1230 1257 * @param array $args Positional CLI args.
1231 1258 * @param array $assoc_args Associative CLI args.
1232 1259 */
1233 1260 public function get_search_algorithm_version( $args, $assoc_args ) {
1234 - if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
1235 - $value = get_site_option( 'ep_search_algorithm_version', '' );
1236 - } else {
1237 - $value = get_option( 'ep_search_algorithm_version', '' );
1238 - }
1261 + $value = Utils\get_option( 'ep_search_algorithm_version', '' );
1239 1262
1240 1263 if ( empty( $value ) ) {
1241 1264 WP_CLI::line( 'default' );
1242 1265 } else {
@@ -1258,39 +1281,14 @@
1258 1281 * @param string $transient Transient name.
1259 1282 * @return true|null
1260 1283 */
1261 1284 public function custom_get_transient( $pre_transient, $transient ) {
1262 - global $wpdb;
1263 -
1264 - if ( wp_using_ext_object_cache() ) {
1265 - /**
1266 - * When external object cache is used we need to make sure to force a remote fetch,
1267 - * so that the value from the local memory is discarded.
1268 - */
1269 - $should_interrupt_sync = wp_cache_get( $transient, 'transient', true );
1270 - } else {
1271 - $options = $wpdb->options;
1272 -
1273 - $should_interrupt_sync = $wpdb->get_var(
1274 - // phpcs:disable
1275 - $wpdb->prepare(
1276 - "
1277 - SELECT option_value
1278 - FROM $options
1279 - WHERE option_name = %s
1280 - LIMIT 1
1281 - ",
1282 - "_transient_{$transient}"
1283 - )
1284 - // phpcs:enable
1285 - );
1286 - }
1287 -
1288 - return $should_interrupt_sync ? (bool) $should_interrupt_sync : null;
1285 + _deprecated_function( __METHOD__, '4.5.0', '\ElasticPress\Command\Utility::custom_get_transient' );
1286 + return Utility::custom_get_transient( $pre_transient, $transient );
1289 1287 }
1290 1288
1291 1289 /**
1292 - * Utilitary function to render Stats for a given index.
1290 + * Utility function to render Stats for a given index.
1293 1291 *
1294 1292 * @since 3.5.6
1295 1293 * @param string $current_index The index name.
1296 1294 * @param array $body The response body.
@@ -1308,9 +1306,9 @@
1308 1306 }
1309 1307 }
1310 1308
1311 1309 /**
1312 - * Function used to ouput messages coming from IndexHelper
1310 + * Function used to output messages coming from IndexHelper
1313 1311 *
1314 1312 * @param array $message Message data
1315 1313 * @param array $args Args sent and processed by IndexHelper
1316 1314 * @param array $index_meta Current index state
@@ -1327,13 +1325,14 @@
1327 1325 case 'warning':
1328 1326 if ( empty( $args['show_errors'] ) ) {
1329 1327 return;
1330 1328 }
1329 +
1331 1330 WP_CLI::warning( $message['message'] );
1332 1331 break;
1333 1332
1334 1333 case 'error':
1335 - $this->clear_index();
1334 + $this->clear_sync();
1336 1335 WP_CLI::error( $message['message'] );
1337 1336 break;
1338 1337
1339 1338 default:
@@ -1341,17 +1340,19 @@
1341 1340 break;
1342 1341 }
1343 1342
1344 1343 if ( 'index_next_batch' === $context ) {
1345 - $counter++;
1344 + ++$counter;
1346 1345 if ( ( $counter % 10 ) === 0 ) {
1347 - $time_elapsed_diff = $time_elapsed > 0 ? ' (+' . (string) ( $this->timer_stop() - $time_elapsed ) . ')' : '';
1348 - $time_elapsed = $this->timer_stop( 2 );
1349 - WP_CLI::log( WP_CLI::colorize( '%Y' . esc_html__( 'Time elapsed: ', 'elasticpress' ) . '%N' . $this->timer_format( $time_elapsed ) . $time_elapsed_diff ) );
1346 + $time_elapsed_diff = $time_elapsed > 0 ? ' (+' . (string) ( Utility::timer_stop() - $time_elapsed ) . ')' : '';
1347 + $time_elapsed = Utility::timer_stop( 2 );
1348 + WP_CLI::log( WP_CLI::colorize( '%Y' . esc_html__( 'Time elapsed: ', 'elasticpress' ) . '%N' . Utility::timer_format( $time_elapsed ) . $time_elapsed_diff ) );
1350 1349
1351 - $current_memory = round( memory_get_usage() / 1024 / 1024, 2 ) . 'mb';
1352 - $peak_memory = ' (Peak: ' . round( memory_get_peak_usage() / 1024 / 1024, 2 ) . 'mb)';
1353 - WP_CLI::log( WP_CLI::colorize( '%Y' . esc_html__( 'Memory Usage: ', 'elasticpress' ) . '%N' . $current_memory . $peak_memory ) );
1350 + $current_memory = memory_get_usage() / 1024 / 1024;
1351 + $current_memory = ( $current_memory > 1000 ) ? round( $current_memory / 1024, 2 ) . 'gb' : round( $current_memory, 2 ) . 'mb';
1352 + $peak_memory = memory_get_peak_usage() / 1024 / 1024;
1353 + $peak_memory = ( $peak_memory > 1000 ) ? round( $peak_memory / 1024, 2 ) . 'gb' : round( $peak_memory, 2 ) . 'mb';
1354 + WP_CLI::log( WP_CLI::colorize( '%Y' . esc_html__( 'Memory Usage: ', 'elasticpress' ) . '%N' . $current_memory . ' (Peak: ' . $peak_memory . ')' ) );
1354 1355 }
1355 1356 }
1356 1357 }
1357 1358
@@ -1362,13 +1363,10 @@
1362 1363 * @param Indexable $indexable Indexable object
1363 1364 * @param bool $result Whether the request was successful or not
1364 1365 */
1365 1366 public function stop_on_failed_mapping( $index_meta, $indexable, $result ) {
1366 - if ( ! $result ) {
1367 - $this->delete_transient();
1368 -
1369 - exit( 1 );
1370 - }
1367 + _deprecated_function( __METHOD__, '4.5.0', '\ElasticPress\Command\Utility::stop_on_failed_mapping' );
1368 + Utility::stop_on_failed_mapping( $index_meta, $indexable, $result );
1371 1369 }
1372 1370
1373 1371 /**
1374 1372 * Ties the `ep_cli_put_mapping` action to `ep_sync_put_mapping`.
@@ -1379,17 +1377,10 @@
1379 1377 * @param Indexable $indexable Indexable object
1380 1378 * @return void
1381 1379 */
1382 1380 public function call_ep_cli_put_mapping( $index_meta, $indexable ) {
1383 - /**
1384 - * Fires after CLI put mapping
1385 - *
1386 - * @hook ep_cli_put_mapping
1387 - * @param {Indexable} $indexable Indexable involved in mapping
1388 - * @param {array} $args CLI command position args
1389 - * @param {array} $assoc_args CLI command associative args
1390 - */
1391 - do_action( 'ep_cli_put_mapping', $indexable, $this->args, $this->assoc_args );
1381 + _deprecated_function( __METHOD__, '4.5.0', '\ElasticPress\Command\Utility::call_ep_cli_put_mapping' );
1382 + Utility::call_ep_cli_put_mapping( $index_meta, $indexable );
1392 1383 }
1393 1384
1394 1385 /**
1395 1386 * Send a HTTP request to Elasticsearch
@@ -1418,8 +1409,9 @@
1418 1409 * @param array $args Positional CLI args.
1419 1410 * @param array $assoc_args Associative CLI args.
1420 1411 */
1421 1412 public function request( $args, $assoc_args ) {
1413 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
1422 1414 $path = $args[0];
1423 1415 $method = isset( $assoc_args['method'] ) ? $assoc_args['method'] : 'GET';
1424 1416 $body = isset( $assoc_args['body'] ) ? $assoc_args['body'] : '';
1425 1417 $request_args = [
@@ -1428,54 +1420,9 @@
1428 1420 if ( 'GET' !== $method && ! empty( $body ) ) {
1429 1421 $request_args['body'] = $body;
1430 1422 }
1431 1423
1432 - if ( ! empty( $assoc_args['debug-http-request'] ) ) {
1433 - add_filter(
1434 - 'http_api_debug',
1435 - function ( $response, $context, $transport, $request_args, $url ) {
1436 - // phpcs:disable WordPress.PHP.DevelopmentFunctions
1437 - WP_CLI::line(
1438 - sprintf(
1439 - /* translators: URL of the request */
1440 - esc_html__( 'URL: %s', 'elasticpress' ),
1441 - $url
1442 - )
1443 - );
1444 - WP_CLI::line(
1445 - sprintf(
1446 - /* translators: Request arguments (outputted with print_r()) */
1447 - esc_html__( 'Request Args: %s', 'elasticpress' ),
1448 - print_r( $request_args, true )
1449 - )
1450 - );
1451 - WP_CLI::line(
1452 - sprintf(
1453 - /* translators: HTTP transport used */
1454 - esc_html__( 'Transport: %s', 'elasticpress' ),
1455 - $transport
1456 - )
1457 - );
1458 - WP_CLI::line(
1459 - sprintf(
1460 - /* translators: Context under which the http_api_debug hook is fired */
1461 - esc_html__( 'Context: %s', 'elasticpress' ),
1462 - $context
1463 - )
1464 - );
1465 - WP_CLI::line(
1466 - sprintf(
1467 - /* translators: HTTP response (outputted with print_r()) */
1468 - esc_html__( 'Response: %s', 'elasticpress' ),
1469 - print_r( $response, true )
1470 - )
1471 - );
1472 - // phpcs:enable WordPress.PHP.DevelopmentFunctions
1473 - },
1474 - 10,
1475 - 5
1476 - );
1477 - }
1424 + $this->maybe_add_http_api_debug_filter( $assoc_args );
1478 1425 $response = Elasticsearch::factory()->remote_request( $path, $request_args, [], 'wp_cli_request' );
1479 1426
1480 1427 if ( is_wp_error( $response ) ) {
1481 1428 WP_CLI::error( $response->get_error_message() );
@@ -1480,9 +1427,9 @@
1480 1427 if ( is_wp_error( $response ) ) {
1481 1428 WP_CLI::error( $response->get_error_message() );
1482 1429 }
1483 1430
1484 - $this->print_json_response( $response, ! empty( $assoc_args['pretty'] ) );
1431 + $this->print_json_response( $response, $pretty );
1485 1432 }
1486 1433
1487 1434 /**
1488 1435 * Reset all ElasticPress settings stored in WP options and transients.
@@ -1509,45 +1456,10 @@
1509 1456
1510 1457 WP_CLI::line( esc_html__( 'Settings deleted.', 'elasticpress' ) );
1511 1458 }
1512 1459
1513 - /**
1514 - * Starts the timer.
1515 - *
1516 - * @since 4.2.0
1517 - * @return true
1518 - */
1519 - protected function timer_start() {
1520 - $this->time_start = microtime( true );
1521 - return true;
1522 - }
1523 1460
1524 1461 /**
1525 - * Stops the timer.
1526 - *
1527 - * @since 4.2.0
1528 - * @param int $precision The number of digits from the right of the decimal to display. Default 3.
1529 - * @return float Time spent so far
1530 - */
1531 - protected function timer_stop( $precision = 3 ) {
1532 - $diff = microtime( true ) - $this->time_start;
1533 - return (float) number_format( (float) $diff, $precision );
1534 - }
1535 -
1536 - /**
1537 - * Given a timestamp in microseconds, returns it in the given format.
1538 - *
1539 - * @since 4.2.0
1540 - * @param float $microtime Unix timestamp in ms
1541 - * @param string $format Desired format
1542 - * @return string
1543 - */
1544 - protected function timer_format( $microtime, $format = 'H:i:s.u' ) {
1545 - $microtime_date = \DateTime::createFromFormat( 'U.u', number_format( (float) $microtime, 3, '.', '' ) );
1546 - return $microtime_date->format( $format );
1547 - }
1548 -
1549 - /**
1550 1462 * Print an HTTP response.
1551 1463 *
1552 1464 * @since 4.1.0
1553 1465 * @param array $response HTTP Response.
@@ -1576,8 +1488,203 @@
1576 1488 * @param array $json_obj The JSON object or array.
1577 1489 * @param boolean $pretty_print_flag Whether it should or not be formatted.
1578 1490 */
1579 1491 protected function pretty_json_encode( $json_obj, $pretty_print_flag ) {
1580 - $flag = $pretty_print_flag ? JSON_PRETTY_PRINT : null;
1492 + $flag = $pretty_print_flag ? JSON_PRETTY_PRINT : 0;
1581 1493 WP_CLI::line( wp_json_encode( $json_obj, $flag ) );
1494 + }
1495 +
1496 + /**
1497 + * Gets the Instant Results search template.
1498 + *
1499 + * ## OPTIONS
1500 + *
1501 + * [--pretty]
1502 + * : Use this flag to render a pretty-printed version of the JSON response.
1503 + *
1504 + * @since 4.5.0
1505 + * @param array $args Positional CLI args.
1506 + * @param array $assoc_args Associative CLI args.
1507 + *
1508 + * @subcommand get-search-template
1509 + */
1510 + public function get_search_template( $args, $assoc_args ) {
1511 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
1512 + $instant_results = Features::factory()->get_registered_feature( 'instant-results' );
1513 + $template = json_decode( $instant_results->epio_get_search_template() );
1514 +
1515 + $this->pretty_json_encode( $template, $pretty );
1516 + }
1517 +
1518 + /**
1519 + * Saves the Instant Results search template to EPIO.
1520 + *
1521 + * ## OPTIONS
1522 + *
1523 + * [--network-wide]
1524 + * : Save the template for all sites in the network if plugin is network activated. Otherwise, save the template for the current site only.
1525 + *
1526 + * @since 4.5.0
1527 + * @param array $args Positional CLI args.
1528 + * @param array $assoc_args Associative CLI args.
1529 + * @subcommand put-search-template
1530 + */
1531 + public function put_search_template( $args, $assoc_args ) {
1532 + $network_wide = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network-wide', false );
1533 + $instant_results = Features::factory()->get_registered_feature( 'instant-results' );
1534 +
1535 + $instant_results->epio_save_site_search_template( $network_wide );
1536 +
1537 + WP_CLI::success( esc_html__( 'Done.', 'elasticpress' ) );
1538 + }
1539 +
1540 + /**
1541 + * Deletes the Instant Results search template.
1542 + *
1543 + * ## OPTIONS
1544 + *
1545 + * [--network-wide]
1546 + * : Delete the template for all sites in the network if plugin is network activated. Otherwise, delete the template for the current site only.
1547 + *
1548 + * @since 4.5.0
1549 + * @param array $args Positional CLI args.
1550 + * @param array $assoc_args Associative CLI args.
1551 + * @subcommand delete-search-template
1552 + */
1553 + public function delete_search_template( $args, $assoc_args ) {
1554 + $network_wide = \WP_CLI\Utils\get_flag_value( $assoc_args, 'network-wide', false );
1555 + $instant_results = Features::factory()->get_registered_feature( 'instant-results' );
1556 +
1557 + $instant_results->epio_delete_site_search_template( $network_wide );
1558 +
1559 + WP_CLI::success( esc_html__( 'Done.', 'elasticpress' ) );
1560 + }
1561 +
1562 + /**
1563 + * Get an index settings
1564 + *
1565 + * ## OPTIONS
1566 + *
1567 + * <index_name>
1568 + * : Index name
1569 + *
1570 + * [--pretty]
1571 + * : Use this flag to render a pretty-printed version of the JSON response.
1572 + *
1573 + * @subcommand get-index-settings
1574 + *
1575 + * @since 4.7.0
1576 + *
1577 + * @param array $args Positional CLI args.
1578 + * @param array $assoc_args Associative CLI args.
1579 + */
1580 + public function get_index_settings( $args, $assoc_args ) {
1581 + $response = Elasticsearch::factory()->get_index_settings( $args[0], true );
1582 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
1583 +
1584 + $this->pretty_json_encode( $response, $pretty );
1585 + }
1586 +
1587 + /**
1588 + * Get a specific content in Elasticsearch
1589 + *
1590 + * ## OPTIONS
1591 + *
1592 + * <indexable>
1593 + * : Indexable slug. Example: `post`
1594 + *
1595 + * <ID>
1596 + * : Content ID
1597 + *
1598 + * [--debug-http-request]
1599 + * : Enable debugging
1600 + *
1601 + * [--pretty]
1602 + * : Use this flag to render a pretty-printed version of the JSON response.
1603 + *
1604 + * @since 4.7.0
1605 + *
1606 + * @param array $args Positional CLI args.
1607 + * @param array $assoc_args Associative CLI args.
1608 + */
1609 + public function get( $args, $assoc_args ) {
1610 + $indexables = Indexables::factory();
1611 +
1612 + $indexable = $indexables->get( $args[0] );
1613 + if ( ! $indexable || ! $indexables->is_active( $args[0] ) ) {
1614 + $message = wp_sprintf(
1615 + /* translators: list of active indexables slugs */
1616 + esc_html__( 'Indexable not found or inactive. Active indexables are: %l', 'elasticpress' ),
1617 + $indexables->get_all( null, true )
1618 + );
1619 + WP_CLI::error( $message );
1620 + }
1621 +
1622 + $this->maybe_add_http_api_debug_filter( $assoc_args );
1623 +
1624 + $object = $indexable->get( $args[1] );
1625 + if ( ! $object ) {
1626 + WP_CLI::error( esc_html__( 'Not found', 'elasticpress' ) );
1627 + }
1628 +
1629 + $pretty = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pretty' );
1630 +
1631 + $this->pretty_json_encode( $object, $pretty );
1632 + }
1633 +
1634 + /**
1635 + * Given associative CLI args, conditionally displays HTTP debug info
1636 + *
1637 + * @since 4.7.0
1638 + * @param array $assoc_args Associative CLI args.
1639 + */
1640 + protected function maybe_add_http_api_debug_filter( $assoc_args ) {
1641 + $debug_http_request = \WP_CLI\Utils\get_flag_value( $assoc_args, 'debug-http-request' );
1642 +
1643 + if ( ! empty( $debug_http_request ) ) {
1644 + add_filter(
1645 + 'http_api_debug',
1646 + function ( $response, $context, $transport, $request_args, $url ) {
1647 + // phpcs:disable WordPress.PHP.DevelopmentFunctions
1648 + WP_CLI::line(
1649 + sprintf(
1650 + /* translators: URL of the request */
1651 + esc_html__( 'URL: %s', 'elasticpress' ),
1652 + $url
1653 + )
1654 + );
1655 + WP_CLI::line(
1656 + sprintf(
1657 + /* translators: Request arguments (outputted with print_r()) */
1658 + esc_html__( 'Request Args: %s', 'elasticpress' ),
1659 + print_r( $request_args, true )
1660 + )
1661 + );
1662 + WP_CLI::line(
1663 + sprintf(
1664 + /* translators: HTTP transport used */
1665 + esc_html__( 'Transport: %s', 'elasticpress' ),
1666 + $transport
1667 + )
1668 + );
1669 + WP_CLI::line(
1670 + sprintf(
1671 + /* translators: Context under which the http_api_debug hook is fired */
1672 + esc_html__( 'Context: %s', 'elasticpress' ),
1673 + $context
1674 + )
1675 + );
1676 + WP_CLI::line(
1677 + sprintf(
1678 + /* translators: HTTP response (outputted with print_r()) */
1679 + esc_html__( 'Response: %s', 'elasticpress' ),
1680 + print_r( $response, true )
1681 + )
1682 + );
1683 + // phpcs:enable WordPress.PHP.DevelopmentFunctions
1684 + },
1685 + 10,
1686 + 5
1687 + );
1688 + }
1582 1689 }
1583 1690 }