Inheritance, Encapsulation and Polymorphism in PHP

Inheritance

Inheritance is commonly used when we want to create classes that would reuse the properties and/or class methods that are similar to existing classes. It is common to have this abstract high level functionality in a class that is referred to as the base or the parent class and then group this functionality into multiple different subclasses or child classes that would use properties or methods from that base class.

In PHP, we use the extends keyword to inherit the functionality of a base class. There are at least two pieces of information that we would need for inheritance; the first is the parent class or base class and the second is the child class or subclass. In PHP, a child class can only have one parent class. Let's go over an example of inheritance where we create an Animal class and then inherit the functionality from the Animal base class to a Dog subclass. For this example, we will be housing these classes in different files, so the Animal class will go into Animal.php and the Dog subclass will go into Dog.php. Consider the following example where the Animal class will go into Animal.php:

<?php
class Animal{
 public $name;
 public function __construct($name){
 $this->name = $name;
 }
 public function greet(){
 return "Hello ".$this->name."\n";
 }
}
?>

Consider the following example where the Dog subclass will go into Dog.php:

<?php
require('Animal.php');
class Dog extends Animal{
 public function run(){
 return $this->name." likes to run \n";
 }
}
$dog = new Dog("scooby");
echo $dog->greet();
echo $dog->run();
?> 
Note The require keyword is used to insert the content of a PHP file
 into another file and the PHP engine explicitly verifies that the
 content has only been added once.

After requiring the Animal.php file, we are creating the Dog subclass and are using the extends keyword to inherit the functionality of the Animal class. As dogs love to run, let us add a run method into the Dog class. After we have defined the Dog class, we will go forward and create an object of the Dog class and pass in a name to it. Though we have not declared and defined a constructor for the Dog class or the greet method in the Dog class, they would be available as we are inheriting the method from the Animal base class and both the constructor and the greet method are public.

The output for the previous code snippets is as follows:

Hello scooby
scooby likes to run

Encapsulation

With abstraction, we have seen how to hide the underlying implementation that provides properties and methods. With encapsulation, let us see how we can expose a specific set of methods and properties, while hiding or restricting access to another set of properties and/or methods based on who is accessing this functionality. In our last example, we have used the keyword public while declaring the properties to define the access to those properties.

The access to the properties and methods in a class can be defined using public, protected, or private keywords as shown in the following table:

VisibilityDescriptionComment
publicA public variable or a method can be accessed by anybody who can access the class.All properties and methods are public by default.
protectedA protected variable or a method can only be accessed by the class members and the class members of a child class.
privateA private variable or a method can only be accessed by the internal class members.

Polymorphism

Polymorphism, as the name suggests, is a principle that allows different classes to have common methods that have the same name and signature but provide different functionality. It is a practice of sharing common programming functionality among classes in a single project. Let us take the example of a cat, a dog, and a duck. All of them are animals; cats and dogs belong to the family of mammals, while a duck belongs to the family of birds. Though all of them are animals, when representing them as objects, they share a few common features and those common features can live in the Animal base class. Let us consider a common feature such as talking or speaking, while a human can speak, a dog barks, a cat meows, and a duck quacks. So if the common feature is communication, the way communication is implemented among them is different. Let us use the concept of polymorphism to tackle this example and represent it in objects and classes in Animal.php as shown in the following example:

<?php
class Animal{
public $name;
 protected $type;
 public function __construct($name){
 $this->name = $name;
 }
 public function greet(){
 return "Hello ".$this->name."\n";
 }
 public function run(){
 return $this->name." runs \n";
 }
 public function communicate(){
 return $this->name." says rrrrrr";
 }
}
?>

Consider the following example in the Dog.php file extending the Animal base class:

<?php
require('Animal.php');
class Dog extends Animal{
 protected $type=__CLASS__;
 public function __get($property){
 if(property_exists($this, $property)){
 return $this->$property."\n";
 }
 else{
 return $property." does not exist \n";
 }
 }
 public function run(){
 return $this->name." likes to run \n";
 }
 public function communicate(){
 return $this->name." says bow wow \n";
 }
}
$dog = new Dog("scooby");
echo $dog->type;
echo $dog->greet();
echo $dog->run();
echo $dog->communicate();
?>

Let us discuss this example before we look at our new Cat class. We have added the run() and communicate() methods to our Animal base class and are overriding these methods in the Dog subclass. My dog loves to run, so I am overriding the run method in the base class as it is too generic; by overriding this method in my subclass, I am making sure that my subclass functionality is implemented.

The output of the previous code snippet is as follows:

Dog
Hello scooby
scooby likes to run
scooby says bow wow

Upon execution, the name of the class is printed as our __get() magic method is fired. Then the greet() method is executed, and the outputs of the two methods that were overridden in the subclass are printed. Scooby really loves to run and is quite a talker. Now let us look at the implementation of these methods in our Cat class in Cat.php as shown in the following example:

<?php
require('Animal.php');
class Cat extends Animal{
 protected $type=__CLASS__;
 public function __get($property){
 if(property_exists($this, $property)){
 return $this->$property."\n";
 }
 else{
 return $property." does not exist \n";
 }
 }
 public function run(){
 return $this->name." hates to run \n";
 }
 public function communicate(){
 return $this->name." says meow \n";
 }
}
$cat = new Cat("cuddles");
echo $cat->type;
echo $cat->greet();
echo $cat->run();
echo $cat->communicate();
?>

Our Cat class is similar to the Dog class, except for the implementation of the run() and communicate() method. We are using the greet() method from the Animal base class. Let us execute this code and examine the output shown as follows:

Cat
Hello cuddles
cuddles hates to run
cuddles says meow

Upon execution, the name of the class is fired by our getter method and the greet() method is fired next. After the greet() method, the overridden implementations of the run() and communicate() method are fired. Unlike Scooby, Cuddles is very lazy and hates to run.