Ссылка: Что такое область видимости переменных, какие переменные доступны откуда и каковы ошибки «неопределенных переменных»?

167

Примечание: это справочный вопрос для работы с переменной областью в PHP. Пожалуйста, закройте любой из множества вопросов, подходящих к этому шаблону, как дубликат этого.

Что такое «переменная область» в PHP? Доступны ли переменные из одного файла .php в другом? Почему я иногда получаю ошибки «неопределенная переменная» ?

децезе
источник
1
Если вы назвали это как «неопределенная переменная», вы получите больше хитов :) хорошая работа
Дейл
@Dale, на самом деле нет. 2k просмотров за 2 года это ....
Pacerier
7
@Pacerier ... о правильном времени, чтобы оставить случайный комментарий?
Дейл
@Pacerier Я действительно не уверен, что ты пытаешься сказать этим комментарием. "Это ...." ... что ?! : P
deceze
@Dale, сейчас самое подходящее время: вау, несмотря на то, что вопрос застоялся на 2 года, после того, как слово « без гражданства » добавлено в его GoogleDex, его рейтинг попаданий буквально в 3 раза выше всего за 6 месяцев.
Pacerier

Ответы:

188

Что такое «переменная область»?

Переменные имеют ограниченную «область видимости» или «места, из которых они доступны». То, что вы $foo = 'bar';однажды написали где-то в своем приложении, не означает, что вы можете ссылаться $fooиз любого другого места внутри приложения. Переменная $fooимеет определенную область, в которой она действительна, и только код в той же области имеет доступ к переменной.

Как определяется область действия в PHP?

Очень просто: PHP имеет область действия функции . Это единственный вид разделителя области видимости, который существует в PHP. Переменные внутри функции доступны только внутри этой функции. Переменные вне функций доступны везде вне функций, но не внутри какой-либо функции. Это означает, что в PHP есть одна специальная область: глобальная область. Любая переменная, объявленная вне какой-либо функции, находится в этой глобальной области видимости.

Пример:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;
}

$fooнаходится в глобальной области видимости, $bazнаходится в локальной области видимости изнутри myFunc. Только код внутри myFuncимеет доступ к $baz. Только код снаружи 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

Область и включенные файлы

Границы файла не разделяют область видимости:

a.php

<?php

$foo = 'bar';

b.php

<?php

include 'a.php';

echo $foo;  // works!

К included-коду применяются те же правила, что и к любому другому коду: только functionотдельная область. Для целей охвата вы можете подумать о включении таких файлов, как копирование и вставка кода:

c.php

<?php

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

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!

В приведенном выше примере, a.phpбыл включен внутри myFunc, любые переменные внутри a.phpимеют только локальную область видимости функции. Просто потому , что они появляются , чтобы быть в глобальном масштабе в a.phpвовсе не обязательно означает , что они, на самом деле зависит от того, какой контекст , что код включен / выполняется в.

Как насчет функций внутри функций и классов?

Каждая новая functionдекларация вводит новую область, это так просто.

(анонимные) функции внутри функций

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

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

    // no access to $baz
}

классы

$foo = 'foo';

class Bar {

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

}

// no access to $baz

Для чего нужна сфера?

Решение проблем с областями видимости может показаться раздражающим, но ограниченная область применения переменных необходима для написания сложных приложений! Если бы каждая объявленная вами переменная была бы доступна из любого места внутри вашего приложения, вы бы пошагово перебирали все переменные, не имея реального способа отследить, что и что изменится. Существует только так много разумных имен, которые вы можете дать своим переменным, вы, вероятно, захотите использовать переменную " $name" более чем в одном месте. Если бы вы могли иметь это уникальное имя переменной только один раз в своем приложении, вам пришлось бы прибегнуть к действительно сложным схемам именования, чтобы убедиться, что ваши переменные уникальны и что вы не меняете неправильную переменную из неправильного фрагмента кода.

Заметим:

function foo() {
    echo $bar;
}

Если бы не было области видимости, что бы делала вышеуказанная функция? Откуда $barберутся? В каком состоянии он находится? Это даже инициализировано? Вы должны проверять каждый раз? Это не подлежит ремонту. Что приводит нас к ...

Пересечение границ границ

Правильный путь: передача переменных в и из

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

Переменная $barявно входит в эту область в качестве аргумента функции. Глядя на эту функцию, становится ясно, откуда берутся значения, с которыми она работает. Затем он явно возвращает значение. Вызывающая сторона уверена, что знает, с какими переменными будет работать функция и откуда возвращаются ее значения:

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

Расширение области видимости переменных в анонимные функции

$foo = 'bar';

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

$baz();

Анонимная функция явно включает в себя $fooиз окружающей ее области видимости. Обратите внимание, что это не то же самое, что глобальный область.

Неправильный путь: global

Как было сказано ранее, глобальная область видимости является чем-то особенным, и функции могут явно импортировать переменные из нее:

$foo = 'bar';

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

Эта функция использует и изменяет глобальную переменную $foo. Не делай этого! (Если только вы действительно действительно действительно не знаете, что делаете, и даже тогда: не надо!)

Все, что вызывает эта функция, видит так:

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

Там нет никаких признаков того, что эта функция имеет какие-либо побочные эффекты , но это так. Это очень легко становится запутанным беспорядком, поскольку некоторые функции продолжают изменяться и требуют некоторого глобального состояния. Вы хотите, чтобы функции были без состояния , действовали только на свои входы и возвращали определенный вывод, сколько бы раз вы их ни вызывали.

Вы должны избегать использования глобальной области видимости в максимально возможной степени; Скорее всего, вы не должны «вытягивать» переменные из глобальной области в локальную область.

децезе
источник
Вы только что сказали неправильный путьglobal , поэтому, пожалуйста, сообщите нам, когда мы должны использовать global? И, пожалуйста, объясните (немного), что static..?
@stack Нет «правильного» пути для global. Это всегда неправильно. Передача параметров функции правильная. staticхорошо объяснено в руководстве и не имеет большого отношения к области применения. В двух словах, это можно рассматривать как «глобальную переменную в области видимости». Я расширяю его использование здесь kunststube.net/static .
deceze
Моя простая мысль: если переменная php достаточно важна, чтобы заслужить глобальный статус, она заслуживает столбца в базе данных. Может быть, это излишнее, но это
Артур Тарасов
@ Артур Там так много всего, что нужно распаковать ... ಠ_ಠ Это, безусловно, не тот подход, который я бы одобрил.
deceze
@deceze чуть-чуть аргумента, происходящего здесь сегодня stackoverflow.com/q/51409392 - где OP упоминает, что дубликат (здесь) не упоминается include_onceи, возможно, require_onceтакже должен быть где-то добавлен; просто говорю. ОП проголосовал, чтобы вновь открыть их вопрос. Будет ли их пост особым случаем и что с этим делать?
Funk Forty Niner
10

Хотя переменные, определенные внутри области действия функции, не могут быть доступны извне, это не означает, что вы не можете использовать их значения после завершения этой функции. В PHP есть хорошо известное staticключевое слово, которое широко используется в объектно-ориентированном PHP для определения статических методов и свойств, но следует помнить, что оно staticтакже может использоваться внутри функций для определения статических переменных.

Что такое «статическая переменная»?

Статическая переменная отличается от обычной переменной, определенной в области действия функции, в том случае, если она не теряет значение, когда выполнение программы выходит из этой области. Давайте рассмотрим следующий пример использования статических переменных:

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

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

Результат:

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

Если бы мы определили $counterбез этого, staticто каждый раз отображаемое значение будет таким же, как $numпараметр, передаваемый в функцию. Использование staticпозволяет построить этот простой счетчик без дополнительных обходных путей.

Примеры использования статических переменных

  1. Для сохранения значений между последовательными вызовами функции.
  2. Хранить значения между рекурсивными вызовами, когда нет возможности (или нет цели) передать их как параметры.
  3. Кэшировать значение, которое обычно лучше получить один раз. Например, результат чтения неизменяемого файла на сервере.

Ухищрения

Статическая переменная существует только в локальной области действия функции. К нему нельзя получить доступ за пределами функции, в которой он был определен. Поэтому вы можете быть уверены, что оно сохранит свое значение без изменений до следующего вызова этой функции.

Статическая переменная может быть определена только как скаляр или скалярное выражение (начиная с PHP 5.6). Присвоение ему других значений неизбежно ведет к провалу, по крайней мере, на момент написания этой статьи. Тем не менее, вы можете сделать это только на следующей строке вашего кода:

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

Результат:

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

Статическая функция является своего рода «общей» для методов объектов одного и того же класса. Это легко понять, просмотрев следующий пример:

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

Это работает только с объектами одного класса. Если объекты принадлежат разным классам (даже расширяют друг друга), поведение статических переменных будет таким, как ожидалось.

Является ли статическая переменная единственным способом сохранить значения между вызовами функции?

Другой способ сохранить значения между вызовами функций - использовать замыкания. Замыкания были введены в PHP 5.3. В двух словах они позволяют вам ограничить доступ к некоторому набору переменных в области действия функции другой анонимной функции, которая будет единственным способом доступа к ним. Находящиеся в замыкании переменные могут имитировать (более или менее успешно) такие концепции ООП, как «константы класса» (если они были переданы в замыкании по значению) или «частные свойства» (если они передаются по ссылке) в структурном программировании.

Последнее фактически позволяет использовать замыкания вместо статических переменных. Что использовать, решать только разработчикам, но следует отметить, что статические переменные, безусловно, полезны при работе с рекурсиями и заслуживают того, чтобы их заметили разработчики.

Алекс Мызников
источник
2

Я не буду публиковать полный ответ на вопрос, поскольку существующие и руководство по PHP делают большую часть объяснения большей части этого.

Но один вопрос , который был пропущен, что из суперглобальных , в том числе широко используются $_POST, $_GET, $_SESSIONи т.д. Эти переменные являются массивы, которые доступны всегда, в любой области, без globalдекларации.

Например, эта функция выведет имя пользователя, выполняющего скрипт PHP. Переменная доступна для функции без каких-либо проблем.

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

Общее правило «глобальные ошибки плохие», как правило, изменяется в PHP на «глобальные ошибки плохие, но суперглобальные в порядке», если не злоупотреблять ими. (Все эти переменные доступны для записи, поэтому их можно использовать, чтобы избежать внедрения зависимостей, если вы действительно ужасны.)

Эти переменные не обязательно присутствуют; администратор может отключить некоторые или все из них, используя variables_orderдирективу in php.ini, но это не обычное поведение.


Список текущих суперглобалей:

  • $GLOBALS - Все глобальные переменные в текущем скрипте
  • $_SERVER - Информация о сервере и среде исполнения
  • $_GET - значения, передаваемые в строке запроса URL, независимо от метода HTTP, используемого для запроса
  • $_POST- Значения, передаваемые в запросе HTTP POST с типами MIME application/x-www-form-urlencodedили multipart/form-dataMIME
  • $_FILES- Файлы, переданные в HTTP-запросе POST с multipart/form-dataтипом MIME
  • $_COOKIE - куки переданы с текущим запросом
  • $_SESSION - переменные сессии хранятся внутри PHP
  • $_REQUEST- Как правило, комбинация $_GETи $_POST, но иногда $_COOKIES. Содержание определяется request_orderдирективой в php.ini.
  • $_ENV - Переменные окружения текущего скрипта
miken32
источник