Skip to content

Commit

Permalink
Reference bug (#155)
Browse files Browse the repository at this point in the history
* Fixed #154 - reference bug

* Updated changelog
  • Loading branch information
sorinsarca authored Jan 10, 2025
1 parent 1a22899 commit 9b6ef5f
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
CHANGELOG
---------

### v4.3.1, 2025.01.10

- Fixed reference bug [#154](https://github.com/opis/closure/issues/154)

### v4.3.0, 2025.01.08

- Proper serialization of private properties
Expand Down
9 changes: 6 additions & 3 deletions src/DeserializationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Opis\Closure;

use stdClass, WeakMap, Closure;
use stdClass, WeakMap, Closure, SplObjectStorage;
use function unserialize;

/**
Expand All @@ -15,6 +15,8 @@ class DeserializationHandler
private ?array $visitedArrays = null;
private array $options;

private ?SplObjectStorage $refKeepAlive;

public function __construct(?array $options = null)
{
$this->options = $options ?? [];
Expand All @@ -25,6 +27,7 @@ public function unserialize(string $serialized): mixed
$this->unboxed = new WeakMap();
$this->refs = new WeakMap();
$this->visitedArrays = [];
$this->refKeepAlive = new SplObjectStorage();

if (Serializer::$v3Compatible) {
$this->v3_unboxed = [];
Expand All @@ -46,7 +49,7 @@ public function unserialize(string $serialized): mixed

return $data;
} finally {
$this->unboxed = $this->refs = $this->visitedArrays = null;
$this->unboxed = $this->refs = $this->visitedArrays = $this->refKeepAlive = null;
$this->v3_unboxed = $this->v3_refs = null;
}
}
Expand Down Expand Up @@ -74,7 +77,7 @@ private function handleIterable(array|object &$iterable): void

private function handleArray(array &$array): void
{
$id = ReflectionClass::getRefId($array);
$id = ReflectionClass::getRefId($array, $this->refKeepAlive);
if (!isset($this->visitedArrays[$id])) {
$this->visitedArrays[$id] = true;
$this->handleIterable($array);
Expand Down
12 changes: 10 additions & 2 deletions src/ReflectionClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,17 @@ public static function objectIsEnum(object $value): bool
return self::$enumExists && ($value instanceof \UnitEnum);
}

public static function getRefId(mixed &$reference): ?string
public static function getRefId(mixed &$reference, ?\SplObjectStorage $keepAlive = null): ?string
{
return \ReflectionReference::fromArrayElement([&$reference], 0)?->getId();
$ref = \ReflectionReference::fromArrayElement([&$reference], 0);
if (!$ref) {
return null;
}

// we save this so the ref ids cannot be reused while serializing/deserializing
$keepAlive?->attach($ref);

return $ref->getId();
}

public static function isAnonymousClassName(string $class): bool
Expand Down
14 changes: 12 additions & 2 deletions src/SerializationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class SerializationHandler

private bool $hasClosures;

private ?SplObjectStorage $refKeepAlive;

public function serialize(mixed $data): string
{
$this->arrayMap = [];
Expand All @@ -27,6 +29,7 @@ public function serialize(mixed $data): string
$this->shouldBox = new WeakMap();
$this->info = [];
$this->hasClosures = false;
$this->refKeepAlive = new SplObjectStorage();

try {
// get boxed structure
Expand All @@ -37,7 +40,12 @@ public function serialize(mixed $data): string
}
return serialize($data);
} finally {
$this->arrayMap = $this->objectMap = $this->priority = $this->shouldBox = $this->info = null;
$this->arrayMap =
$this->objectMap =
$this->priority =
$this->refKeepAlive =
$this->shouldBox =
$this->info = null;
}
}

Expand Down Expand Up @@ -155,12 +163,14 @@ private function handleObject(object $data): object
return $box;
}

private SplObjectStorage $keep;

private function &handleArray(array &$data, bool $skipRefId = false): array
{
if ($skipRefId) {
$box = [];
} else {
$id = ReflectionClass::getRefId($data);
$id = ReflectionClass::getRefId($data, $this->refKeepAlive);
if (array_key_exists($id, $this->arrayMap)) {
return $this->arrayMap[$id];
}
Expand Down

0 comments on commit 9b6ef5f

Please sign in to comment.