Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions data-machine.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@
require_once __DIR__ . '/vendor/woocommerce/action-scheduler/action-scheduler.php';
}

if ( function_exists( 'wp_installing' ) && wp_installing() ) {
add_action( 'wp_loaded', 'datamachine_skip_action_scheduler_migration_during_install', 0 );
}

/**
* Prevent AS migration scheduling during wp-phpunit install bootstrap.
*
* @return void
*/
function datamachine_skip_action_scheduler_migration_during_install(): void {
if ( ! class_exists( '\Action_Scheduler\Migration\Controller' ) ) {
return;
}

remove_action( 'wp_loaded', array( \Action_Scheduler\Migration\Controller::instance(), 'schedule_migration' ) );
}


function datamachine_run_datamachine_plugin() {
if ( ! datamachine_should_load_full_runtime() ) {
Expand Down
21 changes: 21 additions & 0 deletions inc/Core/Admin/FlowFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,32 @@ private static function get_next_run_time( ?int $flow_id ): ?string {
return null;
}

if ( ! self::is_action_scheduler_datastore_ready() ) {
return null;
}

$next_timestamp = as_next_scheduled_action( 'datamachine_run_flow_now', array( $flow_id ), 'data-machine' );

return $next_timestamp ? wp_date( 'Y-m-d H:i:s', $next_timestamp, new \DateTimeZone( 'UTC' ) ) : null;
}

/**
* Check whether Action Scheduler's datastore is ready for procedural API reads.
*
* @return bool True when Action Scheduler can safely query scheduled actions.
*/
private static function is_action_scheduler_datastore_ready(): bool {
if ( function_exists( 'did_action' ) && 0 === did_action( 'action_scheduler_init' ) ) {
return false;
}

if ( ! class_exists( '\ActionScheduler' ) || ! method_exists( '\ActionScheduler', 'is_initialized' ) ) {
return true;
}

return \ActionScheduler::is_initialized();
}

/**
* Batch-fetch next run times for multiple flows in a single query.
*
Expand Down
26 changes: 26 additions & 0 deletions inc/Engine/Tasks/RecurringScheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,31 @@ public static function isScheduled( string $hook, array $args, string $group = s
if ( ! function_exists( 'as_next_scheduled_action' ) ) {
return false;
}

if ( ! self::isActionSchedulerDataStoreReady() ) {
return false;
}

return false !== as_next_scheduled_action( $hook, $args, $group );
}

/**
* Check whether Action Scheduler's datastore is ready for procedural API reads.
*
* @return bool True when Action Scheduler can safely query scheduled actions.
*/
private static function isActionSchedulerDataStoreReady(): bool {
if ( function_exists( 'did_action' ) && 0 === did_action( 'action_scheduler_init' ) ) {
return false;
}

if ( ! class_exists( '\ActionScheduler' ) || ! method_exists( '\ActionScheduler', 'is_initialized' ) ) {
return true;
}

return \ActionScheduler::is_initialized();
}

/**
* Create a WP_Error with optional data without tripping scoped PHPStan stubs.
*
Expand All @@ -291,6 +313,10 @@ private static function getPendingAction( string $hook, array $args, string $gro
return null;
}

if ( ! self::isActionSchedulerDataStoreReady() ) {
return null;
}

$actions = as_get_scheduled_actions(
array(
'hook' => $hook,
Expand Down
65 changes: 65 additions & 0 deletions tests/recurring-scheduler-as-readiness-smoke.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php
/**
* Pure-PHP smoke for RecurringScheduler Action Scheduler datastore readiness.
*
* Run with: php tests/recurring-scheduler-as-readiness-smoke.php
*
* @package DataMachine\Tests
*/

if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}

if ( ! class_exists( 'WP_Error' ) ) {
class WP_Error {
public function __construct( string $code = '', string $message = '', array $data = array() ) {
unset( $code, $message, $data );
}
}
}

class ActionScheduler {
public static bool $initialized = false;

public static function is_initialized( $function_name = null ): bool {
unset( $function_name );
return self::$initialized;
}
}

$GLOBALS['datamachine_as_next_scheduled_calls'] = 0;

function as_next_scheduled_action( string $hook, ?array $args = null, string $group = '' ) {
unset( $hook, $args, $group );
++$GLOBALS['datamachine_as_next_scheduled_calls'];
return time() + 3600;
}

require_once __DIR__ . '/../inc/Engine/Tasks/RecurringScheduler.php';

use DataMachine\Engine\Tasks\RecurringScheduler;

function datamachine_assert( bool $condition, string $message ): void {
if ( $condition ) {
echo " [PASS] {$message}\n";
return;
}

echo " [FAIL] {$message}\n";
exit( 1 );
}

echo "=== recurring-scheduler-as-readiness-smoke ===\n";

ActionScheduler::$initialized = false;
$result = RecurringScheduler::isScheduled( 'datamachine_hook', array(), RecurringScheduler::GROUP );
datamachine_assert( false === $result, 'uninitialized Action Scheduler datastore reports unscheduled' );
datamachine_assert( 0 === $GLOBALS['datamachine_as_next_scheduled_calls'], 'as_next_scheduled_action is not called before datastore initialization' );

ActionScheduler::$initialized = true;
$result = RecurringScheduler::isScheduled( 'datamachine_hook', array(), RecurringScheduler::GROUP );
datamachine_assert( true === $result, 'initialized Action Scheduler datastore delegates to as_next_scheduled_action' );
datamachine_assert( 1 === $GLOBALS['datamachine_as_next_scheduled_calls'], 'as_next_scheduled_action is called after datastore initialization' );

echo "\nAll recurring scheduler AS readiness assertions passed.\n";
Loading