On Fri, Nov 6, 2026, at 6:59 AM, Artem Ukrainskiy wrote:
Hi internals,
I'd like to open discussion on this RFC:
PHP: rfc:prepared_literal_string_matching
It proposes Str\Matcher and Str\Replacer for prepared literal substring
search and replacement. Matcher provides presence checks, unique matched
keywords, occurrence counts, and enumeration of all overlapping matches
with byte offsets, using Str\MatchResult objects. Replacer performs
non-overlapping replacements, choosing the earliest start and then the
longest key, without rescanning inserted text.
Both classes prepare an immutable dictionary during construction.
Matching is byte-oriented, binary-safe, and case-sensitive. Objects can be
reused within a request or in long-running applications.
I've implemented the API and published reproducible benchmarks covering
both prepared calls and one-shot use, including construction and destruction.
Preparation can pay off even for a single call on some inputs. On others,
existing PHP functions or PCRE remain faster, even than a prepared object.
I propose including these classes in ext/standard in the Str namespace.
The current implementation is in ext/strmatch. I'd welcome feedback on
the API, integration, error types, and whether Matcher and Replacer
should be considered together or separately.
Implementation:
[RFC] ext/strmatch: Add prepared string matching and replacement by ArtUkrainskiy · Pull Request #25122 · php/php-src · GitHub
Benchmarks, methodology and raw data:
php-src-bench/reports/multi-pattern-search at main · ArtUkrainskiy/php-src-bench · GitHub
--
Best regards,
Artem Ukrainskiy
This looks interesting, and I'm not opposed to it. It somewhat parallels the recently discussed Pcre class.
My main question is that this feels very niche. Based on the benchmarks, if I'm reading them correctly, it sounds like 90% of uses would be better served with the existing APIs. Does this make sense for stdlib then? It would be helpful to include more practical examples of where I'd want to use this instead of str_contains(), rather than just a table of byte counts. The "Where the API can help" section isn't sufficient as-is.
For example... would this pay off in a long-running process like FrankenPHP or Swoole, if the object is saved between requests?
Other issues:
- Matcher::matchAll() returns a list, but each of the items in it contains, I believe, a unique value. Could it then return a keyed array for easier lookup? (The example suggests I may be wrong, but if so, please spell this out explicitly.)
- The inout $count argument to Replacer::replace(): Eew. Please explore a better way of handling that. inout params in a language like PHP are the absolute last resort.
- I'm not clear if it says explicitly anywhere if there's behavioral differences from the existing functions. If so it wasn't clear. Would swapping Replacer in for strtr() require any subtle logic changes, or is it a drop-in syntax change? That should be made explicit.
- "Reinitializing an already prepared object throws an Error, even if its dictionary is empty." - How would one reinitialize it? Do you mean calling the constructor explicitly, which PHP allows even though it's never ever safe to do anyway?
If so, please clarify that's what you mean.
- "two distinct objects compare unequal with ==, even if their dictionaries are identical. " - I disagree with this. The whole point of == on objects is to ask "are these equivalent," and two Matchers with the same dictionary are equivalent. Thus they should be ==.
- The RFC notes that these routines are byte-based only, which is fine, as that's what most of PHP is. However, is there a use for a Unicode/Grapheme equivalent? Even if future scope, it's worth mentioning.
- In "Design choices," you list how existing implementations in other languages differ. Please also explain why you've chosen to not follow the same semantics as one of the existing tools, as that means someone moving from those languages to PHP is going to be easily confused by the edge cases.
- In "Where existing tools are preferable", there's a broken link in the 2nd paragraph.
- These are closely related enough that I think a single vote is fine. There's no need to separate them.
--Larry Garfield