Understanding Inheritance
Inheritance allows you to create a new class based on an existing class. The new class (child) inherits all properties and methods from the existing class (parent), and can add or override functionality.
Key Terms:
- Parent Class (Base/Super Class): The class being inherited from
- Child Class (Derived/Sub Class): The class that inherits
- extends: Keyword used to create inheritance
- Inheritance promotes code reuse and establishes relationships between classes
Basic Inheritance Example
Let's create a simple inheritance hierarchy:
<?php
// Parent class
class Animal {
protected $name;
protected $age;
protected $species;
public function __construct($name, $age, $species) {
$this->name = $name;
$this->age = $age;
$this->species = $species;
}
public function eat() {
return "{$this->name} is eating.";
}
public function sleep() {
return "{$this->name} is sleeping.";
}
public function getInfo() {
return "{$this->name} is a {$this->age} year old {$this->species}";
}
}
// Child class inherits from Animal
class Dog extends Animal {
private $breed;
public function __construct($name, $age, $breed) {
// Call parent constructor
parent::__construct($name, $age, "Dog");
$this->breed = $breed;
}
// Add new method specific to Dog
public function bark() {
return "{$this->name} says: Woof! Woof!";
}
// Add another method
public function fetch() {
return "{$this->name} is fetching the ball!";
}
}
// Another child class
class Cat extends Animal {
private $color;
public function __construct($name, $age, $color) {
parent::__construct($name, $age, "Cat");
$this->color = $color;
}
public function meow() {
return "{$this->name} says: Meow!";
}
public function scratch() {
return "{$this->name} is scratching the furniture!";
}
}
// Usage
$dog = new Dog("Max", 3, "Labrador");
echo $dog->bark(); // Dog-specific method
echo $dog->eat(); // Inherited from Animal
echo $dog->sleep(); // Inherited from Animal
echo $dog->getInfo(); // Inherited from Animal
$cat = new Cat("Whiskers", 2, "Gray");
echo $cat->meow(); // Cat-specific method
echo $cat->eat(); // Inherited from Animal
?>
parent::__construct(): Always call the parent constructor to properly initialize inherited properties. Use parent:: to access parent class methods.
Method Overriding
Child classes can override parent methods to provide specialized behavior:
<?php
class Animal {
protected $name;
public function __construct($name) {
$this->name = $name;
}
public function makeSound() {
return "{$this->name} makes a sound";
}
public function move() {
return "{$this->name} is moving";
}
}
class Dog extends Animal {
// Override makeSound method
public function makeSound() {
return "{$this->name} barks: Woof! Woof!";
}
// Override move method
public function move() {
return "{$this->name} runs on four legs";
}
}
class Bird extends Animal {
// Override makeSound method
public function makeSound() {
return "{$this->name} chirps: Tweet! Tweet!";
}
// Override move method
public function move() {
return "{$this->name} flies in the sky";
}
}
class Fish extends Animal {
// Override makeSound method
public function makeSound() {
return "{$this->name} makes bubbles";
}
// Override move method
public function move() {
return "{$this->name} swims underwater";
}
}
// Each animal behaves differently
$dog = new Dog("Max");
echo $dog->makeSound(); // Max barks: Woof! Woof!
echo $dog->move(); // Max runs on four legs
$bird = new Bird("Tweety");
echo $bird->makeSound(); // Tweety chirps: Tweet! Tweet!
echo $bird->move(); // Tweety flies in the sky
$fish = new Fish("Nemo");
echo $fish->makeSound(); // Nemo makes bubbles
echo $fish->move(); // Nemo swims underwater
?>
Understanding Polymorphism
Polymorphism means "many forms." It allows objects of different classes to be treated as objects of a common parent class, while still maintaining their unique behaviors.
<?php
// Polymorphism in action
function describeAnimal(Animal $animal) {
echo $animal->makeSound() . "\n";
echo $animal->move() . "\n";
}
$dog = new Dog("Buddy");
$bird = new Bird("Chirpy");
$fish = new Fish("Goldie");
// Same function handles different animal types
describeAnimal($dog); // Dog behavior
describeAnimal($bird); // Bird behavior
describeAnimal($fish); // Fish behavior
?>
Polymorphism Benefits:
- Code Flexibility: Write functions that work with any subclass
- Extensibility: Add new classes without changing existing code
- Maintainability: Easier to manage and update code
Real-World Example: Employee System
Let's build a practical employee management system:
<?php
class Employee {
protected $name;
protected $id;
protected $baseSalary;
protected $department;
public function __construct($name, $id, $baseSalary, $department) {
$this->name = $name;
$this->id = $id;
$this->baseSalary = $baseSalary;
$this->department = $department;
}
public function getName() {
return $this->name;
}
public function getId() {
return $this->id;
}
// Can be overridden by child classes
public function calculateSalary() {
return $this->baseSalary;
}
public function getDetails() {
return "ID: {$this->id}, Name: {$this->name}, Department: {$this->department}";
}
// Can be overridden for different employee types
public function getRole() {
return "Employee";
}
}
class Manager extends Employee {
private $bonus;
private $teamSize;
public function __construct($name, $id, $baseSalary, $department, $teamSize) {
parent::__construct($name, $id, $baseSalary, $department);
$this->teamSize = $teamSize;
$this->bonus = 0;
}
public function setBonus($amount) {
$this->bonus = $amount;
}
// Override salary calculation
public function calculateSalary() {
return $this->baseSalary + $this->bonus;
}
// Override role
public function getRole() {
return "Manager (Team of {$this->teamSize})";
}
public function conductMeeting() {
return "{$this->name} is conducting a team meeting";
}
}
class Developer extends Employee {
private $programmingLanguages;
private $projectBonus;
public function __construct($name, $id, $baseSalary, $department, $languages) {
parent::__construct($name, $id, $baseSalary, $department);
$this->programmingLanguages = $languages;
$this->projectBonus = 0;
}
public function addProjectBonus($amount) {
$this->projectBonus += $amount;
}
// Override salary calculation
public function calculateSalary() {
return $this->baseSalary + $this->projectBonus;
}
// Override role
public function getRole() {
return "Developer (" . implode(", ", $this->programmingLanguages) . ")";
}
public function writeCode() {
return "{$this->name} is writing code in " . implode(", ", $this->programmingLanguages);
}
}
class Intern extends Employee {
private $mentor;
private $duration; // months
public function __construct($name, $id, $baseSalary, $department, $mentor, $duration) {
parent::__construct($name, $id, $baseSalary, $department);
$this->mentor = $mentor;
$this->duration = $duration;
}
// Override salary calculation (interns might get reduced salary)
public function calculateSalary() {
return $this->baseSalary * 0.6; // 60% of base salary
}
// Override role
public function getRole() {
return "Intern ({$this->duration} months program)";
}
public function learn() {
return "{$this->name} is learning from mentor: {$this->mentor}";
}
}
// Function using polymorphism
function printEmployeeReport(Employee $employee) {
echo "=== Employee Report ===\n";
echo $employee->getDetails() . "\n";
echo "Role: " . $employee->getRole() . "\n";
echo "Salary: $" . number_format($employee->calculateSalary(), 2) . "\n";
echo "===================\n\n";
}
// Create different employees
$manager = new Manager("Ahmed Ali", "M001", 8000, "Engineering", 10);
$manager->setBonus(2000);
$dev = new Developer("Sara Hassan", "D001", 6000, "Engineering", ["PHP", "JavaScript", "Python"]);
$dev->addProjectBonus(1000);
$intern = new Intern("Omar Khalil", "I001", 2000, "Engineering", "Sara Hassan", 6);
// Use polymorphism - same function for all employee types
printEmployeeReport($manager);
printEmployeeReport($dev);
printEmployeeReport($intern);
// Type-specific methods
echo $manager->conductMeeting() . "\n";
echo $dev->writeCode() . "\n";
echo $intern->learn() . "\n";
?>
The final Keyword
Use final to prevent classes from being inherited or methods from being overridden:
<?php
// Cannot be extended
final class Configuration {
private $settings;
public function get($key) {
return $this->settings[$key] ?? null;
}
}
// This would cause an error:
// class CustomConfig extends Configuration {} // Error!
class User {
protected $name;
// Cannot be overridden in child classes
final public function getId() {
return $this->id;
}
// Can be overridden
public function getRole() {
return "User";
}
}
class Admin extends User {
// This is fine
public function getRole() {
return "Administrator";
}
// This would cause an error:
// public function getId() {} // Error: Cannot override final method
}
?>
When to use final: Use it for critical methods that must not be changed (security, licensing, core functionality) or for classes that should not be extended.
Abstract Classes
Abstract classes serve as templates and cannot be instantiated directly:
<?php
abstract class Shape {
protected $color;
public function __construct($color) {
$this->color = $color;
}
public function getColor() {
return $this->color;
}
// Abstract method - must be implemented by child classes
abstract public function calculateArea();
abstract public function calculatePerimeter();
// Concrete method - can be used as-is
public function describe() {
return "A {$this->color} shape with area " . $this->calculateArea();
}
}
class Rectangle extends Shape {
private $width;
private $height;
public function __construct($color, $width, $height) {
parent::__construct($color);
$this->width = $width;
$this->height = $height;
}
// Must implement abstract methods
public function calculateArea() {
return $this->width * $this->height;
}
public function calculatePerimeter() {
return 2 * ($this->width + $this->height);
}
}
class Circle extends Shape {
private $radius;
public function __construct($color, $radius) {
parent::__construct($color);
$this->radius = $radius;
}
public function calculateArea() {
return pi() * $this->radius ** 2;
}
public function calculatePerimeter() {
return 2 * pi() * $this->radius;
}
}
// Cannot do this:
// $shape = new Shape("red"); // Error: Cannot instantiate abstract class
// Can do this:
$rect = new Rectangle("blue", 10, 5);
echo $rect->describe(); // A blue shape with area 50
echo $rect->calculatePerimeter(); // 30
$circle = new Circle("red", 7);
echo $circle->describe(); // A red shape with area 153.94
echo $circle->calculatePerimeter(); // 43.98
?>
Abstract Classes vs Regular Classes:
- Abstract: Cannot be instantiated, can have abstract methods
- Regular: Can be instantiated, all methods must be implemented
- Use abstract: When you want to define a common interface but force child classes to implement specific methods
Multi-Level Inheritance
Classes can inherit from classes that inherit from other classes:
<?php
class Vehicle {
protected $brand;
public function __construct($brand) {
$this->brand = $brand;
}
public function start() {
return "Starting {$this->brand} vehicle...";
}
}
class Car extends Vehicle {
protected $doors;
public function __construct($brand, $doors) {
parent::__construct($brand);
$this->doors = $doors;
}
public function honk() {
return "Beep beep!";
}
}
class ElectricCar extends Car {
private $batteryCapacity;
public function __construct($brand, $doors, $batteryCapacity) {
parent::__construct($brand, $doors);
$this->batteryCapacity = $batteryCapacity;
}
public function charge() {
return "Charging {$this->brand} with {$this->batteryCapacity}kWh battery";
}
// Can access methods from all ancestors
public function displayInfo() {
return $this->start() . " " . $this->honk() . " " . $this->charge();
}
}
$tesla = new ElectricCar("Tesla", 4, 100);
echo $tesla->start(); // From Vehicle
echo $tesla->honk(); // From Car
echo $tesla->charge(); // From ElectricCar
echo $tesla->displayInfo(); // Uses all three
?>
Exercise:
Create a payment processing system with inheritance:
- Abstract class
Payment with properties: amount, currency, status
- Abstract methods:
processPayment(), validatePayment()
- Concrete method:
getDetails()
- Class
CreditCardPayment extends Payment with: cardNumber, expiryDate, cvv
- Class
PayPalPayment extends Payment with: email, transactionId
- Class
BankTransferPayment extends Payment with: accountNumber, bankName
- Implement all abstract methods with appropriate logic
- Create a function
processPayments(array $payments) that uses polymorphism
Best Practices
Important Guidelines:
- Favor composition over inheritance: Sometimes it's better to have objects contain other objects rather than inherit
- Keep inheritance shallow: Avoid deep inheritance chains (3+ levels)
- Use protected for shared data: Allow child classes to access what they need
- Call parent constructors: Always call parent::__construct() when overriding
- Document inheritance: Make it clear what methods should/shouldn't be overridden
Summary
In this lesson, you learned:
- How inheritance creates parent-child relationships between classes
- How to use the
extends keyword
- Method overriding to specialize behavior
- Polymorphism and its benefits
- The
final keyword to prevent inheritance/overriding
- Abstract classes as templates for child classes
- Multi-level inheritance hierarchies
- Best practices for effective inheritance
Next, we'll learn about static members and constants for class-level data and functionality!