Re: [RFC] Scope functions

From: Date: Fri, 08 May 2026 00:56:15 +0000
Subject: Re: [RFC] Scope functions
References: 1 2 3 4  Groups: php.internals 
Request: Send a blank email to internals+get-130802@lists.php.net to get a copy of this message
On Thu, 7 May 2026 at 19:03 Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote: > On 07/05/2026 13:16, Bob Weinand wrote: > > I am not opposed at all to such a consistent macro system though. > > ... but I would not make a transaction like that a macro. For inlining > variables in a html or sql snippet with proper escaping, definitely great.. > > To me, macros should meaningfully transform their contained code rather > than wrapping a bit of logic around, which would be covered by this RFC. > > It's complimentary, not a replacement. > > > Thinking about this further, I agree that full-blooded macros would be > overkill for this case, but my starting point was that closures don't > feel like the right solution either - they're a different feature being > bent into shape. > > So rather than starting with a Closure, and taking things away, I was > trying to think of ways to start with a simple block of code, and build > it up. > > > For instance, the array_walk example in the RFC is just an ugly (and > probably inefficient) way of writing a foreach loop: > > array_walk($numbers, fn($number) { > $count = bcadd($count, '1'); > $sum = bcadd($sum, $number); > }); > > foreach($numbers as $number) { > $count = bcadd($count, '1'); > $sum = bcadd($sum, $number); > } > > So, what would it look like to generalise that, so we could do the same > for other scenarios, like async() and transaction()? > > > A simple first step would be something that could take a block of code, > and say when to execute it: > > custom_block transaction($dbConnection) { > try { > $dbConnection->beginTransaction(); > __execute_block_body(); > } > catch (\Throwable $e) { > $dbConnection->rollbackTransaction(); > throw $e; > } > $dbConnection->commitTransaction(); > } > > transaction($myDb) { > $myDb->query('UPDATE ...'); > } > > If the block body is never represented as a variable, there's no need to > define what happens when it outlives scope, or the user tries to clone > it, rebind it, etc. > > And if the calling code doesn't look like it's creating a Closure, users > won't have any wrong expectations about variable scopes or lifetimes. > > > Onto that, we can add syntax to push values into the block, e.g. using > the "as" keyword like in "foreach" (example based on the "Generator > decorator managers" section at the end of the Context Managers RFC): > > custom_block opening($filename) { > $f = fopen($filename, "r"); > if (!$f) { > throw new Exception("fopen($filename) failed"); > } > try { > __execute_block_body($f); > } finally { > fclose($f); > } > } > > opening(__FILE__ as $f) { > var_dump($f); > } > > > And some way to pull values out, e.g. a "break with" keyword: > > custom_block transaction(DBConnection $conn) { > $conn->beginTran(); > try { > if (__execute_block_body() === DBConnection::TRANSACTION_ABORT) { > $conn->rollbackTran(); > return; > } > } catch (\Throwable $e) { > $conn->rollback(); > throw $e; > } > $conn->commitTran(); > } > > transaction($connection) { > $affectedRows = $connection->query("UPDATE ..."); > if ($affectedRows === 0) { > break with DBConnection::TRANSACTION_ABORT; > } > // ... > } That looks like a function (aka closure) with a lot of weird extra steps. It’s been years, several proposals and multiple demonstrations on how arrow function syntax can be expanded to be useful with multi-statements. So much gymnastics goes on to deny such a simple and elegant syntax. >

« previous php.internals (#130802) next »