Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:130535 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id 686EF1A00BC for ; Thu, 2 Apr 2026 17:36:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1775151408; bh=Y/PuW0zDMX7nBWzVr42zdolNZQQbGi2cNO+wS99Zpzo=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=jKk3sxGdmVM3ktkE3Ok0Xc3uwB/TrTB54h9vEMR/RIdCu+N5zhlbLalN5cA47YM9G 77ndSFfjFgSukecYX99fdEYvn7KWXCOC8mLflZMRRsvpHrybGTSIT7BeQ8XywPqqwH e4r7UhC8nQ44HZ6YiRaxhaSgb6JjNG7dyW+PXH30c8DI1CJxMRN6tKvBUcbNwV4mzX 4euQ04aIRqqx+ih3cKBpAoYhvVvbrGyZ3l1d0KE1/50u6fyFkeM9CczgYnwk/y7eHH ltWygvpIiMOpvzWC8tpc2QPwe7evZgY467KPypeYhfEgQmh+ukGUgaC3Qtn8g2sXeV btoK70+LGXrQw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 6BEA6180057 for ; Thu, 2 Apr 2026 17:36:32 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: * X-Spam-Status: No, score=1.7 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_50, DMARC_NONE,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,HTML_MESSAGE,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-oi1-f176.google.com (mail-oi1-f176.google.com [209.85.167.176]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 2 Apr 2026 17:36:32 +0000 (UTC) Received: by mail-oi1-f176.google.com with SMTP id 5614622812f47-4671cbce465so456075b6e.3 for ; Thu, 02 Apr 2026 10:36:27 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1775151386; cv=none; d=google.com; s=arc-20240605; b=ea/RVfjPAo3lq9U+8LpnDacuRoNyTUBXldwfVrZ88i11jZfDD3QHP9+3Vkmz/qX1XF rqsDIStLrPeqL/iwd024apUmTRyEd3DbUX845w6truFSTsIIFnaJBX2S53uUiad+meLF dWeRGJ1DqEFCFxR259UNelAGxDc6j1/AgW6smq2zZW6rv7HWNCFRaV+SObQP+lELwC08 7yyxMN5GN0z9B3vUPeqLTAbDSP7/aT2wZAY2iZrW8LHONzl4JN7FEtUpVtsbj07CqUSV 7nQp9B6JS92k9t83Amiu9J2WsyrztOO91gDl9kcd5oTz7xc0ImAIZcYyPSiJqGXrA4Kr tswg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version; bh=U4nWle0TaCE80k2k15A4IzmILm1Jjg0Cm9FBJVn1HKM=; fh=nwulvJE+n6vPanvJqsln7Tv3kjp8rNUwSPQo0Erxf+s=; b=RkbUH6mpJRqXvd6r4w+ooMeg5ZrVkoaox5Qjp6WFFOiJRciiOISIn2cyACacS/aW1c 3uGL/OAKwr7BpmqLq3Iu7TX7CaPrfMx6q6d2VmNhTOV97ntxG4OgBviynJLkzklETiV6 JBas30FClGlYL+5WAedzybBCto7w7Qt9t7NEq+W3wcV3P4PZSSy1IdZ7T1S3oFy+sKTs EgC7iLCKeQwKuDAeOcSxFKviCvD00Rp6JQmrVGZV+81DuP+LAly2nnPkLaIc8POzToRd 09rEdvTuu1u7cUgwDGXSVfSSLRLUvkNTFatgC+CxIAidOzTTRWGHVwIEq+L7LWNsjgUy Hp6g==; darn=lists.php.net ARC-Authentication-Results: i=1; mx.google.com; arc=none X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1775151386; x=1775756186; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=U4nWle0TaCE80k2k15A4IzmILm1Jjg0Cm9FBJVn1HKM=; b=jl+dxzso6hW9ytC1m6gP4FmBwp/O4eXC9S45D6cGqhLdcmvXGsYFjbs8GX7eoAUQ5J L071qdIUXYO7BguEax5WREkVTSVgnm+Kr7kgpIqO6jsvVSE0Ph03mErWGP+25UdPl1jg ZkCQfkqJMo0U1R3zube7otZe3KqO0WsJBPjutSZLaPvCJLL7+DisRZyxoFzWVGnzxRXd VtU40KLYtyQLosrb6iYsrTEvGyTzKxxMV5Q6W4MOOHYbyPOi7bRUpz1utGHyOEN9LpLG xjL1wsYANR6h+n0WbBQ0CyG0UCRV0QVzf8IjVF4+pb9ABuROrbZss+tNnaftb7KRa28v a2kA== X-Forwarded-Encrypted: i=1; AJvYcCUn7hCXkn1Ae3eDtAFtZiKOUUok65IOmlNvZ3KD3koR1/1WzFbcXBv0/U6TS31b1zV+nNDeycrYCvg=@lists.php.net X-Gm-Message-State: AOJu0Yx1OHXol+499weg3C7zJz5GT2lTyhKr8pgyJHnS4KKNNhu2VTjw ohwy4ZelpzhR2VTl/MLKzMXY3MihzhbsBInGJKR6nDEUf68acTOezop8OujXcBakradPaGAFL+Z vgEKDjImsVsAQ6tVI+FYge+PPSWJAcyY= X-Gm-Gg: ATEYQzxahl1SsFBGHpKIJ6Vxz/isveP8gAAANze6ZnF9dYmReSyBp1/2Ly+zmCuDnFi kbJ9rkqEBqFXrk8IzkEzxu9lY+PHP1EPMlWzYy6D3GHDuam1g5OM6pSBenRx6dNlIWR8yyRnWJs mQpvrIWW1b65z6B0t6tD4XGvkX60VsAy1ntgU0KoRBM7eO74Nsw5XYwuecIM3h6m6VisjqluBg6 gEeO7TNz2HplXze/kHRqqwoCzhrZokN7vH7AAx5Tzl6Z7so6gejArzV2m2D9LGGqzZsuexxQobB RDJBoBY= X-Received: by 2002:a05:6808:6d8d:b0:467:49c0:5f6d with SMTP id 5614622812f47-46ef62e99a8mr130119b6e.23.1775151386238; Thu, 02 Apr 2026 10:36:26 -0700 (PDT) Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 References: <1597c99a0ad7dc1e55a40f952d7536c5@bastelstu.be> In-Reply-To: Date: Thu, 2 Apr 2026 19:36:14 +0200 X-Gm-Features: AQROBzBHtCWyUr9tb-Mvmmv8s91rwVEaL2pDhx3I1Sb2repUbE8H9pPqWbMin3E Message-ID: Subject: Re: [PHP-DEV] Re: [RFC] Stream Error Handling Improvements To: Pierre Joye Cc: =?UTF-8?Q?Tim_D=C3=BCsterhus?= , Nicolas Grekas , PHP internals list Content-Type: multipart/alternative; boundary="000000000000cca472064e7da368" From: bukka@php.net (Jakub Zelenka) --000000000000cca472064e7da368 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi, On Thu, Apr 2, 2026 at 11:18=E2=80=AFAM Pierre Joye = wrote: > Hi Tim, Jakub, > > On Tue, Mar 31, 2026 at 5:52=E2=80=AFPM Tim D=C3=BCsterhus 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 mo= re > > >> 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 t= he > > >> 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 f= or > > > 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 o= f > > `$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 --000000000000cca472064e7da368 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi,

On Thu, Apr 2, 2026 at 11:18= =E2=80=AFAM Pierre Joye <pierre.= php@gmail.com> wrote:
Hi Tim, Jakub,

On Tue, Mar 31, 2026 at 5:52=E2=80=AFPM Tim D=C3=BCsterhus <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 ha= ve both
> >> `X_get_last_error()` and `X_last_error()`. The latter seems t= o 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 rathe= r a
> >> global.
> >>
> >>
> > Good point, I changed it but because it now returns array (no lin= ked
> > list),
> > it's called stream_last_errors(). I also added=C2=A0 stream_c= lear_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!<= br> >
...
> 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 e= num
> makes more sense and allow for simpler / more efficient code when folk= s
> 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<= br> when all we did was to wrap the legacy procedural implementation in
dumb OO wrapper, saving, if lucky, user's keystrokes. That's not ev= en
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 c= hain
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<= br> block. That's essentially reimplementing what catch
(StreamNetworkException $e) already does, except the type system can't<= br> help, static analysis can't reason about it, and it can't compose i= t
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. Afte= r 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 wer= e 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 shou= ld be decided on its own and later.

=C2=A0
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 i= s 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.<= /div>

Kind regards,

Jakub=C2=A0=
--000000000000cca472064e7da368--