Pourquoi il ne faut pas utiliser isset et empty ?

Pourquoi il ne faut pas utiliser isset et empty ?

Tags : Développement, Outil, PHP, Performance, Symfony, Tutoriaux, Tutoriel
Catégorie : Programmation
Mise en ligne : 18 Août 2019 à 12:25

Vous connaissez sans doute tous les fonctions PHP isset et empty ? Mais savez-vous vraiment ce qu'il se cache derrière et pourquoi il ne faut pas les utiliser ?

Elles font beaucoup trop de choses

Pour les deux du fond qui ont dormit pendant les cours de PHP et qui ne savent pas lire une documentation, voici une explication assez brève.

isset

Le isset permet, comme son nom l'indique, de regarder si une variable ou une entrée du tableau est définie ou non. Mais là où elle est vicieuse, c'est que si vous testez une variable qui a comme contenu null ou une clé de tableau qui elle aussi contient null, isset va vous renvoyer false.

empty

Le empty quand à lui permet de regarder si une variable est vide ou non. Mais quand je dis vide c'est un vide assez bizarre. En effet, la fonction va retourner true si elle est égale à :

  • "" (une chaîne vide)
  • 0 (0 en tant qu'entier)
  • 0.0 (0 en tant que nombre à virgule flottante)
  • "0" (0 en tant que chaîne de caractères)
  • null
  • false
  • array() (un tableau vide)

Comme vous pouvez le voir, empty est bien trop vague.

Exemple

Imaginons que vous proposez aux utilisateurs de votre site une API pour créer des galeries d'images. S'ils souhaitent en créer une et ajouter des images il va soumettre quelque chose comme cela :

POST /gallery
{
    "name": "Foo Gallery",
    "images": [
        {
            "filename": "image1.jpg",
            "label": "Foo label"
        },
        [...]
    ]
}

Parfait, elle est maintenant créée. Si je souhaite la modifier, je peux faire un PUT. Je vais donc renvoyer mon objet images et en PHP je vais avoir un code comme celui-ci :

if (isset($_POST['images'])) {
    // Traitement
}

ou

if (empty($_POST['images'])) {
    // Traitement
}

Dans le cas d'un PATCH où je ne renverrai que le nom de ma galerie, cela ne pose pas de problème.

Mais imaginons que je veux supprimer toutes les images de la galerie et dans mes specs je dis qu'il faut me renvoyer un objet images avec une valeur à null. Si je laisse le code comme cela, jamais je n'entrerai dans la boucle qui me permet de supprimer toutes les images.

PUT /gallery/{id}
{
    "name": "Foo Gallery",
    "images": null
}

L'autre erreur que je fais, est de penser que l'utilisateur va bien m'envoyer un objet images qui sera un tableau. Et comme disaient les Inconnus :

Il ne faut pas prendre les clients pour des cons, mais il ne faut pas oublier qu'ils le sont.

Et bien là c'est pareil. Qu'est-ce qu'il se passe si j'envoie directement les clés filename ou label, ou totalement autre chose ? Et bien mon script va planter. Donc pensez bien à tester ce que vous souhaitez avec les bonnes méthodes array_key_exists, is_array, is_int ...

Les performances

Comme ces fonctions testent beaucoup de choses, elles font du traitement pour rien. Alors oui, pour un site vitrine où vous allez avoir une centaine de visites par mois cela ne va pas se voir. Mais si vous avez un site à forte affluence comme un gros e-commerce ? Là vous risquez de voir vos performances chuter. Et vous le savez, avoir un site lent n'est pas très bon.

Benchmarks

Le test de performance qui suit a été réalisé grâce au site PHP Benchmarks qui vous propose différents benchmarks pour différents frameworks (Symfony, CakePHP, Laravel ...) mais aussi des moteurs de templates (Twig, Smarty ...) et en fonction des différentes versions de PHP. Si vous souhaitez en savoir plus sur le protocole de benchmarks, je vous laisse lire la documentation.

Voici un petit graphique qui vous montre les différentes fonctions, le temps qu'elles prennent à s'exécuter et les versions de PHP :

Comme vous pouvez le voir, le temps de traitement pour un isset est différent d'un array_key_exists. Mais on peut voir aussi que PHP a fait de bonnes améliorations sur array_key_exists qui est maintenant aussi performant que isset sur PHP 7.3. Quant au empty, il est plus long de quelques millisecondes que isset, mais sur un script qui fait beaucoup d'appels à empty, cela peut devenir assez long. Pensez donc bien à tester avec la bonne fonction.

Voici 2 autres graphes mais avec un nombre d'appels différents :

Notez bien le \ avant le array_key_exists, il permet de dire à PHP d'aller chercher la fonction dans le namespace global et pas dans le namespace courant. L'ordre de recherche est :

  1. namespace courant
  2. namespace des use
  3. namespace global

Conclusion

Pour conclure, je dirai que peu importe votre code, testez toujours ce que vous souhaitez pour les performances mais aussi pour la lecture du code. Pensez à celui ou celle qui va relire votre code, ou même vous dans quelques mois/années. Il est toujours plus compliqué de lire :

if (false === is_null($myVar)) {
}

que

if (is_array($myVar)) {
}

Dans le premier cas je vais rentrer dans le if sans savoir ce que contient $myVar, alors que dans le second je sais que j'aurai un tableau.

Et pour terminer, une petite synthèse de ce que vous retournent isset et empty avec soit en paramètre $myVar soit $myVar['key'] :

isset empty
$myVar = "foo"; true false
$myVar = "0"; true true
$myVar = 0; true true
$myVar = 0.0; true true
$myVar = ""; true true
$myVar = null; false true
$myVar = false; true true
$myVar non définie false true
$myVar = []; false true
$myVar = ["key" => "bar"]; true false
$myVar = ["key" => null]; false true

Source

<Laisser un commentaire/>

* Champs obligatoire

BaBeuloula

Posté le 23 Août 2019 à 17:59

@BlipBlop: c'est justement un peu ça le soucis avec le PHP. J'attends vraiment d'en savoir plus sur le P++ car ça devrait changer

BlipBlop

Posté le 23 Août 2019 à 17:37

@Babeuloula
Je n'ai pas dit qu'une string équivalait à un booléen, ni prétendu que ce n'était pas une mauvaise pratique. J'ai seulement mis en avant le fait que c'est bien la permissivité de PHP le problème, pas spécifiquement la fonction empty.

BaBeuloula

Posté le 23 Août 2019 à 14:36

@BlipBlop, un peu quand même. Comment tu peux dire qu'une string équivaut à un boolean ? Le comportement le permet, mais c'est par parce que c'est permis que tu dois le faire. En plus, à comprendre c'est un peu compliqué ...

BlipBlop

Posté le 20 Août 2019 à 09:28

Le comportement n'est pas bizarre dans le contexte de PHP :
empty($var) === !$var