Почему в php существуют двоичные безопасные И двоичные небезопасные функции?


Есть ли какая-либо причина для такого поведения/реализации?
Пример:

$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);

Выходные данные:

Индекс массива
"Д/м/д"
строка(18) "индекс массива"
строка(6) "Y/m/d"

Обратите внимание, что длина $string равна 18, но показано 17 символов.

РЕДАКТИРОВАТЬ

Из возможный дубликат и руководства по php:

Ключ может быть целым числом или строкой. Значение может быть любого типа. Строки, содержащие допустимые целые числа будет приведен к целочисленному типу. Например, ключ "8" на самом деле будет храниться под номером 8. С другой стороны, "08" не будет приведено, так как это недопустимое десятичное целое число. Короче говоря, любая строка может быть ключом. И строка может содержать любые двоичные данные (до 2 ГБ). Следовательно, ключом могут быть любые двоичные данные (поскольку строка может быть любыми двоичными данными).

Из сведения о строке php:

Нет ограничений на значения, из которых может состоять строка; в в частности, байты со значением 0 ("НУЛЕВЫЕ байты") разрешены в любом месте строки (однако несколько функций, которые, как указано в этом руководстве, не являются "безопасными для двоичных файлов", могут передавать строки библиотекам, которые игнорируют данные после нулевого байта.)

Но я все еще не понимаю, почему язык разработан таким образом? Есть ли причины для такого поведения/реализации? Почему PHP не обрабатывает ввод как двоичный безопасный везде, но только в некоторых функциях?

Из комментарий:

Причина просто в том, что многие функции PHP, такие как printf, используют реализацию библиотеки C за кулисами, потому что разработчики PHP были ленивы.

Не такие, как echo, var_dump, print_r ? Другими словами, функции, которые что-то выводят. Они на самом деле безопасны в двоичном формате, если мы взглянем на мой первый пример. Для меня нет смысла реализовывать некоторые двоично-безопасные и двоично-небезопасные функции для вывода. Или просто используйте некоторые, как они есть в std-библиотека на C и напишите несколько совершенно новых функций.

Author: Community, 2016-04-29

3 answers

Короткий ответ на вопрос "почему" - это просто история.

PHP изначально был написан как способ написания сценариев функций C, чтобы их можно было легко вызывать при создании HTML. Поэтому строки PHP были просто строками C, которые представляют собой набор любых байтов. Таким образом, в современных терминах PHP мы бы сказали, что ничто не было безопасным для двоичных файлов, просто потому, что не планировалось ничего другого.

Ранний PHP не был задуман как новый язык программирования и вырос органично, с Лердорфом, отмечающим в ретроспективе: "Я не знаю, как это остановить, никогда не было намерения писать язык программирования [...] Я совершенно не представляю, как писать язык программирования, я просто продолжал добавлять следующий логический шаг на этом пути"

.

Со временем язык расширился, чтобы поддерживать более сложные функции обработки строк, многие из которых учитывают конкретные байты строки и становятся "безопасными для двоичных файлов". Согласно недавно написанному официальному Спецификация PHP:

Относительно того, как байты в строке преобразуются в символы, не указано. Хотя пользователь строки может предпочесть приписать особую семантику байтам, имеющим значение \0, с точки зрения PHP, такие нулевые байты не имеют особого значения. PHP не предполагает, что строки содержат какие-либо конкретные данные или присваивают специальные значения каким-либо байтам или последовательностям.

Как язык, который вырос органически, не было предпринято никаких шагов для универсального лечения строки способом, отличным от C. Поэтому функции и библиотеки являются безопасными для двоичных файлов в каждом конкретном случае.

 8
Author: Matt S, 2016-05-04 14:01:20

Первый пример из вопроса

Ваш первый пример сбивает с толку, потому что сообщение об ошибке - это часть, которая заканчивается нулевым символом, а не потому, что строка неправильно обрабатывается массивом. Исходный код, который вы опубликовали с сообщением об ошибке, следующий:

$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 ОС, в случае Linux этот API в C.

PHP изначально был разработан как тонкая оболочка вокруг C довольно много языков начинаются таким образом и развиваются, PHP не является исключением.

Есть ли есть ли причина для такого поведения/реализации?

В отсутствие каких-либо проблем с обратной совместимостью я бы сказал, что некоторые варианты не являются оптимальными, но я подозреваю, что обратная совместимость является важным фактором.

Но я все еще не понимаю, почему язык разработан таким образом?

Обратная совместимость почти всегда является причиной того, что функции, которые людям не нравятся, остаются в языке. Со временем языки развиваются и удаляйте вещи, но это постепенное и приоритетное. Если бы вы спросили всех разработчиков PHP, хотят ли они лучшей обработки двоичных строк для некоторых функций или компилятора JIT, я думаю, что JIT может выиграть, как это было в PHP 7. Обратите внимание, что люди, выполняющие фактическую работу, в конечном счете решают, над чем они работают, и работать над JIT-компилятором веселее, чем исправлять библиотеки, которые делают вещи, казалось бы, странными способами.

Я не знаю ни одного разработчика языка, который не хотел бы, чтобы они что-то делали по-другому с самого начала. Кто осуществляет компилятор, прежде чем язык-это популярные находится под большим давлением, чтобы получить то, что работает для и что значит срезать углы, не для всех языков, существующих сегодня, был огромная компания, подпирая их, чаще всего, это была небольшая команда, и они совершали ошибки, некоторые из них были достаточно удачливы, чтобы вам заплатили, чтобы сделать это. Называть их ленивыми немного несправедливо.

Во всех языках есть темные углы, бородавки, нарывы и особенности, которые вы в конечном итоге возненавидите. Некоторые больше, чем другие, и у PHP плохая репутация, потому что у него было/было намного больше, чем у большинства. Обратите внимание, PHP 5 - это огромный шаг вперед по сравнению с PHP 4. Я бы предположил, что PHP 7 улучшит ситуацию еще больше.

Любой, кто думает, что их любимый язык свободен от проблем, является бредовым и почти наверняка не постиг глубины инструмента, который они используют, до какой-либо большой глубины.

 1
Author: Harry, 2016-05-06 20:26:46

Функции в PHP, которые внутренне работают со строками C, "небезопасны в двоичном формате" в терминологии PHP. Строка C представляет собой массив байтов, заканчивающийся байтом 0. Когда функция PHP внутренне использует строки C, она считывает один символ за другим, и когда она встречает байт 0, она рассматривает его как конец строки. Байт 0 указывает строковым функциям C, где находится конец строки, так как строка C не содержит никакой информации о длине строки.

"Не двоичный безопасный" означает, что, если функция, которая работает со строкой C, каким-то образом передается строка C, не заканчивающаяся байтом 0, поведение непредсказуемо, потому что функция будет считывать/записывать байты за пределами конца строки, добавляя мусор в строку и/или потенциально приводя к сбою PHP.

В C++, например, у нас есть объект string. Этот объект также содержит массив символов, но у него также есть поле длины, которое он обновляет при любом изменении длины. Таким образом, ему не требуется байт 0, чтобы указать, где находится конец. Вот почему строковый объект может содержит любое количество 0 байт, хотя обычно это недопустимо, так как оно должно содержать только допустимые символы.

Для того, чтобы это было исправлено, все ядро PHP, включая любые модули, которые работают со строками C, необходимо переписать, чтобы отправлять "небинарные безопасные" функции в историю. Объем работы, необходимый для этого, огромен, и всем создателям модулей необходимо создать новый код для своих модулей. Это может привести к появлению новых ошибок и нестабильности в целом история.

Проблема с байтом 0 и "небинарными безопасными" функциями не так уж важна для оправдания перезаписи кода PHP и модулей PHP. Возможно, в какой-нибудь более новой версии PHP, где некоторые вещи нужно кодировать с нуля, имело бы смысл исправить это.

До тех пор вам просто нужно знать, что любые произвольные двоичные данные, помещенные в некоторую строку с помощью двоичных безопасных функций, должны иметь байт 0, добавленный в конце. Обычно вы заметите это, когда в конце ваша строка или PHP вылетает.

 0
Author: BJovke, 2016-05-03 12:26:56