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