Skip to content

Commit 3c8e835

Browse files
committed
Add WeakClosure
1 parent 4f3a2b9 commit 3c8e835

9 files changed

+516
-45
lines changed

src/ObservableCaptureSet.php

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Boson\Component\WeakType;
6+
7+
use Boson\Component\WeakType\Internal\ReferenceReleaseCallback;
8+
9+
/**
10+
* The implementation calls the {@see ObservableCaptureSet::watch()} `$onRelease`
11+
* callback only if {@see ObservableCaptureSet} does not references to the object.
12+
*
13+
* @template TEntry of object = object
14+
*
15+
* @template-implements \IteratorAggregate<array-key, TEntry>
16+
* @template-implements ObservableSetInterface<TEntry>
17+
*/
18+
final readonly class ObservableCaptureSet implements ObservableSetInterface, \IteratorAggregate
19+
{
20+
/**
21+
* @var \SplObjectStorage<TEntry, ReferenceReleaseCallback<TEntry>>
22+
*/
23+
private \SplObjectStorage $memory;
24+
25+
public function __construct()
26+
{
27+
$this->memory = new \SplObjectStorage();
28+
}
29+
30+
/**
31+
* @param TEntry $entry
32+
* @param \Closure(TEntry):void $onRelease
33+
*
34+
* @return TEntry
35+
*/
36+
public function watch(object $entry, \Closure $onRelease): object
37+
{
38+
$this->memory[$entry] = new ReferenceReleaseCallback($entry, $onRelease);
39+
40+
return $entry;
41+
}
42+
43+
public function detach(object $entry): void
44+
{
45+
unset($this->memory[$entry]);
46+
}
47+
48+
public function getIterator(): \Traversable
49+
{
50+
foreach ($this->memory as $entry) {
51+
yield $entry;
52+
}
53+
}
54+
55+
/**
56+
* @return int<0, max>
57+
*/
58+
public function count(): int
59+
{
60+
return $this->memory->count();
61+
}
62+
}

src/ObservableMapInterface.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Boson\Component\WeakType;
6+
7+
/**
8+
* Allows to store a set of objects with referenced values and track their
9+
* destruction (react to GC cleanup).
10+
*
11+
* ```
12+
* $map->watch($id, $ref, function (object $ref): void {
13+
* echo vsprintf('ID has been destroyed, something can be done with its reference %s(%d)', [
14+
* $ref::class,
15+
* spl_object_id($ref),
16+
* ]);
17+
* ));
18+
* ```
19+
*
20+
* @template TObservable of object = object
21+
* @template TValue of object = object
22+
*
23+
* @template-extends \Traversable<TObservable, TValue>
24+
*/
25+
interface ObservableMapInterface extends \Countable, \Traversable
26+
{
27+
/**
28+
* @param TObservable $key
29+
* @param TValue $value
30+
* @param \Closure(TValue):void $onRelease
31+
*
32+
* @return TObservable
33+
*/
34+
public function watch(object $key, object $value, \Closure $onRelease): object;
35+
36+
/**
37+
* @param TObservable $key
38+
*
39+
* @return TValue|null
40+
*/
41+
public function find(object $key): ?object;
42+
43+
/**
44+
* @param TObservable $key
45+
*/
46+
public function has(object $key): bool;
47+
48+
/**
49+
* @param TObservable $key
50+
*/
51+
public function detach(object $key): void;
52+
53+
/**
54+
* @return int<0, max>
55+
*/
56+
public function count(): int;
57+
}

src/ObservableSet.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Boson\Component\WeakType;
6+
7+
use Boson\Component\WeakType\Internal\ReferenceReleaseCallback;
8+
9+
/**
10+
* The implementation calls the {@see ObservableSet::watch()} `$onRelease`
11+
* callback only if there are no references left to the object.
12+
*
13+
* @template TEntry of object = object
14+
*
15+
* @template-implements \IteratorAggregate<array-key, TEntry>
16+
* @template-implements ObservableSetInterface<TEntry>
17+
*/
18+
final readonly class ObservableSet implements ObservableSetInterface, \IteratorAggregate
19+
{
20+
/**
21+
* @var \SplObjectStorage<TEntry, void>
22+
*/
23+
private \SplObjectStorage $references;
24+
25+
/**
26+
* @var \WeakMap<TEntry, ReferenceReleaseCallback<TEntry>>
27+
*/
28+
private \WeakMap $memory;
29+
30+
public function __construct()
31+
{
32+
$this->references = new \SplObjectStorage();
33+
$this->memory = new \WeakMap();
34+
}
35+
36+
/**
37+
* @param TEntry $entry
38+
* @param \Closure(TEntry):void $onRelease
39+
*
40+
* @return TEntry
41+
*/
42+
public function watch(object $entry, \Closure $onRelease): object
43+
{
44+
$this->memory[$entry] = new ReferenceReleaseCallback($entry, $onRelease);
45+
$this->references->offsetSet($entry, null);
46+
47+
return $entry;
48+
}
49+
50+
public function detach(object $entry): void
51+
{
52+
unset($this->memory[$entry], $this->references[$entry]);
53+
}
54+
55+
public function getIterator(): \Traversable
56+
{
57+
return $this->references;
58+
}
59+
60+
/**
61+
* @return int<0, max>
62+
*/
63+
public function count(): int
64+
{
65+
return $this->memory->count();
66+
}
67+
}

src/ObservableSetInterface.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Boson\Component\WeakType;
6+
7+
/**
8+
* Allows to store a set of objects and track their
9+
* destruction (react to GC cleanup).
10+
*
11+
* ```
12+
* $set->watch($object, function (ExampleObject $ref) {
13+
* echo vsprintf('ExampleObject(%d) has been destroyed', [
14+
* get_object_id($ref),
15+
* ]);
16+
* ));
17+
* ```
18+
*
19+
* @template TObservable of object = object
20+
*
21+
* @template-extends \Traversable<array-key, TObservable>
22+
*/
23+
interface ObservableSetInterface extends \Countable, \Traversable
24+
{
25+
/**
26+
* @param TObservable $entry
27+
* @param \Closure(TObservable):void $onRelease
28+
*
29+
* @return TObservable
30+
*/
31+
public function watch(object $entry, \Closure $onRelease): object;
32+
33+
/**
34+
* @param TObservable $entry
35+
*/
36+
public function detach(object $entry): void;
37+
38+
/**
39+
* @return int<0, max>
40+
*/
41+
public function count(): int;
42+
}

src/ObservableWeakMap.php

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,21 @@
77
use Boson\Component\WeakType\Internal\ReferenceReleaseCallback;
88

99
/**
10-
* Allows to store a set of objects with referenced values and
11-
* track their destruction (react to GC cleanup).
10+
* When adding an object using {@see ObservableWeakMap::watch()} method,
11+
* this implementation does not increase its refcount.
1212
*
13-
* ```
14-
* // ObservableWeakMap<ExampleId, CData>
15-
* $map = new ObservableWeakMap();
16-
*
17-
* $map->watch($id, $data, function (CData $ref) {
18-
* echo vsprintf('ID has been destroyed, something can be done with its reference %s(%d)', [
19-
* $ref::class,
20-
* get_object_id($ref),
21-
* ]);
22-
* ));
23-
* ```
24-
*
25-
* @api
26-
*
27-
* @template TKey of object = object
13+
* @template TObservable of object = object
2814
* @template TValue of object = object
2915
*
30-
* @template-implements \IteratorAggregate<TKey, TValue>
16+
* @template-implements \IteratorAggregate<TObservable, TValue>
17+
* @template-implements ObservableMapInterface<TObservable, TValue>
3118
*/
32-
final readonly class ObservableWeakMap implements \IteratorAggregate, \Countable
19+
final readonly class ObservableWeakMap implements
20+
ObservableMapInterface,
21+
\IteratorAggregate
3322
{
3423
/**
35-
* @var \WeakMap<TKey, ReferenceReleaseCallback<TValue>>
24+
* @var \WeakMap<TObservable, ReferenceReleaseCallback<TValue>>
3625
*/
3726
private \WeakMap $memory;
3827

@@ -41,25 +30,13 @@ public function __construct()
4130
$this->memory = new \WeakMap();
4231
}
4332

44-
/**
45-
* @param TKey $key
46-
* @param TValue $value
47-
* @param \Closure(TValue):void $onRelease
48-
*
49-
* @return TKey
50-
*/
5133
public function watch(object $key, object $value, \Closure $onRelease): object
5234
{
5335
$this->memory[$key] = new ReferenceReleaseCallback($value, $onRelease);
5436

5537
return $key;
5638
}
5739

58-
/**
59-
* @param TKey $key
60-
*
61-
* @return TValue|null
62-
*/
6340
public function find(object $key): ?object
6441
{
6542
if (!$this->memory->offsetExists($key)) {
@@ -69,6 +46,16 @@ public function find(object $key): ?object
6946
return $this->memory[$key]->reference;
7047
}
7148

49+
public function has(object $key): bool
50+
{
51+
return $this->memory->offsetExists($key);
52+
}
53+
54+
public function detach(object $key): void
55+
{
56+
unset($this->memory[$key]);
57+
}
58+
7259
public function getIterator(): \Traversable
7360
{
7461
foreach ($this->memory as $key => $ref) {

src/ObservableWeakSet.php

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,18 @@
77
use Boson\Component\WeakType\Internal\ReferenceReleaseCallback;
88

99
/**
10-
* Allows to store a set of objects and track their
11-
* destruction (react to GC cleanup).
10+
* When adding an object using {@see ObservableWeakSet::watch()} method,
11+
* this implementation does not increase its refcount.
1212
*
13-
* ```
14-
* // ObservableWeakSet<ExampleObject>
15-
* $set = new ObservableWeakSet();
16-
*
17-
* $set->watch($object, function (ExampleObject $ref) {
18-
* echo vsprintf('ExampleObject(%d) has been destroyed', [
19-
* get_object_id($ref),
20-
* ]);
21-
* ));
22-
* ```
13+
* The implementation calls the {@see ObservableWeakSet::watch()} `$onRelease`
14+
* callback only if there are no references left to the object.
2315
*
2416
* @template TEntry of object = object
2517
*
2618
* @template-implements \IteratorAggregate<array-key, TEntry>
19+
* @template-implements ObservableSetInterface<TEntry>
2720
*/
28-
final readonly class ObservableWeakSet implements \IteratorAggregate, \Countable
21+
final readonly class ObservableWeakSet implements ObservableSetInterface, \IteratorAggregate
2922
{
3023
/**
3124
* @var \WeakMap<TEntry, ReferenceReleaseCallback<TEntry>>
@@ -50,6 +43,11 @@ public function watch(object $entry, \Closure $onRelease): object
5043
return $entry;
5144
}
5245

46+
public function detach(object $entry): void
47+
{
48+
unset($this->memory[$entry]);
49+
}
50+
5351
public function getIterator(): \Traversable
5452
{
5553
foreach ($this->memory as $key => $_) {

0 commit comments

Comments
 (0)