On Sat, 15 Nov 2025 23:11:44 +0000, Rowan Tommins [IMSoP] wrote:
> Hi all,
>
> The Block Scoping RFC and the Context Manager RFC cover a lot of similar
> use cases, and a lot of the discussion on both threads has been
> explicitly comparing them.
>
> To try to picture better how they compare, I have put together a set of
> examples that implement the same code using both features, as well as
> some other variations, in this git repo:
>
> https://gitlab.com/imsop/raii-vs-cm
>
> A few notes:
>
> - The syntax for the two proposals is based on the current RFC text. If
> they are updated, e.g. to use different keywords, I will update the
> examples.
>
> - I have included examples with closures which automatically capture by
> value, since a lot of the same use cases come up when discussing those.
>
> - There are many scenarios which could be included, and many ways each
> example could be written. I have chosen scenarios to illustrate certain
> strengths and weaknesses, but tried to fairly represent a "good" use of
> each feature. However, I welcome feedback about unintentional bias in my
> choices.
>
> - Corrections and additional examples are welcome as Merge Requests to
> the repo, or replies here.
>
>
> With that out of the way, here are my own initial thoughts from working
> through the examples:
>
> - RAII + block scope is most convenient when protecting an existing
> object which can be edited or extended.
>
> - When protecting a final object, or a native resource, RAII is harder
> to implement. In these cases, the separation of Context Manager from
> managed value is powerful.
>
> - Context Managers are very concise for safely setting and resetting
> global state. RAII can achieve this, but feels less natural.
>
> - An "inversion of control" approach (passing in a callback with the
> body of the protected block) requires capturing all variables *not*
> scoped to the block. Even with automatic by-value capture, those needed
> *after* the block would need to be listed for capture by reference.
>
> - Building a Context Manager from a Generator can lead to very readable
> code in some cases, and closely mimics an "inversion of control"
> approach without the same variable capture problems.
>
>
> I would be interested in other people's thoughts.
>
> Regards,
>
> --
> Rowan Tommins
> [IMSoP]
Another suggestion would be to follow the Java try-with-resources
syntax. It does not require a new keyword to be introduced, as with the
Context Manager syntax. Moreover, it aligns with current
try-catch-finally usage already implemented by PHP developers.
try ($transaction = $db->newTransaction()) {
$db->execute('UPDATE tbl SET cell = :cell', ['cell'=>'value']);
}
Any object that implements TryWithContext can be used with such syntax.
The function returns the exit context operation as callback.
interface TryWithContext {
public function tryWith(): \Closure;
}
For a transaction it might look like this. Rather auto-capture I'd
suggest explicit complete scope capture, by using the use keyword
without parenthesis.
class Transaction implements TryWithContext {
public function tryWith(): \Closure
{
$this->db->beginTransaction();
return function (?\Throwable $e = null) use {
if ($e) {
$this->db->rollbackTransaction();
return;
}
$this->db->commitTransaction();
};
}
}