rfc:optin_block_scoping

PHP RFC: use Statement

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&#39;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.

Add the use statement for block-scoping?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

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.

rfc/optin_block_scoping.txt · Last modified: by timwolla