Skip to content

Commit b7b40c6

Browse files
committed
Add array_str_contains() function to standard library
1 parent 278d290 commit b7b40c6

4 files changed

Lines changed: 162 additions & 0 deletions

File tree

ext/standard/array.c

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7371,3 +7371,53 @@ PHP_FUNCTION(array_combine)
73717371
} ZEND_HASH_FOREACH_END();
73727372
}
73737373
/* }}} */
7374+
/* {{{ Filters array elements that contain a specific substring, preserving keys */
7375+
PHP_FUNCTION(array_str_contains)
7376+
{
7377+
HashTable *haystack;
7378+
zend_string *needle;
7379+
zval *val;
7380+
zend_string *key;
7381+
zend_ulong num_key;
7382+
7383+
ZEND_PARSE_PARAMETERS_START(2, 2)
7384+
Z_PARAM_ARRAY_HT(haystack)
7385+
Z_PARAM_STR(needle)
7386+
ZEND_PARSE_PARAMETERS_END();
7387+
7388+
array_init(return_value);
7389+
7390+
if (zend_hash_num_elements(haystack) == 0) {
7391+
return;
7392+
}
7393+
7394+
/* Empty needle matches every string element */
7395+
bool needle_is_empty = (ZSTR_LEN(needle) == 0);
7396+
7397+
ZEND_HASH_FOREACH_KEY_VAL(haystack, num_key, key, val) {
7398+
ZVAL_DEREF(val);
7399+
7400+
if (Z_TYPE_P(val) != IS_STRING) {
7401+
continue;
7402+
}
7403+
7404+
bool match = false;
7405+
if (needle_is_empty) {
7406+
match = true;
7407+
} else if (Z_STRLEN_P(val) >= ZSTR_LEN(needle)) {
7408+
if (php_memnstr(Z_STRVAL_P(val), ZSTR_VAL(needle), ZSTR_LEN(needle), Z_STRVAL_P(val) + Z_STRLEN_P(val))) {
7409+
match = true;
7410+
}
7411+
}
7412+
7413+
if (match) {
7414+
Z_ADDREF_P(val);
7415+
if (key) {
7416+
zend_hash_add_new(Z_ARRVAL_P(return_value), key, val);
7417+
} else {
7418+
zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, val);
7419+
}
7420+
}
7421+
} ZEND_HASH_FOREACH_END();
7422+
}
7423+
/* }}} */

ext/standard/basic_functions.stub.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,6 +1637,12 @@ function array_walk_recursive(array|object &$array, callable $callback, mixed $a
16371637
*/
16381638
function in_array(mixed $needle, array $haystack, bool $strict = false): bool {}
16391639

1640+
/**
1641+
* @param array $haystack
1642+
* @return array
1643+
*/
1644+
function array_str_contains(array $haystack, string $needle): array {}
1645+
16401646
/**
16411647
* @compile-time-eval
16421648
*/

ext/standard/basic_functions_arginfo.h

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
--TEST--
2+
array_str_contains() basic and edge cases tests
3+
--FILE--
4+
<?php
5+
6+
echo "--- Basic Search ---\n";
7+
$fruits = ['apple', 'banana', 'cherry', 'pineapple', 'grape'];
8+
var_dump(array_str_contains($fruits, 'apple'));
9+
10+
echo "\n--- Preserving String and Numeric Keys ---\n";
11+
$data = [
12+
'first' => 'hello world',
13+
10 => 'foo bar',
14+
'third' => 'world domination',
15+
20 => 'just php',
16+
];
17+
var_dump(array_str_contains($data, 'world'));
18+
19+
echo "\n--- Non-string Values Ignored ---\n";
20+
$mixed = [
21+
'a' => 'test 1',
22+
'b' => 123,
23+
'c' => null,
24+
'd' => 'another test',
25+
'e' => ['nested'],
26+
'f' => true,
27+
];
28+
var_dump(array_str_contains($mixed, 'test'));
29+
30+
echo "\n--- Empty Needle (Matches All String Elements) ---\n";
31+
$items = ['one', 2, 'three', false, ''];
32+
var_dump(array_str_contains($items, ''));
33+
34+
echo "\n--- UTF-8 and Multi-byte Support ---\n";
35+
$persian = [
36+
'سلام دنیا',
37+
'خداحافظ دنیا',
38+
'تست پی‌اچ‌پی',
39+
'PHP 8.7 عالیه 🚀',
40+
'یک ایموجی دیگر 🚀'
41+
];
42+
var_dump(array_str_contains($persian, 'دنیا'));
43+
var_dump(array_str_contains($persian, '🚀'));
44+
45+
echo "\n--- Empty Haystack ---\n";
46+
var_dump(array_str_contains([], 'anything'));
47+
48+
?>
49+
--EXPECT--
50+
--- Basic Search ---
51+
array(2) {
52+
[0]=>
53+
string(5) "apple"
54+
[3]=>
55+
string(9) "pineapple"
56+
}
57+
58+
--- Preserving String and Numeric Keys ---
59+
array(2) {
60+
["first"]=>
61+
string(11) "hello world"
62+
["third"]=>
63+
string(18) "world domination"
64+
}
65+
66+
--- Non-string Values Ignored ---
67+
array(2) {
68+
["a"]=>
69+
string(6) "test 1"
70+
["d"]=>
71+
string(12) "another test"
72+
}
73+
74+
--- Empty Needle (Matches All String Elements) ---
75+
array(3) {
76+
[0]=>
77+
string(3) "one"
78+
[2]=>
79+
string(5) "three"
80+
[4]=>
81+
string(0) ""
82+
}
83+
84+
--- UTF-8 and Multi-byte Support ---
85+
array(2) {
86+
[0]=>
87+
string(17) "سلام دنیا"
88+
[1]=>
89+
string(23) "خداحافظ دنیا"
90+
}
91+
array(2) {
92+
[3]=>
93+
string(18) "PHP 8.7 عالیه 🚀"
94+
[4]=>
95+
string(30) "یک ایموجی دیگر 🚀"
96+
}
97+
98+
--- Empty Haystack ---
99+
array(0) {
100+
}

0 commit comments

Comments
 (0)