Variable scope
Оновлено: 11.05.2023
Область видимості змінної - це контекст, в якому вона визначена. Здебільшого всі змінні PHP мають лише одну область видимості. Ця єдина область видимості охоплює включені та необхідні файли. Наприклад:
<?php
$a = 1;
include 'b.inc';
?>
Тут змінна $a буде доступна у включеному скрипті b.inc. Однак, у визначених користувачем функціях вводиться локальна область видимості функції. Будь-яка змінна, що використовується всередині функції, за замовчуванням обмежена локальною областю видимості функції. Наприклад:
<?php
$a = 1; /* global scope */
function test()
{
echo $a; /* reference to local scope variable */
}
test();
?>
Цей скрипт не виведе жодних результатів, оскільки інструкція echo посилається на локальну версію змінної $a, і їй не було присвоєно значення в межах цієї області видимості. Ви можете помітити, що це дещо відрізняється від мови C у тому, що глобальні змінні у C автоматично доступні для функцій, якщо їх не перевизначено локальним визначенням. Це може спричинити деякі проблеми, оскільки люди можуть ненавмисно змінити глобальну змінну. У PHP глобальні змінні мають бути оголошені глобальними всередині функції, якщо вони будуть використовуватися у цій функції.
По-перше, приклад використання global:
Приклад #1 Використання global
<?php
$a = 1;
$b = 2;
function Sum()
{
global $a, $b;
$b = $a + $b;
}
Sum();
echo $b;
?>
Вищенаведений скрипт виведе 3. Оголосивши $a і $b глобальними у функції, всі посилання на обидві змінні будуть посилатися на глобальну версію. Кількість глобальних змінних, якими може маніпулювати функція, не обмежена.
Другий спосіб доступу до змінних з глобальної області видимості - це використання спеціального масиву $GLOBALS, визначеного PHP. Попередній приклад можна переписати так:
Приклад #2 Використання $GLOBALS замість global
<?php
$a = 1;
$b = 2;
function Sum()
{
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}
Sum();
echo $b;
?>
Масив $GLOBALS - це асоціативний масив, у якому ім'я глобальної змінної є ключем, а вміст цієї змінної - значенням елемента масиву. Зверніть увагу, що $GLOBALS існує у будь-якій області видимості, це тому, що $GLOBALS є суперглобальною. Ось приклад, що демонструє потужність суперглобальних змінних:
Приклад #3 Приклад, що демонструє суперглобали та масштаби
<?php
function test_superglobal()
{
echo $_POST['name'];
}
?>
Зауважте:
Використання ключового слова global поза функцією не є помилкою. Його можна використовувати, якщо файл включається зсередини функції.
Ще однією важливою особливістю області видимості змінних є статична змінна. Статична змінна існує тільки в локальній області видимості функції, але вона не втрачає свого значення, коли виконання програми виходить за межі цієї області. Розглянемо наступний приклад:
Приклад #4 Приклад, що демонструє необхідність використання статичних змінних
<?php
function test()
{
$a = 0;
echo $a;
$a++;
}
?>
Ця функція не приносить користі, оскільки при кожному виклику вона встановлює $a в 0 і виводить 0. $a++, який збільшує змінну, не має ніякого сенсу, оскільки при виході з функції змінна $a зникає. Щоб зробити корисну функцію підрахунку, яка не втрачатиме поточний підрахунок, змінну $a оголошено статичною:
Приклад #5 Приклад використання статичних змінних
<?php
function test()
{
static $a = 0;
echo $a;
$a++;
}
?>
Тепер $a ініціалізується тільки при першому виклику функції, а при кожному виклику функції test() вона буде виводити значення $a і збільшувати його.
Статичні змінні також надають один із способів роботи з рекурсивними функціями. Рекурсивна функція - це функція, яка викликає сама себе. Під час написання рекурсивної функції слід бути обережним, оскільки вона може рекурсивно повторюватися до нескінченності. Ви повинні переконатися, що у вас є адекватний спосіб завершення рекурсії. Наступна проста функція рекурсивно рахує до 10, використовуючи статичну змінну $count для визначення моменту зупинки:
Приклад #6 Статичні змінні з рекурсивними функціями
<?php
function test()
{
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
$count--;
}
?>
Статичним змінним можна присвоювати значення, які є результатом константних виразів, але динамічні вирази, такі як виклики функцій, призведуть до помилки розбору.
Приклад #7 Оголошення статичних змінних
<?php
function foo(){
static $int = 0; // correct
static $int = 1+2; // correct
static $int = sqrt(121); // wrong (as it is a function)
$int++;
echo $int;
}
?>
Починаючи з версії PHP 8.1.0, коли метод, що використовує статичні змінні, успадковується (але не перевизначається), успадкований метод тепер має спільні статичні змінні з батьківським методом. Це означає, що статичні змінні в методах тепер поводяться так само, як і статичні властивості.
Приклад #8 Використання статичних змінних в успадкованих методах
<?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), prior to PHP 8.1.0 int(1)
var_dump(Bar::counter()); // int(4), prior to PHP 8.1.0 int(2)
?>
Зауважте:
Статичні оголошення обробляються під час компіляції.
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);
?>
Аналогічна поведінка застосовується до оператора static. Посилання не зберігаються статично:
<?php
function &get_instance_ref() {
static $obj;
echo 'Static object: ';
var_dump($obj);
if (!isset($obj)) {
$new = new stdClass;
// Assign a reference to the static variable
$obj = &$new;
}
if (!isset($obj->property)) {
$obj->property = 1;
} else {
$obj->property++;
}
return $obj;
}
function &get_instance_noref() {
static $obj;
echo 'Static object: ';
var_dump($obj);
if (!isset($obj)) {
$new = new stdClass;
// Assign the object to the static variable
$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();
?>
Цей приклад демонструє, що при присвоюванні посилання на статичну змінну, воно не запам'ятовується при повторному виклику функції &get_instance_ref().