So far, we've covered OOP basics in PHP, and Static Methods, & Properties, in this guide, we would cover abstract classes in PHP.
What is an abstract class?
Just like I explained in the OOP basics, I said a class describes the concept of creating an Object, there are not necessarily the objects themselves, but as a code template used to create or generate one or more objects.
Look at the point, a class is a code template used to create or generate one or more objects. Abstract class shares that ideology, it is a code template for other classes, it is not a code template for objects but for classes.
Which means, it cannot be instantiated but defines the interface for any class that might extend it, like you would point to it only through a reference to the subclass.
You use an abstract class when you want to create a parent class that houses related commonality or functionality of 2 or more classes, and when you don't want the parent class or the base class to be instantiated.
Abstract classes contain one or more abstract methods. Abstract methods are used to perform a specific task, normally, in a normal class or concrete class, you'll implement the method, however, an abstract method is only declared with no implementation, the implementation would have to be carried out in the class that is extending it.
To better understand abstract classes, let's think of a good example that would be a good candidate for abstractiveness...yeah...I got one...
Suppose, we are building a class around genres of music, and you know different styles of music have somewhat similar characteristics or let me say elements, something like melody, harmony, vocal lyrics, and maybe percussiveness.
For sure, not all music would have the same element, but instead of building different classes for a number of elements, we make an abstract class and define all the common ones, and leave the one that would be different out of it, for example not all genre of music uses percussion, but most of all music has some melodies and harmonies.
The abstract class would only do something like:
Hey, concrete subclasses, I have your generalize templates ready for you to implements, if for some reason the methods I declared for you (the subclasses) isn't enough, you can implement a custom method. I hope you get that.
We would see a practical example in a moment, but let's see one more example, so, you would get the idea fully.
Another good example is of a car, as you know, a car has common attributes such as color, size, engine, price, model, etc. So, for every car, this attribute would be common but might be implemented differently depending on the type of car, but the idea here is that abstract class would prepare the general ones for us, which you must implement, but the operation might be different from one class to the other
Practical Examples of Creating an Abstract Class
The following defines an abstract method for the car attributes:
abstract class Car {
abstract protected function car_color();
abstract protected function car_engine();
abstract protected function car_model();
abstract protected function car_price();
}
The abstract keyword is used to denote both an abstract method and an abstract class. When creating an abstract method, you ensure that implementation will be available in all concrete child classes, but you leave the details of that implementation to the subclasses that would be implementing.
Also, methods must be defined with the same (or a less restricted) visibility. For example, if the abstract method is defined as protected, the function implementation must be defined as either protected or public, but not private.
Now, any car that wants to be instantiated (e.g Bentley, Acura, BMW) must implement all abstract methods.
class Acura extends Car {
protected function car_color() {
// TODO: Implement car_color() method.
$color = "Tiger Eye Pearl";
}
protected function car_model() {
// TODO: Implement car_model() method.
$type = "Type S";
}
protected function car_engine() {
// TODO: Implement car_engine() method.
$engine = "3.0-liter single turbo";
}
protected function car_price() {
// TODO: Implement car_price() method.
$price = 30000;
}
}
I can also do the same for BMW or Toyota, here is where it gets interesting, if for some reason the abstract class doesn't have a common method between the cars, I can go ahead to add the custom feature myself:
class Acura extends Car {
protected function car_color() {
// TODO: Implement car_color() method.
$color = "Tiger Eye Pearl";
}
protected function car_model() {
// TODO: Implement car_model() method.
$type = "Type S";
}
protected function car_engine() {
// TODO: Implement car_engine() method.
$engine = "3.0-liter single turbo";
}
protected function car_price() {
// TODO: Implement car_price() method.
$price = 30000;
}
public function special_features() {
// TODO: Implement special_features
}
}
You might be wondering if you can have non abstract methods inside an abstract class, and the answer is yes, you can, in fact, you can have properties in the non abstract method, and the properties doesn't have to be abstract.
So, to get a result, I would instantiate the child class, so, instead of creating a getter method in the child class, I would declare an abstract method since I know it would be common for all my child classes, this would be the end result:
<?php
declare(strict_types=1);
abstract class Car {
protected $color;
protected $type;
protected $engine;
protected $price;
// Constructor
public function __construct(
string $color,
string $type,
string $engine,
int $price
)
{
$this->color = $color;
$this->type = $type;
$this->engine = $engine;
$this->price = $price;
}
abstract protected function car_color() : string;
abstract protected function car_engine() : string;
abstract protected function car_model() : string;
abstract protected function car_price() : int;
abstract protected function getresult() : string;
}
class Acura extends Car {
protected function car_color() : string {
// TODO: Implement car_color() method.
return $this ->color;
}
protected function car_model() : string {
// TODO: Implement car_model() method.
return $this -> type;
}
protected function car_engine() : string {
// TODO: Implement car_engine() method.
return $this -> engine;
}
protected function car_price() : int {
// TODO: Implement car_price() method.
return $this -> $price;
}
public function special_features() {
// TODO: Implement special_features
}
public function getresult() : string {
return "Car Color: " . $this->color . " \n" . "Car Type: " . $this->type . " \n" . "Car Engine: " . $this->engine . "\n" . "Car Price: " . $this->price;
}
}
$car_acura = new Acura(
"Tiger Eye Pearl",
"Type S",
"3.0-liter single turbo",
30000
);
print $car_acura->getresult();
// Output =>
# Car Color: Tiger Eye Pearl
# Car Type: Type S
# Car Engine: 3.0-liter single turbo
# Car Price: 30000
The constructor method is invoked when an object is created. You can use it to prepare the essential properties, and save the hassle of using -> whenever you need to set a new property value for a new object.
Any arguments supplied here:
$car_acura = new Acura( "Tiger Eye Pearl", "Type S", "3.0-liter single turbo", 30000 );
are passed to the constructor, I am passing the color, the type, engine, and price to the constructor, you don’t have to use the property name as the name of the constructor argument, you can use something meaningful. So, in our example, I am passing the arguments supplied above here:
public function __construct( string $color, string $type, string $engine, int $price ) { $this->color = $color; $this->type = $type; $this->engine = $engine; $this->price = $price; }
In my abstract definition you can see I am using a type hint:
abstract protected function car_color() : string; abstract protected function car_engine() : string; abstract protected function car_model() : string; abstract protected function car_price() : int; abstract protected function getresult() : string;
We are using this to specify the expected type, most of the methods should return a string type, with the exception of the car_price method that is expecting an integer. If the abstract class uses type hinting, the child class should use the same.
Which is why, I have:
protected function car_color() : string { // TODO: Implement car_color() method. return $this ->color; } protected function car_model() : string { // TODO: Implement car_model() method. return $this -> type; } ... ... ...
The return $this is used to return whatever is passed into the properties, and we then created a getter method to display out the result:
public function getresult() : string { return "Car Color: " . $this->color . " \n" . "Car Type: " . $this->type . " \n" . "Car Engine: " . $this->engine . "\n" . "Car Price: " . $this->price; }
and printing the method gives us:
print $car_acura->getresult(); // Output => # Car Color: Tiger Eye Pearl # Car Type: Type S # Car Engine: 3.0-liter single turbo # Car Price: 30000
Bottom line is to use an abstract class when you have some commonalities in your sub-classes, and you want to implement them differently based on the type of the objects.
Interfaces
Abstract classes let you create templates for subclasses, leaving the implementation method to the concrete subclasses, an interface can only define the functionality, with no implementation.
Sounds about the same right? Yes, it is very similar with little differences, we would get to that in a moment, the following is an interface:
<?php
Interface MyInterface {
public function Method1();
public function Method2(): string;
public function Method3() : int;
}
As you can see, Interfaces are defined in the same way as a class, but with the interface keyword replacing the class keyword. Any class that incorporates this interface commits to implementing all the methods it defines, or it must be declared abstract.
Unlike, an abstract class that can have a public, protected, or private method, interface methods must be declared public, that is one difference between abstract and interface classes.
A class can implement an interface using the implements keyword in its declaration. The process of implementing an interface is the same as extending an abstract class that contains only the abstract keyword.
Practical Examples of Creating Interface
Here is an example:
<?php
/*
#
# Declare the Interface myInterface
# It can't contain a protected or a private method, a public method or function is fine
#
*/
interface myInterface {
public function setVar($name);
public function getresult() : string;
}
// Implement the interface
class aClass implements myInterface {
// Constructor
public function __construct(
string $name
)
{
$this->name = $name;
}
public function setVar($name) {
// TODO: Implement setVar() method.
$this -> name = $name;
}
public function getresult() : string {
// TODO: Implement getresult() method.
return "Result:" . $this -> name;
}
}
$something = new aClass(
"Pascal"
);
print $something->getresult();
// Output =>
#
# Result: Pascal
#
As you can see it is no much different from an abstract class, and yes, they both provide some level of abstractions, the following are the key differences:
- An abstract class can have a public, protected, or private method, interface methods must be declared public
- Abstract classes don't necessarily enforce all its methods to be abstract, in interface every method is abstract.
- Interfaces can include constants, but cannot contain member properties or variables.
Also, a class can both extend a superclass and implement any number of interfaces. The extends clause should precede the implements clause like so:
class Acura extends Car implements Service, Service2, Service3 {
// ...
}
As you can see, the Acura class extends the car class, and implement multiple interfaces in a comma-separated list. Note that. PHP only supports inheritance from a single parent, so the extends keyword can precede a single class name only.
Traits
I pointed out above that PHP only supports inheritance from a single parent, but what if you want to share an implementation across inheritance hierarchies? and that is where traits come into play. Traits are used for implementing functionality similar to multiple inheritances.
It is similar to abstract class and inheritance in that it cannot itself be instantiated but can be incorporated into classes. Any methods defined in a trait become available as part of any class that uses it. A trait changes the structure of a class but doesn’t change its type.
If you want to avoid copy and pasting code from one class to another, then you might want to use a trait to reduce that.
Here is how you would use a trait, they are declared using the Trait keyword like so:
Trait orderUtilities {
...
...
}
Once, you've implemented your trait, you can then incorparte it into your classes like so:
class ABC {
use orderUtilities;
}
class ABC extends Service {
use orderUtilities;
}
You can include multiple traits in a class by listing each one after the use keyword, separated by commas. For example:
class ABC {
use orderUtilities;
}
class ABC extends Service {
use orderUtilities, totalUtilities;
}
A code example:
<?php
trait greeting {
public function greetuser() {
return "Hello User";
}
public function differentgreeting() {
return "Welcome To Our Website";
}
}
class ABC {
use greeting;
}
$something = new ABC();
print $something->greetuser() . "\n";
print $something->differentgreeting();
// Output =>
# Hello User
# Welcome To Our Website
If there is other classes that needs to use the method in my trait, I can simply use the trait greeting in those classes, by simply doing: Use greeting; This reduces code duplication as there's no need to redeclare the same method several times, it is as easy as that. If you know you won't be duplicating code, then you don't need it. I like to think of it this way, would I be copying codes from one class to multiple classes, if my answer is yes, then I might consider using trait.