Почему в 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/m/d");

выходы:

2016

но! echo или var_dump(), например, и некоторые другие функции, будет выводить строку "как есть", просто байт скрыты браузерами.

$string = "index-of-an-array";
$strgin2 = "Y/m/d";
echo $string;
echo $string2;
var_dump($string);
var_dump($string2);

выходы:


"Y/m / d"
строка (18)"index-of-an-array"
строка (6)"Y/m/d"

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

редактировать

С возможные дубликаты и в PHP руководство:

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

С php string details:

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

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

С комментарий:

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

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

3 ответов


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

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

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

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

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

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


пример кулака из вопроса

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

$array = array("index-of-an-array" => "value");
$string = "index-of-an-array";
echo $array[$string];

Примечание: неопределенный индекс: индекс в

Примечание, сообщение об ошибке выше было усечено index-of из-за null характер, массив работает как ожидалось, потому что если вы попробуете его таким образом, он будет работать просто отлично:

$array = array("index-of-an-array" => "value");
$string = "index-of-an-array";
echo $array[$string];

сообщение об ошибке правильно определило, что два ключа были неправильными, которые они

"index-of-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["Fooindex_of_an_array2"];//This prints 2
//echo $foo->{"Fooindex_of_an_array2"};//This fails
var_dump($array);
echo array_keys($array)[0]       . "\n";
echo $array["Fooindex_priv"] . "\n";
echo $array["*index_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)
  '*index_prot' =>
  int(1)
  'Fooindex_priv' =>
  int(2)
}
index_public
2
1

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

echo $foo->{"Fooindex_priv"}; //This fails

но как только вы бросили это в массив, то нет такой защиты, т. е. работает

echo $array["Fooindex_priv"]; //This prints 2

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

да. На любой системе, с которой вам нужно взаимодействовать, вам нужно сделать систему звонки, если вы хотите текущее время или конвертировать дату и т. д. Вам нужно поговорить к операционной системе, и это означает вызов API ОС, в случае 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++, например, у нас есть объект string. Этот объект также содержит массив символов, но он также имеет поле длины, которое он обновляет при любом изменении длины. Поэтому не требуется байт 0, чтобы сообщить ему, где находится конец. Вот почему объект string может содержит любое количество 0 байт, хотя обычно это недопустимо, так как оно должно содержать только допустимые символы.

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

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

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