1
0
mirror of https://github.com/danog/amp.git synced 2024-12-02 17:37:50 +01:00

Rename weaken to weakClosure and limit to closures

This commit is contained in:
Aaron Piotrowski 2021-12-02 17:08:00 -06:00
parent d20f07acee
commit 1a2be8f2b2
No known key found for this signature in database
GPG Key ID: ADD1EF783EDE9EEB
2 changed files with 23 additions and 47 deletions

View File

@ -77,7 +77,7 @@ function delay(float $timeout, bool $reference = true, ?Cancellation $cancellati
/** /**
* Wait for signal(s) in a non-blocking way. * Wait for signal(s) in a non-blocking way.
* *
* @param int|array $signals Signal number or array of signal numbers. * @param int|int[] $signals Signal number or array of signal numbers.
* @param bool $reference If false, unreference the underlying watcher. * @param bool $reference If false, unreference the underlying watcher.
* @param Cancellation|null $cancellation Cancel waiting if cancellation is requested. * @param Cancellation|null $cancellation Cancel waiting if cancellation is requested.
* *
@ -116,79 +116,55 @@ function trapSignal(int|array $signals, bool $reference = true, ?Cancellation $c
} }
/** /**
* Returns a callable that maintains a weak reference to any $this object held by the callable. * Returns a Closure that maintains a weak reference to any $this object held by the Closure (a weak-Closure).
* This allows a class to hold a self-referencing callback without creating a circular reference that would * This allows a class to hold a self-referencing Closure without creating a circular reference that would
* prevent or delay automatic garbage collection. * prevent or delay automatic garbage collection.
* Invoking the returned callback after the object is destroyed will throw an instance of Error. * Invoking the returned Closure after the object is destroyed will throw an instance of Error.
* *
* @param callable $callable * @param \Closure $closure
* *
* @return callable * @return \Closure
*/ */
function weaken(callable $callable): callable function weakClosure(\Closure $closure): \Closure
{ {
if (!$callable instanceof \Closure) { $reflection = new \ReflectionFunction($closure);
if (\is_string($callable)) {
return $callable;
}
if (\is_object($callable)) {
$callable = [$callable, '__invoke'];
}
[$that, $method] = $callable;
if (!\is_object($that)) {
return $callable;
}
$reference = \WeakReference::create($that);
return static function (mixed ...$args) use ($reference, $method): mixed {
$that = $reference->get();
if (!$that) {
throw new \Error('Weakened callback invoked after referenced object destroyed');
}
return $that->{$method}(...$args);
};
}
$reflection = new \ReflectionFunction($callable);
$that = $reflection->getClosureThis(); $that = $reflection->getClosureThis();
if (!$that) { if (!$that) {
return $callable; return $closure;
} }
$method = $reflection->getShortName(); $method = $reflection->getShortName();
if ($method !== '{closure}') { if ($method !== '{closure}') {
// Closure from first-class callable or \Closure::fromCallable(), declare an anonymous closure to rebind. // Closure from first-class callable or \Closure::fromCallable(), declare an anonymous closure to rebind.
/** @psalm-suppress InvalidScope Closure is bound before being invoked. */ /** @psalm-suppress InvalidScope Closure is bound before being invoked. */
$callable = fn (mixed ...$args) => $this->{$method}(...$args); $closure = fn (mixed ...$args) => $this->{$method}(...$args);
} else { } else {
// Rebind to remove reference to $that // Rebind to remove reference to $that
$callable = $callable->bindTo(new \stdClass()); $closure = $closure->bindTo(new \stdClass());
} }
// For internal classes use \Closure::bindTo() without scope. // For internal classes use \Closure::bindTo() without scope.
$useBindTo = !(new \ReflectionClass($that))->isUserDefined(); $useBindTo = !(new \ReflectionClass($that))->isUserDefined();
$reference = \WeakReference::create($that); $reference = \WeakReference::create($that);
return static function (mixed ...$args) use ($reference, $callable, $useBindTo): mixed {
return static function (mixed ...$args) use ($reference, $closure, $useBindTo): mixed {
$that = $reference->get(); $that = $reference->get();
if (!$that) { if (!$that) {
throw new \Error('Weakened callback invoked after referenced object destroyed'); throw new \Error('Weakened closure invoked after referenced object destroyed');
} }
if ($useBindTo) { if ($useBindTo) {
$callable = $callable->bindTo($that); $closure = $closure->bindTo($that);
if (!$callable) { if (!$closure) {
throw new \RuntimeException('Unable to rebind function to object of type ' . \get_class($that)); throw new \RuntimeException('Unable to rebind function to object of type ' . \get_class($that));
} }
return $callable(...$args); return $closure(...$args);
} }
return $callable->call($that, ...$args); return $closure->call($that, ...$args);
}; };
} }

View File

@ -5,7 +5,7 @@ namespace Amp;
use Amp\PHPUnit\AsyncTestCase; use Amp\PHPUnit\AsyncTestCase;
use Revolt\EventLoop; use Revolt\EventLoop;
class WeakenTest extends AsyncTestCase class WeakClosureTest extends AsyncTestCase
{ {
public function provideObjectFactories(): iterable public function provideObjectFactories(): iterable
{ {
@ -17,7 +17,7 @@ class WeakenTest extends AsyncTestCase
{ {
$this->callbackId = $id = EventLoop::repeat( $this->callbackId = $id = EventLoop::repeat(
0.001, 0.001,
weaken(function (string $callbackId) use (&$count): void { weakClosure(function (string $callbackId) use (&$count): void {
AsyncTestCase::assertNotNull($this); AsyncTestCase::assertNotNull($this);
AsyncTestCase::assertStringContainsString('anonymous', \get_class($this)); AsyncTestCase::assertStringContainsString('anonymous', \get_class($this));
AsyncTestCase::assertSame($callbackId, $this->callbackId); AsyncTestCase::assertSame($callbackId, $this->callbackId);
@ -40,7 +40,7 @@ class WeakenTest extends AsyncTestCase
public function __construct(int &$count, &$id) public function __construct(int &$count, &$id)
{ {
$callbackIdRef = &$this->callbackId; $callbackIdRef = &$this->callbackId;
$this->callbackId = $id = EventLoop::repeat(0.001, weaken(static function (string $callbackId) use ( $this->callbackId = $id = EventLoop::repeat(0.001, weakClosure(static function (string $callbackId) use (
&$count, &$count,
&$callbackIdRef &$callbackIdRef
): void { ): void {
@ -66,7 +66,7 @@ class WeakenTest extends AsyncTestCase
$this->count = &$count; $this->count = &$count;
$this->callbackId = $id = EventLoop::repeat( $this->callbackId = $id = EventLoop::repeat(
0.001, 0.001,
weaken(\Closure::fromCallable([$this, 'callback'])) weakClosure(\Closure::fromCallable([$this, 'callback']))
); );
} }
@ -93,7 +93,7 @@ class WeakenTest extends AsyncTestCase
public function __construct(int &$count, &$id) public function __construct(int &$count, &$id)
{ {
$this->count = &$count; $this->count = &$count;
$this->callbackId = $id = EventLoop::repeat(0.001, weaken($this)); $this->callbackId = $id = EventLoop::repeat(0.001, weakClosure(\Closure::fromCallable($this)));
} }
public function __invoke(string $callbackId): void public function __invoke(string $callbackId): void