Make WordPress Core


Ignore:
Timestamp:
10/10/2015 09:05:04 AM (9 years ago)
Author:
westonruter
Message:

Customizer: Fix scalability performance problem for previewing multidimensional settings.

As the number of multidimensional settings (serialized options and theme mods) increase for a given ID base (e.g. a widget of a certain type), the number of calls to the multidimensional methods on WP_Customize_Setting increase exponentially, and the time for the preview to refresh grows in time exponentially as well.

To improve performance, this change reduces the number of filters needed to preview the settings off of a multidimensional root from N to 1. This improves performance from O(n^2) to O(n), but the linear increase is so low that the performance is essentially O(1) in comparison. This is achieved by introducing the concept of an "aggregated multidimensional" setting, where the root value of the multidimensional serialized setting value gets cached in a static array variable shared across all settings.

Also improves performance by only adding preview filters if there is actually a need to do so: there is no need to add a filter if there is an initial value and if there is no posted value for a given setting (if it is not dirty).

Fixes #32103.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/customize/setting.php

    r34838 r35007  
    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() );
     
    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)
     
    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
     
    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() );
     
    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)
     
    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 );
     
    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
     
    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() );
     
    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() );
Note: See TracChangeset for help on using the changeset viewer.