On 01/12/2025 14:26, Tim Düsterhus wrote:
You are wrong here. Manually interacting with an Iterator is not just
safe, it is also necessary for some use cases. The simplest example
would be iterating two Iterators in lock-step (i.e. doing a `zip()`
operation / array_combine()).
It is certainly *possible* to use the Iterator methods safely without a
foreach() construct or built-in aggregate; but it's also easy to miss
the implications, and cause very confusing behaviour. For instance, you
could call next() in two different pieces of code, and each would miss
half the items; or you could call rewind() on an iterator that was
already in use elsewhere.
The same is true of the ContextManager interface: you could certainly
call enterContext() and exitContext() manually with no problems at all.
It might even be necessary, for much the same reasons as iterators: you
might want to write code that aggregates two context managers in a
specific arrangement.
As I understand it, your concern is that someone will call
enterContext() without exitContext(). You could do exactly the same
thing if you manually handle an Iterator.
Consider this method:
function getDataIterator(): Generator {
$this->acquireLock();
while ( $this->hasMoreResults() ) {
yield $this->getNextResult();
}
$this->releaseLock();
}
If the user calls $it = $foo->getDataIterator(), they can call
$it->current() and get a result, but never call $it->next(). They could
even use it in a foreach() loop but `break` out before consuming all the
results. In such cases, the lock would never be released, unless there's
an extra safety check in the destructor.
I think the currently proposed names actually make that *less* likely
for a ContextManager: if you enter something, you probably want to exit
it later.
We could reinforce that further by also prefixing the method names with
"__", but we don't have any precedent for that, and misuse would still
be possible.
--
Rowan Tommins
[IMSoP]