A quoi sert declare(strict_types=1) en PHP ?

Vous avez déjà vu passer dans des fichiers PHP declare(strict_types=1) tout en haut des fichiers mais sans savoir à quoi ça sert ?

Jérémy 🤘
Jérémy 🤘

Vous avez sans doute déjà vu passer dans des fichiers PHP declare(strict_types=1) tout en haut des fichiers mais vous n'avez jamais réellement compris à quoi cela pouvait bien servir ? Et bien voici l'explication.

Avec l'arrivée de PHP 7.0, il est possible de typer les paramètres des fonctions et des méthodes avec int, float, string ... et afin de ne pas rompre la compatibilité avec les anciens scripts, il a été décidé de ne pas forcer le typage dans la configuration de PHP. Sinon les sites n'ayant pas pris la peine de se mettre à jour mais utilisant des librairies qui elles le sont, auraient tous bel et bien planté. Voilà pourquoi, il a été décidé de créer la directive strict_types.

Celle-ci doit se mettre en haut du fichier PHP et doit surtout être présente principalement sur le fichier PHP appelant. Même s'il est préférable de la mettre sur les deux fichiers. Voici un petit exemple pour que vous compreniez bien comment cela fonctionne.

Demo

  • index.php
Copier
<?php

require 'Toto.php';

$toto = new Toto();
$toto->setToto("123");
<?php

require 'Toto.php';

$toto = new Toto();
$toto->setToto("123");
  • Toto.php
Copier
<?php

class Toto
{
    public function setToto(int $toto)
    {
        echo gettype($toto);
    }
}
<?php

class Toto
{
    public function setToto(int $toto)
    {
        echo gettype($toto);
    }
}

Sans declare(strict_types=1)

Si je lance mon script je vais avoir comme résultat integer. Pourquoi ? Car N'ayant pas mis la directive, PHP va automatiquement caster ma chaine de caractère 123 en un entier.

Avec declare(strict_types=1) sur Toto.php

Le résultat est le même, pourquoi ? Car comme dit plus haut, la directive n'a d'importance que sur le fichier appelant.

Avec declare(strict_types=1) sur index.php

Copier
Fatal error: Uncaught TypeError: Argument 1 passed to Toto::setToto() must be of the type int, string given, called in /usr/src/myapp/index.php on line 8 and defined in /usr/src/myapp/Toto.php:7
Stack trace:
#0 /usr/src/myapp/index.php(8): Toto->setToto('123')
#1 {main}
  thrown in /usr/src/myapp/Toto.php on line 7
Fatal error: Uncaught TypeError: Argument 1 passed to Toto::setToto() must be of the type int, string given, called in /usr/src/myapp/index.php on line 8 and defined in /usr/src/myapp/Toto.php:7
Stack trace:
#0 /usr/src/myapp/index.php(8): Toto->setToto('123')
#1 {main}
  thrown in /usr/src/myapp/Toto.php on line 7

Comme je l'ai placé sur l'appelant, maintenant PHP me force à mettre un entier et non une chaine de caractère comme paramètre de ma méthode setToto.

Conclusion

Il pourrait être tentant de le mettre uniquement sur le fichier principal de son code, son point d'entrée. Et bien non car maintenant avec frameworks et surtout si vous programmez correctement, vous n'avez pas un seul fichier qui appelle directement toutes vos dépendances. Et même vos dépendances vont elles aussi appeler des méthodes internes ou externes. Donc si par exemple dans ma classe Toto j'avais une autre méthode à qui je lui passerai un autre type que celui attendu, PHP casterait automatiquement.

Utiliser cette directive dans votre code, vous permet d'éviter bon nombre de bugs qui pourraient survenir, mais aussi être obligé de tester si le type de la variable est bien le bon à faire en fonction de votre traitement.

Juste histoire d'aller plus loin

Vous pouvez cibler une partie du code qui utilisera cette directive :

Copier
<?php
// Ces déclarations sont identiques.

// Vous pouvez utiliser ceci
declare(strict_types=1) {
    // script entier ici
}

// ou ceci
declare(strict_types=1);
// script entier ici
<?php
// Ces déclarations sont identiques.

// Vous pouvez utiliser ceci
declare(strict_types=1) {
    // script entier ici
}

// ou ceci
declare(strict_types=1);
// script entier ici