web-dev-qa-db-fra.com

Comment éviter isset () et empty ()

J'ai plusieurs applications plus anciennes qui jettent beaucoup de messages "xyz is undefined" et "undefined offset" lors de l'exécution au niveau d'erreur E_NOTICE, car l'existence de variables n'est pas vérifiée explicitement à l'aide de isset() et consorts.

J'envisage de les étudier pour les rendre compatibles avec E_NOTICE, car les avis sur les variables manquantes ou les décalages peuvent sauver des vies, il peut y avoir des améliorations de performances mineures à gagner, et c'est globalement la manière la plus propre.

Cependant, je n'aime pas ce qui inflige des centaines de isset()empty() et array_key_exists() s à mon code. Il se gonfle, devient moins lisible, sans rien gagner en valeur ou en sens.

Comment puis-je structurer mon code sans trop de contrôles de variables, tout en étant compatible E_NOTICE?

96
Pekka 웃

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


À mon humble avis, vous devriez penser non seulement à rendre l'application "compatible E_NOTICE", mais à restructurer le tout. Avoir des centaines de points dans votre code qui essaient régulièrement d'utiliser des variables inexistantes ressemble à un programme assez mal structuré. Essayer d'accéder à des variables inexistantes ne devrait jamais arriver, d'autres langages hésitent à cela 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 aider 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, ma mauvaise, permettez-moi de corriger cela dès que possible . " Comment allez-vous faire la différence entre " les variables qui fonctionnent très bien non définies " et un code honnêtement erroné qui peut conduire à de graves erreurs ? C'est aussi la raison pour laquelle vous développez toujours, toujours, avec le rapport d'erreurs transformé en 11 et continuez à vous brancher sur votre code jusqu'à ce que pas un seul NOTICE est émis. La désactivation du rapport d'erreurs est réservée aux environnements de production, afin d'éviter les fuites d'informations et d'offrir une meilleure expérience utilisateur même en cas de code bogué.


Élaborer:

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

Arguments de la fonction:

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

Il n'est pas nécessaire de vérifier si $bar ou $baz sont définies à l'intérieur de la fonction parce que vous les définissez, tout ce dont vous avez besoin est de savoir si leur valeur est évaluée à true ou false (ou autre).

Variables régulières n'importe où:

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

Initialisez vos variables en haut d'un bloc de code dans lequel vous allez les utiliser. Cela résout le !isset problème, garantit que vos variables ont toujours une valeur par défaut connue, donne au lecteur une idée de ce sur quoi le code suivant va fonctionner et sert ainsi également comme une sorte d'auto-documentation.

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 les écrasez avec des valeurs réelles.

Dans les autres cas, disons un modèle dans lequel vous émettez des valeurs qui peuvent ou non être définies par un contrôleur, il vous suffira 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 utilisez régulièrement array_key_exists, vous devez évaluer à quoi vous l'utilisez. La seule fois où cela fait une différence, c'est ici:

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

Comme indiqué ci-dessus cependant, 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 ne sera probablement pas null mais '', 0, '0', false ou quelque chose comme ça, c'est-à-dire une valeur que vous pouvez évaluer avec isset ou empty, selon votre intention. Si vous définissez régulièrement une clé de tableau sur null et que cela signifie autre chose que false, c'est-à-dire si dans l'exemple ci-dessus les résultats différents de isset et array_key_exists faire une différence dans la logique de votre programme, vous devriez vous demander pourquoi. La simple existence d'une variable ne devrait pas être importante, seule sa valeur devrait avoir une conséquence. Si la clé est un indicateur true/false, utilisez alors true ou false, pas null. La seule exception à cela serait les bibliothèques tierces qui veulent que null signifie quelque chose, mais comme null est si difficile à détecter dans PHP je n'ai pas encore trouver une bibliothèque qui fait cela.

127
deceze

Écrivez simplement 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 des choses triviales comme get_number(), get_boolean(), get_array() et ainsi de suite.

37
BalusC

Je crois que l'un des meilleurs moyens de faire face à ce problème est d'accéder aux valeurs de GET et des tableaux POST (COOKIE, SESSION, etc.) via une classe.

Créez une classe pour chacun de ces tableaux et déclarez les méthodes __get Et __set ( surcharge ). __get Accepte un argument qui sera le nom d'une valeur. Cette méthode doit vérifier cette valeur dans le tableau global correspondant en utilisant isset() ou empty() et renvoyer 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 de tableau de cette manière: $POST->username Et effectuer toute validation si nécessaire sans utiliser de isset() s ou empty() s. Si username n'existe pas dans le tableau global correspondant, null sera renvoyé, donc aucun avertissement ni avis ne sera généré.

13
Jamol

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


Cependant, j'utilise une fonction simple qui est utile dans ce cas, et dans d'autres situations pour traiter les index de tableau :

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 disposez des tableaux suivants:

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

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

Comment obtenez-vous la "valeur" des tableaux? Facile:

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à des tableaux unidimensionnels et multidimensionnels couverts, que pouvons-nous faire d'autre?


Prenons l'exemple de code suivant:

$url = 'https://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';
}

Assez ennuyeux n'est-ce pas? Voici une autre approche utilisant la fonction Value():

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

Comme exemple supplémentaire, prenez la fonction RealIP() pour un test:

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

Neat, hein? ;)

6
Alix Axel

J'utilise ces fonctions

function load(&$var) { return isset($var) ? $var : null; }
function POST($var) { return isset($_POST[$var]) ? $_POST[$var] : null; }

Exemples

$y = load($x); // null, no notice

// this attitude is both readable and comfortable
if($login=POST("login")) // really =, not ==
if($pass=POST("pass"))
if($login=="Admin" && $pass==...) {
  // login and pass are not empty, login is "Admin" and pass is ...
  $authorized = true;
  ...
}
3
Jan Turoň

Je suis là avec toi. Mais PHP les concepteurs ont fait des erreurs bien plus graves que cela. À défaut de définir une fonction personnalisée pour toute lecture de valeur, il n'y a aucun moyen de contourner cela.

3
vava

Bienvenue dans l'opérateur de coalescence Null (PHP> = 7.0.1):

$field = $_GET['field'] ?? null;

PHP dit:

L'opérateur de coalescence nul (??) a été ajouté en tant que sucre syntaxique dans le cas courant de la nécessité d'utiliser un ternaire en conjonction avec isset (). Il retourne son premier opérande s'il existe et n'est pas NULL; sinon, elle renvoie son deuxième opérande.

2
Alexandre Thebaldi

Créez une fonction qui renvoie false si elle n'est pas définie et, si elle est spécifiée, false si elle est vide. S'il est valide, il renvoie la variable. Vous pouvez ajouter plus d'options comme indiqué dans le code ci-dessous:

<?php
function isset_globals($method, $name, $option = "") {
    if (isset($method[$name])) {    // Check if such a variable
        if ($option === "empty" && empty($method[$name])) { return false; } // Check if empty 
        if ($option === "stringLength" && strlen($method[$name])) { return strlen($method[$name]); }    // Check length of string -- used when checking length of textareas
        return ($method[$name]);
    } else { return false; }
}

if (!isset_globals("$_post", "input_name", "empty")) {
    echo "invalid";
} else {
    /* You are safe to access the variable without worrying about errors! */
    echo "you uploaded: " . $_POST["input_name"];
}
?>
1
dragonfire

le logiciel ne fonctionne pas comme par magie grâce à Dieu, si vous attendez quelque chose qui manque, vous devez le gérer correctement. si vous l'ignorez, vous créez probablement des failles de sécurité dans vos applications. sur les langages statiques, l'accès à une variable non définie n'est tout simplement pas possible, il ne se contentera pas de compiler ou de planter votre application si elle est nulle. en outre rend votre application irréalisable, et vous allez devenir fou lorsque des choses inattendues se produisent. la rigueur du langage est un must et php, de par sa conception, est erroné à bien des égards. cela fera de vous un mauvais programmeur si vous n'êtes pas au courant.

0
knoopx

Je ne sais pas quelle est votre définition de la lisibilité, mais l'utilisation correcte des blocs empty (), isset () et try/throw/catch est assez importante pour l'ensemble du processus. Si votre E_NOTICE provient de $ _GET ou $ _POST, alors ils doivent être vérifiés par rapport à empty () à droite avec tous les autres contrôles de sécurité que ces données doivent avoir à passer. S'il provient de flux externes ou de bibliothèques, il doit être entouré de try/catch. Si cela vient de la base de données, $ db_num_rows () ou son équivalent doit être vérifié. Si cela provient de variables internes, elles doivent être correctement initialisées. Souvent, ces types d'avis proviennent de l'affectation d'une nouvelle variable au retour d'une fonction qui renvoie FAUX en cas d'échec, ceux-ci doivent être encapsulés dans un test qui, en cas d'échec, peut affecter à la variable une valeur par défaut acceptable que le code peut gérer, ou lever une exception que le code peut gérer. Ces choses allongent le code, ajoutent des blocs supplémentaires et ajoutent des tests supplémentaires, mais je ne suis pas d'accord avec vous en ce sens que je pense qu'ils ajoutent très certainement de la valeur supplémentaire.

0
Mlutz