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


Generated : Fri Mar 29 08:20:02 2024 Cross-referenced by PHPXref