Dans un backend PHP, le vrai sujet n’est pas de savoir si une donnée peut être partagée partout, mais de comprendre qui la lit, qui la modifie et à quel moment. Je vais clarifier la portée des variables, distinguer `global`, `$GLOBALS` et les superglobales, puis montrer dans quels cas je les accepte encore dans une API et dans quels cas je les évite franchement.
Les points essentiels à garder en tête avant d’utiliser un global
- Une variable globale n’est pas visible partout automatiquement dans le code des fonctions.
- `global` expose une variable du scope global à l’intérieur d’une fonction, tandis que `$GLOBALS` permet d’y accéder via un tableau superglobal.
- Les superglobales comme `$_GET`, `$_POST` et `$_SERVER` sont disponibles partout, mais elles contiennent des données externes à valider.
- Dans une API, les variables globales cachent les dépendances et rendent les tests plus fragiles.
- Pour du code moderne, je privilégie les arguments, les objets de contexte et l’injection de dépendances.
Ce qu’il faut garder en tête avant de toucher au global
En PHP, une variable déclarée en dehors d’une fonction appartient au scope global, mais elle n’est pas automatiquement visible à l’intérieur d’une fonction. C’est souvent là que les débutants se trompent : le fichier entier ne se comporte pas comme un seul bloc homogène, chaque fonction a sa propre portée locale. Je le rappelle toujours parce qu’une bonne partie des bugs “inexplicables” viennent simplement d’un accès hors scope.
Dans un backend classique exécuté via PHP-FPM ou un serveur web traditionnel, l’état global ne traverse généralement pas les requêtes. Autrement dit, un global peut servir à partager une valeur dans un script pendant une requête, mais pas à stocker durablement l’état d’un utilisateur ou d’une application. Si vous avez besoin de persister une information, la bonne cible est plutôt la session, un cache, une base de données ou un stockage dédié.
Cette séparation entre portée locale et portée globale est la base du reste. Une fois qu’on la maîtrise, la différence entre `global`, `$GLOBALS` et les superglobales devient beaucoup plus lisible.
Utiliser `global` et `$GLOBALS` sans les confondre
Le mot-clé `global` permet d’importer une variable du scope global à l’intérieur d’une fonction. `$GLOBALS`, lui, donne un accès direct au tableau associatif de toutes les variables globales disponibles. La documentation PHP rappelle d’ailleurs que les superglobales sont accessibles dans toutes les portées, sans déclaration préalable.
Les deux approches fonctionnent, mais je ne les mets pas au même niveau. `global` est plus compact, tandis que `$GLOBALS` rend l’accès plus explicite, au prix d’un code plus verbeux. Dans les deux cas, la dépendance reste cachée dans le corps de la fonction plutôt que dans sa signature, et c’est précisément ce qui me gêne dans un projet qui doit évoluer.
| Mécanisme | Disponible où | Usage utile | Limite principale |
|---|---|---|---|
global |
À l’intérieur d’une fonction après déclaration | Refonte rapide d’un code procédural ancien | Dépendance implicite, tests plus difficiles |
$GLOBALS |
Partout dans le script | Accès explicite à une variable globale existante | Couplage fort au symbol table, usage peu élégant |
| Superglobales | Dans tous les scopes | Données de requête, environnement, session | Ce sont des entrées externes, pas de l’état métier |
$_REQUEST |
Dans tous les scopes | Cas très spécifiques et scripts simples | Origine des données ambiguë, donc moins lisible |
Point technique utile : depuis PHP 8.1, une copie de `$GLOBALS` ne se comporte plus comme auparavant pour modifier les variables globales via cette copie. Je vois ce changement comme un bon rappel du fait que ce mécanisme n’a jamais été une base solide pour une architecture propre. C’est aussi pour cela que, dans un code moderne, je préfère réduire au maximum son usage.
Quand le global reste acceptable dans un backend
Je ne considère pas les variables globales comme interdites par principe. Elles restent parfois pratiques dans trois cas précis : un script court, une refonte progressive d’un code hérité, ou un outil de maintenance qui doit rester simple. Dans ces situations, le bénéfice immédiat peut dépasser le coût architectural, à condition de garder le périmètre très petit.
- Dans un script de migration ponctuel, un état partagé temporaire peut éviter une sur-ingénierie inutile.
- Dans un vieux projet procédural, je peux tolérer un global comme étape de transition, pas comme solution finale.
- Dans un bootstrap de configuration, une valeur lue une seule fois et ensuite traitée en lecture seule peut rester acceptable si le code est court.
- Dans un outil CLI simple, la portée globale peut dépanner, surtout si le script vit peu longtemps et n’a pas d’utilisateur concurrent.
En revanche, dès qu’on parle d’API publique, de logique métier partagée ou de code appelé depuis plusieurs couches, la tolérance baisse vite. À ce niveau, le global cesse d’être un raccourci pratique et devient une source de couplage. La prochaine question logique est donc : qu’est-ce qui casse réellement quand on en abuse ?
Les pièges qui coûtent du temps en débogage
Le premier problème, ce n’est pas la syntaxe. C’est le couplage caché. Une fonction qui dépend d’une variable globale peut sembler pure à la lecture, alors qu’elle ne l’est pas du tout. Quand je relis ce genre de code, je dois remonter ailleurs pour comprendre d’où vient la donnée, et ce temps perdu se paie ensuite en tests plus lourds, en bugs plus subtils et en refontes plus coûteuses.
| Mauvais réflexe | Effet concret | Ce que je fais à la place |
|---|---|---|
| Lire et écrire la même globale depuis plusieurs fonctions | Effets de bord difficiles à anticiper | Passer la valeur en argument ou l’encapsuler dans un objet |
| Utiliser `$_REQUEST` pour aller plus vite | Origine des données floue | Choisir explicitement `$_GET`, `$_POST` ou `$_COOKIE` selon le besoin |
| Tirer de la logique métier directement depuis `$_GET` ou `$_POST` | Code lié à HTTP au lieu d’être lié au domaine | Normaliser les entrées au bord de l’application |
| Conserver de l’état mutable dans un process persistant | Fuites de contexte entre requêtes | Réinitialiser le contexte à chaque requête ou éviter l’état partagé |
Sur le plan sécurité, je suis également strict : les superglobales d’entrée représentent des données externes, pas des valeurs de confiance. Il faut donc filtrer, valider et normaliser tôt, avant que la donnée n’entre dans le cœur du backend. Cette discipline évite de transformer un simple accès à une variable en point d’entrée pour des comportements imprévisibles.
Ce que je préfère à la place dans un projet moderne
Dans un projet backend ou API, je privilégie presque toujours des dépendances explicites. Si une fonction a besoin d’une valeur, je lui passe cette valeur. Si plusieurs fonctions ont besoin du même ensemble de paramètres, je regroupe ces paramètres dans un objet de contexte ou un service dédié. Le résultat est plus lisible, plus testable et plus facile à refactorer.
Cette version dit tout dès la signature : la fonction dépend de `appName` et de `path`. Je peux la tester sans préparer un état global préalable, et je peux la déplacer dans un autre service sans casser silencieusement son comportement.
- Arguments de fonction pour faire circuler les données de façon explicite.
- Objets de contexte pour regrouper plusieurs valeurs liées, comme la configuration ou l’identité de requête.
- Injection de dépendances pour les services, les loggers, les clients HTTP ou les accès externes.
- Session, cache ou base de données si la donnée doit survivre à plusieurs requêtes.
- Variables `static` locales seulement pour un cache interne très ciblé, jamais pour simuler un état applicatif global.
La nuance importante, c’est que tout n’a pas besoin d’être un objet gigantesque. Un simple tableau de configuration en lecture seule peut suffire dans beaucoup de scripts. Ce qui compte, c’est que la dépendance soit visible et contrôlable, pas cachée derrière un accès implicite à un état partagé.
La règle simple que j’applique avant d’introduire un global
Je me pose toujours la même question : puis-je comprendre cette fonction sans aller chercher ailleurs une variable cachée ? Si la réponse est non, je privilégie une dépendance explicite. Cette règle me paraît plus fiable que n’importe quel réflexe de confort, parce qu’elle protège à la fois la lisibilité, les tests et l’évolution du code.
- Si la donnée sert à une seule fonction, je la passe en paramètre.
- Si plusieurs couches en ont besoin, je crée un contexte ou un service dédié.
- Si la donnée doit vivre au-delà d’une requête, je choisis un stockage adapté, pas un global.
- Si je dois garder un global dans un héritage procédural, je le limite, je le documente et je prépare sa suppression.
Dans une API, cette discipline fait vite la différence entre un code qui “marche” et un code qu’on peut encore faire évoluer dans six mois. Quand la dépendance est visible dans la signature, je lis le programme plus vite, je le teste plus vite, et je corrige les erreurs sans devoir deviner ce que cachait le scope global.