Plugin Directory

Changeset 3328081


Ignore:
Timestamp:
07/15/2025 09:33:30 AM (5 months ago)
Author:
jorgeatorres
Message:

Tagging version 3.9.3

Location:
action-scheduler
Files:
2 added
42 edited
1 copied

Legend:

Unmodified
Added
Removed
  • action-scheduler/tags/3.9.3/action-scheduler.php

    r3233744 r3328081  
    66 * Author: Automattic
    77 * Author URI: https://automattic.com/
    8  * Version: 3.9.2
     8 * Version: 3.9.3
    99 * License: GPLv3
    1010 * Requires at least: 6.5
    11  * Tested up to: 6.7
    12  * Requires PHP: 7.1
     11 * Tested up to: 6.8
     12 * Requires PHP: 7.2
    1313 *
    1414 * Copyright 2019 Automattic, Inc.  (https://automattic.com/contact/)
     
    3030 */
    3131
    32 if ( ! function_exists( 'action_scheduler_register_3_dot_9_dot_2' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
     32if ( ! function_exists( 'action_scheduler_register_3_dot_9_dot_3' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
    3333
    3434    if ( ! class_exists( 'ActionScheduler_Versions', false ) ) {
     
    3737    }
    3838
    39     add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_9_dot_2', 0, 0 ); // WRCS: DEFINED_VERSION.
     39    add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_9_dot_3', 0, 0 ); // WRCS: DEFINED_VERSION.
    4040
    4141    // phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
     
    4343     * Registers this version of Action Scheduler.
    4444     */
    45     function action_scheduler_register_3_dot_9_dot_2() { // WRCS: DEFINED_VERSION.
     45    function action_scheduler_register_3_dot_9_dot_3() { // WRCS: DEFINED_VERSION.
    4646        $versions = ActionScheduler_Versions::instance();
    47         $versions->register( '3.9.2', 'action_scheduler_initialize_3_dot_9_dot_2' ); // WRCS: DEFINED_VERSION.
     47        $versions->register( '3.9.3', 'action_scheduler_initialize_3_dot_9_dot_3' ); // WRCS: DEFINED_VERSION.
    4848    }
    4949
     
    5252     * Initializes this version of Action Scheduler.
    5353     */
    54     function action_scheduler_initialize_3_dot_9_dot_2() { // WRCS: DEFINED_VERSION.
     54    function action_scheduler_initialize_3_dot_9_dot_3() { // WRCS: DEFINED_VERSION.
    5555        // A final safety check is required even here, because historic versions of Action Scheduler
    5656        // followed a different pattern (in some unusual cases, we could reach this point and the
     
    6464    // Support usage in themes - load this version if no plugin has loaded a version yet.
    6565    if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) {
    66         action_scheduler_initialize_3_dot_9_dot_2(); // WRCS: DEFINED_VERSION.
     66        action_scheduler_initialize_3_dot_9_dot_3(); // WRCS: DEFINED_VERSION.
    6767        do_action( 'action_scheduler_pre_theme_init' );
    6868        ActionScheduler_Versions::initialize_latest_version();
  • action-scheduler/tags/3.9.3/changelog.txt

    r3233744 r3328081  
    11*** Changelog ***
     2
     3= 3.9.3 - 2025-07-15 =
     4* Add hook 'action_scheduler_ensure_recurring_actions' specifically for scheduling recurring actions.
     5* Assume an action is valid until proven otherwise.
     6* Implement SKIP LOCKED during action claiming.
     7* Import `get_flag_value()` from `WP_CLI\Utils` before using.
     8* Make `$unique` available to all pre-creation/short-circuit hooks.
     9* Make version/source information available via new class.
     10* Only release claims on pending actions.
     11* Tweak - WP 6.8 compatibility.
     12* Update minimum supported php and phpunit versions.
     13* Update readme.txt.
     14* WP CLI get action command: correct parentheses/nesting of conditional checks.
    215
    316= 3.9.2 - 2025-02-03 =
  • action-scheduler/tags/3.9.3/classes/ActionScheduler_DataController.php

    r3189111 r3328081  
    163163        }
    164164
    165         $wp_object_cache->group_ops      = array();
    166         $wp_object_cache->stats          = array();
    167         $wp_object_cache->memcache_debug = array();
    168         $wp_object_cache->cache          = array();
     165        // Not all drop-ins support these props, however, there may be existing installations that rely on these being cleared.
     166        if ( property_exists( $wp_object_cache, 'group_ops' ) ) {
     167            $wp_object_cache->group_ops = array();
     168        }
     169        if ( property_exists( $wp_object_cache, 'stats' ) ) {
     170            $wp_object_cache->stats = array();
     171        }
     172        if ( property_exists( $wp_object_cache, 'memcache_debug' ) ) {
     173            $wp_object_cache->memcache_debug = array();
     174        }
     175        if ( property_exists( $wp_object_cache, 'cache' ) ) {
     176            $wp_object_cache->cache = array();
     177        }
    169178
    170179        if ( is_callable( array( $wp_object_cache, '__remoteset' ) ) ) {
  • action-scheduler/tags/3.9.3/classes/ActionScheduler_wcSystemStatus.php

    r2622865 r3328081  
    7777        $action = $this->store->query_actions(
    7878            array(
    79                 'claimed'  => false,
    8079                'status'   => $status,
    8180                'per_page' => 1,
  • action-scheduler/tags/3.9.3/classes/WP_CLI/Action/Create_Command.php

    r3226133 r3328081  
    22
    33namespace Action_Scheduler\WP_CLI\Action;
     4
     5use function \WP_CLI\Utils\get_flag_value;
    46
    57/**
  • action-scheduler/tags/3.9.3/classes/WP_CLI/Action/Get_Command.php

    r3226133 r3328081  
    2525
    2626        $only_logs   = ! empty( $this->assoc_args['field'] ) && 'log_entries' === $this->assoc_args['field'];
    27         $only_logs   = $only_logs || ( ! empty( $this->assoc_args['fields'] && 'log_entries' === $this->assoc_args['fields'] ) );
     27        $only_logs   = $only_logs || ( ! empty( $this->assoc_args['fields'] ) && 'log_entries' === $this->assoc_args['fields'] );
    2828        $log_entries = array();
    2929
  • action-scheduler/tags/3.9.3/classes/WP_CLI/System_Command.php

    r3233744 r3328081  
    263263
    264264        $args = array(
    265             'claimed'  => false,
    266265            'status'   => $status,
    267266            'per_page' => 1,
  • action-scheduler/tags/3.9.3/classes/abstracts/ActionScheduler.php

    r3226133 r3328081  
    185185        ActionScheduler_DataController::init();
    186186
    187         $store      = self::store();
    188         $logger     = self::logger();
    189         $runner     = self::runner();
    190         $admin_view = self::admin_view();
     187        $store                      = self::store();
     188        $logger                     = self::logger();
     189        $runner                     = self::runner();
     190        $admin_view                 = self::admin_view();
     191        $recurring_action_scheduler = new ActionScheduler_RecurringActionScheduler();
    191192
    192193        // Ensure initialization on plugin activation.
     
    197198            add_action( 'init', array( $logger, 'init' ), 1, 0 );
    198199            add_action( 'init', array( $runner, 'init' ), 1, 0 );
     200            add_action( 'init', array( $recurring_action_scheduler, 'init' ), 1, 0 );
    199201
    200202            add_action(
     
    224226            $logger->init();
    225227            $runner->init();
     228            $recurring_action_scheduler->init();
    226229            self::$data_store_initialized = true;
    227230
  • action-scheduler/tags/3.9.3/classes/abstracts/ActionScheduler_Abstract_QueueRunner.php

    r3189111 r3328081  
    8787        try {
    8888            try {
    89                 $valid_action = false;
     89                $valid_action = true;
     90
    9091                do_action( 'action_scheduler_before_execute', $action_id, $context );
    9192
    9293                if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) {
     94                    $valid_action = false;
    9395                    do_action( 'action_scheduler_execution_ignored', $action_id, $context );
    9496                    return;
    9597                }
    9698
    97                 $valid_action = true;
    9899                do_action( 'action_scheduler_begin_execute', $action_id, $context );
    99100
  • action-scheduler/tags/3.9.3/classes/data-stores/ActionScheduler_DBStore.php

    r3189111 r3328081  
    932932         */
    933933        global $wpdb;
    934 
    935934        $now  = as_get_datetime_object();
    936935        $date = is_null( $before_date ) ? $now : clone $before_date;
    937         // can't use $wpdb->update() because of the <= condition.
    938         $update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s";
    939         $params = array(
    940             $claim_id,
    941             $now->format( 'Y-m-d H:i:s' ),
    942             current_time( 'mysql' ),
    943         );
    944936
    945937        // Set claim filters.
     
    955947        }
    956948
    957         $where    = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s';
    958         $params[] = $date->format( 'Y-m-d H:i:s' );
    959         $params[] = self::STATUS_PENDING;
     949        $where        = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s';
     950        $where_params = array(
     951            $date->format( 'Y-m-d H:i:s' ),
     952            self::STATUS_PENDING,
     953        );
    960954
    961955        if ( ! empty( $hooks ) ) {
    962956            $placeholders = array_fill( 0, count( $hooks ), '%s' );
    963             $where       .= ' AND hook IN (' . join( ', ', $placeholders ) . ')';
    964             $params       = array_merge( $params, array_values( $hooks ) );
     957            $where        .= ' AND hook IN (' . join( ', ', $placeholders ) . ')';
     958            $where_params = array_merge( $where_params, array_values( $hooks ) );
    965959        }
    966960
     
    997991         * Sets the order-by clause used in the action claim query.
    998992         *
    999          * @since 3.4.0
    1000          * @since 3.8.3 Made $claim_id and $hooks available.
    1001          *
    1002993         * @param string $order_by_sql
    1003994         * @param string $claim_id Claim Id.
    1004          * @param array  $hooks Hooks to filter for.
    1005          */
    1006         $order    = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC', $claim_id, $hooks );
    1007         $params[] = $limit;
    1008 
    1009         $sql           = $wpdb->prepare( "{$update} {$where} {$order} LIMIT %d", $params ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders
    1010         $rows_affected = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     995         * @param array  $hooks    Hooks to filter for.
     996         *
     997         * @since 3.8.3 Made $claim_id and $hooks available.
     998         * @since 3.4.0
     999         */
     1000        $order       = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC', $claim_id, $hooks );
     1001        $skip_locked = $this->db_supports_skip_locked() ? ' SKIP LOCKED' : '';
     1002
     1003        // Selecting the action_ids that we plan to claim, while skipping any locked rows to avoid deadlocking.
     1004        $select_sql = $wpdb->prepare( "SELECT action_id from {$wpdb->actionscheduler_actions} {$where} {$order} LIMIT %d FOR UPDATE{$skip_locked}", array_merge( $where_params, array( $limit ) ) );
     1005
     1006        // Now place it into an UPDATE statement by joining the result sets, allowing for the SKIP LOCKED behavior to take effect.
     1007        $update_sql    = "UPDATE {$wpdb->actionscheduler_actions} t1 JOIN ( $select_sql ) t2 ON t1.action_id = t2.action_id SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s";
     1008        $update_params = array(
     1009            $claim_id,
     1010            $now->format( 'Y-m-d H:i:s' ),
     1011            current_time( 'mysql' ),
     1012        );
     1013
     1014        $rows_affected = $wpdb->query( $wpdb->prepare( $update_sql, $update_params ) );
    10111015        if ( false === $rows_affected ) {
    10121016            $error = empty( $wpdb->last_error )
    10131017                ? _x( 'unknown', 'database error', 'action-scheduler' )
    10141018                : $wpdb->last_error;
    1015 
    10161019            throw new \RuntimeException(
    10171020                sprintf(
     
    10271030
    10281031    /**
     1032     * Determines whether the database supports using SKIP LOCKED. This logic mimicks the $wpdb::has_cap() logic.
     1033     *
     1034     * SKIP_LOCKED support was added to MariaDB in 10.6.0 and to MySQL in 8.0.1
     1035     *
     1036     * @return bool
     1037     */
     1038    private function db_supports_skip_locked() {
     1039        global $wpdb;
     1040        $db_version     = $wpdb->db_version();
     1041        $db_server_info = $wpdb->db_server_info();
     1042        $is_mariadb     = ( false !== strpos( $db_server_info, 'MariaDB' ) );
     1043
     1044        if ( $is_mariadb &&
     1045             '5.5.5' === $db_version &&
     1046             PHP_VERSION_ID < 80016 // PHP 8.0.15 or older.
     1047        ) {
     1048            /*
     1049             * Account for MariaDB version being prefixed with '5.5.5-' on older PHP versions.
     1050             */
     1051            $db_server_info = preg_replace( '/^5\.5\.5-(.*)/', '$1', $db_server_info );
     1052            $db_version     = preg_replace( '/[^0-9.].*/', '', $db_server_info );
     1053        }
     1054
     1055        $is_supported = ( $is_mariadb && version_compare( $db_version, '10.6.0', '>=' ) ) ||
     1056                        ( ! $is_mariadb && version_compare( $db_version, '8.0.1', '>=' ) );
     1057
     1058        /**
     1059         * Filter whether the database supports the SKIP LOCKED modifier for queries.
     1060         *
     1061         * @param bool $is_supported Whether SKIP LOCKED is supported.
     1062         *
     1063         * @since 3.9.3
     1064         */
     1065        return apply_filters( 'action_scheduler_db_supports_skip_locked', $is_supported );
     1066    }
     1067
     1068    /**
    10291069     * Get the number of active claims.
    10301070     *
     
    10951135
    10961136    /**
    1097      * Release actions from a claim and delete the claim.
     1137     * Release pending actions from a claim and delete the claim.
    10981138     *
    10991139     * @param ActionScheduler_ActionClaim $claim Claim object.
     
    11071147         */
    11081148        global $wpdb;
     1149
     1150        if ( 0 === intval( $claim->get_id() ) ) {
     1151            // Verify that the claim_id is valid before attempting to release it.
     1152            return;
     1153        }
    11091154
    11101155        /**
     
    11141159         *
    11151160         * We resolve this by getting all the actions_id that we want to release claim from in a separate query, and then releasing the claim on each of them. This way, our lock is acquired on the action_id index instead of the claim_id index. Note that the lock on claim_id will still be acquired, but it will only when we actually make the update, rather than when we select the actions.
    1116          */
    1117         $action_ids = $wpdb->get_col( $wpdb->prepare( "SELECT action_id FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d", $claim->get_id() ) );
     1161         *
     1162         * We only release pending actions in order for them to be claimed by another process.
     1163         */
     1164        $action_ids = $wpdb->get_col( $wpdb->prepare( "SELECT action_id FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d AND status = %s", $claim->get_id(), self::STATUS_PENDING ) );
    11181165
    11191166        $row_updates = 0;
  • action-scheduler/tags/3.9.3/classes/data-stores/ActionScheduler_HybridStore.php

    r3189111 r3328081  
    433433
    434434    /**
    435      * Release a claim in the table data store.
     435     * Release a claim in the table data store on any pending actions.
    436436     *
    437437     * @param ActionScheduler_ActionClaim $claim Claim object.
  • action-scheduler/tags/3.9.3/classes/data-stores/ActionScheduler_wpPostStore.php

    r3189111 r3328081  
    792792
    793793    /**
    794      * Release claim.
     794     * Release pending actions from a claim.
    795795     *
    796796     * @param ActionScheduler_ActionClaim $claim Claim object to release.
     
    799799     */
    800800    public function release_claim( ActionScheduler_ActionClaim $claim ) {
    801         $action_ids = $this->find_actions_by_claim_id( $claim->get_id() );
    802         if ( empty( $action_ids ) ) {
    803             return; // nothing to do.
    804         }
    805         $action_id_string = implode( ',', array_map( 'intval', $action_ids ) );
    806801        /**
    807802         * Global wpdb object.
     
    810805         */
    811806        global $wpdb;
     807
     808        $claim_id = $claim->get_id();
     809        if ( trim( $claim_id ) === '' ) {
     810            // Verify that the claim_id is valid before attempting to release it.
     811            return;
     812        }
     813
     814        // Only attempt to release pending actions to be claimed again. Running and complete actions are no longer relevant outside of admin/analytics.
     815        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     816        $action_ids = $wpdb->get_col(
     817            $wpdb->prepare(
     818                "SELECT ID, post_date_gmt FROM {$wpdb->posts} WHERE post_type = %s AND post_password = %s AND post_status = %s",
     819                self::POST_TYPE,
     820                $claim_id,
     821                self::STATUS_PENDING
     822            )
     823        );
     824
     825        if ( empty( $action_ids ) ) {
     826            return; // nothing to do.
     827        }
     828        $action_id_string = implode( ',', array_map( 'intval', $action_ids ) );
    812829
    813830        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
  • action-scheduler/tags/3.9.3/classes/schema/ActionScheduler_StoreSchema.php

    r3189111 r3328081  
    2121     * @var int
    2222     */
    23     protected $schema_version = 7;
     23    protected $schema_version = 8;
    2424
    2525    /**
     
    8181                        KEY group_id (group_id),
    8282                        KEY last_attempt_gmt (last_attempt_gmt),
    83                         KEY `claim_id_status_scheduled_date_gmt` (`claim_id`, `status`, `scheduled_date_gmt`)
     83                        KEY `claim_id_status_priority_scheduled_date_gmt` (`claim_id`,`status`,`priority`,`scheduled_date_gmt`),
     84                        KEY `status_last_attempt_gmt` (`status`,`last_attempt_gmt`),
     85                        KEY `status_claim_id` (`status`,`claim_id`)
    8486                        ) $charset_collate";
    8587
  • action-scheduler/tags/3.9.3/functions.php

    r3189111 r3328081  
    8787     * @param string   $group      Action group.
    8888     * @param int      $priorities Action priority.
     89     * @param bool     $unique     Unique action.
    8990     */
    90     $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group, $priority );
     91    $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group, $priority, $unique );
    9192    if ( null !== $pre ) {
    9293        return is_int( $pre ) ? $pre : 0;
     
    160161     * @param string   $group               Action group.
    161162     * @param int      $priority            Action priority.
     163     * @param bool     $unique              Unique action.
    162164     */
    163     $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group, $priority );
     165    $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group, $priority, $unique );
    164166    if ( null !== $pre ) {
    165167        return is_int( $pre ) ? $pre : 0;
     
    226228     * @param string   $group      Action group.
    227229     * @param int      $priority   Action priority.
     230     * @param bool     $unique     Unique action.
    228231     */
    229     $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group, $priority );
     232    $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group, $priority, $unique );
    230233    if ( null !== $pre ) {
    231234        return is_int( $pre ) ? $pre : 0;
     
    494497    return $date;
    495498}
     499
     500/**
     501 * Check if a specific feature is supported by the current version of Action Scheduler.
     502 *
     503 * @since 3.9.3
     504 *
     505 * @param string $feature The feature to check support for.
     506 *
     507 * @return bool True if the feature is supported, false otherwise.
     508 */
     509function as_supports( string $feature ): bool {
     510    $supported_features = array( 'ensure_recurring_actions_hook' );
     511
     512    return in_array( $feature, $supported_features, true );
     513}
  • action-scheduler/tags/3.9.3/readme.txt

    r3233744 r3328081  
    22Contributors: Automattic, wpmuguru, claudiosanches, peterfabian1000, vedjain, jamosova, obliviousharmony, konamiman, sadowski, royho, barryhughes-1
    33Tags: scheduler, cron
    4 Stable tag: 3.9.2
     4Stable tag: 3.9.3
    55License: GPLv3
    66Requires at least: 6.5
    7 Tested up to: 6.7
    8 Requires PHP: 7.1
     7Tested up to: 6.8
     8Requires PHP: 7.2
    99
    1010Action Scheduler - Job Queue for WordPress
     
    3030## Learn More
    3131
    32 To learn more about how to Action Scheduler works, and how to use it in your plugin, check out the docs on [ActionScheduler.org](https://actionscheduler.org).
     32To learn more about how Action Scheduler works, and how to use it in your plugin, check out the docs on [ActionScheduler.org](https://actionscheduler.org).
    3333
    3434There you will find:
     
    4747
    4848== Changelog ==
     49
     50= 3.9.3 - 2025-07-15 =
     51* Add hook 'action_scheduler_ensure_recurring_actions' specifically for scheduling recurring actions.
     52* Assume an action is valid until proven otherwise.
     53* Implement SKIP LOCKED during action claiming.
     54* Import `get_flag_value()` from `WP_CLI\Utils` before using.
     55* Make `$unique` available to all pre-creation/short-circuit hooks.
     56* Make version/source information available via new class.
     57* Only release claims on pending actions.
     58* Tweak - WP 6.8 compatibility.
     59* Update minimum supported php and phpunit versions.
     60* Update readme.txt.
     61* WP CLI get action command: correct parentheses/nesting of conditional checks.
    4962
    5063= 3.9.2 - 2025-02-03 =
  • action-scheduler/tags/3.9.3/vendor/autoload.php

    r3151094 r3328081  
    2323require_once __DIR__ . '/composer/autoload_real.php';
    2424
    25 return ComposerAutoloaderInit81fd3951ccfed0a222e6f4b949998df1::getLoader();
     25return ComposerAutoloaderInit55c846ad8e6e369a91b7de2e9976f685::getLoader();
  • action-scheduler/tags/3.9.3/vendor/composer/InstalledVersions.php

    r3151094 r3328081  
    323323
    324324        $installed = array();
     325        $copiedLocalDir = false;
    325326
    326327        if (self::$canGetVendors) {
     
    331332                    /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
    332333                    $required = require $vendorDir.'/composer/installed.php';
    333                     $installed[] = self::$installedByVendor[$vendorDir] = $required;
    334                     if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
    335                         self::$installed = $installed[count($installed) - 1];
     334                    self::$installedByVendor[$vendorDir] = $required;
     335                    $installed[] = $required;
     336                    if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
     337                        self::$installed = $required;
     338                        $copiedLocalDir = true;
    336339                    }
    337340                }
     
    351354        }
    352355
    353         if (self::$installed !== array()) {
     356        if (self::$installed !== array() && !$copiedLocalDir) {
    354357            $installed[] = self::$installed;
    355358        }
  • action-scheduler/tags/3.9.3/vendor/composer/autoload_real.php

    r3151094 r3328081  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit81fd3951ccfed0a222e6f4b949998df1
     5class ComposerAutoloaderInit55c846ad8e6e369a91b7de2e9976f685
    66{
    77    private static $loader;
     
    2525        require __DIR__ . '/platform_check.php';
    2626
    27         spl_autoload_register(array('ComposerAutoloaderInit81fd3951ccfed0a222e6f4b949998df1', 'loadClassLoader'), true, true);
     27        spl_autoload_register(array('ComposerAutoloaderInit55c846ad8e6e369a91b7de2e9976f685', 'loadClassLoader'), true, true);
    2828        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
    29         spl_autoload_unregister(array('ComposerAutoloaderInit81fd3951ccfed0a222e6f4b949998df1', 'loadClassLoader'));
     29        spl_autoload_unregister(array('ComposerAutoloaderInit55c846ad8e6e369a91b7de2e9976f685', 'loadClassLoader'));
    3030
    3131        require __DIR__ . '/autoload_static.php';
    32         call_user_func(\Composer\Autoload\ComposerStaticInit81fd3951ccfed0a222e6f4b949998df1::getInitializer($loader));
     32        call_user_func(\Composer\Autoload\ComposerStaticInit55c846ad8e6e369a91b7de2e9976f685::getInitializer($loader));
    3333
    3434        $loader->register(true);
  • action-scheduler/tags/3.9.3/vendor/composer/autoload_static.php

    r3151094 r3328081  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit81fd3951ccfed0a222e6f4b949998df1
     7class ComposerStaticInit55c846ad8e6e369a91b7de2e9976f685
    88{
    99    public static $classMap = array (
     
    1414    {
    1515        return \Closure::bind(function () use ($loader) {
    16             $loader->classMap = ComposerStaticInit81fd3951ccfed0a222e6f4b949998df1::$classMap;
     16            $loader->classMap = ComposerStaticInit55c846ad8e6e369a91b7de2e9976f685::$classMap;
    1717
    1818        }, null, ClassLoader::class);
  • action-scheduler/tags/3.9.3/vendor/composer/installed.php

    r3233744 r3328081  
    22    'root' => array(
    33        'name' => 'woocommerce/action-scheduler',
    4         'pretty_version' => 'dev-release/3.9.2',
    5         'version' => 'dev-release/3.9.2',
    6         'reference' => 'efbb7953f72a433086335b249292f280dd43ddfe',
     4        'pretty_version' => 'dev-release/3.9.3',
     5        'version' => 'dev-release/3.9.3',
     6        'reference' => 'c58cdbab17651303d406cd3b22cf9d75c71c986c',
    77        'type' => 'wordpress-plugin',
    88        'install_path' => __DIR__ . '/../../',
     
    1212    'versions' => array(
    1313        'woocommerce/action-scheduler' => array(
    14             'pretty_version' => 'dev-release/3.9.2',
    15             'version' => 'dev-release/3.9.2',
    16             'reference' => 'efbb7953f72a433086335b249292f280dd43ddfe',
     14            'pretty_version' => 'dev-release/3.9.3',
     15            'version' => 'dev-release/3.9.3',
     16            'reference' => 'c58cdbab17651303d406cd3b22cf9d75c71c986c',
    1717            'type' => 'wordpress-plugin',
    1818            'install_path' => __DIR__ . '/../../',
  • action-scheduler/tags/3.9.3/vendor/composer/platform_check.php

    r3189111 r3328081  
    55$issues = array();
    66
    7 if (!(PHP_VERSION_ID >= 70100)) {
    8     $issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.0". You are running ' . PHP_VERSION . '.';
     7if (!(PHP_VERSION_ID >= 70200)) {
     8    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
    99}
    1010
  • action-scheduler/trunk/action-scheduler.php

    r3233744 r3328081  
    66 * Author: Automattic
    77 * Author URI: https://automattic.com/
    8  * Version: 3.9.2
     8 * Version: 3.9.3
    99 * License: GPLv3
    1010 * Requires at least: 6.5
    11  * Tested up to: 6.7
    12  * Requires PHP: 7.1
     11 * Tested up to: 6.8
     12 * Requires PHP: 7.2
    1313 *
    1414 * Copyright 2019 Automattic, Inc.  (https://automattic.com/contact/)
     
    3030 */
    3131
    32 if ( ! function_exists( 'action_scheduler_register_3_dot_9_dot_2' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
     32if ( ! function_exists( 'action_scheduler_register_3_dot_9_dot_3' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
    3333
    3434    if ( ! class_exists( 'ActionScheduler_Versions', false ) ) {
     
    3737    }
    3838
    39     add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_9_dot_2', 0, 0 ); // WRCS: DEFINED_VERSION.
     39    add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_9_dot_3', 0, 0 ); // WRCS: DEFINED_VERSION.
    4040
    4141    // phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
     
    4343     * Registers this version of Action Scheduler.
    4444     */
    45     function action_scheduler_register_3_dot_9_dot_2() { // WRCS: DEFINED_VERSION.
     45    function action_scheduler_register_3_dot_9_dot_3() { // WRCS: DEFINED_VERSION.
    4646        $versions = ActionScheduler_Versions::instance();
    47         $versions->register( '3.9.2', 'action_scheduler_initialize_3_dot_9_dot_2' ); // WRCS: DEFINED_VERSION.
     47        $versions->register( '3.9.3', 'action_scheduler_initialize_3_dot_9_dot_3' ); // WRCS: DEFINED_VERSION.
    4848    }
    4949
     
    5252     * Initializes this version of Action Scheduler.
    5353     */
    54     function action_scheduler_initialize_3_dot_9_dot_2() { // WRCS: DEFINED_VERSION.
     54    function action_scheduler_initialize_3_dot_9_dot_3() { // WRCS: DEFINED_VERSION.
    5555        // A final safety check is required even here, because historic versions of Action Scheduler
    5656        // followed a different pattern (in some unusual cases, we could reach this point and the
     
    6464    // Support usage in themes - load this version if no plugin has loaded a version yet.
    6565    if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) {
    66         action_scheduler_initialize_3_dot_9_dot_2(); // WRCS: DEFINED_VERSION.
     66        action_scheduler_initialize_3_dot_9_dot_3(); // WRCS: DEFINED_VERSION.
    6767        do_action( 'action_scheduler_pre_theme_init' );
    6868        ActionScheduler_Versions::initialize_latest_version();
  • action-scheduler/trunk/changelog.txt

    r3233744 r3328081  
    11*** Changelog ***
     2
     3= 3.9.3 - 2025-07-15 =
     4* Add hook 'action_scheduler_ensure_recurring_actions' specifically for scheduling recurring actions.
     5* Assume an action is valid until proven otherwise.
     6* Implement SKIP LOCKED during action claiming.
     7* Import `get_flag_value()` from `WP_CLI\Utils` before using.
     8* Make `$unique` available to all pre-creation/short-circuit hooks.
     9* Make version/source information available via new class.
     10* Only release claims on pending actions.
     11* Tweak - WP 6.8 compatibility.
     12* Update minimum supported php and phpunit versions.
     13* Update readme.txt.
     14* WP CLI get action command: correct parentheses/nesting of conditional checks.
    215
    316= 3.9.2 - 2025-02-03 =
  • action-scheduler/trunk/classes/ActionScheduler_DataController.php

    r3189111 r3328081  
    163163        }
    164164
    165         $wp_object_cache->group_ops      = array();
    166         $wp_object_cache->stats          = array();
    167         $wp_object_cache->memcache_debug = array();
    168         $wp_object_cache->cache          = array();
     165        // Not all drop-ins support these props, however, there may be existing installations that rely on these being cleared.
     166        if ( property_exists( $wp_object_cache, 'group_ops' ) ) {
     167            $wp_object_cache->group_ops = array();
     168        }
     169        if ( property_exists( $wp_object_cache, 'stats' ) ) {
     170            $wp_object_cache->stats = array();
     171        }
     172        if ( property_exists( $wp_object_cache, 'memcache_debug' ) ) {
     173            $wp_object_cache->memcache_debug = array();
     174        }
     175        if ( property_exists( $wp_object_cache, 'cache' ) ) {
     176            $wp_object_cache->cache = array();
     177        }
    169178
    170179        if ( is_callable( array( $wp_object_cache, '__remoteset' ) ) ) {
  • action-scheduler/trunk/classes/ActionScheduler_wcSystemStatus.php

    r2622865 r3328081  
    7777        $action = $this->store->query_actions(
    7878            array(
    79                 'claimed'  => false,
    8079                'status'   => $status,
    8180                'per_page' => 1,
  • action-scheduler/trunk/classes/WP_CLI/Action/Create_Command.php

    r3226133 r3328081  
    22
    33namespace Action_Scheduler\WP_CLI\Action;
     4
     5use function \WP_CLI\Utils\get_flag_value;
    46
    57/**
  • action-scheduler/trunk/classes/WP_CLI/Action/Get_Command.php

    r3226133 r3328081  
    2525
    2626        $only_logs   = ! empty( $this->assoc_args['field'] ) && 'log_entries' === $this->assoc_args['field'];
    27         $only_logs   = $only_logs || ( ! empty( $this->assoc_args['fields'] && 'log_entries' === $this->assoc_args['fields'] ) );
     27        $only_logs   = $only_logs || ( ! empty( $this->assoc_args['fields'] ) && 'log_entries' === $this->assoc_args['fields'] );
    2828        $log_entries = array();
    2929
  • action-scheduler/trunk/classes/WP_CLI/System_Command.php

    r3233744 r3328081  
    263263
    264264        $args = array(
    265             'claimed'  => false,
    266265            'status'   => $status,
    267266            'per_page' => 1,
  • action-scheduler/trunk/classes/abstracts/ActionScheduler.php

    r3226133 r3328081  
    185185        ActionScheduler_DataController::init();
    186186
    187         $store      = self::store();
    188         $logger     = self::logger();
    189         $runner     = self::runner();
    190         $admin_view = self::admin_view();
     187        $store                      = self::store();
     188        $logger                     = self::logger();
     189        $runner                     = self::runner();
     190        $admin_view                 = self::admin_view();
     191        $recurring_action_scheduler = new ActionScheduler_RecurringActionScheduler();
    191192
    192193        // Ensure initialization on plugin activation.
     
    197198            add_action( 'init', array( $logger, 'init' ), 1, 0 );
    198199            add_action( 'init', array( $runner, 'init' ), 1, 0 );
     200            add_action( 'init', array( $recurring_action_scheduler, 'init' ), 1, 0 );
    199201
    200202            add_action(
     
    224226            $logger->init();
    225227            $runner->init();
     228            $recurring_action_scheduler->init();
    226229            self::$data_store_initialized = true;
    227230
  • action-scheduler/trunk/classes/abstracts/ActionScheduler_Abstract_QueueRunner.php

    r3189111 r3328081  
    8787        try {
    8888            try {
    89                 $valid_action = false;
     89                $valid_action = true;
     90
    9091                do_action( 'action_scheduler_before_execute', $action_id, $context );
    9192
    9293                if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) {
     94                    $valid_action = false;
    9395                    do_action( 'action_scheduler_execution_ignored', $action_id, $context );
    9496                    return;
    9597                }
    9698
    97                 $valid_action = true;
    9899                do_action( 'action_scheduler_begin_execute', $action_id, $context );
    99100
  • action-scheduler/trunk/classes/data-stores/ActionScheduler_DBStore.php

    r3189111 r3328081  
    932932         */
    933933        global $wpdb;
    934 
    935934        $now  = as_get_datetime_object();
    936935        $date = is_null( $before_date ) ? $now : clone $before_date;
    937         // can't use $wpdb->update() because of the <= condition.
    938         $update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s";
    939         $params = array(
    940             $claim_id,
    941             $now->format( 'Y-m-d H:i:s' ),
    942             current_time( 'mysql' ),
    943         );
    944936
    945937        // Set claim filters.
     
    955947        }
    956948
    957         $where    = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s';
    958         $params[] = $date->format( 'Y-m-d H:i:s' );
    959         $params[] = self::STATUS_PENDING;
     949        $where        = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s';
     950        $where_params = array(
     951            $date->format( 'Y-m-d H:i:s' ),
     952            self::STATUS_PENDING,
     953        );
    960954
    961955        if ( ! empty( $hooks ) ) {
    962956            $placeholders = array_fill( 0, count( $hooks ), '%s' );
    963             $where       .= ' AND hook IN (' . join( ', ', $placeholders ) . ')';
    964             $params       = array_merge( $params, array_values( $hooks ) );
     957            $where        .= ' AND hook IN (' . join( ', ', $placeholders ) . ')';
     958            $where_params = array_merge( $where_params, array_values( $hooks ) );
    965959        }
    966960
     
    997991         * Sets the order-by clause used in the action claim query.
    998992         *
    999          * @since 3.4.0
    1000          * @since 3.8.3 Made $claim_id and $hooks available.
    1001          *
    1002993         * @param string $order_by_sql
    1003994         * @param string $claim_id Claim Id.
    1004          * @param array  $hooks Hooks to filter for.
    1005          */
    1006         $order    = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC', $claim_id, $hooks );
    1007         $params[] = $limit;
    1008 
    1009         $sql           = $wpdb->prepare( "{$update} {$where} {$order} LIMIT %d", $params ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders
    1010         $rows_affected = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     995         * @param array  $hooks    Hooks to filter for.
     996         *
     997         * @since 3.8.3 Made $claim_id and $hooks available.
     998         * @since 3.4.0
     999         */
     1000        $order       = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC', $claim_id, $hooks );
     1001        $skip_locked = $this->db_supports_skip_locked() ? ' SKIP LOCKED' : '';
     1002
     1003        // Selecting the action_ids that we plan to claim, while skipping any locked rows to avoid deadlocking.
     1004        $select_sql = $wpdb->prepare( "SELECT action_id from {$wpdb->actionscheduler_actions} {$where} {$order} LIMIT %d FOR UPDATE{$skip_locked}", array_merge( $where_params, array( $limit ) ) );
     1005
     1006        // Now place it into an UPDATE statement by joining the result sets, allowing for the SKIP LOCKED behavior to take effect.
     1007        $update_sql    = "UPDATE {$wpdb->actionscheduler_actions} t1 JOIN ( $select_sql ) t2 ON t1.action_id = t2.action_id SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s";
     1008        $update_params = array(
     1009            $claim_id,
     1010            $now->format( 'Y-m-d H:i:s' ),
     1011            current_time( 'mysql' ),
     1012        );
     1013
     1014        $rows_affected = $wpdb->query( $wpdb->prepare( $update_sql, $update_params ) );
    10111015        if ( false === $rows_affected ) {
    10121016            $error = empty( $wpdb->last_error )
    10131017                ? _x( 'unknown', 'database error', 'action-scheduler' )
    10141018                : $wpdb->last_error;
    1015 
    10161019            throw new \RuntimeException(
    10171020                sprintf(
     
    10271030
    10281031    /**
     1032     * Determines whether the database supports using SKIP LOCKED. This logic mimicks the $wpdb::has_cap() logic.
     1033     *
     1034     * SKIP_LOCKED support was added to MariaDB in 10.6.0 and to MySQL in 8.0.1
     1035     *
     1036     * @return bool
     1037     */
     1038    private function db_supports_skip_locked() {
     1039        global $wpdb;
     1040        $db_version     = $wpdb->db_version();
     1041        $db_server_info = $wpdb->db_server_info();
     1042        $is_mariadb     = ( false !== strpos( $db_server_info, 'MariaDB' ) );
     1043
     1044        if ( $is_mariadb &&
     1045             '5.5.5' === $db_version &&
     1046             PHP_VERSION_ID < 80016 // PHP 8.0.15 or older.
     1047        ) {
     1048            /*
     1049             * Account for MariaDB version being prefixed with '5.5.5-' on older PHP versions.
     1050             */
     1051            $db_server_info = preg_replace( '/^5\.5\.5-(.*)/', '$1', $db_server_info );
     1052            $db_version     = preg_replace( '/[^0-9.].*/', '', $db_server_info );
     1053        }
     1054
     1055        $is_supported = ( $is_mariadb && version_compare( $db_version, '10.6.0', '>=' ) ) ||
     1056                        ( ! $is_mariadb && version_compare( $db_version, '8.0.1', '>=' ) );
     1057
     1058        /**
     1059         * Filter whether the database supports the SKIP LOCKED modifier for queries.
     1060         *
     1061         * @param bool $is_supported Whether SKIP LOCKED is supported.
     1062         *
     1063         * @since 3.9.3
     1064         */
     1065        return apply_filters( 'action_scheduler_db_supports_skip_locked', $is_supported );
     1066    }
     1067
     1068    /**
    10291069     * Get the number of active claims.
    10301070     *
     
    10951135
    10961136    /**
    1097      * Release actions from a claim and delete the claim.
     1137     * Release pending actions from a claim and delete the claim.
    10981138     *
    10991139     * @param ActionScheduler_ActionClaim $claim Claim object.
     
    11071147         */
    11081148        global $wpdb;
     1149
     1150        if ( 0 === intval( $claim->get_id() ) ) {
     1151            // Verify that the claim_id is valid before attempting to release it.
     1152            return;
     1153        }
    11091154
    11101155        /**
     
    11141159         *
    11151160         * We resolve this by getting all the actions_id that we want to release claim from in a separate query, and then releasing the claim on each of them. This way, our lock is acquired on the action_id index instead of the claim_id index. Note that the lock on claim_id will still be acquired, but it will only when we actually make the update, rather than when we select the actions.
    1116          */
    1117         $action_ids = $wpdb->get_col( $wpdb->prepare( "SELECT action_id FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d", $claim->get_id() ) );
     1161         *
     1162         * We only release pending actions in order for them to be claimed by another process.
     1163         */
     1164        $action_ids = $wpdb->get_col( $wpdb->prepare( "SELECT action_id FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d AND status = %s", $claim->get_id(), self::STATUS_PENDING ) );
    11181165
    11191166        $row_updates = 0;
  • action-scheduler/trunk/classes/data-stores/ActionScheduler_HybridStore.php

    r3189111 r3328081  
    433433
    434434    /**
    435      * Release a claim in the table data store.
     435     * Release a claim in the table data store on any pending actions.
    436436     *
    437437     * @param ActionScheduler_ActionClaim $claim Claim object.
  • action-scheduler/trunk/classes/data-stores/ActionScheduler_wpPostStore.php

    r3189111 r3328081  
    792792
    793793    /**
    794      * Release claim.
     794     * Release pending actions from a claim.
    795795     *
    796796     * @param ActionScheduler_ActionClaim $claim Claim object to release.
     
    799799     */
    800800    public function release_claim( ActionScheduler_ActionClaim $claim ) {
    801         $action_ids = $this->find_actions_by_claim_id( $claim->get_id() );
    802         if ( empty( $action_ids ) ) {
    803             return; // nothing to do.
    804         }
    805         $action_id_string = implode( ',', array_map( 'intval', $action_ids ) );
    806801        /**
    807802         * Global wpdb object.
     
    810805         */
    811806        global $wpdb;
     807
     808        $claim_id = $claim->get_id();
     809        if ( trim( $claim_id ) === '' ) {
     810            // Verify that the claim_id is valid before attempting to release it.
     811            return;
     812        }
     813
     814        // Only attempt to release pending actions to be claimed again. Running and complete actions are no longer relevant outside of admin/analytics.
     815        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     816        $action_ids = $wpdb->get_col(
     817            $wpdb->prepare(
     818                "SELECT ID, post_date_gmt FROM {$wpdb->posts} WHERE post_type = %s AND post_password = %s AND post_status = %s",
     819                self::POST_TYPE,
     820                $claim_id,
     821                self::STATUS_PENDING
     822            )
     823        );
     824
     825        if ( empty( $action_ids ) ) {
     826            return; // nothing to do.
     827        }
     828        $action_id_string = implode( ',', array_map( 'intval', $action_ids ) );
    812829
    813830        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
  • action-scheduler/trunk/classes/schema/ActionScheduler_StoreSchema.php

    r3189111 r3328081  
    2121     * @var int
    2222     */
    23     protected $schema_version = 7;
     23    protected $schema_version = 8;
    2424
    2525    /**
     
    8181                        KEY group_id (group_id),
    8282                        KEY last_attempt_gmt (last_attempt_gmt),
    83                         KEY `claim_id_status_scheduled_date_gmt` (`claim_id`, `status`, `scheduled_date_gmt`)
     83                        KEY `claim_id_status_priority_scheduled_date_gmt` (`claim_id`,`status`,`priority`,`scheduled_date_gmt`),
     84                        KEY `status_last_attempt_gmt` (`status`,`last_attempt_gmt`),
     85                        KEY `status_claim_id` (`status`,`claim_id`)
    8486                        ) $charset_collate";
    8587
  • action-scheduler/trunk/functions.php

    r3189111 r3328081  
    8787     * @param string   $group      Action group.
    8888     * @param int      $priorities Action priority.
     89     * @param bool     $unique     Unique action.
    8990     */
    90     $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group, $priority );
     91    $pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group, $priority, $unique );
    9192    if ( null !== $pre ) {
    9293        return is_int( $pre ) ? $pre : 0;
     
    160161     * @param string   $group               Action group.
    161162     * @param int      $priority            Action priority.
     163     * @param bool     $unique              Unique action.
    162164     */
    163     $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group, $priority );
     165    $pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group, $priority, $unique );
    164166    if ( null !== $pre ) {
    165167        return is_int( $pre ) ? $pre : 0;
     
    226228     * @param string   $group      Action group.
    227229     * @param int      $priority   Action priority.
     230     * @param bool     $unique     Unique action.
    228231     */
    229     $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group, $priority );
     232    $pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group, $priority, $unique );
    230233    if ( null !== $pre ) {
    231234        return is_int( $pre ) ? $pre : 0;
     
    494497    return $date;
    495498}
     499
     500/**
     501 * Check if a specific feature is supported by the current version of Action Scheduler.
     502 *
     503 * @since 3.9.3
     504 *
     505 * @param string $feature The feature to check support for.
     506 *
     507 * @return bool True if the feature is supported, false otherwise.
     508 */
     509function as_supports( string $feature ): bool {
     510    $supported_features = array( 'ensure_recurring_actions_hook' );
     511
     512    return in_array( $feature, $supported_features, true );
     513}
  • action-scheduler/trunk/readme.txt

    r3233744 r3328081  
    22Contributors: Automattic, wpmuguru, claudiosanches, peterfabian1000, vedjain, jamosova, obliviousharmony, konamiman, sadowski, royho, barryhughes-1
    33Tags: scheduler, cron
    4 Stable tag: 3.9.2
     4Stable tag: 3.9.3
    55License: GPLv3
    66Requires at least: 6.5
    7 Tested up to: 6.7
    8 Requires PHP: 7.1
     7Tested up to: 6.8
     8Requires PHP: 7.2
    99
    1010Action Scheduler - Job Queue for WordPress
     
    3030## Learn More
    3131
    32 To learn more about how to Action Scheduler works, and how to use it in your plugin, check out the docs on [ActionScheduler.org](https://actionscheduler.org).
     32To learn more about how Action Scheduler works, and how to use it in your plugin, check out the docs on [ActionScheduler.org](https://actionscheduler.org).
    3333
    3434There you will find:
     
    4747
    4848== Changelog ==
     49
     50= 3.9.3 - 2025-07-15 =
     51* Add hook 'action_scheduler_ensure_recurring_actions' specifically for scheduling recurring actions.
     52* Assume an action is valid until proven otherwise.
     53* Implement SKIP LOCKED during action claiming.
     54* Import `get_flag_value()` from `WP_CLI\Utils` before using.
     55* Make `$unique` available to all pre-creation/short-circuit hooks.
     56* Make version/source information available via new class.
     57* Only release claims on pending actions.
     58* Tweak - WP 6.8 compatibility.
     59* Update minimum supported php and phpunit versions.
     60* Update readme.txt.
     61* WP CLI get action command: correct parentheses/nesting of conditional checks.
    4962
    5063= 3.9.2 - 2025-02-03 =
  • action-scheduler/trunk/vendor/autoload.php

    r3151094 r3328081  
    2323require_once __DIR__ . '/composer/autoload_real.php';
    2424
    25 return ComposerAutoloaderInit81fd3951ccfed0a222e6f4b949998df1::getLoader();
     25return ComposerAutoloaderInit55c846ad8e6e369a91b7de2e9976f685::getLoader();
  • action-scheduler/trunk/vendor/composer/InstalledVersions.php

    r3151094 r3328081  
    323323
    324324        $installed = array();
     325        $copiedLocalDir = false;
    325326
    326327        if (self::$canGetVendors) {
     
    331332                    /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
    332333                    $required = require $vendorDir.'/composer/installed.php';
    333                     $installed[] = self::$installedByVendor[$vendorDir] = $required;
    334                     if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
    335                         self::$installed = $installed[count($installed) - 1];
     334                    self::$installedByVendor[$vendorDir] = $required;
     335                    $installed[] = $required;
     336                    if (strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
     337                        self::$installed = $required;
     338                        $copiedLocalDir = true;
    336339                    }
    337340                }
     
    351354        }
    352355
    353         if (self::$installed !== array()) {
     356        if (self::$installed !== array() && !$copiedLocalDir) {
    354357            $installed[] = self::$installed;
    355358        }
  • action-scheduler/trunk/vendor/composer/autoload_real.php

    r3151094 r3328081  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInit81fd3951ccfed0a222e6f4b949998df1
     5class ComposerAutoloaderInit55c846ad8e6e369a91b7de2e9976f685
    66{
    77    private static $loader;
     
    2525        require __DIR__ . '/platform_check.php';
    2626
    27         spl_autoload_register(array('ComposerAutoloaderInit81fd3951ccfed0a222e6f4b949998df1', 'loadClassLoader'), true, true);
     27        spl_autoload_register(array('ComposerAutoloaderInit55c846ad8e6e369a91b7de2e9976f685', 'loadClassLoader'), true, true);
    2828        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
    29         spl_autoload_unregister(array('ComposerAutoloaderInit81fd3951ccfed0a222e6f4b949998df1', 'loadClassLoader'));
     29        spl_autoload_unregister(array('ComposerAutoloaderInit55c846ad8e6e369a91b7de2e9976f685', 'loadClassLoader'));
    3030
    3131        require __DIR__ . '/autoload_static.php';
    32         call_user_func(\Composer\Autoload\ComposerStaticInit81fd3951ccfed0a222e6f4b949998df1::getInitializer($loader));
     32        call_user_func(\Composer\Autoload\ComposerStaticInit55c846ad8e6e369a91b7de2e9976f685::getInitializer($loader));
    3333
    3434        $loader->register(true);
  • action-scheduler/trunk/vendor/composer/autoload_static.php

    r3151094 r3328081  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInit81fd3951ccfed0a222e6f4b949998df1
     7class ComposerStaticInit55c846ad8e6e369a91b7de2e9976f685
    88{
    99    public static $classMap = array (
     
    1414    {
    1515        return \Closure::bind(function () use ($loader) {
    16             $loader->classMap = ComposerStaticInit81fd3951ccfed0a222e6f4b949998df1::$classMap;
     16            $loader->classMap = ComposerStaticInit55c846ad8e6e369a91b7de2e9976f685::$classMap;
    1717
    1818        }, null, ClassLoader::class);
  • action-scheduler/trunk/vendor/composer/installed.php

    r3233744 r3328081  
    22    'root' => array(
    33        'name' => 'woocommerce/action-scheduler',
    4         'pretty_version' => 'dev-release/3.9.2',
    5         'version' => 'dev-release/3.9.2',
    6         'reference' => 'efbb7953f72a433086335b249292f280dd43ddfe',
     4        'pretty_version' => 'dev-release/3.9.3',
     5        'version' => 'dev-release/3.9.3',
     6        'reference' => 'c58cdbab17651303d406cd3b22cf9d75c71c986c',
    77        'type' => 'wordpress-plugin',
    88        'install_path' => __DIR__ . '/../../',
     
    1212    'versions' => array(
    1313        'woocommerce/action-scheduler' => array(
    14             'pretty_version' => 'dev-release/3.9.2',
    15             'version' => 'dev-release/3.9.2',
    16             'reference' => 'efbb7953f72a433086335b249292f280dd43ddfe',
     14            'pretty_version' => 'dev-release/3.9.3',
     15            'version' => 'dev-release/3.9.3',
     16            'reference' => 'c58cdbab17651303d406cd3b22cf9d75c71c986c',
    1717            'type' => 'wordpress-plugin',
    1818            'install_path' => __DIR__ . '/../../',
  • action-scheduler/trunk/vendor/composer/platform_check.php

    r3189111 r3328081  
    55$issues = array();
    66
    7 if (!(PHP_VERSION_ID >= 70100)) {
    8     $issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.0". You are running ' . PHP_VERSION . '.';
     7if (!(PHP_VERSION_ID >= 70200)) {
     8    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.';
    99}
    1010
Note: See TracChangeset for help on using the changeset viewer.