Pourquoi il ne faut pas utiliser isset et empty ?

Vous connaissez 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 ?

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

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 :

Copier
POST /gallery
{
	"name": "Foo Gallery",
	"images": [
		{
			"filename": "image1.jpg",
			"label": "Foo label"
		},
		[...]
	]
}
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 :

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

ou

Copier
if (empty($_POST['images'])) {
	// Traitement
}
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.

Copier
PUT /gallery/{id}
{
	"name": "Foo Gallery",
	"images": null
}
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 :

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

que

Copier
if (is_array($myVar)) {
}
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