Yesterday, a colleague at Sensio showed me a very weird error when combining abstract classes, abstract methods, concrete classes and interfaces together. The implementation I'm going to describe in the following lines has already been reported on the PHP's bugs tracker but the core team decided that it wasn't a bug but an expected result. In the PHP world, it's not a bug, but it's "feature"...
The Data Model
Let's use a real life data model as example to illustrate the problem.
The Aircraft abstract class
The following PHP class tries to modelize any kind of aircraft objects like
planes, UFOs, parachutes, hang-gliders, zeppelins or spaceships. So, that's why
there is an abstract class called Aircraft, which encapsulates generic
aircrafts' properties and methods like the getAltitude() method.
<?php // Aircraft.php abstract class Aircraft { abstract public function getAltitude(); }
There is nothing special to say unlike the fact that the getAltitude() method
has been declared as abstract. Consider it was a decision taken by the
programmer.
The ILocalizable interface
Consider the specifications want that only some kind of aircrafts can be localizable when they are in the sky. It's typically the case for civil planes, which transport people from point A to point B on the Earth. On the contrary, it's difficult to locate flying means likes parachutes or UFOs (the truth is out there...).
To gain in flexibility and to take advantage of object oriented programming, the
localization API is managed by an interface, called ILocalizable.
<?php // ILocalizable.php interface ILocalizable { public function locate(); public function getLongitude(); public function getLatitude(); public function getAltitude(); public function getSpeed(); }
The ILocalizable interface defines the five following methods:
locate(), which gives information about the object's position,getLatitude(), which returns the latitude coordinate,getLongitude(), which returns the longitude coordinate,getSpeed(), which returns the object's ground speed,getAltitude(), which returns the object's altitude.
Thanks to this interface, any type of object can become localizable thanks to
the same API. For instance, objects like boats, cars, cellphones or even human
beings... can be localizable if they conform to the ILocalizable interface.
The important thing to notice is that the getAltitude() method is defined
twice. It's defined in both Aircraft abstract class and ILocalizable
interface.
The Plane concrete class
The Aircraft class and the ILocalizable interface are ready. Let's create a
Plane concrete class that inherits Aircraft and implements the
ILocalizable interface.
<?php // Plane.php require_once __DIR__ .'/ILocalizable.php'; require_once __DIR__ .'/Aircraft.php'; class Plane extends Aircraft implements ILocalizable { public function locate() { return array( 'Latitude' => $this->getLatitude(), 'Longitude' => $this->getLongitude(), 'Altitude' => $this->getAltitude(), 'Speed' => $this->getSpeed() ); } public function getLatitude() { return 49.09181; } public function getLongitude() { return 3.98507; } public function getAltitude() { return 26000; } public function getSpeed() { return 560; } }
The Plane concrete class implements all ILocalizable interface's methods.
The locate() method returns an array containing the latitude, the longitude,
the altitude in feet and the ground speed in miles per hour.
How to reproduce and explain the bug?
Now everything is ready. Let's test it by executing the following index.php
file.
<?php // index.php require_once __DIR__ .'/Plane.php'; $plane = new Plane(); echo $plane->getAltitude() ."\n";
Can you guess the result? Obviously, it should be the following one.
26000
Well, it's not... Execute the index.php in the command line as described
below:
Hugo:BlogPost Hugo$ php index.php
Fatal error: Can't inherit abstract function ILocalizable::getAltitude()
(previously declared abstract in Aircraft) in
/Users/Hugo/Sites/BlogPost/Plane.php on line 6
Call Stack:
0.0005 629376 1. {main}() /Users/Hugo/Sites/BlogPost/index.php:0
0.0007 641208 2. require_once('/Users/Hugo/Sites/BlogPost/Plane.php')
/Users/Hugo/Sites/BlogPost/index.php:3
The PHP interpreter throws a fatal error saying that we are trying to declare an
already declared abstract method getAltitude() when implementing the
ILocalizable interface.
The interpreter doesn't accept to implement an abstract method coming from an
interface if it's already declared abstract in the parent class. There is a
conflict as the interpreter thinks we are declaring the abstract method
getAltitude() twice. Weird isn't?
This strange behavior has already been reported to the PHP core team twice:
According to the core team, it's not a bug. Well maybe, but what is the behavior with another object oriented programming language? The following section describes the same implementation in Java code.
The Java implementation
Let's implement our real world example with Java. But why Java? In fact, because the PHP's object oriented API is mainly inspired on the Java's object oriented model. As they are very close with their syntax and OO philosophy, we could be able to compare both PHP and Java results.
First, let's create the Aircraft abstract class in Java.
// Aircraft.java abstract public class Aircraft { abstract public int getAltitude(); }
Then, the ILocalizable interface.
// ILocalizable.java import java.util.HashMap; public interface ILocalizable { public HashMap locate(); public float getLatitude(); public float getLongitude(); public int getSpeed(); public int getAltitude(); }
Then, the Plane concrete class.
// Plane.java import java.util.HashMap; public class Plane extends Aircraft implements ILocalizable { public int getAltitude() { return 26000; } public HashMap locate() { HashMap coords = new HashMap(); coords.put("Latitude", this.getLatitude()); coords.put("Longitude", this.getLongitude()); coords.put("Altitude", this.getAltitude()); coords.put("Speed", this.getSpeed()); return coords; } public float getLatitude() { return (float) 49.09181; } public float getLongitude() { return (float) 3.98507; } public int getSpeed() { return 560; } }
And finally, the Main class, which will be executed.
// Main.java public class Main { public static void main(String[] args) { Plane plane = new Plane(); System.out.println(plane.getAltitude()); } }
Compile and run the Java project and watch the printed result in the Java console tool.
26000
The result is the expected one. The IDE, NetBeans, and the compiler didn't throw any exception or warning for this implementation. In Java, there is no problem to declare an abstract method in an abstract class, and then redeclare it from an interface unlike PHP...
How to solve it?
The way to "solve" this problem is to not declare the getAltitude() method as abstract in the Aircraft class if the method has to be declared in an interface. Just declare the getAltitude() method and leave its body empty.
<?php // Aircraft.php abstract class Aircraft { public function getAltitude() { } }
This way, the PHP interpreter doesn't generate any error and everything works as expected! Why does PHP generate an error if the method is declared as abstract whereas it doesn't with concrete methods...
Conclusion
So, finally this Java implementation proves that PHP is still buggy. As of today, the code has been tested with a PHP 5.3.1 version but I'm pretty sure it's not fixed in PHP 5.3.2 or PHP 5.3.3. This strange behavior has been reported five years ago and was never fixed...



Posted by Mikael RANDY - about 1 year ago
Okay, i'm not sure the current problem must be a "feature", but i dont understand why you compare it with Java implementation.
Posted by Hugo HAMON - about 1 year ago
I compare it with Java as they are similar. PHP takes a lot of inspiration from Java. The behavior I described seems to be buggy only with PHP according I read from the two tickets I linked in this article. I was wondering why PHP doesn't interpret this design as Java (or any other OO language) does.
I understand PHP is not Java but I think every OO developper would have expected the Java result instead of the leveraged fatal error.
Posted by Thibault - about 1 year ago
I'd like to add that a better "solution" to this issue would be to make the unimplemented getAltitude method throw an exception like:
abstract class Aircraft {
public function getAltitude() {
throw new Exception('The Aircraft::getAltitude method must be implemented');
}
}
This way we come closer to the expected behavior of the abstract class.
Posted by mageekguy - about 1 year ago
An aircraft is not mandatory localizable ?
Posted by Hugo Hamon - about 1 year ago
Posted by Romain Dorgueil - about 1 year ago
Posted by Victor Nicoller - about 1 year ago
I guess what this tells us is that PHP does not have interfaces in the classic Object-Oriented sense of imposing compile-time constraints on class members (this class must have members "X", "Y", and "Z"). What we have here is more like a trick to allow multiple inheritance for type-checking purposes.
Implementing an "interface" concept as extending a class with only abstract member functions makes technical sense (it's easier to do it this way) but runs into the typical multiple inheritance issues of having two parents with two members with the same name.