Область видимости переменной — контекст, в котором определили переменную. В PHP предусмотрели область видимости функции и глобальную область видимости. Переменная, которую определили за пределами функции, ограничивается глобальной областью видимости. Код включаемого файла наследует ту же область видимости переменных, что и строка, на которой включается файл.
Пример #1 Пример глобальной области видимости переменных
<?php
$a = 1;
include 'b.inc'; // Код внутри файла b.inc получит доступ к переменной $a
?>
Переменная, которую создали внутри именованной или анонимной функции, ограничивается областью видимости тела функции. Стрелочные функции вместо этого связывают переменные родительской области видимости и переменные становятся доступны в теле стрелочной функции. При включении файла внутри функции переменные, которые содержатся во включаемом файле, будут доступны так, как будто переменные определили внутри вызывающей функции.
Пример #2 Пример локальной области видимости переменных
<?php
$a = 1; // Определяем переменную $a в глобальной области видимости
function test()
{
echo $a; // Переменная $a не определена, поскольку конструкция echo указывает на локальную версию переменной $a
}
?>
До PHP 8.0.0 при вызове функции приведённый пример сгенерирует ошибку уровня E_NOTICE
о неопределённой переменной, а в новых версиях ошибку уровня E_WARNING
.
Причина ошибки состоит в том, что языковая конструкция echo указывает на локальную версию переменной
$a, а переменной не присвоили значение в локальной области видимости.
Обратите внимание, поведение переменных отличается от языка C в том, что глобальные переменные в C
автоматически доступны функциям, если только глобальную переменную
не перезаписали локальным определением. Несовпадение поведения иногда вызывает проблемы
при непреднамеренном изменении глобальной переменной.
В PHP глобальные переменные потребуется объявить глобальными
внутри функции, если функция будет использовать эти переменные.
global
Ключевое слово global
связывает переменную
глобальной области видимости с локальной областью действия переменной. Ключевое слово
принимает отдельную переменную или список переменных. Ключевое слово создаст
локальную переменную, которая ссылается на глобальную переменную с тем же названием.
Ключевое слово создаст переменную в глобальной области видимости со значением null
,
если переменную не установили в глобальной области действия переменной.
Пример #3 Пример поведения ключевого слова global
<?php
$a = 1;
$b = 2;
function Sum()
{
global $a, $b;
$b = $a + $b;
}
Sum();
echo $b;
?>
Результат выполнения приведённого примера:
3
При объявлении переменных $a и $b глобальными внутри функции ссылки на эти переменные будут указывать на глобальную версию. Количество глобальных переменных, которыми умеет манипулировать функция, не ограничивается.
Второй способ доступа к переменным глобальной области видимости — суперглобальный PHP-массив $GLOBALS. Перепишем предыдущий пример так:
Пример #4 Работа с суперглобальной переменной $GLOBALS вместо ключевого слова global
<?php
$a = 1;
$b = 2;
function Sum()
{
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}
Sum();
echo $b;
?>
Массив $GLOBALS — ассоциативный массив, в котором ключ представляет название глобальной переменной, а значение элемента массива — содержимое переменной. Обратите внимание, суперглобальная переменная $GLOBALS существует в любой области видимости, причина состоит в том, что $GLOBALS — суперглобальная переменная. Пример ниже показывает силу суперглобальных переменных:
Пример #5 Суперглобальные переменные и область видимости
<?php
function test_superglobal()
{
echo $_POST['name'];
}
?>
Замечание: Не будет ошибкой, если указать ключевое слово
global
вне функции, например в файле, который включается внутри функции другого файла.
static
Другая важная особенность области видимости переменной — статическая переменная. Статическая переменная существует только в локальной области видимости функции, но не теряет своего значения, когда выполнение программы выходит из этой области видимости. Рассмотрим следующий пример:
Пример #6 Пример показывает, когда требуется статическая переменная
<?php
function test()
{
$a = 0;
echo $a;
$a++;
}
?>
Эта функция бесполезна, поскольку при каждом вызове
устанавливает для переменной $a значение 0
и выводит 0
. Инкремент переменной $a++
здесь не играет роли, поскольку при выходе из функции переменная
$a исчезает. Чтобы написать полезную
функцию подсчёта, которая не потеряет текущего значения счётчика,
переменную $a объявляют статической:
Пример #7 Пример со статической переменной
<?php
function test()
{
static $a = 0;
echo $a;
$a++;
}
?>
Теперь функция проинициализирует переменную $a только
при первом вызове, а при каждом вызове функция test()
будет выводить значение переменной $a, а затем инкрементировать
значение.
Со статическими переменными также работают в рекурсивных функциях. Следующая функция рекурсивно считает до 10, а статическая переменная $count помогает определить момент остановки:
Пример #8 Статические переменные и рекурсивные функции
<?php
function test()
{
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
$count--;
}
?>
До PHP 8.3.0 статические переменные разрешалось инициализировать только константными выражениями. С PHP 8.3.0 также разрешили динамические выражения наподобие вызовов функций:
Пример #9 Объявление статических переменных
<?php
function foo()
{
static $int = 0; // Правильно
static $int = 1 + 2; // Правильно
static $int = sqrt(121); // Правильно с PHP 8.3.0
$int++;
echo $int;
}
?>
Статические переменные внутри анонимных функций сохраняются только внутри этого конкретного экземпляра функции. Статическая переменная переинициализируется, если анонимная функция воссоздаётся при каждом вызове.
Пример #10 Статические переменные в анонимных функциях
<?php
function exampleFunction($input)
{
$result = (static function () use ($input) {
static $counter = 0;
$counter++;
return "Функция получила значение: $input, счётчик: $counter\n";
});
return $result();
}
// Вызов функции exampleFunction пересоздаст анонимную функцию, поэтому статическая
// переменная не сохранит значение
echo exampleFunction('A'); // Выводит: Функция получила значение: A, счётчик: 1
echo exampleFunction('B'); // Выводит: Функция получила значение: B, счётчик: 1
?>
Начиная с PHP 8.1.0 при наследовании, но не переопределении, метода со статическими переменными унаследованный метод будет использовать статические переменные совместно с родительским методом. Поэтому статические переменные в методах теперь ведут себя как статические свойства класса.
Начиная PHP 8.3.0 статические переменные разрешили инициализировать произвольными выражениями. Поэтому статическую переменную получится инициализировать, например, вызовом метода.
Пример #11 Статические переменные в унаследованных методах
<?php
class Foo
{
public static function counter()
{
static $counter = 0;
$counter++;
return $counter;
}
}
class Bar extends Foo {}
var_dump(Foo::counter()); // int(1)
var_dump(Foo::counter()); // int(2)
var_dump(Bar::counter()); // int(3), до PHP 8.1.0 int(1)
var_dump(Bar::counter()); // int(4), до PHP 8.1.0 int(2)
?>
global
)
и статическими (static
) переменными
PHP использует модификаторы переменных
static и
global как
ссылки. Например, реальная
глобальная переменная, которую внедрили в область видимости функции
через ключевое слово global
, в действительности создаёт
ссылку на глобальную переменную. Это приводит к неожиданному
поведению, как показывает следующий пример:
<?php
function test_global_ref()
{
global $obj;
$new = new stdClass();
$obj = &$new;
}
function test_global_noref()
{
global $obj;
$new = new stdClass();
$obj = $new;
}
test_global_ref();
var_dump($obj);
test_global_noref();
var_dump($obj);
?>
Результат выполнения приведённого примера:
NULL object(stdClass)#1 (0) { }
Аналогично ведёт себя и инструкция static
. Ссылки не
хранятся статично:
<?php
function &get_instance_ref()
{
static $obj;
echo 'Статический объект: ';
var_dump($obj);
if (!isset($obj)) {
$new = new stdClass();
// Присвоить ссылку статической переменной
$obj = &$new;
}
if (!isset($obj->property)) {
$obj->property = 1;
} else {
$obj->property++;
}
return $obj;
}
function &get_instance_noref()
{
static $obj;
echo 'Статический объект: ';
var_dump($obj);
if (!isset($obj)) {
$new = new stdClass();
// Присвоить объект статической переменной
$obj = $new;
}
if (!isset($obj->property)) {
$obj->property = 1;
} else {
$obj->property++;
}
return $obj;
}
$obj1 = get_instance_ref();
$still_obj1 = get_instance_ref();
echo "\n";
$obj2 = get_instance_noref();
$still_obj2 = get_instance_noref();
?>
Результат выполнения приведённого примера:
Статический объект: NULL Статический объект: NULL Статический объект: NULL Статический объект: object(stdClass)#3 (1) { ["property"]=> int(1) }
Пример показывает, что при назначении ссылки статической
переменной эта переменная не запоминается,
при повторном вызове функции &get_instance_ref()
.