Class Extension

php.internals

Holly Schilling

55 days ago
Following the discussion in Discord yesterday, I started working on the feasibility of implementing class extensions. I believe I’ve proven out the concept and viability of it in a 2-phase approach using an extension syntax similar to that of Swift. Before I go into much more detail, what is the general interest in adding this to the language?

Michael Morris

55 days ago
On Wed, Jul 8, 2026 at 1:08 AM Holly Schilling < holly.a.schilling@outlook.com> wrote:
> Following the discussion in Discord yesterday, I started working on the > feasibility of implementing class extensions. I believe I’ve proven out the > concept and viability of it in a 2-phase approach using an extension syntax > similar to that of Swift. > > Before I go into much more detail, what is the general interest in adding > this to the language? >
We have inheritance (extends keyword, parent / child classes) We have traits (an implementation of multiple classes taken from Ruby) Looking at Swift's extension syntax I fail to see anything it adds not covered by the above.

Alex Pierstoval Rock

55 days ago
Le 08/07/2026 à 14:07, Michael Morris a écrit :
> > On Wed, Jul 8, 2026 at 1:08 AM Holly Schilling > <holly.a.schilling@outlook.com> wrote: > > Following the discussion in Discord yesterday, I started working > on the feasibility of implementing class extensions. I believe > I’ve proven out the concept and viability of it in a 2-phase > approach using an extension syntax similar to that of Swift. > > Before I go into much more detail, what is the general interest in > adding this to the language? > > > We have inheritance (extends keyword, parent / child classes) > We have traits (an implementation of multiple classes taken from Ruby) > > Looking at Swift's extension syntax I fail to see anything it adds not > covered by the above.
I think that Swift's extension system goes the other way round: the class itself doesn't declare its extensions, but they are declared from userland. It's similar to how Rust's traits system can allow extending other types (though there's kind of a safeguard to avoid extending nonsense) , you can check it there: https://doc.rust-lang.org/book/ch10-02-traits.html PHP with inheritance goes this way: - Declare Class A   - Class A internally defines which class it inherits from   - Class A internally defines which traits it uses - Everything is now defined for Class A and its structure is finite as of the end of its declaration With an "extension" system, it might go this way: - Declare Class A   - Class A internally defines which class it inherits from   - Class A internally defines which traits it uses - Class A is defined, but extensions can update it somehow. - An extension is later declared for Class A: it can customize the inner structure of Class A, like new traits or implemented interfaces - Maybe (depending on how the RFC goes) other extensions can be declared later, in order to customize other things. Conceptually, I'm not against it, but just like Michael said: I can't find use-cases that would be better than what we actually have. Extending a class "from the outside", whatever the way to do it, implies that a new safeguard has to be added. We could use the "final" keyword, but "final" is not at all the same thing as "no extension allowed": a class could be declared as not-final while still disallowing extensibility from the outside. That's the case when you have a non-final class but some of its methods are "final". Disallowing extensibility would require an entirely new keyword for that, and it adds another burden to code maintainers, especially if extensibility is opt-out. So, IMO, such RFC should contain this: - Thorough examples of the actual problems it solves for package maintainers and application developers - Clear views on how extensibility impacts the current ecosystem (I guess it shouldn't have any impact on Composer and the autoloading system, because if it's similar to Swift, an extension should just behave like a class in terms of autoload, but I might have not thought about it completely) - Extensibility must be either opt-in by design, or opt-out by design, but the decision must be made **after** having analysed the impact on the ecosystem. I can't imagine extensibility being opt-out and people starting to wildly override frameworks core classes. (yeah, I'm talking about some Laravel nerds that tend to bend and break fast (pun intended)) - Extension points must be defined on both the extender and the extended structures. Like, for example, making sure it's impossible to extend something that's "final" by default, but making a non-final structure being non-extensible should also be possible. That's part of the concept of encapsulation in general, but it also respects the Open/Close principle (from SOLID) and several other programming practices that make sure we don't do bullcrap in all codebases. And trust me, PHP is already full of bullcrap, so adding more isn't a good idea at all. PHP hasn't become "more and more strict" over time for nothing... and still, that strictness is opt-in (by the eyes of PHP, not from framework maintainers, which can be seriously opinionated). Hope this helps :)

John Bafford

55 days ago
> On Jul 8, 2026, at 14:07, Michael Morris <tendoaki@gmail.com> wrote: > > > On Wed, Jul 8, 2026 at 1:08 AM Holly Schilling <holly.a.schilling@outlook.com> wrote: > Following the discussion in Discord yesterday, I started working on the feasibility of implementing class extensions. I believe I’ve proven out the concept and viability of it in a 2-phase approach using an extension syntax similar to that of Swift. > > Before I go into much more detail, what is the general interest in adding this to the language? > > We have inheritance (extends keyword, parent / child classes) > We have traits (an implementation of multiple classes taken from Ruby) > > Looking at Swift's extension syntax I fail to see anything it adds not covered by the above.
Traits and extensions approach a similar problem from the opposite direction. Where a class uses a trait to add functionality, instead, an extension declares that it has added functionality to a class (or interface). Besides allowing one avenue for default implementations for interface methods, extensions would also allow for polyfills for class methods and properties that can be hooked. For example, PHP 8.5 adds the (sadly not documented yet!) `Dom\Element::getElementsByClassName()` and `Dom\Element::insertAdjacentHTML()` methods. If PHP 8.4 already had extensions, then extensions would have made it possible for a userspace library to provide an implementation for those methods on PHP 8.4. In the hypothetical case where we have method calls on scalar types, extensions could also be used as a means of adding methods to those types (either in userspace, or internally or in php extensions). -John

Rowan Tommins [IMSoP]

55 days ago
On 8 July 2026 06:08:42 BST, Holly Schilling <holly.a.schilling@outlook.com> wrote:
>Following the discussion in Discord yesterday, I started working on the feasibility of implementing class extensions. I believe I’ve proven out the concept and viability of it in a 2-phase approach using an extension syntax similar to that of Swift. > >Before I go into much more detail, what is the general interest in adding this to the language?
Hi Holly, Can you give a brief summary of what the feature is, and how it would apply to PHP, for those of us who aren't familiar with Swift, and haven't seen the Discord discussion? Thanks, Rowan Tommins [IMSoP]

Nick

55 days ago
Hey Holly, On 08.07.26 12:08, Holly Schilling wrote:
> Following the discussion in Discord yesterday, I started working on > the feasibility of implementing class extensions. I believe I’ve > proven out the concept and viability of it in a 2-phase approach using > an extension syntax similar to that of Swift. > > Before I go into much more detail, what is the general interest in > adding this to the language?
I like how extensions in Swift allow to organise code! If we ignore implementation details and ownership it's pretty similar to what we have with traits. In PHP, what would be the benefit of extensions over traits? One thing that comes to my mind is that traits can be used by whatever chooses to add it (back to ownership). Good one, but what else? If ownership is the only concern then maybe a small addition to traits could achieve the same? ``` trait Foo {     for Baz\Bar::class;  // inverse of "use" to limit usage } class Bar {     use Foo; } ``` Brings conflict resolution for free; and I think it would be cheaper than yet another class-like thing. Maybe also feels more php-ish since we already have the trait concept?
> Before I go into much more detail, what is the general interest in > adding this to the language?
Interested in some sort of solution!
-- Cheers Nick

Pierre Joye

53 days ago
hello, On Wed, Jul 8, 2026, 12:11 PM Holly Schilling <holly.a.schilling@outlook.com> wrote:
> Following the discussion in Discord yesterday, I started working on the > feasibility of implementing class extensions. >
For one, I read only internals and github. I keep seeing "we talked on discord" but there is no reference whatsoever to a php.net's discord anywhere i searched for. so maybe I would suggest to be a tat bit more descriptive first here too? I believe I’ve proven out the concept and viability of it in a 2-phase
> approach using an extension syntax similar to that of Swift. > > Before I go into much more detail, what is the general interest in adding > this to the language? >
reading the thread, I feel both swift and you aim to the same goal, some parts use different approaches but as I am not a user of swift, it may be nicer if you could work together, if desired? best,
-- Pierre @pierrejoye

Holly Schilling

53 days ago
For one, I read only internals and github. I keep seeing "we talked on discord" but there is no reference whatsoever to a php.net<http://php.net/>'s discord anywhere i searched for. so maybe I would suggest to be a tat bit more descriptive first here too? Yes, this has not been as public of a discussion as it should be, so please allow me to share some more details here for you and the rest of the Internals community. There are currently 3 RFCs comprising 3 phases and feature sets to build for extensions. 1. General Class Extension Syntax: https://gist.github.com/hollyschilling/f590f3a1e488732eea5b0d8014702276 This RFC adds a basic extensions using this syntax: ``` extension \DateTimeImmutable { public function isWeekend(): bool { return in_array((int)$this->format('N'), [6, 7], true); } } var_dump((new DateTimeImmutable('2026-07-11'))->isWeekend()); // bool(true) ``` 2. Scalar Method Extensions: https://gist.github.com/hollyschilling/1d247189b8bf45fe044bfbe7fb07dcdb Allow extensions to be applied to scalars, like strings and ints. This has a lot of code changes to make this work and the blast radius is huge. That’s why it’s a separate component. ``` extension string { public function length(): int { return strlen($this); } } var_dump("hello"->length()); // int(5) ``` 3. Extension Visibility and Importing: https://gist.github.com/hollyschilling/dc97ea217302de2f4c9ad7d527aaa65b This adds rules and syntax to be able to import an extension without using `require_once` (or similar). First, the autoloader needs extensions to have a name, so we give them a name when declared. ``` // vendor/acme/dom-kit/src/traversal.php namespace Acme\DomKit; extension DomTraversal on \DOMElement { public function firstByClass(string $class): ?\DOMElement { /* ... */ } } ``` Then, as we want to use our declared extensions, we import them with a `use`, similar to normal classes. ``` use extension Acme\DomKit\DomTraversal; $el->firstByClass('hero'); // resolves here ``` I believe I’ve proven out the concept and viability of it in a 2-phase approach using an extension syntax similar to that of Swift. Before I go into much more detail, what is the general interest in adding this to the language? reading the thread, I feel both swift and you aim to the same goal, some parts use different approaches but as I am not a user of swift, it may be nicer if you could work together, if desired? Extension methods allow adding functions to existing classes. As a future consideration, they can also be used to add readonly virtual properties (No setter; No storage). This is purely syntactic sugar, but it can be extremely helpful making code easier to read and increasing code reuse. Think of it as a Trait that you apply to someone else’s code after the fact. It is also possible to apply an extension to an Interface. While it cannot be used to implement members of the interface, it can make all classes implementing that interface have useful methods. The other useful feature is that a class may implement its own version of a function and the function resolution will go to the class’s implementation as you might expect. I hope this helps. The 3 links go to the Gists for each phase and a link from each Gist goes to the implementation for each. I’m happy to answer any additional questions you or anyone else has as you read through the docs. Holly

Rowan Tommins [IMSoP]

53 days ago
On 10 July 2026 05:38:08 BST, Holly Schilling <holly.a.schilling@outlook.com> wrote:
> >There are currently 3 RFCs comprising 3 phases and feature sets to build for extensions.
Hi Holly, Thanks for sharing those links, that gives a better idea what we're actually discussing here. My initial reaction is that the anonymous/global extensions and named/scoped extensions feel more like competing versions of the same feature than natural phases that build on each other. For instance we might choose different conflict resolution behaviour if extensions are always scoped, but would hesitate to have two different behaviours if we implemented both types of extension. The named/scoped version feels like it fits better with how I see PHP applications structured - heavy reliance on autoloading, as little global bootstrapping code as possible. It also gives static analysers and IDEs a clearer signal where to look for the extension. For the anonymous version, does the target have to be defined for the extension to be loaded? If it isn't, will the autoloader be called? I'm struggling to picture how I'd use that version in practice. Thanks for your work on this so far, Rowan Tommins [IMSoP]

Alex Pierstoval Rock

53 days ago
Le 10/07/2026 à 06:38, Holly Schilling a écrit :
> >> >> For one, I read only internals and github.  I keep seeing "we talked >> on discord" but there is no reference whatsoever to a php.net >> <http://php.net/>'s discord anywhere i searched for. >> >> so maybe I would suggest to be a tat bit more descriptive first here too? > > Yes, this has not been as public of a discussion as it should be, so > please allow me to share some more details here for you and the rest > of the Internals community. > > There are currently 3 RFCs comprising 3 phases and feature sets to > build for extensions. > > 1. General Class Extension Syntax: > https://gist.github.com/hollyschilling/f590f3a1e488732eea5b0d8014702276 > This RFC adds a basic extensions using this syntax: > ``` > extension \DateTimeImmutable { > public function isWeekend():bool { > return in_array((int)$this->format('N'), [6,7],true); > } > } > var_dump((new DateTimeImmutable('2026-07-11'))->isWeekend());// bool(true) > ``` > > 2. Scalar Method Extensions: > https://gist.github.com/hollyschilling/1d247189b8bf45fe044bfbe7fb07dcdb > Allow extensions to be applied to scalars, like strings and ints. This > has a lot of code changes to make this work and the blast radius is > huge. That’s why it’s a separate component. > ``` > extension string { > public function length():int {return strlen($this); } > } > > var_dump("hello"->length());// int(5) > ``` > > 3. Extension Visibility and Importing: > https://gist.github.com/hollyschilling/dc97ea217302de2f4c9ad7d527aaa65b > This adds rules and syntax to be able to import an extension without > using `require_once` (or similar). > > First, the autoloader needs extensions to have a name, so we give them > a name when declared. > ``` > // vendor/acme/dom-kit/src/traversal.php > namespace Acme\DomKit; > > extension DomTraversal on \DOMElement { > public function firstByClass(string $class): ?\DOMElement {/* ... */ } > } > ``` > Then, as we want to use our declared extensions, we import them with a `use`, similar to normal classes. > ``` > use extension Acme\DomKit\DomTraversal; > > $el->firstByClass('hero');// resolves here > ``` > >> >> I believe I’ve proven out the concept and viability of it in a >> 2-phase approach using an extension syntax similar to that of Swift. >> >> Before I go into much more detail, what is the general interest >> in adding this to the language? >> >> >> reading the thread, I feel both swift and you aim to the same goal, >> some parts use different approaches but as I am not a user of swift, >> it may be nicer if you could work together,  if desired? >> > > Extension methods allow adding functions to existing classes. As a > future consideration, they can also be used to add readonly virtual > properties (No setter; No storage). This is purely syntactic sugar, > but it can be extremely helpful making code easier to read and > increasing code reuse. Think of it as a Trait that you apply to > someone else’s code after the fact. > > It is also possible to apply an extension to an Interface. While it > cannot be used to implement members of the interface, it can make all > classes implementing that interface have useful methods. > > The other useful feature is that a class may implement its own version > of a function and the function resolution will go to the class’s > implementation as you might expect. > > I hope this helps. The 3 links go to the Gists for each phase and a > link from each Gist goes to the implementation for each. > > I’m happy to answer any additional questions you or anyone else has as > you read through the docs. > > Holly
Thanks for all these links, I think the conversation should start from this thread from now on, to avoid referring to unknown mails or discord channels :) On the feature itself, I'm not knowledgeable enough on the engine impact, and the concept of Extensions could be a really great addition to PHP, however on the userland-side, things must be extra clear to avoid messing up with potential unidentified edge-cases when declaring or using an extension, and the actual impact on the ecosystem and usage. The RFC about scalar extensions can be deferred and is optional (though very good, but having built-in classes for scalars, but the first RFC and the visibility RFC should be merged and re-thought in order to fix the loading issue. Maybe you could chat with @azjezz about this part, because it's the kind of thing that could be shipped in his "PHP PSL" project (visible there: https://github.com/php-standard-library/php-standard-library) The two other RFCs imply extensions will need to be imported via manual require/require_once calls, therefore package maintainers declaring extensions will have to use the "autoload.files" composer option. It works, but it will add an overhead (wasting time in file load and wasting memory usage) on extensions that are declared but not used. If autoload was triggered when encountering "use extension", it would have the advantage of being explicit, but it's counter-intuitive: "use" statements shouldn't usually trigger anything and just serve as aliases for symbols referenced in the file, so it's inconsistent with current usage of the "use" keyword. And if package maintainers don't use "autoload.files" to avoid the potentially unnecessary overhead, this means that userland code would be forced to write "require_once 'path/to/extension.php'; " in their own files, which is not at all what anyone would expect, and defeats the purpose of having a dependencies manager like Composer in the first place, even though "it works". To me, fixing this loading process is the very first issue to investigate. All the rest of the RFCs seem good to me, though I'm probably not legitimate on judging it anyway Side-note 2cents: Apart from Swift and C#, there's also this concept Rust, with a syntax also allowing to add traits to an external structure, with just a bit more code:     // main.rs     trait StringExtension {         fn shout(&self) -> String;     }     impl StringExtension for String { // ➡️ "String" is a built-in Rust class/struct         fn shout(&self) -> String {             self.to_uppercase() + "!"         }     }     fn main() {         let s = String::from("hello");         println!("{}", s.shout()); // HELLO!     } However, there's another feature in Rust that's nice: importing traits at runtime (similar to the "use extension", but in PHP it would have to trigger autoload to work):     use std::fs::File;     use std::io::Write; // Trait import     fn main() {         let mut buffer = File::create("file.txt").unwrap();         buffer.write(b"File content").unwrap();     } Here, the "use std::io::Write;" line is mandatory. If you remove it, even though you have "use std::fs::File", the call to "buffer.write()" would throw an error  saying"no method named `write` found for struct `File` in the current scope", because the "std::io::Write;" crate has something like "impl Write for File  { ... }" which adds the methods from the "Write" trait to the "File" struct/class. Since Rust is compiled, "use" imports are resolved at compile-time everywhere (that's a different paradigm from "use" in PHP), but it's similar to how "use extension" could trigger autoload somehow, just my 2cents

Holly Schilling

53 days ago
Holly
> On Jul 10, 2026, at 10:20 AM, Alex Rock <pierstoval@gmail.com> wrote: > > Le 10/07/2026 à 06:38, Holly Schilling a écrit : >> >>> >>> For one, I read only internals and github. I keep seeing "we talked on discord" but there is no reference whatsoever to a php.net <http://php.net/>'s discord anywhere i searched for. >>> >>> so maybe I would suggest to be a tat bit more descriptive first here too? >> >> Yes, this has not been as public of a discussion as it should be, so please allow me to share some more details here for you and the rest of the Internals community. >> >> There are currently 3 RFCs comprising 3 phases and feature sets to build for extensions. >> >> 1. General Class Extension Syntax: https://gist.github.com/hollyschilling/f590f3a1e488732eea5b0d8014702276 >> This RFC adds a basic extensions using this syntax: >> ``` >> extension \DateTimeImmutable { >> public function isWeekend():bool { >> return in_array((int)$this->format('N'), [6,7],true); >> } >> } >> var_dump((new DateTimeImmutable('2026-07-11'))->isWeekend());// bool(true) >> ``` >> >> 2. Scalar Method Extensions: https://gist.github.com/hollyschilling/1d247189b8bf45fe044bfbe7fb07dcdb >> Allow extensions to be applied to scalars, like strings and ints. This has a lot of code changes to make this work and the blast radius is huge. That’s why it’s a separate component. >> ``` >> extension string { >> public function length():int {return strlen($this); } >> } >> >> var_dump("hello"->length());// int(5) >> ``` >> >> 3. Extension Visibility and Importing: https://gist.github.com/hollyschilling/dc97ea217302de2f4c9ad7d527aaa65b >> This adds rules and syntax to be able to import an extension without using `require_once` (or similar). >> >> First, the autoloader needs extensions to have a name, so we give them a name when declared. >> ``` >> // vendor/acme/dom-kit/src/traversal.php >> namespace Acme\DomKit; >> >> extension DomTraversal on \DOMElement { >> public function firstByClass(string $class): ?\DOMElement {/* ... */ } >> } >> ``` >> Then, as we want to use our declared extensions, we import them with a `use`, similar to normal classes. >> ``` >> use extension Acme\DomKit\DomTraversal; >> >> $el->firstByClass('hero');// resolves here >> ``` >> >>> >>> I believe I’ve proven out the concept and viability of it in a >>> 2-phase approach using an extension syntax similar to that of Swift. >>> >>> Before I go into much more detail, what is the general interest >>> in adding this to the language? >>> >>> >>> reading the thread, I feel both swift and you aim to the same goal, some parts use different approaches but as I am not a user of swift, it may be nicer if you could work together, if desired? >>> >> >> Extension methods allow adding functions to existing classes. As a future consideration, they can also be used to add readonly virtual properties (No setter; No storage). This is purely syntactic sugar, but it can be extremely helpful making code easier to read and increasing code reuse. Think of it as a Trait that you apply to someone else’s code after the fact. >> >> It is also possible to apply an extension to an Interface. While it cannot be used to implement members of the interface, it can make all classes implementing that interface have useful methods. >> >> The other useful feature is that a class may implement its own version of a function and the function resolution will go to the class’s implementation as you might expect. >> >> I hope this helps. The 3 links go to the Gists for each phase and a link from each Gist goes to the implementation for each. >> >> I’m happy to answer any additional questions you or anyone else has as you read through the docs. >> >> Holly > > > Thanks for all these links, I think the conversation should start from this thread from now on, to avoid referring to unknown mails or discord channels :) > > On the feature itself, I'm not knowledgeable enough on the engine impact, and the concept of Extensions could be a really great addition to PHP, however on the userland-side, things must be extra clear to avoid messing up with potential unidentified edge-cases when declaring or using an extension, and the actual impact on the ecosystem and usage.
There are lots of potential sharp edges here. I believe I’ve addressed them all in the code and RFC, but I’m definitely open to having more pointed out.
> > The RFC about scalar extensions can be deferred and is optional (though very good, but having built-in classes for scalars, but the first RFC and the visibility RFC should be merged and re-thought in order to fix the loading issue. Maybe you could chat with @azjezz about this part, because it's the kind of thing that could be shipped in his "PHP PSL" project (visible there: https://github.com/php-standard-library/php-standard-library)
I’ve never looked much at PHP PSL. I’ll do that. As far as scalar methods goes, I hate my implementation. I’m not suggesting someone else could do better, only that the compromises necessary to implement it weren’t worth it. After having implemented it and seen what it demands, I think the right choice is Autoboxing with extensions on the box rather than ever allowing `$this` to not be an object. This is a heated topic lately and I don’t pretend to be an expert.
> > The two other RFCs imply extensions will need to be imported via manual require/require_once calls, therefore package maintainers declaring extensions will have to use the "autoload.files" composer option. It works, but it will add an overhead (wasting time in file load and wasting memory usage) on extensions that are declared but not used.
With the inclusion of the Visibility RFC, explicit `require` or `require_once` is not necessary. The `use extension` syntax replaces it. The consequence of this is that extension methods are only visible in scopes that explicitly want them. This prevents the from polluting globally.
> If autoload was triggered when encountering "use extension", it would have the advantage of being explicit, but it's counter-intuitive: "use" statements shouldn't usually trigger anything and just serve as aliases for symbols referenced in the file, so it's inconsistent with current usage of the "use" keyword. > And if package maintainers don't use "autoload.files" to avoid the potentially unnecessary overhead, this means that userland code would be forced to write "require_once 'path/to/extension.php'; " in their own files, which is not at all what anyone would expect, and defeats the purpose of having a dependencies manager like Composer in the first place, even though "it works".
The new `use extension` syntax is a slightly different purpose than developers are used to with an ordinary `use` declaration, but it follows the same spirit. There is a thing not in this file that needs to be brought into scope.

Holly Schilling

53 days ago
.
> > As far as scalar methods goes, I hate my implementation. I’m not suggesting someone else could do better, only that the compromises necessary to implement it weren’t worth it. After having implemented it and seen what it demands, I think the right choice is Autoboxing with extensions on the box rather than ever allowing `$this` to not be an object. > > This is a heated topic lately and I don’t pretend to be an expert. >
Typing this email this morning gave me real hesitation. If I can’t support my own implementation, no one else should either. I immediately set out to build a better version that I could put my full weight behind. Autoboxing turned out to be a non-starter as well. The code just didn’t fit the model of the code PHP uses. That led me to a diffident path. C# 14 recently added a new extensions syntax to allow for properties and more. When I saw it, I knew it was the right solution. ``` extension string $str { function length() { return strlen($str); } } var_dump(“test”->length()); // int(4) ``` By declaring the parameter inline with the extension declaration, we eliminate the downcast of `$this`, the fake accessibility constraints, and all the sharp edges the old way introduces. I immediately rewrote all 3 RFCs to use this new syntax, as well as the code to implement it. Anyone who has been following these discussions, please skim the updated docs for changes.

Alex Pierstoval Rock

52 days ago
Le 10/07/2026 à 20:33, Holly Schilling a écrit :
> . >> As far as scalar methods goes, I hate my implementation. I’m not suggesting someone else could do better, only that the compromises necessary to implement it weren’t worth it. After having implemented it and seen what it demands, I think the right choice is Autoboxing with extensions on the box rather than ever allowing `$this` to not be an object. >> >> This is a heated topic lately and I don’t pretend to be an expert. >> > Typing this email this morning gave me real hesitation. If I can’t support my own implementation, no one else should either. I immediately set out to build a better version that I could put my full weight behind. > > Autoboxing turned out to be a non-starter as well. The code just didn’t fit the model of the code PHP uses. That led me to a diffident path. > > C# 14 recently added a new extensions syntax to allow for properties and more. When I saw it, I knew it was the right solution. > > ``` > extension string $str { > function length() { > return strlen($str); > } > } > > var_dump(“test”->length()); // int(4) > ``` > > By declaring the parameter inline with the extension declaration, we eliminate the downcast of `$this`, the fake accessibility constraints, and all the sharp edges the old way introduces. > > I immediately rewrote all 3 RFCs to use this new syntax, as well as the code to implement it. > > Anyone who has been following these discussions, please skim the updated docs for changes.
Thanks for these changes, they bring better stuff to the table indeed :) Still my first thought stands: I think scalar extensions are dispensible, and having scalars behave "like objects" is quite new to PHP workarounds do exist for that: there are quite a few PHP packages to manipulate strings, utf8 handling, graphemes or codepoints, etc., that could be used "as string extensions" if such RFC came to become real in the language. I think it needs more time, investigation on packages, whether to decide on adding "standard extensions" to start uniformizing string-related functions (something that's been asked for a long time). There's also a few libraries that could benefit from it, like nette/utils, with their String manipulation API (check it out: https://doc.nette.org/en/utils/strings). For short: great idea, but needs more benchmarks, packages and ecosystem analyses, etc.

Holly Schilling

52 days ago
Holly
> On Jul 11, 2026, at 4:12 AM, Alex Pierstoval Rock <pierstoval@gmail.com> wrote: > > Le 10/07/2026 à 20:33, Holly Schilling a écrit : >> . >>> As far as scalar methods goes, I hate my implementation. I’m not suggesting someone else could do better, only that the compromises necessary to implement it weren’t worth it. After having implemented it and seen what it demands, I think the right choice is Autoboxing with extensions on the box rather than ever allowing `$this` to not be an object. >>> >>> This is a heated topic lately and I don’t pretend to be an expert. >>> >> Typing this email this morning gave me real hesitation. If I can’t support my own implementation, no one else should either. I immediately set out to build a better version that I could put my full weight behind. >> >> Autoboxing turned out to be a non-starter as well. The code just didn’t fit the model of the code PHP uses. That led me to a diffident path. >> >> C# 14 recently added a new extensions syntax to allow for properties and more. When I saw it, I knew it was the right solution. >> >> ``` >> extension string $str { >> function length() { >> return strlen($str); >> } >> } >> >> var_dump(“test”->length()); // int(4) >> ``` >> >> By declaring the parameter inline with the extension declaration, we eliminate the downcast of `$this`, the fake accessibility constraints, and all the sharp edges the old way introduces. >> >> I immediately rewrote all 3 RFCs to use this new syntax, as well as the code to implement it. >> >> Anyone who has been following these discussions, please skim the updated docs for changes. > > > Thanks for these changes, they bring better stuff to the table indeed :) > > Still my first thought stands: I think scalar extensions are dispensible, and having scalars behave "like objects" is quite new to PHP
I have some people saying that scalar methods are a hard requirement. I have others like yourself saying they are a separate issue and should be treated as such. I believe I have struck a good balance here for two reasons. First, they *ARE* a separate RFC that can receive separate voting and discussion. Second, even if they are adopted, there is no mechanism for global pollution. A user only has methods available that they deliberately bring into scope.
> workarounds do exist for that: there are quite a few PHP packages to manipulate strings, utf8 handling, graphemes or codepoints, etc., that could be used "as string extensions" if such RFC came to become real in the language. I think it needs more time, investigation on packages, whether to decide on adding "standard extensions" to start uniformizing string-related functions (something that's been asked for a long time).
This is also something I’m going to push back on. There is nothing that “more time and investigation” will change about how existing libraries will be affected by this. This is an additional tool for them and they will need to find what works best for them once it lands. What Stringy feels works best for them doesn’t have to be what Nette believes works best. That’s the strength of the PHP ecosystem. We create the tools and let people find the best way to use them. The tool is not prescriptive.

Alex Pierstoval Rock

52 days ago
Le 10/07/2026 à 17:41, Holly Schilling a écrit :
>> The two other RFCs imply extensions will need to be imported via manual require/require_once calls, therefore package maintainers declaring extensions will have to use the "autoload.files" composer option. It works, but it will add an overhead (wasting time in file load and wasting memory usage) on extensions that are declared but not used. > With the inclusion of the Visibility RFC, explicit `require` or `require_once` is not necessary. The `use extension` syntax replaces it. The consequence of this is that extension methods are only visible in scopes that explicitly want them. This prevents the from polluting globally. > >> If autoload was triggered when encountering "use extension", it would have the advantage of being explicit, but it's counter-intuitive: "use" statements shouldn't usually trigger anything and just serve as aliases for symbols referenced in the file, so it's inconsistent with current usage of the "use" keyword. >> And if package maintainers don't use "autoload.files" to avoid the potentially unnecessary overhead, this means that userland code would be forced to write "require_once 'path/to/extension.php'; " in their own files, which is not at all what anyone would expect, and defeats the purpose of having a dependencies manager like Composer in the first place, even though "it works". > The new `use extension` syntax is a slightly different purpose than developers are used to with an ordinary `use` declaration, but it follows the same spirit. There is a thing not in this file that needs to be brought into scope.
Again: the "use extension" isn't bad per se, it's just that the "use" keyword *doesn't do anything*, and never has. It's just an alias. Adding "use extension", though being "slightly different" that just having "use", still means that it's gonna be on top of the PHP file, many IDEs already fold these statements for readability. Something else I am thinking about changing the keyword and applying the extension to a class directly at the same time: <?php // vendor/foo/bar/src/Extension/RequestExtension.php namespace Foo\Bar\Extension; use Symfony\Component\HttpFoundation\IpUtils; use Symfony\Component\HttpFoundation\Request; extension RequestExtension for Request {     public function isBehindProxy(string $ip): bool     {         return IpUtils::checkIp($this->server->get('REMOTE_ADDR', ''), [$ip]);     } } ?> <?php // index.php use Symfony\Component\HttpFoundation\Request; use Foo\Bar\Extension\RequestExtension; extend Request with RequestExtension; $request = Request::createFromGlobals(); var_dump($request->isBehindProxy()); // works We can imagine that the "extends A with B;" keyword is redundant, but it brings an advantage in the fact that an extension declares the "minimum" class it's capable of extending, but it could also declare no class at all (thus removing the "for ..." part of the extension's declaration). In such case, the *user* determines which "maximum" class it will actually extend, and if there is a mismatch it could throw an error: <?php class StandaloneClass {} interface SomeInterface {} abstract class SomeClass {} class SomeImpl implements SomeInterface {} class SomeChild extends SomeClass {} class LongerTree extends SomeClass {} abstract class TransientClass implements SomeInterface {} class LastImpl extends TransientClass {} // Extends nothing in particular, behaves just like an "external trait". extension StandaloneExtension { /* ... */ } // Can be used only if the extended class has the structure in its tree. extension InterfaceExtension for SomeInterface { /* ... */ } extension ClassExtension for SomeClass { /* ... */ } extend StandaloneClass with StandaloneExtension; // Works extend SomeImpl with InterfaceExtension; // Works extend SomeChild with ClassExtension; // Works extend LastImpl with InterfaceExtension; // Works: LastImpl -> TransientClass -> SomeInterface extend StandaloneClass with InterfaceExtension; // Error: "StandaloneClass" does not extend nor implement "SomeInterface" extend StandaloneClass with ClassExtension; // Error: "StandaloneClass" does not extend nor implement "SomeClass" The "extend ... with ..." is clearer on what it does, userland has a bit more control, but extension defines its rules. WDYT?

Holly Schilling

52 days ago
> Again: the "use extension" isn't bad per se, it's just that the "use" keyword *doesn't do anything*, and never has. It's just an alias. Adding "use extension", though being "slightly different" that just having "use", still means that it's gonna be on top of the PHP file, many IDEs already fold these statements for readability.
What is wrong with folding these statements? I think you’re overthinking what will likely end up being the common use case in an IDE. The user is going to type something like `$m->extensionMethod()`, it’s going to get a red squiggly line under it, they’ll click the suggestion to add the `use extension` at the top, and they will move on. When the PR goes up for review, the reviewer is going to see it, check if they used the right extension, and flag it if they used the wrong one.
> Something else I am thinking about changing the keyword and applying the extension to a class directly at the same time:
This sounds harsh, so my apologies in advance. I reject this functionality. 1. Since extensions are scoped to a single file (which is typically a single class) and not global, they don’t encounter many situations where this would even be a consideration. 2. Extensions only occupy the path that would otherwise throw a method not found exception. They are the method of last resort for a match, so they never override functionality. Calling an extension method on any class that didn’t have the extension applied always results in an exception being thrown. 3. The functionality you are describing already exists in Traits. Extensions are complementary to Traits, not a replacement. For the situation you are describing, an Extension is not the right tool and trying to make it fit that use case would be damaging to the overall simplicity of them.