PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-sync / src / modules / class-callables.php
jetpack / jetpack_vendor / automattic / jetpack-sync / src / modules Last commit date
class-attachments.php 9 months ago class-callables.php 2 months ago class-comments.php 2 months ago class-constants.php 9 months ago class-full-sync-immediately.php 3 months ago class-full-sync.php 3 months ago class-import.php 9 months ago class-menus.php 2 months ago class-meta.php 2 months ago class-module.php 5 months ago class-network-options.php 9 months ago class-options.php 3 months ago class-plugins.php 3 months ago class-posts.php 2 months ago class-protect.php 9 months ago class-search.php 3 months ago class-stats.php 6 months ago class-term-relationships.php 9 months ago class-terms.php 2 months ago class-themes.php 2 months ago class-updates.php 8 months ago class-users.php 2 months ago class-woocommerce-hpos-orders.php 2 months ago class-woocommerce-products.php 8 months ago class-woocommerce.php 2 months ago class-wp-super-cache.php 9 months ago
class-callables.php
683 lines
1 <?php
2 /**
3 * Callables sync module.
4 *
5 * @package automattic/jetpack-sync
6 */
7
8 namespace Automattic\Jetpack\Sync\Modules;
9
10 use Automattic\Jetpack\Constants as Jetpack_Constants;
11 use Automattic\Jetpack\Sync\Dedicated_Sender;
12 use Automattic\Jetpack\Sync\Defaults;
13 use Automattic\Jetpack\Sync\Functions;
14 use Automattic\Jetpack\Sync\Settings;
15
16 if ( ! defined( 'ABSPATH' ) ) {
17 exit( 0 );
18 }
19
20 /**
21 * Class to handle sync for callables.
22 */
23 class Callables extends Module {
24 /**
25 * Name of the callables checksum option.
26 *
27 * @var string
28 */
29 const CALLABLES_CHECKSUM_OPTION_NAME = 'jetpack_callables_sync_checksum';
30
31 /**
32 * Name of the transient for locking callables.
33 *
34 * @var string
35 */
36 const CALLABLES_AWAIT_TRANSIENT_NAME = 'jetpack_sync_callables_await';
37
38 /**
39 * Whitelist for callables we want to sync.
40 *
41 * @access private
42 *
43 * @var array
44 */
45 private $callable_whitelist;
46
47 /**
48 * For some options, we should always send the change right away!
49 *
50 * @access public
51 *
52 * @var array
53 */
54 const ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS = array(
55 'jetpack_active_modules',
56 'home', // option is home, callable is home_url.
57 'siteurl',
58 'jetpack_sync_error_idc',
59 'paused_plugins',
60 'paused_themes',
61
62 );
63
64 const ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS_NEXT_TICK = array(
65 'stylesheet',
66 );
67 /**
68 * Setting this value to true will make it so that the callables will not be unlocked
69 * but the lock will be removed after content is send so that callables will be
70 * sent in the next request.
71 *
72 * @var bool
73 */
74 private $force_send_callables_on_next_tick = false;
75
76 /**
77 * For some options, the callable key differs from the option name/key
78 *
79 * @access public
80 *
81 * @var array
82 */
83 const OPTION_NAMES_TO_CALLABLE_NAMES = array(
84 // @TODO: Audit the other option names for differences between the option names and callable names.
85 'home' => 'home_url',
86 'siteurl' => 'site_url',
87 'jetpack_active_modules' => 'active_modules',
88 );
89
90 /**
91 * Sync module name.
92 *
93 * @access public
94 *
95 * @return string
96 */
97 public function name() {
98 return 'functions';
99 }
100
101 /**
102 * Set module defaults.
103 * Define the callable whitelist based on whether this is a single site or a multisite installation.
104 *
105 * @access public
106 */
107 public function set_defaults() {
108 if ( is_multisite() ) {
109 $this->callable_whitelist = array_merge( Defaults::get_callable_whitelist(), Defaults::get_multisite_callable_whitelist() );
110 } else {
111 $this->callable_whitelist = Defaults::get_callable_whitelist();
112 }
113 $this->force_send_callables_on_next_tick = false; // Resets here as well mostly for tests.
114 }
115
116 /**
117 * Set module defaults at a later time.
118 * Reset the callable whitelist if needed to account for plugins adding the 'jetpack_sync_callable_whitelist'
119 * and 'jetpack_sync_multisite_callable_whitelist' filters late.
120 *
121 * @see Automattic\Jetpack\Sync\Modules::set_module_defaults
122 * @access public
123 */
124 public function set_late_default() {
125 if ( is_multisite() ) {
126 $late_callables = array_merge(
127 apply_filters( 'jetpack_sync_callable_whitelist', array() ),
128 apply_filters( 'jetpack_sync_multisite_callable_whitelist', array() )
129 );
130 } else {
131 $late_callables = apply_filters( 'jetpack_sync_callable_whitelist', array() );
132 }
133 if ( ! empty( $late_callables ) && is_array( $late_callables ) ) {
134 $this->callable_whitelist = array_merge( $this->callable_whitelist, $late_callables );
135 }
136 }
137
138 /**
139 * Initialize callables action listeners.
140 *
141 * @access public
142 *
143 * @param callable $callable Action handler callable.
144 */
145 public function init_listeners( $callable ) {
146 add_action( 'jetpack_sync_callable', $callable, 10, 2 );
147 add_action( 'current_screen', array( $this, 'set_plugin_action_links' ), 9999 ); // Should happen very late.
148
149 foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS as $option ) {
150 add_action( "update_option_{$option}", array( $this, 'unlock_sync_callable' ) );
151 add_action( "delete_option_{$option}", array( $this, 'unlock_sync_callable' ) );
152 }
153
154 foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS_NEXT_TICK as $option ) {
155 add_action( "update_option_{$option}", array( $this, 'unlock_sync_callable_next_tick' ) );
156 add_action( "delete_option_{$option}", array( $this, 'unlock_sync_callable_next_tick' ) );
157 }
158
159 // Provide a hook so that hosts can send changes to certain callables right away.
160 // Especially useful when a host uses constants to change home and siteurl.
161 add_action( 'jetpack_sync_unlock_sync_callable', array( $this, 'unlock_sync_callable' ) );
162
163 // get_plugins and wp_version
164 // gets fired when new code gets installed, updates etc.
165 add_action( 'upgrader_process_complete', array( $this, 'unlock_plugin_action_link_and_callables' ) );
166 add_action( 'update_option_active_plugins', array( $this, 'unlock_plugin_action_link_and_callables' ) );
167 // Deleting an inactive plugin fires neither of the above, so without this the synced
168 // get_plugins callable stays stale until the periodic re-check happens to run.
169 add_action( 'deleted_plugin', array( $this, 'unlock_plugin_action_link_and_callables' ) );
170 }
171
172 /**
173 * Initialize callables action listeners for full sync.
174 *
175 * @access public
176 *
177 * @param callable $callable Action handler callable.
178 */
179 public function init_full_sync_listeners( $callable ) {
180 add_action( 'jetpack_full_sync_callables', $callable );
181 }
182
183 /**
184 * Initialize the module in the sender.
185 *
186 * @access public
187 */
188 public function init_before_send() {
189 add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_sync_callables' ) );
190
191 // Full sync.
192 add_filter( 'jetpack_sync_before_send_jetpack_full_sync_callables', array( $this, 'expand_callables' ) );
193 }
194
195 /**
196 * Perform module cleanup.
197 * Deletes any transients and options that this module uses.
198 * Usually triggered when uninstalling the plugin.
199 *
200 * @access public
201 */
202 public function reset_data() {
203 delete_option( self::CALLABLES_CHECKSUM_OPTION_NAME );
204 delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
205
206 $url_callables = array( 'home_url', 'site_url', 'main_network_site_url' );
207 foreach ( $url_callables as $callable ) {
208 delete_option( Functions::HTTPS_CHECK_OPTION_PREFIX . $callable );
209 }
210 }
211
212 /**
213 * Set the callable whitelist.
214 *
215 * @access public
216 *
217 * @param array $callables The new callables whitelist.
218 */
219 public function set_callable_whitelist( $callables ) {
220 $this->callable_whitelist = $callables;
221 }
222
223 /**
224 * Get the callable whitelist.
225 *
226 * @access public
227 *
228 * @return array The callables whitelist.
229 */
230 public function get_callable_whitelist() {
231 return $this->callable_whitelist;
232 }
233
234 /**
235 * Retrieve all callables as per the current callables whitelist.
236 *
237 * @access public
238 *
239 * @return array All callables.
240 */
241 public function get_all_callables() {
242 // get_all_callables should run as the master user always.
243 $current_user_id = get_current_user_id();
244 wp_set_current_user( \Jetpack_Options::get_option( 'master_user' ) );
245 $callables = array_combine(
246 array_keys( $this->get_callable_whitelist() ),
247 array_map( array( $this, 'get_callable' ), array_values( $this->get_callable_whitelist() ) )
248 );
249 wp_set_current_user( $current_user_id );
250 return $callables;
251 }
252
253 /**
254 * Invoke a particular callable.
255 * Used as a wrapper to standartize invocation.
256 *
257 * @access private
258 *
259 * @param callable $callable Callable to invoke.
260 * @return mixed Return value of the callable, null if not callable.
261 */
262 private function get_callable( $callable ) {
263 if ( is_callable( $callable ) ) {
264 return call_user_func( $callable );
265 } else {
266 return null;
267 }
268 }
269
270 /**
271 * Enqueue the callable actions for full sync.
272 *
273 * @access public
274 *
275 * @param array $config Full sync configuration for this sync module.
276 * @param int $max_items_to_enqueue Maximum number of items to enqueue.
277 * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
278 * @return array Number of actions enqueued, and next module state.
279 */
280 public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
281 /**
282 * Tells the client to sync all callables to the server
283 *
284 * @since 1.6.3
285 * @since-jetpack 4.2.0
286 *
287 * @param boolean Whether to expand callables (should always be true)
288 */
289 do_action( 'jetpack_full_sync_callables', true );
290
291 // The number of actions enqueued, and next module state (true == done).
292 return array( 1, true );
293 }
294
295 /**
296 * Send the callable actions for full sync.
297 *
298 * @access public
299 *
300 * @param array $config Full sync configuration for this sync module.
301 * @param array $status This Module Full Sync Status.
302 * @param int $send_until The timestamp until the current request can send.
303 * @param int $started The timestamp when the full sync started.
304 *
305 * @return array This Module Full Sync Status.
306 */
307 public function send_full_sync_actions( $config, $status, $send_until, $started ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
308 // we call this instead of do_action when sending immediately.
309 $result = $this->send_action( 'jetpack_full_sync_callables', array( true ) );
310
311 if ( is_wp_error( $result ) ) {
312 $status['error'] = true;
313 return $status;
314 }
315 $status['finished'] = true;
316 $status['sent'] = $status['total'];
317 return $status;
318 }
319
320 /**
321 * Retrieve an estimated number of actions that will be enqueued.
322 *
323 * @access public
324 *
325 * @param array $config Full sync configuration for this sync module.
326 * @return int Number of items yet to be enqueued.
327 */
328 public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
329 return 1;
330 }
331
332 /**
333 * Retrieve the actions that will be sent for this module during a full sync.
334 *
335 * @access public
336 *
337 * @return array Full sync actions of this module.
338 */
339 public function get_full_sync_actions() {
340 return array( 'jetpack_full_sync_callables' );
341 }
342
343 /**
344 * Unlock callables so they would be available for syncing again.
345 *
346 * @access public
347 */
348 public function unlock_sync_callable() {
349 delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
350 }
351
352 /**
353 * Unlock callables on the next tick.
354 * Sometime the true callable values are only present on the next tick.
355 * When switching themes for example.
356 *
357 * @access public
358 */
359 public function unlock_sync_callable_next_tick() {
360 $this->force_send_callables_on_next_tick = true;
361 }
362
363 /**
364 * Unlock callables and plugin action links.
365 *
366 * @access public
367 */
368 public function unlock_plugin_action_link_and_callables() {
369 delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
370 delete_transient( 'jetpack_plugin_api_action_links_refresh' );
371 add_filter( 'jetpack_check_and_send_callables', '__return_true' );
372 }
373
374 /**
375 * Parse and store the plugin action links if on the plugins page.
376 *
377 * @uses \DOMDocument
378 * @uses libxml_use_internal_errors
379 * @uses mb_convert_encoding
380 *
381 * @access public
382 */
383 public function set_plugin_action_links() {
384 if (
385 ! class_exists( '\DOMDocument' ) ||
386 ! function_exists( 'mb_convert_encoding' )
387 ) {
388 return;
389 }
390
391 $current_screeen = get_current_screen();
392
393 $plugins_action_links = array();
394 // Is the transient lock in place?
395 $plugins_lock = get_transient( 'jetpack_plugin_api_action_links_refresh' );
396 if ( ! empty( $plugins_lock ) && ( isset( $current_screeen->id ) && 'plugins' !== $current_screeen->id ) ) {
397 return;
398 }
399 $plugins = Functions::get_plugins();
400 if ( ! is_array( $plugins ) ) {
401 return;
402 }
403 foreach ( $plugins as $plugin_file => $plugin_data ) {
404 /**
405 * Plugins often like to unset things but things break if they are not able to.
406 */
407 $action_links = array(
408 'deactivate' => '',
409 'activate' => '',
410 'details' => '',
411 'delete' => '',
412 'edit' => '',
413 );
414 /** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
415 $action_links = apply_filters( 'plugin_action_links', $action_links, $plugin_file, $plugin_data, 'all' );
416 // Verify $action_links is still an array.
417 if ( ! is_array( $action_links ) ) {
418 $action_links = array();
419 }
420 /** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
421 $action_links = apply_filters( "plugin_action_links_{$plugin_file}", $action_links, $plugin_file, $plugin_data, 'all' );
422 // Verify $action_links is still an array to resolve warnings from filters not returning an array.
423 if ( is_array( $action_links ) ) {
424 $action_links = array_filter( $action_links );
425 } else {
426 $action_links = array();
427 }
428 $formatted_action_links = null;
429 if ( $action_links ) {
430 $dom_doc = new \DOMDocument();
431 foreach ( $action_links as $action_link ) {
432 // The @ is not enough to suppress errors when dealing with libxml,
433 // we have to tell it directly how we want to handle errors.
434 libxml_use_internal_errors( true );
435 $dom_doc->loadHTML( '<?xml encoding="utf-8" ?>' . $action_link );
436 libxml_use_internal_errors( false );
437
438 $link_elements = $dom_doc->getElementsByTagName( 'a' );
439 if ( 0 === $link_elements->length ) {
440 continue;
441 }
442
443 $link_element = $link_elements->item( 0 );
444 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
445 if ( $link_element instanceof \DOMElement && $link_element->hasAttribute( 'href' ) && $link_element->nodeValue ) {
446 $link_url = trim( $link_element->getAttribute( 'href' ) );
447
448 // Add the full admin path to the url if the plugin did not provide it.
449 $link_url_scheme = wp_parse_url( $link_url, PHP_URL_SCHEME );
450 if ( empty( $link_url_scheme ) ) {
451 $link_url = admin_url( $link_url );
452 }
453
454 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
455 $formatted_action_links[ $link_element->nodeValue ] = $link_url;
456 }
457 }
458 }
459 if ( $formatted_action_links ) {
460 $plugins_action_links[ $plugin_file ] = $formatted_action_links;
461 }
462 }
463 // Cache things for a long time.
464 set_transient( 'jetpack_plugin_api_action_links_refresh', time(), DAY_IN_SECONDS );
465 update_option( 'jetpack_plugin_api_action_links', $plugins_action_links );
466 }
467
468 /**
469 * Whether a certain callable should be sent.
470 *
471 * @access public
472 *
473 * @param array $callable_checksums Callable checksums.
474 * @param string $name Name of the callable.
475 * @param string $checksum A checksum of the callable.
476 * @return boolean Whether to send the callable.
477 */
478 public function should_send_callable( $callable_checksums, $name, $checksum ) {
479 $idc_override_callables = array(
480 'main_network_site',
481 'home_url',
482 'site_url',
483 );
484 if ( in_array( $name, $idc_override_callables, true ) && \Jetpack_Options::get_option( 'migrate_for_idc' ) ) {
485 return true;
486 }
487
488 return ! $this->still_valid_checksum( $callable_checksums, $name, $checksum );
489 }
490
491 /**
492 * Sync the callables if we're supposed to.
493 *
494 * @access public
495 */
496 public function maybe_sync_callables() {
497 $callables = $this->get_all_callables();
498 if ( ! apply_filters( 'jetpack_check_and_send_callables', false ) ) {
499 /**
500 * Treating Dedicated Sync requests a bit differently from normal. We want to send callables
501 * normally with all Sync actions, no matter if they were with admin action origin or not,
502 * since Dedicated Sync runs out of bound and the requests are never coming from an admin.
503 */
504 if ( ! is_admin() && ! Dedicated_Sender::is_dedicated_sync_request() ) {
505 // If we're not an admin and we're not doing cron and this isn't WP_CLI, don't sync anything.
506 if ( ! Settings::is_doing_cron() && ! Jetpack_Constants::get_constant( 'WP_CLI' ) ) {
507 return;
508 }
509 // If we're not an admin and we are doing cron, sync the Callables that are always supposed to sync ( See https://github.com/Automattic/jetpack/issues/12924 ).
510 $callables = $this->get_always_sent_callables();
511 }
512 if ( get_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME ) ) {
513 if ( $this->force_send_callables_on_next_tick ) {
514 $this->unlock_sync_callable();
515 }
516 return;
517 }
518 }
519
520 if ( empty( $callables ) ) {
521 return;
522 }
523 // No need to set the transiant we are trying to remove it anyways.
524 if ( ! $this->force_send_callables_on_next_tick ) {
525 set_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME, microtime( true ), Defaults::$default_sync_callables_wait_time );
526 }
527
528 $callable_checksums = (array) \Jetpack_Options::get_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, array() );
529 $has_changed = false;
530 // Only send the callables that have changed.
531 foreach ( $callables as $name => $value ) {
532 $checksum = $this->get_check_sum( $value );
533
534 // Explicitly not using Identical comparison as get_option returns a string.
535 if ( $value !== null && $this->should_send_callable( $callable_checksums, $name, $checksum ) ) {
536
537 // Only send callable if the non sorted checksum also does not match.
538 if ( $this->should_send_callable( $callable_checksums, $name, $this->get_check_sum( $value, false ) ) ) {
539
540 /**
541 * Tells the client to sync a callable (aka function) to the server
542 *
543 * @param string The name of the callable
544 * @param mixed The value of the callable
545 *
546 * @since 1.6.3
547 * @since-jetpack 4.2.0
548 */
549 do_action( 'jetpack_sync_callable', $name, $value );
550 }
551
552 $callable_checksums[ $name ] = $checksum;
553 $has_changed = true;
554 } else {
555 $callable_checksums[ $name ] = $checksum;
556 }
557 }
558 if ( $has_changed ) {
559 \Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callable_checksums );
560 }
561
562 if ( $this->force_send_callables_on_next_tick ) {
563 $this->unlock_sync_callable();
564 }
565 }
566
567 /**
568 * Get the callables that should always be sent, e.g. on cron.
569 *
570 * @return array Callables that should always be sent
571 */
572 protected function get_always_sent_callables() {
573 $callables = $this->get_all_callables();
574 $cron_callables = array();
575 foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS as $option_name ) {
576 if ( array_key_exists( $option_name, $callables ) ) {
577 $cron_callables[ $option_name ] = $callables[ $option_name ];
578 continue;
579 }
580
581 // Check for the Callable name/key for the option, if different from option name.
582 if ( array_key_exists( $option_name, self::OPTION_NAMES_TO_CALLABLE_NAMES ) ) {
583 $callable_name = self::OPTION_NAMES_TO_CALLABLE_NAMES[ $option_name ];
584 if ( array_key_exists( $callable_name, $callables ) ) {
585 $cron_callables[ $callable_name ] = $callables[ $callable_name ];
586 }
587 }
588 }
589 return $cron_callables;
590 }
591
592 /**
593 * Expand the callables within a hook before they are serialized and sent to the server.
594 *
595 * @access public
596 *
597 * @param array $args The hook parameters.
598 * @return array $args The hook parameters.
599 */
600 public function expand_callables( $args ) {
601 if ( $args[0] ) {
602 $callables = $this->get_all_callables();
603 $callables_checksums = array();
604 foreach ( $callables as $name => $value ) {
605 $callables_checksums[ $name ] = $this->get_check_sum( $value );
606 }
607 \Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callables_checksums );
608 return $callables;
609 }
610
611 return $args;
612 }
613
614 /**
615 * Return Total number of objects.
616 *
617 * @param array $config Full Sync config.
618 *
619 * @return int total
620 */
621 public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
622 return count( $this->get_callable_whitelist() );
623 }
624
625 /**
626 * Retrieve a set of callables by their IDs.
627 *
628 * @access public
629 *
630 * @param string $object_type Object type.
631 * @param array $ids Object IDs.
632 * @return array Array of objects.
633 */
634 public function get_objects_by_id( $object_type, $ids ) {
635 if ( empty( $ids ) || empty( $object_type ) || 'callable' !== $object_type ) {
636 return array();
637 }
638
639 $objects = array();
640 foreach ( (array) $ids as $id ) {
641 $object = $this->get_object_by_id( $object_type, $id );
642
643 if ( 'CALLABLE-DOES-NOT-EXIST' !== $object ) {
644 if ( 'all' === $id ) {
645 // If all was requested it contains all options and can simply be returned.
646 return $object;
647 }
648 $objects[ $id ] = $object;
649 }
650 }
651
652 return $objects;
653 }
654
655 /**
656 * Retrieve a callable by its name.
657 *
658 * @access public
659 *
660 * @param string $object_type Type of the sync object.
661 * @param string $id ID of the sync object.
662 * @return mixed Value of Callable.
663 */
664 public function get_object_by_id( $object_type, $id ) {
665 if ( 'callable' === $object_type ) {
666
667 // Only whitelisted options can be returned.
668 if ( array_key_exists( $id, $this->get_callable_whitelist() ) ) {
669 // requires master user to be in context.
670 $current_user_id = get_current_user_id();
671 wp_set_current_user( \Jetpack_Options::get_option( 'master_user' ) );
672 $callable = $this->get_callable( $this->callable_whitelist[ $id ] );
673 wp_set_current_user( $current_user_id );
674 return $callable;
675 } elseif ( 'all' === $id ) {
676 return $this->get_all_callables();
677 }
678 }
679
680 return 'CALLABLE-DOES-NOT-EXIST';
681 }
682 }
683