| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress Customize Setting classes 4 * 5 * @package WordPress 6 * @subpackage Customize 7 * @since 3.4.0 8 */ 9 10 // Don't load directly. 11 if ( ! defined( 'ABSPATH' ) ) { 12 exit; 13 } 14 15 /** 16 * Customize Setting class. 17 * 18 * Handles saving and sanitizing of settings. 19 * 20 * @since 3.4.0 21 * 22 * @see WP_Customize_Manager 23 * @link https://developer.wordpress.org/themes/customize-api 24 */ 25 #[AllowDynamicProperties] 26 class WP_Customize_Setting { 27 /** 28 * Customizer bootstrap instance. 29 * 30 * @since 3.4.0 31 * @var WP_Customize_Manager 32 */ 33 public $manager; 34 35 /** 36 * Unique string identifier for the setting. 37 * 38 * @since 3.4.0 39 * @var string 40 */ 41 public $id; 42 43 /** 44 * Type of customize settings. 45 * 46 * @since 3.4.0 47 * @var string 48 */ 49 public $type = 'theme_mod'; 50 51 /** 52 * Capability required to edit this setting. 53 * 54 * @since 3.4.0 55 * @var string|array 56 */ 57 public $capability = 'edit_theme_options'; 58 59 /** 60 * Theme features required to support the setting. 61 * 62 * @since 3.4.0 63 * @var string|string[] 64 */ 65 public $theme_supports = ''; 66 67 /** 68 * The default value for the setting. 69 * 70 * @since 3.4.0 71 * @var mixed 72 */ 73 public $default = ''; 74 75 /** 76 * Options for rendering the live preview of changes in Customizer. 77 * 78 * Set this value to 'postMessage' to enable a custom JavaScript handler to render changes to this setting 79 * as opposed to reloading the whole page. 80 * 81 * @since 3.4.0 82 * @var string 83 */ 84 public $transport = 'refresh'; 85 86 /** 87 * Server-side validation callback for the setting's value. 88 * 89 * @since 4.6.0 90 * @var callable 91 */ 92 public $validate_callback = ''; 93 94 /** 95 * Callback to filter a Customize setting value in un-slashed form. 96 * 97 * @since 3.4.0 98 * @var callable 99 */ 100 public $sanitize_callback = ''; 101 102 /** 103 * Callback to convert a Customize PHP setting value to a value that is JSON serializable. 104 * 105 * @since 3.4.0 106 * @var callable 107 */ 108 public $sanitize_js_callback = ''; 109 110 /** 111 * Whether or not the setting is initially dirty when created. 112 * 113 * This is used to ensure that a setting will be sent from the pane to the 114 * preview when loading the Customizer. Normally a setting only is synced to 115 * the preview if it has been changed. This allows the setting to be sent 116 * from the start. 117 * 118 * @since 4.2.0 119 * @var bool 120 */ 121 public $dirty = false; 122 123 /** 124 * ID Data. 125 * 126 * @since 3.4.0 127 * @var array 128 */ 129 protected $id_data = array(); 130 131 /** 132 * Whether or not preview() was called. 133 * 134 * @since 4.4.0 135 * @var bool 136 */ 137 protected $is_previewed = false; 138 139 /** 140 * Cache of multidimensional values to improve performance. 141 * 142 * @since 4.4.0 143 * @var array 144 */ 145 protected static $aggregated_multidimensionals = array(); 146 147 /** 148 * Whether the multidimensional setting is aggregated. 149 * 150 * @since 4.4.0 151 * @var bool 152 */ 153 protected $is_multidimensional_aggregated = false; 154 155 /** 156 * Constructor. 157 * 158 * Any supplied $args override class property defaults. 159 * 160 * @since 3.4.0 161 * 162 * @param WP_Customize_Manager $manager Customizer bootstrap instance. 163 * @param string $id A specific ID of the setting. 164 * Can be a theme mod or option name. 165 * @param array $args { 166 * Optional. Array of properties for the new Setting object. Default empty array. 167 * 168 * @type string $type Type of the setting. Default 'theme_mod'. 169 * @type string $capability Capability required for the setting. Default 'edit_theme_options' 170 * @type string|string[] $theme_supports Theme features required to support the panel. Default is none. 171 * @type mixed $default Default value for the setting. Default is empty string. 172 * @type string $transport Options for rendering the live preview of changes in Customizer. 173 * Using 'refresh' makes the change visible by reloading the whole preview. 174 * Using 'postMessage' allows a custom JavaScript to handle live changes. 175 * Default is 'refresh'. 176 * @type callable $validate_callback Server-side validation callback for the setting's value. 177 * @type callable $sanitize_callback Callback to filter a Customize setting value in un-slashed form. 178 * @type callable $sanitize_js_callback Callback to convert a Customize PHP setting value to a value that is 179 * JSON serializable. 180 * @type bool $dirty Whether or not the setting is initially dirty when created. 181 * } 182 */ 183 public function __construct( $manager, $id, $args = array() ) { 184 $keys = array_keys( get_object_vars( $this ) ); 185 foreach ( $keys as $key ) { 186 if ( isset( $args[ $key ] ) ) { 187 $this->$key = $args[ $key ]; 188 } 189 } 190 191 $this->manager = $manager; 192 $this->id = $id; 193 194 // Parse the ID for array keys. 195 $this->id_data['keys'] = preg_split( '/\[/', str_replace( ']', '', $this->id ) ); 196 $this->id_data['base'] = array_shift( $this->id_data['keys'] ); 197 198 // Rebuild the ID. 199 $this->id = $this->id_data['base']; 200 if ( ! empty( $this->id_data['keys'] ) ) { 201 $this->id .= '[' . implode( '][', $this->id_data['keys'] ) . ']'; 202 } 203 204 if ( $this->validate_callback ) { 205 add_filter( "customize_validate_{$this->id}", $this->validate_callback, 10, 3 ); 206 } 207 if ( $this->sanitize_callback ) { 208 add_filter( "customize_sanitize_{$this->id}", $this->sanitize_callback, 10, 2 ); 209 } 210 if ( $this->sanitize_js_callback ) { 211 add_filter( "customize_sanitize_js_{$this->id}", $this->sanitize_js_callback, 10, 2 ); 212 } 213 214 if ( 'option' === $this->type || 'theme_mod' === $this->type ) { 215 // Other setting types can opt-in to aggregate multidimensional explicitly. 216 $this->aggregate_multidimensional(); 217 218 // Allow option settings to indicate whether they should be autoloaded. 219 if ( 'option' === $this->type && isset( $args['autoload'] ) ) { 220 self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload'] = $args['autoload']; 221 } 222 } 223 } 224 225 /** 226 * Get parsed ID data for multidimensional setting. 227 * 228 * @since 4.4.0 229 * 230 * @return array { 231 * ID data for multidimensional setting. 232 * 233 * @type string $base ID base 234 * @type array $keys Keys for multidimensional array. 235 * } 236 */ 237 final public function id_data() { 238 return $this->id_data; 239 } 240 241 /** 242 * Set up the setting for aggregated multidimensional values. 243 * 244 * When a multidimensional setting gets aggregated, all of its preview and update 245 * calls get combined into one call, greatly improving performance. 246 * 247 * @since 4.4.0 248 */ 249 protected function aggregate_multidimensional() { 250 $id_base = $this->id_data['base']; 251 self::$aggregated_multidimensionals[ $this->type ] ??= array(); 252 if ( ! isset( self::$aggregated_multidimensionals[ $this->type ][ $id_base ] ) ) { 253 self::$aggregated_multidimensionals[ $this->type ][ $id_base ] = array( 254 'previewed_instances' => array(), // Calling preview() will add the $setting to the array. 255 'preview_applied_instances' => array(), // Flags for which settings have had their values applied. 256 'root_value' => $this->get_root_value( array() ), // Root value for initial state, manipulated by preview and update calls. 257 ); 258 } 259 260 if ( ! empty( $this->id_data['keys'] ) ) { 261 // Note the preview-applied flag is cleared at priority 9 to ensure it is cleared before a deferred-preview runs. 262 add_action( "customize_post_value_set_{$this->id}", array( $this, '_clear_aggregated_multidimensional_preview_applied_flag' ), 9 ); 263 $this->is_multidimensional_aggregated = true; 264 } 265 } 266 267 /** 268 * Reset `$aggregated_multidimensionals` static variable. 269 * 270 * This is intended only for use by unit tests. 271 * 272 * @since 4.5.0 273 * @ignore 274 */ 275 public static function reset_aggregated_multidimensionals() { 276 self::$aggregated_multidimensionals = array(); 277 } 278 279 /** 280 * The ID for the current site when the preview() method was called. 281 * 282 * @since 4.2.0 283 * @var int 284 */ 285 protected $_previewed_blog_id; 286 287 /** 288 * Return true if the current site is not the same as the previewed site. 289 * 290 * @since 4.2.0 291 * 292 * @return bool If preview() has been called. 293 */ 294 public function is_current_blog_previewed() { 295 if ( ! isset( $this->_previewed_blog_id ) ) { 296 return false; 297 } 298 return ( get_current_blog_id() === $this->_previewed_blog_id ); 299 } 300 301 /** 302 * Original non-previewed value stored by the preview method. 303 * 304 * @see WP_Customize_Setting::preview() 305 * @since 4.1.1 306 * @var mixed 307 */ 308 protected $_original_value; 309 310 /** 311 * Add filters to supply the setting's value when accessed. 312 * 313 * If the setting already has a pre-existing value and there is no incoming 314 * post value for the setting, then this method will short-circuit since 315 * there is no change to preview. 316 * 317 * @since 3.4.0 318 * @since 4.4.0 Added boolean return value. 319 * 320 * @return bool False when preview short-circuits due no change needing to be previewed. 321 */ 322 public function preview() { 323 if ( ! isset( $this->_previewed_blog_id ) ) { 324 $this->_previewed_blog_id = get_current_blog_id(); 325 } 326 327 // Prevent re-previewing an already-previewed setting. 328 if ( $this->is_previewed ) { 329 return true; 330 } 331 332 $id_base = $this->id_data['base']; 333 $is_multidimensional = ! empty( $this->id_data['keys'] ); 334 $multidimensional_filter = array( $this, '_multidimensional_preview_filter' ); 335 336 /* 337 * Check if the setting has a pre-existing value (an isset check), 338 * and if doesn't have any incoming post value. If both checks are true, 339 * then the preview short-circuits because there is nothing that needs 340 * to be previewed. 341 */ 342 $undefined = new stdClass(); 343 $needs_preview = ( $undefined !== $this->post_value( $undefined ) ); 344 $value = null; 345 346 // Since no post value was defined, check if we have an initial value set. 347 if ( ! $needs_preview ) { 348 if ( $this->is_multidimensional_aggregated ) { 349 $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; 350 $value = $this->multidimensional_get( $root, $this->id_data['keys'], $undefined ); 351 } else { 352 $default = $this->default; 353 $this->default = $undefined; // Temporarily set default to undefined so we can detect if existing value is set. 354 $value = $this->value(); 355 $this->default = $default; 356 } 357 $needs_preview = ( $undefined === $value ); // Because the default needs to be supplied. 358 } 359 360 // If the setting does not need previewing now, defer to when it has a value to preview. 361 if ( ! $needs_preview ) { 362 if ( ! has_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) ) ) { 363 add_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) ); 364 } 365 return false; 366 } 367 368 switch ( $this->type ) { 369 case 'theme_mod': 370 if ( ! $is_multidimensional ) { 371 add_filter( "theme_mod_{$id_base}", array( $this, '_preview_filter' ) ); 372 } else { 373 if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) { 374 // Only add this filter once for this ID base. 375 add_filter( "theme_mod_{$id_base}", $multidimensional_filter ); 376 } 377 self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this; 378 } 379 break; 380 case 'option': 381 if ( ! $is_multidimensional ) { 382 add_filter( "pre_option_{$id_base}", array( $this, '_preview_filter' ) ); 383 } else { 384 if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) { 385 // Only add these filters once for this ID base. 386 add_filter( "option_{$id_base}", $multidimensional_filter ); 387 add_filter( "default_option_{$id_base}", $multidimensional_filter ); 388 } 389 self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this; 390 } 391 break; 392 default: 393 /** 394 * Fires when the WP_Customize_Setting::preview() method is called for settings 395 * not handled as theme_mods or options. 396 * 397 * The dynamic portion of the hook name, `$this->id`, refers to the setting ID. 398 * 399 * @since 3.4.0 400 * 401 * @param WP_Customize_Setting $setting WP_Customize_Setting instance. 402 */ 403 do_action( "customize_preview_{$this->id}", $this ); 404 405 /** 406 * Fires when the WP_Customize_Setting::preview() method is called for settings 407 * not handled as theme_mods or options. 408 * 409 * The dynamic portion of the hook name, `$this->type`, refers to the setting type. 410 * 411 * @since 4.1.0 412 * 413 * @param WP_Customize_Setting $setting WP_Customize_Setting instance. 414 */ 415 do_action( "customize_preview_{$this->type}", $this ); 416 } 417 418 $this->is_previewed = true; 419 420 return true; 421 } 422 423 /** 424 * Clear out the previewed-applied flag for a multidimensional-aggregated value whenever its post value is updated. 425 * 426 * This ensures that the new value will get sanitized and used the next time 427 * that `WP_Customize_Setting::_multidimensional_preview_filter()` 428 * is called for this setting. 429 * 430 * @since 4.4.0 431 * 432 * @see WP_Customize_Manager::set_post_value() 433 * @see WP_Customize_Setting::_multidimensional_preview_filter() 434 */ 435 final public function _clear_aggregated_multidimensional_preview_applied_flag() { 436 unset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['preview_applied_instances'][ $this->id ] ); 437 } 438 439 /** 440 * Callback function to filter non-multidimensional theme mods and options. 441 * 442 * If switch_to_blog() was called after the preview() method, and the current 443 * site is now not the same site, then this method does a no-op and returns 444 * the original value. 445 * 446 * @since 3.4.0 447 * 448 * @param mixed $original Old value. 449 * @return mixed New or old value. 450 */ 451 public function _preview_filter( $original ) { 452 if ( ! $this->is_current_blog_previewed() ) { 453 return $original; 454 } 455 456 $undefined = new stdClass(); // Symbol hack. 457 $post_value = $this->post_value( $undefined ); 458 if ( $undefined !== $post_value ) { 459 $value = $post_value; 460 } else { 461 /* 462 * Note that we don't use $original here because preview() will 463 * not add the filter in the first place if it has an initial value 464 * and there is no post value. 465 */ 466 $value = $this->default; 467 } 468 return $value; 469 } 470 471 /** 472 * Callback function to filter multidimensional theme mods and options. 473 * 474 * For all multidimensional settings of a given type, the preview filter for 475 * the first setting previewed will be used to apply the values for the others. 476 * 477 * @since 4.4.0 478 * 479 * @see WP_Customize_Setting::$aggregated_multidimensionals 480 * @param mixed $original Original root value. 481 * @return mixed New or old value. 482 */ 483 final public function _multidimensional_preview_filter( $original ) { 484 if ( ! $this->is_current_blog_previewed() ) { 485 return $original; 486 } 487 488 $id_base = $this->id_data['base']; 489 490 // If no settings have been previewed yet (which should not be the case, since $this is), just pass through the original value. 491 if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) { 492 return $original; 493 } 494 495 foreach ( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] as $previewed_setting ) { 496 // Skip applying previewed value for any settings that have already been applied. 497 if ( ! empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] ) ) { 498 continue; 499 } 500 501 // Do the replacements of the posted/default sub value into the root value. 502 $value = $previewed_setting->post_value( $previewed_setting->default ); 503 $root = self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value']; 504 $root = $previewed_setting->multidimensional_replace( $root, $previewed_setting->id_data['keys'], $value ); 505 self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value'] = $root; 506 507 // Mark this setting having been applied so that it will be skipped when the filter is called again. 508 self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] = true; 509 } 510 511 return self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; 512 } 513 514 /** 515 * Checks user capabilities and theme supports, and then saves 516 * the value of the setting. 517 * 518 * @since 3.4.0 519 * 520 * @return void|false Void on success, false if cap check fails 521 * or value isn't set or is invalid. 522 */ 523 final public function save() { 524 $value = $this->post_value(); 525 526 if ( ! $this->check_capabilities() || ! isset( $value ) ) { 527 return false; 528 } 529 530 $id_base = $this->id_data['base']; 531 532 /** 533 * Fires when the WP_Customize_Setting::save() method is called. 534 * 535 * The dynamic portion of the hook name, `$id_base` refers to 536 * the base slug of the setting name. 537 * 538 * @since 3.4.0 539 * 540 * @param WP_Customize_Setting $setting WP_Customize_Setting instance. 541 */ 542 do_action( "customize_save_{$id_base}", $this ); 543 544 $this->update( $value ); 545 } 546 547 /** 548 * Fetch and sanitize the $_POST value for the setting. 549 * 550 * During a save request prior to save, post_value() provides the new value while value() does not. 551 * 552 * @since 3.4.0 553 * 554 * @param mixed $default_value A default value which is used as a fallback. Default null. 555 * @return mixed The default value on failure, otherwise the sanitized and validated value. 556 */ 557 final public function post_value( $default_value = null ) { 558 return $this->manager->post_value( $this, $default_value ); 559 } 560 561 /** 562 * Sanitize an input. 563 * 564 * @since 3.4.0 565 * 566 * @param string|array $value The value to sanitize. 567 * @return string|array|null|WP_Error Sanitized value, or `null`/`WP_Error` if invalid. 568 */ 569 public function sanitize( $value ) { 570 571 /** 572 * Filters a Customize setting value in un-slashed form. 573 * 574 * @since 3.4.0 575 * 576 * @param mixed $value Value of the setting. 577 * @param WP_Customize_Setting $setting WP_Customize_Setting instance. 578 */ 579 return apply_filters( "customize_sanitize_{$this->id}", $value, $this ); 580 } 581 582 /** 583 * Validates an input. 584 * 585 * @since 4.6.0 586 * 587 * @see WP_REST_Request::has_valid_params() 588 * 589 * @param mixed $value Value to validate. 590 * @return true|WP_Error True if the input was validated, otherwise WP_Error. 591 */ 592 public function validate( $value ) { 593 if ( is_wp_error( $value ) ) { 594 return $value; 595 } 596 if ( is_null( $value ) ) { 597 return new WP_Error( 'invalid_value', __( 'Invalid value.' ) ); 598 } 599 600 $validity = new WP_Error(); 601 602 /** 603 * Validates a Customize setting value. 604 * 605 * Plugins should amend the `$validity` object via its `WP_Error::add()` method. 606 * 607 * The dynamic portion of the hook name, `$this->ID`, refers to the setting ID. 608 * 609 * @since 4.6.0 610 * 611 * @param WP_Error $validity Filtered from `true` to `WP_Error` when invalid. 612 * @param mixed $value Value of the setting. 613 * @param WP_Customize_Setting $setting WP_Customize_Setting instance. 614 */ 615 $validity = apply_filters( "customize_validate_{$this->id}", $validity, $value, $this ); 616 617 if ( is_wp_error( $validity ) && ! $validity->has_errors() ) { 618 $validity = true; 619 } 620 return $validity; 621 } 622 623 /** 624 * Get the root value for a setting, especially for multidimensional ones. 625 * 626 * @since 4.4.0 627 * 628 * @param mixed $default_value Value to return if root does not exist. 629 * @return mixed 630 */ 631 protected function get_root_value( $default_value = null ) { 632 $id_base = $this->id_data['base']; 633 if ( 'option' === $this->type ) { 634 return get_option( $id_base, $default_value ); 635 } elseif ( 'theme_mod' === $this->type ) { 636 return get_theme_mod( $id_base, $default_value ); 637 } else { 638 /* 639 * Any WP_Customize_Setting subclass implementing aggregate multidimensional 640 * will need to override this method to obtain the data from the appropriate 641 * location. 642 */ 643 return $default_value; 644 } 645 } 646 647 /** 648 * Set the root value for a setting, especially for multidimensional ones. 649 * 650 * @since 4.4.0 651 * 652 * @param mixed $value Value to set as root of multidimensional setting. 653 * @return bool Whether the multidimensional root was updated successfully. 654 */ 655 protected function set_root_value( $value ) { 656 $id_base = $this->id_data['base']; 657 if ( 'option' === $this->type ) { 658 $autoload = true; 659 if ( isset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload'] ) ) { 660 $autoload = self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload']; 661 } 662 return update_option( $id_base, $value, $autoload ); 663 } elseif ( 'theme_mod' === $this->type ) { 664 set_theme_mod( $id_base, $value ); 665 return true; 666 } else { 667 /* 668 * Any WP_Customize_Setting subclass implementing aggregate multidimensional 669 * will need to override this method to obtain the data from the appropriate 670 * location. 671 */ 672 return false; 673 } 674 } 675 676 /** 677 * Save the value of the setting, using the related API. 678 * 679 * @since 3.4.0 680 * 681 * @param mixed $value The value to update. 682 * @return bool The result of saving the value. 683 */ 684 protected function update( $value ) { 685 $id_base = $this->id_data['base']; 686 if ( 'option' === $this->type || 'theme_mod' === $this->type ) { 687 if ( ! $this->is_multidimensional_aggregated ) { 688 return $this->set_root_value( $value ); 689 } else { 690 $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; 691 $root = $this->multidimensional_replace( $root, $this->id_data['keys'], $value ); 692 self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'] = $root; 693 return $this->set_root_value( $root ); 694 } 695 } else { 696 /** 697 * Fires when the WP_Customize_Setting::update() method is called for settings 698 * not handled as theme_mods or options. 699 * 700 * The dynamic portion of the hook name, `$this->type`, refers to the type of setting. 701 * 702 * @since 3.4.0 703 * 704 * @param mixed $value Value of the setting. 705 * @param WP_Customize_Setting $setting WP_Customize_Setting instance. 706 */ 707 do_action( "customize_update_{$this->type}", $value, $this ); 708 709 return (bool) has_action( "customize_update_{$this->type}" ); 710 } 711 } 712 713 /** 714 * Deprecated method. 715 * 716 * @since 3.4.0 717 * @deprecated 4.4.0 Deprecated in favor of update() method. 718 */ 719 protected function _update_theme_mod() { 720 _deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' ); 721 } 722 723 /** 724 * Deprecated method. 725 * 726 * @since 3.4.0 727 * @deprecated 4.4.0 Deprecated in favor of update() method. 728 */ 729 protected function _update_option() { 730 _deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' ); 731 } 732 733 /** 734 * Fetch the value of the setting. 735 * 736 * @since 3.4.0 737 * 738 * @return mixed The value. 739 */ 740 public function value() { 741 $id_base = $this->id_data['base']; 742 $is_core_type = ( 'option' === $this->type || 'theme_mod' === $this->type ); 743 744 if ( ! $is_core_type && ! $this->is_multidimensional_aggregated ) { 745 746 // Use post value if previewed and a post value is present. 747 if ( $this->is_previewed ) { 748 $value = $this->post_value( null ); 749 if ( null !== $value ) { 750 return $value; 751 } 752 } 753 754 $value = $this->get_root_value( $this->default ); 755 756 /** 757 * Filters a Customize setting value not handled as a theme_mod or option. 758 * 759 * The dynamic portion of the hook name, `$id_base`, refers to 760 * the base slug of the setting name, initialized from `$this->id_data['base']`. 761 * 762 * For settings handled as theme_mods or options, see those corresponding 763 * functions for available hooks. 764 * 765 * @since 3.4.0 766 * @since 4.6.0 Added the `$setting` instance as the second parameter. 767 * 768 * @param mixed $default_value The setting default value. Default empty. 769 * @param WP_Customize_Setting $setting The setting instance. 770 */ 771 $value = apply_filters( "customize_value_{$id_base}", $value, $this ); 772 } elseif ( $this->is_multidimensional_aggregated ) { 773 $root_value = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value']; 774 $value = $this->multidimensional_get( $root_value, $this->id_data['keys'], $this->default ); 775 776 // Ensure that the post value is used if the setting is previewed, since preview filters aren't applying on cached $root_value. 777 if ( $this->is_previewed ) { 778 $value = $this->post_value( $value ); 779 } 780 } else { 781 $value = $this->get_root_value( $this->default ); 782 } 783 return $value; 784 } 785 786 /** 787 * Sanitize the setting's value for use in JavaScript. 788 * 789 * @since 3.4.0 790 * 791 * @return mixed The requested escaped value. 792 */ 793 public function js_value() { 794 795 /** 796 * Filters a Customize setting value for use in JavaScript. 797 * 798 * The dynamic portion of the hook name, `$this->id`, refers to the setting ID. 799 * 800 * @since 3.4.0 801 * 802 * @param mixed $value The setting value. 803 * @param WP_Customize_Setting $setting WP_Customize_Setting instance. 804 */ 805 $value = apply_filters( "customize_sanitize_js_{$this->id}", $this->value(), $this ); 806 807 if ( is_string( $value ) ) { 808 return html_entity_decode( $value, ENT_QUOTES, 'UTF-8' ); 809 } 810 811 return $value; 812 } 813 814 /** 815 * Retrieves the data to export to the client via JSON. 816 * 817 * @since 4.6.0 818 * 819 * @return array Array of parameters passed to JavaScript. 820 */ 821 public function json() { 822 return array( 823 'value' => $this->js_value(), 824 'transport' => $this->transport, 825 'dirty' => $this->dirty, 826 'type' => $this->type, 827 ); 828 } 829 830 /** 831 * Validate user capabilities whether the theme supports the setting. 832 * 833 * @since 3.4.0 834 * 835 * @return bool False if theme doesn't support the setting or user can't change setting, otherwise true. 836 */ 837 final public function check_capabilities() { 838 if ( $this->capability && ! current_user_can( $this->capability ) ) { 839 return false; 840 } 841 842 if ( $this->theme_supports && ! current_theme_supports( ...(array) $this->theme_supports ) ) { 843 return false; 844 } 845 846 return true; 847 } 848 849 /** 850 * Multidimensional helper function. 851 * 852 * @since 3.4.0 853 * 854 * @param array $root 855 * @param array $keys 856 * @param bool $create Default false. 857 * @return array|null Keys are 'root', 'node', and 'key'. 858 */ 859 final protected function multidimensional( &$root, $keys, $create = false ) { 860 if ( $create && empty( $root ) ) { 861 $root = array(); 862 } 863 864 if ( ! isset( $root ) || empty( $keys ) ) { 865 return null; 866 } 867 868 $last = array_pop( $keys ); 869 $node = &$root; 870 871 foreach ( $keys as $key ) { 872 if ( $create && ! isset( $node[ $key ] ) ) { 873 $node[ $key ] = array(); 874 } 875 876 if ( ! is_array( $node ) || ! isset( $node[ $key ] ) ) { 877 return null; 878 } 879 880 $node = &$node[ $key ]; 881 } 882 883 if ( $create ) { 884 if ( ! is_array( $node ) ) { 885 // Account for an array overriding a string or object value. 886 $node = array(); 887 } 888 $node[ $last ] ??= array(); 889 } 890 891 if ( ! isset( $node[ $last ] ) ) { 892 return null; 893 } 894 895 return array( 896 'root' => &$root, 897 'node' => &$node, 898 'key' => $last, 899 ); 900 } 901 902 /** 903 * Will attempt to replace a specific value in a multidimensional array. 904 * 905 * @since 3.4.0 906 * 907 * @param array $root 908 * @param array $keys 909 * @param mixed $value The value to update. 910 * @return mixed 911 */ 912 final protected function multidimensional_replace( $root, $keys, $value ) { 913 if ( ! isset( $value ) ) { 914 return $root; 915 } elseif ( empty( $keys ) ) { // If there are no keys, we're replacing the root. 916 return $value; 917 } 918 919 $result = $this->multidimensional( $root, $keys, true ); 920 921 if ( isset( $result ) ) { 922 $result['node'][ $result['key'] ] = $value; 923 } 924 925 return $root; 926 } 927 928 /** 929 * Will attempt to fetch a specific value from a multidimensional array. 930 * 931 * @since 3.4.0 932 * 933 * @param array $root 934 * @param array $keys 935 * @param mixed $default_value A default value which is used as a fallback. Default null. 936 * @return mixed The requested value or the default value. 937 */ 938 final protected function multidimensional_get( $root, $keys, $default_value = null ) { 939 if ( empty( $keys ) ) { // If there are no keys, test the root. 940 return $root ?? $default_value; 941 } 942 943 $result = $this->multidimensional( $root, $keys ); 944 return isset( $result ) ? $result['node'][ $result['key'] ] : $default_value; 945 } 946 947 /** 948 * Will attempt to check if a specific value in a multidimensional array is set. 949 * 950 * @since 3.4.0 951 * 952 * @param array $root 953 * @param array $keys 954 * @return bool True if value is set, false if not. 955 */ 956 final protected function multidimensional_isset( $root, $keys ) { 957 $result = $this->multidimensional_get( $root, $keys ); 958 return isset( $result ); 959 } 960 } 961 962 /** 963 * WP_Customize_Filter_Setting class. 964 */ 965 require_once ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php'; 966 967 /** 968 * WP_Customize_Header_Image_Setting class. 969 */ 970 require_once ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php'; 971 972 /** 973 * WP_Customize_Background_Image_Setting class. 974 */ 975 require_once ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php'; 976 977 /** 978 * WP_Customize_Nav_Menu_Item_Setting class. 979 */ 980 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php'; 981 982 /** 983 * WP_Customize_Nav_Menu_Setting class. 984 */ 985 require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php';
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Mon Nov 9 08:20:01 2026 | Cross-referenced by PHPXref |