PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.5.1
Defender Security – Malware Scanner, Login Security & Firewall v5.5.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / class-network-cron-manager.php

class-network-cron-manager.php in Defender Security – Malware Scanner, Login Security & Firewall 5.5.1, at src/component/class-network-cron-manager.php

250 lines 6.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Network Cron Manager for WP Defender.
4 *
5 * This file contains the Network_Cron_Manager class which manages
6 * centralized cron jobs across a multisite network with locking.
7 *
8 * @package WP_Defender\Component
9 */
10
11 namespace WP_Defender\Component;
12
13 use WP_Defender\Component;
14
15 /**
16 * Network Cron Manager class.
17 *
18 * Provides centralized cron management with locking and multisite execution control.
19 */
20 class Network_Cron_Manager extends Component {
21 /**
22 * Array of registered callbacks.
23 *
24 * @var array
25 */
26 private $callbacks = array();
27
28 /**
29 * Prefix for lock keys.
30 *
31 * @var string
32 */
33 private $lock_prefix = 'wpdef_cron_manager_lock_';
34
35 /**
36 * Prefix for last run timestamp keys.
37 *
38 * @var string
39 */
40 private $lastrun_prefix = 'wpdef_cron_manager_lastrun_';
41
42 /**
43 * Option name for storing callbacks.
44 *
45 * @var string
46 */
47 private $callbacks_option = 'wpdef_cron_manager_callbacks';
48
49 /**
50 * Constructor.
51 *
52 * Initializes the cron manager and hooks into WordPress.
53 */
54 public function __construct() {
55 add_action( 'init', array( $this, 'check_and_execute_callbacks' ), 20 );
56 }
57
58 /**
59 * Load callbacks from network options.
60 */
61 private function load_callbacks() {
62 $this->callbacks = get_network_option( get_main_network_id(), $this->callbacks_option, array() );
63 }
64
65 /**
66 * Save callbacks to network options.
67 */
68 private function save_callbacks() {
69 update_network_option( get_main_network_id(), $this->callbacks_option, $this->callbacks );
70 }
71
72 /**
73 * Register a callback for cron execution.
74 *
75 * @param string $hook_name The hook name.
76 * @param callable $callback The callback function.
77 * @param int $interval_seconds The interval in seconds.
78 * @param array $args Arguments for the callback.
79 * @return bool|void False on validation failure, void on success.
80 */
81 public function register_callback( $hook_name, $callback, $interval_seconds, $args = array() ) {
82 $hook_name = sanitize_key( $hook_name );
83 if ( empty( $hook_name ) || ! is_string( $hook_name ) ) {
84 return false;
85 }
86 if ( ! is_callable( $callback ) ) {
87 return false;
88 }
89 if ( ! is_numeric( $interval_seconds ) || $interval_seconds < 1 ) {
90 return false;
91 }
92 $this->callbacks[ $hook_name ] = array(
93 'callback' => $callback,
94 'interval' => $interval_seconds,
95 'args' => $args,
96 );
97 $this->save_callbacks();
98 }
99
100 /**
101 * Check and execute registered callbacks.
102 */
103 public function check_and_execute_callbacks() {
104 $this->load_callbacks();
105 if ( empty( $this->callbacks ) ) {
106 return;
107 }
108 foreach ( $this->callbacks as $hook_name => $config ) {
109 $this->execute_callback( $hook_name, $config );
110 }
111 }
112
113 /**
114 * Execute a specific callback.
115 *
116 * @param string $hook_name The hook name.
117 * @param array $config The callback configuration.
118 */
119 private function execute_callback( $hook_name, $config ) {
120 if ( ! $this->should_execute( $hook_name, $config['interval'] ) ) {
121 return;
122 }
123 if ( ! $this->acquire_lock( $hook_name ) ) {
124 $this->log( "Failed to acquire lock for {$hook_name}", 'cron-manager' );
125 return;
126 }
127 try {
128 if ( is_callable( $config['callback'] ) ) {
129 call_user_func_array( $config['callback'], $config['args'] );
130 $this->update_last_run( $hook_name );
131 }
132 } catch ( \Exception $exception ) {
133 $this->log( "Exception in {$hook_name}: " . $exception->getMessage(), 'cron-manager' );
134 } finally {
135 $this->release_lock( $hook_name );
136 }
137 }
138
139 /**
140 * Check if a callback should be executed based on interval.
141 *
142 * @param string $hook_name The hook name.
143 * @param int $interval The interval in seconds.
144 * @return bool Whether the callback should execute.
145 */
146 private function should_execute( $hook_name, $interval ) {
147 /**
148 * Filter to modify execution intervals for network cron jobs.
149 *
150 * @param int $interval The interval in seconds.
151 * @param string $hook_name The hook name being executed.
152 */
153 $interval = apply_filters( 'wpdef_network_cron_interval', $interval, $hook_name );
154 $last_run = $this->get_last_run( $hook_name );
155 $current_time = time();
156 $time_diff = $current_time - $last_run;
157 if ( $last_run && $time_diff < $interval ) {
158 $this->log( "Skipping {$hook_name}: not enough time elapsed ({$time_diff} < {$interval})", 'cron-manager' );
159 return false;
160 }
161 return true;
162 }
163
164 /**
165 * Acquire a lock for a specific hook.
166 *
167 * @param string $hook_name The hook name.
168 * @return bool Whether the lock was acquired.
169 */
170 private function acquire_lock( $hook_name ) {
171 /**
172 * Filter to modify lock timeout for network cron jobs.
173 *
174 * @param int $timeout The timeout in seconds.
175 * @param string $hook_name The hook name being locked.
176 */
177 $lock_timeout = apply_filters( 'wpdef_network_cron_lock_timeout', 300, $hook_name );
178 $lock_key = $this->lock_prefix . $hook_name;
179 $existing_lock = get_network_option( get_main_network_id(), $lock_key );
180 if ( $existing_lock && ( time() - $existing_lock ) < $lock_timeout ) {
181 return false;
182 }
183 $lock_value = time();
184 return update_network_option( get_main_network_id(), $lock_key, $lock_value );
185 }
186
187 /**
188 * Release a lock for a specific hook.
189 *
190 * @param string $hook_name The hook name.
191 */
192 private function release_lock( $hook_name ) {
193 $lock_key = $this->lock_prefix . $hook_name;
194 delete_network_option( get_main_network_id(), $lock_key );
195 }
196
197 /**
198 * Get the last run timestamp for a hook.
199 *
200 * @param string $hook_name The hook name.
201 * @return int The last run timestamp.
202 */
203 private function get_last_run( $hook_name ) {
204 $lastrun_key = $this->lastrun_prefix . $hook_name;
205 return get_network_option( get_main_network_id(), $lastrun_key, 0 );
206 }
207
208 /**
209 * Update the last run timestamp for a hook.
210 *
211 * @param string $hook_name The hook name.
212 */
213 private function update_last_run( $hook_name ) {
214 $lastrun_key = $this->lastrun_prefix . $hook_name;
215 $timestamp = time();
216 update_network_option( get_main_network_id(), $lastrun_key, $timestamp );
217 }
218
219 /**
220 * Get all registered callbacks.
221 *
222 * @return array The registered callbacks.
223 */
224 public function get_callbacks() {
225 return $this->callbacks;
226 }
227
228 /**
229 * Remove all Network Cron Manager data during uninstallation.
230 */
231 public function remove_data() {
232 if ( ! is_multisite() ) {
233 return;
234 }
235
236 $network_id = get_main_network_id();
237
238 delete_network_option( $network_id, $this->callbacks_option );
239
240 global $wpdb;
241 $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
242 $wpdb->prepare(
243 "DELETE FROM {$wpdb->sitemeta} WHERE meta_key LIKE %s OR meta_key LIKE %s",
244 $this->lock_prefix . '%',
245 $this->lastrun_prefix . '%'
246 )
247 );
248 }
249 }
250