Re: [RFC] [VOTE] PREG_THROW_ON_ERROR
| From: | Larry Garfield | Date: | Sun, 20 Sep 2026 16:10:39 +0000 |
| Subject: | Re: [RFC] [VOTE] PREG_THROW_ON_ERROR | ||
| References: | 1 2 3 4 5 6 7 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-132566@lists.php.net to get a copy of this message | ||
On Sat, Sep 19, 2026, at 8:08 PM, Osama Aldemeery wrote:
> Hi Tim,
>
> I went looking at how some other languages answer this exact question....
> Python, Java and C# each have a counterpart to
>
preg_replace_callback()...that is a regex replace that takes a
> callback and has its own exception to throw.
> And the result across the three languages is unanimous...every one of
> them lets the callback's exception propagate as-is.
>
> Here is each one:
> • Python: https://onecompiler.com/python/453rzfyge
> • C#: https://onecompiler.com/csharp/453rzn4dq
> • Java: https://onecompiler.com/java/453rwcp43
> This one does not just behave this way...but the docs spell it out as
> the method's contract
>
> <https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Matcher.html#:~:text=Exceptions%20thrown%20by%20the%20function%20are%20relayed%20to%20the%20caller>:
> "Exceptions thrown by the function are relayed to the caller."
> Now, the other arguments made through this thread plus this one settle
> it toward not wrapping, then the flag keeps letting a callback's
> exception propagate and we are done here.
>
> However, if you still read the throwable policy as requiring the wrap,
> then I believe this is now a question about the policy rather than this
> RFC, and I would not want the RFC to hinge on this one question, so I
> scanned the top 4865 packages to see the cost of putting that one
> question aside, and here are the results:
> • preg_replace_callback +
> preg_replace_callback_array: 2,017
> occurrences = 4.9% of the eight functions' total (41,413 occurrences)
> • the other six: 39,396 occurrences = 95.1%
> Anyone can check roughly the same ratio on all of public GitHub with
> these two searches:
> •
>
> https://github.com/search?q=%28%22preg_replace_callback%28%22+OR+%22preg_replace_callback_array%28%22%29+language%3APHP&type=code
> •
>
> https://github.com/search?q=%28%22preg_match%28%22+OR+%22preg_match_all%28%22+OR+%22preg_replace%28%22+OR+%22preg_filter%28%22+OR+%22preg_split%28%22+OR+%22preg_grep%28%22%29+language%3APHP&type=code
> So if we cannot agree on the callback question, I would rather move the
> two callback functions to future scope.
> Partly because I do not want to spend more weeks on a point that could
> end in a rejection either way, and partly because the numbers say the
> useful case is overwhelmingly the one with no callback in it.
>
> That is, the flag ships on the six functions where none of this arises,
> the two callback functions reject the flag for now, and whether their
> version of it wraps or propagates gets settled with the room it needs
> in its own follow-up after this RFC (if it passes).
>
> Regards,
> Osama
When to wrap an exception is a tricky question overall, and one that user-space struggles with a
fair bit, too.
For me, the key deciding question is whether the error indicates a failure of the implementation
details (eg, wrapping a DB error vs wrapping a Redis error) or a failure of the input.
Implementation details not relevant to the caller should get wrapped into a more locally-meaningful
exception, as the current policy states.
However, I believe Python, C#, and Java are correct in this case: The callback is an input.
It's not an implementation detail hidden from the caller, it's explicitly provided by the
caller. Thus if that fails, the caller does care if it's a DB error or Redis error or whatever.
That's in contrast to, say, an invalid regex string, which is an error in the regex system
itself, and thus a Preg exception/error is entirely reasonable.
If the standing policy isn't nuanced enough to handle that, then the standing policy should be
updated.
--Larry Garfield