<?php OOP: Zero to Hero Chapter 07 of 20

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.

๐Ÿ‘จโ€๐Ÿ‘ง
Real-world analogy

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");
Hello, I am Chanda. Chanda is studying BSc Computing. Hello, I am Mr. Mwale. Mr. Mwale is teaching 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.

The "is-a" test

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!
}
Hello, I am Chanda. Good morning class, I am Mr. Mwale, your lecturer.

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();
Hello, I am Chanda. My student number is LGU2026-001.
Golden rule

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
STUDENT CARD โ€” ID: 123456/78/9 Fatal error: Cannot access protected property
Inside the classInside a child classOutside
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".

โœ๏ธ Try it yourself

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();
The Toyota engine starts. Vroom. The Tesla powers on. ...silence.
Quick quiz

Which keyword makes one class inherit from another?

Quick quiz

A child class defines its own __construct. What about the parent's constructor?

Quick quiz

Who can access a protected property?

๐Ÿ”‘ Key points
  • 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.
  • final forbids overriding/extending.