public function get_option()
{
// Use cached value for reads, except when we're unscheduling and state is important
if (!$this->is_unscheduling()) {
$cached_option = wp_cache_get(self::CACHE_KEY, null, true);
if (false !== $cached_option) {
return $cached_option;
}
}
// Start building a new cron option
$cron_array = array('version' => 2);
// Get events to re-render as the cron option
$page = 1;
do {
$jobs_posts = $this->get_jobs(array('post_type' => self::POST_TYPE, 'post_status' => self::POST_STATUS_PENDING, 'suppress_filters' => false, 'posts_per_page' => 100, 'paged' => $page, 'orderby' => 'date', 'order' => 'ASC'));
// Nothing more to add
if (empty($jobs_posts)) {
break;
}
$page++;
// Something's probably wrong if a site has more than 1,500 pending cron actions
if ($page > 15) {
do_action('a8c_cron_control_stopped_runaway_cron_option_rebuild');
break;
}
// Loop through results and built output Core expects
if (!empty($jobs_posts)) {
foreach ($jobs_posts as $jobs_post) {
// Determine event timestamp
$timestamp = strtotime($jobs_post->post_date_gmt);
// When timestamp is invalid, perhaps due to post date being set to `0000-00-00 00:00:00`, attempt to fall back to the original value
if ($timestamp <= 0) {
$event_data = $jobs_post->post_title;
$event_data = explode('|', $event_data);
if (is_numeric($event_data[0])) {
$timestamp = (int) $event_data[0];
}
}
// If timestamp is still invalid, event is removed to let its source fix it
if ($timestamp <= 0) {
$this->mark_job_post_completed($jobs_post->ID);
continue;
}
// Retrieve event's remaining data
$job_args = maybe_unserialize($jobs_post->post_content_filtered);
if (!is_array($job_args)) {
$this->mark_job_post_completed($jobs_post->ID);
continue;
}
// Basic arguments to identify a job in the array format Core expects
$action = $job_args['action'];
$instance = $job_args['instance'];
$args = $job_args['args'];
// Exclude duplicates and prevent them from being run again
// Would normally do this asynchronously, but don't want to delay and risk the duplicate being run while the original is also pending/processing
if (isset($cron_array[$timestamp][$action][$instance])) {
$this->mark_job_post_completed($jobs_post->ID, false);
continue;
}
// Populate remaining job data
$cron_array[$timestamp][$action][$instance] = array('schedule' => $args['schedule'], 'args' => $args['args']);
if (isset($args['interval'])) {
$cron_array[$timestamp][$action][$instance]['interval'] = $args['interval'];
}
}
}
} while (true);
// Re-sort the array just as Core does when events are scheduled
// Ensures events are sorted chronologically
uksort($cron_array, 'strnatcasecmp');
// If we're unscheduling an event, hold onto the previous value so we can identify what's unscheduled
if ($this->is_unscheduling()) {
$this->option_before_unscheduling = $cron_array;
} else {
$this->option_before_unscheduling = null;
}
// Cache the results, bearing in mind that they won't be used during unscheduling events
wp_cache_set(self::CACHE_KEY, $cron_array, null, 1 * \HOUR_IN_SECONDS);
return $cron_array;
}