Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127635 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 13B701A00BC for ; Mon, 9 Jun 2025 19:27:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1749497153; bh=9Z0m1eb1TaUHnpeBIqLxHiR35bw8nn7E126vdt5WjKY=; h=Date:From:To:In-Reply-To:References:Subject:From; b=oLkSdsPfnmTMssXaAavz1mMT54iifScdznMFXOCRaLhn/rXkiAq8cJr/Kp+trLNaR 4CsTJDUrBZvV4W+rU5IrrUvQnkB3GVO68zKaTVEdLhQwyz9ahj7rPYJ2psCbNVvjx7 4tbCSI0a0R2UBAuASvjhvJ0s5tjMNbZXVWqlL9ec8GOMNnczl/qnrNJ2Vr6GKjkUUF bFJU5AV82d4pbGzuAEWaf0B0FVLl1P0XAbfs8FhN8J5CPzLBcnpByCydskBJcT1gct L/DS9jNlXuSJlA8L49KSqlX50/tYsEzggocr7ABfB55nYPR2C8c4B4qnU3bf0Zeudo m0hmtwaKWadLQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 45DFB180083 for ; Mon, 9 Jun 2025 19:25:52 +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=-2.8 required=5.0 tests=BAYES_00,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: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from fhigh-b4-smtp.messagingengine.com (fhigh-b4-smtp.messagingengine.com [202.12.124.155]) (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 ; Mon, 9 Jun 2025 19:25:52 +0000 (UTC) Received: from phl-compute-10.internal (phl-compute-10.phl.internal [10.202.2.50]) by mailfhigh.stl.internal (Postfix) with ESMTP id 216FC25401F3 for ; Mon, 9 Jun 2025 15:27:53 -0400 (EDT) Received: from phl-imap-02 ([10.202.2.81]) by phl-compute-10.internal (MEProxy); Mon, 09 Jun 2025 15:27:53 -0400 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=fm1; t=1749497272; x=1749583672; bh=YfhtDBWklOCd4z0hj6+Dq XkAZd7cSGmw0Wo8LNIzE98=; b=ipl12RvTrEVXi1eowrZW+eQVij2K2C3hyHD5x daRY5UDemuvf99wLLwIvjyWegMg0Ojtx+cgtdrBJfng6pVybvmgpyc91mVg+GFRf qi7YmbPirNr/T2J0iUAkNlnz+P2DWae3E0ZiFREsJsm0Tbe9PQ3vPz6N/RWKQB0U rVnUmzTJK3EEudfHPUN5Ofo+hXvt1e57YUDxh3+m94J7COv2dpJ3Dm/ncOjjenTn EZIM+F1C1HSAfBPe00zcunJRGO12ZvWSlwO+mU84mInRja21QOy9r1cip9uBXBv1 HOeqN86nnEHqHzjgG/FecqYXu6P1tIUDMjgTfaovcP8MBGkug== 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=fm1; t=1749497272; x=1749583672; bh=Y fhtDBWklOCd4z0hj6+DqXkAZd7cSGmw0Wo8LNIzE98=; b=PhDN63sDFHzO4wtl1 S6ka6b+50touVg0Aw7E779i8KjyLjya57h+5zTO50bRMK0AX1dK7iAZkFdzAEYQL BWQwCujDjj8EL1kTFFhZIDZnV7ixjjpHzzzPBacIlRsMjYa57Rvh/flZXZfdZcdT dL8PtkvaWAYFZsgOssB5uIaXabVqJsuuJCW71AvqEFdbQwN2uWBibaud0/P5EHhC A00Ocp83D7HZW1V2kBo6ugAueuBZAgjW0V7aNwreyzFTGoLfzkDSffQnHJX43XPQ zpDARUIrjY/bJbSL3mKiRqfppnkET7rLNdWSzPIgMiIH+m2C67nQi8WjLWuZAM7k cEz0A== X-ME-Sender: X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeeffedrtddugdeljeduucetufdoteggodetrfdotf fvucfrrhhofhhilhgvmecuhfgrshhtofgrihhlpdggtfgfnhhsuhgsshgtrhhisggvpdfu rfetoffkrfgpnffqhgenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnh htshculddquddttddmnecujfgurhepofggfffhvffkjghfufgtgfesthejredtredttden ucfhrhhomhepfdfnrghrrhihucfirghrfhhivghlugdfuceolhgrrhhrhiesghgrrhhfih gvlhguthgvtghhrdgtohhmqeenucggtffrrghtthgvrhhnpedtvdfhteeiueeuieffledt kefglefhveefvefgjedvueeggeevtdfggedujeeltdenucffohhmrghinhepkhhothhlih hnlhgrnhhgrdhorhhgpdhgihhthhhusgdrtghomhenucevlhhushhtvghrufhiiigvpedt necurfgrrhgrmhepmhgrihhlfhhrohhmpehlrghrrhihsehgrghrfhhivghlughtvggthh drtghomhdpnhgspghrtghpthhtohepuddpmhhouggvpehsmhhtphhouhhtpdhrtghpthht ohepihhnthgvrhhnrghlsheslhhishhtshdrphhhphdrnhgvth X-ME-Proxy: Feedback-ID: i8414410d:Fastmail Received: by mailuser.phl.internal (Postfix, from userid 501) id A282D700062; Mon, 9 Jun 2025 15:27:52 -0400 (EDT) X-Mailer: MessagingEngine.com Webmail Interface Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 X-ThreadId: Tddf95baf0bdb99dd Date: Mon, 09 Jun 2025 14:27:32 -0500 To: "php internals" Message-ID: In-Reply-To: References: Subject: Re: [PHP-DEV] Feature Discussion | Content-Type: text/plain Content-Transfer-Encoding: 7bit From: larry@garfieldtech.com ("Larry Garfield") On Mon, Jun 9, 2025, at 12:34 PM, Dmitry Derepko wrote: > Hello, Internals! > > I've implemented an alpha implementation of the Extension Functions in > PHP. > Basically, it's a syntax sugar of the imperative call of the function > with the passing the object as a first argument, but anyway. > > Here is how it looks like in Kotlin: https://kotlinlang.org/docs/extensions.html > > In PHP it might looks like so: > > function stdClass.getName() { > return $this->name; > } > > $obj = new stdClass; > $obj->name = 'Dmitrii'; > echo $obj->getName(); // prints Dmitrii > > so desugared version is: > > function getName(stdClass $obj) { > return $obj->name; > } > > $obj = new stdClass; > $obj->name = 'Dmitrii'; > echo getName($obj); // prints Dmitrii > > Quite simple improvement, but a really convenient way to "attach" > behaviour and extend vendor code to make it more readable. > > Functions register as usual, using `use` keyword. So there are no > surprises when you call a function which does not exist, only an > implicit way to specify the function. > > "Attaching" the function with the name of an existing member function > raises an exception. > But you may juggle different functions from different namespaces: > > namespace A; > > use function aa; // from global namespace > OR > use function B\aa; // from B namespace > OR > use function B\aa as Baa; // from B namespace with alias > > `use function` construction may be enlarged with the "extension" > keyword: > use function getTime; // regular function > use extension function getTime; // extension function, unable to call > without correct receiver > > As in Kotlin, it should access only public members. > > class A { private $name; } > > function A.getName() { > return $this->name; // raises an exception because private and > protected members aren't available > } > > Supporting types DNF is under question, but I'd leave them as future scope. > > function ((A&B)|null).smth() { ... } > but it could be resolved to > function smth((A&B)|null $obj) { ... } > > The dot as a delimiter is also under the question, here are a few options: > > function stdClass::getName(); > function stdClass->getName(); > function stdClass.getName(); > function ::stdClass getName(); > function stdClass<-getName(); > function getName of stdClass(); > > etc. > > I've tried to implement the feature through attaching a function to the > class functions scope, which is wrong and does not follow the > requirements. It has memory leaks. > https://github.com/php/php-src/compare/master...xepozz:php-src:extension-functions?expand=1#diff-1dd36b02e5025ec3a5a546f8e41374ee4fc18c411bce447dd1dc2952329ccbe6R25 > > I also thought about adding this feature as a custom attribute > behaviour, but it's a way difficult: > > #[Extension("stdClass")] > function hasName(): bool { > return $this->name; > } > > I can try to implement the de-sugared version to make it work correctly. > > --- > > WDYT guys? 1. I love extension functions as a concept. Easily my favorite part of Kotlin, coming from PHP. So, +1 in the abstract. 2. Please link to a PR of your actual implementation. In context it looks like your branch comparison link is to the version you said didn't work, so it's not that helpful. 3. The biggest question that has come up in the past (Ilija and I have discussed it at length) is, naturally, autoloading. How if at all do you address that? 4. The other big question was determining when to match a given "method" call to an extension function, when the type of a variable is not always known at compile time. How did you address this? --Larry Garfield