I have been meaning to write a little article on the use of interfaces in PHP 5. For me, they have not been the easiest thing to get to grips with and the php.net documentation is not exhaustive to say the least. Alot of this knowledge comes from the excellent bulletin board at <!-- w --><a class="postlink" href="http://www.sitepoint.com">www.sitepoint.com</a><!-- w -->. The article assumes a reasonable understanding of the PHP 5 object model and of object oriented design in general.
The usefulness of interfaces is buried quite deeply in object oriented methodology and can easily be overlooked.
Essentially interfaces allow multiple inheritance without implementation.
In languages which offer true multiple inheritance one can easily be found in the 'dreaded diamond of death' whereby if a class extends two classes which define the same method, how would the class know which to inherit? Interfaces do not face this problem as they never implement a method, that is up to the implementing class.
// The dreaded diamond of death
class Foobar
{
function foo(){}
}
class Foo extends Foobar{}
class Bar extends Foobar{}
class Barfoo extends Foo, Bar{}
PHP 5, like Java, offers multiple interfaces rather than multiple inheritance. PHP 4 is stuck with single inheritance. It's worth bearing in mind that if you try to implement two interfaces which contain the same method description, you will get an error (and rightly so).
Fatal error: Can't inherit abstract function Test2::test() (previously declared abstract in Test1)
We can also think of interfaces in terms of types. When a class implements an interface it also takes it's type. This allows the developer to think of a object in terms of it's interface(s). As there is the facility for mutiple implementation this allows the developer to take different actions based on an objects' type.
interface ITest1
{
function test1();
}
interface ITest2
{
function test2();
}
class Foobar implements ITest1, ITest2
{
// We must implement the interfaces' methods
function test1(){}
function test2(){}
}
$foobar = new Foobar;
// Check if this object implements an interface
if( $foobar instanceof ITest1 )
{
// Yes so call the interface method
$foobar->test1();
}
At first this doesn't seem very useful. After all the same type checking can be done with classes and single inheritance. It becomes more clear when you think in terms of specialisation. Class Car might extend (specialise) Class Vehicle and so a Car object is_a() Vehicle. The time might come when you want to make a second specialisation, Truck. Do you rewrite your object model to accomodate this?
// The wrong way
abstract class Vehicle
{
abstract function getNumberOfWheels();
}
class Car extends Vehicle
{
function getNumberOfWheels(){} // Implemented method from Foobar
}
Everything is fine until we make a new specialisation, which needs different functionality e.g. class Truck needs a getFreightType() method. In order to retain the type of Vehicle we'd need to add getFreightType() into Vehicle...but then class Car would be broken
abstract class Vehicle
{
abstract function getNumberOfWheels();
abstract function getFreightType();
}
class Truck extends Vehicle
{
function getNumberOfWheels(){}
function getFreightType(){} // Remember Car will be broken until it also implements getFreightType()
}
Rather than this we can use (multiple) interfaces. By creating interfaces for Domestic and Freight types we can specialise the Vehicle type without worrying about breaking any legacy code (it's common practice to prefix interface names with the letter I).
interface IVehicle
{
function getNumberOfWheels();
}
interface IDomestic
{
function getColour();
}
interface IFreight
{
function getFreightType();
}
class Car implements IVehicle, IDomestic{}
class Truck implements IVehicle, IFreight{}
Since Truck doesn't implement IDomestic, that interface can be changed to add new functionality to Car, without worrying about breaking Truck. Likewise with Truck and IFreight. And yet we can still force both types to adopt new functionality by changing IVehicle. And no inheritance in sight.
An example is attached to this post which (hopefully) illustrates the above in a working example. You will need remove .txt from the filename.
I hope this is useful to people wanting to learn more about PHP 5's new features.
The usefulness of interfaces is buried quite deeply in object oriented methodology and can easily be overlooked.
Essentially interfaces allow multiple inheritance without implementation.
In languages which offer true multiple inheritance one can easily be found in the 'dreaded diamond of death' whereby if a class extends two classes which define the same method, how would the class know which to inherit? Interfaces do not face this problem as they never implement a method, that is up to the implementing class.
// The dreaded diamond of death
class Foobar
{
function foo(){}
}
class Foo extends Foobar{}
class Bar extends Foobar{}
class Barfoo extends Foo, Bar{}
PHP 5, like Java, offers multiple interfaces rather than multiple inheritance. PHP 4 is stuck with single inheritance. It's worth bearing in mind that if you try to implement two interfaces which contain the same method description, you will get an error (and rightly so).
Fatal error: Can't inherit abstract function Test2::test() (previously declared abstract in Test1)
We can also think of interfaces in terms of types. When a class implements an interface it also takes it's type. This allows the developer to think of a object in terms of it's interface(s). As there is the facility for mutiple implementation this allows the developer to take different actions based on an objects' type.
interface ITest1
{
function test1();
}
interface ITest2
{
function test2();
}
class Foobar implements ITest1, ITest2
{
// We must implement the interfaces' methods
function test1(){}
function test2(){}
}
$foobar = new Foobar;
// Check if this object implements an interface
if( $foobar instanceof ITest1 )
{
// Yes so call the interface method
$foobar->test1();
}
At first this doesn't seem very useful. After all the same type checking can be done with classes and single inheritance. It becomes more clear when you think in terms of specialisation. Class Car might extend (specialise) Class Vehicle and so a Car object is_a() Vehicle. The time might come when you want to make a second specialisation, Truck. Do you rewrite your object model to accomodate this?
// The wrong way
abstract class Vehicle
{
abstract function getNumberOfWheels();
}
class Car extends Vehicle
{
function getNumberOfWheels(){} // Implemented method from Foobar
}
Everything is fine until we make a new specialisation, which needs different functionality e.g. class Truck needs a getFreightType() method. In order to retain the type of Vehicle we'd need to add getFreightType() into Vehicle...but then class Car would be broken
abstract class Vehicle
{
abstract function getNumberOfWheels();
abstract function getFreightType();
}
class Truck extends Vehicle
{
function getNumberOfWheels(){}
function getFreightType(){} // Remember Car will be broken until it also implements getFreightType()
}
Rather than this we can use (multiple) interfaces. By creating interfaces for Domestic and Freight types we can specialise the Vehicle type without worrying about breaking any legacy code (it's common practice to prefix interface names with the letter I).
interface IVehicle
{
function getNumberOfWheels();
}
interface IDomestic
{
function getColour();
}
interface IFreight
{
function getFreightType();
}
class Car implements IVehicle, IDomestic{}
class Truck implements IVehicle, IFreight{}
Since Truck doesn't implement IDomestic, that interface can be changed to add new functionality to Car, without worrying about breaking Truck. Likewise with Truck and IFreight. And yet we can still force both types to adopt new functionality by changing IVehicle. And no inheritance in sight.
An example is attached to this post which (hopefully) illustrates the above in a working example. You will need remove .txt from the filename.
I hope this is useful to people wanting to learn more about PHP 5's new features.