Ответ 1
Вы можете буквально выбрать любое имя для переменной. "i"
и "foo"
- очевидные варианты, но ""
, "\n"
и "foo.bar"
также допустимы. Причина? Таблица символов PHP - это просто словарь: строковый ключ, содержащий ноль или более байтов, отображается в структурированном значении (называемом zval). Интересно, что есть два способа доступа к этой таблице символов: лексические переменные и динамические переменные.
Лексические переменные - это то, о чем вы читаете в документации по "переменным". Лексические переменные определяют ключ таблицы символов во время компиляции (то есть, когда движок выполняет лексинг и анализ кода). Чтобы этот лексер был простым, лексические переменные начинаются с [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
$
и должны соответствовать регулярному выражению [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
. Сохраняя его простым, этот способ означает, что парсер не должен выяснять, например, является ли $foo.bar
переменной с ключом "foo.bar"
или переменной строкой "foo"
объединенной с константой bar
.
Теперь динамические переменные - то, где это становится интересным. Динамические переменные позволяют получить доступ к тем более необычным именам переменных. PHP вызывает эти переменные переменные. (Мне не нравится это имя, поскольку их противоположность - логически "постоянная переменная", что сбивает с толку. Но я буду называть их переменными здесь.) Основное использование выглядит так:
$a = 'b';
$b = 'SURPRISE!';
var_dump($$a, ${$a}); // both emit a surprise
Переменные переменные анализируются иначе, чем лексические переменные. Вместо определения ключа таблицы символов во время лексирования ключ таблицы символов оценивается во время выполнения. Логика выглядит следующим образом: лексер PHP видит синтаксис переменной переменной ($$a
или, в более общем случае, ${expression}
), синтаксический анализатор PHP откладывает вычисление выражения до момента выполнения, а затем во время выполнения движок использует результат выражения для ввода в таблицу символов. Это немного больше работы, чем лексические переменные, но гораздо более мощный.
Внутри ${}
вы можете иметь выражение, которое оценивает любую последовательность байтов. Пустая строка, нулевой байт, все это. Все идет. Это удобно, например, в heredocs. Это также удобно для доступа к удаленным переменным как переменным PHP. Например, JSON допускает любой символ в имени ключа, и вам может потребоваться доступ к ним как к прямым переменным (а не к элементам массива):
$decoded = json_decode('{ "foo.bar" : 1 }');
foreach ($decoded as $key => $value) {
${$key} = $value;
}
var_dump(${'foo.bar'});
Таким образом, использование переменных переменных аналогично использованию массива в качестве "таблицы символов", например, $array['foo.bar']
, но подход с использованием переменных переменных вполне приемлем и немного быстрее.
добавление
Под "немного быстрее" мы говорим так далеко справа от десятичной запятой, что они практически неразличимы. Только в 10 ^ 8 символических доступах разница превышает 1 секунду в моих тестах.
Set array key: 0.000000119529
Set var-var: 0.000000101196
Increment array key: 0.000000159856
Increment var-var: 0.000000136778
Потеря ясности и условности, вероятно, не стоит этого.
$N = 100000000;
$elapsed = -microtime(true);
$syms = [];
for ($i = 0; $i < $N; $i++) { $syms['foo.bar'] = 1; }
printf("Set array key: %.12f\n", ($elapsed + microtime(true)) / $N);
$elapsed = -microtime(true);
for ($i = 0; $i < $N; $i++) { ${'foo.bar'} = 1; }
printf("Set var-var: %.12f\n", ($elapsed + microtime(true)) / $N);
$elapsed = -microtime(true);
$syms['foo.bar'] = 1;
for ($i = 0; $i < $N; $i++) { $syms['foo.bar']++; }
printf("Increment array key: %.12f\n", ($elapsed + microtime(true)) / $N);
$elapsed = -microtime(true);
${'foo.bar'} = 1;
for ($i = 0; $i < $N; $i++) { ${'foo.bar'}++; }
printf("Increment var-var: %.12f\n", ($elapsed + microtime(true)) / $N);