Make WordPress Core

Changeset 62524


Ignore:
Timestamp:
06/18/2026 05:49:04 PM (3 months ago)
Author:
dmsnell
Message:

Performance: avoid over-allocation in wp_is_numeric_array()

When a trace of allocations revealed that wp_is_numeric_array()
accounted for a significant fraction of the allocations in a page
render, it was observed that the function eagerly allocates and copies
array keys and then filters them when all it wants to know is whether a
single key in the array meets a condition.

In this patch the array_filter( array_keys() ) invocation is replaced
with early-aborting iteration to avoid the memory allocation and
copying.

This patch was prepared as part of WCEU 2026 Contributor Day.

Developed in: https://github.com/WordPress/wordpress-develop/pull/12100
Discussed in: https://core.trac.wordpress.org/ticket/65467

Follow-up to [34927].

Props dmsnell, westonruter, yusufmudagal.
Fixes #65467.

Location:
trunk
Files:
2 edited

Legend:

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

    r62521 r62524  
    52915291 * Determines if the variable is a numeric-indexed array.
    52925292 *
     5293 * Note! This answers a different question than {@see array_is_list()} and is
     5294 *       more flexible to handle situations where some numeric array indices
     5295 *       have been removed. A numeric-indexed array is only a “list” when the
     5296 *       array keys form a contiguous range from zero to the highest key.
     5297 *
     5298 * Example:
     5299 *
     5300 *     true  === wp_is_numeric_array( array( 1, 2, 3, 4 ) );
     5301 *     false === wp_is_numeric_array( array( 'name' => 'WordPress' ) );
     5302 *
     5303 *     // All-numeric keys vs. list.
     5304 *     $above_two   = array_filter( array( 1, 2, 8, 9 ), fn ( $v ) => $v > 2 );
     5305 *     $above_two === array( '2' => 8, '3' => 9 );
     5306 *     true       === wp_is_numeric_array( $above_two );
     5307 *     false      === array_is_list( $above_two );
     5308 *
    52935309 * @since 4.4.0
    52945310 *
    52955311 * @param mixed $data Variable to check.
    52965312 * @return bool Whether the variable is a list.
    5297  */
    5298 function wp_is_numeric_array( $data ) {
     5313 *
     5314 * @phpstan-assert-if-true array<int, mixed> $data
     5315 */
     5316function wp_is_numeric_array( $data ): bool {
    52995317        if ( ! is_array( $data ) ) {
    53005318                return false;
    53015319        }
    53025320
    5303         $keys        = array_keys( $data );
    5304         $string_keys = array_filter( $keys, 'is_string' );
    5305 
    5306         return count( $string_keys ) === 0;
     5321        foreach ( $data as $key => $value ) {
     5322                if ( is_string( $key ) ) {
     5323                        return false;
     5324                }
     5325        }
     5326
     5327        return true;
    53075328}
    53085329
  • trunk/tests/phpunit/tests/functions/wpIsNumericArray.php

    r56971 r62524  
    2727        public function data_wp_is_numeric_array() {
    2828                return array(
    29                         'no index'             => array(
     29                        'no index'                             => array(
    3030                                'test_array' => array( 'www', 'eee' ),
    3131                                'expected'   => true,
    3232                        ),
    33                         'text index'           => array(
     33                        'text index'                           => array(
    3434                                'test_array' => array( 'www' => 'eee' ),
    3535                                'expected'   => false,
    3636                        ),
    37                         'numeric index'        => array(
     37                        'numeric index'                        => array(
    3838                                'test_array' => array( 99 => 'eee' ),
    3939                                'expected'   => true,
    4040                        ),
    41                         '- numeric index'      => array(
     41                        'filtered list (missing numeric keys)' => array(
     42                                'test_array' => array_filter(
     43                                        array( 1, 12, 13, 15, 16, 17, 20 ),
     44                                        fn ( $v ) => 0 === $v % 2
     45                                ),
     46                                'expected'   => true,
     47                        ),
     48                        '- numeric index'                      => array(
    4249                                'test_array' => array( -11 => 'eee' ),
    4350                                'expected'   => true,
    4451                        ),
    45                         'numeric string index' => array(
     52                        'numeric string index'                 => array(
    4653                                'test_array' => array( '11' => 'eee' ),
    4754                                'expected'   => true,
    4855                        ),
    49                         'nested number index'  => array(
     56                        'nested number index'                  => array(
    5057                                'test_array' => array(
    5158                                        'next' => array(
     
    5562                                'expected'   => false,
    5663                        ),
    57                         'nested string index'  => array(
     64                        'nested string index'                  => array(
    5865                                'test_array' => array(
    5966                                        '11' => array(
     
    6370                                'expected'   => true,
    6471                        ),
    65                         'not an array'         => array(
     72                        'not an array'                         => array(
    6673                                'test_array' => null,
    6774                                'expected'   => false,
Note: See TracChangeset for help on using the changeset viewer.