Есть ли причина для такого поведения / реализации?
Пример:
$array = array("index_of_an_array" => "value");
class Foo {
private $index_of_an_array;
function __construct() {}
}
$foo = new Foo();
$array = (array)$foo;
$key = str_replace("Foo", "", array_keys($array)[0]);
echo $array[$key];
Дает нам ошибку который завершен:
ВНИМАНИЕ Неопределенный индекс: в строке номер 9
Пример № 2:
echo date("Y\0/m/d");
Выходы:
2016
НО! echo
или же var_dump()
Например, и некоторые другие функции будут выводить строку «как есть», просто \ 0 байтов скрыты браузерами.
$string = "index-of\0-an-array";
$strgin2 = "Y\0/m/d";
echo $string;
echo $string2;
var_dump($string);
var_dump($string2);
Выходы:
Индекс-оф-ан-массива
«Y / м / д»
строка (18) «индекс-массива»
Строка (6) «Г / м / д»
Заметить, что $string
длина 18, но показаны 17 символов.
РЕДАКТИРОВАТЬ
От возможный дубликат а также Руководство по PHP:
Ключ может быть целым числом или строкой. Значение может быть любого типа.
Строки, содержащие действительные целые числа, будут преобразованы в целочисленный тип. Например. ключ «8» фактически будет храниться в 8. С другой стороны, «08» не будет разыгрываться, поскольку он не является действительным десятичным целым числом. Короче говоря, любая строка может быть ключом. И строка может содержать любые двоичные данные (до 2 ГБ). Следовательно, ключом могут быть любые двоичные данные (поскольку строка может быть любыми двоичными данными).
Нет ограничений на значения, из которых может состоять строка;
в частности, байты со значением 0 («NUL bytes») разрешены где угодно
в строке (однако, несколько функций, сказано в этом руководстве, чтобы не быть
«Бинарный сейф», может передавать строки библиотекам, которые игнорируют данные
после байта NUL.)
Но я до сих пор не понимаю, почему язык разработан таким образом? Есть ли причины для такого поведения / реализации? Почему PHP не обрабатывает ввод как бинарный безопасный везде, но только в некоторых функциях?
От комментарий:
Причина в том, что многие функции PHP, такие как
printf
использовать реализацию библиотеки C за кулисами, потому что разработчики PHP были ленивы.
Такие, как echo
, var_dump
, print_r
? Другими словами, функции, которые что-то выводят. На самом деле они бинарно безопасны, если мы посмотрим на мой первый пример. Не имеет смысла реализовывать некоторые бинарно-безопасные и бинарно-небезопасные функции для вывода. Или просто используйте некоторые из них, как они есть в std lib в C, и напишите некоторые совершенно новые функции.
Короткий ответ на вопрос «почему» просто история.
Изначально PHP был написан как способ написания скриптов на C-функциях, чтобы их можно было легко вызывать при генерации HTML. Следовательно PHP строки были просто C строк, которые представляют собой набор любых байтов. Так что в современных терминах PHP мы бы сказали, что ничто не является бинарно-безопасным просто потому, что это не было запланировано быть чем-то еще.
Ранний PHP не был задуман как новый язык программирования, и он рос органично, и Лердорф заметил в ретроспективе: «Я не знаю, как это остановить, никогда не было никакого намерения писать язык программирования […] У меня нет абсолютно никакого Идея, как написать язык программирования, я просто продолжал добавлять следующий логический шаг на этом пути «.
Со временем язык стал поддерживать более сложные функции обработки строк, многие из которых учитывают специфические байты строки и становятся «бинарно-безопасными». Согласно недавно написанному формальная спецификация PHP:
Что касается того, как байты в строке переводятся в символы, не определено. Хотя пользователь строки может решить приписать особую семантику байтам, имеющим значение
\0
с точки зрения PHP, такие нулевые байты не имеют особого значения. PHP не предполагает, что строки содержат какие-либо конкретные данные или присваивают специальные значения каким-либо байтам или последовательностям.
Как язык, который вырос органически, не было никакого движения к универсальной обработке строк способом, отличным от C. Поэтому функции и библиотеки являются бинарно-безопасными в каждом конкретном случае.
Пример кулака из вопроса
Ваш первый пример сбивает с толку, потому что сообщение об ошибке — это часть, оканчивающаяся нулевым символом, а не потому, что строка неправильно обрабатывается массивом. Ниже приведен исходный код, который вы разместили с сообщением об ошибке:
$array = array("index-of-an-array" => "value");
$string = "index-of\0-an-array";
echo $array[$string];
Примечание: неопределенный индекс: индекс в
Обратите внимание, что сообщение об ошибке выше было усечено index-of
из-за нулевого символа, массив работает должным образом, потому что если вы попробуете его таким образом, он будет работать нормально:
$array = array("index-of\0-an-array" => "value");
$string = "index-of\0-an-array";
echo $array[$string];
В сообщении об ошибке правильно указано, что два ключа были неправильными, что
они есть
"index-of\0-an-array" != "index-of-an-array"
Проблема в том, что в сообщении об ошибке распечатано все до нулевого символа. Если это так, то некоторые могут считать это ошибкой.
Второй пример запускает глубину PHP 🙂
Я добавил немного кода, чтобы мы могли видеть, что происходит
<?php
class Foo {
public $index_public;
protected $index_prot;
private $index_priv;
function __construct() {
$this->index_public = 0;
$this->index_prot = 1;
$this->index_priv = 2;
}
}
$foo = new Foo();
$array = (array)$foo;
print_r($foo);
print_r($array);
//echo $array["\0Foo\0index_of_an_array2"];//This prints 2
//echo $foo->{"\0Foo\0index_of_an_array2"};//This fails
var_dump($array);
echo array_keys($array)[0] . "\n";
echo $array["\0Foo\0index_priv"] . "\n";
echo $array["\0*\0index_prot"] . "\n";
Вышеуказанные коды выводятся
Foo Object
(
[index_public] => 0
[index_prot:protected] => 1
[index_priv:Foo:private] => 2
)
Array
(
[index_public] => 0
[*index_prot] => 1
[Fooindex_priv] => 2
)
array(3) {
'index_public' =>
int(0)
'\0*\0index_prot' =>
int(1)
'\0Foo\0index_priv' =>
int(2)
}
index_public
2
1
Разработчики PHP решили использовать \0
символ как способ разделения типов переменных членов. Обратите внимание, что защищенные поля используют *
чтобы указать, что переменная-член может фактически принадлежать многим классам. Он также используется для защиты частного доступа, т.е. этот код не будет работать.
echo $foo->{"\0Foo\0index_priv"}; //This fails
но как только вы приведете его к массиву, такой защиты не будет, т.е.
echo $array["\0Foo\0index_priv"]; //This prints 2
Есть ли причина для такого поведения?
/реализация?
Да. В любой системе, с которой вам нужно взаимодействовать, вам нужно сделать систему
звонки, если вы хотите, чтобы текущее время или конвертировать дату и т. д. вам нужно поговорить
к операционной системе, и это означает вызов API OS, в случае Linux
этот API находится в C
,
PHP изначально разрабатывался как тонкая оболочка C
довольно много языков
Начните с этого пути и развивайтесь, PHP не является исключением.
Есть ли причина для этого
поведение/реализация?
В отсутствие каких-либо проблем с обратной совместимостью я бы сказал, что некоторые варианты выбора не являются оптимальными, но я подозреваю, что обратная совместимость является важным фактором.
Но я до сих пор не понимаю, почему язык разработан таким образом?
Обратная совместимость почти всегда является причиной того, что функции, которые не нравятся людям, остаются в языке. Со временем языки развиваются и удаляются, но это постепенно и приоритетно. Если бы вы спросили всех разработчиков PHP, хотят ли они лучшей обработки двоичных строк для некоторых функций или JIT-компилятора, я думаю, что JIT может выиграть, как в PHP 7. Обратите внимание, что люди, выполняющие реальную работу, в конечном итоге решают, над чем они работают, и работать над JIT-компилятором гораздо веселее, чем исправлять библиотеки, которые делают что-то странное.
Я не знаю ни одного языкового разработчика, который не хотел бы, чтобы они делали некоторые вещи иначе, чем с самого начала. Любой, кто реализует компилятор до
язык популярен и находится под большим давлением, чтобы получить то, что работает для
их, и это означает, что срезание углов, не все языки, существующие сегодня
огромная компания, поддерживающая их, чаще всего это была небольшая преданная команда, и они
совершил ошибки, некоторым посчастливилось получить за это деньги. Называя их ленивыми
немного несправедливо.
У всех языков есть темные углы бородавок и кипит и особенности, которые вы в конечном итоге будете ненавидеть. У некоторых больше, чем у других, и у PHP плохая репутация, потому что у него было / было намного больше, чем у большинства. Обратите внимание, что PHP 5 — это огромный шаг вперед по сравнению с PHP 4. Я думаю, что PHP 7 улучшит ситуацию еще больше.
Любой, кто думает, что его любимый язык свободен от проблем, бредит и почти наверняка не изучил глубины инструмента, который они используют, до какой-то большой глубины.
Функции в PHP, которые внутренне работают со строками C, «не бинарно безопасны» в терминологии PHP. Строка C — это массив байтов, заканчивающийся байтом 0. Когда функция PHP внутренне использует строки C, она читает один за другим символ, а когда она встречает байт 0, она считает ее концом строки. Байт 0 сообщает строковым функциям C, где находится конец строки, поскольку строка C не содержит никакой информации о длине строки.
«Не бинарный» означает, что если функции, которая работает со строкой C, каким-то образом передается строка C, не заканчивающаяся байтом 0, поведение непредсказуемо, поскольку функция будет читать / записывать байты за пределами конца строки, добавляя мусор в строку и / или потенциально сбой PHP.
Например, в C ++ у нас есть строковый объект. Этот объект также содержит массив символов, но он также имеет поле длины, которое он обновляет при любом изменении длины. Таким образом, он не требует байта 0, чтобы сказать ему, где конец. Вот почему строковый объект может содержать любое количество 0 байтов, хотя обычно это недопустимо, поскольку он должен содержать только допустимые символы.
Чтобы это исправить, нужно переписать все ядро PHP, включая все модули, работающие со строками Си, чтобы отправлять «недвоичные безопасные» функции в историю. Объем работы, необходимый для этого, огромен, и создатели всех модулей должны создать новый код для своих модулей. Это может внести новые ошибки и нестабильности во всю историю.
Проблема с байтом 0 и «небинарно-безопасными» функциями не так уж критична, чтобы оправдать переписывание кода модулей PHP и PHP. Возможно, в более новой версии PHP, где некоторые вещи нужно кодировать с нуля, имеет смысл исправить это.
До этого вам просто нужно знать, что любые произвольные двоичные данные, помещенные в некоторую строку с использованием бинарно-безопасных функций, должны иметь добавленный байт 0 в конце. Обычно вы замечаете это, когда в конце строки возникает неожиданный мусор или происходит сбой PHP.