In a previous post, we talked about the existing relationship between type hinting and interfaces, and the fact that type hinting can lead to overuse of interfaces.
In this post, we're are going to explain where and when interfaces can be useful (outside of the type hinting concerns) in PHP.
In statically typed languages, interfaces are first used to make different classes interchangeable without breaking type safety. But this is not the property we are concerned about because PHP is a dynamically typed language so this issue doesn't apply (well expect if you are using type hinting).
But interfaces are also an alternative to multiple inheritance because, in general, multiple inheritance creates more problems than it solves (e.g the diamond problem). Moreover, when you are looking to multiple inheritance to solve a problem, most of the time the solution is to not use multiple inheritance at all! Indeed, you should always consider composition of features before you think about inheritance. For example, a Car
doesn't need to extend Engine
and Wheels
classes! In this case, having a Car
which contains an Engine
instance and 4 Wheel
instances makes a far more sense.
Languages like Java or C# preferred to use the interface concept instead of multiple inheritance and this is also the way chosen by PHP.
Are interfaces are "equivalent" to multiple inheritance ?
No. They're two different concepts, although both allow attaching "things" to classes, where "things" are:
- multiple parent classes (for multiple inheritance)
- contracts (for interfaces)
To simplify: an interface is a kind of type (like an integer, a float or whatever) but if it's too abstract for you, just see interfaces as a way to attach "contracts" to classes.
For example if a Car
class implements a Vehicle
interface, it only means that:
Car
is assured to have implemented allVehicle
's methods- you can use
instanceof
to verify that an instance implements theVehicle
"contract"
What interfaces and multiple inheritance both solves ?
When you are writing business logic, you may need be able to manage some conditional behavior based on the type of an instance.
if ($instance instanceof Countable) {
return $instance->count();
}
In the above example, if Countable
is a class name it can be problematic. Indeed if $instance
is an instance of a class A
which extends a class B
, $instance
won't be able to also extend a Countable
class. However if Countable
is an interface, the problem disappear.
When should I use interfaces?
I'll explain this on piece of code found in the Zend Framework 2 (apologies to the author).
/**
* Process the select part
*
* @param PlatformInterface $platform
* @param DriverInterface $driver
* @param ParameterContainer $parameterContainer
* @return null|array
*/
protected function processSelect(PlatformInterface $platform, DriverInterface $driver = null, ParameterContainer $parameterContainer = null)
{
...
$table = $this->table;
...
// create quoted table name to use in columns processinga if ($table instanceof TableIdentifier) {
list($table, $schema) = $table->getTableAndSchema();
}
if ($table instanceof Select) {
$table = '(' . $this->processSubselect($table, $platform, $driver, $parameterContainer) . ')';
} else {
$table = $platform->quoteIdentifier($table);
}
...
}
People often use interfaces in method declarations (where they can be of little value), but miss the opportunity to use them in strategic places! In the above piece of code, all instanceof
statements should have been applied on interfaces instead of a class name. Indeed, if your class already extends another parent class, you won't be able to use your class implementation with the above piece of code!
In conclusion, interfaces in dynamic languages should not be thrown around indiscriminately. The fact that "type hinting" seems to be a practice adopted by many frameworks and libraries tends to lead to interface abuse, and creates problems which never needed to exist!
So, if you want to make you life easier, just avoid type hinting and use interfaces only when they really add value!
PS: For those who are using some dependency injection system based on a DI container which introspects the code using the PHP reflection layer to extract the "type hint" of methods parameters to automatically inject instances of the correct type, I think simpler approaches need to be considered here.