为什么php中有二进制安全函数和二进制不安全函数?

为什么php中有二进制安全函数和二进制不安全函数?,php,string,io,binary-data,Php,String,Io,Binary Data,这种行为/实施有什么原因吗?示例: $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 $ar

这种行为/实施有什么原因吗?
示例:

$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:

产出:

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/m/d”
字符串(18)“数组索引”
串(6)“Y/m/d”

请注意,
$string
长度为18个字符,但显示了17个字符

编辑

从和:

键可以是整数或字符串。该值可以是任何类型。 包含有效整数的字符串将强制转换为整数类型。例如,键“8”实际上将存储在8下。另一方面,“08”不会被强制转换,因为它不是有效的十进制整数。简而言之,任何字符串都可以是键。字符串可以包含任何二进制数据(最多2GB)。因此,键可以是任何二进制数据(因为字符串可以是任何二进制数据)

发件人:

字符串可以包含的值没有限制; 特别是,任何地方都允许使用值为0的字节(“NUL字节”) 在字符串中(不过,有几个函数,在本手册中说不可以) “二进制安全”,可以将字符串传递给忽略数据的库 在NUL字节之后。)

但我仍然不明白为什么语言是这样设计的?这种行为/实施是否有原因?为什么PHP除了在某些函数中之外,在任何地方都不能将输入处理为二进制安全的呢?

发件人:

原因很简单,因为PHP开发人员很懒惰,许多PHP函数(如
printf
)都在后台使用C库的实现


像是
echo
var\u dump
print\r
?换句话说,就是输出某些东西的函数。如果我们看一下我的第一个示例,它们实际上是二进制安全的。对我来说,为输出实现一些二进制安全和二进制不安全函数是没有意义的。或者只使用C中std库中的一些函数,编写一些全新的函数。

PHP中内部使用C字符串操作的函数在PHP术语中是“非二进制安全的”。C字符串是以字节0结尾的字节数组。当PHP函数在内部使用C字符串时,它会逐个读取字符,当遇到字节0时,它会将其视为字符串的结尾。字节0告诉C字符串函数哪里是字符串的结尾,因为C字符串不包含任何关于字符串长度的信息

“非二进制安全”意味着,如果使用C字符串操作的函数以某种方式被传递给一个未以字节0结尾的C字符串,则行为是不可预测的,因为函数将读取/写入超出字符串结尾的字节,从而给字符串添加垃圾和/或可能导致PHP崩溃

在C++中,例如,我们有字符串对象。此对象还包含一个字符数组,但它还有一个长度字段,它会在任何长度更改时更新该字段。因此,它不需要字节0来告诉它结束的位置。这就是为什么字符串对象可以包含任意数量的0字节,尽管这通常是无效的,因为它应该只包含有效字符

为了纠正这种情况,需要重写整个PHP核心,包括使用C字符串操作的任何模块,以便将“非二进制安全”函数发送到历史记录。这需要大量的工作,所有模块的创建者都需要为他们的模块生成新代码。这可能会在整个故事中引入新的bug和不稳定性

字节0和“非二进制安全”函数的问题对于重新编写PHP和PHP模块代码来说并不是那么关键。也许在一些更新的PHP版本中,有些东西需要从头开始编码,纠正这一点是有意义的

在此之前,您只需要知道,任何通过使用二进制安全函数放入某个字符串的任意二进制数据都需要在末尾添加字节0。通常,当字符串末尾出现意外垃圾或PHP崩溃时,您会注意到这一点。

对“为什么”的简短回答就是历史

PHP最初是作为编写C函数脚本的一种方式编写的,以便在生成HTML时可以轻松调用这些函数。因此,PHP字符串只是C字符串,它是一组任意字节。所以在现代PHP术语中,我们会说没有任何东西是二进制安全的,仅仅是因为

早期的PHP并不打算成为一种新的编程语言,而是有机地发展起来的,Lerdorf在回顾时指出:“我不知道如何阻止它,从来没有任何编写编程语言的意图[…]我完全不知道如何编写编程语言,我只是不断地添加下一个逻辑步骤。”

随着时间的推移,该语言逐渐发展为支持更精细的字符串处理功能,许多功能都考虑了字符串的特定字节,并变得“二进制安全”。根据最近编写的:

至于字符串中的字节如何转换为字符,还没有明确说明。尽管字符串用户可能会选择将特殊语义赋予值为
\0
的字节,但从PHP的角度来看,这种空字节没有特殊意义。PHP不假设字符串包含任何特定数据,也不为任何字节或序列分配特殊值

作为一种有机发展起来的语言,目前还没有一种与C语言不同的方式来普遍处理字符串。因此,函数和库在具体情况下是二进制安全的。

问题的第一个例子

第一个示例令人困惑,因为错误消息是以空字符终止的部分,而不是因为字符串处理不正确
$string = "index-of\0-an-array";
$strgin2 = "Y\0/m/d";
echo $string;
echo $string2;
var_dump($string);
var_dump($string2);
$array = array("index-of-an-array" => "value");
$string = "index-of\0-an-array";
echo $array[$string];
$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
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
echo $foo->{"\0Foo\0index_priv"}; //This fails
echo $array["\0Foo\0index_priv"]; //This prints 2