Consider the following PHP interfaces:
interface Item {
// some methods here
}
interface SuperItem extends Item {
// some extra methods here, not define
PHP 7.4, released November 2014 has improved type variance.
The code from the question remains invalid:
interface Item {
// some methods here
}
interface SuperItem extends Item {
// some extra methods here, not defined in Item
}
interface Collection {
public function add(Item $item);
// more methods here
}
interface SuperCollection extends Collection {
public function add(SuperItem $item); // This will still be a compile error
// more methods here that "override" the Collection methods like "add()" does
}
Because the Collection
interface guarantees that anything that implements it can accept any object of type Item
as the parameter of add
.
However, the following code is valid in PHP 7.4:
interface Item {
// some methods here
}
interface SuperItem extends Item {
// some extra methods here, not defined in Item
}
interface Collection {
public function add(SuperItem $item);
// more methods here
}
interface SuperCollection extends Collection {
public function add(Item $item); // no problem
// more methods here that "override" the Collection methods like "add()" does
}
In this case Collection
guarantees that that it can accept any SuperItem
. Since all SuperItem
s are Item
s, SuperCollection
also makes this guarantee, while also guaranteeing that it can accept any other type of Item
. This is known as a Contravariant method parameter type.
There is a limited form of type variance in earlier versions of PHP. Assuming other interfaces are as in the question, the SuperCollection
may be defined as:
interface SuperCollection extends Collection {
public function add($item); // no problem
// more methods here that "override" the Collection methods like "add()" does
}
This can be interpreted as meaning any value whatsoever may be passed to the add
method. That of course includes all Item
s, so this is still type safe, or it can be interpreted as meaning that an unspecified class of values, generally documented as mixed
may be passed and the programmer needs to use other knowledge of exactly what can work with the function.
No, I'm pretty sure PHP doesn't support this, in any version, and it would rather defeat the point of an interface.
The point of an interface is that it gives you a fixed contract with other code that references the same interface.
For example, consider a function like this:
function doSomething(Collection $loopMe) { ..... }
This function expects to receive an object that implements the Collection
interface.
Within the function, the programmer would be able to write calls to methods that are defined in Collection
, knowing that the object would implement those methods.
If you have an overridden interface like this, then you have a problem with this, because a SuperCollection
object could be passed into the function. It would work because it also is a Collection
object due to the inheritance. But then the code in the function could no longer be sure that it knows what the definition of the add()
method is.
An interface is, by definition, a fixed contract. It is immutable.
As an alternative, you could consider using abstract classes instead of interfaces. This would allow you to override in non-Strict mode, although you'll still get errors if you use Strict Mode, for the same reasons.
You cannot change the methods arguments.
http://php.net/manual/en/language.oop5.interfaces.php
The problem is not in the IDE. In PHP you cannot override a method. And the compatibility is only in the opposite direction - you can safely expect an instance of the parent class and receive a subclass. But when you expect a subclass, you cannot be safe if you receive the parent class - the subclass may define methods that do not exist in the parent. But still, you can't override the method
As a workaround I'm using PHPDoc blocks in interfaces.
interface Collection {
/**
* @param Item $item
*/
public function add($item);
// more methods here
}
interface SuperCollection extends Collection {
/**
* @param SuperItem $item
*/
public function add($item);
// more methods here that "override" the Collection methods like "add()" does
}
This way, in case you are properly using interfaces, IDE should help you catch some errors. You can use similar technique to override return value types as well.
Extending interface is not allowed to change method definitions. If your SuperItem is extending Item, it should be passing through classes implementing Collection interface without problems.
But based on what you really want to do, you can try:
Create interface with slightly different methods for SuperItem and implement that:
interface SuperCollection extends Collection {
public function addSuper(SuperItem $superItem);
}
Use decorator pattern to create almost same interface without extending:
interface Collection {
public function add(Item $item);
// more methods here
}
interface SuperCollection {
public function add(SuperItem $item);
// more methods here that "override" the Collection methods like "add()" does
}
Then decorator (abstract) class, which will use this interface:
class BasicCollection implements Collection {
public function add(Item $item)
{
}
}
class DecoratingBasicCollection implements SuperCollection {
protected $collection;
public function __construct(Collection $collection)
{
$this->collection = $collection;
}
public function add(SuperItem $item)
{
$this->collection->add($item);
}
}