On 13/06/2022 13:29, Arnaud Le Blanc wrote:
Following your comment, I have clarified a few things in the "Auto-capture
semantics" section. This includes a list of way in which these effects can be
observed. These are really marginal cases that are not relevant for most
programs.
I'm not sure I agree that all of these are marginal, or with the way
you've characterised them...
> Note that destructor timing is undefined in PHP, especially when
reference cycles exist.
Outside of reference cycles, which are pretty rare and generally easy to
avoid, PHP's destructors are entirely deterministic. Unlike in fully
garbage-collected languages, you can use a plain object to implement an
"RAII" pattern - e.g. the constructor locks a file and the destructor
unlocks it; or the constructor starts a transaction, and the destructor
rolls it back if not yet committed.
A related case is resource lifetime: file and network handles are
guaranteed to be closed when they go out of scope, and accidentally
taking an extra copy of their "value" can prevent that.
> It ends up capturing the same variables that would have been captured
by a manually curated |use| list.
This slightly muddles two different questions:
1) Given a well-written closure, where all variables are either clearly
local or clearly intended to be captured, does the implementation do a
good job of distinguishing them?
2) Given a badly-written closure, where variables are accidentally
ambiguous, what side-effects might the user experience?
The answer to question 1 seems to be yes, the implementation does a good
job, and that's good news, and thank you for working on it.
That is not the same, however, as saying that question 2 is never
relevant. Consider the following, adapted from an example in the RFC:
$filter = fn ($user) {
if ( $user->id !== -1 ) {
$guest = $repository->findByUserId($user->id);
}
return isset($guest) && in_array($guest->id, $guestsIds);
};
This is not particularly great code, but it works ... unless the parent
scope happens to have a variable named $guest, which will then be bound
to the closure, since there is a path where it is read before being
written. In this case, side effects include:
* The behaviour will change based on the captured value of $guest
* Any resources held by that value will be held until $filter is
destructed, rather than when $guest is destructed
Whether the risk of these side effects is a big problem is up for
debate, but it's wrong to suggest they don't exist.
Regards,
--
Rowan Tommins
[IMSoP]
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: https://www.php.net/unsub.php