Re: [RFC] Context Managers, v2
| From: | Larry Garfield | Date: | Wed, 11 Nov 2026 14:46:12 +0000 |
| Subject: | Re: [RFC] Context Managers, v2 | ||
| References: | 1 2 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-133075@lists.php.net to get a copy of this message | ||
On Tue, Nov 10, 2026, at 5:32 AM, Côme Chilliet wrote:
> Hello,
>
> Le jeudi 5 novembre 2026, 18:33:14 heure normale d’Europe centrale
> Larry Garfield a écrit :
>> Hi folks. Based on the feedback and problems with the previous Context Managers RFC,
>> Arnaud and I have a new approach to present:
>>
>> https://wiki.php.net/rfc/context-managers-v2
>>
>> It basically swaps the two-method approach in favor of "it's a generator,
>> you're welcome." The result is, I think, both simpler and more flexible, and resolves a
>> number of other issues as well. More details in the RFC.
>>
>> There is a linked implementation, though Arnaud warns it's only partially complete so
>> go easy on it. :-) But the design in the RFC is all ready for review.
>
> I do not understand this sentence: «Additionally, it is legal for a
> Context Manager object to return $this as the context variable if that
> is appropriate in the specific use case.»
> If ContextManager::getContextManager returns $this, which Generator is
> used 🤔
That should read "yield $this". Like so:
class Foo implements ContextManager {
public function getContextManager(): \Generator
{
yield $this;
{
}
In which case the context variable will be an instance of Foo. I'll adjust the language.
> The whole section «Mixing with try-catch» is also a mystery to me. I do
> not understand why "using" would need a short syntax for try catch any
> more than any other keyword we often put in a try/catch, nor what the
> explanations in there mean.
As stated in the RFC, we expect doing this to be very common:
function something(): \Generator
{
try {
$something = init_something();
yield $something;
} finally {
deinit($something);
}
}
try {
using (something() => $var) {
// Do stuff with $var.
};
} catch (\Exception $e) {
// Additional error handling, but we don't need to clean up $something.. That's already
handled.
}
So the short-hand form just lets us reduce the nesting a bit for a common case. One could make an
argument that we could support arbitrary single-expression try statements this way; I'd be open
to that myself, but that's out of scope for this RFC.
> Is the semi-colon necessary after the closing '}' ? That reads weird,
> it’s not clear to me why it’s not a single '}' like after an if or
> foreach or whatever.
Because using() is an expression, like match() or an anonymous function. Those have semicolons
after them as well, because they're not a complete statement, just an expression. The same
applies here.
> Also, this semi-colon is missing in at least one example.
Which one?
> In the transaction example, $conn does not exist, I suppose both
> instances should be $this?
You are correct, fixed. Thanks.
> «A return statement inside the BODY will return from the function the
> code is currently executing in. A finally block in the generator
> context manager will still be executed, but otherwise control returns
> to the function's caller.»
> This is also hard to get for me.
> Can you detail what this would output:
> function context(): \Generator {
> echo "A\n";
> yield "B";
> echo "C\n";
> return "D";
> }
> var_dump(
> using (context() => $c) {
> echo "E\n";
> return "F";
> }
> );
I believe (without actually running it) that would output:
A
E
The return "F" terminates the function that the var_dump()/using() construct is in. The
generator is discarded without ever reaching its end, so the generator's return statement is
never reached.
That said, you have brought up an interesting question, I have no idea what happens when you
var_dump() a compound expression like this that returns. Something for Arnaud to write tests for.
0:-)
--Larry Garfield