Все еще, стало быть, пытаюсь осилить главу "Language Basics" книжечки "Programming PHP" от O'Reilly.
Дошел до "Variable Variables". Всего два предложения на тему:
Так и так, вы можете обращаться к значению переменной, чье имя хранится в другой переменной, используя дополнительный "$":
$var = "lol";
$$var = "kek";
echo $lol; // outputs "kek"
За сим все, едем дальше.
И вот такое "раскрытие" темы встречаю в каждой первой книге по PHP (штуки три, все-таки, ранее прочитал, было это, правда, лет пять-семь назад). Да что там в книгах, даже в официальной документации по PHP пишут практически то же самое.
Мне же никогда не нравилось ни название "переменные переменных", ни эта запись "$$". Сложно, непонятно, да и просто криво как-то.
Почему нельзя заместо "variable variables" использовать какое-нибудь другое определение, что-то вроде "динамическое именование переменных". И до кучи применить т. н. complex (curly) syntax. Тогда все встает на свои места:
В PHP имена переменных можно назначать динамически,
$var = "lol";
${$var} = "kek";
echo $lol; // outputs "kek"
Логично и понятно, создали переменную, подставив в качестве ее имени строку, которая хранилась в другой переменной. Никаких там $$
и переменных переменных переменных.
Вообще, что такое $
. Это shorthand от ${}
. Который, в свою очередь, является оператором. Который говорит следующее: "вернуть значение переменной, чье имя - следующая строка".
Строка в значении строка. Буквальном. Т. е. без всего этого "название переменной должно начинаться с a-Z или подчеркивания". Название переменной может быть хоть "123lol". Или даже "1 ^_^ !!!". Никакой суть разницы.
Объявим четыре переменные,
$var = 'foo';
${'my'} = 'foo';
${'123lol'} = 'foo';
${'1 ^_^ !!!'} = 'foo';
И задампим эти объявленные переменные с помощью get_defined_vars()
, где увидим следующее:
'var' => string(3) "foo"
'my' => string(3) "foo"
'123lol' => string(3) "foo"
'1 ^_^ !!!' => string(3) "foo"
Т. е. да, никакой разницы, название переменной - любая строка. А $my
- это ровно то же самое, что и ${'my'}
.
Ограничение на используемые символы в названии переменных накладывает не ${}
, а именно что сокращенная запись без скобок. Ну просто потому что с переменными работать приходится постоянно, каждый раз писать ${'i'}
замучаешься, а $i
вроде сподручней будет. Откуда и возникают уже ограничения. Почему нельзя использовать пробел - понятно, парсер споткнется. А вот все остальное (нельзя начинать с цифры, использовать спец. символы) - лишь дань общепринятым стандартам в именовании переменных. Т. е. в других языках эти ограничения взялись не с потолка (например, где нет префиксов перед переменной, запрет на цифирные имена совершенно очевиден, т. к. в противном случае не поймешь, 123 - это переменная или integer), а вот в пхп просто чтобы было "как у всех", технических ограничений никаких, благо $123 с 123 не спутаешь. Но в целом все сделали правильно, единообразие оно всегда хорошо.
Другими словами, используем полную запись ${}
- имя переменной любая строка. Используем просто $
- уже надо укладываться в ограничения на символы.
Т. е. мы можем объявить переменную, как ${'i'}, и далее в коде обращаться к ней, как к $i
. А можем объявить переменную, как ${':3'}
, что вполне себе валидное имя, но обращаться к ней через просто символ доллара - уже нет, не можем.
В скобках, стало быть, допустимо любое выражение, которое возвращает строку, не только другая переменная.
$var = 'lol';
${$var} = 'foo'; // just the same as $lol = 'foo';
${'some'} = 'foo'; // $some = foo;
${$var . '_some'} = 'foo'; // $lol_some = 'foo';
${date('M')} = 'foo'; // $Dec = 'foo';
Т. е. "переменные переменных" - это лишь частный случай динамического создание имен переменных.
Где сие может понадобится?
Предположим, у нас есть два массива.
$cars = array();
$books = array();
И надо вывести первый или второй, в зависимости от GET параметра, который может быть либо q=cars
, либо q=books
.
Ничего сложного и без динамических переменных,
$cars = array();
$books = array();
if (!empty($_GET['q'])) {
switch ($_GET['q']) {
case 'cars':
// output $cars
break;
case 'books';
// output $books
break;
}
}
Ну т. е. либо свитч, либо нагромождение ифов. А если этих массивов не два, а сто? Не писать же сто же условий? И динамические имена переменных вполне себе решение в данном случае:
$cars = array();
$books = array();
if (!empty($_GET['q'])) {
// output ${$_GET['q']};
}
Естественно, надо будет нагрузить дополнительными проверками и валидацией, но сама суть понятна.
Или вот еще пример. Есть страничка с несколькими GET параметрами, index.php?user=nick&id=123&page=2
. В коде это надо как-то распарсить, и под каждый аргумент создать свою переменную, чтобы дальше можно было оперировать $user
, $id
и $page
. Можно так:
if (!empty($_GET['user'])) $user = $_GET['user'];
if (!empty($_GET['id'])) $id = $_GET['id'];
if (!empty($_GET['page'])) $page = $_GET['page'];
А можно через динамическое создание переменных:
foreach(array('user', 'id', 'page') as $value) {
if (!empty($_GET[$value])) ${$value} = $_GET[$value];
}
И там и там три строчки, но если переменных надо создать уже несколько десятков, то разница становится очевидна.
Хотя, конечно же, в этих примерах прекрасно можно было бы обойтись и без динамических переменных, а использовать те же ассоциативные массивы. Тут уж кому как удобней, так-то. Лично я при прочих равных выберу именно ассоциативные массивы.
Комментарии
мне они пригождались разве что для обращения к свойсвам объектов
Еще, как я понял, это повсеместно используется для обфускации кода.