Summary
AttributeGroupsGenerator copies attributes to the proxy by feeding runtime ReflectionAttribute::getArguments() values into php-parser's BuilderFactory::args() → BuilderHelpers::normalizeValue(), which only accepts scalars/arrays. Any attribute argument that evaluates to an object kills the whole weave with LogicException: Invalid value.
Affected argument kinds:
- enum cases (PHP 8.1):
#[RichAttr(Status::Active)]
new in initializers (PHP 8.1): #[RichAttr(new \ArrayObject([1, 2]))]
- closures / first-class callables in constant expressions (PHP 8.5):
#[ExprAttr(strlen(...))], #[ExprAttr(static function (int $x): int { return $x * 2; })]
Found during the PHP 8.5 support audit (PR #597). Reproduces on PHP 8.4 / 8.5.10 / 8.6.0beta2 (closure cases are 8.5+ syntax).
Reproduction
TRANSFORM ERROR for Php81NonScalarAttributeArgs: Invalid value
LogicException: Invalid value in vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php:276
#0 PhpParser\BuilderFactory->args()
#1 src/Proxy/Generator/AttributeGroupsGenerator.php(59)
#2 src/Proxy/ClassProxyGenerator.php(153)
#3 src/Instrument/Transformer/WeavingTransformer.php(167)
Fixtures: tests/Fixtures/audit/src/Php81NonScalarAttributeArgs.php, tests/Fixtures/audit/src/Php85ClosuresInConstExpr.php (audit branch).
Even where it does not crash, round-tripping runtime values constant-folds the original expression (e.g. a const fetch Foo::BAR is baked into its value), losing source fidelity in the proxy.
Root cause
src/Proxy/Generator/AttributeGroupsGenerator.php:43-65 uses evaluated argument values instead of the attribute's AST.
Proposed fix
When the reflector is goaop/parser-reflection (weave-time path), take the raw AttributeGroup AST nodes (via getNode()/getTypeNode()-style access, same duck-typing the generators already use for types and defaults in ParameterGenerator/MethodGenerator) and clone them into the proxy verbatim — names already resolved to FQCN or re-resolved against the original namespace. Fall back to the current value-based path only for native reflection, and there skip (with a clear exception message) argument values that cannot be represented.
This is the same AST-first strategy already applied for PHP 8.5 closure/FCC parameter defaults (ValueGenerator::fromExprNode()), extended to attributes.
Environment
- goaop/framework master (4.0-dev), nikic/php-parser 5.8.0, PHP 8.4 / 8.5.10 / 8.6.0beta2
Summary
AttributeGroupsGeneratorcopies attributes to the proxy by feeding runtimeReflectionAttribute::getArguments()values into php-parser'sBuilderFactory::args()→BuilderHelpers::normalizeValue(), which only accepts scalars/arrays. Any attribute argument that evaluates to an object kills the whole weave withLogicException: Invalid value.Affected argument kinds:
#[RichAttr(Status::Active)]newin initializers (PHP 8.1):#[RichAttr(new \ArrayObject([1, 2]))]#[ExprAttr(strlen(...))],#[ExprAttr(static function (int $x): int { return $x * 2; })]Found during the PHP 8.5 support audit (PR #597). Reproduces on PHP 8.4 / 8.5.10 / 8.6.0beta2 (closure cases are 8.5+ syntax).
Reproduction
Fixtures:
tests/Fixtures/audit/src/Php81NonScalarAttributeArgs.php,tests/Fixtures/audit/src/Php85ClosuresInConstExpr.php(audit branch).Even where it does not crash, round-tripping runtime values constant-folds the original expression (e.g. a const fetch
Foo::BARis baked into its value), losing source fidelity in the proxy.Root cause
src/Proxy/Generator/AttributeGroupsGenerator.php:43-65uses evaluated argument values instead of the attribute's AST.Proposed fix
When the reflector is
goaop/parser-reflection(weave-time path), take the rawAttributeGroupAST nodes (viagetNode()/getTypeNode()-style access, same duck-typing the generators already use for types and defaults inParameterGenerator/MethodGenerator) and clone them into the proxy verbatim — names already resolved to FQCN or re-resolved against the original namespace. Fall back to the current value-based path only for native reflection, and there skip (with a clear exception message) argument values that cannot be represented.This is the same AST-first strategy already applied for PHP 8.5 closure/FCC parameter defaults (
ValueGenerator::fromExprNode()), extended to attributes.Environment