Summary
Follow-up to #598 (fixed in #613). Class-level attributes are now correctly preserved on the woven trait — but two built-in attributes are compile-time rejected on traits, so weaving a class that carries them is a new fatal:
PHP Fatal error: Cannot apply #[\Attribute] to trait Ns\ConstAttr__AopProxied
Verified on PHP 8.5.10: #[\Attribute] trait T {} and #[\AllowDynamicProperties] trait T {} are both compile fatals (#[\Deprecated] on a trait is fine). So any attribute class (or #[AllowDynamicProperties]-marked class) inside the weaver's include paths breaks at load — audit fixtures tests/Stubs/Audit/{ConstAttr,ExprAttr,RichAttr}.php reproduce it (audit branch PR #597, harness Php85AuditScratchTest).
Proposed fix
In WeavingTransformer, when converting a class to a trait, drop attribute groups whose (FQ-resolved) name is Attribute or AllowDynamicProperties from the trait while keeping them on the generated proxy class (which is a real class and re-declares attributes via AttributeGroupsGenerator) — same split already used for the readonly class modifier. All other class-level attributes stay on the trait as today.
An alternative is to skip weaving classes marked #[\Attribute] entirely (attribute classes are rarely useful join-point targets), but the drop-from-trait approach keeps behavior uniform.
Environment
Summary
Follow-up to #598 (fixed in #613). Class-level attributes are now correctly preserved on the woven trait — but two built-in attributes are compile-time rejected on traits, so weaving a class that carries them is a new fatal:
Verified on PHP 8.5.10:
#[\Attribute] trait T {}and#[\AllowDynamicProperties] trait T {}are both compile fatals (#[\Deprecated]on a trait is fine). So any attribute class (or#[AllowDynamicProperties]-marked class) inside the weaver's include paths breaks at load — audit fixturestests/Stubs/Audit/{ConstAttr,ExprAttr,RichAttr}.phpreproduce it (audit branch PR #597, harnessPhp85AuditScratchTest).Proposed fix
In
WeavingTransformer, when converting a class to a trait, drop attribute groups whose (FQ-resolved) name isAttributeorAllowDynamicPropertiesfrom the trait while keeping them on the generated proxy class (which is a real class and re-declares attributes viaAttributeGroupsGenerator) — same split already used for thereadonlyclass modifier. All other class-level attributes stay on the trait as today.An alternative is to skip weaving classes marked
#[\Attribute]entirely (attribute classes are rarely useful join-point targets), but the drop-from-trait approach keeps behavior uniform.Environment