facebook youtube pinterest twitter reddit whatsapp instagram

Object Oriented Programming Intro in PHP

We recently went through the understanding of functions in PHP, well, that is one way of writing a PHP code, and the other way is using objects.

In object-oriented programming, objects include data and behavior (code), its methods of operation, and how it interacts with other objects. The definition is a bit blurry...so...

Let's dive in a bit into the reality (I want you to pay close attention to this):

If you look around you, you'll find different types of objects, with their own identity and properties, if you can realize the property, then you know the purpose of the object.

For example, houses, fans, tables, doors, etc are common objects around us. If you look at those objects, then you'll see they differ from one another by some properties. Each and every object in its own state fulfill some purpose for us.

So for example, the fan object will have a component such as a fan motor, blade, electrical wiring, and the likes, all these components are what encompass the fan into becoming something that performs a purpose.

Now, keep the above information at your fingertips, while we dive into object-oriented programming in PHP.

To fully grasp object-oriented programming, you'll want to understand the relationship between the class and the object...

Classes and Objects

Class - The class describes the concept of creating an object, there are not necessarily objects themselves, most people consider a class to be a blueprint (as a guide for how the object would be) for creating new objects.

This is super simple if we take an example, suppose we have a class car. The car is an object and is an instance of a class car. You can have multiple cars from just that class, which is why I think of a class as a code template used to create or generate one or more objects.

So, the below is an example of creating a class:

class User {
    # Body of the class
}

The User class above is an example of a legal class, we haven't actually done anything to the class, but we have defined the template of how our object would be.

Before we get into setting the class properties, let's understand how properties work in a class:

Classes can define special variables called properties, the property which is also known as the member variables holds data that can vary from object to object. So for example, we might use the color red for one car and a color green for another car, you get the idea.

The property in a class is no different than that of a standard variable, the only difference is that when declaring a property, you must precede the property variable with a visibility keyword. This can be public, protected, or private, and it determines the scope from which the property can be accessed.

To better understand the scope of a variable, let's see how it works outside of a class.

The scope refers to the visibility of variables, meaning which parts of your code can see or use the variable, there are mainly two types of scope, and that is the local and global variable scope.

A variable created inside the function is by default only accessible in the function, you won’t be able to use such a variable outside of the function, which is why we say variable is local to the function.

Variable defined outside of the function exists in global scope, let's see an example:

<?php
    $name = "Paul"; # Global Variable

    function scope() {
        echo $name; # reference to local scope variable
    }

    scope();

The above will not produce any output, and that is because the echo statement in the function refers to a local version of the $name variable, which hasn’t been assigned a value within the scope. It doesn’t know of the variable defined in the global scope.

Consider the following example:

<?php
    $name = "Outside Variable"; # Global Variable

    function scope() {
        $name = "Local Variable"; # Local Variable
        echo $name; # reference to local scope variable
    }

    scope();

# 
# Output 
#

Local Variable

That shouldn’t be a surprise, the echo statement is referencing the $name variable that is local to the function, which is the reason why the output was “Local Variable”, in other words, the echo statement is been used inside the function, and when you call the function, it simply reference the $name variable inside the function, now, consider the following example:

<?php
    $name = "Outside Variable"; # Global Variable

    function scope() {
        $name = "Local Variable"; # Local Variable
    }

    echo "$name <br>";
    scope();
    echo "$name <br>";

The output of the both echo statement is "Outside Variable", why though? The variable inside the function won’t be visible outside the function even if you call the function, you can test this by using a new name entirely in the function, you can fix that by declaring a global variable in the function.

The scope is a bit different when creating a class, you can define how you want the properties to be accessed, and from what context using public, protected, and private keywords.

So, let's see an example of declaring some properties using the public keyword:

class User {
    public $first_name = "First Name";
    public $last_name = "Last Name";
    public $age = 23;
}

I set up 3 properties, and assigned a default value to each of the property. Here is where it gets interesting, any objects I instantiate from the User class will be prepolutated with default data.

The public keyword in each property declaration ensures that we can access the property from outside of the object context.

You can access property variables on an object-by-object basis using the characters '->' (the object operator) in conjunction with an object variable and property name, like so:

$user1 = new User();
print $user1->first_name;

# => First Name

I am using the User class as a template for creating the actual object - user, and I am doing that using the new operator. As you can see we are using the new operator in conjunction with the name of a class.

The new operator is invoked with a class name as its only operand and returns an instance of that class; in our case, it generates a User object.

In short, I can generate multiple objects from a class, e.g:

$user1 = new User();
$user2 = new User();
print $user1->first_name;
print $user2->first_name;

The 2 objects; $user1 and $user2 are different objects of the same type generated from a single class.

If all this are still confusing, here is an illustration that would clear thing ups, imagine you have the following channel rack:

1. Channel Racks

We would assume this Channel rack is the class, it has 4 properties, the kick, clap, hat, snare, and the default pattern of how they are all gonna be played.

Here is the thing, the class defines the template of how the object is gonna be modeled, so, in the above example, I am setting properties of how I want my object to look, to make the class do actual work, I'll need to create an instance of the class. if I like I can manipulate the patterns of the kick in a different object entirely or the snare in another object:

Class_objects Ilustration

As you can see from a single set of class, we created different instances of the class known as objects with different manipulations of the properties, if you can understand the illustration, then you've grasped the basic idea of the relationship between class and objects

Now, let's go back to our code, here is the class we are working on:

class User {
    public $first_name = "First Name";
    public $last_name = "Last Name";
    public $age = 23;
}

and here is the object we created:

$user1 = new User();
$user2 = new User();
print $user1->first_name;    # => First Name
print $user2->first_name;    # => First Name

Now, because the properties as public, we can assign values to them just as you can read them, this would replace the default value set in the class:

$user1 = new User();
$user2 = new User();
$user1->first_name = "James Collonel";
$user2->first_name = "Pascalo Faruq";

The -> is used to invoke the property of an object.

Really, you don't have to declare all the properties in the class, you can as well add it dynamically like so:

class Test {
}

$testuser = new Test();
print $testuser -> first_name = "James Miller"; # => James Miller
print $testuser -> age = 34; # => 34

As you can see, I do not even need to declare the properties in the class itself, I am doing it dynamically, but this approach is generally frowned upon, declare properties dynamically can create unnecessary confusion, the worst thing about setting a property dynamically is that PHP won't warn you if you misspell a property name.

So, for example, if the actual property is first_name and I do:

$user1->first_nae = "James";

PHP would consider that as legal as you are declaring the property dynamically, where you would get a problem is when referring to the correct property name when working with it, in short, just bury the idea of setting a property dynamically, we would see a way we can fix incorrect spellings later on with a constructor method.

Now, all we've been doing is creating class, properties, and working with the object's properties from outside the class, this is what we've been doing:

$user1 = new User();

$user1->first_name = "James";
$user1->last_name = "Brad";
$user1->age = 30;

echo "Name: {$user1->first_name} " . "{$user1->last_name}\nAge: {$user1->age}";

// output =>
# Name: James Brad
# Age: 30

Imaging doing this for 7 instance of that class, that's cumbersome, so, let me introduce you to...

Methods

Just as properties are special variables in a class, a method is also a special function declared within a class to perform certain tasks. The only difference between a standard function and a function declared within a class is that a method or a function within a class accepts a visibility keyword.

Like properties, methods can be declared public, protected, or private. declaring a method public means it can be invoked outside of the current object. If you omit the visibility keyword in your method declaration, the method will be declared public implicitly.

Here is an example that simplifies our former code:

<?php

class User {
    public $first_name = "First Name";
    public $last_name = "Last Name";
    public $age = 23;

    public function formatuser() {
        return "Name: " .  $this -> first_name . " "
             . $this -> last_name . " \n" .
             "Age: ". $this -> age;
    }
}

$user1 = new User();

$user1->first_name = "James";
$user1->last_name = "Brad";
$user1->age = 30;

print ($user1->formatuser());

// Output =>
# Name: James Brad 
# Age: 30

The function we created is nothing new, If you have no idea on how functions work in general, please read this guide: Guide To Functions In PHP then come back to this guide.

I am adding the formatuser() method to the User class, and I am using the visibility keyword - Public...which means it can be called from outside the class.

The $this pseudo-variable is used to refer to an object instance from a class. This is like calling whatever stuff you have in your object into the class, if it can't find anything, it would use the default properties you set in the class.

The formatuser() method combines and returns the $first_name, $last_name, and $age properties, which saves us time from formating every instance of an object that wants to use that same formatting.

Here is an example:

<?php

class User {
    public $first_name = "First Name";
    public $last_name = "Last Name";
    public $age = 23;

    public function formatuser() {
        return "Name: " .  $this -> first_name . " "
             . $this -> last_name . " \n" .
             "Age: ". $this -> age . "\n\n";
    }
}

$user1 = new User();

$user1->first_name = "James";
$user1->last_name = "Brad";
$user1->age = 30;

print ($user1->formatuser());

$user2 = new User();

$user2->first_name = "Pascalo";
$user2->last_name = "William";
$user2->age = 23;

print ($user2->formatuser());

// output =>

# Name: James Brad 
# Age: 30
#
# Name: Pascalo William 
# Age: 23

As you can see, we can reuse the same formating over and over again with a new objects, and all this is possible by creating a method within the class.

There is still a little issue, whenever we need a new object with a different property value, we need to write it out outside of the class, it would be crazy if you are trying to instantiate 50 objects with different properties. What if we can somehow create a method that is called automatically when an object is instantiated from a class...introducing...

Constructor Method

A constructor method is invoked when an object is created. You can use it to prepare the essential properties, and save the hassle of using -> whenever you need to set a new property value for a new object, here is how it works:

Note: The __ is a double underscore

<?php

class User {
    public $first_name = "First Name";
    public $last_name = "Last Name";
    public $age = 23;

    public function __construct (
    $first_name,
    $last_name,
    $age
    ) {
        $this -> first_name = $first_name;
        $this -> last_name = $last_name;
        $this -> age = $age;
    }

    public function formatuser() {
        return "Name: " .  $this -> first_name . " "
             . $this -> last_name . " \n" .
             "Age: ". $this -> age . "\n\n";
    }
}

$user1 = new User(
    "James",
    "Brad",
    23
);

print ($user1->formatuser());

$user2 = new User(
    "Pascal",
    "William",
    44
);

print ($user2->formatuser());

// output =>

# Name: James Brad 
# Age: 30
#
# Name: Pascalo William 
# Age: 23

Look how it becomes tidy, any arguments supplied are passed to the constructor, I am passing the first name, the last, and the age to the constructor, you don't have to use the property name as the name of the constructor argument, you can use something meaningful.

Like in this section, I can have:

    public function __construct (
    $first,
    $last,
    $age
    ) {
        $this -> first_name = $first;
        $this -> last_name = $last;
        $this -> age = $age;
    }

Like, it doesn't really matter, the $this pseudo-variable is used to assign values to each of the object's properties.

You don't have to worry about misspelling properties name when using it with an object, we've already created the necessary setup with the __constructor method.

But, we still have work to do, we aren't enforcing the type of data our properties are holding, for example, we can ensure that the $age property is always made up of integer, the issue is how can you achieve this is property data is passed from outside the class... introducing...

Arguments and Types

A data type is an attribute of data that tells the interpreter how the programs intend to use the given data. For example, you use the string type to display character data and manipulate such data with string functions. Integer type for displaying a number, or for use in a mathematical expression, a boolean type for use in a test expression, and so on.

These categories are known as primitive types, for example, our user1 object belongs to the primitive type object.

The interesting thing about coding in PHP is the fact that it is a loosely typed language, meaning a single variable can contain any type of data, string, boolean, etc. Unlike other languages where you need to first declare what the variable would do.

While this gives us great flexibility, it can also lead to an issue if PHP is mixing or coercing data into what you are not intending, so, when you use a strong type, it can be helpful in avoiding putting say an integer into a property that is declared to be a string data type.

There are a couple of ways we can enforce a type, the first way is by determining the type of a variable's value using PHP type-checking function, e.g is_bool is used for checking boolean values, is_int for checking integer values, is_float for floating-point number, is_string for character data, is_object for an object, is_array for an array, and so on. If the particular type you are checking is true, you can then perform an action.

But this well is an archaic solution, a better solution is using the class type declaration, also know as type hints.

To enforce type on a property, you can put the strict type declaration at the top of your file:

<?php
declare(strict_types=1);

You can add a scalar type declarations to add some constraints to the User class:

<?php
declare(strict_types=1);

class User {
    public $first_name = "First Name";
    public $last_name = "Last Name";
    public $age = 23;

    public function __construct (
    string $first,
    string $last,
    int $age
    ) {
        $this -> first_name = $first;
        $this -> last_name = $last;
        $this -> age = $age;
    }

By doing our constructor method this way, we can be sure that the $first_name and $last_name will always contain a string data, and the $age argument will contain integer.

Here is what you would get if you pass a string to the integer arguments:

<?php
declare(strict_types=1);

class User {
    public $first_name = "First Name";
    public $last_name = "Last Name";
    public $age = 23;

    public function __construct (
    string $first,
    string $last,
    int $age
    ) {
        $this -> first_name = $first;
        $this -> last_name = $last;
        $this -> age = $age;
    }

    public function formatuser() {
        return "Name: " .  $this -> first_name . " "
             . $this -> last_name . " \n" .
             "Age: ". $this -> age . "\n\n";
    }
}

$user1 = new User(
    "James",
    "Brad",
    "23"
);

// =>
# PHP TypeError:  Argument 3 passed to User::__construct() must be of the type int, string given

I pass three strings to the constructor, but I failed at the 3rd argument by passing a string instead of an integer. This would give you a straight error and thus would save us the hassle of mixing unnecessary types with one another.

Inheritance

Inheritance is the means by which one or more children's classes can be derived from a parent class. When a class inherits from another class, it is said to be a subclass of that class.

A child class is derived from and inherits characteristics from the parent. The characteristic can be properties, methods, and even both.  The child class will typically add new functionality to that provided by its parent (also known as a superclass); for this reason, a child class is said to extend its parent.

To better understand this, let's assume I am building a class around musical records that would contain musical artists, their songs, and the genre of music they make.

I can do something like the following:

<?php
declare(strict_types=1);

class musicalRecords {
    public $artiste_name = "artiste name";
    public $song_title = "song title";
    public $artiste_description = "artiste description";
    public $no_of_songs = 23;
    public $producer_name = "producer name";
    public $genre_name = "genre name";
    public $genre_description = "genre description";

    public function __construct (
    string $artiste,
    string $song,
    string $artiste_description,
    int $no_of_songs,
    string $producer,
    string $genre,
    string $genre_description
    ) {
        $this -> artiste_name = $artiste;
        $this -> song_title = $song;
        $this -> artiste_description = $artiste_description;
        $this -> no_of_songs = $no_of_songs;
        $this -> producer_name = $producer;
        $this -> genre_name = $genre;
        $this -> genre_description = $genre_description;
    }
}

Even without doing anything, you can see how confusing it is right off the bat, but we would come back in a moment to that. So, if I want to only print the artiste name, song, and the no of plays, I can do something like this:

<?php
declare(strict_types=1);

class musicalRecords {
    public $artiste_name = "artiste name";
    public $song_title = "song title";
    public $artiste_description = "artiste description";
    public $no_of_songs = 23;
    public $producer_name = "producer name";
    public $genre_name = "genre name";
    public $genre_description = "genre description";

    public function __construct (
    string $artiste,
    string $song,
    string $artiste_description,
    int $no_of_songs,
    string $producer,
    string $genre,
    string $genre_description
    ) {
        $this -> artiste_name = $artiste;
        $this -> song_title = $song;
        $this -> artiste_description = $artiste_description;
        $this -> no_of_songs = $no_of_songs;
        $this -> producer_name = $producer;
        $this -> genre_name = $genre;
        $this -> genre_description = $genre_description;
    }

    public function artistesummary() {
        return "Name: " .  $this -> artiste_name . " \n" . "Song: " . $this -> song_title . " \n" . "Plays: ". $this -> no_of_songs . "\n\n";
    }
}

$artiste = new musicalRecords (
    "Avicii",
    "Silhouette",
    "Progressive House Producer"
);

print ($artiste->artistesummary());

You would expect this to work, but it won't, and that is because the constructor method expects you to pass at least 7 arguments, and we are only passing 3, and if you recall that the constructor method is invoked when an object is created. So, you can fix that by setting a default argument in the constructor like so:

class musicalRecords {
    public $artiste_name;
    public $song_title;
    public $artiste_description;
    public $no_of_songs;
    public $producer_name;
    public $genre_name;
    public $genre_description;

    public function __construct (
    string $artiste  = "artiste name",
    string $song  = "song title",
    string $artiste_description  = "artiste description",
    int $no_of_songs = 30,
    string $producer = "Producer Name",
    string $genre = "Genre Name",
    string $genre_description = "Genre Description"
    )

As you can see, I have removed the default property values in the class to avoid duplication in the constructor method, and now, when we instantiate an object from the class to print the artiste name, song, and no of plays, it would work:

<?php
declare(strict_types=1);

class musicalRecords {
    public $artiste_name;
    public $song_title;
    public $artiste_description;
    public $no_of_songs;
    public $producer_name;
    public $genre_name;
    public $genre_description;

    public function __construct (
    string $artiste  = "artiste name",
    string $song  = "song title",
    string $artiste_description  = "artiste description",
    int $no_of_songs = 30,
    string $producer = "Producer Name",
    string $genre = "Genre Name",
    string $genre_description = "Genre Description"
    ) {
        $this -> artiste_name = $artiste;
        $this -> song_title = $song;
        $this -> artiste_description = $artiste_description;
        $this -> no_of_songs = $no_of_songs;
        $this -> producer_name = $producer;
        $this -> genre_name = $genre;
        $this -> genre_description = $genre_description;
    }

    public function artistesummary() {
        return "Name: " .  $this -> artiste_name . " \n" . "Song: " . $this -> song_title . " \n" . "Plays: ". $this -> no_of_songs . "\n\n";
    }
}

$artiste = new musicalRecords (
    "Avicii",
    "Silhouette",
    "Progressive House Producer"
);

print ($artiste->artistesummary());

// =>
# Name: Avicii 
# Song: Silhouette 
# Plays: 30

There are scenario where we might want a different methods for different purposes, for example, we can provide a method to access the genre name, and genre description and another method to access the artiste name and artiste description:

...
    public function getgenre() {
        return "Genre: " .  $this -> genre_name . " \n" . "Genre Description: " . $this -> genre_description . " \n";
    }

    public function getartiste() {
        return "Artiste: " .  $this -> artiste_name . " \n" . "Artiste Description: " . $this -> artiste_description . " \n";
    }
...

I have provided method access to the artiste name and description plus a method for the genre name and description. To make use of the 2 new methods we would instantiate an object from the class musicalRecords, you would do something like:

$artiste_info = new musicalRecords (
    "Avicii",
    "Silhouette",
    "Progressive House Producer"
);
print ($artiste_info->getartiste());

echo "\n";

$genre_info = new musicalRecords (
    "Avicii",
    "Silhouette",
    "Progressive House Producer",
    23,
    "Avicii",
    "Progressive House",
    "A subgenre of house music"
);
print ($genre_info->getgenre());

// output =>
#
# Artiste: Avicii 
# Artiste Description: Progressive House Producer 
#
# Genre: Progressive House 
# Genre Description: A subgenre of house music
#

Can you spot the issue, we need to instantiate using unnecessary constructor argument, this is how it looks in my IDE:

5. Unnecessary Constructor Argument

For the artisteinfo, I only needed the artiste and the artiste_description, if I don't pass them accordingly the method would give me a wrong data, it is even worst when dealing with the genre_info, look at the stuff I had to type just to get it to format the genre details.

This isn't something you would want to deal with when you add more type with its own methods, it would become really hard to manage. It leads to bloated objects with redundant properties and methods.

You can simplify this by creating different classes for different methods, doing things that way might also cause duplication, if for example, you want a certain method to behave identically for each class, you would need to make changes in both classes.

The golden method to solving this kind of issue is using Inheritance.

When building an inheritance tree, the first thing you should keep an eye on is the elements of the base class that don’t fit together or that need to be handled differently.

The getproducer() and getgenre() methods do not belong together, we can do the following to simplify the approach:

<?php
declare(strict_types=1);

class musicalRecords
{
    public $artiste_name;
    public $song_title;
    public $artiste_description;
    public $no_of_songs;
    public $producer_name;
    public $genre_name;
    public $genre_description;

    public function __construct(
        string $artiste = "artiste name",
        string $song = "song title",
        string $artiste_description = "artiste description",
        int $no_of_songs = 30,
        string $producer = "Producer Name",
        string $genre = "Genre Name",
        string $genre_description = "Genre Description"
    )
    {
        $this->artiste_name = $artiste;
        $this->song_title = $song;
        $this->artiste_description = $artiste_description;
        $this->no_of_songs = $no_of_songs;
        $this->producer_name = $producer;
        $this->genre_name = $genre;
        $this->genre_description = $genre_description;
    }

    public function artistesummary()
    {
        return "Name: " . $this->artiste_name . " \n" . "Song: " . $this->song_title . " \n" . "Plays: " . $this->no_of_songs . "\n\n";
    }
}

class musicalRecords_Genre extends musicalRecords {

    public function getgenre() {
        return "Genre: " .  $this -> genre_name . " \n" . "Genre Description: " . $this -> genre_description . " \n";
    }
}

class musicalRecords_Artiste extends musicalRecords {
    public function getartiste() {
        return "Artiste: " .  $this -> artiste_name . " \n" . "Artiste Description: " . $this -> artiste_description . " \n";
    }
}

$artiste_info = new musicalRecords_Artiste (
    "Avicii",
    "Silhouette",
    "Progressive House Producer"
);
print ($artiste_info->getartiste());

echo "\n";

$genre_info = new musicalRecords_Genre (
    "Avicii",
    "Silhouette",
    "Progressive House Producer",
    23,
    "Avicii",
    "Progressive House",
    "A subgenre of house music"
);
print ($genre_info->getgenre());

// Output =>
# Artiste: Avicii
# Artiste Description: Progressive House Producer

# Genre: Progressive House
# Genre Description: A subgenre of house music

To create a child class, you must use the extends keyword in the class declaration. In the example, I created two new classes, musicalRecords_Genre and musicalRecords_Artiste. The both classes extend the musicalRecords class.

This doesn't make much difference though, the only thing it simplifies for us is the class name when instantiating a new object.

The derived classes do not define constructors, the parent class’s constructor is automatically invoked when they are instantiated.

The child classes inherit access to all the parent’s public and protected methods (with the exception of private methods). As you can see we are calling the getgenre() method on an object instantiated from the musicalRecords_Genre class, we are able to do this because the child classes inherit the behavior of the parent class.

Like I said earlier, what we did doesn't make any differences from when we didn't create a child class except that we can now instantiate with a different name. We are still managing data in the base class that should be handled by its children.

The musicalRecords_Genre class should handle the $genre_name, and $genre_description argument and property, and the musicalRecords_Artiste class should handle the $artiste_name and $artiste_description argument and property.

To make this work, we would define constructor methods in each of the child classes. like so:

<?php
declare(strict_types=1);

class musicalRecords
{
    public $artiste_name;
    public $song_title;
    public $artiste_description;
    public $no_of_songs;
    public $producer_name;

    public function __construct(
        string $artiste = "artiste name",
        string $song = "song title",
        string $artiste_description = "artiste description",
        int $no_of_songs = 30,
        string $producer = "Producer Name"
    )
    {
        $this->artiste_name = $artiste;
        $this->song_title = $song;
        $this->artiste_description = $artiste_description;
        $this->no_of_songs = $no_of_songs;
        $this->producer_name = $producer;
    }

    public function artistesummary()
    {
        return "Name: " . $this->artiste_name . " \n" . "Song: " . $this->song_title . " \n" . "Plays: " . $this->no_of_songs . "\n\n";
    }
}

class musicalRecords_Genre extends musicalRecords {

    public $genre_name;
    public $genre_description;

    public function __construct(
        string $genre = "Genre Name",
        string $genre_description = "Genre Description"
    )
    {
        $this->genre_name = $genre;
        $this->genre_description = $genre_description;
    }

    public function getgenre() {
        return "Genre: " .  $this -> genre_name . " \n" . "Genre Description: " . $this -> genre_description . " \n";
    }
}

class musicalRecords_Artiste extends musicalRecords {

    public $artiste_name;
    public $artiste_description;

    public function __construct(
        string $artiste = "Artiste Name",
        string $artiste_description = "Artiste Description"
    )
    {
        $this->artiste_name  = $artiste;
        $this->artiste_description = $artiste_description;
    }
    public function getartiste() {
        return "Artiste: " .  $this -> artiste_name . " \n" . "Artiste Description: " . $this -> artiste_description . " \n";
    }
}

$artiste_info = new musicalRecords_Artiste (
    "Avicii",
    "Progressive House Producer"
);
print ($artiste_info->getartiste());

echo "\n";

$genre_info = new musicalRecords_Genre (
    "Progressive House",
    "A subgenre of house music"
);
print ($genre_info->getgenre());

// Output =>
# Artiste: Avicii
# Artiste Description: Progressive House Producer

# Genre: Progressive House
# Genre Description: A subgenre of house music

As you can see each class now has its own properties with its own constructor method, and thus we can make things simple to manage.

However, when you define a constructor in a child class, you become responsible for passing any arguments on to the parent. If you fail to do this, you can end up with a partially constructed object.

To invoke a method in a parent class, you must first find a way of referring to the class itself: a handle. PHP provides us with the parent keyword for this purpose.

To refer to a method in the context of a class rather than an object, you use:: rather than ->: for example:

parent::__construct()

This means  “Invoke the __construct() method of the parent class.”

Why do we need to use the parent::__constructor though?

If a child class has a constructor, the parent constructor won't be called, unless we explicitly call it using parent::__constructor, in short, call the parent constructor if it does something that you need. Most of the time, you would be extending the parent constructor. So again, call the parent constructor if you need something from it.

Suppose we want to create a class that summarizes the artiste or like artiste info, we can do:

<?php
declare(strict_types=1);

class musicalRecords
{
    public $artiste_name;
    public $song_title;
    public $artiste_description;
    public $no_of_songs;
    public $producer_name;

    public function __construct(
        string $artiste = "artiste name",
        string $song = "song title",
        string $artiste_description = "artiste description",
        int $no_of_songs = 30,
        string $producer = "Producer Name"
    )
    {
        $this->artiste_name = $artiste;
        $this->song_title = $song;
        $this->artiste_description = $artiste_description;
        $this->no_of_songs = $no_of_songs;
        $this->producer_name = $producer;
    }
}

class musicalRecords_ArtisteInfo extends musicalRecords {

    public $artiste_name;
    public $artiste_description;

    public function __construct(
        string $artiste = "Artiste Name",
        string $artiste_description = "Artiste Description",
        int  $no_of_songs = 40,
        string $song = "Song Name",
        string $producer = "Producer Name"
    )
    {
        parent::__construct (
            $artiste,
            $song,
            $artiste_description,
            $no_of_songs,
            $producer
        );

        $this->artiste_name  = $artiste;
        $this->artiste_description = $artiste_description;
        $this->no_of_songs = $no_of_songs;
    }
    public function getartisteinfo() {
        return "Artiste: " .  $this -> artiste_name . " \n" . "Artiste Description: " . $this -> artiste_description . " \n"
            . "No of Songs: " .  $this -> no_of_songs . " \n";
    }
}

$artiste_info = new musicalRecords_ArtisteInfo (
    "Avicii",
    "Progressive House Producer",
    50
);
print ($artiste_info->getartisteinfo());


// Output =>
# Artiste: Avicii
# Artiste Description: Progressive House Producer
# No of Songs: 50

You see what I did there? Since I need to run the parent class constructor as well,  I explicitly called it by using parent::__construct().

Note that you must make sure the parent arguments are correctly ordered. If it is like this in the parent constructor:

public function __construct(
        string $artiste = "artiste name",
        string $song = "song title",
        string $artiste_description = "artiste description",
        int $no_of_songs = 30,
        string $producer = "Producer Name"
    )

Make sure when calling it in a child class, you don't deviate away from the order as we did above, or else you might miss up the wrong data.

So, basically, the child class invokes the constructor of its parent before setting its own properties. The base class now knows only about its own data.

Note that the following part is not needed in the child class:

class musicalRecords_ArtisteInfo extends musicalRecords {

    public $artiste_name;
    public $artiste_description;

Since we've called the parent::constructor we don't need it anymore, it is useful if you want to add a distinct property to only the class, then couple with the parent::constructor you can do whatever you want. For example, you can add a no_of_sold property, and since we have called the parent::constructor we can mix them together.

Access Modifiers: Public, Private and Protected

By using the visibility keywords, we can manage access to our classes. So, far, I have declared all properties public.

Public access is the default setting for methods and for properties.

You have the option to declare public, private, or protected...

Public properties and methods can be accessed from any context, A private method or property can only be accessed from within the enclosing class, subclasses have no access, and a protected method or property can only be accessed from within either the enclosing class or from a subclass. No external code is granted access.

Why use visibility keywords? By using a visibility keyword, you can specify how you want to expose only those aspects of the classes that are required by a client.

By preventing a client from accessing certain properties, access control can also help prevent bugs in your code.

Most times, you might want to deny direct access to properties, providing methods instead that relay the needed values. Such methods are known as accessors or getters and setters.

Say we have private properties like so

<?php
declare(strict_types=1);

class musicalRecords
{
    private $artiste_name;
    private $song_title;

    public function __construct(
        string $artiste = "artiste name",
        string $song = "song title"
    )
    {
        $this->artiste_name = $artiste;
        $this->song_title = $song;
    }

}

and we try accessing or getting the property like so:

echo $artiste->artiste_name;

You will get:

Cannot access private property musicalRecords::$artiste_name ...

If you also try to set it, and try accessing it:

$artiste->artiste_name = "Beautiful Nubia";
echo $artiste->artiste_name;

You will get the same error, to fix it, we would create a method in the class that simply return the values as we've done before in other examples:

<?php
declare(strict_types=1);

class musicalRecords
{
    private $artiste_name;
    private $song_title;

    public function __construct(
        string $artiste = "artiste name",
        string $song = "song title"
    )
    {
        $this->artiste_name = $artiste;
        $this->song_title = $song;
    }

    public function getartisteinfo()
    {
        return "Artiste: " . $this->artiste_name . " \n" . "Song: " . $this->song_title;
    }
}

You then get it this way:

$artiste = new musicalRecords(
    "Asa",
    "Jailer"
);

print ($artiste->getartisteinfo());

// Output =>
# Artiste: Asa 
# Song: Jailer

Nothing new here, we've done that before. Doing things that way makes it much safer as we cannot access it directly, we gotta access it through the method.

We can only get, if you try setting, you'll get an error stating you cannot access private property. If you want to set, then you need to create a new method for that.

Here is an example:

public function setName($art_name)
    {
        $this->artiste_name = $art_name;
    }

The method name is called setName, and it takes in a parameter $art_name. We then say $this->artiste_name = $art_name; meaning, the $art_name is the name that is passed in to the property artiste_name.

So, to set a name, we then do:

$artiste = new musicalRecords(
    "Asa",
    "Jailer"
);


print ($artiste->setName("Beautiful Nubia"));

print ($artiste->getartisteinfo());

// Output =>
# Artiste: Beautiful Nubia 
# Song: Jailer

As you can see, it replaces whatever that is already set, e.g it overrides Asa with Beautiful Nubia.

If you have multiple properties, and you want to get or set them individually, it would make sense to create a bunch of getters and setters method for all of them. Fortunately, there are two magic methods we can use, the first one is __get and the second one is __set.

Let's start with get:

public function __get($property){
      if(property_exists($this, $property)){
        return $this->$property;
      }
    }

The doule underscore (__) represent a magic method just like the constructor method, so, here is how it works:

  • We are passing in $property in the __get argument
  • if(property_exists($this, $property)) is testing if the property actually exist in the class, the $this means the class we are working, and then the $property we are passing in
  • If the property exist we then return the $this->$property, which would represent the property we are passing in.

So, we just created a get magic method, so, to get the $artiste_name we could either use the method we initially created or use the get magic method like so:

<?php
declare(strict_types=1);

class musicalRecords {
    private $artiste_name;
    private $song_title;

    public function __construct(
        string $artiste = "artiste name",
        string $song = "song title"
    ) {
        $this->artiste_name = $artiste;
        $this->song_title = $song;
    }

    public function getartisteinfo() {
        return "Artiste: " . $this->artiste_name . " \n" . "Song: " . $this->song_title;
    }

    public function setName($art_name) {
        $this->artiste_name = $art_name;
    }

    public function __get($property){
        if(property_exists($this, $property)){
            return $this->$property;
        }
    }
}

$artiste = new musicalRecords(
    "Asa",
    "Jailer"
);

print $artiste->__get('artiste_name');

// output
# Asa

As you can see we are no longer using the method we initially created to access the data, we are now using the get magic method, if I want to change the property, I just replace it like so:

$artiste = new musicalRecords(
    "Asa",
    "Jailer"
);

print $artiste->__get('song_title');

// output
# Jailer

Easy peesy, I no longer have to create different methods for different properties, the get magic method save us the hassle.

To create a set magic method, we can do the following:

public function __set($property, $value){
        if(property_exists($this, $property)){
            $this->$property = $value;
        }
        return $this;
    }

Here is our it works:

  • __set($property, $value){ - The set would take the property you wanna set, and the value you want to set it to
  • if(property_exists($this, $property)) is testing if the property actually exist in the class, the $this means the class we are working, and then the $property we are passing in
  • If the property exist we set it to the property that is passed in using $this->$property = $value;
  • we then return $this

So, to set a value using the set magic method, I can do:

$artiste = new musicalRecords(
    "Asa",
    "Jailer"
);

$artiste->__set('song_title', "Colonial Mentality");
print $artiste->__get('song_title');

// Output
# Colonial Mentality

Becareful when using the set and get magic method, they are consider slower than the regular getter and setter we initially created. Whether this is true is up to you, all in all, it depends on your use cases.

Tip: Make properties private or protected at first. then if you ever need to make the public you can do so when needed. The methods in your classes will mostly be public, but you can also lock it down if you ever need to. A method that provides local functionality for other methods in your class has no relevance to your class’s users. Make it private or protected.

Related Post(s)

  • Building Dependency Injection and The Container from Scratch in PHP

    In this guide, you'll learn about dependency injection and a way to build a simple DIC (Dependency Injection Container) in PHP using PSR-11 from scratch. First, What is an Ordinary Dependency? This

  • Creating a Tiny PHP MVC Framework From Scratch

    In this guide, we would go over creating a tiny PHP MVC Framework, this would sharpen your knowledge on how major frameworks (e.g Codeigniter or Laravel) works in general. I believe if you can unders

  • PHP Pluggable and Modular System – Part 2 (Implementation) [Event Dispatcher]

    In the first series of this guide, we discussed the theoretical aspect of building a pluggable system in PHP, I wrote a bit of code in that guide plus a couple of stuff you should avoid, you can lear

  • Best Way To Implement a Non-Breaking Friendly URL In PHP or Laravel or Any Language

    I was working on the link structure of my new Laravel app, and out of the blue I said: "What would happen if a user changes the slug of a post?" First Attempt - 301 Redirection The first solution I t

  • PHP Pluggable and Modular System - Part 1 (Abstract View) [Observable and Mediator Pattern]

    If there is one thing that is brutally confusing to me ever since I started coding, it would be a way to not only implement a Modular system but a way to make it extensible (adding and removing plugi

  • Static Methods and Properties In PHP

    We've recently dived deep into the basics of Object-Programming language in PHP, in this guide, we would look into static methods and properties. In the OOP guide, I said class describes the concept