Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:129858 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 02F351A00D2 for ; Thu, 22 Jan 2026 15:56:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1769097413; bh=ySMLQGdKWKqiITFiDf7ZmFSMZp/MS0LWasErOP6H78g=; h=Date:From:To:In-Reply-To:References:Subject:From; b=VdgvrRbDbxdLWHCWodcasf30l5MXzC938OlVuv1i8Mwu1CR/N3c08lA0g5thR3IfT 3dN4BqckRp6Z75mexG7MAgsyZPKj9W8gnKqqYEvLfBcrUpwab2up0T9Q62WoetYcLN BTfqwOCH1LObDN2z5CFJ07FtdOAeOS/lNXlBcX9++ZEmUx94Vu5/WXFKObPk72KCcK elEeipKyH27YutU47OXDTzcwMg41hVk+zSokKj3K+jKvFtHn5kIQj6wEUmeKP8URbl 6K4SgmbgaLaFA8anA3VlUdET77OphLknEBC2t6lpywXoCfQAWOwjG7KiQW8ghb0IK4 z6+kKKrBA8eWA== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 92262180575 for ; Thu, 22 Jan 2026 15:56:51 +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.1 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_MISSING,RCVD_IN_DNSWL_LOW, SPF_HELO_PASS,SPF_NONE autolearn=no autolearn_force=no version=4.0.1 X-Spam-Virus: No X-Envelope-From: Received: from fhigh-b2-smtp.messagingengine.com (fhigh-b2-smtp.messagingengine.com [202.12.124.153]) (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 ; Thu, 22 Jan 2026 15:56:49 +0000 (UTC) Received: from phl-compute-04.internal (phl-compute-04.internal [10.202.2.44]) by mailfhigh.stl.internal (Postfix) with ESMTP id 544267A00D5 for ; Thu, 22 Jan 2026 10:56:44 -0500 (EST) Received: from phl-imap-02 ([10.202.2.81]) by phl-compute-04.internal (MEProxy); Thu, 22 Jan 2026 10:56:44 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= garfieldtech.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:subject:subject:to :to; s=fm2; t=1769097404; x=1769183804; bh=N37MvrxPu5CNGyJM0j9rS +uarXzFTHLPe0/7oBG7cWI=; b=kVRGhMvYqsfGYjEO9TET+cS1uXyxGgqVYe3Ic BGuW/Wp19lOX9Lb6Ajh1/vnixaiKZo/jMg1QQoLfExCOZQ3ejTJvfFc1r6LK8E7K VFizhUL2GDqytBYeMGbQ0QK3piSqMNc3nXbZ25vii4+9G1VLYYWZHEJWT+a3iaxR ttyqNVCQNEY5/nVH6swwUQXKzhRyh/LhInHPivxWIkBIwcW/j4wD6kSxbhOfn1NO TJ64wawMyGN9mwDLZKuqbtrTh3iBReFGiOYCBO4z5SaOuys9bI5g3H163d/7VF6X GHXmXj+EZMkf23C1DS8ivXh+bA/hh+cH4M6uS9XDkEAzrOcxA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:content-transfer-encoding:content-type :content-type:date:date:feedback-id:feedback-id:from:from :in-reply-to:in-reply-to:message-id:mime-version:references :reply-to:subject:subject:to:to:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm2; t=1769097404; x=1769183804; bh=N 37MvrxPu5CNGyJM0j9rS+uarXzFTHLPe0/7oBG7cWI=; b=u1M4nuy8Lemu/Jzbp IawYTCrhIX8g/vGzpD3KWYYJ1HONulfNQLz6EhAuFoa5g9BFe/hj2soXvnPu/H3z 7KHMw9qSD2tneQpRhqAfeMZ+TVkoYCEbN8J9uBIeJH19Iu/nYB9GSr5yqp+Vz5yt dKrfC/jSVYXzAdX/zTRzv0BhV6LBIXrTl2z8PFuJXhRM5EW+n+tu4gP5vmo8F3ld R2h6Ms2md6tQ6B0kIWD4HV+ymMbn+r4IxLDzhY1skNA8PIzKiHCzntSqEtdnCqn0 fNAI0blpjmU1ylRrPOQj68aVSUgoIP5VixvVWxDzquatoEtXe6KuPYOP1suhyvcY DwWjA== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeefgedrtddtgddugeeiheekucetufdoteggodetrf dotffvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdfurfetoffkrfgpnffqhgenuceu rghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddmnecujf gurhepofggfffhvffkjghfufgtgfesthejredtredttdenucfhrhhomhepfdfnrghrrhih ucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrhhfihgvlhguthgvtghhrdgtohhmqe enucggtffrrghtthgvrhhnpeeuheejudelteelieehleelleekvddtgfettdeghffggeeh vdejgeejfeffuefhvdenucffohhmrghinhepghhithhhuhgsrdgtohhmnecuvehluhhsth gvrhfuihiivgeptdenucfrrghrrghmpehmrghilhhfrhhomheplhgrrhhrhiesghgrrhhf ihgvlhguthgvtghhrdgtohhmpdhnsggprhgtphhtthhopedupdhmohguvgepshhmthhpoh huthdprhgtphhtthhopehinhhtvghrnhgrlhhssehlihhsthhsrdhphhhprdhnvght X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id E4317700065; Thu, 22 Jan 2026 10:56:43 -0500 (EST) X-Mailer: MessagingEngine.com Webmail Interface Precedence: list list-help: list-unsubscribe: list-post: List-Id: x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: ARzF8cVOtAfV Date: Thu, 22 Jan 2026 09:56:23 -0600 To: "php internals" Message-ID: In-Reply-To: References: Subject: Re: [PHP-DEV] Closure optimizations Content-Type: text/plain Content-Transfer-Encoding: 7bit From: larry@garfieldtech.com ("Larry Garfield") On Wed, Jan 21, 2026, at 1:52 PM, Ilija Tovilo wrote: > 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 This all looks great, Ilija! I am not concerned about the BC mentions, as they seem to be accurate anyway. My one question is about the bindTo() on an inferred static closure. What purpose does that serve? I'm assuming that anything that mentions $this in its body won't be marked static, and if it doesn't have $this I don't get what value bindTo() would have. What's the use case for making an exception here? --Larry Garfield