二进制安全

2020/08/18 计算机原理

在 PHP 中很多字符串操作函数都会说明是二进制安全,那么什么是二进制安全呢?

维基百科给出的定义如下:

A binary-safe function is one that treats its input as a raw stream of bytes and ignores every textual aspect it may have. The term is mainly used in the PHP programming language to describe expected behaviour when passing binary data into functions whose main responsibility is text and string manipulating, and is used widely in the official PHP documentation.

二进制安全的函数会将所有输入都视为原始的字节流,忽略其可能会有的任何字面意思。

再看看 stackoverflow 上的一个回答:

It means the function will work correctly when you pass it arbitrary binary data (i.e. strings containing non-ASCII bytes and/or null bytes).

For example, a non-binary-safe function might be based on a C function which expects null-terminated strings, so if the string contains a null character, the function would ignore anything after it.

This is relevant because PHP does not cleanly separate string and binary data.

以上提到了 C 语言的字符串是以null字节结尾的,在遇到null字符时,字符串操作系统会忽略null字符后的所有内容,因此,函数是非二进制安全的。

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    printf("%lu\n", strlen("abc\0def"));
    printf("%d\n", strcmp("abc\0def", "abc"));
    return 0;
}

上例的结果是30\0在 ASCII 中表示null,后面的字符都忽略了。再跟 PHP 的来对比一下:

<?php

printf("%d\n", strlen("abc\0def"));
printf("%d\n", strcmp("abc\0def", "abc"));

结果分别是74

以上通过代码直观地感受了什么叫二进制安全。我们知道 PHP 的底层是用 C 语言来实现的,那 PHP 是怎么实现二进制安全的呢?

在 PHP 7 中,通过 zend_string 结构体对字符串进行了封装,结构体如下:

struct _zend_string {
    zend_refcounted_h   gc;
    zend_ulong          h;
    size_t              len;
    char                val[1];
};

以上的关键就是len字段,也就是字符串的长度。有了len字段,操作字符串的时候就可以忽略\0字符表示的字符串结束标记,保证了 PHP 字符串操作的二进制安全。

当然,并不是说 PHP 中所有字符串函数都是二进制安全的,例如:

<?php

printf("%d\n", strcoll("abc\0def", "abc"));

以上的结果是0。个人觉得原因就是这个函数没有使用len字段作为判断依据,而是使用了 C 语言本身的null字符作为字符串的结束标记。

Search

    Table of Contents