Volker and I drafted a RFC:
https://wiki.php.net/rfc/scope-functions
Please consider it and share your feedback.
I hope it will alleviate pain around some of the most common forms of Closure usage which is "execute this now as part of the called function", which currently can require a lot of "use ($variables)".
For me the primary use case of use ($capturing) was always "I need this function later and want to explicitly document what escapes my function". This, however, required this straightforward usage of Closures to also document every single usage of a variable. Which is really not that beneficial at all.
Thus the scope functions as proposed will be able to fill that gap in future.
Thank you,
Bob
Le 6 mai 2026 à 22:09, Bob Weinand bobwei9@hotmail.com a écrit :
Volker and I drafted a RFC:
https://wiki.php.net/rfc/scope-functions
Please consider it and share your feedback.
I hope it will alleviate pain around some of the most common forms of Closure usage which is "execute this now as part of the called function", which currently can require a lot of "use ($variables)".
For me the primary use case of use ($capturing) was always "I need this function later and want to explicitly document what escapes my function". This, however, required this straightforward usage of Closures to also document every single usage of a variable. Which is really not that beneficial at all.
Thus the scope functions as proposed will be able to fill that gap in future.
Thank you,
Bob
Hi,
This is nice. As I understand it, this RFC could resolve problems that the Context Managers RFC tries to resolve in a simpler and more flexible way. (And it resolves other problems too, of course.)
Taking the first example from the Context Manager RFC:
using (file_for_write('file.txt') => $fp) {
foreach ($someThing as $value) {
fwrite($fp, serialize($value));
}
}
// implementable as:
function file_for_write(string $filename): ContextManager {
return new class($filename) implements ContextManager {
function __construct(private readonly string $filename) { }
private $fp;
function enterContext() {
$this->fp = @fopen($this->filename, 'w');
if (!$this->fp) {
throw new \RuntimeException('Couldn’t open file');
}
return $this->fp;
}
function exitContext(?\Throwable $e = null): ?\Throwable {
@fclose($this->fp);
return $e;
}
};
}
This can be rewritten as:
file_for_write('file.txt', fn($fp) {
foreach ($someThing as $value) {
fwrite($fp, serialize($value));
}
});
// implementable as (which is simpler: one function instead of a whole class):
function file_for_write (string $filename, callable $do_write): void {
$fp = @fopen($filename, 'w');
if (!$fp) {
throw new \RuntimeException('Couldn’t open file');
}
try {
$do_write($fp);
} finally {
@fclose($fp);
}
}
For those of us that abhor exceptions in case of recoverable failure, there is even more. With this RFC, one can easily return true/false (or whatever other signal) for success/failure, while Context Manager strongly leans towards the use of exceptions (although, of course, it remains possible to assign the outcome to a variable and to exit the context with break or goto):
$ok = file_for_write('file.txt', fn($fp) {
foreach ($someThing as $value) {
if (something_is_wrong_with($value))
return false;
fwrite($fp, serialize($value));
}
return true;
});
// implementable as (which is more flexible: exceptions are not the only type of signal):
#[\NoDiscard]
function file_for_write (string $filename, callable $do_write): bool {
$fp = @fopen($filename 'w');
if (!$fp) {
return false;
}
try {
return $do_write($fp);
} finally {
@fclose($fp);
}
}
—Claude