* fix(preprocessor): reject variadic promoted properties and callable property/constant types
Two promotion/type gaps against Zend (probed on 8.4.13):
- `__construct(public int ...$x)` was accepted and even registered
the property before the variadic-position check ran. A variadic
parameter collects its arguments into an array, so there is no single
value to promote; Zend fatals with "Cannot declare variadic promoted
property". The check now precedes the property registration.
- `callable` is a calling-scope-dependent type, so Zend forbids it in
property types (declared, promoted, interface hooked) and class
constant types (class and interface), bare or as a nullable/union
member: "Property A::$x cannot have type ?callable" /
"Class constant A::X cannot have type callable". Intersection
members are left to the compound-type validation, which rejects
every non-class standard type there.
`void`/`never` property and parameter types were already rejected by
parseTypeDecl ("The type `void`/`never` is allowed only for return
type") - verified, no change needed; union members are covered by the
compound-type validation.
* test(preprocessor): cover promotion and property/constant type rules
* fix(preprocessor): reject callable inside intersection and DNF types
typeDeclContainsCallable() deliberately skipped IntersectionType, so a
DNF-nested callable such as `public (Traversable&callable)|stdClass
$value;` sailed past the property checks and died in gen_stub on
assert(!$type->isBuiltin); a bare `Traversable&callable` property
compiled outright.
Zend rejects callable while compiling the intersection type itself,
with its own diagnostic ("Type callable cannot be part of an
intersection type", probed on 8.4.13), in every declaration context and
ahead of the property/constant-specific bans — `callable|(Traversable&
callable)` reports the intersection conflict, not the property one.
A dedicated assertTypeDeclIntersectionsHaveNoCallable() walk (nullable,
union, intersection members) now runs before the existing
typeDeclContainsCallable() checks in all contexts this branch guards:
class properties, promoted properties (both via addClassProperty),
typed class constants, and interface properties/constants. Tests cover
the bare intersection member, DNF in first and second union member,
the promoted and constant/interface variants, and a callable-free DNF
property that must keep compiling.
* fix(preprocessor): validate callable intersections on the common type-declaration path
assertTypeDeclIntersectionsHaveNoCallable() was invoked only from the
property and class/interface-constant paths, so the same invalid type
in a function parameter or return declaration bypassed the check and
reached the later generator path:
function consume(Traversable&callable $value): void {}
function produce(): Traversable&callable {}
Zend rejects both while compiling the type itself ("Type callable
cannot be part of an intersection type", probed on 8.4.13), in every
declaration context.
The walk now lives in parseTypeDecl(), the declaration funnel behind
resolveTypeDecl() that parameters, returns, properties, promoted
properties, class and interface constants, and interface hooked
properties already flow through; the per-context calls are gone, and
the diagnostic points at the offending intersection member. Closure
and arrow-function signatures resolved no full type node anywhere, so
doGenClosure() now routes them through the same funnel - except bare
class names, which the native-object walk there already resolves (and,
inside trait methods, rewrites) via parseTypeDecl().
The property and constant paths resolve the declaration before
applying their own bare/nullable/union callable bans, so a type like
`callable|(Traversable&callable)` keeps reporting the intersection
conflict first, as Zend does.
New negative tests: parameter and return intersections, callable in a
DNF parameter member, and closure parameter and return intersections
(each probed against Zend 8.4.13); positive tests keep bare `callable`
parameters and callable-free DNF properties compiling.
- Replace appendCapturedStmtLines helper with direct formatCapturedStmtLines calls
- Change createPropertyAccessResolver from private to protected for extensibility
- Change resolveNativeInstanceProperty from private to protected for extensibility
- Change resolveNativeStaticProperty from private to protected for extensibility
- Change applyNativePropertyAccessResult from private to protected for extensibility
- Change containsNullsafeChain from private to protected for extensibility
- Replace parseTypeDecl output parameter with resolveTypeDecl returning array
- Change sortFiles to getSortedFiles returning sorted array instead of modifying input
- Update name resolution to handle imported namespace prefixes correctly
- Enhance property hook backing access detection with additional conditions
- Add redis extension availability check to test skip conditions
- Document compiler self-hosting constraints regarding reference parameters across traits
- Replaced all occurrences of self::TYPE_* with Type::* in AnonClassGenerator.php
- Updated ArrayExpressionTrait.php to use Type::ARRAY and Type::VAR instead of self::TYPE_*
- Modified AssignOpTrait.php to reference Type constants instead of self::TYPE_*
- Changed BinaryOpTrait.php to use Type::BIGINT, Type::DECIMAL, Type::BIGFLOAT constants
- Updated CallArgumentGenerator.php to use Type::ARRAY and Type::REF constants
- Added Type import statements to all modified files
- Maintained same functionality while improving code consistency with Type namespace
- Extract getNamespacedClassName method to NameResolutionTrait
- Move upgradeToFullyQualifiedName method to NameResolutionTrait
- Transfer getNamespacedFuncName method to NameResolutionTrait
- Move parseTypeDecl method to NameResolutionTrait
- Add NameResolutionTrait usage in Compiler