Les variables globales en PHP sont-elles considérées comme une mauvaise pratique? Si oui, pourquoi?

86
function foo () {
    global $var;
    // rest of code
}

Dans mes petits projets PHP, j'utilise généralement la méthode procédurale. J'ai généralement une variable qui contient la configuration du système, et quand je veux accéder à cette variable dans une fonction, je le fais global $var;.

Est-ce une mauvaise pratique?

KRTac
la source
19
La variable globale est synonyme de mauvaise pratique
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
2
Essayez le test unitaire / l'acceptation, et vous découvrirez rapidement pourquoi les globaux sont un problème: ils rendent votre code peu fiable lorsque vous faites des choses plus d'une fois.
Kzqai

Réponses:

102

Lorsque les gens parlent de variables globales dans d'autres langues, cela signifie quelque chose de différent de ce que cela fait en PHP. C'est parce que les variables ne sont pas vraiment globales en PHP. La portée d'un programme PHP typique est une requête HTTP. Les variables de session ont en fait une portée plus large que les variables "globales" PHP car elles englobent généralement de nombreuses requêtes HTTP.

Souvent (toujours?), Vous pouvez appeler des fonctions membres dans des méthodes comme preg_replace_callback()celle-ci:

preg_replace_callback('!pattern!', array($obj, 'method'), $str);

Voir les rappels pour en savoir plus.

Le fait est que les objets ont été boulonnés sur PHP et conduisent à certains égards à une certaine maladresse.

Ne vous préoccupez pas trop de l'application de normes ou de constructions de différents langages à PHP. Un autre piège courant consiste à essayer de transformer PHP en un pur langage POO en collant des modèles d'objets au-dessus de tout.

Comme toute autre chose, utilisez des variables «globales», du code procédural, un cadre particulier et une POO car cela a du sens, résout un problème, réduit la quantité de code à écrire ou le rend plus maintenable et plus facile à comprendre, pas parce que vous pensez vous devriez.

cletus
la source
8
Il convient de noter que PHP 5.3 résout certains de ces problèmes avec des fonctions lambda vous permettant d'éviter d'utiliser la fonction déclarée dans la portée globale pour les rappels. +1 pour des conseils de code maintenables et lisibles
Jonathan Fingland
Vous ne pouvez pas utiliser un rappel du formulaire array ($obj, 'callbackMethod')dans les appels à preg_replace_callback()? (Je sais, je suis la proie de cet écueil de POO ...)
grossvogel
25
La question n'était pas "faut-il jamais utiliser des variables globales?". La réponse à cette question serait «sûr à l'occasion si nécessaire». La question est de savoir s'ils sont de mauvaise pratique. La réponse est «oui, parfois». Pour le petit projet d'affiches, rien de mal ne peut en résulter - cependant, pour les projets plus importants avec de nombreux membres de l'équipe et beaucoup de pièces mobiles, une utilisation intensive de variables globales rendra le code difficile à déboguer, presque impossible à refactoriser et pénible à égaliser lis. Pouvez-vous les utiliser parfois, ... bien sûr - est-ce qu'ils sont nuls, ... ouais!
eddiemoya
@eddiemoya Bien dit Eddie. Il y a tellement de gens qui justifient de mauvaises pratiques comme l'utilisation de variables globales. Vous devriez les éviter comme la peste. Tout diplôme décent en génie logiciel vous expliquera cela ... les professeurs ne vous le disent pas seulement ... ils le savent grâce à des décennies d'expérience. Vous devez utiliser les fonctions membres lorsque cela est possible pour accéder aux valeurs dont vous avez besoin, par exemple get_query_var () dans Wordpress, etc.
27

Les variables globales, si elles ne sont pas utilisées avec soin, peuvent rendre les problèmes plus difficiles à trouver. Disons que vous demandez un script php et que vous recevez un avertissement indiquant que vous essayez d'accéder à un index d'un tableau qui n'existe pas dans une fonction.

Si le tableau auquel vous essayez d'accéder est local à la fonction, vous vérifiez la fonction pour voir si vous y avez fait une erreur. Cela peut être un problème avec une entrée de la fonction afin que vous vérifiez les endroits où la fonction est appelée.

Mais si ce tableau est global, vous devez vérifier tous les endroits où vous utilisez cette variable globale, et pas seulement cela, vous devez déterminer dans quel ordre ces références à la variable globale sont accédées.

Si vous avez une variable globale dans un morceau de code, il est difficile d'isoler la fonctionnalité de ce code. Pourquoi voudriez-vous isoler les fonctionnalités? Vous pouvez donc le tester et le réutiliser ailleurs. Si vous avez du code que vous n'avez pas besoin de tester et que vous n'avez pas besoin de réutiliser, alors utiliser des variables globales est très bien.

Rojoca
la source
Mais l'erreur montre principalement dans quel fichier / ligne le script se brise, donc je ne vois pas le problème ici
samayo
8
Endroit où le script s'est cassé! = Endroit où l'erreur a été commise.
HonoredMule
16

je suis d'accord avec cletus. j'ajouterais deux choses:

  1. utilisez un préfixe pour pouvoir l'identifier immédiatement comme global (par exemple $ g_)
  2. déclarez-les en un seul endroit, ne les répandez pas tout autour du code.

Meilleures salutations, don

Don Dickinson
la source
1
Ouais, je préfixe toujours les variables que j'ai l'intention d'utiliser globalement avec un soulignement.
KRTac
9
@KRTac mais $ _testVariable est généralement compris comme une variable privée - c'est une norme informelle pour définir des variables privées, pas des variables globales.
Aditya MP
6
Une pratique courante consiste à définir des variables globales en utilisant ALL CAPS. exemple:$DB = 'foo';
pixeline
7

Qui peut s'opposer à l'expérience, aux diplômes universitaires et au génie logiciel? Pas moi. Je dirais seulement qu'en développant des applications PHP mono-page orientées objet, je m'amuse plus quand je sais que je peux tout construire à partir de zéro sans me soucier des collisions d'espace de noms. Construire à partir de zéro est quelque chose que beaucoup de gens ne font plus. Ils ont un travail, une date limite, un bonus ou une réputation dont ils doivent se soucier. Ces types ont tendance à utiliser tellement de code pré-construit avec des enjeux élevés, qu'ils ne peuvent pas risquer du tout d'utiliser des variables globales.

Il peut être mauvais d'utiliser des variables globales, même si elles ne sont utilisées que dans la zone globale d'un programme, mais n'oublions pas ceux qui veulent juste s'amuser et faire fonctionner quelque chose .

Si cela signifie utiliser quelques variables (<10) dans l'espace de noms global, qui ne sont utilisées que dans la zone globale d'un programme, qu'il en soit ainsi. Oui, oui, MVC, injection de dépendance, code externe, bla, bla, bla, bla. Mais, si vous avez contenu 99,99% de votre code dans des espaces de noms et des classes, et que le code externe est en bac à sable, le monde ne s'arrêtera pas (je le répète, le monde ne s'arrêtera pas) si vous utilisez une variable globale.

En général, je ne dirais pas que l'utilisation de variables globales est une mauvaise pratique . Je dirais que l'utilisation de variables globales (drapeaux et autres) en dehors de la zone globale d'un programme pose des problèmes et (à long terme) est déconseillé car vous pouvez perdre la trace de leurs états assez facilement. Aussi, je dirais que plus vous apprenez, moins vous serez dépendant des variables globales car vous aurez expérimenté la "joie" de traquer les bogues associés à leur utilisation. Cela seul vous incitera à trouver un autre moyen de résoudre le même problème. Par coïncidence, cela tend à pousser les personnes PHP dans le sens de l'apprentissage de l'utilisation des espaces de noms et des classes (membres statiques, etc ...).

Le domaine de l'informatique est vaste. Si nous effrayons tout le monde de faire quelque chose parce que nous le qualifions de mauvais , alors ils perdent le plaisir de vraiment comprendre le raisonnement derrière l'étiquette.

Utilisez des variables globales si nécessaire, mais voyez ensuite si vous pouvez résoudre le problème sans elles. Les collisions, les tests et le débogage signifient plus lorsque vous comprenez intimement la vraie nature du problème, pas seulement une description du problème.

Anthony Rutledge
la source
3

Republié à partir de la version bêta de documentation SO terminée

Nous pouvons illustrer ce problème avec le pseudo-code suivant

function foo() {
     global $bob;
     $bob->doSomething();
}

Votre première question ici est évidente

D'où $bobvient-il?

Êtes-vous confus? Bien. Vous venez d'apprendre pourquoi les globaux sont déroutants et considérés comme une mauvaise pratique. S'il s'agissait d'un vrai programme, votre prochain plaisir est de rechercher toutes les instances de $bobet d'espérer que vous trouverez le bon (cela s'aggrave s'il $bobest utilisé partout). Pire encore, si quelqu'un d'autre définit $bob(ou si vous avez oublié et réutilisé cette variable), votre code peut casser (dans l'exemple de code ci-dessus, avoir le mauvais objet, ou aucun objet du tout, provoquerait une erreur fatale). Puisque pratiquement tous les programmes PHP utilisent du code comme include('file.php');votre travail, maintenir un code comme celui-ci devient exponentiellement plus difficile à mesure que vous ajoutez de fichiers.

Comment éviter les globaux?

La meilleure façon d'éviter les globaux est une philosophie appelée injection de dépendance . C'est là que nous transmettons les outils dont nous avons besoin à la fonction ou à la classe.

function foo(\Bar $bob) {
    $bob->doSomething();
}

C'est beaucoup plus facile à comprendre et à maintenir. Il est impossible de deviner où a $bobété configuré car l'appelant est responsable de le savoir (il nous transmet ce que nous devons savoir). Mieux encore, nous pouvons utiliser des déclarations de type pour restreindre ce qui est passé. Nous savons donc que $bobc'est soit une instance de la Barclasse, soit une instance d'un enfant de Bar, ce qui signifie que nous savons que nous pouvons utiliser les méthodes de cette classe. Combiné à un autoloader standard (disponible depuis PHP 5.3), on peut désormais aller traquer où Barest défini. PHP 7.0 ou version ultérieure inclut des déclarations de type étendues, où vous pouvez également utiliser des types scalaires (comme intou string).

Machavity
la source
Une alternative au passage de $ bob partout, est de faire de la classe Bar un singleton, de stocker une instance de Bar de manière statique dans Bar lui-même, et d'utiliser une méthode statique pour instancier / récupérer l'objet. Ensuite, vous pouvez juste $bob = Bar::instance();quand vous en avez besoin.
Scoots du
1
Sachez simplement que les singletons sont considérés comme un anti-pattern . L'injection de dépendances évite ces pièges
Machavity
2
Il y a un certain degré de discorde dans ce message auquel vous avez lié (par exemple, le commentaire le mieux noté à la réponse acceptée et la deuxième réponse la plus votée en désaccord avec la réponse acceptée), ce qui me donne envie de dire que l'utilisation de Singletons devrait être examinée au cas par cas, plutôt que rejetée sans préavis.
Scoots du
0

Comme:

global $my_global; 
$my_global = 'Transport me between functions';
Equals $GLOBALS['my_global']

est une mauvaise pratique (comme Wordpress $pagenow) ... hmmm

Considérez ceci:

$my-global = 'Transport me between functions';

est une erreur PHP Mais:

$GLOBALS['my-global'] = 'Transport me between functions';

n'est PAS une erreur, les hypens ne seront pas en conflit avec les variables déclarées par l'utilisateur "communes", comme $pagenow. Et l'utilisation de MAJUSCULES indique un superglobal en cours d'utilisation, facile à repérer dans le code, ou à suivre avec recherche dans les fichiers

J'utilise des traits d'union, si je suis paresseux pour créer des classes de tout pour une seule solution, comme:

$GLOBALS['PREFIX-MY-GLOBAL'] = 'Transport me ... ';

Mais dans les cas d'une utilisation plus large, j'utilise ONE globals comme tableau:

$GLOBALS['PREFIX-MY-GLOBAL']['context-something'] = 'Transport me ... ';
$GLOBALS['PREFIX-MY-GLOBAL']['context-something-else']['numbers'][] = 'Transport me ... ';

Ce dernier est pour moi, une bonne pratique sur les objectifs «cola light» ou à utiliser, au lieu de l'encombrement de classes singleton à chaque fois pour «mettre en cache» certaines données. Veuillez faire un commentaire si je me trompe ou si je manque quelque chose de stupide ici ...

Jonas Lundman
la source