1. PHP 7.1 Le type void en déclaration de méthode

Alors même si on aurait tendance à dire que ça sert un peu à rien, c'est toujours plus jolie à regarder lorsqu'on visualise une interface.

Mise à part la beauté générale, les programmes de vérification syntaxique auront également moins de peine à verifier que la méthode comporte bien un type de retour.

Exemple :

<?php
function myFunction(): void
{}


1.1.1 A quand la déclaration d'un retour de fonction d'un tableau d'objets ?

Cependant, il y a quelque chose qui me semble manquer beaucoup plus génant, c'est le retour de type tableau d'instances comme ceci par exemple MyClass[], idem pour le nom des paramètres.

Voici un code qui illustre l'utilisation de genre de type de retour :

<?php
function myFunction(): MyClass[]
{
      return [
        (new MyClass())->setAttr(true),
        (new MyClass())->setAttr(false),
      ];
}

C'est ça que j'appelle le vrai typage fort, dans l'idéal il faudrait arreter de penser qu'il faut à tout pris des retours de type array ou mixed (comme c'est le cas en PHP 8.0...), ça créé toujours de l'ambiguité dans le code, et quand on conçoit du code avec des types ambigüs, on a souvent le droit à des heures de TMA derrière. En outre, dans le monde du travail, c'est rarement le même développeur qui concoit et qui débugue par la suite, donc plus le code est explicite, plus c'est simple à corriger.

2. PHP 7.3 Rien de très utile


2.1. PHP 7.3 JsonException


Cette version de PHP nous fait faire l'économie de ne plus avoir à tester si le retour de json_decode est false pour retourner une exception personnalisé de type JsonException.
On peut en effet désormais utiliser l'option JSON_THROW_ON_ERROR pour obtenir directement une \JsonException.

2.1.1 En PHP <7.3 :

<?php
namespace App\Service;

use App\Service\JsonParserInterface;
use App\Exception\JsonException;

class JsonParser implements JsonParserInterface
{
    public static function jsonDecode(string $string): string
    {
        $decodedJson = json_decode($string);

        if (false === $decodedJson) {
            throw new JsonException();
        }

        return $decodedJson;
    }
}

Et on l'appelait par exemple de cette façon-là :

<?php
try {
    $decodedJson = JsonParser::jsonDecode($jsonToDecode);
} catch (JsonException $e) {
    // Log, alert etc...
}


2.1.2 En PHP 7.3, sans utiliser de classe spécifique :

<?php
try {
    $decodedJson = json_decode('bad json', null, 512, JSON_THROW_ON_ERROR);
} catch (\JsonException $e) {
    // Log, alert etc...
}


On pourrait penser que grâce à la version 7.3 de PHP, on pourrait désormais supprimer le service JsonParser puisque l'exception est désormais renvoyée par json_decode mais étant donné qu'il faut tout de même ajouter l'option JSON_THROW_ON_ERROR, cela semble tout de même toujours plus judicieux de continuer à l'encapsuler afin d'éviter de retrouver des dizaines d'occurences à json_decode dans le code source de son application.

2.1.3 La classe JsonParser en PHP 7.3 :

On pourrait définir cette classe en utilisant l'annotation @throws afin d'indiquer que la méthode retourne une exception native \JsonException.

<?php
namespace App\Service;

use App\Service\JsonParserInterface;

class JsonParser implements JsonParserInterface
{
    /**
    * @throws \JsonException
    */
    public static function jsonDecode(string $string): string
    {
        return json_decode($string, null, 512, JSON_THROW_ON_ERROR);
    }
}

2.1.4 La classe JsonParser en PHP 8.0 :

On peut simplifier l'appel en passant par les attributs nommés, d'où l'intéret de ne le faire qu'une et une seule fois dans son code.

<?php
namespace App\Service;

use App\Service\JsonParserInterface;

class JsonParser implements JsonParserInterface
{
    /**
    * @throws \JsonException
    */
    public static function jsonDecode(string $string): string
    {
        return json_decode($string, flags: JSON_THROW_ON_ERROR);
    }
}


2.2. PHP 7.3 Les virgules en fin de fonctions

Toujours pratique lorsque l'on duplique une ligne courante au moment de l'édition d'une méthode, cela permet d'avoir une façon de coder un peu plus flexible.

C'était déjà bien chouette de pouvoir le faire dans les tableaux.
Par contre, dans le cas des fonctions qui ont peu de paramètres dont le nom du type est court, cela génère beaucoup de lignes.

Voici un exemple d'utilisation où cela fait beaucoup de lignes pour pas grand chose :

<?php
function sum(
    string $a,
    string $b,
) {
   return $a + $b;
}


Voici un exemple d'utilisation où cela est déjà un peu plus pertinent en PHP 8.0 (on voit dans le futur) :

<?php
class UserPDFDocumentConvertor
{
    public function __construct(
        protected UserInterface $userInterface,
        protected DocumentManager $documentManager,
        protected EntityManager $entityManager,
        protected UserToSimpleUserMapper $userToSimpleUserMapper,
        protected PDFConvertor $pdfConvertor,
    ) {}
}

Ici, il est beaucoup plus simple d'injecter des services à notre classe puisqu'avec les raccourcis clavier (Cltr+D par défaut dans PHP Storm 2020.3), il est facile d'ajouter un paramètre de plus.

3. PHP 8.0 Un café bien sucré !

3.1. Introduction

Il y a deux bonnes nouvelles à cette version de PHP, la première est que l'équipe de développement prouve qu'elle se bouge le cul et que la version 7 n'était pas juste un coup d'essai, la seconde est que la compilation JIT vend du rêve : on souhaite tous que sans rien faire à part un apt upgrade, notre code -même moche- s'execute beaucoup plus vite ; c'est bien arrivé avec PHP 7 !

Concernant les autres nouveautés, sans dire que la plupart ne sert pas à grand chose, d'autres sont potentiellement risquées à mettre en place.

Je reprends ici l'article que j'ai vu présentant certaines mises à jour de PHP 8.0, je vous invite à le lire avant cette page : https://hoa.ro/fr/blog/2020-11-26-php-8-0-is-out/

3.2. Les nouveautés une par une

3.2.1 Les arguments nommés

Alors que Java fait ce qu'on appelle du polymorphisme et permet de créer des méthodes du même nom qui traitent d'objets de classe ou de type differents, l'équipe derrière PHP a préféré pallier aux problèmes d'utilisation qu'engendre les arguments facultatifs en introduisant les arguments nommés, le projet était dans les clous depuis 2013, c'est dire la volonté de l'équipe de développer ce truc.

Cette nouvelle version permet, lors de l'appel d'une fonction, de dire quel argument prend quelle valeur, cela permet donc d'éviter de copier/coller les valeurs par défaut de tous les arguments qu'on ne souhaite pas utiliser pour ne modifier que le dernier (ou l'un des suivants), technique donc assez dangereuse puisque, si via une mise à jour du langage, la valeur du paramètre par défaut change, il faudra peut être repassé sur notre code.
Avec les arguments nommés, on doit donc désormais copier coller le nom du paramètre, la bonne affaire !

Autant donner mon avis : c'est continuer à patauger dans la boue que d'utiliser les arguments nommés à tire la rigaud pour pallier aux fonctions natives et historiques ayant trop d'arguments facultatifs.

Ce qui me semble plus judicieux : créer des services et manipuler ces fonctions PHP natives qui ont beaucoup de paramètres facultatifs en jouant sur la valeur des attributs de l'objet qui encapsulera la fonction.
Dit comme cela, c'est très lourd et évidemment si on souhaite utiliser une fonction PHP une seule fois dans une commande par exemple, il est peut être mieux d'utiliser les arguments nommés. Autrement dit, ce n'est qu'une question de mesure et de bon sens.
Il serait assez farfelu par ailleurs d'encapsuler toutes les fonctions de PHP dans des classes. Même si c'est ce que font déjà d'autres langages comme le JS (ex: Math) ou le Java (String).

Certaines fonctions comme la fonction htmlspecialchars mériteraient clairement d'être encapsulées pour éviter de retrouver cette fonction à des milliers d'endroits du code. Ex, la classe PHPEngine du composant Templating de Symfony.

Donc le bon sens pour deux cas de figure :
* vous souhaitez utiliser une fonction native de manière exceptionnelle (dans du code poubelle, dans du code one shoot, dans un algorithme très précis) : OK pour la fonction native et ses arguments nommés
* vous souhaitez utiliser une fonction native de manière fréquente : encapsulez-la dans un service !

3.2.2 Les attributs

Alors là, prudence de chez prudence. La configuration des routes est un très bon exemple, on pourrait le faire en xml, en yaml, en annotation, voilà qu'on peut le faire en attribut. C'est pas loin de la boite de pandore ce qu'ils nous ont pondu là. D'un point de vue personnel, je ne souhaite pas être le premier à me casser les dents là dessus.

Petit détail amusant : on se dit depuis des années qu'il faut arreter d'utiliser le @ qui permet de planquer les erreurs PHP, encore plus pertinent depuis qu'ils ont catchés toutes ces erreurs pour nous permettre de les récupérer sous la forme d'exception. Dire qu'on n'utilise pas le @ parce qu'il est déjà utilisé pour quelque chose de mauvais, c'est d'autant plus dommage. Il aurait fallu qu'ils nous disent "Dès PHP 8, il n'est plus possible d'utiliser le @ pour rendre silencieuse les erreurs". Ils auraient pu alors l'utiliser pour les annotations.

3.2.3 Les types d’union

Cela rejoint mon avis sur les arguments nommés, faire une méthode qui accepte du poulet/du cochon/la valeur 8 et enfin le "j'en sais rien" derrière le mixed, ce n'est vraiment pas ce que je trouve de plus élégant. Le typage fort OUI, à plus forte raison quand on souhaite contrôler ce qui rentre dans les méthodes de nos classes, si c'est pour accepter tout et n'importe quoi en valeur d'argument, autant effectivement ne rien mettre ou mettre du mixed.
Là encore, le polymorphisme me semblait une solution bien plus adaptée.

3.2.3.1 Comparaison polymorphisme VS Type d'union

Si le polymorphisme existait en PHP :

interface MyClass
{
    public function myMethod(int $arg);
    public function myMethod(string $arg);
}


Voilà ce que qu'on peut faire en PHP 8.0 :

interface MyClass
{
    public function myMethod(int|string $arg);
}


Il n'y a alors pas le choix, il faudra forcément distinguer les différents types dans l'implémentation (ou laisser la magie du composant ou de PHP faire le cast lui même, ce qui n'est pas terrible.).

3.2.3.2 Petite apparté pour un cas similaire

Même chose pour le try catch \Exception $e, quand on écrit ceci, c'est surtout de la paresse qui s'exprime et si l'excuse c'est qu'on ne sait pas ce que renvoie telle ou telle librairie comme exception, c'est peut être qu'on n'a pas eu la patience de regarder les cas d'erreurs en détail et donc au final, l'utilisateur de l'application verra un beau message lui indiquant qu'une erreur a eu lieu et que toute l'équipe est sur le pont pour la résoudre, ce qui est bien entendu un mensonge.

3.2.4 Déclaration des attributs dans le constructeur

Alors...oui mais avec parcimonie. Ici aussi c'est du sucre syntaxique, on gagne un peu de temps et on évite de copier coller le nom de l'attribut.
Mais, certains attributs ne sont pas forcément setté par le constructeur, donc cela veut dire qu'il y aura du code dans et en dehors du constructeur, ce qui demandera donc au développeur de regarder à la fois en tête de classe et dans la signature du constructeur, autant dire que pour le côté confort de lecture, on repassera.

3.2.5 Nouvelles fonctions str_*

Mieux vaut tard que jamais. Cela ajoute "peut-être" un gain de performance, à voir. A noter qu'on persiste dans le Snake Case, alors que la tendance est depuis quelques temps au Camel Case (je n'ai pas de préférence entre les deux, mais j'aimerais pour le plaisir éviter de mélanger les deux entre le langage natif et tel ou tel composant).

3.2.6 Nouvelle interface Stringable

Le _toString est quelque chose qui ne devrait être utilisé qu'en dev, tout est dit.

3.2.7 L’opérateur nullsafe

Je dois dire que ce sucre syntaxique là est plaisant mais à utiliser avec parcimonie car comme le @, c'est un coup à se retrouver avec des dizaines de faux '?' qu'un développeur aura mis pour résoudre des bugs alors que peut être que le bug réel est avéré : ex: si le client n'a pas d'adresse postal alors qu'il a passé sa commande et qu'il souhaite être livré, ce n'est pas trop la peine de corriger le bout de code qui fait un getAdress()->getCP() ; non, il faut :
1. mettre à jour la donnée pour renseigner son adresse
2. mettre à jour le code qui aurait dû la renseigner au moment de la commande, quite à ne pas confirmer la commande par ailleurs

3.2.8 Lever des exceptions dans les expressions

Je ne suis pas ultra fan des expressions inlines car elles sont moins lisibles et me semblent plus difficiles à débugguer, mais cela n'est pas génant à voir.

3.2.9 match, le cousin du switch

Encore du sucre, mais plutôt utile.
Dans la plupart des cas cependant, un switch peut aussi faire plusieurs affectations de valeurs, avec le match, on serait obligé de faire du copier/coller tandis qu'avec le switch, chaque test n'est fait qu'une fois.

Ex:

Avec un switch

<?php
$myLang = 'francais';

switch($myLang) {
    case 'francais':
    case 'marseillais':
        $myCountry = 'france';
        $myLocale = 'fr_FR';
        break;
    default:
    case 'anglais':
        $myCountry = 'england';
        $myLocale = 'en_EN';
        break;
}

Avec un match

<?php
$myCountry = match($myLang) {
    'francais' ,  'marseillais' => 'france',
    'anglais', default => 'england',
};

$myLocale = match($myLang) {
    'francais', 'marseillais'  => 'fr_FR',
    'anglais', default  => 'en_EN',
};

Ici, même si on réplique le test, surement négligeable au niveau du temps d'execution, mais si on ajoutait une langue, il faudrait modifier le code à deux endroits.

3.3. Conclusion

Cette version de PHP 8.0 démontre que le développement de PHP continue. On appréciera l'effort que font les développeurs pour simplifier l'utilisation du langage et le rendre plus mature. On regrettera cependant que certaines nouveautés reposent encore sur des bases pointées du doigt depuis longtemps par les développeurs comme très discutables (erreurs @, nombre d'arguments facultatifs, nommage, le type mixed).