From 1767acfda6fd6e54577dcfc749e37512722d5ec8 Mon Sep 17 00:00:00 2001 From: Jarred Stelfox Date: Wed, 25 Mar 2026 15:59:11 -0700 Subject: [PATCH 1/4] fix: Serialize native PHP enums with their name and value instead of opaque 'Object' string Enums were caught by the generic `is_object` branch in `serializeRecursively()`, causing them to render as `Object ClassName` when `serializeAllObjects` was enabled. This adds an early-return for `UnitEnum` before the object block and enhances the output to include the backing value for `BackedEnum` instances. Fixes #2037 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/Serializer/AbstractSerializer.php | 11 ++++- tests/Serializer/AbstractSerializerTest.php | 48 +++++++++++++++++++ tests/Serializer/SerializerTestBackedEnum.php | 10 ++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/Serializer/SerializerTestBackedEnum.php diff --git a/src/Serializer/AbstractSerializer.php b/src/Serializer/AbstractSerializer.php index 9e6312325..5f5d56d19 100644 --- a/src/Serializer/AbstractSerializer.php +++ b/src/Serializer/AbstractSerializer.php @@ -116,6 +116,10 @@ protected function serializeRecursively($value, int $_depth = 0) return $serializedArray; } + if ($value instanceof \UnitEnum) { + return $this->serializeValue($value); + } + if (\is_object($value)) { $classSerializers = $this->resolveClassSerializers($value); @@ -247,8 +251,13 @@ protected function serializeValue($value) if ($value instanceof \UnitEnum) { $reflection = new \ReflectionObject($value); + $enumValue = $reflection->getName() . '::' . $value->name; + + if ($value instanceof \BackedEnum) { + return 'Enum ' . $enumValue . '(' . $value->value . ')'; + } - return 'Enum ' . $reflection->getName() . '::' . $value->name; + return 'Enum ' . $enumValue; } if (\is_object($value)) { diff --git a/tests/Serializer/AbstractSerializerTest.php b/tests/Serializer/AbstractSerializerTest.php index 89932ba1a..38ac0a738 100644 --- a/tests/Serializer/AbstractSerializerTest.php +++ b/tests/Serializer/AbstractSerializerTest.php @@ -66,6 +66,54 @@ public function testEnumsAreNames(): void $this->assertSame('Enum Sentry\Tests\Serializer\SerializerTestEnum::CASE_NAME', $result); } + /** + * @requires PHP >= 8.1 + */ + public function testBackedEnumsIncludeValue(): void + { + $serializer = $this->createSerializer(); + $input = SerializerTestBackedEnum::CASE_NAME; + $result = $this->invokeSerialization($serializer, $input); + + $this->assertSame('Enum Sentry\Tests\Serializer\SerializerTestBackedEnum::CASE_NAME(case_value)', $result); + } + + /** + * @requires PHP >= 8.1 + * @dataProvider serializeAllObjectsDataProvider + */ + public function testEnumsAreNotSerializedAsObjects(bool $serializeAllObjects): void + { + $serializer = $this->createSerializer(); + + if ($serializeAllObjects) { + $serializer->setSerializeAllObjects(true); + } + + $input = SerializerTestEnum::CASE_NAME; + $result = $this->invokeSerialization($serializer, $input); + + $this->assertSame('Enum Sentry\Tests\Serializer\SerializerTestEnum::CASE_NAME', $result); + } + + /** + * @requires PHP >= 8.1 + * @dataProvider serializeAllObjectsDataProvider + */ + public function testBackedEnumsAreNotSerializedAsObjects(bool $serializeAllObjects): void + { + $serializer = $this->createSerializer(); + + if ($serializeAllObjects) { + $serializer->setSerializeAllObjects(true); + } + + $input = SerializerTestBackedEnum::CASE_NAME; + $result = $this->invokeSerialization($serializer, $input); + + $this->assertSame('Enum Sentry\Tests\Serializer\SerializerTestBackedEnum::CASE_NAME(case_value)', $result); + } + public static function objectsWithIdPropertyDataProvider(): array { return [ diff --git a/tests/Serializer/SerializerTestBackedEnum.php b/tests/Serializer/SerializerTestBackedEnum.php new file mode 100644 index 000000000..7a4ee78ac --- /dev/null +++ b/tests/Serializer/SerializerTestBackedEnum.php @@ -0,0 +1,10 @@ + Date: Wed, 25 Mar 2026 16:03:45 -0700 Subject: [PATCH 2/4] style: Fix PHP-CS-Fixer docblock formatting Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/Serializer/AbstractSerializerTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Serializer/AbstractSerializerTest.php b/tests/Serializer/AbstractSerializerTest.php index 38ac0a738..f2e447a70 100644 --- a/tests/Serializer/AbstractSerializerTest.php +++ b/tests/Serializer/AbstractSerializerTest.php @@ -80,6 +80,7 @@ public function testBackedEnumsIncludeValue(): void /** * @requires PHP >= 8.1 + * * @dataProvider serializeAllObjectsDataProvider */ public function testEnumsAreNotSerializedAsObjects(bool $serializeAllObjects): void @@ -98,6 +99,7 @@ public function testEnumsAreNotSerializedAsObjects(bool $serializeAllObjects): v /** * @requires PHP >= 8.1 + * * @dataProvider serializeAllObjectsDataProvider */ public function testBackedEnumsAreNotSerializedAsObjects(bool $serializeAllObjects): void From 62a6bc966a21164eac25c35ca618273f99fde246 Mon Sep 17 00:00:00 2001 From: Jarred Stelfox Date: Thu, 26 Mar 2026 09:58:29 -0700 Subject: [PATCH 3/4] fix: Move UnitEnum check after class serializers to preserve custom serializer priority Moves the enum early-return inside the is_object block, after the class serializers loop, so custom serializers registered via class_serializers still take precedence over the default enum serialization. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/Serializer/AbstractSerializer.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Serializer/AbstractSerializer.php b/src/Serializer/AbstractSerializer.php index 5f5d56d19..f5c119942 100644 --- a/src/Serializer/AbstractSerializer.php +++ b/src/Serializer/AbstractSerializer.php @@ -116,10 +116,6 @@ protected function serializeRecursively($value, int $_depth = 0) return $serializedArray; } - if ($value instanceof \UnitEnum) { - return $this->serializeValue($value); - } - if (\is_object($value)) { $classSerializers = $this->resolveClassSerializers($value); @@ -143,6 +139,10 @@ protected function serializeRecursively($value, int $_depth = 0) return $this->formatDate($value); } + if ($value instanceof \UnitEnum) { + return $this->serializeValue($value); + } + if ($this->serializeAllObjects || ($value instanceof \stdClass)) { return $this->serializeObject($value, $_depth); } From 92daa77163eed25ce50b5c03244da6b3efe24fe2 Mon Sep 17 00:00:00 2001 From: Jarred Stelfox Date: Fri, 27 Mar 2026 10:38:22 -0700 Subject: [PATCH 4/4] test: Add unit enum output format test alongside backed enum test Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/Serializer/AbstractSerializerTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/Serializer/AbstractSerializerTest.php b/tests/Serializer/AbstractSerializerTest.php index f2e447a70..e3fda32f8 100644 --- a/tests/Serializer/AbstractSerializerTest.php +++ b/tests/Serializer/AbstractSerializerTest.php @@ -78,6 +78,18 @@ public function testBackedEnumsIncludeValue(): void $this->assertSame('Enum Sentry\Tests\Serializer\SerializerTestBackedEnum::CASE_NAME(case_value)', $result); } + /** + * @requires PHP >= 8.1 + */ + public function testUnitEnumsShowName(): void + { + $serializer = $this->createSerializer(); + $input = SerializerTestEnum::CASE_NAME; + $result = $this->invokeSerialization($serializer, $input); + + $this->assertSame('Enum Sentry\Tests\Serializer\SerializerTestEnum::CASE_NAME', $result); + } + /** * @requires PHP >= 8.1 *