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

Reply via email to