99 votes

isset() et empty() rendent le code moche

J'ai plusieurs anciennes applications qui envoient beaucoup de messages "xyz is undefined" et "undefined offset" lorsqu'elles sont exécutées au niveau d'erreur E_NOTICE, parce que l'existence des variables n'est pas explicitement vérifiée à l'aide de la commande isset() et consorts.

J'envisage de les retravailler pour les rendre compatibles avec E_NOTICE, car les avis sur les variables manquantes ou les offsets peuvent être salvateurs, il peut y avoir quelques améliorations mineures de performance à gagner, et c'est globalement plus propre.

Cependant, je n'aime pas ce que infliger des centaines de isset() empty() et array_key_exists() s fait à mon code. Il s'alourdit, devient moins lisible, sans rien gagner en termes de valeur ou de sens.

Y a-t-il quelqu'un qui ressent la même chose ? Que faites-vous à ce sujet ?

Je recherche, et j'espère, des solutions de contournement, des fonctions magiques et/ou des meilleures pratiques pour minimiser l'impact de la vérification stricte des variables sur la lisibilité du code.

6 votes

Je suis tout à fait d'accord. C'est pourquoi j'aime tant Zend Framework, le module de requête y est très bon. Si je travaille sur une petite application, je code habituellement une classe de requête simple avec des méthodes magiques __set et __get qui fonctionnent de manière similaire à la requête de ZF. De cette façon, j'évite toutes les occurrences de isset et empty dans mon code. De cette façon, tout ce que vous devez utiliser est if (count($arr) > 0) sur les tableaux avant d'itérer sur eux et if (null !== $variable) à quelques endroits critiques. C'est beaucoup plus propre.

130voto

deceze Points 200115

Pour les personnes intéressées, j'ai développé ce sujet dans un petit article, qui fournit les informations ci-dessous sous une forme un peu mieux structurée : Le guide définitif de l'isset et du empty de PHP


Je pense que vous devriez envisager de ne pas vous contenter de rendre l'application "compatible E_NOTICE", mais de la restructurer entièrement. Avoir des centaines de points dans votre code qui essaient régulièrement d'utiliser des variables inexistantes semble être un programme plutôt mal structuré. Essayer d'accéder à des variables inexistantes ne devrait jamais arriver, d'autres langages s'y opposent au moment de la compilation. Le fait que PHP vous permette de le faire ne signifie pas que vous devriez le faire.

Ces avertissements sont là pour aide pour vous, pas pour vous ennuyer. Si vous recevez un avertissement "Vous essayez de travailler avec quelque chose qui n'existe pas !" votre réaction devrait être "Oups, c'est ma faute, laissez-moi arranger ça dès que possible." Sinon, comment allez-vous faire la différence entre "variables qui fonctionnent très bien non définies" et un code honnêtement erroné qui peut conduire à des erreurs graves ? C'est aussi la raison pour laquelle vous êtes toujours, toujours développer avec des rapports d'erreurs transformé en 11 et continuez à travailler sur votre code jusqu'à ce qu'il n'y ait plus une seule NOTICE est émis. La désactivation des rapports d'erreurs est réservée aux environnements de production, afin d'éviter les fuites d'informations et de fournir une meilleure expérience utilisateur, même en présence d'un code bogué.


Pour élaborer :

Vous aurez toujours besoin isset ou empty quelque part dans votre code, la seule façon de réduire leur occurrence est d'initialiser vos variables correctement. Selon la situation, il existe différentes façons de le faire :

Arguments de fonction :

function foo ($bar, $baz = null) { ... }

Il n'y a pas besoin de vérifier si $bar ou $baz sont définis à l'intérieur de la fonction parce que vous venez de les définir, tout ce dont vous devez vous soucier est de savoir si leur valeur évalue à true ou false (ou autre).

Des variables régulières partout :

$foo = null;
$bar = $baz = 'default value';

Initialisez vos variables au début du bloc de code dans lequel vous allez les utiliser. Cela résout le problème de !isset permet de s'assurer que vos variables ont toujours une valeur par défaut connue, donne au lecteur une idée de ce sur quoi le code suivant fonctionnera et sert donc également d'auto-documentation.

Les tableaux :

$defaults = array('foo' => false, 'bar' => true, 'baz' => 'default value');
$values = array_merge($defaults, $incoming_array);

La même chose que ci-dessus, vous initialisez le tableau avec des valeurs par défaut et vous les remplacez par des valeurs réelles.

Dans les autres cas, disons un modèle où vous produisez des valeurs qui peuvent ou non être définies par un contrôleur, il vous suffit de vérifier :

<table>
    <?php if (!empty($foo) && is_array($foo)) : ?>
        <?php foreach ($foo as $bar) : ?>
            <tr>...</tr>
        <?php endforeach; ?>
    <?php else : ?>
        <tr><td>No Foo!</td></tr>
    <?php endif; ?>
</table>

Si vous vous retrouvez à utiliser régulièrement array_key_exists vous devez évaluer l'usage que vous en faites. Le seul moment où cela fait une différence est ici :

$array = array('key' => null);
isset($array['key']); // false
array_key_exists('key', $array); // true

Comme indiqué ci-dessus, si vous initialisez correctement vos variables, vous n'avez pas besoin de vérifier si la clé existe ou non, car vous savez qu'elle existe. Si vous obtenez le tableau à partir d'une source externe, la valeur sera très probablement différente de celle de la clé. null mais '' , 0 , '0' , false ou quelque chose comme ça, c'est-à-dire une valeur que vous pouvez évaluer avec isset ou empty en fonction de votre intention. Si vous définissez régulièrement une clé de tableau à null et vouloir que ça signifie autre chose que false c'est-à-dire que si dans l'exemple ci-dessus les résultats différents de isset et array_key_exists font une différence dans la logique de votre programme, vous devez vous demander pourquoi. La simple existence d'une variable ne devrait pas être importante, seule sa valeur devrait avoir de l'importance. Si la clé est un true / false puis utiliser true ou false pas null . La seule exception à cette règle serait les bibliothèques de tiers qui veulent null pour signifier quelque chose, mais comme null est si difficile à détecter en PHP que je n'ai pas encore trouvé de bibliothèque qui le fasse.

4 votes

C'est vrai, mais la plupart des tentatives d'accès ratées sont du type if ($array["xyz"]) au lieu de isset() ou array_key_exists() ce que je trouve quelque peu légitime, certainement pas des problèmes structurels (corrigez-moi si je me trompe). Ajout de array_key_exists() ça ressemble à un terrible gâchis pour moi.

9 votes

Je ne peux pas penser à un cas où j'utiliserais array_key_exists au lieu d'un simple isset($array['key']) ou !empty($array['key']) . Bien sûr, les deux ajoutent 7 ou 8 caractères à votre code, mais je n'appellerais pas cela un problème. Cela permet également de clarifier votre code : if (isset($array['key'])) signifie que cette variable est effectivement facultative et peut être absente, alors que if ($array['key']) signifie simplement "si vrai". Si vous recevez un avis pour ce dernier, vous savez que votre logique est défectueuse.

6 votes

Je crois que la différence entre isset() et array_key_exists() est que ce dernier retournera vrai si la valeur est NULL. isset() ne le fera pas.

37voto

BalusC Points 498232

Il suffit d'écrire une fonction pour cela. Quelque chose comme :

function get_string($array, $index, $default = null) {
    if (isset($array[$index]) && strlen($value = trim($array[$index])) > 0) {
        return get_magic_quotes_gpc() ? stripslashes($value) : $value;
    } else {
        return $default;
    }
}

que vous pouvez utiliser comme

$username = get_string($_POST, 'username');

Faites de même pour les choses banales comme get_number() , get_boolean() , get_array() et ainsi de suite.

5 votes

Cela a l'air bien, et vérifie également les citations magiques. Joli !

0 votes

Superbe fonction ! Merci beaucoup pour le partage.

3 votes

Notez que $_POST['something'] peut retourner un tableau, par exemple les entrées avec <input name="something[]" /> . Cela entraînerait une erreur (car le trim ne peut pas être appliqué aux tableaux) en utilisant le code ci-dessus, dans ce cas il faut utiliser is_string et éventuellement strval . Il ne s'agit pas simplement d'un cas où l'on devrait utiliser get_array soit puisque l'entrée de l'utilisateur (malveillante) peut être n'importe quoi et que l'analyseur de l'entrée de l'utilisateur ne devrait jamais lancer d'erreur de toute façon.

13voto

jamolkhon Points 1442

Je pense que l'une des meilleures façons de résoudre ce problème est d'accéder aux valeurs des tableaux GET et POST (COOKIE, SESSION, etc.) par le biais d'une classe.

Créez une classe pour chacun de ces tableaux et déclarez __get et __set méthodes ( surcharge ). __get accepte un argument qui sera le nom d'une valeur. Cette méthode devrait vérifier cette valeur dans le tableau global correspondant, soit en utilisant la fonction isset() ou empty() et retourner la valeur si elle existe ou null (ou une autre valeur par défaut) sinon.

Après cela, vous pouvez accéder en toute confiance aux valeurs des tableaux de cette manière : $POST->username et effectuer toute validation si nécessaire sans utiliser de isset() ou empty() s. Si username n'existe pas dans le tableau global correspondant, alors null sera retourné, donc aucun avertissement ou avis ne sera généré.

1 votes

C'est une excellente idée, et quelque chose pour lequel je suis prêt à restructurer le code. +1

0 votes

Malheureusement, vous ne pourrez pas rendre ces instances superglobales, à moins de les affecter à $_GET ou $_POST, ce qui serait assez laid. Mais vous pourriez utiliser des classes statiques, bien sûr...

1 votes

Vous ne pouvez pas utiliser les getters et setters sur les "classes statiques". Et écrire une classe par variable est une mauvaise pratique car cela implique une duplication du code, ce qui est mauvais. Je ne pense pas que cette solution soit la plus adéquate.

6voto

Alix Axel Points 63455

Cela ne me dérange pas d'utiliser le array_key_exists() en fait, je préfère utiliser cette fonction spécifique plutôt que de s'appuyer sur *hack* des fonctions qui peuvent modifier leur comportement à l'avenir comme empty et isset (barré pour éviter susceptibilités ).


J'utilise cependant une fonction simple qui s'avère pratique dans ce cas et dans d'autres situations. dans le traitement des index de tableaux :

function Value($array, $key, $default = false)
{
    if (is_array($array) === true)
    {
        settype($key, 'array');

        foreach ($key as $value)
        {
            if (array_key_exists($value, $array) === false)
            {
                return $default;
            }

            $array = $array[$value];
        }

        return $array;
    }

    return $default;
}

Disons que vous avez les tableaux suivants :

$arr1 = array
(
    'xyz' => 'value'
);

$arr2 = array
(
    'x' => array
    (
        'y' => array
        (
            'z' => 'value',
        ),
    ),
);

Comment obtenez-vous la "valeur" des tableaux ? C'est simple :

Value($arr1, 'xyz', 'returns this if the index does not exist');
Value($arr2, array('x', 'y', 'z'), 'returns this if the index does not exist');

Nous avons déjà couvert les tableaux uni et multidimensionnels, que pouvons-nous faire d'autre ?


Prenez par exemple le morceau de code suivant :

$url = 'http://stackoverflow.com/questions/1960509';
$domain = parse_url($url);

if (is_array($domain) === true)
{
    if (array_key_exists('host', $domain) === true)
    {
        $domain = $domain['host'];
    }

    else
    {
        $domain = 'N/A';
    }
}

else
{
    $domain = 'N/A';
}

Plutôt ennuyeux, n'est-ce pas ? Voici une autre approche utilisant le Value() fonction :

$url = 'http://stackoverflow.com/questions/1960509';
$domain = Value(parse_url($url), 'host', 'N/A');

Un autre exemple, prendre le RealIP() fonction pour un test :

$ip = Value($_SERVER, 'HTTP_CLIENT_IP', Value($_SERVER, 'HTTP_X_FORWARDED_FOR', Value($_SERVER, 'REMOTE_ADDR')));

Neat, huh ? ;)

6 votes

"S'appuyer sur des fonctions pirates qui peuvent changer leur comportement dans le futur" ? ! Désolé, mais c'est la chose la plus ridicule que j'ai entendue de la semaine. Tout d'abord, isset et empty sont constructions linguistiques et non des fonctions. Deuxièmement, si tout les fonctions de base des bibliothèques/constructions du langage changent de comportement, vous pouvez ou non être fichu. Et si array_key_exists change son comportement ? La réponse est non, tant que vous l'utilisez comme prévu. Et isset est documenté pour être utilisé exactement de cette façon. Dans le pire des cas, les fonctions sont dépréciées après une ou deux versions majeures. Le syndrome NIH est mauvais !

0 votes

Je suis désolé de décevoir, mais tout d'abord pirater est en italiques au cas où vous ne l'auriez pas remarqué. =) Deuxièmement, vous voulez dire que l'on ne devrait pas se fier à array_key_exists() pour vérifier si un la clé existe dans un tableau ? ! array_key_exists() était créé exactement pour cela je m'en remets plutôt à lui dans ce but que isset() et spécialement empty() dont la description officielle est : "déterminer si une variable est vide", ne mentionne rien si elle existe réellement. Votre commentaire et votre vote négatif est l'un des plus ridicules que j'ai vu de toute l'histoire de l'humanité. mois .

0 votes

Aussi, prenez $arr = array('xyz' => null) ; isset($arr['xyz']) ? Oups ! empty($arr['xyz']) ? Oups ! , array_key_exists('xyz', $arr) Enfin ça marche comme il se doit .

3voto

vava Points 11364

Je suis d'accord avec vous. Mais les concepteurs de PHP ont fait des erreurs bien plus graves que ça. A défaut de définir une fonction personnalisée pour toute lecture de valeur, il n'y a aucun moyen de contourner ce problème.

1 votes

Isset(). Rendre tout nul par défaut permettrait d'éviter bien des soucis.

2 votes

Et qu'est-ce que ce "tout" ? Ce serait du gaspillage pour PHP de devoir imaginer tous les noms de variables imaginables et de les mettre à NULL juste pour qu'un développeur paresseux puisse éviter de taper 5 caractères.

5 votes

@Byron, écoutez, c'est vraiment simple, beaucoup d'autres langages le font, Ruby et Perl en sont quelques exemples. VM sait si la variable a été utilisée auparavant ou non, n'est-ce pas ? Il peut toujours retourner null au lieu d'échouer avec ou sans message d'erreur. Et il ne s'agit pas de 5 caractères minables, il s'agit d'écrire params["width"] = params["width"] || 5 pour définir des valeurs par défaut au lieu de toutes ces bêtises avec isset() appels.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X