Re: [RFC] Context Managers

From: Date: Mon, 19 Jan 2026 18:10:43 +0000
Subject: Re: [RFC] Context Managers
References: 1 2 3  Groups: php.internals 
Request: Send a blank email to internals+get-129791@lists.php.net to get a copy of this message
On 19/01/2026 15:58, Larry Garfield wrote:
In an ideal world, if we had auto-capturing long-closures, then I would agree this is largely unnecessary and could instead be implemented like so (to reuse the examples from the RFC): $conn->inTransaction(function () { // SQL stuff. }); ... If we had auto-capturing closures, I would probably argue that is a better approach.
I haven't caught up with the discussion fully, but I want to pick up on this specifically, because I disagree. Inversion of control like this would only be suitable in the general case if we had auto-capture *by reference*. I believe every proposal so far has limited automatic capture to *values only*. Auto-capture by value helps you get values *into* the closure, but does not help get anything back *out*. So, if the code you want to sugar looks like this: ``` try{ $db->beginTransaction(); // ... $newFooId = $db->execute('INSERT INTO Foo ... RETURNING FooId'); $newBarId = $db->execute('INSERT INTO Foo ... RETURNING BarId'); // ... $db->commitTransaction(); } finally{ if( $db->isInTransaction()) { $db->rollbackTransaction(); } } // use $newFooId and $newBarId here

Then your options with a capture-by-value closure are either

a) list the outputs as manual by-ref captures:

``` $newFooId = $newBarId = null;
$db->inTransaction(fn() use (&$newFooId, &$newBarId) {
// ...
$newFooId = $db->execute('INSERT INTO Foo ... RETURNING FooId');
$newBarId = $db->execute('INSERT INTO Foo ... RETURNING BarId');
// ...
}); // use $newFooId and $newBarId here
or b) return the outputs, and extract them using array destructuring or similar: ``` [$newFooId, $newBarId] = $db->inTransaction(fn() { // ... $newFooId = $db->execute('INSERT INTO Foo ... RETURNING FooId'); $newBarId = $db->execute('INSERT INTO Foo ... RETURNING BarId'); // ...
    return [$newFooId, $newBarId];
});// use $newFooId and $newBarId here

A Context Manager - or any other syntax based on a code block rather than a full stack frame - instead gives you direct access to the local variables:

``` using($db->transaction()) {
// ...
$newFooId = $db->execute('INSERT INTO Foo ... RETURNING FooId');
$newBarId = $db->execute('INSERT INTO Foo ... RETURNING BarId');
// ...
}// use $newFooId and $newBarId here
Even if we had automatic capture by value, I think Context Managers would be a useful proposal to discuss. -- Rowan Tommins [IMSoP]

« previous php.internals (#129791) next »