initial commit
This commit is contained in:
467
includes/cli/class-wc-cli-rest-command.php
Normal file
467
includes/cli/class-wc-cli-rest-command.php
Normal file
@ -0,0 +1,467 @@
|
||||
<?php
|
||||
/**
|
||||
* WP_CLI_Rest_Command class file.
|
||||
*
|
||||
* @package WooCommerce\CLI
|
||||
*/
|
||||
|
||||
use Automattic\Jetpack\Constants;
|
||||
use Automattic\WooCommerce\Utilities\NumberUtil;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Command for WooCommere CLI.
|
||||
*
|
||||
* Since a lot of WC operations can be handled via the REST API, we base our CLI
|
||||
* off of Restful to generate commands for each WooCommerce REST API endpoint
|
||||
* so most of the logic is shared.
|
||||
*
|
||||
* Forked from wp-cli/restful (by Daniel Bachhuber, released under the MIT license https://opensource.org/licenses/MIT).
|
||||
* https://github.com/wp-cli/restful
|
||||
*
|
||||
* @version 3.0.0
|
||||
* @package WooCommerce
|
||||
*/
|
||||
class WC_CLI_REST_Command {
|
||||
/**
|
||||
* Endpoints that have a parent ID.
|
||||
* Ex: Product reviews, which has a product ID and a review ID.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routes_with_parent_id = array(
|
||||
'customer_download',
|
||||
'product_review',
|
||||
'order_note',
|
||||
'shop_order_refund',
|
||||
);
|
||||
|
||||
/**
|
||||
* Name of command/endpoint object.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* Endpoint route.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $route;
|
||||
|
||||
/**
|
||||
* Main resource ID.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $resource_identifier;
|
||||
|
||||
/**
|
||||
* Schema for command.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $schema;
|
||||
|
||||
/**
|
||||
* List of supported IDs and their description (name => desc).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $supported_ids = array();
|
||||
|
||||
/**
|
||||
* Sets up REST Command.
|
||||
*
|
||||
* @param string $name Name of endpoint object (comes from schema).
|
||||
* @param string $route Path to route of this endpoint.
|
||||
* @param array $schema Schema object.
|
||||
*/
|
||||
public function __construct( $name, $route, $schema ) {
|
||||
$this->name = $name;
|
||||
|
||||
preg_match_all( '#\([^\)]+\)#', $route, $matches );
|
||||
$first_match = $matches[0];
|
||||
$resource_id = ! empty( $matches[0] ) ? array_pop( $matches[0] ) : null;
|
||||
$this->route = rtrim( $route );
|
||||
$this->schema = $schema;
|
||||
|
||||
$this->resource_identifier = $resource_id;
|
||||
if ( in_array( $name, $this->routes_with_parent_id, true ) ) {
|
||||
$is_singular = substr( $this->route, - strlen( $resource_id ) ) === $resource_id;
|
||||
if ( ! $is_singular ) {
|
||||
$this->resource_identifier = $first_match[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes supported ID arguments (things like product_id, order_id, etc) that we should look for in addition to id.
|
||||
*
|
||||
* @param array $supported_ids List of supported IDs.
|
||||
*/
|
||||
public function set_supported_ids( $supported_ids = array() ) {
|
||||
$this->supported_ids = $supported_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ID of supported ID arguments (things like product_id, order_id, etc) that we should look for in addition to id.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_supported_ids() {
|
||||
return $this->supported_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new item.
|
||||
*
|
||||
* @subcommand create
|
||||
*
|
||||
* @param array $args WP-CLI positional arguments.
|
||||
* @param array $assoc_args WP-CLI associative arguments.
|
||||
*/
|
||||
public function create_item( $args, $assoc_args ) {
|
||||
$assoc_args = self::decode_json( $assoc_args );
|
||||
list( $status, $body ) = $this->do_request( 'POST', $this->get_filled_route( $args ), $assoc_args );
|
||||
if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'porcelain' ) ) {
|
||||
WP_CLI::line( $body['id'] );
|
||||
} else {
|
||||
WP_CLI::success( "Created {$this->name} {$body['id']}." );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing item.
|
||||
*
|
||||
* @subcommand delete
|
||||
*
|
||||
* @param array $args WP-CLI positional arguments.
|
||||
* @param array $assoc_args WP-CLI associative arguments.
|
||||
*/
|
||||
public function delete_item( $args, $assoc_args ) {
|
||||
list( $status, $body ) = $this->do_request( 'DELETE', $this->get_filled_route( $args ), $assoc_args );
|
||||
$object_id = isset( $body['id'] ) ? $body['id'] : '';
|
||||
if ( ! $object_id && isset( $body['slug'] ) ) {
|
||||
$object_id = $body['slug'];
|
||||
}
|
||||
|
||||
if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'porcelain' ) ) {
|
||||
WP_CLI::line( $object_id );
|
||||
} else {
|
||||
if ( empty( $assoc_args['force'] ) ) {
|
||||
WP_CLI::success( __( 'Trashed', 'woocommerce' ) . " {$this->name} {$object_id}" );
|
||||
} else {
|
||||
WP_CLI::success( __( 'Deleted', 'woocommerce' ) . " {$this->name} {$object_id}." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single item.
|
||||
*
|
||||
* @subcommand get
|
||||
*
|
||||
* @param array $args WP-CLI positional arguments.
|
||||
* @param array $assoc_args WP-CLI associative arguments.
|
||||
*/
|
||||
public function get_item( $args, $assoc_args ) {
|
||||
$route = $this->get_filled_route( $args );
|
||||
list( $status, $body, $headers ) = $this->do_request( 'GET', $route, $assoc_args );
|
||||
|
||||
if ( ! empty( $assoc_args['fields'] ) ) {
|
||||
$body = self::limit_item_to_fields( $body, $assoc_args['fields'] );
|
||||
}
|
||||
|
||||
if ( empty( $assoc_args['format'] ) ) {
|
||||
$assoc_args['format'] = 'table';
|
||||
}
|
||||
|
||||
if ( 'headers' === $assoc_args['format'] ) {
|
||||
echo wp_json_encode( $headers );
|
||||
} elseif ( 'body' === $assoc_args['format'] ) {
|
||||
echo wp_json_encode( $body );
|
||||
} elseif ( 'envelope' === $assoc_args['format'] ) {
|
||||
echo wp_json_encode(
|
||||
array(
|
||||
'body' => $body,
|
||||
'headers' => $headers,
|
||||
'status' => $status,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$formatter = $this->get_formatter( $assoc_args );
|
||||
$formatter->display_item( $body );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all items.
|
||||
*
|
||||
* @subcommand list
|
||||
*
|
||||
* @param array $args WP-CLI positional arguments.
|
||||
* @param array $assoc_args WP-CLI associative arguments.
|
||||
*/
|
||||
public function list_items( $args, $assoc_args ) {
|
||||
if ( ! empty( $assoc_args['format'] ) && 'count' === $assoc_args['format'] ) {
|
||||
$method = 'HEAD';
|
||||
} else {
|
||||
$method = 'GET';
|
||||
}
|
||||
|
||||
if ( ! isset( $assoc_args['per_page'] ) || empty( $assoc_args['per_page'] ) ) {
|
||||
$assoc_args['per_page'] = '100';
|
||||
}
|
||||
|
||||
list( $status, $body, $headers ) = $this->do_request( $method, $this->get_filled_route( $args ), $assoc_args );
|
||||
if ( ! empty( $assoc_args['format'] ) && 'ids' === $assoc_args['format'] ) {
|
||||
$items = array_column( $body, 'id' );
|
||||
} else {
|
||||
$items = $body;
|
||||
}
|
||||
|
||||
if ( ! empty( $assoc_args['fields'] ) ) {
|
||||
foreach ( $items as $key => $item ) {
|
||||
$items[ $key ] = self::limit_item_to_fields( $item, $assoc_args['fields'] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $assoc_args['format'] ) ) {
|
||||
$assoc_args['format'] = 'table';
|
||||
}
|
||||
|
||||
if ( ! empty( $assoc_args['format'] ) && 'count' === $assoc_args['format'] ) {
|
||||
echo (int) $headers['X-WP-Total'];
|
||||
} elseif ( 'headers' === $assoc_args['format'] ) {
|
||||
echo wp_json_encode( $headers );
|
||||
} elseif ( 'body' === $assoc_args['format'] ) {
|
||||
echo wp_json_encode( $body );
|
||||
} elseif ( 'envelope' === $assoc_args['format'] ) {
|
||||
echo wp_json_encode(
|
||||
array(
|
||||
'body' => $body,
|
||||
'headers' => $headers,
|
||||
'status' => $status,
|
||||
'api_url' => $this->api_url,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$formatter = $this->get_formatter( $assoc_args );
|
||||
$formatter->display_items( $items );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing item.
|
||||
*
|
||||
* @subcommand update
|
||||
*
|
||||
* @param array $args WP-CLI positional arguments.
|
||||
* @param array $assoc_args WP-CLI associative arguments.
|
||||
*/
|
||||
public function update_item( $args, $assoc_args ) {
|
||||
$assoc_args = self::decode_json( $assoc_args );
|
||||
list( $status, $body ) = $this->do_request( 'POST', $this->get_filled_route( $args ), $assoc_args );
|
||||
if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'porcelain' ) ) {
|
||||
WP_CLI::line( $body['id'] );
|
||||
} else {
|
||||
WP_CLI::success( __( 'Updated', 'woocommerce' ) . " {$this->name} {$body['id']}." );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a REST Request
|
||||
*
|
||||
* @param string $method Request method. Examples: 'POST', 'PUT', 'DELETE' or 'GET'.
|
||||
* @param string $route Resource route.
|
||||
* @param array $assoc_args Associative arguments passed to the originating WP-CLI command.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function do_request( $method, $route, $assoc_args ) {
|
||||
wc_maybe_define_constant( 'REST_REQUEST', true );
|
||||
|
||||
$request = new WP_REST_Request( $method, $route );
|
||||
if ( in_array( $method, array( 'POST', 'PUT' ), true ) ) {
|
||||
$request->set_body_params( $assoc_args );
|
||||
} else {
|
||||
foreach ( $assoc_args as $key => $value ) {
|
||||
$request->set_param( $key, $value );
|
||||
}
|
||||
}
|
||||
if ( Constants::is_true( 'SAVEQUERIES' ) ) {
|
||||
$original_queries = is_array( $GLOBALS['wpdb']->queries ) ? array_keys( $GLOBALS['wpdb']->queries ) : array();
|
||||
}
|
||||
$response = rest_do_request( $request );
|
||||
if ( Constants::is_true( 'SAVEQUERIES' ) ) {
|
||||
$performed_queries = array();
|
||||
foreach ( (array) $GLOBALS['wpdb']->queries as $key => $query ) {
|
||||
if ( in_array( $key, $original_queries, true ) ) {
|
||||
continue;
|
||||
}
|
||||
$performed_queries[] = $query;
|
||||
}
|
||||
usort(
|
||||
$performed_queries,
|
||||
function( $a, $b ) {
|
||||
if ( $a[1] === $b[1] ) {
|
||||
return 0;
|
||||
}
|
||||
return ( $a[1] > $b[1] ) ? -1 : 1;
|
||||
}
|
||||
);
|
||||
|
||||
$query_count = count( $performed_queries );
|
||||
$query_total_time = 0;
|
||||
foreach ( $performed_queries as $query ) {
|
||||
$query_total_time += $query[1];
|
||||
}
|
||||
$slow_query_message = '';
|
||||
if ( $performed_queries && 'wc' === WP_CLI::get_config( 'debug' ) ) {
|
||||
$slow_query_message .= '. Ordered by slowness, the queries are:' . PHP_EOL;
|
||||
foreach ( $performed_queries as $i => $query ) {
|
||||
$i++;
|
||||
$bits = explode( ', ', $query[2] );
|
||||
$backtrace = implode( ', ', array_slice( $bits, 13 ) );
|
||||
$seconds = NumberUtil::round( $query[1], 6 );
|
||||
$slow_query_message .= <<<EOT
|
||||
{$i}:
|
||||
- {$seconds} seconds
|
||||
- {$backtrace}
|
||||
- {$query[0]}
|
||||
EOT;
|
||||
$slow_query_message .= PHP_EOL;
|
||||
}
|
||||
} elseif ( 'wc' !== WP_CLI::get_config( 'debug' ) ) {
|
||||
$slow_query_message = '. Use --debug=wc to see all queries.';
|
||||
}
|
||||
$query_total_time = NumberUtil::round( $query_total_time, 6 );
|
||||
WP_CLI::debug( "wc command executed {$query_count} queries in {$query_total_time} seconds{$slow_query_message}", 'wc' );
|
||||
}
|
||||
|
||||
$error = $response->as_error();
|
||||
|
||||
if ( $error ) {
|
||||
// For authentication errors (status 401), include a reminder to set the --user flag.
|
||||
// WP_CLI::error will only return the first message from WP_Error, so we will pass a string containing both instead.
|
||||
if ( 401 === $response->get_status() ) {
|
||||
$errors = $error->get_error_messages();
|
||||
$errors[] = __( 'Make sure to include the --user flag with an account that has permissions for this action.', 'woocommerce' ) . ' {"status":401}';
|
||||
$error = implode( "\n", $errors );
|
||||
}
|
||||
WP_CLI::error( $error );
|
||||
}
|
||||
return array( $response->get_status(), $response->get_data(), $response->get_headers() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Formatter object based on supplied parameters.
|
||||
*
|
||||
* @param array $assoc_args Parameters passed to command. Determines formatting.
|
||||
* @return \WP_CLI\Formatter
|
||||
*/
|
||||
protected function get_formatter( &$assoc_args ) {
|
||||
if ( ! empty( $assoc_args['fields'] ) ) {
|
||||
if ( is_string( $assoc_args['fields'] ) ) {
|
||||
$fields = explode( ',', $assoc_args['fields'] );
|
||||
} else {
|
||||
$fields = $assoc_args['fields'];
|
||||
}
|
||||
} else {
|
||||
if ( ! empty( $assoc_args['context'] ) ) {
|
||||
$fields = $this->get_context_fields( $assoc_args['context'] );
|
||||
} else {
|
||||
$fields = $this->get_context_fields( 'view' );
|
||||
}
|
||||
}
|
||||
return new \WP_CLI\Formatter( $assoc_args, $fields );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of fields present in a given context
|
||||
*
|
||||
* @param string $context Scope under which the request is made. Determines fields present in response.
|
||||
* @return array
|
||||
*/
|
||||
private function get_context_fields( $context ) {
|
||||
$fields = array();
|
||||
foreach ( $this->schema['properties'] as $key => $args ) {
|
||||
if ( empty( $args['context'] ) || in_array( $context, $args['context'], true ) ) {
|
||||
$fields[] = $key;
|
||||
}
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route for this resource
|
||||
*
|
||||
* @param array $args Positional arguments passed to the originating WP-CLI command.
|
||||
* @return string
|
||||
*/
|
||||
private function get_filled_route( $args = array() ) {
|
||||
$supported_id_matched = false;
|
||||
$route = $this->route;
|
||||
|
||||
foreach ( $this->get_supported_ids() as $id_name => $id_desc ) {
|
||||
if ( 'id' !== $id_name && strpos( $route, '<' . $id_name . '>' ) !== false && ! empty( $args ) ) {
|
||||
$route = str_replace( array( '(?P<' . $id_name . '>[\d]+)', '(?P<' . $id_name . '>\w[\w\s\-]*)' ), $args[0], $route );
|
||||
$supported_id_matched = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! empty( $args ) ) {
|
||||
$id_replacement = $supported_id_matched && ! empty( $args[1] ) ? $args[1] : $args[0];
|
||||
$route = str_replace( array( '(?P<id>[\d]+)', '(?P<id>[\w-]+)' ), $id_replacement, $route );
|
||||
}
|
||||
|
||||
return rtrim( $route );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduce an item to specific fields.
|
||||
*
|
||||
* @param array $item Item to reduce.
|
||||
* @param array $fields Fields to keep.
|
||||
* @return array
|
||||
*/
|
||||
private static function limit_item_to_fields( $item, $fields ) {
|
||||
if ( empty( $fields ) ) {
|
||||
return $item;
|
||||
}
|
||||
if ( is_string( $fields ) ) {
|
||||
$fields = explode( ',', $fields );
|
||||
}
|
||||
foreach ( $item as $i => $field ) {
|
||||
if ( ! in_array( $i, $fields, true ) ) {
|
||||
unset( $item[ $i ] );
|
||||
}
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON can be passed in some more complicated objects, like the payment gateway settings array.
|
||||
* This function decodes the json (if present) and tries to get it's value.
|
||||
*
|
||||
* @param array $arr Array that will be scanned for JSON encoded values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function decode_json( $arr ) {
|
||||
foreach ( $arr as $key => $value ) {
|
||||
if ( '[' === substr( $value, 0, 1 ) || '{' === substr( $value, 0, 1 ) ) {
|
||||
$arr[ $key ] = json_decode( $value, true );
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
}
|
254
includes/cli/class-wc-cli-runner.php
Normal file
254
includes/cli/class-wc-cli-runner.php
Normal file
@ -0,0 +1,254 @@
|
||||
<?php
|
||||
/**
|
||||
* WP_CLI_Runner class file.
|
||||
*
|
||||
* @package WooCommerce\CLI
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* WC API to WC CLI Bridge.
|
||||
*
|
||||
* Hooks into the REST API, figures out which endpoints come from WC,
|
||||
* and registers them as CLI commands.
|
||||
*
|
||||
* Forked from wp-cli/restful (by Daniel Bachhuber, released under the MIT license https://opensource.org/licenses/MIT).
|
||||
* https://github.com/wp-cli/restful
|
||||
*
|
||||
* @version 3.0.0
|
||||
* @package WooCommerce
|
||||
*/
|
||||
class WC_CLI_Runner {
|
||||
/**
|
||||
* Endpoints to disable (meaning they will not be available as CLI commands).
|
||||
* Some of these can either be done via WP already, or are offered with
|
||||
* some other changes (like tools).
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $disabled_endpoints = array(
|
||||
'settings',
|
||||
'settings/(?P<group_id>[\w-]+)',
|
||||
'settings/(?P<group_id>[\w-]+)/batch',
|
||||
'settings/(?P<group_id>[\w-]+)/(?P<id>[\w-]+)',
|
||||
'system_status',
|
||||
'system_status/tools',
|
||||
'system_status/tools/(?P<id>[\w-]+)',
|
||||
'reports',
|
||||
'reports/sales',
|
||||
'reports/top_sellers',
|
||||
);
|
||||
|
||||
/**
|
||||
* The version of the REST API we should target to
|
||||
* generate commands.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $target_rest_version = 'v2';
|
||||
|
||||
/**
|
||||
* Register's all endpoints as commands once WP and WC have all loaded.
|
||||
*/
|
||||
public static function after_wp_load() {
|
||||
global $wp_rest_server;
|
||||
$wp_rest_server = new WP_REST_Server();
|
||||
do_action( 'rest_api_init', $wp_rest_server );
|
||||
|
||||
$request = new WP_REST_Request( 'GET', '/' );
|
||||
$request->set_param( 'context', 'help' );
|
||||
$response = $wp_rest_server->dispatch( $request );
|
||||
$response_data = $response->get_data();
|
||||
if ( empty( $response_data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through all of our endpoints and register any valid WC endpoints.
|
||||
foreach ( $response_data['routes'] as $route => $route_data ) {
|
||||
// Only register endpoints for WC and our target version.
|
||||
if ( substr( $route, 0, 4 + strlen( self::$target_rest_version ) ) !== '/wc/' . self::$target_rest_version ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only register endpoints with schemas.
|
||||
if ( empty( $route_data['schema']['title'] ) ) {
|
||||
/* translators: %s: Route to a given WC-API endpoint */
|
||||
WP_CLI::debug( sprintf( __( 'No schema title found for %s, skipping REST command registration.', 'woocommerce' ), $route ), 'wc' );
|
||||
continue;
|
||||
}
|
||||
// Ignore batch endpoints.
|
||||
if ( 'batch' === $route_data['schema']['title'] ) {
|
||||
continue;
|
||||
}
|
||||
// Disable specific endpoints.
|
||||
$route_pieces = explode( '/', $route );
|
||||
$endpoint_piece = str_replace( '/wc/' . $route_pieces[2] . '/', '', $route );
|
||||
if ( in_array( $endpoint_piece, self::$disabled_endpoints, true ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
self::register_route_commands( new WC_CLI_REST_Command( $route_data['schema']['title'], $route, $route_data['schema'] ), $route, $route_data );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates command information and tells WP CLI about all
|
||||
* commands available from a route.
|
||||
*
|
||||
* @param string $rest_command WC-API command.
|
||||
* @param string $route Path to route endpoint.
|
||||
* @param array $route_data Command data.
|
||||
* @param array $command_args WP-CLI command arguments.
|
||||
*/
|
||||
private static function register_route_commands( $rest_command, $route, $route_data, $command_args = array() ) {
|
||||
// Define IDs that we are looking for in the routes (in addition to id)
|
||||
// so that we can pass it to the rest command, and use it here to generate documentation.
|
||||
$supported_ids = array(
|
||||
'product_id' => __( 'Product ID.', 'woocommerce' ),
|
||||
'customer_id' => __( 'Customer ID.', 'woocommerce' ),
|
||||
'order_id' => __( 'Order ID.', 'woocommerce' ),
|
||||
'refund_id' => __( 'Refund ID.', 'woocommerce' ),
|
||||
'attribute_id' => __( 'Attribute ID.', 'woocommerce' ),
|
||||
'zone_id' => __( 'Zone ID.', 'woocommerce' ),
|
||||
'instance_id' => __( 'Instance ID.', 'woocommerce' ),
|
||||
'id' => __( 'The ID for the resource.', 'woocommerce' ),
|
||||
'slug' => __( 'The slug for the resource.', 'woocommerce' ),
|
||||
);
|
||||
$rest_command->set_supported_ids( $supported_ids );
|
||||
$positional_args = array_keys( $supported_ids );
|
||||
$parent = "wc {$route_data['schema']['title']}";
|
||||
$supported_commands = array();
|
||||
|
||||
// Get a list of supported commands for each route.
|
||||
foreach ( $route_data['endpoints'] as $endpoint ) {
|
||||
preg_match_all( '#\([^\)]+\)#', $route, $matches );
|
||||
$resource_id = ! empty( $matches[0] ) ? array_pop( $matches[0] ) : null;
|
||||
$trimmed_route = rtrim( $route );
|
||||
$is_singular = substr( $trimmed_route, - strlen( $resource_id ) ) === $resource_id;
|
||||
|
||||
// List a collection.
|
||||
if ( array( 'GET' ) === $endpoint['methods'] && ! $is_singular ) {
|
||||
$supported_commands['list'] = ! empty( $endpoint['args'] ) ? $endpoint['args'] : array();
|
||||
}
|
||||
// Create a specific resource.
|
||||
if ( array( 'POST' ) === $endpoint['methods'] && ! $is_singular ) {
|
||||
$supported_commands['create'] = ! empty( $endpoint['args'] ) ? $endpoint['args'] : array();
|
||||
}
|
||||
// Get a specific resource.
|
||||
if ( array( 'GET' ) === $endpoint['methods'] && $is_singular ) {
|
||||
$supported_commands['get'] = ! empty( $endpoint['args'] ) ? $endpoint['args'] : array();
|
||||
}
|
||||
// Update a specific resource.
|
||||
if ( in_array( 'POST', $endpoint['methods'], true ) && $is_singular ) {
|
||||
$supported_commands['update'] = ! empty( $endpoint['args'] ) ? $endpoint['args'] : array();
|
||||
}
|
||||
// Delete a specific resource.
|
||||
if ( array( 'DELETE' ) === $endpoint['methods'] && $is_singular ) {
|
||||
$supported_commands['delete'] = ! empty( $endpoint['args'] ) ? $endpoint['args'] : array();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $supported_commands as $command => $endpoint_args ) {
|
||||
$synopsis = array();
|
||||
$arg_regs = array();
|
||||
$ids = array();
|
||||
|
||||
foreach ( $supported_ids as $id_name => $id_desc ) {
|
||||
if ( strpos( $route, '<' . $id_name . '>' ) !== false ) {
|
||||
$synopsis[] = array(
|
||||
'name' => $id_name,
|
||||
'type' => 'positional',
|
||||
'description' => $id_desc,
|
||||
'optional' => false,
|
||||
);
|
||||
$ids[] = $id_name;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $endpoint_args as $name => $args ) {
|
||||
if ( ! in_array( $name, $positional_args, true ) || strpos( $route, '<' . $id_name . '>' ) === false ) {
|
||||
$arg_regs[] = array(
|
||||
'name' => $name,
|
||||
'type' => 'assoc',
|
||||
'description' => ! empty( $args['description'] ) ? $args['description'] : '',
|
||||
'optional' => empty( $args['required'] ),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $arg_regs as $arg_reg ) {
|
||||
$synopsis[] = $arg_reg;
|
||||
}
|
||||
|
||||
if ( in_array( $command, array( 'list', 'get' ), true ) ) {
|
||||
$synopsis[] = array(
|
||||
'name' => 'fields',
|
||||
'type' => 'assoc',
|
||||
'description' => __( 'Limit response to specific fields. Defaults to all fields.', 'woocommerce' ),
|
||||
'optional' => true,
|
||||
);
|
||||
$synopsis[] = array(
|
||||
'name' => 'field',
|
||||
'type' => 'assoc',
|
||||
'description' => __( 'Get the value of an individual field.', 'woocommerce' ),
|
||||
'optional' => true,
|
||||
);
|
||||
$synopsis[] = array(
|
||||
'name' => 'format',
|
||||
'type' => 'assoc',
|
||||
'description' => __( 'Render response in a particular format.', 'woocommerce' ),
|
||||
'optional' => true,
|
||||
'default' => 'table',
|
||||
'options' => array(
|
||||
'table',
|
||||
'json',
|
||||
'csv',
|
||||
'ids',
|
||||
'yaml',
|
||||
'count',
|
||||
'headers',
|
||||
'body',
|
||||
'envelope',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if ( in_array( $command, array( 'create', 'update', 'delete' ), true ) ) {
|
||||
$synopsis[] = array(
|
||||
'name' => 'porcelain',
|
||||
'type' => 'flag',
|
||||
'description' => __( 'Output just the id when the operation is successful.', 'woocommerce' ),
|
||||
'optional' => true,
|
||||
);
|
||||
}
|
||||
|
||||
$methods = array(
|
||||
'list' => 'list_items',
|
||||
'create' => 'create_item',
|
||||
'delete' => 'delete_item',
|
||||
'get' => 'get_item',
|
||||
'update' => 'update_item',
|
||||
);
|
||||
|
||||
$before_invoke = null;
|
||||
if ( empty( $command_args['when'] ) && \WP_CLI::get_config( 'debug' ) ) {
|
||||
$before_invoke = function() {
|
||||
wc_maybe_define_constant( 'SAVEQUERIES', true );
|
||||
};
|
||||
}
|
||||
|
||||
WP_CLI::add_command(
|
||||
"{$parent} {$command}",
|
||||
array( $rest_command, $methods[ $command ] ),
|
||||
array(
|
||||
'synopsis' => $synopsis,
|
||||
'when' => ! empty( $command_args['when'] ) ? $command_args['when'] : '',
|
||||
'before_invoke' => $before_invoke,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
107
includes/cli/class-wc-cli-tool-command.php
Normal file
107
includes/cli/class-wc-cli-tool-command.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/**
|
||||
* WC_CLI_Tool_Command class file.
|
||||
*
|
||||
* @package WooCommerce\CLI
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks up our system status tools to the CLI.
|
||||
*
|
||||
* Forked from wp-cli/restful (by Daniel Bachhuber, released under the MIT license https://opensource.org/licenses/MIT).
|
||||
* https://github.com/wp-cli/restful
|
||||
*
|
||||
* @version 3.0.0
|
||||
* @package WooCommerce
|
||||
*/
|
||||
class WC_CLI_Tool_Command {
|
||||
|
||||
/**
|
||||
* Registers just a 'list' and 'run' command to the WC CLI
|
||||
* since we only want to enable certain actions on the system status
|
||||
* tools endpoints.
|
||||
*/
|
||||
public static function register_commands() {
|
||||
global $wp_rest_server;
|
||||
|
||||
$request = new WP_REST_Request( 'OPTIONS', '/wc/v2/system_status/tools' );
|
||||
$response = $wp_rest_server->dispatch( $request );
|
||||
$response_data = $response->get_data();
|
||||
if ( empty( $response_data ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent = 'wc tool';
|
||||
$supported_commands = array( 'list', 'run' );
|
||||
foreach ( $supported_commands as $command ) {
|
||||
$synopsis = array();
|
||||
if ( 'run' === $command ) {
|
||||
$synopsis[] = array(
|
||||
'name' => 'id',
|
||||
'type' => 'positional',
|
||||
'description' => __( 'The id for the resource.', 'woocommerce' ),
|
||||
'optional' => false,
|
||||
);
|
||||
$method = 'update_item';
|
||||
$route = '/wc/v2/system_status/tools/(?P<id>[\w-]+)';
|
||||
} elseif ( 'list' === $command ) {
|
||||
$synopsis[] = array(
|
||||
'name' => 'fields',
|
||||
'type' => 'assoc',
|
||||
'description' => __( 'Limit response to specific fields. Defaults to all fields.', 'woocommerce' ),
|
||||
'optional' => true,
|
||||
);
|
||||
$synopsis[] = array(
|
||||
'name' => 'field',
|
||||
'type' => 'assoc',
|
||||
'description' => __( 'Get the value of an individual field.', 'woocommerce' ),
|
||||
'optional' => true,
|
||||
);
|
||||
$synopsis[] = array(
|
||||
'name' => 'format',
|
||||
'type' => 'assoc',
|
||||
'description' => __( 'Render response in a particular format.', 'woocommerce' ),
|
||||
'optional' => true,
|
||||
'default' => 'table',
|
||||
'options' => array(
|
||||
'table',
|
||||
'json',
|
||||
'csv',
|
||||
'ids',
|
||||
'yaml',
|
||||
'count',
|
||||
'headers',
|
||||
'body',
|
||||
'envelope',
|
||||
),
|
||||
);
|
||||
$method = 'list_items';
|
||||
$route = '/wc/v2/system_status/tools';
|
||||
}
|
||||
|
||||
$before_invoke = null;
|
||||
if ( empty( $command_args['when'] ) && WP_CLI::get_config( 'debug' ) ) {
|
||||
$before_invoke = function() {
|
||||
wc_maybe_define_constant( 'SAVEQUERIES', true );
|
||||
};
|
||||
}
|
||||
|
||||
$rest_command = new WC_CLI_REST_Command( 'system_status_tool', $route, $response_data['schema'] );
|
||||
|
||||
WP_CLI::add_command(
|
||||
"{$parent} {$command}",
|
||||
array( $rest_command, $method ),
|
||||
array(
|
||||
'synopsis' => $synopsis,
|
||||
'when' => ! empty( $command_args['when'] ) ? $command_args['when'] : '',
|
||||
'before_invoke' => $before_invoke,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
55
includes/cli/class-wc-cli-tracker-command.php
Normal file
55
includes/cli/class-wc-cli-tracker-command.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
* WC_CLI_Tracker_Command class file.
|
||||
*
|
||||
* @package WooCommerce\CLI
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows access to tracker snapshot for transparency and debugging.
|
||||
*
|
||||
* @since 5.5.0
|
||||
* @package WooCommerce
|
||||
*/
|
||||
class WC_CLI_Tracker_Command {
|
||||
|
||||
/**
|
||||
* Registers a command for showing WooCommerce Tracker snapshot data.
|
||||
*/
|
||||
public static function register_commands() {
|
||||
WP_CLI::add_command( 'wc tracker snapshot', array( 'WC_CLI_Tracker_Command', 'show_tracker_snapshot' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump tracker snapshot data to screen.
|
||||
*
|
||||
* ## EXAMPLES
|
||||
*
|
||||
* wp wc tracker snapshot --format=yaml
|
||||
* wp wc tracker snapshot --format=json
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [--format=<format>]
|
||||
* : Render output in a particular format, see WP_CLI\Formatter for details.
|
||||
*
|
||||
* @see \WP_CLI\Formatter
|
||||
* @see WC_Tracker::get_tracking_data()
|
||||
* @param array $args WP-CLI positional arguments.
|
||||
* @param array $assoc_args WP-CLI associative arguments.
|
||||
*/
|
||||
public static function show_tracker_snapshot( $args, $assoc_args ) {
|
||||
$snapshot_data = WC_Tracker::get_tracking_data();
|
||||
|
||||
$formatter = new \WP_CLI\Formatter(
|
||||
$assoc_args,
|
||||
array_keys( $snapshot_data )
|
||||
);
|
||||
|
||||
$formatter->display_items( array( $snapshot_data ) );
|
||||
}
|
||||
}
|
81
includes/cli/class-wc-cli-update-command.php
Normal file
81
includes/cli/class-wc-cli-update-command.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* WC_CLI_Update_Command class file.
|
||||
*
|
||||
* @package WooCommerce\CLI
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows updates via CLI.
|
||||
*
|
||||
* @version 3.0.0
|
||||
* @package WooCommerce
|
||||
*/
|
||||
class WC_CLI_Update_Command {
|
||||
|
||||
/**
|
||||
* Registers the update command.
|
||||
*/
|
||||
public static function register_commands() {
|
||||
WP_CLI::add_command( 'wc update', array( 'WC_CLI_Update_Command', 'update' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all pending WooCommerce database updates.
|
||||
*/
|
||||
public static function update() {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->hide_errors();
|
||||
|
||||
include_once WC_ABSPATH . 'includes/class-wc-install.php';
|
||||
include_once WC_ABSPATH . 'includes/wc-update-functions.php';
|
||||
|
||||
$current_db_version = get_option( 'woocommerce_db_version' );
|
||||
$update_count = 0;
|
||||
$callbacks = WC_Install::get_db_update_callbacks();
|
||||
$callbacks_to_run = array();
|
||||
|
||||
foreach ( $callbacks as $version => $update_callbacks ) {
|
||||
if ( version_compare( $current_db_version, $version, '<' ) ) {
|
||||
foreach ( $update_callbacks as $update_callback ) {
|
||||
$callbacks_to_run[] = $update_callback;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $callbacks_to_run ) ) {
|
||||
// Ensure DB version is set to the current WC version to match WP-Admin update routine.
|
||||
WC_Install::update_db_version();
|
||||
/* translators: %s Database version number */
|
||||
WP_CLI::success( sprintf( __( 'No updates required. Database version is %s', 'woocommerce' ), get_option( 'woocommerce_db_version' ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
/* translators: 1: Number of database updates 2: List of update callbacks */
|
||||
WP_CLI::log( sprintf( __( 'Found %1$d updates (%2$s)', 'woocommerce' ), count( $callbacks_to_run ), implode( ', ', $callbacks_to_run ) ) );
|
||||
|
||||
$progress = \WP_CLI\Utils\make_progress_bar( __( 'Updating database', 'woocommerce' ), count( $callbacks_to_run ) ); // phpcs:ignore PHPCompatibility.LanguageConstructs.NewLanguageConstructs.t_ns_separatorFound
|
||||
|
||||
foreach ( $callbacks_to_run as $update_callback ) {
|
||||
call_user_func( $update_callback );
|
||||
$result = false;
|
||||
while ( $result ) {
|
||||
$result = (bool) call_user_func( $update_callback );
|
||||
}
|
||||
$update_count ++;
|
||||
$progress->tick();
|
||||
}
|
||||
|
||||
$progress->finish();
|
||||
|
||||
WC_Admin_Notices::remove_notice( 'update', true );
|
||||
|
||||
/* translators: 1: Number of database updates performed 2: Database version number */
|
||||
WP_CLI::success( sprintf( __( '%1$d update functions completed. Database version is %2$s', 'woocommerce' ), absint( $update_count ), get_option( 'woocommerce_db_version' ) ) );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user