[PHP-DEV] [RFC] Context Managers

On Mon, 15 Dec 2025 at 20:01 Ben Ramsey <ben@benramsey.com> wrote:

Since the only feedback on what to use for “as” was that => makes sense, we have changed the RFC to use => instead. So the new syntax is

using (new CM() => $cVar) {
// Do stuff here.
}

Going to be controversial here, but this is confusing, because it operates in the exact opposite of every other usage of => we have. With associative arrays, the left is assigned to the expression on the right; with arrow functions, the return value is the expression on the right; with match, the expression on the right is returned.

This is going to be easy to get wrong.

I agree with Matthew.

I think it makes more sense to reverse them, like this:

using ($cVar => new CM()) {
// Do stuff here.
}

I think it’s still clear what this is doing, when reading it.

Cheers,
Ben

I also agree with Matthew but the reversed proposed here looks very very awkward to me. I think the most natural thing is the “as” but I may have missed the discussion on why it had to be changed.

Thinking of foreach ($array as $value), an item from the array (left) is assigned to $value (right). That seems symmetrical to using (new Manager as $manager) where the instance (left) is assigned to the variable (right). Also when using aliases on the top of the file “use Foo as Bar;” as is also assigning the left to the right.

On Dec 15, 2025, at 18:21, Deleu <deleugyn@gmail.com> wrote:

On Mon, 15 Dec 2025 at 20:01 Ben Ramsey <ben@benramsey.com> wrote:

Since the only feedback on what to use for "as" was that => makes sense, we have changed the RFC to use => instead. So the new syntax is

using (new CM() => $cVar) {
  // Do stuff here.
}

Going to be controversial here, but this is confusing, because it operates in the exact opposite of every other usage of => we have. With associative arrays, the left is assigned to the expression on the right; with arrow functions, the return value is the expression on the right; with match, the expression on the right is returned.

This is going to be easy to get wrong.

I agree with Matthew.

I think it makes more sense to reverse them, like this:

    using ($cVar => new CM()) {
        // Do stuff here.
    }

I think it’s still clear what this is doing, when reading it.

Cheers,
Ben

I also agree with Matthew but the reversed proposed here looks very very awkward to me. I think the most natural thing is the “as” but I may have missed the discussion on why it had to be changed.

Thinking of foreach ($array as $value), an item from the array (left) is assigned to $value (right). That seems symmetrical to using (new Manager as $manager) where the instance (left) is assigned to the variable (right). Also when using aliases on the top of the file “use Foo as Bar;” as is also assigning the left to the right.

This argument makes sense to me (i.e., using `as` instead of `=>`).

I’ll go back through the thread to find the arguments against using `as`, to understand why it changed to `=>`.

Cheers,
Ben

On Dec 16, 2025, at 1:19 AM, Larry Garfield <larry@garfieldtech.com> wrote:

On Thu, Dec 4, 2025, at 10:46 AM, Larry Garfield wrote:

On Tue, Nov 4, 2025, at 2:13 PM, Larry Garfield wrote:
Arnaud and I would like to present another RFC for consideration:
Context Managers.

PHP: rfc:context-managers

You'll probably note that is very similar to the recent proposal from
Tim and Seifeddine. Both proposals grew out of casual discussion
several months ago; I don't believe either team was aware that the
other was also actively working on such a proposal, so we now have two.
C'est la vie. :slight_smile:

Naturally, Arnaud and I feel that our approach is the better one. In
particular, as Arnaud noted in an earlier reply, __destruct() is
unreliable if timing matters. It also does not allow differentiating
between a success or failure exit condition, which for many use cases
is absolutely mandatory (as shown in the examples in the context
manager RFC).

The Context Manager proposal is a near direct port of Python's
approach, which is generally very well thought-out. However, there are
a few open questions as listed in the RFC that we are seeking feedback
on.

Discuss. :slight_smile:

More updates to Context Managers:

* We have added "masking" for the context variable, using essentially
the same technique as the block scope RFC.
* We have added support for `try using`, as a shorthand for when you
want to wrap a try-catch-finally around a using statement anyway.

More details of both are in the RFC.

As no one seems to have a strong opinion on `continue`, we will most
likely proceed with the current approach of matching `switch` behavior.

There doesn't seem to be much interest in making `using` an expression,
which I find unfortunate, but that means we'll probably drop that.
Fortunately it is probably possible to change in the future if the need
arises (the way `throw` was changed).

--Larry Garfield

Since the only feedback on what to use for "as" was that => makes sense, we have changed the RFC to use => instead. So the new syntax is

using (new CM() => $cVar) {
// Do stuff here.
}

--Larry Garfield

I’d ask you to get back to “use” keyword, despite of it’s in use in Laravel or somewhere else.
As a developer I cannot even imagine what “use” could mean in web frameworks context, I hope it could have a better name and at the same time we can advise to use namespaces if you don’t want to get something broken after upgrading language.
Just my 5 cents.

--
Best regards,
Dmitrii Derepko.
@xepozz

On Tue, 16 Dec 2025 at 02:58 Dmitry Derepko <xepozzd@gmail.com> wrote:

On Dec 16, 2025, at 1:19 AM, Larry Garfield <larry@garfieldtech.com> wrote:

On Thu, Dec 4, 2025, at 10:46 AM, Larry Garfield wrote:

On Tue, Nov 4, 2025, at 2:13 PM, Larry Garfield wrote:
Arnaud and I would like to present another RFC for consideration:
Context Managers.

https://wiki.php.net/rfc/context-managers

You’ll probably note that is very similar to the recent proposal from
Tim and Seifeddine. Both proposals grew out of casual discussion
several months ago; I don’t believe either team was aware that the
other was also actively working on such a proposal, so we now have two.
C’est la vie. :slight_smile:

Naturally, Arnaud and I feel that our approach is the better one. In
particular, as Arnaud noted in an earlier reply, __destruct() is
unreliable if timing matters. It also does not allow differentiating
between a success or failure exit condition, which for many use cases
is absolutely mandatory (as shown in the examples in the context
manager RFC).

The Context Manager proposal is a near direct port of Python’s
approach, which is generally very well thought-out. However, there are
a few open questions as listed in the RFC that we are seeking feedback
on.

Discuss. :slight_smile:

More updates to Context Managers:

  • We have added “masking” for the context variable, using essentially
    the same technique as the block scope RFC.
  • We have added support for try using, as a shorthand for when you
    want to wrap a try-catch-finally around a using statement anyway.

More details of both are in the RFC.

As no one seems to have a strong opinion on continue, we will most
likely proceed with the current approach of matching switch behavior.

There doesn’t seem to be much interest in making using an expression,
which I find unfortunate, but that means we’ll probably drop that.
Fortunately it is probably possible to change in the future if the need
arises (the way throw was changed).

–Larry Garfield

Since the only feedback on what to use for “as” was that => makes sense, we have changed the RFC to use => instead. So the new syntax is

using (new CM() => $cVar) {
// Do stuff here.
}

–Larry Garfield

I’d ask you to get back to “use” keyword, despite of it’s in use in Laravel or somewhere else.
As a developer I cannot even imagine what “use” could mean in web frameworks context, I hope it could have a better name and at the same time we can advise to use namespaces if you don’t want to get something broken after upgrading language.
Just my 5 cents.


Best regards,
Dmitrii Derepko.
@xepozz

I have considered asking the same thing in this thread. I have used Laravel everyday for the last decade and I know how Laravel with helper works. The reason I decided not to voice this out is because Larry said that even if Laravel used namespaces it would still not work because the word would be reserved.

I’m sure it wouldn’t be the end of the world to refactor billions of lines of code across the industry for a namespace, but for an entire rename of the function I guess we’re between a rock and a hard place anyway. Plus, I kind of got used to using fairly quickly.

(Replying to a few of you in one go, I hope the formatting is clear)

we have changed the RFC to use => instead. So the new syntax is

using (new CM() => $cVar) {
  // Do stuff here.
}

Going to be controversial here, but this is confusing, because it operates
in the exact opposite of every other usage of => we have.

I agree with Matthew.

I think it makes more sense to reverse them, like this:

    using ($cVar => new CM()) {
        // Do stuff here.
    }

It took me a long time to figure out what you were both saying here, because to me the direction of the arrow seems to consistently indicate data flow: you call the function, and data comes out; you enter the context, and a variable comes out.

But I think I see it now: you're treating the context variable like an array key, that is somehow "bound to" the result. Except that's not what's happening, otherwise we could just use "=".

I also agree with Matthew but the reversed proposed here looks very very
awkward to me. I think the most natural thing is the “as” but I may have
missed the discussion on why it had to be changed.

Thinking of foreach ($array as $value), an item from the array (left) is
assigned to $value (right).

This is the way to read the "as" syntax, yes; as I've said in a few places, a Context Manager is like an iterator that goes around once.

That seems symmetrical to using (new Manager as
$manager) where the instance (left) is assigned to the variable (right).

This, however, is why it was changed: that is *not* what is happening. The Context Manager is not assigned to the variable, it *produces a value* which is assigned to the variable.

Again, look at the foreach equivalence: you wouldn't write "foreach(new MyIterator as $iterator)", because it's not the iterator that ends up in the variable, it's the *items produced by* the iterator.

So you have "foreach(new MyIterator as $item)" and "using(new MyContextManager as $contextVar)".

But evidently that is not obvious to readers.

My favourite among my own suggestions was "using($cValue from new CM())", but Larry didn't like the reversed order. I also suggested 'using(new CM() for $cValue)", but I don't think that's as clear.

Perhaps there is some other word we can use? I suspect it would not need to be fully reserved, as it's used in a very restricted context.

Or, perhaps there's something other than "using" that clues the reader into "this object will be used to produce an arbitrary value" in the same way as "foreach...as" does?

Rowan Tommins
[IMSoP]

Hi

Am 2025-12-16 09:08, schrieb Rowan Tommins [IMSoP]:

I also agree with Matthew but the reversed proposed here looks very very
awkward to me. I think the most natural thing is the “as” but I may have
missed the discussion on why it had to be changed.

Thinking of foreach ($array as $value), an item from the array (left) is
assigned to $value (right).

This is the way to read the "as" syntax, yes; as I've said in a few places, a Context Manager is like an iterator that goes around once.

That definition doesn't make sense to me. An iterator that only ever emits a single value is not actually iterating anything and it would never occur me to interpret it like that. As I had also noted in my reply php.internals: Re: Examples comparing Block Scoped RAII and Context Managers, it is absolutely magic to me that `break;` would target `using()`.

My favourite among my own suggestions was "using($cValue from new CM())", but Larry didn't like the reversed order. I also suggested 'using(new CM() for $cValue)", but I don't think that's as clear.

FWIW: Using `from` would be somewhat consistent with `yield from`. Using `for` would effectively invent another meaning for an existing keyword, something that folks disliked for `use()` as the initial block scoping keyword.

Best regards
Tim Düsterhus

On 2025-12-16 21:08, Rowan Tommins [IMSoP] wrote:

My favourite among my own suggestions was "using($cValue from new CM())", but Larry didn't like the reversed order. I also suggested 'using(new CM() for $cValue)", but I don't think that's as clear.

Perhaps there is some other word we can use? I suspect it would not need to be fully reserved, as it's used in a very restricted context.

I guess a symmetric counterpart to using "from" would give
"using(new CM() to $cValue)". Maybe "into".

My conclusion here is exactly the opposite of yours because this explanation makes as even more symmetrical now. As you’ve mentioned foreach(new MyIterator as $iterator) is not how it works. An iterator produces a value that is assigned to the variable after as. Symmetrically, using(new ContextManager as $context) the ContextManager is also producing a value that is assigned to the variable after as. The only difference is in the keyword: foreach will loop, but using will not loop since ContextManager is not an iterator.

I’m assuming this would also be a valid syntax: using ($manager = new ContextManager as $context) which gives us a shot at capturing the context manager.

I wrote a little snippet to try and see (using GitHub Gist Syntax Highlighter) how some of the options would look like once IDE support / syntax highlighting is baked in:

image.png

Ultimately, people will take a quick reading in a Context Manager documentation and the parallels to explain how it works seems to be very well in place for as, imo.

In the image above I tried adding for using because I was trying really hard to come up with a word that could replace each in foreach to make it even more symmetrical, but I could not find anything decent.

  • for context(new ContextManager as $context)
    This one reads well, but the fact its two words (and a long word for that matter) doesn’t sit well.

  • for with(new ContextManager as $context)
    I’m not a native English speaker, but I think this doesn’t read at all

  • for one(new ContextManager as $context)

Kind of implies it works with iterators (similar to each), which is not exactly the case and the language has very little motivation to create an iterator that loops only once just to make this work.

  • for using(new ContextManager as $context)
    Similar thoughts as for context, the only difference that makes me like this one more is because for context doesn’t sit well with try context while for using could go well with try using.

In any case, the longer I sit with this the more I dislike => for this.

In an array, => is an assignment of key/value pairs.
For foreach ($iterator as $key => $value), the => is a destructing instruction that aligns with how the array was originally assigned.
For the arrow function, it’s kind of implied in the name ARROW function that we would need a syntax in the lines of fn () =>. I don’t think this relates well with arrays, but it works well to me because of how universal arrow functions are in Javascript.
For match ($value), it reads as WHEN ($value) MATCH '' THEN. The arrow here =>` is the THEN.

My conclusion is that => is either a value placement (for arrays) or an instruction execution (for arrow function and match). As such, If => were to be incorporated into Context Managers, I think it would make much more sense like this:

using (new ContextManager as $context) => [single line expression] where the arrow creates a symmetry with how arrow functions work.

···

Marco Deleu

(Attachment image.png is missing)

On Tue, Dec 16, 2025, at 5:25 AM, Deleu wrote:

On Tue, Dec 16, 2025 at 5:10 AM Rowan Tommins [IMSoP]
<imsop.php@rwec.co.uk> wrote:

(Replying to a few of you in one go, I hope the formatting is clear)

>>> we have changed the RFC to use => instead. So the new syntax is
>>>
>>> using (new CM() => $cVar) {
>>> // Do stuff here.
>>> }
>>>
>>
>> Going to be controversial here, but this is confusing, because it operates
>> in the exact opposite of every other usage of => we have.
>
> I agree with Matthew.
>
> I think it makes more sense to reverse them, like this:
>
> using ($cVar => new CM()) {
> // Do stuff here.
> }

It took me a long time to figure out what you were both saying here, because to me the direction of the arrow seems to consistently indicate data flow: you call the function, and data comes out; you enter the context, and a variable comes out.

But I think I see it now: you're treating the context variable like an array key, that is somehow "bound to" the result. Except that's not what's happening, otherwise we could just use "=".

>I also agree with Matthew but the reversed proposed here looks very very
>awkward to me. I think the most natural thing is the “as” but I may have
>missed the discussion on why it had to be changed.
>
>Thinking of foreach ($array as $value), an item from the array (left) is
>assigned to $value (right).

This is the way to read the "as" syntax, yes; as I've said in a few places, a Context Manager is like an iterator that goes around once.

> That seems symmetrical to using (new Manager as
>$manager) where the instance (left) is assigned to the variable (right).

This, however, is why it was changed: that is *not* what is happening. The Context Manager is not assigned to the variable, it *produces a value* which is assigned to the variable.

Again, look at the foreach equivalence: you wouldn't write "foreach(new MyIterator as $iterator)", because it's not the iterator that ends up in the variable, it's the *items produced by* the iterator.

So you have "foreach(new MyIterator as $item)" and "using(new MyContextManager as $contextVar)".

But evidently that is not obvious to readers.

My conclusion here is exactly the opposite of yours because this
explanation makes `as` even more symmetrical now. As you've mentioned
`foreach(new MyIterator as $iterator)` is not how it works. An iterator
*produces a value* that is assigned to the variable after `as`.
Symmetrically, `using(new ContextManager as $context)` the
ContextManager is also producing a value that is assigned to the
variable after `as`. The only difference is in the keyword: foreach
will loop, but using will not loop since ContextManager is not an
iterator.

I'm assuming this would also be a valid syntax: `using ($manager = new
ContextManager as $context)` which gives us a shot at capturing the
context manager.

I wrote a little snippet to try and see (using GitHub Gist Syntax
Highlighter) how some of the options would look like once IDE support /
syntax highlighting is baked in:

image.png

Ultimately, people will take a quick reading in a Context Manager
documentation and the parallels to explain how it works seems to be
very well in place for `as`, imo.

In the image above I tried adding `for using` because I was trying
really hard to come up with a word that could replace `each` in
`foreach` to make it even more symmetrical, but I could not find
anything decent.

- `for context(new ContextManager as $context)`
This one reads well, but the fact its two words (and a long word for
that matter) doesn't sit well.

- `for with(new ContextManager as $context)`
I'm not a native English speaker, but I think this doesn't read at all

- `for one(new ContextManager as $context)`
Kind of implies it works with iterators (similar to each), which is not
exactly the case and the language has very little motivation to create
an iterator that loops only once just to make this work.

- `for using(new ContextManager as $context)`
Similar thoughts as `for context`, the only difference that makes me
like this one more is because `for context` doesn't sit well with `try
context` while `for using` could go well with `try using`.

In any case, the longer I sit with this the more I dislike `=>` for this.

In an array, `=>` is an assignment of key/value pairs.
For foreach ($iterator as $key => $value), the => is a destructing
instruction that aligns with how the array was originally assigned.
For the arrow function, it's kind of implied in the name ARROW function
that we would need a syntax in the lines of `fn () =>`. I don't think
this relates well with arrays, but it works well to me because of how
universal arrow functions are in Javascript.
For match ($value), it reads as `WHEN ($value) MATCH '' THEN. The arrow
here `=>` is the THEN.

My conclusion is that `=>` is either a value placement (for arrays) or
an instruction execution (for arrow function and match). As such, If =>
were to be incorporated into Context Managers, I think it would make
much more sense like this:

`using (new ContextManager as $context) => [single line expression]`
where the arrow creates a symmetry with how arrow functions work.

--
Marco Deleu

First, yeesh, where were y'all 2 weeks ago? :stuck_out_tongue:

Second, I find it fascinating that there's so many different mutually-incompatible spins on what => means. As argued in the short-functions and auto-capture-closure RFCs from a few years ago, => in nearly all cases currently means "evaluates to."

$arr = [
  'a' => 'A",
  'b' => 'B',
];

$arr['a'] // This "evaluates to" A

match ($foo) {
  'a' => 'A',
  'b' => 'B', // The 'b' case "evaluates to" B
};

fn($foo) => 'bar'; // This function "evaluates to" bar

foreach ($arr as $k => $v) {}

The last is a little bit inconsistent, but can still be read as "$k, and the thing that $k evaluates to."

So given that, and as Rowan noted the context manager is *NOT* the context variable:

using ($cm evaluates to $cv) {}

That's the meaning we're going for.

If there's some better word than =>, we're still open to it, but I am going to insist on left-to-right reading. $cm is created first, then it produces $cv.

'as' is what we had originally, since that's what Python used. However, it was pointed out that it was confusing as it implied the CM and CV were the same thing, or rather the expression on the left gets assigned to $cv, which is not the case. Hence the change.

A symbol does have the (dis?)advantage of a somewhat squishier meaning than a keyword.

--Larry Garfield

First, yeesh, where were y’all 2 weeks ago? :stuck_out_tongue:

I searched for the as vs => discussion on the current thread (ref: https://externals.io/message/129077) and it didn’t exist until now. Then I found the discussion happened on the Examples comparing Block Scoped RAII and Context Managers (ref: https://externals.io/message/129251). I wasn’t following that discussion because I thought it was mostly about comparing both implementations and taking knowledge from it. I didn’t expect that to make this RFC to change without it being discussed here.

Second, I find it fascinating that there’s so many different mutually-incompatible spins on what => means. As argued in the short-functions and auto-capture-closure RFCs from a few years ago, => in nearly all cases currently means “evaluates to.”

$arr = [
‘a’ => 'A",
‘b’ => ‘B’,
];

$arr[‘a’] // This “evaluates to” A

match ($foo) {
‘a’ => ‘A’,
‘b’ => ‘B’, // The ‘b’ case “evaluates to” B
};

fn($foo) => ‘bar’; // This function “evaluates to” bar

foreach ($arr as $k => $v) {}

The last is a little bit inconsistent, but can still be read as “$k, and the thing that $k evaluates to.”

I think the problem might be less about the true meaning of => and more about the context that it applies to. You can see that in 3 cases of the arrow it involves some sort of key/value pair (2 array scenarios and 1 match/case). In the case of arrow function, like I said before, arrow functions are kind of a standard that stand up on their own. And even then, the “evaluates to” is only partially because in arrow functions the evaluation does not take immediate effect, it is parsed into a Closure that needs to be invoked. For instance:

$array = [
'key' => throw new Exception('Stop'),
];

Here, the arrow evaluates to an exception that is immediately thrown.

match ($foo) {
'a' => throw new Exception('Stop because of A'),
'b' => throw new Exception('Stop because of B'),
};

Here, the evaluation is more loose and it only takes effect when there is a match, but it does take effect.

fn () => throw new Exception('Stop');

Here, the evaluation never takes effect if the Closure is not invoked.

In these 3 cases, we can see each having its own evaluation rule: immediate, immediate only for the match, and delayed. If I were to put into words, I’d say:

array keys: evaluates to
match: evaluate to the matched key
arrow function: parse the syntax, does not evaluate to.

foreach ($arr as $k => $v) {}

Here I don’t see it only as a bit inconsistent because it’s not really evaluating anything anymore. It is assigning a value. And this is where it blows up for me. The first 3 cases we can argue semantics and point of views, but they are minor nuances. Still, they’re all cases where something on the right is being evaluated for a reason on the left. On foreach, $v stands on its own. It’s not affected / impacted / dicated / evaluated towards the left at all. It’s not a bit inconsistent, it’s completely different. And the syntax for using () is largely similar to foreach, which is why it should use as and not =>.

foreach ($iterator as $key => $value)
using ($contextManager as $context)

We can see that the only place where arrow (“evaluates to” / =>) is used inside an instruction is in foreach and the syntax is split away from the array itself because the keyword as is separating $k => $v from the $iterator. I can totally see an argument for:

using (new ContextManager as $contextManager which evaluates to $context)
using (new ContextManager as $contextManager => $context)

Here, the return of enter would be [$this, $context] and the syntax falls into place with a symmetry from foreach. I wouldn’t strongly push for this syntax because the following also works

using ($manager = new ContextManager as $context)

In any case, I don’t have a vote and I really like the RFC in general. With proper IDE support, I’m sure we will all get used to =>, but from where I stand that’s going to be really awkward for the PHP syntax.

···

Marco Deleu

On Tue, Dec 16, 2025, at 10:49 AM, Deleu wrote:

I think the problem might be less about the true meaning of => and more
about the context that it applies to. You can see that in 3 cases of
the arrow it involves some sort of key/value pair (2 array scenarios
and 1 match/case). In the case of arrow function, like I said before,
arrow functions are kind of a standard that stand up on their own. And
even then, the "evaluates to" is only partially because in arrow
functions the evaluation does not take immediate effect, it is parsed
into a Closure that needs to be invoked. For instance:

$array = [
    'key' => throw new Exception('Stop'),
];

Here, the arrow evaluates to an exception that is immediately thrown.

match ($foo) {
  'a' => throw new Exception('Stop because of A'),
  'b' => throw new Exception('Stop because of B'),
};

Here, the evaluation is more loose and it only takes effect when there
is a match, but it does take effect.

fn () => throw new Exception('Stop');

Here, the evaluation never takes effect if the Closure is not invoked.

In these 3 cases, we can see each having its own evaluation rule:
immediate, immediate only for the match, and delayed. If I were to put
into words, I'd say:

array keys: evaluates to
match: evaluate to the matched key
arrow function: parse the syntax, does not evaluate to.

foreach ($arr as $k => $v) {}

Here I don't see it only as a bit inconsistent because it's not really
evaluating anything anymore. It is assigning a value. And this is where
it blows up for me. The first 3 cases we can argue semantics and point
of views, but they are minor nuances. Still, they're all cases where
something on the right is being evaluated for a reason on the left. On
foreach, $v stands on its own. It's not affected / impacted / dicated /
evaluated towards the left at all. It's not a bit inconsistent, it's
completely different. And the syntax for using () is largely similar to
foreach, which is why it should use `as` and not `=>`.

foreach ($iterator as $key => $value)
using ($contextManager as $context)

We can see that the only place where arrow ("evaluates to" / =>) is
used inside an instruction is in foreach and the syntax is split away
from the array itself because the keyword `as` is separating `$k => $v`
from the $iterator. I can totally see an argument for:

using (new ContextManager as $contextManager which evaluates to $context)
using (new ContextManager as $contextManager => $context)

Here, the return of `enter` would be [$this, $context] and the syntax
falls into place with a symmetry from foreach. I wouldn't strongly push
for this syntax because the following also works

Well, in the vast majority case there will be no need to access the CM within the `using` block. If there is, then it's likely that enterContext() will be returning just $this already. So the $contextManager variable is unnecessary noise 99% of the time.

But if we take that out, which get right back to:

using (new ContextManager => $context)

I suppose grammatically this would make sense:

using (new CM() yields $v)

But that's a new keyword that is very close to an existing keyword that means something totally different, so probably not the best idea.

In any case, I don't have a vote and I really like the RFC in general.

Yay!

--Larry Garfield

On 16/12/2025 11:25, Deleu wrote:

My conclusion here is exactly the opposite of yours because this explanation makes `as` even more symmetrical now.

I haven't exactly reached the "opposite" conclusion. If anything, I've now reached the conclusion that "as" and "=>" are both equally ambiguous and easy to misinterpret.

As you've mentioned `foreach(new MyIterator as $iterator)` is not how it works. An iterator *produces a value* that is assigned to the variable after `as`. Symmetrically, `using(new ContextManager as $context)` the ContextManager is also producing a value that is assigned to the variable after `as`. The only difference is in the keyword: foreach will loop, but using will not loop since ContextManager is not an iterator.

That is certainly how I read it, having already been primed by mentions of iterators in the Python design document.

However, Tim pointed out (as you say, it came up in a different thread) that the "each" is doing important work in the phrasing "for each ... as". (It occurs to me that PHP used to have an actual function called each(), which gave you an item from an array.)

Without that hint, the "X as Y" could easily be misread as meaning that X and Y are the same thing.

In plain English, "using the screwdriver as a hammer" means the screwdriver and the hammer are the same object. In PHP, "use Foo as Bar;" means Foo and Bar refer to the same class.

On 16/12/2025 15:59, Larry Garfield wrote:

Second, I find it fascinating that there's so many different mutually-incompatible spins on what => means.

I think we're all engaging in a lot of post hoc rationalisation for a bunch of syntax that's evolved haphazardly, with multiple inspirations.

I was particularly amused by this:

For the arrow function, it's kind of implied in the name ARROW function that we would need a syntax in the lines of `fn () =>`.

It makes me imagine a Computer Science researcher named Dr. Arrow, who invented a new kind of function, but didn't know how to write it until their friend pointed out that their name would make a suitable pun.

In reality, I suspect there are at least two lines of origin:

1) The key => value syntax for arrays is pretty clearly inspired by Perl, which was far and away the most popular web programming language in the early 1990s.

In Perl, it's called the "fat comma", and (a => 'b') is actually a synonym for ('a', 'b').

2) Using some form of "args arrow expression" for lambda functions comes ultimately, I suspect, from Functional Programming languages. The "=>" in match statements can probably be traced to there as well.

Notably, exactly what arrow is used varies from language to language. CoffeeScript (created in 2009) has both "(args) -> expression" and "(args) => expression" with different semantics; JavaScript/ECMAScript basically adopted the "=>" version in 2015. Meanwhile, C# added "(args) => expression" in 2007; Java added "(args) -> expression" in 2014. Hack used "(args) ==> expression" but needed an ugly parser implementation, and an early PHP proposal for "(args) ~> expression" was rejected, so we ended up with "fn(args) => expression" instead.

Ultimately, the thing that all the uses of "=>" have in common is some reason to want an ASCII representation of an arrow, and the need to fit it into the constrained space of an existing grammar.

The same is basically true of "as" - it's a nice short English word, with multiple meanings. That's a blessing and a curse: a blessing, because we can reuse it in different contexts without reserving more words; a curse, because people might have different intuitions about which meaning was intended.

Which brings me back to where I started this e-mail: my conclusion is that neither "as" nor "=>" is clear and unambiguous. We could probably pick either, and people would get used to it; or we could try for something better.

Regards,

--
Rowan Tommins
[IMSoP]

(Replying to a few of you in one go, I hope the formatting is clear)

we have changed the RFC to use => instead. So the new syntax is

using (new CM() => $cVar) {
// Do stuff here.
}

Going to be controversial here, but this is confusing, because it operates
in the exact opposite of every other usage of => we have.

I agree with Matthew.

I think it makes more sense to reverse them, like this:

using ($cVar => new CM()) {
// Do stuff here.
}

It took me a long time to figure out what you were both saying here, because to me the direction of the arrow seems to consistently indicate data flow: you call the function, and data comes out; you enter the context, and a variable comes out.

But I think I see it now: you’re treating the context variable like an array key, that is somehow “bound to” the result. Except that’s not what’s happening, otherwise we could just use “=”.

I also agree with Matthew but the reversed proposed here looks very very
awkward to me. I think the most natural thing is the “as” but I may have
missed the discussion on why it had to be changed.

Thinking of foreach ($array as $value), an item from the array (left) is
assigned to $value (right).

This is the way to read the “as” syntax, yes; as I’ve said in a few places, a Context Manager is like an iterator that goes around once.

That seems symmetrical to using (new Manager as
$manager) where the instance (left) is assigned to the variable (right).

This, however, is why it was changed: that is not what is happening. The Context Manager is not assigned to the variable, it produces a value which is assigned to the variable.

Again, look at the foreach equivalence: you wouldn’t write “foreach(new MyIterator as $iterator)”, because it’s not the iterator that ends up in the variable, it’s the items produced by the iterator.

So you have “foreach(new MyIterator as $item)” and “using(new MyContextManager as $contextVar)”.

But evidently that is not obvious to readers.

My conclusion here is exactly the opposite of yours because this
explanation makes as even more symmetrical now. As you’ve mentioned
foreach(new MyIterator as $iterator) is not how it works. An iterator
produces a value that is assigned to the variable after as.
Symmetrically, using(new ContextManager as $context) the
ContextManager is also producing a value that is assigned to the
variable after as. The only difference is in the keyword: foreach
will loop, but using will not loop since ContextManager is not an
iterator.

I’m assuming this would also be a valid syntax: using ($manager = new ContextManager as $context) which gives us a shot at capturing the
context manager.

I wrote a little snippet to try and see (using GitHub Gist Syntax
Highlighter) how some of the options would look like once IDE support /
syntax highlighting is baked in:

image.png

Ultimately, people will take a quick reading in a Context Manager
documentation and the parallels to explain how it works seems to be
very well in place for as, imo.

In the image above I tried adding for using because I was trying
really hard to come up with a word that could replace each in
foreach to make it even more symmetrical, but I could not find
anything decent.

  • for context(new ContextManager as $context)
    This one reads well, but the fact its two words (and a long word for
    that matter) doesn’t sit well.

  • for with(new ContextManager as $context)
    I’m not a native English speaker, but I think this doesn’t read at all

  • for one(new ContextManager as $context)
    Kind of implies it works with iterators (similar to each), which is not
    exactly the case and the language has very little motivation to create
    an iterator that loops only once just to make this work.

  • for using(new ContextManager as $context)
    Similar thoughts as for context, the only difference that makes me
    like this one more is because for context doesn’t sit well with try context while for using could go well with try using.

In any case, the longer I sit with this the more I dislike => for this.

In an array, => is an assignment of key/value pairs.
For foreach ($iterator as $key => $value), the => is a destructing
instruction that aligns with how the array was originally assigned.
For the arrow function, it’s kind of implied in the name ARROW function
that we would need a syntax in the lines of fn () =>. I don’t think
this relates well with arrays, but it works well to me because of how
universal arrow functions are in Javascript.
For match ($value), it reads as WHEN ($value) MATCH '' THEN. The arrow here =>` is the THEN.

My conclusion is that => is either a value placement (for arrays) or
an instruction execution (for arrow function and match). As such, If =>
were to be incorporated into Context Managers, I think it would make
much more sense like this:

using (new ContextManager as $context) => [single line expression]
where the arrow creates a symmetry with how arrow functions work.

First, yeesh, where were y’all 2 weeks ago? :stuck_out_tongue:

Second, I find it fascinating that there’s so many different mutually-incompatible spins on what => means. As argued in the short-functions and auto-capture-closure RFCs from a few years ago, => in nearly all cases currently means “evaluates to.”

$arr = [
‘a’ => 'A",
‘b’ => ‘B’,
];

$arr[‘a’] // This “evaluates to” A

match ($foo) {
‘a’ => ‘A’,
‘b’ => ‘B’, // The ‘b’ case “evaluates to” B
};

fn($foo) => ‘bar’; // This function “evaluates to” bar

foreach ($arr as $k => $v) {}

The last is a little bit inconsistent, but can still be read as “$k, and the thing that $k evaluates to.”

This is not at all how I read it. “evaluates to” is an assignment, and assignments in PHP flow right to left.

In the case of an array, if I access the “a” key above, I get the value “A”. For your match example, if the match applies to “a”, I get the value “A”. For an arrow function, I get the callable with the return. In other words, the expression on the right is either evaluated or assigned to the thing on the left.

With the proposed syntax, “using ($expression => $variable) { … }”, this reads backwards: the expression is now on the left, and is captured to the thing on the right. That’s what I’m saying will be confusing.

So given that, and as Rowan noted the context manager is NOT the context variable:

using ($cm evaluates to $cv) {}

That’s the meaning we’re going for.

And again, that doesn’t make sense from typical assignment rules. This is why the other proposal’s syntax (“let ($var = expression)”) is not having the same debate - the syntax is consistent with existing usage. It’s also consistent with how other scripting languages handle block scoping of variables (see JS, shell scripting).

If there’s some better word than =>, we’re still open to it, but I am going to insist on left-to-right reading. $cm is created first, then it produces $cv.

‘as’ is what we had originally, since that’s what Python used. However, it was pointed out that it was confusing as it implied the CM and CV were the same thing, or rather the expression on the left gets assigned to $cv, which is not the case. Hence the change.

I didn’t find it ambiguous, but I can see where others might. That said, I found it less ambiguous than => here.

A symbol does have the (dis?)advantage of a somewhat squishier meaning than a keyword.

I want block scoping; I have been bitten by accidental re-assignment within a block many times, and hate having to come up with an ever-so-slightly-different variable name to disambiguate. I don’t care if it’s this proposal or the “let” proposal in terms of how the engine handles it - but the syntax of “using (expression => $var)” isn’t going to get my vote due to how easily it can be written incorrectly.

I have no problem with the “using (expression as $var)” syntax, and the "let($var = expression) syntax is just fine for me as well.

···

Matthew Weier O’Phinney
mweierophinney@gmail.com
https://mwop.net/
he/him

On 16/12/2025 21:40, Matthew Weier O'Phinney wrote:

I want block scoping; I have been bitten by accidental re-assignment within a block many times, and hate having to come up with an ever-so-slightly-different variable name to disambiguate. I don't care if it's this proposal or the "let" proposal in terms of how the engine handles it - but the syntax of "using (expression => $var)" isn't going to get my vote due to how easily it can be written incorrectly.

I want block scoping too. I don't think *either* of the current proposals achieves that elegantly.

Context Managers aren't even trying to achieve it. Block scoping of the context variable is a minor design detail which was added after the RFC was shared with the list. You could in theory write a Context Manager which took a value in its constructor and spat it out unchanged from its "enter" method, but it would be a weird and wasteful way to create a local variable.

The let() block proposal *is* about block scoping, but only for its own special kind of block. I am not convinced by the reasons given for using this unusual approach.

I would much rather we copy the precedent established by ALGOL 60 years ago, and have variable declarations as statements which can appear in the body of any block. In particular, I would probably just copy JavaScript: allow "let $foo;" and "let $foo=expression;" anywhere, and disallow use in the "temporal dead zone" between the start of the block and the declaration.

However, I would also like Context Managers; which are a separate feature, giving nice sugar for re-usable try-finally blocks.

--
Rowan Tommins
[IMSoP]

On Tue, Dec 16, 2025, at 3:40 PM, Matthew Weier O'Phinney wrote:

If there's some better word than =>, we're still open to it, but I am going to insist on left-to-right reading. $cm is created first, then it produces $cv.

'as' is what we had originally, since that's what Python used. However, it was pointed out that it was confusing as it implied the CM and CV were the same thing, or rather the expression on the left gets assigned to $cv, which is not the case. Hence the change.

I didn't find it ambiguous, but I can see where others might. That
said, I found it _less_ ambiguous than => here.

A symbol does have the (dis?)advantage of a somewhat squishier meaning than a keyword.

I want block scoping; I have been bitten by accidental re-assignment
within a block many times, and hate having to come up with an
ever-so-slightly-different variable name to disambiguate. I don't care
if it's this proposal or the "let" proposal in terms of how the engine
handles it - but the syntax of "using (expression => $var)" isn't going
to get my vote due to how easily it can be written incorrectly.

I have no problem with the "using (expression as $var)" syntax, and the
"let($var = expression) syntax is just fine for me as well.

I think it's important to realize that the two RFCs may be similar, but they're actually doing two VERY different things.

The `let` RFC is defining a guaranteed "unset this variable later". That's it.

The CM RFC is defining a way to package up setup and teardown logic into a reusable component. That happens to also involve unsetting a few variables, but that's not its primary design. The primary design mandates the separation of the context manager from context variable.

The CM RFC does substantially more than the `let` approach would be capable of, and is a model proven in Python to be useful and effective. (As I said elsewhere, I think `let` as an addition to existing structures would make more sense than using it for arbitrary new blocks, which would also avoid any confusion with CMs.)

So are you against the Context Manager proposal entirely, or would you be on board if we can find a non-confusing syntax? Ie,

using (new CM() which produces $var) {

}

If we can find a less verbose way to spell "which produces", would you be on board?

--Larry Garfield

On 16/12/2025 09:07, Tim Düsterhus wrote:

That definition doesn't make sense to me. An iterator that only ever emits a single value is not actually iterating anything and it would never occur me to interpret it like that.

Well, firstly, it is certainly possible to have an iterator that only emits one item; or even no items at all. There are even design patterns that rely on that - in some languages, the Option/Maybe type is a zero-or-one-item iterator, and you unwrap the value using "map" to either call or not call a function.

But that wasn't really the image I was trying to conjure; it's more about the relationship between the Iterator and the item. Here's a metaphor which might or might not work:

You are at an arcade. There is a machine with a single button, and a tray. Every time you press the button, a toy drops into the tray. After you press the button a few times, toys stopping coming out, because there are none left in the machine.

This machine is labelled "Iterator".

Next to it, there is another machine which looks similar. When you press the button on this one, some music starts, and then a toy drops into the tray. When you press the button again, the music stops. No more toys drop.

The toys are not making the music, and the music is not making the toys; they just both happen when you press the button.

This machine is labelled "Context Manager".

Then there's a third machine, which doesn't have the tray, only the button. You press the button once, and the music starts; you press it again, and it stops.

This machine is also labelled "Context Manager".

That's what I mean by "like an iterator with one item": that Iterators and Context Managers are similar kind of machines. The "foreach" and "using" statements don't create or move those machines, they *operate* them, pressing the button so the music plays and the next toy comes out.

--
Rowan Tommins
[IMSoP]

On Tue, Nov 4, 2025, at 2:13 PM, Larry Garfield wrote:

Arnaud and I would like to present another RFC for consideration:
Context Managers.

PHP: rfc:context-managers

You'll probably note that is very similar to the recent proposal from
Tim and Seifeddine. Both proposals grew out of casual discussion
several months ago; I don't believe either team was aware that the
other was also actively working on such a proposal, so we now have two.
C'est la vie. :slight_smile:

Naturally, Arnaud and I feel that our approach is the better one. In
particular, as Arnaud noted in an earlier reply, __destruct() is
unreliable if timing matters. It also does not allow differentiating
between a success or failure exit condition, which for many use cases
is absolutely mandatory (as shown in the examples in the context
manager RFC).

The Context Manager proposal is a near direct port of Python's
approach, which is generally very well thought-out. However, there are
a few open questions as listed in the RFC that we are seeking feedback
on.

Discuss. :slight_smile:

Hi folks. The holidays are over, so we're back on Context Managers.

There are three questions outstanding, 2 of which we want feedback on before we can finalize for a vote.

1. Discussing between us, Arnaud and I aren't confident in expression-based `using`. It would necessitate a trailing semicolon in all cases, and while we have ideas for the return value there's nothing that has clear and indisputable benefit. So we're going to drop this one unless anyone wants to make a strong case for it.

2. The syntax bikeshed. Right now, what we have in the RFC is `using (new CM() => $var)`. We feel that's sufficiently self-descriptive for "produces". However, there was some pushback on that and we're still open to other ideas here. The critera would be "clear, unambiguous, and easy to type". If there's no clear consensus, we'll probably stick with `=>`.

3. One idea we've discussed internally is allowing non-context-manager objects in the `using` statement. If the left-side value in `using` is a non-CM, then it will itself be used as the context variable. It would still get unset at the end of the block, so if simply unsetting it is sufficient cleanup it would eliminate the need for a wrapper. This would remove the need for special casing of `resource` variables, and would also, in effect, absorb the behavior of the block scoping `let` proposal. The block scoping behavior becomes a degenerate case of `using`, feeding two birds with one bird feeder. (To be more animal friendly.)

I'm open to that idea, though Arnaud doesn't like it on the grounds that it could be too confusing for folks. So we're putting it out to see if there is a consensus on it.

Once those issues are addressed, I think we're nearly able to take CMs to a vote. (If anyone else wants to weigh in on some other part as well, even if it's just a voice of support/approval, now is the time.)

Cheers.

--Larry Garfield

Hi

Note: I've not been able to fully work through the ML discussion and meaningfully think about all the commentary. This includes some of the older emails. I have also not yet given the RFC an in-depth read, like I would have normally done.

Some (incomplete) notes for now:

Am 2026-01-13 23:19, schrieb Larry Garfield:

Once those issues are addressed, I think we're nearly able to take CMs to a vote. (If anyone else wants to weigh in on some other part as well, even if it's just a voice of support/approval, now is the time.)

Something that I had noted in response to Rowan in the (what you call) “Bonus Thread” in php.internals: Re: Examples comparing Block Scoped RAII and Context Managers and also in this RFC's discussion in php.internals: Re: [RFC] Context Managers and what was also mentioned by Marco Deleu in php.internals: Re: [RFC] Context Managers and what I feel is neither properly justified within the RFC text and as far as I can tell was not really discussed either:

The fact that `break;` and `continue;` target `using()` blocks. To me it violates the principle of least surprise that

     foreach ($users as $user) {
         using (new SuppressErrors()) {
             if ($user->isAdmin()) {
                 $firstAdmin = $user;
                 break;
             }
         }
     }

is incorrect code. The RFC just states *that* this will happen as a fact, but does not attempt to explain *why* that decision was made and neither are there any examples showcasing how that could be useful.

I'd also like to note that there's also an “Open Issue” listed in the RFC that is related to this topic.

---------------

With regard to the desugaring listed at the top: Can you please also provide the desugaring for the case where no context variable is specified for completeness?

Best regards
Tim Düsterhus

On Thu, Jan 15, 2026, at 9:52 AM, Tim Düsterhus wrote:

Hi

Note: I've not been able to fully work through the ML discussion and
meaningfully think about all the commentary. This includes some of the
older emails. I have also not yet given the RFC an in-depth read, like I
would have normally done.

Some (incomplete) notes for now:

Am 2026-01-13 23:19, schrieb Larry Garfield:

Once those issues are addressed, I think we're nearly able to take CMs
to a vote. (If anyone else wants to weigh in on some other part as
well, even if it's just a voice of support/approval, now is the time.)

Something that I had noted in response to Rowan in the (what you call)
“Bonus Thread” in php.internals: Re: Examples comparing Block Scoped RAII and Context Managers and also
in this RFC's discussion in
php.internals: Re: [RFC] Context Managers and what was also
mentioned by Marco Deleu in
php.internals: Re: [RFC] Context Managers and what I feel is neither
properly justified within the RFC text and as far as I can tell was not
really discussed either:

The fact that `break;` and `continue;` target `using()` blocks. To me it
violates the principle of least surprise that

     foreach ($users as $user) {
         using (new SuppressErrors()) {
             if ($user->isAdmin()) {
                 $firstAdmin = $user;
                 break;
             }
         }
     }

is incorrect code. The RFC just states *that* this will happen as a
fact, but does not attempt to explain *why* that decision was made and
neither are there any examples showcasing how that could be useful.

The core point here is that we needed some way to successfully terminate the block early, and `return` was already taken by the function. `break` seemed like the obvious keyword to use, as in other contexts it also means "skip to the end of this block."

`continue` is really just to mirror what switch does with it. If the consensus is to simply disallow `continue` entirely (which is inconsistent with what switch does today), we're OK with that. I don't feel strongly either way, so we're looking for a consensus.

If there's a different keyword than `break` you think would make more sense here, please do suggest it and provide an argument. But "break does the same thing here it does in switch, foreach, and while" seems like a pretty straightforward approach.

I'd also like to note that there's also an “Open Issue” listed in the
RFC that is related to this topic.

---------------

With regard to the desugaring listed at the top: Can you please also
provide the desugaring for the case where no context variable is
specified for completeness?

Desugaring at the top? The desugaring is explained about a third of the way down. :slight_smile: Is that what you mean? (In the "Implementation" section.) It doesn't make any sense to go into that level of detail in the introduction.

--Larry Garfield

On Tue, Nov 4, 2025 at 9:19 PM Larry Garfield <larry@garfieldtech.com> wrote:

Arnaud and I would like to present another RFC for consideration: Context Managers.

PHP: rfc:context-managers

You'll probably note that is very similar to the recent proposal from Tim and Seifeddine. Both proposals grew out of casual discussion several months ago; I don't believe either team was aware that the other was also actively working on such a proposal, so we now have two. C'est la vie. :slight_smile:

Naturally, Arnaud and I feel that our approach is the better one. In particular, as Arnaud noted in an earlier reply, __destruct() is unreliable if timing matters. It also does not allow differentiating between a success or failure exit condition, which for many use cases is absolutely mandatory (as shown in the examples in the context manager RFC).

The Context Manager proposal is a near direct port of Python's approach, which is generally very well thought-out. However, there are a few open questions as listed in the RFC that we are seeking feedback on.

Discuss. :slight_smile:

--
  Larry Garfield
  larry@garfieldtech.com

Hi Larry, Hi Internals,

Last year I promised you (Larry) some feedback on-list as well and
didn't get around to it until now. I recognize the strain that
repeating arguments has on a discussion like this, and this topic has
already taken up a lot of focus and time for the folks here.

But I wanted to at least explain why I think PHP would be better off
without having this in core and why I think it would be a net negative
for PHP to have this.

So to summarize, I find the feature doesn't fit in PHP. It's
introducing more magically called methods, burdened with unnecessary
complexity, while being very limited in its potential (sensible) uses.
Combined with its class-only high-verbosity approach, I feel this is
lacking places where it would improve PHP code in general and better
suited for a library for people that want this type of, what I
consider, magical indirection.
With the name also being extremely generic and non-descriptive, this
all feels like bloat to me that complicates the language for no
tangible benefits.

To expand a bit on the points:

- Non-local behavior:

Every using statement is a couple of function calls that are
non-obvious in how they delegate to some __magic interface methods
that are not supposed to (but very able to) be called explicitly. With
the implicit catch and exitContext() ability to return true/false; to
rethrow/suppress an exception, adding even more hidden branching to
execution.

- Variable masking:

A new block masking and restoring the context variables but not others
is an additional source of errors and confusion that I feel doesn't
pay off in terms of value vs. added complexity and error sources.

It's not behavior we have anywhere else in PHP and breaks the flow of
reading and reasoning about code in non-obvious ways.

- Break/Continue semantics:

There is no clear reason for me why this block scope should allow
early returns. If the content is growing to a point where it's needed,
a function is already a reasonable scope. Given that PHP allows for
`break 2;`, something that we'll see more of then, it's manageable. It
just adds to the, for me, unreasonable complexity of the feature.

- Naming:

For me, despite having worked with Python, the name means absolutely
nothing. It doesn't even manage the context of the invocation. If
anything, it manages when a resource is released into and removed and
deallocated from a scoped context. And that sentence also is rough.

PHP, for better or worse, doesn't burden its users with having to
study many CS concepts beyond basic OO or procedural programming and
still allows them to write obvious, valuable, and predictable code. I
understand that with its evolution this has changed, and we have added
a lot of redundancy (short arrays, short functions, pipes, etc..) to
provide sugar that has steepened the learning curve for some.
Adding very specific single-use concepts to the language with their
own disconnected naming schemes, syntax, or, in this case, hidden
behaviors should be carefully considered. And while I'm sure you did
we came to different conclusions.

- Block scoping:

Personally, I don't see the need for block scoping in PHP in general.
But having a generic solution that works without creating a new class
for each case would feel like something that at least can be used by
everyone and every part of the language.

Tying this to custom objects doesn't feel like a language level
feature but something that should be in a library.

The worst option would be to allow using() to take a context manager
or a plain expression and make people guess every time the statement
is used if hidden function calls are attached to it.

- Verbosity:

Having to implement three code paths for each ContextManager (enter,
exitWithoutError, exitWithError) within two functions, with a near
mandatory `if` in a separate class, doesn't strike me as useful over
patterns like getting and returning a connection out of a pool
“manually.” The trade-off between this and already existing solutions
to this problem with try/finally or construct/destruct isn't enticing.

- Object lifecycle in PHP:

Just to reiterate because it bugs me as PHP zval life cycles are used
as an argument here: Reference counting in PHP is fully deterministic,
and code like `function () { $x = new Foo(); return; }` will
deterministically construct and destruct (at the end of the function
as the variable gets cleaned up). Use cases where the GC would
actually come into play are extremely rare from the real-world usages
we can see in Python. I also haven't seen an example in PHP nor
something in the RFC that looks overly convincing in improving this
with managed in-function unsets. The error handling option is nice,
but for maintainability, simplicity, and effort in writing code, I'd
still prefer this to try/(catch)/finally

Layering another level of lifecycle management on top of the existing
PHP behavior doesn't feel like a simplification but rather like
another source of complexity with this new niece special case.

--

In summary, this feels like beyond what's necessary to get rid of a
couple of try/finally blocks per application and encourages bad
patterns like using ContextMangers for async instead of more modern
APIs that have evolved since then.

Kind Regards,
Volker