From b9346e647433a82ef0a25ab41f48e3520e9c5020 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:30:40 +0000 Subject: [PATCH 1/2] fix: Entity::normalizeValue() must handle UnitEnum before toArray() Moves the `UnitEnum` instanceof check before `JsonSerializable` and `method_exists($data, 'toArray')` in Entity::normalizeValue(), so that enums implementing toArray() are always normalized as enums rather than as generic objects. Fixes codeigniter4/CodeIgniter4#10136 Agent-Logs-Url: https://github.com/maniaba/CodeIgniter4/sessions/d5c8c660-329b-4872-8633-dd918674e4ac Co-authored-by: maniaba <61078470+maniaba@users.noreply.github.com> --- system/Entity/Entity.php | 10 +++++----- tests/_support/Enum/StateEnum.php | 29 +++++++++++++++++++++++++++++ tests/system/Entity/EntityTest.php | 14 ++++++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 tests/_support/Enum/StateEnum.php diff --git a/system/Entity/Entity.php b/system/Entity/Entity.php index 43182cfb9b2a..e3733bd0fc02 100644 --- a/system/Entity/Entity.php +++ b/system/Entity/Entity.php @@ -458,6 +458,11 @@ private function normalizeValue(mixed $data): mixed // Check for Entity instance (use raw values, recursive) if ($data instanceof self) { $objectData = $data->toRawArray(false, true); + } elseif ($data instanceof UnitEnum) { + return [ + '__class' => $data::class, + '__enum' => $data instanceof BackedEnum ? $data->value : $data->name, + ]; } elseif ($data instanceof JsonSerializable) { $objectData = $data->jsonSerialize(); } elseif (method_exists($data, 'toArray')) { @@ -469,11 +474,6 @@ private function normalizeValue(mixed $data): mixed '__class' => $data::class, '__datetime' => $data->format(DATE_RFC3339_EXTENDED), ]; - } elseif ($data instanceof UnitEnum) { - return [ - '__class' => $data::class, - '__enum' => $data instanceof BackedEnum ? $data->value : $data->name, - ]; } else { $objectData = get_object_vars($data); diff --git a/tests/_support/Enum/StateEnum.php b/tests/_support/Enum/StateEnum.php new file mode 100644 index 000000000000..84d6a1713aae --- /dev/null +++ b/tests/_support/Enum/StateEnum.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Tests\Support\Enum; + +/** + * An enum that also defines toArray(), used to test that UnitEnum handling + * takes precedence over toArray() in Entity::normalizeValue(). + */ +enum StateEnum: string +{ + case DRAFT = 'draft'; + case PUBLISHED = 'published'; + + public function toArray(): array + { + return array_column(self::cases(), 'value'); + } +} diff --git a/tests/system/Entity/EntityTest.php b/tests/system/Entity/EntityTest.php index 7c19d9d09b89..3cc5d0a76764 100644 --- a/tests/system/Entity/EntityTest.php +++ b/tests/system/Entity/EntityTest.php @@ -35,6 +35,7 @@ use Tests\Support\Entity\Cast\NotExtendsBaseCast; use Tests\Support\Enum\ColorEnum; use Tests\Support\Enum\RoleEnum; +use Tests\Support\Enum\StateEnum; use Tests\Support\Enum\StatusEnum; use Tests\Support\SomeEntity; @@ -1045,6 +1046,19 @@ public function testCastEnumSetWithUnitEnumObject(): void $this->assertSame(ColorEnum::RED, $entity->color); } + public function testInjectRawDataWithEnumThatHasToArrayMethod(): void + { + // Regression test for https://github.com/codeigniter4/CodeIgniter4/issues/10136 + // Enums implementing toArray() must still be handled by the UnitEnum branch. + $entity = new class () extends Entity {}; + + $entity->injectRawData(['state' => StateEnum::DRAFT]); + + // Enum value should be preserved, not expanded via toArray() + $this->assertSame(['state' => 'draft'], $entity->toRawArray()); + $this->assertFalse($entity->hasChanged('state')); + } + public function testAsArray(): void { $entity = $this->getEntity(); From 19a5a0173d11da8792e699ddcbb522f07263fb09 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:37:31 +0000 Subject: [PATCH 2/2] fix test: correct toRawArray() assertion for injected enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit toRawArray() returns raw $this->attributes, so an injected enum object stays as an enum object — not the backing string value. The real regression is that hasChanged() must return false (normalizeValue() handles UnitEnum before toArray()). Fixes the failing test from the previous commit. Agent-Logs-Url: https://github.com/maniaba/CodeIgniter4/sessions/d86b9a68-aee9-4178-a8b8-cf1bac285359 Co-authored-by: maniaba <61078470+maniaba@users.noreply.github.com> --- tests/system/Entity/EntityTest.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/system/Entity/EntityTest.php b/tests/system/Entity/EntityTest.php index 3cc5d0a76764..59445f6c294d 100644 --- a/tests/system/Entity/EntityTest.php +++ b/tests/system/Entity/EntityTest.php @@ -1049,13 +1049,18 @@ public function testCastEnumSetWithUnitEnumObject(): void public function testInjectRawDataWithEnumThatHasToArrayMethod(): void { // Regression test for https://github.com/codeigniter4/CodeIgniter4/issues/10136 - // Enums implementing toArray() must still be handled by the UnitEnum branch. + // Enums implementing toArray() must still be handled by the UnitEnum branch in + // normalizeValue(), so hasChanged() does not incorrectly report a change after + // injectRawData() stores the same enum value. $entity = new class () extends Entity {}; $entity->injectRawData(['state' => StateEnum::DRAFT]); - // Enum value should be preserved, not expanded via toArray() - $this->assertSame(['state' => 'draft'], $entity->toRawArray()); + // toRawArray() returns raw attributes, so the enum object is returned as-is. + $this->assertSame(StateEnum::DRAFT, $entity->toRawArray()['state']); + + // The key assertion: normalizeValue() must treat the enum as a UnitEnum + // (not call toArray() on it), so the original and current normalized forms match. $this->assertFalse($entity->hasChanged('state')); }