Full Code of Prospress/action-scheduler for AI

trunk 25c982c3d0f8 cached
164 files
818.7 KB
228.7k tokens
1048 symbols
1 requests
Download .txt
Showing preview only (871K chars total). Download the full file or copy to clipboard to get everything.
Repository: Prospress/action-scheduler
Branch: trunk
Commit: 25c982c3d0f8
Files: 164
Total size: 818.7 KB

Directory structure:
gitextract_minlvy_t/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── release-drafter.yml
│   └── workflows/
│       └── pr-unit-tests.yml
├── .gitignore
├── Gruntfile.js
├── README.md
├── action-scheduler.php
├── changelog.txt
├── classes/
│   ├── ActionScheduler_ActionClaim.php
│   ├── ActionScheduler_ActionFactory.php
│   ├── ActionScheduler_AdminView.php
│   ├── ActionScheduler_AsyncRequest_QueueRunner.php
│   ├── ActionScheduler_Compatibility.php
│   ├── ActionScheduler_DataController.php
│   ├── ActionScheduler_DateTime.php
│   ├── ActionScheduler_Exception.php
│   ├── ActionScheduler_FatalErrorMonitor.php
│   ├── ActionScheduler_InvalidActionException.php
│   ├── ActionScheduler_ListTable.php
│   ├── ActionScheduler_LogEntry.php
│   ├── ActionScheduler_NullLogEntry.php
│   ├── ActionScheduler_OptionLock.php
│   ├── ActionScheduler_QueueCleaner.php
│   ├── ActionScheduler_QueueRunner.php
│   ├── ActionScheduler_RecurringActionScheduler.php
│   ├── ActionScheduler_SystemInformation.php
│   ├── ActionScheduler_Versions.php
│   ├── ActionScheduler_WPCommentCleaner.php
│   ├── ActionScheduler_wcSystemStatus.php
│   ├── WP_CLI/
│   │   ├── Action/
│   │   │   ├── Cancel_Command.php
│   │   │   ├── Create_Command.php
│   │   │   ├── Delete_Command.php
│   │   │   ├── Generate_Command.php
│   │   │   ├── Get_Command.php
│   │   │   ├── List_Command.php
│   │   │   ├── Next_Command.php
│   │   │   └── Run_Command.php
│   │   ├── ActionScheduler_WPCLI_Clean_Command.php
│   │   ├── ActionScheduler_WPCLI_QueueRunner.php
│   │   ├── ActionScheduler_WPCLI_Scheduler_command.php
│   │   ├── Action_Command.php
│   │   ├── Migration_Command.php
│   │   ├── ProgressBar.php
│   │   └── System_Command.php
│   ├── abstracts/
│   │   ├── ActionScheduler.php
│   │   ├── ActionScheduler_Abstract_ListTable.php
│   │   ├── ActionScheduler_Abstract_QueueRunner.php
│   │   ├── ActionScheduler_Abstract_RecurringSchedule.php
│   │   ├── ActionScheduler_Abstract_Schedule.php
│   │   ├── ActionScheduler_Abstract_Schema.php
│   │   ├── ActionScheduler_Lock.php
│   │   ├── ActionScheduler_Logger.php
│   │   ├── ActionScheduler_Store.php
│   │   ├── ActionScheduler_TimezoneHelper.php
│   │   └── ActionScheduler_WPCLI_Command.php
│   ├── actions/
│   │   ├── ActionScheduler_Action.php
│   │   ├── ActionScheduler_CanceledAction.php
│   │   ├── ActionScheduler_FinishedAction.php
│   │   └── ActionScheduler_NullAction.php
│   ├── data-stores/
│   │   ├── ActionScheduler_DBLogger.php
│   │   ├── ActionScheduler_DBStore.php
│   │   ├── ActionScheduler_HybridStore.php
│   │   ├── ActionScheduler_wpCommentLogger.php
│   │   ├── ActionScheduler_wpPostStore.php
│   │   ├── ActionScheduler_wpPostStore_PostStatusRegistrar.php
│   │   ├── ActionScheduler_wpPostStore_PostTypeRegistrar.php
│   │   └── ActionScheduler_wpPostStore_TaxonomyRegistrar.php
│   ├── migration/
│   │   ├── ActionMigrator.php
│   │   ├── ActionScheduler_DBStoreMigrator.php
│   │   ├── BatchFetcher.php
│   │   ├── Config.php
│   │   ├── Controller.php
│   │   ├── DryRun_ActionMigrator.php
│   │   ├── DryRun_LogMigrator.php
│   │   ├── LogMigrator.php
│   │   ├── Runner.php
│   │   └── Scheduler.php
│   ├── schedules/
│   │   ├── ActionScheduler_CanceledSchedule.php
│   │   ├── ActionScheduler_CronSchedule.php
│   │   ├── ActionScheduler_IntervalSchedule.php
│   │   ├── ActionScheduler_NullSchedule.php
│   │   ├── ActionScheduler_Schedule.php
│   │   └── ActionScheduler_SimpleSchedule.php
│   └── schema/
│       ├── ActionScheduler_LoggerSchema.php
│       └── ActionScheduler_StoreSchema.php
├── codecov.yml
├── composer.json
├── deprecated/
│   ├── ActionScheduler_Abstract_QueueRunner_Deprecated.php
│   ├── ActionScheduler_AdminView_Deprecated.php
│   ├── ActionScheduler_Schedule_Deprecated.php
│   ├── ActionScheduler_Store_Deprecated.php
│   └── functions.php
├── docs/
│   ├── CNAME
│   ├── _config.yml
│   ├── _layouts/
│   │   └── default.html
│   ├── admin.md
│   ├── api.md
│   ├── assets/
│   │   └── css/
│   │       └── style.scss
│   ├── browserconfig.xml
│   ├── faq.md
│   ├── google14ef723abb376cd3.html
│   ├── index.md
│   ├── perf.md
│   ├── site.webmanifest
│   ├── usage.md
│   ├── version3-0.md
│   └── wp-cli.md
├── functions.php
├── lib/
│   ├── WP_Async_Request.php
│   └── cron-expression/
│       ├── CronExpression.php
│       ├── CronExpression_AbstractField.php
│       ├── CronExpression_DayOfMonthField.php
│       ├── CronExpression_DayOfWeekField.php
│       ├── CronExpression_FieldFactory.php
│       ├── CronExpression_FieldInterface.php
│       ├── CronExpression_HoursField.php
│       ├── CronExpression_MinutesField.php
│       ├── CronExpression_MonthField.php
│       ├── CronExpression_YearField.php
│       ├── LICENSE
│       └── README.md
├── license.txt
├── package.json
├── phpcs.xml
├── readme.txt
└── tests/
    ├── ActionScheduler_UnitTestCase.php
    ├── README.md
    ├── bin/
    │   └── install.sh
    ├── bootstrap.php
    ├── phpunit/
    │   ├── ActionScheduler_Mock_Async_Request_QueueRunner.php
    │   ├── ActionScheduler_Mocker.php
    │   ├── ActionScheduler_RecurringActionScheduler_Test.php
    │   ├── deprecated/
    │   │   └── ActionScheduler_UnitTestCase.php
    │   ├── helpers/
    │   │   ├── ActionScheduler_Callbacks.php
    │   │   ├── ActionScheduler_Compatibility_Test.php
    │   │   └── ActionScheduler_TimezoneHelper_Test.php
    │   ├── jobs/
    │   │   ├── ActionScheduler_Action_Test.php
    │   │   └── ActionScheduler_NullAction_Test.php
    │   ├── jobstore/
    │   │   ├── AbstractStoreTest.php
    │   │   ├── ActionScheduler_DBStoreMigrator_Test.php
    │   │   ├── ActionScheduler_DBStore_Test.php
    │   │   ├── ActionScheduler_HybridStore_Test.php
    │   │   └── ActionScheduler_wpPostStore_Test.php
    │   ├── lock/
    │   │   └── ActionScheduler_OptionLock_Test.php
    │   ├── logging/
    │   │   ├── ActionScheduler_DBLogger_Test.php
    │   │   └── ActionScheduler_wpCommentLogger_Test.php
    │   ├── migration/
    │   │   ├── ActionMigrator_Test.php
    │   │   ├── BatchFetcher_Test.php
    │   │   ├── Config_Test.php
    │   │   ├── Controller_Test.php
    │   │   ├── LogMigrator_Test.php
    │   │   ├── Runner_Test.php
    │   │   └── Scheduler_Test.php
    │   ├── procedural_api/
    │   │   ├── procedural_api_Test.php
    │   │   └── wc_get_scheduled_actions_Test.php
    │   ├── runner/
    │   │   ├── ActionScheduler_QueueCleaner_Test.php
    │   │   └── ActionScheduler_QueueRunner_Test.php
    │   ├── schedules/
    │   │   ├── ActionScheduler_CronSchedule_Test.php
    │   │   ├── ActionScheduler_IntervalSchedule_Test.php
    │   │   ├── ActionScheduler_NullSchedule_Test.php
    │   │   └── ActionScheduler_SimpleSchedule_Test.php
    │   └── versioning/
    │       └── ActionScheduler_Versions_Test.php
    └── phpunit.xml.dist

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# This file is for unifying the coding style for different editors and IDEs
# editorconfig.org

# WordPress Coding Standards
# https://make.wordpress.org/core/handbook/coding-standards/

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
tab_width = 4
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true

[*.txt]
trim_trailing_whitespace = false

[*.{md,json,yml}]
trim_trailing_whitespace = false
indent_style = space
indent_size = 2


================================================
FILE: .gitattributes
================================================
docs export-ignore
tests export-ignore
codecov.yml export-ignore
.editorconfig export-ignore
.github export-ignore
.travis.yml export-ignore
.gitattributes export-ignore
.gitignore export-ignore
composer.* export-ignore
Gruntfile.js export-ignore
package.json export-ignore
package-lock.json export-ignore
phpcs.xml export-ignore
phpunit.* export-ignore


================================================
FILE: .github/release-drafter.yml
================================================
template: |
  ## next release – date

  <!-- Move the individual changes below into the appropriate section -->

  $CHANGES

  **Added**
  **Changed**
  **Deprecated**
  **Removed**
  **Fixed**
  **Security**

change-template: '* $TITLE (PR #$NUMBER)'


================================================
FILE: .github/workflows/pr-unit-tests.yml
================================================
name: Run unit tests on PR
on: 
  pull_request
jobs:
  test:
    name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} MU ${{ matrix.multisite }} DB ${{ matrix.db }}
    timeout-minutes: 15
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        # We test against the earliest and latest PHP versions for each major supported version.
        php: [ '7.2', '7.4', '8.0', '8.3' ]
        wp: [ '6.5', '6.6', 'latest', 'nightly' ]
        multisite: [ '0', '1' ]
        db: [ 'mysql:5.6', 'mysql:8.1', 'mariadb:10.4', 'mariadb:10.6']
        exclude:
          # WordPress 6.6+ requires PHP 7.2+
          - php: 7.2
            wp: 6.6
          - php: 7.2
            wp: latest
          - php: 7.2
            wp: nightly
    services:
      database:
        image: ${{ matrix.db }}
        env:
          MYSQL_ROOT_PASSWORD: root
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Setup PHP
        uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
        with:
          php-version: ${{ matrix.php }}
          tools: composer
          extensions: mysql
          coverage: none

      - name: Tool versions
        run: |
          php --version
          composer --version

      - name: Get composer cache directory
        id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Install dependencies
        run: composer install --prefer-dist

      - name: Install Subversion
        run: sudo apt-get update && sudo apt-get install -y subversion

      - name: Init DB and WP
        run: |
          # Use mysql_native_password when using PHP < 7.4 and MySQL >= 8.0
          if [ "$(php -r 'echo version_compare(PHP_VERSION, "7.4", "<");')" -eq 1 ] && [ "${{ matrix.db }}" == "mysql:8.1" ]; then
            mysql -uroot -proot -h127.0.0.1 -e "ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root'; FLUSH PRIVILEGES;"
          fi
          ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}

      - name: Run tests
        run: |
          ./vendor/bin/phpunit --version
          WP_MULTISITE=${{ matrix.multisite }} ./vendor/bin/phpunit -c ./tests/phpunit.xml.dist

      - name: Code Coverage
        run: |
          bash <(curl -s https://codecov.io/bash)


================================================
FILE: .gitignore
================================================
# Operating System files
.DS_Store
Thumbs.db

# IDE files
.idea
.vscode/*
project.xml
project.properties
.project
.settings*
*.sublime-project
*.sublime-workspace
.sublimelinterrc

# Project files
node_modules/
vendor/

#PHP Unit
.phpunit.result.cache
phpunit.xml

# Build files
action-scheduler.zip


================================================
FILE: Gruntfile.js
================================================
module.exports = function( grunt ) {
	'use strict';

	grunt.initConfig({
		// Check textdomain errors.
		checktextdomain: {
			options:{
				text_domain: 'action-scheduler',
				keywords: [
					'__:1,2d',
					'_e:1,2d',
					'_x:1,2c,3d',
					'esc_html__:1,2d',
					'esc_html_e:1,2d',
					'esc_html_x:1,2c,3d',
					'esc_attr__:1,2d',
					'esc_attr_e:1,2d',
					'esc_attr_x:1,2c,3d',
					'_ex:1,2c,3d',
					'_n:1,2,4d',
					'_nx:1,2,4c,5d',
					'_n_noop:1,2,3d',
					'_nx_noop:1,2,3c,4d'
				]
			},
			files: {
				src:  [
					'**/*.php',
					'!node_modules/**',
					'!tests/**',
					'!vendor/**',
					'!tmp/**'
				],
				expand: true
			}
		},

		// PHP Code Sniffer.
		phpcs: {
			options: {
				bin: 'vendor/bin/phpcs'
			},
			dist: {
				src:  [
					'**/*.php', // Include all php files.
					'!deprecated/**',
					'!node_modules/**',
					'!vendor/**'
				]
			}
		}
	});

	// Load NPM tasks to be used here.
	grunt.loadNpmTasks( 'grunt-phpcs' );
	grunt.loadNpmTasks( 'grunt-checktextdomain' );
};


================================================
FILE: README.md
================================================
# Action Scheduler - Job Queue for WordPress [![Build Status](https://travis-ci.org/woocommerce/action-scheduler.png?branch=master)](https://travis-ci.org/woocommerce/action-scheduler) [![codecov](https://codecov.io/gh/woocommerce/action-scheduler/branch/master/graph/badge.svg)](https://codecov.io/gh/woocommerce/action-scheduler)

Action Scheduler is a scalable, traceable job queue for background processing large sets of actions in WordPress. It's specially designed to be distributed in WordPress plugins.

Action Scheduler works by triggering an action hook to run at some time in the future. Each hook can be scheduled with unique data, to allow callbacks to perform operations on that data. The hook can also be scheduled to run on one or more occasions.

Think of it like an extension to `do_action()` which adds the ability to delay and repeat a hook.

## Battle-Tested Background Processing

Every month, Action Scheduler processes millions of payments for [Subscriptions](https://woocommerce.com/products/woocommerce-subscriptions/), webhooks for [WooCommerce](https://wordpress.org/plugins/woocommerce/), as well as emails and other events for a range of other plugins.

It's been seen on live sites processing queues in excess of 50,000 jobs and doing resource intensive operations, like processing payments and creating orders, at a sustained rate of over 10,000 / hour without negatively impacting normal site operations.

This is all on infrastructure and WordPress sites outside the control of the plugin author.

If your plugin needs background processing, especially of large sets of tasks, Action Scheduler can help.

## Learn More

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).

There you will find:

* [Usage guide](https://actionscheduler.org/usage/): instructions on installing and using Action Scheduler
* [WP CLI guide](https://actionscheduler.org/wp-cli/): instructions on running Action Scheduler at scale via WP CLI
* [API Reference](https://actionscheduler.org/api/): complete reference guide for all API functions
* [Administration Guide](https://actionscheduler.org/admin/): guide to managing scheduled actions via the administration screen
* [Guide to Background Processing at Scale](https://actionscheduler.org/perf/): instructions for running Action Scheduler at scale via the default WP Cron queue runner

## Credits

Action Scheduler is developed and maintained by [Automattic](http://automattic.com/) with significant early development completed by [Flightless](https://flightless.us/).

Collaboration is cool. We'd love to work with you to improve Action Scheduler. [Pull Requests](https://github.com/woocommerce/action-scheduler/pulls) welcome.


================================================
FILE: action-scheduler.php
================================================
<?php
/**
 * Plugin Name: Action Scheduler
 * Plugin URI: https://actionscheduler.org
 * Description: A robust scheduling library for use in WordPress plugins.
 * Author: Automattic
 * Author URI: https://automattic.com/
 * Version: 3.9.3
 * License: GPLv3
 * Requires at least: 6.5
 * Tested up to: 6.8
 * Requires PHP: 7.2
 *
 * Copyright 2019 Automattic, Inc.  (https://automattic.com/contact/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @package ActionScheduler
 */

if ( ! function_exists( 'action_scheduler_register_3_dot_9_dot_3' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.

	if ( ! class_exists( 'ActionScheduler_Versions', false ) ) {
		require_once __DIR__ . '/classes/ActionScheduler_Versions.php';
		add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
	}

	add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_9_dot_3', 0, 0 ); // WRCS: DEFINED_VERSION.

	// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
	/**
	 * Registers this version of Action Scheduler.
	 */
	function action_scheduler_register_3_dot_9_dot_3() { // WRCS: DEFINED_VERSION.
		$versions = ActionScheduler_Versions::instance();
		$versions->register( '3.9.3', 'action_scheduler_initialize_3_dot_9_dot_3' ); // WRCS: DEFINED_VERSION.
	}

	// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
	/**
	 * Initializes this version of Action Scheduler.
	 */
	function action_scheduler_initialize_3_dot_9_dot_3() { // WRCS: DEFINED_VERSION.
		// A final safety check is required even here, because historic versions of Action Scheduler
		// followed a different pattern (in some unusual cases, we could reach this point and the
		// ActionScheduler class is already defined—so we need to guard against that).
		if ( ! class_exists( 'ActionScheduler', false ) ) {
			require_once __DIR__ . '/classes/abstracts/ActionScheduler.php';
			ActionScheduler::init( __FILE__ );
		}
	}

	// Support usage in themes - load this version if no plugin has loaded a version yet.
	if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) {
		action_scheduler_initialize_3_dot_9_dot_3(); // WRCS: DEFINED_VERSION.
		do_action( 'action_scheduler_pre_theme_init' );
		ActionScheduler_Versions::initialize_latest_version();
	}
}


================================================
FILE: changelog.txt
================================================
*** Changelog ***

= 3.9.3 - 2025-07-15 =
* Add hook 'action_scheduler_ensure_recurring_actions' specifically for scheduling recurring actions.
* Assume an action is valid until proven otherwise.
* Implement SKIP LOCKED during action claiming.
* Import `get_flag_value()` from `WP_CLI\Utils` before using.
* Make `$unique` available to all pre-creation/short-circuit hooks.
* Make version/source information available via new class.
* Only release claims on pending actions.
* Tweak - WP 6.8 compatibility.
* Update minimum supported php and phpunit versions.
* Update readme.txt.
* WP CLI get action command: correct parentheses/nesting of conditional checks.

= 3.9.2 - 2025-02-03 =
* Fixed fatal errors by moving version info methods to a new class and deprecating conflicting ones in ActionScheduler_Versions

= 3.9.1 - 2025-01-21 =
* A number of new WP CLI commands have been added, making it easier to manage actions in the terminal and from scripts.
* New wp action-scheduler source command to help determine how Action Scheduler is being loaded.
* Additional information about the active instance of Action Scheduler is now available in the Help pull-down drawer.
* Make some other nullable parameters explicitly nullable.
* Set option value to `no` rather than deleting.

= 3.9.0 - 2024-11-14 =  
* Minimum required version of PHP is now 7.1.  
* Performance improvements for the `as_pending_actions_due()` function.  
* Existing filter hook `action_scheduler_claim_actions_order_by` enhanced to provide callbacks with additional information.  
* Improved compatibility with PHP 8.4, specifically by making implicitly nullable parameters explicitly nullable.  
* A large number of coding standards-enhancements, to help reduce friction when submitting plugins to marketplaces and plugin directories. Special props @crstauf for this effort.  
* Minor documentation tweaks and improvements.

= 3.8.2 - 2024-09-12 =
* Add missing parameter to the `pre_as_enqueue_async_action` hook.
* Bump minimum PHP version to 7.0.
* Bump minimum WordPress version to 6.4.
* Make the batch size adjustable during processing.

= 3.8.1 - 2024-06-20 =
* Fix typos.
* Improve the messaging in our unidentified action exceptions.

= 3.8.0 - 2024-05-22 =
* Documentation - Fixed typos in perf.md.
* Update - We now require WordPress 6.3 or higher.
* Update - We now require PHP 7.0 or higher.

= 3.7.4 - 2024-04-05 =
* Give a clear description of how the $unique parameter works.
* Preserve the tab field if set.
* Tweak - WP 6.5 compatibility.

= 3.7.3 - 2024-03-20 =
* Do not iterate over all of GET when building form in list table.
* Fix a few issues reported by PCP (Plugin Check Plugin).
* Try to save actions as unique even when the store doesn't support it.
* Tweak - WP 6.4 compatibility.
* Update "Tested up to" tag to WordPress 6.5.
* update version in package-lock.json.

= 3.7.2 - 2024-02-14 =
* No longer user variables in `_n()` translation function.

= 3.7.1 - 2023-12-13 =
* update semver to 5.7.2 because of a security vulnerability in 5.7.1.

= 3.7.0 - 2023-11-20 =
* Important: starting with this release, Action Scheduler follows an L-2 version policy (WordPress, and consequently PHP).
* Add extended indexes for hook_status_scheduled_date_gmt and status_scheduled_date_gmt.
* Catch and log exceptions thrown when actions can't be created, e.g. under a corrupt database schema.
* Tweak - WP 6.4 compatibility.
* Update unit tests for upcoming dependency version policy.
* make sure hook action_scheduler_failed_execution can access original exception object.
* mention dependency version policy in usage.md.

= 3.6.4 - 2023-10-11 =
* Performance improvements when bulk cancelling actions.
* Dev-related fixes.

= 3.6.3 - 2023-09-13 =
* Use `_doing_it_wrong` in initialization check.

= 3.6.2 - 2023-08-09 =
* Add guidance about passing arguments.
* Atomic option locking.
* Improve bulk delete handling.
* Include database error in the exception message.
* Tweak - WP 6.3 compatibility.

= 3.6.1 - 2023-06-14 =
* Document new optional `$priority` arg for various API functions.
* Document the new `--exclude-groups` WP CLI option.
* Document the new `action_scheduler_init` hook.
* Ensure actions within each claim are executed in the expected order.
* Fix incorrect text domain.
* Remove SHOW TABLES usage when checking if tables exist.

= 3.6.0 - 2023-05-10 =
* Add $unique parameter to function signatures.
* Add a cast-to-int for extra safety before forming new DateTime object.
* Add a hook allowing exceptions for consistently failing recurring actions.
* Add action priorities.
* Add init hook.
* Always raise the time limit.
* Bump minimatch from 3.0.4 to 3.0.8.
* Bump yaml from 2.2.1 to 2.2.2.
* Defensive coding relating to gaps in declared schedule types.
* Do not process an action if it cannot be set to `in-progress`.
* Filter view labels (status names) should be translatable | #919.
* Fix WPCLI progress messages.
* Improve data-store initialization flow.
* Improve error handling across all supported PHP versions.
* Improve logic for flushing the runtime cache.
* Support exclusion of multiple groups.
* Update lint-staged and Node/NPM requirements.
* add CLI clean command.
* add CLI exclude-group filter.
* exclude past-due from list table all filter count.
* throwing an exception if as_schedule_recurring_action interval param is not of type integer.

= 3.5.4 - 2023-01-17 =
* Add pre filters during action registration.
* Async scheduling.
* Calculate timeouts based on total actions.
* Correctly order the parameters for `ActionScheduler_ActionFactory`'s calls to `single_unique`.
* Fetch action in memory first before releasing claim to avoid deadlock.
* PHP 8.2: declare property to fix creation of dynamic property warning.
* PHP 8.2: fix "Using ${var} in strings is deprecated, use {$var} instead".
* Prevent `undefined variable` warning for `$num_pastdue_actions`.

= 3.5.3 - 2022-11-09 =
* Query actions with partial match.

= 3.5.2 - 2022-09-16 =
* Fix - erroneous 3.5.1 release.

= 3.5.1 - 2022-09-13 =
* Maintenance on A/S docs.
* fix: PHP 8.2 deprecated notice.

= 3.5.0 - 2022-08-25 =
* Add - The active view link within the "Tools > Scheduled Actions" screen is now clickable.
* Add - A warning when there are past-due actions.
* Enhancement - Added the ability to schedule unique actions via an atomic operation.
* Enhancement - Improvements to cache invalidation when processing batches (when running on WordPress 6.0+).
* Enhancement - If a recurring action is found to be consistently failing, it will stop being rescheduled.
* Enhancement - Adds a new "Past Due" view to the scheduled actions list table.

= 3.4.2 - 2022-06-08 =
* Fix - Change the include for better linting.
* Fix - update: Added Action scheduler completed action hook.

= 3.4.1 - 2022-05-24 =
* Fix - Change the include for better linting.
* Fix - Fix the documented return type.

= 3.4.0 - 2021-10-29 =
* Enhancement - Number of items per page can now be set for the Scheduled Actions view (props @ovidiul). #771
* Fix - Do not lower the max_execution_time if it is already set to 0 (unlimited) (props @barryhughes). #755
* Fix - Avoid triggering autoloaders during the version resolution process (props @olegabr). #731 & #776
* Dev - ActionScheduler_wcSystemStatus PHPCS fixes (props @ovidiul). #761
* Dev - ActionScheduler_DBLogger.php PHPCS fixes (props @ovidiul). #768
* Dev - Fixed phpcs for ActionScheduler_Schedule_Deprecated (props @ovidiul). #762
* Dev - Improve actions table indices (props @glagonikas). #774 & #777
* Dev - PHPCS fixes for ActionScheduler_DBStore.php (props @ovidiul). #769 & #778
* Dev - PHPCS Fixes for ActionScheduler_Abstract_ListTable (props @ovidiul). #763 & #779
* Dev - Adds new filter action_scheduler_claim_actions_order_by to allow tuning of the claim query (props @glagonikas). #773
* Dev - PHPCS fixes for ActionScheduler_WpPostStore class (props @ovidiul). #780

= 3.3.0 - 2021-09-15 =
* Enhancement - Adds as_has_scheduled_action() to provide a performant way to test for existing actions. #645
* Fix - Improves compatibility with environments where NO_ZERO_DATE is enabled. #519
* Fix - Adds safety checks to guard against errors when our database tables cannot be created. #645
* Dev - Now supports queries that use multiple statuses. #649
* Dev - Minimum requirements for WordPress and PHP bumped (to 5.2 and 5.6 respectively). #723

= 3.2.1 - 2021-06-21 =
* Fix - Add extra safety/account for different versions of AS and different loading patterns. #714
* Fix - Handle hidden columns (Tools → Scheduled Actions) | #600.

= 3.2.0 - 2021-06-03 =
* Fix - Add "no ordering" option to as_next_scheduled_action().
* Fix - Add secondary scheduled date checks when claiming actions (DBStore) | #634.
* Fix - Add secondary scheduled date checks when claiming actions (wpPostStore) | #634.
* Fix - Adds a new index to the action table, reducing the potential for deadlocks (props: @glagonikas).
* Fix - Fix unit tests infrastructure and adapt tests to PHP 8.
* Fix - Identify in-use data store.
* Fix - Improve test_migration_is_scheduled.
* Fix - PHP notice on list table.
* Fix - Speed up clean up and batch selects.
* Fix - Update pending dependencies.
* Fix - [PHP 8.0] Only pass action arg values through to do_action_ref_array().
* Fix - [PHP 8] Set the PHP version to 7.1 in composer.json for PHP 8 compatibility.
* Fix - add is_initialized() to docs.
* Fix - fix file permissions.
* Fix - fixes #664 by replacing __ with esc_html__.

= 3.1.6 - 2020-05-12 =
* Change log starts.


================================================
FILE: classes/ActionScheduler_ActionClaim.php
================================================
<?php

/**
 * Class ActionScheduler_ActionClaim
 */
class ActionScheduler_ActionClaim {
	/**
	 * Claim ID.
	 *
	 * @var string
	 */
	private $id = '';

	/**
	 * Claimed action IDs.
	 *
	 * @var int[]
	 */
	private $action_ids = array();

	/**
	 * Construct.
	 *
	 * @param string $id Claim ID.
	 * @param int[]  $action_ids Action IDs.
	 */
	public function __construct( $id, array $action_ids ) {
		$this->id         = $id;
		$this->action_ids = $action_ids;
	}

	/**
	 * Get claim ID.
	 */
	public function get_id() {
		return $this->id;
	}

	/**
	 * Get IDs of claimed actions.
	 */
	public function get_actions() {
		return $this->action_ids;
	}
}


================================================
FILE: classes/ActionScheduler_ActionFactory.php
================================================
<?php

/**
 * Class ActionScheduler_ActionFactory
 */
class ActionScheduler_ActionFactory {

	/**
	 * Return stored actions for given params.
	 *
	 * @param string                        $status The action's status in the data store.
	 * @param string                        $hook The hook to trigger when this action runs.
	 * @param array                         $args Args to pass to callbacks when the hook is triggered.
	 * @param ActionScheduler_Schedule|null $schedule The action's schedule.
	 * @param string                        $group A group to put the action in.
	 * phpcs:ignore Squiz.Commenting.FunctionComment.ExtraParamComment
	 * @param int                           $priority The action priority.
	 *
	 * @return ActionScheduler_Action An instance of the stored action.
	 */
	public function get_stored_action( $status, $hook, array $args = array(), ?ActionScheduler_Schedule $schedule = null, $group = '' ) {
		// The 6th parameter ($priority) is not formally declared in the method signature to maintain compatibility with
		// third-party subclasses created before this param was added.
		$priority = func_num_args() >= 6 ? (int) func_get_arg( 5 ) : 10;

		switch ( $status ) {
			case ActionScheduler_Store::STATUS_PENDING:
				$action_class = 'ActionScheduler_Action';
				break;
			case ActionScheduler_Store::STATUS_CANCELED:
				$action_class = 'ActionScheduler_CanceledAction';
				if ( ! is_null( $schedule ) && ! is_a( $schedule, 'ActionScheduler_CanceledSchedule' ) && ! is_a( $schedule, 'ActionScheduler_NullSchedule' ) ) {
					$schedule = new ActionScheduler_CanceledSchedule( $schedule->get_date() );
				}
				break;
			default:
				$action_class = 'ActionScheduler_FinishedAction';
				break;
		}

		$action_class = apply_filters( 'action_scheduler_stored_action_class', $action_class, $status, $hook, $args, $schedule, $group );

		$action = new $action_class( $hook, $args, $schedule, $group );
		$action->set_priority( $priority );

		/**
		 * Allow 3rd party code to change the instantiated action for a given hook, args, schedule and group.
		 *
		 * @param ActionScheduler_Action   $action The instantiated action.
		 * @param string                   $hook The instantiated action's hook.
		 * @param array                    $args The instantiated action's args.
		 * @param ActionScheduler_Schedule $schedule The instantiated action's schedule.
		 * @param string                   $group The instantiated action's group.
		 * @param int                      $priority The action priority.
		 */
		return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group, $priority );
	}

	/**
	 * Enqueue an action to run one time, as soon as possible (rather a specific scheduled time).
	 *
	 * This method creates a new action using the NullSchedule. In practice, this results in an action scheduled to
	 * execute "now". Therefore, it will generally run as soon as possible but is not prioritized ahead of other actions
	 * that are already past-due.
	 *
	 * @param string $hook The hook to trigger when this action runs.
	 * @param array  $args Args to pass when the hook is triggered.
	 * @param string $group A group to put the action in.
	 *
	 * @return int The ID of the stored action.
	 */
	public function async( $hook, $args = array(), $group = '' ) {
		return $this->async_unique( $hook, $args, $group, false );
	}

	/**
	 * Same as async, but also supports $unique param.
	 *
	 * @param string $hook The hook to trigger when this action runs.
	 * @param array  $args Args to pass when the hook is triggered.
	 * @param string $group A group to put the action in.
	 * @param bool   $unique Whether to ensure the action is unique.
	 *
	 * @return int The ID of the stored action.
	 */
	public function async_unique( $hook, $args = array(), $group = '', $unique = true ) {
		$schedule = new ActionScheduler_NullSchedule();
		$action   = new ActionScheduler_Action( $hook, $args, $schedule, $group );
		return $unique ? $this->store_unique_action( $action, $unique ) : $this->store( $action );
	}

	/**
	 * Create single action.
	 *
	 * @param string $hook  The hook to trigger when this action runs.
	 * @param array  $args  Args to pass when the hook is triggered.
	 * @param int    $when  Unix timestamp when the action will run.
	 * @param string $group A group to put the action in.
	 *
	 * @return int The ID of the stored action.
	 */
	public function single( $hook, $args = array(), $when = null, $group = '' ) {
		return $this->single_unique( $hook, $args, $when, $group, false );
	}

	/**
	 * Create single action only if there is no pending or running action with same name and params.
	 *
	 * @param string $hook The hook to trigger when this action runs.
	 * @param array  $args Args to pass when the hook is triggered.
	 * @param int    $when Unix timestamp when the action will run.
	 * @param string $group A group to put the action in.
	 * @param bool   $unique Whether action scheduled should be unique.
	 *
	 * @return int The ID of the stored action.
	 */
	public function single_unique( $hook, $args = array(), $when = null, $group = '', $unique = true ) {
		$date     = as_get_datetime_object( $when );
		$schedule = new ActionScheduler_SimpleSchedule( $date );
		$action   = new ActionScheduler_Action( $hook, $args, $schedule, $group );
		return $unique ? $this->store_unique_action( $action ) : $this->store( $action );
	}

	/**
	 * Create the first instance of an action recurring on a given interval.
	 *
	 * @param string $hook The hook to trigger when this action runs.
	 * @param array  $args Args to pass when the hook is triggered.
	 * @param int    $first Unix timestamp for the first run.
	 * @param int    $interval Seconds between runs.
	 * @param string $group A group to put the action in.
	 *
	 * @return int The ID of the stored action.
	 */
	public function recurring( $hook, $args = array(), $first = null, $interval = null, $group = '' ) {
		return $this->recurring_unique( $hook, $args, $first, $interval, $group, false );
	}

	/**
	 * Create the first instance of an action recurring on a given interval only if there is no pending or running action with same name and params.
	 *
	 * @param string $hook The hook to trigger when this action runs.
	 * @param array  $args Args to pass when the hook is triggered.
	 * @param int    $first Unix timestamp for the first run.
	 * @param int    $interval Seconds between runs.
	 * @param string $group A group to put the action in.
	 * @param bool   $unique Whether action scheduled should be unique.
	 *
	 * @return int The ID of the stored action.
	 */
	public function recurring_unique( $hook, $args = array(), $first = null, $interval = null, $group = '', $unique = true ) {
		if ( empty( $interval ) ) {
			return $this->single_unique( $hook, $args, $first, $group, $unique );
		}
		$date     = as_get_datetime_object( $first );
		$schedule = new ActionScheduler_IntervalSchedule( $date, $interval );
		$action   = new ActionScheduler_Action( $hook, $args, $schedule, $group );
		return $unique ? $this->store_unique_action( $action ) : $this->store( $action );
	}

	/**
	 * Create the first instance of an action recurring on a Cron schedule.
	 *
	 * @param string $hook The hook to trigger when this action runs.
	 * @param array  $args Args to pass when the hook is triggered.
	 * @param int    $base_timestamp The first instance of the action will be scheduled
	 *        to run at a time calculated after this timestamp matching the cron
	 *        expression. This can be used to delay the first instance of the action.
	 * @param int    $schedule A cron definition string.
	 * @param string $group A group to put the action in.
	 *
	 * @return int The ID of the stored action.
	 */
	public function cron( $hook, $args = array(), $base_timestamp = null, $schedule = null, $group = '' ) {
		return $this->cron_unique( $hook, $args, $base_timestamp, $schedule, $group, false );
	}


	/**
	 * Create the first instance of an action recurring on a Cron schedule only if there is no pending or running action with same name and params.
	 *
	 * @param string $hook The hook to trigger when this action runs.
	 * @param array  $args Args to pass when the hook is triggered.
	 * @param int    $base_timestamp The first instance of the action will be scheduled
	 *        to run at a time calculated after this timestamp matching the cron
	 *        expression. This can be used to delay the first instance of the action.
	 * @param int    $schedule A cron definition string.
	 * @param string $group A group to put the action in.
	 * @param bool   $unique Whether action scheduled should be unique.
	 *
	 * @return int The ID of the stored action.
	 **/
	public function cron_unique( $hook, $args = array(), $base_timestamp = null, $schedule = null, $group = '', $unique = true ) {
		if ( empty( $schedule ) ) {
			return $this->single_unique( $hook, $args, $base_timestamp, $group, $unique );
		}
		$date     = as_get_datetime_object( $base_timestamp );
		$cron     = CronExpression::factory( $schedule );
		$schedule = new ActionScheduler_CronSchedule( $date, $cron );
		$action   = new ActionScheduler_Action( $hook, $args, $schedule, $group );
		return $unique ? $this->store_unique_action( $action ) : $this->store( $action );
	}

	/**
	 * Create a successive instance of a recurring or cron action.
	 *
	 * Importantly, the action will be rescheduled to run based on the current date/time.
	 * That means when the action is scheduled to run in the past, the next scheduled date
	 * will be pushed forward. For example, if a recurring action set to run every hour
	 * was scheduled to run 5 seconds ago, it will be next scheduled for 1 hour in the
	 * future, which is 1 hour and 5 seconds from when it was last scheduled to run.
	 *
	 * Alternatively, if the action is scheduled to run in the future, and is run early,
	 * likely via manual intervention, then its schedule will change based on the time now.
	 * For example, if a recurring action set to run every day, and is run 12 hours early,
	 * it will run again in 24 hours, not 36 hours.
	 *
	 * This slippage is less of an issue with Cron actions, as the specific run time can
	 * be set for them to run, e.g. 1am each day. In those cases, and entire period would
	 * need to be missed before there was any change is scheduled, e.g. in the case of an
	 * action scheduled for 1am each day, the action would need to run an entire day late.
	 *
	 * @param ActionScheduler_Action $action The existing action.
	 *
	 * @return string The ID of the stored action
	 * @throws InvalidArgumentException If $action is not a recurring action.
	 */
	public function repeat( $action ) {
		$schedule = $action->get_schedule();
		$next     = $schedule->get_next( as_get_datetime_object() );

		if ( is_null( $next ) || ! $schedule->is_recurring() ) {
			throw new InvalidArgumentException( __( 'Invalid action - must be a recurring action.', 'action-scheduler' ) );
		}

		$schedule_class = get_class( $schedule );
		$new_schedule   = new $schedule( $next, $schedule->get_recurrence(), $schedule->get_first_date() );
		$new_action     = new ActionScheduler_Action( $action->get_hook(), $action->get_args(), $new_schedule, $action->get_group() );
		$new_action->set_priority( $action->get_priority() );
		return $this->store( $new_action );
	}

	/**
	 * Creates a scheduled action.
	 *
	 * This general purpose method can be used in place of specific methods such as async(),
	 * async_unique(), single() or single_unique(), etc.
	 *
	 * @internal Not intended for public use, should not be overridden by subclasses.
	 *
	 * @param array $options {
	 *     Describes the action we wish to schedule.
	 *
	 *     @type string     $type      Must be one of 'async', 'cron', 'recurring', or 'single'.
	 *     @type string     $hook      The hook to be executed.
	 *     @type array      $arguments Arguments to be passed to the callback.
	 *     @type string     $group     The action group.
	 *     @type bool       $unique    If the action should be unique.
	 *     @type int        $when      Timestamp. Indicates when the action, or first instance of the action in the case
	 *                                 of recurring or cron actions, becomes due.
	 *     @type int|string $pattern   Recurrence pattern. This is either an interval in seconds for recurring actions
	 *                                 or a cron expression for cron actions.
	 *     @type int        $priority  Lower values means higher priority. Should be in the range 0-255.
	 * }
	 *
	 * @return int The action ID. Zero if there was an error scheduling the action.
	 */
	public function create( array $options = array() ) {
		$defaults = array(
			'type'      => 'single',
			'hook'      => '',
			'arguments' => array(),
			'group'     => '',
			'unique'    => false,
			'when'      => time(),
			'pattern'   => null,
			'priority'  => 10,
		);

		$options = array_merge( $defaults, $options );

		// Cron/recurring actions without a pattern are treated as single actions (this gives calling code the ability
		// to use functions like as_schedule_recurring_action() to schedule recurring as well as single actions).
		if ( ( 'cron' === $options['type'] || 'recurring' === $options['type'] ) && empty( $options['pattern'] ) ) {
			$options['type'] = 'single';
		}

		switch ( $options['type'] ) {
			case 'async':
				$schedule = new ActionScheduler_NullSchedule();
				break;

			case 'cron':
				$date     = as_get_datetime_object( $options['when'] );
				$cron     = CronExpression::factory( $options['pattern'] );
				$schedule = new ActionScheduler_CronSchedule( $date, $cron );
				break;

			case 'recurring':
				$date     = as_get_datetime_object( $options['when'] );
				$schedule = new ActionScheduler_IntervalSchedule( $date, $options['pattern'] );
				break;

			case 'single':
				$date     = as_get_datetime_object( $options['when'] );
				$schedule = new ActionScheduler_SimpleSchedule( $date );
				break;

			default:
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
				error_log( "Unknown action type '{$options['type']}' specified when trying to create an action for '{$options['hook']}'." );
				return 0;
		}

		$action = new ActionScheduler_Action( $options['hook'], $options['arguments'], $schedule, $options['group'] );
		$action->set_priority( $options['priority'] );

		$action_id = 0;
		try {
			$action_id = $options['unique'] ? $this->store_unique_action( $action ) : $this->store( $action );
		} catch ( Exception $e ) {
			// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
			error_log(
				sprintf(
					/* translators: %1$s is the name of the hook to be enqueued, %2$s is the exception message. */
					__( 'Caught exception while enqueuing action "%1$s": %2$s', 'action-scheduler' ),
					$options['hook'],
					$e->getMessage()
				)
			);
		}
		return $action_id;
	}

	/**
	 * Save action to database.
	 *
	 * @param ActionScheduler_Action $action Action object to save.
	 *
	 * @return int The ID of the stored action
	 */
	protected function store( ActionScheduler_Action $action ) {
		$store = ActionScheduler_Store::instance();
		return $store->save_action( $action );
	}

	/**
	 * Store action if it's unique.
	 *
	 * @param ActionScheduler_Action $action Action object to store.
	 *
	 * @return int ID of the created action. Will be 0 if action was not created.
	 */
	protected function store_unique_action( ActionScheduler_Action $action ) {
		$store = ActionScheduler_Store::instance();
		if ( method_exists( $store, 'save_unique_action' ) ) {
			return $store->save_unique_action( $action );
		} else {
			/**
			 * Fallback to non-unique action if the store doesn't support unique actions.
			 * We try to save the action as unique, accepting that there might be a race condition.
			 * This is likely still better than giving up on unique actions entirely.
			 */
			$existing_action_id = (int) $store->find_action(
				$action->get_hook(),
				array(
					'args'   => $action->get_args(),
					'status' => ActionScheduler_Store::STATUS_PENDING,
					'group'  => $action->get_group(),
				)
			);
			if ( $existing_action_id > 0 ) {
				return 0;
			}
			return $store->save_action( $action );
		}
	}
}


================================================
FILE: classes/ActionScheduler_AdminView.php
================================================
<?php

/**
 * Class ActionScheduler_AdminView
 *
 * @codeCoverageIgnore
 */
class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprecated {

	/**
	 * Instance.
	 *
	 * @var null|self
	 */
	private static $admin_view = null;

	/**
	 * Screen ID.
	 *
	 * @var string
	 */
	private static $screen_id = 'tools_page_action-scheduler';

	/**
	 * ActionScheduler_ListTable instance.
	 *
	 * @var ActionScheduler_ListTable
	 */
	protected $list_table;

	/**
	 * Get instance.
	 *
	 * @return ActionScheduler_AdminView
	 * @codeCoverageIgnore
	 */
	public static function instance() {

		if ( empty( self::$admin_view ) ) {
			$class            = apply_filters( 'action_scheduler_admin_view_class', 'ActionScheduler_AdminView' );
			self::$admin_view = new $class();
		}

		return self::$admin_view;
	}

	/**
	 * Initialize.
	 *
	 * @codeCoverageIgnore
	 */
	public function init() {
		if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {

			if ( class_exists( 'WooCommerce' ) ) {
				add_action( 'woocommerce_admin_status_content_action-scheduler', array( $this, 'render_admin_ui' ) );
				add_action( 'woocommerce_system_status_report', array( $this, 'system_status_report' ) );
				add_filter( 'woocommerce_admin_status_tabs', array( $this, 'register_system_status_tab' ) );
			}

			add_action( 'admin_menu', array( $this, 'register_menu' ) );
			add_action( 'admin_notices', array( $this, 'maybe_check_pastdue_actions' ) );
			add_action( 'current_screen', array( $this, 'add_help_tabs' ) );
		}
	}

	/**
	 * Print system status report.
	 */
	public function system_status_report() {
		$table = new ActionScheduler_wcSystemStatus( ActionScheduler::store() );
		$table->render();
	}

	/**
	 * Registers action-scheduler into WooCommerce > System status.
	 *
	 * @param array $tabs An associative array of tab key => label.
	 * @return array $tabs An associative array of tab key => label, including Action Scheduler's tabs
	 */
	public function register_system_status_tab( array $tabs ) {
		$tabs['action-scheduler'] = __( 'Scheduled Actions', 'action-scheduler' );

		return $tabs;
	}

	/**
	 * Include Action Scheduler's administration under the Tools menu.
	 *
	 * A menu under the Tools menu is important for backward compatibility (as that's
	 * where it started), and also provides more convenient access than the WooCommerce
	 * System Status page, and for sites where WooCommerce isn't active.
	 */
	public function register_menu() {
		$hook_suffix = add_submenu_page(
			'tools.php',
			__( 'Scheduled Actions', 'action-scheduler' ),
			__( 'Scheduled Actions', 'action-scheduler' ),
			'manage_options',
			'action-scheduler',
			array( $this, 'render_admin_ui' )
		);
		add_action( 'load-' . $hook_suffix, array( $this, 'process_admin_ui' ) );
	}

	/**
	 * Triggers processing of any pending actions.
	 */
	public function process_admin_ui() {
		$this->get_list_table();
	}

	/**
	 * Renders the Admin UI
	 */
	public function render_admin_ui() {
		$table = $this->get_list_table();
		$table->display_page();
	}

	/**
	 * Get the admin UI object and process any requested actions.
	 *
	 * @return ActionScheduler_ListTable
	 */
	protected function get_list_table() {
		if ( null === $this->list_table ) {
			$this->list_table = new ActionScheduler_ListTable( ActionScheduler::store(), ActionScheduler::logger(), ActionScheduler::runner() );
			$this->list_table->process_actions();
		}

		return $this->list_table;
	}

	/**
	 * Action: admin_notices
	 *
	 * Maybe check past-due actions, and print notice.
	 *
	 * @uses $this->check_pastdue_actions()
	 */
	public function maybe_check_pastdue_actions() {

		// Filter to prevent checking actions (ex: inappropriate user).
		if ( ! apply_filters( 'action_scheduler_check_pastdue_actions', current_user_can( 'manage_options' ) ) ) {
			return;
		}

		// Get last check transient.
		$last_check = get_transient( 'action_scheduler_last_pastdue_actions_check' );

		// If transient exists, we're within interval, so bail.
		if ( ! empty( $last_check ) ) {
			return;
		}

		// Perform the check.
		$this->check_pastdue_actions();
	}

	/**
	 * Check past-due actions, and print notice.
	 */
	protected function check_pastdue_actions() {

		// Set thresholds.
		$threshold_seconds = (int) apply_filters( 'action_scheduler_pastdue_actions_seconds', DAY_IN_SECONDS );
		$threshold_min     = (int) apply_filters( 'action_scheduler_pastdue_actions_min', 1 );

		// Set fallback value for past-due actions count.
		$num_pastdue_actions = 0;

		// Allow third-parties to preempt the default check logic.
		$check = apply_filters( 'action_scheduler_pastdue_actions_check_pre', null );

		// If no third-party preempted and there are no past-due actions, return early.
		if ( ! is_null( $check ) ) {
			return;
		}

		// Scheduled actions query arguments.
		$query_args = array(
			'date'     => as_get_datetime_object( time() - $threshold_seconds ),
			'status'   => ActionScheduler_Store::STATUS_PENDING,
			'per_page' => $threshold_min,
		);

		// If no third-party preempted, run default check.
		if ( is_null( $check ) ) {
			$store               = ActionScheduler_Store::instance();
			$num_pastdue_actions = (int) $store->query_actions( $query_args, 'count' );

			// Check if past-due actions count is greater than or equal to threshold.
			$check = ( $num_pastdue_actions >= $threshold_min );
			$check = (bool) apply_filters( 'action_scheduler_pastdue_actions_check', $check, $num_pastdue_actions, $threshold_seconds, $threshold_min );
		}

		// If check failed, set transient and abort.
		if ( ! boolval( $check ) ) {
			$interval = apply_filters( 'action_scheduler_pastdue_actions_check_interval', round( $threshold_seconds / 4 ), $threshold_seconds );
			set_transient( 'action_scheduler_last_pastdue_actions_check', time(), $interval );

			return;
		}

		$actions_url = add_query_arg(
			array(
				'page'   => 'action-scheduler',
				'status' => 'past-due',
				'order'  => 'asc',
			),
			admin_url( 'tools.php' )
		);

		// Print notice.
		echo '<div class="notice notice-warning"><p>';
		printf(
			wp_kses(
				// translators: 1) is the number of affected actions, 2) is a link to an admin screen.
				_n(
					'<strong>Action Scheduler:</strong> %1$d <a href="%2$s">past-due action</a> found; something may be wrong. <a href="https://actionscheduler.org/faq/#my-site-has-past-due-actions-what-can-i-do" target="_blank">Read documentation &raquo;</a>',
					'<strong>Action Scheduler:</strong> %1$d <a href="%2$s">past-due actions</a> found; something may be wrong. <a href="https://actionscheduler.org/faq/#my-site-has-past-due-actions-what-can-i-do" target="_blank">Read documentation &raquo;</a>',
					$num_pastdue_actions,
					'action-scheduler'
				),
				array(
					'strong' => array(),
					'a'      => array(
						'href'   => true,
						'target' => true,
					),
				)
			),
			absint( $num_pastdue_actions ),
			esc_attr( esc_url( $actions_url ) )
		);
		echo '</p></div>';

		// Facilitate third-parties to evaluate and print notices.
		do_action( 'action_scheduler_pastdue_actions_extra_notices', $query_args );
	}

	/**
	 * Provide more information about the screen and its data in the help tab.
	 */
	public function add_help_tabs() {
		$screen = get_current_screen();

		if ( ! $screen || self::$screen_id !== $screen->id ) {
			return;
		}

		$as_version       = ActionScheduler_Versions::instance()->latest_version();
		$as_source        = ActionScheduler_SystemInformation::active_source();
		$as_source_path   = ActionScheduler_SystemInformation::active_source_path();
		$as_source_markup = sprintf( '<code>%s</code>', esc_html( $as_source_path ) );

		if ( ! empty( $as_source ) ) {
			$as_source_markup = sprintf(
				'%s: <abbr title="%s">%s</abbr>',
				ucfirst( $as_source['type'] ),
				esc_attr( $as_source_path ),
				esc_html( $as_source['name'] )
			);
		}

		$screen->add_help_tab(
			array(
				'id'      => 'action_scheduler_about',
				'title'   => __( 'About', 'action-scheduler' ),
				'content' =>
					// translators: %s is the Action Scheduler version.
					'<h2>' . sprintf( __( 'About Action Scheduler %s', 'action-scheduler' ), $as_version ) . '</h2>' .
					'<p>' .
						__( 'Action Scheduler is a scalable, traceable job queue for background processing large sets of actions. Action Scheduler works by triggering an action hook to run at some time in the future. Scheduled actions can also be scheduled to run on a recurring schedule.', 'action-scheduler' ) .
					'</p>' .
					'<h3>' . esc_html__( 'Source', 'action-scheduler' ) . '</h3>' .
					'<p>' .
						esc_html__( 'Action Scheduler is currently being loaded from the following location. This can be useful when debugging, or if requested by the support team.', 'action-scheduler' ) .
					'</p>' .
					'<p>' . $as_source_markup . '</p>' .
					'<h3>' . esc_html__( 'WP CLI', 'action-scheduler' ) . '</h3>' .
					'<p>' .
						sprintf(
							/* translators: %1$s is WP CLI command (not translatable) */
							esc_html__( 'WP CLI commands are available: execute %1$s for a list of available commands.', 'action-scheduler' ),
							'<code>wp help action-scheduler</code>'
						) .
					'</p>',
			)
		);

		$screen->add_help_tab(
			array(
				'id'      => 'action_scheduler_columns',
				'title'   => __( 'Columns', 'action-scheduler' ),
				'content' =>
					'<h2>' . __( 'Scheduled Action Columns', 'action-scheduler' ) . '</h2>' .
					'<ul>' .
					sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Hook', 'action-scheduler' ), __( 'Name of the action hook that will be triggered.', 'action-scheduler' ) ) .
					sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Status', 'action-scheduler' ), __( 'Action statuses are Pending, Complete, Canceled, Failed', 'action-scheduler' ) ) .
					sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Arguments', 'action-scheduler' ), __( 'Optional data array passed to the action hook.', 'action-scheduler' ) ) .
					sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Group', 'action-scheduler' ), __( 'Optional action group.', 'action-scheduler' ) ) .
					sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Recurrence', 'action-scheduler' ), __( 'The action\'s schedule frequency.', 'action-scheduler' ) ) .
					sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Scheduled', 'action-scheduler' ), __( 'The date/time the action is/was scheduled to run.', 'action-scheduler' ) ) .
					sprintf( '<li><strong>%1$s</strong>: %2$s</li>', __( 'Log', 'action-scheduler' ), __( 'Activity log for the action.', 'action-scheduler' ) ) .
					'</ul>',
			)
		);
	}
}


================================================
FILE: classes/ActionScheduler_AsyncRequest_QueueRunner.php
================================================
<?php

defined( 'ABSPATH' ) || exit;

/**
 * ActionScheduler_AsyncRequest_QueueRunner class.
 */
class ActionScheduler_AsyncRequest_QueueRunner extends WP_Async_Request {

	/**
	 * Data store for querying actions
	 *
	 * @var ActionScheduler_Store
	 */
	protected $store;

	/**
	 * Prefix for ajax hooks
	 *
	 * @var string
	 */
	protected $prefix = 'as';

	/**
	 * Action for ajax hooks
	 *
	 * @var string
	 */
	protected $action = 'async_request_queue_runner';

	/**
	 * Initiate new async request.
	 *
	 * @param ActionScheduler_Store $store Store object.
	 */
	public function __construct( ActionScheduler_Store $store ) {
		parent::__construct();
		$this->store = $store;
	}

	/**
	 * Handle async requests
	 *
	 * Run a queue, and maybe dispatch another async request to run another queue
	 * if there are still pending actions after completing a queue in this request.
	 */
	protected function handle() {
		do_action( 'action_scheduler_run_queue', 'Async Request' ); // run a queue in the same way as WP Cron, but declare the Async Request context.

		$sleep_seconds = $this->get_sleep_seconds();

		if ( $sleep_seconds ) {
			sleep( $sleep_seconds );
		}

		$this->maybe_dispatch();
	}

	/**
	 * If the async request runner is needed and allowed to run, dispatch a request.
	 */
	public function maybe_dispatch() {
		if ( ! $this->allow() ) {
			return;
		}

		$this->dispatch();
		ActionScheduler_QueueRunner::instance()->unhook_dispatch_async_request();
	}

	/**
	 * Only allow async requests when needed.
	 *
	 * Also allow 3rd party code to disable running actions via async requests.
	 */
	protected function allow() {

		if ( ! has_action( 'action_scheduler_run_queue' ) || ActionScheduler::runner()->has_maximum_concurrent_batches() || ! $this->store->has_pending_actions_due() ) {
			$allow = false;
		} else {
			$allow = true;
		}

		return apply_filters( 'action_scheduler_allow_async_request_runner', $allow );
	}

	/**
	 * Chaining async requests can crash MySQL. A brief sleep call in PHP prevents that.
	 */
	protected function get_sleep_seconds() {
		return apply_filters( 'action_scheduler_async_request_sleep_seconds', 5, $this );
	}
}


================================================
FILE: classes/ActionScheduler_Compatibility.php
================================================
<?php

/**
 * Class ActionScheduler_Compatibility
 */
class ActionScheduler_Compatibility {
	/**
	 * Converts a shorthand byte value to an integer byte value.
	 *
	 * Wrapper for wp_convert_hr_to_bytes(), moved to load.php in WordPress 4.6 from media.php
	 *
	 * @link https://secure.php.net/manual/en/function.ini-get.php
	 * @link https://secure.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
	 *
	 * @param string $value A (PHP ini) byte value, either shorthand or ordinary.
	 * @return int An integer byte value.
	 */
	public static function convert_hr_to_bytes( $value ) {
		if ( function_exists( 'wp_convert_hr_to_bytes' ) ) {
			return wp_convert_hr_to_bytes( $value );
		}

		$value = strtolower( trim( $value ) );
		$bytes = (int) $value;

		if ( false !== strpos( $value, 'g' ) ) {
			$bytes *= GB_IN_BYTES;
		} elseif ( false !== strpos( $value, 'm' ) ) {
			$bytes *= MB_IN_BYTES;
		} elseif ( false !== strpos( $value, 'k' ) ) {
			$bytes *= KB_IN_BYTES;
		}

		// Deal with large (float) values which run into the maximum integer size.
		return min( $bytes, PHP_INT_MAX );
	}

	/**
	 * Attempts to raise the PHP memory limit for memory intensive processes.
	 *
	 * Only allows raising the existing limit and prevents lowering it.
	 *
	 * Wrapper for wp_raise_memory_limit(), added in WordPress v4.6.0
	 *
	 * @return bool|int|string The limit that was set or false on failure.
	 */
	public static function raise_memory_limit() {
		if ( function_exists( 'wp_raise_memory_limit' ) ) {
			return wp_raise_memory_limit( 'admin' );
		}

		$current_limit     = @ini_get( 'memory_limit' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
		$current_limit_int = self::convert_hr_to_bytes( $current_limit );

		if ( -1 === $current_limit_int ) {
			return false;
		}

		$wp_max_limit       = WP_MAX_MEMORY_LIMIT;
		$wp_max_limit_int   = self::convert_hr_to_bytes( $wp_max_limit );
		$filtered_limit     = apply_filters( 'admin_memory_limit', $wp_max_limit );
		$filtered_limit_int = self::convert_hr_to_bytes( $filtered_limit );

		// phpcs:disable WordPress.PHP.IniSet.memory_limit_Blacklisted
		// phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged

		if ( -1 === $filtered_limit_int || ( $filtered_limit_int > $wp_max_limit_int && $filtered_limit_int > $current_limit_int ) ) {
			if ( false !== @ini_set( 'memory_limit', $filtered_limit ) ) {
				return $filtered_limit;
			} else {
				return false;
			}
		} elseif ( -1 === $wp_max_limit_int || $wp_max_limit_int > $current_limit_int ) {
			if ( false !== @ini_set( 'memory_limit', $wp_max_limit ) ) {
				return $wp_max_limit;
			} else {
				return false;
			}
		}

		// phpcs:enable

		return false;
	}

	/**
	 * Attempts to raise the PHP timeout for time intensive processes.
	 *
	 * Only allows raising the existing limit and prevents lowering it. Wrapper for wc_set_time_limit(), when available.
	 *
	 * @param int $limit The time limit in seconds.
	 */
	public static function raise_time_limit( $limit = 0 ) {
		$limit              = (int) $limit;
		$max_execution_time = (int) ini_get( 'max_execution_time' );

		// If the max execution time is already set to zero (unlimited), there is no reason to make a further change.
		if ( 0 === $max_execution_time ) {
			return;
		}

		// Whichever of $max_execution_time or $limit is higher is the amount by which we raise the time limit.
		$raise_by = 0 === $limit || $limit > $max_execution_time ? $limit : $max_execution_time;

		if ( function_exists( 'wc_set_time_limit' ) ) {
			wc_set_time_limit( $raise_by );
		} elseif ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
			@set_time_limit( $raise_by ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
		}
	}
}


================================================
FILE: classes/ActionScheduler_DataController.php
================================================
<?php

use Action_Scheduler\Migration\Controller;

/**
 * Class ActionScheduler_DataController
 *
 * The main plugin/initialization class for the data stores.
 *
 * Responsible for hooking everything up with WordPress.
 *
 * @package Action_Scheduler
 *
 * @since 3.0.0
 */
class ActionScheduler_DataController {
	/** Action data store class name. */
	const DATASTORE_CLASS = 'ActionScheduler_DBStore';

	/** Logger data store class name. */
	const LOGGER_CLASS = 'ActionScheduler_DBLogger';

	/** Migration status option name. */
	const STATUS_FLAG = 'action_scheduler_migration_status';

	/** Migration status option value. */
	const STATUS_COMPLETE = 'complete';

	/** Migration minimum required PHP version. */
	const MIN_PHP_VERSION = '5.5';

	/**
	 * Instance.
	 *
	 * @var ActionScheduler_DataController
	 */
	private static $instance;

	/**
	 * Sleep time in seconds.
	 *
	 * @var int
	 */
	private static $sleep_time = 0;

	/**
	 * Tick count required for freeing memory.
	 *
	 * @var int
	 */
	private static $free_ticks = 50;

	/**
	 * Get a flag indicating whether the migration environment dependencies are met.
	 *
	 * @return bool
	 */
	public static function dependencies_met() {
		$php_support = version_compare( PHP_VERSION, self::MIN_PHP_VERSION, '>=' );
		return $php_support && apply_filters( 'action_scheduler_migration_dependencies_met', true );
	}

	/**
	 * Get a flag indicating whether the migration is complete.
	 *
	 * @return bool Whether the flag has been set marking the migration as complete
	 */
	public static function is_migration_complete() {
		return get_option( self::STATUS_FLAG ) === self::STATUS_COMPLETE;
	}

	/**
	 * Mark the migration as complete.
	 */
	public static function mark_migration_complete() {
		update_option( self::STATUS_FLAG, self::STATUS_COMPLETE );
	}

	/**
	 * Unmark migration when a plugin is de-activated. Will not work in case of silent activation, for example in an update.
	 * We do this to mitigate the bug of lost actions which happens if there was an AS 2.x to AS 3.x migration in the past, but that plugin is now
	 * deactivated and the site was running on AS 2.x again.
	 */
	public static function mark_migration_incomplete() {
		delete_option( self::STATUS_FLAG );
	}

	/**
	 * Set the action store class name.
	 *
	 * @param string $class Classname of the store class.
	 *
	 * @return string
	 */
	public static function set_store_class( $class ) {
		return self::DATASTORE_CLASS;
	}

	/**
	 * Set the action logger class name.
	 *
	 * @param string $class Classname of the logger class.
	 *
	 * @return string
	 */
	public static function set_logger_class( $class ) {
		return self::LOGGER_CLASS;
	}

	/**
	 * Set the sleep time in seconds.
	 *
	 * @param integer $sleep_time The number of seconds to pause before resuming operation.
	 */
	public static function set_sleep_time( $sleep_time ) {
		self::$sleep_time = (int) $sleep_time;
	}

	/**
	 * Set the tick count required for freeing memory.
	 *
	 * @param integer $free_ticks The number of ticks to free memory on.
	 */
	public static function set_free_ticks( $free_ticks ) {
		self::$free_ticks = (int) $free_ticks;
	}

	/**
	 * Free memory if conditions are met.
	 *
	 * @param int $ticks Current tick count.
	 */
	public static function maybe_free_memory( $ticks ) {
		if ( self::$free_ticks && 0 === $ticks % self::$free_ticks ) {
			self::free_memory();
		}
	}

	/**
	 * Reduce memory footprint by clearing the database query and object caches.
	 */
	public static function free_memory() {
		if ( 0 < self::$sleep_time ) {
			/* translators: %d: amount of time */
			\WP_CLI::warning( sprintf( _n( 'Stopped the insanity for %d second', 'Stopped the insanity for %d seconds', self::$sleep_time, 'action-scheduler' ), self::$sleep_time ) );
			sleep( self::$sleep_time );
		}

		\WP_CLI::warning( __( 'Attempting to reduce used memory...', 'action-scheduler' ) );

		/**
		 * Globals.
		 *
		 * @var $wpdb            \wpdb
		 * @var $wp_object_cache \WP_Object_Cache
		 */
		global $wpdb, $wp_object_cache;

		$wpdb->queries = array();

		if ( ! is_a( $wp_object_cache, 'WP_Object_Cache' ) ) {
			return;
		}

		// Not all drop-ins support these props, however, there may be existing installations that rely on these being cleared.
		if ( property_exists( $wp_object_cache, 'group_ops' ) ) {
			$wp_object_cache->group_ops = array();
		}
		if ( property_exists( $wp_object_cache, 'stats' ) ) {
			$wp_object_cache->stats = array();
		}
		if ( property_exists( $wp_object_cache, 'memcache_debug' ) ) {
			$wp_object_cache->memcache_debug = array();
		}
		if ( property_exists( $wp_object_cache, 'cache' ) ) {
			$wp_object_cache->cache = array();
		}

		if ( is_callable( array( $wp_object_cache, '__remoteset' ) ) ) {
			call_user_func( array( $wp_object_cache, '__remoteset' ) ); // important!
		}
	}

	/**
	 * Connect to table datastores if migration is complete.
	 * Otherwise, proceed with the migration if the dependencies have been met.
	 */
	public static function init() {
		if ( self::is_migration_complete() ) {
			add_filter( 'action_scheduler_store_class', array( 'ActionScheduler_DataController', 'set_store_class' ), 100 );
			add_filter( 'action_scheduler_logger_class', array( 'ActionScheduler_DataController', 'set_logger_class' ), 100 );
			add_action( 'deactivate_plugin', array( 'ActionScheduler_DataController', 'mark_migration_incomplete' ) );
		} elseif ( self::dependencies_met() ) {
			Controller::init();
		}

		add_action( 'action_scheduler/progress_tick', array( 'ActionScheduler_DataController', 'maybe_free_memory' ) );
	}

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

		return self::$instance;
	}
}


================================================
FILE: classes/ActionScheduler_DateTime.php
================================================
<?php

/**
 * ActionScheduler DateTime class.
 *
 * This is a custom extension to DateTime that
 */
class ActionScheduler_DateTime extends DateTime {

	/**
	 * UTC offset.
	 *
	 * Only used when a timezone is not set. When a timezone string is
	 * used, this will be set to 0.
	 *
	 * @var int
	 */
	protected $utcOffset = 0; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase

	/**
	 * Get the unix timestamp of the current object.
	 *
	 * Missing in PHP 5.2 so just here so it can be supported consistently.
	 *
	 * @return int
	 */
	#[\ReturnTypeWillChange]
	public function getTimestamp() {
		return method_exists( 'DateTime', 'getTimestamp' ) ? parent::getTimestamp() : $this->format( 'U' );
	}

	/**
	 * Set the UTC offset.
	 *
	 * This represents a fixed offset instead of a timezone setting.
	 *
	 * @param string|int $offset UTC offset value.
	 */
	public function setUtcOffset( $offset ) {
		$this->utcOffset = intval( $offset ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
	}

	/**
	 * Returns the timezone offset.
	 *
	 * @return int
	 * @link http://php.net/manual/en/datetime.getoffset.php
	 */
	#[\ReturnTypeWillChange]
	public function getOffset() {
		return $this->utcOffset ? $this->utcOffset : parent::getOffset(); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
	}

	/**
	 * Set the TimeZone associated with the DateTime
	 *
	 * @param DateTimeZone $timezone Timezone object.
	 *
	 * @return static
	 * @link http://php.net/manual/en/datetime.settimezone.php
	 */
	#[\ReturnTypeWillChange]
	public function setTimezone( $timezone ) {
		$this->utcOffset = 0; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
		parent::setTimezone( $timezone );

		return $this;
	}

	/**
	 * Get the timestamp with the WordPress timezone offset added or subtracted.
	 *
	 * @since  3.0.0
	 * @return int
	 */
	public function getOffsetTimestamp() {
		return $this->getTimestamp() + $this->getOffset();
	}
}


================================================
FILE: classes/ActionScheduler_Exception.php
================================================
<?php

/**
 * ActionScheduler Exception Interface.
 *
 * Facilitates catching Exceptions unique to Action Scheduler.
 *
 * @package ActionScheduler
 * @since 2.1.0
 */
interface ActionScheduler_Exception {}


================================================
FILE: classes/ActionScheduler_FatalErrorMonitor.php
================================================
<?php

/**
 * Class ActionScheduler_FatalErrorMonitor
 */
class ActionScheduler_FatalErrorMonitor {

	/**
	 * ActionScheduler_ActionClaim instance.
	 *
	 * @var ActionScheduler_ActionClaim
	 */
	private $claim = null;

	/**
	 * ActionScheduler_Store instance.
	 *
	 * @var ActionScheduler_Store
	 */
	private $store = null;

	/**
	 * Current action's ID.
	 *
	 * @var int
	 */
	private $action_id = 0;

	/**
	 * Construct.
	 *
	 * @param ActionScheduler_Store $store Action store.
	 */
	public function __construct( ActionScheduler_Store $store ) {
		$this->store = $store;
	}

	/**
	 * Start monitoring.
	 *
	 * @param ActionScheduler_ActionClaim $claim Claimed actions.
	 */
	public function attach( ActionScheduler_ActionClaim $claim ) {
		$this->claim = $claim;
		add_action( 'shutdown', array( $this, 'handle_unexpected_shutdown' ) );
		add_action( 'action_scheduler_before_execute', array( $this, 'track_current_action' ), 0, 1 );
		add_action( 'action_scheduler_after_execute', array( $this, 'untrack_action' ), 0, 0 );
		add_action( 'action_scheduler_execution_ignored', array( $this, 'untrack_action' ), 0, 0 );
		add_action( 'action_scheduler_failed_execution', array( $this, 'untrack_action' ), 0, 0 );
	}

	/**
	 * Stop monitoring.
	 */
	public function detach() {
		$this->claim = null;
		$this->untrack_action();
		remove_action( 'shutdown', array( $this, 'handle_unexpected_shutdown' ) );
		remove_action( 'action_scheduler_before_execute', array( $this, 'track_current_action' ), 0 );
		remove_action( 'action_scheduler_after_execute', array( $this, 'untrack_action' ), 0 );
		remove_action( 'action_scheduler_execution_ignored', array( $this, 'untrack_action' ), 0 );
		remove_action( 'action_scheduler_failed_execution', array( $this, 'untrack_action' ), 0 );
	}

	/**
	 * Track specified action.
	 *
	 * @param int $action_id Action ID to track.
	 */
	public function track_current_action( $action_id ) {
		$this->action_id = $action_id;
	}

	/**
	 * Un-track action.
	 */
	public function untrack_action() {
		$this->action_id = 0;
	}

	/**
	 * Handle unexpected shutdown.
	 */
	public function handle_unexpected_shutdown() {
		$error = error_get_last();

		if ( $error ) {
			if ( in_array( $error['type'], array( E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ), true ) ) {
				if ( ! empty( $this->action_id ) ) {
					$this->store->mark_failure( $this->action_id );
					do_action( 'action_scheduler_unexpected_shutdown', $this->action_id, $error );
				}
			}

			$this->store->release_claim( $this->claim );
		}
	}
}


================================================
FILE: classes/ActionScheduler_InvalidActionException.php
================================================
<?php

/**
 * InvalidAction Exception.
 *
 * Used for identifying actions that are invalid in some way.
 *
 * @package ActionScheduler
 */
class ActionScheduler_InvalidActionException extends \InvalidArgumentException implements ActionScheduler_Exception {

	/**
	 * Create a new exception when the action's schedule cannot be fetched.
	 *
	 * @param string $action_id The action ID with bad args.
	 * @param mixed  $schedule  Passed schedule.
	 * @return static
	 */
	public static function from_schedule( $action_id, $schedule ) {
		$message = sprintf(
			/* translators: 1: action ID 2: schedule */
			__( 'Action [%1$s] has an invalid schedule: %2$s', 'action-scheduler' ),
			$action_id,
			var_export( $schedule, true ) // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
		);

		return new static( $message );
	}

	/**
	 * Create a new exception when the action's args cannot be decoded to an array.
	 *
	 * @param string $action_id The action ID with bad args.
	 * @param mixed  $args      Passed arguments.
	 * @return static
	 */
	public static function from_decoding_args( $action_id, $args = array() ) {
		$message = sprintf(
			/* translators: 1: action ID 2: arguments */
			__( 'Action [%1$s] has invalid arguments. It cannot be JSON decoded to an array. $args = %2$s', 'action-scheduler' ),
			$action_id,
			var_export( $args, true ) // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
		);

		return new static( $message );
	}
}


================================================
FILE: classes/ActionScheduler_ListTable.php
================================================
<?php

/**
 * Implements the admin view of the actions.
 *
 * @codeCoverageIgnore
 */
class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {

	/**
	 * The package name.
	 *
	 * @var string
	 */
	protected $package = 'action-scheduler';

	/**
	 * Columns to show (name => label).
	 *
	 * @var array
	 */
	protected $columns = array();

	/**
	 * Actions (name => label).
	 *
	 * @var array
	 */
	protected $row_actions = array();

	/**
	 * The active data stores
	 *
	 * @var ActionScheduler_Store
	 */
	protected $store;

	/**
	 * A logger to use for getting action logs to display
	 *
	 * @var ActionScheduler_Logger
	 */
	protected $logger;

	/**
	 * A ActionScheduler_QueueRunner runner instance (or child class)
	 *
	 * @var ActionScheduler_QueueRunner
	 */
	protected $runner;

	/**
	 * Bulk actions. The key of the array is the method name of the implementation.
	 * Example: bulk_<key>(array $ids, string $sql_in).
	 *
	 * See the comments in the parent class for further details
	 *
	 * @var array
	 */
	protected $bulk_actions = array();

	/**
	 * Flag variable to render our notifications, if any, once.
	 *
	 * @var bool
	 */
	protected static $did_notification = false;

	/**
	 * Array of seconds for common time periods, like week or month, alongside an internationalised string representation, i.e. "Day" or "Days"
	 *
	 * @var array
	 */
	private static $time_periods;

	/**
	 * Sets the current data store object into `store->action` and initialises the object.
	 *
	 * @param ActionScheduler_Store       $store Store object.
	 * @param ActionScheduler_Logger      $logger Logger object.
	 * @param ActionScheduler_QueueRunner $runner Runner object.
	 */
	public function __construct( ActionScheduler_Store $store, ActionScheduler_Logger $logger, ActionScheduler_QueueRunner $runner ) {

		$this->store  = $store;
		$this->logger = $logger;
		$this->runner = $runner;

		$this->table_header = __( 'Scheduled Actions', 'action-scheduler' );

		$this->bulk_actions = array(
			'delete' => __( 'Delete', 'action-scheduler' ),
		);

		$this->columns = array(
			'hook'        => __( 'Hook', 'action-scheduler' ),
			'status'      => __( 'Status', 'action-scheduler' ),
			'args'        => __( 'Arguments', 'action-scheduler' ),
			'group'       => __( 'Group', 'action-scheduler' ),
			'recurrence'  => __( 'Recurrence', 'action-scheduler' ),
			'schedule'    => __( 'Scheduled Date', 'action-scheduler' ),
			'log_entries' => __( 'Log', 'action-scheduler' ),
		);

		$this->sort_by = array(
			'schedule',
			'hook',
			'group',
		);

		$this->search_by = array(
			'hook',
			'args',
			'claim_id',
		);

		$request_status = $this->get_request_status();

		if ( empty( $request_status ) ) {
			$this->sort_by[] = 'status';
		} elseif ( in_array( $request_status, array( 'in-progress', 'failed' ), true ) ) {
			$this->columns  += array( 'claim_id' => __( 'Claim ID', 'action-scheduler' ) );
			$this->sort_by[] = 'claim_id';
		}

		$this->row_actions = array(
			'hook' => array(
				'run'    => array(
					'name' => __( 'Run', 'action-scheduler' ),
					'desc' => __( 'Process the action now as if it were run as part of a queue', 'action-scheduler' ),
				),
				'cancel' => array(
					'name'  => __( 'Cancel', 'action-scheduler' ),
					'desc'  => __( 'Cancel the action now to avoid it being run in future', 'action-scheduler' ),
					'class' => 'cancel trash',
				),
			),
		);

		self::$time_periods = array(
			array(
				'seconds' => YEAR_IN_SECONDS,
				/* translators: %s: amount of time */
				'names'   => _n_noop( '%s year', '%s years', 'action-scheduler' ),
			),
			array(
				'seconds' => MONTH_IN_SECONDS,
				/* translators: %s: amount of time */
				'names'   => _n_noop( '%s month', '%s months', 'action-scheduler' ),
			),
			array(
				'seconds' => WEEK_IN_SECONDS,
				/* translators: %s: amount of time */
				'names'   => _n_noop( '%s week', '%s weeks', 'action-scheduler' ),
			),
			array(
				'seconds' => DAY_IN_SECONDS,
				/* translators: %s: amount of time */
				'names'   => _n_noop( '%s day', '%s days', 'action-scheduler' ),
			),
			array(
				'seconds' => HOUR_IN_SECONDS,
				/* translators: %s: amount of time */
				'names'   => _n_noop( '%s hour', '%s hours', 'action-scheduler' ),
			),
			array(
				'seconds' => MINUTE_IN_SECONDS,
				/* translators: %s: amount of time */
				'names'   => _n_noop( '%s minute', '%s minutes', 'action-scheduler' ),
			),
			array(
				'seconds' => 1,
				/* translators: %s: amount of time */
				'names'   => _n_noop( '%s second', '%s seconds', 'action-scheduler' ),
			),
		);

		parent::__construct(
			array(
				'singular' => 'action-scheduler',
				'plural'   => 'action-scheduler',
				'ajax'     => false,
			)
		);

		add_screen_option(
			'per_page',
			array(
				'default' => $this->items_per_page,
			)
		);

		add_filter( 'set_screen_option_' . $this->get_per_page_option_name(), array( $this, 'set_items_per_page_option' ), 10, 3 );
		set_screen_options();
	}

	/**
	 * Handles setting the items_per_page option for this screen.
	 *
	 * @param mixed  $status Default false (to skip saving the current option).
	 * @param string $option Screen option name.
	 * @param int    $value  Screen option value.
	 * @return int
	 */
	public function set_items_per_page_option( $status, $option, $value ) {
		return $value;
	}
	/**
	 * Convert an interval of seconds into a two part human friendly string.
	 *
	 * The WordPress human_time_diff() function only calculates the time difference to one degree, meaning
	 * even if an action is 1 day and 11 hours away, it will display "1 day". This function goes one step
	 * further to display two degrees of accuracy.
	 *
	 * Inspired by the Crontrol::interval() function by Edward Dale: https://wordpress.org/plugins/wp-crontrol/
	 *
	 * @param int $interval A interval in seconds.
	 * @param int $periods_to_include Depth of time periods to include, e.g. for an interval of 70, and $periods_to_include of 2, both minutes and seconds would be included. With a value of 1, only minutes would be included.
	 * @return string A human friendly string representation of the interval.
	 */
	private static function human_interval( $interval, $periods_to_include = 2 ) {

		if ( $interval <= 0 ) {
			return __( 'Now!', 'action-scheduler' );
		}

		$output           = '';
		$num_time_periods = count( self::$time_periods );

		for ( $time_period_index = 0, $periods_included = 0, $seconds_remaining = $interval; $time_period_index < $num_time_periods && $seconds_remaining > 0 && $periods_included < $periods_to_include; $time_period_index++ ) {

			$periods_in_interval = floor( $seconds_remaining / self::$time_periods[ $time_period_index ]['seconds'] );

			if ( $periods_in_interval > 0 ) {
				if ( ! empty( $output ) ) {
					$output .= ' ';
				}
				$output            .= sprintf( translate_nooped_plural( self::$time_periods[ $time_period_index ]['names'], $periods_in_interval, 'action-scheduler' ), $periods_in_interval );
				$seconds_remaining -= $periods_in_interval * self::$time_periods[ $time_period_index ]['seconds'];
				$periods_included++;
			}
		}

		return $output;
	}

	/**
	 * Returns the recurrence of an action or 'Non-repeating'. The output is human readable.
	 *
	 * @param ActionScheduler_Action $action Action object.
	 *
	 * @return string
	 */
	protected function get_recurrence( $action ) {
		$schedule = $action->get_schedule();
		if ( $schedule->is_recurring() && method_exists( $schedule, 'get_recurrence' ) ) {
			$recurrence = $schedule->get_recurrence();

			if ( is_numeric( $recurrence ) ) {
				/* translators: %s: time interval */
				return sprintf( __( 'Every %s', 'action-scheduler' ), self::human_interval( $recurrence ) );
			} else {
				return $recurrence;
			}
		}

		return __( 'Non-repeating', 'action-scheduler' );
	}

	/**
	 * Serializes the argument of an action to render it in a human friendly format.
	 *
	 * @param array $row The array representation of the current row of the table.
	 *
	 * @return string
	 */
	public function column_args( array $row ) {
		if ( empty( $row['args'] ) ) {
			return apply_filters( 'action_scheduler_list_table_column_args', '', $row );
		}

		$row_html = '<ul>';
		foreach ( $row['args'] as $key => $value ) {
			$row_html .= sprintf( '<li><code>%s => %s</code></li>', esc_html( var_export( $key, true ) ), esc_html( var_export( $value, true ) ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
		}
		$row_html .= '</ul>';

		return apply_filters( 'action_scheduler_list_table_column_args', $row_html, $row );
	}

	/**
	 * Prints the logs entries inline. We do so to avoid loading Javascript and other hacks to show it in a modal.
	 *
	 * @param array $row Action array.
	 * @return string
	 */
	public function column_log_entries( array $row ) {

		$log_entries_html = '<ol>';

		$timezone = new DateTimezone( 'UTC' );

		foreach ( $row['log_entries'] as $log_entry ) {
			$log_entries_html .= $this->get_log_entry_html( $log_entry, $timezone );
		}

		$log_entries_html .= '</ol>';

		return $log_entries_html;
	}

	/**
	 * Prints the logs entries inline. We do so to avoid loading Javascript and other hacks to show it in a modal.
	 *
	 * @param ActionScheduler_LogEntry $log_entry Log entry object.
	 * @param DateTimezone             $timezone Timestamp.
	 * @return string
	 */
	protected function get_log_entry_html( ActionScheduler_LogEntry $log_entry, DateTimezone $timezone ) {
		$date = $log_entry->get_date();
		$date->setTimezone( $timezone );
		return sprintf( '<li><strong>%s</strong><br/>%s</li>', esc_html( $date->format( 'Y-m-d H:i:s O' ) ), esc_html( $log_entry->get_message() ) );
	}

	/**
	 * Only display row actions for pending actions.
	 *
	 * @param array  $row         Row to render.
	 * @param string $column_name Current row.
	 *
	 * @return string
	 */
	protected function maybe_render_actions( $row, $column_name ) {
		if ( 'pending' === strtolower( $row['status_name'] ) ) {
			return parent::maybe_render_actions( $row, $column_name );
		}

		return '';
	}

	/**
	 * Renders admin notifications
	 *
	 * Notifications:
	 *  1. When the maximum number of tasks are being executed simultaneously.
	 *  2. Notifications when a task is manually executed.
	 *  3. Tables are missing.
	 */
	public function display_admin_notices() {
		global $wpdb;

		if ( ( is_a( $this->store, 'ActionScheduler_HybridStore' ) || is_a( $this->store, 'ActionScheduler_DBStore' ) ) && apply_filters( 'action_scheduler_enable_recreate_data_store', true ) ) {
			$table_list = array(
				'actionscheduler_actions',
				'actionscheduler_logs',
				'actionscheduler_groups',
				'actionscheduler_claims',
			);

			$found_tables = $wpdb->get_col( "SHOW TABLES LIKE '{$wpdb->prefix}actionscheduler%'" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			foreach ( $table_list as $table_name ) {
				if ( ! in_array( $wpdb->prefix . $table_name, $found_tables, true ) ) {
					$this->admin_notices[] = array(
						'class'   => 'error',
						'message' => __( 'It appears one or more database tables were missing. Attempting to re-create the missing table(s).', 'action-scheduler' ),
					);
					$this->recreate_tables();
					parent::display_admin_notices();

					return;
				}
			}
		}

		if ( $this->runner->has_maximum_concurrent_batches() ) {
			$claim_count           = $this->store->get_claim_count();
			$this->admin_notices[] = array(
				'class'   => 'updated',
				'message' => sprintf(
					/* translators: %s: amount of claims */
					_n(
						'Maximum simultaneous queues already in progress (%s queue). No additional queues will begin processing until the current queues are complete.',
						'Maximum simultaneous queues already in progress (%s queues). No additional queues will begin processing until the current queues are complete.',
						$claim_count,
						'action-scheduler'
					),
					$claim_count
				),
			);
		} elseif ( $this->store->has_pending_actions_due() ) {

			$async_request_lock_expiration = ActionScheduler::lock()->get_expiration( 'async-request-runner' );

			// No lock set or lock expired.
			if ( false === $async_request_lock_expiration || $async_request_lock_expiration < time() ) {
				$in_progress_url = add_query_arg( 'status', 'in-progress', remove_query_arg( 'status' ) );
				/* translators: %s: process URL */
				$async_request_message = sprintf( __( 'A new queue has begun processing. <a href="%s">View actions in-progress &raquo;</a>', 'action-scheduler' ), esc_url( $in_progress_url ) );
			} else {
				/* translators: %d: seconds */
				$async_request_message = sprintf( __( 'The next queue will begin processing in approximately %d seconds.', 'action-scheduler' ), $async_request_lock_expiration - time() );
			}

			$this->admin_notices[] = array(
				'class'   => 'notice notice-info',
				'message' => $async_request_message,
			);
		}

		$notification = get_transient( 'action_scheduler_admin_notice' );

		if ( is_array( $notification ) ) {
			delete_transient( 'action_scheduler_admin_notice' );

			$action           = $this->store->fetch_action( $notification['action_id'] );
			$action_hook_html = '<strong><code>' . $action->get_hook() . '</code></strong>';

			if ( 1 === absint( $notification['success'] ) ) {
				$class = 'updated';
				switch ( $notification['row_action_type'] ) {
					case 'run':
						/* translators: %s: action HTML */
						$action_message_html = sprintf( __( 'Successfully executed action: %s', 'action-scheduler' ), $action_hook_html );
						break;
					case 'cancel':
						/* translators: %s: action HTML */
						$action_message_html = sprintf( __( 'Successfully canceled action: %s', 'action-scheduler' ), $action_hook_html );
						break;
					default:
						/* translators: %s: action HTML */
						$action_message_html = sprintf( __( 'Successfully processed change for action: %s', 'action-scheduler' ), $action_hook_html );
						break;
				}
			} else {
				$class = 'error';
				/* translators: 1: action HTML 2: action ID 3: error message */
				$action_message_html = sprintf( __( 'Could not process change for action: "%1$s" (ID: %2$d). Error: %3$s', 'action-scheduler' ), $action_hook_html, esc_html( $notification['action_id'] ), esc_html( $notification['error_message'] ) );
			}

			$action_message_html = apply_filters( 'action_scheduler_admin_notice_html', $action_message_html, $action, $notification );

			$this->admin_notices[] = array(
				'class'   => $class,
				'message' => $action_message_html,
			);
		}

		parent::display_admin_notices();
	}

	/**
	 * Prints the scheduled date in a human friendly format.
	 *
	 * @param array $row The array representation of the current row of the table.
	 *
	 * @return string
	 */
	public function column_schedule( $row ) {
		return $this->get_schedule_display_string( $row['schedule'] );
	}

	/**
	 * Get the scheduled date in a human friendly format.
	 *
	 * @param ActionScheduler_Schedule $schedule Action's schedule.
	 * @return string
	 */
	protected function get_schedule_display_string( ActionScheduler_Schedule $schedule ) {

		$schedule_display_string = '';

		if ( is_a( $schedule, 'ActionScheduler_NullSchedule' ) ) {
			return __( 'async', 'action-scheduler' );
		}

		if ( ! method_exists( $schedule, 'get_date' ) || ! $schedule->get_date() ) {
			return '0000-00-00 00:00:00';
		}

		$next_timestamp = $schedule->get_date()->getTimestamp();

		$schedule_display_string .= $schedule->get_date()->format( 'Y-m-d H:i:s O' );
		$schedule_display_string .= '<br/>';

		if ( gmdate( 'U' ) > $next_timestamp ) {
			/* translators: %s: date interval */
			$schedule_display_string .= sprintf( __( ' (%s ago)', 'action-scheduler' ), self::human_interval( gmdate( 'U' ) - $next_timestamp ) );
		} else {
			/* translators: %s: date interval */
			$schedule_display_string .= sprintf( __( ' (%s)', 'action-scheduler' ), self::human_interval( $next_timestamp - gmdate( 'U' ) ) );
		}

		return $schedule_display_string;
	}

	/**
	 * Bulk delete.
	 *
	 * Deletes actions based on their ID. This is the handler for the bulk delete. It assumes the data
	 * properly validated by the callee and it will delete the actions without any extra validation.
	 *
	 * @param int[]  $ids Action IDs.
	 * @param string $ids_sql Inherited and unused.
	 */
	protected function bulk_delete( array $ids, $ids_sql ) {
		foreach ( $ids as $id ) {
			try {
				$this->store->delete_action( $id );
			} catch ( Exception $e ) {
				// A possible reason for an exception would include a scenario where the same action is deleted by a
				// concurrent request.
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
				error_log(
					sprintf(
						/* translators: 1: action ID 2: exception message. */
						__( 'Action Scheduler was unable to delete action %1$d. Reason: %2$s', 'action-scheduler' ),
						$id,
						$e->getMessage()
					)
				);
			}
		}
	}

	/**
	 * Implements the logic behind running an action. ActionScheduler_Abstract_ListTable validates the request and their
	 * parameters are valid.
	 *
	 * @param int $action_id Action ID.
	 */
	protected function row_action_cancel( $action_id ) {
		$this->process_row_action( $action_id, 'cancel' );
	}

	/**
	 * Implements the logic behind running an action. ActionScheduler_Abstract_ListTable validates the request and their
	 * parameters are valid.
	 *
	 * @param int $action_id Action ID.
	 */
	protected function row_action_run( $action_id ) {
		$this->process_row_action( $action_id, 'run' );
	}

	/**
	 * Force the data store schema updates.
	 */
	protected function recreate_tables() {
		if ( is_a( $this->store, 'ActionScheduler_HybridStore' ) ) {
			$store = $this->store;
		} else {
			$store = new ActionScheduler_HybridStore();
		}
		add_action( 'action_scheduler/created_table', array( $store, 'set_autoincrement' ), 10, 2 );

		$store_schema  = new ActionScheduler_StoreSchema();
		$logger_schema = new ActionScheduler_LoggerSchema();
		$store_schema->register_tables( true );
		$logger_schema->register_tables( true );

		remove_action( 'action_scheduler/created_table', array( $store, 'set_autoincrement' ), 10 );
	}
	/**
	 * Implements the logic behind processing an action once an action link is clicked on the list table.
	 *
	 * @param int    $action_id Action ID.
	 * @param string $row_action_type The type of action to perform on the action.
	 */
	protected function process_row_action( $action_id, $row_action_type ) {
		try {
			switch ( $row_action_type ) {
				case 'run':
					$this->runner->process_action( $action_id, 'Admin List Table' );
					break;
				case 'cancel':
					$this->store->cancel_action( $action_id );
					break;
			}
			$success       = 1;
			$error_message = '';
		} catch ( Exception $e ) {
			$success       = 0;
			$error_message = $e->getMessage();
		}

		set_transient( 'action_scheduler_admin_notice', compact( 'action_id', 'success', 'error_message', 'row_action_type' ), 30 );
	}

	/**
	 * {@inheritDoc}
	 */
	public function prepare_items() {
		$this->prepare_column_headers();

		$per_page = $this->get_items_per_page( $this->get_per_page_option_name(), $this->items_per_page );

		$query = array(
			'per_page' => $per_page,
			'offset'   => $this->get_items_offset(),
			'status'   => $this->get_request_status(),
			'orderby'  => $this->get_request_orderby(),
			'order'    => $this->get_request_order(),
			'search'   => $this->get_request_search_query(),
		);

		/**
		 * Change query arguments to query for past-due actions.
		 * Past-due actions have the 'pending' status and are in the past.
		 * This is needed because registering 'past-due' as a status is overkill.
		 */
		if ( 'past-due' === $this->get_request_status() ) {
			$query['status'] = ActionScheduler_Store::STATUS_PENDING;
			$query['date']   = as_get_datetime_object();
		}

		$this->items = array();

		$total_items = $this->store->query_actions( $query, 'count' );

		$status_labels = $this->store->get_status_labels();

		foreach ( $this->store->query_actions( $query ) as $action_id ) {
			try {
				$action = $this->store->fetch_action( $action_id );
			} catch ( Exception $e ) {
				continue;
			}
			if ( is_a( $action, 'ActionScheduler_NullAction' ) ) {
				continue;
			}
			$this->items[ $action_id ] = array(
				'ID'          => $action_id,
				'hook'        => $action->get_hook(),
				'status_name' => $this->store->get_status( $action_id ),
				'status'      => $status_labels[ $this->store->get_status( $action_id ) ],
				'args'        => $action->get_args(),
				'group'       => $action->get_group(),
				'log_entries' => $this->logger->get_logs( $action_id ),
				'claim_id'    => $this->store->get_claim_id( $action_id ),
				'recurrence'  => $this->get_recurrence( $action ),
				'schedule'    => $action->get_schedule(),
			);
		}

		$this->set_pagination_args(
			array(
				'total_items' => $total_items,
				'per_page'    => $per_page,
				'total_pages' => ceil( $total_items / $per_page ),
			)
		);
	}

	/**
	 * Prints the available statuses so the user can click to filter.
	 */
	protected function display_filter_by_status() {
		$this->status_counts = $this->store->action_counts() + $this->store->extra_action_counts();
		parent::display_filter_by_status();
	}

	/**
	 * Get the text to display in the search box on the list table.
	 */
	protected function get_search_box_button_text() {
		return __( 'Search hook, args and claim ID', 'action-scheduler' );
	}

	/**
	 * {@inheritDoc}
	 */
	protected function get_per_page_option_name() {
		return str_replace( '-', '_', $this->screen->id ) . '_per_page';
	}
}


================================================
FILE: classes/ActionScheduler_LogEntry.php
================================================
<?php

/**
 * Class ActionScheduler_LogEntry
 */
class ActionScheduler_LogEntry {

	/**
	 * Action's ID for log entry.
	 *
	 * @var int $action_id
	 */
	protected $action_id = '';

	/**
	 * Log entry's message.
	 *
	 * @var string $message
	 */
	protected $message = '';

	/**
	 * Log entry's date.
	 *
	 * @var Datetime $date
	 */
	protected $date;

	/**
	 * Constructor
	 *
	 * @param mixed    $action_id Action ID.
	 * @param string   $message   Message.
	 * @param Datetime $date      Datetime object with the time when this log entry was created. If this parameter is
	 *                            not provided a new Datetime object (with current time) will be created.
	 */
	public function __construct( $action_id, $message, $date = null ) {
		/*
		 * ActionScheduler_wpCommentLogger::get_entry() previously passed a 3rd param of $comment->comment_type
		 * to ActionScheduler_LogEntry::__construct(), goodness knows why, and the Follow-up Emails plugin
		 * hard-codes loading its own version of ActionScheduler_wpCommentLogger with that out-dated method,
		 * goodness knows why, so we need to guard against that here instead of using a DateTime type declaration
		 * for the constructor's 3rd param of $date and causing a fatal error with older versions of FUE.
		 */
		if ( null !== $date && ! is_a( $date, 'DateTime' ) ) {
			_doing_it_wrong( __METHOD__, 'The third parameter must be a valid DateTime instance, or null.', '2.0.0' );
			$date = null;
		}

		$this->action_id = $action_id;
		$this->message   = $message;
		$this->date      = $date ? $date : new Datetime();
	}

	/**
	 * Returns the date when this log entry was created
	 *
	 * @return Datetime
	 */
	public function get_date() {
		return $this->date;
	}

	/**
	 * Get action ID of log entry.
	 */
	public function get_action_id() {
		return $this->action_id;
	}

	/**
	 * Get log entry message.
	 */
	public function get_message() {
		return $this->message;
	}
}



================================================
FILE: classes/ActionScheduler_NullLogEntry.php
================================================
<?php

/**
 * Class ActionScheduler_NullLogEntry
 */
class ActionScheduler_NullLogEntry extends ActionScheduler_LogEntry {

	/**
	 * Construct.
	 *
	 * @param string $action_id Action ID.
	 * @param string $message   Log entry.
	 */
	public function __construct( $action_id = '', $message = '' ) {
		// nothing to see here.
	}

}


================================================
FILE: classes/ActionScheduler_OptionLock.php
================================================
<?php

/**
 * Provide a way to set simple transient locks to block behaviour
 * for up-to a given duration.
 *
 * Class ActionScheduler_OptionLock
 *
 * @since 3.0.0
 */
class ActionScheduler_OptionLock extends ActionScheduler_Lock {

	/**
	 * Set a lock using options for a given amount of time (60 seconds by default).
	 *
	 * Using an autoloaded option avoids running database queries or other resource intensive tasks
	 * on frequently triggered hooks, like 'init' or 'shutdown'.
	 *
	 * For example, ActionScheduler_QueueRunner->maybe_dispatch_async_request() uses a lock to avoid
	 * calling ActionScheduler_QueueRunner->has_maximum_concurrent_batches() every time the 'shutdown',
	 * hook is triggered, because that method calls ActionScheduler_QueueRunner->store->get_claim_count()
	 * to find the current number of claims in the database.
	 *
	 * @param string $lock_type A string to identify different lock types.
	 * @bool True if lock value has changed, false if not or if set failed.
	 */
	public function set( $lock_type ) {
		global $wpdb;

		$lock_key            = $this->get_key( $lock_type );
		$existing_lock_value = $this->get_existing_lock( $lock_type );
		$new_lock_value      = $this->new_lock_value( $lock_type );

		// The lock may not exist yet, or may have been deleted.
		if ( empty( $existing_lock_value ) ) {
			return (bool) $wpdb->insert(
				$wpdb->options,
				array(
					'option_name'  => $lock_key,
					'option_value' => $new_lock_value,
					'autoload'     => 'no',
				)
			);
		}

		if ( $this->get_expiration_from( $existing_lock_value ) >= time() ) {
			return false;
		}

		// Otherwise, try to obtain the lock.
		return (bool) $wpdb->update(
			$wpdb->options,
			array( 'option_value' => $new_lock_value ),
			array(
				'option_name'  => $lock_key,
				'option_value' => $existing_lock_value,
			)
		);
	}

	/**
	 * If a lock is set, return the timestamp it was set to expiry.
	 *
	 * @param string $lock_type A string to identify different lock types.
	 * @return bool|int False if no lock is set, otherwise the timestamp for when the lock is set to expire.
	 */
	public function get_expiration( $lock_type ) {
		return $this->get_expiration_from( $this->get_existing_lock( $lock_type ) );
	}

	/**
	 * Given the lock string, derives the lock expiration timestamp (or false if it cannot be determined).
	 *
	 * @param string $lock_value String containing a timestamp, or pipe-separated combination of unique value and timestamp.
	 *
	 * @return false|int
	 */
	private function get_expiration_from( $lock_value ) {
		$lock_string = explode( '|', $lock_value );

		// Old style lock?
		if ( count( $lock_string ) === 1 && is_numeric( $lock_string[0] ) ) {
			return (int) $lock_string[0];
		}

		// New style lock?
		if ( count( $lock_string ) === 2 && is_numeric( $lock_string[1] ) ) {
			return (int) $lock_string[1];
		}

		return false;
	}

	/**
	 * Get the key to use for storing the lock in the transient
	 *
	 * @param string $lock_type A string to identify different lock types.
	 * @return string
	 */
	protected function get_key( $lock_type ) {
		return sprintf( 'action_scheduler_lock_%s', $lock_type );
	}

	/**
	 * Supplies the existing lock value, or an empty string if not set.
	 *
	 * @param string $lock_type A string to identify different lock types.
	 *
	 * @return string
	 */
	private function get_existing_lock( $lock_type ) {
		global $wpdb;

		// Now grab the existing lock value, if there is one.
		return (string) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT option_value FROM $wpdb->options WHERE option_name = %s",
				$this->get_key( $lock_type )
			)
		);
	}

	/**
	 * Supplies a lock value consisting of a unique value and the current timestamp, which are separated by a pipe
	 * character.
	 *
	 * Example: (string) "649de012e6b262.09774912|1688068114"
	 *
	 * @param string $lock_type A string to identify different lock types.
	 *
	 * @return string
	 */
	private function new_lock_value( $lock_type ) {
		return uniqid( '', true ) . '|' . ( time() + $this->get_duration( $lock_type ) );
	}
}


================================================
FILE: classes/ActionScheduler_QueueCleaner.php
================================================
<?php

/**
 * Class ActionScheduler_QueueCleaner
 */
class ActionScheduler_QueueCleaner {

	/**
	 * The batch size.
	 *
	 * @var int
	 */
	protected $batch_size;

	/**
	 * ActionScheduler_Store instance.
	 *
	 * @var ActionScheduler_Store
	 */
	private $store = null;

	/**
	 * 31 days in seconds.
	 *
	 * @var int
	 */
	private $month_in_seconds = 2678400;

	/**
	 * Default list of statuses purged by the cleaner process.
	 *
	 * @var string[]
	 */
	private $default_statuses_to_purge = array(
		ActionScheduler_Store::STATUS_COMPLETE,
		ActionScheduler_Store::STATUS_CANCELED,
	);

	/**
	 * ActionScheduler_QueueCleaner constructor.
	 *
	 * @param ActionScheduler_Store|null $store      The store instance.
	 * @param int                        $batch_size The batch size.
	 */
	public function __construct( ?ActionScheduler_Store $store = null, $batch_size = 20 ) {
		$this->store      = $store ? $store : ActionScheduler_Store::instance();
		$this->batch_size = $batch_size;
	}

	/**
	 * Default queue cleaner process used by queue runner.
	 *
	 * @since 3.9.4 by default, failed actions are removed after three months.
	 * @return array
	 */
	public function delete_old_actions() {
		/**
		 * Filter the minimum scheduled date age for action deletion.
		 *
		 * @param int $retention_period Minimum scheduled age in seconds of the actions to be deleted.
		 */
		$lifespan = apply_filters( 'action_scheduler_retention_period', $this->month_in_seconds );

		/**
		 * Set the retention period, in seconds, for actions with a status returned by the action_scheduler_default_cleaner_statuses filter.
		 *
		 * @param int $retention_period Retention period in seconds.
		 */
		$lifespan_default = max( 0, (int) apply_filters( 'action_scheduler_retention_period_by_default', $lifespan ) );
		$lifespan_default = $lifespan_default > 0 ? $lifespan_default : $this->month_in_seconds;

		/**
		 * Set the retention period in seconds for actions with a failed status. If the action_scheduler_default_cleaner_statuses filter includes
		 * a failed status, this filter result will be ignored, and the retention period for failed actions will match that of other statuses.
		 *
		 * @param int $retention_period Retention period in seconds.
		 */
		$lifespan_failed = max( 0, (int) apply_filters( 'action_scheduler_retention_period_for_failed', 3 * $this->month_in_seconds ) );
		// We considered 12-month, 3-month, and 1-month options for failed action retention and selected a 3-month period
		// to align with the quarterly accounting cycle. Store owners may adjust the retention period to achieve PCI DSS
		// compliance or to align with a different accounting cycle, as needed.

		try {
			$cutoff_failed  = as_get_datetime_object( $lifespan_failed . ' seconds ago' );
			$cutoff_default = as_get_datetime_object( $lifespan_default . ' seconds ago' );
		} catch ( Exception $e ) {
			_doing_it_wrong(
				__METHOD__,
				sprintf(
					/* Translators: %s is the exception message. */
					esc_html__( 'It was not possible to determine a valid cut-off time: %s.', 'action-scheduler' ),
					esc_html( $e->getMessage() )
				),
				'3.5.5'
			);

			return array();
		}

		/**
		 * Filter the statuses when cleaning the queue.
		 *
		 * @param string[] $default_statuses_to_purge Action statuses to clean.
		 */
		$statuses_to_purge = (array) apply_filters( 'action_scheduler_default_cleaner_statuses', $this->default_statuses_to_purge );

		$deleted_failed_entries = array();
		// Backward compatibility note: if store already purging the failed statuses, don't change the behaviour.
		if ( $lifespan_failed > 0 && ! in_array( ActionScheduler_Store::STATUS_FAILED, $statuses_to_purge, true ) ) {
			// Use a fixed default batch size to ensure that the cleanup of failed actions does not interfere with the regular cleanup.
			$deleted_failed_entries = $this->clean_actions( array( ActionScheduler_Store::STATUS_FAILED ), $cutoff_failed, 20 );
		}

		$deleted_entries = $this->clean_actions( $statuses_to_purge, $cutoff_default, $this->get_batch_size() );

		return array_merge( $deleted_failed_entries, $deleted_entries );
	}

	/**
	 * Delete selected actions limited by status and date.
	 *
	 * @param string[] $statuses_to_purge List of action statuses to purge. Defaults to canceled, complete.
	 * @param DateTime $cutoff_date Date limit for selecting actions. Defaults to 31 days ago.
	 * @param int|null $batch_size Maximum number of actions per status to delete. Defaults to 20.
	 * @param string   $context Calling process context. Defaults to `old`.
	 * @return array Actions deleted.
	 */
	public function clean_actions( array $statuses_to_purge, DateTime $cutoff_date, $batch_size = null, $context = 'old' ) {
		$batch_size = ! is_null( $batch_size ) ? $batch_size : $this->batch_size;
		$cutoff     = ! is_null( $cutoff_date ) ? $cutoff_date : as_get_datetime_object( $this->month_in_seconds . ' seconds ago' );
		$lifespan   = time() - $cutoff->getTimestamp();

		if ( empty( $statuses_to_purge ) ) {
			$statuses_to_purge = $this->default_statuses_to_purge;
		}

		$deleted_actions = array();
		foreach ( $statuses_to_purge as $status ) {
			$actions_to_delete = $this->store->query_actions(
				array(
					'status'           => $status,
					'modified'         => $cutoff,
					'modified_compare' => '<=',
					'per_page'         => $batch_size,
					'orderby'          => 'none',
				)
			);
			$deleted_actions[] = $this->delete_actions( $actions_to_delete, $lifespan, $context );
		}

		return array_merge( array(), ...$deleted_actions );
	}

	/**
	 * Delete actions.
	 *
	 * @param int[]  $actions_to_delete List of action IDs to delete.
	 * @param int    $lifespan Minimum scheduled age in seconds of the actions being deleted.
	 * @param string $context Context of the delete request.
	 * @return array Deleted action IDs.
	 */
	private function delete_actions( array $actions_to_delete, $lifespan = null, $context = 'old' ) {
		$deleted_actions = array();

		if ( is_null( $lifespan ) ) {
			$lifespan = $this->month_in_seconds;
		}

		foreach ( $actions_to_delete as $action_id ) {
			try {
				$this->store->delete_action( $action_id );
				$deleted_actions[] = $action_id;
			} catch ( Exception $e ) {
				/**
				 * Notify 3rd party code of exceptions when deleting a completed action older than the retention period
				 *
				 * This hook provides a way for 3rd party code to log or otherwise handle exceptions relating to their
				 * actions.
				 *
				 * @param int $action_id The scheduled actions ID in the data store
				 * @param Exception $e The exception thrown when attempting to delete the action from the data store
				 * @param int $lifespan The retention period, in seconds, for old actions
				 * @param int $count_of_actions_to_delete The number of old actions being deleted in this batch
				 * @since 2.0.0
				 */
				do_action( "action_scheduler_failed_{$context}_action_deletion", $action_id, $e, $lifespan, count( $actions_to_delete ) );
			}
		}
		return $deleted_actions;
	}

	/**
	 * Unclaim pending actions that have not been run within a given time limit.
	 *
	 * When called by ActionScheduler_Abstract_QueueRunner::run_cleanup(), the time limit passed
	 * as a parameter is 10x the time limit used for queue processing.
	 *
	 * @param int $time_limit The number of seconds to allow a queue to run before unclaiming its pending actions. Default 300 (5 minutes).
	 */
	public function reset_timeouts( $time_limit = 300 ) {
		$timeout = apply_filters( 'action_scheduler_timeout_period', $time_limit );

		if ( $timeout < 0 ) {
			return;
		}

		$cutoff           = as_get_datetime_object( $timeout . ' seconds ago' );
		$actions_to_reset = $this->store->query_actions(
			array(
				'status'           => ActionScheduler_Store::STATUS_PENDING,
				'modified'         => $cutoff,
				'modified_compare' => '<=',
				'claimed'          => true,
				'per_page'         => $this->get_batch_size(),
				'orderby'          => 'none',
			)
		);

		foreach ( $actions_to_reset as $action_id ) {
			$this->store->unclaim_action( $action_id );
			do_action( 'action_scheduler_reset_action', $action_id );
		}
	}

	/**
	 * Mark actions that have been running for more than a given time limit as failed, based on
	 * the assumption some uncatchable and unloggable fatal error occurred during processing.
	 *
	 * When called by ActionScheduler_Abstract_QueueRunner::run_cleanup(), the time limit passed
	 * as a parameter is 10x the time limit used for queue processing.
	 *
	 * @param int $time_limit The number of seconds to allow an action to run before it is considered to have failed. Default 300 (5 minutes).
	 */
	public function mark_failures( $time_limit = 300 ) {
		$timeout = apply_filters( 'action_scheduler_failure_period', $time_limit );

		if ( $timeout < 0 ) {
			return;
		}

		$cutoff           = as_get_datetime_object( $timeout . ' seconds ago' );
		$actions_to_reset = $this->store->query_actions(
			array(
				'status'           => ActionScheduler_Store::STATUS_RUNNING,
				'modified'         => $cutoff,
				'modified_compare' => '<=',
				'per_page'         => $this->get_batch_size(),
				'orderby'          => 'none',
			)
		);

		foreach ( $actions_to_reset as $action_id ) {
			$this->store->mark_failure( $action_id );
			do_action( 'action_scheduler_failed_action', $action_id, $timeout );
		}
	}

	/**
	 * Do all of the cleaning actions.
	 *
	 * @param int $time_limit The number of seconds to use as the timeout and failure period. Default 300 (5 minutes).
	 */
	public function clean( $time_limit = 300 ) {
		$this->delete_old_actions();
		$this->reset_timeouts( $time_limit );
		$this->mark_failures( $time_limit );
	}

	/**
	 * Get the batch size for cleaning the queue.
	 *
	 * @return int
	 */
	protected function get_batch_size() {
		/**
		 * Filter the batch size when cleaning the queue.
		 *
		 * @param int $batch_size The number of actions to clean in one batch.
		 */
		return absint( apply_filters( 'action_scheduler_cleanup_batch_size', $this->batch_size ) );
	}
}


================================================
FILE: classes/ActionScheduler_QueueRunner.php
================================================
<?php

/**
 * Class ActionScheduler_QueueRunner
 */
class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
	const WP_CRON_HOOK = 'action_scheduler_run_queue';

	const WP_CRON_SCHEDULE = 'every_minute';

	/**
	 * ActionScheduler_AsyncRequest_QueueRunner instance.
	 *
	 * @var ActionScheduler_AsyncRequest_QueueRunner
	 */
	protected $async_request;

	/**
	 * ActionScheduler_QueueRunner instance.
	 *
	 * @var ActionScheduler_QueueRunner
	 */
	private static $runner = null;

	/**
	 * Number of processed actions.
	 *
	 * @var int
	 */
	private $processed_actions_count = 0;

	/**
	 * Get instance.
	 *
	 * @return ActionScheduler_QueueRunner
	 * @codeCoverageIgnore
	 */
	public static function instance() {
		if ( empty( self::$runner ) ) {
			$class        = apply_filters( 'action_scheduler_queue_runner_class', 'ActionScheduler_QueueRunner' );
			self::$runner = new $class();
		}

		return self::$runner;
	}

	/**
	 * ActionScheduler_QueueRunner constructor.
	 *
	 * @param ActionScheduler_Store|null                    $store Store object.
	 * @param ActionScheduler_FatalErrorMonitor|null        $monitor Monitor object.
	 * @param ActionScheduler_QueueCleaner|null             $cleaner Cleaner object.
	 * @param ActionScheduler_AsyncRequest_QueueRunner|null $async_request Async request runner object.
	 */
	public function __construct( ?ActionScheduler_Store $store = null, ?ActionScheduler_FatalErrorMonitor $monitor = null, ?ActionScheduler_QueueCleaner $cleaner = null, ?ActionScheduler_AsyncRequest_QueueRunner $async_request = null ) {
		parent::__construct( $store, $monitor, $cleaner );

		if ( is_null( $async_request ) ) {
			$async_request = new ActionScheduler_AsyncRequest_QueueRunner( $this->store );
		}

		$this->async_request = $async_request;
	}

	/**
	 * Initialize.
	 *
	 * @codeCoverageIgnore
	 */
	public function init() {

		add_filter( 'cron_schedules', array( self::instance(), 'add_wp_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.CronSchedulesInterval

		// Check for and remove any WP Cron hook scheduled by Action Scheduler < 3.0.0, which didn't include the $context param.
		$next_timestamp = wp_next_scheduled( self::WP_CRON_HOOK );
		if ( $next_timestamp ) {
			wp_unschedule_event( $next_timestamp, self::WP_CRON_HOOK );
		}

		$cron_context = array( 'WP Cron' );

		if ( ! wp_next_scheduled( self::WP_CRON_HOOK, $cron_context ) ) {
			$schedule = apply_filters( 'action_scheduler_run_schedule', self::WP_CRON_SCHEDULE );
			wp_schedule_event( time(), $schedule, self::WP_CRON_HOOK, $cron_context );
		}

		add_action( self::WP_CRON_HOOK, array( self::instance(), 'run' ) );
		$this->hook_dispatch_async_request();
	}

	/**
	 * Hook check for dispatching an async request.
	 */
	public function hook_dispatch_async_request() {
		add_action( 'shutdown', array( $this, 'maybe_dispatch_async_request' ) );
	}

	/**
	 * Unhook check for dispatching an async request.
	 */
	public function unhook_dispatch_async_request() {
		remove_action( 'shutdown', array( $this, 'maybe_dispatch_async_request' ) );
	}

	/**
	 * Check if we should dispatch an async request to process actions.
	 *
	 * This method is attached to 'shutdown', so is called frequently. To avoid slowing down
	 * the site, it mitigates the work performed in each request by:
	 * 1. checking if it's in the admin context and then
	 * 2. haven't run on the 'shutdown' hook within the lock time (60 seconds by default)
	 * 3. haven't exceeded the number of allowed batches.
	 *
	 * The order of these checks is important, because they run from a check on a value:
	 * 1. in memory - is_admin() maps to $GLOBALS or the WP_ADMIN constant
	 * 2. in memory - transients use autoloaded options by default
	 * 3. from a database query - has_maximum_concurrent_batches() run the query
	 *    $this->store->get_claim_count() to find the current number of claims in the DB.
	 *
	 * If all of these conditions are met, then we request an async runner check whether it
	 * should dispatch a request to process pending actions.
	 */
	public function maybe_dispatch_async_request() {
		// Only start an async queue at most once every 60 seconds.
		if (
			is_admin()
			&& ! ActionScheduler::lock()->is_locked( 'async-request-runner' )
			&& ActionScheduler::lock()->set( 'async-request-runner' )
		) {
			$this->async_request->maybe_dispatch();
		}
	}

	/**
	 * Process actions in the queue. Attached to self::WP_CRON_HOOK i.e. 'action_scheduler_run_queue'
	 *
	 * The $context param of this method defaults to 'WP Cron', because prior to Action Scheduler 3.0.0
	 * that was the only context in which this method was run, and the self::WP_CRON_HOOK hook had no context
	 * passed along with it. New code calling this method directly, or by triggering the self::WP_CRON_HOOK,
	 * should set a context as the first parameter. For an example of this, refer to the code seen in
	 *
	 * @see ActionScheduler_AsyncRequest_QueueRunner::handle()
	 *
	 * @param string $context Optional identifier for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron'
	 *        Generally, this should be capitalised and not localised as it's a proper noun.
	 * @return int The number of actions processed.
	 */
	public function run( $context = 'WP Cron' ) {
		ActionScheduler_Compatibility::raise_memory_limit();
		ActionScheduler_Compatibility::raise_time_limit( $this->get_time_limit() );
		do_action( 'action_scheduler_before_process_queue' );
		$this->run_cleanup();

		$this->processed_actions_count = 0;
		if ( false === $this->has_maximum_concurrent_batches() ) {
			$batch_size = apply_filters( 'action_scheduler_queue_runner_batch_size', 25 );
			// Note: gc_collect_cycles() was considered here and in do_batch/clear_caches, but rejected:
			// upside is speculative, GC sweep cost scales with object count and can cause pauses.
			do {
				$processed_actions_in_batch     = $this->do_batch( $batch_size, $context );
				$this->processed_actions_count += $processed_actions_in_batch;
			} while ( $processed_actions_in_batch > 0 && ! $this->batch_limits_exceeded( $this->processed_actions_count ) ); // keep going until we run out of actions, time, or memory.
		}

		do_action( 'action_scheduler_after_process_queue' );
		return $this->processed_actions_count;
	}

	/**
	 * Process a batch of actions pending in the queue.
	 *
	 * Actions are processed by claiming a set of pending actions then processing each one until either the batch
	 * size is completed, or memory or time limits are reached, defined by @see $this->batch_limits_exceeded().
	 *
	 * @param int    $size The maximum number of actions to process in the batch.
	 * @param string $context Optional identifier for the context in which this action is being processed, e.g. 'WP CLI' or 'WP Cron'
	 *                        Generally, this should be capitalised and not localised as it's a proper noun.
	 * @return int The number of actions processed.
	 */
	protected function do_batch( $size = 100, $context = '' ) {
		$claim = $this->store->stake_claim( $size );
		$this->monitor->attach( $claim );
		$processed_actions = 0;

		$claim_id = $claim->get_id();
		foreach ( $claim->get_actions() as $action_id ) {
			// bail if we lost the claim.
			if ( $claim_id !== $this->store->get_claim_id( $action_id ) ) {
				break;
			}

			$this->process_action( $action_id, $context );
			$processed_actions++;

			if ( $this->batch_limits_exceeded( $processed_actions + $this->processed_actions_count ) ) {
				break;
			}
		}
		$this->store->release_claim( $claim );
		$this->monitor->detach();
		$this->clear_caches();
		return $processed_actions;
	}

	/**
	 * Flush the cache if possible (intended for use after a batch of actions has been processed).
	 *
	 * This is useful because running large batches can eat up memory and because invalid data can accrue in the
	 * runtime cache, which may lead to unexpected results.
	 */
	protected function clear_caches() {
		/*
		 * Calling wp_cache_flush_runtime() lets us clear the runtime cache without invalidating the external object
		 * cache, so we will always prefer this method (as compared to calling wp_cache_flush()) when it is available.
		 *
		 * However, this function was only introduced in WordPress 6.0. Additionally, the preferred way of detecting if
		 * it is supported changed in WordPress 6.1 so we use two different methods to decide if we should utilize it.
		 */
		$flushing_runtime_cache_explicitly_supported = function_exists( 'wp_cache_supports' ) && wp_cache_supports( 'flush_runtime' );
		$flushing_runtime_cache_implicitly_supported = ! function_exists( 'wp_cache_supports' ) && function_exists( 'wp_cache_flush_runtime' );

		if ( $flushing_runtime_cache_explicitly_supported || $flushing_runtime_cache_implicitly_supported ) {
			wp_cache_flush_runtime();
		} elseif (
			! wp_using_ext_object_cache()
			/**
			 * When an external object cache is in use, and when wp_cache_flush_runtime() is not available, then
			 * normally the cache will not be flushed after processing a batch of actions (to avoid a performance
			 * penalty for other processes).
			 *
			 * This filter makes it possible to override this behavior and always flush the cache, even if an external
			 * object cache is in use.
			 *
			 * @since 1.0
			 *
			 * @param bool $flush_cache If the cache should be flushed.
			 */
			|| apply_filters( 'action_scheduler_queue_runner_flush_cache', false )
		) {
			wp_cache_flush();
		}
	}

	/**
	 * Add schedule to WP cron.
	 *
	 * @param array<string, array<string, int|string>> $schedules Schedules.
	 * @return array<string, array<string, int|string>>
	 */
	public function add_wp_cron_schedule( $schedules ) {
		$schedules['every_minute'] = array(
			'interval' => 60, // in seconds.
			'display'  => __( 'Every minute', 'action-scheduler' ),
		);

		return $schedules;
	}
}


================================================
FILE: classes/ActionScheduler_RecurringActionScheduler.php
================================================
<?php

/**
 * Class ActionScheduler_RecurringActionScheduler
 *
 * This class ensures that the `action_scheduler_ensure_recurring_actions` hook is triggered on a daily interval. This
 * simplifies the process for other plugins to register their recurring actions without requiring each plugin to query
 * or schedule actions independently on every request.
 */
class ActionScheduler_RecurringActionScheduler {

	/**
	 * @var string The hook of the scheduled recurring action that is run to trigger the
	 *      `action_scheduler_ensure_recurring_actions` hook that plugins should use.  We can't directly have the
	 *      scheduled action hook be the hook plugins should use because the actions will show as failed if no plugin
	 *      was actively hooked into it.
	 */
	private const RUN_SCHEDULED_RECURRING_ACTIONS_HOOK = 'action_scheduler_run_recurring_actions_schedule_hook';

	/**
	 * Initialize the instance.  Should only be run on a single instance per request.
	 *
	 * @return void
	 */
	public function init(): void {
		add_action( self::RUN_SCHEDULED_RECURRING_ACTIONS_HOOK, array( $this, 'run_recurring_scheduler_hook' ) );
		if ( is_admin() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {
			add_action( 'action_scheduler_init', array( $this, 'schedule_recurring_scheduler_hook' ) );
		}
	}

	/**
	 * Schedule the recurring `action_scheduler_ensure_recurring_actions` action if not already scheduled.
	 *
	 * @return void
	 */
	public function schedule_recurring_scheduler_hook(): void {
		if ( false === get_transient( 'as_is_ensure_recurring_actions_scheduled' ) ) {
			if ( ! as_has_scheduled_action( self::RUN_SCHEDULED_RECURRING_ACTIONS_HOOK ) ) {
				$date = ActionScheduler_TimezoneHelper::set_local_timezone( new DateTime() )->modify( 'tomorrow 3am' );
				as_schedule_recurring_action(
					$date->getTimestamp(),
					DAY_IN_SECONDS,
					self::RUN_SCHEDULED_RECURRING_ACTIONS_HOOK,
					[],
					'ActionScheduler',
					true,
					20
				);
			}
			set_transient( 'as_is_ensure_recurring_actions_scheduled', true, HOUR_IN_SECONDS );
		}
	}

	/**
	 * Trigger the hook to allow other plugins to schedule their recurring actions.
	 *
	 * @return void
	 */
	public function run_recurring_scheduler_hook(): void {
		/**
		 * Fires to allow extensions to verify and ensure their recurring actions are scheduled.
		 *
		 * This action is scheduled to trigger once every 24 hrs for the purpose of having 3rd party plugins verify that
		 * any previously scheduled recurring actions are still scheduled. Because recurring actions could stop getting
		 * rescheduled by default due to excessive failures, database issues, or other interruptions, extensions can use
		 * this hook to check for the existence of their recurring actions and reschedule them if necessary.
		 *
		 * Example usage:
		 *
		 * add_action('action_scheduler_ensure_recurring_actions', function() {
		 *     // Check if the recurring action is scheduled, and reschedule if missing.
		 *     if ( ! as_has_scheduled_action('my_recurring_action') ) {
		 *         as_schedule_recurring_action( time(), HOUR_IN_SECONDS, 'my_recurring_action' );
		 *     }
		 * });
		 *
		 * @since 3.9.3
		 */
		do_action( 'action_scheduler_ensure_recurring_actions' );
	}
}


================================================
FILE: classes/ActionScheduler_SystemInformation.php
================================================
<?php

/**
 * Provides information about active and registered instances of Action Scheduler.
 */
class ActionScheduler_SystemInformation {
	/**
	 * Returns information about the plugin or theme which contains the current active version
	 * of Action Scheduler.
	 *
	 * If this cannot be determined, or if Action Scheduler is being loaded via some other
	 * method, then it will return an empty array. Otherwise, if populated, the array will
	 * look like the following:
	 *
	 *     [
	 *         'type' => 'plugin', # or 'theme'
	 *         'name' => 'Name',
	 *     ]
	 *
	 * @return array
	 */
	public static function active_source(): array {
		$plugins      = get_plugins();
		$plugin_files = array_keys( $plugins );

		foreach ( $plugin_files as $plugin_file ) {
			$plugin_path = trailingslashit( WP_PLUGIN_DIR ) . dirname( $plugin_file );
			$plugin_file = trailingslashit( WP_PLUGIN_DIR ) . $plugin_file;

			if ( 0 !== strpos( dirname( __DIR__ ), $plugin_path ) ) {
				continue;
			}

			$plugin_data = get_plugin_data( $plugin_file );

			if ( ! is_array( $plugin_data ) || empty( $plugin_data['Name'] ) ) {
				continue;
			}

			return array(
				'type' => 'plugin',
				'name' => $plugin_data['Name'],
			);
		}

		$themes = (array) search_theme_directories();

		foreach ( $themes as $slug => $data ) {
			$needle = trailingslashit( $data['theme_root'] ) . $slug . '/';

			if ( 0 !== strpos( __FILE__, $needle ) ) {
				continue;
			}

			$theme = wp_get_theme( $slug );

			if ( ! is_object( $theme ) || ! is_a( $theme, \WP_Theme::class ) ) {
				continue;
			}

			return array(
				'type' => 'theme',
				// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
				'name' => $theme->Name,
			);
		}

		return array();
	}

	/**
	 * Returns the directory path for the currently active installation of Action Scheduler.
	 *
	 * @return string
	 */
	public static function active_source_path(): string {
		return trailingslashit( dirname( __DIR__ ) );
	}

	/**
	 * Get registered sources.
	 *
	 * It is not always possible to obtain this information. For instance, if earlier versions (<=3.9.0) of
	 * Action Scheduler register themselves first, then the necessary data about registered sources will
	 * not be available.
	 *
	 * @return array<string, string>
	 */
	public static function get_sources() {
		$versions = ActionScheduler_Versions::instance();
		return method_exists( $versions, 'get_sources' ) ? $versions->get_sources() : array();
	}
}


================================================
FILE: classes/ActionScheduler_Versions.php
================================================
<?php

/**
 * Class ActionScheduler_Versions
 */
class ActionScheduler_Versions {
	/**
	 * ActionScheduler_Versions instance.
	 *
	 * @var ActionScheduler_Versions
	 */
	private static $instance = null;

	/**
	 * Versions.
	 *
	 * @var array<string, callable>
	 */
	private $versions = array();

	/**
	 * Registered sources.
	 *
	 * @var array<string, string>
	 */
	private $sources = array();

	/**
	 * Register version's callback.
	 *
	 * @param string   $version_string          Action Scheduler version.
	 * @param callable $initialization_callback Callback to initialize the version.
	 */
	public function register( $version_string, $initialization_callback ) {
		if ( isset( $this->versions[ $version_string ] ) ) {
			return false;
		}

		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
		$backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS );
		$source    = $backtrace[0]['file'];

		$this->versions[ $version_string ] = $initialization_callback;
		$this->sources[ $source ]          = $version_string;
		return true;
	}

	/**
	 * Get all versions.
	 */
	public function get_versions() {
		return $this->versions;
	}

	/**
	 * Get registered sources.
	 *
	 * Use with caution: this method is only available as of Action Scheduler's 3.9.1
	 * release and, owing to the way Action Scheduler is loaded, it's possible that the
	 * class definition used at runtime will belong to an earlier version.
	 *
	 * @since 3.9.1
	 *
	 * @return array<string, string>
	 */
	public function get_sources() {
		return $this->sources;
	}

	/**
	 * Get latest version registered.
	 */
	public function latest_version() {
		$keys = array_keys( $this->versions );
		if ( empty( $keys ) ) {
			return false;
		}
		uasort( $keys, 'version_compare' );
		return end( $keys );
	}

	/**
	 * Get callback for latest registered version.
	 */
	public function latest_version_callback() {
		$latest = $this->latest_version();

		if ( empty( $latest ) || ! isset( $this->versions[ $latest ] ) ) {
			return '__return_null';
		}

		return $this->versions[ $latest ];
	}

	/**
	 * Get instance.
	 *
	 * @return ActionScheduler_Versions
	 * @codeCoverageIgnore
	 */
	public static function instance() {
		if ( empty( self::$instance ) ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Initialize.
	 *
	 * @codeCoverageIgnore
	 */
	public static function initialize_latest_version() {
		$self = self::instance();
		call_user_func( $self->latest_version_callback() );
	}

	/**
	 * Returns information about the plugin or theme which contains the current active version
	 * of Action Scheduler.
	 *
	 * If this cannot be determined, or if Action Scheduler is being loaded via some other
	 * method, then it will return an empty array. Otherwise, if populated, the array will
	 * look like the following:
	 *
	 *     [
	 *         'type' => 'plugin', # or 'theme'
	 *         'name' => 'Name',
	 *     ]
	 *
	 * @deprecated 3.9.2 Use ActionScheduler_SystemInformation::active_source().
	 *
	 * @return array
	 */
	public function active_source(): array {
		_deprecated_function( __METHOD__, '3.9.2', 'ActionScheduler_SystemInformation::active_source()' );
		return ActionScheduler_SystemInformation::active_source();
	}

	/**
	 * Returns the directory path for the currently active installation of Action Scheduler.
	 *
	 * @deprecated 3.9.2 Use ActionScheduler_SystemInformation::active_source_path().
	 *
	 * @return string
	 */
	public function active_source_path(): string {
		_deprecated_function( __METHOD__, '3.9.2', 'ActionScheduler_SystemInformation::active_source_path()' );
		return ActionScheduler_SystemInformation::active_source_path();
	}
}


================================================
FILE: classes/ActionScheduler_WPCommentCleaner.php
================================================
<?php

/**
 * Class ActionScheduler_WPCommentCleaner
 *
 * @since 3.0.0
 */
class ActionScheduler_WPCommentCleaner {

	/**
	 * Post migration hook used to cleanup the WP comment table.
	 *
	 * @var string
	 */
	protected static $cleanup_hook = 'action_scheduler/cleanup_wp_comment_logs';

	/**
	 * An instance of the ActionScheduler_wpCommentLogger class to interact with the comments table.
	 *
	 * This instance should only be used as an interface. It should not be initialized.
	 *
	 * @var ActionScheduler_wpCommentLogger
	 */
	protected static $wp_comment_logger = null;

	/**
	 * The key used to store the cached value of whether there are logs in the WP comment table.
	 *
	 * @var string
	 */
	protected static $has_logs_option_key = 'as_has_wp_comment_logs';

	/**
	 * Initialize the class and attach callbacks.
	 */
	public static function init() {
		if ( empty( self::$wp_comment_logger ) ) {
			self::$wp_comment_logger = new ActionScheduler_wpCommentLogger();
		}

		add_action( self::$cleanup_hook, array( __CLASS__, 'delete_all_action_comments' ) );

		// While there are orphaned logs left in the comments table, we need to attach the callbacks which filter comment counts.
		add_action( 'pre_get_comments', array( self::$wp_comment_logger, 'filter_comment_queries' ), 10, 1 );
		add_action( 'wp_count_comments', array( self::$wp_comment_logger, 'filter_comment_count' ), 20, 2 ); // run after WC_Comments::wp_count_comments() to make sure we exclude order notes and action logs.
		add_action( 'comment_feed_where', array( self::$wp_comment_logger, 'filter_comment_feed' ), 10, 2 );

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

	/**
	 * Determines if there are log entries in the wp comments table.
	 *
	 * Uses the flag set on migration completion set by @see self::maybe_schedule_cleanup().
	 *
	 * @return boolean Whether there are scheduled action comments in the comments table.
	 */
	public static function has_logs() {
		return 'yes' === get_option( self::$has_logs_option_key );
	}

	/**
	 * Schedules the WP Post comment table cleanup to run in 6 months if it's not already scheduled.
	 * Attached to the migration complete hook 'action_scheduler/migration_complete'.
	 */
	public static function maybe_schedule_cleanup() {
		$has_logs = 'no';

		$args = array(
			'type'   => ActionScheduler_wpCommentLogger::TYPE,
			'number' => 1,
			'fields' => 'ids',
		);

		if ( (bool) get_comments( $args ) ) {
			$has_logs = 'yes';

			if ( ! as_next_scheduled_action( self::$cleanup_hook ) ) {
				as_schedule_single_action( gmdate( 'U' ) + ( 6 * MONTH_IN_SECONDS ), self::$cleanup_hook );
			}
		}

		update_option( self::$has_logs_option_key, $has_logs, true );
	}

	/**
	 * Delete all action comments from the WP Comments table.
	 */
	public static function delete_all_action_comments() {
		global $wpdb;

		$wpdb->delete(
			$wpdb->comments,
			array(
				'comment_type'  => ActionScheduler_wpCommentLogger::TYPE,
				'comment_agent' => ActionScheduler_wpCommentLogger::AGENT,
			)
		);

		update_option( self::$has_logs_option_key, 'no', true );
	}

	/**
	 * Registers admin notices about the orphaned action logs.
	 */
	public static function register_admin_notice() {
		add_action( 'admin_notices', array( __CLASS__, 'print_admin_notice' ) );
	}

	/**
	 * Prints details about the orphaned action logs and includes information on where to learn more.
	 */
	public static function print_admin_notice() {
		$next_cleanup_message        = '';
		$next_scheduled_cleanup_hook = as_next_scheduled_action( self::$cleanup_hook );

		if ( $next_scheduled_cleanup_hook ) {
			/* translators: %s: date interval */
			$next_cleanup_message = sprintf( __( 'This data will be deleted in %s.', 'action-scheduler' ), human_time_diff( gmdate( 'U' ), $next_scheduled_cleanup_hook ) );
		}

		$notice = sprintf(
			/* translators: 1: next cleanup message 2: github issue URL */
			__( 'Action Scheduler has migrated data to custom tables; however, orphaned log entries exist in the WordPress Comments table. %1$s <a href="%2$s">Learn more &raquo;</a>', 'action-scheduler' ),
			$next_cleanup_message,
			'https://github.com/woocommerce/action-scheduler/issues/368'
		);

		echo '<div class="notice notice-warning"><p>' . wp_kses_post( $notice ) . '</p></div>';
	}
}


================================================
FILE: classes/ActionScheduler_wcSystemStatus.php
================================================
<?php

/**
 * Class ActionScheduler_wcSystemStatus
 */
class ActionScheduler_wcSystemStatus {

	/**
	 * The active data stores
	 *
	 * @var ActionScheduler_Store
	 */
	protected $store;

	/**
	 * Constructor method for ActionScheduler_wcSystemStatus.
	 *
	 * @param ActionScheduler_Store $store Active store object.
	 *
	 * @return void
	 */
	public function __construct( $store ) {
		$this->store = $store;
	}

	/**
	 * Display action data, including number of actions grouped by status and the oldest & newest action in each status.
	 *
	 * Helpful to identify issues, like a clogged queue.
	 */
	public function render() {
		$action_counts     = $this->store->action_counts();
		$status_labels     = $this->store->get_status_labels();
		$oldest_and_newest = $this->get_oldest_and_newest( array_keys( $status_labels ) );

		$this->get_template( $status_labels, $action_counts, $oldest_and_newest );
	}

	/**
	 * Get oldest and newest scheduled dates for a given set of statuses.
	 *
	 * @param array $status_keys Set of statuses to find oldest & newest action for.
	 * @return array
	 */
	protected function get_oldest_and_newest( $status_keys ) {

		$oldest_and_newest = array();

		foreach ( $status_keys as $status ) {
			$oldest_and_newest[ $status ] = array(
				'oldest' => '&ndash;',
				'newest' => '&ndash;',
			);

			if ( 'in-progress' === $status ) {
				continue;
			}

			$oldest_and_newest[ $status ]['oldest'] = $this->get_action_status_date( $status, 'oldest' );
			$oldest_and_newest[ $status ]['newest'] = $this->get_action_status_date( $status, 'newest' );
		}

		return $oldest_and_newest;
	}

	/**
	 * Get oldest or newest scheduled date for a given status.
	 *
	 * @param string $status Action status label/name string.
	 * @param string $date_type Oldest or Newest.
	 * @return DateTime
	 */
	protected function get_action_status_date( $status, $date_type = 'oldest' ) {

		$order = 'oldest' === $date_type ? 'ASC' : 'DESC';

		$action = $this->store->query_actions(
			array(
				'status'   => $status,
				'per_page' => 1,
				'order'    => $order,
			)
		);

		if ( ! empty( $action ) ) {
			$date_object = $this->store->get_date( $action[0] );
			$action_date = $date_object->format( 'Y-m-d H:i:s O' );
		} else {
			$action_date = '&ndash;';
		}

		return $action_date;
	}

	/**
	 * Get oldest or newest scheduled date for a given status.
	 *
	 * @param array $status_labels Set of statuses to find oldest & newest action for.
	 * @param array $action_counts Number of actions grouped by status.
	 * @param array $oldest_and_newest Date of the oldest and newest action with each status.
	 */
	protected function get_template( $status_labels, $action_counts, $oldest_and_newest ) {
		$as_version   = ActionScheduler_Versions::instance()->latest_version();
		$as_datastore = get_class( ActionScheduler_Store::instance() );
		?>

		<table class="wc_status_table widefat" cellspacing="0">
			<thead>
				<tr>
					<th colspan="5" data-export-label="Action Scheduler"><h2><?php esc_html_e( 'Action Scheduler', 'action-scheduler' ); ?><?php echo wc_help_tip( esc_html__( 'This section shows details of Action Scheduler.', 'action-scheduler' ) ); ?></h2></th>
				</tr>
				<tr>
					<td colspan="2" data-export-label="Version"><?php esc_html_e( 'Version:', 'action-scheduler' ); ?></td>
					<td colspan="3"><?php echo esc_html( $as_version ); ?></td>
				</tr>
				<tr>
					<td colspan="2" data-export-label="Data store"><?php esc_html_e( 'Data store:', 'action-scheduler' ); ?></td>
					<td colspan="3"><?php echo esc_html( $as_datastore ); ?></td>
				</tr>
				<tr>
					<td><strong><?php esc_html_e( 'Action Status', 'action-scheduler' ); ?></strong></td>
					<td class="help">&nbsp;</td>
					<td><strong><?php esc_html_e( 'Count', 'action-scheduler' ); ?></strong></td>
					<td><strong><?php esc_html_e( 'Oldest Scheduled Date', 'action-scheduler' ); ?></strong></td>
					<td><strong><?php esc_html_e( 'Newest Scheduled Date', 'action-scheduler' ); ?></strong></td>
				</tr>
			</thead>
			<tbody>
				<?php
				foreach ( $action_counts as $status => $count ) {
					// WC uses the 3rd column for export, so we need to display more data in that (hidden when viewed as part of the table) and add an empty 2nd column.
					printf(
						'<tr><td>%1$s</td><td>&nbsp;</td><td>%2$s<span style="display: none;">, Oldest: %3$s, Newest: %4$s</span></td><td>%3$s</td><td>%4$s</td></tr>',
						esc_html( $status_labels[ $status ] ),
						esc_html( number_format_i18n( $count ) ),
						esc_html( $oldest_and_newest[ $status ]['oldest'] ),
						esc_html( $oldest_and_newest[ $status ]['newest'] )
					);
				}
				?>
			</tbody>
		</table>

		<?php
	}

	/**
	 * Is triggered when invoking inaccessible methods in an object context.
	 *
	 * @param string $name Name of method called.
	 * @param array  $arguments Parameters to invoke the method with.
	 *
	 * @return mixed
	 * @link https://php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.methods
	 */
	public function __call( $name, $arguments ) {
		switch ( $name ) {
			case 'print':
				_deprecated_function( __CLASS__ . '::print()', '2.2.4', __CLASS__ . '::render()' );
				return call_user_func_array( array( $this, 'render' ), $arguments );
		}

		return null;
	}
}


================================================
FILE: classes/WP_CLI/Action/Cancel_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI\Action;

use function \WP_CLI\Utils\get_flag_value;

/**
 * WP-CLI command: action-scheduler action cancel
 */
class Cancel_Command extends \ActionScheduler_WPCLI_Command {

	/**
	 * Execute command.
	 *
	 * @return void
	 */
	public function execute() {
		$hook          = '';
		$group         = get_flag_value( $this->assoc_args, 'group', '' );
		$callback_args = get_flag_value( $this->assoc_args, 'args', null );
		$all           = get_flag_value( $this->assoc_args, 'all', false );

		if ( ! empty( $this->args[0] ) ) {
			$hook = $this->args[0];
		}

		if ( ! empty( $callback_args ) ) {
			$callback_args = json_decode( $callback_args, true );
		}

		if ( $all ) {
			$this->cancel_all( $hook, $callback_args, $group );
			return;
		}

		$this->cancel_single( $hook, $callback_args, $group );
	}

	/**
	 * Cancel single action.
	 *
	 * @param string $hook The hook that the job will trigger.
	 * @param array  $callback_args Args that would have been passed to the job.
	 * @param string $group The group the job is assigned to.
	 * @return void
	 */
	protected function cancel_single( $hook, $callback_args, $group ) {
		if ( empty( $hook ) ) {
			\WP_CLI::error( __( 'Please specify hook of action to cancel.', 'action-scheduler' ) );
		}

		try {
			$result = as_unschedule_action( $hook, $callback_args, $group );
		} catch ( \Exception $e ) {
			$this->print_error( $e, false );
		}

		if ( null === $result ) {
			$e = new \Exception( __( 'Unable to cancel scheduled action: check the logs.', 'action-scheduler' ) );
			$this->print_error( $e, false );
		}

		$this->print_success( false );
	}

	/**
	 * Cancel all actions.
	 *
	 * @param string $hook The hook that the job will trigger.
	 * @param array  $callback_args Args that would have been passed to the job.
	 * @param string $group The group the job is assigned to.
	 * @return void
	 */
	protected function cancel_all( $hook, $callback_args, $group ) {
		if ( empty( $hook ) && empty( $group ) ) {
			\WP_CLI::error( __( 'Please specify hook and/or group of actions to cancel.', 'action-scheduler' ) );
		}

		try {
			$result = as_unschedule_all_actions( $hook, $callback_args, $group );
		} catch ( \Exception $e ) {
			$this->print_error( $e, true );
		}

		/**
		 * Because as_unschedule_all_actions() does not provide a result,
		 * neither confirm or deny actions cancelled.
		 */
		\WP_CLI::success( __( 'Request to cancel scheduled actions completed.', 'action-scheduler' ) );
	}

	/**
	 * Print a success message.
	 *
	 * @return void
	 */
	protected function print_success() {
		\WP_CLI::success( __( 'Scheduled action cancelled.', 'action-scheduler' ) );
	}

	/**
	 * Convert an exception into a WP CLI error.
	 *
	 * @param \Exception $e The error object.
	 * @param bool       $multiple Boolean if multiple actions.
	 * @throws \WP_CLI\ExitException When an error occurs.
	 * @return void
	 */
	protected function print_error( \Exception $e, $multiple ) {
		\WP_CLI::error(
			sprintf(
				/* translators: %1$s: singular or plural %2$s: refers to the exception error message. */
				__( 'There was an error cancelling the %1$s: %2$s', 'action-scheduler' ),
				$multiple ? __( 'scheduled actions', 'action-scheduler' ) : __( 'scheduled action', 'action-scheduler' ),
				$e->getMessage()
			)
		);
	}

}


================================================
FILE: classes/WP_CLI/Action/Create_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI\Action;

use function \WP_CLI\Utils\get_flag_value;

/**
 * WP-CLI command: action-scheduler action create
 */
class Create_Command extends \ActionScheduler_WPCLI_Command {

	const ASYNC_OPTS = array( 'async', 0 );

	/**
	 * Execute command.
	 *
	 * @return void
	 */
	public function execute() {
		$hook           = $this->args[0];
		$schedule_start = $this->args[1];
		$callback_args  = get_flag_value( $this->assoc_args, 'args', array() );
		$group          = get_flag_value( $this->assoc_args, 'group', '' );
		$interval       = absint( get_flag_value( $this->assoc_args, 'interval', 0 ) );
		$cron           = get_flag_value( $this->assoc_args, 'cron', '' );
		$unique         = get_flag_value( $this->assoc_args, 'unique', false );
		$priority       = absint( get_flag_value( $this->assoc_args, 'priority', 10 ) );

		if ( ! empty( $callback_args ) ) {
			$callback_args = json_decode( $callback_args, true );
		}

		$function_args = array(
			'start'         => $schedule_start,
			'cron'          => $cron,
			'interval'      => $interval,
			'hook'          => $hook,
			'callback_args' => $callback_args,
			'group'         => $group,
			'unique'        => $unique,
			'priority'      => $priority,
		);

		try {
			// Generate schedule start if appropriate.
			if ( ! in_array( $schedule_start, static::ASYNC_OPTS, true ) ) {
				$schedule_start         = as_get_datetime_object( $schedule_start );
				$function_args['start'] = $schedule_start->format( 'U' );
			}
		} catch ( \Exception $e ) {
			\WP_CLI::error( $e->getMessage() );
		}

		// Default to creating single action.
		$action_type = 'single';
		$function    = 'as_schedule_single_action';

		if ( ! empty( $interval ) ) { // Creating recurring action.
			$action_type = 'recurring';
			$function    = 'as_schedule_recurring_action';

			$function_args = array_filter(
				$function_args,
				static function( $key ) {
					return in_array( $key, array( 'start', 'interval', 'hook', 'callback_args', 'group', 'unique', 'priority' ), true );
				},
				ARRAY_FILTER_USE_KEY
			);
		} elseif ( ! empty( $cron ) ) { // Creating cron action.
			$action_type = 'cron';
			$function    = 'as_schedule_cron_action';

			$function_args = array_filter(
				$function_args,
				static function( $key ) {
					return in_array( $key, array( 'start', 'cron', 'hook', 'callback_args', 'group', 'unique', 'priority' ), true );
				},
				ARRAY_FILTER_USE_KEY
			);
		} elseif ( in_array( $function_args['start'], static::ASYNC_OPTS, true ) ) { // Enqueue async action.
			$action_type = 'async';
			$function    = 'as_enqueue_async_action';

			$function_args = array_filter(
				$function_args,
				static function( $key ) {
					return in_array( $key, array( 'hook', 'callback_args', 'group', 'unique', 'priority' ), true );
				},
				ARRAY_FILTER_USE_KEY
			);
		} else { // Enqueue single action.
			$function_args = array_filter(
				$function_args,
				static function( $key ) {
					return in_array( $key, array( 'start', 'hook', 'callback_args', 'group', 'unique', 'priority' ), true );
				},
				ARRAY_FILTER_USE_KEY
			);
		}

		$function_args = array_values( $function_args );

		try {
			$action_id = call_user_func_array( $function, $function_args );
		} catch ( \Exception $e ) {
			$this->print_error( $e );
		}

		if ( 0 === $action_id ) {
			$e = new \Exception( __( 'Unable to create a scheduled action.', 'action-scheduler' ) );
			$this->print_error( $e );
		}

		$this->print_success( $action_id, $action_type );
	}

	/**
	 * Print a success message with the action ID.
	 *
	 * @param int    $action_id   Created action ID.
	 * @param string $action_type Type of action.
	 *
	 * @return void
	 */
	protected function print_success( $action_id, $action_type ) {
		\WP_CLI::success(
			sprintf(
				/* translators: %1$s: type of action, %2$d: ID of the created action */
				__( '%1$s action (%2$d) scheduled.', 'action-scheduler' ),
				ucfirst( $action_type ),
				$action_id
			)
		);
	}

	/**
	 * Convert an exception into a WP CLI error.
	 *
	 * @param \Exception $e The error object.
	 * @throws \WP_CLI\ExitException When an error occurs.
	 * @return void
	 */
	protected function print_error( \Exception $e ) {
		\WP_CLI::error(
			sprintf(
				/* translators: %s refers to the exception error message. */
				__( 'There was an error creating the scheduled action: %s', 'action-scheduler' ),
				$e->getMessage()
			)
		);
	}

}


================================================
FILE: classes/WP_CLI/Action/Delete_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI\Action;

/**
 * WP-CLI command: action-scheduler action delete
 */
class Delete_Command extends \ActionScheduler_WPCLI_Command {

	/**
	 * Array of action IDs to delete.
	 *
	 * @var int[]
	 */
	protected $action_ids = array();

	/**
	 * Number of deleted, failed, and total actions deleted.
	 *
	 * @var array<string, int>
	 */
	protected $action_counts = array(
		'deleted' => 0,
		'failed'  => 0,
		'total'   => 0,
	);

	/**
	 * Construct.
	 *
	 * @param string[]              $args       Positional arguments.
	 * @param array<string, string> $assoc_args Keyed arguments.
	 */
	public function __construct( array $args, array $assoc_args ) {
		parent::__construct( $args, $assoc_args );

		$this->action_ids             = array_map( 'absint', $args );
		$this->action_counts['total'] = count( $this->action_ids );

		add_action( 'action_scheduler_deleted_action', array( $this, 'on_action_deleted' ) );
	}

	/**
	 * Execute.
	 *
	 * @return void
	 */
	public function execute() {
		$store = \ActionScheduler::store();

		$progress_bar = \WP_CLI\Utils\make_progress_bar(
			sprintf(
				/* translators: %d: number of actions to be deleted */
				_n( 'Deleting %d action', 'Deleting %d actions', $this->action_counts['total'], 'action-scheduler' ),
				number_format_i18n( $this->action_counts['total'] )
			),
			$this->action_counts['total']
		);

		foreach ( $this->action_ids as $action_id ) {
			try {
				$store->delete_action( $action_id );
			} catch ( \Exception $e ) {
				$this->action_counts['failed']++;
				\WP_CLI::warning( $e->getMessage() );
			}

			$progress_bar->tick();
		}

		$progress_bar->finish();

		/* translators: %1$d: number of actions deleted */
		$format = _n( 'Deleted %1$d action', 'Deleted %1$d actions', $this->action_counts['deleted'], 'action-scheduler' ) . ', ';
		/* translators: %2$d: number of actions deletions failed */
		$format .= _n( '%2$d failure.', '%2$d failures.', $this->action_counts['failed'], 'action-scheduler' );

		\WP_CLI::success(
			sprintf(
				$format,
				number_format_i18n( $this->action_counts['deleted'] ),
				number_format_i18n( $this->action_counts['failed'] )
			)
		);
	}

	/**
	 * Action: action_scheduler_deleted_action
	 *
	 * @param int $action_id Action ID.
	 * @return void
	 */
	public function on_action_deleted( $action_id ) {
		if ( 'action_scheduler_deleted_action' !== current_action() ) {
			return;
		}

		$action_id = absint( $action_id );

		if ( ! in_array( $action_id, $this->action_ids, true ) ) {
			return;
		}

		$this->action_counts['deleted']++;
		\WP_CLI::debug( sprintf( 'Action %d was deleted.', $action_id ) );
	}

}


================================================
FILE: classes/WP_CLI/Action/Generate_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI\Action;

use function \WP_CLI\Utils\get_flag_value;

/**
 * WP-CLI command: action-scheduler action generate
 */
class Generate_Command extends \ActionScheduler_WPCLI_Command {

	/**
	 * Execute command.
	 *
	 * @return void
	 */
	public function execute() {
		$hook           = $this->args[0];
		$schedule_start = $this->args[1];
		$callback_args  = get_flag_value( $this->assoc_args, 'args', array() );
		$group          = get_flag_value( $this->assoc_args, 'group', '' );
		$interval       = (int) get_flag_value( $this->assoc_args, 'interval', 0 ); // avoid absint() to support negative intervals
		$count          = absint( get_flag_value( $this->assoc_args, 'count', 1 ) );

		if ( ! empty( $callback_args ) ) {
			$callback_args = json_decode( $callback_args, true );
		}

		$schedule_start = as_get_datetime_object( $schedule_start );

		$function_args = array(
			'start'         => absint( $schedule_start->format( 'U' ) ),
			'interval'      => $interval,
			'count'         => $count,
			'hook'          => $hook,
			'callback_args' => $callback_args,
			'group'         => $group,
		);

		$function_args = array_values( $function_args );

		try {
			$actions_added = $this->generate( ...$function_args );
		} catch ( \Exception $e ) {
			$this->print_error( $e );
		}

		$num_actions_added = count( (array) $actions_added );

		$this->print_success( $num_actions_added, 'single' );
	}

	/**
	 * Schedule multiple single actions.
	 *
	 * @param int    $schedule_start Starting timestamp of first action.
	 * @param int    $interval How long to wait between runs.
	 * @param int    $count Limit number of actions to schedule.
	 * @param string $hook The hook to trigger.
	 * @param array  $args Arguments to pass when the hook triggers.
	 * @param string $group The group to assign this job to.
	 * @return int[] IDs of actions added.
	 */
	protected function generate( $schedule_start, $interval, $count, $hook, array $args = array(), $group = '' ) {
		$actions_added = array();

		$progress_bar = \WP_CLI\Utils\make_progress_bar(
			sprintf(
				/* translators: %d is number of actions to create */
				_n( 'Creating %d action', 'Creating %d actions', $count, 'action-scheduler' ),
				number_format_i18n( $count )
			),
			$count
		);

		for ( $i = 0; $i < $count; $i++ ) {
			$actions_added[] = as_schedule_single_action( $schedule_start + ( $i * $interval ), $hook, $args, $group );
			$progress_bar->tick();
		}

		$progress_bar->finish();

		return $actions_added;
	}

	/**
	 * Print a success message with the action ID.
	 *
	 * @param int    $actions_added Number of actions generated.
	 * @param string $action_type   Type of actions scheduled.
	 * @return void
	 */
	protected function print_success( $actions_added, $action_type ) {
		\WP_CLI::success(
			sprintf(
				/* translators: %1$d refers to the total number of tasks added, %2$s is the action type */
				_n( '%1$d %2$s action scheduled.', '%1$d %2$s actions scheduled.', $actions_added, 'action-scheduler' ),
				number_format_i18n( $actions_added ),
				$action_type
			)
		);
	}

	/**
	 * Convert an exception into a WP CLI error.
	 *
	 * @param \Exception $e The error object.
	 * @throws \WP_CLI\ExitException When an error occurs.
	 * @return void
	 */
	protected function print_error( \Exception $e ) {
		\WP_CLI::error(
			sprintf(
				/* translators: %s refers to the exception error message. */
				__( 'There was an error creating the scheduled action: %s', 'action-scheduler' ),
				$e->getMessage()
			)
		);
	}

}


================================================
FILE: classes/WP_CLI/Action/Get_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI\Action;

/**
 * WP-CLI command: action-scheduler action get
 */
class Get_Command extends \ActionScheduler_WPCLI_Command {

	/**
	 * Execute command.
	 *
	 * @return void
	 */
	public function execute() {
		$action_id = $this->args[0];
		$store     = \ActionScheduler::store();
		$logger    = \ActionScheduler::logger();
		$action    = $store->fetch_action( $action_id );

		if ( is_a( $action, ActionScheduler_NullAction::class ) ) {
			/* translators: %d is action ID. */
			\WP_CLI::error( sprintf( esc_html__( 'Unable to retrieve action %d.', 'action-scheduler' ), $action_id ) );
		}

		$only_logs   = ! empty( $this->assoc_args['field'] ) && 'log_entries' === $this->assoc_args['field'];
		$only_logs   = $only_logs || ( ! empty( $this->assoc_args['fields'] ) && 'log_entries' === $this->assoc_args['fields'] );
		$log_entries = array();

		foreach ( $logger->get_logs( $action_id ) as $log_entry ) {
			$log_entries[] = array(
				'date'    => $log_entry->get_date()->format( static::DATE_FORMAT ),
				'message' => $log_entry->get_message(),
			);
		}

		if ( $only_logs ) {
			$args = array(
				'format' => \WP_CLI\Utils\get_flag_value( $this->assoc_args, 'format', 'table' ),
			);

			$formatter = new \WP_CLI\Formatter( $args, array( 'date', 'message' ) );
			$formatter->display_items( $log_entries );

			return;
		}

		try {
			$status = $store->get_status( $action_id );
		} catch ( \Exception $e ) {
			\WP_CLI::error( $e->getMessage() );
		}

		$action_arr = array(
			'id'             => $this->args[0],
			'hook'           => $action->get_hook(),
			'status'         => $status,
			'args'           => $action->get_args(),
			'group'          => $action->get_group(),
			'recurring'      => $action->get_schedule()->is_recurring() ? 'yes' : 'no',
			'scheduled_date' => $this->get_schedule_display_string( $action->get_schedule() ),
			'log_entries'    => $log_entries,
		);

		$fields = array_keys( $action_arr );

		if ( ! empty( $this->assoc_args['fields'] ) ) {
			$fields = explode( ',', $this->assoc_args['fields'] );
		}

		$formatter = new \WP_CLI\Formatter( $this->assoc_args, $fields );
		$formatter->display_item( $action_arr );
	}

}


================================================
FILE: classes/WP_CLI/Action/List_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI\Action;

// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping output is not necessary in WP CLI.

/**
 * WP-CLI command: action-scheduler action list
 */
class List_Command extends \ActionScheduler_WPCLI_Command {

	const PARAMETERS = array(
		'hook',
		'args',
		'date',
		'date_compare',
		'modified',
		'modified_compare',
		'group',
		'status',
		'claimed',
		'per_page',
		'offset',
		'orderby',
		'order',
	);

	/**
	 * Execute command.
	 *
	 * @return void
	 */
	public function execute() {
		$store  = \ActionScheduler::store();
		$logger = \ActionScheduler::logger();

		$fields = array(
			'id',
			'hook',
			'status',
			'group',
			'recurring',
			'scheduled_date',
		);

		$this->process_csv_arguments_to_arrays();

		if ( ! empty( $this->assoc_args['fields'] ) ) {
			$fields = $this->assoc_args['fields'];
		}

		$formatter  = new \WP_CLI\Formatter( $this->assoc_args, $fields );
		$query_args = $this->assoc_args;

		/**
		 * The `claimed` parameter expects a boolean or integer:
		 * check for string 'false', and set explicitly to `false` boolean.
		 */
		if ( array_key_exists( 'claimed', $query_args ) && 'false' === strtolower( $query_args['claimed'] ) ) {
			$query_args['claimed'] = false;
		}

		$return_format = 'OBJECT';

		if ( in_array( $formatter->format, array( 'ids', 'count' ), true ) ) {
			$return_format = '\'ids\'';
		}

		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
		$params = var_export( $query_args, true );

		if ( empty( $query_args ) ) {
			$params = 'array()';
		}

		\WP_CLI::debug(
			sprintf(
				'as_get_scheduled_actions( %s, %s )',
				$params,
				$return_format
			)
		);

		if ( ! empty( $query_args['args'] ) ) {
			$query_args['args'] = json_decode( $query_args['args'], true );
		}

		switch ( $formatter->format ) {

			case 'ids':
				$actions = as_get_scheduled_actions( $query_args, 'ids' );
				echo implode( ' ', $actions );
				break;

			case 'count':
				$actions = as_get_scheduled_actions( $query_args, 'ids' );
				$formatter->display_items( $actions );
				break;

			default:
				$actions = as_get_scheduled_actions( $query_args, OBJECT );

				$actions_arr = array();

				foreach ( $actions as $action_id => $action ) {
					$action_arr = array(
						'id'             => $action_id,
						'hook'           => $action->get_hook(),
						'status'         => $store->get_status( $action_id ),
						'args'           => $action->get_args(),
						'group'          => $action->get_group(),
						'recurring'      => $action->get_schedule()->is_recurring() ? 'yes' : 'no',
						'scheduled_date' => $this->get_schedule_display_string( $action->get_schedule() ),
						'log_entries'    => array(),
					);

					foreach ( $logger->get_logs( $action_id ) as $log_entry ) {
						$action_arr['log_entries'][] = array(
							'date'    => $log_entry->get_date()->format( static::DATE_FORMAT ),
							'message' => $log_entry->get_message(),
						);
					}

					$actions_arr[] = $action_arr;
				}

				$formatter->display_items( $actions_arr );
				break;

		}
	}

}


================================================
FILE: classes/WP_CLI/Action/Next_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI\Action;

// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping output is not necessary in WP CLI.

use function \WP_CLI\Utils\get_flag_value;

/**
 * WP-CLI command: action-scheduler action next
 */
class Next_Command extends \ActionScheduler_WPCLI_Command {

	/**
	 * Execute command.
	 *
	 * @return void
	 */
	public function execute() {
		$hook          = $this->args[0];
		$group         = get_flag_value( $this->assoc_args, 'group', '' );
		$callback_args = get_flag_value( $this->assoc_args, 'args', null );
		$raw           = (bool) get_flag_value( $this->assoc_args, 'raw', false );

		if ( ! empty( $callback_args ) ) {
			$callback_args = json_decode( $callback_args, true );
		}

		if ( $raw ) {
			\WP_CLI::line( as_next_scheduled_action( $hook, $callback_args, $group ) );
			return;
		}

		$params = array(
			'hook'    => $hook,
			'orderby' => 'date',
			'order'   => 'ASC',
			'group'   => $group,
		);

		if ( is_array( $callback_args ) ) {
			$params['args'] = $callback_args;
		}

		$params['status'] = \ActionScheduler_Store::STATUS_RUNNING;
		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
		\WP_CLI::debug( 'ActionScheduler()::store()->query_action( ' . var_export( $params, true ) . ' )' );

		$store     = \ActionScheduler::store();
		$action_id = $store->query_action( $params );

		if ( $action_id ) {
			echo $action_id;
			return;
		}

		$params['status'] = \ActionScheduler_Store::STATUS_PENDING;
		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
		\WP_CLI::debug( 'ActionScheduler()::store()->query_action( ' . var_export( $params, true ) . ' )' );

		$action_id = $store->query_action( $params );

		if ( $action_id ) {
			echo $action_id;
			return;
		}

		\WP_CLI::warning( 'No matching next action.' );
	}

}


================================================
FILE: classes/WP_CLI/Action/Run_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI\Action;

/**
 * WP-CLI command: action-scheduler action run
 */
class Run_Command extends \ActionScheduler_WPCLI_Command {

	/**
	 * Array of action IDs to execute.
	 *
	 * @var int[]
	 */
	protected $action_ids = array();

	/**
	 * Number of executed, failed, ignored, invalid, and total actions.
	 *
	 * @var array<string, int>
	 */
	protected $action_counts = array(
		'executed' => 0,
		'failed'   => 0,
		'ignored'  => 0,
		'invalid'  => 0,
		'total'    => 0,
	);

	/**
	 * Construct.
	 *
	 * @param string[]              $args       Positional arguments.
	 * @param array<string, string> $assoc_args Keyed arguments.
	 */
	public function __construct( array $args, array $assoc_args ) {
		parent::__construct( $args, $assoc_args );

		$this->action_ids             = array_map( 'absint', $args );
		$this->action_counts['total'] = count( $this->action_ids );

		add_action( 'action_scheduler_execution_ignored', array( $this, 'on_action_ignored' ) );
		add_action( 'action_scheduler_after_execute', array( $this, 'on_action_executed' ) );
		add_action( 'action_scheduler_failed_execution', array( $this, 'on_action_failed' ), 10, 2 );
		add_action( 'action_scheduler_failed_validation', array( $this, 'on_action_invalid' ), 10, 2 );
	}

	/**
	 * Execute.
	 *
	 * @return void
	 */
	public function execute() {
		$runner = \ActionScheduler::runner();

		$progress_bar = \WP_CLI\Utils\make_progress_bar(
			sprintf(
				/* translators: %d: number of actions */
				_n( 'Executing %d action', 'Executing %d actions', $this->action_counts['total'], 'action-scheduler' ),
				number_format_i18n( $this->action_counts['total'] )
			),
			$this->action_counts['total']
		);

		foreach ( $this->action_ids as $action_id ) {
			$runner->process_action( $action_id, 'Action Scheduler CLI' );
			$progress_bar->tick();
		}

		$progress_bar->finish();

		foreach ( array(
			'ignored',
			'invalid',
			'failed',
		) as $type ) {
			$count = $this->action_counts[ $type ];

			if ( empty( $count ) ) {
				continue;
			}

			/*
			 * translators:
			 * %1$d: count of actions evaluated.
			 * %2$s: type of action evaluated.
			 */
			$format = _n( '%1$d action %2$s.', '%1$d actions %2$s.', $count, 'action-scheduler' );

			\WP_CLI::warning(
				sprintf(
					$format,
					number_format_i18n( $count ),
					$type
				)
			);
		}

		\WP_CLI::success(
			sprintf(
				/* translators: %d: number of executed actions */
				_n( 'Executed %d action.', 'Executed %d actions.', $this->action_counts['executed'], 'action-scheduler' ),
				number_format_i18n( $this->action_counts['executed'] )
			)
		);
	}

	/**
	 * Action: action_scheduler_execution_ignored
	 *
	 * @param int $action_id Action ID.
	 * @return void
	 */
	public function on_action_ignored( $action_id ) {
		if ( 'action_scheduler_execution_ignored' !== current_action() ) {
			return;
		}

		$action_id = absint( $action_id );

		if ( ! in_array( $action_id, $this->action_ids, true ) ) {
			return;
		}

		$this->action_counts['ignored']++;
		\WP_CLI::debug( sprintf( 'Action %d was ignored.', $action_id ) );
	}

	/**
	 * Action: action_scheduler_after_execute
	 *
	 * @param int $action_id Action ID.
	 * @return void
	 */
	public function on_action_executed( $action_id ) {
		if ( 'action_scheduler_after_execute' !== current_action() ) {
			return;
		}

		$action_id = absint( $action_id );

		if ( ! in_array( $action_id, $this->action_ids, true ) ) {
			return;
		}

		$this->action_counts['executed']++;
		\WP_CLI::debug( sprintf( 'Action %d was executed.', $action_id ) );
	}

	/**
	 * Action: action_scheduler_failed_execution
	 *
	 * @param int        $action_id Action ID.
	 * @param \Exception $e         Exception.
	 * @return void
	 */
	public function on_action_failed( $action_id, \Exception $e ) {
		if ( 'action_scheduler_failed_execution' !== current_action() ) {
			return;
		}

		$action_id = absint( $action_id );

		if ( ! in_array( $action_id, $this->action_ids, true ) ) {
			return;
		}

		$this->action_counts['failed']++;
		\WP_CLI::debug( sprintf( 'Action %d failed execution: %s', $action_id, $e->getMessage() ) );
	}

	/**
	 * Action: action_scheduler_failed_validation
	 *
	 * @param int        $action_id Action ID.
	 * @param \Exception $e         Exception.
	 * @return void
	 */
	public function on_action_invalid( $action_id, \Exception $e ) {
		if ( 'action_scheduler_failed_validation' !== current_action() ) {
			return;
		}

		$action_id = absint( $action_id );

		if ( ! in_array( $action_id, $this->action_ids, true ) ) {
			return;
		}

		$this->action_counts['invalid']++;
		\WP_CLI::debug( sprintf( 'Action %d failed validation: %s', $action_id, $e->getMessage() ) );
	}

}


================================================
FILE: classes/WP_CLI/ActionScheduler_WPCLI_Clean_Command.php
================================================
<?php

/**
 * Commands for Action Scheduler.
 */
class ActionScheduler_WPCLI_Clean_Command extends WP_CLI_Command {
	/**
	 * Run the Action Scheduler Queue Cleaner
	 *
	 * ## OPTIONS
	 *
	 * [--batch-size=<size>]
	 * : The maximum number of actions to delete per batch. Defaults to 20.
	 *
	 * [--batches=<size>]
	 * : Limit execution to a number of batches. Defaults to 0, meaning batches will continue all eligible actions are deleted.
	 *
	 * [--status=<status>]
	 * : Only clean actions with the specified status. Defaults to Canceled, Completed. Define multiple statuses as a comma separated string (without spaces), e.g. `--status=complete,failed,canceled`
	 *
	 * [--before=<datestring>]
	 * : Only delete actions with scheduled date older than this. Defaults to 31 days. e.g `--before='7 days ago'`, `--before='02-Feb-2020 20:20:20'`
	 *
	 * [--pause=<seconds>]
	 * : The number of seconds to pause between batches. Default no pause.
	 *
	 * @param array $args Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @throws \WP_CLI\ExitException When an error occurs.
	 *
	 * @subcommand clean
	 */
	public function clean( $args, $assoc_args ) {
		// Handle passed arguments.
		$batch   = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 20 ) );
		$batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) );
		$status  = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'status', '' ) );
		$status  = array_filter( array_map( 'trim', $status ) );
		$before  = \WP_CLI\Utils\get_flag_value( $assoc_args, 'before', '' );
		$sleep   = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 );

		$batches_completed = 0;
		$actions_deleted   = 0;
		$unlimited         = 0 === $batches;
		try {
			$lifespan = as_get_datetime_object( $before );
		} catch ( Exception $e ) {
			$lifespan = null;
		}

		try {
			// Custom queue cleaner instance.
			$cleaner = new ActionScheduler_QueueCleaner( null, $batch );

			// Clean actions for as long as possible.
			while ( $unlimited || $batches_completed < $batches ) {
				if ( $sleep && $batches_completed > 0 ) {
					sleep( $sleep );
				}

				$deleted = count( $cleaner->clean_actions( $status, $lifespan, null, 'CLI' ) );
				if ( $deleted <= 0 ) {
					break;
				}
				$actions_deleted += $deleted;
				$batches_completed++;
				$this->print_success( $deleted );
			}
		} catch ( Exception $e ) {
			$this->print_error( $e );
		}

		$this->print_total_batches( $batches_completed );
		if ( $batches_completed > 1 ) {
			$this->print_success( $actions_deleted );
		}
	}

	/**
	 * Print WP CLI message about how many batches of actions were processed.
	 *
	 * @param int $batches_processed Number of batches processed.
	 */
	protected function print_total_batches( int $batches_processed ) {
		WP_CLI::log(
			sprintf(
				/* translators: %d refers to the total number of batches processed */
				_n( '%d batch processed.', '%d batches processed.', $batches_processed, 'action-scheduler' ),
				$batches_processed
			)
		);
	}

	/**
	 * Convert an exception into a WP CLI error.
	 *
	 * @param Exception $e The error object.
	 */
	protected function print_error( Exception $e ) {
		WP_CLI::error(
			sprintf(
				/* translators: %s refers to the exception error message */
				__( 'There was an error deleting an action: %s', 'action-scheduler' ),
				$e->getMessage()
			)
		);
	}

	/**
	 * Print a success message with the number of completed actions.
	 *
	 * @param int $actions_deleted Number of deleted actions.
	 */
	protected function print_success( int $actions_deleted ) {
		WP_CLI::success(
			sprintf(
				/* translators: %d refers to the total number of actions deleted */
				_n( '%d action deleted.', '%d actions deleted.', $actions_deleted, 'action-scheduler' ),
				$actions_deleted
			)
		);
	}
}


================================================
FILE: classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php
================================================
<?php

use Action_Scheduler\WP_CLI\ProgressBar;

/**
 * WP CLI Queue runner.
 *
 * This class can only be called from within a WP CLI instance.
 */
class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract_QueueRunner {

	/**
	 * Claimed actions.
	 *
	 * @var array
	 */
	protected $actions;

	/**
	 * ActionScheduler_ActionClaim instance.
	 *
	 * @var ActionScheduler_ActionClaim
	 */
	protected $claim;

	/**
	 * Progress bar instance.
	 *
	 * @var \cli\progress\Bar
	 */
	protected $progress_bar;

	/**
	 * ActionScheduler_WPCLI_QueueRunner constructor.
	 *
	 * @param ActionScheduler_Store|null             $store Store object.
	 * @param ActionScheduler_FatalErrorMonitor|null $monitor Monitor object.
	 * @param ActionScheduler_QueueCleaner|null      $cleaner Cleaner object.
	 *
	 * @throws Exception When this is not run within WP CLI.
	 */
	public function __construct( ?ActionScheduler_Store $store = null, ?ActionScheduler_FatalErrorMonitor $monitor = null, ?ActionScheduler_QueueCleaner $cleaner = null ) {
		if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
			/* translators: %s php class name */
			throw new Exception( sprintf( __( 'The %s class can only be run within WP CLI.', 'action-scheduler' ), __CLASS__ ) );
		}

		parent::__construct( $store, $monitor, $cleaner );
	}

	/**
	 * Set up the Queue before processing.
	 *
	 * @param int    $batch_size The batch size to process.
	 * @param array  $hooks      The hooks being used to filter the actions claimed in this batch.
	 * @param string $group      The group of actions to claim with this batch.
	 * @param bool   $force      Whether to force running even with too many concurrent processes.
	 *
	 * @return int The number of actions that will be run.
	 * @throws \WP_CLI\ExitException When there are too many concurrent batches.
	 */
	public function setup( $batch_size, $hooks = array(), $group = '', $force = false ) {
		$this->run_cleanup();
		$this->add_hooks();

		// Check to make sure there aren't too many concurrent processes running.
		if ( $this->has_maximum_concurrent_batches() ) {
			if ( $force ) {
				WP_CLI::warning( __( 'There are too many concurrent batches, but the run is forced to continue.', 'action-scheduler' ) );
			} else {
				WP_CLI::error( __( 'There are too many concurrent batches.', 'action-scheduler' ) );
			}
		}

		// Stake a claim and store it.
		$this->claim = $this->store->stake_claim( $batch_size, null, $hooks, $group );
		$this->monitor->attach( $this->claim );
		$this->actions = $this->claim->get_actions();

		return count( $this->actions );
	}

	/**
	 * Add our hooks to the appropriate actions.
	 */
	protected function add_hooks() {
		add_action( 'action_scheduler_before_execute', array( $this, 'before_execute' ) );
		add_action( 'action_scheduler_after_execute', array( $this, 'after_execute' ), 10, 2 );
		add_action( 'action_scheduler_failed_execution', array( $this, 'action_failed' ), 10, 2 );
	}

	/**
	 * Set up the WP CLI progress bar.
	 */
	protected function setup_progress_bar() {
		$count              = count( $this->actions );
		$this->progress_bar = new ProgressBar(
			/* translators: %d: amount of actions */
			sprintf( _n( 'Running %d action', 'Running %d actions', $count, 'action-scheduler' ), $count ),
			$count
		);
	}

	/**
	 * Process actions in the queue.
	 *
	 * @param string $context Optional runner context. Default 'WP CLI'.
	 *
	 * @return int The number of actions processed.
	 */
	public function run( $context = 'WP CLI' ) {
		do_action( 'action_scheduler_before_process_queue' );
		$this->setup_progress_bar();
		foreach ( $this->actions as $action_id ) {
			// Error if we lost the claim.
			if ( ! in_array( $action_id, $this->store->find_actions_by_claim_id( $this->claim->get_id() ), true ) ) {
				WP_CLI::warning( __( 'The claim has been lost. Aborting current batch.', 'action-scheduler' ) );
				break;
			}

			$this->process_action( $action_id, $context );
			$this->progress_bar->tick();
		}

		$completed = $this->progress_bar->current();
		$this->progress_bar->finish();
		$this->store->release_claim( $this->claim );
		do_action( 'action_scheduler_after_process_queue' );

		return $completed;
	}

	/**
	 * Handle WP CLI message when the action is starting.
	 *
	 * @param int $action_id Action ID.
	 */
	public function before_execute( $action_id ) {
		/* translators: %s refers to the action ID */
		WP_CLI::log( sprintf( __( 'Started processing action %s', 'action-scheduler' ), $action_id ) );
	}

	/**
	 * Handle WP CLI message when the action has completed.
	 *
	 * @param int                         $action_id ActionID.
	 * @param null|ActionScheduler_Action $action The instance of the action. Default to null for backward compatibility.
	 */
	public function after_execute( $action_id, $action = null ) {
		// backward compatibility.
		if ( null === $action ) {
			$action = $this->store->fetch_action( $action_id );
		}
		/* translators: 1: action ID 2: hook name */
		WP_CLI::log( sprintf( __( 'Completed processing action %1$s with hook: %2$s', 'action-scheduler' ), $action_id, $action->get_hook() ) );
	}

	/**
	 * Handle WP CLI message when the action has failed.
	 *
	 * @param int       $action_id Action ID.
	 * @param Exception $exception Exception.
	 * @throws \WP_CLI\ExitException With failure message.
	 */
	public function action_failed( $action_id, $exception ) {
		WP_CLI::error(
			/* translators: 1: action ID 2: exception message */
			sprintf( __( 'Error processing action %1$s: %2$s', 'action-scheduler' ), $action_id, $exception->getMessage() ),
			false
		);
	}

	/**
	 * Sleep and help avoid hitting memory limit
	 *
	 * @param int $sleep_time Amount of seconds to sleep.
	 * @deprecated 3.0.0
	 */
	protected function stop_the_insanity( $sleep_time = 0 ) {
		_deprecated_function( 'ActionScheduler_WPCLI_QueueRunner::stop_the_insanity', '3.0.0', 'ActionScheduler_DataController::free_memory' );

		ActionScheduler_DataController::free_memory();
	}

	/**
	 * Maybe trigger the stop_the_insanity() method to free up memory.
	 */
	protected function maybe_stop_the_insanity() {
		// The value returned by progress_bar->current() might be padded. Remove padding, and convert to int.
		$current_iteration = intval( trim( $this->progress_bar->current() ) );
		if ( 0 === $current_iteration % 50 ) {
			$this->stop_the_insanity();
		}
	}
}


================================================
FILE: classes/WP_CLI/ActionScheduler_WPCLI_Scheduler_command.php
================================================
<?php

/**
 * Commands for Action Scheduler.
 */
class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {

	/**
	 * Force tables schema creation for Action Scheduler
	 *
	 * ## OPTIONS
	 *
	 * @param array $args Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 *
	 * @subcommand fix-schema
	 */
	public function fix_schema( $args, $assoc_args ) {
		$schema_classes = array( ActionScheduler_LoggerSchema::class, ActionScheduler_StoreSchema::class );

		foreach ( $schema_classes as $classname ) {
			if ( is_subclass_of( $classname, ActionScheduler_Abstract_Schema::class ) ) {
				$obj = new $classname();
				$obj->init();
				$obj->register_tables( true );

				WP_CLI::success(
					sprintf(
						/* translators: %s refers to the schema name*/
						__( 'Registered schema for %s', 'action-scheduler' ),
						$classname
					)
				);
			}
		}
	}

	/**
	 * Run the Action Scheduler
	 *
	 * ## OPTIONS
	 *
	 * [--batch-size=<size>]
	 * : The maximum number of actions to run. Defaults to 100.
	 *
	 * [--batches=<size>]
	 * : Limit execution to a number of batches. Defaults to 0, meaning batches will continue being executed until all actions are complete.
	 *
	 * [--cleanup-batch-size=<size>]
	 * : The maximum number of actions to clean up. Defaults to the value of --batch-size.
	 *
	 * [--hooks=<hooks>]
	 * : Only run actions with the specified hook. Omitting this option runs actions with any hook. Define multiple hooks as a comma separated string (without spaces), e.g. `--hooks=hook_one,hook_two,hook_three`
	 *
	 * [--group=<group>]
	 * : Only run actions from the specified group. Omitting this option runs actions from all groups.
	 *
	 * [--exclude-groups=<groups>]
	 * : Run actions from all groups except the specified group(s). Define multiple groups as a comma separated string (without spaces), e.g. '--group_a,group_b'. This option is ignored when `--group` is used.
	 *
	 * [--free-memory-on=<count>]
	 * : The number of actions to process between freeing memory. 0 disables freeing memory. Default 50.
	 *
	 * [--pause=<seconds>]
	 * : The number of seconds to pause when freeing memory. Default no pause.
	 *
	 * [--force]
	 * : Whether to force execution despite the maximum number of concurrent processes being exceeded.
	 *
	 * @param array $args Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @throws \WP_CLI\ExitException When an error occurs.
	 *
	 * @subcommand run
	 */
	public function run( $args, $assoc_args ) {
		// Handle passed arguments.
		$batch          = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 100 ) );
		$batches        = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) );
		$clean          = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'cleanup-batch-size', $batch ) );
		$hooks          = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'hooks', '' ) );
		$hooks          = array_filter( array_map( 'trim', $hooks ) );
		$group          = \WP_CLI\Utils\get_flag_value( $assoc_args, 'group', '' );
		$exclude_groups = \WP_CLI\Utils\get_flag_value( $assoc_args, 'exclude-groups', '' );
		$free_on        = \WP_CLI\Utils\get_flag_value( $assoc_args, 'free-memory-on', 50 );
		$sleep          = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 );
		$force          = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false );

		ActionScheduler_DataController::set_free_ticks( $free_on );
		ActionScheduler_DataController::set_sleep_time( $sleep );

		$batches_completed = 0;
		$actions_completed = 0;
		$unlimited         = 0 === $batches;
		if ( is_callable( array( ActionScheduler::store(), 'set_claim_filter' ) ) ) {
			$exclude_groups = $this->parse_comma_separated_string( $exclude_groups );

			if ( ! empty( $exclude_groups ) ) {
				ActionScheduler::store()->set_claim_filter( 'exclude-groups', $exclude_groups );
			}
		}

		try {
			// Custom queue cleaner instance.
			$cleaner = new ActionScheduler_QueueCleaner( null, $clean );

			// Get the queue runner instance.
			$runner = new ActionScheduler_WPCLI_QueueRunner( null, null, $cleaner );

			// Determine how many tasks will be run in the first batch.
			$total = $runner->setup( $batch, $hooks, $group, $force );

			// Run actions for as long as possible.
			while ( $total > 0 ) {
				$this->print_total_actions( $total );
				$actions_completed += $runner->run();
				$batches_completed++;

				// Maybe set up tasks for the next batch.
				$total = ( $unlimited || $batches_completed < $batches ) ? $runner->setup( $batch, $hooks, $group, $force ) : 0;
			}
		} catch ( Exception $e ) {
			$this->print_error( $e );
		}

		$this->print_total_batches( $batches_completed );
		$this->print_success( $actions_completed );
	}

	/**
	 * Converts a string of comma-separated values into an array of those same values.
	 *
	 * @param string $string The string of one or more comma separated values.
	 *
	 * @return array
	 */
	private function parse_comma_separated_string( $string ): array {
		return array_filter( str_getcsv( $string ) );
	}

	/**
	 * Print WP CLI message about how many actions are about to be processed.
	 *
	 * @param int $total Number of actions found.
	 */
	protected function print_total_actions( $total ) {
		WP_CLI::log(
			sprintf(
				/* translators: %d refers to how many scheduled tasks were found to run */
				_n( 'Found %d scheduled task', 'Found %d scheduled tasks', $total, 'action-scheduler' ),
				$total
			)
		);
	}

	/**
	 * Print WP CLI message about how many batches of actions were processed.
	 *
	 * @param int $batches_completed Number of completed batches.
	 */
	protected function print_total_batches( $batches_completed ) {
		WP_CLI::log(
			sprintf(
				/* translators: %d refers to the total number of batches executed */
				_n( '%d batch executed.', '%d batches executed.', $batches_completed, 'action-scheduler' ),
				$batches_completed
			)
		);
	}

	/**
	 * Convert an exception into a WP CLI error.
	 *
	 * @param Exception $e The error object.
	 *
	 * @throws \WP_CLI\ExitException Under some conditions WP CLI may throw an exception.
	 */
	protected function print_error( Exception $e ) {
		WP_CLI::error(
			sprintf(
				/* translators: %s refers to the exception error message */
				__( 'There was an error running the action scheduler: %s', 'action-scheduler' ),
				$e->getMessage()
			)
		);
	}

	/**
	 * Print a success message with the number of completed actions.
	 *
	 * @param int $actions_completed Number of completed actions.
	 */
	protected function print_success( $actions_completed ) {
		WP_CLI::success(
			sprintf(
				/* translators: %d refers to the total number of tasks completed */
				_n( '%d scheduled task completed.', '%d scheduled tasks completed.', $actions_completed, 'action-scheduler' ),
				$actions_completed
			)
		);
	}
}


================================================
FILE: classes/WP_CLI/Action_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI;

use WP_CLI;

/**
 * Action command for Action Scheduler.
 */
class Action_Command extends \WP_CLI_Command {

	/**
	 * Cancel the next occurrence or all occurrences of a scheduled action.
	 *
	 * ## OPTIONS
	 *
	 * [<hook>]
	 * : Name of the action hook.
	 *
	 * [--group=<group>]
	 * : The group the job is assigned to.
	 *
	 * [--args=<args>]
	 * : JSON object of arguments assigned to the job.
	 * ---
	 * default: []
	 * ---
	 *
	 * [--all]
	 * : Cancel all occurrences of a scheduled action.
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @return void
	 */
	public function cancel( array $args, array $assoc_args ) {
		require_once 'Action/Cancel_Command.php';
		$command = new Action\Cancel_Command( $args, $assoc_args );
		$command->execute();
	}

	/**
	 * Creates a new scheduled action.
	 *
	 * ## OPTIONS
	 *
	 * <hook>
	 * : Name of the action hook.
	 *
	 * <start>
	 * : A unix timestamp representing the date you want the action to start. Also 'async' or 'now' to enqueue an async action.
	 *
	 * [--args=<args>]
	 * : JSON object of arguments to pass to callbacks when the hook triggers.
	 * ---
	 * default: []
	 * ---
	 *
	 * [--cron=<cron>]
	 * : A cron-like schedule string (https://crontab.guru/).
	 * ---
	 * default: ''
	 * ---
	 *
	 * [--group=<group>]
	 * : The group to assign this job to.
	 * ---
	 * default: ''
	 * ---
	 *
	 * [--interval=<interval>]
	 * : Number of seconds to wait between runs.
	 * ---
	 * default: 0
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     wp action-scheduler action create hook_async async
	 *     wp action-scheduler action create hook_single 1627147598
	 *     wp action-scheduler action create hook_recurring 1627148188 --interval=5
	 *     wp action-scheduler action create hook_cron 1627147655 --cron='5 4 * * *'
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @return void
	 */
	public function create( array $args, array $assoc_args ) {
		require_once 'Action/Create_Command.php';
		$command = new Action\Create_Command( $args, $assoc_args );
		$command->execute();
	}

	/**
	 * Delete existing scheduled action(s).
	 *
	 * ## OPTIONS
	 *
	 * <id>...
	 * : One or more IDs of actions to delete.
	 * ---
	 * default: 0
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     # Delete the action with id 100
	 *     $ wp action-scheduler action delete 100
	 *
	 *     # Delete the actions with ids 100 and 200
	 *     $ wp action-scheduler action delete 100 200
	 *
	 *     # Delete the first five pending actions in 'action-scheduler' group
	 *     $ wp action-scheduler action delete $( wp action-scheduler action list --status=pending --group=action-scheduler --format=ids )
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @return void
	 */
	public function delete( array $args, array $assoc_args ) {
		require_once 'Action/Delete_Command.php';
		$command = new Action\Delete_Command( $args, $assoc_args );
		$command->execute();
	}

	/**
	 * Generates some scheduled actions.
	 *
	 * ## OPTIONS
	 *
	 * <hook>
	 * : Name of the action hook.
	 *
	 * <start>
	 * : The Unix timestamp representing the date you want the action to start.
	 *
	 * [--count=<count>]
	 * : Number of actions to create.
	 * ---
	 * default: 1
	 * ---
	 *
	 * [--interval=<interval>]
	 * : Number of seconds to wait between runs.
	 * ---
	 * default: 0
	 * ---
	 *
	 * [--args=<args>]
	 * : JSON object of arguments to pass to callbacks when the hook triggers.
	 * ---
	 * default: []
	 * ---
	 *
	 * [--group=<group>]
	 * : The group to assign this job to.
	 * ---
	 * default: ''
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     wp action-scheduler action generate test_multiple 1627147598 --count=5 --interval=5
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @return void
	 */
	public function generate( array $args, array $assoc_args ) {
		require_once 'Action/Generate_Command.php';
		$command = new Action\Generate_Command( $args, $assoc_args );
		$command->execute();
	}

	/**
	 * Get details about a scheduled action.
	 *
	 * ## OPTIONS
	 *
	 * <id>
	 * : The ID of the action to get.
	 * ---
	 * default: 0
	 * ---
	 *
	 * [--field=<field>]
	 * : Instead of returning the whole action, returns the value of a single field.
	 *
	 * [--fields=<fields>]
	 * : Limit the output to specific fields (comma-separated). Defaults to all fields.
	 *
	 * [--format=<format>]
	 * : Render output in a particular format.
	 * ---
	 * default: table
	 * options:
	 *   - table
	 *   - csv
	 *   - json
	 *   - yaml
	 * ---
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @return void
	 */
	public function get( array $args, array $assoc_args ) {
		require_once 'Action/Get_Command.php';
		$command = new Action\Get_Command( $args, $assoc_args );
		$command->execute();
	}

	/**
	 * Get a list of scheduled actions.
	 *
	 * Display actions based on all arguments supported by
	 * [as_get_scheduled_actions()](https://actionscheduler.org/api/#function-reference--as_get_scheduled_actions).
	 *
	 * ## OPTIONS
	 *
	 * [--<field>=<value>]
	 * : One or more arguments to pass to as_get_scheduled_actions().
	 *
	 * [--field=<field>]
	 * : Prints the value of a single property for each action.
	 *
	 * [--fields=<fields>]
	 * : Limit the output to specific object properties.
	 *
	 * [--format=<format>]
	 * : Render output in a particular format.
	 * ---
	 * default: table
	 * options:
	 *   - table
	 *   - csv
	 *   - ids
	 *   - json
	 *   - count
	 *   - yaml
	 * ---
	 *
	 * ## AVAILABLE FIELDS
	 *
	 * These fields will be displayed by default for each action:
	 *
	 * * id
	 * * hook
	 * * status
	 * * group
	 * * recurring
	 * * scheduled_date
	 *
	 * These fields are optionally available:
	 *
	 * * args
	 * * log_entries
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @return void
	 *
	 * @subcommand list
	 */
	public function subcommand_list( array $args, array $assoc_args ) {
		require_once 'Action/List_Command.php';
		$command = new Action\List_Command( $args, $assoc_args );
		$command->execute();
	}

	/**
	 * Get logs for a scheduled action.
	 *
	 * ## OPTIONS
	 *
	 * <id>
	 * : The ID of the action to get.
	 * ---
	 * default: 0
	 * ---
	 *
	 * @param array $args Positional arguments.
	 * @return void
	 */
	public function logs( array $args ) {
		$command = sprintf( 'action-scheduler action get %d --field=log_entries', $args[0] );
		WP_CLI::runcommand( $command );
	}

	/**
	 * Get the ID or timestamp of the next scheduled action.
	 *
	 * ## OPTIONS
	 *
	 * <hook>
	 * : The hook of the next scheduled action.
	 *
	 * [--args=<args>]
	 * : JSON object of arguments to search for next scheduled action.
	 * ---
	 * default: []
	 * ---
	 *
	 * [--group=<group>]
	 * : The group to which the next scheduled action is assigned.
	 * ---
	 * default: ''
	 * ---
	 *
	 * [--raw]
	 * : Display the raw output of as_next_scheduled_action() (timestamp or boolean).
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @return void
	 */
	public function next( array $args, array $assoc_args ) {
		require_once 'Action/Next_Command.php';
		$command = new Action\Next_Command( $args, $assoc_args );
		$command->execute();
	}

	/**
	 * Run existing scheduled action(s).
	 *
	 * ## OPTIONS
	 *
	 * <id>...
	 * : One or more IDs of actions to run.
	 * ---
	 * default: 0
	 * ---
	 *
	 * ## EXAMPLES
	 *
	 *     # Run the action with id 100
	 *     $ wp action-scheduler action run 100
	 *
	 *     # Run the actions with ids 100 and 200
	 *     $ wp action-scheduler action run 100 200
	 *
	 *     # Run the first five pending actions in 'action-scheduler' group
	 *     $ wp action-scheduler action run $( wp action-scheduler action list --status=pending --group=action-scheduler --format=ids )
	 *
	 * @param array $args       Positional arguments.
	 * @param array $assoc_args Keyed arguments.
	 * @return void
	 */
	public function run( array $args, array $assoc_args ) {
		require_once 'Action/Run_Command.php';
		$command = new Action\Run_Command( $args, $assoc_args );
		$command->execute();
	}

}


================================================
FILE: classes/WP_CLI/Migration_Command.php
================================================
<?php


namespace Action_Scheduler\WP_CLI;

use Action_Scheduler\Migration\Config;
use Action_Scheduler\Migration\Runner;
use Action_Scheduler\Migration\Scheduler;
use Action_Scheduler\Migration\Controller;
use WP_CLI;
use WP_CLI_Command;

/**
 * Class Migration_Command
 *
 * @package Action_Scheduler\WP_CLI
 *
 * @since 3.0.0
 *
 * @codeCoverageIgnore
 */
class Migration_Command extends WP_CLI_Command {

	/**
	 * Number of actions migrated.
	 *
	 * @var int
	 */
	private $total_processed = 0;

	/**
	 * Register the command with WP-CLI
	 */
	public function register() {
		if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
			return;
		}

		WP_CLI::add_command(
			'action-scheduler migrate',
			array( $this, 'migrate' ),
			array(
				'shortdesc' => 'Migrates actions to the DB tables store',
				'synopsis'  => array(
					array(
						'type'        => 'assoc',
						'name'        => 'batch-size',
						'optional'    => true,
						'default'     => 100,
						'description' => 'The number of actions to process in each batch',
					),
					array(
						'type'        => 'assoc',
						'name'        => 'free-memory-on',
						'optional'    => true,
						'default'     => 50,
						'description' => 'The number of actions to process between freeing memory. 0 disables freeing memory',
					),
					array(
						'type'        => 'assoc',
						'name'        => 'pause',
						'optional'    => true,
						'default'     => 0,
						'description' => 'The number of seconds to pause when freeing memory',
					),
					array(
						'type'        => 'flag',
						'name'        => 'dry-run',
						'optional'    => true,
						'description' => 'Reports on the actions that would have been migrated, but does not change any data',
					),
				),
			)
		);
	}

	/**
	 * Process the data migration.
	 *
	 * @param array $positional_args Required for WP CLI. Not used in migration.
	 * @param array $assoc_args Optional arguments.
	 *
	 * @return void
	 */
	public function migrate( $positional_args, $assoc_args ) {
		$this->init_logging();

		$config = $this->get_migration_config( $assoc_args );
		$runner = new Runner( $config );
		$runner->init_destination();

		$batch_size = isset( $assoc_args['batch-size'] ) ? (int) $assoc_args['batch-size'] : 100;
		$free_on    = isset( $assoc_args['free-memory-on'] ) ? (int) $assoc_args['free-memory-on'] : 50;
		$sleep      = isset( $assoc_args['pause'] ) ? (int) $assoc_args['pause'] : 0;
		\ActionScheduler_DataController::set_free_ticks( $free_on );
		\ActionScheduler_DataController::set_sleep_time( $sleep );

		do {
			$actions_processed      = $runner->run( $batch_size );
			$this->total_processed += $actions_processed;
		} while ( $actions_processed > 0 );

		if ( ! $config->get_dry_run() ) {
			// let the scheduler know that there's nothing left to do.
			$scheduler = new Scheduler();
			$scheduler->mark_complete();
		}

		WP_CLI::success( sprintf( '%s complete. %d actions processed.', $config->get_dry_run() ? 'Dry run' : 'Migration', $this->total_processed ) );
	}

	/**
	 * Build the config object used to create the Runner
	 *
	 * @param array $args Optional arguments.
	 *
	 * @return ActionScheduler\Migration\Config
	 */
	private function get_migration_config( $args ) {
		$args = wp_parse_args(
			$args,
			array(
				'dry-run' => false,
			)
		);

		$config = Controller::instance()->get_migration_config_object();
		$config->set_dry_run( ! empty( $args['dry-run'] ) );

		return $config;
	}

	/**
	 * Hook command line logging into migration actions.
	 */
	private function init_logging() {
		add_action(
			'action_scheduler/migrate_action_dry_run',
			function ( $action_id ) {
				WP_CLI::debug( sprintf( 'Dry-run: migrated action %d', $action_id ) );
			}
		);

		add_action(
			'action_scheduler/no_action_to_migrate',
			function ( $action_id ) {
				WP_CLI::debug( sprintf( 'No action found to migrate for ID %d', $action_id ) );
			}
		);

		add_action(
			'action_scheduler/migrate_action_failed',
			function ( $action_id ) {
				WP_CLI::warning( sprintf( 'Failed migrating action with ID %d', $action_id ) );
			}
		);

		add_action(
			'action_scheduler/migrate_action_incomplete',
			function ( $source_id, $destination_id ) {
				WP_CLI::warning( sprintf( 'Unable to remove source action with ID %d after migrating to new ID %d', $source_id, $destination_id ) );
			},
			10,
			2
		);

		add_action(
			'action_scheduler/migrated_action',
			function ( $source_id, $destination_id ) {
				WP_CLI::debug( sprintf( 'Migrated source action with ID %d to new store with ID %d', $source_id, $destination_id ) );
			},
			10,
			2
		);

		add_action(
			'action_scheduler/migration_batch_starting',
			function ( $batch ) {
				WP_CLI::debug( 'Beginning migration of batch: ' . print_r( $batch, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
			}
		);

		add_action(
			'action_scheduler/migration_batch_complete',
			function ( $batch ) {
				WP_CLI::log( sprintf( 'Completed migration of %d actions', count( $batch ) ) );
			}
		);
	}
}


================================================
FILE: classes/WP_CLI/ProgressBar.php
================================================
<?php

namespace Action_Scheduler\WP_CLI;

/**
 * WP_CLI progress bar for Action Scheduler.
 */

/**
 * Class ProgressBar
 *
 * @package Action_Scheduler\WP_CLI
 *
 * @since 3.0.0
 *
 * @codeCoverageIgnore
 */
class ProgressBar {

	/**
	 * Current number of ticks.
	 *
	 * @var integer
	 */
	protected $total_ticks;

	/**
	 * Total number of ticks.
	 *
	 * @var integer
	 */
	protected $count;

	/**
	 * Progress bar update interval.
	 *
	 * @var integer
	 */
	protected $interval;

	/**
	 * Progress bar message.
	 *
	 * @var string
	 */
	protected $message;

	/**
	 * Instance.
	 *
	 * @var \cli\progress\Bar
	 */
	protected $progress_bar;

	/**
	 * ProgressBar constructor.
	 *
	 * @param string  $message    Text to display before the progress bar.
	 * @param integer $count      Total number of ticks to be performed.
	 * @param integer $interval   Optional. The interval in milliseconds between updates. Default 100.
	 *
	 * @throws \Exception When this is not run within WP CLI.
	 */
	public function __construct( $message, $count, $interval = 100 ) {
		if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
			/* translators: %s php class name */
			throw new \Exception( sprintf( __( 'The %s class can only be run within WP CLI.', 'action-scheduler' ), __CLASS__ ) );
		}

		$this->total_ticks = 0;
		$this->message     = $message;
		$this->count       = $count;
		$this->interval    = $interval;
	}

	/**
	 * Increment the progress bar ticks.
	 */
	public function tick() {
		if ( null === $this->progress_bar ) {
			$this->setup_progress_bar();
		}

		$this->progress_bar->tick();
		$this->total_ticks++;

		do_action( 'action_scheduler/progress_tick', $this->total_ticks ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
	}

	/**
	 * Get the progress bar tick count.
	 *
	 * @return int
	 */
	public function current() {
		return $this->progress_bar ? $this->progress_bar->current() : 0;
	}

	/**
	 * Finish the current progress bar.
	 */
	public function finish() {
		if ( null !== $this->progress_bar ) {
			$this->progress_bar->finish();
		}

		$this->progress_bar = null;
	}

	/**
	 * Set the message used when creating the progress bar.
	 *
	 * @param string $message The message to be used when the next progress bar is created.
	 */
	public function set_message( $message ) {
		$this->message = $message;
	}

	/**
	 * Set the count for a new progress bar.
	 *
	 * @param integer $count The total number of ticks expected to complete.
	 */
	public function set_count( $count ) {
		$this->count = $count;
		$this->finish();
	}

	/**
	 * Set up the progress bar.
	 */
	protected function setup_progress_bar() {
		$this->progress_bar = \WP_CLI\Utils\make_progress_bar(
			$this->message,
			$this->count,
			$this->interval
		);
	}
}


================================================
FILE: classes/WP_CLI/System_Command.php
================================================
<?php

namespace Action_Scheduler\WP_CLI;

// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping output is not necessary in WP CLI.

use ActionScheduler_SystemInformation;
use WP_CLI;
use function \WP_CLI\Utils\get_flag_value;

/**
 * System info WP-CLI commands for Action Scheduler.
 */
class System_Command {

	/**
	 * Data store for querying actions
	 *
	 * @var ActionScheduler_Store
	 */
	protected $store;

	/**
	 * Construct.
	 */
	public function __construct() {
		$this->store = \ActionScheduler::store();
	}

	/**
	 * Print in-use data store class.
	 *
	 * @param array $args       Positional args.
	 * @param array $assoc_args Keyed args.
	 * @return void
	 *
	 * @subcommand data-store
	 */
	public function datastore( array $args, array $assoc_args ) {
		echo $this->get_current_datastore();
	}

	/**
	 * Print in-use runner class.
	 *
	 * @param array $args       Positional args.
	 * @param array $assoc_args Keyed args.
	 * @return void
	 */
	public functi
Download .txt
gitextract_minlvy_t/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── release-drafter.yml
│   └── workflows/
│       └── pr-unit-tests.yml
├── .gitignore
├── Gruntfile.js
├── README.md
├── action-scheduler.php
├── changelog.txt
├── classes/
│   ├── ActionScheduler_ActionClaim.php
│   ├── ActionScheduler_ActionFactory.php
│   ├── ActionScheduler_AdminView.php
│   ├── ActionScheduler_AsyncRequest_QueueRunner.php
│   ├── ActionScheduler_Compatibility.php
│   ├── ActionScheduler_DataController.php
│   ├── ActionScheduler_DateTime.php
│   ├── ActionScheduler_Exception.php
│   ├── ActionScheduler_FatalErrorMonitor.php
│   ├── ActionScheduler_InvalidActionException.php
│   ├── ActionScheduler_ListTable.php
│   ├── ActionScheduler_LogEntry.php
│   ├── ActionScheduler_NullLogEntry.php
│   ├── ActionScheduler_OptionLock.php
│   ├── ActionScheduler_QueueCleaner.php
│   ├── ActionScheduler_QueueRunner.php
│   ├── ActionScheduler_RecurringActionScheduler.php
│   ├── ActionScheduler_SystemInformation.php
│   ├── ActionScheduler_Versions.php
│   ├── ActionScheduler_WPCommentCleaner.php
│   ├── ActionScheduler_wcSystemStatus.php
│   ├── WP_CLI/
│   │   ├── Action/
│   │   │   ├── Cancel_Command.php
│   │   │   ├── Create_Command.php
│   │   │   ├── Delete_Command.php
│   │   │   ├── Generate_Command.php
│   │   │   ├── Get_Command.php
│   │   │   ├── List_Command.php
│   │   │   ├── Next_Command.php
│   │   │   └── Run_Command.php
│   │   ├── ActionScheduler_WPCLI_Clean_Command.php
│   │   ├── ActionScheduler_WPCLI_QueueRunner.php
│   │   ├── ActionScheduler_WPCLI_Scheduler_command.php
│   │   ├── Action_Command.php
│   │   ├── Migration_Command.php
│   │   ├── ProgressBar.php
│   │   └── System_Command.php
│   ├── abstracts/
│   │   ├── ActionScheduler.php
│   │   ├── ActionScheduler_Abstract_ListTable.php
│   │   ├── ActionScheduler_Abstract_QueueRunner.php
│   │   ├── ActionScheduler_Abstract_RecurringSchedule.php
│   │   ├── ActionScheduler_Abstract_Schedule.php
│   │   ├── ActionScheduler_Abstract_Schema.php
│   │   ├── ActionScheduler_Lock.php
│   │   ├── ActionScheduler_Logger.php
│   │   ├── ActionScheduler_Store.php
│   │   ├── ActionScheduler_TimezoneHelper.php
│   │   └── ActionScheduler_WPCLI_Command.php
│   ├── actions/
│   │   ├── ActionScheduler_Action.php
│   │   ├── ActionScheduler_CanceledAction.php
│   │   ├── ActionScheduler_FinishedAction.php
│   │   └── ActionScheduler_NullAction.php
│   ├── data-stores/
│   │   ├── ActionScheduler_DBLogger.php
│   │   ├── ActionScheduler_DBStore.php
│   │   ├── ActionScheduler_HybridStore.php
│   │   ├── ActionScheduler_wpCommentLogger.php
│   │   ├── ActionScheduler_wpPostStore.php
│   │   ├── ActionScheduler_wpPostStore_PostStatusRegistrar.php
│   │   ├── ActionScheduler_wpPostStore_PostTypeRegistrar.php
│   │   └── ActionScheduler_wpPostStore_TaxonomyRegistrar.php
│   ├── migration/
│   │   ├── ActionMigrator.php
│   │   ├── ActionScheduler_DBStoreMigrator.php
│   │   ├── BatchFetcher.php
│   │   ├── Config.php
│   │   ├── Controller.php
│   │   ├── DryRun_ActionMigrator.php
│   │   ├── DryRun_LogMigrator.php
│   │   ├── LogMigrator.php
│   │   ├── Runner.php
│   │   └── Scheduler.php
│   ├── schedules/
│   │   ├── ActionScheduler_CanceledSchedule.php
│   │   ├── ActionScheduler_CronSchedule.php
│   │   ├── ActionScheduler_IntervalSchedule.php
│   │   ├── ActionScheduler_NullSchedule.php
│   │   ├── ActionScheduler_Schedule.php
│   │   └── ActionScheduler_SimpleSchedule.php
│   └── schema/
│       ├── ActionScheduler_LoggerSchema.php
│       └── ActionScheduler_StoreSchema.php
├── codecov.yml
├── composer.json
├── deprecated/
│   ├── ActionScheduler_Abstract_QueueRunner_Deprecated.php
│   ├── ActionScheduler_AdminView_Deprecated.php
│   ├── ActionScheduler_Schedule_Deprecated.php
│   ├── ActionScheduler_Store_Deprecated.php
│   └── functions.php
├── docs/
│   ├── CNAME
│   ├── _config.yml
│   ├── _layouts/
│   │   └── default.html
│   ├── admin.md
│   ├── api.md
│   ├── assets/
│   │   └── css/
│   │       └── style.scss
│   ├── browserconfig.xml
│   ├── faq.md
│   ├── google14ef723abb376cd3.html
│   ├── index.md
│   ├── perf.md
│   ├── site.webmanifest
│   ├── usage.md
│   ├── version3-0.md
│   └── wp-cli.md
├── functions.php
├── lib/
│   ├── WP_Async_Request.php
│   └── cron-expression/
│       ├── CronExpression.php
│       ├── CronExpression_AbstractField.php
│       ├── CronExpression_DayOfMonthField.php
│       ├── CronExpression_DayOfWeekField.php
│       ├── CronExpression_FieldFactory.php
│       ├── CronExpression_FieldInterface.php
│       ├── CronExpression_HoursField.php
│       ├── CronExpression_MinutesField.php
│       ├── CronExpression_MonthField.php
│       ├── CronExpression_YearField.php
│       ├── LICENSE
│       └── README.md
├── license.txt
├── package.json
├── phpcs.xml
├── readme.txt
└── tests/
    ├── ActionScheduler_UnitTestCase.php
    ├── README.md
    ├── bin/
    │   └── install.sh
    ├── bootstrap.php
    ├── phpunit/
    │   ├── ActionScheduler_Mock_Async_Request_QueueRunner.php
    │   ├── ActionScheduler_Mocker.php
    │   ├── ActionScheduler_RecurringActionScheduler_Test.php
    │   ├── deprecated/
    │   │   └── ActionScheduler_UnitTestCase.php
    │   ├── helpers/
    │   │   ├── ActionScheduler_Callbacks.php
    │   │   ├── ActionScheduler_Compatibility_Test.php
    │   │   └── ActionScheduler_TimezoneHelper_Test.php
    │   ├── jobs/
    │   │   ├── ActionScheduler_Action_Test.php
    │   │   └── ActionScheduler_NullAction_Test.php
    │   ├── jobstore/
    │   │   ├── AbstractStoreTest.php
    │   │   ├── ActionScheduler_DBStoreMigrator_Test.php
    │   │   ├── ActionScheduler_DBStore_Test.php
    │   │   ├── ActionScheduler_HybridStore_Test.php
    │   │   └── ActionScheduler_wpPostStore_Test.php
    │   ├── lock/
    │   │   └── ActionScheduler_OptionLock_Test.php
    │   ├── logging/
    │   │   ├── ActionScheduler_DBLogger_Test.php
    │   │   └── ActionScheduler_wpCommentLogger_Test.php
    │   ├── migration/
    │   │   ├── ActionMigrator_Test.php
    │   │   ├── BatchFetcher_Test.php
    │   │   ├── Config_Test.php
    │   │   ├── Controller_Test.php
    │   │   ├── LogMigrator_Test.php
    │   │   ├── Runner_Test.php
    │   │   └── Scheduler_Test.php
    │   ├── procedural_api/
    │   │   ├── procedural_api_Test.php
    │   │   └── wc_get_scheduled_actions_Test.php
    │   ├── runner/
    │   │   ├── ActionScheduler_QueueCleaner_Test.php
    │   │   └── ActionScheduler_QueueRunner_Test.php
    │   ├── schedules/
    │   │   ├── ActionScheduler_CronSchedule_Test.php
    │   │   ├── ActionScheduler_IntervalSchedule_Test.php
    │   │   ├── ActionScheduler_NullSchedule_Test.php
    │   │   └── ActionScheduler_SimpleSchedule_Test.php
    │   └── versioning/
    │       └── ActionScheduler_Versions_Test.php
    └── phpunit.xml.dist
Download .txt
SYMBOL INDEX (1048 symbols across 129 files)

FILE: action-scheduler.php
  function action_scheduler_register_3_dot_9_dot_3 (line 45) | function action_scheduler_register_3_dot_9_dot_3() { // WRCS: DEFINED_VE...
  function action_scheduler_initialize_3_dot_9_dot_3 (line 54) | function action_scheduler_initialize_3_dot_9_dot_3() { // WRCS: DEFINED_...

FILE: classes/ActionScheduler_ActionClaim.php
  class ActionScheduler_ActionClaim (line 6) | class ActionScheduler_ActionClaim {
    method __construct (line 27) | public function __construct( $id, array $action_ids ) {
    method get_id (line 35) | public function get_id() {
    method get_actions (line 42) | public function get_actions() {

FILE: classes/ActionScheduler_ActionFactory.php
  class ActionScheduler_ActionFactory (line 6) | class ActionScheduler_ActionFactory {
    method get_stored_action (line 21) | public function get_stored_action( $status, $hook, array $args = array...
    method async (line 72) | public function async( $hook, $args = array(), $group = '' ) {
    method async_unique (line 86) | public function async_unique( $hook, $args = array(), $group = '', $un...
    method single (line 102) | public function single( $hook, $args = array(), $when = null, $group =...
    method single_unique (line 117) | public function single_unique( $hook, $args = array(), $when = null, $...
    method recurring (line 135) | public function recurring( $hook, $args = array(), $first = null, $int...
    method recurring_unique (line 151) | public function recurring_unique( $hook, $args = array(), $first = nul...
    method cron (line 174) | public function cron( $hook, $args = array(), $base_timestamp = null, ...
    method cron_unique (line 193) | public function cron_unique( $hook, $args = array(), $base_timestamp =...
    method repeat (line 228) | public function repeat( $action ) {
    method create (line 268) | public function create( array $options = array() ) {
    method store (line 342) | protected function store( ActionScheduler_Action $action ) {
    method store_unique_action (line 354) | protected function store_unique_action( ActionScheduler_Action $action...

FILE: classes/ActionScheduler_AdminView.php
  class ActionScheduler_AdminView (line 8) | class ActionScheduler_AdminView extends ActionScheduler_AdminView_Deprec...
    method instance (line 37) | public static function instance() {
    method init (line 52) | public function init() {
    method system_status_report (line 70) | public function system_status_report() {
    method register_system_status_tab (line 81) | public function register_system_status_tab( array $tabs ) {
    method register_menu (line 94) | public function register_menu() {
    method process_admin_ui (line 109) | public function process_admin_ui() {
    method render_admin_ui (line 116) | public function render_admin_ui() {
    method get_list_table (line 126) | protected function get_list_table() {
    method maybe_check_pastdue_actions (line 142) | public function maybe_check_pastdue_actions() {
    method check_pastdue_actions (line 164) | protected function check_pastdue_actions() {
    method add_help_tabs (line 246) | public function add_help_tabs() {

FILE: classes/ActionScheduler_AsyncRequest_QueueRunner.php
  class ActionScheduler_AsyncRequest_QueueRunner (line 8) | class ActionScheduler_AsyncRequest_QueueRunner extends WP_Async_Request {
    method __construct (line 36) | public function __construct( ActionScheduler_Store $store ) {
    method handle (line 47) | protected function handle() {
    method maybe_dispatch (line 62) | public function maybe_dispatch() {
    method allow (line 76) | protected function allow() {
    method get_sleep_seconds (line 90) | protected function get_sleep_seconds() {

FILE: classes/ActionScheduler_Compatibility.php
  class ActionScheduler_Compatibility (line 6) | class ActionScheduler_Compatibility {
    method convert_hr_to_bytes (line 18) | public static function convert_hr_to_bytes( $value ) {
    method raise_memory_limit (line 47) | public static function raise_memory_limit() {
    method raise_time_limit (line 93) | public static function raise_time_limit( $limit = 0 ) {

FILE: classes/ActionScheduler_DataController.php
  class ActionScheduler_DataController (line 16) | class ActionScheduler_DataController {
    method dependencies_met (line 58) | public static function dependencies_met() {
    method is_migration_complete (line 68) | public static function is_migration_complete() {
    method mark_migration_complete (line 75) | public static function mark_migration_complete() {
    method mark_migration_incomplete (line 84) | public static function mark_migration_incomplete() {
    method set_store_class (line 95) | public static function set_store_class( $class ) {
    method set_logger_class (line 106) | public static function set_logger_class( $class ) {
    method set_sleep_time (line 115) | public static function set_sleep_time( $sleep_time ) {
    method set_free_ticks (line 124) | public static function set_free_ticks( $free_ticks ) {
    method maybe_free_memory (line 133) | public static function maybe_free_memory( $ticks ) {
    method free_memory (line 142) | public static function free_memory() {
    method init (line 188) | public static function init() {
    method instance (line 203) | public static function instance() {

FILE: classes/ActionScheduler_DateTime.php
  class ActionScheduler_DateTime (line 8) | class ActionScheduler_DateTime extends DateTime {
    method getTimestamp (line 27) | #[\ReturnTypeWillChange]
    method setUtcOffset (line 39) | public function setUtcOffset( $offset ) {
    method getOffset (line 49) | #[\ReturnTypeWillChange]
    method setTimezone (line 62) | #[\ReturnTypeWillChange]
    method getOffsetTimestamp (line 76) | public function getOffsetTimestamp() {

FILE: classes/ActionScheduler_Exception.php
  type ActionScheduler_Exception (line 11) | interface ActionScheduler_Exception {}

FILE: classes/ActionScheduler_FatalErrorMonitor.php
  class ActionScheduler_FatalErrorMonitor (line 6) | class ActionScheduler_FatalErrorMonitor {
    method __construct (line 34) | public function __construct( ActionScheduler_Store $store ) {
    method attach (line 43) | public function attach( ActionScheduler_ActionClaim $claim ) {
    method detach (line 55) | public function detach() {
    method track_current_action (line 70) | public function track_current_action( $action_id ) {
    method untrack_action (line 77) | public function untrack_action() {
    method handle_unexpected_shutdown (line 84) | public function handle_unexpected_shutdown() {

FILE: classes/ActionScheduler_InvalidActionException.php
  class ActionScheduler_InvalidActionException (line 10) | class ActionScheduler_InvalidActionException extends \InvalidArgumentExc...
    method from_schedule (line 19) | public static function from_schedule( $action_id, $schedule ) {
    method from_decoding_args (line 37) | public static function from_decoding_args( $action_id, $args = array()...

FILE: classes/ActionScheduler_ListTable.php
  class ActionScheduler_ListTable (line 8) | class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTab...
    method __construct (line 83) | public function __construct( ActionScheduler_Store $store, ActionSched...
    method set_items_per_page_option (line 205) | public function set_items_per_page_option( $status, $option, $value ) {
    method human_interval (line 221) | private static function human_interval( $interval, $periods_to_include...
    method get_recurrence (line 254) | protected function get_recurrence( $action ) {
    method column_args (line 277) | public function column_args( array $row ) {
    method column_log_entries (line 297) | public function column_log_entries( array $row ) {
    method get_log_entry_html (line 319) | protected function get_log_entry_html( ActionScheduler_LogEntry $log_e...
    method maybe_render_actions (line 333) | protected function maybe_render_actions( $row, $column_name ) {
    method display_admin_notices (line 349) | public function display_admin_notices() {
    method column_schedule (line 458) | public function column_schedule( $row ) {
    method get_schedule_display_string (line 468) | protected function get_schedule_display_string( ActionScheduler_Schedu...
    method bulk_delete (line 505) | protected function bulk_delete( array $ids, $ids_sql ) {
    method row_action_cancel (line 531) | protected function row_action_cancel( $action_id ) {
    method row_action_run (line 541) | protected function row_action_run( $action_id ) {
    method recreate_tables (line 548) | protected function recreate_tables() {
    method process_row_action (line 569) | protected function process_row_action( $action_id, $row_action_type ) {
    method prepare_items (line 592) | public function prepare_items() {
    method display_filter_by_status (line 657) | protected function display_filter_by_status() {
    method get_search_box_button_text (line 665) | protected function get_search_box_button_text() {
    method get_per_page_option_name (line 672) | protected function get_per_page_option_name() {

FILE: classes/ActionScheduler_LogEntry.php
  class ActionScheduler_LogEntry (line 6) | class ActionScheduler_LogEntry {
    method __construct (line 37) | public function __construct( $action_id, $message, $date = null ) {
    method get_date (line 60) | public function get_date() {
    method get_action_id (line 67) | public function get_action_id() {
    method get_message (line 74) | public function get_message() {

FILE: classes/ActionScheduler_NullLogEntry.php
  class ActionScheduler_NullLogEntry (line 6) | class ActionScheduler_NullLogEntry extends ActionScheduler_LogEntry {
    method __construct (line 14) | public function __construct( $action_id = '', $message = '' ) {

FILE: classes/ActionScheduler_OptionLock.php
  class ActionScheduler_OptionLock (line 11) | class ActionScheduler_OptionLock extends ActionScheduler_Lock {
    method set (line 27) | public function set( $lock_type ) {
    method get_expiration (line 67) | public function get_expiration( $lock_type ) {
    method get_expiration_from (line 78) | private function get_expiration_from( $lock_value ) {
    method get_key (line 100) | protected function get_key( $lock_type ) {
    method get_existing_lock (line 111) | private function get_existing_lock( $lock_type ) {
    method new_lock_value (line 133) | private function new_lock_value( $lock_type ) {

FILE: classes/ActionScheduler_QueueCleaner.php
  class ActionScheduler_QueueCleaner (line 6) | class ActionScheduler_QueueCleaner {
    method __construct (line 45) | public function __construct( ?ActionScheduler_Store $store = null, $ba...
    method delete_old_actions (line 56) | public function delete_old_actions() {
    method clean_actions (line 128) | public function clean_actions( array $statuses_to_purge, DateTime $cut...
    method delete_actions (line 162) | private function delete_actions( array $actions_to_delete, $lifespan =...
    method reset_timeouts (line 200) | public function reset_timeouts( $time_limit = 300 ) {
    method mark_failures (line 234) | public function mark_failures( $time_limit = 300 ) {
    method clean (line 263) | public function clean( $time_limit = 300 ) {
    method get_batch_size (line 274) | protected function get_batch_size() {

FILE: classes/ActionScheduler_QueueRunner.php
  class ActionScheduler_QueueRunner (line 6) | class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_Queue...
    method instance (line 38) | public static function instance() {
    method __construct (line 55) | public function __construct( ?ActionScheduler_Store $store = null, ?Ac...
    method init (line 70) | public function init() {
    method hook_dispatch_async_request (line 94) | public function hook_dispatch_async_request() {
    method unhook_dispatch_async_request (line 101) | public function unhook_dispatch_async_request() {
    method maybe_dispatch_async_request (line 123) | public function maybe_dispatch_async_request() {
    method run (line 148) | public function run( $context = 'WP Cron' ) {
    method do_batch (line 180) | protected function do_batch( $size = 100, $context = '' ) {
    method clear_caches (line 211) | protected function clear_caches() {
    method add_wp_cron_schedule (line 250) | public function add_wp_cron_schedule( $schedules ) {

FILE: classes/ActionScheduler_RecurringActionScheduler.php
  class ActionScheduler_RecurringActionScheduler (line 10) | class ActionScheduler_RecurringActionScheduler {
    method init (line 25) | public function init(): void {
    method schedule_recurring_scheduler_hook (line 37) | public function schedule_recurring_scheduler_hook(): void {
    method run_recurring_scheduler_hook (line 60) | public function run_recurring_scheduler_hook(): void {

FILE: classes/ActionScheduler_SystemInformation.php
  class ActionScheduler_SystemInformation (line 6) | class ActionScheduler_SystemInformation {
    method active_source (line 22) | public static function active_source(): array {
    method active_source_path (line 76) | public static function active_source_path(): string {
    method get_sources (line 89) | public static function get_sources() {

FILE: classes/ActionScheduler_Versions.php
  class ActionScheduler_Versions (line 6) | class ActionScheduler_Versions {
    method register (line 34) | public function register( $version_string, $initialization_callback ) {
    method get_versions (line 51) | public function get_versions() {
    method get_sources (line 66) | public function get_sources() {
    method latest_version (line 73) | public function latest_version() {
    method latest_version_callback (line 85) | public function latest_version_callback() {
    method instance (line 101) | public static function instance() {
    method initialize_latest_version (line 113) | public static function initialize_latest_version() {
    method active_source (line 135) | public function active_source(): array {
    method active_source_path (line 147) | public function active_source_path(): string {

FILE: classes/ActionScheduler_WPCommentCleaner.php
  class ActionScheduler_WPCommentCleaner (line 8) | class ActionScheduler_WPCommentCleaner {
    method init (line 36) | public static function init() {
    method has_logs (line 60) | public static function has_logs() {
    method maybe_schedule_cleanup (line 68) | public static function maybe_schedule_cleanup() {
    method delete_all_action_comments (line 91) | public static function delete_all_action_comments() {
    method register_admin_notice (line 108) | public static function register_admin_notice() {
    method print_admin_notice (line 115) | public static function print_admin_notice() {

FILE: classes/ActionScheduler_wcSystemStatus.php
  class ActionScheduler_wcSystemStatus (line 6) | class ActionScheduler_wcSystemStatus {
    method __construct (line 22) | public function __construct( $store ) {
    method render (line 31) | public function render() {
    method get_oldest_and_newest (line 45) | protected function get_oldest_and_newest( $status_keys ) {
    method get_action_status_date (line 73) | protected function get_action_status_date( $status, $date_type = 'olde...
    method get_template (line 102) | protected function get_template( $status_labels, $action_counts, $olde...
    method __call (line 156) | public function __call( $name, $arguments ) {

FILE: classes/WP_CLI/Action/Cancel_Command.php
  class Cancel_Command (line 10) | class Cancel_Command extends \ActionScheduler_WPCLI_Command {
    method execute (line 17) | public function execute() {
    method cancel_single (line 47) | protected function cancel_single( $hook, $callback_args, $group ) {
    method cancel_all (line 74) | protected function cancel_all( $hook, $callback_args, $group ) {
    method print_success (line 97) | protected function print_success() {
    method print_error (line 109) | protected function print_error( \Exception $e, $multiple ) {

FILE: classes/WP_CLI/Action/Create_Command.php
  class Create_Command (line 10) | class Create_Command extends \ActionScheduler_WPCLI_Command {
    method execute (line 19) | public function execute() {
    method print_success (line 125) | protected function print_success( $action_id, $action_type ) {
    method print_error (line 143) | protected function print_error( \Exception $e ) {

FILE: classes/WP_CLI/Action/Delete_Command.php
  class Delete_Command (line 8) | class Delete_Command extends \ActionScheduler_WPCLI_Command {
    method __construct (line 34) | public function __construct( array $args, array $assoc_args ) {
    method execute (line 48) | public function execute() {
    method on_action_deleted (line 93) | public function on_action_deleted( $action_id ) {

FILE: classes/WP_CLI/Action/Generate_Command.php
  class Generate_Command (line 10) | class Generate_Command extends \ActionScheduler_WPCLI_Command {
    method execute (line 17) | public function execute() {
    method generate (line 64) | protected function generate( $schedule_start, $interval, $count, $hook...
    method print_success (line 93) | protected function print_success( $actions_added, $action_type ) {
    method print_error (line 111) | protected function print_error( \Exception $e ) {

FILE: classes/WP_CLI/Action/Get_Command.php
  class Get_Command (line 8) | class Get_Command extends \ActionScheduler_WPCLI_Command {
    method execute (line 15) | public function execute() {

FILE: classes/WP_CLI/Action/List_Command.php
  class List_Command (line 10) | class List_Command extends \ActionScheduler_WPCLI_Command {
    method execute (line 33) | public function execute() {

FILE: classes/WP_CLI/Action/Next_Command.php
  class Next_Command (line 12) | class Next_Command extends \ActionScheduler_WPCLI_Command {
    method execute (line 19) | public function execute() {

FILE: classes/WP_CLI/Action/Run_Command.php
  class Run_Command (line 8) | class Run_Command extends \ActionScheduler_WPCLI_Command {
    method __construct (line 36) | public function __construct( array $args, array $assoc_args ) {
    method execute (line 53) | public function execute() {
    method on_action_ignored (line 114) | public function on_action_ignored( $action_id ) {
    method on_action_executed (line 135) | public function on_action_executed( $action_id ) {
    method on_action_failed (line 157) | public function on_action_failed( $action_id, \Exception $e ) {
    method on_action_invalid (line 179) | public function on_action_invalid( $action_id, \Exception $e ) {

FILE: classes/WP_CLI/ActionScheduler_WPCLI_Clean_Command.php
  class ActionScheduler_WPCLI_Clean_Command (line 6) | class ActionScheduler_WPCLI_Clean_Command extends WP_CLI_Command {
    method clean (line 33) | public function clean( $args, $assoc_args ) {
    method print_total_batches (line 84) | protected function print_total_batches( int $batches_processed ) {
    method print_error (line 99) | protected function print_error( Exception $e ) {
    method print_success (line 114) | protected function print_success( int $actions_deleted ) {

FILE: classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php
  class ActionScheduler_WPCLI_QueueRunner (line 10) | class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract...
    method __construct (line 42) | public function __construct( ?ActionScheduler_Store $store = null, ?Ac...
    method setup (line 62) | public function setup( $batch_size, $hooks = array(), $group = '', $fo...
    method add_hooks (line 86) | protected function add_hooks() {
    method setup_progress_bar (line 95) | protected function setup_progress_bar() {
    method run (line 111) | public function run( $context = 'WP CLI' ) {
    method before_execute (line 138) | public function before_execute( $action_id ) {
    method after_execute (line 149) | public function after_execute( $action_id, $action = null ) {
    method action_failed (line 165) | public function action_failed( $action_id, $exception ) {
    method stop_the_insanity (line 179) | protected function stop_the_insanity( $sleep_time = 0 ) {
    method maybe_stop_the_insanity (line 188) | protected function maybe_stop_the_insanity() {

FILE: classes/WP_CLI/ActionScheduler_WPCLI_Scheduler_command.php
  class ActionScheduler_WPCLI_Scheduler_command (line 6) | class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
    method fix_schema (line 18) | public function fix_schema( $args, $assoc_args ) {
    method run (line 76) | public function run( $args, $assoc_args ) {
    method parse_comma_separated_string (line 137) | private function parse_comma_separated_string( $string ): array {
    method print_total_actions (line 146) | protected function print_total_actions( $total ) {
    method print_total_batches (line 161) | protected function print_total_batches( $batches_completed ) {
    method print_error (line 178) | protected function print_error( Exception $e ) {
    method print_success (line 193) | protected function print_success( $actions_completed ) {

FILE: classes/WP_CLI/Action_Command.php
  class Action_Command (line 10) | class Action_Command extends \WP_CLI_Command {
    method cancel (line 36) | public function cancel( array $args, array $assoc_args ) {
    method create (line 88) | public function create( array $args, array $assoc_args ) {
    method delete (line 120) | public function delete( array $args, array $assoc_args ) {
    method generate (line 169) | public function generate( array $args, array $assoc_args ) {
    method get (line 207) | public function get( array $args, array $assoc_args ) {
    method subcommand_list (line 265) | public function subcommand_list( array $args, array $assoc_args ) {
    method logs (line 285) | public function logs( array $args ) {
    method next (line 317) | public function next( array $args, array $assoc_args ) {
    method run (line 349) | public function run( array $args, array $assoc_args ) {

FILE: classes/WP_CLI/Migration_Command.php
  class Migration_Command (line 22) | class Migration_Command extends WP_CLI_Command {
    method register (line 34) | public function register() {
    method migrate (line 85) | public function migrate( $positional_args, $assoc_args ) {
    method get_migration_config (line 119) | private function get_migration_config( $args ) {
    method init_logging (line 136) | private function init_logging() {

FILE: classes/WP_CLI/ProgressBar.php
  class ProgressBar (line 18) | class ProgressBar {
    method __construct (line 64) | public function __construct( $message, $count, $interval = 100 ) {
    method tick (line 79) | public function tick() {
    method current (line 95) | public function current() {
    method finish (line 102) | public function finish() {
    method set_message (line 115) | public function set_message( $message ) {
    method set_count (line 124) | public function set_count( $count ) {
    method setup_progress_bar (line 132) | protected function setup_progress_bar() {

FILE: classes/WP_CLI/System_Command.php
  class System_Command (line 14) | class System_Command {
    method __construct (line 26) | public function __construct() {
    method datastore (line 39) | public function datastore( array $args, array $assoc_args ) {
    method runner (line 50) | public function runner( array $args, array $assoc_args ) {
    method status (line 61) | public function status( array $args, array $assoc_args ) {
    method version (line 102) | public function version( array $args, array $assoc_args ) {
    method source (line 149) | public function source( array $args, array $assoc_args ) {
    method get_current_datastore (line 201) | protected function get_current_datastore() {
    method get_latest_version (line 211) | protected function get_latest_version( $instance = null ) {
    method get_current_runner (line 224) | protected function get_current_runner() {
    method get_oldest_and_newest (line 234) | protected function get_oldest_and_newest( $status_keys ) {
    method get_action_status_date (line 261) | protected function get_action_status_date( $status, $date_type = 'olde...

FILE: classes/abstracts/ActionScheduler.php
  class ActionScheduler (line 11) | abstract class ActionScheduler {
    method factory (line 37) | public static function factory() {
    method store (line 47) | public static function store() {
    method lock (line 54) | public static function lock() {
    method logger (line 61) | public static function logger() {
    method runner (line 68) | public static function runner() {
    method admin_view (line 75) | public static function admin_view() {
    method plugin_path (line 86) | public static function plugin_path( $path ) {
    method plugin_url (line 102) | public static function plugin_url( $path ) {
    method autoload (line 111) | public static function autoload( $class ) {
    method init (line 175) | public static function init( $plugin_file ) {
    method is_initialized (line 270) | public static function is_initialized( $function_name = null ) {
    method is_class_abstract (line 292) | protected static function is_class_abstract( $class ) {
    method is_class_migration (line 319) | protected static function is_class_migration( $class ) {
    method is_class_cli (line 347) | protected static function is_class_cli( $class ) {
    method __clone (line 365) | final public function __clone() {
    method __wakeup (line 372) | final public function __wakeup() {
    method __construct (line 379) | final private function __construct() {}
    method get_datetime_object (line 389) | public static function get_datetime_object( $when = null, $timezone = ...
    method check_shutdown_hook (line 400) | public static function check_shutdown_hook( $function_name ) {

FILE: classes/abstracts/ActionScheduler_Abstract_ListTable.php
  class ActionScheduler_Abstract_ListTable (line 24) | abstract class ActionScheduler_Abstract_ListTable extends WP_List_Table {
    method translate (line 144) | protected function translate( $text, $context = '' ) {
    method get_bulk_actions (line 157) | protected function get_bulk_actions() {
    method process_bulk_action (line 176) | protected function process_bulk_action() {
    method bulk_delete (line 212) | protected function bulk_delete( array $ids, $ids_sql ) {
    method prepare_column_headers (line 223) | protected function prepare_column_headers() {
    method get_sortable_columns (line 235) | public function get_sortable_columns() {
    method get_columns (line 247) | public function get_columns() {
    method get_items_query_limit (line 263) | protected function get_items_query_limit() {
    method get_items_offset (line 275) | protected function get_items_offset() {
    method get_items_query_offset (line 294) | protected function get_items_query_offset() {
    method get_items_query_order (line 305) | protected function get_items_query_order() {
    method get_request_query_args_to_persist (line 323) | protected function get_request_query_args_to_persist() {
    method get_request_orderby (line 339) | protected function get_request_orderby() {
    method get_request_order (line 357) | protected function get_request_order() {
    method get_request_status (line 373) | protected function get_request_status() {
    method get_request_search_query (line 383) | protected function get_request_search_query() {
    method get_table_columns (line 394) | protected function get_table_columns() {
    method get_items_query_search (line 412) | protected function get_items_query_search() {
    method get_items_query_filters (line 434) | protected function get_items_query_filters() {
    method prepare_items (line 464) | public function prepare_items() {
    method extra_tablenav (line 517) | public function extra_tablenav( $which ) {
    method set_items (line 551) | protected function set_items( array $items ) {
    method column_cb (line 565) | public function column_cb( $row ) {
    method maybe_render_actions (line 579) | protected function maybe_render_actions( $row, $column_name ) {
    method process_row_actions (line 619) | protected function process_row_actions() {
    method column_default (line 655) | public function column_default( $item, $column_name ) {
    method display_header (line 664) | protected function display_header() {
    method display_admin_notices (line 676) | protected function display_admin_notices() {
    method display_filter_by_status (line 687) | protected function display_filter_by_status() {
    method display_table (line 736) | protected function display_table() {
    method process_actions (line 757) | public function process_actions() {
    method display_page (line 771) | public function display_page() {
    method get_search_box_placeholder (line 785) | protected function get_search_box_placeholder() {
    method get_per_page_option_name (line 794) | protected function get_per_page_option_name() {

FILE: classes/abstracts/ActionScheduler_Abstract_QueueRunner.php
  class ActionScheduler_Abstract_QueueRunner (line 6) | abstract class ActionScheduler_Abstract_QueueRunner extends ActionSchedu...
    method __construct (line 46) | public function __construct( ?ActionScheduler_Store $store = null, ?Ac...
    method process_action (line 63) | public function process_action( $action_id, $context = '' ) {
    method fetch_complete_action (line 136) | private function fetch_complete_action( int $action_id ) {
    method cancel_corrupted_action (line 155) | private function cancel_corrupted_action( int $action_id ) {
    method handle_action_error (line 178) | private function handle_action_error( $action_id, $e, $context, $valid...
    method schedule_next_instance (line 207) | protected function schedule_next_instance( ActionScheduler_Action $act...
    method recurring_action_is_consistently_failing (line 236) | private function recurring_action_is_consistently_failing( ActionSched...
    method run_cleanup (line 286) | protected function run_cleanup() {
    method get_allowed_concurrent_batches (line 295) | public function get_allowed_concurrent_batches() {
    method has_maximum_concurrent_batches (line 304) | public function has_maximum_concurrent_batches() {
    method get_time_limit (line 313) | protected function get_time_limit() {
    method get_execution_time (line 331) | protected function get_execution_time() {
    method time_likely_to_be_exceeded (line 352) | protected function time_likely_to_be_exceeded( $processed_actions ) {
    method get_memory_limit (line 375) | protected function get_memory_limit() {
    method memory_exceeded (line 399) | protected function memory_exceeded() {
    method batch_limits_exceeded (line 417) | protected function batch_limits_exceeded( $processed_actions ) {
    method run (line 428) | abstract public function run( $context = '' );

FILE: classes/abstracts/ActionScheduler_Abstract_RecurringSchedule.php
  class ActionScheduler_Abstract_RecurringSchedule (line 6) | abstract class ActionScheduler_Abstract_RecurringSchedule extends Action...
    method __construct (line 44) | public function __construct( DateTime $date, $recurrence, ?DateTime $f...
    method is_recurring (line 55) | public function is_recurring() {
    method get_first_date (line 64) | public function get_first_date() {
    method get_recurrence (line 73) | public function get_recurrence() {
    method __sleep (line 82) | public function __sleep() {
    method __wakeup (line 104) | public function __wakeup() {

FILE: classes/abstracts/ActionScheduler_Abstract_Schedule.php
  class ActionScheduler_Abstract_Schedule (line 6) | abstract class ActionScheduler_Abstract_Schedule extends ActionScheduler...
    method __construct (line 27) | public function __construct( DateTime $date ) {
    method is_recurring (line 36) | abstract public function is_recurring();
    method calculate_next (line 44) | abstract protected function calculate_next( DateTime $after );
    method get_next (line 52) | public function get_next( DateTime $after ) {
    method get_date (line 66) | public function get_date() {
    method __sleep (line 75) | public function __sleep() {
    method __wakeup (line 85) | public function __wakeup() {

FILE: classes/abstracts/ActionScheduler_Abstract_Schema.php
  class ActionScheduler_Abstract_Schema (line 13) | abstract class ActionScheduler_Abstract_Schema {
    method init (line 40) | public function init() {}
    method register_tables (line 49) | public function register_tables( $force_update = false ) {
    method get_table_definition (line 82) | abstract protected function get_table_definition( $table );
    method schema_update_required (line 91) | private function schema_update_required() {
    method mark_schema_update_complete (line 123) | private function mark_schema_update_complete() {
    method update_table (line 139) | private function update_table( $table ) {
    method get_full_table_name (line 160) | protected function get_full_table_name( $table ) {
    method tables_exist (line 169) | public function tables_exist() {

FILE: classes/abstracts/ActionScheduler_Lock.php
  class ActionScheduler_Lock (line 8) | abstract class ActionScheduler_Lock {
    method is_locked (line 30) | public function is_locked( $lock_type ) {
    method set (line 42) | abstract public function set( $lock_type );
    method get_expiration (line 50) | abstract public function get_expiration( $lock_type );
    method get_duration (line 58) | protected function get_duration( $lock_type ) {
    method instance (line 67) | public static function instance() {

FILE: classes/abstracts/ActionScheduler_Logger.php
  class ActionScheduler_Logger (line 8) | abstract class ActionScheduler_Logger {
    method instance (line 22) | public static function instance() {
    method log (line 39) | abstract public function log( $action_id, $message, ?DateTime $date = ...
    method get_entry (line 48) | abstract public function get_entry( $entry_id );
    method get_logs (line 57) | abstract public function get_logs( $action_id );
    method init (line 65) | public function init() {
    method hook_stored_action (line 83) | public function hook_stored_action() {
    method unhook_stored_action (line 90) | public function unhook_stored_action() {
    method log_stored_action (line 99) | public function log_stored_action( $action_id ) {
    method log_canceled_action (line 108) | public function log_canceled_action( $action_id ) {
    method log_started_action (line 118) | public function log_started_action( $action_id, $context = '' ) {
    method log_completed_action (line 135) | public function log_completed_action( $action_id, $action = null, $con...
    method log_failed_action (line 152) | public function log_failed_action( $action_id, Exception $exception, $...
    method log_timed_out_action (line 169) | public function log_timed_out_action( $action_id, $timeout ) {
    method log_unexpected_shutdown (line 180) | public function log_unexpected_shutdown( $action_id, $error ) {
    method log_reset_action (line 192) | public function log_reset_action( $action_id ) {
    method log_ignored_action (line 202) | public function log_ignored_action( $action_id, $context = '' ) {
    method log_failed_fetch_action (line 218) | public function log_failed_fetch_action( $action_id, ?Exception $excep...
    method log_failed_schedule_next_instance (line 236) | public function log_failed_schedule_next_instance( $action_id, Excepti...
    method bulk_log_cancel_actions (line 249) | public function bulk_log_cancel_actions( $action_ids ) {

FILE: classes/abstracts/ActionScheduler_Store.php
  class ActionScheduler_Store (line 8) | abstract class ActionScheduler_Store extends ActionScheduler_Store_Depre...
    method save_action (line 40) | abstract public function save_action( ActionScheduler_Action $action, ...
    method fetch_action (line 49) | abstract public function fetch_action( $action_id );
    method find_action (line 61) | public function find_action( $hook, $params = array() ) {
    method query_actions (line 115) | abstract public function query_actions( $query = array(), $query_type ...
    method query_action (line 128) | public function query_action( $query ) {
    method action_counts (line 145) | abstract public function action_counts();
    method extra_action_counts (line 154) | public function extra_action_counts() {
    method cancel_action (line 183) | abstract public function cancel_action( $action_id );
    method delete_action (line 190) | abstract public function delete_action( $action_id );
    method get_date (line 199) | abstract public function get_date( $action_id );
    method stake_claim (line 212) | abstract public function stake_claim( $max_actions = 10, ?DateTime $be...
    method get_claim_count (line 219) | abstract public function get_claim_count();
    method release_claim (line 226) | abstract public function release_claim( ActionScheduler_ActionClaim $c...
    method unclaim_action (line 233) | abstract public function unclaim_action( $action_id );
    method mark_failure (line 240) | abstract public function mark_failure( $action_id );
    method log_execution (line 247) | abstract public function log_execution( $action_id );
    method mark_complete (line 254) | abstract public function mark_complete( $action_id );
    method get_status (line 262) | abstract public function get_status( $action_id );
    method get_claim_id (line 270) | abstract public function get_claim_id( $action_id );
    method find_actions_by_claim_id (line 278) | abstract public function find_actions_by_claim_id( $claim_id );
    method validate_sql_comparator (line 286) | protected function validate_sql_comparator( $comparison_operator ) {
    method get_scheduled_date_string (line 301) | protected function get_scheduled_date_string( ActionScheduler_Action $...
    method get_scheduled_date_string_local (line 320) | protected function get_scheduled_date_string_local( ActionScheduler_Ac...
    method validate_args (line 339) | protected function validate_args( $args, $action_id ) {
    method validate_schedule (line 359) | protected function validate_schedule( $schedule, $action_id ) {
    method validate_action (line 374) | protected function validate_action( ActionScheduler_Action $action ) {
    method cancel_actions_by_hook (line 390) | public function cancel_actions_by_hook( $hook ) {
    method cancel_actions_by_group (line 415) | public function cancel_actions_by_group( $group ) {
    method bulk_cancel_actions (line 440) | private function bulk_cancel_actions( $action_ids ) {
    method get_status_labels (line 453) | public function get_status_labels() {
    method has_pending_actions_due (line 468) | public function has_pending_actions_due() {
    method init (line 485) | public function init() {}
    method mark_migrated (line 492) | public function mark_migrated( $action_id ) {}
    method instance (line 499) | public static function instance() {

FILE: classes/abstracts/ActionScheduler_TimezoneHelper.php
  class ActionScheduler_TimezoneHelper (line 6) | abstract class ActionScheduler_TimezoneHelper {
    method set_local_timezone (line 24) | public static function set_local_timezone( DateTime $date ) {
    method get_local_timezone_string (line 54) | protected static function get_local_timezone_string( $reset = false ) {
    method get_local_timezone_offset (line 95) | protected static function get_local_timezone_offset() {
    method get_local_timezone (line 112) | public static function get_local_timezone( $reset = false ) {

FILE: classes/abstracts/ActionScheduler_WPCLI_Command.php
  class ActionScheduler_WPCLI_Command (line 6) | abstract class ActionScheduler_WPCLI_Command extends \WP_CLI_Command {
    method __construct (line 31) | public function __construct( array $args, array $assoc_args ) {
    method execute (line 44) | abstract public function execute();
    method get_schedule_display_string (line 53) | protected function get_schedule_display_string( ActionScheduler_Schedu...
    method process_csv_arguments_to_arrays (line 75) | protected function process_csv_arguments_to_arrays() {

FILE: classes/actions/ActionScheduler_Action.php
  class ActionScheduler_Action (line 6) | class ActionScheduler_Action {
    method __construct (line 56) | public function __construct( $hook, array $args = array(), ?ActionSche...
    method execute (line 73) | public function execute() {
    method set_hook (line 94) | protected function set_hook( $hook ) {
    method get_hook (line 101) | public function get_hook() {
    method set_schedule (line 110) | protected function set_schedule( ActionScheduler_Schedule $schedule ) {
    method get_schedule (line 119) | public function get_schedule() {
    method set_args (line 128) | protected function set_args( array $args ) {
    method get_args (line 135) | public function get_args() {
    method set_group (line 144) | protected function set_group( $group ) {
    method get_group (line 153) | public function get_group() {
    method is_finished (line 162) | public function is_finished() {
    method set_priority (line 173) | public function set_priority( $priority ) {
    method get_priority (line 188) | public function get_priority() {

FILE: classes/actions/ActionScheduler_CanceledAction.php
  class ActionScheduler_CanceledAction (line 9) | class ActionScheduler_CanceledAction extends ActionScheduler_FinishedAct...
    method __construct (line 19) | public function __construct( $hook, array $args = array(), ?ActionSche...

FILE: classes/actions/ActionScheduler_FinishedAction.php
  class ActionScheduler_FinishedAction (line 6) | class ActionScheduler_FinishedAction extends ActionScheduler_Action {
    method execute (line 11) | public function execute() {
    method is_finished (line 18) | public function is_finished() {

FILE: classes/actions/ActionScheduler_NullAction.php
  class ActionScheduler_NullAction (line 6) | class ActionScheduler_NullAction extends ActionScheduler_Action {
    method __construct (line 15) | public function __construct( $hook = '', array $args = array(), ?Actio...
    method execute (line 22) | public function execute() {

FILE: classes/data-stores/ActionScheduler_DBLogger.php
  class ActionScheduler_DBLogger (line 10) | class ActionScheduler_DBLogger extends ActionScheduler_Logger {
    method log (line 21) | public function log( $action_id, $message, ?DateTime $date = null ) {
    method get_entry (line 55) | public function get_entry( $entry_id ) {
    method create_entry_from_db_record (line 70) | private function create_entry_from_db_record( $record ) {
    method get_logs (line 91) | public function get_logs( $action_id ) {
    method init (line 105) | public function init() {
    method clear_deleted_action_logs (line 123) | public function clear_deleted_action_logs( $action_id ) {
    method clear_deleted_action_logs_single_batch (line 137) | public function clear_deleted_action_logs_single_batch( $action_id, $c...
    method bulk_log_cancel_actions (line 157) | public function bulk_log_cancel_actions( $action_ids ) {

FILE: classes/data-stores/ActionScheduler_DBStore.php
  class ActionScheduler_DBStore (line 10) | class ActionScheduler_DBStore extends ActionScheduler_Store {
    method init (line 52) | public function init() {
    method save_unique_action (line 67) | public function save_unique_action( ActionScheduler_Action $action, ?D...
    method save_action (line 80) | public function save_action( ActionScheduler_Action $action, ?DateTime...
    method save_action_to_db (line 94) | private function save_action_to_db( ActionScheduler_Action $action, ?D...
    method build_insert_sql (line 150) | private function build_insert_sql( array $data, $unique ) {
    method build_where_clause_for_insert (line 185) | private function build_where_clause_for_insert( $data, $table_name, $u...
    method get_placeholder_for_column (line 228) | private function get_placeholder_for_column( $column_name ) {
    method hash_args (line 250) | protected function hash_args( $args ) {
    method get_args_for_query (line 260) | protected function get_args_for_query( $args ) {
    method get_group_ids (line 275) | protected function get_group_ids( $slugs, $create_if_not_exists = true...
    method create_group (line 312) | protected function create_group( $slug ) {
    method fetch_action (line 332) | public function fetch_action( $action_id ) {
    method get_null_action (line 384) | protected function get_null_action() {
    method make_action_from_db_record (line 395) | protected function make_action_from_db_record( $data ) {
    method get_query_actions_sql (line 429) | protected function get_query_actions_sql( array $query, $select_or_cou...
    method query_actions (line 643) | public function query_actions( $query = array(), $query_type = 'select...
    method action_counts (line 661) | public function action_counts() {
    method cancel_action (line 689) | public function cancel_action( $action_id ) {
    method cancel_actions_by_hook (line 720) | public function cancel_actions_by_hook( $hook ) {
    method cancel_actions_by_group (line 731) | public function cancel_actions_by_group( $group ) {
    method bulk_cancel_actions (line 742) | protected function bulk_cancel_actions( $query_args ) {
    method delete_action (line 797) | public function delete_action( $action_id ) {
    method get_date (line 820) | public function get_date( $action_id ) {
    method get_date_gmt (line 834) | protected function get_date_gmt( $action_id ) {
    method stake_claim (line 864) | public function stake_claim( $max_actions = 10, ?DateTime $before_date...
    method generate_claim_id (line 880) | protected function generate_claim_id() {
    method set_claim_filter (line 901) | public function set_claim_filter( $filter_name, $filter_values ) {
    method get_claim_filter (line 913) | public function get_claim_filter( $filter_name ) {
    method claim_actions (line 934) | protected function claim_actions( $claim_id, $limit, ?DateTime $before...
    method db_supports_skip_locked (line 1045) | private function db_supports_skip_locked() {
    method get_claim_count (line 1080) | public function get_claim_count() {
    method get_claim_id (line 1104) | public function get_claim_id( $action_id ) {
    method find_actions_by_claim_id (line 1124) | public function find_actions_by_claim_id( $claim_id ) {
    method release_claim (line 1158) | public function release_claim( ActionScheduler_ActionClaim $claim ) {
    method unclaim_action (line 1208) | public function unclaim_action( $action_id ) {
    method mark_failure (line 1231) | public function mark_failure( $action_id ) {
    method log_execution (line 1265) | public function log_execution( $action_id ) {
    method mark_complete (line 1299) | public function mark_complete( $action_id ) {
    method get_status (line 1342) | public function get_status( $action_id ) {

FILE: classes/data-stores/ActionScheduler_HybridStore.php
  class ActionScheduler_HybridStore (line 15) | class ActionScheduler_HybridStore extends Store {
    method __construct (line 59) | public function __construct( ?Config $config = null ) {
    method init (line 74) | public function init() {
    method set_autoincrement (line 92) | public function set_autoincrement( $table_name, $table_suffix ) {
    method set_demarkation_id (line 145) | private function set_demarkation_id( $id = null ) {
    method find_action (line 173) | public function find_action( $hook, $params = array() ) {
    method query_actions (line 192) | public function query_actions( $query = array(), $query_type = 'select...
    method action_counts (line 206) | public function action_counts() {
    method stake_claim (line 243) | public function stake_claim( $max_actions = 10, ?DateTime $before_date...
    method migrate (line 261) | private function migrate( $action_ids ) {
    method save_action (line 273) | public function save_action( ActionScheduler_Action $action, ?DateTime...
    method fetch_action (line 282) | public function fetch_action( $action_id ) {
    method cancel_action (line 296) | public function cancel_action( $action_id ) {
    method delete_action (line 308) | public function delete_action( $action_id ) {
    method get_date (line 320) | public function get_date( $action_id ) {
    method mark_failure (line 334) | public function mark_failure( $action_id ) {
    method log_execution (line 346) | public function log_execution( $action_id ) {
    method mark_complete (line 358) | public function mark_complete( $action_id ) {
    method get_status (line 370) | public function get_status( $action_id ) {
    method get_store_from_action_id (line 385) | protected function get_store_from_action_id( $action_id, $primary_firs...
    method get_claim_count (line 425) | public function get_claim_count() {
    method get_claim_id (line 434) | public function get_claim_id( $action_id ) {
    method release_claim (line 443) | public function release_claim( ActionScheduler_ActionClaim $claim ) {
    method unclaim_action (line 452) | public function unclaim_action( $action_id ) {
    method find_actions_by_claim_id (line 461) | public function find_actions_by_claim_id( $claim_id ) {

FILE: classes/data-stores/ActionScheduler_wpCommentLogger.php
  class ActionScheduler_wpCommentLogger (line 6) | class ActionScheduler_wpCommentLogger extends ActionScheduler_Logger {
    method log (line 19) | public function log( $action_id, $message, ?DateTime $date = null ) {
    method create_wp_comment (line 36) | protected function create_wp_comment( $action_id, $message, DateTime $...
    method get_entry (line 60) | public function get_entry( $entry_id ) {
    method get_logs (line 79) | public function get_logs( $action_id ) {
    method get_comment (line 113) | protected function get_comment( $comment_id ) {
    method filter_comment_queries (line 122) | public function filter_comment_queries( $query ) {
    method filter_comment_query_clauses (line 140) | public function filter_comment_query_clauses( $clauses, $query ) {
    method filter_comment_feed (line 156) | public function filter_comment_feed( $where, $query ) {
    method get_where_clause (line 168) | protected function get_where_clause() {
    method filter_comment_count (line 181) | public function filter_comment_count( $stats, $post_id ) {
    method get_comment_count (line 196) | protected function get_comment_count() {
    method delete_comment_count_cache (line 244) | public function delete_comment_count_cache() {
    method init (line 253) | public function init() {
    method disable_comment_counting (line 271) | public function disable_comment_counting() {
    method enable_comment_counting (line 278) | public function enable_comment_counting() {

FILE: classes/data-stores/ActionScheduler_wpPostStore.php
  class ActionScheduler_wpPostStore (line 6) | class ActionScheduler_wpPostStore extends ActionScheduler_Store {
    method save_action (line 38) | public function save_action( ActionScheduler_Action $action, ?DateTime...
    method create_post_array (line 61) | protected function create_post_array( ActionScheduler_Action $action, ...
    method save_post_array (line 80) | protected function save_post_array( $post_array ) {
    method filter_insert_post_data (line 113) | public function filter_insert_post_data( $postdata ) {
    method set_unique_post_slug (line 151) | public function set_unique_post_slug( $override_slug, $slug, $post_ID,...
    method save_post_schedule (line 166) | protected function save_post_schedule( $post_id, $schedule ) {
    method save_action_group (line 177) | protected function save_action_group( $post_id, $group ) {
    method fetch_action (line 191) | public function fetch_action( $action_id ) {
    method get_post (line 213) | protected function get_post( $action_id ) {
    method get_null_action (line 225) | protected function get_null_action() {
    method make_action_from_post (line 235) | protected function make_action_from_post( $post ) {
    method get_action_status_by_post_status (line 258) | protected function get_action_status_by_post_status( $post_status ) {
    method get_post_status_by_action_status (line 286) | protected function get_post_status_by_action_status( $action_status ) {
    method get_query_actions_sql (line 315) | protected function get_query_actions_sql( array $query, $select_or_cou...
    method query_actions (line 464) | public function query_actions( $query = array(), $query_type = 'select...
    method action_counts (line 482) | public function action_counts() {
    method cancel_action (line 511) | public function cancel_action( $action_id ) {
    method delete_action (line 530) | public function delete_action( $action_id ) {
    method get_date (line 547) | public function get_date( $action_id ) {
    method get_date_gmt (line 560) | public function get_date_gmt( $action_id ) {
    method stake_claim (line 585) | public function stake_claim( $max_actions = 10, ?DateTime $before_date...
    method get_claim_count (line 600) | public function get_claim_count() {
    method generate_claim_id (line 617) | protected function generate_claim_id() {
    method claim_actions (line 634) | protected function claim_actions( $claim_id, $limit, ?DateTime $before...
    method get_actions_by_group (line 713) | protected function get_actions_by_group( $group, $limit, DateTime $dat...
    method find_actions_by_claim_id (line 759) | public function find_actions_by_claim_id( $claim_id ) {
    method release_claim (line 800) | public function release_claim( ActionScheduler_ActionClaim $claim ) {
    method unclaim_action (line 851) | public function unclaim_action( $action_id ) {
    method mark_failure (line 881) | public function mark_failure( $action_id ) {
    method get_claim_id (line 905) | public function get_claim_id( $action_id ) {
    method get_status (line 917) | public function get_status( $action_id ) {
    method get_post_column (line 935) | private function get_post_column( $action_id, $column_name ) {
    method log_execution (line 960) | public function log_execution( $action_id ) {
    method mark_complete (line 1000) | public function mark_complete( $action_id ) {
    method mark_migrated (line 1036) | public function mark_migrated( $action_id ) {
    method migration_dependencies_met (line 1051) | public function migration_dependencies_met( $setting ) {
    method validate_action (line 1080) | protected function validate_action( ActionScheduler_Action $action ) {
    method init (line 1093) | public function init() {

FILE: classes/data-stores/ActionScheduler_wpPostStore_PostStatusRegistrar.php
  class ActionScheduler_wpPostStore_PostStatusRegistrar (line 8) | class ActionScheduler_wpPostStore_PostStatusRegistrar {
    method register (line 13) | public function register() {
    method post_status_args (line 23) | protected function post_status_args() {
    method post_status_failed_labels (line 39) | protected function post_status_failed_labels() {
    method post_status_running_labels (line 54) | protected function post_status_running_labels() {

FILE: classes/data-stores/ActionScheduler_wpPostStore_PostTypeRegistrar.php
  class ActionScheduler_wpPostStore_PostTypeRegistrar (line 8) | class ActionScheduler_wpPostStore_PostTypeRegistrar {
    method register (line 12) | public function register() {
    method post_type_args (line 21) | protected function post_type_args() {

FILE: classes/data-stores/ActionScheduler_wpPostStore_TaxonomyRegistrar.php
  class ActionScheduler_wpPostStore_TaxonomyRegistrar (line 8) | class ActionScheduler_wpPostStore_TaxonomyRegistrar {
    method register (line 13) | public function register() {
    method taxonomy_args (line 20) | protected function taxonomy_args() {

FILE: classes/migration/ActionMigrator.php
  class ActionMigrator (line 15) | class ActionMigrator {
    method __construct (line 44) | public function __construct( \ActionScheduler_Store $source_store, \Ac...
    method migrate (line 58) | public function migrate( $source_action_id ) {

FILE: classes/migration/ActionScheduler_DBStoreMigrator.php
  class ActionScheduler_DBStoreMigrator (line 10) | class ActionScheduler_DBStoreMigrator extends ActionScheduler_DBStore {
    method save_action (line 26) | public function save_action( ActionScheduler_Action $action, ?DateTime...

FILE: classes/migration/BatchFetcher.php
  class BatchFetcher (line 16) | class BatchFetcher {
    method __construct (line 29) | public function __construct( Store $source_store ) {
    method fetch (line 40) | public function fetch( $count = 10 ) {
    method get_query_strategies (line 58) | private function get_query_strategies( $count ) {

FILE: classes/migration/Config.php
  class Config (line 19) | class Config {
    method __construct (line 65) | public function __construct() {
    method get_source_store (line 75) | public function get_source_store() {
    method set_source_store (line 88) | public function set_source_store( Store $store ) {
    method get_source_logger (line 98) | public function get_source_logger() {
    method set_source_logger (line 111) | public function set_source_logger( Logger $logger ) {
    method get_destination_store (line 121) | public function get_destination_store() {
    method set_destination_store (line 134) | public function set_destination_store( Store $store ) {
    method get_destination_logger (line 144) | public function get_destination_logger() {
    method set_destination_logger (line 157) | public function set_destination_logger( Logger $logger ) {
    method get_dry_run (line 166) | public function get_dry_run() {
    method set_dry_run (line 175) | public function set_dry_run( $dry_run ) {
    method get_progress_bar (line 184) | public function get_progress_bar() {
    method set_progress_bar (line 193) | public function set_progress_bar( ProgressBar $progress_bar ) {

FILE: classes/migration/Controller.php
  class Controller (line 21) | class Controller {
    method __construct (line 62) | protected function __construct( Scheduler $migration_scheduler ) {
    method get_store_class (line 74) | public function get_store_class( $class ) {
    method get_logger_class (line 92) | public function get_logger_class( $class ) {
    method has_custom_datastore (line 108) | public function has_custom_datastore() {
    method schedule_migration (line 117) | public function schedule_migration() {
    method get_migration_config_object (line 148) | public function get_migration_config_object() {
    method hook_admin_notices (line 172) | public function hook_admin_notices() {
    method display_migration_notice (line 182) | public function display_migration_notice() {
    method hook (line 189) | private function hook() {
    method maybe_hook_migration (line 203) | public function maybe_hook_migration() {
    method allow_migration (line 214) | public function allow_migration() {
    method init (line 229) | public static function init() {
    method instance (line 238) | public static function instance() {

FILE: classes/migration/DryRun_ActionMigrator.php
  class DryRun_ActionMigrator (line 15) | class DryRun_ActionMigrator extends ActionMigrator {
    method migrate (line 23) | public function migrate( $source_action_id ) {

FILE: classes/migration/DryRun_LogMigrator.php
  class DryRun_LogMigrator (line 13) | class DryRun_LogMigrator extends LogMigrator {
    method migrate (line 20) | public function migrate( $source_action_id, $destination_action_id ) {

FILE: classes/migration/LogMigrator.php
  class LogMigrator (line 17) | class LogMigrator {
    method __construct (line 38) | public function __construct( ActionScheduler_Logger $source_logger, Ac...
    method migrate (line 49) | public function migrate( $source_action_id, $destination_action_id ) {

FILE: classes/migration/Runner.php
  class Runner (line 15) | class Runner {
    method __construct (line 77) | public function __construct( Config $config ) {
    method run (line 104) | public function run( $batch_size = 10 ) {
    method migrate_actions (line 128) | public function migrate_actions( array $action_ids ) {
    method init_destination (line 167) | public function init_destination() {

FILE: classes/migration/Scheduler.php
  class Scheduler (line 15) | class Scheduler {
    method hook (line 25) | public function hook() {
    method unhook (line 32) | public function unhook() {
    method run_migration (line 39) | public function run_migration() {
    method mark_complete (line 53) | public function mark_complete() {
    method is_migration_scheduled (line 65) | public function is_migration_scheduled() {
    method schedule_migration (line 78) | public function schedule_migration( $when = 0 ) {
    method unschedule_migration (line 95) | public function unschedule_migration() {
    method get_schedule_interval (line 104) | private function get_schedule_interval() {
    method get_batch_size (line 113) | private function get_batch_size() {
    method get_migration_runner (line 122) | private function get_migration_runner() {

FILE: classes/schedules/ActionScheduler_CanceledSchedule.php
  class ActionScheduler_CanceledSchedule (line 6) | class ActionScheduler_CanceledSchedule extends ActionScheduler_SimpleSch...
    method calculate_next (line 22) | public function calculate_next( DateTime $after ) {
    method get_next (line 33) | public function get_next( DateTime $after ) {
    method is_recurring (line 42) | public function is_recurring() {
    method __wakeup (line 56) | public function __wakeup() {

FILE: classes/schedules/ActionScheduler_CronSchedule.php
  class ActionScheduler_CronSchedule (line 6) | class ActionScheduler_CronSchedule extends ActionScheduler_Abstract_Recu...
    method __construct (line 30) | public function __construct( DateTime $start, $recurrence, ?DateTime $...
    method calculate_next (line 51) | protected function calculate_next( DateTime $after ) {
    method get_recurrence (line 60) | public function get_recurrence() {
    method __sleep (line 77) | public function __sleep() {
    method __wakeup (line 98) | public function __wakeup() {

FILE: classes/schedules/ActionScheduler_IntervalSchedule.php
  class ActionScheduler_IntervalSchedule (line 6) | class ActionScheduler_IntervalSchedule extends ActionScheduler_Abstract_...
    method calculate_next (line 29) | protected function calculate_next( DateTime $after ) {
    method interval_in_seconds (line 39) | public function interval_in_seconds() {
    method __sleep (line 57) | public function __sleep() {
    method __wakeup (line 78) | public function __wakeup() {

FILE: classes/schedules/ActionScheduler_NullSchedule.php
  class ActionScheduler_NullSchedule (line 6) | class ActionScheduler_NullSchedule extends ActionScheduler_SimpleSchedule {
    method __construct (line 20) | public function __construct( ?DateTime $date = null ) {
    method __sleep (line 29) | public function __sleep() {
    method __wakeup (line 36) | public function __wakeup() {

FILE: classes/schedules/ActionScheduler_Schedule.php
  type ActionScheduler_Schedule (line 6) | interface ActionScheduler_Schedule {
    method next (line 14) | public function next( ?DateTime $after = null );
    method is_recurring (line 21) | public function is_recurring();

FILE: classes/schedules/ActionScheduler_SimpleSchedule.php
  class ActionScheduler_SimpleSchedule (line 6) | class ActionScheduler_SimpleSchedule extends ActionScheduler_Abstract_Sc...
    method calculate_next (line 23) | public function calculate_next( DateTime $after ) {
    method is_recurring (line 32) | public function is_recurring() {
    method __sleep (line 49) | public function __sleep() {
    method __wakeup (line 73) | public function __wakeup() {

FILE: classes/schema/ActionScheduler_LoggerSchema.php
  class ActionScheduler_LoggerSchema (line 10) | class ActionScheduler_LoggerSchema extends ActionScheduler_Abstract_Sche...
    method __construct (line 25) | public function __construct() {
    method init (line 34) | public function init() {
    method get_table_definition (line 43) | protected function get_table_definition( $table ) {
    method update_schema_3_0 (line 79) | public function update_schema_3_0( $table, $db_version ) {

FILE: classes/schema/ActionScheduler_StoreSchema.php
  class ActionScheduler_StoreSchema (line 10) | class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
    method __construct (line 28) | public function __construct() {
    method init (line 39) | public function init() {
    method get_table_definition (line 48) | protected function get_table_definition( $table ) {
    method update_schema_5_0 (line 121) | public function update_schema_5_0( $table, $db_version ) {

FILE: deprecated/ActionScheduler_Abstract_QueueRunner_Deprecated.php
  class ActionScheduler_Abstract_QueueRunner_Deprecated (line 6) | abstract class ActionScheduler_Abstract_QueueRunner_Deprecated {
    method get_maximum_execution_time (line 14) | protected function get_maximum_execution_time() {

FILE: deprecated/ActionScheduler_AdminView_Deprecated.php
  class ActionScheduler_AdminView_Deprecated (line 11) | class ActionScheduler_AdminView_Deprecated {
    method action_scheduler_post_type_args (line 18) | public function action_scheduler_post_type_args( $args ) {
    method list_table_views (line 29) | public function list_table_views( $views ) {
    method bulk_actions (line 42) | public function bulk_actions( $actions ) {
    method list_table_columns (line 56) | public function list_table_columns( $columns ) {
    method list_table_sortable_columns (line 67) | public static function list_table_sortable_columns( $columns ) {
    method list_table_column_content (line 78) | public static function list_table_column_content( $column_name, $post_...
    method row_actions (line 91) | public static function row_actions( $actions, $post ) {
    method maybe_execute_action (line 101) | public static function maybe_execute_action() {
    method admin_notices (line 116) | public static function admin_notices() {
    method custom_orderby (line 127) | public function custom_orderby( $orderby, $query ) {
    method search_post_password (line 138) | public function search_post_password( $search, $query ) {
    method post_updated_messages (line 148) | public function post_updated_messages( $messages ) {

FILE: deprecated/ActionScheduler_Schedule_Deprecated.php
  class ActionScheduler_Schedule_Deprecated (line 6) | abstract class ActionScheduler_Schedule_Deprecated implements ActionSche...
    method next (line 16) | public function next( ?DateTime $after = null ) {

FILE: deprecated/ActionScheduler_Store_Deprecated.php
  class ActionScheduler_Store_Deprecated (line 8) | abstract class ActionScheduler_Store_Deprecated {
    method mark_failed_fetch_action (line 17) | public function mark_failed_fetch_action( $action_id ) {
    method hook (line 27) | protected static function hook() {
    method unhook (line 36) | protected static function unhook() {
    method get_local_timezone (line 46) | protected function get_local_timezone() {

FILE: deprecated/functions.php
  function wc_schedule_single_action (line 22) | function wc_schedule_single_action( $timestamp, $hook, $args = array(), ...
  function wc_schedule_recurring_action (line 40) | function wc_schedule_recurring_action( $timestamp, $interval_in_seconds,...
  function wc_schedule_cron_action (line 68) | function wc_schedule_cron_action( $timestamp, $schedule, $hook, $args = ...
  function wc_unschedule_action (line 82) | function wc_unschedule_action( $hook, $args = array(), $group = '' ) {
  function wc_next_scheduled_action (line 98) | function wc_next_scheduled_action( $hook, $args = null, $group = '' ) {
  function wc_get_scheduled_actions (line 126) | function wc_get_scheduled_actions( $args = array(), $return_format = OBJ...

FILE: functions.php
  function as_enqueue_async_action (line 19) | function as_enqueue_async_action( $hook, $args = array(), $group = '', $...
  function as_schedule_single_action (line 69) | function as_schedule_single_action( $timestamp, $hook, $args = array(), ...
  function as_schedule_recurring_action (line 122) | function as_schedule_recurring_action( $timestamp, $interval_in_seconds,...
  function as_schedule_cron_action (line 209) | function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = ...
  function as_unschedule_action (line 267) | function as_unschedule_action( $hook, $args = array(), $group = '' ) {
  function as_unschedule_all_actions (line 312) | function as_unschedule_all_actions( $hook, $args = array(), $group = '' ) {
  function as_next_scheduled_action (line 346) | function as_next_scheduled_action( $hook, $args = null, $group = '' ) {
  function as_has_scheduled_action (line 399) | function as_has_scheduled_action( $hook, $args = null, $group = '' ) {
  function as_get_scheduled_actions (line 442) | function as_get_scheduled_actions( $args = array(), $return_format = OBJ...
  function as_get_datetime_object (line 489) | function as_get_datetime_object( $date_string = null, $timezone = 'UTC' ) {
  function as_supports (line 509) | function as_supports( string $feature ): bool {

FILE: lib/WP_Async_Request.php
  class WP_Async_Request (line 23) | abstract class WP_Async_Request {
    method __construct (line 62) | public function __construct() {
    method data (line 76) | public function data( $data ) {
    method dispatch (line 87) | public function dispatch() {
    method get_query_args (line 99) | protected function get_query_args() {
    method get_query_url (line 122) | protected function get_query_url() {
    method get_post_args (line 142) | protected function get_post_args() {
    method maybe_handle (line 168) | public function maybe_handle() {
    method handle (line 185) | abstract protected function handle();

FILE: lib/cron-expression/CronExpression.php
  class CronExpression (line 16) | class CronExpression
    method factory (line 57) | public static function factory($expression, ?CronExpression_FieldFacto...
    method __construct (line 81) | public function __construct($expression, CronExpression_FieldFactory $...
    method setExpression (line 95) | public function setExpression($value)
    method setPart (line 120) | public function setPart($position, $value)
    method getNextRunDate (line 149) | public function getNextRunDate($currentTime = 'now', $nth = 0, $allowC...
    method getPreviousRunDate (line 166) | public function getPreviousRunDate($currentTime = 'now', $nth = 0, $al...
    method getMultipleRunDates (line 182) | public function getMultipleRunDates($total, $currentTime = 'now', $inv...
    method getExpression (line 201) | public function getExpression($part = null)
    method __toString (line 217) | public function __toString()
    method isDue (line 231) | public function isDue($currentTime = 'now')
    method getRunDate (line 261) | protected function getRunDate($currentTime = null, $nth = 0, $invert =...

FILE: lib/cron-expression/CronExpression_AbstractField.php
  class CronExpression_AbstractField (line 8) | abstract class CronExpression_AbstractField implements CronExpression_Fi...
    method isSatisfied (line 18) | public function isSatisfied($dateValue, $value)
    method isRange (line 36) | public function isRange($value)
    method isIncrementsOfRanges (line 48) | public function isIncrementsOfRanges($value)
    method isInRange (line 61) | public function isInRange($dateValue, $value)
    method isInIncrementsOfRanges (line 76) | public function isInIncrementsOfRanges($dateValue, $value)

FILE: lib/cron-expression/CronExpression_DayOfMonthField.php
  class CronExpression_DayOfMonthField (line 21) | class CronExpression_DayOfMonthField extends CronExpression_AbstractField
    method getNearestWeekday (line 32) | private static function getNearestWeekday($currentYear, $currentMonth,...
    method isSatisfiedBy (line 58) | public function isSatisfiedBy(DateTime $date, $value)
    method increment (line 90) | public function increment(DateTime $date, $invert = false)
    method validate (line 106) | public function validate($value)

FILE: lib/cron-expression/CronExpression_DayOfWeekField.php
  class CronExpression_DayOfWeekField (line 18) | class CronExpression_DayOfWeekField extends CronExpression_AbstractField
    method isSatisfiedBy (line 23) | public function isSatisfiedBy(DateTime $date, $value)
    method increment (line 104) | public function increment(DateTime $date, $invert = false)
    method validate (line 120) | public function validate($value)

FILE: lib/cron-expression/CronExpression_FieldFactory.php
  class CronExpression_FieldFactory (line 9) | class CronExpression_FieldFactory
    method getField (line 24) | public function getField($position)

FILE: lib/cron-expression/CronExpression_FieldInterface.php
  type CronExpression_FieldInterface (line 8) | interface CronExpression_FieldInterface
    method isSatisfiedBy (line 18) | public function isSatisfiedBy(DateTime $date, $value);
    method increment (line 29) | public function increment(DateTime $date, $invert = false);
    method validate (line 38) | public function validate($value);

FILE: lib/cron-expression/CronExpression_HoursField.php
  class CronExpression_HoursField (line 8) | class CronExpression_HoursField extends CronExpression_AbstractField
    method isSatisfiedBy (line 13) | public function isSatisfiedBy(DateTime $date, $value)
    method increment (line 21) | public function increment(DateTime $date, $invert = false)
    method validate (line 43) | public function validate($value)

FILE: lib/cron-expression/CronExpression_MinutesField.php
  class CronExpression_MinutesField (line 8) | class CronExpression_MinutesField extends CronExpression_AbstractField
    method isSatisfiedBy (line 13) | public function isSatisfiedBy(DateTime $date, $value)
    method increment (line 21) | public function increment(DateTime $date, $invert = false)
    method validate (line 35) | public function validate($value)

FILE: lib/cron-expression/CronExpression_MonthField.php
  class CronExpression_MonthField (line 8) | class CronExpression_MonthField extends CronExpression_AbstractField
    method isSatisfiedBy (line 13) | public function isSatisfiedBy(DateTime $date, $value)
    method increment (line 31) | public function increment(DateTime $date, $invert = false)
    method validate (line 51) | public function validate($value)

FILE: lib/cron-expression/CronExpression_YearField.php
  class CronExpression_YearField (line 8) | class CronExpression_YearField extends CronExpression_AbstractField
    method isSatisfiedBy (line 13) | public function isSatisfiedBy(DateTime $date, $value)
    method increment (line 21) | public function increment(DateTime $date, $invert = false)
    method validate (line 39) | public function validate($value)

FILE: tests/ActionScheduler_UnitTestCase.php
  class ActionScheduler_UnitTestCase (line 7) | class ActionScheduler_UnitTestCase extends WP_UnitTestCase {
    method set_up (line 19) | public function set_up() {
    method tear_down (line 27) | public function tear_down() {
    method count (line 37) | public function count(): int {
    method run (line 47) | public function run( ?PHPUnit\Framework\TestResult $result = null ): \...

FILE: tests/phpunit/ActionScheduler_Mock_Async_Request_QueueRunner.php
  class ActionScheduler_Mock_AsyncRequest_QueueRunner (line 8) | class ActionScheduler_Mock_AsyncRequest_QueueRunner extends ActionSchedu...
    method allow (line 13) | protected function allow() {

FILE: tests/phpunit/ActionScheduler_Mocker.php
  class ActionScheduler_Mocker (line 8) | class ActionScheduler_Mocker {
    method get_queue_runner (line 15) | public static function get_queue_runner( ?ActionScheduler_Store $store...
    method get_async_request_queue_runner (line 29) | protected static function get_async_request_queue_runner( ActionSchedu...

FILE: tests/phpunit/ActionScheduler_RecurringActionScheduler_Test.php
  class ActionScheduler_RecurringActionScheduler_Test (line 6) | class ActionScheduler_RecurringActionScheduler_Test extends ActionSchedu...
    method tear_down (line 8) | public function tear_down() {
    method test_init_hooks_into_action_scheduler_init (line 18) | public function test_init_hooks_into_action_scheduler_init() {
    method test_schedule_recurring_scheduler_hook_schedules_action (line 53) | public function test_schedule_recurring_scheduler_hook_schedules_actio...
    method test_schedule_recurring_scheduler_hook__respects_cache (line 72) | public function test_schedule_recurring_scheduler_hook__respects_cache...

FILE: tests/phpunit/deprecated/ActionScheduler_UnitTestCase.php
  class ActionScheduler_UnitTestCase (line 8) | class ActionScheduler_UnitTestCase extends WP_UnitTestCase {
    method set_up (line 20) | public function set_up() {
    method tear_down (line 28) | public function tear_down() {
    method count (line 38) | public function count() {
    method run (line 48) | public function run( ?PHPUnit_Framework_TestResult $result = null ) {

FILE: tests/phpunit/helpers/ActionScheduler_Callbacks.php
  class ActionScheduler_Callbacks (line 6) | class ActionScheduler_Callbacks {
    method add_callbacks (line 16) | public static function add_callbacks() {
    method remove_callbacks (line 23) | public static function remove_callbacks() {
    method empty_callback (line 33) | public static function empty_callback() {}

FILE: tests/phpunit/helpers/ActionScheduler_Compatibility_Test.php
  class ActionScheduler_Compatibility_Test (line 7) | class ActionScheduler_Compatibility_Test extends ActionScheduler_UnitTes...
    method test_raise_time_limit (line 11) | public function test_raise_time_limit() {

FILE: tests/phpunit/helpers/ActionScheduler_TimezoneHelper_Test.php
  class ActionScheduler_TimezoneHelper_Test (line 6) | class ActionScheduler_TimezoneHelper_Test extends ActionScheduler_UnitTe...
    method test_local_timezone_strings (line 15) | public function test_local_timezone_strings( $timezone_string ) {
    method local_timezone_provider (line 35) | public function local_timezone_provider() {
    method test_local_timezone_offsets (line 50) | public function test_local_timezone_offsets( $gmt_offset ) {
    method local_timezone_offsets_provider (line 72) | public function local_timezone_offsets_provider() {

FILE: tests/phpunit/jobs/ActionScheduler_Action_Test.php
  class ActionScheduler_Action_Test (line 7) | class ActionScheduler_Action_Test extends ActionScheduler_UnitTestCase {
    method test_set_schedule (line 8) | public function test_set_schedule() {
    method test_null_schedule (line 15) | public function test_null_schedule() {
    method test_set_hook (line 20) | public function test_set_hook() {
    method test_args (line 25) | public function test_args() {
    method test_set_group (line 33) | public function test_set_group() {
    method test_execute (line 38) | public function test_execute() {

FILE: tests/phpunit/jobs/ActionScheduler_NullAction_Test.php
  class ActionScheduler_NullAction_Test (line 7) | class ActionScheduler_NullAction_Test extends ActionScheduler_UnitTestCa...
    method test_null_action (line 8) | public function test_null_action() {

FILE: tests/phpunit/jobstore/AbstractStoreTest.php
  class AbstractStoreTest (line 20) | abstract class AbstractStoreTest extends ActionScheduler_UnitTestCase {
    method get_store (line 27) | abstract protected function get_store();
    method test_get_status (line 29) | public function test_get_status() {
    method test_query_actions_query_type_arg_invalid_option (line 48) | public function test_query_actions_query_type_arg_invalid_option() {
    method test_query_actions_query_type_arg_valid_options (line 53) | public function test_query_actions_query_type_arg_valid_options() {
    method test_query_actions_by_single_status (line 64) | public function test_query_actions_by_single_status() {
    method test_query_actions_by_array_status (line 79) | public function test_query_actions_by_array_status() {
    method test_has_pending_actions_due (line 127) | public function test_has_pending_actions_due() {
    method test_has_pending_actions_due_only_future_actions (line 150) | public function test_has_pending_actions_due_only_future_actions() {

FILE: tests/phpunit/jobstore/ActionScheduler_DBStoreMigrator_Test.php
  class ActionScheduler_DBStoreMigrator_Test (line 7) | class ActionScheduler_DBStoreMigrator_Test extends ActionScheduler_UnitT...
    method test_create_action_with_last_attempt_date (line 9) | public function test_create_action_with_last_attempt_date() {

FILE: tests/phpunit/jobstore/ActionScheduler_DBStore_Test.php
  class ActionScheduler_DBStore_Test (line 9) | class ActionScheduler_DBStore_Test extends AbstractStoreTest {
    method setUp (line 20) | public function setUp(): void {
    method tearDown (line 34) | public function tearDown(): void {
    method get_store (line 48) | protected function get_store() {
    method test_create_action (line 52) | public function test_create_action() {
    method test_create_action_with_scheduled_date (line 62) | public function test_create_action_with_scheduled_date() {
    method test_retrieve_action (line 72) | public function test_retrieve_action() {
    method test_cancel_action (line 86) | public function test_cancel_action() {
    method test_cancel_actions_by_hook (line 98) | public function test_cancel_actions_by_hook() {
    method test_cancel_actions_by_group (line 117) | public function test_cancel_actions_by_group() {
    method test_claim_actions (line 136) | public function test_claim_actions() {
    method test_claim_actions_order (line 154) | public function test_claim_actions_order() {
    method test_claim_actions_by_hooks (line 179) | public function test_claim_actions_by_hooks() {
    method test_claim_actions_by_group (line 223) | public function test_claim_actions_by_group() {
    method test_claim_actions_with_group_exclusions (line 259) | public function test_claim_actions_with_group_exclusions() {
    method test_claim_actions_by_hook_and_group (line 306) | public function test_claim_actions_by_hook_and_group() {
    method test_claim_actions_respecting_priority (line 391) | public function test_claim_actions_respecting_priority() {
    method test_claim_filters_out_unexpected_future_actions (line 428) | public function test_claim_filters_out_unexpected_future_actions() {
    method test_duplicate_claim (line 466) | public function test_duplicate_claim() {
    method test_release_claim (line 483) | public function test_release_claim() {
    method test_search (line 506) | public function test_search() {
    method test_search_by_group (line 527) | public function test_search_by_group() {
    method test_get_run_date (line 540) | public function test_get_run_date() {
    method test_create_action_unique (line 565) | public function test_create_action_unique() {
    method test_create_action_unique_with_different_groups (line 585) | public function test_create_action_unique_with_different_groups() {
    method test_create_action_unique_and_then_non_unique (line 611) | public function test_create_action_unique_and_then_non_unique() {
    method test_create_action_unique_with_empty_array (line 635) | public function test_create_action_unique_with_empty_array() {
    method test_create_action_unique_with_different_args (line 656) | public function test_create_action_unique_with_different_args() {
    method test_actions_are_processed_in_correct_order (line 679) | public function test_actions_are_processed_in_correct_order() {
    method test_child_actions_are_processed_in_correct_order (line 722) | public function test_child_actions_are_processed_in_correct_order() {
    method test_db_supports_skip_locked (line 756) | public function test_db_supports_skip_locked( bool $expected_result, s...
    method db_supports_skip_locked_provider (line 781) | public static function db_supports_skip_locked_provider(): array {
    method test_get_claim_count_returns_zero_when_no_claims (line 820) | public function test_get_claim_count_returns_zero_when_no_claims() {
    method test_get_claim_count_with_pending_actions (line 828) | public function test_get_claim_count_with_pending_actions() {
    method test_get_claim_count_with_multiple_claims (line 844) | public function test_get_claim_count_with_multiple_claims() {
    method test_get_claim_count_excludes_released_claims (line 863) | public function test_get_claim_count_excludes_released_claims() {
    method test_get_claim_count_excludes_completed_actions (line 881) | public function test_get_claim_count_excludes_completed_actions() {
    method test_get_claim_count_includes_in_progress_actions (line 902) | public function test_get_claim_count_includes_in_progress_actions() {
    method test_get_claim_count_returns_integer (line 926) | public function test_get_claim_count_returns_integer() {

FILE: tests/phpunit/jobstore/ActionScheduler_HybridStore_Test.php
  class ActionScheduler_HybridStore_Test (line 13) | class ActionScheduler_HybridStore_Test extends ActionScheduler_UnitTestC...
    method setUp (line 17) | public function setUp(): void {
    method tearDown (line 29) | public function tearDown(): void {
    method test_actions_are_migrated_on_find (line 40) | public function test_actions_are_migrated_on_find() {
    method test_actions_are_migrated_on_query (line 68) | public function test_actions_are_migrated_on_query() {
    method test_actions_are_migrated_on_claim (line 131) | public function test_actions_are_migrated_on_claim() {
    method test_fetch_respects_demarkation (line 186) | public function test_fetch_respects_demarkation() {
    method test_mark_complete_respects_demarkation (line 232) | public function test_mark_complete_respects_demarkation() {

FILE: tests/phpunit/jobstore/ActionScheduler_wpPostStore_Test.php
  class ActionScheduler_wpPostStore_Test (line 9) | class ActionScheduler_wpPostStore_Test extends AbstractStoreTest {
    method get_store (line 16) | protected function get_store() {
    method test_create_action (line 20) | public function test_create_action() {
    method test_create_action_with_scheduled_date (line 30) | public function test_create_action_with_scheduled_date() {
    method test_retrieve_action (line 41) | public function test_retrieve_action() {
    method test_action_bad_args (line 60) | public function test_action_bad_args( $content ) {
    method provide_bad_args (line 74) | public function provide_bad_args() {
    method test_cancel_action (line 80) | public function test_cancel_action() {
    method test_cancel_actions_by_hook (line 92) | public function test_cancel_actions_by_hook() {
    method test_cancel_actions_by_group (line 111) | public function test_cancel_actions_by_group() {
    method test_claim_actions (line 131) | public function test_claim_actions() {
    method test_claim_actions_order (line 150) | public function test_claim_actions_order() {
    method test_duplicate_claim (line 174) | public function test_duplicate_claim() {
    method test_release_claim (line 192) | public function test_release_claim() {
    method test_search (line 212) | public function test_search() {
    method test_search_by_group (line 234) | public function test_search_by_group() {
    method test_post_author (line 247) | public function test_post_author() {
    method test_post_status_for_recurring_action (line 280) | public function test_post_status_for_recurring_action() {
    method test_get_run_date (line 298) | public function test_get_run_date() {
    method test_claim_actions_by_hooks (line 320) | public function test_claim_actions_by_hooks() {
    method test_claim_actions_by_group (line 360) | public function test_claim_actions_by_group() {
    method test_claim_actions_by_hook_and_group (line 382) | public function test_claim_actions_by_hook_and_group() {
    method test_claim_filters_out_unexpected_future_actions (line 433) | public function test_claim_filters_out_unexpected_future_actions() {

FILE: tests/phpunit/lock/ActionScheduler_OptionLock_Test.php
  class ActionScheduler_OptionLock_Test (line 7) | class ActionScheduler_OptionLock_Test extends ActionScheduler_UnitTestCa...
    method test_instance (line 8) | public function test_instance() {
    method test_is_locked (line 14) | public function test_is_locked() {
    method test_set (line 24) | public function test_set() {
    method test_get_expiration (line 32) | public function test_get_expiration() {
    method test_lock_resists_race_conditions (line 51) | public function test_lock_resists_race_conditions() {

FILE: tests/phpunit/logging/ActionScheduler_DBLogger_Test.php
  class ActionScheduler_DBLogger_Test (line 8) | class ActionScheduler_DBLogger_Test extends ActionScheduler_UnitTestCase {
    method test_default_logger (line 9) | public function test_default_logger() {
    method test_add_log_entry (line 15) | public function test_add_log_entry() {
    method test_storage_logs (line 26) | public function test_storage_logs() {
    method test_execution_logs (line 36) | public function test_execution_logs() {
    method test_failed_execution_logs (line 58) | public function test_failed_execution_logs() {
    method test_fatal_error_log (line 84) | public function test_fatal_error_log() {
    method test_canceled_action_log (line 106) | public function test_canceled_action_log() {
    method test_deleted_action_cleanup (line 115) | public function test_deleted_action_cleanup() {
    method a_hook_callback_that_throws_an_exception (line 131) | public function a_hook_callback_that_throws_an_exception() {

FILE: tests/phpunit/logging/ActionScheduler_wpCommentLogger_Test.php
  class ActionScheduler_wpCommentLogger_Test (line 7) | class ActionScheduler_wpCommentLogger_Test extends ActionScheduler_UnitT...
    method test_default_logger (line 12) | public function test_default_logger() {
    method test_add_log_entry (line 22) | public function test_add_log_entry() {
    method test_add_log_datetime (line 33) | public function test_add_log_datetime() {
    method test_erroneous_entry_id (line 52) | public function test_erroneous_entry_id() {
    method test_storage_comments (line 68) | public function test_storage_comments() {
    method log_entry_to_array (line 78) | protected function log_entry_to_array( $logs ) {
    method test_execution_comments (line 96) | public function test_execution_comments() {
    method test_failed_execution_comments (line 113) | public function test_failed_execution_comments() {
    method test_failed_schedule_next_instance_comments (line 135) | public function test_failed_schedule_next_instance_comments() {
    method test_fatal_error_comments (line 152) | public function test_fatal_error_comments() {
    method test_canceled_action_comments (line 175) | public function test_canceled_action_comments() {
    method a_hook_callback_that_throws_an_exception (line 187) | public function a_hook_callback_that_throws_an_exception() {
    method test_filtering_of_get_comments (line 191) | public function test_filtering_of_get_comments() {
    method using_comment_logger (line 233) | private function using_comment_logger() {

FILE: tests/phpunit/migration/ActionMigrator_Test.php
  class ActionMigrator_Test (line 10) | class ActionMigrator_Test extends ActionScheduler_UnitTestCase {
    method setUp (line 11) | public function setUp(): void {
    method test_migrate_from_wpPost_to_db (line 20) | public function test_migrate_from_wpPost_to_db() {
    method test_does_not_migrate_missing_action_from_wpPost_to_db (line 45) | public function test_does_not_migrate_missing_action_from_wpPost_to_db...
    method test_migrate_completed_action_from_wpPost_to_db (line 60) | public function test_migrate_completed_action_from_wpPost_to_db() {
    method test_migrate_failed_action_from_wpPost_to_db (line 87) | public function test_migrate_failed_action_from_wpPost_to_db() {
    method test_migrate_canceled_action_from_wpPost_to_db (line 114) | public function test_migrate_canceled_action_from_wpPost_to_db() {
    method get_log_migrator (line 141) | private function get_log_migrator() {

FILE: tests/phpunit/migration/BatchFetcher_Test.php
  class BatchFetcher_Test (line 10) | class BatchFetcher_Test extends ActionScheduler_UnitTestCase {
    method setUp (line 11) | public function setUp(): void {
    method test_nothing_to_migrate (line 20) | public function test_nothing_to_migrate() {
    method test_get_due_before_future (line 28) | public function test_get_due_before_future() {
    method test_get_future_before_complete (line 52) | public function test_get_future_before_complete() {

FILE: tests/phpunit/migration/Config_Test.php
  class Config_Test (line 9) | class Config_Test extends ActionScheduler_UnitTestCase {
    method test_source_store_required (line 10) | public function test_source_store_required() {
    method test_source_logger_required (line 16) | public function test_source_logger_required() {
    method test_destination_store_required (line 22) | public function test_destination_store_required() {
    method test_destination_logger_required (line 28) | public function test_destination_logger_required() {

FILE: tests/phpunit/migration/Controller_Test.php
  class Controller_Test (line 17) | class Controller_Test extends ActionScheduler_UnitTestCase {
    method test_schedules_migration (line 21) | public function test_schedules_migration() {
    method test_migration_not_scheduled_if_tables_are_missing (line 39) | public function test_migration_not_scheduled_if_tables_are_missing() {
    method rename_claims_table (line 55) | private function rename_claims_table() {
    method restore_claims_table_name (line 68) | private function restore_claims_table_name() {

FILE: tests/phpunit/migration/LogMigrator_Test.php
  class LogMigrator_Test (line 9) | class LogMigrator_Test extends ActionScheduler_UnitTestCase {
    method setUp (line 10) | public function setUp(): void {
    method test_migrate_from_wpComment_to_db (line 19) | public function test_migrate_from_wpComment_to_db() {

FILE: tests/phpunit/migration/Runner_Test.php
  class Runner_Test (line 13) | class Runner_Test extends ActionScheduler_UnitTestCase {
    method setUp (line 14) | public function setUp(): void {
    method test_migrate_batches (line 23) | public function test_migrate_batches() {

FILE: tests/phpunit/migration/Scheduler_Test.php
  class Scheduler_Test (line 10) | class Scheduler_Test extends ActionScheduler_UnitTestCase {
    method setUp (line 11) | public function setUp(): void {
    method test_migration_is_complete (line 20) | public function test_migration_is_complete() {
    method test_migration_is_not_complete (line 25) | public function test_migration_is_not_complete() {
    method test_migration_is_scheduled (line 31) | public function test_migration_is_scheduled() {
    method test_scheduler_runs_migration (line 48) | public function test_scheduler_runs_migration() {
    method test_scheduler_marks_itself_complete (line 95) | public function test_scheduler_marks_itself_complete() {

FILE: tests/phpunit/procedural_api/procedural_api_Test.php
  class Procedural_API_Test (line 6) | class Procedural_API_Test extends ActionScheduler_UnitTestCase {
    method test_schedule_action (line 9) | public function test_schedule_action() {
    method test_recurring_action (line 20) | public function test_recurring_action() {
    method test_recurring_actions_reject_invalid_intervals (line 38) | public function test_recurring_actions_reject_invalid_intervals() {
    method test_cron_schedule (line 72) | public function test_cron_schedule() {
    method test_get_next (line 87) | public function test_get_next() {
    method test_get_next_async (line 97) | public function test_get_next_async() {
    method provider_time_hook_args_group (line 123) | public function provider_time_hook_args_group() {
    method test_unschedule (line 168) | public function test_unschedule( $time, $hook, $args, $group ) {
    method test_unschedule_all (line 199) | public function test_unschedule_all( $time, $hook, $args, $group ) {
    method test_as_get_datetime_object_default (line 228) | public function test_as_get_datetime_object_default() {
    method test_as_get_datetime_object_relative (line 237) | public function test_as_get_datetime_object_relative() {
    method test_as_get_datetime_object_fixed (line 250) | public function test_as_get_datetime_object_fixed() {
    method test_as_get_datetime_object_timezone (line 263) | public function test_as_get_datetime_object_timezone() {
    method test_as_get_datetime_object_type (line 292) | public function test_as_get_datetime_object_type() {
    method test_as_has_scheduled_action (line 302) | public function test_as_has_scheduled_action() {
    method test_as_has_scheduled_action_with_args (line 324) | public function test_as_has_scheduled_action_with_args() {
    method test_as_has_scheduled_action_with_group (line 334) | public function test_as_has_scheduled_action_with_group() {
    method test_as_enqueue_async_action_unique (line 345) | public function test_as_enqueue_async_action_unique() {
    method test_as_enqueue_async_action_unique_hybrid_best_effort (line 359) | public function test_as_enqueue_async_action_unique_hybrid_best_effort...
    method test_as_schedule_single_action_unique (line 372) | public function test_as_schedule_single_action_unique() {
    method test_as_schedule_recurring_action_unique (line 385) | public function test_as_schedule_recurring_action_unique() {
    method test_as_schedule_cron_action (line 398) | public function test_as_schedule_cron_action() {
    method test_as_recover_from_incorrect_schema (line 411) | public function test_as_recover_from_incorrect_schema() {
    method test_as_supports_for_supported_feature (line 464) | public function test_as_supports_for_supported_feature() {
    method test_as_supports_for_unsupported_feature (line 471) | public function test_as_supports_for_unsupported_feature() {
    method set_action_scheduler_store (line 480) | private function set_action_scheduler_store( $store ) {
    method assertValidAction (line 493) | private function assertValidAction( $action_id ) {

FILE: tests/phpunit/procedural_api/wc_get_scheduled_actions_Test.php
  class as_get_scheduled_actions_Test (line 6) | class as_get_scheduled_actions_Test extends ActionScheduler_UnitTestCase {
    method setUp (line 16) | public function setUp(): void {
    method test_date_queries (line 37) | public function test_date_queries() {
    method test_hook_queries (line 58) | public function test_hook_queries() {
    method test_args_queries (line 79) | public function test_args_queries() {
    method test_group_queries (line 111) | public function test_group_queries() {

FILE: tests/phpunit/runner/ActionScheduler_QueueCleaner_Test.php
  class ActionScheduler_QueueCleaner_Test (line 6) | class ActionScheduler_QueueCleaner_Test extends ActionScheduler_UnitTest...
    method test_delete_old_actions (line 8) | public function test_delete_old_actions() {
    method test_invalid_retention_period_filter_hook (line 38) | public function test_invalid_retention_period_filter_hook() {
    method test_delete_canceled_actions (line 57) | public function test_delete_canceled_actions() {
    method test_do_not_delete_recent_actions (line 90) | public function test_do_not_delete_recent_actions() {
    method test_reset_unrun_actions (line 113) | public function test_reset_unrun_actions() {
    method test_do_not_reset_failed_action (line 139) | public function test_do_not_reset_failed_action() {
    method test_delete_old_failed_actions_separately_by_default (line 179) | public function test_delete_old_failed_actions_separately_by_default() {
    method test_delete_old_failed_actions_with_other_statuses (line 203) | public function test_delete_old_failed_actions_with_other_statuses() {

FILE: tests/phpunit/runner/ActionScheduler_QueueRunner_Test.php
  class ActionScheduler_QueueRunner_Test (line 7) | class ActionScheduler_QueueRunner_Test extends ActionScheduler_UnitTestC...
    method test_create_runner (line 8) | public function test_create_runner() {
    method test_run (line 16) | public function test_run() {
    method test_run_with_future_actions (line 38) | public function test_run_with_future_actions() {
    method test_run_with_action_that_is_already_in_progress (line 73) | public function test_run_with_action_that_is_already_in_progress() {
    method test_completed_action_status (line 119) | public function test_completed_action_status() {
    method test_next_instance_of_cron_action (line 134) | public function test_next_instance_of_cron_action() {
    method test_next_instance_of_interval_action (line 179) | public function test_next_instance_of_interval_action() {
    method test_failing_recurring_actions_are_not_rescheduled_when_threshold_met (line 252) | public function test_failing_recurring_actions_are_not_rescheduled_whe...
    method test_corrupted_actions_are_unscheduled (line 313) | public function test_corrupted_actions_are_unscheduled() {
    method test_exceptions_can_be_made_for_failing_recurring_actions (line 353) | public function test_exceptions_can_be_made_for_failing_recurring_acti...
    method test_hooked_into_wp_cron (line 405) | public function test_hooked_into_wp_cron() {
    method test_batch_count_limit (line 410) | public function test_batch_count_limit() {
    method test_changing_batch_count_limit (line 441) | public function test_changing_batch_count_limit() {
    method return_6 (line 494) | public function return_6() {
    method test_store_fetch_action_failure_schedule_next_instance (line 498) | public function test_store_fetch_action_failure_schedule_next_instance...
    method test_order_in_which_actions_are_processed (line 557) | public function test_order_in_which_actions_are_processed() {
    method test_recoverable_errors_do_not_break_queue_runner (line 588) | public function test_recoverable_errors_do_not_break_queue_runner() {

FILE: tests/phpunit/schedules/ActionScheduler_CronSchedule_Test.php
  class ActionScheduler_CronSchedule_Test (line 7) | class ActionScheduler_CronSchedule_Test extends ActionScheduler_UnitTest...
    method test_creation (line 8) | public function test_creation() {
    method test_creation_with_first_date (line 26) | public function test_creation_with_first_date() {
    method test_next (line 46) | public function test_next() {
    method test_is_recurring (line 53) | public function test_is_recurring() {
    method test_cron_format (line 58) | public function test_cron_format() {

FILE: tests/phpunit/schedules/ActionScheduler_IntervalSchedule_Test.php
  class ActionScheduler_IntervalSchedule_Test (line 7) | class ActionScheduler_IntervalSchedule_Test extends ActionScheduler_Unit...
    method test_creation (line 8) | public function test_creation() {
    method test_creation_with_first_date (line 15) | public function test_creation_with_first_date() {
    method test_next (line 23) | public function test_next() {
    method test_is_recurring (line 32) | public function test_is_recurring() {

FILE: tests/phpunit/schedules/ActionScheduler_NullSchedule_Test.php
  class ActionScheduler_NullSchedule_Test (line 7) | class ActionScheduler_NullSchedule_Test extends ActionScheduler_UnitTest...
    method test_null_schedule (line 8) | public function test_null_schedule() {
    method test_is_recurring (line 13) | public function test_is_recurring() {

FILE: tests/phpunit/schedules/ActionScheduler_SimpleSchedule_Test.php
  class ActionScheduler_SimpleSchedule_Test (line 7) | class ActionScheduler_SimpleSchedule_Test extends ActionScheduler_UnitTe...
    method test_creation (line 8) | public function test_creation() {
    method test_past_date (line 14) | public function test_past_date() {
    method test_future_date (line 20) | public function test_future_date() {
    method test_grace_period_for_next (line 26) | public function test_grace_period_for_next() {
    method test_is_recurring (line 32) | public function test_is_recurring() {

FILE: tests/phpunit/versioning/ActionScheduler_Versions_Test.php
  class ActionScheduler_Versions_Test (line 6) | class ActionScheduler_Versions_Test extends ActionScheduler_UnitTestCase {
    method test_register_version (line 7) | public function test_register_version() {
    method test_duplicate_version (line 21) | public function test_duplicate_version() {
    method test_latest_version (line 32) | public function test_latest_version() {
Condensed preview — 164 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (909K chars).
[
  {
    "path": ".editorconfig",
    "chars": 477,
    "preview": "# This file is for unifying the coding style for different editors and IDEs\n# editorconfig.org\n\n# WordPress Coding Stand"
  },
  {
    "path": ".gitattributes",
    "chars": 354,
    "preview": "docs export-ignore\ntests export-ignore\ncodecov.yml export-ignore\n.editorconfig export-ignore\n.github export-ignore\n.trav"
  },
  {
    "path": ".github/release-drafter.yml",
    "chars": 258,
    "preview": "template: |\n  ## next release &ndash; date\n\n  <!-- Move the individual changes below into the appropriate section -->\n\n "
  },
  {
    "path": ".github/workflows/pr-unit-tests.yml",
    "chars": 2773,
    "preview": "name: Run unit tests on PR\non: \n  pull_request\njobs:\n  test:\n    name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} MU ${{ "
  },
  {
    "path": ".gitignore",
    "chars": 300,
    "preview": "# Operating System files\n.DS_Store\nThumbs.db\n\n# IDE files\n.idea\n.vscode/*\nproject.xml\nproject.properties\n.project\n.setti"
  },
  {
    "path": "Gruntfile.js",
    "chars": 1025,
    "preview": "module.exports = function( grunt ) {\n\t'use strict';\n\n\tgrunt.initConfig({\n\t\t// Check textdomain errors.\n\t\tchecktextdomain"
  },
  {
    "path": "README.md",
    "chars": 2799,
    "preview": "# Action Scheduler - Job Queue for WordPress [![Build Status](https://travis-ci.org/woocommerce/action-scheduler.png?bra"
  },
  {
    "path": "action-scheduler.php",
    "chars": 3021,
    "preview": "<?php\n/**\n * Plugin Name: Action Scheduler\n * Plugin URI: https://actionscheduler.org\n * Description: A robust schedulin"
  },
  {
    "path": "changelog.txt",
    "chars": 9544,
    "preview": "*** Changelog ***\n\n= 3.9.3 - 2025-07-15 =\n* Add hook 'action_scheduler_ensure_recurring_actions' specifically for schedu"
  },
  {
    "path": "classes/ActionScheduler_ActionClaim.php",
    "chars": 652,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_ActionClaim\n */\nclass ActionScheduler_ActionClaim {\n\t/**\n\t * Claim ID.\n\t *\n\t * @var "
  },
  {
    "path": "classes/ActionScheduler_ActionFactory.php",
    "chars": 16303,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_ActionFactory\n */\nclass ActionScheduler_ActionFactory {\n\n\t/**\n\t * Return stored acti"
  },
  {
    "path": "classes/ActionScheduler_AdminView.php",
    "chars": 10627,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_AdminView\n *\n * @codeCoverageIgnore\n */\nclass ActionScheduler_AdminView extends Acti"
  },
  {
    "path": "classes/ActionScheduler_AsyncRequest_QueueRunner.php",
    "chars": 2163,
    "preview": "<?php\n\ndefined( 'ABSPATH' ) || exit;\n\n/**\n * ActionScheduler_AsyncRequest_QueueRunner class.\n */\nclass ActionScheduler_A"
  },
  {
    "path": "classes/ActionScheduler_Compatibility.php",
    "chars": 3904,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Compatibility\n */\nclass ActionScheduler_Compatibility {\n\t/**\n\t * Converts a shorthan"
  },
  {
    "path": "classes/ActionScheduler_DataController.php",
    "chars": 5769,
    "preview": "<?php\n\nuse Action_Scheduler\\Migration\\Controller;\n\n/**\n * Class ActionScheduler_DataController\n *\n * The main plugin/ini"
  },
  {
    "path": "classes/ActionScheduler_DateTime.php",
    "chars": 2060,
    "preview": "<?php\n\n/**\n * ActionScheduler DateTime class.\n *\n * This is a custom extension to DateTime that\n */\nclass ActionSchedule"
  },
  {
    "path": "classes/ActionScheduler_Exception.php",
    "chars": 207,
    "preview": "<?php\n\n/**\n * ActionScheduler Exception Interface.\n *\n * Facilitates catching Exceptions unique to Action Scheduler.\n *\n"
  },
  {
    "path": "classes/ActionScheduler_FatalErrorMonitor.php",
    "chars": 2565,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_FatalErrorMonitor\n */\nclass ActionScheduler_FatalErrorMonitor {\n\n\t/**\n\t * ActionSche"
  },
  {
    "path": "classes/ActionScheduler_InvalidActionException.php",
    "chars": 1487,
    "preview": "<?php\n\n/**\n * InvalidAction Exception.\n *\n * Used for identifying actions that are invalid in some way.\n *\n * @package A"
  },
  {
    "path": "classes/ActionScheduler_ListTable.php",
    "chars": 21622,
    "preview": "<?php\n\n/**\n * Implements the admin view of the actions.\n *\n * @codeCoverageIgnore\n */\nclass ActionScheduler_ListTable ex"
  },
  {
    "path": "classes/ActionScheduler_LogEntry.php",
    "chars": 1942,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_LogEntry\n */\nclass ActionScheduler_LogEntry {\n\n\t/**\n\t * Action's ID for log entry.\n\t"
  },
  {
    "path": "classes/ActionScheduler_NullLogEntry.php",
    "chars": 330,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_NullLogEntry\n */\nclass ActionScheduler_NullLogEntry extends ActionScheduler_LogEntry"
  },
  {
    "path": "classes/ActionScheduler_OptionLock.php",
    "chars": 4076,
    "preview": "<?php\n\n/**\n * Provide a way to set simple transient locks to block behaviour\n * for up-to a given duration.\n *\n * Class "
  },
  {
    "path": "classes/ActionScheduler_QueueCleaner.php",
    "chars": 10051,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_QueueCleaner\n */\nclass ActionScheduler_QueueCleaner {\n\n\t/**\n\t * The batch size.\n\t *\n"
  },
  {
    "path": "classes/ActionScheduler_QueueRunner.php",
    "chars": 9882,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_QueueRunner\n */\nclass ActionScheduler_QueueRunner extends ActionScheduler_Abstract_Q"
  },
  {
    "path": "classes/ActionScheduler_RecurringActionScheduler.php",
    "chars": 3248,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_RecurringActionScheduler\n *\n * This class ensures that the `action_scheduler_ensure_"
  },
  {
    "path": "classes/ActionScheduler_SystemInformation.php",
    "chars": 2497,
    "preview": "<?php\n\n/**\n * Provides information about active and registered instances of Action Scheduler.\n */\nclass ActionScheduler_"
  },
  {
    "path": "classes/ActionScheduler_Versions.php",
    "chars": 3690,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Versions\n */\nclass ActionScheduler_Versions {\n\t/**\n\t * ActionScheduler_Versions inst"
  },
  {
    "path": "classes/ActionScheduler_WPCommentCleaner.php",
    "chars": 4529,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_WPCommentCleaner\n *\n * @since 3.0.0\n */\nclass ActionScheduler_WPCommentCleaner {\n\n\t/"
  },
  {
    "path": "classes/ActionScheduler_wcSystemStatus.php",
    "chars": 5279,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_wcSystemStatus\n */\nclass ActionScheduler_wcSystemStatus {\n\n\t/**\n\t * The active data "
  },
  {
    "path": "classes/WP_CLI/Action/Cancel_Command.php",
    "chars": 3331,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI\\Action;\n\nuse function \\WP_CLI\\Utils\\get_flag_value;\n\n/**\n * WP-CLI command: act"
  },
  {
    "path": "classes/WP_CLI/Action/Create_Command.php",
    "chars": 4458,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI\\Action;\n\nuse function \\WP_CLI\\Utils\\get_flag_value;\n\n/**\n * WP-CLI command: act"
  },
  {
    "path": "classes/WP_CLI/Action/Delete_Command.php",
    "chars": 2661,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI\\Action;\n\n/**\n * WP-CLI command: action-scheduler action delete\n */\nclass Delete"
  },
  {
    "path": "classes/WP_CLI/Action/Generate_Command.php",
    "chars": 3551,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI\\Action;\n\nuse function \\WP_CLI\\Utils\\get_flag_value;\n\n/**\n * WP-CLI command: act"
  },
  {
    "path": "classes/WP_CLI/Action/Get_Command.php",
    "chars": 2208,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI\\Action;\n\n/**\n * WP-CLI command: action-scheduler action get\n */\nclass Get_Comma"
  },
  {
    "path": "classes/WP_CLI/Action/List_Command.php",
    "chars": 3127,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI\\Action;\n\n// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- E"
  },
  {
    "path": "classes/WP_CLI/Action/Next_Command.php",
    "chars": 1859,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI\\Action;\n\n// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- E"
  },
  {
    "path": "classes/WP_CLI/Action/Run_Command.php",
    "chars": 4721,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI\\Action;\n\n/**\n * WP-CLI command: action-scheduler action run\n */\nclass Run_Comma"
  },
  {
    "path": "classes/WP_CLI/ActionScheduler_WPCLI_Clean_Command.php",
    "chars": 3827,
    "preview": "<?php\n\n/**\n * Commands for Action Scheduler.\n */\nclass ActionScheduler_WPCLI_Clean_Command extends WP_CLI_Command {\n\t/**"
  },
  {
    "path": "classes/WP_CLI/ActionScheduler_WPCLI_QueueRunner.php",
    "chars": 6361,
    "preview": "<?php\n\nuse Action_Scheduler\\WP_CLI\\ProgressBar;\n\n/**\n * WP CLI Queue runner.\n *\n * This class can only be called from wi"
  },
  {
    "path": "classes/WP_CLI/ActionScheduler_WPCLI_Scheduler_command.php",
    "chars": 6855,
    "preview": "<?php\n\n/**\n * Commands for Action Scheduler.\n */\nclass ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {\n"
  },
  {
    "path": "classes/WP_CLI/Action_Command.php",
    "chars": 8353,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI;\n\nuse WP_CLI;\n\n/**\n * Action command for Action Scheduler.\n */\nclass Action_Com"
  },
  {
    "path": "classes/WP_CLI/Migration_Command.php",
    "chars": 5048,
    "preview": "<?php\n\n\nnamespace Action_Scheduler\\WP_CLI;\n\nuse Action_Scheduler\\Migration\\Config;\nuse Action_Scheduler\\Migration\\Runner"
  },
  {
    "path": "classes/WP_CLI/ProgressBar.php",
    "chars": 2766,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI;\n\n/**\n * WP_CLI progress bar for Action Scheduler.\n */\n\n/**\n * Class ProgressBa"
  },
  {
    "path": "classes/WP_CLI/System_Command.php",
    "chars": 7237,
    "preview": "<?php\n\nnamespace Action_Scheduler\\WP_CLI;\n\n// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping"
  },
  {
    "path": "classes/abstracts/ActionScheduler.php",
    "chars": 11405,
    "preview": "<?php\n\nuse Action_Scheduler\\WP_CLI\\Migration_Command;\nuse Action_Scheduler\\Migration\\Controller;\n\n/**\n * Class ActionSch"
  },
  {
    "path": "classes/abstracts/ActionScheduler_Abstract_ListTable.php",
    "chars": 25157,
    "preview": "<?php\n\nif ( ! class_exists( 'WP_List_Table' ) ) {\n\trequire_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';\n}"
  },
  {
    "path": "classes/abstracts/ActionScheduler_Abstract_QueueRunner.php",
    "chars": 15431,
    "preview": "<?php\n\n/**\n * Abstract class with common Queue Cleaner functionality.\n */\nabstract class ActionScheduler_Abstract_QueueR"
  },
  {
    "path": "classes/abstracts/ActionScheduler_Abstract_RecurringSchedule.php",
    "chars": 3302,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Abstract_RecurringSchedule\n */\nabstract class ActionScheduler_Abstract_RecurringSche"
  },
  {
    "path": "classes/abstracts/ActionScheduler_Abstract_Schedule.php",
    "chars": 1895,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Abstract_Schedule\n */\nabstract class ActionScheduler_Abstract_Schedule extends Actio"
  },
  {
    "path": "classes/abstracts/ActionScheduler_Abstract_Schema.php",
    "chars": 4899,
    "preview": "<?php\n\n\n/**\n * Class ActionScheduler_Abstract_Schema\n *\n * @package Action_Scheduler\n *\n * @codeCoverageIgnore\n *\n * Uti"
  },
  {
    "path": "classes/abstracts/ActionScheduler_Lock.php",
    "chars": 1823,
    "preview": "<?php\n\n/**\n * Abstract class for setting a basic lock to throttle some action.\n *\n * Class ActionScheduler_Lock\n */\nabst"
  },
  {
    "path": "classes/abstracts/ActionScheduler_Logger.php",
    "chars": 8021,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Logger\n *\n * @codeCoverageIgnore\n */\nabstract class ActionScheduler_Logger {\n\n\t/**\n\t"
  },
  {
    "path": "classes/abstracts/ActionScheduler_Store.php",
    "chars": 14393,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Store\n *\n * @codeCoverageIgnore\n */\nabstract class ActionScheduler_Store extends Act"
  },
  {
    "path": "classes/abstracts/ActionScheduler_TimezoneHelper.php",
    "chars": 4877,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_TimezoneHelper\n */\nabstract class ActionScheduler_TimezoneHelper {\n\n\t/**\n\t * DateTim"
  },
  {
    "path": "classes/abstracts/ActionScheduler_WPCLI_Command.php",
    "chars": 2107,
    "preview": "<?php\n\n/**\n * Abstract for WP-CLI commands.\n */\nabstract class ActionScheduler_WPCLI_Command extends \\WP_CLI_Command {\n\n"
  },
  {
    "path": "classes/actions/ActionScheduler_Action.php",
    "chars": 3912,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Action\n */\nclass ActionScheduler_Action {\n\t/**\n\t * Action's hook.\n\t *\n\t * @var strin"
  },
  {
    "path": "classes/actions/ActionScheduler_CanceledAction.php",
    "chars": 883,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_CanceledAction\n *\n * Stored action which was canceled and therefore acts like a fini"
  },
  {
    "path": "classes/actions/ActionScheduler_FinishedAction.php",
    "chars": 296,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_FinishedAction\n */\nclass ActionScheduler_FinishedAction extends ActionScheduler_Acti"
  },
  {
    "path": "classes/actions/ActionScheduler_NullAction.php",
    "chars": 601,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_NullAction\n */\nclass ActionScheduler_NullAction extends ActionScheduler_Action {\n\n\t/"
  },
  {
    "path": "classes/data-stores/ActionScheduler_DBLogger.php",
    "chars": 6032,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_DBLogger\n *\n * Action logs data table data store.\n *\n * @since 3.0.0\n */\nclass Actio"
  },
  {
    "path": "classes/data-stores/ActionScheduler_DBStore.php",
    "chars": 41736,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_DBStore\n *\n * Action data table data store.\n *\n * @since 3.0.0\n */\nclass ActionSched"
  },
  {
    "path": "classes/data-stores/ActionScheduler_HybridStore.php",
    "chars": 12949,
    "preview": "<?php\n\nuse ActionScheduler_Store as Store;\nuse Action_Scheduler\\Migration\\Runner;\nuse Action_Scheduler\\Migration\\Config;"
  },
  {
    "path": "classes/data-stores/ActionScheduler_wpCommentLogger.php",
    "chars": 7707,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_wpCommentLogger\n */\nclass ActionScheduler_wpCommentLogger extends ActionScheduler_Lo"
  },
  {
    "path": "classes/data-stores/ActionScheduler_wpPostStore.php",
    "chars": 36619,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_wpPostStore\n */\nclass ActionScheduler_wpPostStore extends ActionScheduler_Store {\n\tc"
  },
  {
    "path": "classes/data-stores/ActionScheduler_wpPostStore_PostStatusRegistrar.php",
    "chars": 1858,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_wpPostStore_PostStatusRegistrar\n *\n * @codeCoverageIgnore\n */\nclass ActionScheduler_"
  },
  {
    "path": "classes/data-stores/ActionScheduler_wpPostStore_PostTypeRegistrar.php",
    "chars": 1993,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_wpPostStore_PostTypeRegistrar\n *\n * @codeCoverageIgnore\n */\nclass ActionScheduler_wp"
  },
  {
    "path": "classes/data-stores/ActionScheduler_wpPostStore_TaxonomyRegistrar.php",
    "chars": 762,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_wpPostStore_TaxonomyRegistrar\n *\n * @codeCoverageIgnore\n */\nclass ActionScheduler_wp"
  },
  {
    "path": "classes/migration/ActionMigrator.php",
    "chars": 4267,
    "preview": "<?php\n\n\nnamespace Action_Scheduler\\Migration;\n\n/**\n * Class ActionMigrator\n *\n * @package Action_Scheduler\\Migration\n *\n"
  },
  {
    "path": "classes/migration/ActionScheduler_DBStoreMigrator.php",
    "chars": 1835,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_DBStoreMigrator\n *\n * A class for direct saving of actions to the table data store d"
  },
  {
    "path": "classes/migration/BatchFetcher.php",
    "chars": 1768,
    "preview": "<?php\n\nnamespace Action_Scheduler\\Migration;\n\nuse ActionScheduler_Store as Store;\n\n/**\n * Class BatchFetcher\n *\n * @pack"
  },
  {
    "path": "classes/migration/Config.php",
    "chars": 4206,
    "preview": "<?php\n\n\nnamespace Action_Scheduler\\Migration;\n\nuse Action_Scheduler\\WP_CLI\\ProgressBar;\nuse ActionScheduler_Logger as Lo"
  },
  {
    "path": "classes/migration/Controller.php",
    "chars": 6524,
    "preview": "<?php\n\nnamespace Action_Scheduler\\Migration;\n\nuse ActionScheduler_DataController;\nuse ActionScheduler_LoggerSchema;\nuse "
  },
  {
    "path": "classes/migration/DryRun_ActionMigrator.php",
    "chars": 554,
    "preview": "<?php\n\n\nnamespace Action_Scheduler\\Migration;\n\n/**\n * Class DryRun_ActionMigrator\n *\n * @package Action_Scheduler\\Migrat"
  },
  {
    "path": "classes/migration/DryRun_LogMigrator.php",
    "chars": 459,
    "preview": "<?php\n\n\nnamespace Action_Scheduler\\Migration;\n\n/**\n * Class DryRun_LogMigrator\n *\n * @package Action_Scheduler\\Migration"
  },
  {
    "path": "classes/migration/LogMigrator.php",
    "chars": 1313,
    "preview": "<?php\n\n\nnamespace Action_Scheduler\\Migration;\n\nuse ActionScheduler_Logger;\n\n/**\n * Class LogMigrator\n *\n * @package Acti"
  },
  {
    "path": "classes/migration/Runner.php",
    "chars": 4220,
    "preview": "<?php\n\n\nnamespace Action_Scheduler\\Migration;\n\n/**\n * Class Runner\n *\n * @package Action_Scheduler\\Migration\n *\n * @sinc"
  },
  {
    "path": "classes/migration/Scheduler.php",
    "chars": 3112,
    "preview": "<?php\n\n\nnamespace Action_Scheduler\\Migration;\n\n/**\n * Class Scheduler\n *\n * @package Action_Scheduler\\WP_CLI\n *\n * @sinc"
  },
  {
    "path": "classes/schedules/ActionScheduler_CanceledSchedule.php",
    "chars": 1647,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_SimpleSchedule\n */\nclass ActionScheduler_CanceledSchedule extends ActionScheduler_Si"
  },
  {
    "path": "classes/schedules/ActionScheduler_CronSchedule.php",
    "chars": 3831,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_CronSchedule\n */\nclass ActionScheduler_CronSchedule extends ActionScheduler_Abstract"
  },
  {
    "path": "classes/schedules/ActionScheduler_IntervalSchedule.php",
    "chars": 2633,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_IntervalSchedule\n */\nclass ActionScheduler_IntervalSchedule extends ActionScheduler_"
  },
  {
    "path": "classes/schedules/ActionScheduler_NullSchedule.php",
    "chars": 709,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_NullSchedule\n */\nclass ActionScheduler_NullSchedule extends ActionScheduler_SimpleSc"
  },
  {
    "path": "classes/schedules/ActionScheduler_Schedule.php",
    "chars": 456,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Schedule\n */\ninterface ActionScheduler_Schedule {\n\t/**\n\t * Get the date & time this "
  },
  {
    "path": "classes/schedules/ActionScheduler_SimpleSchedule.php",
    "chars": 2367,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_SimpleSchedule\n */\nclass ActionScheduler_SimpleSchedule extends ActionScheduler_Abst"
  },
  {
    "path": "classes/schema/ActionScheduler_LoggerSchema.php",
    "chars": 3002,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_LoggerSchema\n *\n * @codeCoverageIgnore\n *\n * Creates a custom table for storing acti"
  },
  {
    "path": "classes/schema/ActionScheduler_StoreSchema.php",
    "chars": 5263,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_StoreSchema\n *\n * @codeCoverageIgnore\n *\n * Creates custom tables for storing schedu"
  },
  {
    "path": "codecov.yml",
    "chars": 152,
    "preview": "codecov:\n  branch: master\n\ncoverage:\n  ignore:\n  - tests/.*\n  - lib/.*\n  status:\n    project: false\n    patch: false\n   "
  },
  {
    "path": "composer.json",
    "chars": 1213,
    "preview": "{\n  \"name\": \"woocommerce/action-scheduler\",\n  \"description\": \"Action Scheduler for WordPress and WooCommerce\",\n  \"homepa"
  },
  {
    "path": "deprecated/ActionScheduler_Abstract_QueueRunner_Deprecated.php",
    "chars": 852,
    "preview": "<?php\n\n/**\n * Abstract class with common Queue Cleaner functionality.\n */\nabstract class ActionScheduler_Abstract_QueueR"
  },
  {
    "path": "deprecated/ActionScheduler_AdminView_Deprecated.php",
    "chars": 5466,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_AdminView_Deprecated\n *\n * Store deprecated public functions previously found in the"
  },
  {
    "path": "deprecated/ActionScheduler_Schedule_Deprecated.php",
    "chars": 832,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Abstract_Schedule\n */\nabstract class ActionScheduler_Schedule_Deprecated implements "
  },
  {
    "path": "deprecated/ActionScheduler_Store_Deprecated.php",
    "chars": 1059,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Store_Deprecated\n *\n * @codeCoverageIgnore\n */\nabstract class ActionScheduler_Store_"
  },
  {
    "path": "deprecated/functions.php",
    "chars": 5261,
    "preview": "<?php\n/**\n * Deprecated API functions for scheduling actions\n *\n * Functions with the wc prefix were deprecated to avoid"
  },
  {
    "path": "docs/CNAME",
    "chars": 19,
    "preview": "actionscheduler.org"
  },
  {
    "path": "docs/_config.yml",
    "chars": 326,
    "preview": "title: Action Scheduler - Job Queue for WordPress\ndescription: A scalable, traceable job queue for background processing"
  },
  {
    "path": "docs/_layouts/default.html",
    "chars": 3593,
    "preview": "<!DOCTYPE html>\n<html lang=\"{{ site.lang | default: \"en-US\" }}\">\n  <head>\n    <meta charset='utf-8'>\n    <meta http-equi"
  },
  {
    "path": "docs/admin.md",
    "chars": 912,
    "preview": "---\ndescription: Learn how to administer background jobs with the Action Scheduler job queue for WordPress.\n---\n# Schedu"
  },
  {
    "path": "docs/api.md",
    "chars": 11260,
    "preview": "---\ndescription: Reference guide for background processing functions provided by the Action Scheduler job queue for Word"
  },
  {
    "path": "docs/assets/css/style.scss",
    "chars": 717,
    "preview": "---\n---\n\n@import \"{{ site.theme }}\";\n\na {\n\ttext-shadow: none;\n\ttext-decoration: none;\n}\n\na:hover {\n\ttext-decoration: und"
  },
  {
    "path": "docs/browserconfig.xml",
    "chars": 246,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig>\n    <msapplication>\n        <tile>\n            <square150x150logo"
  },
  {
    "path": "docs/faq.md",
    "chars": 8333,
    "preview": "## FAQ\n\n### Is it safe to release Action Scheduler in my plugin? Won't its functions conflict with another copy of the l"
  },
  {
    "path": "docs/google14ef723abb376cd3.html",
    "chars": 53,
    "preview": "google-site-verification: google14ef723abb376cd3.html"
  },
  {
    "path": "docs/index.md",
    "chars": 4981,
    "preview": "---\ntitle: Action Scheduler - Background Processing Job Queue for WordPress\n---\n## WordPress Job Queue with Background P"
  },
  {
    "path": "docs/perf.md",
    "chars": 7773,
    "preview": "---\ntitle: WordPress Background Processing at Scale - Action Scheduler Job Queue\ndescription: Learn how to do WordPress "
  },
  {
    "path": "docs/site.webmanifest",
    "chars": 426,
    "preview": "{\n    \"name\": \"\",\n    \"short_name\": \"\",\n    \"icons\": [\n        {\n            \"src\": \"/android-chrome-192x192.png\",\n     "
  },
  {
    "path": "docs/usage.md",
    "chars": 9601,
    "preview": "---\ndescription: Learn how to use the Action Scheduler background processing job queue for WordPress in your WordPress p"
  },
  {
    "path": "docs/version3-0.md",
    "chars": 4155,
    "preview": "## Version 3.0 FAQ\n\n### Do we need to wait for this to be bundled with WooCommerce?\n\nNo. Action Scheduler can be run as "
  },
  {
    "path": "docs/wp-cli.md",
    "chars": 7390,
    "preview": "---\ndescription: Learn how to do WordPress background processing at scale with WP CLI and the Action Scheduler job queue"
  },
  {
    "path": "functions.php",
    "chars": 19724,
    "preview": "<?php\n/**\n * General API functions for scheduling actions\n *\n * @package ActionScheduler.\n */\n\n/**\n * Enqueue an action "
  },
  {
    "path": "lib/WP_Async_Request.php",
    "chars": 3718,
    "preview": "<?php\n/**\n * WP Async Request\n *\n * @package WP-Background-Processing\n */\n\n/*\nLibrary URI: https://github.com/deliciousb"
  },
  {
    "path": "lib/cron-expression/CronExpression.php",
    "chars": 11615,
    "preview": "<?php\n\n/**\n * CRON expression parser that can determine whether or not a CRON expression is\n * due to run, the next run "
  },
  {
    "path": "lib/cron-expression/CronExpression_AbstractField.php",
    "chars": 2576,
    "preview": "<?php\n\n/**\n * Abstract CRON expression field\n *\n * @author Michael Dowling <mtdowling@gmail.com>\n */\nabstract class Cron"
  },
  {
    "path": "lib/cron-expression/CronExpression_DayOfMonthField.php",
    "chars": 3596,
    "preview": "<?php\n\n/**\n * Day of month field.  Allows: * , / - ? L W\n *\n * 'L' stands for \"last\" and specifies the last day of the m"
  },
  {
    "path": "lib/cron-expression/CronExpression_DayOfWeekField.php",
    "chars": 3921,
    "preview": "<?php\n\n/**\n * Day of week field.  Allows: * / , - ? L #\n *\n * Days of the week can be represented as a number 0-7 (0|7 ="
  },
  {
    "path": "lib/cron-expression/CronExpression_FieldFactory.php",
    "chars": 1745,
    "preview": "<?php\n\n/**\n * CRON field factory implementing a flyweight factory\n *\n * @author Michael Dowling <mtdowling@gmail.com>\n *"
  },
  {
    "path": "lib/cron-expression/CronExpression_FieldInterface.php",
    "chars": 1138,
    "preview": "<?php\n\n/**\n * CRON field interface\n *\n * @author Michael Dowling <mtdowling@gmail.com>\n */\ninterface CronExpression_Fiel"
  },
  {
    "path": "lib/cron-expression/CronExpression_HoursField.php",
    "chars": 1157,
    "preview": "<?php\n\n/**\n * Hours field.  Allows: * , / -\n *\n * @author Michael Dowling <mtdowling@gmail.com>\n */\nclass CronExpression"
  },
  {
    "path": "lib/cron-expression/CronExpression_MinutesField.php",
    "chars": 761,
    "preview": "<?php\n\n/**\n * Minutes field.  Allows: * , / -\n *\n * @author Michael Dowling <mtdowling@gmail.com>\n */\nclass CronExpressi"
  },
  {
    "path": "lib/cron-expression/CronExpression_MonthField.php",
    "chars": 1399,
    "preview": "<?php\n\n/**\n * Month field.  Allows: * , / -\n *\n * @author Michael Dowling <mtdowling@gmail.com>\n */\nclass CronExpression"
  },
  {
    "path": "lib/cron-expression/CronExpression_YearField.php",
    "chars": 937,
    "preview": "<?php\n\n/**\n * Year field.  Allows: * , / -\n *\n * @author Michael Dowling <mtdowling@gmail.com>\n */\nclass CronExpression_"
  },
  {
    "path": "lib/cron-expression/LICENSE",
    "chars": 1098,
    "preview": "Copyright (c) 2011 Michael Dowling <mtdowling@gmail.com> and contributors\n\nPermission is hereby granted, free of charge,"
  },
  {
    "path": "lib/cron-expression/README.md",
    "chars": 3263,
    "preview": "PHP Cron Expression Parser\n==========================\n\n[![Latest Stable Version](https://poser.pugx.org/mtdowling/cron-e"
  },
  {
    "path": "license.txt",
    "chars": 35149,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "package.json",
    "chars": 1232,
    "preview": "{\n  \"name\": \"action-scheduler\",\n  \"title\": \"Action Scheduler\",\n  \"version\": \"3.9.3\",\n  \"homepage\": \"https://actionschedu"
  },
  {
    "path": "phpcs.xml",
    "chars": 2626,
    "preview": "<?xml version=\"1.0\"?>\n<ruleset name=\"WordPress Coding Standards\">\n\t<description>WooCommerce dev PHP_CodeSniffer ruleset."
  },
  {
    "path": "readme.txt",
    "chars": 12312,
    "preview": "=== Action Scheduler ===\nContributors: Automattic, wpmuguru, claudiosanches, peterfabian1000, vedjain, jamosova, oblivio"
  },
  {
    "path": "tests/ActionScheduler_UnitTestCase.php",
    "chars": 1629,
    "preview": "<?php\n// phpcs:disable WordPress.DateTime.RestrictedFunctions.timezone_change_date_default_timezone_set\n\n/**\n * Class Ac"
  },
  {
    "path": "tests/README.md",
    "chars": 521,
    "preview": "# Action Scheduler tests\n\nTo run unit tests:\n\n1. Make sure that PHPUnit is installed by running:\n    ```\n    $ composer "
  },
  {
    "path": "tests/bin/install.sh",
    "chars": 6004,
    "preview": "#!/usr/bin/env bash\n\nif [ $# -lt 3 ]; then\n\techo \"usage: $0 <db-name> <db-user> <db-pass> [db-host] [wp-version] [skip-d"
  },
  {
    "path": "tests/bootstrap.php",
    "chars": 1677,
    "preview": "<?php\n// phpcs:disable PEAR.Files.IncludingFile.UseRequireOnce\n\n$GLOBALS['wp_tests_options']['template']         = 'twen"
  },
  {
    "path": "tests/phpunit/ActionScheduler_Mock_Async_Request_QueueRunner.php",
    "chars": 309,
    "preview": "<?php\n\ndefined( 'ABSPATH' ) || exit;\n\n/**\n * ActionScheduler_Mock_AsyncRequest_QueueRunner class.\n */\nclass ActionSchedu"
  },
  {
    "path": "tests/phpunit/ActionScheduler_Mocker.php",
    "chars": 782,
    "preview": "<?php\n\ndefined( 'ABSPATH' ) || exit;\n\n/**\n * ActionScheduler_Mocker class.\n */\nclass ActionScheduler_Mocker {\n\n\t/**\n\t * "
  },
  {
    "path": "tests/phpunit/ActionScheduler_RecurringActionScheduler_Test.php",
    "chars": 3026,
    "preview": "<?php\n\n/**\n * Test suite for the ActionScheduler_RecurringActionScheduler class.\n */\nclass ActionScheduler_RecurringActi"
  },
  {
    "path": "tests/phpunit/deprecated/ActionScheduler_UnitTestCase.php",
    "chars": 1650,
    "preview": "<?php\n// phpcs:disable WordPress.DateTime.RestrictedFunctions.timezone_change_date_default_timezone_set\n// phpcs:disable"
  },
  {
    "path": "tests/phpunit/helpers/ActionScheduler_Callbacks.php",
    "chars": 944,
    "preview": "<?php\n\n/**\n * ActionScheduler_Callbacks class.\n */\nclass ActionScheduler_Callbacks {\n\t/**\n\t * Scheduled action hook that"
  },
  {
    "path": "tests/phpunit/helpers/ActionScheduler_Compatibility_Test.php",
    "chars": 1581,
    "preview": "<?php\n// phpcs:disable WordPress.PHP.IniSet.max_execution_time_Blacklisted\n\n/**\n * @group helpers\n */\nclass ActionSchedu"
  },
  {
    "path": "tests/phpunit/helpers/ActionScheduler_TimezoneHelper_Test.php",
    "chars": 2568,
    "preview": "<?php\n\n/**\n * @group timezone\n */\nclass ActionScheduler_TimezoneHelper_Test extends ActionScheduler_UnitTestCase {\n\n\t/**"
  },
  {
    "path": "tests/phpunit/jobs/ActionScheduler_Action_Test.php",
    "chars": 1932,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Action_Test\n * @group actions\n */\nclass ActionScheduler_Action_Test extends ActionSc"
  },
  {
    "path": "tests/phpunit/jobs/ActionScheduler_NullAction_Test.php",
    "chars": 391,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_NullAction_Test\n * @group actions\n */\nclass ActionScheduler_NullAction_Test extends "
  },
  {
    "path": "tests/phpunit/jobstore/AbstractStoreTest.php",
    "chars": 6217,
    "preview": "<?php\n\nnamespace Action_Scheduler\\Tests\\DataStores;\n\nuse ActionScheduler_Action;\nuse ActionScheduler_Callbacks;\nuse Acti"
  },
  {
    "path": "tests/phpunit/jobstore/ActionScheduler_DBStoreMigrator_Test.php",
    "chars": 1033,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_DBStoreMigrator_Test\n * @group tables\n */\nclass ActionScheduler_DBStoreMigrator_Test"
  },
  {
    "path": "tests/phpunit/jobstore/ActionScheduler_DBStore_Test.php",
    "chars": 37090,
    "preview": "<?php\n\nuse Action_Scheduler\\Tests\\DataStores\\AbstractStoreTest;\n\n/**\n * Class ActionScheduler_DBStore_Test\n * @group tab"
  },
  {
    "path": "tests/phpunit/jobstore/ActionScheduler_HybridStore_Test.php",
    "chars": 10178,
    "preview": "<?php\n\n\nuse Action_Scheduler\\Migration\\Config;\nuse ActionScheduler_NullAction as NullAction;\nuse ActionScheduler_wpComme"
  },
  {
    "path": "tests/phpunit/jobstore/ActionScheduler_wpPostStore_Test.php",
    "chars": 19937,
    "preview": "<?php\n\nuse Action_Scheduler\\Tests\\DataStores\\AbstractStoreTest;\n\n/**\n * Class ActionScheduler_wpPostStore_Test\n * @group"
  },
  {
    "path": "tests/phpunit/lock/ActionScheduler_OptionLock_Test.php",
    "chars": 2352,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Lock_Test\n * @package test_cases\\lock\n */\nclass ActionScheduler_OptionLock_Test exte"
  },
  {
    "path": "tests/phpunit/logging/ActionScheduler_DBLogger_Test.php",
    "chars": 5198,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_DBLogger_Test\n * @package test_cases\\logging\n * @group tables\n */\nclass ActionSchedu"
  },
  {
    "path": "tests/phpunit/logging/ActionScheduler_wpCommentLogger_Test.php",
    "chars": 8674,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_wpCommentLogger_Test\n * @package test_cases\\logging\n */\nclass ActionScheduler_wpComm"
  },
  {
    "path": "tests/phpunit/migration/ActionMigrator_Test.php",
    "chars": 6899,
    "preview": "<?php\n\nuse Action_Scheduler\\Migration\\ActionMigrator;\nuse Action_Scheduler\\Migration\\LogMigrator;\n\n/**\n * Class ActionMi"
  },
  {
    "path": "tests/phpunit/migration/BatchFetcher_Test.php",
    "chars": 2416,
    "preview": "<?php\n\nuse Action_Scheduler\\Migration\\BatchFetcher;\nuse ActionScheduler_wpPostStore as PostStore;\n\n/**\n * Class BatchFet"
  },
  {
    "path": "tests/phpunit/migration/Config_Test.php",
    "chars": 830,
    "preview": "<?php\n\nuse Action_Scheduler\\Migration\\Config;\n\n/**\n * Class Config_Test\n * @group migration\n */\nclass Config_Test extend"
  },
  {
    "path": "tests/phpunit/migration/Controller_Test.php",
    "chars": 2223,
    "preview": "<?php\n/**\n * Contains tests for the Migration Controller.\n *\n * @package test_cases\\migration\n */\n\nuse ActionScheduler_S"
  },
  {
    "path": "tests/phpunit/migration/LogMigrator_Test.php",
    "chars": 1437,
    "preview": "<?php\n\nuse Action_Scheduler\\Migration\\LogMigrator;\n\n/**\n * Class LogMigrator_Test\n * @group migration\n */\nclass LogMigra"
  },
  {
    "path": "tests/phpunit/migration/Runner_Test.php",
    "chars": 3079,
    "preview": "<?php\n\n\nuse Action_Scheduler\\Migration\\Config;\nuse Action_Scheduler\\Migration\\Runner;\nuse ActionScheduler_wpCommentLogge"
  },
  {
    "path": "tests/phpunit/migration/Scheduler_Test.php",
    "chars": 4963,
    "preview": "<?php\n\nuse Action_Scheduler\\Migration\\Scheduler;\nuse ActionScheduler_wpPostStore as PostStore;\n\n/**\n * Class Scheduler_T"
  },
  {
    "path": "tests/phpunit/procedural_api/procedural_api_Test.php",
    "chars": 18524,
    "preview": "<?php\n\n/**\n * Class procedural_api_Test\n */\nclass Procedural_API_Test extends ActionScheduler_UnitTestCase {\n\n\t// phpcs:"
  },
  {
    "path": "tests/phpunit/procedural_api/wc_get_scheduled_actions_Test.php",
    "chars": 2983,
    "preview": "<?php\n\n/**\n * Class as_get_scheduled_actions_Test\n */\nclass as_get_scheduled_actions_Test extends ActionScheduler_UnitTe"
  },
  {
    "path": "tests/phpunit/runner/ActionScheduler_QueueCleaner_Test.php",
    "chars": 8515,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_QueueCleaner_Test\n */\nclass ActionScheduler_QueueCleaner_Test extends ActionSchedule"
  },
  {
    "path": "tests/phpunit/runner/ActionScheduler_QueueRunner_Test.php",
    "chars": 24116,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_QueueRunner_Test\n * @group runners\n */\nclass ActionScheduler_QueueRunner_Test extend"
  },
  {
    "path": "tests/phpunit/schedules/ActionScheduler_CronSchedule_Test.php",
    "chars": 3226,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_CronSchedule_Test\n * @group schedules\n */\nclass ActionScheduler_CronSchedule_Test ex"
  },
  {
    "path": "tests/phpunit/schedules/ActionScheduler_IntervalSchedule_Test.php",
    "chars": 1509,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_IntervalSchedule_Test\n * @group schedules\n */\nclass ActionScheduler_IntervalSchedule"
  },
  {
    "path": "tests/phpunit/schedules/ActionScheduler_NullSchedule_Test.php",
    "chars": 442,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_NullSchedule_Test\n * @group schedules\n */\nclass ActionScheduler_NullSchedule_Test ex"
  },
  {
    "path": "tests/phpunit/schedules/ActionScheduler_SimpleSchedule_Test.php",
    "chars": 1170,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_SimpleSchedule_Test\n * @group schedules\n */\nclass ActionScheduler_SimpleSchedule_Tes"
  },
  {
    "path": "tests/phpunit/versioning/ActionScheduler_Versions_Test.php",
    "chars": 1407,
    "preview": "<?php\n\n/**\n * Class ActionScheduler_Versions_Test\n */\nclass ActionScheduler_Versions_Test extends ActionScheduler_UnitTe"
  },
  {
    "path": "tests/phpunit.xml.dist",
    "chars": 1697,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<phpunit backupGlobals=\"false\"\n\t\t\t\t backupStaticAttributes=\"false\"\n\t\t\t\t colors=\""
  }
]

About this extraction

This page contains the full source code of the Prospress/action-scheduler GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 164 files (818.7 KB), approximately 228.7k tokens, and a symbol index with 1048 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!