Re: [PHP-DEV] [RFC] [VOTE] PREG_THROW_ON_ERROR

[email protected] (Sascha Ploss | F&P GmbH)
Newsgroups php.internals
Organization F&P GmbH
Message-ID <[email protected]>
Hi Tim,

Am 04.09.2026 um 19:51 schrieb Tim Düsterhus:
> 2. I disagree with the behavior of not wrapping Exceptions thrown in 
> user callbacks: I believe the correct choice is to throw a 
> \PregException with the Exception thrown in the callback as the 
> `->previous` exception. Not wrapping the user callback exception means 
> that one needs a `catch(Exception)` with a try just around the preg_ 
> call to reliably handle all errors during regular expression 
> execution, which nullifies much of the benefit of having a dedicated 
> exception class in the first place.
>
> It also violates the exception policy in 
> https://github.com/php/policies/blob/main/coding-standards-and-naming.rst#throwables, 
> which states:
>
>> If an extension uses external functionality that may throw an 
>> exception it MUST wrap any exception thrown by that functionality 
>> into an appropriate exception of its own. It MUST set the $previous 
>> property to the original exception when doing so.

Following the principle of least surprise, i would expect that this code:

try {
     preg_replace_callback($pattern, $callback, $subject);
} catch (ValidationException $e) {
     // Handle it
}

can be changed to the following, and still work like expected:

try {
     preg_replace_callback($pattern, $callback, $subject, flags: 
PREG_THROW_ON_ERROR);
} catch (ValidationException $e) {
     // Handle it
}

When PREG wraps everything thrown in a user supplied callback into an 
PregException, above code would no longer work.
Instead, we would need this:

try {
     preg_replace_callback($pattern, $callback, $subject, flags: 
PREG_THROW_ON_ERROR);
} catch (PregException $e) {
     if ($e->getPrevious() instanceof ValidationException) {
         // Handle it
     } else {
         throw $e; // Rethrow if it wasn't the one we expected
     }
}


The policy states: "If an extension uses external functionality"
I would argue, that this should only target exceptions from the 
extension's own external dependencies. As a user i do not care about 
internal implementation details of an extension. In fact, it would be 
surprising to the caller to face foreign exceptions. Therefore it makes 
sense for any extension to catch those and wrap them in their own 
exception hierarchy.

However, i would not regard user supplied callbacks to fall under that 
policy rule. The extension itself is not using that code and knows 
nothing about it. It just calls it, following the "Inversion of Control" 
principle back into userland.



As always, a good indicator for questions like this is to check what 
other languages are doing.

In Javascript, the Exception propagates untouched:

class MyError extends Error {}

const text = "test";

try {
   text.replace(/.+/g, (match) => {
     throw new MyError("User error");
   });
} catch (error) {
   console.log(error instanceof MyError); // true
}

Same in Python:

import re

def my_callback(match):
     raise ValueError("User exception")

try:
     re.sub(r'\d+', my_callback, "test")
except ValueError as e:
     print("Caught user exception")


And while i didn't confirm it by running code myself, my research 
pointed to Java, C#, C++, Rust and Ruby doing the same thing.

So it seems to me, it is standard convention in most popular languages 
to let user exceptions propagate transparent.
PHP would be an outlier, if it started wrapping them inside 
PregException. I don't think this would be the right call.

Regards,

Sascha

-- 


*Freitags habe ich immer frei: Wir arbeiten bei F&P in einer 
4-Tage-Woche. *#32istdasneue40**

F&P ♡ Creating Communities

 
<https://www.fp.de>  
<https://www.linkedin.com/company/f&p-gmbh-feig-&-partner/>  
<https://www.instagram.com/fp_creating_communities/>  
<https://www.kununu.com/de/fundp>
F&P GmbH

Feldstraße 53
95152 Selbitz


Tel. 09280 - 98 11 18 - 0


Sitz der Gesellschaft: Feldstraße 53, 95152 
Selbitz
Geschäftsführer: Dr. Ingmar Ackermann, Frank Noack
Amtsgericht Hof, 
HRB 3352
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.