PHP 8 & 8.1 : nouveautés, compatibilités et migration

PHP 8 a été officiellement lancé le 26 novembre 2020, cela fait donc un peu plus d’un an que nous avons pu expérimenter les différentes nouveautés introduites par cette version très attendue ! Comme pour toute montée de versions majeures, celle-ci est venue avec son lot d’incompatibilités avec les anciennes versions.

Nous vous proposons dans cet article de récapituler tout d’abord les différentes nouveautés apportées par PHP 8 et PHP 8.1. Nous aborderons ensuite les incompatibilités et risques de bugs en cas de montée de versions depuis PHP 7. Enfin, nous vous donnerons notre checklist pour une montée de version réussie et les outils permettant de l’effectuer simplement et rapidement.

Mais pourquoi passer à PHP 8 ?

La première question que l’on se pose lors de la sortie d’une nouvelle version majeure est de savoir si les nouveautés apportées par celle-ci justifie de consacrer plus ou moins rapidement du temps à la mise à jour de son code et dans la gestion des risques associés.

De notre propre expérience, passer à PHP 8 présente des avantages considérables :

Ainsi, chez TheCodingMachine nous sommes persuadés que vous ne regretterez pas le temps consacré à la montée de version de votre environnement ! Et pour les éventuelles complexités, pas de soucis cet article est là pour vous aider.

Les nouveautés introduites par PHP 8 et PHP 8.1 :

PHP 8 et 8.1 ont introduit de nombreuses nouveautés, voici les plus notables :

Nullsafe operator : introduction d’un opérateur logique s’écrivant « ?-> » permettant de vérifier simplement à la chaîne si des variables sont nulles au lieu de faire plusieurs boucles if.

PHP 8 - Nullsafe operator
PHP 8 – Nullsafe operator

Named arguments : les arguments nommés permettent de passer des arguments à une fonction en fonction du nom du paramètre, plutôt que de la position du paramètre. Cela simplifie la documentation et la compréhension du code, rend les arguments indépendants de l’ordre et permet de sauter arbitrairement les valeurs par défaut (pour ne fournir que certains paramètres à une fonction). Ex : positional arguments : array_fill(0, 100, 50) ; named arguments : array_fill(start_index: 0, num: 100, value: 50).

Attributes : les annotations (/** @Annotation() */), sont très couramment utilisées en PHP afin de rajouter des métadonnées au code (classes, fonctions, propriétés… ). PHP 8 propose à présent des attributs (#[Attribute]) permettant de définir ces métadonnées directement grâce au langage. Ainsi, au lieu des annotations PHPDoc ou d’utiliser des outils comme la librairie Doctrine Annotations, vous pouvez désormais utiliser des métadonnées structurées avec la syntaxe native de PHP.

Lambda function : permet d’avoir une syntaxe simplifiée pour les fonctions anonymes afin de gagner en lisibilité.

Match expression : l’expression match est similaire dans son rôle à switch mais contrairement à switch, match ne repose que sur des comparaisons strictes (ce qui évite certains résultats surprenants), son résultat peut être stocké dans une variable ou retourné (en utilisant return) et chaque branche de l’expression match ne nécessite pas l’écriture d’un break; (celui-ci est implicite, un match n’exécute qu’une seule branche, il n’y a pas de « fall-through »).

Union types : la prise en charge des Union types dans le langage permet de déplacer plus d’informations anciennement dans la documentation (phpdoc) vers les signatures de fonction : les types sont en fait appliqués, de sorte que les erreurs peuvent être détectées tôt et que la dette technique est réduite.

PHP 8 - Union Types
PHP 8 – Union Types

[PHP 8.1] Enums : constantes typées fortes, empêchant les valeurs non souhaitées pour des variables de type string or integer sans aucune vérification nécessaire.

[PHP 8.1] Call new in initializers : il est à présent possible d’instancier un objet lors de l’initialisation. Cette initialisation peut concerner les paramètres par défaut d’une fonction, ou la valeur par défaut d’une propriété de classe,… Cela facilite la documentation et la recherche d’information grâce aux annotations directement dans le code. Cela permet aussi de renseigner directement une valeur par défault, plutôt que « null« , et débloque l’utilisation d’attributs dans d’autres attributs (ex : #[new Validalidation(new Integer())]).

Bien sûr il y a d’autres nouveautés, la documentation complète de PHP 8 est disponible sur le site officiel de PHP en cliquant ici et il en va de même pour PHP 8.1 ici.

Les incompatibilités pour une montée de version vers PHP 8

Bien qu’il soit difficile, voire impossible, d’anticiper toutes les complexités pouvant apparaître lors d’une montée de version, une partie des problèmes récurrents peuvent être anticipés. En effet, PHP 8 a renforcé l’aspect typé du langage PHP et par exemple certains conditionnements sont devenus stricts. Ainsi, certains codes, qui étaient souvent faux, vont à présent émettre des exceptions.

Voici, une petite liste des erreurs que nous avons rencontré lors de nos propres expériences et comment les éviter :

ExtensionResource (PHP < 8.0)Object (PHP >= 8.0)
Curl
Curl
Curl
GD
Sockets
Sockets
OpenSSL
OpenSSL
OpenSSL
XMLWriter
XML
GD
FTP
IMAP
finfo
PSpell
PSpell
LDAP
LDAP
LDAP
PgSQL
PgSQL
PgSQL
Curl
curl_multi
curl_share
gd
Socket
AddressInfo
OpenSSL key
OpenSSL X.509
OpenSSL X.509 CSR
xmlwriter
xml
gd font (integer)
ftp
imap
file_info
pspell (int)
pspell config(int)
ldap link
ldap result
ldap result entry
pgsql link
pgsql result
pgsql large object
CurlHandle
CurlMultiHandle
CurlShareHandle
GdImage
Socket
AddressInfo
OpenSSLAsymmetricKey
OpenSSLCertificate
OpenSSLCertificateSigningRequest
XMLWriter
XMLParser
GdFont
FTP\Connection
IMAP\Connection
finfo
PSpell\Dictionary
PSpell\Config
LDAP\Connection
LDAP\Result
LDAP\ResultEntry
\PgSql\Connection
\PgSql\Result
\PgSql\Lob
PHP 8 – Resource Strong Typing

Les étapes clefs pour réussir sa migration et les outils recommandés

Réaliser une montée de version apparaît toujours comme une tâche fastidieuse mais, comme expliqué plus haut, passer à PHP 8 en vaut la peine. Pour vous accompagner dans votre démarche, voici les étapes clés et les outils associés afin de gagner en efficacité :

Les outils que nous recommandons : PHPStan (outil d’analyse statique, TheCodingMachine est fier d’y contribuer), PhpCodeSniffer (Outil permettant de maintenir un code clair et cohérent), Rector (propose une montée de version « quasi » automatique), 3v4l.org (pour tester du code sous différentes versions de PHP).

Bonus : exemple de code reprenant les nouveautés !

Pour les plus passionnés d’entre vous, une « mixtape » des nouveautés PHP 8 composée par nos soins !

class User
{
    public function __construct(
        public string $firstName,
        public string $lastName,
        public ?Address $address = null
    ) {}
}

class Address
{
    public function __construct(
        public string $number,
        public string $street,
        public string $city,
        public string $country
    ) {}
}

enum Undefined
{
    case Undefined;
}

class UserRepository
{
    /** @var array<User> */
    private array $users;

    public function find(
        string|null $firstName = null,
        string|null $lastName = null,
        string|null|Undefined $country = Undefined::Undefined,
    ): array/*<User>*/ {
        $users = $this->users;
        if ($firstName !== null) {
            $users = array_filter($users, fn(User $user) => $user->firstName === $firstName);
        }
        if ($lastName !== null) {
            $users = array_filter($users, fn(User $user) => $user->lastName === $lastName);
        }
        if ($country !== Undefined::Undefined) {
            $users = array_filter($users, fn(User $user) => $user->address?->country === $country);
        }

        return $users;
    }
}

// ...

$userRepository->find(); // Retrieve all users
$userRepository->find(firstName: 'Gandalf'); // Retrieve users whose first name is Gandalf
$userRepository->find(country: 'UK'); // Retrieve users living in UK
$userRepository->find(country: null); // Retrieve users living in no known country (as far as we know)
$userRepository->find(country: null, lastName: 'Troy', firstName: 'Castor'); // Use all arguments you want

A vous de retrouvez dans cet échantillon de code les différentes nouveautés : null-safe operator, lambda function, union type, named arguments et enum !


par tcm

Articles similaires TAG