Release: 0.42 - Life & Everything
0.42.0 - Life & Everything
Released 2026-06-06 · GitHub release
Richer typed PHP interop (attributes, enums, named args, by-ref) + lazy iterator-seq + hygienic let-macros + gacela 1.15 fix.
🐛 Fixed
if-let/when-let/if-some/when-first: now hygienic, a user binding namedtemp-symno longer collides with the macros' internal temporary- Quasiquote of an empty list (
`()) yields an empty list instead of throwingIndex out of bounds str/index-of: returnsnilfor an empty string instead of a raw PHPValueError- Lexer: error/source-map columns counted in code points, not bytes (correct locations for multibyte source)
- Printer: structs print with the
.separator ((my.ns.point 1 2)) instead of\(#2255) - Docs: function
:exampleoutputs now match REPL output, sophel docand the API reference are accurate phel->php: integer/string map keys now convert instead of throwinggetName() on int(#2298)- Structs:
$struct['field']array access now accepts a plain PHP string offset (was keyword-only) (#2313, #2319) - Dependencies: support
gacela-project/gacela1.15. Gacela 1.15 persists the merged app config (gacela-merged-config.php); Phel now points Gacela's cache at the project-relative.phel/cache(was the shared system temp dir) so one project'ssrcDirs/config can't leak into another, fixing the packagedphel test/buildwhen run against a user project. Tests isolate the cache per run via a PHPUnit extension
🎉 Added
defstruct: a:phpblock declares inline PHP magic methods (__invoke,__toString,__get, …) with no backing interface. A custom__invokemust stay map-call-compatible (one arg or variadic), enforced with a clear compile-time errorphel format: collapses consecutive blank lines to one, and indents more definition/body forms (defstruct,defprotocol,defmethod,reify,doseq,letfn, …) the cljfmt way instead of call-style alignment- Docs: the
load/in-ns/use/varspecial forms are now in the API reference andphel doc(a regression test guards against gaps), and core math/predicate functions gained:example/:see-alsometadata phel.reflect:class-attributes/method-attributes/property-attributes/attributesread PHP 8 attributes as{:name :args}maps (#2314, #2320)phel.reflect:enum->keyword/keyword->enum/enum-valuesbridge native PHP enums to keywords and back (#2315, #2321)iterator-seq: lazy seq over a PHPTraversable, pulled one element at a time (#2312, #2318)phel.http: JSON request bodies decode into:parsed-body, plusjson-response/html-responsebuilders (#2271)- Docs: runnable database-CRUD and Doctrine-entity examples (
docs/examples/13/14) plus a maps-not-entities Persistence section inframework-integration.md(#2281, #2282) Richer PHP interop for bridging Phel to typed PHP / framework code (all opt-in, untagged forms unchanged): - Generated classes carry opt-in metadata:
^{:php/attr}PHP 8 attributes (class/method/parameter),^{:tag}typed signatures (incl. union/intersection),^{:php/doc}PHPDoc, and^{:php/json}/^{:php/stringable}on structs (#2280, #2289, #2292, #2293, #2294, #2295) defenumnative backed enums, anddefexceptionwith an optional parent class (#2291, #2297)php/refpasses a local by reference intophp/->/php/::(#2299) and plain PHP function calls likepreg_match/sort(#2310, #2316)hydrate/beanbridge a Phel map and a typed PHP object both ways (#2296)- PHP 8 named arguments in
php/new/php/->/php/::via the:&marker, e.g.(php/new \App\Mailer :& :host "smtp")(#2311, #2317)
⚖️ Changed
- Dev: upgraded the test suite to PHPUnit 13 (from 10). Replaced removed/changed APIs (
assertContainsOnly→assertContainsOnlyString, multipleexpectOutputRegexcalls, closure-comparison in one analyzer test). A follow-up will convert the remaining unconfigured mocks to stubs (PHPUnit-notice only, non-failing) - Internals: modular-architecture cleanup of
src/php/, relocated leaky cross-module value objects to their proper homes and split many oversized analyzer/command/compiler/runtime classes into focused collaborators (pure refactors, no behavior change) (#2261, #2262, #2263, #2264, #2273, #2274, #2275, #2276, #2277, #2278) - Tests:
RegistryTest/PhelVarTestsnapshot and restore the globalRegistry, fixing order-dependentphel.corefailures (#2256) - Docs: condensed every
docs/guide (~17% smaller), verified against the runtime, cross-linked to phel-lang.org - Docs: collapsed needlessly multiline
:doc/docstrings acrosssrc/phel/(418 strings, 57 files) so prose renders on one line inphel docand the API reference instead of with stray line-break spacing; structured docs (code blocks, aligned key tables, multi-paragraph, indented examples) are preserved (#2340) - Quality: repo-wide maintainability pass, docblocks and
:doc/:see-also/:examplemetadata, dead-code/type/naming cleanups, helper extractions, ~40 new unit tests (no behavior change) agent-install: simplified to copying skill files and the.agents/docs tree; dropped version stamping,--check,--listphel.ai: default chat model is nowclaude-sonnet-4-6;nearestcomputes the query magnitude once instead of per item- Dropped redundant
(:require phel.core)from 14 library namespaces (always preloaded); kept inphel.string, which needs thecore/alias forcore/reverse defstruct:phpblock: the incompatible-__invokearity error now points at the[this & _]workaround for callers that want a no-meaningful-argument invoke
👥 Contributors
Full Changelog: v0.41.0...v0.42.0
Downloads
v0.42.0
- phel.phar (1.68 MB)