PHP RFC: use Statement
- Version: 0.9
- Date: 2025-09-08
- Author: Seifeddine Gmati azjezz@carthage.software, Tim Düsterhus timwolla@php.net
- Status: Draft
Introduction
This RFC proposes the introduction of the use
statement, a new language construct for managing temporary variables and resources. It allows developers to define a block or a statement in which one or more variables are in scope. Upon statement completion, these variables are automatically and reliably unset, triggering their destructors and releasing any held resources.
This construct addresses several recurring challenges in PHP. Developers often need to ensure that resources like file handles are properly disposed of, especially when exceptions occur. Additionally, controlling variable scope is a frequent source of subtle bugs, such as the classic foreach
-by-reference issue, where variables leak out of loops. Manually managing this with verbose try
-finally
blocks or error-prone unset()
calls clutters code and reduces clarity.
The use
statement provides a single, elegant solution to these common problems. By adopting a feature that has proven its value in other ecosystems, like Python’s with
or C#’s using
, PHP can offer a safer and more readable way to write robust, maintainable code.
<?php use ($con = $db->pool->getConnection()) { $con->execute('UPDATE users SET last_seen = NOW() WHERE id = ?', $id); } // $con is now unset and the connection is returned to the pool. assert(!isset($connection));
Proposal
We propose a new use() statement. It takes a list of variable declarations and executes a subsequent statement within a new, temporary scope. Upon completion of that statement—whether normally, through an exception, or a return—all variables declared in the use() list are automatically unset.
This feature provides immense value, particularly for modern applications built on long-lived servers, where disciplined and immediate resource cleanup is not just a best practice, but a necessity for stability and performance. By desugaring to a try-finally block internally, it provides a zero-cost abstraction that is both safe and highly ergonomic, eliminating the need for verbose manual cleanup.
For example, the following code:
<?php use ($a = $b, $c) /* <statement> */
Is semantically equivalent to:
<?php try { $a = $b; $c ??= null; // If $c was not declared, it's initialized to null. /* <statement> */ } finally { unset($a, $c); }
This guarantees that objects are destructed and resources are freed immediately upon exiting the scope, rather than waiting for the function to end or for the garbage collector to run.
The full syntax is defined as follows:
<use_statement> ::= "use" "(" <variable_declarations> ")" <statement> <variable_declarations> ::= <variable_declaration> { "," <variable_declarations> } <variable_declaration> ::= (<variable> "=" <expression>) | <variable>
Examples
Simple example:
<?php use ($x = 10, $y) { var_dump($x); // int(10) var_dump($y); // NULL } // Both $x and $y are now unset. var_dump(isset($x)); // bool(false) var_dump(isset($y)); // bool(false)
Example showing an edge case (solving the foreach reference bug):
<?php $array = [1, 2, 3]; use ($value) foreach ($array as &$value) { $value *= 2; } // $value is unset here, breaking the dangerous lingering reference to the last element. // This loop no longer accidentally modifies $array[2]. foreach ([99] as $value) {} var_dump($array); // Correctly outputs: array(3) { [0]=> int(2) [1]=> int(4) [2]=> int(6) }
Example showing reliable resource management:
<?php // Using the https://github.com/azjezz/psl library for an object-oriented file API. use Psl\File; use Psl\File\LockType; function process_file(string $path): string { // Both $file and $lock are guaranteed to be cleaned up after the block. use ( $file = File\open_read_only($path), $lock = $file->lock(LockType::Shared), ) { $content = $file->readAll(); // Do work with the file... return $content; } // The file lock is released here, immediately after it's no longer needed, // which is crucial in high-concurrency environments. }
Backward Incompatible Changes
None. The use keyword is being reused in a new context. The parser can unambiguously distinguish this construct from existing uses of use (for traits, namespace imports, and closures) based on the syntax that follows.
Proposed PHP Version(s)
Next PHP 8.x
RFC Impact
To the Ecosystem
- IDEs, LSPs, and Static Analyzers: Will need updates to understand the new scoping rules. This will enable them to provide correct autocompletion and error analysis, correctly identifying when a variable is out of scope.
- Auto-Formatters and Linters: Will require updates to support the new syntax.
To Existing Extensions
None.
To SAPIs
None.
Open Issues
The final behavior regarding pre-existing variables is subject to discussion on the mailing list before the vote begins. The key question is: what should happen if a variable declared in use() already exists in the outer scope?
- Option A (Unset): The variable is shadowed inside the use block and is unconditionally unset upon exit. Any previous value is lost. This is the current implementation in the PoC.
- Option B (Restore): The variable's original value is saved before entering the use block and restored upon exit. This would create a true block scope that does not affect the outer scope.
Future Scope
This RFC lays the foundation for explicit resource management in PHP. Future proposals could build upon it
- Introducing a Disposable interface (similar to C#'s IDisposable) to allow objects to define custom, explicit cleanup logic that is automatically called by use.
- Extending the use statement to support other resource management patterns.
Voting Choices
This RFC will have a single vote on whether to accept the feature in principle. The behavior for pre-existing variables (Open Issues) will be finalized based on mailing list discussion before the vote starts. A 2/3 majority is required.
Patches and Tests
A proof-of-concept implementation using the `unset` behavior is available at: https://github.com/php/php-src/compare/master...TimWolla:php-src:block-scope
Implementation
After the RFC is implemented, this section should contain:
- The version(s) it was merged into
- A link to the git commit(s)
- A link to the PHP manual entry for the feature
References
Rejected Features
None.