Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129095 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 9A1AE1A00BC for ; Wed, 5 Nov 2025 22:38:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1762382290; bh=IBpz0oMBVeum2uoUEU3ZsHIkZTgeWbS94jwNPficeMQ=; h=Date:From:To:In-Reply-To:References:Subject:From; b=Kjg2VVyr8+zJ0YAy4AbcJuEacsHIHyQ1MCz4rYbvvlq5kwnC9DPkDMUKSF/Baq2Vh DOXv5RdO6nH2kUxPlq81ugiioQ5uhQldKC5ggOMo5B8qPWh66c3xP0GYY/eV2W4O5u lIiWV0ONXzuOaoi75E/NapziepJWlpWas0y0kqGZdzTFfLmV7QnzIM7mUTDEP4rpHm Sw1Lry8nLgMOi/2WR6jxVE3MZ6gzGjNGmkEhUXWVUG8Cw2dT9IK1CJd0md4tUSy3AH YCSBz/vGFJdsFb5bAj3xP9zlSk6n2V3I0f8sECIh76QMOYWYB/RBl3/xfcX7DfzqyR cI3CJ8FRe/IgA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id C7DCC18006D for ; Wed, 5 Nov 2025 22:38:08 +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=-2.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,RCVD_IN_DNSWL_LOW, SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from fhigh-a3-smtp.messagingengine.com (fhigh-a3-smtp.messagingengine.com [103.168.172.154]) (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 ; Wed, 5 Nov 2025 22:38:08 +0000 (UTC) Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailfhigh.phl.internal (Postfix) with ESMTP id BAAAE14001CD for ; Wed, 5 Nov 2025 17:38:02 -0500 (EST) Received: from phl-imap-02 ([10.202.2.81]) by phl-compute-04.internal (MEProxy); Wed, 05 Nov 2025 17:38:02 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= garfieldtech.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to; s=fm3; t=1762382282; x=1762468682; bh=f6Vg4EOKkPBs+bU2AqNyN l7xxG8MVJ0X+xT8sOknpzg=; b=ADOmNXmWrrrp/1DpxL0ThPl5riU9D/7WYcyN8 wV9nMv2FUXUcU91j+fwmYCwj7YI+EzKfYD5dN60C5O/C59jaqEmaiPhBiN+wle9d 3g/FOj/5E3TKqvLdmewHTpZnWZdAP9DnkIWI8MTizqKC7UO07JachMj29Ccle0JH KGm/9qo3GX358ojXkKhxnrqO+ODeobFzJpj11vC2i1NJ44QPGQ1XmGj5fM+2+y2q x2pSsh5C5NTY3Kh9rNP1myVH3DnPQPms8A7VRYenlYzc/ge4dfUUbD/A5TLUyzGY 92xtuJCO9F2cWu6ASbNVghKYIaebYsuf53osKAAtR2IyNydeA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm3; t=1762382282; x=1762468682; bh=f 6Vg4EOKkPBs+bU2AqNyNl7xxG8MVJ0X+xT8sOknpzg=; b=2vE/gzSDanB8b3Vrn oFVQjpmphYsbnwsn6RL6x5uC2tdTxsf6cVC/m97wRt/qEaD+PUn7UuKeFJs137RL wyN/FJRyOsYLthCT3dC4Wt5SRUFBeGXIMtfTIjln9SNF48RHExtahidMippxgBCh 8aiIhq22Xhm9Ui5NaYmPL0Uk6rIVMwUZIeR+f98CfYaImfGGLGaONMljjbx0eZpk 87+gLfPdOA1mOhisMZPITc7QR+bsMCwE/jZFUeplIM8VZFlz0psA25g4hidKqw9o nnAPTqPiSPRS90SuGdqtlQRtOwvV3El7/Ul2yO+s0IiSyYelr2Fo5wAZu/Jtqxeo Kokyw== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtdeggddukeehuddvucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepofggfffhvffkjghfufgtgfesthhqredtredtjeenucfhrhhomhepfdfnrghrrhih ucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhmqe enucggtffrrghtthgvrhhnpeeluddvudelkeeijeekueekgfdvieefleevveejfefhfedt uefgudeiueehffelkeenucffohhmrghinhepphhhphdrnhgvthdpghhithhhuhgsrdgtoh hmnecuvehluhhsthgvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheplhgr rhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhmpdhnsggprhgtphhtthhopedupdhmoh guvgepshhmthhpohhuthdprhgtphhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhp hhhprdhnvght X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id 5460E700063; Wed, 5 Nov 2025 17:38:02 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: ALvh3TDXSVwZ Date: Wed, 05 Nov 2025 16:37:42 -0600 To: "php internals" Message-ID: <566ce444-0cba-4a72-94e3-64f52b3cf93c@app.fastmail.com> In-Reply-To: References: Subject: Re: [PHP-DEV] [RFC] Context Managers Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable From: larry@garfieldtech.com ("Larry Garfield") On Tue, Nov 4, 2025, at 2:13 PM, Larry Garfield wrote: > Arnaud and I would like to present another RFC for consideration:=20 > Context Managers. > > https://wiki.php.net/rfc/context-managers > > You'll probably note that is very similar to the recent proposal from=20 > Tim and Seifeddine. Both proposals grew out of casual discussion=20 > several months ago; I don't believe either team was aware that the=20 > other was also actively working on such a proposal, so we now have two= .=20 > C'est la vie. :-) > > Naturally, Arnaud and I feel that our approach is the better one. In=20 > particular, as Arnaud noted in an earlier reply, __destruct() is=20 > unreliable if timing matters. It also does not allow differentiating=20 > between a success or failure exit condition, which for many use cases=20 > is absolutely mandatory (as shown in the examples in the context=20 > manager RFC). > > The Context Manager proposal is a near direct port of Python's=20 > approach, which is generally very well thought-out. However, there ar= e=20 > a few open questions as listed in the RFC that we are seeking feedback=20 > on. > > Discuss. :-) Hi all. I'm going to reply to several people at once in a single messag= e, for simplicity: On Wed, Nov 5, 2025, at 12:29 AM, Paul Dragoonis wrote: > 1. Apart from wrapping zend_resource into ResourceContext, has there=20 > been discussions or ideas to wrap other things ? Not really. The intent is that users can create their own "setup and te= ardown" logic packages and do with them as they please. Resources are j= ust an oddball case in PHP, because reasons. I'm not sure what other au= to-wrapping cases would make sense. That said, PHP could absolutely ship context managers for people to use = explicitly. My ideal way to address async would be exactly that: The Sc= ope example from the RFC, where the only way to get to a scope (and ther= efore start coroutines) is via a context manager, and PHP iself provides= the scope types we want to support. Nothing else. There may be other managers that PHP would want to ship in the future fo= r whatever reason, but that's out of scope for now. (No pun intended.) > 2. Are there any scenarios where using with() is a bad idea or has=20 > "side effects"? If you have a setup/teardown routine that is only used once or twice, th= en making a context manager for just that one use case is likely overkil= l. Just write try/catch/finally as normal. I don't believe a context manager could handle this, although I've only = rarely seen it in the wild: $success =3D true; try { // ... } catch (\Exception $e) { $success =3D false; } if ($success) { ... } (That is, leaking a variable from the setup/teardown code into the surro= unding scope. Though, I suppose this would work in a pinch: $success =3D false; with (new Foo() as $f) { // ... $success =3D true; } if ($success) { ... } Not ideal, but would work. We're still exploring the possibility of making the context manager keyw= ord an expression rather than a statement, which might offer other alter= natives. Still brainstorming. > 3. In the implementation code there is a lot of mention of "list" and=20 > zend_list .. why? Maybe the answer is obvious but I can't see, at firs= t=20 > glance, why we are implementing list under the hood.=20 I will defer to Arnaud here. On Wed, Nov 5, 2025, at 12:45 AM, Valentin Udaltsov wrote: > Have you considered returning enum instead of ?bool? It would have a=20 > clear self explanatory meaning. We have discussed that a bit, actually. The main concern is usability. = The typical case will be to allow exceptions to propagate. If, say, a = TypeError gets thrown 4 function calls down, you probably do want that t= o propagate to your top level handler, but still want to rollback your t= ransaction or close your file or whatever. So the typical case should b= e easy, hence why we said `null` means the default behavior. And since = `null` is falsy`, that fits neatly into a ?bool return; that is also wha= t Python uses. With an enum, you'd have a much longer thing to type, plus it's less sel= f-evident what no-return means. Ie, you'd have: function exitContext(?Exception $e): ContextResult { if ($e) { $this->conn->rollback(); // This line becomes required. return ContextResult::Propagate; } =20 $this->conn->commit(); return ContextResult::Done; // Or, eh, what? } And not returning becomes a type error. Alternatively, we could assume null implies one of the other cases; but = as shown above, there's still the issue that the return type is only mea= ningful in case of an exception, so it's unclear how that interacts. We're still open to discussion here. It also would play into the outsta= nding question of `with` as an expression. On Wed, Nov 5, 2025, at 1:38 AM, Deleu wrote: > Out of curiosity, what happens if GOTO is used inside a context block=20 > to jump away from it? That would be a success case, just like break or return. Basically anyt= hing other than an exception is a success case. (That said, please don'= t use Goto. :-) ) > Could the RFC clarify the relation between Context and switch/case? I=20 > thought it was really odd that something that triggers a warning on=20 > switch/case is being introduced into a brand new language construct=20 > basically creating the possibility for new code to fall into the same=20 > trap as opposed to avoiding it in the first place. Specially a=20 > construct like switch/case that has been in decline for over a decade=20 > and ever since match came out on 8.0, switch case is practically=20 > deprecated without actually being deprecated yet. What=E2=80=99s the=20 > importance/relevance of being consistent with it? `break` and `continue` are interesting keywords. (In the "may you live = in interesting times" sense.) Sometimes they have the same effect, if a= control structure is non-looping. Or they may have different effects i= n case it is. The main non-looping case is `switch`, where for reasons = that were before my time the decision was made to deprecate `continue` i= n favor of just supporting `break`. However, blocking it entirely is a = problem, because that would change where `continue 2` would go (as `swit= ch` would be removed as a "level" that it could go to). It is kind of a= mess. `with` is a non-looping control structure, and thus it seems logical to = be consistent with other non-looping control structures. But, as noted,= the other non-looping control structure is a mess. :-) Therefore, we g= et to choose between "a consistent mess" and "an inconsistent non-mess i= n one place and a mess in another." Neither is a fantastic option. We're open to both, depending on what th= e consensus is. > While we=E2=80=99re at it, do we really need break; statements inside = context=20 > blocks? If you want out you can: > > - return > - throw Both of those exit the function the `with` statement is in, which is not= always desireable. > In the case of a nested block (break 2;) where I don=E2=80=99t want to= wrap the=20 > entire thing in try/catch, it seems like a GOTO out of it would be mor= e=20 > meaningful with text-based identifiers rather than number-based, which=20 > leads to my first question (although I was more curious than actually=20 > making an argument for it because I would rather avoid nested with as=20 > much as possible). Because Goto was added to PHP as a troll, and not a feature you should a= ctually use in production code 99.999% of the time. :-) On Wed, Nov 5, 2025, at 3:25 AM, Davey Shafik wrote: > I really like this RFC but have a couple of things to discuss: > > - automatically re-throwing exceptions: I think that this behavior,=20 > especially with a boolean return value deciding if it happens or not i= s=20 > not intuitive. I think a better approach is to do nothing with the=20 > exception and let the user re-throw it if desired. I can't think of=20 > anywhere else we re-throw exceptions unless the user indicates=20 > otherwise. I'd rather leave the return value for return values; we=20 > could expand this allow access to the return value like: with (foo() a= s=20 > $foo return $bar) { }, and $bar would be set to null on void returns. Using the return value of exitContext() as the result of a "with express= ion" is something we are considering. However, we're modeling on Python (the most robust such functionality we= are aware of), and they rethrow by default. Essentially, the concept i= s that exitContext() (and it's Python equivalent magic method), is mostl= y a `finally` block, not a `catch` block. `finally` blocks do propagate= exceptions. In practice, many exitContext() methods will not need to d= ifferentiate; they'll just close a file or whatever and move on with lif= e, which is why you would want an exception to propagate. The inclusion= of the exception parameter makes it a sort of combined catch/finally, s= o it has some behavior of each. Another option we kicked around was splitting it into two methods; catch= Context(Throwable $e) and exitContext(). However, that creates two othe= r problems: 1. Because it's an interface, you would need to implement catchContext()= all the time, even if you don't need it. That's very inconvenient. (S= hamless plug for revisiting Levi's Interface Default Methods RFC, which = would solve this issue: https://wiki.php.net/rfc/interface-default-metho= ds) Using magic methods instead would avoid that problem, but then we'r= e dealing with magic methods rather than a clearly-detectable interface. 2. If you need to run logic in both methods, do you duplicate it? Or wo= rse, if you have logic that runs only on a success case, then what? Mos= t likely you'd need to have your own $wasItAnError property inside the c= ontext manager object, which is ugly and annoying. That said, we're open to other ways to structure this logic. But I thin= k in practice it's true that *most* use cases will want to propagate the= exception, after doing appropriate local cleanup. > - context variable and scope: I know that you explicitly are not=20 > creating a new scope, this means that the context variable will clash=20 > with the enclosing scope namespace, and then the variable will be unse= t=20 > after the context ends, this doesn't sit so well with me. I think I'd=20 > rather see the same behavior as arrow function arguments, where it doe= s=20 > not override variables of the same name in the enclosing scope and=20 > whatever value it has is lost at the end of the context, leaving the=20 > outer scope version intact. Arnaud says that masking the context variable itself is probably fairly = straightforward, so we can go ahead and do that. However, masking every= variable that gets created doesn't make sense. This construct is not c= reating a new "block scope" in the language. It's just desugaring into = a reusable try-catch-finally construct. If we wanted to have an actual local scope specific to the `with` block,= then instead of the statement list we should have a callable, which in = most cases would be an anon function. However, PHP's anon functions suc= k to use because of the need to explicitly `use` variables. That would = effectively eliminate any benefit this feature offers, because you can a= lready do `$someWrapper->do($aCallable)`. But `$aCallable` needs a long= list of `use` statements, which makes it fugly. If anon functions were fixed, that would make that approach easier to do= . However, that's been tried at least twice and it's been shot down bot= h times, so I'm assuming we're stuck with a clunky anon function syntax = indefinitely. ----- Also, off-list discussion has shown an interest in multiple context mana= gers in one `with` block, which was one of the outstanding open question= s. It looks like we'll probably include that, as it should be easy enou= gh to do. ----- And now the big one... also in off-list discussion, Seifeddine noted tha= t Laravel already defines a global function named `with`: https://github= .com/laravel/framework/blob/12.x/src/Illuminate/Support/helpers.php#L510 And since this RFC would require `with` to be a semi-reserved keyword at= the parser/token level, that creates a conflict. (This would be true e= ven if it was namespaced, although Laravel is definitely Doing It Wrong(= tm) by using an unnamespaced function.) Rendering all Laravel deploymen= ts incompatible with PHP 8.6 until it makes a breaking API change would = be... not good for the ecosystem. So that means using Python's `with` keyword here is not going to work. = Damn. A couple of other options have presented themselves, but we're open to o= ther suggestions, too: 1. Java uses a parenthetical block on `try` for similar functionality (t= hough without a separate context manager). That would look like: try (new Foo() as $foo) { // ... } // catch and finally become optional if there is a context. Pros here is that it introduces no new keywords, and context managers ar= e effectively "packaged try-catch-finally" logic, so it fits. Downsides= are that it gets more confusing now that `try` only sometimes requires = a catch or finally. The ordering between the context manager and explic= it catch/finally blocks is also non-obvious. It would also entirely pre= clude context blocks being an expression, as `try` is already non-expres= sional. 2. Either `use` or `using`. The semantics here would be identical to th= e current `with` proposal. Pros here are that `use` is already a reserved word, and `using` is, I h= ope, still available in practice. They could also be implemented as exp= ressions if we figure out a way to do so. Downsides are that `use` is a= lready used in a bunch of places to mean different things, so adding yet= another contextual meaning just increases the complexity/confusion. `u= sing` wouldn't have that issue, but we would still need to verify if it'= s available. --Larry Garfield