Tenter de donner un aperçu des différentes discussions et réponses:
Il n'y a pas de réponse unique à la question qui puisse remplacer tous les moyens qui isset
peuvent être utilisés. Certains cas d'utilisation sont traités par d'autres fonctions, tandis que d'autres ne résistent pas à l'examen ou ont une valeur douteuse au-delà du code golf. Loin d 'être «cassés» ou «incohérents», d' autres cas d 'utilisation démontrent pourquoi isset
la réaction de null
est au comportement logique.
Cas d'utilisation réels (avec solutions)
1. Touches de tableau
Les tableaux peuvent être traités comme des collections de variables, avec unset
et en les isset
traitant comme s'ils l'étaient. Cependant, puisqu'elles peuvent être itérées, comptées, etc., une valeur manquante n'est pas la même que celle dont la valeur est null
.
La réponse dans ce cas, est d' utiliser array_key_exists()
au lieu deisset()
.
Puisque ceci prend le tableau à vérifier en tant qu'argument de fonction, PHP lèvera toujours des "avis" si le tableau lui-même n'existe pas. Dans certains cas, on peut valablement faire valoir que chaque dimension aurait dû être initialisée en premier, de sorte que l'avis fait son travail. Pour les autres cas, une fonction "récursive" array_key_exists
, qui vérifie tour à tour chaque dimension du tableau, éviterait cela, mais serait fondamentalement la même que @array_key_exists
. C'est aussi quelque peu tangentiel à la gestion des null
valeurs.
2. Propriétés de l'objet
Dans la théorie traditionnelle de la «programmation orientée objet», l'encapsulation et le polymorphisme sont des propriétés clés des objets; dans une mise en œuvre des propriétés de la POO base de classe comme PHP de, les encapsulées sont déclarées dans le cadre du niveau d'accès définition de la classe, et compte tenu de ( public
, protected
ou private
).
Cependant, PHP vous permet également d'ajouter dynamiquement des propriétés à un objet, comme vous le feriez pour des clés d'un tableau, et certaines personnes utilisent des objets sans classe (techniquement, des instances du intégré stdClass
, qui n'a pas de méthodes ou de fonctionnalités privées) dans un façon de tableaux associatifs. Cela conduit à des situations où une fonction peut vouloir savoir si une propriété particulière a été ajoutée à l'objet qui lui est donné.
Comme avec les clés du tableau, une solution pour le contrôle des propriétés d'objet est inclus dans la langue, appelée, assez raisonnablement,property_exists
.
Cas d'utilisation non justifiables, avec discussion
3. register_globals
et autres pollutions de l'espace de noms mondial
La register_globals
fonctionnalité ajoutait des variables à la portée globale dont les noms étaient déterminés par des aspects de la requête HTTP (paramètres GET et POST et cookies). Cela peut conduire à un code bogué et non sécurisé, c'est pourquoi il a été désactivé par défaut depuis PHP 4.2, publié en août 2000 et complètement supprimé dans PHP 5.4, publié en mars 2012 . Cependant, il est possible que certains systèmes fonctionnent toujours avec cette fonctionnalité activée ou émulée. Il est également possible de «polluer» l'espace de noms global par d'autres moyens, en utilisant le global
mot - clé ou le $GLOBALS
tableau.
Premièrement, il register_globals
est peu probable qu'il produise de manière inattendue une null
variable, car les valeurs GET, POST et cookie seront toujours des chaînes (avec ''
toujours un retour true
de isset
), et les variables de la session devraient être entièrement sous le contrôle du programmeur.
Deuxièmement, la pollution d'une variable avec la valeur null
n'est un problème que si cela écrase une initialisation précédente. "Écraser" une variable non initialisée avec null
ne serait problématique que si du code ailleurs faisait la distinction entre les deux états, donc à elle seule, cette possibilité est un argument contre une telle distinction.
4. get_defined_vars
etcompact
Quelques fonctions rarement utilisées en PHP, telles que get_defined_vars
et compact
, vous permettent de traiter les noms de variables comme s'il s'agissait de clés dans un tableau. Pour les variables globales, le tableau super-global$GLOBALS
permet un accès similaire, et est plus courant. Ces méthodes d'accès se comporteront différemment si une variable n'est pas définie dans la portée correspondante.
Une fois que vous avez décidé de traiter un ensemble de variables comme un tableau en utilisant l'un de ces mécanismes, vous pouvez effectuer toutes les mêmes opérations sur celui-ci que sur n'importe quel tableau normal. Par conséquent, voir 1.
La fonctionnalité qui n'existait que pour prédire comment ces fonctions sont sur le point de se comporter (par exemple "y aura-t-il une clé 'foo' dans le tableau renvoyé par get_defined_vars
?") Est superflue, car vous pouvez simplement exécuter la fonction et découvrir sans effets négatifs.
4a. Variables variables ( $$foo
)
Bien que ce ne soit pas tout à fait la même chose que les fonctions qui transforment un ensemble de variables en un tableau associatif, la plupart des cas utilisant des "variables variables" ("assigner à une variable nommée en fonction de cette autre variable") peuvent et doivent être modifiés pour utiliser un tableau associatif à la place .
Un nom de variable, fondamentalement, est l'étiquette donnée à une valeur par le programmeur; si vous le déterminez au moment de l'exécution, ce n'est pas vraiment une étiquette mais une clé dans un magasin clé-valeur. Plus concrètement, en n'utilisant pas de tableau, vous perdez la capacité de compter, d'itérer, etc. il peut également devenir impossible d'avoir une variable «en dehors» du magasin de valeurs-clés, car elle pourrait être écrasée par $$foo
.
Une fois changé pour utiliser un tableau associatif, le code sera prêt pour la solution 1. L'accès indirect aux propriétés des objets (par exemple $foo->$property_name
) peut être adressé avec la solution 2.
5. isset
est tellement plus facile à taper quearray_key_exists
Je ne suis pas sûr que ce soit vraiment pertinent, mais oui, les noms de fonctions de PHP peuvent parfois être assez longs et incohérents. Apparemment, les versions préhistoriques de PHP utilisaient la longueur d'un nom de fonction comme clé de hachage, de sorte que Rasmus a délibérément composé des noms de fonction de htmlspecialchars
manière à ce qu'ils aient un nombre inhabituel de caractères ...
Pourtant, au moins, nous n'écrivons pas Java, hein? ;)
6. Les variables non initialisées ont un type
La page de manuel sur les principes de base des variables comprend cette déclaration:
Les variables non initialisées ont une valeur par défaut de leur type en fonction du contexte dans lequel elles sont utilisées
Je ne sais pas s'il existe dans le Zend Engine une notion de "type non initialisé mais connu" ou si cela lit trop dans l'instruction.
Ce qui est clair, c'est que cela ne fait aucune différence pratique à leur comportement, puisque les comportements décrits sur cette page pour les variables non initialisées sont identiques au comportement d'une variable dont la valeur est null
. Pour choisir un exemple, les deux $a
et $b
dans ce code finiront par être l'entier 42
:
unset($a);
$a += 42;
$b = null;
$b += 42;
(Le premier soulèvera une notification concernant une variable non déclarée, dans le but de vous faire écrire un meilleur code, mais cela ne fera aucune différence sur la façon dont le code s'exécute réellement.)
99. Détecter si une fonction a été exécutée
(Garder celui-ci en dernier, car il est beaucoup plus long que les autres. Peut-être que je le modifierai plus tard ...)
Considérez le code suivant:
$test_value = 'hello';
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
Si some_function
peut revenir null
, il est possible que le echo
ne soit pas atteint même s'il some_test
est retourné true
. L'intention du programmeur était de détecter quand il $result
n'avait jamais été défini, mais PHP ne leur permet pas de le faire.
Cependant, il existe d'autres problèmes avec cette approche, qui deviennent évidents si vous ajoutez une boucle externe:
foreach ( $list_of_tests as $test_value ) {
// something's missing here...
foreach ( $list_of_things as $thing ) {
if ( some_test($thing, $test_value) ) {
$result = some_function($thing);
}
}
if ( isset($result) ) {
echo 'The test passed at least once!';
}
}
Comme il $result
n'est jamais initialisé explicitement, il prendra une valeur lorsque le tout premier test réussit, ce qui rend impossible de dire si les tests suivants ont réussi ou non. C'est en fait un bogue extrêmement courant lorsque les variables ne sont pas initialisées correctement.
Pour résoudre ce problème, nous devons faire quelque chose sur la ligne où j'ai commenté qu'il manque quelque chose. La solution la plus évidente est de définir $result
une "valeur terminale" qui some_function
ne peut jamais revenir; si tel est le cas null
, le reste du code fonctionnera correctement. S'il n'y a pas de candidat naturel pour une valeur terminale parce some_function
que son type de retour est extrêmement imprévisible (ce qui est probablement un mauvais signe en soi), alors une valeur booléenne supplémentaire, par exemple $found
, pourrait être utilisée à la place.
Première expérience de pensée: la very_null
constante
PHP pourrait théoriquement fournir une constante spéciale - ainsi que null
- à utiliser comme valeur terminale ici; vraisemblablement, il serait illégal de le renvoyer à partir d'une fonction, ou il y serait contraint null
, et la même chose s'appliquerait probablement à sa transmission en tant qu'argument de fonction. Cela simplifierait légèrement ce cas très spécifique, mais dès que vous décidiez de re-factoriser le code - par exemple, de mettre la boucle interne dans une fonction séparée - cela deviendrait inutile. Si la constante pouvait être transmise entre les fonctions, vous ne pouviez pas garantir qu'elle some_function
ne la renverrait pas, elle ne serait donc plus utile comme valeur de terminal universelle.
L'argument pour détecter les variables non initialisées dans ce cas se résume à l'argument de cette constante spéciale: si vous remplacez le commentaire par unset($result)
et que vous le traitez différemment $result = null
, vous introduisez une "valeur" car elle $result
ne peut pas être transmise, et ne peut être détecté par des fonctions intégrées spécifiques.
Deuxième expérience de pensée: compteur d'affectation
Une autre façon de penser à ce que le dernier if
demande est "quelque chose a-t-il été assigné $result
?" Plutôt que de le considérer comme une valeur spéciale de $result
, vous pourriez peut-être considérer cela comme des "métadonnées" sur la variable, un peu comme la "variable tainting" de Perl. Ainsi , plutôt que isset
vous pourriez l' appeler has_been_assigned_to
, et plutôt que unset
, reset_assignment_state
.
Mais si oui, pourquoi s'arrêter à un booléen? Que faire si vous voulez savoir combien de fois le test a réussi; vous pouvez simplement étendre vos métadonnées à un entier et avoir get_assignment_count
et reset_assignment_count
...
De toute évidence, l'ajout d'une telle fonctionnalité aurait un compromis entre la complexité et les performances du langage, il faudrait donc le peser soigneusement par rapport à son utilité attendue. Comme pour une very_null
constante, elle ne serait utile que dans des circonstances très restreintes, et résiste de la même manière au réaffacturage.
La question qui semble évidente est de savoir pourquoi le moteur d'exécution PHP devrait supposer à l'avance que vous voulez garder une trace de ces choses, plutôt que de vous laisser le faire explicitement, en utilisant du code normal.