Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:124377 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by qa.php.net (Postfix) with ESMTPS id 1E9571A00B7 for ; Wed, 10 Jul 2024 23:11:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1720653195; bh=gcjZWtfNIWFPKg+WyoBMnJo7LmsSWYHBArCmTkzshA0=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=iJIvcoHo99RNQHv3zlgMgW8eCY4k6tnXqORdLjvlfteQ+PLqhdxx8DpfPpNPonQqI qejejpD8FFesUbr6XeSKKLot/XPnV8O0PNdsleS0r9CK5NzPgNz/Eb4G86vTVkc8LE zlkPVKE56+7TgayN1nAEqpBIIp/0pzvpjV+XCuSGlG3w1dkrvrQfadHjVvHAkDyT/F rAsknvh5SNdfwU+5KEQVTN2q9N3emdpSlEBf0EZBcgm6Nr21P52bRz1ysA4z/3Vxni EtKjXBXh9nUNjAF2NHF6QGOd3fmg+GE0zmEvxIEQ140ggWpwNZX+lcYJzIE3xZbU/b +Q1//HI+BtebA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 342F7180A9B for ; Wed, 10 Jul 2024 23:13:15 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-il1-f177.google.com (mail-il1-f177.google.com [209.85.166.177]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 10 Jul 2024 23:13:14 +0000 (UTC) Received: by mail-il1-f177.google.com with SMTP id e9e14a558f8ab-38b4e08c3e5so1232055ab.3 for ; Wed, 10 Jul 2024 16:11:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1720653107; x=1721257907; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=oYKFFKGoxU7S89aKY7km7taSp8W/RkFPfjDRF76XH5I=; b=JTALeDs4gE1yLzD4hr+1JKAMIlrEFBnUGWl/3IN2ZMi1vf+BaU1MYbb8gYrf/lR6Nw /JJHd+BjrJTeajzrCHoy+HpO0GT/oSH+C5chpYimOwg58/3RAkbHRrF/3PRVNoxXrj6Y 3o+2G9mhboUwo+Wg5W0iLFOAtiJ9xpjt/IcA6nZTEE8g5SGZEWA5C5XDJSfVTSCxNsjD IU1EgongC5dZEnVMf8XAvLJsWqXvpjly20YhX+NpM/IARaBTj8lGG1iAW86lpsY/h5NR gOJlACM0ccF58EMCgoc3tobIGATYTzEZ//e9CpT48TuwUtMPd9NxLYEoArNq4osxk4Zj qQWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1720653107; x=1721257907; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=oYKFFKGoxU7S89aKY7km7taSp8W/RkFPfjDRF76XH5I=; b=Pch5jG2Kv5snJtn61anGwQNmB5L5FT4xbI9yD75MDrqfJy/DJXhDTILaAWj4V9JSTL 0sM8HVhX0GYot1thXn++O48m5r3DrpW0rh/0VWYnBiNXgJtXUfz62mB1LMOPY4VF/hTj LaoKWvO3zPe+Ca7t76hEwEfH785CnKUFUN2mw3vjFJBwC3myDsO3QYvSFcKWb1/pFGMU /n6Mhz/XvE6NekwFyBrvPpD2Jy5kccvA/MPdJhZTB8N11Q9eT7aMzJ3kp9aGTEWuzSwL wTryD2o/Opt6Bgqr6dYfgl4pNXxIN0kVYIYop5Ul0UCFATM+/IQkJBrE8NLodfd7BheQ 93Bw== X-Forwarded-Encrypted: i=1; AJvYcCUp1wtTd6oJ5vPun2zZAajk7RZiejou7SzCcJsRpC08mxkBK9+ajJnJpHhjiOwEjKmwzbGYjnWFbpdogc1AWtFG0cvt5vfVEA== X-Gm-Message-State: AOJu0YzFAXbESHGkr8fK5MU5GXYJD6IX1gmhjVAMWUSgqDUpVUDdoD9g Kmsmuyiob5XUTBoE1PsBsqSoHEzQpJEwFQk5P46aY2GUjUtmnnFza1SDskTNH6HddMK8KEueSM7 18Ci/7JEnssAUw+eEKt7j4PDKAxmMov7bYGE= X-Google-Smtp-Source: AGHT+IG5Q6xpmtGdqc/bd55CnbmecXn7XFfAo2Dfg9hYVi05y85Bnu8EXbt6OM20XQ6hLIYH7DsneUQ95qobPtw+aDk= X-Received: by 2002:a05:6e02:12e9:b0:375:a37c:14b6 with SMTP id e9e14a558f8ab-38a58d32ca0mr83141185ab.23.1720653107310; Wed, 10 Jul 2024 16:11:47 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net MIME-Version: 1.0 References: <14b769e9-acb7-46d2-85ab-f276e82f3069@app.fastmail.com> <0cfec69f-2b0c-4c1e-9e47-e6379cf90ea2@app.fastmail.com> In-Reply-To: <0cfec69f-2b0c-4c1e-9e47-e6379cf90ea2@app.fastmail.com> Date: Thu, 11 Jul 2024 01:11:36 +0200 Message-ID: Subject: Re: [PHP-DEV] [low priority] WeakMaps with scalar keys To: Rob Landers Cc: "Gina P. Banyard" , internals@lists.php.net Content-Type: multipart/alternative; boundary="0000000000003e39b8061cecc553" From: benjamin.morel@gmail.com (Benjamin Morel) --0000000000003e39b8061cecc553 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable > > The answer is: it depends. If you don=E2=80=99t need the array to clean u= p after > itself, you can indeed use an array of WeakReference to get most of the w= ay > there. If you want it to clean up after an object gets removed, you eithe= r > need to add support to the stored object=E2=80=99s destructor (which isn= =E2=80=99t always > possible for built-in or final types), or create your own garbage collect= or > 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 */ private array $map =3D []; public function count(): int { foreach ($this->map as $value =3D> $weakReference) { if ($weakReference->get() =3D=3D=3D null) { unset($this->map[$value]); } } return count($this->map); } public function getIterator(): Generator { foreach ($this->map as $value =3D> $weakReference) { $object =3D $weakReference->get(); if ($object =3D=3D=3D null) { unset($this->map[$value]); } else { yield $value =3D> $object; } } } public function offsetExists(mixed $offset) { if (isset($this->map[$offset])) { $object =3D $this->map[$offset]->get(); if ($object !=3D=3D null) { return true; } unset($this->map[$offset]); } return false; } public function offsetGet(mixed $offset): object { if (isset($this->map[$offset])) { $object =3D $this->map[$offset]->get(); if ($object !=3D=3D null) { return $object; } unset($this->map[$offset]); } throw new Exception('Undefined offset'); } public function offsetSet(mixed $offset, mixed $value): void { $this->map[$offset] =3D 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 =E2=80=9ConRemove(= )=E2=80=9D > method that takes a callback for the WeakReference class. > > =E2=80=94 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 --0000000000003e39b8061cecc553 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
The answer i= s: it depends. If you don=E2=80=99t 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=E2=80=99s destructor (which isn=E2=80= =99t always possible for built-in or final types), or create your own garba= ge collector that scans the array.=C2=A0

<= /div>
It is indeed doable in userland using WeakReferences, with a smal= l performance penalty:

```
class Reverse= WeakMap implements Countable, IteratorAggregate, ArrayAccess
{
=C2=A0= =C2=A0 /**
=C2=A0 =C2=A0 =C2=A0* @var array<int|string, WeakReferenc= e>
=C2=A0 =C2=A0 =C2=A0*/
=C2=A0 =C2=A0 private array $map =3D [];=

=C2=A0 =C2=A0 public function count(): int
=C2=A0 =C2=A0 {
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 foreach ($this->map as $value =3D> $weakR= eference) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if ($weakReference= ->get() =3D=3D=3D null) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 unset($this->map[$value]);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }

=C2=A0 =C2=A0 =C2= =A0 =C2=A0 return count($this->map);
=C2=A0 =C2=A0 }

=C2=A0 = =C2=A0 public function getIterator(): Generator
=C2=A0 =C2=A0 {
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 foreach ($this->map as $value =3D> $weakRefe= rence) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 $object =3D $weakRefe= rence->get();

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if ($obje= ct =3D=3D=3D null) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 unset($this->map[$value]);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 } else {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = yield $value =3D> $object;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = }
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
=C2=A0 =C2=A0 }

=C2=A0 =C2=A0 = public function offsetExists(mixed $offset)
=C2=A0 =C2=A0 {
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 if (isset($this->map[$offset])) {
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 $object =3D $this->map[$offset]->get();
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if ($object !=3D=3D null) {=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return true;=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }

=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 unset($this->map[$offset]);
=C2=A0 =C2=A0 =C2=A0= =C2=A0 }

=C2=A0 =C2=A0 =C2=A0 =C2=A0 return false;
=C2=A0 =C2=A0= }

=C2=A0 =C2=A0 public function offsetGet(mixed $offset): object=C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (isset($this->map[$of= fset])) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 $object =3D $this-&g= t;map[$offset]->get();

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = if ($object !=3D=3D null) {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 return $object;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = }

=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 unset($this->map[$off= set]);
=C2=A0 =C2=A0 =C2=A0 =C2=A0 }

=C2=A0 =C2=A0 =C2=A0 =C2=A0 = throw new Exception('Undefined offset');
=C2=A0 =C2=A0 }

= =C2=A0 =C2=A0 public function offsetSet(mixed $offset, mixed $value): void<= br>=C2=A0 =C2=A0 {
=C2=A0 =C2=A0 =C2=A0 =C2=A0 $this->map[$offset] = =3D WeakReference::create($value);
=C2=A0 =C2=A0 }

=C2=A0 =C2=A0 = public function offsetUnset(mixed $offset): void
=C2=A0 =C2=A0 {
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 unset($this->map[$offset]);
=C2=A0 =C2=A0 }<= br>}
```=C2=A0

Now that I think about it, it might be simpler= to add an =E2=80=9ConRemove()=E2=80=9D method that takes a callback for th= e WeakReference class.=C2=A0

=E2=80=94 Rob

A callback when an object goes out of scope would be a great a= ddition to both WeakReference & WeakMap indeed, it would allow custom u= serland weak maps like the above, with next to no performance penalty!

- Benjamin
=C2=A0
--0000000000003e39b8061cecc553--