PHP中string即二进制容器,无原生二进制类型;判断二进制数据应结合mb_check_encoding()验证编码、检测NUL字节及控制字符比例,并优先依赖上下文(如文件打开模式、HTTP头、数据库字段类型)而非自动识别。

PHP 中没有原生二进制类型,string 就是二进制容器
PHP 从 5.6 开始就不再区分“字符串”和“二进制字符串”,所有 string 都是字节序列,不带编码标记。也就是说:is_string($var) 为 true 并不意味着它是“文本”,它完全可能包含 \x00、\xff 或任意无效 UTF-8 字节。
所以,你不能靠类型判断来识别“二进制数据”,得靠内容特征或上下文约定。
用 mb_detect_encoding() + mb_check_encoding() 判断是否可能是文本
这是最常用也最实用的反向思路:先尝试验证它是否符合常见文本编码(如 UTF-8、ISO-8859-1),如果全都不符合,大概率就是二进制数据。
-
mb_detect_encoding($var, ['UTF-8', 'ISO-8859-1', 'ASCII'], true)返回false,说明没检测到可信编码 -
!mb_check_encoding($var, 'UTF-8')且!mb_check_encoding($var, 'ASCII'),说明它包含非法 UTF-8 序列(比如孤立的\xc0或截断的多字节字符) - 注意:
mb_detect_encoding()在$strict = true模式下才可靠;默认会“猜测成功”,容易误判 - 对纯 ASCII 内容(如日志片段),这个方法会返回
UTF-8,但它也可能只是二进制数据中恰好没出现坏字节——所以需结合其他线索
检查是否含 NUL 字节或控制字符(快速启发式)
很多二进制格式(PNG、ZIP、ELF)以 \x00 开头或大量使用控制字符(\x00–\x08, \x0b–\x0c, \x0e–\x1f),而正常文本极少连续出现。
立即学习“PHP免费学习笔记(深入)”;
- 用
strpos($var, "\x00") !== false快速排除:有 NUL 基本可判定为二进制(文本中除非故意嵌入,否则不会出现) - 统计控制字符比例:
preg_match_all('/[\x00-\x08\x0b\x0c\x0e-\x1f]/', $var, $matches),若占比 > 5% 且长度 > 100 字节,倾向二进制 - 避免用
ctype_print()或ctype_graph():它们要求**全部**字节可打印,对含空格/换行的文本就失效
依赖来源上下文比“自动识别”更可靠
真正健壮的做法,是把判断逻辑前移到数据生成/接收环节:
- 从
fopen(..., 'rb')读取的文件内容,默认按二进制处理;从file_get_contents()读文本文件时,应明确指定编码并校验 - HTTP 请求体中,靠
Content-Type: application/octet-stream或image/png头判断,而不是解析 body - 数据库字段如果是
BLOB或VARBINARY,PHP 取出来就是原始字节,无需再“识别” - 自己定义协议时,加一个 magic header(如前 4 字节为
"PK\x03\x04")比通用检测稳定得多
硬要用算法猜,永远有边界情况:一段加密后的 base64 文本看起来像随机二进制,而一段精心构造的 shellcode 可能全是可打印 ASCII。











