哪些函数 PHP 被认为不是“二进制安全的”?这些“非二进制安全”函数将字符串传递给哪些库?为啥?

Posted

技术标签:

【中文标题】哪些函数 PHP 被认为不是“二进制安全的”?这些“非二进制安全”函数将字符串传递给哪些库?为啥?【英文标题】:What are the functions PHP which are said not to be "binary safe"? To which libraries these "non-binary safe" functions hand off the strings? And why?哪些函数 PHP 被认为不是“二进制安全的”?这些“非二进制安全”函数将字符串传递给哪些库?为什么? 【发布时间】:2018-12-02 16:22:33 【问题描述】:

我在我的机器上使用 Windows 10 家庭单语言版,它是一个 64 位操作系统

我已经安装了最新版本的 XAMPP,它在我的机器上安装了 PHP 7.2.7

我是根据摘自php Manual 的摘录提出这个问题的:

PHP 中的 字符串 实现为字节数组和整数 表示缓冲区的长度。它没有关于如何 这些字节转换为字符,将该任务留给 程序员。字符串的值没有限制 由...组成的;特别是,值为 0 的字节(“NUL 字节”)是 允许在字符串中的任何位置(但是,有几个函数,在这个 手册不是“二进制安全的”,可能会将字符串交给库 忽略 NUL 字节后的数据。)

我非常了解 PHP 中二进制安全函数和非二进制安全函数之间的区别。我一直在心里怀疑。请一一回答,并附上适当的例子。

PHP 中出现“非二进制安全”和“二进制安全”函数的现象是否仅仅因为整个 PHP 解析器是用 C 语言编写的? 在处理包含任何值(包括 NUL 字节)的字符串时,C 和 PHP 有什么区别? 我想要 PHP 中“非二进制安全”和“二进制安全”函数的完整列表。 “非二进制安全”和“二进制安全”的特性是否仅适用于对字符串进行操作的函数,而不适用于处理 PHP 中其他类型的 PHP 函数? 为什么非二进制安全函数将字符串交给库? 非二进制安全函数是否仅在其处理的字符串包含 NUL 字节时才将字符串移交给库? 这些“非二进制安全”函数将字符串传递给哪些库? 这些库如何处理从“非二进制安全”函数接收到的字符串? 将包含 NUL 字节的字符串移交给某个库后,“非二进制安全”函数是否像“二进制安全”函数一样工作?

【问题讨论】:

PHP 是什么意思?如果您指的是整个 XAMPP 中的 PHP,那么您要求的列表是相当广泛的。 【参考方案1】:

就像 arkascha 解释的那样,“二进制安全”和“非二进制安全”的问题与语言无关。

使用空字节(0x00)来表示字符串的结尾更简单(这可能是 C 使用它的原因), 但缺点是字符串中的任何位置都不能有空字节 如果您必须能够处理各种数据,这是一个很大的限制。 正如 Pete 所示,将长度存储为字符串的元数据部分更为复杂,但它允许您处理任何类型的数据。

关于哪些函数是“二进制安全的”或“非二进制安全的”, 只需阅读 PHP 手册之前 使用这些功能。 我就是做这个的。 无需构建列表,因为 PHP 手册已经解释了您需要了解的有关函数的内容,包括它们是否是二进制安全的。

我相信你的大部分帖子是由于对你引用的 PHP 手册的解释的误解,特别是这部分:

然而,本手册中提到的一些函数不是“二进制安全的”,可能会将字符串移交给忽略 NUL 字节后数据的库。

让我尝试通过添加一些我自己的话使其更清楚:

但是,本手册中提到的一些函数不是“二进制安全的”,这些函数可能会将字符串传递给忽略 NUL 字节后数据的库。

所以它真的没有说“非二进制安全函数将字符串交给库”,这是一种误解。 它的意思是“可能将字符串传递给在 NUL 字节后忽略数据的库的函数,在本手册中被称为不是二进制安全的”。

“移交给库”只是“从其他库调用函数”的另一种说法。 “忽略 NUL 字节后的数据”是一种称为非二进制安全的行为。

另一种说法是:

本手册中的一些函数被称为不是“二进制安全的”因为它们可能会调用其他也不是“二进制安全”的函数(忽略 NUL 字节后数据的函数)。

我希望这可以为您解决问题。

【讨论】:

说明:哪个实体忽略 NUL 字节后的数据?手册中说的那些“少数函数”不是“二进制安全的”,它们可能会将字符串交给库或从手册中说的“少数函数”接收字符串数据的“库”本身不是“二进制安全”? 在最后的声明中,您说“可能会调用其他也不是“二进制安全”的函数(在 NUL 字节后忽略数据的函数)”。根据您的说法,如果所谓的库(即其他函数)在 NUL 字节之后也忽略数据,那么为什么手册中的少数函数说不是“二进制安全”的?如果两个实体即。将字符串传递给库的函数和库函数在 NUL 字节后忽略数据那么将字符串传递给这些无用的库的目的是什么? @user2839497 假设这些功能没有用是另一种误解。我们知道 C 使用以 null 结尾的字符串,因此 C 有许多这样的函数。如果它们没用,那么 C 本身就不会被广泛使用。它不会针对从大型机到微型计算机的各种硬件实施。我可以继续,但简而言之,它们并非没用。所以,你的问题变成了:“将字符串交给这些有用的库的目的是什么?”一个明显的答案是代码重用,使用现有库意味着更短的开发时间,在某些情况下,更快的应用程序。 @LBear :我认为最简单的语句可以是这样的:“但是,本手册中提到的一些函数不是“二进制安全的”,它们在NUL 字节,可以将字符串移交给库。”。我认为只有“非二进制安全”函数会忽略“NUL 字节”之后的数据,并且它们需要其他库的帮助才能考虑“NUL 字节”之后的数据。因此,此类“非二进制安全”函数可能会将字符串移交给不会忽略“NUL 字节”之后的数据的库。如果您认为我建议的陈述是适当的,那么请更改答案【参考方案2】:

传统上,有两种表示字符串的方法:使用特殊字符表示字符串的结尾,或者将其长度与字符串数据一起存储。 C 使用前者;字符串是一个字符数组,末尾有一个空字符。但是,这有一个限制,即 C 中的字符串只能在结尾处使用空字符。

为了克服这个限制,PHP 引擎使用这个结构来表示一个字符串:

struct _zend_string 
    zend_refcounted_h gc; /* refcount struct */
    zend_ulong        h;  /* hash value */
    size_t            len; /* length of string */
    char              val[1]; /* array of chars (using struct "hack") */
;

如您所见,PHP 开发人员选择将字符串的长度与其数据一起存储。

现在如果混合“二进制安全”和“非二进制安全”功能会发生什么?

考虑以下在编写 PHP 扩展时可能会用到的 C 代码:

zend_string *a = zend_string_init("a\0b", /* string length */ 3, 0);
zend_string *b = zend_string_init("a\0c", /* string length */ 3, 0);

if (strcmp(a->val, b->val) == 0) 
    php_printf("Strings are equal!");

你认为会发生什么?此代码输出“字符串相等!”虽然他们显然是不平等的。由于strcmp 没有考虑字符串的长度,所以它是一个非二进制安全函数。

C 的大多数标准库字符串函数都可以归类为“非二进制安全”,因为它依赖于空终止字符。

在扩展代码中处理zend_string时,应该使用Zend字符串函数(zend_string_*)而不是C的字符串库。

修复之前的代码:

if (zend_string_equals(a, b)) 
    php_printf("Equal!");
 else 
    php_printf("Not equal");

现在可以正确打印“不等于”。

【讨论】:

【参考方案3】:

函数是否以“二进制安全”方式处理运行时数据的问题与系统已实现的语言无关。这是一个如何处理数据的问题。 PHP 是一种高级语言,这意味着它具有字符串类型的高级实现。这不依赖于 C 所依赖的终止空字符,而是字符串类型维护有关存储字符串的元数据,这允许更灵活和健壮的实现。然而,这与是否“二进制安全”无关。

您的其余观点无法真正明确地回答。 php 使用哪些库取决于您的设置,即动态环境。潜在的库如何处理移交给他们的数据与 php 函数是否可以被视为“二进制安全”无关 - 库不了解 php,它只会根据库的方式移交数据和处理实施的。

【讨论】:

以上是关于哪些函数 PHP 被认为不是“二进制安全的”?这些“非二进制安全”函数将字符串传递给哪些库?为啥?的主要内容,如果未能解决你的问题,请参考以下文章

这些高频PHP面试题,你能回答出来吗?

深入剖析php执行原理:函数的调用

打开和关闭 PHP 是不是被认为是不好的做法? [关闭]

为啥 sizeof 被认为是运算符?

PHP 函数的哪些部分被命名?

php 上传csv文件