Object-Oriented Programming (OOP) in PHP

Hello and welcome to a (late) Halloween post! We’re going to discuss PHP OOP by making classes for Ghosts, Zombies, and Vampires.
But before that, a little introduction. If you’ve read my javascript OOP post you may have an idea of what to expect. If not, don’t worry, you don’t need to read it. In a nutshell, objects are structures can hold other variables (properties) or functions (methods), and interact with other objects.

The first step towards OOP in PHP is to make a class. A class defines the methods and properties of objects. Classes can also inherit methods from other classes, but we’ll look at that in a bit. You can think of a class as a template or design for every object of that class.

Imagine a ghost. It has a colour (white, red, blue), and maybe a name. Additionally, it can attack humans by scaring them. You could make a class similar to this:

<?php
class Ghost { //use class to define classes!
    private $colour;
    private $name;

    public function attack ($human_name) {
        print $this->name . ' attacked ' . $human_name . ' by scaring them!';
    }
}
?>

Let’s explain a few things.

    private $colour;

To define object properties and methods, you can use the private, public, or protected keywords. ‘private’ will prevent any code outside that class* from accessing the property, ‘protected’ will allow classes that extend the class (children) to access the property as well, and ‘public’ will allow any piece of code to access the property

* Parents can always access children’s properties.

public function attack ($human_name) {
    print $this->name . ' attacked ' . $human_name . ' by scaring them!';
}

$this works only inside object methods, and always refers to the object you’re working with.

Now we can create our objects.

<?php
$ghost = new Ghost();
?>

Of course that’s not very useful as it is. We can’t change the name or colour here, because it’s private. We may not want to make our properties public and accessible to the whole world, either. So we could make ‘getters’ and ‘setters’.
Basically, these are public methods that allow us to indirectly access private properties. One advantage of them is that we could then deny certain values if necessary, or call other required code.

<?php
class Ghost { //use class to define classes!
    private $colour;
    private $name;

    public function attack ($human_name) {
        print $this->name . ' attacked ' . $human_name . ' by scaring them!';
    }

    public function get_colour () {
        return $this->colour;
    }

    public function get_name () {
        return $this->name;
    }

    public function set_colour ($new_colour) {
        $this->colour = $new_colour;
    }

    public function set_name ($new_name) {
        $this->name = $new_name;
    }
}
?>

Now we can do this:

<?php
$ghost = new Ghost();
$ghost->set_name('erry');
$ghost->set_colour('pink');
$ghost->attack('reader');
?>

All these properties and methods will be shared by every single ghost object. Convenient and tidy, right?

You can take this a bit further with a bit of magic. Every time you create an object with new Ghost(); a constructor function is called. You can define this constructor to do something you want it to:

<?php
class Ghost { //use class to define classes!
    private $colour;
    private $name;

    public function __construct ($name, $colour) { //Constructor!
        $this->name = $name;
        $this->colour = $colour;
    }

    public function attack ($human_name) {
        print $this->name . ' attacked ' . $human_name . ' by scaring them!';
    }

    public function get_colour () {
        return $this->colour;
    }

    public function get_name () {
        return $this->name;
    }

    public function set_colour ($new_colour) {
        $this->colour = $new_colour;
    }

    public function set_name ($new_name) {
        $this->name = $new_name;
    }
}
?>

You can then use it like this:

<?php
$ghost1 = new Ghost('erry','pink');
$ghost1->attack('reader');
$ghost2 = new Ghost('reader','blue');
$ghost2->attack('erry');
?>

We talked about extending classes earlier. Now we can demonstrate this. Imagine you have similar objects that aren’t quite the same: Ghosts, Zombies, and Vampires. They’re all monsters, and they all have colours and names, but they attack people in a different way. If you wanted to write classes for all of them, you’d be copy-pasting code a lot. Instead, you could write a Monster class with the common methods and properties, and extend it for methods and properties that change.

<?php
class Monster { //use class to define classes!
    protected $colour;
    protected $name;

    public function __construct ($name, $colour) { //Constructor!
        $this->name = $name;
        $this->colour = $colour;
    }

    public function get_colour () {
        return $this->colour;
    }

    public function get_name () {
        return $this->name;
    }

    public function set_colour ($new_colour) {
        $this->colour = $new_colour;
    }

    public function set_name ($new_name) {
        $this->name = $new_name;
    }
}

class Ghost extends Monster { //Child classes 'extend' the parent.
    public function attack ($human_name) {
        print $this->name . ' attacks ' . $human_name . ' by scaring them!';
    }
}

class Zombie extends Monster {
    public function attack ($human_name) {
        print $this->name . ' attacks ' . $human_name . ' by eating their brains!';
    }
}

class Vampire extends Monster {
    public function attack ($human_name) {
        print $this->name . ' attacks ' . $human_name . ' by drinking their blood!';
    }
}
?>

As you can see, we just changed the Ghost class to Monster, and removed attack. We then made three child classes that extend it, and just implement the attack method.
Note that we also made the two private properties protected, so that the children inherit them. They won’t be inherited when extending if they’re private.

<?php
$ghost = new Ghost('erry','pink');
$ghost->attack('reader');
$zombie = new Zombie('erry','blue');
$zombie->attack('reader');
$vampire = new Vampire('erry','white');
$vampire->attack('reader');
?>

Will then work as expected :D

You could also declare the attack method in the Monster class without defining it. By making it abstract, you could force all children of the class to implement it

abstract class Monster {
//...
    abstract public function attack ($human_name);
}

Note how that function is declared without being implemented. Classes that extend Monster are now forced to implement the attack method. abstract also prevents you from directly creating Monster objects, forcing you to use children classes, which is also good for our design here.

Well, I think that’s all for now… Boo!

Leave a Comment

Your email address will not be published. Required fields are marked *