From 2f5f818e517da584ba066d4a2e9d5431a1dea85f Mon Sep 17 00:00:00 2001 From: Alexander Danilov Date: Fri, 6 Nov 2026 21:53:50 +0200 Subject: [PATCH] ext/standard: Speed up strict in_array()/array_search() with a string needle --- NEWS | 2 + UPGRADING | 2 + ext/standard/array.c | 16 ++++ .../in_array_array_search_strict_string.phpt | 89 +++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 ext/standard/tests/array/in_array_array_search_strict_string.phpt diff --git a/NEWS b/NEWS index 81a3b5ebd558..5a865955281d 100644 --- a/NEWS +++ b/NEWS @@ -193,6 +193,8 @@ PHP NEWS . bindec(), octdec(), hexdec() and base_convert() now throw an Exception when the passed string contains characters that are invalid for the specified number base. (Sjoerd Langkemper) + . Improved performance of in_array() and array_search() in strict mode when + searching for a string. (Alexander Danilov) - URI: . Fix casing of enum cases in UriHostType and UrlHostType to match the RFC diff --git a/UPGRADING b/UPGRADING index 3867497fa9cf..a4df23352639 100644 --- a/UPGRADING +++ b/UPGRADING @@ -206,6 +206,8 @@ PHP 8.7 UPGRADE NOTES UTF-8. . htmlspecialchars() returns its input without copying it when nothing needs encoding. + . Improved performance of in_array() and array_search() in strict mode when + searching for a string. - MBString: . Improved performance of mb_strlen for UTF-8 strings. diff --git a/ext/standard/array.c b/ext/standard/array.c index b4ebd8073216..fe5d7cfd55e5 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1601,6 +1601,22 @@ static zend_always_inline void _php_search_array(zval *return_value, zval *value } } } ZEND_HASH_FOREACH_END(); + } else if (Z_TYPE_P(value) == IS_STRING) { + zend_string *needle = Z_STR_P(value); + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) { + ZVAL_DEREF(entry); + if (Z_TYPE_P(entry) == IS_STRING && zend_string_equals(Z_STR_P(entry), needle)) { + if (behavior == 0) { + RETURN_TRUE; + } else { + if (str_idx) { + RETURN_STR_COPY(str_idx); + } else { + RETURN_LONG(num_idx); + } + } + } + } ZEND_HASH_FOREACH_END(); } else { ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(array), num_idx, str_idx, entry) { ZVAL_DEREF(entry); diff --git a/ext/standard/tests/array/in_array_array_search_strict_string.phpt b/ext/standard/tests/array/in_array_array_search_strict_string.phpt new file mode 100644 index 000000000000..fcf4903f7403 --- /dev/null +++ b/ext/standard/tests/array/in_array_array_search_strict_string.phpt @@ -0,0 +1,89 @@ +--TEST-- +in_array()/array_search() strict mode with a string needle +--FILE-- + 'a', 'y' => 'b', 7 => 'c'], true)); +var_dump(array_search('c', ['x' => 'a', 'y' => 'b', 7 => 'c'], true)); + +echo "-- elements of other types never match --\n"; +var_dump(in_array('1', [1, 1.0, true, [1], null], true)); +var_dump(in_array('', [null, false, 0, []], true)); +var_dump(in_array('abc', [0, true, new stdClass], true)); +var_dump(array_search('1', [1, '1'], true)); + +echo "-- references --\n"; +$s = 'ref'; +$arr = [1, &$s, 3]; +var_dump(array_search('ref', $arr, true)); +$s = 'changed'; +var_dump(in_array('ref', $arr, true)); +var_dump(array_search('changed', $arr, true)); +$i = 5; +var_dump(in_array('5', ['a', &$i], true)); + +echo "-- holes --\n"; +$packed = ['a', 'b', 'c']; +unset($packed[1]); +var_dump(in_array('b', $packed, true)); +var_dump(array_search('c', $packed, true)); +$hash = ['x' => 'a', 'y' => 'b']; +unset($hash['x']); +var_dump(array_search('b', $hash, true)); + +echo "-- empty array --\n"; +var_dump(in_array('a', [], true)); + +?> +--EXPECT-- +-- found / not found -- +bool(true) +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) +int(1) +bool(false) +-- non-interned strings -- +bool(true) +bool(true) +int(0) +-- string and integer keys -- +string(1) "y" +int(7) +-- elements of other types never match -- +bool(false) +bool(false) +bool(false) +int(1) +-- references -- +int(1) +bool(false) +int(1) +bool(false) +-- holes -- +bool(false) +int(2) +string(1) "y" +-- empty array -- +bool(false)