Release: 0.43 - First-Class Callable
0.43.0 - First-Class Callable
Released 2026-06-09 · GitHub release
Up to ~3.7× faster compiler hot paths, first-class PHP callables, readonly structs + enum methods, typed interface constants
🎉 Added
php/callable: first-class callable interop emitting PHP 8.1(...)for free functions ((php/callable \strlen)), static methods ((php/callable Foo bar)), and instance methods ((php/callable obj method))defstruct^:php/readonly: typed fields become PHPreadonlyproperties (untagged →readonly mixed);put/assocstill return copies via a constructor rebuilddefenummethods and interfaces: after the cases, an enum can implement interfaces and carry a:phpmethod block (pure and backed enums)^:php/override: emits PHP 8.3#[\Override]on generated methods (struct/enum interface impls,definterfacemethods); inline struct/enum methods also accept:php/attrand:php/docdefinterfacetyped class constants: a trailing:php/constblock emits PHP 8.3 typed constants, e.g.(^{:tag int} MAX 100)→const int MAX = 100;
Performance
NodeEnvironmentlocal lookups are now O(1) hash-indexed, so symbol resolution no longer degrades withlet/fn/loopnesting depth (~1.7× faster at depth 20)Symbol::hash()caches itscrc32in a readonly property (likeKeyword), so repeated hashing no longer recomputes (~1.9× faster)- Analyzer type-checker set-membership (
CallTypeExpectationResolver,ReturnTypeInferrer,ConstantFolder,DefSymbol) usesissetmaps instead ofin_arrayscans (~3.7× faster) LetSimplifier: the unused-binding pass tracks live references in a set, turning its O(n²) scan into O(n) over alet's bindings- Parser:
AtomParserskips the number-regex for atoms that cannot start a number (~1.5× faster per symbol atom);Parserdispatch uses anissetmap instead ofin_array
⚖️ Changed
- Architecture: satellite-module factories (
Lsp,Lint,Watch,Nrepl,Profile) inject thePhel\Shared\Facade\*FacadeInterfacecontracts instead of neighbour modules' concrete facades, matching the "inject interfaces" rule;RunFacadeInterfacegainsautoDetectEntryPoint(): ?string - Architecture: the eval-result value objects (
EvalResult,EvalError,StackFrame) moved to the leafPhel\Shared\Evalnamespace, and their orchestration toRun\Application\StructuredEvaluator, keeping the VOs logic-free - Architecture: version detection consolidated into
Phel\Shared\VersionResolver; Console and Run both resolve through it, removing Run's dependency on the whole Console module - Architecture: the pure Base64-VLQ codec moved to the leaf
Phel\Shared\SourceMap\VLQ(stateless, likeMunge); the SourceMap producer/consumer stay in Compiler
🐛 Fixed
:tag: anever/void/nullreturn tag on a value-returning function is now a compile error instead of a load-time fatal;mixed,?T, and union/intersection tags still passdefenum: the bare enum name now resolves to its PHP class, soEnumName/caseaccess works
👥 Contributors
@JesusValeraDev @Chemaclass @titanniya542-spec
Full Changelog: v0.42.0...v0.43.0
Downloads
v0.43.0
- phel.phar (1.69 MB)