Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127634 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 DE3571A00BC for ; Mon, 9 Jun 2025 17:34:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1749490345; bh=r6wU9p0xRTW/+eetQtJ1tffSYCymaQnauZIkfmKg+oA=; h=From:Date:Subject:To:From; b=W23A0S6hYlhfwrCN5uwTe9t8n389vff/mxOwfGnAcMPl7giKn+m8HRv46SnxKbA97 wGUen3HrugUT7NpPD8VB11kp4r/6qV7zIsfDOzNR6yQZGrLULr8vkPxosJkFeaXrOZ zhRfqgSKZdrOybtjZniYWx4c3Qu8CbildAGld4doXGjZBs25KBEOg0K0UJo8p4zzk5 3ZPYb5WaI+LFQ48viqDBxsT7ZoDXmzVaClw/5s8EpycWxDo1eREe5ERspjFZjBz49r nSmEfzDBCTpjELl8X2+2o7i0UDLRE74qnb5LfNexvC1mEmv1XQb4w1TphQioU4iZBF Sbm4DP22RvIbw== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 28D1B18006C for ; Mon, 9 Jun 2025 17:32:25 +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.1 required=5.0 tests=BAYES_00,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-pl1-f180.google.com (mail-pl1-f180.google.com [209.85.214.180]) (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 17:32:24 +0000 (UTC) Received: by mail-pl1-f180.google.com with SMTP id d9443c01a7336-234d366e5f2so51941145ad.1 for ; Mon, 09 Jun 2025 10:34:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1749490465; x=1750095265; darn=lists.php.net; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=nlnpUcw1ZG0tosLQfquuLArz6TxVDxoAHKM0OT9AiMo=; b=WfnZiybla2cNpZ56CFvSMYbWEFmDp1PdeU7KkNwEWc81lhS/jp4Whrze9Tb32XKMOT xaE/N7trjIdPOmWymKeAJPq6yUYXtHLLjiX3PJFKLUy4aXvBHJ8e8W9EvhMUwfYCrPqi /dgODGaMyHksEiCxn54Wz/o0jiQeyJV8oD7SAuWVxRVRyLQ86zkYmfBkZhwh/T+rK6RT SCYQzG3lHQJq2ocQxgp6g7qm4Fa0lobdxAQR/orok7Zz7WydtYNnZk8bzwxi20IROddV 54h3PsRq+9gStgpSGQd75bOBFVA3mmLeB3vjs6yD/7G37cCFVNuYIjOchDn+TB/Nyfga 5hyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1749490465; x=1750095265; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=nlnpUcw1ZG0tosLQfquuLArz6TxVDxoAHKM0OT9AiMo=; b=WBizW6q6GIODg21RPUmQMIRW59ZS4ijwmLzUsobOLZVNFWxKJ4aEgRqUJ3gktAJS+o XEFedMooG1Akp5zxFulGsJaDgH8non8et9wygVPkjHMYB9gtGG4MmRunYr/D4gyYxLx6 +IsvqfpJ14HFmNpI2Tfiu+y9m3H5wTC7S7OzllH/T2/EbxG6rF5QaE+Mqa3sjdp3TIeD 0aQbTLVXm6W90RPoUNc+uYB+Ws+RHGBf2/wBz4NvsyosiYDQ/njTYlN1Jgnr9BRemKzg QQTjD1NW1LrDxlCg223o7plEKxJ2MjFbMErEDmJXDWnQ8YK7WbgF8HxNFA6FWEIIMW1O FPWQ== X-Gm-Message-State: AOJu0YxNfzrPWFoqM+7rBLex4+/dDsz2aHU++9AFJcD5wjBDBXgqhbUZ 3nPBItBx4z2HCA+E5gdHfxAlhtRuhMaKD9PY8MytH1y8pvLdDKnMOCbGXnZHev6O4o/MNxSnpav YWpHvzgjsQPW0cOS7d1LS8U8rBgq7eDtxLowvZZw= X-Gm-Gg: ASbGnctLN4RlL1fb9UBCduZR3w1vz2D1rywZW9Q1FQa4oNbADvOvzTfgxo2wn42uDdh Cg2PNsnfIxD8hAW7mfqacBFsvIALKeSGqpYu4fQcUfWMb92tTJMMz3lbrsj7NCdax8TTeu2wDCH Gpg6W1aInCWZCbWTlF7GGrX34v06gKKghlRkciOzPNK6ys X-Google-Smtp-Source: AGHT+IHbqN0LKClbNclxTlb299z1ZbKTHcqf90TCbE5voM0xkxufVbexodIqGE++lt3bdXOZexEkbBf6ufNw3gpH9lQ= X-Received: by 2002:a17:90b:1e01:b0:311:c1ec:7d11 with SMTP id 98e67ed59e1d1-31347407377mr22504477a91.18.1749490454538; Mon, 09 Jun 2025 10:34:14 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 Date: Mon, 9 Jun 2025 20:34:03 +0300 X-Gm-Features: AX0GCFvMJpHHzApi7GoQuoXoKP7Xd7WeuEFEswMYl-U9I0NJzMoJzFqsg8XI9C4 Message-ID: Subject: [PHP-DEV] Feature Discussion | To: PHP internals Content-Type: multipart/alternative; boundary="00000000000014aeb7063726fdba" From: xepozzd@gmail.com (Dmitry Derepko) --00000000000014aeb7063726fdba Content-Type: text/plain; charset="UTF-8" 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? -- Best regards, Dmitrii Derepko. @xepozz --00000000000014aeb7063726fdba Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hello, Internals!

I've i= mplemented an alpha implementation of the Extension Functions in PHP.
=
Basically, it's a syntax sugar of the imperative call of the funct= ion with the passing the object as a first argument, but anyway.
=
Here is how it looks like in Kotlin:=C2=A0https://kotlinlang.org/docs/extension= s.html

In PHP it might looks like so:

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

$obj =3D ne= w 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 'Dmit= rii';
echo getName($obj); // prints Dmitrii

Quite simple improvement, but a really convenient way to &quo= t;attach" behaviour and extend vendor=C2=A0code to make it more readab= le.

Functions register as usual, using `use` keywo= rd. So there are no surprises=C2=A0when 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 f= unction=C2=A0raises an exception.
But you may juggle different fu= nctions 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 wi= thout correct receiver

As in Kotlin, it should acc= ess only public members.

class A { private $name; = }

function A.getName() {=C2=A0
=C2=A0 = =C2=A0 return $this->name; // raises an exception because private and pr= otected members aren't available
}

S= upporting types DNF is under question, but I'd leave them as future sco= pe.

function ((A&B)|null).smth() { ... }<= /div>
but it could be resolved to
function smth((A&B)|nul= l $obj) { ... }

The dot as a delimiter is al= so under the question, here are a few options:

fun= ction stdClass::getName();
function stdClass->getName();
=
function stdClass.getName();
function ::stdClass getName();<= /div>
function stdClass<-getName();
function getName of st= dClass();

etc.

I've t= ried to implement the feature through attaching a function to the class fun= ctions scope, which is wrong and does not follow the requirements. It has m= emory leaks.
https://github.com/php/= php-src/compare/master...xepozz:php-src:extension-functions?expand=3D1#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 {
=C2= =A0 =C2=A0 return $this->name;
}

I can try t= o implement the de-sugared version to make it work correctly.
---

WDYT guys?

--
Be= st regards,
Dmitrii Derepko.
--00000000000014aeb7063726fdba--