Completed
Push — master ( c3c5a5...8d5679 )
by
unknown
211:18 queued 200:42
created

Callables::estimate_full_sync_actions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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\Defaults;
12
use Automattic\Jetpack\Sync\Functions;
13
use Automattic\Jetpack\Sync\Settings;
14
15
/**
16
 * Class to handle sync for callables.
17
 */
18
class Callables extends Module {
19
	/**
20
	 * Name of the callables checksum option.
21
	 *
22
	 * @var string
23
	 */
24
	const CALLABLES_CHECKSUM_OPTION_NAME = 'jetpack_callables_sync_checksum';
25
26
	/**
27
	 * Name of the transient for locking callables.
28
	 *
29
	 * @var string
30
	 */
31
	const CALLABLES_AWAIT_TRANSIENT_NAME = 'jetpack_sync_callables_await';
32
33
	/**
34
	 * Whitelist for callables we want to sync.
35
	 *
36
	 * @access private
37
	 *
38
	 * @var array
39
	 */
40
	private $callable_whitelist;
41
42
	/**
43
	 * For some options, we should always send the change right away!
44
	 *
45
	 * @access public
46
	 *
47
	 * @var array
48
	 */
49
	const ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS = array(
50
		'jetpack_active_modules',
51
		'home', // option is home, callable is home_url.
52
		'siteurl',
53
		'jetpack_sync_error_idc',
54
		'paused_plugins',
55
		'paused_themes',
56
57
	);
58
59
	const ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS_NEXT_TICK = array(
60
		'stylesheet',
61
	);
62
	/**
63
	 * Setting this value to true will make it so that the callables will not be unlocked
64
	 * but the lock will be removed after content is send so that callables will be
65
	 * sent in the next request.
66
	 *
67
	 * @var bool
68
	 */
69
	private $force_send_callables_on_next_tick = false;
70
71
	/**
72
	 * For some options, the callable key differs from the option name/key
73
	 *
74
	 * @access public
75
	 *
76
	 * @var array
77
	 */
78
	const OPTION_NAMES_TO_CALLABLE_NAMES = array(
79
		// @TODO: Audit the other option names for differences between the option names and callable names.
80
		'home'    => 'home_url',
81
		'siteurl' => 'site_url',
82
	);
83
84
	/**
85
	 * Sync module name.
86
	 *
87
	 * @access public
88
	 *
89
	 * @return string
90
	 */
91
	public function name() {
92
		return 'functions';
93
	}
94
95
	/**
96
	 * Set module defaults.
97
	 * Define the callable whitelist based on whether this is a single site or a multisite installation.
98
	 *
99
	 * @access public
100
	 */
101
	public function set_defaults() {
102
		if ( is_multisite() ) {
103
			$this->callable_whitelist = array_merge( Defaults::get_callable_whitelist(), Defaults::get_multisite_callable_whitelist() );
104
		} else {
105
			$this->callable_whitelist = Defaults::get_callable_whitelist();
106
		}
107
		$this->force_send_callables_on_next_tick = false; // Resets here as well mostly for tests.
108
	}
109
110
	/**
111
	 * Initialize callables action listeners.
112
	 *
113
	 * @access public
114
	 *
115
	 * @param callable $callable Action handler callable.
116
	 */
117
	public function init_listeners( $callable ) {
118
		add_action( 'jetpack_sync_callable', $callable, 10, 2 );
119
		add_action( 'current_screen', array( $this, 'set_plugin_action_links' ), 9999 ); // Should happen very late.
120
121 View Code Duplication
		foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS as $option ) {
122
			add_action( "update_option_{$option}", array( $this, 'unlock_sync_callable' ) );
123
			add_action( "delete_option_{$option}", array( $this, 'unlock_sync_callable' ) );
124
		}
125
126 View Code Duplication
		foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS_NEXT_TICK as $option ) {
127
			add_action( "update_option_{$option}", array( $this, 'unlock_sync_callable_next_tick' ) );
128
			add_action( "delete_option_{$option}", array( $this, 'unlock_sync_callable_next_tick' ) );
129
		}
130
131
		// Provide a hook so that hosts can send changes to certain callables right away.
132
		// Especially useful when a host uses constants to change home and siteurl.
133
		add_action( 'jetpack_sync_unlock_sync_callable', array( $this, 'unlock_sync_callable' ) );
134
135
		// get_plugins and wp_version
136
		// gets fired when new code gets installed, updates etc.
137
		add_action( 'upgrader_process_complete', array( $this, 'unlock_plugin_action_link_and_callables' ) );
138
		add_action( 'update_option_active_plugins', array( $this, 'unlock_plugin_action_link_and_callables' ) );
139
	}
140
141
	/**
142
	 * Initialize callables action listeners for full sync.
143
	 *
144
	 * @access public
145
	 *
146
	 * @param callable $callable Action handler callable.
147
	 */
148
	public function init_full_sync_listeners( $callable ) {
149
		add_action( 'jetpack_full_sync_callables', $callable );
150
	}
151
152
	/**
153
	 * Initialize the module in the sender.
154
	 *
155
	 * @access public
156
	 */
157
	public function init_before_send() {
158
		add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_sync_callables' ) );
159
160
		// Full sync.
161
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_callables', array( $this, 'expand_callables' ) );
162
	}
163
164
	/**
165
	 * Perform module cleanup.
166
	 * Deletes any transients and options that this module uses.
167
	 * Usually triggered when uninstalling the plugin.
168
	 *
169
	 * @access public
170
	 */
171
	public function reset_data() {
172
		delete_option( self::CALLABLES_CHECKSUM_OPTION_NAME );
173
		delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
174
175
		$url_callables = array( 'home_url', 'site_url', 'main_network_site_url' );
176
		foreach ( $url_callables as $callable ) {
177
			delete_option( Functions::HTTPS_CHECK_OPTION_PREFIX . $callable );
178
		}
179
	}
180
181
	/**
182
	 * Set the callable whitelist.
183
	 *
184
	 * @access public
185
	 *
186
	 * @param array $callables The new callables whitelist.
187
	 */
188
	public function set_callable_whitelist( $callables ) {
189
		$this->callable_whitelist = $callables;
190
	}
191
192
	/**
193
	 * Get the callable whitelist.
194
	 *
195
	 * @access public
196
	 *
197
	 * @return array The callables whitelist.
198
	 */
199
	public function get_callable_whitelist() {
200
		return $this->callable_whitelist;
201
	}
202
203
	/**
204
	 * Retrieve all callables as per the current callables whitelist.
205
	 *
206
	 * @access public
207
	 *
208
	 * @return array All callables.
209
	 */
210
	public function get_all_callables() {
211
		// get_all_callables should run as the master user always.
212
		$current_user_id = get_current_user_id();
213
		wp_set_current_user( \Jetpack_Options::get_option( 'master_user' ) );
214
		$callables = array_combine(
215
			array_keys( $this->get_callable_whitelist() ),
216
			array_map( array( $this, 'get_callable' ), array_values( $this->get_callable_whitelist() ) )
217
		);
218
		wp_set_current_user( $current_user_id );
219
		return $callables;
220
	}
221
222
	/**
223
	 * Invoke a particular callable.
224
	 * Used as a wrapper to standartize invocation.
225
	 *
226
	 * @access private
227
	 *
228
	 * @param callable $callable Callable to invoke.
229
	 * @return mixed Return value of the callable, null if not callable.
230
	 */
231
	private function get_callable( $callable ) {
232
		if ( is_callable( $callable ) ) {
233
			return call_user_func( $callable );
234
		} else {
235
			return null;
236
		}
237
	}
238
239
	/**
240
	 * Enqueue the callable actions for full sync.
241
	 *
242
	 * @access public
243
	 *
244
	 * @param array   $config               Full sync configuration for this sync module.
245
	 * @param int     $max_items_to_enqueue Maximum number of items to enqueue.
246
	 * @param boolean $state                True if full sync has finished enqueueing this module, false otherwise.
247
	 * @return array Number of actions enqueued, and next module state.
248
	 */
249
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
250
		/**
251
		 * Tells the client to sync all callables to the server
252
		 *
253
		 * @since 4.2.0
254
		 *
255
		 * @param boolean Whether to expand callables (should always be true)
256
		 */
257
		do_action( 'jetpack_full_sync_callables', true );
258
259
		// The number of actions enqueued, and next module state (true == done).
260
		return array( 1, true );
261
	}
262
263
	/**
264
	 * Send the callable actions for full sync.
265
	 *
266
	 * @access public
267
	 *
268
	 * @param array $config Full sync configuration for this sync module.
269
	 * @param int   $send_until The timestamp until the current request can send.
270
	 * @param array $status This Module Full Sync Status.
271
	 *
272
	 * @return array This Module Full Sync Status.
273
	 */
274
	public function send_full_sync_actions( $config, $send_until, $status ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
275
		// we call this instead of do_action when sending immediately.
276
		$this->send_action( 'jetpack_full_sync_callables', array( true ) );
277
278
		// The number of actions enqueued, and next module state (true == done).
279
		return array( 'finished' => true );
280
	}
281
282
	/**
283
	 * Retrieve an estimated number of actions that will be enqueued.
284
	 *
285
	 * @access public
286
	 *
287
	 * @param array $config Full sync configuration for this sync module.
288
	 * @return array Number of items yet to be enqueued.
289
	 */
290
	public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
291
		return 1;
292
	}
293
294
	/**
295
	 * Retrieve the actions that will be sent for this module during a full sync.
296
	 *
297
	 * @access public
298
	 *
299
	 * @return array Full sync actions of this module.
300
	 */
301
	public function get_full_sync_actions() {
302
		return array( 'jetpack_full_sync_callables' );
303
	}
304
305
	/**
306
	 * Unlock callables so they would be available for syncing again.
307
	 *
308
	 * @access public
309
	 */
310
	public function unlock_sync_callable() {
311
		delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
312
	}
313
314
	/**
315
	 * Unlock callables on the next tick.
316
	 * Sometime the true callable values are only present on the next tick.
317
	 * When switching themes for example.
318
	 *
319
	 * @access public
320
	 */
321
	public function unlock_sync_callable_next_tick() {
322
		$this->force_send_callables_on_next_tick = true;
323
	}
324
325
	/**
326
	 * Unlock callables and plugin action links.
327
	 *
328
	 * @access public
329
	 */
330
	public function unlock_plugin_action_link_and_callables() {
331
		delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
332
		delete_transient( 'jetpack_plugin_api_action_links_refresh' );
333
		add_filter( 'jetpack_check_and_send_callables', '__return_true' );
334
	}
335
336
	/**
337
	 * Parse and store the plugin action links if on the plugins page.
338
	 *
339
	 * @uses \DOMDocument
340
	 * @uses libxml_use_internal_errors
341
	 * @uses mb_convert_encoding
342
	 *
343
	 * @access public
344
	 */
345
	public function set_plugin_action_links() {
346
		if (
347
			! class_exists( '\DOMDocument' ) ||
348
			! function_exists( 'libxml_use_internal_errors' ) ||
349
			! function_exists( 'mb_convert_encoding' )
350
		) {
351
			return;
352
		}
353
354
		$current_screeen = get_current_screen();
355
356
		$plugins_action_links = array();
357
		// Is the transient lock in place?
358
		$plugins_lock = get_transient( 'jetpack_plugin_api_action_links_refresh', false );
0 ignored issues
show
Unused Code introduced by
The call to get_transient() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
359
		if ( ! empty( $plugins_lock ) && ( isset( $current_screeen->id ) && 'plugins' !== $current_screeen->id ) ) {
360
			return;
361
		}
362
		$plugins = array_keys( Functions::get_plugins() );
363
		foreach ( $plugins as $plugin_file ) {
364
			/**
365
			 *  Plugins often like to unset things but things break if they are not able to.
366
			 */
367
			$action_links = array(
368
				'deactivate' => '',
369
				'activate'   => '',
370
				'details'    => '',
371
				'delete'     => '',
372
				'edit'       => '',
373
			);
374
			/** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
375
			$action_links = apply_filters( 'plugin_action_links', $action_links, $plugin_file, null, 'all' );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $plugin_file.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
376
			/** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
377
			$action_links = apply_filters( "plugin_action_links_{$plugin_file}", $action_links, $plugin_file, null, 'all' );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $plugin_file.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
378
			// Verify $action_links is still an array to resolve warnings from filters not returning an array.
379
			if ( is_array( $action_links ) ) {
380
				$action_links = array_filter( $action_links );
381
			} else {
382
				$action_links = array();
383
			}
384
			$formatted_action_links = null;
385
			if ( ! empty( $action_links ) && count( $action_links ) > 0 ) {
386
				$dom_doc = new \DOMDocument();
387
				foreach ( $action_links as $action_link ) {
388
					// The @ is not enough to suppress errors when dealing with libxml,
389
					// we have to tell it directly how we want to handle errors.
390
					libxml_use_internal_errors( true );
391
					$dom_doc->loadHTML( mb_convert_encoding( $action_link, 'HTML-ENTITIES', 'UTF-8' ) );
392
					libxml_use_internal_errors( false );
393
394
					$link_elements = $dom_doc->getElementsByTagName( 'a' );
395
					if ( 0 === $link_elements->length ) {
396
						continue;
397
					}
398
399
					$link_element = $link_elements->item( 0 );
400
					// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
401
					if ( $link_element->hasAttribute( 'href' ) && $link_element->nodeValue ) {
0 ignored issues
show
Bug introduced by
The method hasAttribute() does not exist on DOMNode. Did you maybe mean hasAttributes()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
402
						$link_url = trim( $link_element->getAttribute( 'href' ) );
403
404
						// Add the full admin path to the url if the plugin did not provide it.
405
						$link_url_scheme = wp_parse_url( $link_url, PHP_URL_SCHEME );
406
						if ( empty( $link_url_scheme ) ) {
407
							$link_url = admin_url( $link_url );
408
						}
409
410
						// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
411
						$formatted_action_links[ $link_element->nodeValue ] = $link_url;
412
					}
413
				}
414
			}
415
			if ( $formatted_action_links ) {
416
				$plugins_action_links[ $plugin_file ] = $formatted_action_links;
417
			}
418
		}
419
		// Cache things for a long time.
420
		set_transient( 'jetpack_plugin_api_action_links_refresh', time(), DAY_IN_SECONDS );
421
		update_option( 'jetpack_plugin_api_action_links', $plugins_action_links );
422
	}
423
424
	/**
425
	 * Whether a certain callable should be sent.
426
	 *
427
	 * @access public
428
	 *
429
	 * @param array  $callable_checksums Callable checksums.
430
	 * @param string $name               Name of the callable.
431
	 * @param string $checksum           A checksum of the callable.
432
	 * @return boolean Whether to send the callable.
433
	 */
434
	public function should_send_callable( $callable_checksums, $name, $checksum ) {
435
		$idc_override_callables = array(
436
			'main_network_site',
437
			'home_url',
438
			'site_url',
439
		);
440
		if ( in_array( $name, $idc_override_callables, true ) && \Jetpack_Options::get_option( 'migrate_for_idc' ) ) {
441
			return true;
442
		}
443
444
		return ! $this->still_valid_checksum( $callable_checksums, $name, $checksum );
445
	}
446
447
	/**
448
	 * Sync the callables if we're supposed to.
449
	 *
450
	 * @access public
451
	 */
452
	public function maybe_sync_callables() {
453
		$callables = $this->get_all_callables();
454
		if ( ! apply_filters( 'jetpack_check_and_send_callables', false ) ) {
455
			if ( ! is_admin() ) {
456
				// If we're not an admin and we're not doing cron and this isn't WP_CLI, don't sync anything.
457
				if ( ! Settings::is_doing_cron() && ! Jetpack_Constants::get_constant( 'WP_CLI' ) ) {
458
					return;
459
				}
460
				// 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 ).
461
				$callables = $this->get_always_sent_callables();
462
			}
463
			if ( get_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME ) ) {
464
				if ( $this->force_send_callables_on_next_tick ) {
465
					$this->unlock_sync_callable();
466
				}
467
				return;
468
			}
469
		}
470
471
		if ( empty( $callables ) ) {
472
			return;
473
		}
474
		// No need to set the transiant we are trying to remove it anyways.
475
		if ( ! $this->force_send_callables_on_next_tick ) {
476
			set_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME, microtime( true ), Defaults::$default_sync_callables_wait_time );
477
		}
478
479
		$callable_checksums = (array) \Jetpack_Options::get_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, array() );
480
		$has_changed        = false;
481
		// Only send the callables that have changed.
482
		foreach ( $callables as $name => $value ) {
483
			$checksum = $this->get_check_sum( $value );
484
485
			// Explicitly not using Identical comparison as get_option returns a string.
486
			if ( ! is_null( $value ) && $this->should_send_callable( $callable_checksums, $name, $checksum ) ) {
487
488
				// Only send callable if the non sorted checksum also does not match.
489
				if ( $this->should_send_callable( $callable_checksums, $name, $this->get_check_sum( $value, false ) ) ) {
490
491
					/**
492
					 * Tells the client to sync a callable (aka function) to the server
493
					 *
494
					 * @param string The name of the callable
495
					 * @param mixed The value of the callable
496
					 *
497
					 * @since 4.2.0
498
					 */
499
					do_action( 'jetpack_sync_callable', $name, $value );
500
				}
501
502
				$callable_checksums[ $name ] = $checksum;
503
				$has_changed                 = true;
504
			} else {
505
				$callable_checksums[ $name ] = $checksum;
506
			}
507
		}
508
		if ( $has_changed ) {
509
			\Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callable_checksums );
510
		}
511
512
		if ( $this->force_send_callables_on_next_tick ) {
513
			$this->unlock_sync_callable();
514
		}
515
	}
516
517
	/**
518
	 * Get the callables that should always be sent, e.g. on cron.
519
	 *
520
	 * @return array Callables that should always be sent
521
	 */
522
	protected function get_always_sent_callables() {
523
		$callables      = $this->get_all_callables();
524
		$cron_callables = array();
525
		foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS as $option_name ) {
526
			if ( array_key_exists( $option_name, $callables ) ) {
527
				$cron_callables[ $option_name ] = $callables[ $option_name ];
528
				continue;
529
			}
530
531
			// Check for the Callable name/key for the option, if different from option name.
532
			if ( array_key_exists( $option_name, self::OPTION_NAMES_TO_CALLABLE_NAMES ) ) {
533
				$callable_name = self::OPTION_NAMES_TO_CALLABLE_NAMES[ $option_name ];
534
				if ( array_key_exists( $callable_name, $callables ) ) {
535
					$cron_callables[ $callable_name ] = $callables[ $callable_name ];
536
				}
537
			}
538
		}
539
		return $cron_callables;
540
	}
541
542
	/**
543
	 * Expand the callables within a hook before they are serialized and sent to the server.
544
	 *
545
	 * @access public
546
	 *
547
	 * @param array $args The hook parameters.
548
	 * @return array $args The hook parameters.
549
	 */
550
	public function expand_callables( $args ) {
551
		if ( $args[0] ) {
552
			$callables           = $this->get_all_callables();
553
			$callables_checksums = array();
554
			foreach ( $callables as $name => $value ) {
555
				$callables_checksums[ $name ] = $this->get_check_sum( $value );
556
			}
557
			\Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callables_checksums );
558
			return $callables;
559
		}
560
561
		return $args;
562
	}
563
564
	/**
565
	 * Return Total number of objects.
566
	 *
567
	 * @param array $config Full Sync config.
568
	 *
569
	 * @return int total
570
	 */
571
	public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
572
		return count( $this->get_callable_whitelist() );
573
	}
574
575
	/**
576
	 * Retrieve a set of callables by their IDs.
577
	 *
578
	 * @access public
579
	 *
580
	 * @param string $object_type Object type.
581
	 * @param array  $ids         Object IDs.
582
	 * @return array Array of objects.
583
	 */
584 View Code Duplication
	public function get_objects_by_id( $object_type, $ids ) {
585
		if ( empty( $ids ) || empty( $object_type ) || 'callable' !== $object_type ) {
586
			return array();
587
		}
588
589
		$objects = array();
590
		foreach ( (array) $ids as $id ) {
591
			$object = $this->get_object_by_id( $object_type, $id );
592
593
			if ( 'CALLABLE-DOES-NOT-EXIST' !== $object ) {
594
				if ( 'all' === $id ) {
595
					// If all was requested it contains all options and can simply be returned.
596
					return $object;
597
				}
598
				$objects[ $id ] = $object;
599
			}
600
		}
601
602
		return $objects;
603
	}
604
605
	/**
606
	 * Retrieve a callable by its name.
607
	 *
608
	 * @access public
609
	 *
610
	 * @param string $object_type Type of the sync object.
611
	 * @param string $id          ID of the sync object.
612
	 * @return mixed              Value of Callable.
613
	 */
614
	public function get_object_by_id( $object_type, $id ) {
615
		if ( 'callable' === $object_type ) {
616
617
			// Only whitelisted options can be returned.
618
			if ( array_key_exists( $id, $this->get_callable_whitelist() ) ) {
619
				// requires master user to be in context.
620
				$current_user_id = get_current_user_id();
621
				wp_set_current_user( \Jetpack_Options::get_option( 'master_user' ) );
622
				$callable = $this->get_callable( $this->callable_whitelist[ $id ] );
623
				wp_set_current_user( $current_user_id );
624
				return $callable;
625
			} elseif ( 'all' === $id ) {
626
				return $this->get_all_callables();
627
			}
628
		}
629
630
		return 'CALLABLE-DOES-NOT-EXIST';
631
	}
632
633
}
634