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().