You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Added
Anonymous functions can now declare a return type: function (a) -> int { }, including after a use (...) clause (function () use (base) -> string { }), and with void, 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; and public 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 / 2 is an int, PHP_INT_MAX + 1 a 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 .zept format, holding a JSON object merged into the throwaway project's config.json. This makes a case buildable under a non-default compiler configuration (e.g. {"optimizations": {"internal-call-transformation": true}}); namespace and name stay derived from the sources #2021
Added destructuring assignment: let [a, b, c] = expr; assigns consecutive array elements to several variables at once, and let [a, , c] = expr; skips slots. The right-hand side is evaluated exactly once, a slot past the end of the source array is null (as in PHP's list()), 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_parser accepted 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 build and zephir install now report where the extension was installed (Extension installed into /usr/local/lib/php/extensions/no-debug-non-zts-20230831), read from the EXTENSION_DIR the build was configured with, so it names the right directory for a build retargeted with --with-php-config. The plain Extension installed. is kept when the destination cannot be confirmed. The Add "extension=…" hint now names the configured extension-name rather than the namespace #2467
Fixed
Fixed a break written after a return (or throw) inside a switch clause being reported as Unreachable code and, worse, stopping the switch from satisfying the method's return-type hint, so the snippet in the issue failed with Reached end of the method without returning a valid type specified in the return-type hints. A dead break/continue is 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 dead break nested in an if arm is still honoured, because that path leaves the switch without 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 hints for dead code written after a return, and conversely accepted a method that could fall off its end - an if/elseif/else whose elseif arm does not return (the elseif arms were never looked at), a try whose catch body is empty, or a for/while body that returns although the loop can run zero times - in which case ZEPHIR_MM_RESTORE() was skipped too. A single check now models if/elseif/else, switch, try/catch and loops, treating a loop/while true without a break as unable to fall through #1704
Fixed switch clauses not falling through as they do in PHP. A clause that did not break ran the default body instead of the next clause's body, because every clause was emitted as an independent if with default appended unconditionally; switch a { case 1: let r .= "a"; case 2: let r .= "b"; default: let r .= "d"; } gave "ad" for 1 instead of "abd". Clause bodies are now emitted in source order behind a goto dispatch chain, so fall-through, a default written in the middle, and continue behaving as break inside a switch all match PHP. A case expression after the first match is no longer evaluated either #1704
Fixed a use statement naming a class of an optional extension warning as does not exist at compile time when that extension is not loaded in the PHP running Zephir. The bundled prototypes declare those classes, but they were required after the use validation, so building Phalcon without ext-redis or ext-memcached reported Redis, RedisCluster and Memcached as nonexistent. The prototypes — and any prototype-dir of the project — are now loaded before the validation. RedisException, which no prototype declared, was added to the redis prototype phalcon/cphalcon#17517
Fixed a method declared by a parent interface not being resolved, so abstract class Base implements Outer with interface Outer extends Inner rejected this->go() with Class 'Base' does not implement method: 'go' unless the class implemented Inner directly. 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 a long, and every use of (uint), (ulong), (uchar) and (var). Results match PHP, including (bool) 0.4 being true where a C cast would truncate it to false#1841
Fixed (array) and (object) casts overwriting the variable being cast: both lower to kernel conversions that run in place, so let b = (array) a turned a itself 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 emitted zend_class_implements() calls referenced undeclared class entries #2427
Fixed the internal-call-transformation optimization breaking method overriding. It replaced this->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() calling this->multiplyMatrix() always ran Vector's version even on a ColumnVector. The direct call is now used only where PHP resolves the target statically anyway: a final method, a private method of the class being compiled, or this in a final class. self::, parent:: and Class::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 internal methods 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's execute_data and receive their real arguments as trailing pointers, but still ran a ZEND_PARSE_PARAMETERS block against that frame; the block is now skipped and every parameter type is bound from the arguments actually passed. Affects the internal keyword as well as internal-call-transformation#2021
Fixed a segfault whenever an exception is created inside an internal method — which includes every throw out of one, since the constructor runs first. Such a method is called C-to-C on a synthetic zend_execute_data whose func was NULL, and the engine reads a func-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 an internal method crashed for the same reason. Affects the internal keyword and internal-call-transformation on PHP 8.1 and later #2639
Fixed a .zept case being written to the wrong .zep path when a --FILE-- comment happens to contain the words class, interface or trait followed 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 with Unexpected class name ... in file: 'stub/being.zep'. A declaration now has to begin a statement #1098
Fixed internal-call-transformation emitting body-less C functions for abstract methods and for generators, and dropping static from the generated twin. It also generated each parameter conversion twice, because the twin shared the original method's parameter list — a leak for string and array parameters, which allocate #2021
Fixed capturing a string in a closure emitting C that does not compile (ZVAL_STRING applied to a zval). It affected every string held as a zval: a local, a parameter reassigned in the method body, and — with internal-call-transformation enabled — any string parameter, which was the last file of stub/ 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 twin internal-call-transformation generates (<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 .zep source, 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 for ext/modules/<namespace>.so, while the module is built as ext/modules/<extension-name>.so#2467
Fixed zephir install and zephir build reporting success when the extension was never installed. The exit status of sudo make install was ignored — only the presence of ext/modules/<name>.so, which plain make produces, 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 printing Extension installed.#2467