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

Why are there binary safe AND binary unsafe functions in php?

这有什么原因吗behavior/implementation?
示例:

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

给我们一个错误这是完整的:

NOTICE Undefined index: on line number 9

示例 #2:

echo date("Y[=12=]/m/d");

输出:

2016

例如,

BUT! echovar_dump() 以及其他一些函数会输出字符串 "as it is",只是 \0字节被浏览器隐藏。

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

输出:

index-of-an-array
"Y/m/d"
string(18) "index-of-an-array"
string(6) "Y/m/d"

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

编辑

来自 possible duplicate and php manual:

The key can either be an integer or a string. The value can be of any type. Strings containing valid integers will be cast to the integer type. E.g. the key "8" will actually be stored under 8. On the other hand "08" will not be cast, as it isn't a valid decimal integer. So in short, any string can be a key. And a string can contain any binary data (up to 2GB). Therefore, a key can be any binary data (since a string can be any binary data).

来自 php string details:

There are no limitations on the values the string can be composed of; in particular, bytes with value 0 (“NUL bytes”) are allowed anywhere in the string (however, a few functions, said in this manual not to be “binary safe”, may hand off the strings to libraries that ignore data after a NUL byte.)

但是我还是不明白为什么语言要这样设计?这behavior/implementation有什么原因吗?为什么 PHP 不能在所有地方将输入作为二进制安全处理,而只是在某些函数中?

来自 :

The reason is simply that many PHP functions like printf use the C library's implementation behind the scenes, because the PHP developers were lazy.

不就是echovar_dumpprint_r吗?换句话说,输出一些东西的函数。如果我们看一下我的第一个例子,它们实际上是二进制安全的。对我来说,为输出实现一些二进制安全和二进制不安全的函数毫无意义。或者只是使用 C 中标准库中的一些函数并编写一些全新的函数。

PHP 中内部使用 C 字符串操作的函数在 PHP 术语中是 "not binary safe"。 C 字符串是以字节 0 结尾的字节数组。当 PHP 函数在内部使用 C 字符串时,它会一个一个地读取字符,当遇到字节 0 时,它会将其视为字符串的结尾。字节 0 告诉 C 字符串函数字符串的结尾在哪里,因为 C 字符串不包含有关字符串长度的任何信息。

"Not binary safe" 意味着,如果使用 C 字符串操作的函数以某种方式传递给一个不以字节 0 终止的 C 字符串,行为是不可预测的,因为函数将 read/write 字节超出字符串的末尾,向字符串 and/or 添加垃圾可能会导致崩溃 PHP.

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

为了纠正这个问题,需要重写整个 PHP 核心,包括任何使用 C 字符串操作的模块,以便将 "non binary safe" 函数发送到历史记录。为此所需的工作量巨大,所有模块的创建者都需要为其模块生成新代码。这会给整个故事带来新的错误和不稳定性。

字节 0 和 "non binary safe" 函数的问题并不是证明重写 PHP 和 PHP 模块代码的关键。也许在某些需要从头开始编码的较新 PHP 版本中,更正此问题是有意义的。

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

"why" 的简短回答就是历史

PHP 最初是作为编写 C 函数脚本的一种方式编写的,以便在生成 HTML 时可以轻松调用它们。因此 PHP 字符串只是 C 字符串 ,它们是任意字节的集合。所以在现代 PHP 术语中,我们会说没有什么是二进制安全的,仅仅是因为 it wasn't planned to be anything else.

Early PHP was not intended to be a new programming language, and grew organically, with Lerdorf noting in retrospect: "I don’t know how to stop it, there was never any intent to write a programming language […] I have absolutely no idea how to write a programming language, I just kept adding the next logical step on the way."

随着时间的推移,该语言逐渐支持更精细的字符串处理函数,其中许多函数将字符串的特定字节考虑在内并成为 "binary-safe"。 根据最近编写的formal PHP specification:

As to how the bytes in a string translate into characters is unspecified. Although a user of a string might choose to ascribe special semantics to bytes having the value [=10=], from PHP's perspective, such null bytes have no special meaning. PHP does not assume strings contain any specific data or assign special values to any bytes or sequences.

作为一种有机发展的语言,还没有以不同于 C 的方式普遍处理字符串。因此,函数和库在个案基础上是二进制安全的。

问题中的拳头例子

您的第一个示例令人困惑,因为错误消息是终止于空字符的部分,而不是因为数组未正确处理字符串。您随错误消息发布的原始代码如下:

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

Notice: Undefined index: index-of in

请注意,由于空字符,上面的错误消息已被截断 index-of,该数组按预期工作,因为如果您以这种方式尝试它会工作得很好:

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

错误消息正确地指出了两个键是错误的,这 他们是

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

PHP 开发人员选择使用 [=18=] 字符作为拆分成员变量类型的方式。请注意,受保护的字段使用 * 来指示成员变量实际上可能属于许多 类。它还用于保护私人访问,即此代码无效。

echo $foo->{"[=15=]Foo[=15=]index_priv"}; //This fails

但是一旦你将它转换为一个数组,那么就没有这样的保护了,也就是说这是有效的

echo $array["[=16=]Foo[=16=]index_priv"]; //This prints 2

Is there any reason for this behavior/implementation?

是的。在您需要与之交互的任何系统上,您需要制作系统 电话,如果你想要当前时间或转换日期等你需要说话 到操作系统,这意味着在 Linux 的情况下调用 OS API 这个 API 在 C.

PHP 最初是作为 C 多种语言的薄包装而开发的 以这种方式开始并发展,PHP也不例外。

Is there any reason for this behavior/implementation?

在没有任何向后兼容性问题的情况下,我会说有些选择不是最佳选择,但我怀疑向后兼容性是一个很大的因素。

But I still do not understand why the language is designed this way?

向后兼容性几乎总是人们不喜欢的功能保留在一种语言中的原因。随着时间的推移,语言会发展并删除一些东西,但它是渐进的和优先的。如果您问过所有 PHP 开发人员,他们是否希望对某些函数或 JIT 编译器进行更好的二进制字符串处理,我认为 JIT 可能会胜出,正如它在 PHP 7 中所做的那样。注意,实际执行的人员工作最终决定了他们的工作内容,并且在 JIT 编译器上工作比修复以看似奇怪的方式做事的库更有趣。

我不知道有哪个语言实现者不希望他们从一开始就做一些不同的事情。任何在之前实现编译器的人 语言很流行 承受着很大的压力来获得有用的东西 他们,这意味着偷工减料,并非当今存在的所有语言都有 支持他们的大公司,通常是一个小的敬业团队,他们 犯了错误,有些人很幸运能得到报酬。说他们懒惰 有点不公平。

所有语言都有阴暗的角落、疣和疔疮,以及您最终会讨厌的特征。有些人比其他人多,PHP 的名声不好,因为它 has/had 比大多数人多得多。请注意,PHP 5 是 PHP 4 的巨大飞跃。我认为 PHP 7 会进一步改进。

任何认为他们最喜欢的语言没有问题的人都是妄想,并且几乎可以肯定没有深入探索他们使用的工具的深度。