Skip to content

Commit

Permalink
Attributes: Add rebind() and rebindCallback
Browse files Browse the repository at this point in the history
  • Loading branch information
Timm Ortloff committed Dec 15, 2022
1 parent 9277ff6 commit 86d5b2f
Showing 1 changed file with 70 additions and 2 deletions.
72 changes: 70 additions & 2 deletions src/Attributes.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

use ArrayAccess;
use ArrayIterator;
use Closure;
use InvalidArgumentException;
use IteratorAggregate;
use ReflectionException;
use ReflectionFunction;
use Traversable;

use function ipl\Stdlib\get_php_type;
Expand Down Expand Up @@ -509,12 +512,77 @@ public function getIterator(): Traversable
return new ArrayIterator($this->attributes);
}

/**
* Rebind all {@see self::$callbacks} and {@see self::$setterCallbacks} that point to `$oldThis` to `$newThis`.
*
* @param object $oldThis
* @param object $newThis
*
* @return void
* @throws ReflectionException
*/
public function rebind(object $oldThis, object $newThis): void
{
$this->rebindCallback($this->callbacks, true, $oldThis, $newThis);
$this->rebindCallback($this->setterCallbacks, false, $oldThis, $newThis);
}

/**
* Helper Method for {@see self::rebind()}
*
* Loops over all `$callbacks`, binds them to `$newThis` where `$oldThis` matches and
* overwrites the existing callback either in {@see self::$callbacks} or {@see self::$setterCallbacks}, depending
* on `$isGetter`.
*
* @param callable[] $callbacks
* @param bool $isGetter
* @param object $oldThis
* @param object $newThis
*
* @return void
* @throws ReflectionException
*/
private function rebindCallback(array $callbacks, bool $isGetter, object $oldThis, object $newThis): void
{
foreach ($callbacks as $key => $callback) {
if (! $callback instanceof Closure) {
if (is_array($callback) && ! is_string($callback[0])) {
if (spl_object_id($callback[0]) == spl_object_id($oldThis)) {
$this->registerAttributeCallback(
$key,
$isGetter ? Closure::fromCallable($callback)->bindTo($newThis) : null,
! $isGetter ? Closure::fromCallable($callback)->bindTo($newThis) : null
);
}
}

continue;
}

$closureThis = (new ReflectionFunction($callback))
->getClosureThis();

// Closure is most likely static
if ($closureThis === null) {
continue;
}

$closureThisHash = spl_object_id($closureThis);

if ($closureThisHash == spl_object_id($oldThis)) {
$this->registerAttributeCallback(
$key,
$isGetter ? Closure::fromCallable($callback)->bindTo($newThis) : null,
! $isGetter ? Closure::fromCallable($callback)->bindTo($newThis) : null
);
}
}
}

public function __clone()
{
foreach ($this->attributes as &$attribute) {
$attribute = clone $attribute;
}

// TODO(lippserd): Rebind callbacks. However that should work then :).
}
}

0 comments on commit 86d5b2f

Please sign in to comment.