Hi,
I would like to propose a new syntax that let's you implement an interface
only if it exists.
class MyClass extends ?OptionalInterface {}
If the OptionalInterface
exists, the class implements it. If no such
interface can be found, it is ignored.
https://wiki.php.net/rfc/optional-interfaces
The need to declare compatibility with a certain interface without requiring
the interface itself comes up when you're writing code that should work with
various versions of other libraries or PHP itself and when a library
provides interoperability with some other library or a PHP extension, but
does not require it.
Although this would mainly be used by library developers, the consumers of
those libraries would also benefit as this syntax would clearly communicate
the intention of optional interoperability. IDEs and static analysis tools
could also have easier time with ?OptionalInterface
instead of class
definitions inside an if (interface_exists(.)) { . } else {.}
.
I've not entirely sure about the naming as an "optional interface" feels
like an oxymoron. I've also considered names like non-required interfaces,
conditional interfaces, optional/conditional implementation or even soft
interfaces/soft implementation. Please let me know if the naming itself
bothers you.
Previous discussion: https://externals.io/message/125967
WIP implementation: https://github.com/php/php-src/pull/17288
Looking forward to your feedback and suggestions!
Juris
Hi,
I would like to propose a new syntax that let’s you implement an
interface only if it exists.
class MyClass extends ?OptionalInterface {}
If the
OptionalInterface
exists, the class implements it. If no such
interface can be found, it is ignored.https://wiki.php.net/rfc/optional-interfaces
The need to declare compatibility with a certain interface without
requiring the interface itself comes up when you’re writing code that
should work with various versions of other libraries or PHP itself and
when a library provides interoperability with some other library or a
PHP extension, but does not require it.Although this would mainly be used by library developers, the consumers
of those libraries would also benefit as this syntax would clearly
communicate the intention of optional interoperability. IDEs and static
analysis tools could also have easier time with?OptionalInterface
instead of class definitions inside anif (interface_exists(…)) { … } else {…}
.I’ve not entirely sure about the naming as an “optional interface”
feels like an oxymoron. I’ve also considered names like non-required
interfaces, conditional interfaces, optional/conditional implementation
or even soft interfaces/soft implementation. Please let me know if the
naming itself bothers you.Previous discussion: https://externals.io/message/125967
WIP implementation: https://github.com/php/php-src/pull/17288Looking forward to your feedback and suggestions!
Juris
Going by the design/RFC, this seems reasonable to me, and I can see the use for it. My only concern would be if the implementation makes anything more complex for the engine or compiler that could cause future optimization problems, but I'm not qualified to answer that.
It may help the RFC to include some non-trivial real-world-ish examples, in addition to the A and B stuff. (You can probably steal some from the projects linked to.)
--Larry Garfield
Hi,
I would like to propose a new syntax that let’s you implement an interface
only if it exists.
class MyClass extends ?OptionalInterface {}
If the
OptionalInterface
exists, the class implements it. If no such
interface can be found, it is ignored.https://wiki.php.net/rfc/optional-interfaces
Hi Juris,
I think the proposal looks good but I some some points to be discussed:
From what I understand, the autoloading mechanism would be triggered for
the optional interface, but it would not be an error if it would not
succeed. I think we should explicitly spell this out in the RFC, to make it
clear that autoloading will be attempted.
If an interface was not available at class declaration time, the function
class_implements (on both class and object) will return false going
forward, as if the class definition did not contain the interface at all?
Similarly, if at runtime the object would be passed to a function/method
that has the parameter type of that interface, would it fail saying that
the object is not an instance of that interface?
Note: autoloading is not triggered when just using an interface/class name
in a parameter type or as return type.
I think completely erasing the interface sounds good, I just think it
should be explicitly spelled out in the RFC, and all the implications that
follows.
If a second class will implement the same optional interface, will it
trigger autoloading again? I think it should.
If in the meantime, between defining two such classes, the interface would
be defined (using a hack or some sort), would it cause problems? I think
that given the current existing workarounds this is a valid concern.
To avoid problems, it would be wise to completely erase the interface from
first class definition when it was not available, even if the second class
might have it.
-- Alex
Hi,
I would like to propose a new syntax that let’s you implement an interface only if it exists.
class MyClass extends ?OptionalInterface {}
If the
OptionalInterface
exists, the class implements it. If no such interface can be found, it is ignored.
https://wiki.php.net/rfc/optional-interfaces https://wiki.php.net/rfc/optional-interfaces
The need to declare compatibility with a certain interface without requiring the interface itself comes up when you’re writing code that should work with various versions of other libraries or PHP itself and when a library provides interoperability with some other library or a PHP extension, but does not require it.
Although this would mainly be used by library developers, the consumers of those libraries would also benefit as this syntax would clearly communicate the intention of optional interoperability. IDEs and static analysis tools could also have easier time with
?OptionalInterface
instead of class definitions inside anif (interface_exists(…)) { … } else {…}
.
I’ve not entirely sure about the naming as an “optional interface” feels like an oxymoron. I’ve also considered names like non-required interfaces, conditional interfaces, optional/conditional implementation or even soft interfaces/soft implementation. Please let me know if the naming itself bothers you.
Previous discussion: https://externals.io/message/125967 https://externals.io/message/125967
WIP implementation: https://github.com/php/php-src/pull/17288 https://github.com/php/php-src/pull/17288
Looking forward to your feedback and suggestions!
Juris
Hi
I've left some remarks on your PR to help you further with the opcache stuff.
This should fix the tests and answer some questions.
I also left a concern regarding the caching, and in particular I'm thinking about the inheritance cache. I'll copy it here:
Suppose this scenario:
- You do a request and an optional interface is not available, this class is cached without that interface.
- A future request comes in, and reuses the cache, but since this may be via a different code path it's possible that the interface is now available. Yet because the class was cached, this won't implement the interface now even though it's available at this point in time. I'm not entirely sure if this is a real problem but it should be checked. I also don't know what assumptions in the engine this may break.
Kind regards
Niels
Hey,
Thanks for all the feedback!
The RFC is now expanded to explicitly address the concerns on how the
optional interfaces interact with type checks, eval'd interfaces, opcache,
autoloading, reflection and the Override attribute. Did Did I miss
something? Please let me know if anything is still unclear or requires other
improvement.
https://wiki.php.net/rfc/optional-interfaces
I've also polished the implementation and added tests for all the concerns
mentioned above.
https://github.com/php/php-src/pull/17288
However the implementation is not final as I was not able (yet) to solve how
my implementation interacts with the Stringable interface.
BR,
Juris
Hi Juris
Thanks for your efforts.
I've reviewed the patch, it seems there are still a few issues and it
would be great if we could solve them before this goes into voting.
A few thoughts on the RFC itself:
You mention #[Override]
in the RFC and how it will work on optional
interfaces that are available at runtime (the TestClass::method()
example). Given the interface is implemented as optional, it is to be
expected that the interface will go away under certain configurations.
This will break, because the method is no longer overridden. Hence, it
might be best to also disallow #[Override]
on optional interface
methods, to signal the breaking for the missing interface scenario.
A class only conforms to types that are actually implemented.
Just as a note, this also has some semantic implications. Notably
$object instanceof OptionalInterface
will evaluate to false
without errors when the interface goes missing. To catch this issue
with static analysis, the project will need to be analysed without the
interface being available, or they will need to gain new rules that
catch such expressions that can lead to inconsistent results.
Also, when I read the subject "optional interfaces", what came to mind
was "an interface that may or may not be implemented", i.e. similar to
Objective-C's optional protocol methods [1]. I'm not sure if the
naming can be improved.
Overall, I'm not particularly convinced this is a big problem that
needs solving, but I'll leave that up to framework designers to
decide.
Ilija