Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127645 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 6AB9D1A00BC for ; Wed, 11 Jun 2025 08:31:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1749630561; bh=q5SWPJsS5aW4MDuTc3cqqGPGYdO6qD4BeD4BpPGalrk=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=oOcFMAWQhn2g9mlUaSfKZvdw8GDjaEmr3KC6xXgJDFjlsvWDptWSfPOFYxaAINB8S XWrxmrKZpAa17e7kXV3t3iICYlW8CNq38JugCaB6nPCSEWhl6gl+9bzFyZB4jCCp/K cwVZ1Y8fIKugpbGKjlUZIidIVL+wzV5Rf5KQjP4Qx7SDmCZi9Rb1ToA0dHizQMWxoe vKi9uZQ4R5ordnWRSvFamugXRFMwVHbIh6ZkPTCUx7XtKcZ+9OrfzEi2Go6zpVXbK3 lSKQ50dKnyuJGVfiLJy7m5DUV/4x3RF0Dpr9stT9o5IpYPoCQb/OXwVtzKTEzwFHO9 8py/ooLT3cwvg== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id EA0121801EF for ; Wed, 11 Jun 2025 08:29:17 +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.7 required=5.0 tests=BAYES_05,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL, SPF_HELO_NONE,SPF_PASS 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 mail-ej1-f53.google.com (mail-ej1-f53.google.com [209.85.218.53]) (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, 11 Jun 2025 08:29:13 +0000 (UTC) Received: by mail-ej1-f53.google.com with SMTP id a640c23a62f3a-ad88d77314bso1188023766b.1 for ; Wed, 11 Jun 2025 01:31:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1749630673; x=1750235473; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=fDW9mTwDxHxZlgfYO24H13neBoat3WfWTETzk/+q6wM=; b=mclls6HW67ZX7LrW0mACascdrjhgskaj7jYp42nwevY/pni0Z3RhADF3VdQJuNUKJp W4lL5bRLklKIokZbGHKqX+vhfm2fUIczx8mJMXei6UA5icQUVuHEqwhDKOQT3yTFe4ii NsCtYTXHtabWbeEi3E1vPHm+qdl3rXt4ZiKdD4r5nr4ZULCxa0VcjMIlkPniUIIF54+N JQByKwicX8XDDUAa+XYAYS39qsTth8obfffUUPtRbLHrnjkWwZk+MxTVjLuWJ3pGlBxj ixOJlxCOUt7I4AcAhZDlpOKOuKRVqjKIYGS8BRE+xdPZgmSVYlZD6ADWhh0Qr3IBVIDe vtiw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749630673; x=1750235473; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=fDW9mTwDxHxZlgfYO24H13neBoat3WfWTETzk/+q6wM=; b=qt+xhdTeAjTdTeweNxcVOWuSMI52hqLqQNdYMMDg0Rt/kVzuivZjeIc+qRNePu1YMm pJvHpm5aUbUQ4rWHdGKlE8l/tkKylSNbrMzrTQ9/oL0xYUA+R3L+p07//SVr00jVT6eH EKvCLvRdjhFRKieMUi7ZqoE9ZwL/hQOIx93erhAU6jVxm7iug9Vs3WxefX5+oF/jZwyn YDoFrO9dNSifRZdRNBzOA8B92IiG+5GlwiQqVlGGKnlloMYezvUUOGQ+XB8EYB8Vc5+g KOTD+gVKMJuXB9CV/29qWp/NZLMxExujuI0a/ol0vpfnrQuaI/aHR+5Hfuqe75g87eHi S1+g== X-Gm-Message-State: AOJu0Yww30064XeFO3Eoac60WDbhnZkNfskuuqWp15Hc5DVI0PDrEkpe +Ynm6bTm4IJDKJfwIpfmJ9egCtDsTXjZN3k7+mOCNkiRkiIxyQ2GGrqSJVjOQIyPDqTDHT7aSeZ FqVIb9NbQLBWxMg2qZMX4GPd2GzJ/QWrFZrNq X-Gm-Gg: ASbGnctL/k1/hcsxfrvaIL2Rx3YHIpyg/aAEtwv8v9++gLSIzltT0r7sSb862+oNUF6 XbScuSSI98fXtxAJlrSCbp7Wcn8UU//nH5W19ELRyXQ+HmBr6FcysYClXswdyTnEnMrH5n21Il1 LMWmMMa+eCrUMfKdDt5PM+H5D/uQw6ka3UP7Gwam9hNlPT3r7MIUgUVkM72t5DqrUvQlIUuHGMH MlDz8IAAs8q X-Google-Smtp-Source: AGHT+IGvWUqRTiNnvoHlC5QSzx59X+UmF9gGkrj+C/Mj6v9j37BzLQ1X+Lh6sR997NlZwtkKtssyUC1pRxG/iRU/3wc= X-Received: by 2002:a17:906:4796:b0:ad5:78ca:2126 with SMTP id a640c23a62f3a-ade89855d42mr219337166b.59.1749630672792; Wed, 11 Jun 2025 01:31:12 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Wed, 11 Jun 2025 10:30:47 +0200 X-Gm-Features: AX0GCFsE2CtLNAySRPmHsIUnW04xfqcpM43EmelqT8uLULAM-2o9IlZleWF5564 Message-ID: Subject: Re: [PHP-DEV] Feature Discussion | To: Dmitry Derepko Cc: PHP internals Content-Type: multipart/alternative; boundary="000000000000bd76e1063747a26f" From: kjarli@gmail.com (Lynn) --000000000000bd76e1063747a26f Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Mon, Jun 9, 2025 at 7:36=E2=80=AFPM Dmitry Derepko w= rote: > Hello, Internals! > > I've implemented an alpha implementation of the Extension Functions in PH= P. > Basically, it's a syntax sugar of the imperative call of the function wit= h > 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 =3D new stdClass; > $obj->name =3D 'Dmitrii'; > echo $obj->getName(); // prints Dmitrii > > so desugared version is: > > function getName(stdClass $obj) { > return $obj->name; > } > > $obj =3D new stdClass; > $obj->name =3D '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 scop= e. > > 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 requirement= s. > It has memory leaks. > > https://github.com/php/php-src/compare/master...xepozz:php-src:extension-= functions?expand=3D1#diff-1dd36b02e5025ec3a5a546f8e41374ee4fc18c411bce447dd= 1dc2952329ccbe6R25 > > 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? > > -- > Best regards, > Dmitrii Derepko. > @xepozz > It's an interesting concept, and reminds me of how Lua handles this with `string.len(theVariable)` vs `theVariable:len()`. The way you define the function with a dot or colon, determines whether or not you have `self` (`$this`) available or not. Feature wise, what would be use-cases to have this? DTOs in libraries, but perhaps also value objects could add a custom factory function. I don't want my database models/entities to know about the DTO, but the (vendor) DTO can't know about the database model/entity either. This scenario would be to define a runtime static function to create the object, and perhaps even a runtime function the other way around to populate my entity based on the DTO. I could keep this code separate from both classes and merely have it act as a glue. I don't know if this is a good practice though. What about interfaces and abstract classes, would these functions fulfill contracts in any way? It sounds like runtime traits in that regard, but makes static code analysis a lot more complex; not only for tools, but also for humans to parse and understand. Would runtime traits be an alternate solution to function extension to deal with autoloading? --000000000000bd76e1063747a26f Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


On Mon, Jun 9, 2025 at 7:36=E2=80=AFPM Dmitry Derepko <xepozzd@gmail.com> wrote:
Hello, Interna= ls!

I've implemented an alpha implementation o= f the Extension Functions in PHP.
Basically, it's a syntax su= gar 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:=C2=A0https://kotlinlang.org/docs/extensions.html

In PHP it might looks like so:

f= unction stdClass.getName() {=C2=A0
=C2=A0 =C2=A0 return $this->= ;name;
}

$obj =3D new stdClass;
$obj->name =3D 'Dmitrii';
echo $obj->getName(); /= / prints Dmitrii

so desugared version is:

function getName(stdClass $obj) {=C2=A0
=C2= =A0 =C2=A0 return $obj->name;
}

$obj = =3D new stdClass;
$obj->name =3D 'Dmitrii';
= echo getName($obj); // prints Dmitrii

Quite = simple improvement, but a really convenient way to "attach" behav= iour and extend vendor=C2=A0code to make it more readable.

Functions register as usual, using `use` keyword. So there are no = surprises=C2=A0when you call a function which does not exist, only an impli= cit way to specify the function.

"Attaching&q= uot; the function with the name of an existing member function=C2=A0raises = an exception.
But you may juggle different functions from differe= nt namespaces:

namespace A;

use function aa; // from global namespace
OR
use fun= ction 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 fun= ction getTime; // extension function, unable to call without correct receiv= er

As in Kotlin, it should access only public memb= ers.

class A { private $name; }

function A.getName() {=C2=A0
=C2=A0 =C2=A0 return $this-&g= t;name; // raises an exception because private and protected members aren&#= 39;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) { ... }
<= div>
The dot as a delimiter is also under the question,= here are a few options:

function stdClass::getNam= e();
function stdClass->getName();
function stdClass= .getName();
function ::stdClass getName();
function std= Class<-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=3D1#diff-1dd= 36b02e5025ec3a5a546f8e41374ee4fc18c411bce447dd1dc2952329ccbe6R25
<= div>
I also thought about adding this feature as a custom att= ribute behaviour, but it's a way difficult:

#[= Extension("stdClass")]
function hasName(): bool {
=C2=A0 = =C2=A0 return $this->name;
}

I can try to im= plement the de-sugared version to make it work correctly.

---

WDYT guys?

--
Best rega= rds,
Dmitrii Derepko.
=

It's an interesting concept, and= reminds me of how Lua handles this with `string.len(theVariable)` vs `theV= ariable:len()`. The way you define the function with a dot or colon, determ= ines whether or not you have `self` (`$this`) available or not. Feature wis= e, what would be use-cases to have this?

DTOs in l= ibraries, but perhaps also value objects could add a custom factory functio= n. I don't want my database models/entities to know about the DTO, but = the (vendor) DTO can't know about the database model/entity either. Thi= s scenario would be to define a runtime static function to create the objec= t, and perhaps even a runtime function the other way around to populate my = entity based on the DTO. I could keep this code separate from both classes = and merely have it act as a glue. I don't know if this is a good practi= ce though.

What about interfaces and abstract clas= ses, would these functions fulfill contracts in any way? It sounds like run= time traits in that regard, but makes static code analysis a lot more compl= ex; not only for tools, but also for humans to parse and understand. Would = runtime traits be an alternate solution to function extension to deal with = autoloading?

--000000000000bd76e1063747a26f--