File "class-itsec-scheduler.php"
Full Path: /home/siazco/grocery.siazco.se/wp-content/plugins/better-wp-security/core/lib/class-itsec-scheduler.php
File size: 10.99 KB
MIME-type: text/x-php
Charset: utf-8
<?php
use iThemesSecurity\Module_Config;
abstract class ITSEC_Scheduler {
const S_TWICE_HOURLY = 'twice-hourly';
const S_HOURLY = 'hourly';
const S_FOUR_DAILY = 'four-daily';
const S_TWICE_DAILY = 'twice-daily';
const S_DAILY = 'daily';
const S_WEEKLY = 'weekly';
const S_MONTHLY = 'monthly';
const LOCK_SCHEDULING = 'scheduling';
/** @var array */
protected $custom_schedules = array();
/** @var array */
protected $loops = array();
/** @var bool */
private $is_running = false;
/**
* Schedule a recurring event.
*
* Only one event with the given id can be scheduled at a time.
*
* @param string $schedule
* @param string $id
* @param array $data
* @param array $opts
* - fire_at: Manually specify the first time the event should be fired.
*
* @return bool
*/
abstract public function schedule( $schedule, $id, $data = array(), $opts = array() );
/**
* Schedule a single event.
*
* Only one event with the given id and same set of data can be scheduled at the same time.
*
* Unlike Core's CRON implementation, event if a single event is more than 10 minutes in the future, it cannot be scheduled.
*
* @param int $at
* @param string $id
* @param array $data
*
* @return bool
*/
abstract public function schedule_once( $at, $id, $data = array() );
/**
* Schedule a single event to run soon.
*
* @param string $id
* @param array $data
*
* @return bool
*/
public function schedule_soon( $id, $data = array() ) {
return $this->schedule_once( ITSEC_Core::get_current_time_gmt() + 60 * mt_rand( 1, 10 ), $id, $data );
}
/**
* Schedule an event loop.
*
* @param string $id The event ID.
* @param array $data Event data.
* @param array $opts
* - fire_at: Manually specify the first time the event should be fired.
*/
public function schedule_loop( $id, $data = array(), $opts = array() ) {
$start = isset( $opts['fire_at'] ) ? $opts['fire_at'] : ITSEC_Core::get_current_time_gmt() + 60 * mt_rand( 1, 30 );
$this->schedule_once( $start, $id, array_merge( $data, array(
'loop_start' => $start,
'loop_item' => 1,
) ) );
}
/**
* Is a recurring event scheduled.
*
* @param string $id
*
* @return bool
*/
abstract public function is_recurring_scheduled( $id );
/**
* Is a single event scheduled with the given data.
*
* @param string $id The event ID to check.
* @param array|null $data The event data. Pass null to check if any event is scheduled with that ID,
* regardless of the data.
*
* @return bool
*/
abstract public function is_single_scheduled( $id, $data = array() );
/**
* Unschedule a recurring event.
*
* @param string $id
*
* @return bool
*/
abstract public function unschedule( $id );
/**
* Unschedule a single event.
*
* The data specified needs to be identical to the data the single event was scheduled with.
*
* @param string $id The event ID to unschedule.
* @param array|null $data Unschedules the event with the given data. Pass null to delete any and all events matching the ID.
*
* @return bool
*/
abstract public function unschedule_single( $id, $data = array() );
/**
* Get all of the scheduled recurring events.
*
* Each event is an array with the following properties.
* - id: The ID the event was scheduled with.
* - data: The data the event was scheduled with.
* - fire_at: The next time the event should be fired.
* - schedule: The selected schedule. See S_HOURLY, etc...
*
* @return array
*/
abstract public function get_recurring_events();
/**
* Get all of the single events.
*
* Each event is an array with the following properties.
* - id: The ID the event was scheduled with.
* - data: The data the event was scheduled with.
* - fire_at: The time the event should be fired.
* - hash: The event's data hash.
*
* @return array
*/
abstract public function get_single_events();
/**
* Run a recurring event, even if it is not time to.
*
* This will _not_ update the last fired time.
*
* @param string $id
*
* @return void
*/
abstract public function run_recurring_event( $id );
/**
* Run a single event, even if it is not time to.
*
* This will clear the event from the schedule.
*
* @param string $id
* @param array $data
*
* @return void
*/
abstract public function run_single_event( $id, $data = array() );
/**
* Run a single event by it's hash.
*
* @param string $id
* @param string $hash
*
* @return void
*/
abstract public function run_single_event_by_hash( $id, $hash );
/**
* Run any events that are due now.
*
* @param int $now
*
* @return void
*/
abstract public function run_due_now( $now = 0 );
/**
* Code executed on every page load to setup the scheduler.
*
* @return void
*/
abstract public function run();
/**
* Check whether the scheduler is currently executing an event.
*
* @return bool
*/
final public function is_running() {
return $this->is_running;
}
/**
* Manually trigger modules to register their scheduled events.
*
* @return void
*/
public function register_events() {
/**
* Register scheduled events.
*
* Events should be registered in response to a user action, for example activating a module or changing a setting.
* Occasionally, Solid Security will manually ask for all events to be scheduled.
*
* @param ITSEC_Scheduler $this
*/
do_action( 'itsec_scheduler_register_events', $this );
$this->register_events_for_module( ':active' );
}
/**
* Registers events for a given module using the "scheduling.php" module file.
*
* @param string $module The module specifier. For example ':active' or 'global'.
*/
public function register_events_for_module( $module ) {
ITSEC_Modules::load_module_file( 'scheduling.php', $module, function ( $fn ) {
if ( ! is_callable( $fn ) ) {
_doing_it_wrong( 'scheduling.php', __( 'A Solid Security module\'s scheduling.php file must return a callable.', 'better-wp-security' ), '5.8.0' );
return;
}
$fn( $this );
} );
foreach ( ITSEC_Modules::get_config_list( $module ) as $config ) {
$this->register_events_for_config( $config );
}
}
/**
* Registers events based on a module configuration.
*
* @param Module_Config $config The config to use.
*/
public function register_events_for_config( Module_Config $config ) {
foreach ( $config->get_scheduling() as $id => $definition ) {
if ( $conditional_schema = $definition['conditional'] ?? [] ) {
$settings = ITSEC_Modules::get_settings( $config->get_id() );
$validated = rest_validate_value_from_schema( $settings, $conditional_schema );
$sanitized = is_wp_error( $validated ) ? $validated : rest_sanitize_value_from_schema( $settings, $conditional_schema );
$is_active = ! is_wp_error( $validated ) && ! is_wp_error( $sanitized );
if ( ! $is_active ) {
$this->unschedule( $id );
continue;
}
}
$schedule = $definition['schedule'];
$data = $definition['data'] ?? [];
$opts = $definition['opts'] ?? [];
if ( isset( $opts['fire_at'] ) ) {
$opts['fire_at'] = ITSEC_Core::get_current_time_gmt() + $opts['fire_at'];
}
$this->schedule( $schedule, $id, $data, $opts );
}
}
/**
* Unregisters events for a given module.
*
* @param Module_Config $config The config to use.
*/
public function unregister_events_for_config( Module_Config $config ) {
foreach ( $config->get_scheduling() as $id => $definition ) {
$this->unschedule( $id );
}
}
/**
* Register a custom schedule.
*
* @param string $slug
* @param int $interval
*/
public function register_custom_schedule( $slug, $interval ) {
$this->custom_schedules[ $slug ] = $interval;
}
/**
* Get a registered custom schedules.
*
* @return array
*/
public function get_custom_schedules() {
return $this->custom_schedules;
}
/**
* Register an event loop.
*
* This allows for splitting up a long running process across multiple page loads.
*
* @param string $id The event ID.
* @param string $schedule The schedule between loop starts. This is the maximum amount of time to wait.
* @param int $wait Time to wait in seconds between loop parts.
*/
public function register_loop( $id, $schedule, $wait ) {
$this->loops[ $id ] = array(
'schedule' => $schedule,
'wait' => $wait,
);
}
/**
* Get the loop configuration.
*
* @param string $id
*
* @return array
*/
public function get_loop( $id ) {
return isset( $this->loops[ $id ] ) ? $this->loops[ $id ] : array();
}
/**
* Get a lock to be used for scheduling events.
*
* @return bool
*/
protected function scheduling_lock() {
return ITSEC_Lib::get_lock( self::LOCK_SCHEDULING, 5 );
}
/**
* Release the lock used for scheduling events.
*/
protected function scheduling_unlock() {
ITSEC_Lib::release_lock( self::LOCK_SCHEDULING );
}
/**
* Make a job object.
*
* @param string $id
* @param array $data
* @param array $opts
*
* @return ITSEC_Job
*/
protected function make_job( $id, $data, $opts = array() ) {
return new ITSEC_Job( $this, $id, $data, $opts );
}
/**
* Dispatch the action to execute the scheduled job.
*
* @param ITSEC_Job $job
*/
protected final function call_action( ITSEC_Job $job ) {
$interactive = ITSEC_Core::is_interactive();
ITSEC_Core::set_interactive( false );
$this->is_running = true;
try {
/**
* Fires when a scheduled job should be executed.
*
* @param ITSEC_Job $job
*/
do_action( "itsec_scheduled_{$job->get_id()}", $job );
} catch ( Exception $e ) {
ITSEC_Log::add_fatal_error( 'scheduler', 'unhandled-exception', array(
'exception' => (string) $e,
'job' => $job->get_id(),
'data' => $job->get_data(),
) );
$job->reschedule_in( 500 );
}
$this->is_running = false;
ITSEC_Core::set_interactive( $interactive );
}
/**
* Generate a unique hash of the data.
*
* @param array $data
*
* @return string
*/
protected function hash_data( $data ) {
return md5( serialize( $data ) );
}
/**
* Get the interval for the schedule.
*
* @param string $schedule
*
* @return int
*/
final public function get_schedule_interval( $schedule ) {
switch ( $schedule ) {
case self::S_TWICE_HOURLY:
return HOUR_IN_SECONDS / 2;
case self::S_HOURLY:
return HOUR_IN_SECONDS;
case self::S_FOUR_DAILY:
return DAY_IN_SECONDS / 4;
case self::S_TWICE_DAILY:
return DAY_IN_SECONDS / 2;
case self::S_DAILY:
return DAY_IN_SECONDS;
case self::S_WEEKLY:
return WEEK_IN_SECONDS;
case self::S_MONTHLY:
return MONTH_IN_SECONDS;
default:
return isset( $this->custom_schedules[ $schedule ] ) ? $this->custom_schedules[ $schedule ] : false;
}
}
/**
* Run code when the plugin is uninstalled.
*/
public function uninstall() {
}
/**
* Reset the scheduler.
*
* This unregisters all events, and re-registers them.
*/
public function reset() {
$this->uninstall();
$this->run();
$this->register_events();
}
}