In PHP, a variable lives until the function ends, even if you only needed it for one if or one loop. This RFC from Seifeddine Gmati and Tim Düsterhus proposed a let() construct that scopes one or more variables to a single block. When the block ends, each variable goes back to its old value, or gets unset if it didn't exist before. The RFC was declined.
Why it helps
The RFC points to a few common problems:
- Reusing a variable by mistake after it was only meant for one part of a function.
- The
foreachby-reference bug, where the loop variable stays bound to the last array element after the loop. - Cleaning up resources like file handles and locks, which today often takes a
try/finallyblock or manualunset()calls.
How it looks
let ($user = $repository->find(1)) if ($user !== null) { printf("Hello %s!\n", $user->name); } // $user is now unset assert(!isset($user));
Here's the RFC's fix for the foreach reference bug:
$array = [1, 2, 3]; let ($value) foreach ($array as &$value) { $value *= 2; } // $value is unset here, so the reference is gone. foreach ([99] as $value) {} var_dump($array); // [2, 4, 6]
Old values come back when the block ends, no matter how you leave it, including through a thrown exception. If nothing else still holds a stored object, PHP frees it right away and runs its destructor. That means a file lock can be released as soon as the block ends instead of when the function returns.
Under the hood, let() behaves like a try/finally block that PHP writes for you. You can't use global or static inside it, and you can't goto into it.
Scoped variables must be listed at the start of the block. The RFC says this avoids confusing rules about which $array a given line refers to.
What it means for existing code
Nothing changes, since the RFC was declined. Had it passed, let would have become a reserved word, so it could no longer be used as a function name. The RFC found 4 such uses in the top 18,975 Composer packages. Editors and static analysis tools would also have needed updates.
The vote
It failed with 13 votes in favor, 15 against and 5 abstaining, short of the two-thirds majority it needed. Voting closed on February 5, 2026.