Make WordPress Core

Changeset 56609


Ignore:
Timestamp:
09/18/2023 12:39:18 PM (9 months ago)
Author:
spacedmonkey
Message:

Plugins: Store result of call to array_keys, to save repeated calls in WP_Hook class.

In the WP_Hook class the function array_keys was called every time an array of hook priorities was needed. For sites with lots of filters or actions, this would result in thousands of calls to the array_keys function, which uses server resources. Instead of recomputing this array every time it is needed, only compute it when filters are added and removed, then store the result as a class property. Improve unit tests to ensure this behaviour is tested.

Props spacedmonkey, bor0, flixos90, hellofromTonya, mukesh27.
Fixes #58458.

Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-hook.php

    r56549 r56609  
    2828
    2929    /**
     30     * Priorities list.
     31     *
     32     * @since 6.4.0
     33     * @var array
     34     */
     35    protected $priorities = array();
     36
     37    /**
    3038     * The priority keys of actively running iterations of a hook.
    3139     *
     
    8795        }
    8896
     97        $this->priorities = array_keys( $this->callbacks );
     98
    8999        if ( $this->nesting_level > 0 ) {
    90100            $this->resort_active_iterations( $priority, $priority_existed );
     
    103113     */
    104114    private function resort_active_iterations( $new_priority = false, $priority_existed = false ) {
    105         $new_priorities = array_keys( $this->callbacks );
     115        $new_priorities = $this->priorities;
    106116
    107117        // If there are no remaining hooks, clear out all running iterations.
     
    187197            if ( ! $this->callbacks[ $priority ] ) {
    188198                unset( $this->callbacks[ $priority ] );
     199
     200                $this->priorities = array_keys( $this->callbacks );
    189201
    190202                if ( $this->nesting_level > 0 ) {
     
    263275
    264276        if ( false === $priority ) {
    265             $this->callbacks = array();
     277            $this->callbacks  = array();
     278            $this->priorities = array();
    266279        } elseif ( isset( $this->callbacks[ $priority ] ) ) {
    267280            unset( $this->callbacks[ $priority ] );
     281            $this->priorities = array_keys( $this->callbacks );
    268282        }
    269283
     
    290304        $nesting_level = $this->nesting_level++;
    291305
    292         $this->iterations[ $nesting_level ] = array_keys( $this->callbacks );
     306        $this->iterations[ $nesting_level ] = $this->priorities;
    293307
    294308        $num_args = count( $args );
     
    349363    public function do_all_hook( &$args ) {
    350364        $nesting_level                      = $this->nesting_level++;
    351         $this->iterations[ $nesting_level ] = array_keys( $this->callbacks );
     365        $this->iterations[ $nesting_level ] = $this->priorities;
    352366
    353367        do {
     
    482496            $this->callbacks[ $offset ] = $value;
    483497        }
     498
     499        $this->priorities = array_keys( $this->callbacks );
    484500    }
    485501
     
    496512    public function offsetUnset( $offset ) {
    497513        unset( $this->callbacks[ $offset ] );
     514        $this->priorities = array_keys( $this->callbacks );
    498515    }
    499516
  • trunk/tests/phpunit/tests/hooks/addFilter.php

    r55081 r56609  
    3636
    3737        $hook->add_filter( $hook_name, $callback, $priority, $accepted_args );
     38        $this->check_priority_exists( $hook, $priority );
    3839
    3940        $function_index = _wp_filter_build_unique_id( $hook_name, $callback, $priority );
     
    5152
    5253        $hook->add_filter( $hook_name, $callback, $priority, $accepted_args );
     54        $this->check_priority_exists( $hook, $priority );
    5355
    5456        $function_index = _wp_filter_build_unique_id( $hook_name, $callback, $priority );
     
    6567
    6668        $hook->add_filter( $hook_name, $callback, $priority, $accepted_args );
     69        $this->check_priority_exists( $hook, $priority );
    6770
    6871        $function_index = _wp_filter_build_unique_id( $hook_name, $callback, $priority );
     
    8083
    8184        $hook->add_filter( $hook_name, $callback_one, $priority, $accepted_args );
     85        $this->check_priority_exists( $hook, $priority );
    8286        $this->assertCount( 1, $hook->callbacks[ $priority ] );
    8387
     
    9599
    96100        $hook->add_filter( $hook_name, $callback_one, $priority, $accepted_args );
     101        $this->check_priority_exists( $hook, $priority );
    97102        $this->assertCount( 1, $hook->callbacks[ $priority ] );
    98103
    99104        $hook->add_filter( $hook_name, $callback_two, $priority + 1, $accepted_args );
     105        $this->check_priority_exists( $hook, $priority + 1 );
    100106        $this->assertCount( 1, $hook->callbacks[ $priority ] );
    101107        $this->assertCount( 1, $hook->callbacks[ $priority + 1 ] );
     
    110116
    111117        $hook->add_filter( $hook_name, $callback, $priority, $accepted_args );
     118        $this->check_priority_exists( $hook, $priority );
    112119        $this->assertCount( 1, $hook->callbacks[ $priority ] );
    113120
     
    124131
    125132        $hook->add_filter( $hook_name, $callback, $priority, $accepted_args );
     133        $this->check_priority_exists( $hook, $priority );
    126134        $this->assertCount( 1, $hook->callbacks[ $priority ] );
    127135
    128136        $hook->add_filter( $hook_name, $callback, $priority + 1, $accepted_args );
     137        $this->check_priority_exists( $hook, $priority + 1 );
    129138        $this->assertCount( 1, $hook->callbacks[ $priority ] );
    130139        $this->assertCount( 1, $hook->callbacks[ $priority + 1 ] );
     
    142151        $hook->add_filter( $hook_name, array( $c, 'action' ), 8, 1 );
    143152
    144         $this->assertSame( array( 5, 8, 10 ), array_keys( $hook->callbacks ) );
     153        $this->assertSame( array( 5, 8, 10 ), $this->get_priorities( $hook ) );
    145154    }
    146155
     
    149158
    150159        $this->hook->add_filter( 'remove_and_add', '__return_empty_string', 10, 0 );
    151 
     160        $this->check_priority_exists( $this->hook, 10 );
    152161        $this->hook->add_filter( 'remove_and_add', array( $this, '_filter_remove_and_add2' ), 11, 1 );
    153 
     162        $this->check_priority_exists( $this->hook, 11 );
    154163        $this->hook->add_filter( 'remove_and_add', array( $this, '_filter_remove_and_add4' ), 12, 1 );
    155 
     164        $this->check_priority_exists( $this->hook, 12 );
    156165        $value = $this->hook->apply_filters( '', array() );
    157166
     167        $this->assertSameSets( array( 10, 11, 12 ), $this->get_priorities( $this->hook ), 'The priorities should match this array' );
     168
    158169        $this->assertSame( '24', $value );
    159170    }
     
    163174
    164175        $this->hook->add_filter( 'remove_and_add', '__return_empty_string', 10, 0 );
    165 
     176        $this->check_priority_exists( $this->hook, 10 );
    166177        $this->hook->add_filter( 'remove_and_add', array( $this, '_filter_remove_and_add1' ), 11, 1 );
    167 
     178        $this->check_priority_exists( $this->hook, 11 );
    168179        $this->hook->add_filter( 'remove_and_add', array( $this, '_filter_remove_and_add2' ), 12, 1 );
    169 
     180        $this->check_priority_exists( $this->hook, 12 );
    170181        $value = $this->hook->apply_filters( '', array() );
     182
     183        $this->assertSameSets( array( 10, 11, 12 ), $this->get_priorities( $this->hook ), 'The priorities should match this array' );
    171184
    172185        $this->assertSame( '12', $value );
     
    184197        $this->hook->add_filter( 'remove_and_add', array( $this, '_filter_remove_and_add4' ), 12, 1 );
    185198
     199        $this->assertSameSets( array( 10, 11, 12 ), $this->get_priorities( $this->hook ), 'The priorities should match this array' );
     200
    186201        $value = $this->hook->apply_filters( '', array() );
    187202
     
    196211        $this->hook->remove_filter( 'remove_and_add', array( $this, '_filter_remove_and_add2' ), 11 );
    197212        $this->hook->add_filter( 'remove_and_add', array( $this, '_filter_remove_and_add2' ), 11, 1 );
    198 
     213        $this->check_priority_exists( $this->hook, 11 );
    199214        return $value . '2';
    200215    }
     
    206221
    207222        $this->hook->add_filter( 'remove_and_add', array( $this, '_filter_remove_and_recurse_and_add2' ), 11, 1 );
    208 
     223        $this->check_priority_exists( $this->hook, 11 );
    209224        return $value . '2';
    210225    }
     
    292307        $this->action_output .= '4';
    293308    }
     309
     310    protected function check_priority_exists( $hook, $priority ) {
     311        $priorities = $this->get_priorities( $hook );
     312
     313        $this->assertContains( $priority, $priorities );
     314    }
     315
     316    protected function get_priorities( $hook ) {
     317        $reflection          = new ReflectionClass( $hook );
     318        $reflection_property = $reflection->getProperty( 'priorities' );
     319        $reflection_property->setAccessible( true );
     320
     321        return $reflection_property->getValue( $hook );
     322    }
    294323}
  • trunk/tests/phpunit/tests/hooks/removeAllFilters.php

    r53804 r56609  
    1919
    2020        $hook->remove_all_filters();
     21        $this->check_priority_non_existent( $hook, $priority );
    2122
    2223        $this->assertFalse( $hook->has_filters() );
     
    3536
    3637        $hook->remove_all_filters( $priority );
     38        $this->check_priority_non_existent( $hook, $priority );
    3739
    3840        $this->assertFalse( $hook->has_filter( $hook_name, $callback_one ) );
    3941        $this->assertTrue( $hook->has_filters() );
    4042        $this->assertSame( $priority + 1, $hook->has_filter( $hook_name, $callback_two ) );
     43        $this->check_priority_exists( $hook, $priority + 1 );
     44    }
     45
     46    protected function check_priority_non_existent( $hook, $priority ) {
     47        $priorities = $this->get_priorities( $hook );
     48
     49        $this->assertNotContains( $priority, $priorities );
     50    }
     51
     52    protected function check_priority_exists( $hook, $priority ) {
     53        $priorities = $this->get_priorities( $hook );
     54
     55        $this->assertContains( $priority, $priorities );
     56    }
     57    protected function get_priorities( $hook ) {
     58        $reflection          = new ReflectionClass( $hook );
     59        $reflection_property = $reflection->getProperty( 'priorities' );
     60        $reflection_property->setAccessible( true );
     61
     62        return $reflection_property->getValue( $hook );
    4163    }
    4264}
  • trunk/tests/phpunit/tests/hooks/removeFilter.php

    r53804 r56609  
    1818        $hook->add_filter( $hook_name, $callback, $priority, $accepted_args );
    1919        $hook->remove_filter( $hook_name, $callback, $priority );
     20        $this->check_priority_non_existent( $hook, $priority );
    2021
    2122        $this->assertArrayNotHasKey( $priority, $hook->callbacks );
     
    3233        $hook->add_filter( $hook_name, $callback, $priority, $accepted_args );
    3334        $hook->remove_filter( $hook_name, $callback, $priority );
     35        $this->check_priority_non_existent( $hook, $priority );
    3436
    3537        $this->assertArrayNotHasKey( $priority, $hook->callbacks );
     
    4547        $hook->add_filter( $hook_name, $callback, $priority, $accepted_args );
    4648        $hook->remove_filter( $hook_name, $callback, $priority );
     49        $this->check_priority_non_existent( $hook, $priority );
    4750
    4851        $this->assertArrayNotHasKey( $priority, $hook->callbacks );
     
    6366
    6467        $this->assertCount( 1, $hook->callbacks[ $priority ] );
     68        $this->check_priority_exists( $hook, $priority, 'Has priority of 2' );
    6569    }
    6670
     
    7781
    7882        $hook->remove_filter( $hook_name, $callback_one, $priority );
     83        $this->check_priority_non_existent( $hook, $priority );
    7984        $this->assertArrayNotHasKey( $priority, $hook->callbacks );
    8085        $this->assertCount( 1, $hook->callbacks[ $priority + 1 ] );
     86        $this->check_priority_exists( $hook, $priority + 1, 'Should priority of 3' );
     87    }
     88
     89    protected function check_priority_non_existent( $hook, $priority ) {
     90        $priorities = $this->get_priorities( $hook );
     91
     92        $this->assertNotContains( $priority, $priorities );
     93    }
     94
     95    protected function check_priority_exists( $hook, $priority ) {
     96        $priorities = $this->get_priorities( $hook );
     97
     98        $this->assertContains( $priority, $priorities );
     99    }
     100
     101    protected function get_priorities( $hook ) {
     102        $reflection          = new ReflectionClass( $hook );
     103        $reflection_property = $reflection->getProperty( 'priorities' );
     104        $reflection_property->setAccessible( true );
     105
     106        return $reflection_property->getValue( $hook );
    81107    }
    82108}
Note: See TracChangeset for help on using the changeset viewer.