Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use php 8.4 for phpstan github workflow and fix all deprecation errors #7220

Merged
merged 15 commits into from
Jan 27, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/workflows/lint_phpstan.yml
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ on:
- feature/*
- enhancement/*
- fix/*
- dev/*

jobs:
run:
@@ -18,7 +19,7 @@ jobs:
fail-fast: false
matrix:
operating-system: [ubuntu-latest]
php-versions: ['8.2']
php-versions: ['8.4']

name: WPRocket lint with PHP Stan. PHP ${{ matrix.php-versions }} on ${{ matrix.operating-system }}.
steps:
@@ -36,5 +37,5 @@ jobs:
run: composer install --prefer-dist --no-interaction --no-scripts

- name: Lint with PHP Stan
run: composer run-stan -- --error-format=github
run: composer run-stan

2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -67,7 +67,7 @@
"psr/container": "1.1.1",
"roave/security-advisories": "dev-master",
"szepeviktor/phpstan-wordpress": "^1.3",
"woocommerce/action-scheduler": "^3.8",
"woocommerce/action-scheduler": "^3.9",
"wp-coding-standards/wpcs": "^3",
"wp-media/background-processing": "^1.3",
"wp-media/monolog": "^0.0",
18 changes: 9 additions & 9 deletions inc/Dependencies/ActionScheduler/action-scheduler.php
Original file line number Diff line number Diff line change
@@ -5,11 +5,11 @@
* Description: A robust scheduling library for use in WordPress plugins.
* Author: Automattic
* Author URI: https://automattic.com/
* Version: 3.8.1
* Version: 3.9.1
* License: GPLv3
* Requires at least: 6.2
* Requires at least: 6.5
* Tested up to: 6.5
* Requires PHP: 5.6
* Requires PHP: 7.1
*
* Copyright 2019 Automattic, Inc. (https://automattic.com/contact/)
*
@@ -29,29 +29,29 @@
* @package ActionScheduler
*/

if ( ! function_exists( 'action_scheduler_register_3_dot_8_dot_1' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
if ( ! function_exists( 'action_scheduler_register_3_dot_9_dot_1' ) && 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_8_dot_1', 0, 0 ); // WRCS: DEFINED_VERSION.
add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_9_dot_1', 0, 0 ); // WRCS: DEFINED_VERSION.

// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
/**
* Registers this version of Action Scheduler.
*/
function action_scheduler_register_3_dot_8_dot_1() { // WRCS: DEFINED_VERSION.
function action_scheduler_register_3_dot_9_dot_1() { // WRCS: DEFINED_VERSION.
$versions = ActionScheduler_Versions::instance();
$versions->register( '3.8.1', 'action_scheduler_initialize_3_dot_8_dot_1' ); // WRCS: DEFINED_VERSION.
$versions->register( '3.9.1', 'action_scheduler_initialize_3_dot_9_dot_1' ); // WRCS: DEFINED_VERSION.
}

// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
/**
* Initializes this version of Action Scheduler.
*/
function action_scheduler_initialize_3_dot_8_dot_1() { // WRCS: DEFINED_VERSION.
function action_scheduler_initialize_3_dot_9_dot_1() { // 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).
@@ -63,7 +63,7 @@ function action_scheduler_initialize_3_dot_8_dot_1() { // WRCS: DEFINED_VERSION.

// 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_8_dot_1(); // WRCS: DEFINED_VERSION.
action_scheduler_initialize_3_dot_9_dot_1(); // WRCS: DEFINED_VERSION.
do_action( 'action_scheduler_pre_theme_init' );
ActionScheduler_Versions::initialize_latest_version();
}
21 changes: 21 additions & 0 deletions inc/Dependencies/ActionScheduler/changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
*** Changelog ***

= 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.
Original file line number Diff line number Diff line change
@@ -4,20 +4,42 @@
* 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->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;
}
}

Original file line number Diff line number Diff line change
@@ -8,17 +8,17 @@ 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 $schedule The action's schedule.
* @param string $group A group to put the action in.
* @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.
* @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 = '' ) {
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;
@@ -307,6 +307,7 @@ public function create( array $options = array() ) {
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;
}
@@ -318,6 +319,7 @@ public function create( array $options = array() ) {
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. */
132 changes: 95 additions & 37 deletions inc/Dependencies/ActionScheduler/classes/ActionScheduler_AdminView.php
Original file line number Diff line number Diff line change
@@ -2,36 +2,55 @@

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

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

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

/** @var ActionScheduler_ListTable */
/**
* 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');
$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' ) || false == DOING_AJAX ) ) {
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' ) );
@@ -45,6 +64,9 @@ public function init() {
}
}

/**
* Print system status report.
*/
public function system_status_report() {
$table = new ActionScheduler_wcSystemStatus( ActionScheduler::store() );
$table->render();
@@ -78,7 +100,7 @@ public function register_menu() {
'action-scheduler',
array( $this, 'render_admin_ui' )
);
add_action( 'load-' . $hook_suffix , array( $this, 'process_admin_ui' ) );
add_action( 'load-' . $hook_suffix, array( $this, 'process_admin_ui' ) );
}

/**
@@ -119,33 +141,31 @@ protected function get_list_table() {
*/
public function maybe_check_pastdue_actions() {

# Filter to prevent checking actions (ex: inappropriate user).
// 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.
// Get last check transient.
$last_check = get_transient( 'action_scheduler_last_pastdue_actions_check' );

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

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

/**
* Check past-due actions, and print notice.
*
* @todo update $link_url to "Past-due" filter when released (see issue #510, PR #511)
*/
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 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;
@@ -158,53 +178,65 @@ protected function check_pastdue_actions() {
return;
}

# Scheduled actions query arguments.
// 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 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' );
$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 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 );
$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 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' ) );
$actions_url = add_query_arg(
array(
'page' => 'action-scheduler',
'status' => 'past-due',
'order' => 'asc',
),
admin_url( 'tools.php' )
);

# Print notice.
// Print notice.
echo '<div class="notice notice-warning"><p>';
printf(
// 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'
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,
),
)
),
$num_pastdue_actions,
absint( $num_pastdue_actions ),
esc_attr( esc_url( $actions_url ) )
);
echo '</p></div>';

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

@@ -214,11 +246,24 @@ protected function check_pastdue_actions() {
public function add_help_tabs() {
$screen = get_current_screen();

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

$as_version = ActionScheduler_Versions::instance()->latest_version();
$as_version = ActionScheduler_Versions::instance()->latest_version();
$as_source = ActionScheduler_Versions::instance()->active_source();
$as_source_path = ActionScheduler_Versions::instance()->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',
@@ -228,6 +273,19 @@ public function add_help_tabs() {
'<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>',
)
);
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
<?php
/**
* ActionScheduler_AsyncRequest_QueueRunner
*/

defined( 'ABSPATH' ) || exit;

@@ -14,28 +11,27 @@ class ActionScheduler_AsyncRequest_QueueRunner extends WP_Async_Request {
* Data store for querying actions
*
* @var ActionScheduler_Store
* @access protected
*/
protected $store;

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

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

/**
* Initiate new async request
* Initiate new async request.
*
* @param ActionScheduler_Store $store Store object.
*/
public function __construct( ActionScheduler_Store $store ) {
parent::__construct();
@@ -49,7 +45,7 @@ public function __construct( ActionScheduler_Store $store ) {
* 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
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();

Original file line number Diff line number Diff line change
@@ -49,7 +49,7 @@ public static function raise_memory_limit() {
return wp_raise_memory_limit( 'admin' );
}

$current_limit = @ini_get( 'memory_limit' );
$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 ) {
@@ -61,6 +61,9 @@ public static function raise_memory_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;
@@ -74,6 +77,9 @@ public static function raise_memory_limit() {
return false;
}
}

// phpcs:enable

return false;
}

@@ -85,7 +91,7 @@ public static function raise_memory_limit() {
* @param int $limit The time limit in seconds.
*/
public static function raise_time_limit( $limit = 0 ) {
$limit = (int) $limit;
$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.
Original file line number Diff line number Diff line change
@@ -18,24 +18,36 @@ class ActionScheduler_DataController {
const DATASTORE_CLASS = 'ActionScheduler_DBStore';

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

/** Migration status option name. */
const STATUS_FLAG = 'action_scheduler_migration_status';
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';

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

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

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

/**
@@ -137,6 +149,8 @@ public static function free_memory() {
\WP_CLI::warning( __( 'Attempting to reduce used memory...', 'action-scheduler' ) );

/**
* Globals.
*
* @var $wpdb \wpdb
* @var $wp_object_cache \WP_Object_Cache
*/
@@ -154,7 +168,7 @@ public static function free_memory() {
$wp_object_cache->cache = array();

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

Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ class ActionScheduler_DateTime extends DateTime {
*
* @var int
*/
protected $utcOffset = 0;
protected $utcOffset = 0; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase

/**
* Get the unix timestamp of the current object.
@@ -34,10 +34,10 @@ public function getTimestamp() {
*
* This represents a fixed offset instead of a timezone setting.
*
* @param $offset
* @param string|int $offset UTC offset value.
*/
public function setUtcOffset( $offset ) {
$this->utcOffset = intval( $offset );
$this->utcOffset = intval( $offset ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
}

/**
@@ -48,20 +48,20 @@ public function setUtcOffset( $offset ) {
*/
#[\ReturnTypeWillChange]
public function getOffset() {
return $this->utcOffset ? $this->utcOffset : parent::getOffset();
return $this->utcOffset ? $this->utcOffset : parent::getOffset(); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
}

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

return $this;
Original file line number Diff line number Diff line change
@@ -4,51 +4,94 @@
* Class ActionScheduler_FatalErrorMonitor
*/
class ActionScheduler_FatalErrorMonitor {
/** @var ActionScheduler_ActionClaim */
private $claim = NULL;
/** @var ActionScheduler_Store */
private $store = NULL;

/**
* 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 );
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->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 );
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() {
if ( $error = error_get_last() ) {
if ( in_array( $error['type'], array( E_ERROR, E_PARSE, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR ) ) ) {
if ( !empty($this->action_id) ) {
$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 );
}
}
Original file line number Diff line number Diff line change
@@ -13,14 +13,15 @@ class ActionScheduler_InvalidActionException extends \InvalidArgumentException i
* 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 )
var_export( $schedule, true ) // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
);

return new static( $message );
@@ -29,17 +30,16 @@ public static function from_schedule( $action_id, $schedule ) {
/**
* 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.
* @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 )
var_export( $args, true ) // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
);

return new static( $message );
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@

/**
* Implements the admin view of the actions.
*
* @codeCoverageIgnore
*/
class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
@@ -49,9 +50,8 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
protected $runner;

/**
* Bulk actions. The key of the array is the method name of the implementation:
*
* bulk_<key>(array $ids, string $sql_in).
* 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
*
@@ -76,9 +76,9 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
/**
* Sets the current data store object into `store->action` and initialises the object.
*
* @param ActionScheduler_Store $store
* @param ActionScheduler_Logger $logger
* @param ActionScheduler_QueueRunner $runner
* @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 ) {

@@ -118,16 +118,16 @@ public function __construct( ActionScheduler_Store $store, ActionScheduler_Logge

if ( empty( $request_status ) ) {
$this->sort_by[] = 'status';
} elseif ( in_array( $request_status, array( 'in-progress', 'failed' ) ) ) {
} 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' ),
'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' ),
@@ -224,17 +224,18 @@ private static function human_interval( $interval, $periods_to_include = 2 ) {
return __( 'Now!', 'action-scheduler' );
}

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

for ( $time_period_index = 0, $periods_included = 0, $seconds_remaining = $interval; $time_period_index < count( self::$time_periods ) && $seconds_remaining > 0 && $periods_included < $periods_to_include; $time_period_index++ ) {
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 );
$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++;
}
@@ -246,7 +247,7 @@ private static function human_interval( $interval, $periods_to_include = 2 ) {
/**
* Returns the recurrence of an action or 'Non-repeating'. The output is human readable.
*
* @param ActionScheduler_Action $action
* @param ActionScheduler_Action $action Action object.
*
* @return string
*/
@@ -269,7 +270,7 @@ protected function get_recurrence( $action ) {
/**
* 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
* @param array $row The array representation of the current row of the table.
*
* @return string
*/
@@ -280,7 +281,7 @@ public function column_args( array $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 ) ) );
$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>';

@@ -311,8 +312,8 @@ public function column_log_entries( array $row ) {
/**
* 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
* @param DateTimezone $timezone
* @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 ) {
@@ -324,13 +325,13 @@ protected function get_log_entry_html( ActionScheduler_LogEntry $log_entry, Date
/**
* Only display row actions for pending actions.
*
* @param array $row Row to render
* @param string $column_name Current row
* @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' ] ) ) {
if ( 'pending' === strtolower( $row['status_name'] ) ) {
return parent::maybe_render_actions( $row, $column_name );
}

@@ -358,10 +359,10 @@ public function display_admin_notices() {

$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 ) ) {
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' ),
'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();
@@ -390,9 +391,9 @@ public function display_admin_notices() {

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

// No lock set or lock expired
// 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' ) );
$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 {
@@ -411,20 +412,21 @@ public function display_admin_notices() {
if ( is_array( $notification ) ) {
delete_transient( 'action_scheduler_admin_notice' );

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

if ( 1 === absint( $notification['success'] ) ) {
$class = 'updated';
switch ( $notification['row_action_type'] ) {
case 'run' :
case 'run':
/* translators: %s: action HTML */
$action_message_html = sprintf( __( 'Successfully executed action: %s', 'action-scheduler' ), $action_hook_html );
break;
case 'cancel' :
case 'cancel':
/* translators: %s: action HTML */
$action_message_html = sprintf( __( 'Successfully canceled action: %s', 'action-scheduler' ), $action_hook_html );
break;
default :
default:
/* translators: %s: action HTML */
$action_message_html = sprintf( __( 'Successfully processed change for action: %s', 'action-scheduler' ), $action_hook_html );
break;
@@ -449,7 +451,7 @@ public function 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
* @param array $row The array representation of the current row of the table.
*
* @return string
*/
@@ -460,7 +462,7 @@ public function column_schedule( $row ) {
/**
* Get the scheduled date in a human friendly format.
*
* @param ActionScheduler_Schedule $schedule
* @param ActionScheduler_Schedule $schedule Action's schedule.
* @return string
*/
protected function get_schedule_display_string( ActionScheduler_Schedule $schedule ) {
@@ -492,13 +494,13 @@ protected function get_schedule_display_string( ActionScheduler_Schedule $schedu
}

/**
* Bulk delete
* 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 array $ids
* @param string $ids_sql Inherited and unused
* @param int[] $ids Action IDs.
* @param string $ids_sql Inherited and unused.
*/
protected function bulk_delete( array $ids, $ids_sql ) {
foreach ( $ids as $id ) {
@@ -507,6 +509,7 @@ protected function bulk_delete( array $ids, $ids_sql ) {
} 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. */
@@ -523,7 +526,7 @@ protected function bulk_delete( array $ids, $ids_sql ) {
* Implements the logic behind running an action. ActionScheduler_Abstract_ListTable validates the request and their
* parameters are valid.
*
* @param int $action_id
* @param int $action_id Action ID.
*/
protected function row_action_cancel( $action_id ) {
$this->process_row_action( $action_id, 'cancel' );
@@ -533,7 +536,7 @@ protected function row_action_cancel( $action_id ) {
* Implements the logic behind running an action. ActionScheduler_Abstract_ListTable validates the request and their
* parameters are valid.
*
* @param int $action_id
* @param int $action_id Action ID.
*/
protected function row_action_run( $action_id ) {
$this->process_row_action( $action_id, 'run' );
@@ -560,23 +563,23 @@ protected function recreate_tables() {
/**
* Implements the logic behind processing an action once an action link is clicked on the list table.
*
* @param int $action_id
* @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' :
case 'run':
$this->runner->process_action( $action_id, 'Admin List Table' );
break;
case 'cancel' :
case 'cancel':
$this->store->cancel_action( $action_id );
break;
}
$success = 1;
$success = 1;
$error_message = '';
} catch ( Exception $e ) {
$success = 0;
$success = 0;
$error_message = $e->getMessage();
}

@@ -639,11 +642,13 @@ public function prepare_items() {
);
}

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

/**
Original file line number Diff line number Diff line change
@@ -6,30 +6,35 @@
class ActionScheduler_LogEntry {

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

/**
* Log entry's message.
*
* @var string $message
*/
protected $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.
* @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
@@ -44,7 +49,7 @@ public function __construct( $action_id, $message, $date = null ) {

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

/**
@@ -56,10 +61,16 @@ 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;
}
Original file line number Diff line number Diff line change
@@ -4,8 +4,15 @@
* 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
// nothing to see here.
}

}

Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
* for up-to a given duration.
*
* Class ActionScheduler_OptionLock
*
* @since 3.0.0
*/
class ActionScheduler_OptionLock extends ActionScheduler_Lock {
Original file line number Diff line number Diff line change
@@ -5,10 +5,18 @@
*/
class ActionScheduler_QueueCleaner {

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

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

/**
@@ -19,21 +27,23 @@ class ActionScheduler_QueueCleaner {
private $month_in_seconds = 2678400;

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

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

@@ -66,7 +76,6 @@ public function delete_old_actions() {
return array();
}


/**
* Filter the statuses when cleaning the queue.
*
@@ -83,26 +92,30 @@ public function delete_old_actions() {
* @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`.
* @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 = $batch_size !== null ? $batch_size : $this->batch_size;
$cutoff = $cutoff_date !== null ? $cutoff_date : as_get_datetime_object( $this->month_in_seconds . ' seconds ago' );
$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 = [];
$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',
) );
$actions_to_delete = $this->store->query_actions(
array(
'status' => $status,
'modified' => $cutoff,
'modified_compare' => '<=',
'per_page' => $batch_size,
'orderby' => 'none',
)
);

$deleted_actions = array_merge( $deleted_actions, $this->delete_actions( $actions_to_delete, $lifespan, $context ) );
}
@@ -111,14 +124,17 @@ public function clean_actions( array $statuses_to_purge, DateTime $cutoff_date,
}

/**
* @param int[] $actions_to_delete List of action IDs to delete.
* @param int $lifespan Minimum scheduled age in seconds of the actions being deleted.
* 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 = [];
if ( $lifespan === null ) {
$deleted_actions = array();

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

@@ -138,7 +154,6 @@ private function delete_actions( array $actions_to_delete, $lifespan = null, $co
* @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 ) );
}
@@ -156,18 +171,22 @@ private function delete_actions( array $actions_to_delete, $lifespan = null, $co
*/
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',
) );

$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 );
@@ -186,17 +205,21 @@ public function reset_timeouts( $time_limit = 300 ) {
*/
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',
) );

$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 );
@@ -208,7 +231,6 @@ public function mark_failures( $time_limit = 300 ) {
* 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).
* @author Jeremy Pry
*/
public function clean( $time_limit = 300 ) {
$this->delete_old_actions();
@@ -219,7 +241,6 @@ public function clean( $time_limit = 300 ) {
/**
* Get the batch size for cleaning the queue.
*
* @author Jeremy Pry
* @return int
*/
protected function get_batch_size() {
Original file line number Diff line number Diff line change
@@ -8,35 +8,51 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {

const WP_CRON_SCHEDULE = 'every_minute';

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

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

/** @var int */
/**
* 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');
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 $store
* @param ActionScheduler_FatalErrorMonitor $monitor
* @param ActionScheduler_QueueCleaner $cleaner
* @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 ) {
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 ) ) {
@@ -47,13 +63,15 @@ public function __construct( ActionScheduler_Store $store = null, ActionSchedule
}

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

add_filter( 'cron_schedules', array( self::instance(), 'add_wp_cron_schedule' ) );
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
// 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 );
@@ -120,6 +138,7 @@ public function maybe_dispatch_async_request() {
* 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'
@@ -134,11 +153,11 @@ public function run( $context = 'WP Cron' ) {

$this->processed_actions_count = 0;
if ( false === $this->has_maximum_concurrent_batches() ) {
$batch_size = apply_filters( 'action_scheduler_queue_runner_batch_size', 25 );
do {
$batch_size = apply_filters( 'action_scheduler_queue_runner_batch_size', 25 );
$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
} 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' );
@@ -151,19 +170,19 @@ public function run( $context = 'WP Cron' ) {
* 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 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.
* 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);
$claim = $this->store->stake_claim( $size );
$this->monitor->attach( $claim );
$processed_actions = 0;

foreach ( $claim->get_actions() as $action_id ) {
// bail if we lost the claim
if ( ! in_array( $action_id, $this->store->find_actions_by_claim_id( $claim->get_id() ) ) ) {
// bail if we lost the claim.
if ( ! in_array( $action_id, $this->store->find_actions_by_claim_id( $claim->get_id() ), true ) ) {
break;
}
$this->process_action( $action_id, $context );
@@ -173,7 +192,7 @@ protected function do_batch( $size = 100, $context = '' ) {
break;
}
}
$this->store->release_claim($claim);
$this->store->release_claim( $claim );
$this->monitor->detach();
$this->clear_caches();
return $processed_actions;
@@ -218,9 +237,15 @@ protected function clear_caches() {
}
}

/**
* 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
'interval' => 60, // in seconds.
'display' => __( 'Every minute', 'action-scheduler' ),
);

150 changes: 137 additions & 13 deletions inc/Dependencies/ActionScheduler/classes/ActionScheduler_Versions.php
Original file line number Diff line number Diff line change
@@ -5,58 +5,182 @@
*/
class ActionScheduler_Versions {
/**
* ActionScheduler_Versions instance.
*
* @var ActionScheduler_Versions
*/
private static $instance = NULL;
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;
if ( isset( $this->versions[ $version_string ] ) ) {
return false;
}
$this->versions[$version_string] = $initialization_callback;
return TRUE;

// 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.
*
* @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) ) {
$keys = array_keys( $this->versions );
if ( empty( $keys ) ) {
return false;
}
uasort( $keys, 'version_compare' );
return end($keys);
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]) ) {

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

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

/**
* Get instance.
*
* @return ActionScheduler_Versions
* @codeCoverageIgnore
*/
public static function instance() {
if ( empty(self::$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());
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',
* ]
*
* @return array
*/
public function active_source(): array {
$file = __FILE__;
$dir = __DIR__;
$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 function active_source_path(): string {
return trailingslashit( dirname( __DIR__ ) );
}
}

Original file line number Diff line number Diff line change
@@ -42,10 +42,10 @@ public static function init() {

// 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( '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
// 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' ) );
}
@@ -66,22 +66,40 @@ public static function has_logs() {
* Attached to the migration complete hook 'action_scheduler/migration_complete'.
*/
public static function maybe_schedule_cleanup() {
if ( (bool) get_comments( array( 'type' => ActionScheduler_wpCommentLogger::TYPE, 'number' => 1, 'fields' => 'ids' ) ) ) {
update_option( self::$has_logs_option_key, 'yes' );
$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 ) );
delete_option( self::$has_logs_option_key );

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

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

/**
@@ -90,7 +108,7 @@ public static function delete_all_action_comments() {
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.
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?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, $multiple );
}

/**
* 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()
)
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace Action_Scheduler\WP_CLI\Action;

/**
* 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()
)
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?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 ) );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?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()
)
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?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 );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?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;

}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?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.' );
}

}
194 changes: 194 additions & 0 deletions inc/Dependencies/ActionScheduler/classes/WP_CLI/Action/Run_Command.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
<?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() ) );
}

}
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ public function clean( $args, $assoc_args ) {

$batches_completed = 0;
$actions_deleted = 0;
$unlimited = $batches === 0;
$unlimited = 0 === $batches;
try {
$lifespan = as_get_datetime_object( $before );
} catch ( Exception $e ) {
@@ -58,7 +58,7 @@ public function clean( $args, $assoc_args ) {
sleep( $sleep );
}

$deleted = count( $cleaner->clean_actions( $status, $lifespan, null,'CLI' ) );
$deleted = count( $cleaner->clean_actions( $status, $lifespan, null, 'CLI' ) );
if ( $deleted <= 0 ) {
break;
}
@@ -79,7 +79,7 @@ public function clean( $args, $assoc_args ) {
/**
* Print WP CLI message about how many batches of actions were processed.
*
* @param int $batches_processed
* @param int $batches_processed Number of batches processed.
*/
protected function print_total_batches( int $batches_processed ) {
WP_CLI::log(
@@ -95,8 +95,6 @@ protected function print_total_batches( int $batches_processed ) {
* Convert an exception into a WP CLI error.
*
* @param Exception $e The error object.
*
* @throws \WP_CLI\ExitException
*/
protected function print_error( Exception $e ) {
WP_CLI::error(
@@ -111,7 +109,7 @@ protected function print_error( Exception $e ) {
/**
* Print a success message with the number of completed actions.
*
* @param int $actions_deleted
* @param int $actions_deleted Number of deleted actions.
*/
protected function print_success( int $actions_deleted ) {
WP_CLI::success(
Original file line number Diff line number Diff line change
@@ -9,25 +9,37 @@
*/
class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract_QueueRunner {

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

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

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

/**
* ActionScheduler_WPCLI_QueueRunner constructor.
*
* @param ActionScheduler_Store $store
* @param ActionScheduler_FatalErrorMonitor $monitor
* @param ActionScheduler_QueueCleaner $cleaner
* @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
* @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 ) {
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__ ) );
@@ -39,8 +51,6 @@ public function __construct( ActionScheduler_Store $store = null, ActionSchedule
/**
* Set up the Queue before processing.
*
* @author Jeremy Pry
*
* @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.
@@ -72,8 +82,6 @@ public function setup( $batch_size, $hooks = array(), $group = '', $force = fals

/**
* Add our hooks to the appropriate actions.
*
* @author Jeremy Pry
*/
protected function add_hooks() {
add_action( 'action_scheduler_before_execute', array( $this, 'before_execute' ) );
@@ -83,8 +91,6 @@ protected function add_hooks() {

/**
* Set up the WP CLI progress bar.
*
* @author Jeremy Pry
*/
protected function setup_progress_bar() {
$count = count( $this->actions );
@@ -98,8 +104,6 @@ protected function setup_progress_bar() {
/**
* Process actions in the queue.
*
* @author Jeremy Pry
*
* @param string $context Optional runner context. Default 'WP CLI'.
*
* @return int The number of actions processed.
@@ -109,7 +113,7 @@ public function run( $context = 'WP CLI' ) {
$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() ) ) ) {
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;
}
@@ -129,9 +133,7 @@ public function run( $context = 'WP CLI' ) {
/**
* Handle WP CLI message when the action is starting.
*
* @author Jeremy Pry
*
* @param $action_id
* @param int $action_id Action ID.
*/
public function before_execute( $action_id ) {
/* translators: %s refers to the action ID */
@@ -141,13 +143,11 @@ public function before_execute( $action_id ) {
/**
* Handle WP CLI message when the action has completed.
*
* @author Jeremy Pry
*
* @param int $action_id
* @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
// backward compatibility.
if ( null === $action ) {
$action = $this->store->fetch_action( $action_id );
}
@@ -158,10 +158,8 @@ public function after_execute( $action_id, $action = null ) {
/**
* Handle WP CLI message when the action has failed.
*
* @author Jeremy Pry
*
* @param int $action_id
* @param Exception $exception
* @param int $action_id Action ID.
* @param Exception $exception Exception.
* @throws \WP_CLI\ExitException With failure message.
*/
public function action_failed( $action_id, $exception ) {
@@ -175,7 +173,7 @@ public function action_failed( $action_id, $exception ) {
/**
* Sleep and help avoid hitting memory limit
*
* @param int $sleep_time Amount of seconds to sleep
* @param int $sleep_time Amount of seconds to sleep.
* @deprecated 3.0.0
*/
protected function stop_the_insanity( $sleep_time = 0 ) {
Original file line number Diff line number Diff line change
@@ -91,20 +91,20 @@ public function run( $args, $assoc_args ) {

$batches_completed = 0;
$actions_completed = 0;
$unlimited = $batches === 0;
if ( is_callable( [ ActionScheduler::store(), 'set_claim_filter' ] ) ) {
$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 );
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
// Get the queue runner instance.
$runner = new ActionScheduler_WPCLI_QueueRunner( null, null, $cleaner );

// Determine how many tasks will be run in the first batch.
@@ -141,9 +141,7 @@ private function parse_comma_separated_string( $string ): array {
/**
* Print WP CLI message about how many actions are about to be processed.
*
* @author Jeremy Pry
*
* @param int $total
* @param int $total Number of actions found.
*/
protected function print_total_actions( $total ) {
WP_CLI::log(
@@ -158,9 +156,7 @@ protected function print_total_actions( $total ) {
/**
* Print WP CLI message about how many batches of actions were processed.
*
* @author Jeremy Pry
*
* @param int $batches_completed
* @param int $batches_completed Number of completed batches.
*/
protected function print_total_batches( $batches_completed ) {
WP_CLI::log(
@@ -175,11 +171,9 @@ protected function print_total_batches( $batches_completed ) {
/**
* Convert an exception into a WP CLI error.
*
* @author Jeremy Pry
*
* @param Exception $e The error object.
*
* @throws \WP_CLI\ExitException
* @throws \WP_CLI\ExitException Under some conditions WP CLI may throw an exception.
*/
protected function print_error( Exception $e ) {
WP_CLI::error(
@@ -194,9 +188,7 @@ protected function print_error( Exception $e ) {
/**
* Print a success message with the number of completed actions.
*
* @author Jeremy Pry
*
* @param int $actions_completed
* @param int $actions_completed Number of completed actions.
*/
protected function print_success( $actions_completed ) {
WP_CLI::success(
Loading

Unchanged files with check annotations Beta

protected $myrc4;
protected function readfile_chunked( $file, $retbytes=true, $myrc4, $cache_to_file='' ) {

Check warning on line 112 in tests/Fixtures/inc/ThirdParty/Plugins/PDFEmbedder/core/secure/uploads.php

GitHub Actions / WP latest with PHP 8.0 on ubuntu-latest.

Required parameter $myrc4 follows optional parameter $retbytes

Check warning on line 112 in tests/Fixtures/inc/ThirdParty/Plugins/PDFEmbedder/core/secure/uploads.php

GitHub Actions / WP latest with PHP 8.3 on ubuntu-latest.

Optional parameter $retbytes declared before required parameter $myrc4 is implicitly treated as a required parameter

Check warning on line 112 in tests/Fixtures/inc/ThirdParty/Plugins/PDFEmbedder/core/secure/uploads.php

GitHub Actions / WP latest with PHP 8.1 on ubuntu-latest.

Optional parameter $retbytes declared before required parameter $myrc4 is implicitly treated as a required parameter

Check warning on line 112 in tests/Fixtures/inc/ThirdParty/Plugins/PDFEmbedder/core/secure/uploads.php

GitHub Actions / WP latest with PHP 8.2 on ubuntu-latest.

Optional parameter $retbytes declared before required parameter $myrc4 is implicitly treated as a required parameter
$chunksize = 1024 * 1024;
$buffer = '';