[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-customize-setting.php (source)

   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      die( '-1' );
  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 string
  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 string          $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          if ( ! isset( self::$aggregated_multidimensionals[ $this->type ] ) ) {
 252              self::$aggregated_multidimensionals[ $this->type ] = array();
 253          }
 254          if ( ! isset( self::$aggregated_multidimensionals[ $this->type ][ $id_base ] ) ) {
 255              self::$aggregated_multidimensionals[ $this->type ][ $id_base ] = array(
 256                  'previewed_instances'       => array(), // Calling preview() will add the $setting to the array.
 257                  'preview_applied_instances' => array(), // Flags for which settings have had their values applied.
 258                  'root_value'                => $this->get_root_value( array() ), // Root value for initial state, manipulated by preview and update calls.
 259              );
 260          }
 261  
 262          if ( ! empty( $this->id_data['keys'] ) ) {
 263              // Note the preview-applied flag is cleared at priority 9 to ensure it is cleared before a deferred-preview runs.
 264              add_action( "customize_post_value_set_{$this->id}", array( $this, '_clear_aggregated_multidimensional_preview_applied_flag' ), 9 );
 265              $this->is_multidimensional_aggregated = true;
 266          }
 267      }
 268  
 269      /**
 270       * Reset `$aggregated_multidimensionals` static variable.
 271       *
 272       * This is intended only for use by unit tests.
 273       *
 274       * @since 4.5.0
 275       * @ignore
 276       */
 277  	public static function reset_aggregated_multidimensionals() {
 278          self::$aggregated_multidimensionals = array();
 279      }
 280  
 281      /**
 282       * The ID for the current site when the preview() method was called.
 283       *
 284       * @since 4.2.0
 285       * @var int
 286       */
 287      protected $_previewed_blog_id;
 288  
 289      /**
 290       * Return true if the current site is not the same as the previewed site.
 291       *
 292       * @since 4.2.0
 293       *
 294       * @return bool If preview() has been called.
 295       */
 296  	public function is_current_blog_previewed() {
 297          if ( ! isset( $this->_previewed_blog_id ) ) {
 298              return false;
 299          }
 300          return ( get_current_blog_id() === $this->_previewed_blog_id );
 301      }
 302  
 303      /**
 304       * Original non-previewed value stored by the preview method.
 305       *
 306       * @see WP_Customize_Setting::preview()
 307       * @since 4.1.1
 308       * @var mixed
 309       */
 310      protected $_original_value;
 311  
 312      /**
 313       * Add filters to supply the setting's value when accessed.
 314       *
 315       * If the setting already has a pre-existing value and there is no incoming
 316       * post value for the setting, then this method will short-circuit since
 317       * there is no change to preview.
 318       *
 319       * @since 3.4.0
 320       * @since 4.4.0 Added boolean return value.
 321       *
 322       * @return bool False when preview short-circuits due no change needing to be previewed.
 323       */
 324  	public function preview() {
 325          if ( ! isset( $this->_previewed_blog_id ) ) {
 326              $this->_previewed_blog_id = get_current_blog_id();
 327          }
 328  
 329          // Prevent re-previewing an already-previewed setting.
 330          if ( $this->is_previewed ) {
 331              return true;
 332          }
 333  
 334          $id_base                 = $this->id_data['base'];
 335          $is_multidimensional     = ! empty( $this->id_data['keys'] );
 336          $multidimensional_filter = array( $this, '_multidimensional_preview_filter' );
 337  
 338          /*
 339           * Check if the setting has a pre-existing value (an isset check),
 340           * and if doesn't have any incoming post value. If both checks are true,
 341           * then the preview short-circuits because there is nothing that needs
 342           * to be previewed.
 343           */
 344          $undefined     = new stdClass();
 345          $needs_preview = ( $undefined !== $this->post_value( $undefined ) );
 346          $value         = null;
 347  
 348          // Since no post value was defined, check if we have an initial value set.
 349          if ( ! $needs_preview ) {
 350              if ( $this->is_multidimensional_aggregated ) {
 351                  $root  = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
 352                  $value = $this->multidimensional_get( $root, $this->id_data['keys'], $undefined );
 353              } else {
 354                  $default       = $this->default;
 355                  $this->default = $undefined; // Temporarily set default to undefined so we can detect if existing value is set.
 356                  $value         = $this->value();
 357                  $this->default = $default;
 358              }
 359              $needs_preview = ( $undefined === $value ); // Because the default needs to be supplied.
 360          }
 361  
 362          // If the setting does not need previewing now, defer to when it has a value to preview.
 363          if ( ! $needs_preview ) {
 364              if ( ! has_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) ) ) {
 365                  add_action( "customize_post_value_set_{$this->id}", array( $this, 'preview' ) );
 366              }
 367              return false;
 368          }
 369  
 370          switch ( $this->type ) {
 371              case 'theme_mod':
 372                  if ( ! $is_multidimensional ) {
 373                      add_filter( "theme_mod_{$id_base}", array( $this, '_preview_filter' ) );
 374                  } else {
 375                      if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) {
 376                          // Only add this filter once for this ID base.
 377                          add_filter( "theme_mod_{$id_base}", $multidimensional_filter );
 378                      }
 379                      self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this;
 380                  }
 381                  break;
 382              case 'option':
 383                  if ( ! $is_multidimensional ) {
 384                      add_filter( "pre_option_{$id_base}", array( $this, '_preview_filter' ) );
 385                  } else {
 386                      if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) {
 387                          // Only add these filters once for this ID base.
 388                          add_filter( "option_{$id_base}", $multidimensional_filter );
 389                          add_filter( "default_option_{$id_base}", $multidimensional_filter );
 390                      }
 391                      self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this;
 392                  }
 393                  break;
 394              default:
 395                  /**
 396                   * Fires when the WP_Customize_Setting::preview() method is called for settings
 397                   * not handled as theme_mods or options.
 398                   *
 399                   * The dynamic portion of the hook name, `$this->id`, refers to the setting ID.
 400                   *
 401                   * @since 3.4.0
 402                   *
 403                   * @param WP_Customize_Setting $setting WP_Customize_Setting instance.
 404                   */
 405                  do_action( "customize_preview_{$this->id}", $this );
 406  
 407                  /**
 408                   * Fires when the WP_Customize_Setting::preview() method is called for settings
 409                   * not handled as theme_mods or options.
 410                   *
 411                   * The dynamic portion of the hook name, `$this->type`, refers to the setting type.
 412                   *
 413                   * @since 4.1.0
 414                   *
 415                   * @param WP_Customize_Setting $setting WP_Customize_Setting instance.
 416                   */
 417                  do_action( "customize_preview_{$this->type}", $this );
 418          }
 419  
 420          $this->is_previewed = true;
 421  
 422          return true;
 423      }
 424  
 425      /**
 426       * Clear out the previewed-applied flag for a multidimensional-aggregated value whenever its post value is updated.
 427       *
 428       * This ensures that the new value will get sanitized and used the next time
 429       * that `WP_Customize_Setting::_multidimensional_preview_filter()`
 430       * is called for this setting.
 431       *
 432       * @since 4.4.0
 433       *
 434       * @see WP_Customize_Manager::set_post_value()
 435       * @see WP_Customize_Setting::_multidimensional_preview_filter()
 436       */
 437      final public function _clear_aggregated_multidimensional_preview_applied_flag() {
 438          unset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['preview_applied_instances'][ $this->id ] );
 439      }
 440  
 441      /**
 442       * Callback function to filter non-multidimensional theme mods and options.
 443       *
 444       * If switch_to_blog() was called after the preview() method, and the current
 445       * site is now not the same site, then this method does a no-op and returns
 446       * the original value.
 447       *
 448       * @since 3.4.0
 449       *
 450       * @param mixed $original Old value.
 451       * @return mixed New or old value.
 452       */
 453  	public function _preview_filter( $original ) {
 454          if ( ! $this->is_current_blog_previewed() ) {
 455              return $original;
 456          }
 457  
 458          $undefined  = new stdClass(); // Symbol hack.
 459          $post_value = $this->post_value( $undefined );
 460          if ( $undefined !== $post_value ) {
 461              $value = $post_value;
 462          } else {
 463              /*
 464               * Note that we don't use $original here because preview() will
 465               * not add the filter in the first place if it has an initial value
 466               * and there is no post value.
 467               */
 468              $value = $this->default;
 469          }
 470          return $value;
 471      }
 472  
 473      /**
 474       * Callback function to filter multidimensional theme mods and options.
 475       *
 476       * For all multidimensional settings of a given type, the preview filter for
 477       * the first setting previewed will be used to apply the values for the others.
 478       *
 479       * @since 4.4.0
 480       *
 481       * @see WP_Customize_Setting::$aggregated_multidimensionals
 482       * @param mixed $original Original root value.
 483       * @return mixed New or old value.
 484       */
 485  	final public function _multidimensional_preview_filter( $original ) {
 486          if ( ! $this->is_current_blog_previewed() ) {
 487              return $original;
 488          }
 489  
 490          $id_base = $this->id_data['base'];
 491  
 492          // If no settings have been previewed yet (which should not be the case, since $this is), just pass through the original value.
 493          if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) {
 494              return $original;
 495          }
 496  
 497          foreach ( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] as $previewed_setting ) {
 498              // Skip applying previewed value for any settings that have already been applied.
 499              if ( ! empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] ) ) {
 500                  continue;
 501              }
 502  
 503              // Do the replacements of the posted/default sub value into the root value.
 504              $value = $previewed_setting->post_value( $previewed_setting->default );
 505              $root  = self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value'];
 506              $root  = $previewed_setting->multidimensional_replace( $root, $previewed_setting->id_data['keys'], $value );
 507              self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value'] = $root;
 508  
 509              // Mark this setting having been applied so that it will be skipped when the filter is called again.
 510              self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] = true;
 511          }
 512  
 513          return self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
 514      }
 515  
 516      /**
 517       * Checks user capabilities and theme supports, and then saves
 518       * the value of the setting.
 519       *
 520       * @since 3.4.0
 521       *
 522       * @return void|false Void on success, false if cap check fails
 523       *                    or value isn't set or is invalid.
 524       */
 525  	final public function save() {
 526          $value = $this->post_value();
 527  
 528          if ( ! $this->check_capabilities() || ! isset( $value ) ) {
 529              return false;
 530          }
 531  
 532          $id_base = $this->id_data['base'];
 533  
 534          /**
 535           * Fires when the WP_Customize_Setting::save() method is called.
 536           *
 537           * The dynamic portion of the hook name, `$id_base` refers to
 538           * the base slug of the setting name.
 539           *
 540           * @since 3.4.0
 541           *
 542           * @param WP_Customize_Setting $setting WP_Customize_Setting instance.
 543           */
 544          do_action( "customize_save_{$id_base}", $this );
 545  
 546          $this->update( $value );
 547      }
 548  
 549      /**
 550       * Fetch and sanitize the $_POST value for the setting.
 551       *
 552       * During a save request prior to save, post_value() provides the new value while value() does not.
 553       *
 554       * @since 3.4.0
 555       *
 556       * @param mixed $default_value A default value which is used as a fallback. Default null.
 557       * @return mixed The default value on failure, otherwise the sanitized and validated value.
 558       */
 559  	final public function post_value( $default_value = null ) {
 560          return $this->manager->post_value( $this, $default_value );
 561      }
 562  
 563      /**
 564       * Sanitize an input.
 565       *
 566       * @since 3.4.0
 567       *
 568       * @param string|array $value The value to sanitize.
 569       * @return string|array|null|WP_Error Sanitized value, or `null`/`WP_Error` if invalid.
 570       */
 571  	public function sanitize( $value ) {
 572  
 573          /**
 574           * Filters a Customize setting value in un-slashed form.
 575           *
 576           * @since 3.4.0
 577           *
 578           * @param mixed                $value   Value of the setting.
 579           * @param WP_Customize_Setting $setting WP_Customize_Setting instance.
 580           */
 581          return apply_filters( "customize_sanitize_{$this->id}", $value, $this );
 582      }
 583  
 584      /**
 585       * Validates an input.
 586       *
 587       * @since 4.6.0
 588       *
 589       * @see WP_REST_Request::has_valid_params()
 590       *
 591       * @param mixed $value Value to validate.
 592       * @return true|WP_Error True if the input was validated, otherwise WP_Error.
 593       */
 594  	public function validate( $value ) {
 595          if ( is_wp_error( $value ) ) {
 596              return $value;
 597          }
 598          if ( is_null( $value ) ) {
 599              return new WP_Error( 'invalid_value', __( 'Invalid value.' ) );
 600          }
 601  
 602          $validity = new WP_Error();
 603  
 604          /**
 605           * Validates a Customize setting value.
 606           *
 607           * Plugins should amend the `$validity` object via its `WP_Error::add()` method.
 608           *
 609           * The dynamic portion of the hook name, `$this->ID`, refers to the setting ID.
 610           *
 611           * @since 4.6.0
 612           *
 613           * @param WP_Error             $validity Filtered from `true` to `WP_Error` when invalid.
 614           * @param mixed                $value    Value of the setting.
 615           * @param WP_Customize_Setting $setting  WP_Customize_Setting instance.
 616           */
 617          $validity = apply_filters( "customize_validate_{$this->id}", $validity, $value, $this );
 618  
 619          if ( is_wp_error( $validity ) && ! $validity->has_errors() ) {
 620              $validity = true;
 621          }
 622          return $validity;
 623      }
 624  
 625      /**
 626       * Get the root value for a setting, especially for multidimensional ones.
 627       *
 628       * @since 4.4.0
 629       *
 630       * @param mixed $default_value Value to return if root does not exist.
 631       * @return mixed
 632       */
 633  	protected function get_root_value( $default_value = null ) {
 634          $id_base = $this->id_data['base'];
 635          if ( 'option' === $this->type ) {
 636              return get_option( $id_base, $default_value );
 637          } elseif ( 'theme_mod' === $this->type ) {
 638              return get_theme_mod( $id_base, $default_value );
 639          } else {
 640              /*
 641               * Any WP_Customize_Setting subclass implementing aggregate multidimensional
 642               * will need to override this method to obtain the data from the appropriate
 643               * location.
 644               */
 645              return $default_value;
 646          }
 647      }
 648  
 649      /**
 650       * Set the root value for a setting, especially for multidimensional ones.
 651       *
 652       * @since 4.4.0
 653       *
 654       * @param mixed $value Value to set as root of multidimensional setting.
 655       * @return bool Whether the multidimensional root was updated successfully.
 656       */
 657  	protected function set_root_value( $value ) {
 658          $id_base = $this->id_data['base'];
 659          if ( 'option' === $this->type ) {
 660              $autoload = true;
 661              if ( isset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload'] ) ) {
 662                  $autoload = self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload'];
 663              }
 664              return update_option( $id_base, $value, $autoload );
 665          } elseif ( 'theme_mod' === $this->type ) {
 666              set_theme_mod( $id_base, $value );
 667              return true;
 668          } else {
 669              /*
 670               * Any WP_Customize_Setting subclass implementing aggregate multidimensional
 671               * will need to override this method to obtain the data from the appropriate
 672               * location.
 673               */
 674              return false;
 675          }
 676      }
 677  
 678      /**
 679       * Save the value of the setting, using the related API.
 680       *
 681       * @since 3.4.0
 682       *
 683       * @param mixed $value The value to update.
 684       * @return bool The result of saving the value.
 685       */
 686  	protected function update( $value ) {
 687          $id_base = $this->id_data['base'];
 688          if ( 'option' === $this->type || 'theme_mod' === $this->type ) {
 689              if ( ! $this->is_multidimensional_aggregated ) {
 690                  return $this->set_root_value( $value );
 691              } else {
 692                  $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
 693                  $root = $this->multidimensional_replace( $root, $this->id_data['keys'], $value );
 694                  self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'] = $root;
 695                  return $this->set_root_value( $root );
 696              }
 697          } else {
 698              /**
 699               * Fires when the WP_Customize_Setting::update() method is called for settings
 700               * not handled as theme_mods or options.
 701               *
 702               * The dynamic portion of the hook name, `$this->type`, refers to the type of setting.
 703               *
 704               * @since 3.4.0
 705               *
 706               * @param mixed                $value   Value of the setting.
 707               * @param WP_Customize_Setting $setting WP_Customize_Setting instance.
 708               */
 709              do_action( "customize_update_{$this->type}", $value, $this );
 710  
 711              return has_action( "customize_update_{$this->type}" );
 712          }
 713      }
 714  
 715      /**
 716       * Deprecated method.
 717       *
 718       * @since 3.4.0
 719       * @deprecated 4.4.0 Deprecated in favor of update() method.
 720       */
 721  	protected function _update_theme_mod() {
 722          _deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' );
 723      }
 724  
 725      /**
 726       * Deprecated method.
 727       *
 728       * @since 3.4.0
 729       * @deprecated 4.4.0 Deprecated in favor of update() method.
 730       */
 731  	protected function _update_option() {
 732          _deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' );
 733      }
 734  
 735      /**
 736       * Fetch the value of the setting.
 737       *
 738       * @since 3.4.0
 739       *
 740       * @return mixed The value.
 741       */
 742  	public function value() {
 743          $id_base      = $this->id_data['base'];
 744          $is_core_type = ( 'option' === $this->type || 'theme_mod' === $this->type );
 745  
 746          if ( ! $is_core_type && ! $this->is_multidimensional_aggregated ) {
 747  
 748              // Use post value if previewed and a post value is present.
 749              if ( $this->is_previewed ) {
 750                  $value = $this->post_value( null );
 751                  if ( null !== $value ) {
 752                      return $value;
 753                  }
 754              }
 755  
 756              $value = $this->get_root_value( $this->default );
 757  
 758              /**
 759               * Filters a Customize setting value not handled as a theme_mod or option.
 760               *
 761               * The dynamic portion of the hook name, `$id_base`, refers to
 762               * the base slug of the setting name, initialized from `$this->id_data['base']`.
 763               *
 764               * For settings handled as theme_mods or options, see those corresponding
 765               * functions for available hooks.
 766               *
 767               * @since 3.4.0
 768               * @since 4.6.0 Added the `$this` setting instance as the second parameter.
 769               *
 770               * @param mixed                $default_value The setting default value. Default empty.
 771               * @param WP_Customize_Setting $setting       The setting instance.
 772               */
 773              $value = apply_filters( "customize_value_{$id_base}", $value, $this );
 774          } elseif ( $this->is_multidimensional_aggregated ) {
 775              $root_value = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
 776              $value      = $this->multidimensional_get( $root_value, $this->id_data['keys'], $this->default );
 777  
 778              // Ensure that the post value is used if the setting is previewed, since preview filters aren't applying on cached $root_value.
 779              if ( $this->is_previewed ) {
 780                  $value = $this->post_value( $value );
 781              }
 782          } else {
 783              $value = $this->get_root_value( $this->default );
 784          }
 785          return $value;
 786      }
 787  
 788      /**
 789       * Sanitize the setting's value for use in JavaScript.
 790       *
 791       * @since 3.4.0
 792       *
 793       * @return mixed The requested escaped value.
 794       */
 795  	public function js_value() {
 796  
 797          /**
 798           * Filters a Customize setting value for use in JavaScript.
 799           *
 800           * The dynamic portion of the hook name, `$this->id`, refers to the setting ID.
 801           *
 802           * @since 3.4.0
 803           *
 804           * @param mixed                $value   The setting value.
 805           * @param WP_Customize_Setting $setting WP_Customize_Setting instance.
 806           */
 807          $value = apply_filters( "customize_sanitize_js_{$this->id}", $this->value(), $this );
 808  
 809          if ( is_string( $value ) ) {
 810              return html_entity_decode( $value, ENT_QUOTES, 'UTF-8' );
 811          }
 812  
 813          return $value;
 814      }
 815  
 816      /**
 817       * Retrieves the data to export to the client via JSON.
 818       *
 819       * @since 4.6.0
 820       *
 821       * @return array Array of parameters passed to JavaScript.
 822       */
 823  	public function json() {
 824          return array(
 825              'value'     => $this->js_value(),
 826              'transport' => $this->transport,
 827              'dirty'     => $this->dirty,
 828              'type'      => $this->type,
 829          );
 830      }
 831  
 832      /**
 833       * Validate user capabilities whether the theme supports the setting.
 834       *
 835       * @since 3.4.0
 836       *
 837       * @return bool False if theme doesn't support the setting or user can't change setting, otherwise true.
 838       */
 839  	final public function check_capabilities() {
 840          if ( $this->capability && ! current_user_can( $this->capability ) ) {
 841              return false;
 842          }
 843  
 844          if ( $this->theme_supports && ! current_theme_supports( ...(array) $this->theme_supports ) ) {
 845              return false;
 846          }
 847  
 848          return true;
 849      }
 850  
 851      /**
 852       * Multidimensional helper function.
 853       *
 854       * @since 3.4.0
 855       *
 856       * @param array $root
 857       * @param array $keys
 858       * @param bool  $create Default false.
 859       * @return array|void Keys are 'root', 'node', and 'key'.
 860       */
 861  	final protected function multidimensional( &$root, $keys, $create = false ) {
 862          if ( $create && empty( $root ) ) {
 863              $root = array();
 864          }
 865  
 866          if ( ! isset( $root ) || empty( $keys ) ) {
 867              return;
 868          }
 869  
 870          $last = array_pop( $keys );
 871          $node = &$root;
 872  
 873          foreach ( $keys as $key ) {
 874              if ( $create && ! isset( $node[ $key ] ) ) {
 875                  $node[ $key ] = array();
 876              }
 877  
 878              if ( ! is_array( $node ) || ! isset( $node[ $key ] ) ) {
 879                  return;
 880              }
 881  
 882              $node = &$node[ $key ];
 883          }
 884  
 885          if ( $create ) {
 886              if ( ! is_array( $node ) ) {
 887                  // Account for an array overriding a string or object value.
 888                  $node = array();
 889              }
 890              if ( ! isset( $node[ $last ] ) ) {
 891                  $node[ $last ] = array();
 892              }
 893          }
 894  
 895          if ( ! isset( $node[ $last ] ) ) {
 896              return;
 897          }
 898  
 899          return array(
 900              'root' => &$root,
 901              'node' => &$node,
 902              'key'  => $last,
 903          );
 904      }
 905  
 906      /**
 907       * Will attempt to replace a specific value in a multidimensional array.
 908       *
 909       * @since 3.4.0
 910       *
 911       * @param array $root
 912       * @param array $keys
 913       * @param mixed $value The value to update.
 914       * @return mixed
 915       */
 916  	final protected function multidimensional_replace( $root, $keys, $value ) {
 917          if ( ! isset( $value ) ) {
 918              return $root;
 919          } elseif ( empty( $keys ) ) { // If there are no keys, we're replacing the root.
 920              return $value;
 921          }
 922  
 923          $result = $this->multidimensional( $root, $keys, true );
 924  
 925          if ( isset( $result ) ) {
 926              $result['node'][ $result['key'] ] = $value;
 927          }
 928  
 929          return $root;
 930      }
 931  
 932      /**
 933       * Will attempt to fetch a specific value from a multidimensional array.
 934       *
 935       * @since 3.4.0
 936       *
 937       * @param array $root
 938       * @param array $keys
 939       * @param mixed $default_value A default value which is used as a fallback. Default null.
 940       * @return mixed The requested value or the default value.
 941       */
 942  	final protected function multidimensional_get( $root, $keys, $default_value = null ) {
 943          if ( empty( $keys ) ) { // If there are no keys, test the root.
 944              return isset( $root ) ? $root : $default_value;
 945          }
 946  
 947          $result = $this->multidimensional( $root, $keys );
 948          return isset( $result ) ? $result['node'][ $result['key'] ] : $default_value;
 949      }
 950  
 951      /**
 952       * Will attempt to check if a specific value in a multidimensional array is set.
 953       *
 954       * @since 3.4.0
 955       *
 956       * @param array $root
 957       * @param array $keys
 958       * @return bool True if value is set, false if not.
 959       */
 960  	final protected function multidimensional_isset( $root, $keys ) {
 961          $result = $this->multidimensional_get( $root, $keys );
 962          return isset( $result );
 963      }
 964  }
 965  
 966  /**
 967   * WP_Customize_Filter_Setting class.
 968   */
 969  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php';
 970  
 971  /**
 972   * WP_Customize_Header_Image_Setting class.
 973   */
 974  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php';
 975  
 976  /**
 977   * WP_Customize_Background_Image_Setting class.
 978   */
 979  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php';
 980  
 981  /**
 982   * WP_Customize_Nav_Menu_Item_Setting class.
 983   */
 984  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php';
 985  
 986  /**
 987   * WP_Customize_Nav_Menu_Setting class.
 988   */
 989  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php';


Generated : Thu Jan 30 08:20:01 2025 Cross-referenced by PHPXref