Squashed 'includes/libraries/action-scheduler/' changes from bd0277d..963b006
73e9082 Merge pull request #196 from Prospress/version_2_1_0 d9d2a29 Merge pull request #202 from Prospress/issue_179_redux b876d8c Convert DateTime to ActionScheduler_DateTime 3f90172 Use ActionScheduler_DateTime objects rather than DateTime 5843134 Add test for a DateTime object b095094 Merge pull request #201 from Prospress/as_unschedule_all_actions 70e76ab Improve unit tests for as_unschedule_action() c934f4f Add as_unschedule_all_actions() d693b02 Return a value from as_unschedule_action() fb7c07f Update docblock on as_unschedule_action() aef2fce Remove test_local_timezone_offsets_utc() 2d35840 Deprecate get_local_timezone() APIs 3512ad9 Add TimezoneHelper::set_local_timezone() 5024f5c Add TimezoneHelper::get_local_timezone_string() 8e8bffb Add TimezoneHelper::get_local_timezone_offset() a73ccfe Merge pull request #152 from Prospress/handle_bad_args f539880 Add more helper methods to ActionScheduler_DateTime class 7485262 Include last-ditch attempt to find correct timezone mapping c3fb3e5 Update timezone string check b452722 Add Timezone unit tests a6dd57d Handle args that cannot be decoded properly d940ec5 Create Exception interface and InvalidAction exception 959c951 Merge pull request #198 from Prospress/docs_followup 3619fab Fix typos and minor wording tweaks 682bd47 Merge pull request #197 from Prospress/install_docs 09ecfd1 Add Usage docs 3ee8fed Update version to 2.1.0 e8d9073 Merge pull request #194 from Prospress/issue_193 bf377e1 Fix calls to remove_action() 29f5901 Merge pull request #191 from Prospress/issue_176 2ca3735 Merge pull request #192 from Prospress/issue_184 f48341e Only process pending actions b509c89 Add Compatibility::raise_memory_limit() 53ee32c Restore batch processing loop b0347fe Add memory APIs to Abstract_QueueRunner 324e734 Improve ActionScheduler_Logger abstraction be43437 Add Compatibility::convert_hr_to_bytes() 0b33c26 Merge pull request #190 from Prospress/issue_189 00b1d06 Use var_export() for admin list table args column 84ba9b3 Move timing methods to Abstract_QueueRunner 7a93e55 Merge pull request #187 from Prospress/issue_185 e9c3e01 Add ActionScheduler_wpPostStore::validate_action 8dcc262 Merge pull request #186 from Prospress/remove_composer_version 439a1c3 Remove the version from composer.json 2c3c555 Merge pull request #183 from Prospress/issue_182 a57830d Use sanitize_text_field() instead of wc_clean() 354e74c Merge pull request #181 from Prospress/change_function_prefix 93b4ded Use as_ prefix not wc_ prefix for public APIs 350f3ac Merge pull request #177 from Prospress/mention_dependencies_in_readme 850e4d5 Add cautionary note on action dependencies 771e34c Minor readme tweaks git-subtree-dir: includes/libraries/action-scheduler git-subtree-split: 963b00614af83a047724f48bea7d4ee787b48d5b
This commit is contained in:
parent
10c4a515e4
commit
3c610aec4a
151
README.md
151
README.md
|
@ -38,6 +38,90 @@ The events logged by default include when an action:
|
|||
|
||||
Actions can also be grouped together using a custom taxonomy named `action-group`.
|
||||
|
||||
## Usage
|
||||
|
||||
There are two ways to install Action Scheduler:
|
||||
|
||||
1. as a library within your plugin or theme; or
|
||||
1. as a regular WordPress plugin
|
||||
|
||||
### Usage as a Library
|
||||
|
||||
To use Action Scheduler as a library:
|
||||
|
||||
1. include the Action Scheduler codebase
|
||||
1. load the library by including the `action-scheduler.php` file
|
||||
|
||||
#### Including Action Scheduler Codebase
|
||||
|
||||
Using a [subtree in your plugin, theme or site's Git repository](https://www.atlassian.com/blog/git/alternatives-to-git-submodule-git-subtree) to include Action Scheduler is the recommended method. Composer can also be used.
|
||||
|
||||
To include Action Scheduler as a git subtree:
|
||||
|
||||
##### Step 1. Add the Repository as a Remote
|
||||
|
||||
```
|
||||
git remote add -f subtree-action-scheduler https://github.com/Prospress/action-scheduler.git
|
||||
```
|
||||
|
||||
Adding the subtree as a remote allows us to refer to it in short from via the name `subtree-action-scheduler`, instead of the full GitHub URL.
|
||||
|
||||
##### Step 2. Add the Repo as a Subtree
|
||||
|
||||
```
|
||||
git subtree add --prefix libraries/action-scheduler subtree-action-scheduler master --squash
|
||||
```
|
||||
|
||||
This will add the `master` branch of Action Scheduler to your repository in the folder `libraries/action-scheduler`.
|
||||
|
||||
You can change the `--prefix` to change where the code is included. Or change the `master` branch to a tag, like `2.1.0` to include only a stable version.
|
||||
|
||||
##### Step 3. Update the Subtree
|
||||
|
||||
To update Action Scheduler to a new version, use the commands:
|
||||
|
||||
```
|
||||
git fetch subtree-action-scheduler master
|
||||
git subtree pull --prefix libraries/action-scheduler subtree-action-scheduler master --squash
|
||||
```
|
||||
|
||||
#### Loading Action Scheduler
|
||||
|
||||
To load Action Scheduler, you only need to include `action-scheduler.php` file, e.g.
|
||||
|
||||
```php
|
||||
<?php
|
||||
require_once( plugin_dir_path( __FILE__ ) . '/libraries/action-scheduler/action-scheduler.php' );
|
||||
```
|
||||
|
||||
There is no need to call any functions or do else to initialize Action Scheduler.
|
||||
|
||||
When the `action-scheduler.php` file is included, Action Scheduler will register the version in that file and then load the most recent version of itself on the site. It will also load the most recent version of [all API functions](https://github.com/prospress/action-scheduler#api-functions).
|
||||
|
||||
#### Load Order
|
||||
|
||||
Action Scheduler will register its version on `'plugins_loaded'` with priority `0` - after all other plugin codebases has been loaded. Therefore **the `action-scheduler.php` file must be included before `'plugins_loaded'` priority `0`**.
|
||||
|
||||
It is recommended to load it _when the file including it is included_. However, if you need to load it on a hook, then the hook must occur before `'plugins_loaded'`, or you can use `'plugins_loaded'` with negative priority, like `-10`.
|
||||
|
||||
Action Scheduler will later initialize itself on `'init'` with priority `1`. Action Scheduler APIs should not be used until after `'init'` with priority `1`.
|
||||
|
||||
### Usage as a Plugin
|
||||
|
||||
Action Scheduler includes the necessary file headers to be used as a plugin.
|
||||
|
||||
To install it as a plugin:
|
||||
|
||||
1. Download the .zip archive of the latest [stable release](https://github.com/Prospress/action-scheduler/releases)
|
||||
1. Go to the **Plugins > Add New > Upload** administration screen on your WordPress site
|
||||
1. Select the archive file you just downloaded
|
||||
1. Click **Install Now**
|
||||
1. Click **Activate**
|
||||
|
||||
Or clone the Git repository into your site's `wp-content/plugins` folder.
|
||||
|
||||
Using Action Scheduler as a plugin can be handy for developing against newer versions, rather than having to update the subtree in your codebase. **When installed as a plugin, Action Scheduler does not provide any user interfaces for scheduling actions**. The only way to interact with Action Scheduler is via code.
|
||||
|
||||
## Managing Scheduled Actions
|
||||
|
||||
Action Scheduler has a built in administration screen for monitoring, debugging and manually triggering scheduled actions.
|
||||
|
@ -62,7 +146,7 @@ Still have questions? Check out the [FAQ below](#faq).
|
|||
|
||||
Action Scheduler has custom [WP CLI](http://wp-cli.org) commands available for processing actions.
|
||||
|
||||
For many sites, WP CLI is a much better choice for running queues of actions than the default WP Cron runner. These are some common cases where WP CLI is a better option:
|
||||
For large sites, WP CLI is a much better choice for running queues of actions than the default WP Cron runner. These are some common cases where WP CLI is a better option:
|
||||
|
||||
* long-running tasks - Tasks that take a significant amount of time to run
|
||||
* large queues - A large number of tasks will naturally take a longer time
|
||||
|
@ -70,7 +154,7 @@ For many sites, WP CLI is a much better choice for running queues of actions tha
|
|||
|
||||
With a regular web request, you may have to deal with script timeouts enforced by hosts, or other restraints that make it more challenging to run Action Scheduler tasks. Utilizing WP CLI to run commands directly on the server give you more freedom. This means that you typically don't have the same constraints of a normal web request.
|
||||
|
||||
If you choose to utilize WP CLI exclusively, you can disable the normal WP CLI queue runner by installing the [Action Scheduler - Disable Default Queue Runner](https://github.com/Prospress/action-scheduler-disable-default-runner) plugin. Note that if you do this, you **must** run Action Scheduler manually.
|
||||
If you choose to utilize WP CLI exclusively, you can disable the normal WP CLI queue runner by installing the [Action Scheduler - Disable Default Queue Runner](https://github.com/Prospress/action-scheduler-disable-default-runner) plugin. Note that if you do this, you **must** run Action Scheduler via WP CLI or another method, otherwise no scheduled actions will be processed.
|
||||
|
||||
### Commands
|
||||
|
||||
|
@ -87,9 +171,31 @@ These are the commands available to use with Action Scheduler:
|
|||
|
||||
The best way to get a full list of commands and their available options is to use WP CLI itself. This can be done by running `wp action-scheduler` to list all Action Scheduler commands, or by including the `--help` flag with any of the individual commands. This will provide all relevant parameters and flags for the command.
|
||||
|
||||
### Improving Performance with `--group`
|
||||
### Cautionary Note on Action Dependencies when using `--group` or `--hooks` Options
|
||||
|
||||
Being able to run queues for specific groups of actions is valuable at scale. Why? Because it means you can restrict the concurrency for similar actions.
|
||||
The `--group` and `--hooks` options should be used with caution if you have an implicit dependency between scheduled actions based on their schedule.
|
||||
|
||||
For example, consider two scheduled actions for the same subscription:
|
||||
|
||||
* `scheduled_payment` scheduled for `2015-11-13 00:00:00` and
|
||||
* `scheduled_expiration` scheduled for `2015-11-13 00:01:00`.
|
||||
|
||||
Under normal conditions, Action Scheduler will ensure the `scheduled_payment` action is run before the `scheduled_expiration` action. Becuase that's how they are scheduled.
|
||||
|
||||
However, when using the `--hooks` option, the `scheduled_payment` and `scheduled_expiration` actions will be processed in separate queues. As a result, this dependency is not guaranteed.
|
||||
|
||||
For example, consider a site with both:
|
||||
|
||||
* 100,000 `scheduled_payment` actions, scheduled for `2015-11-13 00:00:00`
|
||||
* 100 `scheduled_expiration` actions, scheduled for `2015-11-13 00:01:00`
|
||||
|
||||
If two queue runners are running alongside each other with each runner dedicated to just one of these hooks, the queue runner handling expiration hooks will complete the processing of the expiration hooks more quickly than the queue runner handling all the payment actions.
|
||||
|
||||
**Because of this, the `--group` and `--hooks` options should be used with caution to avoid processing actions with an implicit dependency based on their schedule in separate queues.**
|
||||
|
||||
### Improving Performance with `--group` or `--hooks`
|
||||
|
||||
Being able to run queues for specific hooks or groups of actions is valuable at scale. Why? Because it means you can restrict the concurrency for similar actions.
|
||||
|
||||
For example, let's say you have 300,000 actions queued up comprised of:
|
||||
|
||||
|
@ -115,13 +221,18 @@ The Action Scheduler API functions are designed to mirror the WordPress [WP-Cron
|
|||
|
||||
Functions return similar values and accept similar arguments to their WP-Cron counterparts. The notable differences are:
|
||||
|
||||
* `wc_schedule_single_action()` & `wc_schedule_recurring_action()` will return the post ID of the scheduled action rather than boolean indicating whether the event was scheduled
|
||||
* `wc_schedule_recurring_action()` takes an interval in seconds as the recurring interval rather than an arbitrary string
|
||||
* `wc_schedule_single_action()` & `wc_schedule_recurring_action()` can accept a `$group` parameter to group different actions for the one plugin together.
|
||||
* the `wp_` prefix is substituted with `wc_` and the term `event` is replaced with `action`
|
||||
* `as_schedule_single_action()` & `as_schedule_recurring_action()` will return the post ID of the scheduled action rather than boolean indicating whether the event was scheduled
|
||||
* `as_schedule_recurring_action()` takes an interval in seconds as the recurring interval rather than an arbitrary string
|
||||
* `as_schedule_single_action()` & `as_schedule_recurring_action()` can accept a `$group` parameter to group different actions for the one plugin together.
|
||||
* the `wp_` prefix is substituted with `as_` and the term `event` is replaced with `action`
|
||||
|
||||
### API Function Availability
|
||||
|
||||
#### Function Reference / `wc_schedule_single_action()`
|
||||
As mentioned in the [Usage - Load Order](#load-order) section, Action Scheduler will initialize itself on the `'init'` hook with priority `1`. While API functions are loaded prior to this and call be called, they should not be called until after `'init'` with priority `1`, because each component, like the data store, has not yet been initialized.
|
||||
|
||||
Do not use Action Scheduler API functions prior to `'init'` hook with priority `1`. Doing so could lead to unexpected results, like data being stored in the incorrect location.
|
||||
|
||||
#### Function Reference / `as_schedule_single_action()`
|
||||
|
||||
##### Description
|
||||
|
||||
|
@ -130,7 +241,7 @@ Schedule an action to run one time.
|
|||
##### Usage
|
||||
|
||||
```php
|
||||
<?php wc_schedule_single_action( $timestamp, $hook, $args, $group ); ?>
|
||||
<?php as_schedule_single_action( $timestamp, $hook, $args, $group ); ?>
|
||||
````
|
||||
|
||||
##### Parameters
|
||||
|
@ -145,7 +256,7 @@ Schedule an action to run one time.
|
|||
(integer) the action's ID in the [posts](http://codex.wordpress.org/Database_Description#Table_Overview) table.
|
||||
|
||||
|
||||
#### Function Reference / `wc_schedule_recurring_action()`
|
||||
#### Function Reference / `as_schedule_recurring_action()`
|
||||
|
||||
##### Description
|
||||
|
||||
|
@ -154,7 +265,7 @@ Schedule an action to run repeatedly with a specified interval in seconds.
|
|||
##### Usage
|
||||
|
||||
```php
|
||||
<?php wc_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args, $group ); ?>
|
||||
<?php as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args, $group ); ?>
|
||||
````
|
||||
|
||||
##### Parameters
|
||||
|
@ -170,7 +281,7 @@ Schedule an action to run repeatedly with a specified interval in seconds.
|
|||
(integer) the action's ID in the [posts](http://codex.wordpress.org/Database_Description#Table_Overview) table.
|
||||
|
||||
|
||||
#### Function Reference / `wc_schedule_cron_action()`
|
||||
#### Function Reference / `as_schedule_cron_action()`
|
||||
|
||||
##### Description
|
||||
|
||||
|
@ -179,7 +290,7 @@ Schedule an action that recurs on a cron-like schedule.
|
|||
##### Usage
|
||||
|
||||
```php
|
||||
<?php wc_schedule_cron_action( $timestamp, $schedule, $hook, $args, $group ); ?>
|
||||
<?php as_schedule_cron_action( $timestamp, $schedule, $hook, $args, $group ); ?>
|
||||
````
|
||||
|
||||
##### Parameters
|
||||
|
@ -195,7 +306,7 @@ Schedule an action that recurs on a cron-like schedule.
|
|||
(integer) the action's ID in the [posts](http://codex.wordpress.org/Database_Description#Table_Overview) table.
|
||||
|
||||
|
||||
#### Function Reference / `wc_unschedule_action()`
|
||||
#### Function Reference / `as_unschedule_action()`
|
||||
|
||||
##### Description
|
||||
|
||||
|
@ -204,7 +315,7 @@ Cancel the next occurrence of a job.
|
|||
##### Usage
|
||||
|
||||
```php
|
||||
<?php wc_unschedule_action( $hook, $args, $group ); ?>
|
||||
<?php as_unschedule_action( $hook, $args, $group ); ?>
|
||||
````
|
||||
|
||||
##### Parameters
|
||||
|
@ -218,7 +329,7 @@ Cancel the next occurrence of a job.
|
|||
(null)
|
||||
|
||||
|
||||
#### Function Reference / `wc_next_scheduled_action()`
|
||||
#### Function Reference / `as_next_scheduled_action()`
|
||||
|
||||
##### Description
|
||||
|
||||
|
@ -227,7 +338,7 @@ Returns the next timestamp for a scheduled action.
|
|||
##### Usage
|
||||
|
||||
```php
|
||||
<?php wc_next_scheduled_action( $hook, $args, $group ); ?>
|
||||
<?php as_next_scheduled_action( $hook, $args, $group ); ?>
|
||||
````
|
||||
|
||||
##### Parameters
|
||||
|
@ -241,7 +352,7 @@ Returns the next timestamp for a scheduled action.
|
|||
(integer|boolean) The timestamp for the next occurrence, or false if nothing was found.
|
||||
|
||||
|
||||
#### Function Reference / `wc_get_scheduled_actions()`
|
||||
#### Function Reference / `as_get_scheduled_actions()`
|
||||
|
||||
##### Description
|
||||
|
||||
|
@ -250,7 +361,7 @@ Find scheduled actions.
|
|||
##### Usage
|
||||
|
||||
```php
|
||||
<?php wc_get_scheduled_actions( $args, $return_format ); ?>
|
||||
<?php as_get_scheduled_actions( $args, $return_format ); ?>
|
||||
````
|
||||
|
||||
##### Parameters
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Description: A robust scheduling library for use in WordPress plugins.
|
||||
* Author: Prospress
|
||||
* Author URI: http://prospress.com/
|
||||
* Version: 2.0.0
|
||||
* Version: 2.1.0
|
||||
* License: GPLv3
|
||||
*
|
||||
* Copyright 2018 Prospress, Inc. (email : freedoms@prospress.com)
|
||||
|
@ -25,21 +25,21 @@
|
|||
*
|
||||
*/
|
||||
|
||||
if ( ! function_exists( 'action_scheduler_register_2_dot_0_dot_0' ) ) {
|
||||
if ( ! function_exists( 'action_scheduler_register_2_dot_1_dot_0' ) ) {
|
||||
|
||||
if ( ! class_exists( 'ActionScheduler_Versions' ) ) {
|
||||
require_once( 'classes/ActionScheduler_Versions.php' );
|
||||
add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
|
||||
}
|
||||
|
||||
add_action( 'plugins_loaded', 'action_scheduler_register_2_dot_0_dot_0', 0, 0 );
|
||||
add_action( 'plugins_loaded', 'action_scheduler_register_2_dot_1_dot_0', 0, 0 );
|
||||
|
||||
function action_scheduler_register_2_dot_0_dot_0() {
|
||||
function action_scheduler_register_2_dot_1_dot_0() {
|
||||
$versions = ActionScheduler_Versions::instance();
|
||||
$versions->register( '2.0.0', 'action_scheduler_initialize_2_dot_0_dot_0' );
|
||||
$versions->register( '2.1.0', 'action_scheduler_initialize_2_dot_1_dot_0' );
|
||||
}
|
||||
|
||||
function action_scheduler_initialize_2_dot_0_dot_0() {
|
||||
function action_scheduler_initialize_2_dot_1_dot_0() {
|
||||
require_once( 'classes/ActionScheduler.php' );
|
||||
ActionScheduler::init( __FILE__ );
|
||||
}
|
||||
|
|
|
@ -67,8 +67,8 @@ abstract class ActionScheduler {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( file_exists( $dir.$class.'.php' ) ) {
|
||||
include( $dir.$class.'.php' );
|
||||
if ( file_exists( "{$dir}{$class}.php" ) ) {
|
||||
include( "{$dir}{$class}.php" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +97,10 @@ abstract class ActionScheduler {
|
|||
|
||||
require_once( self::plugin_path('functions.php') );
|
||||
|
||||
if ( apply_filters( 'action_scheduler_load_deprecated_functions', true ) ) {
|
||||
require_once( self::plugin_path('deprecated/functions.php') );
|
||||
}
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Scheduler_command' );
|
||||
}
|
||||
|
|
|
@ -275,7 +275,7 @@ abstract class ActionScheduler_Abstract_ListTable extends WP_List_Table {
|
|||
$valid_sortable_columns = array_values( $this->sort_by );
|
||||
|
||||
if ( ! empty( $_GET['orderby'] ) && in_array( $_GET['orderby'], $valid_sortable_columns ) ) {
|
||||
$orderby = wc_clean( $_GET['orderby'] );
|
||||
$orderby = sanitize_text_field( $_GET['orderby'] );
|
||||
} else {
|
||||
$orderby = $valid_sortable_columns[0];
|
||||
}
|
||||
|
|
|
@ -14,6 +14,16 @@ abstract class ActionScheduler_Abstract_QueueRunner {
|
|||
/** @var ActionScheduler_Store */
|
||||
protected $store;
|
||||
|
||||
/**
|
||||
* The created time.
|
||||
*
|
||||
* Represents when the queue runner was constructed and used when calculating how long a PHP request has been running.
|
||||
* For this reason it should be as close as possible to the PHP request start time.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $created_time;
|
||||
|
||||
/**
|
||||
* ActionScheduler_Abstract_QueueRunner constructor.
|
||||
*
|
||||
|
@ -22,6 +32,9 @@ abstract class ActionScheduler_Abstract_QueueRunner {
|
|||
* @param ActionScheduler_QueueCleaner $cleaner
|
||||
*/
|
||||
public function __construct( ActionScheduler_Store $store = null, ActionScheduler_FatalErrorMonitor $monitor = null, ActionScheduler_QueueCleaner $cleaner = null ) {
|
||||
|
||||
$this->created_time = microtime( true );
|
||||
|
||||
$this->store = $store ? $store : ActionScheduler_Store::instance();
|
||||
$this->monitor = $monitor ? $monitor : new ActionScheduler_FatalErrorMonitor( $this->store );
|
||||
$this->cleaner = $cleaner ? $cleaner : new ActionScheduler_QueueCleaner( $this->store );
|
||||
|
@ -35,6 +48,12 @@ abstract class ActionScheduler_Abstract_QueueRunner {
|
|||
public function process_action( $action_id ) {
|
||||
try {
|
||||
do_action( 'action_scheduler_before_execute', $action_id );
|
||||
|
||||
if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) {
|
||||
do_action( 'action_scheduler_execution_ignored', $action_id );
|
||||
return;
|
||||
}
|
||||
|
||||
$action = $this->store->fetch_action( $action_id );
|
||||
$this->store->log_execution( $action_id );
|
||||
$action->execute();
|
||||
|
@ -79,6 +98,115 @@ abstract class ActionScheduler_Abstract_QueueRunner {
|
|||
return apply_filters( 'action_scheduler_queue_runner_concurrent_batches', 5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of seconds a batch can run for.
|
||||
*
|
||||
* @return int The number of seconds.
|
||||
*/
|
||||
protected function get_maximum_execution_time() {
|
||||
|
||||
// There are known hosts with a strict 60 second execution time.
|
||||
if ( defined( 'WPENGINE_ACCOUNT' ) || defined( 'PANTHEON_ENVIRONMENT' ) ) {
|
||||
$maximum_execution_time = 60;
|
||||
} elseif ( false !== strpos( getenv( 'HOSTNAME' ), '.siteground.' ) ) {
|
||||
$maximum_execution_time = 120;
|
||||
} else {
|
||||
$maximum_execution_time = ini_get( 'max_execution_time' );
|
||||
}
|
||||
|
||||
return absint( apply_filters( 'action_scheduler_maximum_execution_time', $maximum_execution_time ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of seconds a batch has run for.
|
||||
*
|
||||
* @return int The number of seconds.
|
||||
*/
|
||||
protected function get_execution_time() {
|
||||
$execution_time = microtime( true ) - $this->created_time;
|
||||
|
||||
// Get the CPU time if the hosting environment uses it rather than wall-clock time to calculate a process's execution time.
|
||||
if ( function_exists( 'getrusage' ) && apply_filters( 'action_scheduler_use_cpu_execution_time', defined( 'PANTHEON_ENVIRONMENT' ) ) ) {
|
||||
$resource_usages = getrusage();
|
||||
|
||||
if ( isset( $resource_usages['ru_stime.tv_usec'], $resource_usages['ru_stime.tv_usec'] ) ) {
|
||||
$execution_time = $resource_usages['ru_stime.tv_sec'] + ( $resource_usages['ru_stime.tv_usec'] / 1000000 );
|
||||
}
|
||||
}
|
||||
|
||||
return $execution_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the host's max execution time is (likely) to be exceeded if processing more actions.
|
||||
*
|
||||
* @param int $processed_actions The number of actions processed so far - used to determine the likelihood of exceeding the time limit if processing another action
|
||||
* @return bool
|
||||
*/
|
||||
protected function time_likely_to_be_exceeded( $processed_actions ) {
|
||||
|
||||
$execution_time = $this->get_execution_time();
|
||||
$max_execution_time = $this->get_maximum_execution_time();
|
||||
$time_per_action = $execution_time / $processed_actions;
|
||||
$estimated_time = $execution_time + ( $time_per_action * 2 );
|
||||
$likely_to_be_exceeded = $estimated_time > $this->get_maximum_execution_time();
|
||||
|
||||
return apply_filters( 'action_scheduler_maximum_execution_time_likely_to_be_exceeded', $likely_to_be_exceeded, $this, $processed_actions, $execution_time, $max_execution_time );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit
|
||||
*
|
||||
* Based on WP_Background_Process::get_memory_limit()
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_memory_limit() {
|
||||
if ( function_exists( 'ini_get' ) ) {
|
||||
$memory_limit = ini_get( 'memory_limit' );
|
||||
} else {
|
||||
$memory_limit = '128M'; // Sensible default, and minimum required by WooCommerce
|
||||
}
|
||||
|
||||
if ( ! $memory_limit || -1 === $memory_limit || '-1' === $memory_limit ) {
|
||||
// Unlimited, set to 32GB.
|
||||
$memory_limit = '32G';
|
||||
}
|
||||
|
||||
return ActionScheduler_Compatibility::convert_hr_to_bytes( $memory_limit );
|
||||
}
|
||||
|
||||
/**
|
||||
* Memory exceeded
|
||||
*
|
||||
* Ensures the batch process never exceeds 90% of the maximum WordPress memory.
|
||||
*
|
||||
* Based on WP_Background_Process::memory_exceeded()
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function memory_exceeded() {
|
||||
|
||||
$memory_limit = $this->get_memory_limit() * 0.90;
|
||||
$current_memory = memory_get_usage( true );
|
||||
$memory_exceeded = $current_memory >= $memory_limit;
|
||||
|
||||
return apply_filters( 'action_scheduler_memory_exceeded', $memory_exceeded, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* See if the batch limits have been exceeded, which is when memory usage is almost at
|
||||
* the maximum limit, or the time to process more actions will exceed the max time limit.
|
||||
*
|
||||
* Based on WC_Background_Process::batch_limits_exceeded()
|
||||
*
|
||||
* @param int $processed_actions The number of actions processed so far - used to determine the likelihood of exceeding the time limit if processing another action
|
||||
* @return bool
|
||||
*/
|
||||
protected function batch_limits_exceeded( $processed_actions ) {
|
||||
return $this->memory_exceeded() || $this->time_likely_to_be_exceeded( $processed_actions );
|
||||
}
|
||||
|
||||
/**
|
||||
* Process actions in the queue.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<?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' );
|
||||
$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 );
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,16 @@
|
|||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Get the unix timestamp of the current object.
|
||||
*
|
||||
|
@ -17,4 +27,50 @@ class ActionScheduler_DateTime extends DateTime {
|
|||
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 $offset
|
||||
*/
|
||||
public function setUtcOffset( $offset ) {
|
||||
$this->utcOffset = intval( $offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timezone offset.
|
||||
*
|
||||
* @return int
|
||||
* @link http://php.net/manual/en/datetime.getoffset.php
|
||||
*/
|
||||
public function getOffset() {
|
||||
return $this->utcOffset ? $this->utcOffset : parent::getOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the TimeZone associated with the DateTime
|
||||
*
|
||||
* @param DateTimeZone $timezone
|
||||
*
|
||||
* @return static
|
||||
* @link http://php.net/manual/en/datetime.settimezone.php
|
||||
*/
|
||||
public function setTimezone( $timezone ) {
|
||||
$this->utcOffset = 0;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* ActionScheduler Exception Interface.
|
||||
*
|
||||
* Facilitates catching Exceptions unique to Action Scheduler.
|
||||
*
|
||||
* @package Prospress\ActionScheduler
|
||||
* @since %VERSION%
|
||||
*/
|
||||
interface ActionScheduler_Exception {}
|
|
@ -19,6 +19,7 @@ class ActionScheduler_FatalErrorMonitor {
|
|||
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 );
|
||||
}
|
||||
|
||||
|
@ -26,9 +27,10 @@ class ActionScheduler_FatalErrorMonitor {
|
|||
$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, 1 );
|
||||
remove_action( 'action_scheduler_after_execute', array( $this, 'untrack_action' ), 0, 0 );
|
||||
remove_action( 'action_scheduler_failed_execution', array( $this, 'untrack_action' ), 0, 0 );
|
||||
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 );
|
||||
}
|
||||
|
||||
public function track_current_action( $action_id ) {
|
||||
|
@ -51,4 +53,3 @@ class ActionScheduler_FatalErrorMonitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* InvalidAction Exception.
|
||||
*
|
||||
* Used for identifying actions that are invalid in some way.
|
||||
*
|
||||
* @package Prospress\ActionScheduler
|
||||
*/
|
||||
class ActionScheduler_InvalidActionException extends \InvalidArgumentException implements ActionScheduler_Exception {
|
||||
|
||||
/**
|
||||
* Create a new exception when the action's args cannot be decoded to an array.
|
||||
*
|
||||
* @author Jeremy Pry
|
||||
*
|
||||
* @param string $action_id The action ID with bad args.
|
||||
* @return static
|
||||
*/
|
||||
public static function from_decoding_args( $action_id ) {
|
||||
$message = sprintf(
|
||||
__( 'Action [%s] has invalid arguments. It cannot be JSON decoded to an array.', 'action-scheduler' ),
|
||||
$action_id
|
||||
);
|
||||
|
||||
return new static( $message );
|
||||
}
|
||||
}
|
|
@ -242,7 +242,7 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
|||
|
||||
$row_html = '<ul>';
|
||||
foreach ( $row['args'] as $key => $value ) {
|
||||
$row_html .= sprintf( '<li><code>%s => %s</code></li>', esc_html( $key ), esc_html( $value ) );
|
||||
$row_html .= sprintf( '<li><code>%s => %s</code></li>', esc_html( var_export( $key, true ) ), esc_html( var_export( $value, true ) ) );
|
||||
}
|
||||
$row_html .= '</ul>';
|
||||
|
||||
|
|
|
@ -41,7 +41,58 @@ abstract class ActionScheduler_Logger {
|
|||
*/
|
||||
abstract public function get_logs( $action_id );
|
||||
|
||||
abstract public function init();
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'action_scheduler_stored_action', array( $this, 'log_stored_action' ), 10, 1 );
|
||||
add_action( 'action_scheduler_canceled_action', array( $this, 'log_canceled_action' ), 10, 1 );
|
||||
add_action( 'action_scheduler_before_execute', array( $this, 'log_started_action' ), 10, 1 );
|
||||
add_action( 'action_scheduler_after_execute', array( $this, 'log_completed_action' ), 10, 1 );
|
||||
add_action( 'action_scheduler_failed_execution', array( $this, 'log_failed_action' ), 10, 2 );
|
||||
add_action( 'action_scheduler_failed_action', array( $this, 'log_timed_out_action' ), 10, 2 );
|
||||
add_action( 'action_scheduler_unexpected_shutdown', array( $this, 'log_unexpected_shutdown' ), 10, 2 );
|
||||
add_action( 'action_scheduler_reset_action', array( $this, 'log_reset_action' ), 10, 1 );
|
||||
add_action( 'action_scheduler_execution_ignored', array( $this, 'log_ignored_action' ), 10, 1 );
|
||||
}
|
||||
|
||||
public function log_stored_action( $action_id ) {
|
||||
$this->log( $action_id, __( 'action created', 'action-scheduler' ) );
|
||||
}
|
||||
|
||||
public function log_canceled_action( $action_id ) {
|
||||
$this->log( $action_id, __( 'action canceled', 'action-scheduler' ) );
|
||||
}
|
||||
|
||||
public function log_started_action( $action_id ) {
|
||||
$this->log( $action_id, __( 'action started', 'action-scheduler' ) );
|
||||
}
|
||||
|
||||
public function log_completed_action( $action_id ) {
|
||||
$this->log( $action_id, __( 'action complete', 'action-scheduler' ) );
|
||||
}
|
||||
|
||||
public function log_failed_action( $action_id, \Exception $exception ) {
|
||||
$this->log( $action_id, sprintf( __( 'action failed: %s', 'action-scheduler' ), $exception->getMessage() ) );
|
||||
}
|
||||
|
||||
public function log_timed_out_action( $action_id, $timeout ) {
|
||||
$this->log( $action_id, sprintf( __( 'action timed out after %s seconds', 'action-scheduler' ), $timeout ) );
|
||||
}
|
||||
|
||||
public function log_unexpected_shutdown( $action_id, $error ) {
|
||||
if ( ! empty( $error ) ) {
|
||||
$this->log( $action_id, sprintf( __( 'unexpected shutdown: PHP Fatal error %s in %s on line %s', 'action-scheduler' ), $error['message'], $error['file'], $error['line'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
public function log_reset_action( $action_id ) {
|
||||
$this->log( $action_id, __( 'action reset', 'action_scheduler' ) );
|
||||
}
|
||||
|
||||
public function log_ignored_action( $action_id ) {
|
||||
$this->log( $action_id, __( 'action ignored', 'action-scheduler' ) );
|
||||
}
|
||||
}
|
||||
|
|
@ -11,16 +11,6 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
|
|||
/** @var ActionScheduler_QueueRunner */
|
||||
private static $runner = null;
|
||||
|
||||
/**
|
||||
* The created time.
|
||||
*
|
||||
* Represents when the queue runner was constructed and used when calculating how long a PHP request has been running.
|
||||
* For this reason it should be as close as possible to the PHP request start time.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $created_time;
|
||||
|
||||
/**
|
||||
* @return ActionScheduler_QueueRunner
|
||||
* @codeCoverageIgnore
|
||||
|
@ -42,7 +32,6 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
|
|||
*/
|
||||
public function __construct( ActionScheduler_Store $store = null, ActionScheduler_FatalErrorMonitor $monitor = null, ActionScheduler_QueueCleaner $cleaner = null ) {
|
||||
parent::__construct( $store, $monitor, $cleaner );
|
||||
$this->created_time = microtime( true );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,43 +50,39 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
|
|||
}
|
||||
|
||||
public function run() {
|
||||
@ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
|
||||
ActionScheduler_Compatibility::raise_memory_limit();
|
||||
@set_time_limit( apply_filters( 'action_scheduler_queue_runner_time_limit', 600 ) );
|
||||
do_action( 'action_scheduler_before_process_queue' );
|
||||
$this->run_cleanup();
|
||||
$count = 0;
|
||||
$processed_actions = 0;
|
||||
if ( $this->store->get_claim_count() < $this->get_allowed_concurrent_batches() ) {
|
||||
$batch_size = apply_filters( 'action_scheduler_queue_runner_batch_size', 25 );
|
||||
$count = $this->do_batch( $batch_size );
|
||||
do {
|
||||
$processed_actions_in_batch = $this->do_batch( $batch_size );
|
||||
$processed_actions += $processed_actions_in_batch;
|
||||
} while ( $processed_actions_in_batch > 0 && ! $this->batch_limits_exceeded( $processed_actions ) ); // keep going until we run out of actions, time, or memory
|
||||
}
|
||||
|
||||
do_action( 'action_scheduler_after_process_queue' );
|
||||
return $count;
|
||||
return $processed_actions;
|
||||
}
|
||||
|
||||
protected function do_batch( $size = 100 ) {
|
||||
$claim = $this->store->stake_claim($size);
|
||||
$this->monitor->attach($claim);
|
||||
$processed_actions = 0;
|
||||
$maximum_execution_time = $this->get_maximum_execution_time();
|
||||
$processed_actions = 0;
|
||||
|
||||
foreach ( $claim->get_actions() as $action_id ) {
|
||||
if ( 0 !== $processed_actions ) {
|
||||
$time_elapsed = $this->get_execution_time();
|
||||
$average_processing_time = $time_elapsed / $processed_actions;
|
||||
|
||||
// Bail early if the time it has taken to process this batch is approaching the maximum execution time.
|
||||
if ( $time_elapsed + ( $average_processing_time * 2 ) > $maximum_execution_time ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// bail if we lost the claim
|
||||
if ( ! in_array( $action_id, $this->store->find_actions_by_claim_id( $claim->get_id() ) ) ) {
|
||||
break;
|
||||
}
|
||||
$this->process_action( $action_id );
|
||||
$processed_actions++;
|
||||
|
||||
if ( $this->batch_limits_exceeded( $processed_actions ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->store->release_claim($claim);
|
||||
$this->monitor->detach();
|
||||
|
@ -127,43 +112,4 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
|
|||
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum number of seconds a batch can run for.
|
||||
*
|
||||
* @return int The number of seconds.
|
||||
*/
|
||||
protected function get_maximum_execution_time() {
|
||||
|
||||
// There are known hosts with a strict 60 second execution time.
|
||||
if ( defined( 'WPENGINE_ACCOUNT' ) || defined( 'PANTHEON_ENVIRONMENT' ) ) {
|
||||
$maximum_execution_time = 60;
|
||||
} elseif ( false !== strpos( getenv( 'HOSTNAME' ), '.siteground.' ) ) {
|
||||
$maximum_execution_time = 120;
|
||||
} else {
|
||||
$maximum_execution_time = ini_get( 'max_execution_time' );
|
||||
}
|
||||
|
||||
return absint( apply_filters( 'action_scheduler_maximum_execution_time', $maximum_execution_time ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of seconds a batch has run for.
|
||||
*
|
||||
* @return int The number of seconds.
|
||||
*/
|
||||
protected function get_execution_time() {
|
||||
$execution_time = microtime( true ) - $this->created_time;
|
||||
|
||||
// Get the CPU time if the hosting environment uses it rather than wall-clock time to calculate a process's execution time.
|
||||
if ( function_exists( 'getrusage' ) && apply_filters( 'action_scheduler_use_cpu_execution_time', defined( 'PANTHEON_ENVIRONMENT' ) ) ) {
|
||||
$resource_usages = getrusage();
|
||||
|
||||
if ( isset( $resource_usages['ru_stime.tv_usec'], $resource_usages['ru_stime.tv_usec'] ) ) {
|
||||
$execution_time = $resource_usages['ru_stime.tv_sec'] + ( $resource_usages['ru_stime.tv_usec'] / 1000000 );
|
||||
}
|
||||
}
|
||||
|
||||
return $execution_time;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,20 +168,11 @@ abstract class ActionScheduler_Store {
|
|||
if ( ! $next ) {
|
||||
throw new InvalidArgumentException( __( 'Invalid schedule. Cannot save action.', 'action-scheduler' ) );
|
||||
}
|
||||
$next->setTimezone( $this->get_local_timezone() );
|
||||
|
||||
ActionScheduler_TimezoneHelper::set_local_timezone( $next );
|
||||
return $next->format( 'Y-m-d H:i:s' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the site's local time. Wrapper for ActionScheduler_TimezoneHelper::get_local_timezone().
|
||||
*
|
||||
* @return DateTimeZone
|
||||
*/
|
||||
protected function get_local_timezone() {
|
||||
return ActionScheduler_TimezoneHelper::get_local_timezone();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
@ -207,4 +198,15 @@ abstract class ActionScheduler_Store {
|
|||
}
|
||||
return self::$store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the site's local time.
|
||||
*
|
||||
* @deprecated 2.1.0
|
||||
* @return DateTimeZone
|
||||
*/
|
||||
protected function get_local_timezone() {
|
||||
_deprecated_function( __FUNCTION__, '2.1.0', 'ActionScheduler_TimezoneHelper::set_local_timezone()' );
|
||||
return ActionScheduler_TimezoneHelper::get_local_timezone();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,102 @@
|
|||
*/
|
||||
abstract class ActionScheduler_TimezoneHelper {
|
||||
private static $local_timezone = NULL;
|
||||
|
||||
/**
|
||||
* Set a DateTime's timezone to the WordPress site's timezone, or a UTC offset
|
||||
* if no timezone string is available.
|
||||
*
|
||||
* @since 2.1.0
|
||||
*
|
||||
* @param DateTime $date
|
||||
* @return ActionScheduler_DateTime
|
||||
*/
|
||||
public static function set_local_timezone( DateTime $date ) {
|
||||
|
||||
// Accept a DateTime for easier backward compatibility, even though we require methods on ActionScheduler_DateTime
|
||||
if ( ! is_a( $date, 'ActionScheduler_DateTime' ) ) {
|
||||
$date = as_get_datetime_object( $date->format( 'U' ) );
|
||||
}
|
||||
|
||||
if ( get_option( 'timezone_string' ) ) {
|
||||
$date->setTimezone( new DateTimeZone( self::get_local_timezone_string() ) );
|
||||
} else {
|
||||
$date->setUtcOffset( self::get_local_timezone_offset() );
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to retrieve the timezone string for a site until a WP core method exists
|
||||
* (see https://core.trac.wordpress.org/ticket/24730).
|
||||
*
|
||||
* Adapted from wc_timezone_string() and https://secure.php.net/manual/en/function.timezone-name-from-abbr.php#89155.
|
||||
*
|
||||
* If no timezone string is set, and its not possible to match the UTC offset set for the site to a timezone
|
||||
* string, then an empty string will be returned, and the UTC offset should be used to set a DateTime's
|
||||
* timezone.
|
||||
*
|
||||
* @since 2.1.0
|
||||
* @return string PHP timezone string for the site or empty if no timezone string is available.
|
||||
*/
|
||||
protected static function get_local_timezone_string( $reset = false ) {
|
||||
// If site timezone string exists, return it.
|
||||
$timezone = get_option( 'timezone_string' );
|
||||
if ( $timezone ) {
|
||||
return $timezone;
|
||||
}
|
||||
|
||||
// Get UTC offset, if it isn't set then return UTC.
|
||||
$utc_offset = intval( get_option( 'gmt_offset', 0 ) );
|
||||
if ( 0 === $utc_offset ) {
|
||||
return 'UTC';
|
||||
}
|
||||
|
||||
// Adjust UTC offset from hours to seconds.
|
||||
$utc_offset *= 3600;
|
||||
|
||||
// Attempt to guess the timezone string from the UTC offset.
|
||||
$timezone = timezone_name_from_abbr( '', $utc_offset );
|
||||
if ( $timezone ) {
|
||||
return $timezone;
|
||||
}
|
||||
|
||||
// Last try, guess timezone string manually.
|
||||
foreach ( timezone_abbreviations_list() as $abbr ) {
|
||||
foreach ( $abbr as $city ) {
|
||||
if ( (bool) date( 'I' ) === (bool) $city['dst'] && $city['timezone_id'] && intval( $city['offset'] ) === $utc_offset ) {
|
||||
return $city['timezone_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No timezone string
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timezone offset in seconds.
|
||||
*
|
||||
* @since 2.1.0
|
||||
* @return float
|
||||
*/
|
||||
protected static function get_local_timezone_offset() {
|
||||
$timezone = get_option( 'timezone_string' );
|
||||
|
||||
if ( $timezone ) {
|
||||
$timezone_object = new DateTimeZone( $timezone );
|
||||
return $timezone_object->getOffset( new DateTime( 'now' ) );
|
||||
} else {
|
||||
return floatval( get_option( 'gmt_offset', 0 ) ) * HOUR_IN_SECONDS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2.1.0
|
||||
*/
|
||||
public static function get_local_timezone( $reset = FALSE ) {
|
||||
_deprecated_function( __FUNCTION__, '2.1.0', 'ActionScheduler_TimezoneHelper::set_local_timezone()' );
|
||||
if ( $reset ) {
|
||||
self::$local_timezone = NULL;
|
||||
}
|
||||
|
@ -18,18 +113,32 @@ abstract class ActionScheduler_TimezoneHelper {
|
|||
$tzstring = 'UTC';
|
||||
} else {
|
||||
$gmt_offset *= HOUR_IN_SECONDS;
|
||||
$tzstring = timezone_name_from_abbr('', $gmt_offset);
|
||||
$tzstring = timezone_name_from_abbr( '', $gmt_offset, 1 );
|
||||
|
||||
// If there's no timezone string, try again with no DST.
|
||||
if ( false === $tzstring ) {
|
||||
$tzstring = timezone_name_from_abbr( '', $gmt_offset, 0 );
|
||||
}
|
||||
|
||||
// Try mapping to the first abbreviation we can find.
|
||||
if ( false === $tzstring ) {
|
||||
$is_dst = date( 'I' );
|
||||
foreach ( timezone_abbreviations_list() as $abbr ) {
|
||||
foreach ( $abbr as $city ) {
|
||||
if ( $city['dst'] == $is_dst && $city['offset'] == $gmt_offset ) {
|
||||
// If there's no valid timezone ID, keep looking.
|
||||
if ( null === $city['timezone_id'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tzstring = $city['timezone_id'];
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we still have no valid string, then fall back to UTC.
|
||||
if ( false === $tzstring ) {
|
||||
$tzstring = 'UTC';
|
||||
}
|
||||
|
@ -41,4 +150,3 @@ abstract class ActionScheduler_TimezoneHelper {
|
|||
return self::$local_timezone;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,15 +18,16 @@ class ActionScheduler_wpCommentLogger extends ActionScheduler_Logger {
|
|||
if ( empty($date) ) {
|
||||
$date = as_get_datetime_object();
|
||||
} else {
|
||||
$date = clone $date;
|
||||
$date = as_get_datetime_object( clone $date );
|
||||
}
|
||||
$comment_id = $this->create_wp_comment( $action_id, $message, $date );
|
||||
return $comment_id;
|
||||
}
|
||||
|
||||
protected function create_wp_comment( $action_id, $message, DateTime $date ) {
|
||||
|
||||
$comment_date_gmt = $date->format('Y-m-d H:i:s');
|
||||
$date->setTimezone( ActionScheduler_TimezoneHelper::get_local_timezone() );
|
||||
ActionScheduler_TimezoneHelper::set_local_timezone( $date );
|
||||
$comment_data = array(
|
||||
'comment_post_ID' => $action_id,
|
||||
'comment_date' => $date->format('Y-m-d H:i:s'),
|
||||
|
@ -51,7 +52,7 @@ class ActionScheduler_wpCommentLogger extends ActionScheduler_Logger {
|
|||
}
|
||||
|
||||
$date = as_get_datetime_object( $comment->comment_date_gmt );
|
||||
$date->setTimezone( ActionScheduler_TimezoneHelper::get_local_timezone() );
|
||||
ActionScheduler_TimezoneHelper::set_local_timezone( $date );
|
||||
return new ActionScheduler_LogEntry( $comment->comment_post_ID, $comment->comment_content, $date );
|
||||
}
|
||||
|
||||
|
@ -217,14 +218,9 @@ class ActionScheduler_wpCommentLogger extends ActionScheduler_Logger {
|
|||
public function init() {
|
||||
add_action( 'action_scheduler_before_process_queue', array( $this, 'disable_comment_counting' ), 10, 0 );
|
||||
add_action( 'action_scheduler_after_process_queue', array( $this, 'enable_comment_counting' ), 10, 0 );
|
||||
add_action( 'action_scheduler_stored_action', array( $this, 'log_stored_action' ), 10, 1 );
|
||||
add_action( 'action_scheduler_canceled_action', array( $this, 'log_canceled_action' ), 10, 1 );
|
||||
add_action( 'action_scheduler_before_execute', array( $this, 'log_started_action' ), 10, 1 );
|
||||
add_action( 'action_scheduler_after_execute', array( $this, 'log_completed_action' ), 10, 1 );
|
||||
add_action( 'action_scheduler_failed_execution', array( $this, 'log_failed_action' ), 10, 2 );
|
||||
add_action( 'action_scheduler_failed_action', array( $this, 'log_timed_out_action' ), 10, 2 );
|
||||
add_action( 'action_scheduler_unexpected_shutdown', array( $this, 'log_unexpected_shutdown' ), 10, 2 );
|
||||
add_action( 'action_scheduler_reset_action', array( $this, 'log_reset_action' ), 10, 1 );
|
||||
|
||||
parent::init();
|
||||
|
||||
add_action( 'pre_get_comments', array( $this, 'filter_comment_queries' ), 10, 1 );
|
||||
add_action( 'wp_count_comments', array( $this, '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( $this, 'filter_comment_feed' ), 10, 2 );
|
||||
|
@ -241,38 +237,4 @@ class ActionScheduler_wpCommentLogger extends ActionScheduler_Logger {
|
|||
wp_defer_comment_counting(false);
|
||||
}
|
||||
|
||||
public function log_stored_action( $action_id ) {
|
||||
$this->log( $action_id, __('action created', 'action-scheduler') );
|
||||
}
|
||||
|
||||
public function log_canceled_action( $action_id ) {
|
||||
$this->log( $action_id, __('action canceled', 'action-scheduler') );
|
||||
}
|
||||
|
||||
public function log_started_action( $action_id ) {
|
||||
$this->log( $action_id, __('action started', 'action-scheduler') );
|
||||
}
|
||||
|
||||
public function log_completed_action( $action_id ) {
|
||||
$this->log( $action_id, __('action complete', 'action-scheduler') );
|
||||
}
|
||||
|
||||
public function log_failed_action( $action_id, Exception $exception ) {
|
||||
$this->log( $action_id, sprintf(__('action failed: %s', 'action-scheduler'), $exception->getMessage() ));
|
||||
}
|
||||
|
||||
public function log_timed_out_action( $action_id, $timeout) {
|
||||
$this->log( $action_id, sprintf( __('action timed out after %s seconds', 'action-scheduler'), $timeout ) );
|
||||
}
|
||||
|
||||
public function log_unexpected_shutdown( $action_id, $error ) {
|
||||
if ( !empty($error) ) {
|
||||
$this->log( $action_id, sprintf(__('unexpected shutdown: PHP Fatal error %s in %s on line %s', 'action-scheduler'), $error['message'], $error['file'], $error['line'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
public function log_reset_action( $action_id ) {
|
||||
$this->log( $action_id, __('action reset', 'action_scheduler') );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,8 +11,12 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|||
/** @var DateTimeZone */
|
||||
protected $local_timezone = NULL;
|
||||
|
||||
/** @var int */
|
||||
private static $max_index_length = 191;
|
||||
|
||||
public function save_action( ActionScheduler_Action $action, DateTime $scheduled_date = NULL ){
|
||||
try {
|
||||
$this->validate_action( $action );
|
||||
$post_array = $this->create_post_array( $action, $scheduled_date );
|
||||
$post_id = $this->save_post_array( $post_array );
|
||||
$this->save_post_schedule( $post_id, $action->get_schedule() );
|
||||
|
@ -91,6 +95,12 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|||
protected function make_action_from_post( $post ) {
|
||||
$hook = $post->post_title;
|
||||
$args = json_decode( $post->post_content, true );
|
||||
|
||||
// Handle args that do not decode properly.
|
||||
if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $args ) ) {
|
||||
throw ActionScheduler_InvalidActionException::from_decoding_args( $post->ID );
|
||||
}
|
||||
|
||||
$schedule = get_post_meta( $post->ID, self::SCHEDULE_META_KEY, true );
|
||||
if ( empty($schedule) ) {
|
||||
$schedule = new ActionScheduler_NullSchedule();
|
||||
|
@ -410,18 +420,18 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|||
* @param string $action_id
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @return DateTime The date the action is schedule to run, or the date that it ran.
|
||||
* @return ActionScheduler_DateTime The date the action is schedule to run, or the date that it ran.
|
||||
*/
|
||||
public function get_date( $action_id ) {
|
||||
$date = $this->get_date_gmt( $action_id );
|
||||
return $date->setTimezone( $this->get_local_timezone() );
|
||||
$next = $this->get_date_gmt( $action_id );
|
||||
return ActionScheduler_TimezoneHelper::set_local_timezone( $next );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action_id
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
* @return DateTime The date the action is schedule to run, or the date that it ran.
|
||||
* @return ActionScheduler_DateTime The date the action is schedule to run, or the date that it ran.
|
||||
*/
|
||||
public function get_date_gmt( $action_id ) {
|
||||
$post = get_post($action_id);
|
||||
|
@ -716,6 +726,21 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* InnoDB indexes have a maximum size of 767 bytes by default, which is only 191 characters with utf8mb4.
|
||||
*
|
||||
* Previously, AS wasn't concerned about args length, as we used the (unindex) post_content column. However,
|
||||
* as we prepare to move to custom tables, and can use an indexed VARCHAR column instead, we want to warn
|
||||
* developers of this impending requirement.
|
||||
*
|
||||
* @param ActionScheduler_Action $action
|
||||
*/
|
||||
protected function validate_action( ActionScheduler_Action $action ) {
|
||||
if ( strlen( json_encode( $action->get_args() ) ) > self::$max_index_length ) {
|
||||
_doing_it_wrong( 'ActionScheduler_Action::$args', sprintf( 'To ensure the action args column can be indexed, action args should not be more than %d characters when encoded as JSON. Support for strings longer than this will be removed in a future version.', self::$max_index_length ), '2.1.0' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"name": "prospress/action-scheduler",
|
||||
"version": "2.0.0",
|
||||
"description": "Action Scheduler for WordPress and WooCommerce",
|
||||
"type": "wordpress-plugin",
|
||||
"license": "GPL-3.0",
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Deprecated API functions for scheduling actions
|
||||
*
|
||||
* Functions with the wc prefix were deprecated to avoid confusion with
|
||||
* Action Scheduler being included in WooCommerce core, and it providing
|
||||
* a different set of APIs for working with the action queue.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Schedule an action to run one time
|
||||
*
|
||||
* @param int $timestamp When the job will run
|
||||
* @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 string The job ID
|
||||
*/
|
||||
function wc_schedule_single_action( $timestamp, $hook, $args = array(), $group = '' ) {
|
||||
_deprecated_function( __FUNCTION__, '2.1.0', 'as_schedule_single_action()' );
|
||||
return as_schedule_single_action( $timestamp, $hook, $args, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a recurring action
|
||||
*
|
||||
* @param int $timestamp When the first instance of the job will run
|
||||
* @param int $interval_in_seconds How long to wait between runs
|
||||
* @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
|
||||
*
|
||||
* @deprecated 2.1.0
|
||||
*
|
||||
* @return string The job ID
|
||||
*/
|
||||
function wc_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '' ) {
|
||||
_deprecated_function( __FUNCTION__, '2.1.0', 'as_schedule_recurring_action()' );
|
||||
return as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule an action that recurs on a cron-like schedule.
|
||||
*
|
||||
* @param int $timestamp The schedule will start on or after this time
|
||||
* @param string $schedule A cron-link schedule string
|
||||
* @see http://en.wikipedia.org/wiki/Cron
|
||||
* * * * * * *
|
||||
* ┬ ┬ ┬ ┬ ┬ ┬
|
||||
* | | | | | |
|
||||
* | | | | | + year [optional]
|
||||
* | | | | +----- day of week (0 - 7) (Sunday=0 or 7)
|
||||
* | | | +---------- month (1 - 12)
|
||||
* | | +--------------- day of month (1 - 31)
|
||||
* | +-------------------- hour (0 - 23)
|
||||
* +------------------------- min (0 - 59)
|
||||
* @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
|
||||
*
|
||||
* @deprecated 2.1.0
|
||||
*
|
||||
* @return string The job ID
|
||||
*/
|
||||
function wc_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '' ) {
|
||||
_deprecated_function( __FUNCTION__, '2.1.0', 'as_schedule_cron_action()' );
|
||||
return as_schedule_cron_action( $timestamp, $schedule, $hook, $args, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the next occurrence of a job.
|
||||
*
|
||||
* @param string $hook The hook that the job will trigger
|
||||
* @param array $args Args that would have been passed to the job
|
||||
* @param string $group
|
||||
*
|
||||
* @deprecated 2.1.0
|
||||
*/
|
||||
function wc_unschedule_action( $hook, $args = array(), $group = '' ) {
|
||||
_deprecated_function( __FUNCTION__, '2.1.0', 'as_unschedule_action()' );
|
||||
as_unschedule_action( $hook, $args, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hook
|
||||
* @param array $args
|
||||
* @param string $group
|
||||
*
|
||||
* @deprecated 2.1.0
|
||||
*
|
||||
* @return int|bool The timestamp for the next occurrence, or false if nothing was found
|
||||
*/
|
||||
function wc_next_scheduled_action( $hook, $args = NULL, $group = '' ) {
|
||||
_deprecated_function( __FUNCTION__, '2.1.0', 'as_next_scheduled_action()' );
|
||||
return as_next_scheduled_action( $hook, $args, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Find scheduled actions
|
||||
*
|
||||
* @param array $args Possible arguments, with their default values:
|
||||
* 'hook' => '' - the name of the action that will be triggered
|
||||
* 'args' => NULL - the args array that will be passed with the action
|
||||
* 'date' => NULL - the scheduled date of the action. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone.
|
||||
* 'date_compare' => '<=' - operator for testing "date". accepted values are '!=', '>', '>=', '<', '<=', '='
|
||||
* 'modified' => NULL - the date the action was last updated. Expects a DateTime object, a unix timestamp, or a string that can parsed with strtotime(). Used in UTC timezone.
|
||||
* 'modified_compare' => '<=' - operator for testing "modified". accepted values are '!=', '>', '>=', '<', '<=', '='
|
||||
* 'group' => '' - the group the action belongs to
|
||||
* 'status' => '' - ActionScheduler_Store::STATUS_COMPLETE or ActionScheduler_Store::STATUS_PENDING
|
||||
* 'claimed' => NULL - TRUE to find claimed actions, FALSE to find unclaimed actions, a string to find a specific claim ID
|
||||
* 'per_page' => 5 - Number of results to return
|
||||
* 'offset' => 0
|
||||
* 'orderby' => 'date' - accepted values are 'hook', 'group', 'modified', or 'date'
|
||||
* 'order' => 'ASC'
|
||||
* @param string $return_format OBJECT, ARRAY_A, or ids
|
||||
*
|
||||
* @deprecated 2.1.0
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function wc_get_scheduled_actions( $args = array(), $return_format = OBJECT ) {
|
||||
_deprecated_function( __FUNCTION__, '2.1.0', 'as_get_scheduled_actions()' );
|
||||
return as_get_scheduled_actions( $args, $return_format );
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
*
|
||||
* @return string The job ID
|
||||
*/
|
||||
function wc_schedule_single_action( $timestamp, $hook, $args = array(), $group = '' ) {
|
||||
function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '' ) {
|
||||
return ActionScheduler::factory()->single( $hook, $args, $timestamp, $group );
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ function wc_schedule_single_action( $timestamp, $hook, $args = array(), $group =
|
|||
*
|
||||
* @return string The job ID
|
||||
*/
|
||||
function wc_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '' ) {
|
||||
function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '' ) {
|
||||
return ActionScheduler::factory()->recurring( $hook, $args, $timestamp, $interval_in_seconds, $group );
|
||||
}
|
||||
|
||||
|
@ -54,18 +54,27 @@ function wc_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook,
|
|||
*
|
||||
* @return string The job ID
|
||||
*/
|
||||
function wc_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '' ) {
|
||||
function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '' ) {
|
||||
return ActionScheduler::factory()->cron( $hook, $args, $timestamp, $schedule, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the next occurrence of a job.
|
||||
* Cancel the next occurrence of a scheduled action.
|
||||
*
|
||||
* While only the next instance of a recurring or cron action is unscheduled by this method, that will also prevent
|
||||
* all future instances of that recurring or cron action from being run. Recurring and cron actions are scheduled in
|
||||
* a sequence instead of all being scheduled at once. Each successive occurrence of a recurring action is scheduled
|
||||
* only after the former action is run. If the next instance is never run, because it's unscheduled by this function,
|
||||
* then the following instance will never be scheduled (or exist), which is effectively the same as being unscheduled
|
||||
* by this method also.
|
||||
*
|
||||
* @param string $hook The hook that the job will trigger
|
||||
* @param array $args Args that would have been passed to the job
|
||||
* @param string $group
|
||||
*
|
||||
* @return string The scheduled action ID if a scheduled action was found, or empty string if no matching action found.
|
||||
*/
|
||||
function wc_unschedule_action( $hook, $args = array(), $group = '' ) {
|
||||
function as_unschedule_action( $hook, $args = array(), $group = '' ) {
|
||||
$params = array();
|
||||
if ( is_array($args) ) {
|
||||
$params['args'] = $args;
|
||||
|
@ -74,11 +83,25 @@ function wc_unschedule_action( $hook, $args = array(), $group = '' ) {
|
|||
$params['group'] = $group;
|
||||
}
|
||||
$job_id = ActionScheduler::store()->find_action( $hook, $params );
|
||||
if ( empty($job_id) ) {
|
||||
return;
|
||||
|
||||
if ( ! empty( $job_id ) ) {
|
||||
ActionScheduler::store()->cancel_action( $job_id );
|
||||
}
|
||||
|
||||
ActionScheduler::store()->cancel_action( $job_id );
|
||||
return $job_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all occurrences of a scheduled action.
|
||||
*
|
||||
* @param string $hook The hook that the job will trigger
|
||||
* @param array $args Args that would have been passed to the job
|
||||
* @param string $group
|
||||
*/
|
||||
function as_unschedule_all_actions( $hook, $args = array(), $group = '' ) {
|
||||
do {
|
||||
$unscheduled_action = as_unschedule_action( $hook, $args, $group );
|
||||
} while ( ! empty( $unscheduled_action ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,7 +111,7 @@ function wc_unschedule_action( $hook, $args = array(), $group = '' ) {
|
|||
*
|
||||
* @return int|bool The timestamp for the next occurrence, or false if nothing was found
|
||||
*/
|
||||
function wc_next_scheduled_action( $hook, $args = NULL, $group = '' ) {
|
||||
function as_next_scheduled_action( $hook, $args = NULL, $group = '' ) {
|
||||
$params = array();
|
||||
if ( is_array($args) ) {
|
||||
$params['args'] = $args;
|
||||
|
@ -130,7 +153,7 @@ function wc_next_scheduled_action( $hook, $args = NULL, $group = '' ) {
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
function wc_get_scheduled_actions( $args = array(), $return_format = OBJECT ) {
|
||||
function as_get_scheduled_actions( $args = array(), $return_format = OBJECT ) {
|
||||
$store = ActionScheduler::store();
|
||||
foreach ( array('date', 'modified') as $key ) {
|
||||
if ( isset($args[$key]) ) {
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group timezone
|
||||
*/
|
||||
class ActionScheduler_TimezoneHelper_Test extends ActionScheduler_UnitTestCase {
|
||||
|
||||
/**
|
||||
* Ensure that the timezone string we expect works properly.
|
||||
*
|
||||
* @dataProvider local_timezone_provider
|
||||
*
|
||||
* @param $timezone_string
|
||||
*/
|
||||
public function test_local_timezone_strings( $timezone_string ) {
|
||||
$timezone_filter = function ( $tz ) use ( $timezone_string ) {
|
||||
return $timezone_string;
|
||||
};
|
||||
|
||||
add_filter( 'option_timezone_string', $timezone_filter );
|
||||
|
||||
$date = new ActionScheduler_DateTime();
|
||||
$timezone = ActionScheduler_TimezoneHelper::set_local_timezone( $date )->getTimezone();
|
||||
$this->assertInstanceOf( 'DateTimeZone', $timezone );
|
||||
$this->assertEquals( $timezone_string, $timezone->getName() );
|
||||
|
||||
remove_filter( 'option_timezone_string', $timezone_filter );
|
||||
}
|
||||
|
||||
public function local_timezone_provider() {
|
||||
return array(
|
||||
array( 'America/New_York' ),
|
||||
array( 'Australia/Melbourne' ),
|
||||
array( 'UTC' ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that most GMT offsets don't return UTC as the timezone.
|
||||
*
|
||||
* @dataProvider local_timezone_offsets_provider
|
||||
*
|
||||
* @param $gmt_offset
|
||||
*/
|
||||
public function test_local_timezone_offsets( $gmt_offset ) {
|
||||
$gmt_filter = function ( $gmt ) use ( $gmt_offset ) {
|
||||
return $gmt_offset;
|
||||
};
|
||||
|
||||
$date = new ActionScheduler_DateTime();
|
||||
|
||||
add_filter( 'option_gmt_offset', $gmt_filter );
|
||||
ActionScheduler_TimezoneHelper::set_local_timezone( $date );
|
||||
remove_filter( 'option_gmt_offset', $gmt_filter );
|
||||
|
||||
$offset_in_seconds = $gmt_offset * HOUR_IN_SECONDS;
|
||||
|
||||
$this->assertEquals( $offset_in_seconds, $date->getOffset() );
|
||||
$this->assertEquals( $offset_in_seconds, $date->getOffsetTimestamp() - $date->getTimestamp() );
|
||||
}
|
||||
|
||||
public function local_timezone_offsets_provider() {
|
||||
return array(
|
||||
array( '-11' ),
|
||||
array( '-10.5' ),
|
||||
array( '-10' ),
|
||||
array( '-9' ),
|
||||
array( '-8' ),
|
||||
array( '-7' ),
|
||||
array( '-6' ),
|
||||
array( '-5' ),
|
||||
array( '-4.5' ),
|
||||
array( '-4' ),
|
||||
array( '-3.5' ),
|
||||
array( '-3' ),
|
||||
array( '-2' ),
|
||||
array( '-1' ),
|
||||
array( '1' ),
|
||||
array( '1.5' ),
|
||||
array( '2' ),
|
||||
array( '3' ),
|
||||
array( '4' ),
|
||||
array( '5' ),
|
||||
array( '5.5' ),
|
||||
array( '5.75' ),
|
||||
array( '6' ),
|
||||
array( '7' ),
|
||||
array( '8' ),
|
||||
array( '8.5' ),
|
||||
array( '9' ),
|
||||
array( '9.5' ),
|
||||
array( '10' ),
|
||||
array( '10.5' ),
|
||||
array( '11' ),
|
||||
array( '11.5' ),
|
||||
array( '12' ),
|
||||
array( '13' ),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -41,6 +41,29 @@ class ActionScheduler_wpPostStore_Test extends ActionScheduler_UnitTestCase {
|
|||
$this->assertEquals($action->get_group(), $retrieved->get_group());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException ActionScheduler_InvalidActionException
|
||||
* @dataProvider provide_bad_args
|
||||
*
|
||||
* @param string $content
|
||||
*/
|
||||
public function test_action_bad_args( $content ) {
|
||||
$store = new ActionScheduler_wpPostStore();
|
||||
$post_id = wp_insert_post( array(
|
||||
'post_type' => ActionScheduler_wpPostStore::POST_TYPE,
|
||||
'post_status' => ActionScheduler_Store::STATUS_PENDING,
|
||||
'post_content' => $content,
|
||||
) );
|
||||
|
||||
$store->fetch_action( $post_id );
|
||||
}
|
||||
|
||||
public function provide_bad_args() {
|
||||
return array(
|
||||
array( '{"bad_json":true}}' ),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_cancel_action() {
|
||||
$time = as_get_datetime_object();
|
||||
$schedule = new ActionScheduler_SimpleSchedule($time);
|
||||
|
|
|
@ -12,7 +12,7 @@ class ActionScheduler_wpCommentLogger_Test extends ActionScheduler_UnitTestCase
|
|||
}
|
||||
|
||||
public function test_add_log_entry() {
|
||||
$action_id = wc_schedule_single_action( time(), 'a hook' );
|
||||
$action_id = as_schedule_single_action( time(), 'a hook' );
|
||||
$logger = ActionScheduler::logger();
|
||||
$message = 'Logging that something happened';
|
||||
$log_id = $logger->log( $action_id, $message );
|
||||
|
@ -22,6 +22,25 @@ class ActionScheduler_wpCommentLogger_Test extends ActionScheduler_UnitTestCase
|
|||
$this->assertEquals( $message, $entry->get_message() );
|
||||
}
|
||||
|
||||
public function test_add_log_datetime() {
|
||||
$action_id = as_schedule_single_action( time(), 'a hook' );
|
||||
$logger = ActionScheduler::logger();
|
||||
$message = 'Logging that something happened';
|
||||
$date = new DateTime( 'now', new DateTimeZone( 'UTC' ) );
|
||||
$log_id = $logger->log( $action_id, $message, $date );
|
||||
$entry = $logger->get_entry( $log_id );
|
||||
|
||||
$this->assertEquals( $action_id, $entry->get_action_id() );
|
||||
$this->assertEquals( $message, $entry->get_message() );
|
||||
|
||||
$date = new ActionScheduler_DateTime( 'now', new DateTimeZone( 'UTC' ) );
|
||||
$log_id = $logger->log( $action_id, $message, $date );
|
||||
$entry = $logger->get_entry( $log_id );
|
||||
|
||||
$this->assertEquals( $action_id, $entry->get_action_id() );
|
||||
$this->assertEquals( $message, $entry->get_message() );
|
||||
}
|
||||
|
||||
public function test_null_log_entry() {
|
||||
$logger = ActionScheduler::logger();
|
||||
$entry = $logger->get_entry( 1 );
|
||||
|
@ -42,7 +61,7 @@ class ActionScheduler_wpCommentLogger_Test extends ActionScheduler_UnitTestCase
|
|||
}
|
||||
|
||||
public function test_storage_comments() {
|
||||
$action_id = wc_schedule_single_action( time(), 'a hook' );
|
||||
$action_id = as_schedule_single_action( time(), 'a hook' );
|
||||
$logger = ActionScheduler::logger();
|
||||
$logs = $logger->get_logs( $action_id );
|
||||
$expected = new ActionScheduler_LogEntry( $action_id, 'action created' );
|
||||
|
@ -62,7 +81,7 @@ class ActionScheduler_wpCommentLogger_Test extends ActionScheduler_UnitTestCase
|
|||
}
|
||||
|
||||
public function test_execution_comments() {
|
||||
$action_id = wc_schedule_single_action( time(), 'a hook' );
|
||||
$action_id = as_schedule_single_action( time(), 'a hook' );
|
||||
$logger = ActionScheduler::logger();
|
||||
$started = new ActionScheduler_LogEntry( $action_id, 'action started' );
|
||||
$finished = new ActionScheduler_LogEntry( $action_id, 'action complete' );
|
||||
|
@ -78,7 +97,7 @@ class ActionScheduler_wpCommentLogger_Test extends ActionScheduler_UnitTestCase
|
|||
public function test_failed_execution_comments() {
|
||||
$hook = md5(rand());
|
||||
add_action( $hook, array( $this, '_a_hook_callback_that_throws_an_exception' ) );
|
||||
$action_id = wc_schedule_single_action( time(), $hook );
|
||||
$action_id = as_schedule_single_action( time(), $hook );
|
||||
$logger = ActionScheduler::logger();
|
||||
$started = new ActionScheduler_LogEntry( $action_id, 'action started' );
|
||||
$finished = new ActionScheduler_LogEntry( $action_id, 'action complete' );
|
||||
|
@ -95,7 +114,7 @@ class ActionScheduler_wpCommentLogger_Test extends ActionScheduler_UnitTestCase
|
|||
|
||||
public function test_fatal_error_comments() {
|
||||
$hook = md5(rand());
|
||||
$action_id = wc_schedule_single_action( time(), $hook );
|
||||
$action_id = as_schedule_single_action( time(), $hook );
|
||||
$logger = ActionScheduler::logger();
|
||||
do_action( 'action_scheduler_unexpected_shutdown', $action_id, array(
|
||||
'type' => E_ERROR,
|
||||
|
@ -115,8 +134,8 @@ class ActionScheduler_wpCommentLogger_Test extends ActionScheduler_UnitTestCase
|
|||
}
|
||||
|
||||
public function test_canceled_action_comments() {
|
||||
$action_id = wc_schedule_single_action( time(), 'a hook' );
|
||||
wc_unschedule_action( 'a hook' );
|
||||
$action_id = as_schedule_single_action( time(), 'a hook' );
|
||||
as_unschedule_action( 'a hook' );
|
||||
$logger = ActionScheduler::logger();
|
||||
$logs = $logger->get_logs( $action_id );
|
||||
$expected = new ActionScheduler_LogEntry( $action_id, 'action canceled' );
|
||||
|
@ -143,7 +162,7 @@ class ActionScheduler_wpCommentLogger_Test extends ActionScheduler_UnitTestCase
|
|||
$this->assertEquals( $comment_id, $comments[0]->comment_ID );
|
||||
|
||||
|
||||
$action_id = wc_schedule_single_action( time(), 'a hook' );
|
||||
$action_id = as_schedule_single_action( time(), 'a hook' );
|
||||
$logger = ActionScheduler::logger();
|
||||
$message = 'Logging that something happened';
|
||||
$log_id = $logger->log( $action_id, $message );
|
||||
|
|
|
@ -8,7 +8,7 @@ class procedural_api_Test extends ActionScheduler_UnitTestCase {
|
|||
public function test_schedule_action() {
|
||||
$time = time();
|
||||
$hook = md5(rand());
|
||||
$action_id = wc_schedule_single_action( $time, $hook );
|
||||
$action_id = as_schedule_single_action( $time, $hook );
|
||||
|
||||
$store = ActionScheduler::store();
|
||||
$action = $store->fetch_action($action_id);
|
||||
|
@ -19,7 +19,7 @@ class procedural_api_Test extends ActionScheduler_UnitTestCase {
|
|||
public function test_recurring_action() {
|
||||
$time = time();
|
||||
$hook = md5(rand());
|
||||
$action_id = wc_schedule_recurring_action( $time, HOUR_IN_SECONDS, $hook );
|
||||
$action_id = as_schedule_recurring_action( $time, HOUR_IN_SECONDS, $hook );
|
||||
|
||||
$store = ActionScheduler::store();
|
||||
$action = $store->fetch_action($action_id);
|
||||
|
@ -31,7 +31,7 @@ class procedural_api_Test extends ActionScheduler_UnitTestCase {
|
|||
public function test_cron_schedule() {
|
||||
$time = as_get_datetime_object('2014-01-01');
|
||||
$hook = md5(rand());
|
||||
$action_id = wc_schedule_cron_action( $time->getTimestamp(), '0 0 10 10 *', $hook );
|
||||
$action_id = as_schedule_cron_action( $time->getTimestamp(), '0 0 10 10 *', $hook );
|
||||
|
||||
$store = ActionScheduler::store();
|
||||
$action = $store->fetch_action($action_id);
|
||||
|
@ -43,28 +43,108 @@ class procedural_api_Test extends ActionScheduler_UnitTestCase {
|
|||
public function test_get_next() {
|
||||
$time = as_get_datetime_object('tomorrow');
|
||||
$hook = md5(rand());
|
||||
wc_schedule_recurring_action( $time->getTimestamp(), HOUR_IN_SECONDS, $hook );
|
||||
as_schedule_recurring_action( $time->getTimestamp(), HOUR_IN_SECONDS, $hook );
|
||||
|
||||
$next = wc_next_scheduled_action( $hook );
|
||||
$next = as_next_scheduled_action( $hook );
|
||||
|
||||
$this->assertEquals( $time->getTimestamp(), $next );
|
||||
}
|
||||
|
||||
public function test_unschedule() {
|
||||
$time = time();
|
||||
$hook = md5(rand());
|
||||
$action_id = wc_schedule_single_action( $time, $hook );
|
||||
public function provider_time_hook_args_group() {
|
||||
$time = time() + 60 * 2;
|
||||
$hook = md5( rand() );
|
||||
$args = array( rand(), rand() );
|
||||
$group = 'test_group';
|
||||
|
||||
wc_unschedule_action( $hook );
|
||||
return array(
|
||||
|
||||
$next = wc_next_scheduled_action( $hook );
|
||||
// Test with no args or group
|
||||
array(
|
||||
'time' => $time,
|
||||
'hook' => $hook,
|
||||
'args' => array(),
|
||||
'group' => '',
|
||||
),
|
||||
|
||||
// Test with args but no group
|
||||
array(
|
||||
'time' => $time,
|
||||
'hook' => $hook,
|
||||
'args' => $args,
|
||||
'group' => '',
|
||||
),
|
||||
|
||||
// Test with group but no args
|
||||
array(
|
||||
'time' => $time,
|
||||
'hook' => $hook,
|
||||
'args' => array(),
|
||||
'group' => $group,
|
||||
),
|
||||
|
||||
// Test with args & group
|
||||
array(
|
||||
'time' => $time,
|
||||
'hook' => $hook,
|
||||
'args' => $args,
|
||||
'group' => $group,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provider_time_hook_args_group
|
||||
*/
|
||||
public function test_unschedule( $time, $hook, $args, $group ) {
|
||||
|
||||
$action_id_unscheduled = as_schedule_single_action( $time, $hook, $args, $group );
|
||||
$action_scheduled_time = $time + 1;
|
||||
$action_id_scheduled = as_schedule_single_action( $action_scheduled_time, $hook, $args, $group );
|
||||
|
||||
as_unschedule_action( $hook, $args, $group );
|
||||
|
||||
$next = as_next_scheduled_action( $hook, $args, $group );
|
||||
$this->assertEquals( $action_scheduled_time, $next );
|
||||
|
||||
$store = ActionScheduler::store();
|
||||
$unscheduled_action = $store->fetch_action( $action_id_unscheduled );
|
||||
|
||||
// Make sure the next scheduled action is unscheduled
|
||||
$this->assertEquals( $hook, $unscheduled_action->get_hook() );
|
||||
$this->assertNull( $unscheduled_action->get_schedule()->next() );
|
||||
|
||||
// Make sure other scheduled actions are not unscheduled
|
||||
$scheduled_action = $store->fetch_action( $action_id_scheduled );
|
||||
|
||||
$this->assertEquals( $hook, $scheduled_action->get_hook() );
|
||||
$this->assertEquals( $action_scheduled_time, $scheduled_action->get_schedule()->next()->getTimestamp() );
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provider_time_hook_args_group
|
||||
*/
|
||||
public function test_unschedule_all( $time, $hook, $args, $group ) {
|
||||
|
||||
$hook = md5( $hook );
|
||||
$action_ids = array();
|
||||
|
||||
for ( $i = 0; $i < 3; $i++ ) {
|
||||
$action_ids[] = as_schedule_single_action( $time, $hook, $args, $group );
|
||||
}
|
||||
|
||||
as_unschedule_all_actions( $hook, $args, $group );
|
||||
|
||||
$next = as_next_scheduled_action( $hook );
|
||||
$this->assertFalse($next);
|
||||
|
||||
$store = ActionScheduler::store();
|
||||
$action = $store->fetch_action($action_id);
|
||||
|
||||
$this->assertNull($action->get_schedule()->next());
|
||||
$this->assertEquals($hook, $action->get_hook() );
|
||||
foreach ( $action_ids as $action_id ) {
|
||||
$action = $store->fetch_action($action_id);
|
||||
|
||||
$this->assertNull($action->get_schedule()->next());
|
||||
$this->assertEquals($hook, $action->get_hook() );
|
||||
}
|
||||
}
|
||||
|
||||
public function test_as_get_datetime_object_default() {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class wc_get_scheduled_actions_Test
|
||||
* Class as_get_scheduled_actions_Test
|
||||
*/
|
||||
class wc_get_scheduled_actions_Test extends ActionScheduler_UnitTestCase {
|
||||
class as_get_scheduled_actions_Test extends ActionScheduler_UnitTestCase {
|
||||
private $hooks = array();
|
||||
private $args = array();
|
||||
private $groups = array();
|
||||
|
@ -30,13 +30,13 @@ class wc_get_scheduled_actions_Test extends ActionScheduler_UnitTestCase {
|
|||
}
|
||||
|
||||
public function test_date_queries() {
|
||||
$actions = wc_get_scheduled_actions(array(
|
||||
$actions = as_get_scheduled_actions(array(
|
||||
'date' => as_get_datetime_object(gmdate('Y-m-d 00:00:00')),
|
||||
'per_page' => -1,
|
||||
), 'ids');
|
||||
$this->assertCount(30, $actions);
|
||||
|
||||
$actions = wc_get_scheduled_actions(array(
|
||||
$actions = as_get_scheduled_actions(array(
|
||||
'date' => as_get_datetime_object(gmdate('Y-m-d 00:00:00')),
|
||||
'date_compare' => '>=',
|
||||
'per_page' => -1,
|
||||
|
@ -45,13 +45,13 @@ class wc_get_scheduled_actions_Test extends ActionScheduler_UnitTestCase {
|
|||
}
|
||||
|
||||
public function test_hook_queries() {
|
||||
$actions = wc_get_scheduled_actions(array(
|
||||
$actions = as_get_scheduled_actions(array(
|
||||
'hook' => $this->hooks[2],
|
||||
'per_page' => -1,
|
||||
), 'ids');
|
||||
$this->assertCount(10, $actions);
|
||||
|
||||
$actions = wc_get_scheduled_actions(array(
|
||||
$actions = as_get_scheduled_actions(array(
|
||||
'hook' => $this->hooks[2],
|
||||
'date' => as_get_datetime_object(gmdate('Y-m-d 00:00:00')),
|
||||
'per_page' => -1,
|
||||
|
@ -60,20 +60,20 @@ class wc_get_scheduled_actions_Test extends ActionScheduler_UnitTestCase {
|
|||
}
|
||||
|
||||
public function test_args_queries() {
|
||||
$actions = wc_get_scheduled_actions(array(
|
||||
$actions = as_get_scheduled_actions(array(
|
||||
'args' => array($this->args[5]),
|
||||
'per_page' => -1,
|
||||
), 'ids');
|
||||
$this->assertCount(10, $actions);
|
||||
|
||||
$actions = wc_get_scheduled_actions(array(
|
||||
$actions = as_get_scheduled_actions(array(
|
||||
'args' => array($this->args[5]),
|
||||
'hook' => $this->hooks[3],
|
||||
'per_page' => -1,
|
||||
), 'ids');
|
||||
$this->assertCount(1, $actions);
|
||||
|
||||
$actions = wc_get_scheduled_actions(array(
|
||||
$actions = as_get_scheduled_actions(array(
|
||||
'args' => array($this->args[5]),
|
||||
'hook' => $this->hooks[3],
|
||||
'date' => as_get_datetime_object(gmdate('Y-m-d 00:00:00')),
|
||||
|
@ -83,13 +83,13 @@ class wc_get_scheduled_actions_Test extends ActionScheduler_UnitTestCase {
|
|||
}
|
||||
|
||||
public function test_group_queries() {
|
||||
$actions = wc_get_scheduled_actions(array(
|
||||
$actions = as_get_scheduled_actions(array(
|
||||
'group' => $this->groups[1],
|
||||
'per_page' => -1,
|
||||
), 'ids');
|
||||
$this->assertCount(10, $actions);
|
||||
|
||||
$actions = wc_get_scheduled_actions(array(
|
||||
$actions = as_get_scheduled_actions(array(
|
||||
'group' => $this->groups[1],
|
||||
'hook' => $this->hooks[9],
|
||||
'per_page' => -1,
|
||||
|
|
Loading…
Reference in New Issue