/home/arranoyd/energyclinic/wp-content/plugins/docket-cache/includes/src/CronAgent.php
<?php
/**
* Docket Cache.
*
* @author Nawawi Jamili
* @license MIT
*
* @see https://github.com/nawawi/docket-cache
*/
namespace Nawawi\DocketCache;
\defined('ABSPATH') || exit;
final class CronAgent
{
private $is_pingpong;
private $pt;
public function __construct(Plugin $pt)
{
$this->pt = $pt;
$this->is_pingpong = false;
// warn issue: a non-numeric value encountered
!\defined('WP_CRON_LOCK_TIMEOUT') && \define('WP_CRON_LOCK_TIMEOUT', MINUTE_IN_SECONDS);
}
public function register()
{
add_action(
'wp',
function () {
$this->receive_ping();
},
\PHP_INT_MIN
);
add_action(
'shutdown',
function () {
$this->check_connection();
},
\PHP_INT_MAX
);
add_filter(
'docketcache/filter/active/cronbot',
function ($status) {
$status = $status ? 'on' : 'off';
return $this->send_action($status);
},
\PHP_INT_MAX
);
add_filter(
'docketcache/filter/check/cronbot',
function () {
return $this->send_action('on', 'pong');
},
\PHP_INT_MAX
);
add_filter(
'docketcache/filter/runevent/cronbot',
function ($runnow) {
$is_switch = $this->pt->switch_cron_site();
$results = $this->run_wpcron($runnow);
if (!empty($results) && \is_array($results) && !empty($results['wpcron_return'])) {
@Crawler::fetch_admin(admin_url('/'));
} else {
$results = false;
}
if ($is_switch) {
restore_current_blog();
}
return $results;
},
\PHP_INT_MAX
);
}
private function is_ping_request()
{
return !empty($_POST['ping']) && !empty($_GET['docketcache_ping']) && !empty($_SERVER['REQUEST_URI']) && false !== strpos($_SERVER['REQUEST_URI'], '/?docketcache_ping=');
}
private function maybe_disable_wp_cron()
{
// signal wp do not run wp-cron
$this->pt->cf()->maybe_define('DISABLE_WP_CRON', true);
}
private function send_action($action, $is_hello = false)
{
$is_quick = $is_hello && 'pong' !== $is_hello ? true : false;
$is_pong = 'pong' === $is_hello;
$uip = $this->pt->get_user_ip();
static $stmp = 0;
static $cache = [];
if (isset($cache[$uip])) {
return $cache[$uip];
}
$site_url = $this->pt->site_url();
$site_key = substr(md5($site_url), 0, 22);
$site_body = $this->pt->nw_encrypt($site_url, $site_key);
$site_id = $this->pt->nw_encrypt($site_key, $site_body);
$args = [
'blocking' => $is_quick ? false : true,
'body' => [
'timestamp' => date('Y-m-d H:i:s T'),
'timezone' => wp_timezone_string(),
'site' => $this->pt->base64_encode_url($site_body),
'meta' => $this->pt->site_meta(),
'status' => $action,
],
'headers' => [
'REFERER' => $this->pt->site_url(true, true),
'DOCKETID' => $site_id,
'DOCKETOB' => $this->pt->cx()->validate() ? 'on' : 'off',
],
];
if (0 === $stmp) {
$stmp = time() + 120;
}
$cronbot_endpoint = $this->pt->cronbot_endpoint.'/checkstatus?v='.$stmp;
$results = Crawler::post($cronbot_endpoint, $args);
if ($is_quick) {
return true;
}
$output = [
'timestamp' => time(),
'endpoint' => $cronbot_endpoint,
'connected' => false,
'last_status' => $action,
'request' => [
'headers' => $args['headers'],
'content' => $args['body'],
],
];
if (is_wp_error($results)) {
$output['error'] = $results->get_error_message();
$this->pt->co()->save_part($output, 'cronbot');
$this->pt->co()->lookup_set('cronboterror', $output['error']);
$cache[$uip] = false;
return false;
}
$output['response'] = wp_remote_retrieve_body($results);
if (!empty($output['response'])) {
$output['response'] = json_decode($output['response'], true);
if (\JSON_ERROR_NONE === json_last_error()) {
if (!empty($output['response']['error'])) {
$output['error'] = $output['response']['error'];
$this->pt->co()->save_part($output, 'cronbot');
$this->pt->co()->lookup_set('cronboterror', $output['error']);
$cache[$uip] = false;
return false;
}
}
}
$code = (int) wp_remote_retrieve_response_code($results);
if ($code > 400) {
$output['error'] = $code;
if (!$is_pong) {
$this->pt->co()->save_part($output, 'cronbot');
}
$this->pt->co()->lookup_set('cronboterror', 'Error '.$output['error']);
$cache[$uip] = false;
return false;
}
$output['connected'] = 'off' === $action ? false : true;
if (!$is_pong) {
$this->pt->co()->save_part($output, 'cronbot');
}
$cache[$uip] = true;
return true;
}
private function close_ping($response)
{
$output = $response;
$output['request'] = array_filter(
$_SERVER,
function ($arr) {
if ('HTTP_' === substr($arr, 0, 5)) {
return true;
}
},
\ARRAY_FILTER_USE_KEY
);
$output['selfcheck'] = time() + 5400; // 90min
$this->pt->co()->save_part($output, 'pings');
$this->pt->json_header();
$this->pt->close_exit(json_encode($response, \JSON_UNESCAPED_SLASHES));
}
private function run_wpcron($run_now = false)
{
$this->pt->cf()->maybe_define('DOING_RUN_WPCRON', true);
$run_uno = false;
$uno_ehk = '';
$uno_eky = '';
if (!empty($run_now) && \is_array($run_now)) {
if (empty($run_now['ehk']) || empty($run_now['eky'])) {
$results['wpcron_return'] = 0;
$results['wpcron_msg'] = esc_html__('Invalid request for single event', 'docket-cache');
return $results;
}
$uno_ehk = sanitize_text_field($run_now['ehk']);
$uno_eky = sanitize_text_field($run_now['eky']);
if (!has_action($uno_ehk)) {
$results['wpcron_return'] = 1;
/* translators: %s: Event Hook. */
$results['wpcron_msg'] = sprintf(esc_html__('Event hook not found %s', 'docket-cache'), $uno_ehk);
return $results;
}
$run_now = true;
$run_uno = true;
}
$crons = $this->pt->get_crons($run_now, $cron_event);
$results = [
'wpcron_return' => 0,
'wpcron_msg' => '',
'wpcron_crons' => $cron_event,
'wpcron_event' => 0,
];
if (empty($crons)) {
$results['wpcron_return'] = 1;
$results['wpcron_msg'] = esc_html__('No scheduled event ready to run', 'docket-cache');
return $results;
}
if (false !== strpos($_SERVER['REQUEST_URI'], '/wp-cron.php') || isset($_GET['doing_wp_cron']) || wp_doing_cron()) {
$results['wpcron_return'] = 1;
$results['wpcron_msg'] = esc_html__('Another cron process is currently running wp-cron.php', 'docket-cache');
return $results;
}
$gmt_time = microtime(true);
// overwrite cron lock
$doing_wp_cron = sprintf('%.22F', microtime(true));
set_transient('doing_cron', $doing_wp_cron, 86400);
$run_event = 0;
$slowdown = 0;
$delay = $this->is_pingpong ? 850 : 200;
$max_execution_time = $this->pt->get_max_execution_time();
foreach ($crons as $timestamp => $cronhooks) {
if ($max_execution_time > 0 && (microtime(true) - WP_START_TIMESTAMP) > $max_execution_time) {
break;
}
if (false === $run_now && ($timestamp > $gmt_time)) {
continue;
}
if ($slowdown > 10) {
$slowdown = 0;
usleep($delay);
}
++$slowdown;
foreach ($cronhooks as $hook => $keys) {
if (!has_action($hook)) {
// wp_clear_scheduled_hook($hook);
continue;
}
// single
if ($run_uno && $hook !== $uno_ehk) {
continue;
}
foreach ($keys as $k => $v) {
// single
if ($run_uno && $k !== $uno_eky) {
continue;
}
$schedule = $v['schedule'];
if ($schedule) {
if (false === wp_reschedule_event($timestamp, $schedule, $hook, $v['args'])) {
continue;
}
}
if (false === wp_unschedule_event($timestamp, $hook, $v['args'])) {
continue;
}
$hcontent = '';
try {
ob_start();
do_action_ref_array($hook, $v['args']);
$hcontent = trim(ob_get_contents());
ob_end_clean();
++$run_event;
} catch (\Throwable $e) {
$results['wpcron_error'][$hook] = $e->getMessage();
// wp_clear_scheduled_hook($hook);
if ($run_uno) {
$results['wpcron_return'] = 0;
$results['wpcron_event'] = 1;
$results['wpcron_uno'] = $uno_ehk;
break;
}
--$run_event;
}
if ('' !== $hcontent) {
$results['wpcron_output'][$hook] = $hcontent;
}
usleep(100);
}
}
}
unset($crons, $cronhooks, $hook, $keys);
// lock must below 10 minutes
// wp-includes/cron.php -> spawn_cron()
// wp-cron.php
$lock_wp_cron = microtime(true) + 300;
set_transient('doing_cron', $lock_wp_cron, 86400);
$results['wpcron_return'] = 1;
$results['wpcron_event'] = $run_event;
if ($run_uno && 1 === $run_event) {
$results['wpcron_uno'] = $uno_ehk;
}
return $results;
}
private function receive_ping()
{
if (headers_sent() || !$this->is_ping_request()) {
return;
}
if ($_POST['ping'] !== md5($_GET['docketcache_ping'])) {
$this->close_ping('Invalid ping');
return;
}
// signal wp do not run wp-cron
$this->maybe_disable_wp_cron();
$site_url = $this->pt->site_url();
if (!empty($_POST['token'])) {
$verify = $this->pt->nw_decrypt($_POST['token'], $_POST['ping']);
if ($verify !== $site_url) {
$this->close_ping('Invalid token');
return;
}
}
$uagent = $this->pt->get_user_agent();
if (false === strpos($uagent, 'docket-cache/') || !@preg_match('@compatible;\s+cronbot/[0-9\.]+;\s+docket\-cache/[0-9\.]+;\s+@', $uagent)) {
$this->close_ping('Invalid version');
return;
}
$uip = $this->pt->get_user_ip();
static $cache = [];
if (!empty($cache[$uip])) {
return $cache[$uip];
}
$response = [
'timestamp' => date('Y-m-d H:i:s T'),
'timezone' => wp_timezone_string(),
'site' => $this->pt->site_url(),
'meta' => $this->pt->site_meta(),
'status' => 1,
];
if ($this->pt->cf()->is_dcfalse('CRONBOT')) {
$response['status'] = 0;
$cache[$uip] = $response;
$this->close_ping($response);
}
if ($this->pt->co()->lockproc('receive_ping', time() + 60)) {
$response['msg'] = esc_html__('Already received. Try again in a few minutes', 'docket-cache');
$this->close_ping($response);
return false;
}
$this->is_pingpong = true;
$is_multisite = is_multisite();
$siteall = 0;
$sites = $this->pt->get_network_sites($siteall);
$maxrun = 0;
$maxcan = (int) $this->pt->cf()->dcvalue('CRONBOT_MAX');
$maxcan = $maxcan < 1 ? 5 : $maxcan;
$halt = false;
// finish it if user connection closed
if (\function_exists('ignore_user_abort')) {
ignore_user_abort(true);
}
foreach ($sites as $num => $site) {
if ($halt) {
break;
}
if ($is_multisite) {
switch_to_blog($site['id']);
}
if ($maxrun >= $maxcan) {
$results = [
'wpcron_return' => 3,
'wpcron_msg' => 'Reach maximum run: '.$maxrun.'/'.$siteall,
'wpcron_crons' => 0,
'wpcron_event' => 0,
];
$halt = true;
} else {
$results = $this->run_wpcron();
}
if ($is_multisite) {
restore_current_blog();
}
if ($site['is_main']) {
$response = array_merge($response, $results);
--$maxrun;
} else {
$response['site_'.$site['id']] = [
'site' => $site['url'],
'wpcron' => $results,
];
}
++$maxrun;
usleep(100);
}
unset($sites);
$cache[$uip] = $response;
$this->is_pingpong = false;
$this->close_ping($cache[$uip]);
}
private function check_connection()
{
static $done = false;
if ($done) {
return;
}
if (\function_exists('wp_is_maintenance_mode') && wp_is_maintenance_mode()) {
return;
}
// only main site
if (!is_main_site() || $this->pt->cf()->is_true('WP_IMPORTING') || $this->pt->is_request_from_theme_editor() || $this->is_ping_request()) {
return;
}
if ($this->pt->co()->lockexp('check_connection')) {
return;
}
$crondata = $this->pt->co()->get_part('cronbot', true);
if (empty($crondata)) {
return;
}
if (\is_array($crondata) && !empty($crondata['connected'])) {
$pingdata = $this->pt->co()->get_part('pings', true);
if (empty($pingdata)) {
return;
}
if (\is_array($pingdata) && !empty($pingdata['selfcheck'])) {
$selfcheck = $pingdata['selfcheck'];
if (0 === $this->pt->sanitize_timestamp($selfcheck)) {
return;
}
$locktime = time() + 300; // 5min
if (($selfcheck > 0 && time() > $selfcheck) && $this->pt->co()->setlock('check_connection', $locktime)) {
// signal wp do not run wp-cron
$this->maybe_disable_wp_cron();
// go background if possible
$this->pt->fastcgi_close();
$this->send_action('on', true);
$pingdata['selfcheck'] = time() + 5400; // 90min
$this->pt->co()->save_part($pingdata, 'pings');
if (!empty($crondata) && !$this->pt->cx()->validate()) {
$crondata['connected'] = false;
$this->pt->co()->save_part($crondata, 'cronbot');
}
$done = true;
}
}
}
unset($crondata);
}
}