PrimitiveType

PHP 5 Abstract Classes And Methods

This article takes a look at PHP5's Object Oriented Programming (OOP) support for abstraction. Though it's easily possible to script in PHP without paying much attention to this powerful feature, larger projects will benefit from the increased class organization and structure that can be obtained as a result of careful usage of abstract classes and methods. This article assumes you have some knowledge of PHP OOP prgramming such as was available in PHP4, and in particular that you understand the concept of inheritance.

An abstract class is a class that has not been fully implemented, and which will usually be used as a base class for other classes to inherit from and complete the implementation. The usefulness of this is that with an abstract class you define the interface all subclasses must have, whilst preventing anyone from instantiating a particular instance of the abstract class. A key advantage of an abstract class in comparison to an interface is that you can add the common elements of implementation that will be shared by the subclasses, and not just specify the interface of the subclasses.

PHP 5 supports abstract classes and methods in a similar way to Java. The first thing to look at is how to create an abstract class by using the abstract keyword:

<?php abstract class MyAbstractClass {}

This class has no body, so it's not a useful class that does anything, nor does it define an interface for subclasses to inherit, but it is still a legal class. As mentioned above, you cannot instantiate an abstract class, as executing the following program shows:

<?php abstract class MyAbstractClass {} $obj = new MyAbstractClass(); /* output: Fatal error: Cannot instantiate abstract class MyAbstractClass in /path/to/test.php on line 3 */

An abstract class can be partially (or even fully) implemented, so it is fine for it to contain normal methods:

<?php abstract class MyAbstractClass { public function doSomething() { echo "MyAbstractClass::doSomething()\n"; } }

In an abstract class, you can also declare a method as abstract, which means that the method is not implemented and that any non-abstract subclass must implement it. An abstract method has no body:

<?php abstract class MyAbstractClass { public abstract function doSomething(); }

Remember that an abstract class need not contain any abstract methods (the first class displayed above, for example, had no body at all), though in practice they usually will. On the other hand, if a class is not declared as abstract, it cannot contain any abstract methods:

<?php class MyClass { public abstract function doSomething(); } /* output: Fatal error: Class MyClass contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (MyClass::doSomething) in /path/to/test.php on line 4 */

You can create a new abstract class that extends an earlier one (note how you do not have to implement the method body of doSomething() in MyAbstractSubClass below because the latter is also an abstract class):

<?php abstract class MyAbstractBaseClass { public abstract function doSomething(); } abstract class MyAbstractSubClass extends MyAbstractBaseClass {}

So now that we've covered some of the rules concerning how to declare abstract classes and methods, we will look at a simple program that makes use of them:

<?php abstract class Vehicle { public abstract function getNumWheels(); public function getName() { return get_class($this); } } class Car extends Vehicle { public function getNumWheels() { return 4; } } class Bike extends Vehicle { public function getNumWheels() { return 2; } } function printNumWheels(Vehicle $v) { echo "A " . $v->getName() . " has " . $v->getNumWheels() . " wheels\n"; } $car = new Car(); $bike = new Bike(); printNumWheels($car); printNumWheels($bike); /* output: A Car has 4 wheels A Bike has 2 wheels */

The program starts by creating an abstract class Vehicle which has an abstract method getNumWheels() and a normal method getName(). Then two subclasses of Vehicle are created, Car and Bike, both very similar. They each implement the method getNumWheels() (if they didn't, they would have to be declared abstract as well), but they implement it differently in terms of the integer that is returned (4 for Car and 2 for Bike); Then there is a standalone function printNumWheels that takes a Vehicle object and prints the name of the object along with the number of wheels it has.

There are a few things to note here. Firstly, printNumWheels expects an instance of Vehicle, and if it doesn't get one, a fatal error occurs and the program exits with an error message. Cars and Bikes are Vehicles, so the there is no complaint from the PHP interpreter. However, even if you created a class that does not inherit from Vehicle but which has the same interface (or methods, in this case), the error would still occur. Next, notice that though getName() is only implemented in Vehicle, it still produces the correct name according to the vehicle type. Because the implementation of getName() is common to all Vehicles and subclasses of Vehicle, it was possible to add it directly to the Vehicle class, and in so doing prevent subclasses from having to implement it themselves (though they could override it if they wished). Lastly, note how the number of wheels matches the vehicle type. The correct version of the getNumWheels() method is being called even though the printNumWheels function expects only a Vehicle and knows nothing about Cars or Bikes.

Well that's all for now. I hope this article has helped you to understand how to use abstract classes and methods in PHP5 a little better!