| @@ -58,12 +58,8 @@ | ||
| 58 | 58 | * |
| 59 | 59 | * Initializes the cron manager and hooks into WordPress. |
| 60 | 60 | */ |
| 61 | 61 | public function __construct() { |
| 62 | - if ( did_action( 'after_setup_theme' ) > 0 ) { | |
| 63 | - $this->load_callbacks(); | |
| 64 | - } | |
| 65 | - | |
| 66 | 62 | add_action( 'shutdown', array( $this, 'check_and_execute_callbacks' ), PHP_INT_MAX ); |
| 67 | 63 | } |
| 68 | 64 | |
| 69 | 65 | /** |
| @@ -117,10 +113,10 @@ | ||
| 117 | 113 | wp_clear_scheduled_hook( $hook_name ); |
| 118 | 114 | $this->log( "Cleared existing WordPress cron event for '{$hook_name}' to prevent conflicts", self::LOG_FILE_NAME ); |
| 119 | 115 | } |
| 120 | 116 | |
| 121 | - // Only save if this is a new callback. | |
| 122 | - if ( ! isset( $this->callbacks[ $hook_name ] ) || $this->callbacks[ $hook_name ]['interval'] !== $interval_seconds ) { | |
| 117 | + // Only save if this is a new callback or something changed. | |
| 118 | + if ( ! isset( $this->callbacks[ $hook_name ] ) || $this->callbacks[ $hook_name ]['interval'] !== $interval_seconds || $this->callbacks[ $hook_name ]['start_time'] !== $start_time ) { | |
| 123 | 119 | $this->callbacks[ $hook_name ] = array( |
| 124 | 120 | 'interval' => $interval_seconds, |
| 125 | 121 | 'start_time' => $start_time, |
| 126 | 122 | 'args' => $args, |
| @@ -198,8 +194,23 @@ | ||
| 198 | 194 | // Early exit during plugin deletion/uninstall. |
| 199 | 195 | if ( defined( 'WP_UNINSTALL_PLUGIN' ) ) { |
| 200 | 196 | return; |
| 201 | 197 | } |
| 198 | + | |
| 199 | + // Skip if page post ID exists, or during AJAX/cron execution to prevent concurrent runs. | |
| 200 | + $post_data = defender_get_data_from_request( null, 'p' ); | |
| 201 | + if ( | |
| 202 | + ( is_array( $post_data ) && array() !== $post_data ) | |
| 203 | + || defined( 'DOING_AJAX' ) | |
| 204 | + || defined( 'DOING_CRON' ) | |
| 205 | + ) { | |
| 206 | + return; | |
| 207 | + } | |
| 208 | + | |
| 209 | + if ( did_action( 'after_setup_theme' ) > 0 ) { | |
| 210 | + $this->load_callbacks(); | |
| 211 | + } | |
| 212 | + | |
| 202 | 213 | if ( array() === $this->callbacks ) { |
| 203 | 214 | return; |
| 204 | 215 | } |
| 205 | 216 | foreach ( $this->callbacks as $hook_name => $config ) { |
| @@ -248,9 +259,10 @@ | ||
| 248 | 259 | * |
| 249 | 260 | * @param int $interval The interval in seconds. |
| 250 | 261 | * @param string $hook_name The hook name being executed. |
| 251 | 262 | */ |
| 252 | - $interval = (int) apply_filters( 'wpdef_network_cron_interval', $interval, $hook_name ); | |
| 263 | + $interval = apply_filters( 'wpdef_network_cron_interval', $interval, $hook_name ); | |
| 264 | + $interval = ! is_int( $interval ) ? (int) $interval : $interval; | |
| 253 | 265 | $last_run = $this->get_last_run( $hook_name ); |
| 254 | 266 | $current_time = defender_get_current_time(); |
| 255 | 267 | |
| 256 | 268 | if ( ! $last_run && isset( $config['start_time'] ) && null !== $config['start_time'] ) { |
| @@ -282,12 +294,13 @@ | ||
| 282 | 294 | * |
| 283 | 295 | * @param int $timeout The timeout in seconds. |
| 284 | 296 | * @param string $hook_name The hook name being locked. |
| 285 | 297 | */ |
| 286 | - $lock_timeout = (int) apply_filters( 'wpdef_network_cron_lock_timeout', 300, $hook_name ); | |
| 298 | + $lock_timeout = apply_filters( 'wpdef_network_cron_lock_timeout', 300, $hook_name ); | |
| 299 | + $lock_timeout = ! is_int( $lock_timeout ) ? (int) $lock_timeout : $lock_timeout; | |
| 287 | 300 | $lock_key = $this->lock_prefix . $hook_name; |
| 288 | 301 | $existing_lock = get_network_option( get_main_network_id(), $lock_key ); |
| 289 | - if ( $existing_lock && ( defender_get_current_time() - $existing_lock ) < $lock_timeout ) { | |
| 302 | + if ( 0 < $existing_lock && ( defender_get_current_time() - $existing_lock ) < $lock_timeout ) { | |
| 290 | 303 | return false; |
| 291 | 304 | } |
| 292 | 305 | $lock_value = defender_get_current_time(); |
| 293 | 306 | return update_network_option( get_main_network_id(), $lock_key, $lock_value ); |