Interesting details about polymorphism in PHP (that you might not have expected)
After so many years with PHP, changes in syntax, and the language’s approach to the OOP paradigm, I’ve encountered some limitations, many nuances… and practical solutions worth knowing. Here, I explain it the way I wish I had read it when I started 💯.
1️⃣ Before we begin…, a few clarifications
I’m going to assume that we already have some knowledge of PHP and the object-oriented programming paradigm. In any case, I’ll try to keep the information accessible 🤝.
2️⃣ Method overload?
Two things: there is no method overloading in PHP (at least for now) and it is not true polymorphism. Of course, understanding overloading as it occurs in other languages like Java.
In PHP, however, the term overloading applies to the dynamic creation of properties and methods. None of this really has to do with polymorphism. ❓❓❓❓❓❓ However, do we have a way to simulate classic method overloading that could be mistaken for polymorphism?
3️⃣ Fake overload, fake polymorphism: Fake overload ash Nazg thrakatulûk agh burzum-ishi krimpatul!!! 💍
Fake overload: one array()
Okay, I’d better continue. One way to simulate that a method (or function) will behave differently depending on the parameter(s) received is by using an array, as seen in the example:
class OperationsUtil {
public function do(array $params): string {
return match (true) {
isset($params['sum']) => "Result: " . array_sum($params['sum']),
isset($params['product']) => "Result: " . array_product($params['product']),
isset($params['double']) => "Result: " . ($params['double'] * 2),
default => "Invalid operation",
};
}
}
$operationsUtil = new OperationsUtil();
echo $operationsUtil->do(Array('sum'=>[2,5,7])), "\n", // Result: 14
$operationsUtil->do(Array('product'=>[2, 3, 4])), "\n", // Result: 24
$operationsUtil->do(Array('double'=>2.3)), "\n", // Result: 4.6
$operationsUtil->do(Array('whatif'=>'whatyoufeelabout')); // Result: Invalid operation
🚧 Note! You can use match() starting with PHP version 8. 🚧
Fake overload with optional arguments
Well, we check if a parameter has the default value or not, the number of parameters, etc, and act accordingly:
class ProductUtil {
public function adminProduct($product, $action='insert', $quantity=null) {
switch($action) {
case 'insert':
echo isset($quantity) ? 'Inserting ' . $quantity . ' of ' . $product . PHP_EOL : 'Inserting ' . $product . PHP_EOL;
break;
case 'update':
echo isset($quantity) ? 'Updating ' . $quantity . ' of ' . $product . PHP_EOL : 'Updating ' . $product . PHP_EOL;
break;
case 'delete':
echo isset($quantity) ? 'Deleting ' . $quantity . ' of ' . $product . PHP_EOL : 'Deleting ' . $product . PHP_EOL;
break;
default:
echo 'Invalid action: ' . $action . PHP_EOL;
}
}
}
$productUtil = new ProductUtil();
$productUtil->adminProduct('product1', 'insert', 5);
$productUtil->adminProduct('product2', 'update');
$productUtil->adminProduct('product3', 'delete', 10);
$productUtil->adminProduct('product4', 'delete');
$productUtil->adminProduct('product5', 'invalidaction');
And… okay, based on a mapping, using switch(), using if(), etc., we can alternatively apply the concepts of “fake overloading” seen in:
- variadic parameters,
- named parameters, or
- mixed type.
Of course, we can also improve or make our algorithm more flexible with the count() and func_num_args() functions, along with other functions applicable to the arguments of a function.
☝️ But polymorphism is something else
How do we actually apply polymorphism in our beloved PHP language? Well, by doing the classic thing, right? Using abstract methods of equally abstract classes, or better yet, with interfaces. Just like in other languages. Let’s see an example anyway:
interface Shape {
public function getArea(): float;
}
class Circle implements Shape {
public function __construct(private float $radius) {}
public function getArea(): float {
return pi() * $this->radius ** 2;
}
}
class Rectangle implements Shape {
public function __construct(
private float $base,
private float $height
) {}
public function getArea(): float {
return $this->base * $this->height;
}
}
class Triangle implements Shape {
public function __construct(
private float $base,
private float $height
) {}
public function getArea(): float {
return ($this->base * $this->height) / 2;
}
}
$shapes = [
new Circle(3),
new Rectangle(4, 5),
new Triangle(6, 2),
];
foreach ($shapes as $shape) {
echo "Area: " . $shape->getArea() . PHP_EOL;
}
// Results:
Area: 28.274333882308
Area: 20
Area: 6
So what’s the point? I don’t know about you, but this has always sounded like “reinventing the wheel” to me. Why don’t I just create the getArea() method in each class and be done with it? I know, it’s subjective, but what can I do?
Well, I prefer to apply that base using some pattern that has a more “polymorphic flavor” to me. Let’s see:
interface Notification {
public function send(): string;
}
class EmailNotification implements Notification {
public function send(): string {
return "Sending notification via email.";
}
}
class SMSNotification implements Notification {
public function send(): string {
return "Sending notification via SMS.";
}
}
class PushNotification implements Notification {
public function send(): string {
return "Sending push notification to the device.";
}
}
class NotificationUtil {
public static function notify(Notification $notification): void {
echo $notification->send() . PHP_EOL;
}
}
// Usage
$email = new EmailNotification();
$sms = new SMSNotification();
$push = new PushNotification();
NotificationUtil::notify($email); // Sending notification via email.
NotificationUtil::notify($sms); // Sending notification via SMS.
NotificationUtil::notify($push); // Sending push notification to the device.
Mmmm, yes, this is the smell I prefer, totally object oriented programming number 5. The code is clear, easily scalable (we can easily add other types of notifications without any problems), and the NotificationUtil class abstracts the sending logic. Okay, it can also be improved.
Nothing more, it’s a pleasure to be able to write these articles, and if they’re also useful to you, it’s doubly satisfying. Let’s keep learning!
Did you like the article, the game, or both? You can help by donating by Paypal 💪
With Monetag, you can earn money by advertising on your blog or website. If you prefer, simply click here and a window with ads will open.
Another way to help me is by following me and clapping the article.
Oh! By the way. Do you want free courses with certificates? 💯 Here are some very interesting ones.