Re: [RFC] Scope functions
| From: | Claude Pache | Date: | Wed, 06 May 2026 22:26:33 +0000 |
| Subject: | Re: [RFC] Scope functions | ||
| References: | 1 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-130789@lists.php.net to get a copy of this message | ||
> 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:
```php
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:
```php
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):
```php
$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