Your IP : 216.73.216.162


Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/
Upload File :
Current File : /home/x/b/o/xbodynamge/namtation/wp-content/migration.tar

Scheduler.php000066600000006050151142233530007177 0ustar00<?php


namespace Action_Scheduler\Migration;

/**
 * Class Scheduler
 *
 * @package Action_Scheduler\WP_CLI
 *
 * @since 3.0.0
 *
 * @codeCoverageIgnore
 */
class Scheduler {
	/** Migration action hook. */
	const HOOK = 'action_scheduler/migration_hook';

	/** Migration action group. */
	const GROUP = 'action-scheduler-migration';

	/**
	 * Set up the callback for the scheduled job.
	 */
	public function hook() {
		add_action( self::HOOK, array( $this, 'run_migration' ), 10, 0 );
	}

	/**
	 * Remove the callback for the scheduled job.
	 */
	public function unhook() {
		remove_action( self::HOOK, array( $this, 'run_migration' ), 10 );
	}

	/**
	 * The migration callback.
	 */
	public function run_migration() {
		$migration_runner = $this->get_migration_runner();
		$count            = $migration_runner->run( $this->get_batch_size() );

		if ( 0 === $count ) {
			$this->mark_complete();
		} else {
			$this->schedule_migration( time() + $this->get_schedule_interval() );
		}
	}

	/**
	 * Mark the migration complete.
	 */
	public function mark_complete() {
		$this->unschedule_migration();

		\ActionScheduler_DataController::mark_migration_complete();
		do_action( 'action_scheduler/migration_complete' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
	}

	/**
	 * Get a flag indicating whether the migration is scheduled.
	 *
	 * @return bool Whether there is a pending action in the store to handle the migration
	 */
	public function is_migration_scheduled() {
		$next = as_next_scheduled_action( self::HOOK );

		return ! empty( $next );
	}

	/**
	 * Schedule the migration.
	 *
	 * @param int $when Optional timestamp to run the next migration batch. Defaults to now.
	 *
	 * @return string The action ID
	 */
	public function schedule_migration( $when = 0 ) {
		$next = as_next_scheduled_action( self::HOOK );

		if ( ! empty( $next ) ) {
			return $next;
		}

		if ( empty( $when ) ) {
			$when = time() + MINUTE_IN_SECONDS;
		}

		return as_schedule_single_action( $when, self::HOOK, array(), self::GROUP );
	}

	/**
	 * Remove the scheduled migration action.
	 */
	public function unschedule_migration() {
		as_unschedule_action( self::HOOK, null, self::GROUP );
	}

	/**
	 * Get migration batch schedule interval.
	 *
	 * @return int Seconds between migration runs. Defaults to 0 seconds to allow chaining migration via Async Runners.
	 */
	private function get_schedule_interval() {
		return (int) apply_filters( 'action_scheduler/migration_interval', 0 ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
	}

	/**
	 * Get migration batch size.
	 *
	 * @return int Number of actions to migrate in each batch. Defaults to 250.
	 */
	private function get_batch_size() {
		return (int) apply_filters( 'action_scheduler/migration_batch_size', 250 ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
	}

	/**
	 * Get migration runner object.
	 *
	 * @return Runner
	 */
	private function get_migration_runner() {
		$config = Controller::instance()->get_migration_config_object();

		return new Runner( $config );
	}

}
Config.php000066600000010156151142233530006470 0ustar00<?php


namespace Action_Scheduler\Migration;

use Action_Scheduler\WP_CLI\ProgressBar;
use ActionScheduler_Logger as Logger;
use ActionScheduler_Store as Store;

/**
 * Class Config
 *
 * @package Action_Scheduler\Migration
 *
 * @since 3.0.0
 *
 * A config builder for the ActionScheduler\Migration\Runner class
 */
class Config {
	/**
	 * Source store instance.
	 *
	 * @var ActionScheduler_Store
	 */
	private $source_store;

	/**
	 * Source logger instance.
	 *
	 * @var ActionScheduler_Logger
	 */
	private $source_logger;

	/**
	 * Destination store instance.
	 *
	 * @var ActionScheduler_Store
	 */
	private $destination_store;

	/**
	 * Destination logger instance.
	 *
	 * @var ActionScheduler_Logger
	 */
	private $destination_logger;

	/**
	 * Progress bar object.
	 *
	 * @var Action_Scheduler\WP_CLI\ProgressBar
	 */
	private $progress_bar;

	/**
	 * Flag indicating a dryrun.
	 *
	 * @var bool
	 */
	private $dry_run = false;

	/**
	 * Config constructor.
	 */
	public function __construct() {

	}

	/**
	 * Get the configured source store.
	 *
	 * @return ActionScheduler_Store
	 * @throws \RuntimeException When source store is not configured.
	 */
	public function get_source_store() {
		if ( empty( $this->source_store ) ) {
			throw new \RuntimeException( __( 'Source store must be configured before running a migration', 'action-scheduler' ) );
		}

		return $this->source_store;
	}

	/**
	 * Set the configured source store.
	 *
	 * @param ActionScheduler_Store $store Source store object.
	 */
	public function set_source_store( Store $store ) {
		$this->source_store = $store;
	}

	/**
	 * Get the configured source logger.
	 *
	 * @return ActionScheduler_Logger
	 * @throws \RuntimeException When source logger is not configured.
	 */
	public function get_source_logger() {
		if ( empty( $this->source_logger ) ) {
			throw new \RuntimeException( __( 'Source logger must be configured before running a migration', 'action-scheduler' ) );
		}

		return $this->source_logger;
	}

	/**
	 * Set the configured source logger.
	 *
	 * @param ActionScheduler_Logger $logger Logger object.
	 */
	public function set_source_logger( Logger $logger ) {
		$this->source_logger = $logger;
	}

	/**
	 * Get the configured destination store.
	 *
	 * @return ActionScheduler_Store
	 * @throws \RuntimeException When destination store is not configured.
	 */
	public function get_destination_store() {
		if ( empty( $this->destination_store ) ) {
			throw new \RuntimeException( __( 'Destination store must be configured before running a migration', 'action-scheduler' ) );
		}

		return $this->destination_store;
	}

	/**
	 * Set the configured destination store.
	 *
	 * @param ActionScheduler_Store $store Action store object.
	 */
	public function set_destination_store( Store $store ) {
		$this->destination_store = $store;
	}

	/**
	 * Get the configured destination logger.
	 *
	 * @return ActionScheduler_Logger
	 * @throws \RuntimeException When destination logger is not configured.
	 */
	public function get_destination_logger() {
		if ( empty( $this->destination_logger ) ) {
			throw new \RuntimeException( __( 'Destination logger must be configured before running a migration', 'action-scheduler' ) );
		}

		return $this->destination_logger;
	}

	/**
	 * Set the configured destination logger.
	 *
	 * @param ActionScheduler_Logger $logger Logger object.
	 */
	public function set_destination_logger( Logger $logger ) {
		$this->destination_logger = $logger;
	}

	/**
	 * Get flag indicating whether it's a dry run.
	 *
	 * @return bool
	 */
	public function get_dry_run() {
		return $this->dry_run;
	}

	/**
	 * Set flag indicating whether it's a dry run.
	 *
	 * @param bool $dry_run Dry run toggle.
	 */
	public function set_dry_run( $dry_run ) {
		$this->dry_run = (bool) $dry_run;
	}

	/**
	 * Get progress bar object.
	 *
	 * @return ActionScheduler\WPCLI\ProgressBar
	 */
	public function get_progress_bar() {
		return $this->progress_bar;
	}

	/**
	 * Set progress bar object.
	 *
	 * @param ActionScheduler\WPCLI\ProgressBar $progress_bar Progress bar object.
	 */
	public function set_progress_bar( ProgressBar $progress_bar ) {
		$this->progress_bar = $progress_bar;
	}
}
ActionMigrator.php000066600000010253151142233530010203 0ustar00<?php


namespace Action_Scheduler\Migration;

/**
 * Class ActionMigrator
 *
 * @package Action_Scheduler\Migration
 *
 * @since 3.0.0
 *
 * @codeCoverageIgnore
 */
class ActionMigrator {
	/**
	 * Source store instance.
	 *
	 * @var ActionScheduler_Store
	 */
	private $source;

	/**
	 * Destination store instance.
	 *
	 * @var ActionScheduler_Store
	 */
	private $destination;

	/**
	 * LogMigrator instance.
	 *
	 * @var LogMigrator
	 */
	private $log_migrator;

	/**
	 * ActionMigrator constructor.
	 *
	 * @param \ActionScheduler_Store $source_store Source store object.
	 * @param \ActionScheduler_Store $destination_store Destination store object.
	 * @param LogMigrator            $log_migrator Log migrator object.
	 */
	public function __construct( \ActionScheduler_Store $source_store, \ActionScheduler_Store $destination_store, LogMigrator $log_migrator ) {
		$this->source       = $source_store;
		$this->destination  = $destination_store;
		$this->log_migrator = $log_migrator;
	}

	/**
	 * Migrate an action.
	 *
	 * @param int $source_action_id Action ID.
	 *
	 * @return int 0|new action ID
	 * @throws \RuntimeException When unable to delete action from the source store.
	 */
	public function migrate( $source_action_id ) {
		try {
			$action = $this->source->fetch_action( $source_action_id );
			$status = $this->source->get_status( $source_action_id );
		} catch ( \Exception $e ) {
			$action = null;
			$status = '';
		}

		if ( is_null( $action ) || empty( $status ) || ! $action->get_schedule()->get_date() ) {
			// null action or empty status means the fetch operation failed or the action didn't exist.
			// null schedule means it's missing vital data.
			// delete it and move on.
			try {
				$this->source->delete_action( $source_action_id );
			} catch ( \Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
				// nothing to do, it didn't exist in the first place.
			}
			do_action( 'action_scheduler/no_action_to_migrate', $source_action_id, $this->source, $this->destination ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores

			return 0;
		}

		try {

			// Make sure the last attempt date is set correctly for completed and failed actions.
			$last_attempt_date = ( \ActionScheduler_Store::STATUS_PENDING !== $status ) ? $this->source->get_date( $source_action_id ) : null;

			$destination_action_id = $this->destination->save_action( $action, null, $last_attempt_date );
		} catch ( \Exception $e ) {
			do_action( 'action_scheduler/migrate_action_failed', $source_action_id, $this->source, $this->destination ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores

			return 0; // could not save the action in the new store.
		}

		try {
			switch ( $status ) {
				case \ActionScheduler_Store::STATUS_FAILED:
					$this->destination->mark_failure( $destination_action_id );
					break;
				case \ActionScheduler_Store::STATUS_CANCELED:
					$this->destination->cancel_action( $destination_action_id );
					break;
			}

			$this->log_migrator->migrate( $source_action_id, $destination_action_id );
			$this->source->delete_action( $source_action_id );

			$test_action = $this->source->fetch_action( $source_action_id );
			if ( ! is_a( $test_action, 'ActionScheduler_NullAction' ) ) {
				// translators: %s is an action ID.
				throw new \RuntimeException( sprintf( __( 'Unable to remove source migrated action %s', 'action-scheduler' ), $source_action_id ) );
			}
			do_action( 'action_scheduler/migrated_action', $source_action_id, $destination_action_id, $this->source, $this->destination ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores

			return $destination_action_id;
		} catch ( \Exception $e ) {
			// could not delete from the old store.
			$this->source->mark_migrated( $source_action_id );

			// phpcs:disable WordPress.NamingConventions.ValidHookName.UseUnderscores
			do_action( 'action_scheduler/migrate_action_incomplete', $source_action_id, $destination_action_id, $this->source, $this->destination );
			do_action( 'action_scheduler/migrated_action', $source_action_id, $destination_action_id, $this->source, $this->destination );
			// phpcs:enable

			return $destination_action_id;
		}
	}
}
DryRun_LogMigrator.php000066600000000713151142233530011012 0ustar00<?php


namespace Action_Scheduler\Migration;

/**
 * Class DryRun_LogMigrator
 *
 * @package Action_Scheduler\Migration
 *
 * @codeCoverageIgnore
 */
class DryRun_LogMigrator extends LogMigrator {
	/**
	 * Simulate migrating an action log.
	 *
	 * @param int $source_action_id Source logger object.
	 * @param int $destination_action_id Destination logger object.
	 */
	public function migrate( $source_action_id, $destination_action_id ) {
		// no-op.
	}
}
Runner.php000066600000010174151142233530006534 0ustar00<?php


namespace Action_Scheduler\Migration;

/**
 * Class Runner
 *
 * @package Action_Scheduler\Migration
 *
 * @since 3.0.0
 *
 * @codeCoverageIgnore
 */
class Runner {
	/**
	 * Source store instance.
	 *
	 * @var ActionScheduler_Store
	 */
	private $source_store;

	/**
	 * Destination store instance.
	 *
	 * @var ActionScheduler_Store
	 */
	private $destination_store;

	/**
	 * Source logger instance.
	 *
	 * @var ActionScheduler_Logger
	 */
	private $source_logger;

	/**
	 * Destination logger instance.
	 *
	 * @var ActionScheduler_Logger
	 */
	private $destination_logger;

	/**
	 * Batch fetcher instance.
	 *
	 * @var BatchFetcher
	 */
	private $batch_fetcher;

	/**
	 * Action migrator instance.
	 *
	 * @var ActionMigrator
	 */
	private $action_migrator;

	/**
	 * Log migrator instance.
	 *
	 * @var LogMigrator
	 */
	private $log_migrator;

	/**
	 * Progress bar instance.
	 *
	 * @var ProgressBar
	 */
	private $progress_bar;

	/**
	 * Runner constructor.
	 *
	 * @param Config $config Migration configuration object.
	 */
	public function __construct( Config $config ) {
		$this->source_store       = $config->get_source_store();
		$this->destination_store  = $config->get_destination_store();
		$this->source_logger      = $config->get_source_logger();
		$this->destination_logger = $config->get_destination_logger();

		$this->batch_fetcher = new BatchFetcher( $this->source_store );
		if ( $config->get_dry_run() ) {
			$this->log_migrator    = new DryRun_LogMigrator( $this->source_logger, $this->destination_logger );
			$this->action_migrator = new DryRun_ActionMigrator( $this->source_store, $this->destination_store, $this->log_migrator );
		} else {
			$this->log_migrator    = new LogMigrator( $this->source_logger, $this->destination_logger );
			$this->action_migrator = new ActionMigrator( $this->source_store, $this->destination_store, $this->log_migrator );
		}

		if ( defined( 'WP_CLI' ) && WP_CLI ) {
			$this->progress_bar = $config->get_progress_bar();
		}
	}

	/**
	 * Run migration batch.
	 *
	 * @param int $batch_size Optional batch size. Default 10.
	 *
	 * @return int Size of batch processed.
	 */
	public function run( $batch_size = 10 ) {
		$batch      = $this->batch_fetcher->fetch( $batch_size );
		$batch_size = count( $batch );

		if ( ! $batch_size ) {
			return 0;
		}

		if ( $this->progress_bar ) {
			/* translators: %d: amount of actions */
			$this->progress_bar->set_message( sprintf( _n( 'Migrating %d action', 'Migrating %d actions', $batch_size, 'action-scheduler' ), $batch_size ) );
			$this->progress_bar->set_count( $batch_size );
		}

		$this->migrate_actions( $batch );

		return $batch_size;
	}

	/**
	 * Migration a batch of actions.
	 *
	 * @param array $action_ids List of action IDs to migrate.
	 */
	public function migrate_actions( array $action_ids ) {
		do_action( 'action_scheduler/migration_batch_starting', $action_ids ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores

		\ActionScheduler::logger()->unhook_stored_action();
		$this->destination_logger->unhook_stored_action();

		foreach ( $action_ids as $source_action_id ) {
			$destination_action_id = $this->action_migrator->migrate( $source_action_id );
			if ( $destination_action_id ) {
				$this->destination_logger->log(
					$destination_action_id,
					sprintf(
						/* translators: 1: source action ID 2: source store class 3: destination action ID 4: destination store class */
						__( 'Migrated action with ID %1$d in %2$s to ID %3$d in %4$s', 'action-scheduler' ),
						$source_action_id,
						get_class( $this->source_store ),
						$destination_action_id,
						get_class( $this->destination_store )
					)
				);
			}

			if ( $this->progress_bar ) {
				$this->progress_bar->tick();
			}
		}

		if ( $this->progress_bar ) {
			$this->progress_bar->finish();
		}

		\ActionScheduler::logger()->hook_stored_action();

		do_action( 'action_scheduler/migration_batch_complete', $action_ids ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
	}

	/**
	 * Initialize destination store and logger.
	 */
	public function init_destination() {
		$this->destination_store->init();
		$this->destination_logger->init();
	}
}
LogMigrator.php000066600000002441151142233530007507 0ustar00<?php


namespace Action_Scheduler\Migration;

use ActionScheduler_Logger;

/**
 * Class LogMigrator
 *
 * @package Action_Scheduler\Migration
 *
 * @since 3.0.0
 *
 * @codeCoverageIgnore
 */
class LogMigrator {
	/**
	 * Source logger instance.
	 *
	 * @var ActionScheduler_Logger
	 */
	private $source;

	/**
	 * Destination logger instance.
	 *
	 * @var ActionScheduler_Logger
	 */
	private $destination;

	/**
	 * ActionMigrator constructor.
	 *
	 * @param ActionScheduler_Logger $source_logger Source logger object.
	 * @param ActionScheduler_Logger $destination_logger Destination logger object.
	 */
	public function __construct( ActionScheduler_Logger $source_logger, ActionScheduler_Logger $destination_logger ) {
		$this->source      = $source_logger;
		$this->destination = $destination_logger;
	}

	/**
	 * Migrate an action log.
	 *
	 * @param int $source_action_id Source logger object.
	 * @param int $destination_action_id Destination logger object.
	 */
	public function migrate( $source_action_id, $destination_action_id ) {
		$logs = $this->source->get_logs( $source_action_id );

		foreach ( $logs as $log ) {
			if ( absint( $log->get_action_id() ) === absint( $source_action_id ) ) {
				$this->destination->log( $destination_action_id, $log->get_message(), $log->get_date() );
			}
		}
	}
}
DryRun_ActionMigrator.php000066600000001052151142233530011503 0ustar00<?php


namespace Action_Scheduler\Migration;

/**
 * Class DryRun_ActionMigrator
 *
 * @package Action_Scheduler\Migration
 *
 * @since 3.0.0
 *
 * @codeCoverageIgnore
 */
class DryRun_ActionMigrator extends ActionMigrator {
	/**
	 * Simulate migrating an action.
	 *
	 * @param int $source_action_id Action ID.
	 *
	 * @return int
	 */
	public function migrate( $source_action_id ) {
		do_action( 'action_scheduler/migrate_action_dry_run', $source_action_id ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores

		return 0;
	}
}
BatchFetcher.php000066600000003350151142233530007603 0ustar00<?php

namespace Action_Scheduler\Migration;

use ActionScheduler_Store as Store;

/**
 * Class BatchFetcher
 *
 * @package Action_Scheduler\Migration
 *
 * @since 3.0.0
 *
 * @codeCoverageIgnore
 */
class BatchFetcher {
	/**
	 * Store instance.
	 *
	 * @var ActionScheduler_Store
	 */
	private $store;

	/**
	 * BatchFetcher constructor.
	 *
	 * @param ActionScheduler_Store $source_store Source store object.
	 */
	public function __construct( Store $source_store ) {
		$this->store = $source_store;
	}

	/**
	 * Retrieve a list of actions.
	 *
	 * @param int $count The number of actions to retrieve.
	 *
	 * @return int[] A list of action IDs
	 */
	public function fetch( $count = 10 ) {
		foreach ( $this->get_query_strategies( $count ) as $query ) {
			$action_ids = $this->store->query_actions( $query );
			if ( ! empty( $action_ids ) ) {
				return $action_ids;
			}
		}

		return array();
	}

	/**
	 * Generate a list of prioritized of action search parameters.
	 *
	 * @param int $count Number of actions to find.
	 *
	 * @return array
	 */
	private function get_query_strategies( $count ) {
		$now  = as_get_datetime_object();
		$args = array(
			'date'     => $now,
			'per_page' => $count,
			'offset'   => 0,
			'orderby'  => 'date',
			'order'    => 'ASC',
		);

		$priorities = array(
			Store::STATUS_PENDING,
			Store::STATUS_FAILED,
			Store::STATUS_CANCELED,
			Store::STATUS_COMPLETE,
			Store::STATUS_RUNNING,
			'', // any other unanticipated status.
		);

		foreach ( $priorities as $status ) {
			yield wp_parse_args(
				array(
					'status'       => $status,
					'date_compare' => '<=',
				),
				$args
			);

			yield wp_parse_args(
				array(
					'status'       => $status,
					'date_compare' => '>=',
				),
				$args
			);
		}
	}
}
ActionScheduler_DBStoreMigrator.php000066600000003453151142233530013430 0ustar00<?php

/**
 * Class ActionScheduler_DBStoreMigrator
 *
 * A class for direct saving of actions to the table data store during migration.
 *
 * @since 3.0.0
 */
class ActionScheduler_DBStoreMigrator extends ActionScheduler_DBStore {

	/**
	 * Save an action with optional last attempt date.
	 *
	 * Normally, saving an action sets its attempted date to 0000-00-00 00:00:00 because when an action is first saved,
	 * it can't have been attempted yet, but migrated completed actions will have an attempted date, so we need to save
	 * that when first saving the action.
	 *
	 * @param ActionScheduler_Action $action Action to migrate.
	 * @param null|DateTime          $scheduled_date Optional date of the first instance to store.
	 * @param null|DateTime          $last_attempt_date Optional date the action was last attempted.
	 *
	 * @return string The action ID
	 * @throws \RuntimeException When the action is not saved.
	 */
	public function save_action( ActionScheduler_Action $action, ?DateTime $scheduled_date = null, ?DateTime $last_attempt_date = null ) {
		try {
			/**
			 * Global.
			 *
			 * @var \wpdb $wpdb
			 */
			global $wpdb;

			$action_id = parent::save_action( $action, $scheduled_date );

			if ( null !== $last_attempt_date ) {
				$data = array(
					'last_attempt_gmt'   => $this->get_scheduled_date_string( $action, $last_attempt_date ),
					'last_attempt_local' => $this->get_scheduled_date_string_local( $action, $last_attempt_date ),
				);

				$wpdb->update( $wpdb->actionscheduler_actions, $data, array( 'action_id' => $action_id ), array( '%s', '%s' ), array( '%d' ) );
			}

			return $action_id;
		} catch ( \Exception $e ) {
			// translators: %s is an error message.
			throw new \RuntimeException( sprintf( __( 'Error saving action: %s', 'action-scheduler' ), $e->getMessage() ), 0 );
		}
	}
}
Controller.php000066600000014574151142233530007416 0ustar00<?php

namespace Action_Scheduler\Migration;

use ActionScheduler_DataController;
use ActionScheduler_LoggerSchema;
use ActionScheduler_StoreSchema;
use Action_Scheduler\WP_CLI\ProgressBar;

/**
 * Class Controller
 *
 * The main plugin/initialization class for migration to custom tables.
 *
 * @package Action_Scheduler\Migration
 *
 * @since 3.0.0
 *
 * @codeCoverageIgnore
 */
class Controller {
	/**
	 * Instance.
	 *
	 * @var self
	 */
	private static $instance;

	/**
	 * Scheduler instance.
	 *
	 * @var Action_Scheduler\Migration\Scheduler
	 */
	private $migration_scheduler;

	/**
	 * Class name of the store object.
	 *
	 * @var string
	 */
	private $store_classname;

	/**
	 * Class name of the logger object.
	 *
	 * @var string
	 */
	private $logger_classname;

	/**
	 * Flag to indicate migrating custom store.
	 *
	 * @var bool
	 */
	private $migrate_custom_store;

	/**
	 * Controller constructor.
	 *
	 * @param Scheduler $migration_scheduler Migration scheduler object.
	 */
	protected function __construct( Scheduler $migration_scheduler ) {
		$this->migration_scheduler = $migration_scheduler;
		$this->store_classname     = '';
	}

	/**
	 * Set the action store class name.
	 *
	 * @param string $class Classname of the store class.
	 *
	 * @return string
	 */
	public function get_store_class( $class ) {
		if ( \ActionScheduler_DataController::is_migration_complete() ) {
			return \ActionScheduler_DataController::DATASTORE_CLASS;
		} elseif ( \ActionScheduler_Store::DEFAULT_CLASS !== $class ) {
			$this->store_classname = $class;
			return $class;
		} else {
			return 'ActionScheduler_HybridStore';
		}
	}

	/**
	 * Set the action logger class name.
	 *
	 * @param string $class Classname of the logger class.
	 *
	 * @return string
	 */
	public function get_logger_class( $class ) {
		\ActionScheduler_Store::instance();

		if ( $this->has_custom_datastore() ) {
			$this->logger_classname = $class;
			return $class;
		} else {
			return \ActionScheduler_DataController::LOGGER_CLASS;
		}
	}

	/**
	 * Get flag indicating whether a custom datastore is in use.
	 *
	 * @return bool
	 */
	public function has_custom_datastore() {
		return (bool) $this->store_classname;
	}

	/**
	 * Set up the background migration process.
	 *
	 * @return void
	 */
	public function schedule_migration() {
		$logging_tables = new ActionScheduler_LoggerSchema();
		$store_tables   = new ActionScheduler_StoreSchema();

		/*
		 * In some unusual cases, the expected tables may not have been created. In such cases
		 * we do not schedule a migration as doing so will lead to fatal error conditions.
		 *
		 * In such cases the user will likely visit the Tools > Scheduled Actions screen to
		 * investigate, and will see appropriate messaging (this step also triggers an attempt
		 * to rebuild any missing tables).
		 *
		 * @see https://github.com/woocommerce/action-scheduler/issues/653
		 */
		if (
			ActionScheduler_DataController::is_migration_complete()
			|| $this->migration_scheduler->is_migration_scheduled()
			|| ! $store_tables->tables_exist()
			|| ! $logging_tables->tables_exist()
		) {
			return;
		}

		$this->migration_scheduler->schedule_migration();
	}

	/**
	 * Get the default migration config object
	 *
	 * @return ActionScheduler\Migration\Config
	 */
	public function get_migration_config_object() {
		static $config = null;

		if ( ! $config ) {
			$source_store  = $this->store_classname ? new $this->store_classname() : new \ActionScheduler_wpPostStore();
			$source_logger = $this->logger_classname ? new $this->logger_classname() : new \ActionScheduler_wpCommentLogger();

			$config = new Config();
			$config->set_source_store( $source_store );
			$config->set_source_logger( $source_logger );
			$config->set_destination_store( new \ActionScheduler_DBStoreMigrator() );
			$config->set_destination_logger( new \ActionScheduler_DBLogger() );

			if ( defined( 'WP_CLI' ) && WP_CLI ) {
				$config->set_progress_bar( new ProgressBar( '', 0 ) );
			}
		}

		return apply_filters( 'action_scheduler/migration_config', $config ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
	}

	/**
	 * Hook dashboard migration notice.
	 */
	public function hook_admin_notices() {
		if ( ! $this->allow_migration() || \ActionScheduler_DataController::is_migration_complete() ) {
			return;
		}
		add_action( 'admin_notices', array( $this, 'display_migration_notice' ), 10, 0 );
	}

	/**
	 * Show a dashboard notice that migration is in progress.
	 */
	public function display_migration_notice() {
		printf( '<div class="notice notice-warning"><p>%s</p></div>', esc_html__( 'Action Scheduler migration in progress. The list of scheduled actions may be incomplete.', 'action-scheduler' ) );
	}

	/**
	 * Add store classes. Hook migration.
	 */
	private function hook() {
		add_filter( 'action_scheduler_store_class', array( $this, 'get_store_class' ), 100, 1 );
		add_filter( 'action_scheduler_logger_class', array( $this, 'get_logger_class' ), 100, 1 );
		add_action( 'init', array( $this, 'maybe_hook_migration' ) );
		add_action( 'wp_loaded', array( $this, 'schedule_migration' ) );

		// Action Scheduler may be displayed as a Tools screen or WooCommerce > Status administration screen.
		add_action( 'load-tools_page_action-scheduler', array( $this, 'hook_admin_notices' ), 10, 0 );
		add_action( 'load-woocommerce_page_wc-status', array( $this, 'hook_admin_notices' ), 10, 0 );
	}

	/**
	 * Possibly hook the migration scheduler action.
	 */
	public function maybe_hook_migration() {
		if ( ! $this->allow_migration() || \ActionScheduler_DataController::is_migration_complete() ) {
			return;
		}

		$this->migration_scheduler->hook();
	}

	/**
	 * Allow datastores to enable migration to AS tables.
	 */
	public function allow_migration() {
		if ( ! \ActionScheduler_DataController::dependencies_met() ) {
			return false;
		}

		if ( null === $this->migrate_custom_store ) {
			$this->migrate_custom_store = apply_filters( 'action_scheduler_migrate_data_store', false );
		}

		return ( ! $this->has_custom_datastore() ) || $this->migrate_custom_store;
	}

	/**
	 * Proceed with the migration if the dependencies have been met.
	 */
	public static function init() {
		if ( \ActionScheduler_DataController::dependencies_met() ) {
			self::instance()->hook();
		}
	}

	/**
	 * Singleton factory.
	 */
	public static function instance() {
		if ( ! isset( self::$instance ) ) {
			self::$instance = new static( new Scheduler() );
		}

		return self::$instance;
	}
}