[PHP-DEV] [RFC] Stream Error Handling Improvements

Hi,

On Thu, Apr 2, 2026 at 11:18 AM Pierre Joye <pierre.php@gmail.com> wrote:

Hi Tim, Jakub,

On Tue, Mar 31, 2026 at 5:52 PM Tim Düsterhus <tim@bastelstu.be> wrote:

Hi

Am 2026-03-29 20:25, schrieb Jakub Zelenka:

For the naming of stream_get_last_error(): Within PHP we have both
X_get_last_error() and X_last_error(). The latter seems to be more
common and also what I would prefer here, because the stream_get_
prefix sounds to me like we would get something from a stream, but the
returned value is not related to a specific stream, but rather a
global.

Good point, I changed it but because it now returns array (no linked
list),
it’s called stream_last_errors(). I also added stream_clear_errors for
explicit clearing which might be useful in some situations.

That both makes sense to me.

The RFC and the implementation is updated so please take a look!

  1. For “StreamErrorCode::is*()”

Can error codes fall into multiple categories or is it always a single
one? If it’s guaranteed to be a single category, then perhaps a
->getErrorCategory() method returning a StreamErrorCodeCategory enum
makes more sense and allow for simpler / more efficient code when folks
are interested in checking for multiple different categories. Instead of
$code->isNetworkError() || $code->isFileSystemError() they can do
\in_array($code->getErrorCategory(), [StreamErrorCodeCategory::NetworkError, StreamErrorCodeCategory::FileSystemError], true) or use a match()

These points actually are on the spot about what I’ve been trying to
raise earlier in this thread, or they demonstrate it nicely.

Suggesting a getErrorCategory() method or a StreamErrorCodeCategory
enum to make category checks cleaner. That’s a reasonable API
improvement, in the context of what the RFC proposes in the current
state. But it also highlights the underlying design issue: we’re
building a parallel categorization system on top of error codes, when
the type system already provides exactly this, through exception
subclasses, for free. That reminds a bit of the early OO’s time in php
when all we did was to wrap the legacy procedural implementation in
dumb OO wrapper, saving, if lucky, user’s keystrokes. That’s not even
the case here.

The point is that a stream operation can produce errors of different
categories and therefore the exception “cannot be reasonably
categorised”: this is how exceptions have always worked. One throws
the primary exception, the one that caused the operation to fail, and
chain additional context via $previous or attach it as metadata. The
caller catches what they care about:

catch (StreamNetworkException $e) {…}

The exception type answers “what do I do about this?” The error chain
answers exactly what happens, where in detail. These are different
questions for different uses, and conflating them is what leads to the
current design where it catches a flat StreamException and then has to
inspect its contents to find out what kind of failure it was.

getErrorCategory() suggestion would give us
match($e->getErrors()[0]->getErrorCategory()) { … } inside a catch
block. That’s essentially reimplementing what catch
(StreamNetworkException $e) already does, except the type system can’t
help, static analysis can’t reason about it, and it can’t compose it
with other exception handling in a codebase.

The current RFC introduces exception syntax without exception
semantics. The isFileSystemError(), isNetworkError() methods, and now
potentially getErrorCategory(). are workarounds for the absence of a
typed hierarchy, not features we should need in the 1st place.

Thanks for the thorough feedback. After some considering I decided not to expose those helpers at this stage. The reason is that the current classification is a bit uncertain and I want to have more final set of error codes before anything like this is introduced. The current setup was a bit random and sometimes it could go to multiple categories so there are some decisions to be made. Also the is* methods were not the best design as pointed out and other option should be considered. I’m also not too sure about usefulness of categories. All of this should be decided on its own and later.

Since StreamException does not exist in any current PHP version, there
is zero BC cost to making it a proper base class now. It takes more
effort to design such additions correctly, however it would be a
significant improvement in the long run for php’s stream, beyond a
current internal refactoring and long due cleaning :).

I don’t see how it takes more effort later. There is also pretty much no BC break even if we went if subclassing exceptions as I noted before. I think actually exactly opposite here. If we rush it and use incorrect categories for some errors, it will be a BC break to correct so this needs a proper considering and this feature should be added later.

Kind regards,

Jakub

Hi

Am 2026-04-02 19:25, schrieb Jakub Zelenka:

After doing a review of the categories, I decided to drop all the helpers
and not expose categories in any way. More on the reasoning in my other
replay to Pierre.

That works for me, better start with only the things we are sure about and then add the other bits in a future PHP version.

I've now given the RFC another full read and I don't have further comments. It's looking good to me now and I'm really looking forward to this improved error handling for streams!

Best regards
Tim Düsterhus

hi,

On Fri, Apr 3, 2026, 3:52 AM Tim Düsterhus <tim@bastelstu.be> wrote:

Hi

Am 2026-04-02 19:25, schrieb Jakub Zelenka:

After doing a review of the categories, I decided to drop all the
helpers
and not expose categories in any way. More on the reasoning in my other
replay to Pierre.

That works for me, better start with only the things we are sure about
and then add the other bits in a future PHP version.

I’ve now given the RFC another full read and I don’t have further
comments. It’s looking good to me now and I’m really looking forward to
this improved error handling for streams!

same here, if or when it goes, it would be good to have two votes, the cleaning and refactoring, and the exception addition, to which i will vote against as explained in my earlier replies.

best,

Pierre

@pierrejoye | http://www.libgd.org

Hi,

On Tue, Nov 18, 2025 at 7:38 PM Jakub Zelenka <bukka@php.net> wrote:

Hello,

I would like to introduce a new stream error handling RFC that is part of my stream evolution work (PHP Foundation project funded by Sovereign Tech Fund) :

https://wiki.php.net/rfc/stream_errors

Just a heads up that I plan to open voting on this on Sun 3rd May.

Cheers

Jakub