| 1 |
<?php |
| 2 |
/** |
| 3 |
* A pseudo-cron daemon for scheduling WordPress tasks. |
| 4 |
* |
| 5 |
* WP-Cron is triggered when the site receives a visit. In the scenario |
| 6 |
* where a site may not receive enough visits to execute scheduled tasks |
| 7 |
* in a timely manner, this file can be called directly or via a server |
| 8 |
* cron daemon for X number of times. |
| 9 |
* |
| 10 |
* Defining DISABLE_WP_CRON as true and calling this file directly are |
| 11 |
* mutually exclusive and the latter does not rely on the former to work. |
| 12 |
* |
| 13 |
* The HTTP request to this file will not slow down the visitor who happens to |
| 14 |
* visit when a scheduled cron event runs. |
| 15 |
* |
| 16 |
* @package WordPress |
| 17 |
*/ |
| 18 |
|
| 19 |
ignore_user_abort( true ); |
| 20 |
|
| 21 |
if ( ! headers_sent() ) { |
| 22 |
header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' ); |
| 23 |
header( 'Cache-Control: no-cache, must-revalidate, max-age=0' ); |
| 24 |
} |
| 25 |
|
| 26 |
$response['status'] = 'error'; |
| 27 |
|
| 28 |
if (! empty( $_POST ) || defined( 'DOING_AJAX' ) || defined( 'DOING_CRON' ) ) { |
| 29 |
$response['message'] = 'Error: Cannot start wp-cron.'; |
| 30 |
// wp_send_json is not loaded yet. |
| 31 |
echo json_encode( $response ); |
| 32 |
exit; |
| 33 |
} |
| 34 |
|
| 35 |
/** |
| 36 |
* Tell WordPress the cron task is running. |
| 37 |
*/ |
| 38 |
define( 'DOING_CRON', true ); |
| 39 |
|
| 40 |
/** |
| 41 |
* Load WordPress bootstrap file and our MU plugin. |
| 42 |
*/ |
| 43 |
if (! is_file( __DIR__ .'/tmp/profiler.inc.php') ) { |
| 44 |
$response['message'] = 'Error: Cannot find [/tmp/profiler.inc.php].'; |
| 45 |
echo json_encode( $response ); |
| 46 |
exit; |
| 47 |
} |
| 48 |
include_once __DIR__ .'/tmp/profiler.inc.php'; |
| 49 |
require_once ABSPATH . 'wp-load.php'; |
| 50 |
|
| 51 |
/** |
| 52 |
* No one else is allowed to run this script. |
| 53 |
* Full verification is done by the MU plugin. |
| 54 |
*/ |
| 55 |
if (! isset( $_REQUEST['CODE_PROFILER_ON'] ) || |
| 56 |
! preg_match('/^\d{10}\.\d+$/', $_REQUEST['CODE_PROFILER_ON'] ) ) { |
| 57 |
|
| 58 |
$response['message'] = 'Not allowed.'; |
| 59 |
wp_send_json( $response ); |
| 60 |
} |
| 61 |
|
| 62 |
/** |
| 63 |
* Make sure we have a valid cron task. |
| 64 |
*/ |
| 65 |
if ( empty( $_GET['wpcron'] ) ) { |
| 66 |
$response['message'] = 'Error: No cron event selected.'; |
| 67 |
wp_send_json( $response ); |
| 68 |
} |
| 69 |
|
| 70 |
/** |
| 71 |
* Get the event to run. |
| 72 |
*/ |
| 73 |
$wpcron = trim( $_GET['wpcron'] ); |
| 74 |
$crons = []; |
| 75 |
$wpcrons = _get_cron_array(); |
| 76 |
|
| 77 |
foreach ( $wpcrons as $timestamp => $cronhooks ) { |
| 78 |
foreach ( $cronhooks as $hook => $keys ) { |
| 79 |
if ( $hook == $wpcron ) { |
| 80 |
$time = (int) microtime( true ); |
| 81 |
/** |
| 82 |
* There could be multiple cron events scheduled at the same time. |
| 83 |
*/ |
| 84 |
$crons[ $time - 10 ][ $hook ] = $keys; |
| 85 |
break 2; |
| 86 |
} |
| 87 |
} |
| 88 |
} |
| 89 |
if ( empty( $crons ) ) { |
| 90 |
$response['message'] = 'Error: Cannot find the requested cron event.'; |
| 91 |
wp_send_json( $response ); |
| 92 |
} |
| 93 |
|
| 94 |
// Attempt to raise the PHP memory limit for cron event processing. |
| 95 |
wp_raise_memory_limit( 'cron' ); |
| 96 |
|
| 97 |
/** |
| 98 |
* Retrieves the cron lock. |
| 99 |
* |
| 100 |
* Returns the uncached `doing_cron` transient. |
| 101 |
* |
| 102 |
* @ignore |
| 103 |
* @since 3.3.0 |
| 104 |
* |
| 105 |
* @global wpdb $wpdb WordPress database abstraction object. |
| 106 |
* |
| 107 |
* @return string|int|false Value of the `doing_cron` transient, 0|false otherwise. |
| 108 |
*/ |
| 109 |
function _get_cron_lock() { |
| 110 |
global $wpdb; |
| 111 |
|
| 112 |
$value = 0; |
| 113 |
if ( wp_using_ext_object_cache() ) { |
| 114 |
/* |
| 115 |
* Skip local cache and force re-fetch of doing_cron transient |
| 116 |
* in case another process updated the cache. |
| 117 |
*/ |
| 118 |
$value = wp_cache_get( 'doing_cron', 'transient', true ); |
| 119 |
} else { |
| 120 |
$row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", '_transient_doing_cron' ) ); |
| 121 |
if ( is_object( $row ) ) { |
| 122 |
$value = $row->option_value; |
| 123 |
} |
| 124 |
} |
| 125 |
|
| 126 |
return $value; |
| 127 |
} |
| 128 |
|
| 129 |
$gmt_time = microtime( true ); |
| 130 |
/** |
| 131 |
* The cron lock: a unix timestamp from when the cron was spawned. |
| 132 |
*/ |
| 133 |
$doing_cron_transient = get_transient( 'doing_cron' ); |
| 134 |
/** |
| 135 |
* Use global $doing_wp_cron lock, otherwise use the GET lock. If no lock, try to grab a new lock. |
| 136 |
*/ |
| 137 |
if ( empty( $doing_wp_cron ) ) { |
| 138 |
if ( empty( $_GET['doing_wp_cron'] ) ) { |
| 139 |
// Called from external script/job. Try setting a lock. |
| 140 |
if ( $doing_cron_transient && ( $doing_cron_transient + WP_CRON_LOCK_TIMEOUT > $gmt_time ) ) { |
| 141 |
$response['message'] = 'Error: WordPress cannot set a lock.'; |
| 142 |
wp_send_json( $response ); |
| 143 |
} |
| 144 |
$doing_wp_cron = sprintf( '%.22F', microtime( true ) ); |
| 145 |
$doing_cron_transient = $doing_wp_cron; |
| 146 |
set_transient( 'doing_cron', $doing_wp_cron ); |
| 147 |
} else { |
| 148 |
$doing_wp_cron = $_GET['doing_wp_cron']; |
| 149 |
} |
| 150 |
} |
| 151 |
|
| 152 |
/* |
| 153 |
* The cron lock (a unix timestamp set when the cron was spawned), |
| 154 |
* must match $doing_wp_cron (the "key"). |
| 155 |
*/ |
| 156 |
if ( $doing_cron_transient !== $doing_wp_cron ) { |
| 157 |
$response['message'] = 'Error: Transients do not match.'; |
| 158 |
wp_send_json( $response ); |
| 159 |
} |
| 160 |
|
| 161 |
foreach ( $crons as $timestamp => $cronhooks ) { |
| 162 |
foreach ( $cronhooks as $hook => $keys ) { |
| 163 |
foreach ( $keys as $k => $v ) { |
| 164 |
/** |
| 165 |
* Fires scheduled events. |
| 166 |
* |
| 167 |
* @ignore |
| 168 |
* @since 2.1.0 |
| 169 |
* |
| 170 |
* @param string $hook Name of the hook that was scheduled to be fired. |
| 171 |
* @param array $args The arguments to be passed to the hook. |
| 172 |
*/ |
| 173 |
do_action_ref_array( $hook, $v['args'] ); |
| 174 |
|
| 175 |
// If the hook ran too long and another cron process stole the lock, quit. |
| 176 |
if ( _get_cron_lock() !== $doing_wp_cron ) { |
| 177 |
$response['message'] = 'Error: The hook ran too long.'; |
| 178 |
wp_send_json( $response ); |
| 179 |
} |
| 180 |
} |
| 181 |
} |
| 182 |
} |
| 183 |
|
| 184 |
if ( _get_cron_lock() === $doing_wp_cron ) { |
| 185 |
delete_transient( 'doing_cron' ); |
| 186 |
} |
| 187 |
|
| 188 |
die(); |
| 189 |
|