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-options.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 2 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 2 months ago class-plugins.php 2 months ago class-posts.php 2 months ago class-protect.php 9 months ago class-search.php 2 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-options.php
546 lines
1 <?php
2 /**
3 * Options sync module.
4 *
5 * @package automattic/jetpack-sync
6 */
7
8 namespace Automattic\Jetpack\Sync\Modules;
9
10 use Automattic\Jetpack\Sync\Defaults;
11 use Automattic\Jetpack\Sync\Settings;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit( 0 );
15 }
16
17 /**
18 * Class to handle sync for options.
19 */
20 class Options extends Module {
21 /**
22 * Whitelist for options we want to sync.
23 *
24 * @access private
25 *
26 * @var array
27 */
28 private $options_whitelist;
29
30 /**
31 * Contentless options we want to sync.
32 *
33 * @access private
34 *
35 * @var array
36 */
37 private $options_contentless;
38
39 /**
40 * Sync module name.
41 *
42 * @access public
43 *
44 * @return string
45 */
46 public function name() {
47 return 'options';
48 }
49
50 /**
51 * Initialize options action listeners.
52 *
53 * @access public
54 *
55 * @param callable $callable Action handler callable.
56 */
57 public function init_listeners( $callable ) {
58 // Options.
59 add_action( 'added_option', $callable, 10, 2 );
60 add_action( 'updated_option', $callable, 10, 3 );
61 add_action( 'deleted_option', $callable, 10, 1 );
62
63 // Sync Core Icon: Detect changes in Core's Site Icon and make it syncable.
64 add_action( 'add_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
65 add_action( 'update_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
66 add_action( 'delete_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
67
68 // Handle deprecated options.
69 add_filter( 'jetpack_options_whitelist', array( $this, 'add_deprecated_options' ) );
70
71 $whitelist_option_handler = array( $this, 'whitelist_options' );
72 add_filter( 'jetpack_sync_before_enqueue_deleted_option', $whitelist_option_handler );
73 add_filter( 'jetpack_sync_before_enqueue_added_option', $whitelist_option_handler );
74 add_filter( 'jetpack_sync_before_enqueue_updated_option', $whitelist_option_handler );
75 }
76
77 /**
78 * Initialize options action listeners for full sync.
79 *
80 * @access public
81 *
82 * @param callable $callable Action handler callable.
83 */
84 public function init_full_sync_listeners( $callable ) {
85 add_action( 'jetpack_full_sync_options', $callable );
86 }
87
88 /**
89 * Initialize the module in the sender.
90 *
91 * @access public
92 */
93 public function init_before_send() {
94 // Full sync.
95 add_filter( 'jetpack_sync_before_send_jetpack_full_sync_options', array( $this, 'expand_options' ) );
96 }
97
98 /**
99 * Set module defaults.
100 * Define the options whitelist and contentless options.
101 *
102 * @access public
103 */
104 public function set_defaults() {
105 $this->update_options_whitelist();
106 $this->update_options_contentless();
107 }
108
109 /**
110 * Set module defaults at a later time.
111 *
112 * @access public
113 */
114 public function set_late_default() {
115 /** This filter is already documented in json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php */
116 $late_options = apply_filters( 'jetpack_options_whitelist', array() );
117 $late_options = apply_filters( 'jetpack_sync_options_whitelist', $late_options );
118 if ( ! empty( $late_options ) && is_array( $late_options ) ) {
119 $this->options_whitelist = array_merge( $this->options_whitelist, $late_options );
120 }
121 }
122
123 /**
124 * Add old deprecated options to the list of options to keep in sync.
125 *
126 * @since 1.14.0
127 *
128 * @access public
129 *
130 * @param array $options The default list of site options.
131 */
132 public function add_deprecated_options( $options ) {
133 global $wp_version;
134
135 $deprecated_options = array(
136 'blacklist_keys' => '5.5-alpha', // Replaced by disallowed_keys.
137 'comment_whitelist' => '5.5-alpha', // Replaced by comment_previously_approved.
138 );
139
140 foreach ( $deprecated_options as $option => $version ) {
141 if ( version_compare( $wp_version, $version, '<=' ) ) {
142 $options[] = $option;
143 }
144 }
145
146 return $options;
147 }
148
149 /**
150 * Enqueue the options actions for full sync.
151 *
152 * @access public
153 *
154 * @param array $config Full sync configuration for this sync module.
155 * @param int $max_items_to_enqueue Maximum number of items to enqueue.
156 * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
157 * @return array Number of actions enqueued, and next module state.
158 */
159 public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
160 /**
161 * Tells the client to sync all options to the server
162 *
163 * @since 1.6.3
164 * @since-jetpack 4.2.0
165 *
166 * @param boolean Whether to expand options (should always be true)
167 */
168 do_action( 'jetpack_full_sync_options', true );
169
170 // The number of actions enqueued, and next module state (true == done).
171 return array( 1, true );
172 }
173
174 /**
175 * Send the options actions for full sync.
176 *
177 * @access public
178 *
179 * @param array $config Full sync configuration for this sync module.
180 * @param array $status This module Full Sync status.
181 * @param int $send_until The timestamp until the current request can send.
182 * @param int $started The timestamp when the full sync started.
183 *
184 * @return array This module Full Sync status.
185 */
186 public function send_full_sync_actions( $config, $status, $send_until, $started ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
187 // we call this instead of do_action when sending immediately.
188 $result = $this->send_action( 'jetpack_full_sync_options', array( true ) );
189
190 if ( is_wp_error( $result ) ) {
191 $status['error'] = true;
192 return $status;
193 }
194 $status['finished'] = true;
195 $status['sent'] = $status['total'];
196 return $status;
197 }
198
199 /**
200 * Retrieve an estimated number of actions that will be enqueued.
201 *
202 * @access public
203 *
204 * @param array $config Full sync configuration for this sync module.
205 * @return int Number of items yet to be enqueued.
206 */
207 public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
208 return 1;
209 }
210
211 /**
212 * Retrieve the actions that will be sent for this module during a full sync.
213 *
214 * @access public
215 *
216 * @return array Full sync actions of this module.
217 */
218 public function get_full_sync_actions() {
219 return array( 'jetpack_full_sync_options' );
220 }
221
222 /**
223 * Retrieve all options as per the current options whitelist.
224 * Public so that we don't have to store so much data all the options twice.
225 *
226 * @access public
227 *
228 * @return array All options.
229 */
230 public function get_all_options() {
231 $options = array();
232 $random_string = wp_generate_password();
233 foreach ( $this->options_whitelist as $option ) {
234 if ( str_starts_with( $option, Settings::SETTINGS_OPTION_PREFIX ) ) {
235 $option_value = Settings::get_setting( str_replace( Settings::SETTINGS_OPTION_PREFIX, '', $option ) );
236 $options[ $option ] = $option_value;
237 } else {
238 $option_value = get_option( $option, $random_string );
239 if ( $option_value !== $random_string ) {
240 $options[ $option ] = $option_value;
241 }
242 }
243 }
244
245 // Add theme mods.
246 $theme_mods_option = 'theme_mods_' . get_option( 'stylesheet' );
247 $theme_mods_value = get_option( $theme_mods_option, $random_string );
248 if ( $theme_mods_value === $random_string ) {
249 return $options;
250 }
251 $this->filter_theme_mods( $theme_mods_value );
252 $options[ $theme_mods_option ] = $theme_mods_value;
253 return $options;
254 }
255
256 /**
257 * Update the options whitelist to the default one.
258 *
259 * @access public
260 */
261 public function update_options_whitelist() {
262 $this->options_whitelist = Defaults::get_options_whitelist();
263 }
264
265 /**
266 * Set the options whitelist.
267 *
268 * @access public
269 *
270 * @param array $options The new options whitelist.
271 */
272 public function set_options_whitelist( $options ) {
273 $this->options_whitelist = $options;
274 }
275
276 /**
277 * Get the options whitelist.
278 *
279 * @access public
280 *
281 * @return array The options whitelist.
282 */
283 public function get_options_whitelist() {
284 return $this->options_whitelist;
285 }
286
287 /**
288 * Update the contentless options to the defaults.
289 *
290 * @access public
291 */
292 public function update_options_contentless() {
293 $this->options_contentless = Defaults::get_options_contentless();
294 }
295
296 /**
297 * Get the contentless options.
298 *
299 * @access public
300 *
301 * @return array Array of the contentless options.
302 */
303 public function get_options_contentless() {
304 return $this->options_contentless;
305 }
306
307 /**
308 * Reject any options that aren't whitelisted or contentless.
309 *
310 * @access public
311 *
312 * @param array $args The hook parameters.
313 * @return array $args The hook parameters.
314 */
315 public function whitelist_options( $args ) {
316 // Reject non-whitelisted options.
317 if ( ! $this->is_whitelisted_option( $args[0] ) ) {
318 return false;
319 }
320
321 // Check if 'jetpack_options' were updated and reject if the change affected only blacklisted keys.
322 if ( 'jetpack_options' === $args[0] && 3 === count( $args ) ) {
323 if ( ! $this->should_enqueue_jetpack_options_update( $args[1], $args[2] ) ) {
324 return false;
325 }
326
327 return $args;
328 }
329
330 // Filter our weird array( false ) value for theme_mods_*.
331 if ( str_starts_with( $args[0], 'theme_mods_' ) ) {
332 $this->filter_theme_mods( $args[1] );
333 if ( isset( $args[2] ) ) {
334 $this->filter_theme_mods( $args[2] );
335 }
336 }
337
338 // Set value(s) of contentless option to empty string(s).
339 if ( $this->is_contentless_option( $args[0] ) ) {
340 // Create a new array matching length of $args, containing empty strings.
341 $empty = array_fill( 0, count( $args ), '' );
342 $empty[0] = $args[0];
343 return $empty;
344 }
345
346 return $args;
347 }
348
349 /**
350 * Whether a certain option is whitelisted for sync.
351 *
352 * @access public
353 *
354 * @param string $option Option name.
355 * @return boolean Whether the option is whitelisted.
356 */
357 public function is_whitelisted_option( $option ) {
358 return in_array( $option, $this->options_whitelist, true ) || str_starts_with( $option, 'theme_mods_' );
359 }
360
361 /**
362 * Whether a certain option is a contentless one.
363 *
364 * @access private
365 *
366 * @param string $option Option name.
367 * @return boolean Whether the option is contentless.
368 */
369 private function is_contentless_option( $option ) {
370 return in_array( $option, $this->options_contentless, true );
371 }
372
373 /**
374 * Filters out falsy values from theme mod options.
375 *
376 * @access private
377 *
378 * @param array $value Option value.
379 */
380 private function filter_theme_mods( &$value ) {
381 if ( is_array( $value ) && isset( $value[0] ) ) {
382 unset( $value[0] );
383 }
384 }
385
386 /**
387 * Handle changes in the core site icon and sync them.
388 *
389 * @access public
390 */
391 public function jetpack_sync_core_icon() {
392 $url = get_site_icon_url();
393
394 $jetpack_url = \Jetpack_Options::get_option( 'site_icon_url' );
395 if ( defined( 'JETPACK__PLUGIN_DIR' ) ) {
396 if ( ! function_exists( 'jetpack_site_icon_url' ) ) {
397 require_once JETPACK__PLUGIN_DIR . 'modules/site-icon/site-icon-functions.php';
398 }
399 $jetpack_url = jetpack_site_icon_url();
400 }
401
402 // If there's a core icon, maybe update the option. If not, fall back to Jetpack's.
403 if ( ! empty( $url ) && $jetpack_url !== $url ) {
404 // This is the option that is synced with dotcom.
405 \Jetpack_Options::update_option( 'site_icon_url', $url );
406 } elseif ( empty( $url ) ) {
407 \Jetpack_Options::delete_option( 'site_icon_url' );
408 }
409 }
410
411 /**
412 * Expand all options within a hook before they are serialized and sent to the server.
413 *
414 * @access public
415 *
416 * @param array $args The hook parameters.
417 * @return array $args The hook parameters.
418 */
419 public function expand_options( $args ) {
420 if ( $args[0] ) {
421 return $this->get_all_options();
422 }
423
424 return $args;
425 }
426
427 /**
428 * Return Total number of objects.
429 *
430 * @param array $config Full Sync config.
431 *
432 * @return int total
433 */
434 public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
435 return count( Defaults::get_options_whitelist() );
436 }
437
438 /**
439 * Retrieve a set of options by their IDs.
440 *
441 * @access public
442 *
443 * @param string $object_type Object type.
444 * @param array $ids Object IDs.
445 * @return array Array of objects.
446 */
447 public function get_objects_by_id( $object_type, $ids ) {
448 if ( empty( $ids ) || empty( $object_type ) || 'option' !== $object_type ) {
449 return array();
450 }
451
452 $objects = array();
453 foreach ( (array) $ids as $id ) {
454 $object = $this->get_object_by_id( $object_type, $id );
455
456 // Only add object if we have the object.
457 if ( 'OPTION-DOES-NOT-EXIST' !== $object ) {
458 if ( 'all' === $id ) {
459 // If all was requested it contains all options and can simply be returned.
460 return $object;
461 }
462 $objects[ $id ] = $object;
463 }
464 }
465
466 return $objects;
467 }
468
469 /**
470 * Retrieve an option by its name.
471 *
472 * @access public
473 *
474 * @param string $object_type Type of the sync object.
475 * @param string $id ID of the sync object.
476 * @return mixed Value of Option or 'OPTION-DOES-NOT-EXIST' if not found.
477 */
478 public function get_object_by_id( $object_type, $id ) {
479 if ( 'option' === $object_type ) {
480 // Utilize Random string as default value to distinguish between false and not exist.
481 $random_string = wp_generate_password();
482 // Only whitelisted options can be returned.
483 if ( in_array( $id, $this->options_whitelist, true ) ) {
484 if ( str_starts_with( $id, Settings::SETTINGS_OPTION_PREFIX ) ) {
485 $option_value = Settings::get_setting( str_replace( Settings::SETTINGS_OPTION_PREFIX, '', $id ) );
486 return $option_value;
487 } else {
488 $option_value = get_option( $id, $random_string );
489 if ( $option_value !== $random_string ) {
490 return $option_value;
491 }
492 }
493 } elseif ( 'all' === $id ) {
494 return $this->get_all_options();
495 }
496 }
497
498 return 'OPTION-DOES-NOT-EXIST';
499 }
500
501 /**
502 * Check if 'jetpack_options' option update should be processed based on excluded keys.
503 *
504 * @param mixed $old_value The old option value.
505 * @param mixed $value The new option value.
506 * @return bool False if only excluded keys changed (or no change), true otherwise.
507 */
508 private function should_enqueue_jetpack_options_update( $old_value, $value ) {
509 // No changes at all.
510 if ( $old_value === $value ) {
511 return false;
512 }
513 // Values are different but not both arrays - meaningful change.
514 if ( ! is_array( $old_value ) || ! is_array( $value ) ) {
515 return true;
516 }
517 // Determine all top-level keys present in either the old or new value.
518 $all_keys = array_unique(
519 array_merge(
520 array_keys( $old_value ),
521 array_keys( $value )
522 )
523 );
524 // Short-circuit as soon as we find a changed key that is not blacklisted.
525 foreach ( $all_keys as $key ) {
526 $old_has_key = array_key_exists( $key, $old_value );
527 $new_has_key = array_key_exists( $key, $value );
528 // Key was added or removed.
529 if ( ! $old_has_key || ! $new_has_key ) {
530 if ( ! in_array( $key, Defaults::$jetpack_options_blacklist, true ) ) {
531 return true;
532 }
533 continue;
534 }
535 // Key exists in both arrays but the value changed.
536 if ( $old_value[ $key ] !== $value[ $key ] ) {
537 if ( ! in_array( $key, Defaults::$jetpack_options_blacklist, true ) ) {
538 return true;
539 }
540 }
541 }
542 // Either there were no effective changes, or all changed keys are excluded.
543 return false;
544 }
545 }
546