Skip to content

Enum proxies lose constant-expression case values (invalid backed enum generated) #600

Description

@lisachenko

Summary

EnumProxyGenerator only understands literal String_/Int_ case values. Any backed-enum case whose value is a constant expression is re-declared in the proxy enum without a value, producing a fatal error: a pure case inside a backed enum.

Found during the PHP 8.5 support audit (PR #597). Reproduces on PHP 8.4 / 8.5.10 / 8.6.0beta2.

Reproduction

enum Php81EnumConstExprCases: int
{
    private const int SHIFT = 2;

    case Negative  = -1;          // UnaryMinus
    case Shifted   = 1 << 2;      // BitwiseShift
    case FromConst = self::SHIFT + 10; // ClassConstFetch + Plus

    public function describe(): string { ... }
}

Generated proxy:

PHP Fatal error: Case Negative of backed enum ...\Php81EnumConstExprCases must have a value

Fixture: tests/Fixtures/audit/src/Php81EnumConstExprCases.php (audit branch).

Root cause

src/Proxy/EnumProxyGenerator.phpresolveEnumData() (~lines 288-295):

if ($stmt->expr instanceof String_)  { $caseValue = $stmt->expr->value; }
elseif ($stmt->expr instanceof Int_) { $caseValue = $stmt->expr->value; }

Everything else leaves $caseValue = null, and EnumGenerator::addEnumCase() then skips setValue().

Proposed fix

Stop evaluating the expression at all: carry the original Expr node from the enum case into EnumGenerator and emit it verbatim (the proxy enum lives in the same namespace, and self:: refers to the proxy enum where the constants are preserved via the trait — note self::SHIFT needs the constant to be resolvable from the proxy; since class constants stay in the woven trait and trait constants are usable since PHP 8.2, self::SHIFT on the proxy resolves through the used trait). Evaluating via ReflectionEnumBackedCase::getBackingValue() is a simpler alternative (constant-fold at weave time), but emitting the AST expression preserves source fidelity; either fixes the fatal.

Add fixtures for negative, bitwise, and self::CONST case values.

Environment

  • goaop/framework master (4.0-dev), PHP 8.4 / 8.5.10 / 8.6.0beta2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions