[RFC] array_search_range() function

php.internals

سپهر محمودی

15 days ago
Hello internals, I'd like to open discussion on a new RFC proposing the `array_search_range()` function. This function extends `array_search()` by allowing the search to be restricted to a specific range of the array, using an `$offset` and `$length` parameter — similar in spirit to how `array_slice()` works. RFC: https://wiki.php.net/rfc/array_search_range Implementation: https://github.com/php/php-src/pull/23325 Proposed signature: function array_search_range(mixed $needle, array $haystack, int $offset = 0, ?int $length = null, bool $strict = false): int|false The main motivation is to avoid the overhead of slicing a large array just to search within a known sub-range, which is a common pattern when working with pagination or windowed processing. Example: $arr = ['a', 'b', 'c', 'd', 'e']; var_dump(array_search_range('c', $arr, 1, 3)); // 2 var_dump(array_search_range('a', $arr, 3, 3)); // false The RFC is now Under Discussion. I welcome your feedback, questions, and suggestions. Best regards, Sepehr

Larry Garfield

14 days ago
On Mon, Aug 17, 2026, at 4:46 AM, سپهر محمودی wrote:
> Hello internals, > > I'd like to open discussion on a new RFC proposing the > `array_search_range()` function. > > This function extends `array_search()` by allowing the search to be > restricted to a specific range of the array, using an `$offset` and > `$length` parameter — similar in spirit to how `array_slice()` works. > > RFC: https://wiki.php.net/rfc/array_search_range > Implementation: https://github.com/php/php-src/pull/23325 > > Proposed signature: > > function array_search_range(mixed $needle, array $haystack, int > $offset = 0, ?int $length = null, bool $strict = false): int|false > > The main motivation is to avoid the overhead of slicing a large array > just to search within a known sub-range, which is a common pattern when > working with pagination or windowed processing. > > Example: > > $arr = ['a', 'b', 'c', 'd', 'e']; > var_dump(array_search_range('c', $arr, 1, 3)); // 2 > var_dump(array_search_range('a', $arr, 3, 3)); // false > > The RFC is now Under Discussion. I welcome your feedback, questions, > and suggestions. > > Best regards, > Sepehr
I agree with Rowan's comments in the previous thread. I'd rather see an efficient array-range syntax first, then array_search() will "just work" in this way. I don't see it as a common enough use case that it is worth its own dedicated special function. --Larry Garfield

سپهر محمودی

14 days ago
Hi Rowan, Thanks for the detailed and thoughtful feedback — I really appreciate it. You make a great point. A “lazy array slice” (like Swift’s ArraySlice) would indeed be a more general and composable solution, and it would benefit array_find(), array_find_key(), array_all(), array_any() and others — not just array_search(). That said, I see array_search_range() as a small, focused step that solves a concrete and common problem today, without waiting for a much larger design discussion. I’d be genuinely interested in your opinion on the best path forward: 1. Should we proceed with array_search_range() as a focused addition now, and explore “lazy array slice” as a separate, broader RFC? 2. Or do you think it would be better to fold this idea into the larger concept from the start? Either way, I’m happy to collaborate and learn from the discussion. Regards, Sepehr On دوشنبه ۱۷ اوت ۲۰۲۶، ۲۲:۳۰ Larry Garfield <larry@garfieldtech.com> wrote:

سپهر محمودی

14 days ago
Hi Larry, Apologies — my previous message was addressed to Rowan by mistake, but it was meant as a reply to the thread, and I was also responding to your point. Both of your feedback is very helpful. Thanks again for taking the time to share your thoughts. Regards, Sepehr On دوشنبه ۱۷ اوت ۲۰۲۶، ۲۳:۵۵ سپهر محمودی <sepehrphpr@gmail.com> wrote:

Larry Garfield

14 days ago
On Mon, Aug 17, 2026, at 3:39 PM, سپهر محمودی wrote:
> Hi Larry, > > Apologies — my previous message was addressed to Rowan by mistake, > > but it was meant as a reply to the thread, and I was also responding > > to your point. Both of your feedback is very helpful. > > Thanks again for taking the time to share your thoughts. > > Regards, > > Sepehr
First, please do not top post.
> On دوشنبه ۱۷ اوت ۲۰۲۶، ۲۳:۵۵ سپهر محمودی <sepehrphpr@gmail.com> wrote:
>> I’d be genuinely interested in your opinion on the best path forward: >> 1. Should we proceed with array_search_range() as a focused addition >> now, and explore “lazy array slice” as a separate, broader RFC? >> 2. Or do you think it would be better to fold this idea into the >> larger concept from the start? >> >> Either way, I’m happy to collaborate and learn from the discussion. >> >> Regards, >> >> Sepehr
I would favor shelving this idea for now, and instead putting effort into the more robust solution that would benefit everything at once. If you have a specific use case yourself where this would actually make a big performance difference (which I do not believe is the typical case), then a custom extension with a single function seems like it would be pretty easy, especially if you've already built it as a patch. --Larry Garfield

سپهر محمودی

14 days ago
Hi Larry, That’s a fair point, and I agree that a lazy array slice would be a more powerful and general solution in the long run. However, I’d like to point out that such a feature is a very large undertaking. It would require: - A new ArraySlice type in the Zend Engine - Changes to memory management and reference counting - Updating potentially hundreds of array functions to accept it - A major RFC with significant design discussion This is not a small patch — it’s a project that could take months or even years, and it may never land at all. array_search_range(), on the other hand, is small, focused, and ready today. It solves a real, concrete problem without waiting for a much larger design that doesn’t exist yet. My question is: should we block a small, useful improvement on the hope of a much larger feature that may never come? Or would it make more sense to accept the small step now, and let the lazy slice idea evolve separately? I’d genuinely appreciate your thoughts on this. Regards, Sepehr On سه‌شنبه ۱۸ اوت ۲۰۲۶، ۰۱:۲۹ Larry Garfield <larry@garfieldtech.com> wrote:

mickmackusa

14 days ago
As a demonstration, this is new "top posted" text because the earlier posted conversation in the thread exists below the newly written reply text. [BAD] On Tue, 18 Aug 2026, 08:36 سپهر محمودی, <sepehrphpr@gmail.com> wrote:
> Hi Larry, > > [...] > > Regards, > > Sepehr >
This is new "bottom posted" text because the earlier posted conversation in the thread exists above the newly written reply text. [GOOD] In the PHP Internals mailing list, you are expected to only "bottom post". This rule is to allow easy human comprehension of the conversation. By always bottom posting, the discussion in the thread of emails always reads as oldest communication at the top and most recent at the bottom. Sepehr, many of your recent emails have violated this simple rule. Please permanently stop writing your replies with the historic content below your new reply. Also, I am not a voting member of this organization, but I can say that I've never had a professional project that required your proposed function nor do I recall ever encountering a Stack Overflow question which would benefit from your proposed function (and I have been a heavy curator of php&arrays tagged content for many years). That doesn't mean that your proposed function is worthless, I just haven't encountered a use case personally. If a PHP array needed a pagination-style search function, should perhaps the data structure be reconsidered? Is the data structure purpose-built or is the function overcoming a suboptimal data structure? mickmackusa

سپهر محمودی

14 days ago
On سه‌شنبه ۱۸ اوت ۲۰۲۶، ۰۲:۳۳ mickmackusa <mickmackusa@gmail.com> wrote:
> As a demonstration, this is new "top posted" text because the earlier > posted conversation in the thread exists below the newly written reply > text. [BAD] > > > On Tue, 18 Aug 2026, 08:36 سپهر محمودی, <sepehrphpr@gmail.com> wrote: > >> Hi Larry, >> >> [...] >> >> Regards, >> >> Sepehr >> > > This is new "bottom posted" text because the earlier posted conversation > in the thread exists above the newly written reply text. [GOOD] > > In the PHP Internals mailing list, you are expected to only "bottom post". > This rule is to allow easy human comprehension of the conversation. By > always bottom posting, the discussion in the thread of emails always reads > as oldest communication at the top and most recent at the bottom. Sepehr, > many of your recent emails have violated this simple rule. Please > permanently stop writing your replies with the historic content below your > new reply. > > Also, I am not a voting member of this organization, but I can say that > I've never had a professional project that required your proposed function > nor do I recall ever encountering a Stack Overflow question which would > benefit from your proposed function (and I have been a heavy curator of > php&arrays tagged content for many years). That doesn't mean that your > proposed function is worthless, I just haven't encountered a use case > personally. > > If a PHP array needed a pagination-style search function, should perhaps > the data structure be reconsidered? Is the data structure purpose-built or > is the function overcoming a suboptimal data structure? > > mickmackusa >
Hi mickmackusa, Thank you for the feedback. First, I sincerely apologize for the bottom-posting mistake. I am new to the PHP Internals mailing list, and I’m still learning the rules. I will make sure to follow bottom posting correctly from now on. Regarding the use case: I agree that this function is not needed for small arrays, where array_slice() is perfectly fine. However, for very large arrays that are kept in memory (such as cached database results, log files read line by line, streaming data chunks, or paginated queues), array_slice() creates a full copy of the array. This copy costs both time and memory, and it grows with the size of the array. array_search_range() avoids this copy entirely, because it only searches within a range of the existing array without creating a new one. So while the use case may not be common on Stack Overflow, it does exist in real systems that work with large in-memory arrays. I’d be happy to hear more of your thoughts. Regards, Sepehr

Rowan Tommins [IMSoP]

14 days ago
On 17 August 2026 23:34:07 BST, "سپهر محمودی" <sepehrphpr@gmail.com> wrote:
>However, I’d like to point out that such a feature is a very large > >undertaking. It would require: > > - A new ArraySlice type in the Zend Engine > - Changes to memory management and reference counting > - Updating potentially hundreds of array functions to accept it > - A major RFC with significant design discussion
Hi Sepehr, As I said on the previous thread (I'm not sure why you started a new one), I don't think it's that big a task. - It doesn't need to be a new base type, just a new internal class. - We don't have to update every function at once, because a function expecting "array" will naturally reject "ArraySlice" until we change something. - The internal mechanism would either be an implementation of the existing Iterator mechanism, or something very similar. In fact, the simplest starting point would be no functions at all, just an optimised ArraySliceIterator. The actual search part is fairly easy to write in user code. Then in a separate RFC, add a new set of functions like "iter_search", "iter_any", etc, which would be useful with *any* iterator, not just this specific one. And possibly a third RFC to add special syntax for creating an ArraySliceIterator, similar to how it looks in Python or Swift. That way, each small step is useful in itself, but building towards something very general, rather than only solving one narrow use case. Regards, Rowan Tommins [IMSoP]

Rowan Tommins [IMSoP]

14 days ago
On 18 August 2026 16:08:58 BST, "Rowan Tommins [IMSoP]" <imsop.php@rwec.co.uk> wrote:
>In fact, the simplest starting point would be no functions at all, just an optimised ArraySliceIterator. The actual search part is fairly easy to write in user code. > >Then in a separate RFC, add a new set of functions like "iter_search", "iter_any", etc, which would be useful with *any* iterator, not just this specific one.
Just to add, both ArraySliceIterator and iter_search can be implemented in less than twenty lines of code each: <https://3v4l.org/HmYZV> Which is great for adding them natively: users of older versions or who need to support multiple versions can "polyfill" them to start using straight away, and then get more optimised versions when they upgrade. To be fair, the same is true of array_search_range: a memory-efficient user implementation is simple, but can't be quite as time-efficient as an internal one because it can't use optimisations based on the memory layout of the array. Only lightly tested, but covers all of the proposed signature in about 30 lines: <https://3v4l.org/VAsXY> For the archive, here's the sample implementations: ``` class ArraySliceIterator implements IteratorAggregate { private LimitIterator $backingIterator; public function __construct(array $array, int $offset, int $limit) { $this->backingIterator = new LimitIterator( new ArrayIterator($array), $offset, $limit ); } public function getIterator(): Traversable { return $this->backingIterator; } } function iter_search(mixed $needle, iterable $haystack, bool $strict = false): int|string|false { foreach ( $haystack as $key => $value ) { if ( ( $strict && $value === $needle ) || ( ! $strict && $value == $needle ) ) { return $key; } } return false; } function array_search_range( mixed $needle, array $haystack, int $offset = 0, ?int $length = null, bool $strict = false, ): int|string|false { if ( $offset < 0 ) { $offset = count($haystack) + $offset; } if ( $length < 0 ) { $length = count($haystack) + $length - $offset; } $currentOffset = -1; foreach ( $haystack as $key => $value ) { $currentOffset++; if ( $currentOffset < $offset ) { continue; } if ( $length !== null && $currentOffset >= $offset + $length ) { break; } if ( ( $strict && $value === $needle ) || ( ! $strict && $value == $needle ) ) { return $key; } } return false; } ``` Regards, Rowan Tommins [IMSoP]

سپهر محمودی

13 days ago
On سه‌شنبه ۱۸ اوت ۲۰۲۶، ۲۰:۳۴ Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:
> On 18 August 2026 16:08:58 BST, "Rowan Tommins [IMSoP]" < > imsop.php@rwec.co.uk> wrote: > >In fact, the simplest starting point would be no functions at all, just > an optimised ArraySliceIterator. The actual search part is fairly easy to > write in user code. > > > >Then in a separate RFC, add a new set of functions like "iter_search", > "iter_any", etc, which would be useful with *any* iterator, not just this > specific one. > > > Just to add, both ArraySliceIterator and iter_search can be implemented in > less than twenty lines of code each: <https://3v4l.org/HmYZV> > > Which is great for adding them natively: users of older versions or who > need to support multiple versions can "polyfill" them to start using > straight away, and then get more optimised versions when they upgrade. > > To be fair, the same is true of array_search_range: a memory-efficient > user implementation is simple, but can't be quite as time-efficient as an > internal one because it can't use optimisations based on the memory layout > of the array. Only lightly tested, but covers all of the proposed signature > in about 30 lines: <https://3v4l.org/VAsXY> > > For the archive, here's the sample implementations: > > ``` > class ArraySliceIterator implements IteratorAggregate { > private LimitIterator $backingIterator; > > public function __construct(array $array, int $offset, int $limit) > { > $this->backingIterator = new LimitIterator( > new ArrayIterator($array), > $offset, > $limit > ); > } > > public function getIterator(): Traversable > { > return $this->backingIterator; > } > } > > function iter_search(mixed $needle, iterable $haystack, bool $strict = > false): int|string|false > { > foreach ( $haystack as $key => $value ) { > if ( > ( $strict && $value === $needle ) > || > ( ! $strict && $value == $needle ) > ) { > return $key; > } > } > return false; > } > > function array_search_range( > mixed $needle, > array $haystack, > int $offset = 0, > ?int $length = null, > bool $strict = false, > ): int|string|false { > if ( $offset < 0 ) { > $offset = count($haystack) + $offset; > } > if ( $length < 0 ) { > $length = count($haystack) + $length - $offset; > } > > $currentOffset = -1; > foreach ( $haystack as $key => $value ) { > $currentOffset++; > if ( $currentOffset < $offset ) { > continue; > } > if ( $length !== null && $currentOffset >= $offset + > $length ) { > break; > } > if ( > ( $strict && $value === $needle ) > || > ( ! $strict && $value == $needle ) > ) { > return $key; > } > } > return false; > } > ``` > > Regards, > > Rowan Tommins > [IMSoP] >
‐-------- Hey Rowan, Man, I owe you one — seriously, thanks a lot for this! Your point about the polyfill was genuinely brilliant. It actually made me realize I was overcomplicating things. I went ahead and added a “Polyfill” section to the RFC, so anyone on an older PHP version can just copy-paste it and start using the function right away. No blockers for adoption anymore. And you hit the nail on the head about the memory layout thing — that’s exactly why the native version still matters. The userland polyfill is memory-friendly, but it walks element-by-element through the iterator. The internal one goes straight at the HashTable and stops the moment it finds a match. That’s the whole story in one sentence, honestly. Here’s the updated RFC: https://wiki.php.net/rfc/array_search_range Would love to hear what you think of the new section. Cheers, Sepehr

Rowan Tommins [IMSoP]

13 days ago
On 18 August 2026 19:32:08 BST, "سپهر محمودی" <sepehrphpr@gmail.com> wrote:
> >Your point about the polyfill was genuinely brilliant. It actually made > >me realize I was overcomplicating things. I went ahead and added a > >“Polyfill” section to the RFC
The polyfill you've added to the RFC uses array_slice, so has to copy part of the array into new memory, and potentially iterate the array elements *twice* (once in array_slice, and again in array_search). The polyfill I included in my last email just loops over the array once, and stops when either a match is found or the limit is reached. That's what I mean by "memory-efficient": there is no need to make a copy of any part of the array. That's basically the same algorithm that you'd need to implement in C, if you didn't build a reusable ArraySliceIterator. (I note that the implementation you've linked doesn't actually compile on any of the CI targets.) There might be some performance gained just by looping in C rather than PHP, but the optimisations I can see being more significant are taking advantage of the memory layout to jump quickly to the initial offset: - Since PHP 7, some PHP arrays are stored "packed" - that is, with the elements sequentially in memory - so you can calculate the memory position of any element without iterating the array at all. You can see that in action if you look up the source code for array_slice, and both ArraySliceIterator and array_search_range could take advantage of it. - For a hash-based array, you might still need to iterate to find the bucket with the initial offset; but an ArraySliceIterator could then cache the memory location of that bucket, so that iterating the same slice a second time was much faster. None of this changes my opinion that the general-purpose ArraySliceIterator is a better feature for the language than a single array_search_range function that doesn't build towards any future scope. Regards, Rowan Tommins [IMSoP]

mickmackusa

12 days ago
Hi Sepehr, I've taken some time to review your proposal more thoroughly and I have some feedback. 1. Is the example in your RFC for a large array of database data an XY Problem? My first instinct would be to build pagination into the query itself rather than process the result set data. 2. Is the example in your RFC for a large array of filesystem data an XY Problem? Should the script which calls `file()` actually do the following to avoid loading the unwanted portions of data in the first place? ```php $file = new SplFileObject('access.log'); $file->seek(100000); $end = 200000; while (!$file->eof() && $file->key() < $end) { if (str_contains($file->current(), 'ERROR')) { echo $file->key(); break; } $file->next(); } ``` 3. I'm still not convinced that slicing a copy of the array is necessary and your proposal seems to be founded on that premise. The enormous data payload is already loaded into memory, it just needs to be iterated. Why can't you use a foreach and conditionally continue/break the loop while searching? In a scenario which involves an indexed array, a for() loop can be used, but for utility a foreach() is more suitable. ```php if (!function_exists('array_search_range')) { function array_search_range( mixed $needle, array $haystack, int $offset = 0, ?int $length = null, bool $strict = false ): int|string|false { $count = count($haystack); if ($offset < 0) { $offset = max(0, $count + $offset); } if ($length === null) { $length = $count - $offset; } elseif ($length < 0) { $length = $count - $offset + $length; } if ($length <= 0 || $offset >= $count) { return false; } $position = 0; $end = $offset + $length; foreach ($haystack as $key => $value) { if ($position >= $end) { break; } if ($position >= $offset) { if ($strict ? $value === $needle : $value == $needle) { return $key; } } ++$position; } return false; } } ``` 4. I am not a fan of "falsible return values" because I prefer to null coalesce in my code and that is the reason that I try to avoid old native PHP functions that return false on an unsuccessful process. I can appreciate that you are trying to maintain consistency with array_search() - which I almost never use because value-searching an array is seldom the most efficient process. 5. Is your proposed function name ideal? The coding intention is to hybridize `array_search()` and `array_slice()`, so shouldn't it be `array_slice_search()` or `array_search_slice()`? The "range" in the function name might mislead developers into believing that the function searches for a range of needles. 6. If your coding intention is to meaningfully interrogate portions of an enormous array and performance is a concern, then perhaps it is time to consider partitioning the enormous array into smaller, more manageable chunks or a more searchable map. On relatively small arrays, I find this proposal even less compelling. Ultimately, I still feel that this RFC is solving a problem that should usually be mitigated by an earlier refactor. 7. I find the last line under the "Why This Function Is Worth It" to be unusual. "Available now: usable today, not after a multi-month RFC" ...well, it's not usable today - it will need to go through the RFC vetting process and then get implemented. Your statement feels like unnecessary and misleading marketing speak. While I can imagine there might be a few developers who can benefit from this RFC, I remain unconvinced that this function would be widely used by PHP developers. mickmackusa