Skip to content

Commit 9569f22

Browse files
committed
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: WordPress#12100 Discussed in: https://core.trac.wordpress.org/ticket/65467 Follow-up to [34927]. Props dmsnell, westonruter, yusufmudagal. Fixes #65467. git-svn-id: https://develop.svn.wordpress.org/trunk@62524 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 54eca7b commit 9569f22

2 files changed

Lines changed: 40 additions & 12 deletions

File tree

src/wp-includes/functions.php

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5290,20 +5290,41 @@ function _wp_to_kebab_case( $input_string ) {
52905290
/**
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.
5313+
*
5314+
* @phpstan-assert-if-true array<int, mixed> $data
52975315
*/
5298-
function wp_is_numeric_array( $data ) {
5316+
function 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' );
5321+
foreach ( $data as $key => $value ) {
5322+
if ( is_string( $key ) ) {
5323+
return false;
5324+
}
5325+
}
53055326

5306-
return count( $string_keys ) === 0;
5327+
return true;
53075328
}
53085329

53095330
/**

tests/phpunit/tests/functions/wpIsNumericArray.php

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,43 +26,50 @@ public function test_wp_is_numeric_array( $input, $expected ) {
2626
*/
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(
5259
11 => 'vvv',
5360
),
5461
),
5562
'expected' => false,
5663
),
57-
'nested string index' => array(
64+
'nested string index' => array(
5865
'test_array' => array(
5966
'11' => array(
6067
'eee' => 'vvv',
6168
),
6269
),
6370
'expected' => true,
6471
),
65-
'not an array' => array(
72+
'not an array' => array(
6673
'test_array' => null,
6774
'expected' => false,
6875
),

0 commit comments

Comments
 (0)