Référence: Qu'est-ce que la portée des variables, quelles variables sont accessibles à partir d'où et quelles sont les erreurs de «variable indéfinie»?

167

Remarque: Ceci est une question de référence pour traiter la portée des variables en PHP. Veuillez fermer l'une des nombreuses questions correspondant à ce modèle comme un double de celui-ci.

Qu'est-ce que la "portée variable" en PHP? Les variables d'un fichier .php sont-elles accessibles dans un autre? Pourquoi ai-je parfois des erreurs "variable non définie" ?

déceler
la source
1
Si vous avez intitulé ceci comme "Variable non définie", vous obtiendrez beaucoup plus de hits :) Bon travail cependant
Dale
@Dale, en fait non. 2k vues en 2 ans, c'est ....
Pacerier
7
@Pacerier ... sur le bon moment pour laisser un commentaire au hasard?
Dale
@Pacerier Je ne suis pas sûr non plus de ce que vous essayez de dire avec ce commentaire. "Est ..." ... quoi?! : P
deceze
@Dale, c'est le bon moment: wow même si la question a stagné pendant 2 ans, après l' ajout du mot « apatride » à son GoogleDex, son taux de réussite a littéralement 3x-ed en seulement 6 mois.
Pacerier le

Réponses:

188

Qu'est-ce qu'une "portée variable"?

Les variables ont une «portée» limitée, ou des «endroits à partir desquels elles sont accessibles». Ce n'est pas parce que vous avez écrit $foo = 'bar';une fois quelque part dans votre application que vous pouvez vous référer $foode partout ailleurs dans l'application. La variable $fooa une certaine portée dans laquelle elle est valide et seul le code de la même portée a accès à la variable.

Comment une portée est-elle définie en PHP?

Très simple: PHP a une portée fonctionnelle . C'est le seul type de séparateur de portée qui existe en PHP. Les variables à l'intérieur d'une fonction ne sont disponibles qu'à l'intérieur de cette fonction. Les variables en dehors des fonctions sont disponibles n'importe où en dehors des fonctions, mais pas à l'intérieur d'une fonction. Cela signifie qu'il y a une portée spéciale en PHP: la portée globale . Toute variable déclarée en dehors de toute fonction est dans cette portée globale.

Exemple:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;
}

$fooest dans la portée globale , $bazest dans une portée locale à l' intérieur myFunc. Seul le code à l'intérieur myFunca accès à $baz. Seul le code extérieur myFunc a accès à $foo. Aucun n'a accès à l'autre:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;

    echo $foo;  // doesn't work
    echo $baz;  // works
}

echo $foo;  // works
echo $baz;  // doesn't work

Portée et fichiers inclus

Les limites des fichiers ne séparent pas la portée:

a.php

<?php

$foo = 'bar';

b.php

<?php

include 'a.php';

echo $foo;  // works!

Les mêmes règles s'appliquent au includecode d que pour tout autre code: seulement functionune portée distincte. Aux fins de la portée, vous pouvez penser à inclure des fichiers tels que copier-coller du code:

c.php

<?php

function myFunc() {
    include 'a.php';

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!

Dans l'exemple ci-dessus, a a.phpété inclus à l'intérieur myFunc, toutes les variables à l'intérieur a.phpn'ont qu'une portée de fonction locale. Ce n'est pas parce qu'ils semblent être dans la portée globale de a.phpcela que cela signifie nécessairement qu'ils le sont, cela dépend en fait du contexte dans lequel le code est inclus / exécuté.

Qu'en est-il des fonctions à l'intérieur des fonctions et des classes?

Chaque nouvelle functiondéclaration introduit une nouvelle portée, c'est aussi simple que cela.

(anonyme) fonctions à l'intérieur des fonctions

function foo() {
    $foo = 'bar';

    $bar = function () {
        // no access to $foo
        $baz = 'baz';
    };

    // no access to $baz
}

Des classes

$foo = 'foo';

class Bar {

    public function baz() {
        // no access to $foo
        $baz = 'baz';
    }

}

// no access to $baz

À quoi sert la portée?

Traiter les problèmes de cadrage peut sembler ennuyeux, mais une portée variable limitée est essentielle pour écrire des applications complexes! Si chaque variable que vous déclarez était disponible partout ailleurs dans votre application, vous parcourriez toutes vos variables sans véritable moyen de suivre ce qui change quoi. Il n'y a qu'un nombre limité de noms sensés que vous pouvez donner à vos variables, vous voudrez probablement utiliser la variable " $name" à plus d'un endroit. Si vous ne pouviez avoir ce nom de variable unique qu'une seule fois dans votre application, vous devrez recourir à des schémas de dénomination très compliqués pour vous assurer que vos variables sont uniques et que vous ne changez pas la mauvaise variable du mauvais morceau de code.

Observer:

function foo() {
    echo $bar;
}

S'il n'y avait pas de portée, que ferait la fonction ci-dessus? D'où $barvient-il? Quel état a-t-il? Est-il même initialisé? Devez-vous vérifier à chaque fois? Ce n'est pas maintenable. Ce qui nous amène à ...

Traverser les limites de la portée

La bonne façon: faire entrer et sortir des variables

function foo($bar) {
    echo $bar;
    return 42;
}

La variable $barentre explicitement dans cette portée en tant qu'argument de fonction. En regardant simplement cette fonction, il est clair d'où proviennent les valeurs avec lesquelles elle fonctionne. Il renvoie ensuite explicitement une valeur. L'appelant a la confiance nécessaire pour savoir avec quelles variables la fonction fonctionnera et d'où viennent ses valeurs de retour:

$baz   = 'baz';
$blarg = foo($baz);

Extension de la portée des variables aux fonctions anonymes

$foo = 'bar';

$baz = function () use ($foo) {
    echo $foo;
};

$baz();

La fonction anonyme inclut explicitement $foode sa portée environnante. Notez que ce n'est pas la même chose que la portée globale .

La mauvaise direction: global

Comme indiqué précédemment, la portée globale est quelque peu spéciale et les fonctions peuvent en importer explicitement des variables:

$foo = 'bar';

function baz() {
    global $foo;
    echo $foo;
    $foo = 'baz';
}

Cette fonction utilise et modifie la variable globale $foo. Ne faites pas cela! (À moins que vous ne sachiez vraiment vraiment vraiment ce que vous faites, et même alors: ne le faites pas!)

Tout ce que voit l'appelant de cette fonction est ceci:

baz(); // outputs "bar"
unset($foo);
baz(); // no output, WTF?!
baz(); // outputs "baz", WTF?!?!!

Rien n'indique que cette fonction ait des effets secondaires , mais c'est le cas. Cela devient très facilement un désordre enchevêtré car certaines fonctions continuent de se modifier et nécessitent un état global. Vous voulez que les fonctions soient sans état , agissant uniquement sur leurs entrées et renvoyant une sortie définie, quel que soit le nombre de fois que vous les appelez.

Vous devez éviter autant que possible d'utiliser la portée globale; vous ne devriez certainement pas "extraire" des variables de la portée globale vers une portée locale.

déceler
la source
Vous venez de dire le mauvais sensglobal , alors dites-nous quand devrions-nous utiliser global? Et expliquez (un peu) ce que c'est static..?
@stack Il n'y a pas de "bonne" manière pour global. C'est toujours faux. Passer des paramètres de fonction est juste. staticest bien expliqué dans le manuel et n'a pas grand chose à voir avec la portée. En un mot, il peut être considéré comme une «variable globale de portée». Je développe un peu son utilisation ici kunststube.net/static .
deceze
Ma pensée simple est que si une variable php est suffisamment importante pour mériter un statut global, elle mérite une colonne dans une base de données. C'est peut-être exagéré, mais c'est une approche infaillible qui correspond à mon esprit de programmation médiocre
Arthur Tarasov
@Arthur Il y a tellement de choses à déballer là-bas… ಠ_ಠ Ce n'est certainement pas une approche que j'approuverais.
deceze
@deceze wee bit d'un argument qui se passe ici aujourd'hui stackoverflow.com/q/51409392 - où l'OP mentionne que le doublon (ici) ne mentionne pas include_onceet require_oncedevrait peut -être aussi être ajouté quelque part; juste en disant. OP a également voté pour rouvrir sa question. Leur publication constituerait-elle un cas particulier et que faudrait-il faire à ce sujet?
Funk Forty Niner
10

Bien que les variables définies à l'intérieur de la portée d'une fonction ne soient pas accessibles de l'extérieur, cela ne signifie pas que vous ne pouvez pas utiliser leurs valeurs une fois la fonction terminée. PHP a un staticmot-clé bien connu qui est largement utilisé dans PHP orienté objet pour définir des méthodes et des propriétés statiques, mais il faut garder à l'esprit qu'il staticpeut également être utilisé à l'intérieur de fonctions pour définir des variables statiques.

Qu'est-ce que c'est «variable statique»?

La variable statique diffère de la variable ordinaire définie dans la portée de la fonction dans le cas où elle ne perd pas de valeur lorsque l'exécution du programme quitte cette portée. Prenons l'exemple suivant d'utilisation de variables statiques:

function countSheep($num) {
 static $counter = 0;
 $counter += $num;
 echo "$counter sheep jumped over fence";
}

countSheep(1);
countSheep(2);
countSheep(3);

Résultat:

1 sheep jumped over fence
3 sheep jumped over fence
6 sheep jumped over fence

Si nous avions défini $countersans staticalors chaque fois la valeur écho serait la même que le $numparamètre passé à la fonction. L'utilisation staticpermet de créer ce compteur simple sans solution de contournement supplémentaire.

Cas d'utilisation des variables statiques

  1. Pour stocker des valeurs entre des appels de fonction conséquents.
  2. Pour stocker des valeurs entre des appels récursifs lorsqu'il n'y a aucun moyen (ou aucun but) de les passer en tant que paramètres.
  3. Pour mettre en cache la valeur qui est normalement préférable de récupérer une fois. Par exemple, résultat de la lecture d'un fichier immuable sur le serveur.

Des trucs

La variable statique n'existe que dans une portée de fonction locale. Il n'est pas accessible en dehors de la fonction dans laquelle il a été défini. Vous pouvez donc être sûr qu'il conservera sa valeur inchangée jusqu'au prochain appel à cette fonction.

La variable statique ne peut être définie que comme un scalaire ou comme une expression scalaire (depuis PHP 5.6). Lui attribuer d'autres valeurs conduit inévitablement à un échec au moins au moment de la rédaction de cet article. Néanmoins, vous pouvez le faire uniquement sur la ligne suivante de votre code:

function countSheep($num) {
  static $counter = 0;
  $counter += sqrt($num);//imagine we need to take root of our sheep each time
  echo "$counter sheep jumped over fence";
}

Résultat:

2 sheep jumped over fence
5 sheep jumped over fence
9 sheep jumped over fence

La fonction statique est un peu «partagée» entre les méthodes d'objets de la même classe. Il est facile à comprendre en consultant l'exemple suivant:

class SomeClass {
  public function foo() {
    static $x = 0;
    echo ++$x;
  }
}

$object1 = new SomeClass;
$object2 = new SomeClass;

$object1->foo(); // 1
$object2->foo(); // 2 oops, $object2 uses the same static $x as $object1
$object1->foo(); // 3 now $object1 increments $x
$object2->foo(); // 4 and now his twin brother

Cela ne fonctionne qu'avec des objets de la même classe. Si les objets sont de classes différentes (même s'étendant les uns les autres), le comportement des variables statiques sera comme prévu.

La variable statique est-elle le seul moyen de conserver les valeurs entre les appels à une fonction?

Une autre façon de conserver les valeurs entre les appels de fonction consiste à utiliser des fermetures. Les fermetures ont été introduites dans PHP 5.3. En deux mots, ils vous permettent de limiter l'accès à un ensemble de variables dans une portée de fonction à une autre fonction anonyme qui sera le seul moyen d'y accéder. Être dans des variables de fermeture peut imiter (avec plus ou moins de succès) des concepts de POO comme les «constantes de classe» (si elles ont été passées en fermeture par valeur) ou les «propriétés privées» (si elles sont passées par référence) dans la programmation structurée.

Ce dernier permet en fait d'utiliser des fermetures au lieu de variables statiques. Ce qu'il faut utiliser est toujours au développeur de décider, mais il convient de mentionner que les variables statiques sont vraiment utiles lorsque vous travaillez avec des récursions et méritent d'être remarquées par les développeurs.

Alex Myznikov
la source
2

Je ne publierai pas de réponse complète à la question, car les versions existantes et le manuel PHP font un excellent travail pour expliquer la plupart de cela.

Mais un sujet qui a été manquée était celle de superglobales , y compris le couramment utilisé $_POST, $_GET, $_SESSION, etc. Ces variables sont des tableaux qui sont toujours disponibles, dans une portée, sans globaldéclaration.

Par exemple, cette fonction affichera le nom de l'utilisateur exécutant le script PHP. La variable est disponible pour la fonction sans aucun problème.

<?php
function test() {
    echo $_ENV["user"];
}

La règle générale «les globaux sont mauvais» est généralement modifiée en PHP en «les globaux sont mauvais mais les superglobals sont bien», tant qu'on ne les utilise pas à mauvais escient. (Toutes ces variables sont inscriptibles, elles pourraient donc être utilisées pour éviter l'injection de dépendances si vous étiez vraiment terrible.)

La présence de ces variables n'est pas garantie; un administrateur peut désactiver certains d'entre eux ou tous à l'aide de la variables_orderdirective in php.ini, mais ce n'est pas un comportement courant.


Une liste des superglobales actuelles:

  • $GLOBALS - Toutes les variables globales du script courant
  • $_SERVER - Informations sur le serveur et l'environnement d'exécution
  • $_GET - Valeurs passées dans la chaîne de requête de l'URL, quelle que soit la méthode HTTP utilisée pour la requête
  • $_POST- Valeurs passées dans une requête HTTP POST avec application/x-www-form-urlencodedou multipart/form-datatypes MIME
  • $_FILES- Fichiers passés dans une requête HTTP POST avec un multipart/form-datatype MIME
  • $_COOKIE - Cookies transmis avec la demande en cours
  • $_SESSION - Variables de session stockées en interne par PHP
  • $_REQUEST- Généralement une combinaison de $_GETet $_POST, mais parfois $_COOKIES. Le contenu est déterminé par la request_orderdirective dans php.ini.
  • $_ENV - Les variables d'environnement du script courant
miken32
la source