| 1 |
<?php |
| 2 |
/** |
| 3 |
* ElasticPress CLI Utility |
| 4 |
* |
| 5 |
* @since 4.5.0 |
| 6 |
* @package elasticpress |
| 7 |
*/ |
| 8 |
|
| 9 |
namespace ElasticPress\Command; |
| 10 |
|
| 11 |
use WP_CLI; |
| 12 |
|
| 13 |
if ( ! defined( 'ABSPATH' ) ) { |
| 14 |
exit; // Exit if accessed directly. |
| 15 |
} |
| 16 |
|
| 17 |
/** |
| 18 |
* Utility class for WP CLI commands. |
| 19 |
*/ |
| 20 |
class Utility { |
| 21 |
|
| 22 |
/** |
| 23 |
* Internal timer. |
| 24 |
* |
| 25 |
* @var float |
| 26 |
*/ |
| 27 |
protected static $time_start = null; |
| 28 |
|
| 29 |
/** |
| 30 |
* Properly clean up when receiving SIGINT on indexing |
| 31 |
* |
| 32 |
* @param int $signal_no Signal number |
| 33 |
*/ |
| 34 |
public static function delete_transient_on_int( $signal_no ) { |
| 35 |
if ( SIGINT === $signal_no ) { |
| 36 |
self::delete_transient(); |
| 37 |
WP_CLI::log( esc_html__( 'Indexing cleaned up.', 'elasticpress' ) ); |
| 38 |
WP_CLI::halt( 0 ); |
| 39 |
} |
| 40 |
} |
| 41 |
|
| 42 |
/** |
| 43 |
* Delete transient that indicates indexing is occurring |
| 44 |
*/ |
| 45 |
public static function delete_transient() { |
| 46 |
\ElasticPress\IndexHelper::factory()->clear_index_meta(); |
| 47 |
|
| 48 |
if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { |
| 49 |
delete_site_transient( 'ep_cli_sync_progress' ); |
| 50 |
delete_site_transient( 'ep_wpcli_sync_interrupted' ); |
| 51 |
} else { |
| 52 |
delete_transient( 'ep_cli_sync_progress' ); |
| 53 |
delete_transient( 'ep_wpcli_sync_interrupted' ); |
| 54 |
} |
| 55 |
} |
| 56 |
|
| 57 |
/** |
| 58 |
* Stops the timer. |
| 59 |
* |
| 60 |
* @param int $precision The number of digits from the right of the decimal to display. Default 3. |
| 61 |
* @return float Time spent so far |
| 62 |
*/ |
| 63 |
public static function timer_stop( $precision = 3 ) { |
| 64 |
$diff = microtime( true ) - self::$time_start; |
| 65 |
return (float) number_format( (float) $diff, $precision, '.', '' ); |
| 66 |
} |
| 67 |
|
| 68 |
/** |
| 69 |
* Starts the timer. |
| 70 |
* |
| 71 |
* @return true |
| 72 |
*/ |
| 73 |
public static function timer_start() { |
| 74 |
self::$time_start = microtime( true ); |
| 75 |
return true; |
| 76 |
} |
| 77 |
|
| 78 |
/** |
| 79 |
* Check if sync should be interrupted |
| 80 |
*/ |
| 81 |
public static function should_interrupt_sync() { |
| 82 |
$should_interrupt_sync = get_transient( 'ep_wpcli_sync_interrupted' ); |
| 83 |
|
| 84 |
if ( $should_interrupt_sync ) { |
| 85 |
WP_CLI::line( esc_html__( 'Sync was interrupted', 'elasticpress' ) ); |
| 86 |
self::delete_transient_on_int( 2 ); |
| 87 |
WP_CLI::halt( 0 ); |
| 88 |
} |
| 89 |
} |
| 90 |
|
| 91 |
/** |
| 92 |
* Given a timestamp in microseconds, returns it in the given format. |
| 93 |
* |
| 94 |
* @param float $microtime Unix timestamp in ms |
| 95 |
* @param string $format Desired format |
| 96 |
* @return string |
| 97 |
*/ |
| 98 |
public static function timer_format( $microtime, $format = 'H:i:s.u' ) { |
| 99 |
$microtime_date = \DateTime::createFromFormat( 'U.u', number_format( (float) $microtime, 3, '.', '' ) ); |
| 100 |
return $microtime_date->format( $format ); |
| 101 |
} |
| 102 |
|
| 103 |
/** |
| 104 |
* If put_mapping fails while indexing, stop the index process. |
| 105 |
* |
| 106 |
* @param array $index_meta Index meta info |
| 107 |
* @param Indexable $indexable Indexable object |
| 108 |
* @param bool $result Whether the request was successful or not |
| 109 |
*/ |
| 110 |
public static function stop_on_failed_mapping( $index_meta, $indexable, $result ) { |
| 111 |
if ( ! $result ) { |
| 112 |
self::delete_transient(); |
| 113 |
|
| 114 |
WP_CLI::error( esc_html__( 'Mapping Failed.', 'elasticpress' ) ); |
| 115 |
} |
| 116 |
} |
| 117 |
|
| 118 |
/** |
| 119 |
* Ties the `ep_cli_put_mapping` action to `ep_sync_put_mapping`. |
| 120 |
* |
| 121 |
* @param array $index_meta Index meta information |
| 122 |
* @param Indexable $indexable Indexable object |
| 123 |
* @return void |
| 124 |
*/ |
| 125 |
public static function call_ep_cli_put_mapping( $index_meta, $indexable ) { |
| 126 |
/** |
| 127 |
* Fires after CLI put mapping |
| 128 |
* |
| 129 |
* @hook ep_cli_put_mapping |
| 130 |
* @param {Indexable} $indexable Indexable involved in mapping |
| 131 |
* @param {array} $args CLI command position args |
| 132 |
* @param {array} $assoc_args CLI command associative args |
| 133 |
*/ |
| 134 |
do_action( 'ep_cli_put_mapping', $indexable, WP_CLI::get_runner()->arguments, WP_CLI::get_runner()->assoc_args ); |
| 135 |
} |
| 136 |
|
| 137 |
|
| 138 |
/** |
| 139 |
* Custom get_transient to WP-CLI env. |
| 140 |
* |
| 141 |
* We are using the direct SQL query instead of |
| 142 |
* the regular function call to retrieve the updated |
| 143 |
* value to stop the sync. Otherwise, we always get |
| 144 |
* false after the command is running even when the value |
| 145 |
* is updated. |
| 146 |
* |
| 147 |
* @param mixed $pre_transient The default value. |
| 148 |
* @param string $transient Transient name. |
| 149 |
* @return true|null |
| 150 |
*/ |
| 151 |
public static function custom_get_transient( $pre_transient, $transient ) { |
| 152 |
global $wpdb; |
| 153 |
|
| 154 |
if ( wp_using_ext_object_cache() ) { |
| 155 |
/** |
| 156 |
* When external object cache is used we need to make sure to force a remote fetch, |
| 157 |
* so that the value from the local memory is discarded. |
| 158 |
*/ |
| 159 |
$should_interrupt_sync = wp_cache_get( $transient, 'transient', true ); |
| 160 |
} else { |
| 161 |
// phpcs:disable WordPress.DB.DirectDatabaseQuery |
| 162 |
$should_interrupt_sync = $wpdb->get_var( |
| 163 |
$wpdb->prepare( |
| 164 |
" |
| 165 |
SELECT option_value |
| 166 |
FROM $wpdb->options |
| 167 |
WHERE option_name = %s |
| 168 |
LIMIT 1 |
| 169 |
", |
| 170 |
"_transient_{$transient}" |
| 171 |
) |
| 172 |
); |
| 173 |
// phpcs:enable WordPress.DB.DirectDatabaseQuery |
| 174 |
} |
| 175 |
|
| 176 |
return $should_interrupt_sync ? (bool) $should_interrupt_sync : null; |
| 177 |
} |
| 178 |
} |
| 179 |
|