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.php — resolveEnumData() (~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
Summary
EnumProxyGeneratoronly understands literalString_/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
Generated proxy:
Fixture:
tests/Fixtures/audit/src/Php81EnumConstExprCases.php(audit branch).Root cause
src/Proxy/EnumProxyGenerator.php—resolveEnumData()(~lines 288-295):Everything else leaves
$caseValue = null, andEnumGenerator::addEnumCase()then skipssetValue().Proposed fix
Stop evaluating the expression at all: carry the original
Exprnode from the enum case intoEnumGeneratorand emit it verbatim (the proxy enum lives in the same namespace, andself::refers to the proxy enum where the constants are preserved via the trait — noteself::SHIFTneeds 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::SHIFTon the proxy resolves through the used trait). Evaluating viaReflectionEnumBackedCase::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::CONSTcase values.Environment