Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129825 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id 09F5F1A00BC for ; Wed, 21 Jan 2026 19:53:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1769025196; bh=OCi4O7K1SQA4sgGVS4+1+5SkSMjYgOwMvOVrskdVPPk=; h=From:Date:Subject:To:From; b=UZ6PX/ThowKwpXUYyprKSZ6T2uoD8Kk5pxW3a789YE0wu2c4iA4fb9xURV0YUrinw M1HPXiBeFKshHbBrrhp4tqYYBBeY4AxFeb1MFRsJE/acnvOc/zT8aq0rXWMn+vC3Is PFfCom1blIFvT7J8B9/Fo9smahrnLSG53S70DhR9FOq8iH8YsDnR7A/C56wYc0kVms vVSet/04BFVfouDdeWBsJiixoMouVFbiCCDDnyFHJuqVapDj1SYJSQL/MPp1bkgAIp r5ITCLV7l5mcDIYaJ08PFoFKaj8Vd1fEksUJnqs0rOhih8wLPym9bu7U7hDjAOjl6B 2vXVBc2rkEcsg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id AD3A91805E4 for ; Wed, 21 Jan 2026 19:53:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.6 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_50, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS, FREEMAIL_FROM,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE, T_SPF_TEMPERROR autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from mail-qk1-f174.google.com (mail-qk1-f174.google.com [209.85.222.174]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Wed, 21 Jan 2026 19:53:12 +0000 (UTC) Received: by mail-qk1-f174.google.com with SMTP id af79cd13be357-8c530866cf0so22815085a.1 for ; Wed, 21 Jan 2026 11:53:07 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1769025186; cv=none; d=google.com; s=arc-20240605; b=QWiSuzZz3ACwwIZI3JnoRm0E+WBspxzwHpHWF9oxKg2rL0KdkcbBu3UUEciR2mYopK 37RDfVHY0XfV7TvqP1tdDfKEXrbrTO+bPyvOGB86iYnxW+Uzz4mwzHj1liQH9EcVUmTp RIoUtABrO438dn1ciS1mYl60rEx4KVPxy+iHCkU9uJbPYBYSbd6CnnVZTn3yjMkri0u1 jFaAaqQTrE0GLW6/Jf8DGq/pBVyLO3AuAWzX8lcImxzKC/17CNLWwJjC0WO+DsZNogsf 2Z6VVhFj+AiLoLzMYOHQJ/cUSdRrzl48/4OSNRpjC+qa8ZUnkAV5hdNF8CFYFKz2JL2b iRog== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=to:subject:message-id:date:from:mime-version:dkim-signature; bh=lGrKnW1LXT+T/4QT/X7l5Fqn9luHB9xnT4oA4ZYiVk0=; fh=PwvV1jWZOR90rDIG/6XexqaHJyAFBTdnFVhsS64qdEQ=; b=L0zh7hi5CZfKHvSIEX6o/8Q5Brzp4JdkeZTX6vJ+MP4XjoHttX3uyiGPc00YMN/m9I ptWJIXBwz0CAarkq4x8RBw+FeTUF+WlZZqYel3RmbglLktvVSJP1sHKjLV3ZyYxF4iuU DSfutl8ebAU4OAlf9KGk53MOV050XY49QQ3Uy8QyLBMewHmXGTWJh6kD+7xbyLnIZHcx PrLb34BMR+quUj/yW32LlX1I+Rf6+Oj6iVRBJpfCuChdKMy0gMebyrjfLA6Bs7dhWpMd Sq1+y/IkXVuXG3MawQLoK970m/UW9JhKtUBEnRrT28+omsOXK0TBiemoSe16ouZ3D0XZ ffcw==; darn=lists.php.net ARC-Authentication-Results: i=1; mx.google.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769025186; x=1769629986; darn=lists.php.net; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=lGrKnW1LXT+T/4QT/X7l5Fqn9luHB9xnT4oA4ZYiVk0=; b=OUVc89Il8vhBajoBOqzZJf0BrXWwIQ0dZNa62FPPlql1FayzL7ifvkF1DsVRZUFrFd 6zjvzryKiH5wsLTPNh38K32nDTUmmW1YyGGYRqps9sRyyW/CUfYgDJQmrMTs5ZcQVjIo 3J1WL3PH0EAmSM635zDErDj+l1QbCyPYzaThdgQgqBO0vU7sQbGbM3AaAMl3wrklTMcX SF41L7Ahjwty2u8LAID+vktXsAG/iV8k8XWLdGLUOrK+ywN2ODwoHWgyQH/YBzTjw2Sr yrdHulEDU9F+m2QR3fB5A/p/hCbkA2fUMWaNAXF6FJmTNQqSnFrSxo2+PbgL2zwjcmWJ +DKA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769025186; x=1769629986; h=to:subject:message-id:date:from:mime-version:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=lGrKnW1LXT+T/4QT/X7l5Fqn9luHB9xnT4oA4ZYiVk0=; b=WCbQpM4bSmaeKfYwZWn/rDKH6kfcU2LakpQL2t5DgNIvXJDjz6mpzgqmoJuNAR1Oj0 qgAq0dDGMfLdIsTiL28JTJRwKkJYJ0aJceRWLpVaOr1/7QaeqvZyCwPJ4062t8E+fpqo WiislpZx4aTt2xAyeh1HpYwq1yAqQdri4vDplJxKwRM8L+8fw65SniKRLfOLwFQABn2I p61HkvEUTgZ4EOcBrZ5ww4b6Rlqi5t3YDZIXfy1W3gMg0xS+7hSL6kX0a/4nwEsamLzz xYlzaPXARKaNS+ATFuJyVOCuf2rVbL0kSWlyxmRJbl9xlUNjDwFodOvTqHu96mo26odV vV7Q== X-Gm-Message-State: AOJu0YyNY3c+j1fWVqJN3m3uJbjHXGWuuSyWQBSxjJhpGsrvsep/FXet OoNlmkilW0zbtDZ22VNlDOgIAYYNCuoDmnWgZ65ouG+Ffjm+o0+rRgzuzKyfvq/KRr4rJpH3BWf 3vCShv2TgH97xhVFTnTX1IX6XDo9fnk08VTAa3cc= X-Gm-Gg: AZuq6aJ8S89zqdl/OOFx6tolMYLT0hDWOlcMaPK20Z97ajga+lTtitBKkM+VbFcbhf/ 8Y8PUjHXKiJq29zA5hWIQcBCmhHmWMudYOCFOZQ4/RBg09tPPgSSnyayREgRkngCyv/fQ2MhmoV +8vvxd6+Zv/SyzstY3GMTt/Yme2KrXqtDlclPks7BOYZcQGAy+rAz6uNzz+LAsu1khtPfkisWzq 6OZimNyRxjgeVbNhpM+lJS6pWR7eBOPtwmUwvCarqnyv47Z1Gniy2y5weJ3rfcB083AsCJGCrRM q1ttWsQXwGF1ZiGE/65+2+ifonoS X-Received: by 2002:a05:620a:4112:b0:8b2:ef5e:d27e with SMTP id af79cd13be357-8c6cce21a14mr857522985a.51.1769025186434; Wed, 21 Jan 2026 11:53:06 -0800 (PST) Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 Date: Wed, 21 Jan 2026 20:52:55 +0100 X-Gm-Features: AZwV_QgTteynbziPeaDf1ItK5SzByyIrZRybgWqOzb5aSue_pTSHirvF5a1kY3w Message-ID: Subject: [PHP-DEV] Closure optimizations To: PHP internals Content-Type: text/plain; charset="UTF-8" From: tovilo.ilija@gmail.com (Ilija Tovilo) Hi everyone I've created a PR implementing two new optimizations for closures. [1] There are some theoretical BC breaks, so please let me know if any of them are of concern to you. 1. Static inference The engine will attempt to infer static for closures that are guaranteed not to make any use of $this. This has the benefit of avoiding a cycle when a closure is stored in a property of the declarator object. Frequently, such objects and their closures will not be collected for the entirety of the request, given the GC often doesn't run at all. But the real reason for this optimization is to enable optimization nr. 2. It's worth noting the rules for inferring static are slightly esoteric. You can read more about the rules in the PR description. 2. Stateless closure caching Stateless closures, i.e. those that are static, don't capture any variables and don't declare any static variables are now cached. function test() { $x = function () {}; } for ($i = 0; $i < 10_000_000; $i++) { test(); } Previously, this would have created 10 000 000 closure instances, even though all closures are effectively identical. Now, the first closure will be kept alive for reuse. With these two optimizations, this small benchmark improves by ~80%. Of course, this is a very synthetic benchmark. However, I could also measure improvements in real applications that instantiate many stateless closures. For example, in the Laravel template these two optimizations can avoid 2384 out of 3637 closure instantiations, improving performance by ~3% on my machine. The foreshadowed BC breaks come down to a three things: 1. For closures that are inferred as static, ReflectionFunction::getClosureThis() will now return NULL. 2. Objects that would previously have created cycles may be collected earlier, also triggering destructors earlier. IMO, this is a feature and more predictable than the current behavior. 3. Two stateless closures originating from the same lexical location will now be identical. I.e.: function test() { return function () {}; } test() === test(); // true Of note is that Closure::bind() and Closure::bindTo() usually throw when attempting to bind an object to a static closure. In my PR, this is explicitly allowed only for closures that are inferred as static, but not those that are explicitly static. I'm looking forward to your feedback. If there are no concerns, I will merge this PR into master for PHP 8.6 in roughly two weeks. Ilija [1] https://github.com/php/php-src/pull/19941