[ Index ]

PHP Cross Reference of WordPress

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  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';


Generated: Thu Nov 21 01:00:03 2024 Cross-referenced by PHPXref 0.7.1