PHP RFC: using Statement
- Version: 1.0
- Date: 2025-02-11
- Author: Seifeddine Gmati azjezz@protonmail.com, Tim Düsterhus timwolla@php.net
- Status: Draft
- First Published at: http://wiki.php.net/rfc/optin_block_scoping
Introduction
This RFC proposes the introduction of a new language construct, the using
statement. The using
statement allows developers to define a block in which certain variables are in scope, and, upon block completion, are automatically unset—thereby triggering any object destructors and releasing resources.
Resource management is a recurring challenge in PHP applications. Developers often need to ensure that resources (files, database connections, etc.) are properly disposed of, even in the event of exceptions. While PHP’s garbage collection and destructor mechanisms usually suffice, there are scenarios where immediate cleanup is desired. Similar language constructs in other ecosystems (such as Python’s with
, Hack’s using
, and C#’s using
) have proven their utility in writing cleaner, more maintainable code.
using ($user = $this->getUser()) if ($user !== null) { echo "Logged in as ", $user->getName(), PHP_EOL; } else { echo "You are a guest", PHP_EOL; } // `$user` is unset() now. The object may be garbage collected and destructors run, // unless referenced elsewhere. IDEs and static analyzers would know that `$user` // no longer refers to a valid variable and would no longer autocomplete it and point // out any accidental usages.
Proposal
This RFC proposes a new using()
statement, which takes a list of variables (with an optional assignment) and a statement (including block statements using curly braces).
Before executing the given statement, using()
will define all listed variables, such that they may be accessed within the statement without triggering the “Undefined variable $foo” warning. If the assignment-form is used, the variables will be assigned the result of the given expression. Otherwise if the variable is already defined, it will keep its existing value and if it is not defined, it will be assigned a default value of null
.
After the set-up phase, the given statement or list of statements will be executed and after the given statement finished executing, the listed variables will be unset()
, independent of whether the statement ran to completion or whether it threw an Exception.
In more technical terms, a using
statement such as:
using ($a = $b, $c) /* <statement> */
will behave as if the programmer had written:
try { $a = $b; $c ??= null; /* <statement> */ } finally { unset($a, $c); }
This guarantees that, regardless of how the <statement>
terminates (normally, via an exception, or by returning from the function), the variables listed in the using()
statement are automatically unset()
and the reference count of the stored value be decreased. Unsetting the last reference (i.e. decreasing the reference count to zero) will trigger the regular destruction logic, calling the __destruct()
method of objects and will thereby be releasing resources.
The full syntax for the using()
statement is defined as follows:
<using_statement> ::= "using" "(" <variable_declarations> ")" <statement> <variable_declarations> ::= <variable_declaration> { "," <variable_declarations> } <variable_declaration> ::= (<variable> "=" <expression>) | <variable>
Examples
Simple examples showcasing the basic syntax and semantics:
using ($x = 10) { var_dump($x); // int(10) } var_dump($x); // Warning: Undefined variable $x using ($a = "hello", $b = 20) { // string(5) "hello" // int(20) var_dump($a, $b); } // Warning: Undefined variable $a // Warning: Undefined variable $b var_dump($a, $b); $existing = "foo"; using ($existing, $fresh) { // string(3) "foo" // NULL var_dump($existing, $fresh); } // Warning: Undefined variable $existing // Warning: Undefined variable $fresh var_dump($existing, $fresh);
Examples combining using()
with control statements:
using ($choice = random_int(1, 100)) if (($choice % 2) == 0) { echo "The choice {$choice} is even"; } else { echo "The choice {$choice} is odd"; } // Warning: Undefined variable $choice var_dump($choice); using ($i) for ($i = 0; $i < 100; $i++) { echo "{$i} "; } // Warning: Undefined variable $i var_dump($i);
Use Cases
Reliably unlocking “locks” at the end of processing a lockable resource, without waiting for the entire function to finish:
<?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($path): string { using ( $file = File\open_read_only($path), $lock = $file->lock(LockType::Shared), ) { $content = $file->readAll(); } // $lock and $file will be `unset()` at this point, // ensuring that the file does not remain locked until // the entire processing finishes. // Pretend to do hard work. sleep(5); return $content; }
Preventing the “foreach with references” gotcha (see RFC: Unwrap reference after foreach):
$array = [1, 2, 3]; using ($value) foreach ($array as &$value) { $value *= 2; } // array(3) { // [0]=> // int(2) // [1]=> // int(4) // [2]=> // int(6) <- Not a reference. // } var_dump($array);
Another example using a non-block statement:
<?php ... final class SomeController extends Controller { public function __invoke(Request $request): Response { using ($user = $this->getUser()) if ($user !== null) { $this->logger->debug("user {name} is fetching data...", ['name' => $user->name]); } else { $this->logger->debug("anonymous user is fetching data..."); } assert(false === isset($user), "user is disposed of"); ... } }
In these examples, the using
statement binds one or more variables to values or expressions, and then executes the subsequent statement (or block) with these variables in scope.
Conclusion
The using
statement enhances PHP’s language constructs by providing an explicit and concise mechanism for resource cleanup. By de-sugaring into a try-finally block, the construct ensures that resources are consistently released, paving the way for safer and more robust code in PHP applications. Despite the current limitations compared to languages like C# or Hack, this proposal represents a significant step forward in PHP’s evolution.
Backward Incompatible Changes
The introduction of using introduces a new reserved word in PHP.
Proposed PHP Version(s)
Next PHP 8.x (8.5)
RFC Impact
To SAPIs
None.
To Existing Extensions
None.
To Opcache
TODO
New Constants
None.
php.ini Defaults
None.
Open Issues
None.
Unaffected PHP Functionality
List existing areas/features of PHP that will not be changed by the RFC.
This helps avoid any ambiguity, shows that you have thought deeply about the RFC's impact, and helps reduces mail list noise.
Future Scope
This RFC lays the foundation for explicit resource management in PHP. Future proposals may consider:
- Introducing a Disposable interface to allow objects to implement custom cleanup logic.
- Extending the using statement to support additional patterns beyond variable unsetting.
Proposed Voting Choices
Include these so readers know where you are heading and can discuss the proposed voting options.
Patches and Tests
TODO
Implementation
After the project 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
- a link to the language specification section (if any)
References
The using statement is inspired by similar constructs in other programming languages. For further context and design rationale, refer to:
Rejected Features
None.