Pre-RFC proposal: opt-in implicit interfaces / structural typing

php.internals

Spencer Malone

298 days ago
Hey all! Long time browser, first time emailer. I wanted to start a pre-RFC discussion on the proposal of opt-in implicit interfaces / structural typing / "golang style interfaces". I have an early mostly working POC at https://github.com/php/php-src/compare/master...SpencerMalone:php-src:implicit, and was curious if this was an idea that could get any traction as an RFC? For those who don't want to open the link, it's essentially: ``` implicit interface Drawable { public function draw(): void; } class Circle { public function draw(): void { } } assert((new Circle()) instanceof Drawable); // This would be true ``` There is some previous art around here in the form of... https://wiki.php.net/rfc/protocol_type_hinting - I couldn't find any discussion on this other than reading that the author withdrew all their RFCs for unrelated reasons I think? Some of the core ideas here I think are still relevant, although in my implementation I chose to use syntax that adds a keyword onto the interface rather than at the call sites because I wanted to provide the ability for interface definers to keep their interfaces as explicit if they wanted, as well as... Well honestly, adding angle brackets around objects feels like a syntax misstep since that pattern is very associated with generics in my mind. I could be convinced of this though if that's the syntax people prefer. https://externals.io/message/115554#115603 - Convinced me that others may want this as well, although again that proposed syntax ended up stepping on the toes of attributes. I do think this does a good job of explaining _why_ I want this. - Spencer

John Bafford

298 days ago
Hi Spencer,
> On Nov 7, 2025, at 12:05, Spencer Malone <malone.spencer@gmail.com> wrote: > > Hey all! Long time browser, first time emailer. I wanted to start a pre-RFC discussion on the proposal of opt-in implicit interfaces / structural typing / "golang style interfaces". > > I have an early mostly working POC at https://github.com/php/php-src/compare/master...SpencerMalone:php-src:implicit, and was curious if this was an idea that could get any traction as an RFC? > > For those who don't want to open the link, it's essentially: > > ``` > implicit interface Drawable { > public function draw(): void; > } > > class Circle { > public function draw(): void { > } > } > > assert((new Circle()) instanceof Drawable); // This would be true > ``` > > There is some previous art around here in the form of... > > https://wiki.php.net/rfc/protocol_type_hinting - I couldn't find any discussion on this other than reading that the author withdrew all their RFCs for unrelated reasons I think? Some of the core ideas here I think are still relevant, although in my implementation I chose to use syntax that adds a keyword onto the interface rather than at the call sites because I wanted to provide the ability for interface definers to keep their interfaces as explicit if they wanted, as well as... Well honestly, adding angle brackets around objects feels like a syntax misstep since that pattern is very associated with generics in my mind. I could be convinced of this though if that's the syntax people prefer. > > https://externals.io/message/115554#115603 - Convinced me that others may want this as well, although again that proposed syntax ended up stepping on the toes of attributes. I do think this does a good job of explaining _why_ I want this. > > - Spencer
I think this is an interesting idea, but personally, I would prefer to see classes always explicitly declare their interfaces. An interface is a contract, and implementing the interface is agreeing to conform to the contract. Just because a class has the shape of an interface doesn't mean it follows the contract. implicit would probably be most useful for very simple interfaces, but those are exactly the kind of interfaces that could be misinterpreted. For example, this poorly designed class: ``` class DeckOfCards { public ?Card $mostRecentlyDrawnCard; public function draw() : void { ... } } ``` should not be instanceof the Drawable interface, because it doesn't conform to the contract, even though it structurally matches. Perhaps slightly less torturously, ``` class Canvas { public array $contents; //list<Drawable> private CanvasInternalRepresentation $internalRepresentation public function draw() : void { ... update the canvas's internal representation ... } public function data($imageType) : string { .. } // return binary blob of $imageType data from the internal representation } ``` This Canvas takes a list of Drawables and uses a draw() method to update its internal representation. But it itself is not Drawable (let's say, because Canvas can't be nested for whatever reason). In real code, there's probably a lot of things that structurally would conform to some sort of simple Cache/Cacheable or Request/Response interface but for reasons wouldn't obey whatever implied contract those interfaces would demand. In my opinion, a better solution would be to allow for extensions that allow declaring interface conformances. e.g. ``` extension Circle implements Drawable { // We have now explicitly declared that Circle is a Drawable and conforms to its contract. // This extension body is empty because Circle already has a conforming draw method, // but if it didn't, we could add one here and it would be treated as part of the Circle class. } ``` Of course, this would be a much more involved proposal than implicit interfaces, which does have elegance in its simplicity. -John

Spencer Malone

298 days ago
The extension idea is interesting! I hadn't thought of that. Autoloading might be a PITA with that, I'm trying to think of how you would manage that? On the subject of "mistaken typing" or implicit interfaces being met by classes that don't actually do the desired behavior, I think you're right it's a risk, my counterargument is typescript and golang both have implicit interfaces in some form (well typescript calls them structural typing), but I don't think in either language I've ever really run into a case where that risk becomes a reality. I'm hesitant to declare this because I'm not sure this is the best way to frame this from a PR perspective, but in many respects this proposal is viewable as a syntax shortcut to duck typing, which is already pretty common across PHP. While there are definitely drawbacks to duck typing, it's still a very useful tool IMO (and becomes very cumbersome if you need to duck type a larger set of functions). On Fri, Nov 7, 2025 at 9:51 AM John Bafford <jbafford@zort.net> wrote:

Larry Garfield

298 days ago
On Fri, Nov 7, 2025, at 12:23 PM, Spencer Malone wrote:
> The extension idea is interesting! I hadn't thought of that. > Autoloading might be a PITA with that, I'm trying to think of how you > would manage that? > > On the subject of "mistaken typing" or implicit interfaces being met by > classes that don't actually do the desired behavior, I think you're > right it's a risk, my counterargument is typescript and golang both > have implicit interfaces in some form (well typescript calls them > structural typing), but I don't think in either language I've ever > really run into a case where that risk becomes a reality. I'm hesitant > to declare this because I'm not sure this is the best way to frame this > from a PR perspective, but in many respects this proposal is viewable > as a syntax shortcut to duck typing, which is already pretty common > across PHP. While there are definitely drawbacks to duck typing, it's > still a very useful tool IMO (and becomes very cumbersome if you need > to duck type a larger set of functions).
(Please do not top post.) Languages with implicit interfaces like Go also go (no pun intended) "all in" on it. All methods on an object are defined externally, possibly not even in the same file. The whole standard lib was built on that model from the ground up. That's not the case in PHP. I've been pondering extension functions for some time, and have kicked the idea around with John before. (I would do it as functions, a la Kotlin, not as a secondary class, like Rust.) Autoloading and efficiently determining that you need to call an extension are the key stumbling blocks (though my stance on autoloading at this point is "we've got opcache enabled 100% of the time, we have things like FrankenPHP, just front-load the file and move on with your life). I could see implicit interfaces working in conjunction with extension functions, if we can figure those out. I'm not sure if they offer much value without them, given the state of PHP. It would basically mean you have to opt-out of a type rather than opt-in, as now. That could get... weird. --Larry Garfield

John Bafford

298 days ago
> On Nov 7, 2025, at 13:54, Larry Garfield <larry@garfieldtech.com> wrote: > > Languages with implicit interfaces like Go also go (no pun intended) "all in" on it. All methods on an object are defined externally, possibly not even in the same file. The whole standard lib was built on that model from the ground up. That's not the case in PHP. > > I've been pondering extension functions for some time, and have kicked the idea around with John before. (I would do it as functions, a la Kotlin, not as a secondary class, like Rust.) Autoloading and efficiently determining that you need to call an extension are the key stumbling blocks (though my stance on autoloading at this point is "we've got opcache enabled 100% of the time, we have things like FrankenPHP, just front-load the file and move on with your life). > > I could see implicit interfaces working in conjunction with extension functions, if we can figure those out. I'm not sure if they offer much value without them, given the state of PHP. It would basically mean you have to opt-out of a type rather than opt-in, as now. That could get... weird. > > --Larry Garfield
Opting out would both be weird, and from a practical standpoint, is unreasonable to ask of userspace developers. For correctness, every potential implicit interface match would have to be explicitly declared as non-comforming in order to be correct. It's both very noisy and does not scale. From a library perspective, it's impossible: a library class can't possibly declare all the possible does_not_implement conformances because the entire universe of interfaces that might overlap its shape can't be known. A pathological example might look something like: ``` class DeckOfCards implements Shuffleable, \Deck\Drawable // We don't actually import any of these, but we have to explicitly deny them because otherwise it will cause problems in end-user code does_not_implement \FooGraphics\Drawable, \BarGraphics\Drawable, \ZodGraphics\Drawable, \GameEngine\TieInterface, \BarGameEngine\DeckDrawable, \Vehicle\Drawable {...} ``` There's also a problem point with any code that calls `get_declared_classes()` and filters by `instanceof SomeInterface`. Now all that code is potentially broken (if implicit conformances don't meet contractual requirements). Would we need to add an implicit/explicit/either option to `is_a()` to make userspace able to correctly reason about types? Are there other cases where implicit conformance needs to be treated differently than explicit conformance? Importing a package that has an implicit type would basically have to come with a warning, "don't make your classes look like this interface or else things might break". And I'm sure someone would also find a creative way to abuse the trivial case, `implicit interface AnyObject {}`. -John

Larry Garfield

297 days ago
On Fri, Nov 7, 2025, at 1:28 PM, John Bafford wrote:
>> I could see implicit interfaces working in conjunction with extension functions, if we can figure those out. I'm not sure if they offer much value without them, given the state of PHP. It would basically mean you have to opt-out of a type rather than opt-in, as now. That could get... weird. >> >> --Larry Garfield > > Opting out would both be weird, and from a practical standpoint, is > unreasonable to ask of userspace developers. > > For correctness, every potential implicit interface match would have to > be explicitly declared as non-comforming in order to be correct. It's > both very noisy and does not scale. From a library perspective, it's > impossible: a library class can't possibly declare all the possible > does_not_implement conformances because the entire universe of > interfaces that might overlap its shape can't be known.
OK, maybe opt-out wasn't the right term, since I agree marking "I do NOT implement interface X" is kinda ridiculous. :-) Implicit interfaces wouldn't really be opt-out, but they're more than the opt-in that we currently have. --Larry Garfield

Rowan Tommins [IMSoP]

298 days ago
On 07/11/2025 17:05, Spencer Malone wrote:
> Hey all! Long time browser, first time emailer. I wanted to start a > pre-RFC discussion on the proposal of opt-in implicit interfaces / > structural typing / "golang style interfaces".
Hi and welcome :) In general, I prefer explicit code over implicit, so wouldn't be an enthusiastic supporter of this (although I might abstain rather than voting against). However, I though it worth mentioning that PHP has one implicit interface already: any class with an __toString() method magically implements Stringable. I've never really understood why, or why that interface even exists; but at least having a general concept of "implicit interface" would make it less magical.
-- Rowan Tommins [IMSoP]

Larry Garfield

297 days ago
On Fri, Nov 7, 2025, at 1:19 PM, Rowan Tommins [IMSoP] wrote:
> On 07/11/2025 17:05, Spencer Malone wrote: >> Hey all! Long time browser, first time emailer. I wanted to start a >> pre-RFC discussion on the proposal of opt-in implicit interfaces / >> structural typing / "golang style interfaces". > > > Hi and welcome :) > > In general, I prefer explicit code over implicit, so wouldn't be an > enthusiastic supporter of this (although I might abstain rather than > voting against). > > However, I though it worth mentioning that PHP has one implicit > interface already: any class with an __toString() method magically > implements Stringable. I've never really understood why, or why that > interface even exists; but at least having a general concept of > "implicit interface" would make it less magical.
__toString() is *old*. It may go all the way back to 5.0, but it was there at least as of 5.2. The problem is that there was no way to type against it. You could type `string`, but not `string or object that implements string`. When we got union types in 8.0, that was tempting but not yet possible. So Stringable was added basically for typing against `string|Stringable`. And it was made implicitly added because there were a bajillion __toString() functions in the wild already that shouldn't have to be modified before you could type against them. </history lesson> --Larry Garfield

Rowan Tommins [IMSoP]

297 days ago
On 7 November 2025 21:34:56 GMT, Larry Garfield <larry@garfieldtech.com> wrote:
>__toString() is *old*. It may go all the way back to 5.0, but it was there at least as of 5.2. The problem is that there was no way to type against it.
I know all that. I've just never understood why you would *want* to type against it. The only thing you can do with that knowledge is turn the object into a string, so why not just ask for a string in the first place? But I apologise for drifting off topic; the relevant point is that it works exactly how the proposed implicit interfaces would, so whatever reasoning was used for it might serve a useful example for other potential uses. If nothing else, being able to say "it's a built-in implicit interface" would be less confusing than explaining it as a magical special case. Who knows, maybe there are people wishing they could type against other magic methods, who could do so by declaring their own implicit interfaces. As I say, I'm personally much more interested in highly explicit code, so am more likely to go completely the opposite direction: use an *empty interface*, where the only thing you're doing is "tagging" a class as intended for a particular purpose. But, I know there are people who like "duck typing", and implicit interfaces or structural typing give a way to formalise that, so I can see value for those people. Rowan Tommins [IMSoP]

hakre

297 days ago
On Friday 07 November 2025 20:19:37 (+01:00), Rowan Tommins [IMSoP] wrote:
> On 07/11/2025 17:05, Spencer Malone wrote: > > Hey all! Long time browser, first time emailer. I wanted to start > > a pre-RFC discussion on the proposal of opt-in implicit interfaces > > / structural typing / "golang style interfaces". > > > Hi and welcome :) > > In general, I prefer explicit code over implicit, so wouldn't be an > enthusiastic supporter of this [...] > However, I though it worth mentioning that PHP has one implicit > interface already: any class with an __toString() method magically > implements Stringable. I've never really understood why, or why that > interface even exists; but at least having a general concept of > "implicit interface" would make it less magical. >
Good thinking, inspiring, in my eyes that __toString() magically, both in the method as in the inverse of the direction towards the Stringable interface definition, is rather stemming from the needs of some object oriented PHP framework authors IIRC. In terms of it as a historical artifact to learn from or take something away from it for similar implicit interfaces, I'd like to add that I find it rather explicit (like a parental advisory sticker on a CD jewel case) due to the inability of PHP to resolve such (internal) interfaces at place of use (the implements keyword) while there was no problem with the implicit magic method. This is perhaps why I as well have very little understanding what that single instance has brought to the table finally, it appears to stand in the way much too often for me on the observable surface. The only hope I nevertheless had with it is, that it would have helped to solve the problem of __toString() fatalling in the engine. So Stringable/object{__toString(): string} is certainly a very special example in its very own not as a _magical_ special case, but it is a fundamental special case every programming language that has "output" has to solve. Not having any output would solve the turing problem by its higher math, but such machines also have effectively undefined themselves and by that any programming language. That problem is inherit with output and it is the formatting of output, even if it is only dumping the content of the process registers. What we can't see does not exist. So every programming language at that point must re-invent itself somehow, as it certainly does not compile recursively, but rather has the concepts of compiletime, runtime, whatnottime and the recursion, direct or indirect, often a property stemming from these, it (recursion) is always a thing, too.
> 12. Recursion is the root of computation since it trades description > for time.
Unless strict normative rules are given by the design to prevent that (e.g. to make things maintainable safely in the longtime), but every extension will add, not remove, so longtime or eternity often only remains the idea, bugs and regressions the reality along the road of success. If we take output then for a given, we have to solve this problem at its core, otherwise we can't built upon it. This is where yours mentioned implicit Stringable interface comes into play. For those Framework authors it certainly was enough to given them a way to go from a method pointer ("__toString") or a memory pointer (the string), from a memory object to a PHP language classlike object, e.g. by declaring the type with a function or method parameter (in) or return value (out). Regardless if only compiled once to its executable form, ErrorException (PHP 5.1), or as in PHP userland compiled, interpreted and re-compiled until it gets there: What do you do when that error is to be handled and the error handler is in userland (or at compile time) and includes (or has) more code to be evaluated while handling the error still on the engine level? See in PHP thanks to the scripts, we have this inane recursion problem. This is a bit like heart surgery on the open heart, the pump is still pumping and one wrong cut everything can bleed out. The doctor may survive, so in terms of software, there is not much need to really know what we are doing here and can defer some work until later. In real life this causes grave injuries if not deaths. How successful such an implicit Stringable interface (PHP 8.0 IIRC) was in this sense I do not know. I do not even know if the introduction of Stringable has been discussed afterwards in that regard. But I'm derailing, what I wanted to prolong on is, that Stringable in itself as an "implicit" interface as you have rightfully mentioned, is certainly touching many more parts than the suggested "implicit interface" by Spence Malone would need to care about. So what are other examples? Tagging (with empty interfaces) often comes to my mind then, but all interfaces, empty or not, also have the probem of the loading order in PHP. At place of use, where they would shine, requires not only to explicitly define them (good!), but also to explicitly have them loaded. So there perhaps is still a problem in PHP core to get compilation and execution into a quite better order in the sense of their arrangement to make use of the language with the runtime. The framework authors didn't had that problem, because they voided it by separating each classlike into its own file (object) and then let having PHP/SPL resolve the loading order (a.k.a. class autoloading). At the core we can still see that problem as separating each classlike into an object (file) of its own requires preproduction (those are actually deployables, so we're not programming any longer but have longtime stopped and already are building the artifacts.) If there is something to strengthen, then it is the ability of PHP developers to program in PHP, not to waste time thinking about that they need to learn some special technique how to arrange their code later on so that it fullfills some dependency, theirs dependency managers and then call that the art of doing module management. This is all longtime afterards and therefore always inherits any previous flaw at the languages core every PHP framework author intends to not address at all. So in my opinion your raise of the dynamics from explicit to implicit and vice-versa can bring interesting aspects to the table regardless which style each of us is currently thinking to prefer, but as you rightfully raised, PHP core developers should also confront themselves on how to pick up the loose ends we still have while new authors make useful suggestions to extend language-level intermediates. From PHP history, IMHO (and regardless how I use or not use them), Traits are a key milestone in this way of PHP design done right for me. The proposal aligns well for me in that regard. Stringable on the other hand, a bad example of everything but making problems in core and use of the PHP language itself more visible; and that is often a good thing, see the output problem above I mentioned, my current understanding is that we always inherit that on computer systems and when programming machines. So as long as we can get output, we can benefit. Naturally we benefit from explicit output much more then from implicit one - until otherwise.
> 4. Every program is a part of some other program and rarely fits.
my 2 cents, - h.k.

Pedro Magalhães

297 days ago
On Fri, Nov 7, 2025 at 5:05 PM Spencer Malone <malone.spencer@gmail.com> wrote:
> Hey all! Long time browser, first time emailer. I wanted to start a > pre-RFC discussion on the proposal of opt-in implicit interfaces / > structural typing / "golang style interfaces". > > I have an early mostly working POC at > https://github.com/php/php-src/compare/master...SpencerMalone:php-src:implicit, > and was curious if this was an idea that could get any traction as an RFC? > > For those who don't want to open the link, it's essentially: > > ``` > implicit interface Drawable { > public function draw(): void; > } > > class Circle { > public function draw(): void { > } > } > > assert((new Circle()) instanceof Drawable); // This would be true > ``` > > There is some previous art around here in the form of... > > https://wiki.php.net/rfc/protocol_type_hinting - I couldn't find any > discussion on this other than reading that the author withdrew all their > RFCs for unrelated reasons I think? Some of the core ideas here I think are > still relevant, although in my implementation I chose to use syntax that > adds a keyword onto the interface rather than at the call sites because I > wanted to provide the ability for interface definers to keep their > interfaces as explicit if they wanted, as well as... Well honestly, adding > angle brackets around objects feels like a syntax misstep since that > pattern is very associated with generics in my mind. I could be convinced > of this though if that's the syntax people prefer. > > https://externals.io/message/115554#115603 - Convinced me that others may > want this as well, although again that proposed syntax ended up stepping on > the toes of attributes. I do think this does a good job of explaining _why_ > I want this. > > - Spencer > >
Hi Spencer, A few years ago I had a go at it. In case it is useful in any way, you can check it in https://github.com/php/php-src/compare/master...pmmaga:php-src:implicit-interfaces Regards, Pedro

hakre

297 days ago
On Friday 07 November 2025 18:05:03 (+01:00), Spencer Malone wrote:
> Hey all! Long time browser, first time emailer. I wanted to start a
pre-RFC
> discussion on the proposal of opt-in implicit interfaces / structural > typing / "golang style interfaces". > > I have an early mostly working POC at >
https://github.com/php/php-src/compare/master...SpencerMalone:php-src:implicit,
> and was curious if this was an idea that could get any traction as an
RFC?
> > For those who don't want to open the link, it's essentially: > > ``` > implicit interface Drawable { > public function draw(): void; > }
If you may be that kind and allow an implicit interface to use a trait...
> > class Circle { > public function draw(): void { > } > } >
... then the class could use a trait as well which is then closing the distance to traits that are not able to full-fill interfaces and their using classes, so the classes can borrow from the traits and both then somewhat profit from implicit interfaces. The "innerface" naturally could only use the non-body parts (abstract parts) of the trait methods / properties, so the copy direction towards the interface is a reduction to the interface the trait implies. The trait then can be used in the implicit interface definition to do both at once (defining an interface and providing an implementation).
> > assert((new Circle()) instanceof Drawable); // This would be true > ``` >
and if I may add, truly great - regardless with or without such trait reuse. if it then could work en-par with function defintions (define after use which currently "explicit" interfaces are not capable of) this would make it a perfect and modern addition to the language in my very own eyes.
> There is some previous art around here in the form of... > [...] > > - Spencer >
- h.k.