====== RFC: array_str_contains ====== * **Author:** Sepehr Mahmoudi, sepehrphpr@gmail.com * **Status:** Under Discussion * **First Published at:** 2026-08-22 * **Target PHP Version:** PHP 8.7 * **Version:** 0.4 * pr:https://github.com/php/php-src/pull/23512 ===== Introduction ===== Finding elements in an array that contain a specific substring is a very common task in PHP. Currently, developers must rely on ''array_filter()'' combined with a userland closure and ''str_contains()'', or write a custom ''foreach'' loop. Executing a userland callback for every element in a large array using ''array_filter()'' introduces unnecessary performance overhead. This RFC proposes a native C function, ''array_str_contains()'', to perform this task efficiently, eliminating the closure overhead and providing a much cleaner, more readable syntax. ===== Proposal ===== This RFC proposes the addition of a new array function: function array_str_contains( array $haystack, string $needle ): array {} **Parameters:** * ''$haystack'': The input array to be searched. * ''$needle'': The substring to search for within the array values. The function searches for the exact substring ''$needle'' in the values of the ''$haystack''. It returns a new array containing only the matched elements. * The search is **case-sensitive** (consistent with the behavior of ''str_contains'') and performs a strict byte-for-byte comparison. * By matching exact byte sequences natively in C, it provides full, highly efficient support for **UTF-8 characters, non-Latin scripts, and emojis** out of the box, without relying on ''ext/intl'' or incurring any ICU overhead. * Non-string values in the array are implicitly cast to strings before the check. * Original array keys are preserved in the returned array. ===== Use Cases ===== This function simplifies many common filtering tasks, including but not limited to: * **File filtering:** Filtering a list of filenames or paths to find specific extensions (e.g., ''.php'' or ''.jpg''). * **Log analysis:** Searching an array of log lines to extract entries containing specific keywords like ''"ERROR"'' or ''"Warning"''. * **Multilingual & Emoji Data Processing:** Safely and efficiently filtering datasets containing UTF-8 text (like Japanese, Persian, Arabic) or emojis, because the search operates strictly on exact byte sequences rather than requiring Unicode-aware decoding. * **Data processing:** Filtering a dataset of URLs, URIs, or text snippets for a specific substring without writing verbose closures. **Why a native function instead of array_filter?** While the same result can be achieved using ''array_filter()'' combined with a closure and ''str_contains()'', a native ''array_str_contains()'' function provides better ergonomics and cleaner syntax. Furthermore, a native C implementation eliminates the overhead of repeatedly calling closures and context switching for each array element, which can be highly beneficial when processing large arrays or performing heavy log and data analysis. ===== Examples ===== ==== Example 1: Basic Substring Search ==== // When working with a list of URLs, you might want to extract only the links related to a specific section, such as products. $urls = [ 0 => 'https://example.com/products/laptops', 1 => 'https://example.com/about-us', 2 => 'https://example.com/products/phones', 3 => 'https://example.com/contact' ]; $result = array_str_contains($urls, 'products'); /* Returns: [ 0 => 'https://example.com/products/laptops', 2 => 'https://example.com/products/phones' ] */ **Explanation:** The function searches for the exact substring 'products' within the array of URLs. It is case-sensitive, so uppercase variations like 'Products' are not matched. The original array keys are strictly preserved in the returned array. ==== Example 2: Searching in Log Lines ==== $logs = [ 200 => 'INFO: Application started', 201 => 'ERROR: Database timeout', 202 => 'WARNING: CPU load high', 203 => 'ERROR: Invalid user input' ]; $errors = array_str_contains($logs, 'ERROR:'); print_r($errors); /* Output: Array ( [201] => ERROR: Database timeout [203] => ERROR: Invalid user input ) */ **Explanation:** This example demonstrates filtering a list of log messages to easily extract the lines containing the ''"ERROR:"'' substring. Custom keys are maintained in the result. ==== Example 3: Handling Non-String Values ==== $data = [ 'user_100', 100, 10055, 'guest', true ]; $matches = array_str_contains($data, '100'); print_r($matches); /* Output: Array ( [0] => user_100 [1] => 100 [2] => 10055 ) */ **Explanation:** Non-string values are implicitly cast to strings before the comparison. For instance, the integer ''100'' becomes the string ''"100"'', which successfully matches the needle ''"100"''. ==== Example 4: UTF-8, Multilingual and Emoji Support ==== $messages = [ 'msg1' => 'Hello world! 🌍', 'msg2' => 'こんにけは、Yuyaさん', // Japanese: Hello, Yuya-san 'msg3' => 'Ψ³Ω„Ψ§Ω… Ψ―Ω†ΫŒΨ§', 'msg4' => 'γ“γ‚“γ«γ‘γ―γ€δΈ–η•Œ', // Japanese: Hello, World ]; $japanese_matches = array_str_contains($messages, 'こんにけは'); /* Returns: [ 'msg2' => 'こんにけは、Yuyaさん', 'msg4' => 'γ“γ‚“γ«γ‘γ―γ€δΈ–η•Œ' ] */ $emoji_matches = array_str_contains($messages, '🌍'); /* Returns: [ 'msg1' => 'Hello world! 🌍' ] */ **Explanation:** Because the function matches exact byte sequences natively in C, it flawlessly supports UTF-8 characters (like Japanese and Persian) and emojis without requiring any extra configuration, extensions, or Unicode decoding. ===== Polyfill / Userland Equivalent ===== The behavior of the proposed function can be illustrated by the following userland implementation. (Note: This approach uses a standard loop to avoid the closure overhead typical of ''array_filter()''): $value) { // Cast to string to handle non-string values safely, matching str_contains behavior if (str_contains((string) $value, $needle)) { $result[$key] = $value; } } return $result; } ===== Behavior with Numeric & Mixed Values ===== `array_str_contains()` gracefully handles scalar numeric values (integers and floats) alongside strings without throwing type errors, making it robust for real-world data processing (e.g., database records, product IDs, or mixed logs). === Example 1: Basic Mixed Types === $items = ["user_101", 2048, "order_105", 300, "item_20"]; // Searching for "10" matches both string codes and numeric values $result1 = array_str_contains($items, "10"); var_dump($result1); // Output: ["user_101", "order_105"] // Searching for "20" matches the integer 2048 and the string "item_20" $result2 = array_str_contains($items, "20"); var_dump($result2); // Output: [2048, "item_20"] === Example 2: Practical E-Commerce Product Code Filtering === $product_skus = [ "SKU-2026-X", 20260115, // Integer timestamp/batch ID "PROD-9902", 2026998, // Numeric serial number "ACCESSORY-12" ]; // Easily find all items belonging to batch "2026" $batch_2026 = array_str_contains($product_skus, "2026"); var_dump($batch_2026); // Output: ["SKU-2026-X", 20260115, 2026998] ===== Backward Incompatible Changes ===== None. ===== Proposed PHP Version(s) ===== Next minor version (PHP 8.7). ===== RFC Impact ===== * **SAPIs:** No impact. * **Existing Extensions:** No impact. * **Opcache:** No impact. ===== Open Issues ===== None at this time. ===== Proposed Voting Choices ===== State whether to accept the RFC and merge the patch into PHP 8.6. As this is a standard feature addition, it requires a 2/3 majority to pass. ===== Implementation ===== In Progress (TBD). ===== Real-world Usage Analysis ===== A static analysis was performed across 200 widely used PHP packages to identify code patterns related to the proposed ''array_str_contains()'' function. The full dataset, search queries, and methodology are available on GitHub: [[https://gist.github.com/sepehrphpr/d4d371ba682cc43c4f258de94684cc91|Static Analysis Dataset & Methodology]]. The analysis found 32 occurrences of code that perform, or are related to, the operation represented by this function. However, these results should be interpreted as an upper bound rather than as a count of direct replacements. Manual inspection showed that at least 15 of the 32 occurrences contain additional logic and cannot be replaced by ''array_str_contains()'' alone. For example, some callbacks perform further checks, transformations, or conditions in addition to checking whether a string contains a given substring. The remaining occurrences are direct or near-direct uses of the pattern and may be simplified by a dedicated native function. ^ Category ^ Number of occurrences ^ | Total related occurrences | 32 | | Occurrences with additional logic | At least 15 | | Direct or near-direct occurrences | Up to 17 | This analysis does not claim that every identified occurrence can be replaced one-to-one with ''array_str_contains()''. Instead, it demonstrates that the underlying operation appears repeatedly in real-world PHP code and that a native function could simplify at least some of these cases. The results also suggest that the actual number of potential use cases may be higher, since the analysis covered only 200 packages and relied on recognizable static code patterns.