Посилання · PHP Manual
Що таке посилання
Посилання в PHP - це спосіб звертатися до одного й того самого вмісту змінної під різними іменами. Вони не схожі на вказівники C: з ними не можна робити арифметику вказівників, вони не є реальними адресами памʼяті тощо. Докладніше див. Чим посилання не є. Натомість це псевдоніми в таблиці символів. Зверніть увагу, що в PHP імʼя змінної та вміст змінної - різні речі, тож один і той самий вміст може мати різні імена. Найближча аналогія - імена файлів і файли в Unix: імена змінних - це записи в каталозі, а вміст змінної - сам файл. Посилання можна порівняти з жорсткими посиланнями (hardlink) у файловій системі Unix.
Що роблять посилання
З посиланнями виконують три базові операції: присвоєння за посиланням, передавання за посиланням і повернення за посиланням. Цей розділ дає вступ до цих операцій із посиланнями на подальше читання.
Присвоєння за посиланням
Перша з них: посилання PHP дозволяють зробити так, щоб дві змінні вказували на один вміст. Тобто коли ви пишете:
<?php
$a =& $b;
це означає, що $a і $b вказують на один і той самий вміст.
$aі$bтут цілком рівноправні.$aне вказує на$bі навпаки.$aі$bвказують на одне й те саме місце.
Якщо ви присвоюєте, передаєте або повертаєте за посиланням невизначену змінну, її буде створено.
Приклад: Використання посилань із невизначеними змінними
<?php function foo(&$var) {} foo($a); // $a «створюється» і отримує значення null $b = array(); foo($b['b']); var_dump(array_key_exists('b', $b)); // bool(true) $c = new stdClass(); foo($c->d); var_dump(property_exists($c, 'd')); // bool(true)
Той самий синтаксис можна використовувати з функціями, які повертають посилання:
<?php
$foo =& find_var($bar);
Використання того самого синтаксису з функцією, яка не повертає за посиланням, призведе до помилки, як і використання його з результатом оператора new. Хоча обʼєкти передаються як вказівники, це не те саме, що посилання, як пояснено в розділі Обʼєкти і посилання.
Попередження
Якщо ви присвоюєте посилання змінній, оголошеній як
globalусередині функції, посилання буде видиме лише всередині функції. Цього можна уникнути, використавши масив$GLOBALS.Приклад: Посилання на глобальні змінні всередині функцій
<?php $var1 = "Example variable"; $var2 = ""; function global_references($use_globals) { global $var1, $var2; if (!$use_globals) { $var2 =& $var1; // видиме лише всередині функції } else { $GLOBALS["var2"] =& $var1; // видиме також у глобальному контексті } } global_references(false); echo "var2 is set to '$var2'\n"; // var2 is set to '' global_references(true); echo "var2 is set to '$var2'\n"; // var2 is set to 'Example variable'Думайте про
global $var;як про скорочення для$var =& $GLOBALS['var'];. Тому присвоєння$varіншого посилання змінює лише посилання локальної змінної.
Якщо ви присвоюєте значення змінній із посиланнями в конструкції
foreach, посилання теж змінюються.Приклад: Посилання і конструкція foreach
<?php $ref = 0; $row =& $ref; foreach (array(1, 2, 3) as $row) { // Робимо щось } echo $ref; // 3 - останній елемент масиву, який перебирали
Хоча це не є строго присвоєнням за посиланням, вирази, створені мовною конструкцією array(), теж можуть поводитися подібно, якщо перед елементом масиву, який додається, поставити &. Приклад:
<?php
$a = 1;
$b = array(2, 3);
$arr = array(&$a, &$b[0], &$b[1]);
$arr[0]++;
$arr[1]++;
$arr[2]++;
/* $a == 2, $b == array(3, 4); */
var_dump($a);
var_dump($b);
Проте зверніть увагу, що посилання всередині масивів потенційно небезпечні. Звичайне (не за посиланням) присвоєння, де праворуч стоїть посилання, не перетворює ліву частину на посилання, але посилання всередині масивів зберігаються під час таких звичайних присвоєнь. Це стосується й викликів функцій, куди масив передається за значенням. Приклад:
<?php
/* Присвоєння скалярних змінних */
$a = 1;
$b =& $a;
$c = $b;
$c = 7; // $c не є посиланням; $a і $b не змінюються
print "a = {$a}; b = {$b}; c = {$c}\n\n";
/* Присвоєння змінних-масивів */
$arr = array(1);
$a =& $arr[0]; // $a і $arr[0] належать до однієї множини посилань
$arr2 = $arr; // Це не присвоєння за посиланням!
$arr2[0]++;
/* $a == 2, $arr == array(2) */
/* Вміст $arr змінився, хоча він не є посиланням! */
print "a = {$a}\n";
var_dump($arr);
var_dump($arr2);
Інакше кажучи, поведінка масивів щодо посилань визначається поелементно; поведінка окремих елементів щодо посилань не повʼязана зі статусом посилання самого масиву-контейнера.
Передавання за посиланням
Друге, що роблять посилання, - передають змінні за посиланням. Це досягається тим, що локальна змінна у функції та змінна в області виклику посилаються на один вміст. Приклад:
<?php
function foo(&$var)
{
$var++;
}
$a = 5;
foo($a);
print $a;
зробить $a рівним 6. Так стається, бо у функції foo змінна $var посилається на той самий вміст, що й $a. Докладніше про це читайте в розділі передавання за посиланням.
Повернення за посиланням
Третє, що вміють посилання, - повернення за посиланням.
Чим посилання не є
Як уже сказано, посилання не є вказівниками. Тому така конструкція не зробить того, чого ви очікуєте:
<?php
function foo(&$var)
{
$var =& $GLOBALS["baz"];
}
foo($bar);
Відбувається ось що: $var у foo буде звʼязана з $bar у місці виклику, але потім переповʼязана з $GLOBALS["baz"]. Немає способу звʼязати $bar в області виклику з чимось іншим за допомогою механізму посилань, оскільки $bar недоступна у функції foo (вона представлена через $var, але $var має лише вміст змінної, а не звʼязок «імʼя-значення» в таблиці символів того, хто викликає). Щоб посилатися на змінні, вибрані функцією, можна скористатися поверненням посилань.
Передавання за посиланням
Змінну можна передати у функцію за посиланням, щоб функція могла її змінити. Синтаксис такий:
<?php
function foo(&$var)
{
$var++;
}
$a = 5;
foo($a);
print $a; // тут $a дорівнює 6
Під час виклику функції знак посилання не ставиться - тільки в оголошенні функції. Самого оголошення функції достатньо, щоб аргумент правильно передавався за посиланням.
За посиланням можна передавати таке:
-
Змінні, тобто
foo($a) -
Посилання, повернуті з функцій, тобто:
<?php function foo(&$var) { $var++; print $var; } function &bar() { $a = 5; return $a; } foo(bar());Докладніше про повернення за посиланням.
Жодних інших виразів передавати за посиланням не слід, бо результат не визначений. Наприклад, такі приклади передавання за посиланням є недопустимими:
<?php
function foo(&$var)
{
$var++;
}
function bar() // Зверніть увагу на відсутній &
{
$a = 5;
return $a;
}
foo(bar()); // Видає notice
foo($a = 5); // Вираз, а не змінна
foo(5); // Видає fatal error
class Foobar {}
foo(new Foobar()) // Видає notice починаючи з PHP 7.0.7
// Notice: Only variables should be passed by reference
Повернення посилань
Повернення за посиланням корисне, коли ви хочете використати функцію для визначення того, з якою змінною має бути звʼязане посилання. Не використовуйте повернення за посиланням для підвищення продуктивності. Рушій оптимізує це автоматично. Повертайте посилання лише тоді, коли на це є вагома технічна причина. Щоб повертати посилання, використовуйте такий синтаксис:
<?php
class Foo
{
public $value = 42;
public function &getValue()
{
return $this->value;
}
}
$obj = new Foo();
$myValue = &$obj->getValue(); // $myValue - це посилання на $obj->value, яке дорівнює 42
$obj->value = 2;
echo $myValue; // Виводить нове значення $obj->value, тобто 2
У цьому прикладі буде встановлено властивість обʼєкта, повернену функцією getValue, а не її копію, як було б без синтаксису посилань.
На відміну від передавання параметрів, тут
&треба вказати в обох місцях - щоб позначити, що ви хочете повернути посилання, а не копію, і щоб позначити, що для$myValueмає бути виконане звʼязування посилання, а не звичайне присвоєння.
Якщо ви спробуєте повернути посилання з функції за допомогою синтаксису
return ($this->value);, це не спрацює, бо ви намагаєтеся повернути за посиланням результат виразу, а не змінну. З функції можна повертати за посиланням тільки змінні - і нічого іншого.
Щоб скористатися поверненим посиланням, треба застосувати присвоєння за посиланням:
<?php
function &collector()
{
static $collection = array();
return $collection;
}
$collection = &collector();
// Тепер $collection - це змінна-посилання, яка посилається на статичний масив усередині функції
$collection[] = 'foo';
print_r(collector());
Результат виконання цього прикладу:
Array
(
[0] => foo
)
Якщо присвоєння зроблено без символу
&, наприклад$collection = collector();, змінна$collectionотримає копію значення, а не посилання, повернене функцією.
Щоб передати повернене посилання в іншу функцію, яка очікує посилання, можна використати такий синтаксис:
<?php
function &collector()
{
static $collection = array();
return $collection;
}
array_push(collector(), 'foo');
Зверніть увагу, що
array_push(&collector(), 'foo');не спрацює, це призводить до fatal error.
Знищення посилань
Коли ви знищуєте посилання через unset, ви лише розриваєте звʼязок між імʼям змінної та її вмістом. Це не означає, що вміст змінної буде знищено. Наприклад:
<?php
$a = 1;
$b =& $a;
unset($a);
var_dump($a);
var_dump($b);
не знищить $b, а лише $a.
Знову ж таки, тут може бути корисною аналогія з Unix-викликом unlink.
Як розпізнати посилання
Багато синтаксичних конструкцій PHP реалізовано через механізми посилань, тож усе сказане тут про звʼязування посилань стосується й цих конструкцій. Деякі з них, як-от передавання й повернення за посиланням, згадані вище. Інші конструкції, що використовують посилання:
Посилання global
Коли ви оголошуєте змінну як global $var, ви насправді створюєте посилання на глобальну змінну. Тобто це те саме, що:
<?php
$var =& $GLOBALS["var"];
Це також означає, що знищення $var через unset не знищить глобальну змінну.
Перекладаємо з офіційної документації, розділ за розділом, і не ховаємо недоперекладене. Помітили неточність у терміні чи реченні: напишіть, виправимо.