src/WeakMapStasis.php source

1 <?php
2
3 declare(strict_types=1);
4
5 namespace Serializor;
6
7 use WeakMap;
8
9 /**
10 * Stasis for WeakMap objects.
11 * Keys are weakly referenced - entries with keys not strongly referenced
12 * elsewhere are excluded during unserialization.
13 */
14 final class WeakMapStasis extends Stasis
15 {
16 private array $keys = [];
17 private array $values = [];
18 private array $dead = [];
19
20 private function __construct() {}
21
22 public function __serialize(): array
23 {
24 $data = ['k' => $this->keys, 'v' => $this->values];
25 // Only include dead array if there are dead entries
26 $hasDead = false;
27 foreach ($this->dead as $isDead) {
28 if ($isDead) {
29 $hasDead = true;
30 break;
31 }
32 }
33 if ($hasDead) {
34 $data['d'] = $this->dead;
35 }
36 return $data;
37 }
38
39 public function __unserialize(array $data): void
40 {
41 $this->keys = $data['k'];
42 $this->values = $data['v'];
43 $this->dead = $data['d'] ?? array_fill(0, count($this->keys), false);
44 }
45
46 public function getClassName(): string
47 {
48 return WeakMap::class;
49 }
50
51 public static function fromWeakMap(WeakMap $value): WeakMapStasis
52 {
53 $frozen = new WeakMapStasis();
54
55 foreach ($value as $obj => $data) {
56 $frozen->keys[] = $obj;
57 $frozen->values[] = $data;
58 }
59 $frozen->dead = array_fill(0, count($frozen->keys), false);
60
61 return $frozen;
62 }
63
64 /**
65 * Mark an entry as dead by its key's object ID.
66 */
67 public function markDeadByIndex(int $index): void
68 {
69 $this->dead[$index] = true;
70 }
71
72 /**
73 * Get the keys for strong reference tracking.
74 */
75 public function &getKeys(): array
76 {
77 return $this->keys;
78 }
79
80 /**
81 * Get the values for transformation.
82 */
83 public function &getValues(): array
84 {
85 return $this->values;
86 }
87
88 /**
89 * Get the dead flags array.
90 */
91 public function &getDead(): array
92 {
93 return $this->dead;
94 }
95
96 public function &getInstance(): mixed
97 {
98 if ($this->hasInstance()) {
99 return $this->getCachedInstance();
100 }
101
102 $map = new WeakMap();
103
104 for ($i = 0, $len = count($this->keys); $i < $len; $i++) {
105 // Skip entries marked as dead
106 if (!empty($this->dead[$i])) {
107 continue;
108 }
109 // Only add if key is a valid object
110 if (\is_object($this->keys[$i])) {
111 $map[$this->keys[$i]] = $this->values[$i];
112 }
113 }
114
115 $this->setInstance($map);
116 return $map;
117 }
118 }
119