Address both review findings on the trait composition fix:
1. An abstract trait requirement was discarded without validating the
concrete implementation. composeTraitAst now validates the
implementation - the class's own method, a concrete method from
another trait (in either collection order), or a class method
matching an aliased abstract - against the abstract declaration
before dropping it, following Zend's trait-composition rules:
matching staticness, a kept by-reference return, no additional
required parameters, contravariant parameter types, and a covariant
return type. Type variance reuses the existing override-check
machinery on the preprocessed definitions; late-bound self/static/
parent keywords are unified through the recorded type keywords, and
self-in-trait resolutions are remapped to the consuming class.
Visibility is intentionally not restricted: Zend allows an
implementation of any visibility to fulfill an abstract trait
requirement (verified against Zend 8.4).
2. Multiple alias adaptations of the same method depended on source
order because a same-name visibility change mutated the statement
that later adaptations cloned. Every adaptation now derives its
flags from the immutable original flags, and the original statement
is only mutated after all adaptations are processed, so
`value as protected; value as alias;` leaves `alias` public in both
adaptation orders (matching Zend, where each adaptation derives from
the original and the last same-name adaptation wins).
Both behaviors were pinned against Zend PHP 8.4 before implementing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three defects in composeTraitAst diverged from PHP trait semantics:
1. When a later trait supplied a concrete method for a name an earlier
trait declared abstract, the branch unset the new concrete statement
while the already-merged abstract one stayed in the class AST. The
composed class kept only the abstract declaration, so instantiating
it failed with "Cannot instantiate abstract class" even though the
method body existed. The reverse trait order worked. Now the merged
abstract declaration is dropped and the concrete method is kept.
2. Alias adaptations assigned the new modifier over the whole flag set,
wiping static/final/abstract: `use Maker { make as protected; }`
turned a static method into an instance method, and static:: calls
on it miscompiled. PHP replaces only the visibility bits (and keeps
the original visibility when the modifier carries none, e.g.
`as final`).
3. A method defined by the class itself did not suppress the
trait-vs-trait conflict check, so `class C { use A, B; function f(){} }`
with f() in both traits died with a spurious "method already exists"
fatal. The class-method check now runs before conflict resolution and
suppressed trait copies are no longer registered as trait methods.
- Create test verifying trait class constants remain bound to composing class
- Add test with ClassIdentityTrait using __CLASS__ and self::class
- Include Base class that uses the trait and Child extending Base
- Verify magicClass() returns "Base" instead of "Child"
- Verify selfClass() returns "Base" instead of "Child"
- Add expected output showing correct binding behavior
- Add fake_scope_guard keyword to reserved words list
- Implement trait property scope handling in PropertyAccessTrait
- Generate proper scope guards for trait methods in Translator
- Add WASM implementation documentation outlining technical approach
- Include trait-fake-scope-guard-param test case
- Link WASM plan in main documentation index
- Renamed ExtensionProvider attribute to MethodsFor across all test files
- Updated keyword extension tests to use MethodsFor instead of ExtensionProvider
- Modified universal method extension tests to reflect the attribute name change
- Adjusted stream method extension tests to use new attribute naming
- Changed object extension tests to use MethodsFor attribute
- Updated constant expression validation logic for PHP 8.3+ static initializers
- Added support for dynamic static variable initializers in PHP 8.3 and later
- Removed