Я пытаюсь изменить глобальное значение, которое в данный момент является ссылкой на другое значение. Моя текущая реализация не работает:
$anna = array('Name' => "Anna");
$bella = array('Name' => "Bella");
$girl = &$anna;
function change(){
global $girl, $bella;
$girl = &$bella;
// $girl['Name'] is now 'Bella'
output_girl(); // This still outputs 'Anna'
}
function output_girl(){
global $girl;
echo $girl['Name'];
}
change();
// What do we want to see?
echo $anna['Name']; // Should still be 'Anna'
echo $bella['Name']; // Should still be 'Bella'
echo $girl['Name']; // Should now be 'Bella' (in above example this will still be 'Anna')
Важные примечания:
$girl = &change();
не является решением моей проблемы. Важно, чтобы глобальный $girl
был изменен до завершения функции change()
. (Причина в том, что это проблема внутри сложного проекта с вызовами вложенных функций, которые хотят использовать одни и те же глобальные переменные.)$GLOBALS
. Однако меня особенно интересует, можно ли решить эту проблему с помощью ключевого слова global
, как показано в примере кода. А если нет, узнать, почему это невозможно в PHP.&
чтобы сделать это $girl = $bella
тоже не решение. Глобальный объект $girl всегда должен быть ссылкой на исходные массивы, определенные в начале скрипта. Мы никогда не сможем делать копии/разрушать ссылки.Вопрос: невозможно ли это в PHP, когда я использую ключевое слово global
? Если да, может ли кто-нибудь объяснить, почему? Если нет, может ли кто-нибудь объяснить, как заставить это работать?
@RobEyre Вы правы, и я знаю. Когда я создаю глобальную переменную $foo = 1
и затем присваиваю ей значение global $foo; $foo = 2;
, глобальная переменная действительно изменилась. Что мне интересно, так это то, почему я не могу этого сделать, если $foo является ссылкой на переменную, и я хочу изменить то, на что она ссылается.
We cannot ever make copies / break references
...почему? Это кажется несколько произвольным требованием.
Я понимаю, что это существующая кодовая база, и она может оказаться сложной... но использование глобальных переменных всегда сопряжено с потенциалом создания непредсказуемого хаоса и утомительной отладки. Можете ли вы рассмотреть возможность рефакторинга? Пример, который вы создали, на самом деле представляет собой просто тривиальное переназначение и эхо, поэтому трудно увидеть, каков будет потенциал рефакторинга. На самом деле ваши примеры функций на самом деле сводятся к следующему: 3v4l.org/uQT0s. Я предполагаю, что реальность более сложна, но, не видя лучшего примера, трудно понять, где ее можно исправить, ничего не сломав.
Нельзя ли использовать в качестве глобальных переменных объекты вместо массивов? Хорошо работает с объектами: onlinephp.io/c/f45d2
@ADyson Я понимаю твое желание, но это, по сути, языковой вопрос. Я хочу выяснить, может ли язык сделать это с помощью ключевого слова global
. Не ищу альтернативного пути достижения аналогичного результата.
@MichalHynčica Спасибо за комментарий! К сожалению, для моего проекта это не вариант. Я специально пытаюсь выяснить, способен ли язык PHP сделать это с помощью ключевого слова global
для переменных, которые ссылаются на другую переменную.
Я думаю, вы уже показали, что это невозможно сделать, и я думаю, Роб Эйр и Михал Хинчица поняли, почему. И вы уже исключили все очевидные обходные пути. Так куда еще нам обратиться за решением? Разве вы просто не хотите узнать причину, а не искать код, который будет работать?
@ADyson Мои знания PHP не на 100%, поэтому я еще не уверен, что это невозможно сделать. Возможно, есть способ изменить то, на что ссылается глобальная ссылочная переменная, о котором ни я, ни вы не знаете. Это мой вопрос. Мой пример не «показывает, что это невозможно сделать». Он просто показывает реализацию, которая не работает.
Невозможно получить ссылку на глобальную область видимости с помощью ключевого слова global. Он просто предоставляет новый локальный символ с тем же именем, и изменение того, на что указывает этот локальный символ, не может изменить символ в глобальной области видимости. Я не думаю, что есть что добавить к ответу @MichalHynčica, но если вы действительно хотите начать копаться в исходном коде PHP, то ключевое слово global
реализовано здесь: github.com/php/php-src/blob/master/ Zend/zend_compile.c#L5398
В PHP это невозможно сделать, используя только ключевое слово global
.
Это потому, что references
в php — это не то же самое, что pointers
, как мы их знаем, например, из C/C++.
Когда вы выполняете $girl = &$anna;
в строке 3, адрес переменной $anna
не сохраняется в переменной $girl. Скорее, новый символ $girl
создается для ссылки на ту же переменную, что и символ $anna
.
Когда вы делаете global $girl, $bella;
внутри функции. Создаются новые локальные символы $girl
и $bella
, которые ссылаются на свой глобальный аналог.
Когда вы делаете $girl = &$bella;
внутри функции, локальный символ $girl
заменяется на ссылку на ту же переменную, что и локальный символ $bella
, но глобальный символ $girl
не изменяется. После этого оба локальных символа $girl
и $bella
ссылаются на один и тот же глобальный символ $bella
.
Спасибо! Это ясно объяснило, почему концепция ссылок не позволяет этого сделать с помощью ключевого слова global
. Я очень ценю это!
Проблема в том, что ваша глобальная переменная все еще ссылается на $anna.
вы можете проверить это, добавив print_r($GLOBALS['girl']):
function change(){
global $girl, $bella;
$girl = &$bella;
print_r($girl); //referencing to bella
print_r($GLOBALS['girl']); //referencing to Anna
output_girl(); // This still outputs 'Anna'
}
}
Я не знаю правильного решения этой проблемы, но есть способ обойти эту проблему. Просто добавьте:
function change(){
global $girl, $bella;
$GLOBALS['girl'] = &$bella;
output_girl(); // now is 'Bella'
}
Помните, что использование
global
создает ссылку на глобальную переменную, а не на реальную переменную. Это может объяснить то, что вы видите. Из документации (php.net/manual/en/…): «настоящая глобальная переменная, импортированная внутри области функции с помощью глобального оператора, фактически создает ссылку на глобальную переменную»