RFC Karma request: native defer keyword (Zig-style)

php.internals

Khaled Alam

249 days ago
Hi, I'm Khaled Alam, a software engineer based in the UAE and a long-time PHP user. I'd like to request RFC karma to publish an RFC proposing a native "*defer"* feature and gather community feedback. Inspired by Zig's "defer", it schedules a block to run when the current function exits (return, exception, or natural end), executed in LIFO order, aimed at safer cleanup without repetitive try/finally scaffolding. Proof-of-concept implementation PR <https://github.com/php/php-src/pull/20786>. Current status: [x] Parser/AST support for defer { ... } [x] Compilation + VM support with per-call defer stack (not shared on op_array) [x] Defers run correctly on explicit/implicit returns, LIFO order [_] I’m currently working on exception-unwinding handling so defers also run when an exception is thrown Wiki username: *khaledalamxyz*GitHub username*: *khaledalam Thank you for considering granting RFC karma. Best regards, Khaled Alam

Ilija Tovilo

248 days ago
Hi Khaled On Fri, Dec 26, 2025 at 4:41 PM Khaled Alam <khaledalam.net@gmail.com> wrote:
> I'd like to request RFC karma to publish an RFC proposing a native "defer" feature and gather community feedback.
RFC karma was granted. Good luck!
> Inspired by Zig's "defer", it schedules a block to run when the current function exits (return, exception, or natural end), executed in LIFO order, aimed at safer cleanup without repetitive try/finally scaffolding.
I've already shared some technical feedback on the PR, let me reiterate here. I think it's important that defers are executed on scope-end, rather than function-end. For functions with many loop iterations, this would otherwise schedule tons of defer executions for resources that could have long been released. Relatedly, the way this is currently implemented, defer referencing changing variables is ineffective. For example: function test() { for ($i = 0; $i < 10; $i++) { $resource = allocate_some_resource(); defer { close($resource); } } } is roughly equivalent to: function test() { for ($i = 0; $i < 10; $i++) { $resource = allocate_some_resource(); } for ($i = 0; $i < 10; $i++) { close($resource); } } Which is obviously incorrect, closing the same resource over and over again. Executing at end-of-scope would mostly resolve this issue. On a more technical level, the implementation mostly mirrors FAST_CALL/FAST_RET used for finally. This seems unnecessary. FAST_CALL/FAST_RET also already handle exceptions, which your implementation is currently missing. I believe `defer { <code> } <code until end of scope>` could mostly be sugar for `try { <code until end of scope> } finally { <code> }`. Given those things are so similar, the RFC should make it clear why a new syntax is beneficial. I suppose it puts the alloc/free code lexically closer together, which might be worth it for some people. Ilija

Ilija Tovilo

247 days ago
On Sat, Dec 27, 2025 at 9:14 PM Ilija Tovilo <tovilo.ilija@gmail.com> wrote:
> > On Fri, Dec 26, 2025 at 4:41 PM Khaled Alam <khaledalam.net@gmail.com> wrote: > > > Inspired by Zig's "defer", it schedules a block to run when the current function exits (return, exception, or natural end), executed in LIFO order, aimed at safer cleanup without repetitive try/finally scaffolding.
Oh, and what I forgot to mention here (but not in the PR), there are two recent RFCs that try to solve a very related problem. - https://wiki.php.net/rfc/optin_block_scoping - https://wiki.php.net/rfc/context-managers Ilija