Chapter 9: Object-Oriented Programming
In the next two chapters, we are going to cover another important concept in PHP – the concept of object-oriented programming (OOP).
OOP is a major topic. Hence, a full discussion of it is beyond the scope of this book. In this chapter, we’ll cover the core concepts in OOP. In the next, we’ll talk about inheritance.
Ready? Let’s get started.
9.1 What is OOP?
First off, what is OOP?
OOP is an approach to programming where we organize our code by grouping related variables, constants and functions into a class. This class serves as a template from which we can create what is known as objects. Objects can then be used to store data and access functions defined inside the class.
Confused? No worries. The best way to understand OOP is to look at an example. Let’s write our own class now.
9.2 Writing our own class
To write our own class, we use the class
keyword, followed by the name of the class.
The name of the class is not case-sensitive and can contain letters, numbers, or underscores. However, it cannot be a PHP reserved word (i.e., a word that has a predefined meaning in PHP, such as echo
, switch
, break
, etc.) and cannot start with a number.
It is a convention to use pascal case when naming our classes. Pascal case refers to the practice of capitalizing the first letter of each word, including the first word (e.g., ClassName).
To create our class, let’s first create a file in Brackets and save it as Movie.php to our htdocs folder. Next, add the following code to Movie.php.
<?php
class Movie{
//Add class members here
}
Here, we use the class
keyword to declare a class called Movie
.
Within the Movie
class (inside the pair of braces {}
), we are going to add variables, constants and functions. Variables and functions declared inside a class are known as properties and methods respectively. Collectively, these properties, constants and methods are known as class members.
We’ll add properties to our Movie
class first. To do that, add the following code to Movie.php (inside the pair of braces):
private $id;
public $title;
public $rentalPrice;
Here, we declare three properties: $id
, $title
and $rentalPrice
. Notice that we did not initialize them in the code above? This is because we’ll be initializing them in a special method known as the constructor later. Also, note that we preceded the property declarations with the words public
or private
. Don’t worry about these keywords at the moment; we’ll come back to them later.
Next, let’s add a constant to our Movie
class. To do that, add the following code to Movie.php (inside the pair of braces):
const DISCOUNT = 10;
Here, we define a constant called DISCOUNT
and assign the value 10
to it. Notice that we define a constant differently here (compared to what we learned in Chapter 4.1)? Indeed, to define a constant inside a class, we do not use the define()
function. Instead, we use the const
keyword as shown above.
Finally, let’s add some methods to our class. As mentioned previously, a method is a function that is defined inside a class. We’ll start with the constructor.
A constructor is a magic method in PHP. Magic methods are methods that have special functionalities in PHP; their names are predefined and always start with two underscores.
The constructor is named __construct()
and is the first method to be called whenever we create an object from the class. We typically use this method to initialize the properties in the class.
Add the following code to the Movie
class after the constant (but before the closing brace of the Movie
class):
public function __construct($pId, $pTitle, $pRentalPrice){
$this->id = $pId;
$this->title = $pTitle;
$this->rentalPrice = $pRentalPrice;
}
Here, we define a constructor with three parameters: $pId
, $pTitle
and $pRentalPrice
.
Inside the constructor, we initialize $id
, $title
and $rentalPrice
with the values of $pId
, $pTitle
and $pRentalPrice
respectively.
Notice a new keyword $this
in the code above? We’ll explain this keyword later when we learn to create objects in the next section. For now, just know that whenever we want to access the properties and methods of a class inside the class, we need to use the $this
keyword, followed by the ->
operator.
Next, let’s add a regular (i.e., non-magic) method to our Movie
class. Add the following method after the __construct()
method (but before the closing brace of the Movie
class):
public function conversion($country){
$rate = 1;
switch($country){
case 'UK':
$rate = 0.76;
break;
case 'Japan':
$rate = 110;
break;
}
return round($rate*$this->rentalPrice, 2);
}
Here, we define a method called conversion()
. This method has one parameter – $country
– and converts USD to pounds or yen depending on the value of $country
.
Within the method, we declare and initialize a variable called $rate
and use a switch
statement to update its value based on the value of $country
.
Next, we multiply $rate
with the $rentalPrice
property ( $rate*$this->rentalPrice
) and pass the product as an argument to a built-in function called round()
. This function accepts two arguments and rounds the first argument off to the precision indicated by the second. In our method, we round the product off to 2 decimal places.
Finally, we use the return
keyword to return the result of the round()
function.
Within the method, notice that we did not use the $this
keyword to access $country
and $rate
?
This is because $country
is a parameter while $rate
is a local variable (i.e., a variable declared inside the method). In other words, they are not class properties. We use the $this
keyword only when accessing class properties and methods. For instance, we use the $this
keyword to access the $rentalPrice
property.
Got it? Once you have added the conversion()
method to the Movie
class, our class is complete. We are now ready to make use of this class. To do that, we need to create an object from it.
9.3 Creating an Object
To create an object, we use the new
keyword. An object is also known as an instance of the class, and the process of creating an object is known as instantiating the class.
Create a new file in Bracket and save it as chap9.php to your htdocs folder. Add the following code to chap9.php:
<?php
include 'Movie.php';
$mov1 = new Movie('N0001', 'Lusso', 4.99);
Here, we first use the include
statement to include Movie.php (which contains the code for the Movie
class).
Next, we use the new
keyword to create a Movie
object called $mov1
, passing 'N0001'
, 'Lusso'
and 4.99
as arguments to the __construct()
method.
As __construct()
is a magic method, we do not call it using its name.
Instead, when we create a new Movie
object, PHP looks for the __construct()
method and executes it for us automatically.
Remember the $this
keyword in our Movie
class constructor? $this
refers to the current object. When we use the statement
$mov1 = new Movie('N0001', 'Lusso', 4.99);
to create $mov1
, $this
refers to $mov1
. In other words, when PHP executes the constructor, the statement
$this->id = $pId;
in the constructor becomes
$mov1->id = 'N0001';
As a result, the value 'N0001'
gets assigned to the $id
property of $mov1
. The same applies to the other two assignment statements in the constructor. Hence, the values 'Lusso'
and 4.99
get assigned to the $title
and $rentalPrice
properties of $mov1
respectively.
Next, let’s create a second Movie
object called $mov2
using the statement below:
$mov2 = new Movie('P0002', 'Junior', 5.99);
When PHP creates $mov2
and calls the constructor, $this
refers to $mov2
. Hence, the statement
$this->id = $pId;
in the constructor becomes
$mov2->id = 'P0002';
The same applies to the other two assignment statements in the constructor. As a result, the values 'P0002'
, 'Junior'
and 5.99
get assigned to the $id
, $title
and $rentalPrice
properties of $mov2
respectively.
Got it? Good! Let’s move on.
9.4 Accessing Class Members
After creating an object, we can use the object name and the ->
operator to access its properties and methods.
For instance, to access the properties and methods of $mov1
in chap9.php, we use the code below:
echo $mov1->title.'<BR>';
echo $mov1->conversion('Japan').'<BR>';
Here, we first use the ->
operator to access the $title
property of $mov1
. Next, we use the ->
operator to call the conversion()
method, passing 'Japan'
as an argument to the method.
Add the code above to chap9.php (after the instantiation statements) and load the page, you’ll get
Lusso
548.9
as the output. Pretty straightforward, right?
Now, suppose we want to access the DISCOUNT
constant defined in the Movie
class, how do we do that?
To access a class constant, we do not use the ->
operator. Instead, we use the ::
operator.
This is because class constants are different from class properties; they are allocated once per class, not once for each object. This means that all objects of the same class ( $mov1
and $mov2
in our example) share the same constants. In other words, even if there are 100 Movie
objects, there is only one memory location allocated to store the DISCOUNT
constant.
To access the DISCOUNT
constant in the Movie
class, add the following code to chap9.php:
echo Movie::DISCOUNT.'<BR>';
echo $mov1::DISCOUNT.'<BR>';
echo $mov2::DISCOUNT.'<BR>';
If you run the code above, you’ll get
10
displayed three times. This shows that we can use either the class name ( Movie
) or the object name ( $mov1
or $mov2
) to access a class constant. All three give us the same value as they are accessing the same memory location.
9.5 Access Modifiers
Now that we know how to create objects and access class members using these objects, let’s discuss a concept we skipped previously – the public
and private
keywords.
These two keywords are known as access modifiers; they serve as gatekeepers controlling where we can access a particular class member.
public
class members can be assessed everywhere while private
class members can only be accessed within the class in which they are declared.
In our Movie
class, we have two public
properties ( $title
and $rentalPrice
) and one private
property ( $id
).
Previously, we learned to access the $title
property of $mov1
in chap9.php. As $title
is a public
class property, we had no problems accessing it in chap9.php. Now, let’s try accessing the $id
property. Add the line
echo $mov1->id.'<BR>';
to chap9.php and load the page again. What do you get? You get
Fatal error: Uncaught Error: Cannot access private property Movie::$id...
added to the output, right? This is because $id
is a private
property. As private
class members can only be assessed within the class in which they are declared, we are not allowed to access the $id
property in chap9.php, which is outside the Movie
class.
This is the gist of how access modifiers work; they basically control whether we can access a particular class member outside the class in which it is declared. Got it?
In PHP, access modifiers are mandatory for properties but optional for methods. If we fail to declare the modifier for a property, we’ll get an error message. If we do not declare the modifier for methods, the default modifier is public
. As of PHP 7.1.0, we can also add access modifiers for class constants. If we do not declare the modifier for constants, the default is public
.
Besides public
and private
members, PHP also has protected
members. These members can be accessed inside the class in which they are declared and any subclass that inherits from that class. We’ll discuss protected
members and inheritance in the next chapter.
At this point, some of you may be wondering why we want class members to be private
. For instance, why bother declaring the $id
property if we cannot access it?
The reason is that while private
members cannot be accessed outside the class in which they are declared, they can be accessed inside it. For instance, we can write a function inside the Movie
class to display the page heading based on the $id
property. To see how that works, add the code below to Movie.php (after the conversion()
method but before the closing brace of the Movie
class):
public function displayHeading($tag){
if (substr($this->id, 0, 1) == 'N')
return "<$tag>Movies</$tag>";
else
return "<$tag>Award Winning Movies</$tag>";
}
This method checks if $id
starts with 'N'
using the built-in substr()
function. If it does, it returns an HTML element (as defined by the parameter $tag
) with the word “Movies” enclosed. Else, it returns an element with the words “Award Winning Movies” enclosed.
Next, replace the line
echo $mov1->id.'<BR>';
in chap9.php with
echo $mov1->displayHeading('H1');
As displayHeading()
is a public
method, we have no problems accessing it outside the class. If you run the code now, you’ll get “Movies” displayed as an <h1>
element. Got it?
9.6 Getter and Setter
In the previous section, we talked about the difference between public
and private
class members.
Whenever possible, we should declare class members as private
if code outside the class does not need to access them. This act of preventing code outside from accessing class members unnecessarily is known as encapsulation.
Encapsulation makes it easy for us to make changes to class members without affecting code outside the class. For instance, if we want to change the name of the $id
property in our Movie
class to $movieID
, we only need to make changes within the Movie
class, any code outside the class is not affected. This is one of the advantages of declaring a class property as private
.
Another advantage of declaring class properties as private
is that it helps prevent unauthorized modifications to our object properties. To see why this is so, add the following code to chap9.php:
$mov1->rentalPrice = -20;
echo $mov1->rentalPrice.'<BR>';
Here, we first assign -20
to the $rentalPrice
property of $mov1
. Next, we use an echo
statement to echo its value. If you run the code above, you’ll get -20 as the output.
As you can see, we manage to change the $rentalPrice
property of $mov1
to -20
. This is because $rentalPrice
is a public
property. Hence, we can access it outside the Movie
class and change it to any value we like. This is definitely not desirable as rental price should not be negative.
To prevent such modifications from happening, we should not declare the $rentalPrice
property as public
. Instead, we should declare it as private
. Try changing the $rentalPrice
property to private
in Movie.php and run chap9.php again, what do you get?
You get something similar to the output below, right?
Fatal error: Uncaught Error: Cannot access private property Movie::$rentalPrice in…
We are no longer allowed to access and modify the $rentalPrice
property of $mov1
as it is now a private
property.
Whenever possible, we should always declare our class properties as private
; this helps to prevent any unauthorized access or modifications to them. However, if we declare all our class properties as private
, what happens if code outside the class needs to access or modify those properties?
In cases like these, we can use getters and setters. These are magic methods that allow us to provide limited and controlled access to our private
and protected
properties.
To see how this works, add the following methods to Movie.php (after the displayHeading()
method but before the closing brace of the Movie
class):
public function __get($propertyRequested){
if ($propertyRequested == 'id')
return 'You do not have permission to access id.<BR>';
else
return $this->$propertyRequested;
}
public function __set($propertyToModify, $value){
if ($propertyToModify == 'rentalPrice' && $value > $this->rentalPrice)
$this->rentalPrice = $value;
else
echo 'Failed to modify '.$propertyToModify.'<BR>';
}
The first method ( __get()
) is known as a getter; it controls which property can be accessed outside the class and has one parameter called $propertyRequested
. This parameter stores the name of the property we want to access.
Within the __get()
method, we use an if-else
statement to check If $propertyRequested
equals 'id'
. If it equals, the __get()
method returns a string informing us that we do not have permission to access the $id
property. Else, it returns the property requested. For instance, if $propertyRequested
equals 'rentalPrice'
, it returns $this->rentalPrice
.
Next, we have the __set()
method, which is known as a setter. This method controls which property can be modified. It has two parameters; the first stores the name of the property we want to modify ( $propertyToModify
), and the second stores the new value ( $value
) to assign to this property.
Within our __set()
method, we use an if-else
statement to check if $propertyToModify
equals 'rentalPrice'
and if $value
is greater than the current $rentalPrice
value. If both conditions are met, it allows us to modify the $rentalPrice
property. Else, it echoes a string informing us that it is unable to modify the property. Got it? Good!
Now, let’s look at how we can use the __get()
and __set()
methods. To do that, we simply use the ->
operator, followed by the property name. Behind the scene, as long as we have defined our __get()
and __set()
methods, PHP will call these magic methods automatically.
First, let’s change the access modifiers of all the properties in our Movie
class to private
.
Next, load chap9.php again. Recall that previously, we tried to change the $rentalPrice
property of $mov1
to -20
and got a fatal error? If you load chap9.php now, you’ll no longer get an error. Instead, you’ll get the following output:
Failed to modify rentalPrice
4.99
This is because we have defined our __get()
and __set()
methods.
When we try to modify the value of the $rentalPrice
property, PHP calls the __set()
method for us automatically. As -20
is smaller than the current $rentalPrice
value (which is 4.99
), our __set()
method prevented us from changing the $rentalPrice
value. When we use an echo
statement to echo the $rentalPrice
value, the __get()
method gives us 4.99 as the output.
Next, add the following code to chap9.php and run the page again:
$mov1->id = 'A12387';
echo $mov1->id;
You’ll get
Failed to modify id
You do not have permission to access id.
added to the output. Here, the __set()
method prevented us from modifying the $id
property. Similarly, the __get()
method prevented us from accessing the $id
property.
Last but not least, add the following code to chap9.php and run the page: $mov1->rentalPrice = 5.99;
echo $mov1->rentalPrice;
You’ll get
5.99
added to the output. Here, we try to change the $rentalPrice
value to 5.99
. As 5.99
is greater than the current $rentalPrice
value, the __set()
method allowed us to make the change. When we use an echo
statement to echo the value of $rentalPrice
again, the __get()
method gives us 5.99 as the output.
Got it? Great!
9.7 Printing a String Representation of the Object
Besides the __set()
and __get()
methods, another commonly defined magic method in PHP is the __toString()
method. Let’s add one to our Movie
class.
Add the following code to Movie.php (after the __set()
method but before the closing brace of the Movie
class):
public function __toString(){
return
'Discount = '.self::DISCOUNT.'%'.
'<BR>Id = '.$this->id.
'<BR>Title = '.$this->title.
'<BR>Rental Price (USD) = '.$this->rentalPrice;
}
This method simply returns a string containing information about the Movie
class. Notice a new keyword, self
, in the method above? This keyword is used to access the DISCOUNT
constant defined earlier in the class.
Previously, we learned that to access a class constant outside the class in which it is defined, we can use either the class name or the object name. For instance, in chap9.php, we used $mov1::DISCOUNT
, $mov2::DISCOUNT
and Movie::DISCOUNT
to access the DISCOUNT
constant.
What if we want to access this constant inside the Movie
class itself (i.e., inside the class in which it is defined)? To do that, we can use either the class name or the self
keyword.
In the __toString()
method above, we used the self
keyword. Alternatively, we could have used the class name ( Movie::DISCOUNT
) as well.
After declaring the __toString()
method, we can use it to print a string representation of our Movie
class objects. To do that, we simply use the echo
statement.
To see how this works, add the following lines to chap9.php and run the page again:
echo '<BR>';
echo $mov1;
you’ll get
Discount = 10%
Id = N0001
Title = Lusso
Rental Price (USD) = 5.99
added to the output.