Make WordPress Core

Ticket #59588: option.php

File option.php, 80.9 KB (added by cweberDC, 17 months ago)

option.php file with my code changes for example

Line 
1<?php
2/**
3 * Option API
4 *
5 * @package WordPress
6 * @subpackage Option
7 */
8
9/**
10 * Retrieves an option value based on an option name.
11 *
12 * If the option does not exist, and a default value is not provided,
13 * boolean false is returned. This could be used to check whether you need
14 * to initialize an option during installation of a plugin, however that
15 * can be done better by using add_option() which will not overwrite
16 * existing options.
17 *
18 * Not initializing an option and using boolean `false` as a return value
19 * is a bad practice as it triggers an additional database query.
20 *
21 * The type of the returned value can be different from the type that was passed
22 * when saving or updating the option. If the option value was serialized,
23 * then it will be unserialized when it is returned. In this case the type will
24 * be the same. For example, storing a non-scalar value like an array will
25 * return the same array.
26 *
27 * In most cases non-string scalar and null values will be converted and returned
28 * as string equivalents.
29 *
30 * Exceptions:
31 *
32 * 1. When the option has not been saved in the database, the `$default_value` value
33 *    is returned if provided. If not, boolean `false` is returned.
34 * 2. When one of the Options API filters is used: {@see 'pre_option_$option'},
35 *    {@see 'default_option_$option'}, or {@see 'option_$option'}, the returned
36 *    value may not match the expected type.
37 * 3. When the option has just been saved in the database, and get_option()
38 *    is used right after, non-string scalar and null values are not converted to
39 *    string equivalents and the original type is returned.
40 *
41 * Examples:
42 *
43 * When adding options like this: `add_option( 'my_option_name', 'value' )`
44 * and then retrieving them with `get_option( 'my_option_name' )`, the returned
45 * values will be:
46 *
47 *   - `false` returns `string(0) ""`
48 *   - `true`  returns `string(1) "1"`
49 *   - `0`     returns `string(1) "0"`
50 *   - `1`     returns `string(1) "1"`
51 *   - `'0'`   returns `string(1) "0"`
52 *   - `'1'`   returns `string(1) "1"`
53 *   - `null`  returns `string(0) ""`
54 *
55 * When adding options with non-scalar values like
56 * `add_option( 'my_array', array( false, 'str', null ) )`, the returned value
57 * will be identical to the original as it is serialized before saving
58 * it in the database:
59 *
60 *     array(3) {
61 *         [0] => bool(false)
62 *         [1] => string(3) "str"
63 *         [2] => NULL
64 *     }
65 *
66 * @since 1.5.0
67 *
68 * @global wpdb $wpdb WordPress database abstraction object.
69 *
70 * @param string $option        Name of the option to retrieve. Expected to not be SQL-escaped.
71 * @param mixed  $default_value Optional. Default value to return if the option does not exist.
72 * @return mixed Value of the option. A value of any type may be returned, including
73 *               scalar (string, boolean, float, integer), null, array, object.
74 *               Scalar and null values will be returned as strings as long as they originate
75 *               from a database stored option value. If there is no option in the database,
76 *               boolean `false` is returned.
77 */
78function get_option( $option, $default_value = false ) {
79        global $wpdb;
80
81        if ( is_scalar( $option ) ) {
82                $option = trim( $option );
83        }
84
85        if ( empty( $option ) ) {
86                return false;
87        }
88
89        /*
90         * Until a proper _deprecated_option() function can be introduced,
91         * redirect requests to deprecated keys to the new, correct ones.
92         */
93        $deprecated_keys = array(
94                'blacklist_keys'    => 'disallowed_keys',
95                'comment_whitelist' => 'comment_previously_approved',
96        );
97
98        if ( isset( $deprecated_keys[ $option ] ) && ! wp_installing() ) {
99                _deprecated_argument(
100                        __FUNCTION__,
101                        '5.5.0',
102                        sprintf(
103                                /* translators: 1: Deprecated option key, 2: New option key. */
104                                __( 'The "%1$s" option key has been renamed to "%2$s".' ),
105                                $option,
106                                $deprecated_keys[ $option ]
107                        )
108                );
109
110                return get_option( $deprecated_keys[ $option ], $default_value );
111        }
112
113        /**
114         * Filters the value of an existing option before it is retrieved.
115         *
116         * The dynamic portion of the hook name, `$option`, refers to the option name.
117         *
118         * Returning a value other than false from the filter will short-circuit retrieval
119         * and return that value instead.
120         *
121         * @since 1.5.0
122         * @since 4.4.0 The `$option` parameter was added.
123         * @since 4.9.0 The `$default_value` parameter was added.
124         *
125         * @param mixed  $pre_option    The value to return instead of the option value. This differs from
126         *                              `$default_value`, which is used as the fallback value in the event
127         *                              the option doesn't exist elsewhere in get_option().
128         *                              Default false (to skip past the short-circuit).
129         * @param string $option        Option name.
130         * @param mixed  $default_value The fallback value to return if the option does not exist.
131         *                              Default false.
132         */
133        $pre = apply_filters( "pre_option_{$option}", false, $option, $default_value );
134
135        /**
136         * Filters the value of all existing options before it is retrieved.
137         *
138         * Returning a truthy value from the filter will effectively short-circuit retrieval
139         * and return the passed value instead.
140         *
141         * @since 6.1.0
142         *
143         * @param mixed  $pre_option    The value to return instead of the option value. This differs from
144         *                              `$default_value`, which is used as the fallback value in the event
145         *                              the option doesn't exist elsewhere in get_option().
146         *                              Default false (to skip past the short-circuit).
147         * @param string $option        Name of the option.
148         * @param mixed  $default_value The fallback value to return if the option does not exist.
149         *                              Default false.
150         */
151        $pre = apply_filters( 'pre_option', $pre, $option, $default_value );
152
153        if ( false !== $pre ) {
154                return $pre;
155        }
156
157        if ( defined( 'WP_SETUP_CONFIG' ) ) {
158                return false;
159        }
160
161        // Distinguish between `false` as a default, and not passing one.
162        $passed_default = func_num_args() > 1;
163
164        if ( ! wp_installing() ) {
165                // Prevent non-existent options from triggering multiple queries.
166                $notoptions = wp_cache_get( 'notoptions', 'options' );
167
168                // Prevent non-existent `notoptions` key from triggering multiple key lookups.
169                if ( ! is_array( $notoptions ) ) {
170                        $notoptions = array();
171                        wp_cache_set( 'notoptions', $notoptions, 'options' );
172                }
173
174                if ( isset( $notoptions[ $option ] ) ) {
175                        /**
176                         * Filters the default value for an option.
177                         *
178                         * The dynamic portion of the hook name, `$option`, refers to the option name.
179                         *
180                         * @since 3.4.0
181                         * @since 4.4.0 The `$option` parameter was added.
182                         * @since 4.7.0 The `$passed_default` parameter was added to distinguish between a `false` value and the default parameter value.
183                         *
184                         * @param mixed  $default_value  The default value to return if the option does not exist
185                         *                               in the database.
186                         * @param string $option         Option name.
187                         * @param bool   $passed_default Was `get_option()` passed a default value?
188                         */
189                        return apply_filters( "default_option_{$option}", $default_value, $option, $passed_default );
190                }
191
192                $alloptions = wp_load_alloptions();
193
194                if ( isset( $alloptions[ $option ] ) ) {
195                        $value = $alloptions[ $option ];
196                } else {
197                        $value = wp_cache_get( $option, 'options' );
198
199                        if ( false === $value ) {
200                                $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) );
201
202                                // Has to be get_row() instead of get_var() because of funkiness with 0, false, null values.
203                                if ( is_object( $row ) ) {
204                                        $value = $row->option_value;
205                                        wp_cache_add( $option, $value, 'options' );
206                                } else { // Option does not exist, so we must cache its non-existence.
207                                        if ( ! is_array( $notoptions ) ) {
208                                                $notoptions = array();
209                                        }
210
211                                        $notoptions[ $option ] = true;
212                                        wp_cache_set( 'notoptions', $notoptions, 'options' );
213
214                                        /** This filter is documented in wp-includes/option.php */
215                                        return apply_filters( "default_option_{$option}", $default_value, $option, $passed_default );
216                                }
217                        }
218                }
219        } else {
220                $suppress = $wpdb->suppress_errors();
221                $row      = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $option ) );
222                $wpdb->suppress_errors( $suppress );
223
224                if ( is_object( $row ) ) {
225                        $value = $row->option_value;
226                } else {
227                        /** This filter is documented in wp-includes/option.php */
228                        return apply_filters( "default_option_{$option}", $default_value, $option, $passed_default );
229                }
230        }
231
232        // If home is not set, use siteurl.
233        if ( 'home' === $option && '' === $value ) {
234                return get_option( 'siteurl' );
235        }
236
237        if ( in_array( $option, array( 'siteurl', 'home', 'category_base', 'tag_base' ), true ) ) {
238                $value = untrailingslashit( $value );
239        }
240
241        /**
242         * Filters the value of an existing option.
243         *
244         * The dynamic portion of the hook name, `$option`, refers to the option name.
245         *
246         * @since 1.5.0 As 'option_' . $setting
247         * @since 3.0.0
248         * @since 4.4.0 The `$option` parameter was added.
249         *
250         * @param mixed  $value  Value of the option. If stored serialized, it will be
251         *                       unserialized prior to being returned.
252         * @param string $option Option name.
253         */
254    $data = maybe_unserialize($value);
255
256    if (!$data && $default_value !== false && gettype($data) !== gettype($default_value))
257        $data = $default_value;
258
259    return apply_filters( "option_{$option}", $data, $option );
260}
261
262/**
263 * Protects WordPress special option from being modified.
264 *
265 * Will die if $option is in protected list. Protected options are 'alloptions'
266 * and 'notoptions' options.
267 *
268 * @since 2.2.0
269 *
270 * @param string $option Option name.
271 */
272function wp_protect_special_option( $option ) {
273        if ( 'alloptions' === $option || 'notoptions' === $option ) {
274                wp_die(
275                        sprintf(
276                                /* translators: %s: Option name. */
277                                __( '%s is a protected WP option and may not be modified' ),
278                                esc_html( $option )
279                        )
280                );
281        }
282}
283
284/**
285 * Prints option value after sanitizing for forms.
286 *
287 * @since 1.5.0
288 *
289 * @param string $option Option name.
290 */
291function form_option( $option ) {
292        echo esc_attr( get_option( $option ) );
293}
294
295/**
296 * Loads and caches all autoloaded options, if available or all options.
297 *
298 * @since 2.2.0
299 * @since 5.3.1 The `$force_cache` parameter was added.
300 *
301 * @global wpdb $wpdb WordPress database abstraction object.
302 *
303 * @param bool $force_cache Optional. Whether to force an update of the local cache
304 *                          from the persistent cache. Default false.
305 * @return array List of all options.
306 */
307function wp_load_alloptions( $force_cache = false ) {
308        global $wpdb;
309
310        /**
311         * Filters the array of alloptions before it is populated.
312         *
313         * Returning an array from the filter will effectively short circuit
314         * wp_load_alloptions(), returning that value instead.
315         *
316         * @since 6.2.0
317         *
318         * @param array|null $alloptions  An array of alloptions. Default null.
319         * @param bool       $force_cache Whether to force an update of the local cache from the persistent cache. Default false.
320         */
321        $alloptions = apply_filters( 'pre_wp_load_alloptions', null, $force_cache );
322        if ( is_array( $alloptions ) ) {
323                return $alloptions;
324        }
325
326        if ( ! wp_installing() || ! is_multisite() ) {
327                $alloptions = wp_cache_get( 'alloptions', 'options', $force_cache );
328        } else {
329                $alloptions = false;
330        }
331
332        if ( ! $alloptions ) {
333                $suppress      = $wpdb->suppress_errors();
334                $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options WHERE autoload = 'yes'" );
335                if ( ! $alloptions_db ) {
336                        $alloptions_db = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options" );
337                }
338                $wpdb->suppress_errors( $suppress );
339
340                $alloptions = array();
341                foreach ( (array) $alloptions_db as $o ) {
342                        $alloptions[ $o->option_name ] = $o->option_value;
343                }
344
345                if ( ! wp_installing() || ! is_multisite() ) {
346                        /**
347                         * Filters all options before caching them.
348                         *
349                         * @since 4.9.0
350                         *
351                         * @param array $alloptions Array with all options.
352                         */
353                        $alloptions = apply_filters( 'pre_cache_alloptions', $alloptions );
354
355                        wp_cache_add( 'alloptions', $alloptions, 'options' );
356                }
357        }
358
359        /**
360         * Filters all options after retrieving them.
361         *
362         * @since 4.9.0
363         *
364         * @param array $alloptions Array with all options.
365         */
366        return apply_filters( 'alloptions', $alloptions );
367}
368
369/**
370 * Loads and primes caches of certain often requested network options if is_multisite().
371 *
372 * @since 3.0.0
373 * @since 6.3.0 Also prime caches for network options when persistent object cache is enabled.
374 *
375 * @global wpdb $wpdb WordPress database abstraction object.
376 *
377 * @param int $network_id Optional. Network ID of network for which to prime network options cache. Defaults to current network.
378 */
379function wp_load_core_site_options( $network_id = null ) {
380        global $wpdb;
381
382        if ( ! is_multisite() || wp_installing() ) {
383                return;
384        }
385
386        if ( empty( $network_id ) ) {
387                $network_id = get_current_network_id();
388        }
389
390        $core_options = array( 'site_name', 'siteurl', 'active_sitewide_plugins', '_site_transient_timeout_theme_roots', '_site_transient_theme_roots', 'site_admins', 'can_compress_scripts', 'global_terms_enabled', 'ms_files_rewriting' );
391
392        if ( wp_using_ext_object_cache() ) {
393                $cache_keys = array();
394                foreach ( $core_options as $option ) {
395                        $cache_keys[] = "{$network_id}:{$option}";
396                }
397                wp_cache_get_multiple( $cache_keys, 'site-options' );
398
399                return;
400        }
401
402        $core_options_in = "'" . implode( "', '", $core_options ) . "'";
403        $options         = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM $wpdb->sitemeta WHERE meta_key IN ($core_options_in) AND site_id = %d", $network_id ) );
404
405        $data = array();
406        foreach ( $options as $option ) {
407                $key                = $option->meta_key;
408                $cache_key          = "{$network_id}:$key";
409                $option->meta_value = maybe_unserialize( $option->meta_value );
410
411                $data[ $cache_key ] = $option->meta_value;
412        }
413        wp_cache_set_multiple( $data, 'site-options' );
414}
415
416/**
417 * Updates the value of an option that was already added.
418 *
419 * You do not need to serialize values. If the value needs to be serialized,
420 * then it will be serialized before it is inserted into the database.
421 * Remember, resources cannot be serialized or added as an option.
422 *
423 * If the option does not exist, it will be created.
424
425 * This function is designed to work with or without a logged-in user. In terms of security,
426 * plugin developers should check the current user's capabilities before updating any options.
427 *
428 * @since 1.0.0
429 * @since 4.2.0 The `$autoload` parameter was added.
430 *
431 * @global wpdb $wpdb WordPress database abstraction object.
432 *
433 * @param string      $option   Name of the option to update. Expected to not be SQL-escaped.
434 * @param mixed       $value    Option value. Must be serializable if non-scalar. Expected to not be SQL-escaped.
435 * @param string|bool $autoload Optional. Whether to load the option when WordPress starts up. For existing options,
436 *                              `$autoload` can only be updated using `update_option()` if `$value` is also changed.
437 *                              Accepts 'yes'|true to enable or 'no'|false to disable. For non-existent options,
438 *                              the default value is 'yes'. Default null.
439 * @return bool True if the value was updated, false otherwise.
440 */
441function update_option( $option, $value, $autoload = null ) {
442        global $wpdb;
443
444        if ( is_scalar( $option ) ) {
445                $option = trim( $option );
446        }
447
448        if ( empty( $option ) ) {
449                return false;
450        }
451
452        /*
453         * Until a proper _deprecated_option() function can be introduced,
454         * redirect requests to deprecated keys to the new, correct ones.
455         */
456        $deprecated_keys = array(
457                'blacklist_keys'    => 'disallowed_keys',
458                'comment_whitelist' => 'comment_previously_approved',
459        );
460
461        if ( isset( $deprecated_keys[ $option ] ) && ! wp_installing() ) {
462                _deprecated_argument(
463                        __FUNCTION__,
464                        '5.5.0',
465                        sprintf(
466                                /* translators: 1: Deprecated option key, 2: New option key. */
467                                __( 'The "%1$s" option key has been renamed to "%2$s".' ),
468                                $option,
469                                $deprecated_keys[ $option ]
470                        )
471                );
472                return update_option( $deprecated_keys[ $option ], $value, $autoload );
473        }
474
475        wp_protect_special_option( $option );
476
477        if ( is_object( $value ) ) {
478                $value = clone $value;
479        }
480
481        $value     = sanitize_option( $option, $value );
482        $old_value = get_option( $option );
483
484        /**
485         * Filters a specific option before its value is (maybe) serialized and updated.
486         *
487         * The dynamic portion of the hook name, `$option`, refers to the option name.
488         *
489         * @since 2.6.0
490         * @since 4.4.0 The `$option` parameter was added.
491         *
492         * @param mixed  $value     The new, unserialized option value.
493         * @param mixed  $old_value The old option value.
494         * @param string $option    Option name.
495         */
496        $value = apply_filters( "pre_update_option_{$option}", $value, $old_value, $option );
497
498        /**
499         * Filters an option before its value is (maybe) serialized and updated.
500         *
501         * @since 3.9.0
502         *
503         * @param mixed  $value     The new, unserialized option value.
504         * @param string $option    Name of the option.
505         * @param mixed  $old_value The old option value.
506         */
507        $value = apply_filters( 'pre_update_option', $value, $option, $old_value );
508
509        /*
510         * If the new and old values are the same, no need to update.
511         *
512         * Unserialized values will be adequate in most cases. If the unserialized
513         * data differs, the (maybe) serialized data is checked to avoid
514         * unnecessary database calls for otherwise identical object instances.
515         *
516         * See https://core.trac.wordpress.org/ticket/38903
517         */
518        if ( $value === $old_value || maybe_serialize( $value ) === maybe_serialize( $old_value ) ) {
519                return false;
520        }
521
522        /** This filter is documented in wp-includes/option.php */
523        if ( apply_filters( "default_option_{$option}", false, $option, false ) === $old_value ) {
524                // Default setting for new options is 'yes'.
525                if ( null === $autoload ) {
526                        $autoload = 'yes';
527                }
528
529                return add_option( $option, $value, '', $autoload );
530        }
531
532        $serialized_value = maybe_serialize( $value );
533
534        /**
535         * Fires immediately before an option value is updated.
536         *
537         * @since 2.9.0
538         *
539         * @param string $option    Name of the option to update.
540         * @param mixed  $old_value The old option value.
541         * @param mixed  $value     The new option value.
542         */
543        do_action( 'update_option', $option, $old_value, $value );
544
545        $update_args = array(
546                'option_value' => $serialized_value,
547        );
548
549        if ( null !== $autoload ) {
550                $update_args['autoload'] = ( 'no' === $autoload || false === $autoload ) ? 'no' : 'yes';
551        }
552
553        $result = $wpdb->update( $wpdb->options, $update_args, array( 'option_name' => $option ) );
554        if ( ! $result ) {
555                return false;
556        }
557
558        $notoptions = wp_cache_get( 'notoptions', 'options' );
559
560        if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) {
561                unset( $notoptions[ $option ] );
562                wp_cache_set( 'notoptions', $notoptions, 'options' );
563        }
564
565        if ( ! wp_installing() ) {
566                $alloptions = wp_load_alloptions( true );
567                if ( isset( $alloptions[ $option ] ) ) {
568                        $alloptions[ $option ] = $serialized_value;
569                        wp_cache_set( 'alloptions', $alloptions, 'options' );
570                } else {
571                        wp_cache_set( $option, $serialized_value, 'options' );
572                }
573        }
574
575        /**
576         * Fires after the value of a specific option has been successfully updated.
577         *
578         * The dynamic portion of the hook name, `$option`, refers to the option name.
579         *
580         * @since 2.0.1
581         * @since 4.4.0 The `$option` parameter was added.
582         *
583         * @param mixed  $old_value The old option value.
584         * @param mixed  $value     The new option value.
585         * @param string $option    Option name.
586         */
587        do_action( "update_option_{$option}", $old_value, $value, $option );
588
589        /**
590         * Fires after the value of an option has been successfully updated.
591         *
592         * @since 2.9.0
593         *
594         * @param string $option    Name of the updated option.
595         * @param mixed  $old_value The old option value.
596         * @param mixed  $value     The new option value.
597         */
598        do_action( 'updated_option', $option, $old_value, $value );
599
600        return true;
601}
602
603/**
604 * Adds a new option.
605 *
606 * You do not need to serialize values. If the value needs to be serialized,
607 * then it will be serialized before it is inserted into the database.
608 * Remember, resources cannot be serialized or added as an option.
609 *
610 * You can create options without values and then update the values later.
611 * Existing options will not be updated and checks are performed to ensure that you
612 * aren't adding a protected WordPress option. Care should be taken to not name
613 * options the same as the ones which are protected.
614 *
615 * @since 1.0.0
616 *
617 * @global wpdb $wpdb WordPress database abstraction object.
618 *
619 * @param string      $option     Name of the option to add. Expected to not be SQL-escaped.
620 * @param mixed       $value      Optional. Option value. Must be serializable if non-scalar.
621 *                                Expected to not be SQL-escaped.
622 * @param string      $deprecated Optional. Description. Not used anymore.
623 * @param string|bool $autoload   Optional. Whether to load the option when WordPress starts up.
624 *                                Default is enabled. Accepts 'no' to disable for legacy reasons.
625 * @return bool True if the option was added, false otherwise.
626 */
627function add_option( $option, $value = '', $deprecated = '', $autoload = 'yes' ) {
628        global $wpdb;
629
630        if ( ! empty( $deprecated ) ) {
631                _deprecated_argument( __FUNCTION__, '2.3.0' );
632        }
633
634        if ( is_scalar( $option ) ) {
635                $option = trim( $option );
636        }
637
638        if ( empty( $option ) ) {
639                return false;
640        }
641
642        /*
643         * Until a proper _deprecated_option() function can be introduced,
644         * redirect requests to deprecated keys to the new, correct ones.
645         */
646        $deprecated_keys = array(
647                'blacklist_keys'    => 'disallowed_keys',
648                'comment_whitelist' => 'comment_previously_approved',
649        );
650
651        if ( isset( $deprecated_keys[ $option ] ) && ! wp_installing() ) {
652                _deprecated_argument(
653                        __FUNCTION__,
654                        '5.5.0',
655                        sprintf(
656                                /* translators: 1: Deprecated option key, 2: New option key. */
657                                __( 'The "%1$s" option key has been renamed to "%2$s".' ),
658                                $option,
659                                $deprecated_keys[ $option ]
660                        )
661                );
662                return add_option( $deprecated_keys[ $option ], $value, $deprecated, $autoload );
663        }
664
665        wp_protect_special_option( $option );
666
667        if ( is_object( $value ) ) {
668                $value = clone $value;
669        }
670
671        $value = sanitize_option( $option, $value );
672
673        /*
674         * Make sure the option doesn't already exist.
675         * We can check the 'notoptions' cache before we ask for a DB query.
676         */
677        $notoptions = wp_cache_get( 'notoptions', 'options' );
678
679        if ( ! is_array( $notoptions ) || ! isset( $notoptions[ $option ] ) ) {
680                /** This filter is documented in wp-includes/option.php */
681                if ( apply_filters( "default_option_{$option}", false, $option, false ) !== get_option( $option ) ) {
682                        return false;
683                }
684        }
685
686        $serialized_value = maybe_serialize( $value );
687        $autoload         = ( 'no' === $autoload || false === $autoload ) ? 'no' : 'yes';
688
689        /**
690         * Fires before an option is added.
691         *
692         * @since 2.9.0
693         *
694         * @param string $option Name of the option to add.
695         * @param mixed  $value  Value of the option.
696         */
697        do_action( 'add_option', $option, $value );
698
699        $result = $wpdb->query( $wpdb->prepare( "INSERT INTO `$wpdb->options` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE `option_name` = VALUES(`option_name`), `option_value` = VALUES(`option_value`), `autoload` = VALUES(`autoload`)", $option, $serialized_value, $autoload ) );
700        if ( ! $result ) {
701                return false;
702        }
703
704        if ( ! wp_installing() ) {
705                if ( 'yes' === $autoload ) {
706                        $alloptions            = wp_load_alloptions( true );
707                        $alloptions[ $option ] = $serialized_value;
708                        wp_cache_set( 'alloptions', $alloptions, 'options' );
709                } else {
710                        wp_cache_set( $option, $serialized_value, 'options' );
711                }
712        }
713
714        // This option exists now.
715        $notoptions = wp_cache_get( 'notoptions', 'options' ); // Yes, again... we need it to be fresh.
716
717        if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) {
718                unset( $notoptions[ $option ] );
719                wp_cache_set( 'notoptions', $notoptions, 'options' );
720        }
721
722        /**
723         * Fires after a specific option has been added.
724         *
725         * The dynamic portion of the hook name, `$option`, refers to the option name.
726         *
727         * @since 2.5.0 As "add_option_{$name}"
728         * @since 3.0.0
729         *
730         * @param string $option Name of the option to add.
731         * @param mixed  $value  Value of the option.
732         */
733        do_action( "add_option_{$option}", $option, $value );
734
735        /**
736         * Fires after an option has been added.
737         *
738         * @since 2.9.0
739         *
740         * @param string $option Name of the added option.
741         * @param mixed  $value  Value of the option.
742         */
743        do_action( 'added_option', $option, $value );
744
745        return true;
746}
747
748/**
749 * Removes an option by name. Prevents removal of protected WordPress options.
750 *
751 * @since 1.2.0
752 *
753 * @global wpdb $wpdb WordPress database abstraction object.
754 *
755 * @param string $option Name of the option to delete. Expected to not be SQL-escaped.
756 * @return bool True if the option was deleted, false otherwise.
757 */
758function delete_option( $option ) {
759        global $wpdb;
760
761        if ( is_scalar( $option ) ) {
762                $option = trim( $option );
763        }
764
765        if ( empty( $option ) ) {
766                return false;
767        }
768
769        wp_protect_special_option( $option );
770
771        // Get the ID, if no ID then return.
772        $row = $wpdb->get_row( $wpdb->prepare( "SELECT autoload FROM $wpdb->options WHERE option_name = %s", $option ) );
773        if ( is_null( $row ) ) {
774                return false;
775        }
776
777        /**
778         * Fires immediately before an option is deleted.
779         *
780         * @since 2.9.0
781         *
782         * @param string $option Name of the option to delete.
783         */
784        do_action( 'delete_option', $option );
785
786        $result = $wpdb->delete( $wpdb->options, array( 'option_name' => $option ) );
787
788        if ( ! wp_installing() ) {
789                if ( 'yes' === $row->autoload ) {
790                        $alloptions = wp_load_alloptions( true );
791                        if ( is_array( $alloptions ) && isset( $alloptions[ $option ] ) ) {
792                                unset( $alloptions[ $option ] );
793                                wp_cache_set( 'alloptions', $alloptions, 'options' );
794                        }
795                } else {
796                        wp_cache_delete( $option, 'options' );
797                }
798        }
799
800        if ( $result ) {
801
802                /**
803                 * Fires after a specific option has been deleted.
804                 *
805                 * The dynamic portion of the hook name, `$option`, refers to the option name.
806                 *
807                 * @since 3.0.0
808                 *
809                 * @param string $option Name of the deleted option.
810                 */
811                do_action( "delete_option_{$option}", $option );
812
813                /**
814                 * Fires after an option has been deleted.
815                 *
816                 * @since 2.9.0
817                 *
818                 * @param string $option Name of the deleted option.
819                 */
820                do_action( 'deleted_option', $option );
821
822                return true;
823        }
824
825        return false;
826}
827
828/**
829 * Deletes a transient.
830 *
831 * @since 2.8.0
832 *
833 * @param string $transient Transient name. Expected to not be SQL-escaped.
834 * @return bool True if the transient was deleted, false otherwise.
835 */
836function delete_transient( $transient ) {
837
838        /**
839         * Fires immediately before a specific transient is deleted.
840         *
841         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
842         *
843         * @since 3.0.0
844         *
845         * @param string $transient Transient name.
846         */
847        do_action( "delete_transient_{$transient}", $transient );
848
849        if ( wp_using_ext_object_cache() || wp_installing() ) {
850                $result = wp_cache_delete( $transient, 'transient' );
851        } else {
852                $option_timeout = '_transient_timeout_' . $transient;
853                $option         = '_transient_' . $transient;
854                $result         = delete_option( $option );
855
856                if ( $result ) {
857                        delete_option( $option_timeout );
858                }
859        }
860
861        if ( $result ) {
862
863                /**
864                 * Fires after a transient is deleted.
865                 *
866                 * @since 3.0.0
867                 *
868                 * @param string $transient Deleted transient name.
869                 */
870                do_action( 'deleted_transient', $transient );
871        }
872
873        return $result;
874}
875
876/**
877 * Retrieves the value of a transient.
878 *
879 * If the transient does not exist, does not have a value, or has expired,
880 * then the return value will be false.
881 *
882 * @since 2.8.0
883 *
884 * @param string $transient Transient name. Expected to not be SQL-escaped.
885 * @return mixed Value of transient.
886 */
887function get_transient( $transient ) {
888
889        /**
890         * Filters the value of an existing transient before it is retrieved.
891         *
892         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
893         *
894         * Returning a value other than false from the filter will short-circuit retrieval
895         * and return that value instead.
896         *
897         * @since 2.8.0
898         * @since 4.4.0 The `$transient` parameter was added
899         *
900         * @param mixed  $pre_transient The default value to return if the transient does not exist.
901         *                              Any value other than false will short-circuit the retrieval
902         *                              of the transient, and return that value.
903         * @param string $transient     Transient name.
904         */
905        $pre = apply_filters( "pre_transient_{$transient}", false, $transient );
906
907        if ( false !== $pre ) {
908                return $pre;
909        }
910
911        if ( wp_using_ext_object_cache() || wp_installing() ) {
912                $value = wp_cache_get( $transient, 'transient' );
913        } else {
914                $transient_option = '_transient_' . $transient;
915                if ( ! wp_installing() ) {
916                        // If option is not in alloptions, it is not autoloaded and thus has a timeout.
917                        $alloptions = wp_load_alloptions();
918                        if ( ! isset( $alloptions[ $transient_option ] ) ) {
919                                $transient_timeout = '_transient_timeout_' . $transient;
920                                $timeout           = get_option( $transient_timeout );
921                                if ( false !== $timeout && $timeout < time() ) {
922                                        delete_option( $transient_option );
923                                        delete_option( $transient_timeout );
924                                        $value = false;
925                                }
926                        }
927                }
928
929                if ( ! isset( $value ) ) {
930                        $value = get_option( $transient_option );
931                }
932        }
933
934        /**
935         * Filters an existing transient's value.
936         *
937         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
938         *
939         * @since 2.8.0
940         * @since 4.4.0 The `$transient` parameter was added
941         *
942         * @param mixed  $value     Value of transient.
943         * @param string $transient Transient name.
944         */
945        return apply_filters( "transient_{$transient}", $value, $transient );
946}
947
948/**
949 * Sets/updates the value of a transient.
950 *
951 * You do not need to serialize values. If the value needs to be serialized,
952 * then it will be serialized before it is set.
953 *
954 * @since 2.8.0
955 *
956 * @param string $transient  Transient name. Expected to not be SQL-escaped.
957 *                           Must be 172 characters or fewer in length.
958 * @param mixed  $value      Transient value. Must be serializable if non-scalar.
959 *                           Expected to not be SQL-escaped.
960 * @param int    $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
961 * @return bool True if the value was set, false otherwise.
962 */
963function set_transient( $transient, $value, $expiration = 0 ) {
964
965        $expiration = (int) $expiration;
966
967        /**
968         * Filters a specific transient before its value is set.
969         *
970         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
971         *
972         * @since 3.0.0
973         * @since 4.2.0 The `$expiration` parameter was added.
974         * @since 4.4.0 The `$transient` parameter was added.
975         *
976         * @param mixed  $value      New value of transient.
977         * @param int    $expiration Time until expiration in seconds.
978         * @param string $transient  Transient name.
979         */
980        $value = apply_filters( "pre_set_transient_{$transient}", $value, $expiration, $transient );
981
982        /**
983         * Filters the expiration for a transient before its value is set.
984         *
985         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
986         *
987         * @since 4.4.0
988         *
989         * @param int    $expiration Time until expiration in seconds. Use 0 for no expiration.
990         * @param mixed  $value      New value of transient.
991         * @param string $transient  Transient name.
992         */
993        $expiration = apply_filters( "expiration_of_transient_{$transient}", $expiration, $value, $transient );
994
995        if ( wp_using_ext_object_cache() || wp_installing() ) {
996                $result = wp_cache_set( $transient, $value, 'transient', $expiration );
997        } else {
998                $transient_timeout = '_transient_timeout_' . $transient;
999                $transient_option  = '_transient_' . $transient;
1000
1001                if ( false === get_option( $transient_option ) ) {
1002                        $autoload = 'yes';
1003                        if ( $expiration ) {
1004                                $autoload = 'no';
1005                                add_option( $transient_timeout, time() + $expiration, '', 'no' );
1006                        }
1007                        $result = add_option( $transient_option, $value, '', $autoload );
1008                } else {
1009                        /*
1010                         * If expiration is requested, but the transient has no timeout option,
1011                         * delete, then re-create transient rather than update.
1012                         */
1013                        $update = true;
1014
1015                        if ( $expiration ) {
1016                                if ( false === get_option( $transient_timeout ) ) {
1017                                        delete_option( $transient_option );
1018                                        add_option( $transient_timeout, time() + $expiration, '', 'no' );
1019                                        $result = add_option( $transient_option, $value, '', 'no' );
1020                                        $update = false;
1021                                } else {
1022                                        update_option( $transient_timeout, time() + $expiration );
1023                                }
1024                        }
1025
1026                        if ( $update ) {
1027                                $result = update_option( $transient_option, $value );
1028                        }
1029                }
1030        }
1031
1032        if ( $result ) {
1033
1034                /**
1035                 * Fires after the value for a specific transient has been set.
1036                 *
1037                 * The dynamic portion of the hook name, `$transient`, refers to the transient name.
1038                 *
1039                 * @since 3.0.0
1040                 * @since 3.6.0 The `$value` and `$expiration` parameters were added.
1041                 * @since 4.4.0 The `$transient` parameter was added.
1042                 *
1043                 * @param mixed  $value      Transient value.
1044                 * @param int    $expiration Time until expiration in seconds.
1045                 * @param string $transient  The name of the transient.
1046                 */
1047                do_action( "set_transient_{$transient}", $value, $expiration, $transient );
1048
1049                /**
1050                 * Fires after the value for a transient has been set.
1051                 *
1052                 * @since 3.0.0
1053                 * @since 3.6.0 The `$value` and `$expiration` parameters were added.
1054                 *
1055                 * @param string $transient  The name of the transient.
1056                 * @param mixed  $value      Transient value.
1057                 * @param int    $expiration Time until expiration in seconds.
1058                 */
1059                do_action( 'setted_transient', $transient, $value, $expiration );
1060        }
1061
1062        return $result;
1063}
1064
1065/**
1066 * Deletes all expired transients.
1067 *
1068 * Note that this function won't do anything if an external object cache is in use.
1069 *
1070 * The multi-table delete syntax is used to delete the transient record
1071 * from table a, and the corresponding transient_timeout record from table b.
1072 *
1073 * @global wpdb $wpdb WordPress database abstraction object.
1074 *
1075 * @since 4.9.0
1076 *
1077 * @param bool $force_db Optional. Force cleanup to run against the database even when an external object cache is used.
1078 */
1079function delete_expired_transients( $force_db = false ) {
1080        global $wpdb;
1081
1082        if ( ! $force_db && wp_using_ext_object_cache() ) {
1083                return;
1084        }
1085
1086        $wpdb->query(
1087                $wpdb->prepare(
1088                        "DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b
1089                        WHERE a.option_name LIKE %s
1090                        AND a.option_name NOT LIKE %s
1091                        AND b.option_name = CONCAT( '_transient_timeout_', SUBSTRING( a.option_name, 12 ) )
1092                        AND b.option_value < %d",
1093                        $wpdb->esc_like( '_transient_' ) . '%',
1094                        $wpdb->esc_like( '_transient_timeout_' ) . '%',
1095                        time()
1096                )
1097        );
1098
1099        if ( ! is_multisite() ) {
1100                // Single site stores site transients in the options table.
1101                $wpdb->query(
1102                        $wpdb->prepare(
1103                                "DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b
1104                                WHERE a.option_name LIKE %s
1105                                AND a.option_name NOT LIKE %s
1106                                AND b.option_name = CONCAT( '_site_transient_timeout_', SUBSTRING( a.option_name, 17 ) )
1107                                AND b.option_value < %d",
1108                                $wpdb->esc_like( '_site_transient_' ) . '%',
1109                                $wpdb->esc_like( '_site_transient_timeout_' ) . '%',
1110                                time()
1111                        )
1112                );
1113        } elseif ( is_multisite() && is_main_site() && is_main_network() ) {
1114                // Multisite stores site transients in the sitemeta table.
1115                $wpdb->query(
1116                        $wpdb->prepare(
1117                                "DELETE a, b FROM {$wpdb->sitemeta} a, {$wpdb->sitemeta} b
1118                                WHERE a.meta_key LIKE %s
1119                                AND a.meta_key NOT LIKE %s
1120                                AND b.meta_key = CONCAT( '_site_transient_timeout_', SUBSTRING( a.meta_key, 17 ) )
1121                                AND b.meta_value < %d",
1122                                $wpdb->esc_like( '_site_transient_' ) . '%',
1123                                $wpdb->esc_like( '_site_transient_timeout_' ) . '%',
1124                                time()
1125                        )
1126                );
1127        }
1128}
1129
1130/**
1131 * Saves and restores user interface settings stored in a cookie.
1132 *
1133 * Checks if the current user-settings cookie is updated and stores it. When no
1134 * cookie exists (different browser used), adds the last saved cookie restoring
1135 * the settings.
1136 *
1137 * @since 2.7.0
1138 */
1139function wp_user_settings() {
1140
1141        if ( ! is_admin() || wp_doing_ajax() ) {
1142                return;
1143        }
1144
1145        $user_id = get_current_user_id();
1146        if ( ! $user_id ) {
1147                return;
1148        }
1149
1150        if ( ! is_user_member_of_blog() ) {
1151                return;
1152        }
1153
1154        $settings = (string) get_user_option( 'user-settings', $user_id );
1155
1156        if ( isset( $_COOKIE[ 'wp-settings-' . $user_id ] ) ) {
1157                $cookie = preg_replace( '/[^A-Za-z0-9=&_]/', '', $_COOKIE[ 'wp-settings-' . $user_id ] );
1158
1159                // No change or both empty.
1160                if ( $cookie == $settings ) {
1161                        return;
1162                }
1163
1164                $last_saved = (int) get_user_option( 'user-settings-time', $user_id );
1165                $current    = isset( $_COOKIE[ 'wp-settings-time-' . $user_id ] ) ? preg_replace( '/[^0-9]/', '', $_COOKIE[ 'wp-settings-time-' . $user_id ] ) : 0;
1166
1167                // The cookie is newer than the saved value. Update the user_option and leave the cookie as-is.
1168                if ( $current > $last_saved ) {
1169                        update_user_option( $user_id, 'user-settings', $cookie, false );
1170                        update_user_option( $user_id, 'user-settings-time', time() - 5, false );
1171                        return;
1172                }
1173        }
1174
1175        // The cookie is not set in the current browser or the saved value is newer.
1176        $secure = ( 'https' === parse_url( admin_url(), PHP_URL_SCHEME ) );
1177        setcookie( 'wp-settings-' . $user_id, $settings, time() + YEAR_IN_SECONDS, SITECOOKIEPATH, '', $secure );
1178        setcookie( 'wp-settings-time-' . $user_id, time(), time() + YEAR_IN_SECONDS, SITECOOKIEPATH, '', $secure );
1179        $_COOKIE[ 'wp-settings-' . $user_id ] = $settings;
1180}
1181
1182/**
1183 * Retrieves user interface setting value based on setting name.
1184 *
1185 * @since 2.7.0
1186 *
1187 * @param string       $name          The name of the setting.
1188 * @param string|false $default_value Optional. Default value to return when $name is not set. Default false.
1189 * @return mixed The last saved user setting or the default value/false if it doesn't exist.
1190 */
1191function get_user_setting( $name, $default_value = false ) {
1192        $all_user_settings = get_all_user_settings();
1193
1194        return isset( $all_user_settings[ $name ] ) ? $all_user_settings[ $name ] : $default_value;
1195}
1196
1197/**
1198 * Adds or updates user interface setting.
1199 *
1200 * Both `$name` and `$value` can contain only ASCII letters, numbers, hyphens, and underscores.
1201 *
1202 * This function has to be used before any output has started as it calls `setcookie()`.
1203 *
1204 * @since 2.8.0
1205 *
1206 * @param string $name  The name of the setting.
1207 * @param string $value The value for the setting.
1208 * @return bool|null True if set successfully, false otherwise.
1209 *                   Null if the current user is not a member of the site.
1210 */
1211function set_user_setting( $name, $value ) {
1212        if ( headers_sent() ) {
1213                return false;
1214        }
1215
1216        $all_user_settings          = get_all_user_settings();
1217        $all_user_settings[ $name ] = $value;
1218
1219        return wp_set_all_user_settings( $all_user_settings );
1220}
1221
1222/**
1223 * Deletes user interface settings.
1224 *
1225 * Deleting settings would reset them to the defaults.
1226 *
1227 * This function has to be used before any output has started as it calls `setcookie()`.
1228 *
1229 * @since 2.7.0
1230 *
1231 * @param string $names The name or array of names of the setting to be deleted.
1232 * @return bool|null True if deleted successfully, false otherwise.
1233 *                   Null if the current user is not a member of the site.
1234 */
1235function delete_user_setting( $names ) {
1236        if ( headers_sent() ) {
1237                return false;
1238        }
1239
1240        $all_user_settings = get_all_user_settings();
1241        $names             = (array) $names;
1242        $deleted           = false;
1243
1244        foreach ( $names as $name ) {
1245                if ( isset( $all_user_settings[ $name ] ) ) {
1246                        unset( $all_user_settings[ $name ] );
1247                        $deleted = true;
1248                }
1249        }
1250
1251        if ( $deleted ) {
1252                return wp_set_all_user_settings( $all_user_settings );
1253        }
1254
1255        return false;
1256}
1257
1258/**
1259 * Retrieves all user interface settings.
1260 *
1261 * @since 2.7.0
1262 *
1263 * @global array $_updated_user_settings
1264 *
1265 * @return array The last saved user settings or empty array.
1266 */
1267function get_all_user_settings() {
1268        global $_updated_user_settings;
1269
1270        $user_id = get_current_user_id();
1271        if ( ! $user_id ) {
1272                return array();
1273        }
1274
1275        if ( isset( $_updated_user_settings ) && is_array( $_updated_user_settings ) ) {
1276                return $_updated_user_settings;
1277        }
1278
1279        $user_settings = array();
1280
1281        if ( isset( $_COOKIE[ 'wp-settings-' . $user_id ] ) ) {
1282                $cookie = preg_replace( '/[^A-Za-z0-9=&_-]/', '', $_COOKIE[ 'wp-settings-' . $user_id ] );
1283
1284                if ( strpos( $cookie, '=' ) ) { // '=' cannot be 1st char.
1285                        parse_str( $cookie, $user_settings );
1286                }
1287        } else {
1288                $option = get_user_option( 'user-settings', $user_id );
1289
1290                if ( $option && is_string( $option ) ) {
1291                        parse_str( $option, $user_settings );
1292                }
1293        }
1294
1295        $_updated_user_settings = $user_settings;
1296        return $user_settings;
1297}
1298
1299/**
1300 * Private. Sets all user interface settings.
1301 *
1302 * @since 2.8.0
1303 * @access private
1304 *
1305 * @global array $_updated_user_settings
1306 *
1307 * @param array $user_settings User settings.
1308 * @return bool|null True if set successfully, false if the current user could not be found.
1309 *                   Null if the current user is not a member of the site.
1310 */
1311function wp_set_all_user_settings( $user_settings ) {
1312        global $_updated_user_settings;
1313
1314        $user_id = get_current_user_id();
1315        if ( ! $user_id ) {
1316                return false;
1317        }
1318
1319        if ( ! is_user_member_of_blog() ) {
1320                return;
1321        }
1322
1323        $settings = '';
1324        foreach ( $user_settings as $name => $value ) {
1325                $_name  = preg_replace( '/[^A-Za-z0-9_-]+/', '', $name );
1326                $_value = preg_replace( '/[^A-Za-z0-9_-]+/', '', $value );
1327
1328                if ( ! empty( $_name ) ) {
1329                        $settings .= $_name . '=' . $_value . '&';
1330                }
1331        }
1332
1333        $settings = rtrim( $settings, '&' );
1334        parse_str( $settings, $_updated_user_settings );
1335
1336        update_user_option( $user_id, 'user-settings', $settings, false );
1337        update_user_option( $user_id, 'user-settings-time', time(), false );
1338
1339        return true;
1340}
1341
1342/**
1343 * Deletes the user settings of the current user.
1344 *
1345 * @since 2.7.0
1346 */
1347function delete_all_user_settings() {
1348        $user_id = get_current_user_id();
1349        if ( ! $user_id ) {
1350                return;
1351        }
1352
1353        update_user_option( $user_id, 'user-settings', '', false );
1354        setcookie( 'wp-settings-' . $user_id, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH );
1355}
1356
1357/**
1358 * Retrieve an option value for the current network based on name of option.
1359 *
1360 * @since 2.8.0
1361 * @since 4.4.0 The `$use_cache` parameter was deprecated.
1362 * @since 4.4.0 Modified into wrapper for get_network_option()
1363 *
1364 * @see get_network_option()
1365 *
1366 * @param string $option        Name of the option to retrieve. Expected to not be SQL-escaped.
1367 * @param mixed  $default_value Optional. Value to return if the option doesn't exist. Default false.
1368 * @param bool   $deprecated    Whether to use cache. Multisite only. Always set to true.
1369 * @return mixed Value set for the option.
1370 */
1371function get_site_option( $option, $default_value = false, $deprecated = true ) {
1372        return get_network_option( null, $option, $default_value );
1373}
1374
1375/**
1376 * Adds a new option for the current network.
1377 *
1378 * Existing options will not be updated. Note that prior to 3.3 this wasn't the case.
1379 *
1380 * @since 2.8.0
1381 * @since 4.4.0 Modified into wrapper for add_network_option()
1382 *
1383 * @see add_network_option()
1384 *
1385 * @param string $option Name of the option to add. Expected to not be SQL-escaped.
1386 * @param mixed  $value  Option value, can be anything. Expected to not be SQL-escaped.
1387 * @return bool True if the option was added, false otherwise.
1388 */
1389function add_site_option( $option, $value ) {
1390        return add_network_option( null, $option, $value );
1391}
1392
1393/**
1394 * Removes an option by name for the current network.
1395 *
1396 * @since 2.8.0
1397 * @since 4.4.0 Modified into wrapper for delete_network_option()
1398 *
1399 * @see delete_network_option()
1400 *
1401 * @param string $option Name of the option to delete. Expected to not be SQL-escaped.
1402 * @return bool True if the option was deleted, false otherwise.
1403 */
1404function delete_site_option( $option ) {
1405        return delete_network_option( null, $option );
1406}
1407
1408/**
1409 * Updates the value of an option that was already added for the current network.
1410 *
1411 * @since 2.8.0
1412 * @since 4.4.0 Modified into wrapper for update_network_option()
1413 *
1414 * @see update_network_option()
1415 *
1416 * @param string $option Name of the option. Expected to not be SQL-escaped.
1417 * @param mixed  $value  Option value. Expected to not be SQL-escaped.
1418 * @return bool True if the value was updated, false otherwise.
1419 */
1420function update_site_option( $option, $value ) {
1421        return update_network_option( null, $option, $value );
1422}
1423
1424/**
1425 * Retrieves a network's option value based on the option name.
1426 *
1427 * @since 4.4.0
1428 *
1429 * @see get_option()
1430 *
1431 * @global wpdb $wpdb WordPress database abstraction object.
1432 *
1433 * @param int    $network_id    ID of the network. Can be null to default to the current network ID.
1434 * @param string $option        Name of the option to retrieve. Expected to not be SQL-escaped.
1435 * @param mixed  $default_value Optional. Value to return if the option doesn't exist. Default false.
1436 * @return mixed Value set for the option.
1437 */
1438function get_network_option( $network_id, $option, $default_value = false ) {
1439        global $wpdb;
1440
1441        if ( $network_id && ! is_numeric( $network_id ) ) {
1442                return false;
1443        }
1444
1445        $network_id = (int) $network_id;
1446
1447        // Fallback to the current network if a network ID is not specified.
1448        if ( ! $network_id ) {
1449                $network_id = get_current_network_id();
1450        }
1451
1452        /**
1453         * Filters the value of an existing network option before it is retrieved.
1454         *
1455         * The dynamic portion of the hook name, `$option`, refers to the option name.
1456         *
1457         * Returning a value other than false from the filter will short-circuit retrieval
1458         * and return that value instead.
1459         *
1460         * @since 2.9.0 As 'pre_site_option_' . $key
1461         * @since 3.0.0
1462         * @since 4.4.0 The `$option` parameter was added.
1463         * @since 4.7.0 The `$network_id` parameter was added.
1464         * @since 4.9.0 The `$default_value` parameter was added.
1465         *
1466         * @param mixed  $pre_option    The value to return instead of the option value. This differs from
1467         *                              `$default_value`, which is used as the fallback value in the event
1468         *                              the option doesn't exist elsewhere in get_network_option().
1469         *                              Default false (to skip past the short-circuit).
1470         * @param string $option        Option name.
1471         * @param int    $network_id    ID of the network.
1472         * @param mixed  $default_value The fallback value to return if the option does not exist.
1473         *                              Default false.
1474         */
1475        $pre = apply_filters( "pre_site_option_{$option}", false, $option, $network_id, $default_value );
1476
1477        if ( false !== $pre ) {
1478                return $pre;
1479        }
1480
1481        // Prevent non-existent options from triggering multiple queries.
1482        $notoptions_key = "$network_id:notoptions";
1483        $notoptions     = wp_cache_get( $notoptions_key, 'site-options' );
1484
1485        if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) {
1486
1487                /**
1488                 * Filters the value of a specific default network option.
1489                 *
1490                 * The dynamic portion of the hook name, `$option`, refers to the option name.
1491                 *
1492                 * @since 3.4.0
1493                 * @since 4.4.0 The `$option` parameter was added.
1494                 * @since 4.7.0 The `$network_id` parameter was added.
1495                 *
1496                 * @param mixed  $default_value The value to return if the site option does not exist
1497                 *                              in the database.
1498                 * @param string $option        Option name.
1499                 * @param int    $network_id    ID of the network.
1500                 */
1501                return apply_filters( "default_site_option_{$option}", $default_value, $option, $network_id );
1502        }
1503
1504        if ( ! is_multisite() ) {
1505                /** This filter is documented in wp-includes/option.php */
1506                $default_value = apply_filters( 'default_site_option_' . $option, $default_value, $option, $network_id );
1507                $value         = get_option( $option, $default_value );
1508        } else {
1509                $cache_key = "$network_id:$option";
1510                $value     = wp_cache_get( $cache_key, 'site-options' );
1511
1512                if ( ! isset( $value ) || false === $value ) {
1513                        $row = $wpdb->get_row( $wpdb->prepare( "SELECT meta_value FROM $wpdb->sitemeta WHERE meta_key = %s AND site_id = %d", $option, $network_id ) );
1514
1515                        // Has to be get_row() instead of get_var() because of funkiness with 0, false, null values.
1516                        if ( is_object( $row ) ) {
1517                                $value = $row->meta_value;
1518                                $value = maybe_unserialize( $value );
1519                                wp_cache_set( $cache_key, $value, 'site-options' );
1520                        } else {
1521                                if ( ! is_array( $notoptions ) ) {
1522                                        $notoptions = array();
1523                                }
1524
1525                                $notoptions[ $option ] = true;
1526                                wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
1527
1528                                /** This filter is documented in wp-includes/option.php */
1529                                $value = apply_filters( 'default_site_option_' . $option, $default_value, $option, $network_id );
1530                        }
1531                }
1532        }
1533
1534        if ( ! is_array( $notoptions ) ) {
1535                $notoptions = array();
1536                wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
1537        }
1538
1539        /**
1540         * Filters the value of an existing network option.
1541         *
1542         * The dynamic portion of the hook name, `$option`, refers to the option name.
1543         *
1544         * @since 2.9.0 As 'site_option_' . $key
1545         * @since 3.0.0
1546         * @since 4.4.0 The `$option` parameter was added.
1547         * @since 4.7.0 The `$network_id` parameter was added.
1548         *
1549         * @param mixed  $value      Value of network option.
1550         * @param string $option     Option name.
1551         * @param int    $network_id ID of the network.
1552         */
1553        return apply_filters( "site_option_{$option}", $value, $option, $network_id );
1554}
1555
1556/**
1557 * Adds a new network option.
1558 *
1559 * Existing options will not be updated.
1560 *
1561 * @since 4.4.0
1562 *
1563 * @see add_option()
1564 *
1565 * @global wpdb $wpdb WordPress database abstraction object.
1566 *
1567 * @param int    $network_id ID of the network. Can be null to default to the current network ID.
1568 * @param string $option     Name of the option to add. Expected to not be SQL-escaped.
1569 * @param mixed  $value      Option value, can be anything. Expected to not be SQL-escaped.
1570 * @return bool True if the option was added, false otherwise.
1571 */
1572function add_network_option( $network_id, $option, $value ) {
1573        global $wpdb;
1574
1575        if ( $network_id && ! is_numeric( $network_id ) ) {
1576                return false;
1577        }
1578
1579        $network_id = (int) $network_id;
1580
1581        // Fallback to the current network if a network ID is not specified.
1582        if ( ! $network_id ) {
1583                $network_id = get_current_network_id();
1584        }
1585
1586        wp_protect_special_option( $option );
1587
1588        /**
1589         * Filters the value of a specific network option before it is added.
1590         *
1591         * The dynamic portion of the hook name, `$option`, refers to the option name.
1592         *
1593         * @since 2.9.0 As 'pre_add_site_option_' . $key
1594         * @since 3.0.0
1595         * @since 4.4.0 The `$option` parameter was added.
1596         * @since 4.7.0 The `$network_id` parameter was added.
1597         *
1598         * @param mixed  $value      Value of network option.
1599         * @param string $option     Option name.
1600         * @param int    $network_id ID of the network.
1601         */
1602        $value = apply_filters( "pre_add_site_option_{$option}", $value, $option, $network_id );
1603
1604        $notoptions_key = "$network_id:notoptions";
1605
1606        if ( ! is_multisite() ) {
1607                $result = add_option( $option, $value, '', 'no' );
1608        } else {
1609                $cache_key = "$network_id:$option";
1610
1611                /*
1612                 * Make sure the option doesn't already exist.
1613                 * We can check the 'notoptions' cache before we ask for a DB query.
1614                 */
1615                $notoptions = wp_cache_get( $notoptions_key, 'site-options' );
1616
1617                if ( ! is_array( $notoptions ) || ! isset( $notoptions[ $option ] ) ) {
1618                        if ( false !== get_network_option( $network_id, $option, false ) ) {
1619                                return false;
1620                        }
1621                }
1622
1623                $value = sanitize_option( $option, $value );
1624
1625                $serialized_value = maybe_serialize( $value );
1626                $result           = $wpdb->insert(
1627                        $wpdb->sitemeta,
1628                        array(
1629                                'site_id'    => $network_id,
1630                                'meta_key'   => $option,
1631                                'meta_value' => $serialized_value,
1632                        )
1633                );
1634
1635                if ( ! $result ) {
1636                        return false;
1637                }
1638
1639                wp_cache_set( $cache_key, $value, 'site-options' );
1640
1641                // This option exists now.
1642                $notoptions = wp_cache_get( $notoptions_key, 'site-options' ); // Yes, again... we need it to be fresh.
1643
1644                if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) {
1645                        unset( $notoptions[ $option ] );
1646                        wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
1647                }
1648        }
1649
1650        if ( $result ) {
1651
1652                /**
1653                 * Fires after a specific network option has been successfully added.
1654                 *
1655                 * The dynamic portion of the hook name, `$option`, refers to the option name.
1656                 *
1657                 * @since 2.9.0 As "add_site_option_{$key}"
1658                 * @since 3.0.0
1659                 * @since 4.7.0 The `$network_id` parameter was added.
1660                 *
1661                 * @param string $option     Name of the network option.
1662                 * @param mixed  $value      Value of the network option.
1663                 * @param int    $network_id ID of the network.
1664                 */
1665                do_action( "add_site_option_{$option}", $option, $value, $network_id );
1666
1667                /**
1668                 * Fires after a network option has been successfully added.
1669                 *
1670                 * @since 3.0.0
1671                 * @since 4.7.0 The `$network_id` parameter was added.
1672                 *
1673                 * @param string $option     Name of the network option.
1674                 * @param mixed  $value      Value of the network option.
1675                 * @param int    $network_id ID of the network.
1676                 */
1677                do_action( 'add_site_option', $option, $value, $network_id );
1678
1679                return true;
1680        }
1681
1682        return false;
1683}
1684
1685/**
1686 * Removes a network option by name.
1687 *
1688 * @since 4.4.0
1689 *
1690 * @see delete_option()
1691 *
1692 * @global wpdb $wpdb WordPress database abstraction object.
1693 *
1694 * @param int    $network_id ID of the network. Can be null to default to the current network ID.
1695 * @param string $option     Name of the option to delete. Expected to not be SQL-escaped.
1696 * @return bool True if the option was deleted, false otherwise.
1697 */
1698function delete_network_option( $network_id, $option ) {
1699        global $wpdb;
1700
1701        if ( $network_id && ! is_numeric( $network_id ) ) {
1702                return false;
1703        }
1704
1705        $network_id = (int) $network_id;
1706
1707        // Fallback to the current network if a network ID is not specified.
1708        if ( ! $network_id ) {
1709                $network_id = get_current_network_id();
1710        }
1711
1712        /**
1713         * Fires immediately before a specific network option is deleted.
1714         *
1715         * The dynamic portion of the hook name, `$option`, refers to the option name.
1716         *
1717         * @since 3.0.0
1718         * @since 4.4.0 The `$option` parameter was added.
1719         * @since 4.7.0 The `$network_id` parameter was added.
1720         *
1721         * @param string $option     Option name.
1722         * @param int    $network_id ID of the network.
1723         */
1724        do_action( "pre_delete_site_option_{$option}", $option, $network_id );
1725
1726        if ( ! is_multisite() ) {
1727                $result = delete_option( $option );
1728        } else {
1729                $row = $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM {$wpdb->sitemeta} WHERE meta_key = %s AND site_id = %d", $option, $network_id ) );
1730                if ( is_null( $row ) || ! $row->meta_id ) {
1731                        return false;
1732                }
1733                $cache_key = "$network_id:$option";
1734                wp_cache_delete( $cache_key, 'site-options' );
1735
1736                $result = $wpdb->delete(
1737                        $wpdb->sitemeta,
1738                        array(
1739                                'meta_key' => $option,
1740                                'site_id'  => $network_id,
1741                        )
1742                );
1743        }
1744
1745        if ( $result ) {
1746
1747                /**
1748                 * Fires after a specific network option has been deleted.
1749                 *
1750                 * The dynamic portion of the hook name, `$option`, refers to the option name.
1751                 *
1752                 * @since 2.9.0 As "delete_site_option_{$key}"
1753                 * @since 3.0.0
1754                 * @since 4.7.0 The `$network_id` parameter was added.
1755                 *
1756                 * @param string $option     Name of the network option.
1757                 * @param int    $network_id ID of the network.
1758                 */
1759                do_action( "delete_site_option_{$option}", $option, $network_id );
1760
1761                /**
1762                 * Fires after a network option has been deleted.
1763                 *
1764                 * @since 3.0.0
1765                 * @since 4.7.0 The `$network_id` parameter was added.
1766                 *
1767                 * @param string $option     Name of the network option.
1768                 * @param int    $network_id ID of the network.
1769                 */
1770                do_action( 'delete_site_option', $option, $network_id );
1771
1772                return true;
1773        }
1774
1775        return false;
1776}
1777
1778/**
1779 * Updates the value of a network option that was already added.
1780 *
1781 * @since 4.4.0
1782 *
1783 * @see update_option()
1784 *
1785 * @global wpdb $wpdb WordPress database abstraction object.
1786 *
1787 * @param int    $network_id ID of the network. Can be null to default to the current network ID.
1788 * @param string $option     Name of the option. Expected to not be SQL-escaped.
1789 * @param mixed  $value      Option value. Expected to not be SQL-escaped.
1790 * @return bool True if the value was updated, false otherwise.
1791 */
1792function update_network_option( $network_id, $option, $value ) {
1793        global $wpdb;
1794
1795        if ( $network_id && ! is_numeric( $network_id ) ) {
1796                return false;
1797        }
1798
1799        $network_id = (int) $network_id;
1800
1801        // Fallback to the current network if a network ID is not specified.
1802        if ( ! $network_id ) {
1803                $network_id = get_current_network_id();
1804        }
1805
1806        wp_protect_special_option( $option );
1807
1808        $old_value = get_network_option( $network_id, $option, false );
1809
1810        /**
1811         * Filters a specific network option before its value is updated.
1812         *
1813         * The dynamic portion of the hook name, `$option`, refers to the option name.
1814         *
1815         * @since 2.9.0 As 'pre_update_site_option_' . $key
1816         * @since 3.0.0
1817         * @since 4.4.0 The `$option` parameter was added.
1818         * @since 4.7.0 The `$network_id` parameter was added.
1819         *
1820         * @param mixed  $value      New value of the network option.
1821         * @param mixed  $old_value  Old value of the network option.
1822         * @param string $option     Option name.
1823         * @param int    $network_id ID of the network.
1824         */
1825        $value = apply_filters( "pre_update_site_option_{$option}", $value, $old_value, $option, $network_id );
1826
1827        /*
1828         * If the new and old values are the same, no need to update.
1829         *
1830         * Unserialized values will be adequate in most cases. If the unserialized
1831         * data differs, the (maybe) serialized data is checked to avoid
1832         * unnecessary database calls for otherwise identical object instances.
1833         *
1834         * See https://core.trac.wordpress.org/ticket/44956
1835         */
1836        if ( $value === $old_value || maybe_serialize( $value ) === maybe_serialize( $old_value ) ) {
1837                return false;
1838        }
1839
1840        if ( false === $old_value ) {
1841                return add_network_option( $network_id, $option, $value );
1842        }
1843
1844        $notoptions_key = "$network_id:notoptions";
1845        $notoptions     = wp_cache_get( $notoptions_key, 'site-options' );
1846
1847        if ( is_array( $notoptions ) && isset( $notoptions[ $option ] ) ) {
1848                unset( $notoptions[ $option ] );
1849                wp_cache_set( $notoptions_key, $notoptions, 'site-options' );
1850        }
1851
1852        if ( ! is_multisite() ) {
1853                $result = update_option( $option, $value, 'no' );
1854        } else {
1855                $value = sanitize_option( $option, $value );
1856
1857                $serialized_value = maybe_serialize( $value );
1858                $result           = $wpdb->update(
1859                        $wpdb->sitemeta,
1860                        array( 'meta_value' => $serialized_value ),
1861                        array(
1862                                'site_id'  => $network_id,
1863                                'meta_key' => $option,
1864                        )
1865                );
1866
1867                if ( $result ) {
1868                        $cache_key = "$network_id:$option";
1869                        wp_cache_set( $cache_key, $value, 'site-options' );
1870                }
1871        }
1872
1873        if ( $result ) {
1874
1875                /**
1876                 * Fires after the value of a specific network option has been successfully updated.
1877                 *
1878                 * The dynamic portion of the hook name, `$option`, refers to the option name.
1879                 *
1880                 * @since 2.9.0 As "update_site_option_{$key}"
1881                 * @since 3.0.0
1882                 * @since 4.7.0 The `$network_id` parameter was added.
1883                 *
1884                 * @param string $option     Name of the network option.
1885                 * @param mixed  $value      Current value of the network option.
1886                 * @param mixed  $old_value  Old value of the network option.
1887                 * @param int    $network_id ID of the network.
1888                 */
1889                do_action( "update_site_option_{$option}", $option, $value, $old_value, $network_id );
1890
1891                /**
1892                 * Fires after the value of a network option has been successfully updated.
1893                 *
1894                 * @since 3.0.0
1895                 * @since 4.7.0 The `$network_id` parameter was added.
1896                 *
1897                 * @param string $option     Name of the network option.
1898                 * @param mixed  $value      Current value of the network option.
1899                 * @param mixed  $old_value  Old value of the network option.
1900                 * @param int    $network_id ID of the network.
1901                 */
1902                do_action( 'update_site_option', $option, $value, $old_value, $network_id );
1903
1904                return true;
1905        }
1906
1907        return false;
1908}
1909
1910/**
1911 * Deletes a site transient.
1912 *
1913 * @since 2.9.0
1914 *
1915 * @param string $transient Transient name. Expected to not be SQL-escaped.
1916 * @return bool True if the transient was deleted, false otherwise.
1917 */
1918function delete_site_transient( $transient ) {
1919
1920        /**
1921         * Fires immediately before a specific site transient is deleted.
1922         *
1923         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
1924         *
1925         * @since 3.0.0
1926         *
1927         * @param string $transient Transient name.
1928         */
1929        do_action( "delete_site_transient_{$transient}", $transient );
1930
1931        if ( wp_using_ext_object_cache() || wp_installing() ) {
1932                $result = wp_cache_delete( $transient, 'site-transient' );
1933        } else {
1934                $option_timeout = '_site_transient_timeout_' . $transient;
1935                $option         = '_site_transient_' . $transient;
1936                $result         = delete_site_option( $option );
1937
1938                if ( $result ) {
1939                        delete_site_option( $option_timeout );
1940                }
1941        }
1942
1943        if ( $result ) {
1944
1945                /**
1946                 * Fires after a transient is deleted.
1947                 *
1948                 * @since 3.0.0
1949                 *
1950                 * @param string $transient Deleted transient name.
1951                 */
1952                do_action( 'deleted_site_transient', $transient );
1953        }
1954
1955        return $result;
1956}
1957
1958/**
1959 * Retrieves the value of a site transient.
1960 *
1961 * If the transient does not exist, does not have a value, or has expired,
1962 * then the return value will be false.
1963 *
1964 * @since 2.9.0
1965 *
1966 * @see get_transient()
1967 *
1968 * @param string $transient Transient name. Expected to not be SQL-escaped.
1969 * @return mixed Value of transient.
1970 */
1971function get_site_transient( $transient ) {
1972
1973        /**
1974         * Filters the value of an existing site transient before it is retrieved.
1975         *
1976         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
1977         *
1978         * Returning a value other than boolean false will short-circuit retrieval and
1979         * return that value instead.
1980         *
1981         * @since 2.9.0
1982         * @since 4.4.0 The `$transient` parameter was added.
1983         *
1984         * @param mixed  $pre_site_transient The default value to return if the site transient does not exist.
1985         *                                   Any value other than false will short-circuit the retrieval
1986         *                                   of the transient, and return that value.
1987         * @param string $transient          Transient name.
1988         */
1989        $pre = apply_filters( "pre_site_transient_{$transient}", false, $transient );
1990
1991        if ( false !== $pre ) {
1992                return $pre;
1993        }
1994
1995        if ( wp_using_ext_object_cache() || wp_installing() ) {
1996                $value = wp_cache_get( $transient, 'site-transient' );
1997        } else {
1998                // Core transients that do not have a timeout. Listed here so querying timeouts can be avoided.
1999                $no_timeout       = array( 'update_core', 'update_plugins', 'update_themes' );
2000                $transient_option = '_site_transient_' . $transient;
2001                if ( ! in_array( $transient, $no_timeout, true ) ) {
2002                        $transient_timeout = '_site_transient_timeout_' . $transient;
2003                        $timeout           = get_site_option( $transient_timeout );
2004                        if ( false !== $timeout && $timeout < time() ) {
2005                                delete_site_option( $transient_option );
2006                                delete_site_option( $transient_timeout );
2007                                $value = false;
2008                        }
2009                }
2010
2011                if ( ! isset( $value ) ) {
2012                        $value = get_site_option( $transient_option );
2013                }
2014        }
2015
2016        /**
2017         * Filters the value of an existing site transient.
2018         *
2019         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
2020         *
2021         * @since 2.9.0
2022         * @since 4.4.0 The `$transient` parameter was added.
2023         *
2024         * @param mixed  $value     Value of site transient.
2025         * @param string $transient Transient name.
2026         */
2027        return apply_filters( "site_transient_{$transient}", $value, $transient );
2028}
2029
2030/**
2031 * Sets/updates the value of a site transient.
2032 *
2033 * You do not need to serialize values. If the value needs to be serialized,
2034 * then it will be serialized before it is set.
2035 *
2036 * @since 2.9.0
2037 *
2038 * @see set_transient()
2039 *
2040 * @param string $transient  Transient name. Expected to not be SQL-escaped. Must be
2041 *                           167 characters or fewer in length.
2042 * @param mixed  $value      Transient value. Expected to not be SQL-escaped.
2043 * @param int    $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
2044 * @return bool True if the value was set, false otherwise.
2045 */
2046function set_site_transient( $transient, $value, $expiration = 0 ) {
2047
2048        /**
2049         * Filters the value of a specific site transient before it is set.
2050         *
2051         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
2052         *
2053         * @since 3.0.0
2054         * @since 4.4.0 The `$transient` parameter was added.
2055         *
2056         * @param mixed  $value     New value of site transient.
2057         * @param string $transient Transient name.
2058         */
2059        $value = apply_filters( "pre_set_site_transient_{$transient}", $value, $transient );
2060
2061        $expiration = (int) $expiration;
2062
2063        /**
2064         * Filters the expiration for a site transient before its value is set.
2065         *
2066         * The dynamic portion of the hook name, `$transient`, refers to the transient name.
2067         *
2068         * @since 4.4.0
2069         *
2070         * @param int    $expiration Time until expiration in seconds. Use 0 for no expiration.
2071         * @param mixed  $value      New value of site transient.
2072         * @param string $transient  Transient name.
2073         */
2074        $expiration = apply_filters( "expiration_of_site_transient_{$transient}", $expiration, $value, $transient );
2075
2076        if ( wp_using_ext_object_cache() || wp_installing() ) {
2077                $result = wp_cache_set( $transient, $value, 'site-transient', $expiration );
2078        } else {
2079                $transient_timeout = '_site_transient_timeout_' . $transient;
2080                $option            = '_site_transient_' . $transient;
2081
2082                if ( false === get_site_option( $option ) ) {
2083                        if ( $expiration ) {
2084                                add_site_option( $transient_timeout, time() + $expiration );
2085                        }
2086                        $result = add_site_option( $option, $value );
2087                } else {
2088                        if ( $expiration ) {
2089                                update_site_option( $transient_timeout, time() + $expiration );
2090                        }
2091                        $result = update_site_option( $option, $value );
2092                }
2093        }
2094
2095        if ( $result ) {
2096
2097                /**
2098                 * Fires after the value for a specific site transient has been set.
2099                 *
2100                 * The dynamic portion of the hook name, `$transient`, refers to the transient name.
2101                 *
2102                 * @since 3.0.0
2103                 * @since 4.4.0 The `$transient` parameter was added
2104                 *
2105                 * @param mixed  $value      Site transient value.
2106                 * @param int    $expiration Time until expiration in seconds.
2107                 * @param string $transient  Transient name.
2108                 */
2109                do_action( "set_site_transient_{$transient}", $value, $expiration, $transient );
2110
2111                /**
2112                 * Fires after the value for a site transient has been set.
2113                 *
2114                 * @since 3.0.0
2115                 *
2116                 * @param string $transient  The name of the site transient.
2117                 * @param mixed  $value      Site transient value.
2118                 * @param int    $expiration Time until expiration in seconds.
2119                 */
2120                do_action( 'setted_site_transient', $transient, $value, $expiration );
2121        }
2122
2123        return $result;
2124}
2125
2126/**
2127 * Registers default settings available in WordPress.
2128 *
2129 * The settings registered here are primarily useful for the REST API, so this
2130 * does not encompass all settings available in WordPress.
2131 *
2132 * @since 4.7.0
2133 * @since 6.0.1 The `show_on_front`, `page_on_front`, and `page_for_posts` options were added.
2134 */
2135function register_initial_settings() {
2136        register_setting(
2137                'general',
2138                'blogname',
2139                array(
2140                        'show_in_rest' => array(
2141                                'name' => 'title',
2142                        ),
2143                        'type'         => 'string',
2144                        'description'  => __( 'Site title.' ),
2145                )
2146        );
2147
2148        register_setting(
2149                'general',
2150                'blogdescription',
2151                array(
2152                        'show_in_rest' => array(
2153                                'name' => 'description',
2154                        ),
2155                        'type'         => 'string',
2156                        'description'  => __( 'Site tagline.' ),
2157                )
2158        );
2159
2160        if ( ! is_multisite() ) {
2161                register_setting(
2162                        'general',
2163                        'siteurl',
2164                        array(
2165                                'show_in_rest' => array(
2166                                        'name'   => 'url',
2167                                        'schema' => array(
2168                                                'format' => 'uri',
2169                                        ),
2170                                ),
2171                                'type'         => 'string',
2172                                'description'  => __( 'Site URL.' ),
2173                        )
2174                );
2175        }
2176
2177        if ( ! is_multisite() ) {
2178                register_setting(
2179                        'general',
2180                        'admin_email',
2181                        array(
2182                                'show_in_rest' => array(
2183                                        'name'   => 'email',
2184                                        'schema' => array(
2185                                                'format' => 'email',
2186                                        ),
2187                                ),
2188                                'type'         => 'string',
2189                                'description'  => __( 'This address is used for admin purposes, like new user notification.' ),
2190                        )
2191                );
2192        }
2193
2194        register_setting(
2195                'general',
2196                'timezone_string',
2197                array(
2198                        'show_in_rest' => array(
2199                                'name' => 'timezone',
2200                        ),
2201                        'type'         => 'string',
2202                        'description'  => __( 'A city in the same timezone as you.' ),
2203                )
2204        );
2205
2206        register_setting(
2207                'general',
2208                'date_format',
2209                array(
2210                        'show_in_rest' => true,
2211                        'type'         => 'string',
2212                        'description'  => __( 'A date format for all date strings.' ),
2213                )
2214        );
2215
2216        register_setting(
2217                'general',
2218                'time_format',
2219                array(
2220                        'show_in_rest' => true,
2221                        'type'         => 'string',
2222                        'description'  => __( 'A time format for all time strings.' ),
2223                )
2224        );
2225
2226        register_setting(
2227                'general',
2228                'start_of_week',
2229                array(
2230                        'show_in_rest' => true,
2231                        'type'         => 'integer',
2232                        'description'  => __( 'A day number of the week that the week should start on.' ),
2233                )
2234        );
2235
2236        register_setting(
2237                'general',
2238                'WPLANG',
2239                array(
2240                        'show_in_rest' => array(
2241                                'name' => 'language',
2242                        ),
2243                        'type'         => 'string',
2244                        'description'  => __( 'WordPress locale code.' ),
2245                        'default'      => 'en_US',
2246                )
2247        );
2248
2249        register_setting(
2250                'writing',
2251                'use_smilies',
2252                array(
2253                        'show_in_rest' => true,
2254                        'type'         => 'boolean',
2255                        'description'  => __( 'Convert emoticons like :-) and :-P to graphics on display.' ),
2256                        'default'      => true,
2257                )
2258        );
2259
2260        register_setting(
2261                'writing',
2262                'default_category',
2263                array(
2264                        'show_in_rest' => true,
2265                        'type'         => 'integer',
2266                        'description'  => __( 'Default post category.' ),
2267                )
2268        );
2269
2270        register_setting(
2271                'writing',
2272                'default_post_format',
2273                array(
2274                        'show_in_rest' => true,
2275                        'type'         => 'string',
2276                        'description'  => __( 'Default post format.' ),
2277                )
2278        );
2279
2280        register_setting(
2281                'reading',
2282                'posts_per_page',
2283                array(
2284                        'show_in_rest' => true,
2285                        'type'         => 'integer',
2286                        'description'  => __( 'Blog pages show at most.' ),
2287                        'default'      => 10,
2288                )
2289        );
2290
2291        register_setting(
2292                'reading',
2293                'show_on_front',
2294                array(
2295                        'show_in_rest' => true,
2296                        'type'         => 'string',
2297                        'description'  => __( 'What to show on the front page' ),
2298                )
2299        );
2300
2301        register_setting(
2302                'reading',
2303                'page_on_front',
2304                array(
2305                        'show_in_rest' => true,
2306                        'type'         => 'integer',
2307                        'description'  => __( 'The ID of the page that should be displayed on the front page' ),
2308                )
2309        );
2310
2311        register_setting(
2312                'reading',
2313                'page_for_posts',
2314                array(
2315                        'show_in_rest' => true,
2316                        'type'         => 'integer',
2317                        'description'  => __( 'The ID of the page that should display the latest posts' ),
2318                )
2319        );
2320
2321        register_setting(
2322                'discussion',
2323                'default_ping_status',
2324                array(
2325                        'show_in_rest' => array(
2326                                'schema' => array(
2327                                        'enum' => array( 'open', 'closed' ),
2328                                ),
2329                        ),
2330                        'type'         => 'string',
2331                        'description'  => __( 'Allow link notifications from other blogs (pingbacks and trackbacks) on new articles.' ),
2332                )
2333        );
2334
2335        register_setting(
2336                'discussion',
2337                'default_comment_status',
2338                array(
2339                        'show_in_rest' => array(
2340                                'schema' => array(
2341                                        'enum' => array( 'open', 'closed' ),
2342                                ),
2343                        ),
2344                        'type'         => 'string',
2345                        'description'  => __( 'Allow people to submit comments on new posts.' ),
2346                )
2347        );
2348}
2349
2350/**
2351 * Registers a setting and its data.
2352 *
2353 * @since 2.7.0
2354 * @since 3.0.0 The `misc` option group was deprecated.
2355 * @since 3.5.0 The `privacy` option group was deprecated.
2356 * @since 4.7.0 `$args` can be passed to set flags on the setting, similar to `register_meta()`.
2357 * @since 5.5.0 `$new_whitelist_options` was renamed to `$new_allowed_options`.
2358 *              Please consider writing more inclusive code.
2359 *
2360 * @global array $new_allowed_options
2361 * @global array $wp_registered_settings
2362 *
2363 * @param string $option_group A settings group name. Should correspond to an allowed option key name.
2364 *                             Default allowed option key names include 'general', 'discussion', 'media',
2365 *                             'reading', 'writing', and 'options'.
2366 * @param string $option_name The name of an option to sanitize and save.
2367 * @param array  $args {
2368 *     Data used to describe the setting when registered.
2369 *
2370 *     @type string     $type              The type of data associated with this setting.
2371 *                                         Valid values are 'string', 'boolean', 'integer', 'number', 'array', and 'object'.
2372 *     @type string     $description       A description of the data attached to this setting.
2373 *     @type callable   $sanitize_callback A callback function that sanitizes the option's value.
2374 *     @type bool|array $show_in_rest      Whether data associated with this setting should be included in the REST API.
2375 *                                         When registering complex settings, this argument may optionally be an
2376 *                                         array with a 'schema' key.
2377 *     @type mixed      $default           Default value when calling `get_option()`.
2378 * }
2379 */
2380function register_setting( $option_group, $option_name, $args = array() ) {
2381        global $new_allowed_options, $wp_registered_settings;
2382
2383        /*
2384         * In 5.5.0, the `$new_whitelist_options` global variable was renamed to `$new_allowed_options`.
2385         * Please consider writing more inclusive code.
2386         */
2387        $GLOBALS['new_whitelist_options'] = &$new_allowed_options;
2388
2389        $defaults = array(
2390                'type'              => 'string',
2391                'group'             => $option_group,
2392                'description'       => '',
2393                'sanitize_callback' => null,
2394                'show_in_rest'      => false,
2395        );
2396
2397        // Back-compat: old sanitize callback is added.
2398        if ( is_callable( $args ) ) {
2399                $args = array(
2400                        'sanitize_callback' => $args,
2401                );
2402        }
2403
2404        /**
2405         * Filters the registration arguments when registering a setting.
2406         *
2407         * @since 4.7.0
2408         *
2409         * @param array  $args         Array of setting registration arguments.
2410         * @param array  $defaults     Array of default arguments.
2411         * @param string $option_group Setting group.
2412         * @param string $option_name  Setting name.
2413         */
2414        $args = apply_filters( 'register_setting_args', $args, $defaults, $option_group, $option_name );
2415
2416        $args = wp_parse_args( $args, $defaults );
2417
2418        // Require an item schema when registering settings with an array type.
2419        if ( false !== $args['show_in_rest'] && 'array' === $args['type'] && ( ! is_array( $args['show_in_rest'] ) || ! isset( $args['show_in_rest']['schema']['items'] ) ) ) {
2420                _doing_it_wrong( __FUNCTION__, __( 'When registering an "array" setting to show in the REST API, you must specify the schema for each array item in "show_in_rest.schema.items".' ), '5.4.0' );
2421        }
2422
2423        if ( ! is_array( $wp_registered_settings ) ) {
2424                $wp_registered_settings = array();
2425        }
2426
2427        if ( 'misc' === $option_group ) {
2428                _deprecated_argument(
2429                        __FUNCTION__,
2430                        '3.0.0',
2431                        sprintf(
2432                                /* translators: %s: misc */
2433                                __( 'The "%s" options group has been removed. Use another settings group.' ),
2434                                'misc'
2435                        )
2436                );
2437                $option_group = 'general';
2438        }
2439
2440        if ( 'privacy' === $option_group ) {
2441                _deprecated_argument(
2442                        __FUNCTION__,
2443                        '3.5.0',
2444                        sprintf(
2445                                /* translators: %s: privacy */
2446                                __( 'The "%s" options group has been removed. Use another settings group.' ),
2447                                'privacy'
2448                        )
2449                );
2450                $option_group = 'reading';
2451        }
2452
2453        $new_allowed_options[ $option_group ][] = $option_name;
2454
2455        if ( ! empty( $args['sanitize_callback'] ) ) {
2456                add_filter( "sanitize_option_{$option_name}", $args['sanitize_callback'] );
2457        }
2458        if ( array_key_exists( 'default', $args ) ) {
2459                add_filter( "default_option_{$option_name}", 'filter_default_option', 10, 3 );
2460        }
2461
2462        /**
2463         * Fires immediately before the setting is registered but after its filters are in place.
2464         *
2465         * @since 5.5.0
2466         *
2467         * @param string $option_group Setting group.
2468         * @param string $option_name  Setting name.
2469         * @param array  $args         Array of setting registration arguments.
2470         */
2471        do_action( 'register_setting', $option_group, $option_name, $args );
2472
2473        $wp_registered_settings[ $option_name ] = $args;
2474}
2475
2476/**
2477 * Unregisters a setting.
2478 *
2479 * @since 2.7.0
2480 * @since 4.7.0 `$sanitize_callback` was deprecated. The callback from `register_setting()` is now used instead.
2481 * @since 5.5.0 `$new_whitelist_options` was renamed to `$new_allowed_options`.
2482 *              Please consider writing more inclusive code.
2483 *
2484 * @global array $new_allowed_options
2485 * @global array $wp_registered_settings
2486 *
2487 * @param string   $option_group The settings group name used during registration.
2488 * @param string   $option_name  The name of the option to unregister.
2489 * @param callable $deprecated   Optional. Deprecated.
2490 */
2491function unregister_setting( $option_group, $option_name, $deprecated = '' ) {
2492        global $new_allowed_options, $wp_registered_settings;
2493
2494        /*
2495         * In 5.5.0, the `$new_whitelist_options` global variable was renamed to `$new_allowed_options`.
2496         * Please consider writing more inclusive code.
2497         */
2498        $GLOBALS['new_whitelist_options'] = &$new_allowed_options;
2499
2500        if ( 'misc' === $option_group ) {
2501                _deprecated_argument(
2502                        __FUNCTION__,
2503                        '3.0.0',
2504                        sprintf(
2505                                /* translators: %s: misc */
2506                                __( 'The "%s" options group has been removed. Use another settings group.' ),
2507                                'misc'
2508                        )
2509                );
2510                $option_group = 'general';
2511        }
2512
2513        if ( 'privacy' === $option_group ) {
2514                _deprecated_argument(
2515                        __FUNCTION__,
2516                        '3.5.0',
2517                        sprintf(
2518                                /* translators: %s: privacy */
2519                                __( 'The "%s" options group has been removed. Use another settings group.' ),
2520                                'privacy'
2521                        )
2522                );
2523                $option_group = 'reading';
2524        }
2525
2526        $pos = array_search( $option_name, (array) $new_allowed_options[ $option_group ], true );
2527
2528        if ( false !== $pos ) {
2529                unset( $new_allowed_options[ $option_group ][ $pos ] );
2530        }
2531
2532        if ( '' !== $deprecated ) {
2533                _deprecated_argument(
2534                        __FUNCTION__,
2535                        '4.7.0',
2536                        sprintf(
2537                                /* translators: 1: $sanitize_callback, 2: register_setting() */
2538                                __( '%1$s is deprecated. The callback from %2$s is used instead.' ),
2539                                '<code>$sanitize_callback</code>',
2540                                '<code>register_setting()</code>'
2541                        )
2542                );
2543                remove_filter( "sanitize_option_{$option_name}", $deprecated );
2544        }
2545
2546        if ( isset( $wp_registered_settings[ $option_name ] ) ) {
2547                // Remove the sanitize callback if one was set during registration.
2548                if ( ! empty( $wp_registered_settings[ $option_name ]['sanitize_callback'] ) ) {
2549                        remove_filter( "sanitize_option_{$option_name}", $wp_registered_settings[ $option_name ]['sanitize_callback'] );
2550                }
2551
2552                // Remove the default filter if a default was provided during registration.
2553                if ( array_key_exists( 'default', $wp_registered_settings[ $option_name ] ) ) {
2554                        remove_filter( "default_option_{$option_name}", 'filter_default_option', 10 );
2555                }
2556
2557                /**
2558                 * Fires immediately before the setting is unregistered and after its filters have been removed.
2559                 *
2560                 * @since 5.5.0
2561                 *
2562                 * @param string $option_group Setting group.
2563                 * @param string $option_name  Setting name.
2564                 */
2565                do_action( 'unregister_setting', $option_group, $option_name );
2566
2567                unset( $wp_registered_settings[ $option_name ] );
2568        }
2569}
2570
2571/**
2572 * Retrieves an array of registered settings.
2573 *
2574 * @since 4.7.0
2575 *
2576 * @global array $wp_registered_settings
2577 *
2578 * @return array List of registered settings, keyed by option name.
2579 */
2580function get_registered_settings() {
2581        global $wp_registered_settings;
2582
2583        if ( ! is_array( $wp_registered_settings ) ) {
2584                return array();
2585        }
2586
2587        return $wp_registered_settings;
2588}
2589
2590/**
2591 * Filters the default value for the option.
2592 *
2593 * For settings which register a default setting in `register_setting()`, this
2594 * function is added as a filter to `default_option_{$option}`.
2595 *
2596 * @since 4.7.0
2597 *
2598 * @param mixed  $default_value  Existing default value to return.
2599 * @param string $option         Option name.
2600 * @param bool   $passed_default Was `get_option()` passed a default value?
2601 * @return mixed Filtered default value.
2602 */
2603function filter_default_option( $default_value, $option, $passed_default ) {
2604        if ( $passed_default ) {
2605                return $default_value;
2606        }
2607
2608        $registered = get_registered_settings();
2609        if ( empty( $registered[ $option ] ) ) {
2610                return $default_value;
2611        }
2612
2613        return $registered[ $option ]['default'];
2614}