Skip to content

Fix #139: Add EnumException and throw it in Hydrator::create() when an enum is passed, which also prevents HydratorTypeCaster from hydrating enums from array input - #140

Merged
samdark merged 3 commits into
masterfrom
fix/139-enum-array-casting
Nov 4, 2026
Merged

samdark merged 3 commits into
masterfrom
fix/139-enum-array-casting

Conversation

@viktorprogger

@viktorprogger viktorprogger commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor
Q A
Is bugfix? ✅
New feature? ❌
Breaks BC? ❌

Summary

Fixes #139

Summary by Sourcery

Prevent invalid enum hydration by rejecting enum instantiation explicitly and handling enum types safely during type casting.

Bug Fixes:

  • Prevent enum types from being incorrectly hydrated from array input, including enum-containing union types.

Enhancements:

  • Add a dedicated exception when callers attempt to create enum instances and document the object factory contract for enum inputs.

Tests:

  • Add regression coverage for enum creation and enum-related hydrator type casting with multiple object factories and data attributes.

@sourcery-ai

sourcery-ai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

The PR prevents array input from reaching nested enum hydration by explicitly treating reflected enums as non-instantiable, and adds focused factory and type-caster regression coverage for direct and union enum types.

Sequence diagram for preventing enum hydration from array input

sequenceDiagram
    participant HydratorTypeCaster
    participant ReflectionObjectFactory
    participant EnumType
    HydratorTypeCaster->>ReflectionObjectFactory: create(ReflectionClass, constructorArguments)
    ReflectionObjectFactory->>EnumType: isEnum()
    EnumType-->>ReflectionObjectFactory: true
    ReflectionObjectFactory-->>HydratorTypeCaster: throw NonInstantiableException
Loading

File-Level Changes

Change Details Files
Reject enum types explicitly in the reflection-based object factory so hydrator fallback cannot attempt to instantiate them.
  • Check ReflectionClass::isEnum() before abstract-class handling.
  • Throw NonInstantiableException with an enum-specific message.
  • Document the new exception in the factory method contract.
src/ObjectFactory/ReflectionObjectFactory.php
Add regression tests covering enum rejection and array hydration behavior for direct and union enum types.
  • Verify enum reflection creation throws the expected exception and message.
  • Verify array input fails for named enum parameters.
  • Verify array input fails for enum
string union parameters.
Record the bug fix in the development changelog. CHANGELOG.md

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov

codecov Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (e67a852) to head (c2b3026).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##              master      #140   +/-   ##
===========================================
  Coverage     100.00%   100.00%           
- Complexity       388       390    +2     
===========================================
  Files             44        45    +1     
  Lines            913       921    +8     
===========================================
+ Hits             913       921    +8     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@viktorprogger
viktorprogger force-pushed the fix/139-enum-array-casting branch from 737b094 to e88da3b Compare November 2, 2026 17:26
@viktorprogger
viktorprogger requested review from a team, samdark and vjik November 2, 2026 17:28
@viktorprogger
viktorprogger marked this pull request as ready for review November 2, 2026 17:29
sourcery-ai[bot]
sourcery-ai Bot previously approved these changes Nov 2, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!

Linked issues

#139 HydratorTypeCaster attempts to instantiate enum from array · 1 of 1 met

# Requirement Status Notes
1 Return Result::fail() for enum types instead of attempting nested hydration. ✅ Met castInternal() checks whether the class implements UnitEnum and fails before calling $hydrator->create(). This also applies when an enum is a member of a union type.

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@viktorprogger
viktorprogger force-pushed the fix/139-enum-array-casting branch from e88da3b to cb4141c Compare November 2, 2026 20:10
@viktorprogger
viktorprogger marked this pull request as draft November 2, 2026 20:10
@viktorprogger
viktorprogger marked this pull request as ready for review November 2, 2026 20:17

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/ObjectFactory/ReflectionObjectFactory.php" line_range="31" />
<code_context>
      */
     public function create(ReflectionClass $reflectionClass, array $constructorArguments): object
     {
+        if ($reflectionClass->isEnum()) {
+            throw new NonInstantiableException(
+                sprintf(
</code_context>
<issue_to_address>
**Enum arrays abort hydration**

When the hydrator uses a custom object factory that does not reject enums with `NonInstantiableException` and receives an array for an enum type, `HydratorTypeCaster` delegates enum creation through `Hydrator::create` to the configured factory, so `ContainerObjectFactory` bypasses `ReflectionObjectFactory`'s guard and injector exceptions escape the caster's `NonInstantiableException` catch; array hydration aborts instead of returning a failed cast.

Reject enum types in `HydratorTypeCaster` before delegating to the configured object factory.
</issue_to_address>

### Comment 2
<location path="src/ObjectFactory/ReflectionObjectFactory.php" line_range="31-38" />
<code_context>
      */
     public function create(ReflectionClass $reflectionClass, array $constructorArguments): object
     {
+        if ($reflectionClass->isEnum()) {
+            throw new NonInstantiableException(
+                sprintf(
+                    '"%s" is not instantiable because it is an enum.',
+                    $reflectionClass->getName(),
+                ),
+            );
+        }
+
         if ($reflectionClass->isAbstract()) {
</code_context>
<issue_to_address>
**Rejected enum leaves resolver side effects**

When an enum in a union has a data-attribute resolver with external side effects and a later union arm can be hydrated, the enum’s data-attribute resolver runs before the factory rejects instantiation; the type caster catches that rejection and can continue to a later union arm, so the caller sees a successful cast while the resolver’s external state change remains.

Reject enum types before calling `Hydrator::create` in the type caster, while keeping the factory check as a general safeguard.
</issue_to_address>

Linked issues

#139 HydratorTypeCaster attempts to instantiate enum from array · 1 of 1 met

# Requirement Status Notes
1 For array input, do not hydrate an enum as a nested object; return Result::fail() for the enum type. ✅ Met ReflectionObjectFactory rejects enum instantiation with NonInstantiableException, which HydratorTypeCaster catches and converts to Result::fail(); tests cover named and union enum types.

Sourcery assessment

Approval pending. 2 findings to address first.

Blocking findings: src/ObjectFactory/ReflectionObjectFactory.php:31, src/ObjectFactory/ReflectionObjectFactory.php:38


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@viktorprogger
viktorprogger force-pushed the fix/139-enum-array-casting branch from cb4141c to 298d53f Compare November 2, 2026 20:24
@sourcery-ai
sourcery-ai Bot dismissed their stale review November 2, 2026 20:25

Sourcery withdrew this approval because the latest commits introduced blocking findings.

@samdark samdark left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default reflection-factory case is fixed, but the paths described in the existing container-factory thread and enum-resolver thread remain incomplete. With ContainerObjectFactory(new Injector()), an array for an enum still throws InvalidArgumentException and prevents an enum-first union from reaching a valid object alternative. With the default factory, an enum’s data resolver still runs before rejection and can abort that fallback.

Please reject enum types in HydratorTypeCaster before calling Hydrator::create(), while retaining the factory safeguard. Both remaining paths already existed on master; they are incomplete coverage of #139 rather than new regressions.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sourcery assessment

Approved.

@vjik

vjik commented Nov 3, 2026

Copy link
Copy Markdown
Member

I moved the enum check to Hydrator::create(). It now throws EnumException (extends NonInstantiableException) before data attributes are handled and before the object factory is called. This fixes both cases: ContainerObjectFactory is never called for enums, and the enum's data resolver never runs. It also covers direct create(SomeEnum::class) calls. HydratorTypeCaster already catches NonInstantiableException, so it needs no changes. The factory check is no longer needed: ObjectFactoryInterface now states that enums are never passed to it.

@vjik
vjik requested a review from samdark November 3, 2026 11:07
@vjik vjik added the status:code review The pull request needs review. label Nov 3, 2026
@vjik
vjik requested a review from a team November 3, 2026 11:08
@samdark samdark changed the title Fix #139: Prevent HydratorTypeCaster from hydrating enums from arrays Fix #139: Add EnumException and throw it in Hydrator::create() when an enum is passed, which also prevents HydratorTypeCaster from hydrating enums from array input Nov 4, 2026
@samdark
samdark merged commit 3cfd660 into master Nov 4, 2026
29 checks passed
@samdark

samdark commented Nov 4, 2026

Copy link
Copy Markdown
Member

👍

@samdark
samdark deleted the fix/139-enum-array-casting branch November 4, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status:code review The pull request needs review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HydratorTypeCaster attempts to instantiate enum from array

3 participants