Chapter 07 ยท OOP Foundations
Inheritance
Sometimes two classes are 90% the same. Students and lecturers both have names, emails and ID numbers โ only some details differ. Must we copy-paste? Never! Inheritance lets a new class receive everything from an existing one, then add its own extras.
A child inherits from a parent: the surname, the family home, certain traits โ automatically, without asking. Then the child adds things of their own: new skills, a new phone, a different hairstyle. In OOP, a child class inherits all properties and methods from a parent class and can add or change whatever it needs.
The extends keyword
<?php
// The parent (also called "base class" or "super class")
class Person {
public function __construct(
public string $name,
public string $email
) {}
public function introduce(): string {
return "Hello, I am $this->name.";
}
}
// The children โ each one "extends" (inherits from) Person
class Student extends Person {
public string $program = "Undeclared";
public function study(): string {
return "$this->name is studying $this->program.";
}
}
class Lecturer extends Person {
public function teach(string $course): string {
return "$this->name is teaching $course.";
}
}
$s = new Student("Chanda", "[email protected]");
$s->program = "BSc Computing";
echo $s->introduce(); // inherited from Person โ we never wrote it in Student!
echo "<br>";
echo $s->study(); // Student's own new skill
$l = new Lecturer("Mr. Mwale", "[email protected]");
echo "<br>" . $l->introduce();
echo "<br>" . $l->teach("PHP OOP");
Notice: Student never declared $name, $email,
__construct or introduce() โ yet it has them all. They flowed down from
Person. We wrote the shared code once.
Use inheritance only when the sentence sounds right: a Student is a Person โ. A Lecturer is a Person โ. A Car is a Person โ โ don't force it. If the true relationship is "has a" (a Car has an Engine), the answer is a property, not inheritance.
Overriding โ the child does it differently
A child can replace an inherited method by simply declaring a method with the same name. This is our first taste of polymorphism (same instruction, different behaviour):
class Person {
public function __construct(public string $name) {}
public function introduce(): string {
return "Hello, I am $this->name.";
}
}
class Lecturer extends Person {
// Same method name = override. The child's version wins.
public function introduce(): string {
return "Good morning class, I am $this->name, your lecturer.";
}
}
$people = [new Person("Chanda"), new Lecturer("Mr. Mwale")];
foreach ($people as $p) {
echo $p->introduce() . "<br>"; // same call, different results!
}
That loop is the magic moment: we call the same method on every object and each object answers in its own way. The loop doesn't need to know or care who is who.
parent:: โ extend, don't replace
Often the child doesn't want to throw away the parent's version โ it wants to
add to it. The keyword parent:: calls the parent's version:
class Student extends Person {
public function __construct(string $name, public string $studentNumber) {
parent::__construct($name); // let Person handle the name first
// then do my own extra setup
}
public function introduce(): string {
return parent::introduce() . " My student number is $this->studentNumber.";
}
}
$s = new Student("Chanda", "LGU2026-001");
echo $s->introduce();
If a child class defines its own __construct, the parent's constructor does
not run automatically anymore. Call it yourself with
parent::__construct(...) โ usually as the first line โ or the parent's properties
will be left unset.
Where protected finally makes sense
Remember the middle visibility from last chapter? private is so strict that even
children are locked out. protected is the family key:
class Person {
protected string $idNumber = ""; // family only: me + my children
public function __construct(string $idNumber) {
$this->idNumber = $idNumber;
}
}
class Student extends Person {
public function idCard(): string {
// โ allowed: protected is visible to children
return "STUDENT CARD โ ID: $this->idNumber";
}
}
$s = new Student("123456/78/9");
echo $s->idCard(); // works
echo $s->idNumber; // ๐ฅ error: outsiders still locked out
| Inside the class | Inside a child class | Outside | |
|---|---|---|---|
public | โ | โ | โ |
protected | โ | โ | โ |
private | โ | โ | โ |
Stopping inheritance with final
Occasionally you want to say "this may not be overridden / extended". The final
keyword does that โ on a method (final public function pay()...) or a whole class
(final class PaymentGateway { ... }). You'll mostly read it in other
people's code; just know it means "hands off".
Create a parent class Vehicle with a promoted property $brand and a
method start() returning "The Toyota engine starts. Vroom.". Create a
child ElectricCar extends Vehicle that overrides start() to return
"The Tesla powers on. ...silence." โ using $this->brand, not
hard-coded names. Test both.
Show solution
<?php
class Vehicle {
public function __construct(public string $brand) {}
public function start(): string {
return "The $this->brand engine starts. Vroom.";
}
}
class ElectricCar extends Vehicle {
public function start(): string {
return "The $this->brand powers on. ...silence.";
}
}
$v = new Vehicle("Toyota");
$e = new ElectricCar("Tesla");
echo $v->start() . "<br>";
echo $e->start();
Which keyword makes one class inherit from another?
A child class defines its own __construct. What about the parent's constructor?
Who can access a protected property?
class Student extends Personโ Student inherits every property and method of Person.- Use the "is-a" test: inherit only when "Child is a Parent" sounds true.
- Overriding: redeclare a method in the child to replace the parent's version โ polymorphism in action.
parent::method()calls the parent's version โ essential in child constructors.protected= visible to the class and its children, hidden from the outside world.finalforbids overriding/extending.