Classes,
objects, methods and properties
Object-oriented
programming is a programming style in which it is customary to group all of the
variables and functions of a particular topic into a single class.
Object-oriented programming is considered to be more advanced and efficient
than the procedural style of programming. This efficiency stems from the fact
that it supports better code organization, provides modularity, and reduces the
need to repeat ourselves. That being said, we may still prefer the procedural
style in small and simple projects. However, as our projects grow in
complexity, we are better off using the object oriented style.
With this tutorial, we are going to take our first steps into
the world of object oriented programming by learning the most basic terms in
the field:
·
classes
·
objects
·
methods
·
properties
You'll learn
·
How to create classes?
·
How to add properties to a class?
·
How to create objects from a class?
·
How to get and set the objects' properties?
·
How to add methods to a class?
How to create classes?
In order to create
a class, we group the code that handles a certain topic
into one place. For example, we can group all of the code that handles the
users of a blog into one class, all of the code that is involved with the
publication of the posts in the blog into a second class, and all the code that
is devoted to comments into a third class.
To name the class, it is
customary to use a singular noun that starts with a capital letter. For
example, we can group a code that handles users into a User class,
the code that handles posts into a Post class,
and the code that is devoted to comments into a Comment class.
For the example given
below, we are going to create a Car class
into which we will group all of the code which has something to do with cars.
class Car {
// The code
}
·
We declare the class with the class keyword.
·
We write the name of the class and capitalize the first letter.
·
If the class name contains more than one word, we capitalize each
word. This is known as upper camel case. For
example, JapaneseCars, AmericanIdol, EuropeTour, etc.
·
We circle the class body within curly braces. Inside the curly
braces, we put our code.
How to add properties to a class?
We call properties to the variables inside a class.
Properties can accept values like strings, integers, and booleans (true/false
values), like any other variable. Let's add some properties to the Car class.
class Car {
public $comp;
public $color = 'beige';
public $hasSunRoof = true;
}
·
We put the public keyword
in front of a class property.
·
The naming convention is to start the property name with a lower
case letter.
·
If the name contains more than one word, all of the words, except
for the first word, start with an upper case letter. For example, $color or $hasSunRoof.
·
A property can have a default value. For example, $color
= 'beige'.
·
We can also create a property without a default value. See the
property $comp in the above example.
How to create objects from a class?
We can create several objects from the same class, with each
object having its own set of properties.
In order to work with a
class, we need to create an object from it.
In order to create an object, we use the new keyword.
For example:
$bmw = new Car ();
·
We created the object $bmw from
the class Carwith the new keyword.
·
The process of creating an object is also known as instantiation.
We can
create more than one object from the same class.
$bmw = new Car ();
$mercedes = new Car ();
In
fact, we can create as many objects as we like from the same class, and then give
each object its own set of properties.
Objects, what are they good for?
While in the procedural
style of programming, all of the functions and variables sit together in
the global scope in a way that allows their use just
by calling their name, the use of classes makes anything inside the classes
hidden from the global scope. That's because the code inside the classes is
encapsulated within the class scope, outside
the reach of the global scope. So, we need a way to allow the code from the
global scope to use the code within the class, and we do this by creating
objects from a class.
I say
objects, and not object, because we can create as many objects as we would like
from the same class and they all will share the class's methods and properties.
See the image below:
From
the same Car class, we created three individual objects with the name
of: Mercedes, Bmw, and Audi.
Although
all of the objects were created from the same class and thus have the class's
methods and properties, they are still different. This is not only, because
they have different names, but also because they may have different values
assigned to their properties. For example, in the image above, they differ by
the color property - the Mercedes is green while the Bmw is blue and the Audi is
orange.
THE MESSAGE TO TAKE HOME IS:
A class
holds the methods and properties that are shared by all of the objects that are
created from it.
Although
the objects share the same code, they can behave differently because they can
have different values assigned to them.
How to get an object's properties?
Once we
create an object, we can get its properties. For example:
echo $bmw -> color;
echo $mercedes -> color;
·
In order to get a property, we write the object name, and then
dash greater than (->), and then the property name.
·
Note that the property name does not
start with the $ sign; only the object name starts with a $.
Result:beige
beige
beige
How to set the object's properties?
In
order to set an object property, we use a similar approach.
For
example, in order to set the color to 'blue' in the bmw object:
$bmw -> color = 'blue';
and in order to set the
value of the $comp property for both objects:
$bmw -> comp = "BMW";
$mercedes -> comp = "Mercedes Benz";
Once we
set the value of a property, we can get its value.
In order to get the color
of the $bmw object, we use the following line of code:
echo $bmw -> color;
Result:blue
We can
also get the company name and the color of the second car object.
echo $mercedes -> color;
echo $mercedes -> comp;
Result:beige
Mercedes Benz
Mercedes Benz
How to add methods to a class?
The classes most often
contain functions. A function inside a class is called a method. Here we add the method hello()to the
class with the prefix public.
class Car {
public $comp;
public $color = 'beige';
public $hasSunRoof = true;
public function hello()
{
return "beep";
}
}
·
We put the public keyword
in front of a method.
·
The naming convention is to start the function name with a lower
case letter.
·
If the name contains more than one word, all of the words, except
for the first word, start with an upper case letter. For example, helloUser() or flyPanAm().
We can
approach the methods similar to the way that we approach the properties, but we
first need to create at least one object from the class.
$bmw = new Car ();
$mercedes = new Car ();
echo $bmw -> hello();
echo $mercedes -> hello();
Result:beep
beep
beep
Here is
the full code that we have written during this tutorial:
<?php
// Declare the class
class Car {
// properties
public $comp;
public $color = 'beige';
public $hasSunRoof = true;
// method that says hello
public function hello()
{
return "beep";
}
}
// Create an instance
$bmw = new Car ();
$mercedes = new Car ();
// Get the values
echo $bmw -> color; // beige
echo "<br />";
echo $mercedes -> color; // beige
echo "<hr />";
// Set the values
$bmw -> color = 'blue';
$bmw -> comp = "BMW";
$mercedes -> comp = "Mercedes Benz";
// Get the values again
echo $bmw -> color; // blue
echo "<br />";
echo $mercedes -> color; // beige
echo "<br />";
echo $bmw -> comp; // BMW
echo "<br />";
echo $mercedes -> comp; // Mercedes Benz
echo "<hr />";
// Use the methods to get a beep
echo $bmw -> hello(); // beep
echo "<br />";
echo $mercedes -> hello(); // beep
The $this keyword
In the first tutorial of this series, we learned how to
group the code related to a certain topic into one single class. As an example,
we wrote a Car class
that groups the code that handles cars.
classCar {
public$comp;
public$color ='beige';
public$hasSunRoof = true;
publicfunctionhello()
{
return"beep";
}
}
We also
created two objects out of the class in order to be able to use its code.
$bmw =newCar ();
$mercedes =newCar ();
You'll learn
·
Why use the $this keyword?
·
How to use the $this keyword to approach the
class's own properties and methods from within the class?
The $this keyword
The $this keyword
indicates that we use the class's own methods and properties, and allows us to
have access to them within the class's scope.
The $this keyword
allows us to approach the class properties and methods from within the class
using the following syntax:
·
Only the $this keyword starts with
the $ sign, while the names of the properties and methods do not
start with it.
$this -> propertyName;
$this-> methodName();
The $this keyword
indicates that we use the class's own methods and properties, and allows us to
have access to them within the class's scope.
Let's illustrate what we have just said on the Car class. We will enable the hello() method to approach the class's own
properties by using the $this keyword.
In
order to approach the class $comp property. We use:
$this-> comp
In
order to approach the class $color property. We use:
$this-> color
That's what
the code looks like:
classCar {
// The properties
public$comp;
public$color ='beige';
public$hasSunRoof = true;
// The method can now approach the class properties
// with the $this keyword
publicfunctionhello()
{
return"Beep I am a <i>".$this -> comp."</i>, and I am <i>".
$this -> color;
}
}
Let
us create two objects from the class:
$bmw =newCar();
$mercedes =newCar ();
and
set the values for the class properties:
$bmw -> comp ="BMW";
$bmw -> color ="blue";
$mercedes -> comp ="Mercedes Benz";
$mercedes -> color ="green";
We
can now call the hello() method for the first car
object:
echo$bmw -> hello();
Result:
Beep I am a BMW, and I am blue.
Beep I am a BMW, and I am blue.
And
for the second car object.
echo$mercedes -> hello();
Result:
Beep I am a Mercedes Benz, and I am green.
Beep I am a Mercedes Benz, and I am green.
This is the
code that we have written in this tutorial:
classCar {
// The properties
public$comp;
public$color ='beige';
public$hasSunRoof = true;
// The method that says hello
publicfunctionhello()
{
return"Beep I am a <i>".$this -> comp.
"</i>, and I am <i>".$this -> color;
}
}
// We can now create an object from the class.
$bmw =newCar();
$mercedes =newCar();
// Set the values of the class properties.
$bmw -> color ='blue';
$bmw -> comp ="BMW";
$mercedes -> comp ="Mercedes Benz";
// Call the hello method for the $bmw object.
echo$bmw -> hello();
Chaining methods
and properties
In the previous tutorial, we learned to use the $this keyword to approach properties and methods within the
scope of the class. In this chapter we will learn that, when a class's methods
return the $thiskeyword, they can be chained together to create much more
streaming code.
For instance, in our Car class, let's say that we want
to measure how much fuel we have in our car's tank. The amount of fuel in the tank
is dependent upon the number of miles we have driven in our car, as well as the
amount of fuel that we put in the tank.
In order to achieve our goal, we are going to put a public
property $tankto our class that represents the number of gallons of fuel that
we have in the car's tank.
class Car {
public$tank;
}
You'll learn
·
How to chain methods and
properties?
·
Why do we want to chain methods
and properties?
We must also add two methods to our Car class:
1. The fill() method
adds gallons of fuel to our car's tank.
2. The ride() method
calculates how much fuel we consume when we ride a certain distance, and then
subtracts it from the tank. In our example, we assume that the car consumes 1
gallon of fuel every 50 miles.
class Car {
public$tank;
// Add gallons of fuel to the tank when we fill it.
publicfunctionfill($float)
{
$this-> tank += $float;
}
// Substract gallons of fuel from the tank as we ride the car.
publicfunctionride($float)
{
$miles = $float;
$gallons = $miles/50;
$this-> tank -= $gallons;
}
}
As we would like our code to look
elegant, we will chain the methods and properties. Note the arrows in the code.
1$tank = $car -> fill(10) -> ride(40) -> tank;
In words: How
much fuel do we have left in our tank after putting in 10 gallons, and driving
40 miles?
In order for us to be able to perform the chaining, the methods
should return the object and, since we are inside the class, the methods should
return the $this keyword.
In the code below, we can see how each method returns the $this keyword
in order to allow the chaining.
class Car {
public$tank;
// Add gallons of fuel to the tank when we fill it.
publicfunctionfill($float)
{
$this-> tank += $float;
return $this;
}
// Substract gallons of fuel from the tank as we ride the car.
publicfunctionride($float)
{
$miles = $float;
$gallons = $miles/50;
$this-> tank -= ($gallons);
return $this;
}
}
Now, we can create an object from the Car class
with the name of $bmw and
find out the number of gallons of fuel left in our car's tank after we have
filled the tank with 10 gallons of fuel and driven 40 miles.
// Create a new object from the Car class.
$bmw =newCar();
// Add 10 gallons of fuel, then ride 40 miles,
// and get the number of gallons in the tank.
$tank = $bmw -> fill(10) -> ride(40) -> tank;
// Print the results to the screen.
echo"The number of gallons left in the tank: ". $tank ." gal.";
Result:
The number of gallons left in the tank: 9.2 gal.
The number of gallons left in the tank: 9.2 gal.
Access modifiers:
public vs. private
In the previous tutorials, we used the public access modifier in
front of the methods and properties in our classes without any explanation. The
public access modifier is only one of several modifiers that we use. In this
tutorial, we will learn about another modifier called the private access modifier.
While the public access
modifier allows a code from outside or inside the class to access the class's
methods and properties, the private modifier
prevents access to a class's methods or properties from any code that is
outside the class.
You'll learn
·
How to use the public access modifier?
·
How to use the privateaccess modifier?
·
How to access a private property?
·
Why do we need access modifiers?
The public access
modifier
The following example should already be familiar to you. In the
example, the class’s property and method are defined as public, so the code outside the class can directly
interact with them.
<?php
class Car {
// public methods and properties.
public$model;
publicfunctiongetModel()
{
return"The car model is ".$this-> model;
}
}
$mercedes = new Car();
//Here we access a property from outside the class
$mercedes -> model ="Mercedes";
//Here we access a method from outside the class
echo $mercedes -> getModel();
Result:
The car model is Mercedes
The car model is Mercedes
The private
access modifier
We can prevent access to the properties and methods inside our
classes if we define them with the private access
modifier instead of the public access
modifier.
In the following example, we define the property $model as private, and when we try to set its value
from outside the class, we encounter a fatal error.
<?php
class Car {
//private
private$model;
publicfunctiongetModel()
{
return"The car model is ".$this-> model;
}
}
$mercedes =newCar();
// We try to access a private property from outside the class.
$mercedes -> model ="Mercedes benz";
echo $mercedes -> getModel();
?>
Result:
Fatal error: Cannot access private property Car::$model
Fatal error: Cannot access private property Car::$model
How to access a
private property?
We saw that
we have no access to private properties from outside the class, but we still
have to somehow set and get the properties’ values. In order to interact with
private properties, we use public methods because they can interact with both
the code outside of the class’s scope as well as the code inside the class. The
public methods that can interact in this manner are commonly divided into two
kinds of methods:
Setters that set the values of the private
properties.
Getters that get the values of the private properties.
In the following example, we will see that we can get and set
the value of a private property, $carModel, through
the use of setter and getter methods. We will use the setModel() method in order to set the value of the
car model, and the getModel() method
to get the value of the property.
<?php
class Car {
//the private access modifier denies access to the method from outside the class’s scope
private$model;
//the public access modifier allows the access to the method from outside the class
publicfunctionsetModel($model)
{
$this-> model = $model;
}
publicfunctiongetModel()
{
return"The car model is ".$this-> model;
}
}
$mercedes =newCar();
//Sets the car’s model
$mercedes -> setModel("Mercedes benz");
//Gets the car’s model
echo $mercedes -> getModel();
?>
Result:
The car model is Mercedes benz
The car model is Mercedes benz
Why do we need
access modifiers?
We need access
modifiers in order to limit the modifications that code from
outside the classes can do to the classes' methods and properties. Once we
define a property or method as private, only
methods that are within the class are allowed to approach it. So, in order to
interact with private methods and properties, we need to provide public
methods. Inside these methods, we can put logic that can validate and restrict
data that comes from outside the class.
In our example, we can validate that only certain car models can
make their way, and be assigned to the private $modelproperty, by defining the allowed alternatives
for models in the public setModel() method.
For this purpose, we define inside the setModel() method
an array of allowed car models, and check that only these models are assigned
to the $model property.
<?php
class Car {
//the private access modifier denies access to the method from outside the class’s scope
private$model;
//the public access modifier allows the access to the method from outside the class
publicfunctionsetModel($model)
{
//validate that only certain car models are assigned to the $carModel property
$allowedModels = array("Mercedes benz","BMW");
if(in_array($model,$allowedModels))
{
$this-> model = $model;
}
else
{
$this-> model ="not in our list of models.";
}
}
publicfunctiongetModel()
{
return"The car model is ".$this-> model;
}
}
$mercedes =newCar();
//Sets the car’s model
$mercedes -> setModel("Mercedes benz");
//Gets the car’s model
echo $mercedes -> getModel();
?>
Magic methods and
constants unveiled
Object Oriented PHP offers several magic methods and constants that
enable a lot of programming with very little code, but which suffer from a few
disadvantages like a slight reduction in the performance time as well as in the
level of code clarity.
There is
much dispute about the use of magic methods, so in this tutorial we explain the
parts of the field which are widely agreed, but ignore the parts in dispute.
You'll learn
·
About the __construct()magic method.
·
How to write a constructor method without risking an error?
·
How to use magic constants?
The __construct()
magic method
The names of magic methods always start with two underscores,
and the __construct() magic
method is no exception. We use __construct() in
order to do something as soon as we create an object out of a class. A method
of this kind is called a constructor.
Usually we use the constructor to set a value to a property.
In our simple example, we want to set the value of the $model property as soon as we create the object,
so we add a constructor inside the class that sets the value of the $model property.
class Car{
private$model;
// A constructor method.
publicfunction__construct($model)
{
$this-> model = $model;
}
}
In order to
use the constructor, we have to pass an argument to the class with the name of
the model as soon as we create the object, but if we try to create a new object
without assigning the value that the constructor needs, we will encounter an
error.
$car1 =newCar();
Result:
Warning: Missing argument 1 for Car::__construct()
Warning: Missing argument 1 for Car::__construct()
In order to avoid such an error, we have to assign a value to
the constructor. Thus, for the sake of example, we assign the value "Mercedes" to the constructor by
writing it within the brackets of the newly created object.
$car1 =newCar("Mercedes");
Now, let’s add the method getCarModel() in
order to echo the car model from the object that we have just created.
class Car {
private$model;
//__construct
publicfunction__construct($model)
{
$this-> model = $model;
}
publicfunctiongetCarModel()
{
return' The car model is: '.$this-> model;
}
}
//We pass the value of the variable once we create the object
$car1 =newCar("Mercedes");
echo$car1 -> getCarModel();
Result:
The car model is: Mercedes.
The car model is: Mercedes.
How to write a
constructor method without risking an error?
When we try
to create an object that has a constructor method, we run the risk of our code
breaking if we don’t pass a value to the constructor. In order to avoid this
risk, we can define a default value for the properties that we would like to
set through the constructor. The default value may be the most reasonable
choice for the property, zero, an empty string, or even a null.
If we use a
null as the default value, we can use a condition to assess if a value was
passed and then, only in that case, assign the value to the property.
In the example below, we give a default value of null to
the $model property and, only if a
value is passed to the constructor, we assign this value to the property. In
any other case, the $model property
has a default value of "N/A"string.
class Car {
// The $model property has a default value of "N/A"
private$model ="N/A";
// We don’t have to assign a value to the $model property
//since it already has a default value
publicfunction__construct($model = null)
{
// Only if the model value is passed it will be assigned
if($model)
{
$this-> model = $model;
}
}
publicfunctiongetCarModel()
{
return' The car model is: '.$this-> model;
}
}
//We create the new Car object without passing a value to the model
$car1 =newCar();
echo$car1 -> getCarModel();
Even though
we created the object without passing a value to the model property, we didn't
cause an error because the model property in the constructor has a default
value of null.
Result:
The car model is: N/A
The car model is: N/A
On the other hand, let’s see what happens when we define the
model once we create the object. In the example, we assign the value "Merceds" to the $model property as soon as we create the object.
class Car {
private$model ='';
//__construct
publicfunction__construct($model = null)
{
if($model)
{
$this-> model = $model;
}
}
publicfunctiongetCarModel()
{
return' The car model is: '.$this-> model;
}
}
//We create the new Car object with the value of the model
$car1 =newCar('Mercedes');
echo$car1 -> getCarModel();
Result:
The car model is: Mercedes
The car model is: Mercedes
Magic constants
In addition
to magic methods, the PHP language offers several magic constants.
For example, we may use the magic constant __CLASS__ (magic constants are written in uppercase
letters and prefixed and suffixed with two underlines) in order to get the name
of the class in which it resides.
Let’s take a look at the following example in which we use
the __CLASS__ magic constant in
the getCarModel()method to
get the class's name:
class Car {
private$model ='';
//__construct
publicfunction__construct($model = null)
{
if($model)
{
$this-> model = $model;
}
}
publicfunctiongetCarModel()
{
//We use the __CLASS__ magic constant in order to get the class name
return" The <b>". __CLASS__ ."</b> model is: ".$this-> model;
}
}
$car1 =newCar('Mercedes');
echo$car1 -> getCarModel();
Result:
TheCarmodel is: Mercedes
TheCarmodel is: Mercedes
Other magic
constants that may be of help are:
__LINE__ to get
the line number in which the constant is used.
__FILE__ to get the full path or the filename in which the constant is used.
__METHOD__ to get the name of the method in which the constant is used.
__FILE__ to get the full path or the filename in which the constant is used.
__METHOD__ to get the name of the method in which the constant is used.
Inheritance in
object-oriented PHP
One of the main advantages of object-oriented programming is the
ability to reduce code duplication with inheritance. Code duplication occurs when a
programmer writes the same code more than once, a problem that inheritance strives to
solve. In inheritance, we have a parent class with its own methods and
properties, and a child class (or classes) that can use the code from the
parent. By using inheritance, we can create a reusable piece of code that we
write only once in the parent class, and use again as much as we need in the
child classes.
You'll learn
·
How can a class inherit the code of another class?
·
How can a child class have its own methods and properties?
·
About the protected access control modifier.
·
How to override the parent’s properties and methods in the child
class?
How can a class
inherit the code of another class?
Inheritance
allows us to write the code only once in the parent, and then use the code in
both the parent and the child classes.
In order to declare that one class inherits the code from
another class, we use the extends keyword.
Let's see the general case:
class Parent {
// The parent’s class code
}
classChildextendsParent {
// The child can use the parent's class code
}
The child
class can make use of all the non-private methods and properties that it
inherits from the parent class. This allows us to write the code only once in
the parent, and then use it in both the parent and the child classes.
In the example given below, the SportsCar class inherits the Car class, so it has access to all of the Car’s methods and properties that are not private. This allows us
to write the setModel() and hello() public methods only once in the parent,
and then use these methods in both the parent and the child classes.
//The parent class
class Car {
// Private property inside the class
private$model;
//Public setter method
publicfunctionsetModel($model)
{
$this-> model = $model;
}
publicfunctionhello()
{
return"beep! I am a <i>".$this-> model ."</i><br />";
}
}
//The child class inherits the code from the parent class
classSportsCarextendsCar {
//No code in the child class
}
//Create an instance from the child class
$sportsCar1 =newSportsCar();
// Set the value of the class’ property.
// For this aim, we use a method that we created in the parent
$sportsCar1 -> setModel('Mercedes Benz');
//Use another method that the child class inherited from the parent class
echo $sportsCar1 -> hello();
Result:
beep! I am a Mercedes Benz
beep! I am a Mercedes Benz
How can a child
class have its own methods and properties?
Just as a
child class can use the properties and methods of its parent class, it can have
properties and methods of its own as well. However, while a child class can use
the code it inherited from the parent, the parent class is not allowed to use
the child class’s code.
In the example given below, we will add to the child class some
code of its own by adding the $style property
as well as the driveItWithStyle() method:
// The parent class has its
properties and methods
class Car {
//A private property or method can be used only by the parent.
private$model;
// Public methods and properties can be used by both the parent and the child classes.
publicfunctionsetModel($model)
{
$this-> model = $model;
}
publicfunctiongetModel()
{
return$this -> model;
}
}
//The child class can use the code it inherited from the parent class,
// and it can also have its own code
classSportsCarextendsCar{
private$style ='fast and furious';
publicfunctiondriveItWithStyle()
{
return'Drive a '.$this-> getModel() .' <i>'.$this-> style .'</i>';
}
}
//create an instance from the child class
$sportsCar1 =newSportsCar();
// Use a method that the child class inherited from the parent class
$sportsCar1 -> setModel('Ferrari');
// Use a method that was added to the child class
echo $sportsCar1 -> driveItWithStyle();
Result:
Drive a Ferrari fast and furious.
Drive a Ferrari fast and furious.
The protected
access control modifier
When we
declare a property or a method as protected, we can approach it from both the
parent and the child classes.
In a previous tutorial, we learned that we can use the
public access modifier to allow access to a class’s methods and properties from
both inside and outside the class. We also learned that those methods and
properties that are private can only be used from inside the class.
In this tutorial, we will learn about a third modifier -
the protected modifier,
which allows code usage from both inside the class and from its child classes.
The first example demonstrates what might happen when we declare
the $model property in the parent
as private, but still try to access it from its child class.
What do you
think might happen when we try to call a private method or property from
outside the class?
Here is the
code:
// The parent class
class Car {
//The $model property is private, thus it can be accessed
// only from inside the class
private$model;
//Public setter method
publicfunctionsetModel($model)
{
$this-> model = $model;
}
}
// The child class
classSportsCarextendsCar{
//Tries to get a private property that belongs to the parent
publicfunctionhello()
{
return"beep! I am a <i>".$this-> model ."</i><br />";
}
}
//Create an instance from the child class
$sportsCar1 =newSportsCar();
//Set the class model name
$sportsCar1 -> setModel('Mercedes Benz');
//Get the class model name
echo $sportsCar1 -> hello();
Result:
Notice: Undefined property: SportsCar::$model
Notice: Undefined property: SportsCar::$model
We get an error because the hello() method
in the child class is trying to approach a private property, $model, that belongs to the parent class.
We can fix the problem by declaring the $model property in the parent as protected,
instead of private, because when we declare a property or a method as
protected, we can approach it from both the parent and the child classes.
// The parent class
class Car {
//The $model property is now protected, so it can be accessed
// from within the class and its child classes
protected$model;
//Public setter method
publicfunctionsetModel($model)
{
$this-> model = $model;
}
}
// The child class
classSportsCarextendsCar {
//Has no problem to get a protected property that belongs to the parent
publicfunctionhello()
{
return"beep! I am a <i>".$this-> model ."</i><br />";
}
}
//Create an instance from the child class
$sportsCar1 = new SportsCar();
//Set the class model name
$sportsCar1 -> setModel('Mercedes Benz');
//Get the class model name
echo $sportsCar1 -> hello();
Result:
beep! I am a Mercedes Benz
beep! I am a Mercedes Benz
Now it
works, because we can access a protected code that belongs to a parent from a
child class.
How to override
the parent’s properties and methods in the child class?
In the same way that the child class can have its own properties
and methods, it can override the
properties and methods of the parent class. When we override the class’s
properties and methods, we rewrite a method or property that exists in the
parent again in the child, but assign to it a different value or code.
In the example given below, we create a hello() method in the parent class that returns the
string "beep" and
override it in the child class with a method by the same name that returns a
different string, "Halllo".
Here is the
code:
// The parent class has hello method that returns "beep".
class Car {
publicfunctionhello()
{
return"beep";
}
}
//The child class has hello method that returns "Halllo"
classSportsCarextendsCar {
publicfunctionhello()
{
return"Hallo";
}
}
//Create a new object
$sportsCar1 =newSportsCar();
//Get the result of the hello method
echo $sportsCar1 -> hello();
Result:
Halllo
Halllo
The result reflects the fact that the hello() method from the parent class was
overridden by the child method with the same name.
How to prevent
the child class from overriding the parent’s methods?
In order to prevent the method in the child class from
overriding the parent’s methods, we can prefix the method in the parent with
the final keyword.
In the example given below, we declare the hello() method in the parent as final, but still try to override it in the child class. What do you
think might happen if we try to override a method that was declared as final?
// The parent class has hello method that returns "beep".
class Car {
finalpublicfunctionhello()
{
return"beep";
}
}
//The child class has hello method that tries to override the hello method in the parent
classSportsCarextendsCar {
publicfunctionhello()
{
return"Hallo";
}
}
//Create a new object
$sportsCar1 =newSportsCar();
//Get the result of the hello method
echo $sportsCar1 -> hello();
Result:
Fatal error: Cannot override final method Car::hello()
Fatal error: Cannot override final method Car::hello()
Since we declared the hello method as final in the parent, we cannot override the method in the child
class.
Abstract classes
and methods
We use abstract
classes when we want to commit the programmer (either
oneself or someone else) to write a certain class method, but we are only sure
about the name of the method, and not the details of how it should be written.
To take an example, circles, rectangles, octagons, etc. may all look different,
but are all 2D shapes nonetheless, and thus all possess the traits of area and
circumference. So, it makes perfect sense to group the code that they have in
common into one parent class. In this parent class, we would have the two
properties of area and circumference, and we might even consider adding a
method that calculates the area (which might be problematic since different
shapes require different calculations). In these kinds of cases, when we need
to commit the child classes to certain methods that they inherit from the
parent class but we cannot commit about the code that should be used in the
methods, we use abstract classes.
We use abstract
classes and methods when we need to commit the child classes to certain methods
that they inherit from the parent class but we cannot commit about the code
that should be written inside the methods.
An abstract
class is a class that has at least one abstract method. Abstract
methods can only have names and arguments, and no other code. Thus, we cannot
create objects out of abstract classes. Instead, we need to create child
classes that add the code into the bodies of the methods, and use these child classes
to create objects.
You'll learn
·
How to declare classes and methods as abstract?
·
Can we have non abstract methods inside an abstract class?
·
How to create child classes from an abstract class?
How to declare
classes and methods as abstract?
In order to declare a class as abstract, we need to prefix the
name of the class with the abstract keyword.
See the
following example:
1abstractclassCar { }
We put the abstract methods that are also declared with
the abstract keyword within the
abstract class. Abstract methods inside an abstract class don't have a body,
only a name and parameters inside parentheses.
In the example given below, we create a public abstract
method, calcNumMilesOnFullTank(), that is
the skeleton for methods that we will create in the child classes. Once
created, these methods will return the number of miles a car can be driven on a
tank of gas.
// Abstract classes are declared with the abstract keyword, and contain abstract methods.
abstractclassCar {
abstractpublicfunctioncalcNumMilesOnFullTank();
}
It is
important to know that once we have an abstract method in a class, the class
must also be abstract.
Can we have non
abstract methods inside an abstract class?
An abstract
class can have non abstract methods. In fact, it can even have properties, and
properties couldn't be abstract.
Let's add to our example the protected property, $tankVolume, and public method with the name of setTankVolume().
abstractclassCar {
// Abstract classes can have properties
protected$tankVolume;
// Abstract classes can have non abstract methods
publicfunctionsetTankVolume($volume)
{
$this-> tankVolume = $volume;
}
// Abstract method
abstractpublicfunctioncalcNumMilesOnFullTank();
}
How to create
child classes from an abstract class?
Since we cannot create objects from abstract classes, we need to
create child classes that inherit the abstract class code. Child classes of
abstract classes are formed with the help of the extends keyword, like any other child class. They
are different in that they have to add the bodies to the abstract methods.
The child
classes that inherit from abstract classes must add bodies to the abstract
methods.
Let's create a child class with the name of Honda, and define in it the abstract method that it inherited from
the parent, calcNumMilesOnFullTank().
classHondaextendsCar {
// Since we inherited abstract method, we need to define it in the child class,
// by adding code to the method's body.
publicfunctioncalcNumMilesOnFullTank()
{
$miles =$this-> tankVolume*30;
return$miles;
}
}
We can create another child class from the Car abstract class and call it Toyota, and here again define the abstract
method calcNumMilesOnFullTank() with a
slight change in the calculation. We will also add to the child class its own
method with the name of getColor() that
returns the string "beige".
classToyotaextendsCar {
// Since we inherited abstract method, we need to define it in the child class,
// by adding code to the method's body.
publicfunctioncalcNumMilesOnFullTank()
{
return$miles =$this-> tankVolume*33;
}
publicfunctiongetColor()
{
return"beige";
}
}
Let's create a new object, $toyota1, with
volume of 10 Gallons, and let it return the number of miles on full tank and
the car's color.
$toyota1 =newToyota();
$toyota1 -> setTankVolume(10);
echo$toyota1 -> calcNumMilesOnFullTank();//330
echo$toyota1 -> getColor();//beige
Interfaces - the
next level of abstraction
Interfaces resemble abstract classes in that
they include abstract methods that the programmer must define in the classes
that inherit from the interface. In this way, interfaces contribute to code
organization because they commit the child classes to abstract methods that
they should implement. The use of interfaces becomes very helpful when we work
in a team of programmers and want to ensure that all the programmers write the
methods that they should work on, or even in the case of a single programmer
that wants to commit himself to write certain methods in the child classes.
An interface
commits its child classes to abstract methods that they should implement.
You'll learn
·
How to declare and implement an interface?
·
Can we implement more than one interface in the same class?
·
What are the differences between abstract classes and interfaces?
How to declare
and implement an interface?
We declare an interface with the interface keyword and, the class that inherits from
an interface with the implementskeyword.
Let's see the general case:
interface interfaceName {
// abstract methods
}
classChildimplementsinterfaceName {
// defines the interface methods and may have its own code
}
In the simple example given below, we will create an interface
for the classes that handle cars, which commits all its child classes to setModel() and getModel() methods.
interface Car {
publicfunctionsetModel($name);
publicfunctiongetModel();
}
Interfaces,
like abstract classes, include abstract methods and constants. However, unlike
abstract classes, interfaces can have only public methods, and cannot have
variables.
The classes that implement the interfaces must define all the
methods that they inherit from the interfaces, including all the parameters.
So, in our concrete class with the name of miniCar, we add the
code to all the abstract methods.
classminiCarimplementsCar {
private$model;
publicfunctionsetModel($name)
{
$this-> model = $name;
}
publicfunctiongetModel()
{
return$this-> model;
}
}
Can we implement
more than one interface in the same class?
We can
implement a number of interfaces in the same class.
We can implement a number of interfaces in the same class, and
so circumvent the law that prohibits the inheritance from more than one parent
class. In order to demonstrate multiple inheritance from different interfaces,
we create another interface, Vehicle, that
commits the classes that implement it to a boolean $hasWheels property.
interface Vehicle {
publicfunctionsetHasWheels($bool);
publicfunctiongetHasWheels();
}
Now, our
child class can implement the two interfaces.
classminiCarimplementsCar, Vehicle {
private$model;
private$hasWheels;
publicfunctionsetModel($name)
{
$this-> model = $name;
}
publicfunctiongetModel()
{
return$this-> model;
}
publicfunctionsetHasWheels($bool)
{
$this-> hasWheels = $bool;
}
publicfunctiongetHasWheels()
{
return($this-> hasWheels)?"has wheels":"no wheels";
}
}
What are the
differences between abstract classes and interfaces?
We saw that
abstract classes and interfaces are similar in that they provide abstract
methods that must be implemented in the child classes. However, they still have
the following differences:
·
Interfaces can include abstract methods and constants, but cannot
contain concrete methods and variables.
·
All the methods in the interface must be in the public visibility scope.
·
A class can implement more than one interface, while it can
inherit from only one abstract class.
Let's
summarize these differences in the following table:
|
interface
|
abstract class
|
|
|
the
code
|
- abstract
methods
- constants |
- abstract
methods
- constants - concrete methods - concrete variables |
|
access
modifiers
|
- public
|
- public
- protected - private etc. |
|
number
of parents
|
The same class
can implement more than 1 interface
|
The child class
can inherit only from 1 abstract class
|
Polymorphism in
PHP
In this tutorial, we are going to learn about Polymorphism (Greek for
"many forms") a naming convention that can help us write code which
is much more coherent and easy to use. According to the Polymorphismprinciple, methods
in different classes that do similar things should have the same name.
According to
the Polymorphism principle, methods in different classes that do similar things
should have the same name.
A prime example is of classes that represent geometric shapes
(such as rectangles, circles and octagons) that are different from each other
in the number of ribs and in the formula that calculates their area, but they
all have in common an area that can be calculated by a method. The polymorphism principle says
that, in this case, all the methods that calculate the area (and it doesn't
matter for which shape or class) would have the same name.
For example, we can call the method that calculates the
area calcArea() and
decide that we put, in each class that represents a shape, a method with this
name that calculates the area according to the shape. Now, whenever we would want
to calculate the area for the different shapes, we would call a method with the
name of calcArea()without
having to pay too much attention to the technicalities of how to actually
calculate the area for the different shapes. The only thing that we would need
to know is the name of the method that calculates the area.
Tutorials
list
How to implement
the polymorphism principle?
In order to
implement the polymorphism principle, we can choose between abstract classes
and interfaces.
In order to ensure that the classes do implement the polymorphism principle, we can
choose between one of the two options of either abstract classes or interfaces.
In the example given below, the interface with the name of Shape commits all the classes that implement it to define an
abstract method with the name of calcArea().
interface Shape {
publicfunctioncalcArea();
}
In accordance, the Circle class
implements the interface by putting into the calcArea() method
the formula that calculates the area of circles.
classCircleimplementsShape {
private$radius;
publicfunction__construct($radius)
{
$this-> radius = $radius;
}
// calcArea calculates the area of circles
publicfunctioncalcArea()
{
return $this-> radius *$this-> radius * pi();
}
}
The rectangle class also implements the Shape interface but defines the method calcArea() with a calculation formula that is
suitable for rectangles:
classRectangleimplementsShape {
private$width;
private$height;
publicfunction__construct($width, $height)
{
$this-> width = $width;
$this-> height = $height;
}
// calcArea calculates the area of rectangles
publicfunctioncalcArea()
{
return $this-> width *$this-> height;
}
}
Now, we can
create objects from the concrete classes:
$circ =newCircle(3);
$rect =newRectangle(3,4);
We can be sure that all of the objects calculate the area with
the method that has the name of calcArea(), whether it
is a rectangle object or a circle object (or any other shape), as long as they
implement the Shape interface.
Now, we can use the calcArea() methods
to calculate the area of the shapes:
echo $circ -> calcArea();
echo$rect -> calcArea();
Result:
28.274333882308
12
28.274333882308
12
Type hinting
With Type
hinting we can specify the expected data type (arrays,
objects, interface, etc.) for an argument in a function declaration. This
practice can be most advantageous because it results in better code
organization and improved error messages.
This
tutorial will start by explaining the subject of type hinting for arrays and
objects which is supported in both PHP5 as well as PHP7.
It will also
explain the subject of type hinting for basic data types (integers, floats,
strings, and booleans) which is only supported in PHP7.
You'll learn
·
How to do array type hinting?
·
How to do object type hinting?
·
PHP support for basic data types
How to do array
type hinting?
When we would like to force a function to get only arguments of
the type array, we can
put the keyword array in
front of the argument name, with the following syntax:
functionfunctionName (array$argumentName)
{
//code
}
In the following example, the calcNumMilesOnFullTank() function
calculates the number of miles a car can be driven on a full tank of gas by
using the tank volume as well as the number of miles per gallon (mpg). This
function accepts only array as an argument, as we can see from the fact that
the argument name is preceded by the array keyword.
// The function can only get array as an argument.
functioncalcNumMilesOnFullTank(array$models)
{
foreach($modelsas$item)
{
echo$carModel = $item[0];
echo" : ";
echo$numberOfMiles = $item[1] * $item[2];
echo"<br />";
}
}
First, let's
try to pass to the function an argument which is not an array to see what might
happen in such a case:
calcNumMilesOnFullTank("Toyota");
Result:
Catchable fatal error: Argument 1 passed to calcNumMilesOnFullTank() must be of the type array, string given
Catchable fatal error: Argument 1 passed to calcNumMilesOnFullTank() must be of the type array, string given
This error
is a precise description of what went wrong with our code. From it, we can
understand that the function expected an array variable, and not a string.
Let's
rewrite the code and pass to the function an array with the expected items,
including the model names, the tank volumes, and the mpg (miles per gallon).
$models = array(
array('Toyota', 12, 44),
array('BMW', 13, 41)
);
calcNumMilesOnFullTank($models);
Result:
Toyota : 528
BMW : 533
Toyota : 528
BMW : 533
Now it's
working because we passed to the function the array that it expected to get.
How to do object
type hinting?
Type hinting can also be used to force a
function to get an argument of type Object. For this purpose, we put the name
of the class in front of the argument name in the function.
In the following example, the class's constructor can only get
objects that were created from the Driver class.
We ensure this by putting the word Driver in
front of the argument name in the constructor.
class Car {
protected$driver;
// The constructor can only get Driver objects as arguments.
publicfunction__construct(Driver$driver)
{
$this-> driver = $driver;
}
}
class Driver {}
$driver1 =newDriver();
$car1 =newCar($driver1);
Does PHP support
type hinting to basic data types?
It depends.
Whereas PHP5 doesn’t allow type hinting for basic data types
(integers, floats, strings and booleans), PHP7 does support scalar type hinting.
PHP5 does
not support type hinting to basic data types like integers, booleans or
strings. So, when we need to validate that an argument belongs to a basic data
type, we can use one of PHP’s “is_” family functions. For example:
·
is_bool - to find out whether a variable is a
boolean (true or false).
·
is_int - to find out whether a variable is an
integer.
·
is_float - to find out whether a variable is a
float (3.14, 1.2e3 or 3E-10).
·
is_null - to find out whether a variable is null.
·
is_string - to find out whether a variable is a
string.
On the other
hand, PHP7 does support scalar type hinting. The supported types are: integers,
floats, strings, and booleans.
The
following code example can only work in PHP7.
class car {
protected$model;
protected$hasSunRoof;
protected$numberOfDoors;
protected$price;
// string type hinting
publicfunctionsetModel(string$model)
{
$this->model = $model;
}
// boolean type hinting
publicfunctionsetHasSunRoof(bool$value)
{
$this->hasSunRoof = $value;
}
// integer type hinting
publicfunctionsetNumberOfDoors(int$value)
{
$this->numberOfDoors = $value;
}
// float type hinting
publicfunctionsetPrice(float$value)
{
$this->price = $value;
}
}
Type hinting for
interfaces
In the previous tutorial, we learned that type hinting contributes
to code organization and improves error messages. In this tutorial, we will
learn how to implement type hinting for interfaces.
In the first part of the chapter we will see that the use
of type hinting for objects is not always sufficient,
and in the second part we will learn how to fix the problem by the use of type hinting for interfaces.
You'll learn
·
Why type hinting for objects may not be sufficient?
·
How to do type hinting for interfaces?
Why type hinting
for objects may not be sufficient?
We will base our tutorial on the following imaginary scenario: A
manager in a car rental company that rents only BMWs hired a programmer to
write a program that calculates the price for a full tank of gas for each and
every car that the company owns. In accordance with these demands, the
programmer writes a class to which he decides to call Bmw that holds the code for the task. This class has the data
about the BMWs including the license plate number, the car model and, most
importantly, it has a method that calculates the volume of the fuel tank.
The function that calculates the volume is called calcTankVolume. This method calculates the tank
volume by multiplying the base area (the square of the base ribs' length) and
the height. The height, as well as the length of the base ribs and the license
plate number, is introduced to the class through the constructor.
class Bmw {
protected$model;
protected$rib;
protected$height;
// The properties are introduced to the class through the constructor
publicfunction__construct($model, $rib, $height)
{
$this-> model = $model;
$this-> rib = $rib;
$this-> height = $height;
}
// Calculate the tank volume for rectangular tanks
publicfunctioncalcTankVolume()
{
return $this-> rib *$this-> rib *$this-> height;
}
}
Outside of the class, the programmer writes a function to
calculate the price for a full tank of gas by multiplying the tank volume
(gallons) and the price per gallon. However, since he doesn't want the function
to get any argument other than those that belong to the Bmw class, he uses type hinting:
// Type hinting ensures that the function gets only Bmw objects as arguments
functioncalcTankPrice(Bmw$bmw, $pricePerGalon)
{
return$bmw -> calcTankVolume() * 0.0043290 * $pricePerGalon ."$";
}
Now, he can easily calculate how much a full tank of gas costs
for BMWs. For example, for a car with the license plate number of '62182791', a rib length of 14″ a height of 21″, and
using gas priced at 3 dollars per gallon.
$bmw1 =newBmw('62182791', 14, 21);
echocalcTankPrice($bmw1, 3) ;
Result:
53.454492$
53.454492$
How to do type
hinting for interfaces?
After a short time, the manager decides to introduce to his
fleet of cars a brand new Mercedes, but the problem is that the calcTankPrice() function can only perform
calculations for BMWs. It turns out that, while BMWs have a rectangular shaped
gas tank, Mercedes have a cylindrical shaped gas tank. So, our programmer is
summoned once again to write another class that can handle this new task at
hand. To this end, our programmer now writes the following class with the name
of Mercedes that can calculate the
tank volume for cylindrical shaped tanks.
class Mercedes {
protected$model;
protected$radius;
protected$height;
publicfunction__construct($model, $radius, $height)
{
$this-> model = $model;
$this-> radius = $radius;
$this-> height = $height;
}
// Calculates the volume of cylinder
publicfunctioncalcTankVolume()
{
return $this-> radius *$this-> radius * pi() *$this-> height;
}
}
When our programmer completed the task of writing the Mercedes class, he tried to calculate the price of
a full tank of gas for a Mercedes.
$mercedes1 =newMercedes('12189796', 7, 28);
echocalcTankPrice($mercedes1, 3);
However, the
result wasn't quite what he had expected:
Result:
Catchable fatal error: Argument 1 passed to calcTankPrice() must be an instance of Bmw, instance of Mercedes given
Catchable fatal error: Argument 1 passed to calcTankPrice() must be an instance of Bmw, instance of Mercedes given
This error message is the result of not passing the right object
to the function, since he tried to pass a Mercedes object
to the calcTankVolume() function
whereas the function can only accept objects that belong to the Bmw class.
First, our programmer attempted to solve the problem by not
using any type hinting, but then he understood that a better solution would be
the use of type hinting for interfaces.
Here I use interface in its wider meaning that includes both abstract classes as well as real interfaces.
Here I use
interface in its wider meaning that includes both abstract classes as well as
real interfaces.
So, first he created an abstract class with the name of Car that both the Bmw and
the Mercedes (and, in fact, any
other car model) can inherit from.
abstractclassCar {
protected$model;
protected$height;
abstractpublicfunctioncalcTankVolume();
}
Then he re-factored the Bmw and Mercedes classes so that they inherit from
the Car class:
classBmwextendsCar {
protected$rib;
publicfunction__construct($model, $rib, $height)
{
$this-> model = $model;
$this-> rib = $rib;
$this-> height = $height;
}
// Calculates a rectangular tank volume
publicfunctioncalcTankVolume()
{
return $this-> rib *$this-> rib *$this-> height;
}
}
classMercedesextendsCar {
protected$radius;
publicfunction__construct($model, $radius, $height)
{
$this->model = $model;
$this-> radius = $radius;
$this-> height = $height;
}
// Calculates the volume of cylinders
publicfunctioncalcTankVolume()
{
return $this-> radius *$this-> radius * pi() *$this-> height;
}
}
The following diagram can help us to better understand the
relation between the Car interface
and the concrete classes (Bmw and Mercedes) that implement the interface for different gas
tank shapes:

Since both the classes inherit from the same interface, he could
comfortably type hint the function calcTankPrice()with
the Car interface, so that the
function can get any object as long as it belongs to this interface.
// Type hinting ensures that the function gets only objects
// that belong to the Car interface
functioncalcTankPrice(Car$car, $pricePerGalon)
{
echo$car -> calcTankVolume() * 0.0043290 * $pricePerGalon . "$";
}
Now, let's see the result when we try to use the function calcTankPrice() on both a Bmw and a Mercedes objects:
$bmw1 =newBmw('62182791', 14, 21);
echo calcTankPrice($bmw1, 3);
$mercedes1 =newMercedes('12189796', 7, 28);
echocalcTankPrice($mercedes1, 3);
Result:
53.454492$
55.977413122858$
53.454492$
55.977413122858$
Whenever we
need to do type hinting to more than one related classes, we should be using
interface type hinting.
THE MESSAGE TO TAKE HOME IS:
The message
to take home from this tutorial is that, whenever we need to do type hinting to
more than one related classes, we should be using interface type hinting.
Programming to an interface makes
the code much more flexible and ready for changes.
Another very important lesson that the above examples can teach
us, is the importance of programming
to an interface. When the programmer had made the calcTankPrice function dependent on
the Bmw class, he restricted the class
use to only one car type. But when he chose to make the function dependent on
an interface he allowed the function to be used for any car type, and so made
it much more flexible. From this we can conclude that programming to an interfacemakes
the code much more flexible and ready for changes.
Static methods
and properties
In certain cases, it is better to approach methods and
properties of a class without the need to create an object out of the class.
This can be achieved by defining the methods and properties of a class as static. Even though the use of
static methods and properties is considered a bad practice, there are cases in
which their use is quite handy and justified.
In certain
cases, it is better to approach methods and properties of a class without the
need to create an object out of the class.
We have already learned about the three access modifiers
called public, protected and private. This tutorial is devoted to the fourth
modifier, static, that
allows access to classes' properties and methods without the need to create
objects out of the classes.
You'll learn
·
How to define methods and properties as static?
·
How to approach the staticmethods from within the class?
·
When to use static properties and methods?
·
Why static should be used with
caution?
How to define
methods and properties as static?
In order to define methods and properties as static, we use the reserved
keyword static. In the
following example, the class Utilis has a
static public property with the name of $numCars and so
the variable name is preceded by both the static and
the public keywords.
class Utilis {
// static methods and properties are defined with the static keyword.
static public$numCars = 0;
}
How to approach
static methods and properties?
In order to approach static methods and properties we use
the scope resolution operator (::).
In the example given below, in order to work with the static property of $numCars, we use the following code:
// set the number of cars
Utilis::$numCars = 3;
// get the number of cars
echoUtilis::$numCars;
Result:
3
3
Pay attention to the use of the $ sign right after the scope resolution operator.
How to approach
the static methods from within the class?
In the same manner that we used the $this keyword to approach the class's own properties and methods
from within the class, we use the reserved keyword self to approach static methods and properties.
In the example given below, the method addToNumCars() gets and sets the number of
cars from the static $numCarsproperty
within the same class by using the self keyword.
class Utilis {
staticpublic$numCars = 0;
staticpublicfunctionaddToNumCars($int)
{
$int = (int)$int;
self::$numCars += $int;
}
}
Now, we can
use the code we have written so far to set and get the number of cars.
echo Utilis::$numCars;
Utilis::addToNumCars(3);
echo Utilis::$numCars;
Utilis::addToNumCars(-1);
echoUtilis::$numCars;
Result:
0
3
2
0
3
2
When to use
static properties and methods?
The main
cases in which we consider the use of static methods and properties are when we
need them as counters and for utility classes.
The use of
static properties and methods is considered to be a bad practice. However, in
some cases, the ability to use a property or a method without the need to
create an object can be advantageous. The main cases in which we consider the
use of static methods and properties are when we need them as counters and for
utility classes.
Use case 1: as
counters
We use static properties as counters since they are able to save
the last value that has been assigned to them. For example, the method add1ToCars() adds 1 to the $numberOfCars property each time the method
is called.
class Utilis {
// Hold the number of cars.
static public$numberOfCars = 0;
// Add 1 to the number of cars each time the method is called.
static publicfunctionadd1ToCars()
{
self::$numberOfCars++;
}
}
echo Utilis::$numberOfCars;
Utilis::add1ToCars();
echo Utilis::$numberOfCars;
Utilis::add1ToCars();
echo Utilis::$numberOfCars;
Utilis::add1ToCars();
echoUtilis::$numberOfCars;
Result:
0
1
2
3
0
1
2
3
Use case 2: for
utility classes
It is very common to use static methods for utility classes. The sole
purpose of utility classes is to provide services to the main classes. Utility
methods can perform all kinds of tasks, such as: conversion between measurement
systems (kilograms to pounds), data encryption, sanitation, and any other task
that is not more than a service for the main classes in our application.
The example given below is of a static method with the name
of redirect that redirects the user
to the URL that we pass to it as an argument.
class Utilis {
// The method uses PHP's header function to redirect the user.
static publicfunctionredirect($url)
{
header("Location: $url");
exit;
}
}
In order to use the redirect() method,
all we need to do is call it from the scope of the Utilis class without having the need to create an
object. It's as simple as this:
1Utilis::redirect("http://www.phpenthusiast.com");
Why static should
be used with caution?
Whenever you
use static, be sure to use it for utilities and not for convenience reasons.
If you are
considering the use of static methods and properties because they are
convenient and can be approached without the need to first create an object,
please be cautious, because static methods suffer from the following two major
disadvantages:
1. You will
have a hard time performing automated testing on classes that use static
methods.
2. Static
methods and variables present globals to the code that can be approached from
anywhere. This is something that we should try to avoid as much as possible.
So, whenever
you use static, be sure to use it for utilities and not for convenience
reasons.
No comments:
Post a Comment