Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading