On Tue, Nov 4, 2025 at 9:19 PM Larry Garfield <[email protected]> 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. :-)
>
> 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. :-)
>
> --
> Larry Garfield
> [email protected]
>
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