What's New in PHP 8 - Part 2

Ronaldo B.July 27, 202213 min read

In this article

This is the second article in our series about the new features of PHP 8. In the first article, we talked about 8 new features of this version. You can access that article by clicking here.

In this article, we will discuss 8 more new features of the version. Are you ready? Then let's go for more news!

New Nullsafe Operator

The latest versions of PHP have added several operators, which have helped us in various situations. In version 8, we will have the implementation of one more operator that will also be very useful. We are talking about the Nullsafe operator.

An excellent habit we developed when we began to mature as programmers is to always execute some code after verifying that its expression is not null. Usually, we do this using an if() checking if that variable is different from NULL. This is great; but if the checks are too extensive, our code can become verbose and repetitive. Let's see an example of this.

Imagine that we have a city class. It will have the following content:

class City {

    public function __construct(
        public string $name,
        public string $state
    ) {

    }

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

    public function get_name() {
        return $this;
    }

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

}

Let's instantiate a new instance of this class with the following code:

$city = new City("Sao Bernardo do Campo", "SP");

Now imagine that we want to retrieve the value of the city name, set a new name, and then display that new name in capital letters, but always checking that the properties are not null. We could do this with the following code:

if ($city !== NULL) {

    $name = $city->get_name();

    if ($name !== NULL) {

        $city->set_name("Sao Paulo");

        $new_name = $city->get_uppercase();

        if ($new_name !== NULL) {

            echo $new_name;

        }

    }

}

This code would work. However, it is a bit repetitive, isn't it? In this situation, the new Nullsafe operator can help us. It allows us to check if the result of methods or properties is null, just by adding a question mark (?) before the “->” sign. This means we could rewrite the above code with the following line:

echo $city?->get_name()?->set_name('Sao Paulo')?->get_uppercase();

Wow, much simpler, isn't it?

Note that this operator will mainly be used in chained method calls. So, if you want to use it, you might need to adapt your classes and objects to work this way.

The Nullsafe operator also cancels the execution of the methods if any of them return a null value. This can help us with the performance of our server. Imagine that some method in our project consumes a lot of processing or memory from the server. If some other method before it returns NULL, this method will not be executed, which will save the server's resources. This concept is called “short circuiting”.

New Return Type - static

To talk about this news, we need to remember the concept of two very used keywords when working with Object-Oriented Programming in PHP: static and self. Both are used to refer to methods and attributes already declared in classes, but with a small difference. To visualize this difference, it is necessary to apply the concept of inheritance in Object-Oriented Programming. Let's see an example of this.

We will create two classes, one called First and the other called Second. They will have the following code:

class First {

    public static function class_name() {
        return __CLASS__;
    }

    public static function show() {
        return self::class_name();
    }

}

class Second extends First {

    public static function class_name() {
        return __CLASS__;
    }

}

Explaining: the second class extends the first. Both have the same method called class_name(). It returns the name of the class in which it was created. We also have the show() method, which is responsible for invoking the class_name() method. It is in this method that we will focus our attention. Note that we are using the self keyword to refer to the class_name() method.

Let's perform a test. Let's call the show() method in the Second class like this:

echo Second::show();

This method was inherited from the First class. Now we are left with the question: “Which class name will be displayed in the browser?” We might think: “From the second class. After all, we called the show() method from there.” When we access the browser, we see the following result:

Result of calling show() in the browser

Notice that the displayed class was the first! This happens because of self. It refers to the methods of the “original” class, that is, the class where the method was initially created. If we want to display the name of the second class, we would need to replace self with static in the show() method:

public static function show() {
    return static::class_name();
}

The static refers to the method of the class that was called. It makes this reference at runtime. So when we access the browser again, we will see the following result:

Result of static in the show() method

This concept is called “Late Static Bindings” (LSB).

Alright, but why are we talking about this whole concept? We did this because the novelty we are currently discussing involves the application of the LSB concept, but now focused on the return of methods. Thus, if we have any method that should return the instance of the class, we can specify static, not just self. Let's adapt the last example to see the practical application of this novelty. Our classes will now look like this:

class First {
    public function show($class_name): self {
        return $class_name;
    }
}

class Second extends First {
    public function show($class_name): self {
        return $class_name;
    }
}

class Third extends Second {}

Note that we created a third class called Third, which will extend from Second. Also, note that the show() method in the First and Second classes will return the name of the class that it receives as a parameter, but this return should be the word self, that is, the class where the method was created.

Let's instantiate the Third class and provide the Second class as a parameter for the show() method:

$third = new Third();

$third->show(new Second);

This code will work because we used self in the show() method. Now, let’s add the static word to the method in the Second class, like this:

class Second extends First {
    public function show($class_name): static {
        return $class_name;
    }
}

Now, when executing the same code, we will see the following result:

Error returned in our browser

We see that an error was returned in the browser, informing us that the return should be an instance of the Third class. Therefore, in order for the code to work again, we need to leave it like this:

$third->show(new Third);

Perhaps we might ask ourselves: “What is the advantage of this novelty? Why was it implemented?” It ensures that the return of the methods created in our classes is of the type we desire, which gives us greater control over our code.

This is a complex concept that can confuse us when we hear it for the first time, but as we see it in practice, it becomes more natural to us. ?

Mixed Type

In the last article, we talked about the novelty “Union Types” that will be implemented in PHP 8 and will allow us to define more than one type for a variable or method return in our projects.

Using the concept of this new feature, Mixed Type will also be added to PHP 8. Using the reserved word mixed, we will be able to refer to a series of types. This already existed in several IDEs or in PHPDoc, but did not exist natively in PHP.

Mixed encompasses the types array, bool, callable, int, float, null, object, resource, and string, all summarized in a single word. Let's see an example of this resource.

Let's create a new class, which will have the following structure:

class Person {

    public mixed $info;

    public function set_info(mixed $info) {

        $this->info = $info;

    }

}

With the code above, we can provide any type of data to the set_info() method. For example:

$glaucio = new Person();

$glaucio->set_info('Glaucio Daniel');

In this code, we provide a string, defining the person's name. But if we want to provide her age, it is also possible:

$glaucio->set_info(25);

I put the age of 25 for Glaucio to be happy ?.

This is a very interesting resource, but it is important to mention some important points about it.

For example, the Mixed Type can only be used alone. After all, it encompasses all types. This means that the code below would not work:

function hcode(mixed | string $name) {}

It cannot be used together with another type.

The Mixed Type also encompasses the null type. Therefore, we cannot use it in a nullable manner:

function hcode(?mixed $name) {}

This code also would not work.

We can also use mixed as a method return. However, when specifying it as a return, we will be required to return some information. See, for example, the code below:

function sum($num1, $num2): mixed {

    echo $num1 + $num2;

}

Note that the sum() function will display the result of the calculation in the browser. However, it is not returning that value. Therefore, when we call this function, we will see the following result:

Error returned in the browser

A fatal error is returned. The correct way is for the function to be like this:

function sum($num1, $num2): mixed {

    return $num1 + $num2;

}

Now, upon calling the method again, we will see the correct result!

Something interesting about the Mixed Type is that methods will adopt it by default for parameters for which we do not specify any specific type. The great advantage of this new feature is to clarify what we expect from the return in the methods or what we expect from the arguments we will provide to the methods. Once again, we see an advance in the maturity of PHP with this new resource.

Weak Maps

The next novelty is a new class called WeakMap. This class will allow us to create a reference to an object, defining that when this object is removed, its reference will also be removed. Let's see this in practice!

Let's create an array called $years and add a value to it, specifying its key through a variable. The code will look like this:

$years = [];

$key = "year";

$years[$key] = 1968;

var_dump($years);

When we access the browser, we see the following result:

Array returned in our browser

Very good! The array was correctly returned.

The key “year” is defined through the variable $key. What happens if we remove it? Let's test this by adding the following code:

echo "<br/>";

unset($key);

var_dump($years);

When we access the browser again, we see the following:

Arrays being returned in the browser

The array was displayed once again. Notice that the variable was removed, but its reference, or mapping, remains the same. Now let's apply the same logic using the new WeakMap class. The code will look like this:

$years = new WeakMap;

$key = (object) "year";

$years[$key] = 1968;

var_dump($years);

echo "<br/>";

unset($key);

var_dump($years);

Note that the variable $years now holds a new instance of the WeakMap class. Additionally, it was necessary to convert the string in the variable $key to an object, as only this type of data is valid for the WeakMap class. Now, when testing the same code in the browser, we will see this result:

New result of our code in the browser

Notice that now the object was returned only in the first var_dump(). The moment we removed the variable $key, its reference was also erased. This resource will help us save memory on our server, which will improve the performance of our applications.

Using ::class in Objects

The reserved word ::class can be used within classes to refer to their names. However, many programmers expected to be able to use it also in the objects that instantiate the classes. That wait is over. Starting from PHP version 8, we will be able to use this word outside of classes as well. Let's create an example!

Let's create a class Company with the following code:

class Company {

    public function index() {

        return 'Hcode Treinamentos';

    }

}

$hcode = new Company();

Until PHP 7, if we wanted to see the name of the class, we would have to use the following code:

var_dump(get_class($hcode));

Now, starting from version 8, we can do the same with the following code:

var_dump($hcode::class);

That way it is much simpler. This is an excellent novelty, don’t you agree?

Improvements in Abstract Methods of Traits

Trait is a feature that was added in PHP version 5.4 and allows us to isolate parts of our code and reuse it in as many classes as we want. It is a very useful resource, but a small "problem" was identified some time ago, related to the abstract methods created within a Trait. Let's create an example to see this problem.

We will create a Trait called Computer and a class called Notebook, which will make use of it. Our code will look like this:

trait Computer {

    abstract public function set_processor($processor): int;

}

class Notebook {

    use Computer;

    public function set_processor($processor): string {

    }

}

Did you notice something strange? Note that the set_processor() method has been redefined in the Notebook class but specifying a different return type. If we were working with an interface, this code would not work, but with Trait it does. Considering this issue, starting from PHP 8, the signature of a Trait is a bit more specific, and now the validation of this resource also involves the return of methods. Thus, if we test the code above with PHP 8, we will see the following result:

New error message when working with Traits

Comma in the Last Position in Parameter Passing

To talk about the next novelty, I need to ask you a question: “Do you add a comma after the last parameter specified for a function or method?” Many programmers do this. PHP already allows us to specify this final comma when we are defining the arguments for some already created class. For example:

new Address(
    "Avenida Paulista",
    260,
    "São Paulo",
    "SP",
    "Brasil",
);

Note that after the argument “Brasil” there is a comma. This code works without issues. However, imagine we want to do the same when creating the class, like this:

class Address {

    public function __construct(
        public string $street,
        public string $number,
        public string $city,
        public string $state,
        public string $country,
    ) {

    }

}

Note that we also added a comma after the $country parameter. However, currently, this code would not work; even the Text Editor would return an error. This is a bit strange, don’t you think? You have probably already encountered some problem in this context.

The good news is that aiming for consistency in PHP, starting from version 8, this syntax will now work as well. Thus, we will have one less headache from now on ?.

New Interface - Stringable

This new interface will use the new Union Types feature to allow the use of a string or an object Stringable in the magic method __toString() in our classes. According to Nicolas Grekas, the author of this new feature and an engineer in the Symfony framework, one of the goals is to allow a direct upgrade from PHP 7 to 8.

Conclusion

In this article, we reviewed 8 more new features of PHP 8. It is possible to see how PHP is evolving and maturing more and more.

While we await the release of this version, which will likely occur on November 26th, don't miss the opportunity to learn the core of this amazing language in our Complete PHP 7 Course.

Until the next article :)

Keep learning with Hcode

Sign up for the course platform and follow the next blog guides.