PHP 8 is scheduled for release on November 26 of this year and will bring many new features that will help us and elevate this language to another level.
Through this post, we are starting a series of articles that will discuss the new features of this version through practical examples. Some of the new features are new resources, while others are improvements on existing features.
In this article, we will cover the first 8 new features.
Simplified Constructor Method
When working with Object-Oriented Programming, we learn that the constructor method is one of the most important in a class. PHP 8 will simplify it so that it does not become redundant in the process of defining attributes when the object is instantiated. Let’s look at an example of this issue.
Imagine we want to create a Person class that will store the information of a person's name, age, and height. Currently, with PHP version 7.4, we could create this class with the following code:
class Person {
public string $name;
public int $age;
public float $height;
public function __construct(
string $name,
int $age,
float $height
) {
$this->name = $name;
$this->age = $age;
$this->height = $height;
}
}
This code would work without errors. However, have you noticed how we repeated the attribute names? We did this three times: when creating the attributes, in the constructor method parameters, and in setting them in the body of the constructor method. This is a bit redundant, don't you agree? In version 8, we can perform this same process with the following code:
class Person {
public function __construct(
public string $name,
public int $age,
public float $height
) {
}
}
Much simpler! Note that now we define the visibility of the attributes when defining the parameters of the constructor method.
It is important to remember that the attributes that will be created automatically will have the same name as the provided parameters. Additionally, this functionality has some exceptions. This feature will only work for constructors of non-abstract classes. That is, it will not be recognized in simple functions, abstract classes, or interfaces.
Union Types
The next feature we will analyze is called Union Types. Its purpose is to allow PHP to support multiple data types in its structure, not just a single value. This can be applied to method parameters and also to method returns in our classes. To define the various types that a variable can receive, simply use the | (pipe) sign. Let’s see an example.
We will create a Product class that will store the product price value. This value can be an integer or decimal (float). Creating this class, applying the new concept of the simplified constructor method, will look like this:
class Product {
public function __construct(
public int|float $price
) { }
}
In this way, if we inform an integer value, such as 15, or a decimal value, such as 18.5, it will be accepted by PHP.
This feature can also be applied to method parameters. For example: we could have this same logic applied in a set_price() method:
public function set_price(int|float $new_price) {
$this->price = $new_price;
}
We can make this definition even in the return of methods. See how the method get_price() would look:
public function get_price(): int|float {
return $this->price;
}
In this example, we added only two possible types for the methods, but we could define even more if we wanted.
There may be some situations where this feature might return an error, especially when we define some redundant configuration. For example, it is not recommended to repeat the same type in the same context. Imagine defining string|STRING in a method's parameters. This would return an error.
Also, there are types that are similar. For example: if we define the type bool|false|true, we will also see an error. It is recommended to define only bool in this case to avoid issues.
Changes in Inheritance of Private Methods
Encapsulation and visibility are also very important concepts in Object-Oriented Programming. We know that there are three levels of visibility for a method or attribute: public, protected, and private. All three types undergo the same verification when we perform inheritance of methods from one class to another. However, private methods are not inherited by child classes. Therefore, it doesn't seem to make much sense to perform the same verification on methods of this type, does it? With that in mind, starting from PHP 8, this verification will no longer occur for these methods.
Improvement in Exception Handling
Now let's talk about something that programmers are very familiar with: errors ?. Throwing Exceptions is a process that we repeat quite frequently, and starting from version 8 we will see a small change that will make our code a bit simpler. To see this in practice, let's do another example.
We will create a function that will multiply two numbers. If the result is less than 10, an Exception will be thrown. The function will look like this:
function multiply_numbers($number1, $number2) {
$result = ($number1 * $number2);
if ($result < 10) {
throw new Exception();
}
}
Note that we did not define any message when calling the Exception class. We did this because we want to return the error in a static manner, through a try/catch block. If we are using PHP 7.4, this code would look like this:
try {
multiply_numbers(2, 3);
} catch (Exception $error) {
echo "Error in multiplication";
}
We perform echo of an error message. However, it was necessary to define a variable called $error, even if we do not use it anywhere. This doesn’t seem to make much sense, but if we did not make this definition, the code wouldn't work. On the other hand, using PHP 8, we could create the same code as follows:
try {
multiply_numbers(2, 3);
} catch (Exception) {
echo "Error in multiplication";
}
A bit simpler and less confusing, don’t you agree?
Throw Expression
Still talking about errors, in version 8 we will see an evolution in Exception declaration. As we know, we can define a throw new Exception in functions and methods. However, it was not possible to do the same in ternary operators or in arrow functions in PHP 7.
The good news is that starting from PHP 8 this will be possible. Let’s see some examples.
Imagine we want to create an arrow function that displays an error message. In PHP 8, we can create it as follows:
$show_error = fn($error) => throw new Exception($error);
$show_error("Error in PHP!!!");
Another situation: imagine we have a function that makes a request to some external API. If data is returned from the API, we will display them. If this does not happen, we will return an Exception. We could use a ternary if or the null coalescing operator for this, as follows:
$result = call_api();
return $result ?? throw new Exception("Error in API call");
Our code is quite simple and easy to understand, don’t you think?
Fatal Error in Method Overriding
In Object-Oriented Programming, a common error that can occur is when we override an already created method. This can occur in two contexts: when we implement an interface or when we extend a class from another. Let’s see an example of this issue. We will create an interface called Automobile and a class Car that will implement the Interface:
interface Automobile {
public function show_details(array $details);
}
class Car implements Automobile {
public function show_details(string $details) { }
}
new Car();
Note that both the interface and the class have the show_details() method. However, the expected parameter type is different in them. If we are testing this code in PHP 7.4, we will see the following result in the browser:
Note that a fatal error was returned due to the method overriding. Now let’s see what happens when we perform the same overriding through two classes. The code will look like this:
class Location {
public function show_details(array $details) {}
}
class City extends Location {
public function show_details(string $details) {}
}
new City();
When testing the code, we will see the following result:
Notice that now a Warning was returned. However, don’t you agree that the concept of the two situations is the same? So why return two different messages for them? With that in mind, from PHP 8 onward, both situations will return a fatal error, standardizing this issue.
Arrays with Negative Indices
Have you ever thought about using arrays with negative indices? Quite interesting, isn’t it? Starting from version 8, PHP will allow us to start arrays using negative indices. For example, think about the array_fill() function. It creates an array for us and expects three parameters: 1) the starting index of the array, 2) the number of positions the array will have, and 3) the value that will be added repeatedly in each position of the array. What would happen if we provided the first option with a negative index? Let’s test this with the following code:
$cars = array_fill(-5, 7, "Ferrari");
print_r($cars);
If we are using PHP 7.4, we will see the following result in the browser:
Note that the first position of the array was -5, the starting index we provided for the function. However, the other indices were defined starting from 0. Wouldn’t it make more sense if they continued from -5? Now see the result we get when we test the same code with PHP 8:
Excellent, it occurred just as we were hoping!
Adjustments in String and Number Concatenation
We will talk about one of the first concepts we learned when studying PHP and also one of the concepts that can cause us a lot of headaches: concatenation. We know that PHP is a loosely typed language and tries to convert numbers into strings when we use the . (dot) operator in our code. However, this can cause us some problems. Let’s see an example.
We will create two numeric variables, perform their sum, and then display the result in the browser. We can do this through the following code:
$number1 = 10;
$number2 = 15;
echo "Result: " . $number1 + $number2;
When we test this code, we see the following result:
The result is incorrect and a Warning was returned. This occurs because PHP tried to concatenate the string “Result” with the number 10, which did not work. The reason the sum was disregarded and concatenation occurred is due to what we call operator precedence. It defines the priority of operators in the code. You might remember this concept from math expression classes in school.
Currently, the concatenation operator (.) has a higher precedence compared to the addition operator (+). To solve the above problem, we would need to isolate the sum in the expression using parentheses, as follows:
echo "Result: " . ($number1 + $number2);
However, we might forget this and continue having the same error. With the aim of resolving this inconvenience, it was proposed that in version 8 of PHP the concatenation operator has a lower precedence than arithmetic operators, and fortunately, this proposal was accepted. Thus, if we execute the initial code using PHP 8, we will see the following result:
Perfect! The operation was completed and the string “Result” was displayed.
Conclusion
In this article, we saw some of the amazing new features that PHP 8 is bringing. However, we will still talk about many more in the upcoming articles in this series. Don’t forget to share this content with others; it greatly helps our work, thank you very much :)
See you in the next article.