Hi Tim, Jakub,

On Tue, Mar 31, 2026 at 5:52 PM Tim Düsterhus <[email protected]> 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!
>
...
> 3. 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.

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 :).

-- 
Pierre

Reply via email to