Table of Contents

RFC: array_str_contains

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:

The function searches for the exact substring $needle in the values of the $haystack. It returns a new array containing only the matched elements.

Use Cases

This function simplifies many common filtering tasks, including but not limited to:

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

// 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()):

<?php
function array_str_contains(array $haystack, string $needle): array {
    $result = [];
    foreach ($haystack as $key => $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

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: 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.