什么是PHP所谓的“二进制安全”功能?这些“非二进制安全”功能向哪些库传递字符串?为什么?

时间:2018-06-23 11:18:45

标签: php string php-internals

我正在使用 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字节的字符串移交给某个库之后,“非二进制安全”功能是否像“二进制安全”功能一样工作?

3 个答案:

答案 0 :(得分:9)

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

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

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

我相信您的大部分帖子是由于您引用的PHP手册的解释有误,尤其是这部分:

  

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

让我尝试通过添加一些自己的词来使其更清晰:

  

但是,一些功能在本手册中表示不是“二进制安全的”,是可以将字符串移交给忽略NUL字节后的数据的库的功能

因此,它实际上并没有说“非二进制安全函数将字符串移交给库”,这是一种误解。 意思是“本函数中所说的函数可能会将字符串传递给忽略NUL字节后的数据的库,因此不是二进制安全的”。

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

另一种放置方式是:

  

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

我希望这可以为您清除它。

答案 1 :(得分:5)

传统上,有两种表示字符串的方法:通过使用特殊字符来表示字符串的结尾,或者将其长度与字符串数据一起存储。 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");
}

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

答案 2 :(得分:3)

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

您的其他观点并不能真正以清晰的方式得到回答。 php使用的库本身取决于您的设置,即动态环境。潜在的库如何处理移交给他们的数据又与php函数是否可以被认为是“二进制安全的”无关-库不了解php,它仅根据库的方式移交数据并进行处理已实施。