PHP 8.0 - language evolution
PHP, in recent years, became one of the most dynamically developed programming languages. Version 7 (especially 7.4) pushed PHP towards the strictly typed language and introduced a great breath of freshness to the PHP itself. The same applies to PHP 8, which includes many new, usable features, such as:
- Constructor Property Promotion,
- Match Expressions,
- Union Types,
- Named Arguments,
- Nullsafe Operator,
- Mixed type,
- Just In Time Compilation.
This major update tries to resolve past problems and makes PHP more developer-friendly by improving its transparency. The official release date of the 8th version was November 26, 2020 (4 days before PHP 7.2 EOL).
PHP 8 features
PHP 8 attributes are the new way for describing and granting special behavior for some classes. It’s also a more efficient alternative to the well-known PHPDoc way of handling such cases. To define the attribute, we have to use the following pattern “#[ImpactClass]”. While creating the update: “<<ImpactClass>>” was also considered but ended-up dismissed. For me, this change seems to be a good choice. It’s cleaner and easier to apply.
Attributes can be used for:
- Properties and constants,
- Methods and functions,
- Method and functions parameters.
Attributes can be added before, after, or instead of PHPDoc blocks.
Defining Symfony Route using PHP 8 attributes
Defining Constraint from Symfony Validator for class properties.
Constructor property promotion
This PHP 8.0 feature allows us to define class properties directly in its constructor arguments and assign its values immediately without writing any code. The main advantage of this solution is saving a large number of written lines of code. On the other hand, we can lose readability if we mix those declarations with arguments that won’t be assigned as class properties.
In brief, we can describe it as a short version of the switch case statement. The advantage of the match-over switch case is that we can obtain a value depending on the beginning’s argument without using a break or return keywords. Similarly to constructor property promotion, we will write less code while achieving the same goal. This PHP 8 feature allows resolving the returned value match expression basis on the strictly typed comparison operator (===). We can also add a “default” option (as in the switch statement). Without a “default” scenario with no matching option, PHP 8 will throw an UnhandledMatchError exception.
Resolving BMI status with a match expression
PHP 8 opens the door to declaring more than one type for classes properties and methods/function options with this feature. Thanks to the “pipe” operator, we can declare many types of values assigned to class properties. When it comes to methods and functions, we can declare many types that will be accepted on the entry as arguments and on the escape as a return type hint. Personally, as a big fan of strict typing, I’m not appeased with this solution. It creates the possibility of dynamic writing, which changes made in PHP 7.4 tried to limit. Still, while used wisely, this can be a really beneficial tool.
Sometimes we have a function that defines several optional arguments, and we need to pass a value, not for the first one. In previous PHP versions, we had to copy all preceding arguments and then pass the one we’re interested in. PHP 8.0 provided a solution called “named arguments”. Passed value can now be proceeded by argument name and a colon. That is how PHP will know which value should be assigned to a particular argument. The concern of keeping the proper order of passing arguments does no longer exists. It’s worth mentioning that we can mix named arguments and arguments passed in a classic way.
How many times have you needed to get a value from multi-level nested objects? What did you do if any object from that chain could be null? In my case, it often ended with many nested if or inline if statements. Since we have PHP 8, we can use a nullsafe operator and do the same logic more elegantly with fewer code lines. The example below presents getting country value with nullsafe operator. If any of User or Address getters returns null -it will be returned immediately as a result of the whole expression.
The type that describes absolutely everything, for us well known from some of the PHPDocs comments. PHP decided to introduce it as a type that we can use for class properties declarations or methods/functions arguments and return typing. In case of this feature again I have to say that this is a step back from the path that PHP wanted to walk from version 7 – strict typing. But how about the example when we use it as a type for an abstract function argument and then implement it in the child’s class with more concrete typing, e.g. string? Then I’ll say: Why not? Maybe it is a good solution for generic typing.
str_contains(), str_starts_with() and str_ends_with functions
PHP 8 also introduces new string functions that will make our code more human-readable and clear. It seems to be the end of the most uses of strpos() then.
Just In Time Compilation – JIT
Probably the most-awaited and famous change included in the new version of PHP. We could write a separate article to present all the possibilities that JIT gives us. But, there’s one thing that we have to know at the beginning. Maybe it will be a shock for us, web developers, but Just In Time Compilation will have almost no effect on our standard web applications if it comes to the performance area. The real performance boost is happening at long and complex calculations. It also opens the door for new areas where PHP can be used, for example, Artificial Intelligence. But how exactly does the JIT work according to previous versions flow? At the beginning of PHP script execution, the OPcache is checked to find saved OPcodes that can be executed by Zend Virtual Machine. If cached OPcodes are found, they can be immediately passed and executed by VM. Otherwise, the script is parsed to OPcodes and saved in OPcache. The problem is that OPcode still isn’t a native machine code that the CPU can understand. And here’s the place where the JIT compiler can really help as a compiler of OPcodes to machine code and as an extra cache layer to store that machine code.
After installing PHP 8, JIT is not a ready-to-use feature. It has to be enabled and configured in php.ini file. First of all, we have to enable OPcache, by setting opcache.enable_cli option to 1. It is necessary as the JIT compiler will cooperate with OPcache. Then we should decide how much memory we want to allocate for storing compiled machine code. This value should be assigned to opcache.jit_buffer_size. The last step, setting JIT control options (opcache.jit). It can be fully customizable configured by 4 digits code (more information at https://wiki.php.net/rfc/jit#phpini_defaults), or we can use one of the predefined configurations (function and tracing). Function variant looks for function scoped parts of code that can be marked as “hot”, compiled and saved in the JIT cache layer. Saved machine code can then be restored and executed. The tracing works exactly in the same way, but it tries to find bigger parts of optimizable code and save more information. Tracing is the most recommended method of using the JIT.
For many years, every new PHP version introduced a large number of new possibilities, which keeps language fresh, modern, and on a very high level. The same applies to PHP 8 release – we are offered some breaking changes, as well as many new features and performance improvements. Besides the obvious possibilities, PHP 8.0 creates new ways of using the language. PHP developers in the industry won’t ever be bored – with each release of a new PHP version we’re getting new options, alternative ways to grow our skills, and higher business potential of our products. PHP 8.0 is another big change that lets us be focused and interested in programming.