WordPress.org

Make WordPress Core

Ticket #32103: 32103.4.diff

File 32103.4.diff, 28.5 KB (added by westonruter, 5 years ago)

https://github.com/xwp/wordpress-develop/pull/125

  • src/wp-includes/class-wp-customize-setting.php

    diff --git src/wp-includes/class-wp-customize-setting.php src/wp-includes/class-wp-customize-setting.php
    index 6630157..01b84c8 100644
    class WP_Customize_Setting { 
    8282        protected $id_data = array();
    8383
    8484        /**
     85         * Cache of multidimensional values to improve performance.
     86         *
     87         * @since 4.4.0
     88         * @access protected
     89         * @var array
     90         * @static
     91         */
     92        protected static $aggregated_multidimensionals = array();
     93
     94        /**
     95         * Whether the multidimensional setting is aggregated.
     96         *
     97         * @since 4.4.0
     98         * @access protected
     99         * @var bool
     100         */
     101        protected $is_multidimensional_aggregated = false;
     102
     103        /**
    85104         * Constructor.
    86105         *
    87106         * Any supplied $args override class property defaults.
    class WP_Customize_Setting { 
    96115        public function __construct( $manager, $id, $args = array() ) {
    97116                $keys = array_keys( get_object_vars( $this ) );
    98117                foreach ( $keys as $key ) {
    99                         if ( isset( $args[ $key ] ) )
     118                        if ( isset( $args[ $key ] ) ) {
    100119                                $this->$key = $args[ $key ];
     120                        }
    101121                }
    102122
    103123                $this->manager = $manager;
    104124                $this->id = $id;
    105125
    106126                // Parse the ID for array keys.
    107                 $this->id_data[ 'keys' ] = preg_split( '/\[/', str_replace( ']', '', $this->id ) );
    108                 $this->id_data[ 'base' ] = array_shift( $this->id_data[ 'keys' ] );
     127                $this->id_data['keys'] = preg_split( '/\[/', str_replace( ']', '', $this->id ) );
     128                $this->id_data['base'] = array_shift( $this->id_data['keys'] );
    109129
    110130                // Rebuild the ID.
    111131                $this->id = $this->id_data[ 'base' ];
    112                 if ( ! empty( $this->id_data[ 'keys' ] ) )
    113                         $this->id .= '[' . implode( '][', $this->id_data[ 'keys' ] ) . ']';
     132                if ( ! empty( $this->id_data[ 'keys' ] ) ) {
     133                        $this->id .= '[' . implode( '][', $this->id_data['keys'] ) . ']';
     134                }
    114135
    115                 if ( $this->sanitize_callback )
     136                if ( $this->sanitize_callback ) {
    116137                        add_filter( "customize_sanitize_{$this->id}", $this->sanitize_callback, 10, 2 );
    117 
    118                 if ( $this->sanitize_js_callback )
     138                }
     139                if ( $this->sanitize_js_callback ) {
    119140                        add_filter( "customize_sanitize_js_{$this->id}", $this->sanitize_js_callback, 10, 2 );
     141                }
     142
     143                if ( 'option' === $this->type || 'theme_mod' === $this->type ) {
     144                        // Other setting types can opt-in to aggregate multidimensional explicitly.
     145                        $this->aggregate_multidimensional();
     146                }
     147        }
     148
     149        /**
     150         * Get parsed ID data for multidimensional setting.
     151         *
     152         * @since 4.4.0
     153         * @access public
     154         *
     155         * @return array {
     156         *     ID data for multidimensional setting.
     157         *
     158         *     @type string $base ID base
     159         *     @type array  $keys Keys for multidimensional array.
     160         * }
     161         */
     162        final public function id_data() {
     163                return $this->id_data;
     164        }
     165
     166        /**
     167         * Set up the setting for aggregated multidimensional values.
     168         *
     169         * When a multidimensional setting gets aggregated, all of its preview and update
     170         * calls get combined into one call, greatly improving performance.
     171         *
     172         * @since 4.4.0
     173         * @access protected
     174         */
     175        protected function aggregate_multidimensional() {
     176                if ( empty( $this->id_data['keys'] ) ) {
     177                        return;
     178                }
     179
     180                $id_base = $this->id_data['base'];
     181                if ( ! isset( self::$aggregated_multidimensionals[ $this->type ] ) ) {
     182                        self::$aggregated_multidimensionals[ $this->type ] = array();
     183                }
     184                if ( ! isset( self::$aggregated_multidimensionals[ $this->type ][ $id_base ] ) ) {
     185                        self::$aggregated_multidimensionals[ $this->type ][ $id_base ] = array(
     186                                'previewed_instances'       => array(), // Calling preview() will add the $setting to the array.
     187                                'preview_applied_instances' => array(), // Flags for which settings have had their values applied.
     188                                'root_value'                => $this->get_root_value( array() ), // Root value for initial state, manipulated by preview and update calls.
     189                        );
     190                }
     191                $this->is_multidimensional_aggregated = true;
    120192        }
    121193
    122194        /**
    class WP_Customize_Setting { 
    153225        protected $_original_value;
    154226
    155227        /**
    156          * Set up filters for the setting so that the preview request
    157          * will render the drafted changes.
     228         * Add filters to supply the setting's value when accessed.
     229         *
     230         * If the setting already has a pre-existing value and there is no incoming
     231         * post value for the setting, then this method will short-circuit since
     232         * there is no change to preview.
    158233         *
    159234         * @since 3.4.0
     235         * @since 4.4.0 Added boolean return value.
     236         * @access public
     237         *
     238         * @return bool False when preview short-circuits due no change needing to be previewed.
    160239         */
    161240        public function preview() {
    162                 if ( ! isset( $this->_original_value ) ) {
    163                         $this->_original_value = $this->value();
    164                 }
    165241                if ( ! isset( $this->_previewed_blog_id ) ) {
    166242                        $this->_previewed_blog_id = get_current_blog_id();
    167243                }
     244                $id_base = $this->id_data['base'];
     245                $is_multidimensional = ! empty( $this->id_data['keys'] );
     246                $multidimensional_filter = array( $this, '_multidimensional_preview_filter' );
    168247
    169                 switch( $this->type ) {
     248                /*
     249                 * Check if the setting has a pre-existing value (an isset check),
     250                 * and if doesn't have any incoming post value. If both checks are true,
     251                 * then the preview short-circuits because there is nothing that needs
     252                 * to be previewed.
     253                 */
     254                $undefined = new stdClass();
     255                $needs_preview = ( $undefined !== $this->post_value( $undefined ) );
     256                $value = null;
     257
     258                // Since no post value was defined, check if we have an initial value set.
     259                if ( ! $needs_preview ) {
     260                        if ( $this->is_multidimensional_aggregated ) {
     261                                $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
     262                                $value = $this->multidimensional_get( $root, $this->id_data['keys'], $undefined );
     263                        } else {
     264                                $default = $this->default;
     265                                $this->default = $undefined; // Temporarily set default to undefined so we can detect if existing value is set.
     266                                $value = $this->value();
     267                                $this->default = $default;
     268                        }
     269                        $needs_preview = ( $undefined === $value ); // Because the default needs to be supplied.
     270                }
     271
     272                if ( ! $needs_preview ) {
     273                        return false;
     274                }
     275
     276                switch ( $this->type ) {
    170277                        case 'theme_mod' :
    171                                 add_filter( 'theme_mod_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
     278                                if ( ! $is_multidimensional ) {
     279                                        add_filter( "theme_mod_{$id_base}", array( $this, '_preview_filter' ) );
     280                                } else {
     281                                        if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) {
     282                                                // Only add this filter once for this ID base.
     283                                                add_filter( "theme_mod_{$id_base}", $multidimensional_filter );
     284                                        }
     285                                        self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this;
     286                                }
    172287                                break;
    173288                        case 'option' :
    174                                 if ( empty( $this->id_data[ 'keys' ] ) )
    175                                         add_filter( 'pre_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
    176                                 else {
    177                                         add_filter( 'option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
    178                                         add_filter( 'default_option_' . $this->id_data[ 'base' ], array( $this, '_preview_filter' ) );
     289                                if ( ! $is_multidimensional ) {
     290                                        add_filter( "pre_option_{$id_base}", array( $this, '_preview_filter' ) );
     291                                } else {
     292                                        if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) {
     293                                                // Only add these filters once for this ID base.
     294                                                add_filter( "option_{$id_base}", $multidimensional_filter );
     295                                                add_filter( "default_option_{$id_base}", $multidimensional_filter );
     296                                        }
     297                                        self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'][ $this->id ] = $this;
    179298                                }
    180299                                break;
    181300                        default :
    class WP_Customize_Setting { 
    204323                                 */
    205324                                do_action( "customize_preview_{$this->type}", $this );
    206325                }
     326                return true;
    207327        }
    208328
    209329        /**
    210          * Callback function to filter the theme mods and options.
     330         * Callback function to filter non-multidimensional theme mods and options.
    211331         *
    212332         * If switch_to_blog() was called after the preview() method, and the current
    213333         * blog is now not the same blog, then this method does a no-op and returns
    214334         * the original value.
    215335         *
    216336         * @since 3.4.0
    217          * @uses WP_Customize_Setting::multidimensional_replace()
    218337         *
    219338         * @param mixed $original Old value.
    220339         * @return mixed New or old value.
    class WP_Customize_Setting { 
    224343                        return $original;
    225344                }
    226345
    227                 $undefined = new stdClass(); // symbol hack
     346                $undefined = new stdClass(); // Symbol hack.
    228347                $post_value = $this->post_value( $undefined );
    229                 if ( $undefined === $post_value ) {
    230                         $value = $this->_original_value;
    231                 } else {
     348                if ( $undefined !== $post_value ) {
    232349                        $value = $post_value;
     350                } else {
     351                        /*
     352                         * Note that we don't use $original here because preview() will
     353                         * not add the filter in the first place if it has an initial value
     354                         * and there is no post value.
     355                         */
     356                        $value = $this->default;
    233357                }
     358                return $value;
     359        }
     360
     361        /**
     362         * Callback function to filter multidimensional theme mods and options.
     363         *
     364         * For all multidimensional settings of a given type, the preview filter for
     365         * the first setting previewed will be used to apply the values for the others.
     366         *
     367         * @since 4.4.0
     368         * @access public
     369         *
     370         * @see WP_Customize_Setting::$aggregated_multidimensionals
     371         * @param mixed $original Original root value.
     372         * @return mixed New or old value.
     373         */
     374        public function _multidimensional_preview_filter( $original ) {
     375                if ( ! $this->is_current_blog_previewed() ) {
     376                        return $original;
     377                }
     378
     379                $id_base = $this->id_data['base'];
     380
     381                // If no settings have been previewed yet (which should not be the case, since $this is), just pass through the original value.
     382                if ( empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] ) ) {
     383                        return $original;
     384                }
     385
     386                foreach ( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['previewed_instances'] as $previewed_setting ) {
     387                        // Skip applying previewed value for any settings that have already been applied.
     388                        if ( ! empty( self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] ) ) {
     389                                continue;
     390                        }
     391
     392                        // Do the replacements of the posted/default sub value into the root value.
     393                        $value = $previewed_setting->post_value( $previewed_setting->default );
     394                        $root = self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value'];
     395                        $root = $previewed_setting->multidimensional_replace( $root, $previewed_setting->id_data['keys'], $value );
     396                        self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['root_value'] = $root;
    234397
    235                 return $this->multidimensional_replace( $original, $this->id_data['keys'], $value );
     398                        // Mark this setting having been applied so that it will be skipped when the filter is called again.
     399                        self::$aggregated_multidimensionals[ $previewed_setting->type ][ $id_base ]['preview_applied_instances'][ $previewed_setting->id ] = true;
     400                }
     401
     402                return self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
    236403        }
    237404
    238405        /**
    class WP_Customize_Setting { 
    299466        }
    300467
    301468        /**
    302          * Save the value of the setting, using the related API.
     469         * Get the root value for a setting, especially for multidimensional ones.
    303470         *
    304          * @since 3.4.0
     471         * @since 4.4.0
     472         * @access protected
    305473         *
    306          * @param mixed $value The value to update.
    307          * @return bool The result of saving the value.
     474         * @param mixed $default Value to return if root does not exist.
     475         * @return mixed
    308476         */
    309         protected function update( $value ) {
    310                 switch ( $this->type ) {
    311                         case 'theme_mod' :
    312                                 $this->_update_theme_mod( $value );
    313                                 return true;
    314 
    315                         case 'option' :
    316                                 return $this->_update_option( $value );
    317 
    318                         default :
    319 
    320                                 /**
    321                                  * Fires when the {@see WP_Customize_Setting::update()} method is called for settings
    322                                  * not handled as theme_mods or options.
    323                                  *
    324                                  * The dynamic portion of the hook name, `$this->type`, refers to the type of setting.
    325                                  *
    326                                  * @since 3.4.0
    327                                  *
    328                                  * @param mixed                $value Value of the setting.
    329                                  * @param WP_Customize_Setting $this  WP_Customize_Setting instance.
    330                                  */
    331                                 do_action( "customize_update_{$this->type}", $value, $this );
    332 
    333                                 return has_action( "customize_update_{$this->type}" );
     477        protected function get_root_value( $default = null ) {
     478                $id_base = $this->id_data['base'];
     479                if ( 'option' === $this->type ) {
     480                        return get_option( $id_base, $default );
     481                } else if ( 'theme_mod' ) {
     482                        return get_theme_mod( $id_base, $default );
     483                } else {
     484                        /*
     485                         * Any WP_Customize_Setting subclass implementing aggregate multidimensional
     486                         * will need to override this method to obtain the data from the appropriate
     487                         * location.
     488                         */
     489                        return $default;
    334490                }
    335491        }
    336492
    337493        /**
    338          * Update the theme mod from the value of the parameter.
     494         * Set the root value for a setting, especially for multidimensional ones.
    339495         *
    340          * @since 3.4.0
     496         * @since 4.4.0
     497         * @access protected
    341498         *
    342          * @param mixed $value The value to update.
     499         * @param mixed $value Value to set as root of multidimensional setting.
     500         * @return bool Whether the multidimensional root was updated successfully.
    343501         */
    344         protected function _update_theme_mod( $value ) {
    345                 // Handle non-array theme mod.
    346                 if ( empty( $this->id_data[ 'keys' ] ) ) {
    347                         set_theme_mod( $this->id_data[ 'base' ], $value );
    348                         return;
    349                 }
    350                 // Handle array-based theme mod.
    351                 $mods = get_theme_mod( $this->id_data[ 'base' ] );
    352                 $mods = $this->multidimensional_replace( $mods, $this->id_data[ 'keys' ], $value );
    353                 if ( isset( $mods ) ) {
    354                         set_theme_mod( $this->id_data[ 'base' ], $mods );
     502        protected function set_root_value( $value ) {
     503                $id_base = $this->id_data['base'];
     504                if ( 'option' === $this->type ) {
     505                        return update_option( $id_base, $value );
     506                } else if ( 'theme_mod' ) {
     507                        set_theme_mod( $id_base, $value );
     508                        return true;
     509                } else {
     510                        /*
     511                         * Any WP_Customize_Setting subclass implementing aggregate multidimensional
     512                         * will need to override this method to obtain the data from the appropriate
     513                         * location.
     514                         */
     515                        return false;
    355516                }
    356517        }
    357518
    358519        /**
    359          * Update the option from the value of the setting.
     520         * Save the value of the setting, using the related API.
    360521         *
    361522         * @since 3.4.0
    362523         *
    363524         * @param mixed $value The value to update.
    364525         * @return bool The result of saving the value.
    365526         */
    366         protected function _update_option( $value ) {
    367                 // Handle non-array option.
    368                 if ( empty( $this->id_data[ 'keys' ] ) )
    369                         return update_option( $this->id_data[ 'base' ], $value );
     527        protected function update( $value ) {
     528                $id_base = $this->id_data['base'];
     529                if ( 'option' === $this->type || 'theme_mod' === $this->type ) {
     530                        if ( ! $this->is_multidimensional_aggregated ) {
     531                                return $this->set_root_value( $value );
     532                        } else {
     533                                $root = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
     534                                $root = $this->multidimensional_replace( $root, $this->id_data['keys'], $value );
     535                                self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'] = $root;
     536                                return $this->set_root_value( $root );
     537                        }
     538                } else {
     539                        /**
     540                         * Fires when the {@see WP_Customize_Setting::update()} method is called for settings
     541                         * not handled as theme_mods or options.
     542                         *
     543                         * The dynamic portion of the hook name, `$this->type`, refers to the type of setting.
     544                         *
     545                         * @since 3.4.0
     546                         *
     547                         * @param mixed                $value Value of the setting.
     548                         * @param WP_Customize_Setting $this  WP_Customize_Setting instance.
     549                         */
     550                        do_action( "customize_update_{$this->type}", $value, $this );
     551
     552                        return has_action( "customize_update_{$this->type}" );
     553                }
     554        }
     555
     556        /**
     557         * Deprecated method.
     558         *
     559         * @since 3.4.0
     560         * @deprecated 4.4.0 Deprecated in favor of update() method.
     561         */
     562        protected function _update_theme_mod() {
     563                _deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' );
     564        }
    370565
    371                 // Handle array-based options.
    372                 $options = get_option( $this->id_data[ 'base' ] );
    373                 $options = $this->multidimensional_replace( $options, $this->id_data[ 'keys' ], $value );
    374                 if ( isset( $options ) )
    375                         return update_option( $this->id_data[ 'base' ], $options );
     566        /**
     567         * Deprecated method.
     568         *
     569         * @since 3.4.0
     570         * @deprecated 4.4.0 Deprecated in favor of update() method.
     571         */
     572        protected function _update_option() {
     573                _deprecated_function( __METHOD__, '4.4.0', __CLASS__ . '::update()' );
    376574        }
    377575
    378576        /**
    class WP_Customize_Setting { 
    383581         * @return mixed The value.
    384582         */
    385583        public function value() {
    386                 // Get the callback that corresponds to the setting type.
    387                 switch( $this->type ) {
    388                         case 'theme_mod' :
    389                                 $function = 'get_theme_mod';
    390                                 break;
    391                         case 'option' :
    392                                 $function = 'get_option';
    393                                 break;
    394                         default :
    395 
    396                                 /**
    397                                  * Filter a Customize setting value not handled as a theme_mod or option.
    398                                  *
    399                                  * The dynamic portion of the hook name, `$this->id_date['base']`, refers to
    400                                  * the base slug of the setting name.
    401                                  *
    402                                  * For settings handled as theme_mods or options, see those corresponding
    403                                  * functions for available hooks.
    404                                  *
    405                                  * @since 3.4.0
    406                                  *
    407                                  * @param mixed $default The setting default value. Default empty.
    408                                  */
    409                                 return apply_filters( 'customize_value_' . $this->id_data[ 'base' ], $this->default );
     584                $id_base = $this->id_data['base'];
     585                $is_core_type = ( 'option' === $this->type || 'theme_mod' === $this->type );
     586
     587                if ( ! $is_core_type && ! $this->is_multidimensional_aggregated ) {
     588                        $value = $this->get_root_value( $this->default );
     589
     590                        /**
     591                         * Filter a Customize setting value not handled as a theme_mod or option.
     592                         *
     593                         * The dynamic portion of the hook name, `$this->id_date['base']`, refers to
     594                         * the base slug of the setting name.
     595                         *
     596                         * For settings handled as theme_mods or options, see those corresponding
     597                         * functions for available hooks.
     598                         *
     599                         * @since 3.4.0
     600                         *
     601                         * @param mixed $default The setting default value. Default empty.
     602                         */
     603                        $value = apply_filters( "customize_value_{$id_base}", $value );
     604                } else if ( $this->is_multidimensional_aggregated ) {
     605                        $root_value = self::$aggregated_multidimensionals[ $this->type ][ $id_base ]['root_value'];
     606                        $value = $this->multidimensional_get( $root_value, $this->id_data['keys'], $this->default );
     607                } else {
     608                        $value = $this->get_root_value( $this->default );
    410609                }
    411 
    412                 // Handle non-array value
    413                 if ( empty( $this->id_data[ 'keys' ] ) )
    414                         return $function( $this->id_data[ 'base' ], $this->default );
    415 
    416                 // Handle array-based value
    417                 $values = $function( $this->id_data[ 'base' ] );
    418                 return $this->multidimensional_get( $values, $this->id_data[ 'keys' ], $this->default );
     610                return $value;
    419611        }
    420612
    421613        /**
    class WP_Customize_Setting { 
    520712         * @param $root
    521713         * @param $keys
    522714         * @param mixed $value The value to update.
    523          * @return
     715         * @return mixed
    524716         */
    525717        final protected function multidimensional_replace( $root, $keys, $value ) {
    526718                if ( ! isset( $value ) )
    class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { 
    8531045        }
    8541046
    8551047        /**
    856          * Get the instance data for a given widget setting.
     1048         * Get the instance data for a given nav_menu_item setting.
    8571049         *
    8581050         * @since 4.3.0
    8591051         * @access public
    class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { 
    9871179         * Handle previewing the setting.
    9881180         *
    9891181         * @since 4.3.0
     1182         * @since 4.4.0 Added boolean return value.
    9901183         * @access public
    9911184         *
    9921185         * @see WP_Customize_Manager::post_value()
     1186         *
     1187         * @return bool False if method short-circuited due to no-op.
    9931188         */
    9941189        public function preview() {
    9951190                if ( $this->is_previewed ) {
    996                         return;
     1191                        return false;
     1192                }
     1193
     1194                $undefined = new stdClass();
     1195                $is_placeholder = ( $this->post_id < 0 );
     1196                $is_dirty = ( $undefined !== $this->post_value( $undefined ) );
     1197                if ( ! $is_placeholder && ! $is_dirty ) {
     1198                        return false;
    9971199                }
    9981200
    9991201                $this->is_previewed              = true;
    class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { 
    10091211                }
    10101212
    10111213                // @todo Add get_post_metadata filters for plugins to add their data.
     1214
     1215                return true;
    10121216        }
    10131217
    10141218        /**
    class WP_Customize_Nav_Menu_Setting extends WP_Customize_Setting { 
    16211825         * Handle previewing the setting.
    16221826         *
    16231827         * @since 4.3.0
     1828         * @since 4.4.0 Added boolean return value
    16241829         * @access public
    16251830         *
    16261831         * @see WP_Customize_Manager::post_value()
     1832         *
     1833         * @return bool False if method short-circuited due to no-op.
    16271834         */
    16281835        public function preview() {
    16291836                if ( $this->is_previewed ) {
    1630                         return;
     1837                        return false;
     1838                }
     1839
     1840                $undefined = new stdClass();
     1841                $is_placeholder = ( $this->term_id < 0 );
     1842                $is_dirty = ( $undefined !== $this->post_value( $undefined ) );
     1843                if ( ! $is_placeholder && ! $is_dirty ) {
     1844                        return false;
    16311845                }
    16321846
    16331847                $this->is_previewed       = true;
    class WP_Customize_Nav_Menu_Setting extends WP_Customize_Setting { 
    16381852                add_filter( 'wp_get_nav_menu_object', array( $this, 'filter_wp_get_nav_menu_object' ), 10, 2 );
    16391853                add_filter( 'default_option_nav_menu_options', array( $this, 'filter_nav_menu_options' ) );
    16401854                add_filter( 'option_nav_menu_options', array( $this, 'filter_nav_menu_options' ) );
     1855
     1856                return true;
    16411857        }
    16421858
    16431859        /**
  • tests/phpunit/tests/customize/setting.php

    diff --git tests/phpunit/tests/customize/setting.php tests/phpunit/tests/customize/setting.php
    index 069a8ae..600fb3f 100644
    class Tests_WP_Customize_Setting extends WP_UnitTestCase { 
    102102                        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    103103                        $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $name, $this->undefined ) );
    104104                        $this->assertEquals( $default, $setting->value() );
    105                         $setting->preview();
     105                        $this->assertTrue( $setting->preview(), 'Preview should not no-op since setting has no existing value.' );
    106106                        $this->assertEquals( $default, call_user_func( $type_options['getter'], $name, $this->undefined ), sprintf( 'Expected %s(%s) to return setting default: %s.', $type_options['getter'], $name, $default ) );
    107107                        $this->assertEquals( $default, $setting->value() );
    108108
    class Tests_WP_Customize_Setting extends WP_UnitTestCase { 
    114114                        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    115115                        $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name ) );
    116116                        $this->assertEquals( $initial_value, $setting->value() );
    117                         $setting->preview();
     117                        $this->assertFalse( $setting->preview(), 'Preview should no-op since setting value was extant and no post value was present.' );
    118118                        $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods)
    119119                        $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods)
    120120                        $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name ) );
    121121                        $this->assertEquals( $initial_value, $setting->value() );
    122122
    123                         // @todo What if we call the setter after preview() is called? If no post_value, should the new set value be stored? If that happens, then the following 3 assertions should be inverted
    124123                        $overridden_value = "overridden_value_$name";
    125124                        call_user_func( $type_options['setter'], $name, $overridden_value );
    126                         $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name ) );
    127                         $this->assertEquals( $initial_value, $setting->value() );
    128                         $this->assertNotEquals( $overridden_value, $setting->value() );
     125                        $message = 'Initial value should be overridden because initial preview() was no-op due to setting having existing value and/or post value was absent.';
     126                        $this->assertEquals( $overridden_value, call_user_func( $type_options['getter'], $name ), $message );
     127                        $this->assertEquals( $overridden_value, $setting->value(), $message );
     128                        $this->assertNotEquals( $initial_value, $setting->value(), $message );
    129129
    130130                        // Non-multidimensional: Test unset setting being overridden by a post value
    131131                        $name = "unset_{$type}_overridden";
    class Tests_WP_Customize_Setting extends WP_UnitTestCase { 
    133133                        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    134134                        $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $name, $this->undefined ) );
    135135                        $this->assertEquals( $default, $setting->value() );
    136                         $setting->preview(); // activate post_data
     136                        $this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // activate post_data
    137137                        $this->assertEquals( $this->post_data_overrides[ $name ], call_user_func( $type_options['getter'], $name, $this->undefined ) );
    138138                        $this->assertEquals( $this->post_data_overrides[ $name ], $setting->value() );
    139139
    class Tests_WP_Customize_Setting extends WP_UnitTestCase { 
    145145                        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    146146                        $this->assertEquals( $initial_value, call_user_func( $type_options['getter'], $name, $this->undefined ) );
    147147                        $this->assertEquals( $initial_value, $setting->value() );
    148                         $setting->preview(); // activate post_data
     148                        $this->assertTrue( $setting->preview(), 'Preview applies because setting has post_data_overrides.' ); // activate post_data
    149149                        $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ) ); // only applicable for custom types (not options or theme_mods)
    150150                        $this->assertEquals( 0, did_action( "customize_preview_{$setting->type}" ) ); // only applicable for custom types (not options or theme_mods)
    151151                        $this->assertEquals( $this->post_data_overrides[ $name ], call_user_func( $type_options['getter'], $name, $this->undefined ) );
    class Tests_WP_Customize_Setting extends WP_UnitTestCase { 
    167167                        $setting = new WP_Customize_Setting( $this->manager, $name, compact( 'type', 'default' ) );
    168168                        $this->assertEquals( $this->undefined, call_user_func( $type_options['getter'], $base_name, $this->undefined ) );
    169169                        $this->assertEquals( $default, $setting->value() );
    170                         $setting->preview();
     170                        $this->assertTrue( $setting->preview() );
    171171                        $base_value = call_user_func( $type_options['getter'], $base_name, $this->undefined );
    172172                        $this->assertArrayHasKey( 'foo', $base_value );
    173173                        $this->assertEquals( $default, $base_value['foo'] );
    class Tests_WP_Customize_Setting extends WP_UnitTestCase { 
    311311                $this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) );
    312312                $this->assertEquals( $initial_value, $setting->value() );
    313313                $setting->preview();
    314                 $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ) );
    315                 $this->assertEquals( 2, did_action( "customize_preview_{$setting->type}" ) );
     314                $this->assertEquals( 0, did_action( "customize_preview_{$setting->id}" ), 'Zero preview actions because initial value is set with no incoming post value, so there is no preview to apply.' );
     315                $this->assertEquals( 1, did_action( "customize_preview_{$setting->type}" ) );
    316316                $this->assertEquals( $initial_value, $this->custom_type_getter( $name, $this->undefined ) ); // should be same as above
    317317                $this->assertEquals( $initial_value, $setting->value() ); // should be same as above
    318318
    class Tests_WP_Customize_Setting extends WP_UnitTestCase { 
    325325                $this->assertEquals( $this->undefined, $this->custom_type_getter( $name, $this->undefined ) );
    326326                $this->assertEquals( $default, $setting->value() );
    327327                $setting->preview();
    328                 $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ) );
    329                 $this->assertEquals( 3, did_action( "customize_preview_{$setting->type}" ) );
     328                $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ), 'One preview action now because initial value was not set and/or there is no incoming post value, so there is is a preview to apply.' );
     329                $this->assertEquals( 2, did_action( "customize_preview_{$setting->type}" ) );
    330330                $this->assertEquals( $post_data_overrides[ $name ], $this->custom_type_getter( $name, $this->undefined ) );
    331331                $this->assertEquals( $post_data_overrides[ $name ], $setting->value() );
    332332
    class Tests_WP_Customize_Setting extends WP_UnitTestCase { 
    342342                $this->assertEquals( $initial_value, $setting->value() );
    343343                $setting->preview();
    344344                $this->assertEquals( 1, did_action( "customize_preview_{$setting->id}" ) );
    345                 $this->assertEquals( 4, did_action( "customize_preview_{$setting->type}" ) );
     345                $this->assertEquals( 3, did_action( "customize_preview_{$setting->type}" ) );
    346346                $this->assertEquals( $post_data_overrides[ $name ], $this->custom_type_getter( $name, $this->undefined ) );
    347347                $this->assertEquals( $post_data_overrides[ $name ], $setting->value() );
    348348