Re: [low priority] WeakMaps with scalar keys
On Thu, Jul 11, 2024, at 01:11, Benjamin Morel wrote:
>> The answer is: it depends. If you don’t need the array to clean up after itself, you can
>> indeed use an array of WeakReference to get most of the way there. If you want it to clean up after
>> an object gets removed, you either need to add support to the stored object’s destructor (which
>> isn’t always possible for built-in or final types), or create your own garbage collector that
>> scans the array.
>
> It is indeed doable in userland using WeakReferences, with a small performance penalty:
>
> ```
> class ReverseWeakMap implements Countable, IteratorAggregate, ArrayAccess
> {
> /**
> * @var array<int|string, WeakReference>
> */
> private array $map = [];
>
> public function count(): int
> {
> foreach ($this->map as $value => $weakReference) {
> if ($weakReference->get() === null) {
> unset($this->map[$value]);
> }
> }
>
> return count($this->map);
> }
>
> public function getIterator(): Generator
> {
> foreach ($this->map as $value => $weakReference) {
> $object = $weakReference->get();
>
> if ($object === null) {
> unset($this->map[$value]);
> } else {
> yield $value => $object;
> }
> }
> }
>
> public function offsetExists(mixed $offset)
> {
> if (isset($this->map[$offset])) {
> $object = $this->map[$offset]->get();
>
> if ($object !== null) {
> return true;
> }
>
> unset($this->map[$offset]);
> }
>
> return false;
> }
>
> public function offsetGet(mixed $offset): object
> {
> if (isset($this->map[$offset])) {
> $object = $this->map[$offset]->get();
>
> if ($object !== null) {
> return $object;
> }
>
> unset($this->map[$offset]);
> }
>
> throw new Exception('Undefined offset');
> }
>
> public function offsetSet(mixed $offset, mixed $value): void
> {
> $this->map[$offset] = WeakReference::create($value);
> }
>
> public function offsetUnset(mixed $offset): void
> {
> unset($this->map[$offset]);
> }
> }
> ```
>
>> Now that I think about it, it might be simpler to add an “onRemove()” method that takes
>> a callback for the WeakReference class.
>>
>> — Rob
>
>
> A callback when an object goes out of scope would be a great addition to both WeakReference
> & WeakMap indeed, it would allow custom userland weak maps like the above, with next to no
> performance penalty!
>
> - Benjamin
>
The callback is surprisingly easy to implement, at least for WeakReference (did it in about 10
minutes on the train as a hack). I haven’t looked into WeakMap yet, but I suspect much of the
plumbing is the same.
I also looked into the ReverseWeakMap a bit and it seems there are just too many foorguns to make it
worthwhile. For example:
$reverseWeakMap[$key] = new Obj();
is actually a noop as in it does absolutely nothing. It gets worse, but I won’t bore you with the
details since I won’t be doing it.
Anyway, while I feel like the implementation for a callback will be extremely straightforward and
the RFC rather simple, I need to go back and read the original discussion threads for this feature
first to see if a callback was addressed. So, still not until after 8.4.
— Rob
Thread (6 messages)