[ 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   * @link https://developer.wordpress.org/themes/customize-api
  17   *
  18   * @see WP_Customize_Manager
  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 string
 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  	static public 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 $this 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 $this 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 False if cap check fails or value isn't set or is invalid.
 517       */
 518  	final public function save() {
 519          $value = $this->post_value();
 520  
 521          if ( ! $this->check_capabilities() || ! isset( $value ) ) {
 522              return false;
 523          }
 524  
 525          $id_base = $this->id_data['base'];
 526  
 527          /**
 528           * Fires when the WP_Customize_Setting::save() method is called.
 529           *
 530           * The dynamic portion of the hook name, `$id_base` refers to
 531           * the base slug of the setting name.
 532           *
 533           * @since 3.4.0
 534           *
 535           * @param WP_Customize_Setting $this WP_Customize_Setting instance.
 536           */
 537          do_action( "customize_save_{$id_base}", $this );
 538  
 539          $this->update( $value );
 540      }
 541  
 542      /**
 543       * Fetch and sanitize the $_POST value for the setting.
 544       *
 545       * During a save request prior to save, post_value() provides the new value while value() does not.
 546       *
 547       * @since 3.4.0
 548       *
 549       * @param mixed $default A default value which is used as a fallback. Default is null.
 550       * @return mixed The default value on failure, otherwise the sanitized and validated value.
 551       */
 552  	final public function post_value( $default = null ) {
 553          return $this->manager->post_value( $this, $default );
 554      }
 555  
 556      /**
 557       * Sanitize an input.
 558       *
 559       * @since 3.4.0
 560       *
 561       * @param string|array $value    The value to sanitize.
 562       * @return string|array|null|WP_Error Sanitized value, or `null`/`WP_Error` if invalid.
 563       */
 564  	public function sanitize( $value ) {
 565  
 566          /**
 567           * Filters a Customize setting value in un-slashed form.
 568           *
 569           * @since 3.4.0
 570           *
 571           * @param mixed                $value Value of the setting.
 572           * @param WP_Customize_Setting $this  WP_Customize_Setting instance.
 573           */
 574          return apply_filters( "customize_sanitize_{$this->id}", $value, $this );
 575      }
 576  
 577      /**
 578       * Validates an input.
 579       *
 580       * @since 4.6.0
 581       *
 582       * @see WP_REST_Request::has_valid_params()
 583       *
 584       * @param mixed $value Value to validate.
 585       * @return true|WP_Error True if the input was validated, otherwise WP_Error.
 586       */
 587  	public function validate( $value ) {
 588          if ( is_wp_error( $value ) ) {
 589              return $value;
 590          }
 591          if ( is_null( $value ) ) {
 592              return new WP_Error( 'invalid_value', __( 'Invalid value.' ) );
 593          }
 594  
 595          $validity = new WP_Error();
 596  
 597          /**
 598           * Validates a Customize setting value.
 599           *
 600           * Plugins should amend the `$validity` object via its `WP_Error::add()` method.
 601           *
 602           * The dynamic portion of the hook name, `$this->ID`, refers to the setting ID.
 603           *
 604           * @since 4.6.0
 605           *
 606           * @param WP_Error             $validity Filtered from `true` to `WP_Error` when invalid.
 607           * @param mixed                $value    Value of the setting.
 608           * @param WP_Customize_Setting $this     WP_Customize_Setting instance.
 609           */
 610          $validity = apply_filters( "customize_validate_{$this->id}", $validity, $value, $this );
 611  
 612          if ( is_wp_error( $validity ) && ! $validity->has_errors() ) {
 613              $validity = true;
 614          }
 615          return $validity;
 616      }
 617  
 618      /**
 619       * Get the root value for a setting, especially for multidimensional ones.
 620       *
 621       * @since 4.4.0
 622       *
 623       * @param mixed $default Value to return if root does not exist.
 624       * @return mixed
 625       */
 626  	protected function get_root_value( $default = null ) {
 627          $id_base = $this->id_data['base'];
 628          if ( 'option' === $this->type ) {
 629              return get_option( $id_base, $default );
 630          } elseif ( 'theme_mod' === $this->type ) {
 631              return get_theme_mod( $id_base, $default );
 632          } else {
 633              /*
 634               * Any WP_Customize_Setting subclass implementing aggregate multidimensional
 635               * will need to override this method to obtain the data from the appropriate
 636               * location.
 637               */
 638              return $default;
 639          }
 640      }
 641  
 642      /**
 643       * Set the root value for a setting, especially for multidimensional ones.
 644       *
 645       * @since 4.4.0
 646       *
 647       * @param mixed $value Value to set as root of multidimensional setting.
 648       * @return bool Whether the multidimensional root was updated successfully.
 649       */
 650  	protected function set_root_value( $value ) {
 651          $id_base = $this->id_data['base'];
 652          if ( 'option' === $this->type ) {
 653              $autoload = true;
 654              if ( isset( self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload'] ) ) {
 655                  $autoload = self::$aggregated_multidimensionals[ $this->type ][ $this->id_data['base'] ]['autoload'];
 656              }
 657              return update_option( $id_base, $value, $autoload );
 658          } elseif ( 'theme_mod' === $this->type ) {
 659              set_theme_mod( $id_base, $value );
 660              return true;
 661          } else {
 662              /*
 663               * Any WP_Customize_Setting subclass implementing aggregate multidimensional
 664               * will need to override this method to obtain the data from the appropriate
 665               * location.
 666               */
 667              return false;
 668          }
 669      }
 670  
 671      /**
 672       * Save the value of the setting, using the related API.
 673       *
 674       * @since 3.4.0
 675       *
 676       * @param mixed $value The value to update.
 677       * @return bool The result of saving the value.
 678       */
 679  	protected function update( $value ) {
 680          $id_base = $this->id_data['base'];
 681          if ( 'option' === $this->type || 'theme_mod' === $this->type ) {
 682              if ( ! $this->is_multidimensional_aggregated ) {
 683                  return $this->set_root_value( $value );
 684              } else {
 685                  $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
 686                  $root = $this->multidimensional_replace( $root, $this->id_data['keys'], $value );
 687                  self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'] = $root;
 688                  return $this->set_root_value( $root );
 689              }
 690          } else {
 691              /**
 692               * Fires when the WP_Customize_Setting::update() method is called for settings
 693               * not handled as theme_mods or options.
 694               *
 695               * The dynamic portion of the hook name, `$this->type`, refers to the type of setting.
 696               *
 697               * @since 3.4.0
 698               *
 699               * @param mixed                $value Value of the setting.
 700               * @param WP_Customize_Setting $this  WP_Customize_Setting instance.
 701               */
 702              do_action( "customize_update_{$this->type}", $value, $this );
 703  
 704              return has_action( "customize_update_{$this->type}" );
 705          }
 706      }
 707  
 708      /**
 709       * Deprecated method.
 710       *
 711       * @since 3.4.0
 712       * @deprecated 4.4.0 Deprecated in favor of update() method.
 713       */
 714  	protected function _update_theme_mod() {
 715          _deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' );
 716      }
 717  
 718      /**
 719       * Deprecated method.
 720       *
 721       * @since 3.4.0
 722       * @deprecated 4.4.0 Deprecated in favor of update() method.
 723       */
 724  	protected function _update_option() {
 725          _deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' );
 726      }
 727  
 728      /**
 729       * Fetch the value of the setting.
 730       *
 731       * @since 3.4.0
 732       *
 733       * @return mixed The value.
 734       */
 735  	public function value() {
 736          $id_base      = $this->id_data['base'];
 737          $is_core_type = ( 'option' === $this->type || 'theme_mod' === $this->type );
 738  
 739          if ( ! $is_core_type && ! $this->is_multidimensional_aggregated ) {
 740  
 741              // Use post value if previewed and a post value is present.
 742              if ( $this->is_previewed ) {
 743                  $value = $this->post_value( null );
 744                  if ( null !== $value ) {
 745                      return $value;
 746                  }
 747              }
 748  
 749              $value = $this->get_root_value( $this->default );
 750  
 751              /**
 752               * Filters a Customize setting value not handled as a theme_mod or option.
 753               *
 754               * The dynamic portion of the hook name, `$id_base`, refers to
 755               * the base slug of the setting name, initialized from `$this->id_data['base']`.
 756               *
 757               * For settings handled as theme_mods or options, see those corresponding
 758               * functions for available hooks.
 759               *
 760               * @since 3.4.0
 761               * @since 4.6.0 Added the `$this` setting instance as the second parameter.
 762               *
 763               * @param mixed                $default The setting default value. Default empty.
 764               * @param WP_Customize_Setting $this    The setting instance.
 765               */
 766              $value = apply_filters( "customize_value_{$id_base}", $value, $this );
 767          } elseif ( $this->is_multidimensional_aggregated ) {
 768              $root_value = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
 769              $value      = $this->multidimensional_get( $root_value, $this->id_data['keys'], $this->default );
 770  
 771              // Ensure that the post value is used if the setting is previewed, since preview filters aren't applying on cached $root_value.
 772              if ( $this->is_previewed ) {
 773                  $value = $this->post_value( $value );
 774              }
 775          } else {
 776              $value = $this->get_root_value( $this->default );
 777          }
 778          return $value;
 779      }
 780  
 781      /**
 782       * Sanitize the setting's value for use in JavaScript.
 783       *
 784       * @since 3.4.0
 785       *
 786       * @return mixed The requested escaped value.
 787       */
 788  	public function js_value() {
 789  
 790          /**
 791           * Filters a Customize setting value for use in JavaScript.
 792           *
 793           * The dynamic portion of the hook name, `$this->id`, refers to the setting ID.
 794           *
 795           * @since 3.4.0
 796           *
 797           * @param mixed                $value The setting value.
 798           * @param WP_Customize_Setting $this  WP_Customize_Setting instance.
 799           */
 800          $value = apply_filters( "customize_sanitize_js_{$this->id}", $this->value(), $this );
 801  
 802          if ( is_string( $value ) ) {
 803              return html_entity_decode( $value, ENT_QUOTES, 'UTF-8' );
 804          }
 805  
 806          return $value;
 807      }
 808  
 809      /**
 810       * Retrieves the data to export to the client via JSON.
 811       *
 812       * @since 4.6.0
 813       *
 814       * @return array Array of parameters passed to JavaScript.
 815       */
 816  	public function json() {
 817          return array(
 818              'value'     => $this->js_value(),
 819              'transport' => $this->transport,
 820              'dirty'     => $this->dirty,
 821              'type'      => $this->type,
 822          );
 823      }
 824  
 825      /**
 826       * Validate user capabilities whether the theme supports the setting.
 827       *
 828       * @since 3.4.0
 829       *
 830       * @return bool False if theme doesn't support the setting or user can't change setting, otherwise true.
 831       */
 832  	final public function check_capabilities() {
 833          if ( $this->capability && ! current_user_can( $this->capability ) ) {
 834              return false;
 835          }
 836  
 837          if ( $this->theme_supports && ! current_theme_supports( ... (array) $this->theme_supports ) ) {
 838              return false;
 839          }
 840  
 841          return true;
 842      }
 843  
 844      /**
 845       * Multidimensional helper function.
 846       *
 847       * @since 3.4.0
 848       *
 849       * @param $root
 850       * @param $keys
 851       * @param bool $create Default is false.
 852       * @return array|void Keys are 'root', 'node', and 'key'.
 853       */
 854  	final protected function multidimensional( &$root, $keys, $create = false ) {
 855          if ( $create && empty( $root ) ) {
 856              $root = array();
 857          }
 858  
 859          if ( ! isset( $root ) || empty( $keys ) ) {
 860              return;
 861          }
 862  
 863          $last = array_pop( $keys );
 864          $node = &$root;
 865  
 866          foreach ( $keys as $key ) {
 867              if ( $create && ! isset( $node[ $key ] ) ) {
 868                  $node[ $key ] = array();
 869              }
 870  
 871              if ( ! is_array( $node ) || ! isset( $node[ $key ] ) ) {
 872                  return;
 873              }
 874  
 875              $node = &$node[ $key ];
 876          }
 877  
 878          if ( $create ) {
 879              if ( ! is_array( $node ) ) {
 880                  // Account for an array overriding a string or object value.
 881                  $node = array();
 882              }
 883              if ( ! isset( $node[ $last ] ) ) {
 884                  $node[ $last ] = array();
 885              }
 886          }
 887  
 888          if ( ! isset( $node[ $last ] ) ) {
 889              return;
 890          }
 891  
 892          return array(
 893              'root' => &$root,
 894              'node' => &$node,
 895              'key'  => $last,
 896          );
 897      }
 898  
 899      /**
 900       * Will attempt to replace a specific value in a multidimensional array.
 901       *
 902       * @since 3.4.0
 903       *
 904       * @param $root
 905       * @param $keys
 906       * @param mixed $value The value to update.
 907       * @return mixed
 908       */
 909  	final protected function multidimensional_replace( $root, $keys, $value ) {
 910          if ( ! isset( $value ) ) {
 911              return $root;
 912          } elseif ( empty( $keys ) ) { // If there are no keys, we're replacing the root.
 913              return $value;
 914          }
 915  
 916          $result = $this->multidimensional( $root, $keys, true );
 917  
 918          if ( isset( $result ) ) {
 919              $result['node'][ $result['key'] ] = $value;
 920          }
 921  
 922          return $root;
 923      }
 924  
 925      /**
 926       * Will attempt to fetch a specific value from a multidimensional array.
 927       *
 928       * @since 3.4.0
 929       *
 930       * @param $root
 931       * @param $keys
 932       * @param mixed $default A default value which is used as a fallback. Default is null.
 933       * @return mixed The requested value or the default value.
 934       */
 935  	final protected function multidimensional_get( $root, $keys, $default = null ) {
 936          if ( empty( $keys ) ) { // If there are no keys, test the root.
 937              return isset( $root ) ? $root : $default;
 938          }
 939  
 940          $result = $this->multidimensional( $root, $keys );
 941          return isset( $result ) ? $result['node'][ $result['key'] ] : $default;
 942      }
 943  
 944      /**
 945       * Will attempt to check if a specific value in a multidimensional array is set.
 946       *
 947       * @since 3.4.0
 948       *
 949       * @param $root
 950       * @param $keys
 951       * @return bool True if value is set, false if not.
 952       */
 953  	final protected function multidimensional_isset( $root, $keys ) {
 954          $result = $this->multidimensional_get( $root, $keys );
 955          return isset( $result );
 956      }
 957  }
 958  
 959  /**
 960   * WP_Customize_Filter_Setting class.
 961   */
 962  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php';
 963  
 964  /**
 965   * WP_Customize_Header_Image_Setting class.
 966   */
 967  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php';
 968  
 969  /**
 970   * WP_Customize_Background_Image_Setting class.
 971   */
 972  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php';
 973  
 974  /**
 975   * WP_Customize_Nav_Menu_Item_Setting class.
 976   */
 977  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php';
 978  
 979  /**
 980   * WP_Customize_Nav_Menu_Setting class.
 981   */
 982  require_once  ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php';


Generated : Wed Apr 1 08:20:01 2020 Cross-referenced by PHPXref