Files
carpseeds.pl/wp-content/plugins/jetpack.bck/json-endpoints/jetpack/class.jetpack-json-api-cron-endpoint.php
2024-07-15 11:28:08 +02:00

253 lines
7.5 KiB
PHP

<?php
// GET /sites/%s/cron
class Jetpack_JSON_API_Cron_Endpoint extends Jetpack_JSON_API_Endpoint {
protected $needed_capabilities = 'manage_options';
protected function validate_call( $_blog_id, $capability, $check_manage_active = true ) {
return parent::validate_call( $_blog_id, $capability, false );
}
protected function result() {
return array(
'cron_array' => _get_cron_array(),
'current_timestamp' => time()
);
}
protected function sanitize_hook( $hook ) {
return preg_replace( '/[^A-Za-z0-9-_]/', '', $hook );
}
protected function resolve_arguments() {
$args = $this->input();
return isset( $args['arguments'] ) ? json_decode( $args['arguments'] ) : array();
}
protected function is_cron_locked( $gmt_time ) {
// The cron lock: a unix timestamp from when the cron was spawned.
$doing_cron_transient = $this->get_cron_lock();
if ( $doing_cron_transient && ( $doing_cron_transient + WP_CRON_LOCK_TIMEOUT > $gmt_time ) ) {
return new WP_Error( 'cron-is-locked', 'Current there is a cron already happening.', 403 );
}
return $doing_cron_transient;
}
protected function maybe_unlock_cron( $doing_wp_cron ) {
if ( $this->get_cron_lock() == $doing_wp_cron ) {
delete_transient( 'doing_cron' );
}
}
protected function lock_cron() {
$lock = sprintf( '%.22F', microtime( true ) );
set_transient( 'doing_cron', $lock );
return $lock;
}
protected function get_schedules( $hook, $args ) {
$crons = _get_cron_array();
$key = md5(serialize($args));
if ( empty( $crons ) )
return array();
$found = array();
foreach ( $crons as $timestamp => $cron ) {
if ( isset( $cron[$hook][$key] ) )
$found[] = $timestamp;
}
return $found;
}
/**
* This function is based on the one found in wp-cron.php with a similar name
* @return int
*/
protected function get_cron_lock() {
global $wpdb;
$value = 0;
if ( wp_using_ext_object_cache() ) {
/*
* Skip local cache and force re-fetch of doing_cron transient
* in case another process updated the cache.
*/
$value = wp_cache_get( 'doing_cron', 'transient', true );
} else {
$row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", '_transient_doing_cron' ) );
if ( is_object( $row ) ) {
$value = $row->option_value;
}
}
return $value;
}
}
// POST /sites/%s/cron
class Jetpack_JSON_API_Cron_Post_Endpoint extends Jetpack_JSON_API_Cron_Endpoint {
protected function result() {
define( 'DOING_CRON', true );
set_time_limit( 0 );
$args = $this->input();
if ( false === $crons = _get_cron_array() ) {
return new WP_Error( 'no-cron-event', 'Currently there are no cron events', 400 );
}
$timestamps_to_run = array_keys( $crons );
$gmt_time = microtime( true );
if ( isset( $timestamps_to_run[0] ) && $timestamps_to_run[0] > $gmt_time ) {
return new WP_Error( 'no-cron-event', 'Currently there are no cron events ready to be run', 400 );
}
$locked = $this->is_cron_locked( $gmt_time );
if ( is_wp_error( $locked ) ) {
return $locked;
}
$lock = $this->lock_cron();
$processed_events = array();
foreach ( $crons as $timestamp => $cronhooks ) {
if ( $timestamp > $gmt_time && ! isset( $args[ 'hook' ] ) ) {
break;
}
foreach ( $cronhooks as $hook => $hook_data ) {
if ( isset( $args[ 'hook' ] ) && ! in_array( $hook, $args['hook'] ) ) {
continue;
}
foreach ( $hook_data as $hash => $hook_item ) {
$schedule = $hook_item['schedule'];
$arguments = $hook_item['args'];
if ( $schedule != false ) {
wp_reschedule_event( $timestamp, $schedule, $hook, $arguments );
}
wp_unschedule_event( $timestamp, $hook, $arguments );
do_action_ref_array( $hook, $arguments );
$processed_events[] = array( $hook => $arguments );
// If the hook ran too long and another cron process stole the lock,
// or if we things are taking longer then 20 seconds then quit.
if ( ( $this->get_cron_lock() != $lock ) || ( $gmt_time + 20 > microtime( true ) ) ) {
$this->maybe_unlock_cron( $lock );
return array( 'success' => $processed_events );
}
}
}
}
$this->maybe_unlock_cron( $lock );
return array( 'success' => $processed_events );
}
}
// POST /sites/%s/cron/schedule
class Jetpack_JSON_API_Cron_Schedule_Endpoint extends Jetpack_JSON_API_Cron_Endpoint {
protected function result() {
$args = $this->input();
if ( ! isset( $args['timestamp'] ) ) {
return new WP_Error( 'missing_argument', 'Please provide the timestamp argument', 400 );
}
if ( ! is_int( $args['timestamp'] ) || $args['timestamp'] < time() ) {
return new WP_Error( 'timestamp-invalid', 'Please provide timestamp that is an integer and set in the future', 400 );
}
if ( ! isset( $args['hook'] ) ) {
return new WP_Error( 'missing_argument', 'Please provide the hook argument', 400 );
}
$hook = $this->sanitize_hook( $args['hook'] );
$locked = $this->is_cron_locked( microtime( true ) );
if ( is_wp_error( $locked ) ) {
return $locked;
}
$arguments = $this->resolve_arguments();
$next_scheduled = $this->get_schedules( $hook, $arguments );
if ( isset( $args['recurrence'] ) ) {
$schedules = wp_get_schedules();
if ( ! isset( $schedules[ $args['recurrence'] ] ) ) {
return new WP_Error( 'invalid-recurrence', 'Please provide a valid recurrence argument', 400 );
}
if ( count( $next_scheduled ) > 0 ) {
return new WP_Error( 'event-already-scheduled', 'This event is ready scheduled', 400 );
}
$lock = $this->lock_cron();
wp_schedule_event( $args['timestamp'], $args['recurrence'], $hook, $arguments );
$this->maybe_unlock_cron( $lock );
return array( 'success' => true );
}
foreach( $next_scheduled as $scheduled_time ) {
if ( abs( $scheduled_time - $args['timestamp'] ) <= 10 * MINUTE_IN_SECONDS ) {
return new WP_Error( 'event-already-scheduled', 'This event is ready scheduled', 400 );
}
}
$lock = $this->lock_cron();
$next = wp_schedule_single_event( $args['timestamp'], $hook, $arguments );
$this->maybe_unlock_cron( $lock );
/**
* Note: Before WP 5.1, the return value was either `false` or `null`.
* With 5.1 and later, the return value is now `false` or `true`.
* We need to account for both.
*/
return array( 'success' => false !== $next );
}
}
// POST /sites/%s/cron/unschedule
class Jetpack_JSON_API_Cron_Unschedule_Endpoint extends Jetpack_JSON_API_Cron_Endpoint {
protected function result() {
$args = $this->input();
if ( !isset( $args['hook'] ) ) {
return new WP_Error( 'missing_argument', 'Please provide the hook argument', 400 );
}
$hook = $this->sanitize_hook( $args['hook'] );
$locked = $this->is_cron_locked( microtime( true ) );
if ( is_wp_error( $locked ) ) {
return $locked;
}
$crons = _get_cron_array();
if ( empty( $crons ) ) {
return new WP_Error( 'cron-not-present', 'Unable to unschedule an event, no events in the cron', 400 );
}
$arguments = $this->resolve_arguments();
if ( isset( $args['timestamp'] ) ) {
$next_schedulded = $this->get_schedules( $hook, $arguments );
if ( in_array( $args['timestamp'], $next_schedulded ) ) {
return new WP_Error( 'event-not-present', 'Unable to unschedule the event, the event doesn\'t exist', 400 );
}
$lock = $this->lock_cron();
wp_unschedule_event( $args['timestamp'], $hook, $arguments );
$this->maybe_unlock_cron( $lock );
return array( 'success' => true );
}
$lock = $this->lock_cron();
wp_clear_scheduled_hook( $hook, $arguments );
$this->maybe_unlock_cron( $lock );
return array( 'success' => true );
}
}