$args */ public function dispatch(string $hook, array $args): void { $this->requireTransactionalStore(); $id = as_enqueue_async_action($hook, [$args], self::GROUP, true); if ($id === 0 && as_has_scheduled_action($hook, [$args], self::GROUP)) { return; } $this->assertEnqueued($id); } /** * @param array $args */ public function schedule(int $timestamp, string $hook, array $args): void { $this->assertEnqueued(as_schedule_single_action($timestamp, $hook, [$args], self::GROUP)); } private function requireTransactionalStore(): void { if ($this->storageChecked || !class_exists(\ActionScheduler::class)) { return; } $store = \ActionScheduler::store(); if ($store instanceof \ActionScheduler_HybridStore) { $store = \Action_Scheduler\Migration\Controller::instance()->get_migration_config_object()->get_destination_store(); } if (!($store instanceof \ActionScheduler_DBStore)) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- operator text: caught upstream and returned or stored as plain text, never printed as HTML. throw new QueueFailure(__('Submission handoff requires the Action Scheduler database store.', 'wconvert')); } global $wpdb; // A one-off read of the schema itself, which no WordPress API exposes; // its answer is cached in this object for the request. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- see above. $rows = $wpdb->get_col($wpdb->prepare( 'SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME IN (%s, %s)', $wpdb->prefix . 'actionscheduler_actions', $wpdb->prefix . 'actionscheduler_groups' )); if (count($rows) !== 2 || array_filter($rows, static fn ($engine): bool => strtoupper((string) $engine) !== 'INNODB') !== []) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- operator text: caught upstream and returned or stored as plain text, never printed as HTML. throw new QueueFailure(__('Submission handoff requires transactional Action Scheduler storage.', 'wconvert')); } $this->storageChecked = true; } private function assertEnqueued(int $actionId): void { // Action Scheduler uses zero when the store rejects an action. Treat // that as a failed operation rather than reporting a job that does not // exist; callers decide whether the surrounding operation can proceed. if ($actionId === 0) { // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- operator text: caught upstream and returned or stored as plain text, never printed as HTML. throw new QueueFailure(__('WConvert could not enqueue a scheduled action.', 'wconvert')); } } }