6

PHP is Evolving – Deprecations and Revolutions

We did well. The PHP community did well; we were lucky. Indeed, PHP has been evolving very strongly for a few years now. But this strong evolution hasn’t always been there. This was mainly due to problems during the development of PHP 6, which was why this version was never released. This explains why so many projects were (and still are) stuck at PHP 5.

PHP 7 has wiped the slate clean and brought a real revival to the language. Moreover, it is a real breath of fresh air that has boosted the language toward new horizons.

PHP went from being an almost dead language to a language catching up and projecting itself in the future. In this last chapter, dedicated to the clean-code theory, we will focus on the following points:

  • How PHP is different from its past versions
  • How these changes will help you become a more rigorous and better developer, and not only in PHP
  • What the major new features of PHP are in its latest versions

Old versus new PHP

PHP has likely helped you become a much more rigorous developer over the years. If during its first decades of existence, PHP allowed you to write code the way you wanted to and without restricting you from doing so, with the (very) few advantages that this brings, in hindsight, it was mostly the opportunity to have as many ways to write code as there are developers (which rarely lead to exceptional results) that made it popular. As we now know, that can be a source of endless and infernal bugs to debug. Fortunately, the evolution of the language in the last few years has fixed a lot of these bugs, to the benefit of our applications.

Strict typing

First, let’s look at one of the most important things you should be using in the newest versions of PHP from version 7.4 – the strict typing of properties.

There was a time you were allowed to pass any data to any variable and cast variables as much as you wish, without a real and native way to prevent this – converting an array variable to a string, a string to an integer, and so on. That can be pretty confusing and potentially the source of so many problems. What would happen if you multiplied a string by an integer, for example? Well, the result is totally unexpected. This isn’t well managed like it is in Python, where these types of operations are allowed but well controlled. If your PHP code relies on the ability of weak typing, there may be a problem with your code architecture, and you must absolutely review the parts that need this weak typing.

PHP now allows you to strictly type variables in some situations. As of PHP 8.1, you can’t type a variable if it is not a class attribute or a method argument. That said, you should type all the attributes of your classes and method arguments – less confusion, more rigor. You may have to rethink some parts of your code, but you get the benefit of cleaner, more understandable code. There are no surprises at runtime because of an unexpected cast. If you really need to pass any type of data to your methods, you can still rely on the mixed keyword, which tells PHP that this variable can be any type of data, or that the method can return any type of data. Of course, you must avoid this if you can and only use it in very precise cases (such as interface method definitions, where the interface’s implementations can return several types of data).

Error reporting

PHP 8 now displays deprecations and more strict errors by default. The error reporting level was lower in earlier versions of PHP. With this change, you’ll be able to see way more easily where you need to pay attention to deprecation, for example. You’ll be thankful for this change when you have to operate a PHP version upgrade, for example. If you take care of fixing deprecations as soon as they appear, upgrading to another version of PHP will be an easy task. Moreover, there is almost always a message resulting from the deprecation, telling you how to fix it precisely. Taking care of these errors and especially deprecations at the earliest opportunity is definitely a clever move and makes you adopt a clean-code mindset.

Attributes

Comments in source code were invented to give a better understanding of tricky code parts. This means that if we remove all comments in source code, it should work perfectly as well, as compilers and interpreters shouldn’t consider comments. That’s the main and very first reason source code comments were invented.

Then, annotations were created. Logic and mechanisms were introduced right in the comment sections. Don’t get me wrong – annotations are very practical. You get every bit of information and metadata about an element where you need it and when you need it. But it is somehow an aberration when you think about it. And remember what we said in previous chapters: if you write code cleanly, then there is a great chance that you never need to write a single line of comment, or at the most just a few (writing tricky code parts cannot always be avoided, even for the best clean coders).

Attributes have been part of the PHP language since version 8.0. Simply put, attributes have the same role as annotations: add metadata to different elements, including classes, properties, and methods. The difference is that they use another syntax, which is not the type used for comments. More than having better readability, comments will go back to their first use: being informative. In an instance, you can immediately differentiate metadata (described with attributes) and comments to better understand the code part you’re working on.

Let’s see what attributes look like:

<?php

namespace AppController;

class ExampleController

{

    public function home(#[CurrentUser] User $user)

    {

        // ...

    }

}

It is clear that the currently logged-in user will probably be injected in the $user variable. The code looks sharp, and we have all the information we need to understand what’s happening at a glance. We also have a perfectly blank space if we ever need to add information in the comments section above the home() method. You can now see clearly how attributes help you to be more rigorous – remove all comment blocks and think twice before adding a new one.

There are obviously still a lot of things happening in the most recent versions of PHP, starting from version 8.0. Here is a non-exhaustive list:

  • Union types
  • Match syntax
  • Named arguments
  • Enumerations
  • JIT compiler
  • Fibers
  • Numeric separator

Let’s see a few of them in the next section.

The version 8 revolution

As we have seen, PHP has experienced exceptional momentum in its evolution for the last few years. While we thought that version 7 was a real rebirth of the language, version 8 proved that it was only the beginning. Here are the main new features that will help you write clear and concise code, and that will help you to push even further the principles of clean code that we have seen throughout these chapters.

Match syntax

The match syntax is the condensed version of the classic switch/case. It should not be used everywhere because it can quickly become unreadable. However, if you choose the places where you use it sparingly, your code can become much clearer in an instant. Here is an example of the match syntax:

$foo = match($var) {

    ‹value 1› => Bar::myMethod1(),

    ‹value 2› => Bar::myMethod2(),

};

It works the same way as switch. However, note the difference in the length of the code and how readability is increased. You can also immediately see the limitations of such a syntax: if there is more than one statement to execute per case, it is not at all adaptable, and you run the risk of ending up with something unreadable. It is then preferable to stick to the more classical switch/case block.

Named arguments

If you are used to using other languages regularly, you may be familiar with this evolution of PHP. Since version 8.0, it is possible to pass arguments to methods in the order you want. This is done by specifying the name of the argument just before its value.

$this->myMethodCall(needle: 'Bar', enabled: true);

We can quickly see two cases where this can be useful:

  • First, we sometimes encounter methods with a lot of optional arguments that already have a default value defined. Sometimes, we want to change the value of an argument that may be the 5th or 11th in a list. This is followed by a lengthy process of rewriting the default values of all the parameters before it. This is clearly not ideal, but it is a problem that can be solved by using named arguments. You simply specify the name of the argument you want to give and its value, and you’re done.
  • A second case is where you add clarity to a method call, even if it has no optional arguments. If your method has a lot of arguments, you might think about using named arguments to explicitly show what you are sending to the method. However, if this is the case, the real solution to make code more readable is to refactor it so that you don’t need to pass so many arguments to a method. This can be done by splitting the method into several ones, or by creating Value Objects (VOs). These are objects containing only simple properties, without other methods or logic, to transfer data from one point to another in code. This avoids the worry of endless arguments, and it also adds a layer of validation with strong-typed properties and adds some context to the data you carry with it.

Read-only classes and properties

Here is a little riddle to introduce read-only classes and properties. In a PHP class, how can you ensure that an attribute will be assigned only once – that is, it can be assigned a value only once, and all future attempts to assign it will fail? Note that this also applies to assignments within the class itself. This means that a mutator throwing an exception in the case of a call cannot work because we cannot be 100% sure that the developer will go through the mutator within the class scope.

Actually, the answer is quite simple: it is impossible. If you are using PHP 8.0 or earlier, it just isn’t possible. PHP 8.1 brings us a native solution to solve this problem: read-only properties. By declaring a class property with the readonly keyword, the variable can only be assigned once, regardless of the context. This can be especially useful when defining and using DTOs and VOs. By restricting access to the mutation of these properties, the developer using them will have to think carefully about the use of the object. If they want to make changes, they will have to create a new object. The original object cannot be modified, which can guarantee better stability and robustness of code. Here is how read-only properties are declared:

<?php

namespace AppModel;

class MyValueObject

{

    protected readonly string $foo;

    public function __construct(string $foo)

    {

        $this->foo = $foo; // First assignment, all good

        // Any further assignment of $this->foo will result

          in a fatal error

    }

}

Since PHP 8.2, it has even been possible to declare an entire class as readonly by placing this modifier just before class keyword, so you don’t have to use the keyword on every property the class includes. This also gives you a big advantage: a class declared as readonly will not be able to have new properties declared dynamically. Although this behavior is deprecated and should be avoided at all costs (dynamic declaration of class properties is a nightmare in terms of debugging, stability, and code complexity), it is still possible to do so in versions up to PHP 9.0, in which a fatal error will be triggered if this behavior occurs. If you are not using PHP 9.0, declaring your class as readonly will protect you from this behavior.

This is a big step forward for PHP developers. Indeed, all thanks to this feature, we will need to be, once again, increasingly rigorous on how we interact with objects and their properties.

Migrating resources to proper classes

Depending on how long you have been developing in PHP, you should be more or less familiar with what we call resources. A resource is a special type of variable, which represents a reference to an external resource. This may sound a bit vague, but it is actually quite trivial. A resource can be the following, for example:

  • An open file
  • A database connection
  • A cURL call
  • A connection to an LDAP directory (which is a way to manage user accounts in companies, generally)
  • A font for a GD image manipulation extension

This has worked well for a few decades now, but we understand that the term resource is not really adapted anymore by being too generalist and especially quite old-fashioned now that classes and objects are predominant in modern code. Why aren’t these resources simply objects like any other? Well, there is no particular reason (at least not at present) to justify this. And that’s why PHP 8.0 has started a long but necessary migration to convert the old resources into full-fledged classes. It makes much more sense.

The use of resources was complex. They are complicated to debug and understand how they work and their internal state. They can only be called by special functions that handle resources. This is a big step forward for developers who will have more control and more tools to improve the rigor of development and robustness of their code.

The change is made as new versions of PHP are released. For example, PHP 8.0 embeds the migration of resources such as the following:

  • GD, for the manipulation of images
  • cURL
  • OpenSSL
  • XML
  • Sockets

Conversely, PHP 8.1 embeds the migration of the following resources:

  • GD fonts
  • FTP
  • IMAP
  • finfo, for file management
  • Pspell, for spell checking
  • LDAP
  • PostgreSQL

The following versions of PHP continue to do the same – to eventually remove the use of resources in the standard PHP library. Furthermore, the core PHP developers have decided since PHP 8.1 to create some of these classes under namespaces. We can see that the language, with this kind of action and development, is catching up after a long delay and making every effort to restore its reputation. PHP shows it clearly: the language is here to stay.

Protecting your sensitive arguments from leaking

If you have been developing with PHP for some time, you are surely aware of the many ways to display the content of a variable or a function argument. You can think of var_dump and print_r in particular. There are also other occasions when arguments and their values can be displayed: when the stack trace (or call stack) is displayed. This happens either by a manual call to a method such as debug_print_backtrace but also and often when an exception is thrown. In any case, if sensitive information is contained somewhere in the variables or call stack with the arguments of method calls, this can be problematic. You might think that this only happens in a development environment, but this is a mistake. It is highly likely that you write your exception messages in an error log somewhere on your server(s). This could result in sensitive information being displayed in your logs. This is obviously not recommended. Sensitive information should not be written in clear text anywhere. Also, and although it is a mistake, the security of application logs is often not as good as that of a database, for example. Furthermore, there is a great chance that many developers of the project (if not all of them) have access to such logs to debug the application. The threat does not always come from outside.

Fortunately, PHP 8.2 includes a new attribute to remedy this problem. It is indeed possible to indicate the #[SensitiveParameter] attribute before any function argument. This will tell PHP not to display the parameter value in var_dump, a stack trace, and so on. Placed cleverly, you’ll be sure not to leak a sensitive value in an error message, for example. Indeed, it is not uncommon for sites to display the server error directly on the frontend. This should obviously be banned as soon as possible, but at least it helps to limit the damage. Let’s see how this new attribute can be used:

<?php

namespace AppController;

class SecurityController

{

    public function authenticate(string $username,

      #[SensitiveParameter] string $password)

    {

        // In case of any exception occurring or var_dump

          being called in here, the value of $password will

          be hidden in the different outputs

    }

}

In the internal workings of PHP, this attribute will replace the argument with an object of type SensitiveParameterValue, which will hide the real value of the argument. The argument will be displayed and present in the output, but its value will be hidden. Adding this attribute to your sensitive method arguments is a clever and welcome way to add rigor to your code and make it more resistant to attacks.

Summary

It cannot be repeated enough: PHP is evolving in the most beautiful way and is catching up with its competitors in the web world. The language listens to the community and the developers by offering them the tools they need to answer modern problems in the most viable way possible.

We have come a long way from a language that allowed everything and was very (too) lax for the challenges of today’s web applications. Despite the explosion of frontend frameworks and technologies aimed at replacing server languages with languages intended for the frontend (such as Node.js with JavaScript), PHP has nothing to be ashamed of. Its impressive performance, its speed of evolution, and the reputation it has built over the years show that it still has a bright future ahead of it.

Although clean code is, as we have seen, a state of mind and, in a way, a philosophy, solutions native to the language are arriving in spades to help us apply them as well as possible. Even better, these features brought to PHP allow us to see new possibilities that we would not necessarily have thought of initially to make our code robust, maintainable, and viable in the long term. Just think about named arguments, read-only classes and properties, strict typing, or simply the last topic we covered in this chapter: protecting sensitive arguments from leaking into application logs and exception messages.

Having said that, it’s time to get down to business. We’ll start by looking at tools that will give you a very quick, numerical overview of the quality of your code. Having metrics for this kind of thing allows you to see what improvements you’ll make to your code, or whether it’s actually time to act because quality is diminishing as you develop. So, let’s dive into the next chapter, which highlights code quality tools dedicated to the PHP language.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset