| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| zephir.phar | 2026-08-25 | 3.3 MB | |
| 1.3.0 source code.tar.gz | 2026-08-25 | 2.1 MB | |
| 1.3.0 source code.zip | 2026-08-25 | 3.5 MB | |
| README.md | 2026-08-25 | 12.7 kB | |
| Totals: 4 Items | 9.0 MB | 0 | |
Added
- Anonymous functions can now declare a return type:
function (a) -> int { }, including after ause (...)clause (function () use (base) -> string { }), and withvoid, union (int|string) and<Class>types. The type is declared on the closure's__invoke, so Reflection reports it. Requires Zephir Parser 2.6.0 #1841 - Class constant initializers and class/trait property defaults accept a full constant expression instead of a single literal, so
const INT8_MIN = -0x7f - 1;,const MASK = 0xff << 8 | 0x0f;andpublic size = 1024 * 8;now compile. Arithmetic, bitwise, concatenation, comparison, logical and ternary operators are supported, as are parentheses, other constants (self::KB * 2) and expressions inside array constants; the result is folded to a literal at compile time with PHP's own semantics (4 / 2is anint,PHP_INT_MAX + 1a float). An initializer that is not resolvable at compile time is now rejected by name instead of failing as a syntax error. Parameter defaults are unchanged. The new syntax needs Zephir Parser 2.7.0; an older extension is bypassed in favour of the built-in parser (see below) #2061 - Added an optional
--CONFIG--section to the.zeptformat, holding a JSON object merged into the throwaway project'sconfig.json. This makes a case buildable under a non-default compiler configuration (e.g.{"optimizations": {"internal-call-transformation": true}});namespaceandnamestay derived from the sources #2021 - Added destructuring assignment:
let [a, b, c] = expr;assigns consecutive array elements to several variables at once, andlet [a, , c] = expr;skips slots. The right-hand side is evaluated exactly once, a slot past the end of the source array isnull(as in PHP'slist()), and a non-array source is rejected at compile time. Nested (let [[a, b], c] = expr;) and keyed destructuring are not supported #2496
Changed
- The minimum
ext-zephir_parseraccepted as the fast parsing path is now 2.7.0 (was 2.1.0). An installed extension older than that cannot parse everything the compiler accepts, so it is ignored and the built-in pure-PHP parser is used instead — compiling still succeeds, just without the C parser's speed. Upgrade the extension to restore it #2061 zephir buildandzephir installnow report where the extension was installed (Extension installed into /usr/local/lib/php/extensions/no-debug-non-zts-20230831), read from theEXTENSION_DIRthe build was configured with, so it names the right directory for a build retargeted with--with-php-config. The plainExtension installed.is kept when the destination cannot be confirmed. TheAdd "extension=…"hint now names the configuredextension-namerather than the namespace #2467
Fixed
- Fixed a
breakwritten after areturn(orthrow) inside aswitchclause being reported asUnreachable codeand, worse, stopping theswitchfrom satisfying the method's return-type hint, so the snippet in the issue failed withReached end of the method without returning a valid type specified in the return-type hints. A deadbreak/continueis a no-op that PHP accepts without any diagnostic, and the completeness check now stops at the first statement that actually transfers control instead of looking only at the last one. A deadbreaknested in anifarm is still honoured, because that path leaves theswitchwithout returning #1704 - Fixed the check deciding whether a method can reach its end without returning, which had three overlapping implementations and inspected only one statement of each block. It reported
Reached end of the method without returning a valid type specified in the return-type hintsfor dead code written after areturn, and conversely accepted a method that could fall off its end - anif/elseif/elsewhoseelseifarm does not return (theelseifarms were never looked at), atrywhosecatchbody is empty, or afor/whilebody that returns although the loop can run zero times - in which caseZEPHIR_MM_RESTORE()was skipped too. A single check now modelsif/elseif/else,switch,try/catchand loops, treating aloop/while truewithout abreakas unable to fall through #1704 - Fixed
switchclauses not falling through as they do in PHP. A clause that did notbreakran thedefaultbody instead of the next clause's body, because every clause was emitted as an independentifwithdefaultappended unconditionally;switch a { case 1: let r .= "a"; case 2: let r .= "b"; default: let r .= "d"; }gave"ad"for1instead of"abd". Clause bodies are now emitted in source order behind agotodispatch chain, so fall-through, adefaultwritten in the middle, andcontinuebehaving asbreakinside aswitchall match PHP. Acaseexpression after the first match is no longer evaluated either #1704 - Fixed a
usestatement naming a class of an optional extension warning asdoes not exist at compile timewhen that extension is not loaded in the PHP running Zephir. The bundled prototypes declare those classes, but they were required after theusevalidation, so building Phalcon withoutext-redisorext-memcachedreportedRedis,RedisClusterandMemcachedas nonexistent. The prototypes — and anyprototype-dirof the project — are now loaded before the validation.RedisException, which no prototype declared, was added to theredisprototype [phalcon/cphalcon#17517](https://github.com/phalcon/cphalcon/issues/17517) - Fixed a method declared by a parent interface not being resolved, so
abstract class Base implements Outerwithinterface Outer extends Innerrejectedthis->go()withClass 'Base' does not implement method: 'go'unless the class implementedInnerdirectly. An interface now reports the methods of its parent interfaces at any depth, including when it extends several. A concrete class is likewise held to the whole chain at build time instead of failing later as a PHP fatal error #2635 - Casts now accept every source type. The cast operator enumerated sources per target, so more than half of all combinations failed with
Cannot cast: X to Y— among them(string) 5,(string) "abc",(array) 5,(bool) [],(char) 65,(int)of along, and every use of(uint),(ulong),(uchar)and(var). Results match PHP, including(bool) 0.4beingtruewhere a C cast would truncate it tofalse#1841 - Fixed
(array)and(object)casts overwriting the variable being cast: both lower to kernel conversions that run in place, solet b = (array) aturnedaitself into an array #1841 - Fixed an interface
extendsing a bundled interface declared in a PHP extension header (e.g.interface I extends \JsonSerializable, \SeekableIterator) failing to compile with'php_json_serializable_ce' undeclared. The required#includes were collected but then discarded for interface files, so the emittedzend_class_implements()calls referenced undeclared class entries #2427 - Fixed the
internal-call-transformationoptimization breaking method overriding. It replacedthis->method()with a direct call to the callee's C function, which skips PHP's dispatch, so a subclass override was never reached —Vector::multiply()callingthis->multiplyMatrix()always ranVector's version even on aColumnVector. The direct call is now used only where PHP resolves the target statically anyway: afinalmethod, aprivatemethod of the class being compiled, orthisin afinalclass.self::,parent::andClass::method()calls are unaffected, and a userland PHP class extending a Zephir one now overrides correctly too. Accessors generated from{get}/{set}are always public, so they no longer take the direct call #2021 - Fixed
internalmethods with parameters reading the caller's arguments instead of their own, which crashed the process outright whenever the two arities differed. They are called C-to-C with the caller'sexecute_dataand receive their real arguments as trailing pointers, but still ran aZEND_PARSE_PARAMETERSblock against that frame; the block is now skipped and every parameter type is bound from the arguments actually passed. Affects theinternalkeyword as well asinternal-call-transformation#2021 - Fixed a segfault whenever an exception is created inside an
internalmethod — which includes everythrowout of one, since the constructor runs first. Such a method is called C-to-C on a syntheticzend_execute_datawhosefuncwasNULL, and the engine reads afunc-less frame as a generator placeholder frame only, so the backtrace capture that every Exception constructor performs dereferenced it. The frame now carries an anonymous internal function and is skipped in userland backtraces.debug_backtrace()inside aninternalmethod crashed for the same reason. Affects theinternalkeyword andinternal-call-transformationon PHP 8.1 and later #2639 - Fixed a
.zeptcase being written to the wrong.zeppath when a--FILE--comment happens to contain the wordsclass,interfaceortraitfollowed by a name. The path is derived by matching the declaration over the whole section body, and prose such as "a method of the class being compiled" won, so the build failed withUnexpected class name ... in file: 'stub/being.zep'. A declaration now has to begin a statement #1098 - Fixed
internal-call-transformationemitting body-less C functions forabstractmethods and for generators, and droppingstaticfrom the generated twin. It also generated each parameter conversion twice, because the twin shared the original method's parameter list — a leak forstringandarrayparameters, which allocate #2021 - Fixed capturing a
stringin a closure emitting C that does not compile (ZVAL_STRINGapplied to azval). It affected everystringheld as a zval: a local, a parameter reassigned in the method body, and — withinternal-call-transformationenabled — anystringparameter, which was the last file ofstub/failing to build under that option. Such a capture is no longer boxed a second time #2638 - Fixed
__FUNCTION__and__METHOD__reporting a compiler-generated method name. Both are folded at compile time from the method being compiled, and the compiler recompiles a method body under a mangled name in two cases: the twininternal-call-transformationgenerates (<name>_zephir_internal_call) and the step a generator's body is moved into (zephir_gen_step_<name>). They now report the name declared in the.zepsource, as do compile diagnostics that name the method #2643 - Fixed the stale shared object not being removed before a full rebuild in projects that set
extension-name: the cleanup looked forext/modules/<namespace>.so, while the module is built asext/modules/<extension-name>.so#2467 - Fixed
zephir installandzephir buildreporting success when the extension was never installed. The exit status ofsudo make installwas ignored — only the presence ofext/modules/<name>.so, which plainmakeproduces, was checked — and the CLI discarded the result of the install step on top of that. A missing sudo, a declined password or a read-only extension directory now fails with the exit code and a non-zero status instead of printingExtension installed.#2467