MidТеория2 min

Область видимости и ссылки

global, static, ссылки, pass by value vs reference

Область видимости (Scope)

В PHP функции имеют собственную область видимости. Переменные извне НЕ видны внутри функции автоматически.

<?php
declare(strict_types=1);

$globalVar = 'Hello';

function test(): void
{
    // $globalVar is NOT accessible here!
    // echo $globalVar;  // Warning: Undefined variable
}

test();

Ключевое слово global

<?php
declare(strict_types=1);

$counter = 0;

function increment(): void
{
    global $counter;  // Import global variable
    $counter++;
}

increment();
increment();
echo $counter;  // 2

// Alternative: use $GLOBALS superglobal
function incrementAlt(): void
{
    $GLOBALS['counter']++;
}

Запомни: Использование global считается плохой практикой. Вместо этого передавайте значения через параметры и возвращайте результат. Суперглобальные массивы ($_GET, $_POST, $_SERVER и т.д.) доступны везде без global.

Статические переменные

<?php
declare(strict_types=1);

function getNextId(): int
{
    static $id = 0;  // Initialized ONCE, persists between calls
    return ++$id;
}

echo getNextId();  // 1
echo getNextId();  // 2
echo getNextId();  // 3

// Static variable initializer (PHP 8.1+: can use expressions)
function getConnection(): PDO
{
    static $pdo = null;
    if ($pdo === null) {
        $pdo = new PDO('sqlite::memory:');
    }
    return $pdo;
}

// PHP 8.3+: static initializer with new
function getLogger(): Logger
{
    static $logger = new Logger();  // PHP 8.3+
    return $logger;
}

Ловушка экзамена: Статическая переменная инициализируется только ОДИН раз при первом вызове функции. Значение сохраняется между вызовами. С PHP 8.3 в инициализаторе можно использовать new и другие выражения.

Передача по значению vs по ссылке

По значению (по умолчанию)

<?php
declare(strict_types=1);

function doubleValue(int $x): void
{
    $x *= 2;
    echo "Inside: $x\n";
}

$num = 5;
doubleValue($num);     // Inside: 10
echo "Outside: $num";  // Outside: 5 — NOT changed!

По ссылке (&)

<?php
declare(strict_types=1);

function doubleReference(int &$x): void
{
    $x *= 2;
}

$num = 5;
doubleReference($num);
echo $num;  // 10 — CHANGED!

// Arrays are passed by value (copy-on-write)
function addItem(array $arr): void
{
    $arr[] = 'new';  // Modifies local copy only
}

$items = ['a', 'b'];
addItem($items);
echo count($items);  // 2 — unchanged

// Pass array by reference
function addItemRef(array &$arr): void
{
    $arr[] = 'new';
}

addItemRef($items);
echo count($items);  // 3 — modified!

Ловушка экзамена: Массивы передаются по значению (с copy-on-write оптимизацией). Копия создаётся только при модификации. Объекты всегда передаются по "идентификатору" — переменная содержит ссылку на объект, но сама передаётся по значению.

Объекты и ссылки

<?php
declare(strict_types=1);

class Counter
{
    public int $value = 0;
}

// Objects: variable holds an object identifier (handle)
function incrementCounter(Counter $counter): void
{
    $counter->value++;  // Modifies THE SAME object
}

$c = new Counter();
incrementCounter($c);
echo $c->value;  // 1 — changed!

// BUT: reassigning the variable doesn't affect original
function replaceCounter(Counter $counter): void
{
    $counter = new Counter();  // Local variable now points to new object
    $counter->value = 100;
}

replaceCounter($c);
echo $c->value;  // Still 1! Not 100!

// With reference: reassignment DOES affect original
function replaceCounterRef(Counter &$counter): void
{
    $counter = new Counter();
    $counter->value = 100;
}

replaceCounterRef($c);
echo $c->value;  // 100 — replaced!

Ссылки в деталях

<?php
declare(strict_types=1);

// Creating a reference
$a = 10;
$b = &$a;  // $b is a reference to $a

$b = 20;
echo $a;  // 20 — $a changed because $b is a reference

// Unsetting a reference
unset($b);  // Only removes the reference, not the value
echo $a;     // 20 — still exists

// References and arrays
$arr = [1, 2, 3];
$ref = &$arr[1];  // Reference to second element
$ref = 99;
echo $arr[1];  // 99

// Global creates a reference
$globalVar = 'original';
function modifyGlobal(): void
{
    global $globalVar;  // Creates local reference to global
    $globalVar = 'modified';
}
modifyGlobal();
echo $globalVar;  // 'modified'

// Functions returning references
function &getRef(array &$arr, int $key): mixed
{
    return $arr[$key];
}

$data = [10, 20, 30];
$ref = &getRef($data, 1);
$ref = 99;
echo $data[1];  // 99

Ловушка экзамена: unset() на ссылке удаляет только ссылочную связь, а НЕ значение. $a = 10; $b = &$a; unset($b); — переменная $a со значением 10 остаётся нетронутой. unset уничтожает только переменную $b.


Проверь себя

Что произойдёт: `$a = 1; $b = &$a; unset($a); echo $b;`?

Что выведет `echo getNext(); echo getNext();` при `static $n = 0; return ++$n;`?

Какие массивы доступны в функции без `global`?

Объекты в PHP передаются...

Что выведет код? ```php $x = 5; function test(): void { echo $x; } test(); ```