Odniesienie: Co to jest zakres zmiennej, które zmienne są dostępne skąd i jakie są błędy „niezdefiniowanej zmiennej”?

167

Uwaga: To jest pytanie referencyjne dotyczące obsługi zakresu zmiennych w PHP. Zamknij dowolne z wielu pytań pasujących do tego wzoru jako duplikat tego.

Co to jest „zakres zmienny” w PHP? Czy zmienne z jednego pliku .php są dostępne w innym? Dlaczego czasami otrzymuję błędy „niezdefiniowana zmienna” ?

zamrozić
źródło
1
Jeśli zatytułujesz to jako „Niezdefiniowana zmienna”, otrzymasz o wiele więcej trafień :) dobra robota
Dale
@Dale, właściwie nie. 2 tys. Wyświetleń w ciągu 2 lat to ....
Pacerier
7
@Pacerier ... o odpowiednim momencie, aby zostawić losowy komentarz?
Dale
@Pacerier Ja też nie jestem pewien, co próbujesz powiedzieć w tym komentarzu. „Czy…” … co ?! : P
deceze
@Dale, teraz jest właściwy moment: wow, mimo że pytanie pozostawało w stagnacji przez 2 lata, po dodaniu słowa „ bezstanowy ” do GoogleDex, jego wskaźnik trafień dosłownie trzykrotnie wzrósł w ciągu zaledwie 6 miesięcy.
Pacerier

Odpowiedzi:

188

Co to jest „zakres zmienny”?

Zmienne mają ograniczony „zakres” lub „miejsca, z których są dostępne”. To, że napisałeś $foo = 'bar';kiedyś gdzieś w aplikacji, nie oznacza, że ​​możesz odwoływać się $fooz dowolnego miejsca w aplikacji. Zmienna $fooma określony zakres, w którym jest prawidłowa i tylko kod z tego samego zakresu ma dostęp do zmiennej.

Jak zdefiniowany jest zakres w PHP?

Bardzo proste: PHP ma zakres funkcji . To jedyny rodzaj separatora zakresu, który istnieje w PHP. Zmienne wewnątrz funkcji są dostępne tylko wewnątrz tej funkcji. Zmienne poza funkcjami są dostępne gdziekolwiek poza funkcjami, ale nie wewnątrz żadnej funkcji. Oznacza to, że w PHP jest jeden specjalny zakres: globalny zasięg . Każda zmienna zadeklarowana poza jakąkolwiek funkcją mieści się w tym zasięgu globalnym.

Przykład:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;
}

$foojest w zasięgu globalnym , $bazw zasięgu lokalnymmyFunc . Tylko kod wewnątrz myFuncma dostęp $baz. Dostęp ma tylko kod z zewnątrz . Żaden nie ma dostępu do drugiego:myFunc$foo

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;

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

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

Zakres i dołączone pliki

Granice plików nie oddzielają zakresu:

a.php

<?php

$foo = 'bar';

b.php

<?php

include 'a.php';

echo $foo;  // works!

Te same zasady mają zastosowanie do includekodu d, co do każdego innego kodu: tylko functionoddzielny zakres. Ze względu na zakres możesz pomyśleć o dołączeniu plików, takich jak kopiowanie i wklejanie kodu:

c.php

<?php

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

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!

W powyższym przykładzie, a.phpzostał zawarty w środku myFunc, wszystkie zmienne wewnątrz a.phpmają tylko lokalny zasięg funkcji. To, że wydają się być w zasięgu globalnym, a.phpniekoniecznie oznacza, że ​​tak jest, w rzeczywistości zależy to od kontekstu, w którym kod jest zawarty / wykonywany.

A co z funkcjami wewnątrz funkcji i klas?

Każda nowa functiondeklaracja wprowadza nowy zakres, to takie proste.

(anonimowe) funkcje wewnątrz funkcji

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

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

    // no access to $baz
}

zajęcia

$foo = 'foo';

class Bar {

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

}

// no access to $baz

Do czego służy luneta?

Radzenie sobie z problemami z zakresem może wydawać się irytujące, ale ograniczony zakres zmiennych jest niezbędny do pisania złożonych aplikacji! Gdyby każda zadeklarowana zmienna była dostępna z dowolnego miejsca w aplikacji, przechodziłbyś przez wszystkie zmienne bez rzeczywistego sposobu na śledzenie, co się zmienia. Jest tylko tyle sensownych nazw, które możesz nadać swoim zmiennym, prawdopodobnie będziesz chciał użyć zmiennej " $name" w więcej niż jednym miejscu. Gdybyś mógł mieć tę unikalną nazwę zmiennej tylko raz w swojej aplikacji, musiałbyś uciekać się do naprawdę skomplikowanych schematów nazewnictwa, aby upewnić się, że zmienne są unikalne i że nie zmieniasz niewłaściwej zmiennej z niewłaściwego fragmentu kodu.

Przestrzegać:

function foo() {
    echo $bar;
}

Gdyby nie było zakresu, co zrobiłaby powyższa funkcja? Skąd się $barbierze? Jaki ma stan? Czy jest w ogóle zainicjowany? Czy za każdym razem musisz sprawdzać? Tego nie da się utrzymać. Co prowadzi nas do ...

Przekraczanie granic zakresu

Właściwy sposób: przekazywanie i wychodzenie zmiennych

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

Zmienna $barjawnie wchodzi do tego zakresu jako argument funkcji. Wystarczy spojrzeć na tę funkcję, aby było jasne, skąd pochodzą wartości, z którymi ona pracuje. Następnie jawnie zwraca wartość. Wzywający ma pewność, że wie, z jakimi zmiennymi funkcja będzie współpracować i skąd pochodzą jej wartości zwracane:

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

Rozszerzenie zakresu zmiennych na funkcje anonimowe

$foo = 'bar';

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

$baz();

Funkcja anonimowa jawnie obejmuje $fooz otaczającego zakresu. Zauważ, że to nie to samo, co zasięg globalny .

Zły kierunek: global

Jak wspomniano wcześniej, zasięg globalny jest nieco wyjątkowy, a funkcje mogą jawnie importować z niego zmienne:

$foo = 'bar';

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

Ta funkcja używa i modyfikuje zmienną globalną $foo. Nie rób tego! (Chyba że naprawdę naprawdę naprawdę wiesz, co robisz, a nawet wtedy: nie rób tego!)

Wszystko, co widzi wywołujący tę funkcję, to:

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

Nic nie wskazuje na to, że ta funkcja ma jakiekolwiek skutki uboczne , ale tak jest. Bardzo łatwo staje się to zagmatwanym bałaganem, ponieważ niektóre funkcje ciągle się modyfikują i wymagają pewnego stanu globalnego. Chcesz, aby funkcje były bezstanowe , działały tylko na swoich wejściach i zwracały zdefiniowane dane wyjściowe, niezależnie od tego, ile razy je wywołasz.

W miarę możliwości należy unikać używania zakresu globalnego; z całą pewnością nie powinieneś „wyciągać” zmiennych z zasięgu globalnego do zasięgu lokalnego.

zamrozić
źródło
Właśnie powiedziałeś niewłaściwy sposóbglobal , więc powiedz nam, kiedy powinniśmy użyć global? I proszę wyjaśnij (trochę), co to jest static..?
@stack Nie ma „właściwej” drogi global. To zawsze jest złe. Przekazywanie parametrów funkcji jest prawidłowe. staticjest dobrze wyjaśniony w instrukcji i nie ma wiele wspólnego z zakresem. W skrócie można ją traktować jako „globalną zmienną o określonym zakresie”. Rozwijam nieco jego użycie tutaj kunststube.net/static .
deceze
Moja prosta myśl jest taka, że ​​jeśli zmienna php jest na tyle ważna, że ​​zasługuje na status globalny, zasługuje na kolumnę w bazie danych. Może to przesada, ale jest to niezawodne podejście, które pasuje do mojego przeciętnego dowcipu programistycznego
Arthur Tarasov
@Arthur Jest tam tak wiele do rozpakowania… ಠ_ಠ Z pewnością nie jest to podejście, które bym poparł.
deceze
@deceze Wee Trochę kłótni, która dzieje się tutaj dzisiaj stackoverflow.com/q/51409392 - gdzie OP wspomina, że ​​duplikat (tutaj) nie wspomina o include_oncei prawdopodobnie require_oncepowinien być gdzieś dodany; tylko mówię. OP zagłosował również za ponownym otwarciem swojego pytania. Czy ich post byłby szczególnym przypadkiem i co z tym zrobić?
Funk Forty Niner
10

Chociaż do zmiennych zdefiniowanych w zakresie funkcji nie można uzyskać dostępu z zewnątrz, nie oznacza to, że nie można używać ich wartości po zakończeniu tej funkcji. PHP ma dobrze znane staticsłowo kluczowe, które jest szeroko stosowane w PHP zorientowanym obiektowo do definiowania statycznych metod i właściwości, ale należy pamiętać, że staticmoże być również używane wewnątrz funkcji do definiowania zmiennych statycznych.

Co to jest „zmienna statyczna”?

Zmienna statyczna różni się od zwykłej zmiennej zdefiniowanej w zakresie funkcji tym, że nie traci wartości, gdy wykonanie programu opuszcza ten zakres. Rozważmy następujący przykład użycia zmiennych statycznych:

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

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

Wynik:

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

Gdybyśmy zdefiniowali $counterbez, staticto za każdym razem powtarzana wartość byłaby taka sama, jak $numparametr przekazywany do funkcji. Użycie staticpozwala zbudować ten prosty licznik bez dodatkowego obejścia.

Przypadki użycia zmiennych statycznych

  1. Przechowywanie wartości między następującymi po sobie wywołaniami funkcji.
  2. Do przechowywania wartości między wywołaniami rekurencyjnymi, gdy nie ma sposobu (lub celu) przekazania ich jako parametrów.
  3. Do pamięci podręcznej wartości, którą zwykle lepiej jest pobrać raz. Na przykład wynik odczytu niezmiennego pliku na serwerze.

Wydziwianie

Zmienna statyczna istnieje tylko w zakresie funkcji lokalnej. Nie można uzyskać do niego dostępu poza funkcją, w której został zdefiniowany. Możesz więc być pewien, że zachowa swoją wartość niezmienioną do następnego wywołania tej funkcji.

Zmienna statyczna może być zdefiniowana tylko jako wartość skalarna lub wyrażenie skalarne (od PHP 5.6). Nadanie mu innych wartości nieuchronnie prowadzi do niepowodzenia, przynajmniej w momencie pisania tego artykułu. Niemniej jednak możesz to zrobić tylko w następnym wierszu swojego kodu:

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";
}

Wynik:

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

Funkcja statyczna jest w pewnym sensie „współdzielona” między metodami obiektów tej samej klasy. Łatwo to zrozumieć, przeglądając następujący przykład:

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

Działa to tylko z obiektami tej samej klasy. Jeśli obiekty pochodzą z różnych klas (nawet wzajemnie się rozszerzają), zachowanie zmiennych statycznych będzie zgodne z oczekiwaniami.

Czy zmienna statyczna to jedyny sposób na zachowanie wartości między wywołaniami funkcji?

Innym sposobem zachowania wartości między wywołaniami funkcji jest użycie domknięć. Zamknięcia zostały wprowadzone w PHP 5.3. Krótko mówiąc, pozwalają one ograniczyć dostęp do pewnego zestawu zmiennych w zakresie funkcji do innej funkcji anonimowej, która będzie jedynym sposobem uzyskania do nich dostępu. Bycie w zamknięciu zmiennych może imitować (mniej lub bardziej skutecznie) koncepcje OOP, takie jak „stałe klasy” (jeśli zostały przekazane w domknięciu przez wartość) lub „właściwości prywatne” (jeśli zostały przekazane przez odniesienie) w programowaniu strukturalnym.

Ta ostatnia faktycznie pozwala na użycie domknięć zamiast zmiennych statycznych. O tym, czego użyć, zawsze decyduje programista, ale należy wspomnieć, że zmienne statyczne są zdecydowanie przydatne podczas pracy z rekurencjami i zasługują na uwagę programistów.

Alex Myznikov
źródło
2

Nie opublikuję pełnej odpowiedzi na to pytanie, ponieważ istniejące i podręcznik PHP świetnie sobie z tym radzą.

Ale jeden przedmiot, który został brakowało że od superglobals , w tym powszechnie stosowane $_POST, $_GET, $_SESSION, itd. Te zmienne są tablice, które są zawsze dostępne, w każdym zakresie, bez globaldeklaracji.

Na przykład ta funkcja wydrukuje nazwę użytkownika uruchamiającego skrypt PHP. Zmienna jest dostępna dla funkcji bez żadnego problemu.

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

Ogólna zasada „globalne są złe” jest zwykle zmieniana w PHP na „globalne są złe, ale superglobale są w porządku”, o ile nie nadużywa się ich. (Wszystkie te zmienne są zapisywalne, więc można ich użyć, aby uniknąć wstrzyknięcia zależności, gdybyś był naprawdę okropny).

Nie ma gwarancji, że te zmienne będą obecne; administrator może wyłączyć niektóre lub wszystkie z nich za pomocą variables_orderdyrektywy w php.ini, ale nie jest to typowe zachowanie.


Lista obecnych superglobali:

  • $GLOBALS - Wszystkie zmienne globalne w bieżącym skrypcie
  • $_SERVER - Informacje o serwerze i środowisku wykonawczym
  • $_GET - Wartości przekazane w ciągu zapytania adresu URL, niezależnie od metody HTTP użytej w żądaniu
  • $_POST- Wartości przekazane w żądaniu HTTP POST z typami MIME application/x-www-form-urlencodedlubmultipart/form-data
  • $_FILES- Pliki przekazane w żądaniu HTTP POST z multipart/form-datatypem MIME
  • $_COOKIE - Pliki cookie przekazane wraz z bieżącym żądaniem
  • $_SESSION - Zmienne sesji przechowywane wewnętrznie przez PHP
  • $_REQUEST- Zazwyczaj połączenie $_GETi $_POST, ale czasami $_COOKIES. Treść jest określona przez request_orderdyrektywę w php.ini.
  • $_ENV - Zmienne środowiskowe bieżącego skryptu
miken32
źródło