Object-oriented programming

Last updated Mar 23, 2024 Published Mar 20, 2018

The content here is under the Attribution 4.0 International (CC BY 4.0) license

As PHP evolved, the importance given to Object Oriented Programming (OOP) grew. I would venture to say that, starting with version 5.4 of the language, we saw many gains focused on OOP.

We will go through creating classes, using magic methods, access modifiers, the difference between the reserved words self and static, and much more. A shorter version of this page is availabe as well, it is a shallow intro in comparison with

Creating a class

When we are working with Object Oriented Programming, the first thing that comes to mind logically are objects, in PHP. We use the reserved word class to define the design of our object.

See in our example below the class Cup which, despite having nothing inside, is a valid class.

class Cup {
}

Now that we have the structure of the object, we need to add its properties, since making a comparison with the real world, they are the characteristics of a real glass. And what characteristics can a glass have? Size, color and weight are some of them.

For our class, we will only use the first two: size and color.

class Cup {

    public $size;
    public $color;
    
}

Now, our object already has some defined properties that it can use. See below how we actually create our object in PHP, and note that to access and set values ​​in our objects, we use a right arrow with the characters - and >.

$cup = new Cup();
$cup->size = 'Large';
$cup->color = 'transparent';

In PHP, we cannot force any types, such as arrays, classes or interfaces, into object properties as we can in functions.

The first thing to notice is the use of the reserved word new to instantiate our object. The second thing is its use in the variable $cup. Next, we see our object in detail:

Object Cup
(
    [size] => Large
    [color] => transparent
)

Very interesting! However, so far, our object cannot do anything, it only has some properties (characteristics), but it does not have any behavior or action. Next, let’s add a method that will bring our object to life.

Methods are like functions: their signatures are exactly the same, but in Object Oriented Programming, methods have some additional features that we will see next, such as access modifiers (public, private, protected). But remember that within classes, functions are called methods.

class Cup {

    public $size;
    public $color;
    
    function addDrink($drink) {
        print 'Chosen drink' . $drink;
    }
}

We created our first method that actually doesn’t do much other than display the chosen drink. But as you can see, methods are exactly like functions, but within a class. And to access them, we use the same characters to access properties.

$cup = new Cup();
$cup->adddrink('water'); // Drink chosen: water

To inspect the methods that objects have, we can use the get_class_methods function, which will return an array containing all the class’s methods:

Array
(
    [0] => addDrink
)

Constants can also be defined for our objects with the reserved word const. We will create a constant in our Cup class called LIMIT, which will define the maximum limit that our object can receive drinks. We will also modify the addDrink method so that, in addition to the drink chosen, receive as a parameter how much of it will be added:

class Cup {

    const LIMIT = 100;
    
    public $size;
    public $color;
    
    function addDrink($drink, $quantity) {
        if ($quantity > self::LIMIT) {
            print 'The quantity exceeds the limit supported by the cup';
            exit();
        }

        print 'Chosen drink' . $drink;
    }
}

By convention, we define constants using only uppercase letters, but we can also use lowercase letters. However, remember that it is not advisable due to the code standards that PHP follows (see more at http://www.php-fig.org/). Therefore, always follow the convention. Below is an example just to illustrate:

class Cup {
    const limit = 100;
}

Unlike attributes and methods, for constants we use :: to access their values. We can access them in two ways: one is through the instantiated object, as we see:

$cup = new Cup();

print $cup::LIMIT; //100

And the other is using the full class name, without its instance:

$cup = new Cup();

$cup::LIMIT; //100

Inheritance

Inheritance is a well-known technique in OOP, in which we can inherit all the behavior of a class, eliminating code duplication and facilitating its maintenance. In PHP, we can inherit the behavior of a class using the reserved word extends.

The first thing we will do is create a class called Casa to represent the base class, and then we will create the class CasaReformada, which will extend the Casa class and, in addition to the inherited behavior, will have its specific method .

class Home {

    public $color;
    public $quantityOfRooms;
    
    function openRoomDoor()
    {
        print 'Open room door';
    }
}

Let’s suppose that our house, represented by the House class, has been renovated and now we can, in addition to opening the living room door, also open the bedroom window. But, instead of changing the Casa class, let’s just create another class and apply what changed in the reform.

class CasaReformada extends Casa {

    function openJanelaDoQuarto()
    {
        print 'Bedroom window open';
    }
}

When using inheritance, it is not necessary to rewrite the entire class. Instead, we can just inherit what the Casa class has, and in the CasaReformada class, we can just put what we want.

In PHP, there is no multiple inheritance, and it is not possible to inherit from multiple classes at the same time.

Now, in addition to having the methods and properties of the CasaReformada class, we also have those of the Casa class.

$casaReformada = new CasaReformada();
$casaReformada->cor = 'Blue';
$casaReformada->abrirPortaDaSala(); // Room door open
$casaReformada->openBedroomWindow() // Bedroom window open

Abstract class

Now that we understand inheritance, we can use abstract classes with PHP. Many times, we want to create a hierarchy, but without the need to create a concrete class (that can be instantiated). In these cases, we use abstract classes that provide us with a template for this.

Its syntax is very simple, just put the reserved word abstract before the reserved word class:

abstract class MyAbstractClass {}

We could use an abstract class to represent a web service, for example. We know that we need at least two actions: one to handle incoming requests, and another to handle responses to be sent to the client.

See how we can create a template for this web service:

abstract class WebService {
     abstract function Handleequest();
    
     abstract function handleResponse();
}

After that, we can specialize a different behavior for each type of web service, to handle requests and responses. Our first specialization will be made to handle requests/responses made with JSON:

class WebServiceJson {
     public function HandleRequest()
     {
         // handles data sent as JSON type
     }
    
     public function handleResponse()
     {
         // send JSON response
     }
}

Now imagine that we must manipulate such data, but in XML, supporting both JSON and XML. Thanks to our WebService template, we just need to create one more specialization to handle XML.

class WebServiceXml {
     public function HandleRequest()
     {
         // manipulate data sent as XML type
     }
    
     public function handleResponse()
     {
         // send XML type response
     }
}

Through this type of processing, we have the possibility of manipulating both types of data. See below how simple the code is without the need for if to identify which type of data we should use. The most important thing is that, if in the future there is a need to create another type of web service data manipulation, it will only be necessary to create a new class with the specific treatment.

// Manage JSON type requests
$json = new WebServiceJson();
$json->handleRequest();

// Manage XML type requests
$xml = new WebServiceXml();
$xml->handleRequest();

Unlike “normal” classes, abstract classes cannot be instantiated and, if you try to instantiate it, a FATAL ERROR is displayed, as shown in the example:

$abstract = new WebService(); // attempt to instantiate

PHP Fatal error: Cannot instantiate abstract class WebService in /my_dir/oop/classe_abstrata.php on line 5
PHP stack trace:
PHP 1. {main}() /my_dir/oop/classe_abstrata.php:0

Fatal error: Cannot instantiate abstract class WebService in /my_dir/oop/classe_abstrata.php on line 5

Call Stack:
     0.0001 230544 1. {main}() /my_dir/oop/classe_abstrata.php:0

We use abstract classes to create a generalization and then specialize specific behaviors in other classes. Let’s look at a new example using the types of people:

abstract class Person {

     public abstract function floor();

}

class Adult extends Person {

     public function walk() {
         print 'Quick';
     }
}

class Child extends Person {

     public function walk() {
         print 'Slowly';
     }
}

As in our example, we have an abstract class (contract) called Person, and we also have two classes that inherit from Person, which are the Adult and Child classes.

The first thing we can notice is that both the Person and Child classes override the walk method of our abstract class, and this is no coincidence. The second thing we should note is the use of the reserved word abstract in the declaration of the andar() method.

With abstract classes, we can declare methods without a body, using the abstract reserved word. This means that the inheriting class is forced to implement this method. In our example, we use this type of behavior so that classes that inherit from Person define how they walk, as adults and children walk differently, even if they are both the same type.

If an abstract method exists and it is not overridden in the child class, PHP will display a FATAL ERROR. Let’s try to create an Elder class, and just extend the Person class:

class Elderly extends Person {

}


$older = new Elderly();

See that we only extended the Pessoa class and did not override the andar method, which results in the fatal error, warning that we are forced to implement the andar method.

PHP Fatal error: Class Idoso contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Pessoa::andar) in /my_dir/oop/abstract.php on line 11

We can also define complete methods to be used by child classes. Let’s modify our abstract class from the previous example and add the eat method, but this time this method will be concrete and will not need to be overridden by the child classes (which inherit from the Person class).

abstract class Person {

     public abstract function floor();

     public function eat()
     {
         print 'Coming method invoked on object ' . get_class($this);
     }
}

The first interesting thing about our modification is the power that the abstract class gives us. We can force the child classes to implement the methods we want (in our example, we force the implementation of the andar method) and, in addition, we can create our own methods in the abstract class.

Another point to note is the use of the word $this within the comer method. As we cannot create instances of the Person class, the $this context will always refer to the child class that was instantiated. See below that the response when using the eat method changes according to the class we are using:

$adult = new Adult();
$adult->eat(); // Eat method invoked on the Adult object

$child = new Child();
$child->eat(); // Eat method invoked on the Child object

Trait

Starting with PHP version 5.4, a new concept was added to the language to overcome the lack of multiple inheritance. With that, trait came to life. Just like an abstract class, a trait cannot be instantiated, and we must use it together with a class.

A common task in which we can use traits is the famous logs. This way, it is possible to know what happens in different parts of the system, without having to duplicate code or create separate classes for this task. Logs are useful in production environments where the end customer has access and where we, programmers, cannot display data directly on the screen.

trait Log {}

A trait can have methods and properties, just like a normal class:

trait Log {

     public function record($message)
     {
         return file_put_contents('log.txt', $message);
     }
}

To use a trait, we must first import it into our class with the reserved word use. Note that, unlike classes that we import right after declaring the namespace, traits must be imported within the scope of the class.

class LogManager {
    
     use Log;
}

This way, we have access to all trait properties and methods within the LogGerenciador class.

$LogManager = new LogManager();

$logger->record('log message'); // method existing in the trait

We must pay attention to inheritance precedence when using traits. because elements of the class we import it into override existing methods in it.

class LogManager {
    
     use Log;
    
     public function record($message)
     {
         print 'This method overrides the existing method in the trait';
     }
}

When we try to access the write method now, instead of saving the content to the file with the file_put_contents function, the method will just display the message: "This method overwrites the existing method in the trait".

$LogManager = new LogManager();

// This method overrides the existing method in the trait
$logger->record('log message');

A very interesting behavior of trait is that we can use them with abstract methods, as well as in abstract classes. This allows us to create a generalization and force anyone who uses trait to implement the methods we want.

To illustrate, let’s use a Facebook post as an example. We will create a general trait in which the use of the message method will be mandatory. In other words, for each class that uses our trait, there must be a method that returns a message. After all, it is not possible to create a post without text.

trait FacebookPost {
    
     // All posts must contain a message
     abstract public function message();
}

Now that we have our base trait for the posts we are going to make, we can use it. To do this, we will create a class called PostSimples, which will only post text and nothing else.

trait PostSimple {
    
     public function message()
     {
         return 'Post containing text only';
     }
}

Now imagine that, in addition to the text message, we also need to create a post that contains images. How could we do this? With traits, solving this problem is very easy. Let’s create a new class called ImagemPost, which will be responsible for managing the image and message for our post.

As the FacebookPost trait has an abstract method, we are forced to implement it ensuring that the image post will also have a text message.

class PostImage {
     use FacebookPost;

     public function message()
     {
         return 'Message from ImagePost class';
     }
    
     public function image()
     {
         return 'facebook.png';
     }
}

If we try to use a trait that has an abstract method and do not override it, a FATAL ERROR will be displayed by PHP.

class PostImage {

     use FacebookPost;
}

PHP Fatal error: Class ImagemPost contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (ImagemPost::mensagem) in /my_dir/oop/trait.php on line 9
PHP stack trace:
PHP 1. {main}() /my_dir/oop/trait.php:0

In addition to the use shown here of traits, there are some interesting small nuances, such as, for example, modifying the access of each method in the class in which the trait is used and method overriding. See the official PHP documentation, at http://php.net/manual/language.oop5.traits.php, which has this additional information.

##interface

Interfaces are a little further beyond generalization than abstract classes. Imagine that interfaces are like contracts, in which whoever implements them must follow the rules. However, it doesn’t matter how you get the result and, unlike abstract classes, interfaces do not have a body in their methods.

interface Car {}

Defining an interface is very simple, as we can see in this example. However, our interface does not have any methods. Unlike abstract classes, which can have complete method definitions, interfaces can only have their definitions.

interface Car {

     public function accelerate();

     public function stop();
}

To implement our interface, we must use the reserved word implements.

class EconomyCar implements Car {}

The script generates a FATAL ERROR, because, when we use interfaces, we are forced to implement all the methods defined in it. Interfaces are like contracts and, as in a contract, we must follow everything that is written, and implement all existing methods in the interface.

PHP Fatal error: Class CarroEconomico contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Carro::acelerar, Carro::parar) in /my_dir/oop/interface.php on line 10
PHP stack trace:
PHP 1. {main}() /my_dir/oop/interface.php:0

Let’s correct our class so that it works with our interface:

class EconomyCar implements Car {

     public function accelerate() {}

     public function stop() {}
}

Final

Just as we can inherit properties and methods from classes, we can also prevent this behavior. In other words, it is possible to block inheritance of the class we created using the final reserved word.

final class Television {
     public $channel = 99;
}

See that we have the Television object, which has a channel property. Let’s now try to define a new channel by inheriting this class:

class NovaTelevisao extends Televisao {
     public $channel = 99;
}

When executing this script, we get a FATAL ERROR, as we cannot inherit from any class that is final.

PHP Fatal error: Class NovaTelevisao may not inherit from final class (Televisao) in /my_dir/oop/final.php on line 9

Fatal error: Class NovaTelevisao may not inherit from final class (Televisao) in /my_dir/oop/final.php on line 9

Therefore, we chose to block the entire class from being inherited, but we can also define only certain methods so that they are not inherited. Let’s then apply this to our Television class.

class Television {
     private $channel = 99;

     final public function changeCanal($canal)
     {
         $this->channel = $channel;
     }
}

Now, we can effectively extend the class, but we cannot inherit the mudarCanal($canal) method.

class NovaTelevisao extends Televisao {}

Now we will try to override the mudarCanal($canal) method.


class NovaTelevisao extends Televisao {

     public function changeChannel($channel) {}
}

We get a FATAL ERROR as expected, as this method is final.

PHP Fatal error: Cannot override final method Televisao::mudarCanal() in /my_dir/oop/final.php on line 18

Fatal error: Cannot override final method Televisao::mudarCanal() in /my_dir/oop/final.php on line 18

In class properties, it is not possible to use the reserved word final. For this, in PHP, we use the reserved word const.

Access modifiers

In PHP we have three types of access modifiers and in this section we will explore how to use each of them in detail, see below the list of possible access modifiers:

  1. public (public)
  2. protected (protected)
  3. private (private)

public

The public, as we have already seen, is where we can access our properties and methods freely. Public mode is assumed by PHP by default if we do not declare any type of access, that is, if we do not use the reserved word public.

For class properties, declaration is mandatory, but for methods, it is not.

class Car {

     //restricted word public, protected or private mandatory
     public $mark;
    
     // public word omitted
     function callMotor()
     {
         print 'Engine on';
     }
}

But it is also possible to make this explicit in the code whenever we want:

class Car {

     public $brand;
    
     public function callMotor()
     {
         print 'Engine on';
     }
}

Without restrictions, we can easily use our object as follows:

$myCar = new Car();
$myCar->brand = 'Ford';
$myCar->startEngine();

protected

Now, we start to restrict the access we want using the word protected. Let’s start with the $brand property. Let’s change it to be protected, not public anymore.

class Car {

     protected $marca = 'GM';
    
     public function callMotor()
     {
         print 'Engine on';
     }
}

Look at this code and, based on what you’ve seen so far, try to guess what the result will be when you run the following code:

$myCar = new Car();
$myCar->brand = 'Ford';
$myCar->startEngine();

When we execute the code, PHP returns the following FATAL ERROR:

PHP Fatal error: Cannot access protected property Carro::$marca in /my_dir/oop/visibility/protegido.php on line 14
PHP stack trace:
PHP 1. {main}() /my_dir/oop/visibility/protegado.php:0

Fatal error: Cannot access protected property Carro::$marca in /my_dir/oop/visibility/protegido.php on line 14

Call Stack:
     0.0002 233888 1. {main}() /my_dir/oop/visibility/protetido.php:0

Unlike the public modifier, properties and methods that use protected can only be accessed from within the class itself or from classes that inherit from that class. Let’s use our Car class as a template for our Truck class.

class Truck extends Car {

     public function displayMarca()
     {
         print $this->mark;
     }
}

As we can no longer directly access the $marca property, we are forced to create a method to manipulate it.

$caminhao = new Caminhao();
$truck->exhibirMarca(); //GM

###private

We arrive at the most restricted way that a property or method can have, within a class. Using private, only the class itself has access to the attribute or method.

In our example below, we have the attribute $numberOfRodas that belongs only to the Car class. See the modified Car class:

class Car {

     protected $brand = 'GM';
     private $numberOfWheels = 4;
    
     public function displayNumeroOfRodas()
     {
         print $this->numberOfWheels;
     }
}


$carro = new Carro();
$car->exhibirNumeroOfRodas();

When we run this script, we have the following result:

4

But, what if we try to extend this class to access the $wheelnumber attribute? Look at the following class and try to figure out what the output generated by the script will be, before moving on to the answer.

class Van extends Car {
     public function __construct()
     {
         print $this->numberOfWheels;
     }
}

$van = new Van();

When we instantiate the Van class, there is no property in the class called $wheelnumber, which causes PHP to display the Undefined property error.

PHP Notice: Undefined property: Van::$numeroDeRodas in /my_dir/oop/private.php on line 15
PHP stack trace:
PHP 1. {main}() /my_dir/oop/visibility/private.php:0
PHP 2. PenetaPreta->__construct() /my_dir/oop/private.php:19

This occurs because the property only belongs to the Car class, and only it can manipulate this property. No class that extends it or any external access attempt can modify it

$this

In our previous examples, to access class properties and methods, we used a special variable called $this. It is reserved for PHP and cannot be used as any variable, which makes the following example invalid code:

$this = 'ZCPE';

When running this script, we get the following error:

PHP Fatal error: Cannot re-assign $this in /my_dir/oop/this.php on line 3

Fatal error: Cannot re-assign $this in /my_dir/oop/this.php on line 3

This shows us that we cannot use $this as any variable. $this refers to the current class instance. We need to use $this to access any method or property within a class; otherwise, PHP will try to look for a variable within the scope of the method. See the following example:

class File {
     private $file = 'file.txt';
    
     public function displayName()
     {
         print $file;
     }
}

$file = new File();
$file->displayName();

What do you think will happen to this code when it runs?

PHP Notice: Undefined variable: file in /my_dir/oop/this.php on line 8
PHP stack trace:
PHP 1. {main}() /my_dir/oop/this.php:0
PHP 2. File->displayName() /my_dir/oop/this.php:13

Notice: Undefined variable: file in /my_dir/oop/this.php on line 8

Call Stack:
     0.0001 233264 1. {main}() /mnt/c/wamp/www/github/my_dir/oop/this.php:0
     0.0013 233480 2. File->displayName() /mnt/c/wamp/www/github/my_dir/oop/this.php:13

We get the error Undefined variable: archive, which tells us that the variable $file does not exist. To fix this error, we need to use $this to refer to the $file class property.

...

public function displayName()
{
     print $this->file;
}

After this adjustment, we were able to execute the code perfectly and have the file name displayed as a result.

file.txt

All items covered so far about access modifiers are covered in the official PHP documentation, which you can check at http://php.net/manual/language.oop5.visibility.php. In the official documentation, they call visibility instead of access modifiers.

Magic methods

As PHP evolved, the language provided many features for object-oriented programming. One of them is magical methods, which provide us with enormous flexibility and also advantages when using them. All magic methods begin with the characters __ (two underscores) followed by their name. Below is a list of existing methods to date:

Method Description
__construct Object constructor.
__destruct Object destructor.
__call Invoked when a method does not exist on the object.
__callStatic Invoked when a static method does not exist on the object.
__get Invoked when a non-existing property of the object is used.
__set When a property invoked to set its value does not exist, this method is executed.
__isset When a member is not found, __isset is invoked using the isset() or empty() functions to perform this check.
__unset When declared, this magic method is responsible for receiving the call when the unset() function is used and for non-accessible members.
__sleep When you want to confirm some data that is pending for the class to function, to clean properties already set, or deal with very large objects, __sleep is the method you will use for this purpose.
__wakeup When we use the method to serialize (in this case, __sleep), in some contexts we may end up losing a connection to the database. If it is necessary to do the opposite (in this case, deserialize), we use __wakeup and, in addition, we could also reestablish the connection with the database.
__toString It is invoked, for example, when you try to execute echo on the object, PHP will convert it to strings and, if your class contains the magic method __toString, the contents of the method will be displayed.
__invoke It is invoked when you try to call an object as a function, not to be confused with functions you create or methods.
__clone Method invoked when cloning the object with the reserved word clone

These are the magic methods that are covered in the PHP 5.5 certification, but there is one more magic method added to version 5.6 of the language. It will not be covered here, as our focus is PHP 5.5. For more information about __debugInfo, access the official documentation at http://php.net/manual/language.oop5.magic.php#object.debuginfo.

__construct

The __construct method is well used, as it is what we use to pass arguments in the construction of our object. Previously, in version 4 of PHP, we used the same class name to do this type of work (just as it is done in Java), however, after the implementation of the magic method __construct, it fell into disuse.

class Book {
    
     public function __construct($author)
     {
         print $author;
     }
}

As we can see, we are using __construct to make it mandatory to pass the $author parameter.

$casaDoCodigo = new Livro('Martin Fowler');

When we run this method, the result we get is:

Martin Fowler

__destruct

Just as we have a method to use as soon as we instantiate an object, we also have one that is executed as soon as the object is destroyed from memory. However, notice that this does not receive any arguments:

class Book {
    
     public function __construct()
     {
         print 'Book Object created';
     }
    
     public function __destruct()
     {
         print 'Destroyed Book Object';
     }
}

$book = new Book();
sleep(2);

The sleep function will help us better visualize when the object was destroyed. When we run this script, we have the following result:

Book object created
Destroyed Book object // will appear after 2 seconds

We can also simulate the destruction of this object using the unset function. This time, let’s destroy the object before the script finishes executing:

$book = new Book(); Book object created
sleep(1);

unset($book); // Destroyed Book Object

sleep(2);

Through this example, we were able to define exactly when our object will be destroyed. After instantiating the Book class, the message Book Object created is displayed and the script execution will be interrupted for 1 second. Right after this 1 second, we destroy the object with the unset function and the message Book Object Destroyed is displayed to us.

And again we use the sleep function to stop the script from executing for 2 seconds. After this time, the script execution ends.

Although we use __destruct in conjunction with the sleep function to destroy objects, we must be careful, as we cannot determine at what time exactly the object will be cleaned from memory through the carbage collector. However, the __destruct method is an excellent place to perform closing operations, such as connecting to databases and data or streams.

__call

Using the __call method, we can access methods without declaring them within our class. With each call of a method that does not exist in the class or is not accessible (private or protected methods), PHP will look for the __call method and execute it.

class Cellphone {

     public function __call($method, array $args) {
         print 'Method ' . $method . 'invoked';
     }
}

We define a class with just the magic method __call, which means that it doesn’t matter which method we call, because that method will always be invoked. See that, in our example, we tried to invoke the call method. Since it does not exist in our Cellular class, the __call method is invoked instead.

$motorola = new Cellular();
$motorola->connect();

The result we get when running the script is the message:

Call method invoked

Also see that we have a second argument in the __call method. It serves us to receive all arguments sent to the invoked method. All parameters are placed in an array, and we can iterate over this array to get each of these arguments. Next, we modify our Cellular class so that, in addition to displaying the method that was invoked, it also displays all arguments passed through foreach.

class Cellphone {

     public function __call($method, array $arguments) {
         print 'Method ' . $method . ' invoked with arguments' . PHP_EOL;
        
         foreach($arguments as $argument) {
             print $argument . PHP_EOL;
         }
     }
}

Let’s also change the use of the class to define the number of the Cellular object, invoking the definirNumero method.

$motorola = new Cellular();
$motorola->setNumber('(11) 1234-1234');

Now we can display the invoked method and also the passed parameters:

defineNumber method invoked with arguments
(11) 1234-1234

The __call method has some details regarding access modifiers that we will see below. Imagine that we have a method in the Cellular class called ligarTela, but this method is protected (protected), as shown in the example:

class Cellphone {

     protected function callScreen($seconds)
     {
         print 'Turning on cell phone screen by ' . $seconds . 'seconds';
     }

     public function __call($method, array $arguments) {
         print 'Method ' . $method . ' invoked with arguments' . PHP_EOL;
        
         foreach($arguments as $argument) {
             print $argument . PHP_EOL;
         }
     }
}

Now let’s invoke the ligarTela method, which we just created:

$motorola = new Cellular();
$motorola->connectScreen(2);

What output will we get when running this script?

CallScreen method invoked with arguments
two

The __call method is invoked. This occurs because the ligarTela method is not accessible through the instance of the Cellular class, and the same occurs with private methods. Always remember that if the class method does not exist or is not accessible, the __call method will be invoked.

In the same way that we used the __call method, it is possible to use __callStatic. The differences between these methods are simple: the __callStatic method is called in the static context, and its signature must also be static.


class Car {
    
     public function __callStatic($method, $arguments)
     {
         print 'Statically invoked method:' . $method;
     }
}

Car::call();

When we run this script, we get the following result:

Statically invoked method :call

__get

As the name suggests, the __get method is used to return the value of a non-accessible or non-existent property of a class.

class Fan {

     public function __get($name) {
         print 'Attempt to access property ' . $name;
     }
}

As we can see, the __get method must have the parameter, as it is through this that we will know which property they tried to access from that object.

$fan = new Fan();
$fan->brand;

The result we get when running the script is:

Attempt to access the brand property

__set

As we have just seen, we have a method exclusively to be invoked when a non-existing property is called. We also have a method specifically to be called if they try to set a value to a property that is not accessible or does not exist. This gives us a lot of flexibility in working with objects to set or return values.

In our example below, we created a Fan class, and only defined the __set method so that it is possible to define any property within the object, without it actually existing.

class Fan {

     public function __set($name, $value) {
         print 'Attempt to set the property value ' . $name . ' with the value ' . $value;
     }
}
$fan = new Fan();
$fan->price = 90.00;

And the result we get is:

Attempt to set the value of the price property to the value 90

__isset

When a property is not found using the isset or empty functions, the __isset magic method is invoked. See our example below, in which we use the Collection class simply to store the properties and values within the $dados array:

class Collection
{
     private $data = [];
    
     public function __set($name, $value)
     {
         echo "Assigning the index '$nome' with the value '$valor'";
         $this->data[$name] = $value;
     }
}

$obj = new Collection;

$obj->a = 1;
echo $obj->a; // 1

Now that we have our class working, let’s implement the __isset method. Therefore, every time we use the isset or empty function, the __isset method will be invoked, thus allowing us to check whether the accessed property exists in our $dados array

class Collection
{
     private $data = [];
    
     public function __set($name, $value)
     {
         echo "Assigning the index '$nome' with the value '$valor'";
        
         $this->data[$name] = $value;
     }
    
     public function __isset($name)
     {
         echo "Check if '$name' was set?";
        
         return array_key_exists($name, $this->data);
     }
}
    
$obj = new Collection();

$obj->a = 1;

$propertyA = isset($obj->a);

var_dump($propertyA);

When we run the script, we have the following result:

Assigning the index 'a' with the value '1'

Check if 'a' was set? bool(true)

Let’s go in parts to make it easier to understand. The first thing we do here is create a Collection object and assign the value 1 to the a property. As the property a does not exist, the __set method is invoked and, in this way, we store the name of the invoked property (a) and its value (1) in the $dados array.

After that, we use the isset function to check if the property really exists. By doing this, the __isset method is invoked and we check whether the property a exists in our array $data by the array_key_exists function. As it was previously defined with the value 1, true is returned (true), and we assign this value to the variable $propiedadeA.

Lastly, we just display the value of the $propertyA property with the var_dump function.

We used the isset function in our example, but if we change it to the empty function, we get the same result.

__unset

We can use the __unset magic method to remove properties defined with the __set magic method. To make it easier to understand, we will continue with our Collection class that we used in the previous section.

We’re just going to make a small modification to it to use the __unset method, and we’re going to remove the __isset method. See how our Collection class will look:

class Collection
{
     private $data = [];
    
     public function __set($name, $value)
     {
         $this->data[$name] = $value;
     }
    
     public function __unset($name)
     {
         // Remove the property only if it exists in the $data array
        
         if (array_key_exists($name, $this->data)) {
             unset($this->data[$name]);
         }
     }
}

The first thing we are going to do to understand the __unset method is to define some properties. Let’s create two of them: property b and property c, both with the value 1.

$obj = new Collection;

$obj->b = 1;
$obj->c = 1;

print_r($obj);

When executing this script, we have the following result:

Object Collection (
     [data:Collection:private] => Array
         (
             [b] => 1
             [c] => 1
         )
)

With our properties defined, we can now remove them using the unset function. When using this function, we automatically invoke the __unset method within the Colecao class.

unset($obj->b);

print_r($obj);

Removing the b property left us with only c, and we proved this by displaying the entire Colecao object with the print_r function.

Removing property b

Object Collection
(
     [data:Collection:private] => Array
         (
             [c] => 1
         )

)

__sleep and __wakeup

__sleep and __wakeup are methods especially for working with serialization of your object. When PHP’s serialize function is called on the object, the __sleep method is invoked if it exists in the class. The same occurs with the __wakeup method when the unserialize function is invoked.

class Serialize {

     public function __sleep()
     {
         print 'Method invoked when using the serialize function';

         return [];
     }
}

serialize(new Serialize());

The first thing we must do is create our __sleep method in the class, and we must return an array-type value; otherwise, the following NOTICE will be displayed:

Notice: serialize(): __sleep should return an array only containing the names of instance-variables to serialize in /my_dir/oop/__sleep.php on line 13
PHP stack trace:
PHP 1. {main}() /my_dir/oop/__sleep.php:0
PHP 2. serialize() /my_dir/oop/__sleep.php:13

Executing this code will display the following message:

Method invoked when using the serialize function

Once serialized, we can also return the state of the object using the unserialize function. Let’s change our class that we used as an example for this:

class Serialize {

     public function __wakeup()
     {
         print 'method invoked when using the unserialize function';
     }
}

$serialized object = serialize(new Serialize());

unserialize($objectSerialized);

Unlike the __sleep method, we do not need to return any value when implementing the __wakeup method. The only thing we need to make sure of before using the unserialize function is to invoke the serialize method first; otherwise, the following WARNING will be displayed:

PHP Warning: unserialize() expects parameter 1 to be string, object given in /my_dir/oop/__serialize.php on line 13
PHP stack trace:
PHP 1. {main}() /my_dir/oop/__serialize.php:0
PHP 2. unserialize() /my_dir/oop/__serialize.php:13

If you ran this code, you saw that we used the serialize function before invoking the unserialize function. Therefore, the following result will be displayed:

method invoked when using the unserialize function

__toString

To understand the __toString method, let’s first imagine the following scenario: you have a Book class with some public properties, such as $name and $author.

class Book {
     public $name;
     public $author;
}

And you want to print the existing values and properties using echo or print.

$zcpe = new Livro();

print $zcpe;

However, when we run the code, we get a FATAL ERROR, saying that we cannot convert an object to a string:

PHP Catchable fatal error: Object of class Livro could not be converted to string in /my_dir/oop/to_string.php on line 6
PHP stack trace:
PHP 1. {main}() /my_dir/oop/to_string.php:0

Catchable fatal error: Object of class Livro could not be converted to string in /my_dir/oop/to_string.php on line 6

Call Stack:
     0.0001 230688 1. {main}() /my_dir/oop/to_string.php:0

For this to work, we must implement the magic __toString method in our class, which gives us the possibility of displaying whatever we want from the object through a string.

class Book {
     public $name;
     public $author;

     public function __toString()
     {
         return 'name: ' . $this->name . ' author: ' . $this->author;
     }
}

Note that, when implementing the method, we can define how the object will be displayed. In our case, we are just displaying the instance values.

__invoke

Through the __invoke method, we can treat objects as functions.

class Computer {
     public function __invoke()
     {
         print '__invoke method executed';
     }
}

By implementing this method, it is possible to use our object with the same syntax as a function:

$computer = new Computer();

$computer(); // invoking the object with a function

When executing the code, we have the following result:

__invoke method executed

We can also pass arguments, in addition to executing our object as a function. Let’s modify our class. To do this, we will use func_get_args, which returns all the parameters passed to a function, and we will go parameter by parameter through foreach to display them.

class Computer {
     public function __invoke()
     {
         print '__invoke method executed';

         foreach (func_get_args() as $parameter) {
             print 'parameter: ' . $parameter;
         }
     }
}

After this small modification, we can now retrieve any passed parameter:

$computer = new Computer();

$computer(1, 2, 'third parameter');

When running this script, we get the following result:

__invoke method executed
parameter: 1
parameter: 2
parameter: third parameter

If you prefer, you can also fix the exact number of arguments you want. Next, we will change our Computer class to accept only two arguments: $name and $brand.

class Computer {
     public function __invoke($name, $brand)
     {
         print '__invoke method executed';

         print 'name : ' . $name . ' brand: ' . $brand;
     }
}

Choosing this type of syntax makes it mandatory to pass parameters, just like in a method or a normal function.

$computer = new Computer();

$computer('Computer1', 'Asus');

__clone

The magic method __clone is only used when trying to clone an object with the reserved word clone. If the __clone method exists within the class, it will be executed; otherwise it will just be ignored.

class Home {
     public $number;
}

$casa1 = new Casa();
$casa2 = clone $casa1;

In this example, we are not using any magical method and, even so, we managed to clone our object, which is perfectly valid. We have two identical objects so far.

var_dump($casa1 == $casa2); // bool(true)

The objects are the same as we do not change any properties within them. Let’s then change the number of the object $casa1 and $casa2.

$casa1->number = 123;
$casa2->number = 456;


var_dump($casa1 == $casa2); // bool(false)

Now, we get false when trying to compare the objects $casa1 and $casa2, as these objects have different values. However, let’s imagine that we are going to use the Address class together with the Casa class to make our code more elegant.

class Address {
     public $street;
     public $number;
}

We will make a small modification to our Casa class to use the Address class. Let’s instantiate a new Address object in the constructor method of the Casa class.

class Home {
     public $color;
     public $address;

     public function __construct()
     {
         $this->address = new Address();
     }
}

Now that we have both objects, let’s clone them:

$casa1 = new Casa();
$casa2 = clone $casa1;

var_dump($casa1 == $casa2); // bool (true)

Nothing new, right? We just clone the Casa class and, comparing the two objects, we get the answer true. In the next example, try to analyze more carefully:

$casa1 = new Casa();
$casa1->address->street = 'Av. São Paulo';

$casa2 = clone $casa1;
$casa2->address->street = 'Av. Brazil';

var_dump($casa1 == $casa2); // bool (true)

Can you understand why even changing the $street property of the Address object we still get true as a response?

This occurs because in PHP we have the “raza” type of cloning, in which objects inside other objects are not cloned. What happens is that the object that performs the cloning (in our case, the variable $casa2) continues to point to the object Address in the variable $casa1.

Now that we understand the problem, we need to understand how we can solve it. In our case, we will force the cloning of the internal objects of our class through the magic method __clone.

class Home {
     public $color;
     public $address;

     public function __construct()
     {
         $this->address = new Address();
     }

     public function __clone()
     {
         $this->address = clone $this->address;
     }
}

With the addition of the __clone magic method in our Casa class, we force the cloning of the Address object in the $address property.

$casa1 = new Casa();
$casa1->address->street = 'Av. São Paulo';

$casa2 = clone $casa1;
$casa2->address->street = 'Av. Brazil';

var_dump($casa1 == $casa2); // bool (false)

Now we have the expected result when comparing the $casa1 object with the $casa2 object. It is very important to remember this concept, as it is a reason for prank questions on the certification test. Just try to note that PHP, by default, does a “clear” cloning (shallow) and, to change this behavior, we must use the magic __clone method.

It is important to note that implementing magic methods within a class is not mandatory. All will be executed according to the scenario they propose and only if they are implemented; otherwise, PHP will just ignore these methods and continue with its normal execution.

Try/catch exceptions

In programming, there are cases where, depending on our verification, we decide not to return a value, for example, a boolean. Sometimes we just want to divert the execution of our script if something doesn’t go the way we’re expecting, like a logical check (1 == 2).

Execution flow and exception handling {w=100%}

Note that, in our flow, process 3 will only be executed if an exception is thrown; otherwise, execution will proceed normally through flow 1 and 2.

Fortunately, PHP has exception throwing, which allows us to divert the flow of our script’s execution however we want. To create your first exception, you must use the reserved word throw in your code.

function largestNumber($firstNumber = 0, $secondNumber = 0) {
     if ($firstNumber > $secondNumber) {
         throw new Exception('The first number is greater than the second');
     }
    
     return true;
}

numberLargest(2, 1);

When we run the script, we have our exception thrown:

PHP Fatal error: Uncaught exception 'Exception' with message 'The first number is greater than the second' in /my_dir/excecao.php:5
Stacktrace:
#0 /my_dir/excecao.php(11): numberLargest(2, 1)
#1 {main}
   thrown in /my_dir/excecao.php on line 5

Fatal error: Uncaught exception 'Exception' with message 'The first number is greater than the second' in /my_dir/excecao.php on line 5

Exception: The first number is greater than the second in /my_dir/excecao.php on line 5

Call Stack:
     0.0002 232432 1. {main}() /my_dir/excecao.php:0
     0.0002 232560 2. numeroMaiores() /my_dir/excecao.php:11

This is PHP’s default behavior: when faced with an exception that is not handled, a fatal error is displayed, and the script ends. In many cases, including ours, we do not want a FATAL ERROR to be displayed to the user. To do this, we need to use exception handling through the try and catch blocks.

Through this handling, we were able to “catch” this exception thrown by the script and handle it in an elegant way for our user, without ending the script with a fatal error.

try {

} catch (Exception $excecao) {
}

See the syntax used to use the try and catch blocks. Inside the try block, this is where we should place our main flow of the script, and inside the catch block, this is where we should handle the exception, if it is thrown.

Let’s look at a new example using the same function we used previously:

try {
     numberLargest(2, 1);
} catch (Exception $excecao) {
     print $excecao->getMessage();
}

Now, running our script again, we have a much more elegant output without fatal errors displayed to the user:

The first number is greater than the second

Note that, inside the catch block, we are expecting an object of type Exception. And it is through this object that we can access different methods to find out which exception was thrown, what the message code was, etc.

See the Exception class in detail in the PHP documentation, at http://php.net/manual/language.exceptions.extending.php

In addition to handling our exceptions in the catch block, we can specify which exception we want to handle. Let’s modify our script so that it throws two different exceptions:

function largestNumber($firstNumber = 0, $secondNumber = 0) {
     if ($firstNumber > $secondNumber) {
         throw new Exception(
             'The first number is greater than the second'
         );
     }
    
     if ($firstNumber === $secondNumber) {
         throw new InvalidArgumentException(
             'The numbers must not be the same'
         );
     }
    
     return true;
}

We now have a function that throws two different types of exceptions:

try {
     numberLargest(1, 1);
} catch (Exception $excecao) {
     print $excecao->getMessage();
}

Note that we have a generic exception class, which is Exception. In other words, all exception classes inherit from the Exception class, which makes exception handling very easy, as we saw in the previous example. Note that the type of exception thrown is an invalid argument InvalidArgumentException, however in the catch block we use the generic class Exception to handle any exception thrown within the try block.

With this, we have the following result after running the script:

The numbers must not be the same

Now let’s make things a little more difficult. With exceptions, we saw that we can throw several of any type and handle them through the catch block with the Exception class. But what if we want to apply different treatment depending on the exception thrown? With PHP, it is possible to nest several catch blocks.

try {
     numberLargest(1, 1);
} catch (InvalidArgumentException $excecao) {
     print 'Invalid argument ' . $excecao->getMessage();
} catch (Exception $excecao) {
     print 'Generic treatment: ' . $excecao->getMessage();
}

As the example shows, we can specify different types of treatments for certain exceptions. Furthermore, it is possible to define generic handling with the Exception class if no handling is applied to a specific exception. When we run the script, we have the following result:

Invalid argument The first number is greater than the second

But, in addition to specific or generic treatment, we have a very important rule about exceptions, which is the order in which they are handled:

The catch block will be executed by the first class that satisfies the exception that was thrown.

Let’s take as a basis the previous example that executes the catch block and expects an exception of type InvalidArgumentException. Pay attention to the following part:

} catch (InvalidArgumentException $excecao) {
     print 'Invalid argument ' . $excecao->getMessage();
} catch (Exception $excecao) {
     print 'Generic treatment: ' . $excecao->getMessage();
}

What if we invert the catch blocks?

} catch (Exception $excecao) {
     print 'Generic treatment: ' . $excecao->getMessage();
} catch (InvalidArgumentException $excecao) {
     print 'Invalid argument ' . $excecao->getMessage();
}

What would be the output when we run the code again?

This time, we have a different result:

Generic treatment: The numbers must not be the same

This occurs because PHP does not have cascading exception handling, that is, the first catch that satisfies its condition will be executed.

##finally

In PHP 5.5, we introduced a new reserved word for working with exceptions, called finally. With this new functionality, we can guarantee the execution of the code within the finally block, regardless of whether an exception is thrown or not.

try {
     // Normal flow
} catch (Exception $excecao) {
     // Exception handling
} finally {
     // Mandatory execute this block
}

Let’s go to our first example, which will be forcing an exception to be thrown:

try {
     throw new Exception('Exception thrown');
} catch (Exception $excecao) {
     print $excecao->getMessage();
} finally {
     print 'Block finally executed';
}

When we run this script, we have the following result:

Exception thrown
Finally block executed

The finally block is executed even if an exception is not thrown:

try {
     print 'Normal execution';
} catch (Exception $excecao) {
     print $excecao->getMessage();
} finally {
     print 'Block finally executed';
}

Which gives us the following result:

Normal execution
Finally block executed

Creating your exception

So far, we have only used exceptions that PHP provides us by default, but we can also create our own exception to use. Below, we have an image of what the PHP exception hierarchy looks like.

PHP exceptions hierarchy {w=85%}

The base class for all exceptions is Exception. To create our own exception, just extend it.

class Invalid Number extends Exception {}

With that, we already have our own exception to use:

function throwExcecao($number)
{
     if (!is_numeric($numeric)) {
         throw new InvalidNumber(
             'Variable $number is not of numeric type'
         );
     }
}

Like the other exceptions, we can handle our exception normally:

try {
     throwExcecao('string');
} catch (InvalidNumber $excecao) {
     print $excecao->getMessage();
}

And if we run the script, we have the following result:

Variable $number is not of numeric type

PHP already provides us with some exception classes to use in our code. You can see the classes that exist at the time of writing this book in the table below. Probably, as the language evolves, new classes will be added.

  • BadFunctionCallException – Must be used when a call is made to an undefined function/argument.
  • BadMethodCallException – Must be used when a call is made to an undefined method.
  • DomainException – Must be used when the expected domain value is not met, for example, when you expect an image of type .jpeg and receive one of type .png, or when you expect an email from domain @casadocodigo and receives a @gmail.
  • InvalidArgumentException – Must be used when the expected argument value is not correct.
  • LengthException – Should be used when the length received is larger than expected, for example, a person’s name. You are expecting a name with a maximum of 30 characters, but one with 40 is sent.
  • LogicException – Should be used when the necessary logic is not satisfied
  • OutOfBoundsException – Used when errors cannot be detected at compile time, such as a very large integer.
  • OutOfRangeException – Should be used when the desired range is not satisfied, such as trying to access a key that does not exist in an array.
  • OverflowException – Should be used when it is no longer possible to add items to a full container. If you have a list that only accepts 30 items and they try to add one more, this exception should be used.
  • RangeException – Must be used when the expected range is not reached.
  • RuntimeException – Should be used when errors are found during program execution.
  • UnderflowException – Should be used when trying to manipulate an empty container (the opposite of the OverflowException exception).
  • UnexpectedValueException – Instead of the argument being invalid like the InvalidArgumentException exception, this exception should be used when the expected value is not provided.

Late static binding and self

Before we discover what Late static binding is, we must first understand what the reserved word self is and what its limitations are.

We use the reserved word self to refer to the static context, just as $this is used to refer to the instance. Let’s look at an example to make it clearer:

class A
{
     public static function who()
     {
         print __CLASS__;
     }

     public static function test()
     {
         self::who();
     }
}

class B extends A {
     public static function who() {
         echo __CLASS__;
     }
}

B::test();

This example shows us two classes: class A and class B, which extends class A, and two static methods in class A. In class B, we have a single method that overrides the method in class A.

Try analyzing the code for a few minutes, and then proceed to the explanation.

Normally, we would think that the response to this code when executed would be to display the name of class B, but what is displayed is the name of class A (I recommend that you try running this code on your computer). But, after all, why does this happen?

The answer is very simple and practical. In PHP, we have the self context which refers to the context of the class in which the method or property was defined, not the class that is invoking it. In other words, in our example, the person invoking the method is class B, but the method test is defined in class A which, in turn, invokes the method of the person using the reserved word self to refer to its own class (i.e. class A).

Now that we understand static behavior, we can figure out what this late static binding thing is. This fancy name is nothing more than referring to the class that is invoking the property or method rather than where they were defined.

class A
{
     public static function who()
     {
         print __CLASS__;
     }

     public static function test()
     {
         static::who(); // Using late static binding
     }
}

class B extends A {
     public static function who() {
         echo __CLASS__;
     }
}

B::test();

By changing our script from self to static, we managed to get the expected result. When executing the script, the class name B is displayed, and no longer A.

In the language’s official documentation, you will find a topic just about late static binding, which can be seen at http://php.net/manual/language.oop5.late-static-bindings.php.

Summary

This is the point where, generally, for those starting out with Object Oriented Programming, they really feel like a real programmer! But, in addition to being a subject that attracts a lot of attention, it is also a real step forward for us, as PHP was not born with all the baggage that we have today, with OOP.

Before we finish, another issue to highlight is the use of the standard classes provided by PHP, the famous SPL. You may have noticed that throughout the chapters we have used some in our examples, and this will continue in the remaining chapters. However, we will not dedicate a chapter just to this, as SPL in itself would already be a book.

However, we advise you to see the SPL page in the official PHP documentation, at http://php.net/manual/book.spl.php. There are questions related to this topic. We are unable to specify which class you may fall into on the day of your test, but take a look at as many classes as you can. If you want a tip, pay attention to these classes:

  • ArrayAccess
  • ArrayObject
  • ArrayIterator
  • IteratorIterator
  • RecursiveIteratorIterator
  • InvalidArgumentException
  • SplFileObject
  • SplObserver
  • SplSubject

Resources