From c5f82d0b32b40e01848ae98587c0d8dd925f71e7 Mon Sep 17 00:00:00 2001 From: Andrei Shilov Date: Sun, 30 Jun 2024 17:49:59 +0300 Subject: [PATCH] Fix case when SerializableClosure uses as class property --- .gitignore | 1 + src/Serializers/Native.php | 30 ++++++++++++++++++---- tests/Fixtures/ClassWithPublicProperty.php | 15 +++++++++++ tests/SerializerTest.php | 29 +++++++++++++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 tests/Fixtures/ClassWithPublicProperty.php diff --git a/.gitignore b/.gitignore index 660fc15e..cc31cac1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ composer.lock /phpunit.xml .phpunit.result.cache +.idea diff --git a/src/Serializers/Native.php b/src/Serializers/Native.php index 4a04b981..5d57f0bf 100644 --- a/src/Serializers/Native.php +++ b/src/Serializers/Native.php @@ -194,15 +194,35 @@ public function __unserialize($data) $this->closure = $this->closure->bindTo($this->code['this'], $this->code['scope']); - if (! empty($this->code['objects'])) { - foreach ($this->code['objects'] as $item) { - $item['property']->setValue($item['instance'], $item['object']->getClosure()); - } - } + $this->bindObjectsIfNeeded(); $this->code = $this->code['function']; } + private function bindObjectsIfNeeded() + { + if (!empty($this->code['objects'])) { + $this->bindObjects(); + } + } + + private function bindObjects() + { + foreach ($this->code['objects'] as $item) { + $item['property']->setValue($item['instance'], $this->calculateObjectValue($item)); + } + } + + private function calculateObjectValue($item) + { + return $this->isSerializableClosure($item['object']) ? $item['object'] : $item['object']->getClosure(); + } + + private function isSerializableClosure($object): bool + { + return $object instanceof SerializableClosure || $object instanceof UnsignedSerializableClosure; + } + /** * Ensures the given closures are serializable. * diff --git a/tests/Fixtures/ClassWithPublicProperty.php b/tests/Fixtures/ClassWithPublicProperty.php new file mode 100644 index 00000000..2cdb0c2d --- /dev/null +++ b/tests/Fixtures/ClassWithPublicProperty.php @@ -0,0 +1,15 @@ +closure = $closure; + } +} diff --git a/tests/SerializerTest.php b/tests/SerializerTest.php index 9c2e70bd..7b57b5ac 100644 --- a/tests/SerializerTest.php +++ b/tests/SerializerTest.php @@ -6,6 +6,7 @@ use Laravel\SerializableClosure\Serializers\Signed; use Laravel\SerializableClosure\Support\ReflectionClosure; use Laravel\SerializableClosure\UnsignedSerializableClosure; +use Tests\Fixtures\ClassWithPublicProperty; use Tests\Fixtures\Model; test('closure with simple const', function () { @@ -485,6 +486,34 @@ function () { new CarbonImmutable, ]); +test('SerializableClosure in the class property', function () { + SerializableClosure::setSecretKey('foo'); + + $innerClosure = new ClassWithPublicProperty(new SerializableClosure(function () { + })); + $outerClosure = new SerializableClosure(function () use ($innerClosure) { + return $innerClosure->closure; + }); + + $unSerializedOuterClosure = unserialize(serialize($outerClosure)); + + expect($outerClosure())->toBeInstanceOf(SerializableClosure::class); + expect($unSerializedOuterClosure())->toBeInstanceOf(SerializableClosure::class); +}); + +test('UnsignedSerializableClosure in the class property', function () { + $innerClosure = new ClassWithPublicProperty(SerializableClosure::unsigned(function () { + })); + $outerClosure = SerializableClosure::unsigned(function () use ($innerClosure) { + return $innerClosure->closure; + }); + + $unSerializedOuterClosure = unserialize(serialize($outerClosure)); + + expect($outerClosure())->toBeInstanceOf(UnsignedSerializableClosure::class); + expect($unSerializedOuterClosure())->toBeInstanceOf(UnsignedSerializableClosure::class); +}); + function serializer_php_74_switch_statement_test_is_two($a) { return $a === 2;