From 97baa5b5e8703b9c918e70f4a5eca9b1bfdee080 Mon Sep 17 00:00:00 2001
From: Erick Hitter <git-contrib@ethitter.com>
Date: Fri, 8 Sep 2017 14:58:17 -0700
Subject: [PATCH] CLI command to offload a CLI command

---
 includes/functions.php          | 20 ++++++++++++-----
 includes/schedule.php           | 38 ++++++++++++++++++++++++++++++++-
 wp-cli-cron-control-offload.php |  1 +
 3 files changed, 53 insertions(+), 6 deletions(-)

diff --git a/includes/functions.php b/includes/functions.php
index 94c175b..68a96c0 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -1,13 +1,14 @@
 <?php
 
 namespace Automattic\WP\WP_CLI_Cron_Control_Offload;
+use WP_Error;
 
 /**
  * Create cron event for a given WP-CLI command
  *
  * @param string $command
  * @param int $timestamp Optional.
- * @return bool|\WP_Error
+ * @return int|WP_Error
  */
 function schedule_cli_command( $command, $timestamp = null ) {
 	$command = validate_command( $command );
@@ -20,18 +21,26 @@ function schedule_cli_command( $command, $timestamp = null ) {
 		$timestamp = strtotime( '+30 seconds' );
 	}
 
+	if ( $timestamp <= time() ) {
+		return new WP_Error( 'invalid-timestamp', __( 'Timestamp is in the past.', 'wp-cli-cron-control-offload' ) );
+	}
+
 	$event_args = array( 'command' => $command, );
 
 	$scheduled = wp_schedule_single_event( $timestamp, ACTION, $event_args );
 
-	return false !== $scheduled;
+	if ( false === $scheduled ) {
+		return new WP_Error( 'not-scheduled', __( 'Command may already be scheduled, or it was blocked via the `schedule_event` filter.', 'wp-cli-cron-control-offload' ) );
+	}
+
+	return $timestamp;
 }
 
 /**
  * Validate WP-CLI command to be scheduled
  *
  * @param string $command
- * @return array|\WP_Error
+ * @return array|WP_Error
  */
 function validate_command( $command ) {
 	$command = trim( $command );
@@ -45,7 +54,7 @@ function validate_command( $command ) {
 	$first_command = explode( ' ', $command );
 	$first_command = array_shift( $first_command );
 	if ( ! is_command_allowed( $first_command ) ) {
-		return new \WP_Error( sprintf( __( '%1$s: `%2$s` not allowed', 'wp-cli-cron-control-offload' ), MESSAGE_PREFIX, $first_command ) );
+		return new WP_Error( 'blocked-command', sprintf( __( '`%1$s` not allowed', 'wp-cli-cron-control-offload' ), $first_command ) );
 	}
 
 	// Don't worry about the user WP-CLI runs as
@@ -57,7 +66,7 @@ function validate_command( $command ) {
 
 	// Nothing to run
 	if ( empty( $command ) ) {
-		return new \WP_Error( 'Invalid command provided' );
+		return new WP_Error( 'invalid-command', 'Invalid command provided' );
 	}
 
 	return $command;
@@ -118,6 +127,7 @@ function get_command_whitelist() {
 function get_command_blacklist() {
 	// TODO: constant!
 	return array(
+		CLI_NAMESPACE, // Don't support scheduling loops
 		'cli',
 		'config',
 		'core',
diff --git a/includes/schedule.php b/includes/schedule.php
index de83129..18df07a 100644
--- a/includes/schedule.php
+++ b/includes/schedule.php
@@ -6,4 +6,40 @@ if ( ! defined( 'WP_CLI' ) || ! \WP_CLI ) {
 	return false;
 }
 
-// TODO: WP-CLI command to schedule an event
+use WP_CLI;
+use WP_CLI_Command;
+
+/**
+ * Offload WP-CLI commands to cron
+ */
+class CLI extends WP_CLI_Command {
+	/**
+	 * Create an event to run a given WP-CLI command
+	 *
+	 * @subcommand create
+	 * @synopsis --command=<command> [--timestamp=<timestamp>]
+	 */
+	public function create( $args, $assoc_args ) {
+		$command = WP_CLI\Utils\get_flag_value( $assoc_args, 'command', '' );
+		$command = validate_command( $command );
+
+		if ( is_wp_error( $command ) ) {
+			WP_CLI::error( $command->get_error_message() );
+		}
+
+		$timestamp = WP_CLI\Utils\get_flag_value( $assoc_args, 'timestamp', null );
+		if ( is_numeric( $timestamp ) ) {
+			$timestamp = absint( $timestamp );
+		}
+
+		$scheduled = schedule_cli_command( $command, $timestamp );
+
+		if ( is_wp_error( $scheduled ) ) {
+			WP_CLI::error( $scheduled->get_error_message() );
+		}
+
+		WP_CLI::success( __( 'Command scheduled!', 'wp-cli-cron-control-offload' ) );
+	}
+}
+
+WP_CLI::add_command( CLI_NAMESPACE, __NAMESPACE__ . '\CLI' );
diff --git a/wp-cli-cron-control-offload.php b/wp-cli-cron-control-offload.php
index 40d30f9..9cdae62 100644
--- a/wp-cli-cron-control-offload.php
+++ b/wp-cli-cron-control-offload.php
@@ -15,6 +15,7 @@
 namespace Automattic\WP\WP_CLI_Cron_Control_Offload;
 
 const ACTION         = 'wp_cli_cron_control_offload';
+const CLI_NAMESPACE  = 'cli-cron-offload';
 const MESSAGE_PREFIX = 'WP-CLI via Cron';
 
 require_once __DIR__ . '/includes/functions.php';
-- 
GitLab