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