☰
PHP hex2bin()函数用法讲解
2026/10/8 21:53:41 网站建设 项目流程

前言


hex2bin()有一个非常容易误会的名字。看到「hex to bin」,很多人的第一反应是「把十六进制数字转成二进制数字」,比如把FF变成11111111。这是错的,而且官方手册在hex2bin()页面里专门用一条 Caution(警告)把这件事写了出来:这个函数不会把十六进制数转换成二进制数,那个需求要用base_convert()来做。


hex2bin()真正做的事是:把一串十六进制文本按「每两个字符一组」解包成原始字节(byte)。'48656c6c6f'解出来不是数字,而是 5 个字节,恰好组成字符串Hello。它是bin2hex()的逆操作,两个函数合起来完成「二进制数据 ↔ 可打印文本」的互转。


为什么要做这种转换?因为二进制数据(加密后的密文、随机令牌、图片片段、压缩结果)直接存进文本字段、日志或 URL 里常常会出问题:里面可能出现 NUL 字节、换行、引号这些「会破坏外层格式」的字符。十六进制是它们的安全表示方式——只用 0-9 和 a-f 两种字符集,长度翻倍,但任何文本环境都能安全承载。


另外要强调一句:hex2bin()/bin2hex()是编码与解码,不是加密与解密。十六进制文本不含任何保密性,它只是换了一种写法。


一、签名与错误处理


官方手册给出的签名是:


hex2bin(string $string): string|false

参数是要解码的十六进制字符串,返回值是解出的原始字节串,失败时返回false(返回类型里明确带着|false)。函数自PHP 5.4.0起可用。


手册的「错误/异常」一栏写得很明确:输入字符串长度为奇数,或者含有非十六进制的字符时,会产生一个 E_WARNING 级别的错误(并返回false)。注意这里的措辞——是警告加返回值,不是抛异常。这一点和很多现代 PHP 函数不同,容易被记错,所以拿到返回值必须显式判断:


<?php // 适用于 PHP 8.0+
$result = hex2bin($someHex);

if ($result === false) {
// 必须用 === 判断:解出的字节串可能是 "" 之外的任何内容,
// 而且 false 松散比较时会与 "" 相等,用 == 会漏判
echo '解码失败', PHP_EOL;
}

用==判断在这里尤其危险:空字符串与false松散相等,用==会把「解码成功但结果为空」和「解码失败」混为一谈。


几组典型的输入输出:




输入结果说明



'48656c6c6f''Hello'正常的偶数长度十六进制

'48656C6C6F''Hello'大写同样接受

''''空串长度为 0,是偶数,返回空串

'abc'false+E_WARNING奇数长度

'zz'false+E_WARNING含非十六进制字符

'0x41'false+E_WARNING不接受0x前缀,且长度是偶数也照样失败

'48 65'false+E_WARNING不接受空格等分隔符



大小写都能接受这一点可以从 PHP 源码里确认:解码时对每个字符做了一次大小写归一(把字节与0x20取反后比较),所以a和A等价。而空格、换行、0x前缀这些「看起来人畜无害」的字符都会被判定为非法,直接返回false。如果数据来源不可控,先做清洗再解码:


<?php // 适用于 PHP 8.0+
function safe_hex_decode(string $hex): string|false
{
$hex = str_replace([' ', "\t", "\r", "\n"], '', $hex); // 去掉常见空白
$hex = strtolower($hex);
if (str_starts_with($hex, '0x')) {
$hex = substr($hex, 2); // 去掉 0x 前缀
}
if ($hex === '' || strlen($hex) % 2 !== 0) {
return false; // 长度必须是偶数
}
if (!ctype_xdigit($hex)) {
return false; // 只允许 0-9 a-f
}
return hex2bin($hex); // 前面都校验过了,这里一定成功
}

str_starts_with()需要PHP 8.0 及以上,在 7.x 上可以改用strpos($hex, '0x') === 0(必须用===,第 0 位命中时strpos()返回整数0)。ctype_xdigit()属于 ctype 扩展,用于判断字符串是否全部为十六进制字符。


二、它到底做了什么:按字节解包


bin2hex()的规则是「逐字节转换,高四位在前」,把每个字节拆成两个十六进制字符;hex2bin()就是反过来,每两个字符合成一个字节。用一个例子走一遍:


原字符串 : H e l l o
UTF-8 字节 : 0x48 0x65 0x6C 0x6C 0x6F
bin2hex() : 48 65 6c 6c 6f -> "48656c6c6f"
hex2bin() : 48->0x48 65->0x65 ... -> "Hello"

<?php // 适用于 PHP 8.0+
$text = 'Hello world!';
$hex = bin2hex($text);
var_dump($hex); // string(24) "48656c6c6f20776f726c6421"
var_dump(hex2bin($hex)); // string(12) "Hello world!"

// 往返一致
var_dump(hex2bin(bin2hex($text)) === $text); // bool(true)

这段往返在任意字节序列上都成立,包括中文(UTF-8 多字节)、二进制文件内容、含 NUL 的数据:


<?php // 适用于 PHP 8.0+
$binary = random_bytes(16); // 16 个随机字节,可能含 NUL
$hex = bin2hex($binary);

echo $hex, PHP_EOL; // 32 个十六进制字符
var_dump(strlen($hex)); // int(32):长度正好翻倍
var_dump(hex2bin($hex) === $binary); // bool(true):完整还原

这里体现了「长度翻倍」这个固定关系:bin2hex()的输出长度恒等于输入的 2 倍,hex2bin()的输出长度恒等于输入的 1/2(因此输入长度必须是偶数)。


hex2bin()是二进制安全的:它按长度处理,不会因为内容里出现 NUL 字节而截断,返回的字符串里也可能包含 NUL。这意味着把解码结果写进数据库时,字段类型必须是二进制安全的(例如VARBINARY、BLOB),写进文本字段(VARCHAR+ 非二进制排序规则)容易被截断或被转义规则改写。


三、和 bin2hex()、base_convert()、hexdec() 的分工


这四个函数名字都带 hex 或 bin,功能完全不同,混用会直接得到错误结果:




需求用哪个函数输入与输出的关系



字节串 → 十六进制文本bin2hex(string $string): string每个字节变两个小写十六进制字符,长度 ×2

十六进制文本 → 字节串`hex2bin(string $string): string\false`

十六进制文本 → 十进制数值`hexdec(string $hex_string): int\float`

数值进制转换base_convert(string $num, int $from_base, int $to_base): string任意 2~36 进制互转



最典型的混淆是前两个和第三个:


<?php // 适用于 PHP 8.0+
$bytes = hex2bin('ff'); // 一个字节,其值为 0xFF
var_dump(strlen($bytes)); // int(1):长度为 1
var_dump(bin2hex($bytes)); // string(2) "ff":可以原样转回去
var_dump(hexdec('ff')); // int(255):这是一个整数

hex2bin('ff')返回的是一个长度为 1 的字符串,它的那一个字节值是 0xFF;这个字节本身不是一个合法的 UTF-8 字符,直接回显到页面上通常显示不出来,所以上面用strlen()和bin2hex()来观察它,而不是直接打印。hexdec('ff')返回的则是整数 255,可以直接参与算术。两者名字相似,结果毫无关系。


至于手册 Caution 里提到的base_convert(),用法是这样:


<?php // 适用于 PHP 8.0+
echo base_convert('ff', 16, 2), PHP_EOL; // 11111111
echo base_convert('11111111', 2, 16), PHP_EOL; // ff

这里要注意base_convert()手册里的一条 Warning:大数会丢精度,因为内部用浮点做中转。位数较多的数值不适合用它,那种场景应该用base_convert()之外的方案(例如gmp_*或bcmath系列)。


四、实战:两个真会用到的场景


场景一:把随机令牌变成可安全传输的文本


生成令牌时用random_bytes()(密码学安全的随机源,PHP 7.0+),再用bin2hex()转成十六进制文本,就能直接放进 URL、Cookie 或表单里:


<?php // 适用于 PHP 7.0+
$token = bin2hex(random_bytes(16)); // 16 字节 -> 32 个十六进制字符
echo $token, PHP_EOL;

// 校验时把用户提交的值解回二进制再比较
// (实践中直接比较十六进制文本也安全,因为两边格式相同)
$decoded = hex2bin($token);
var_dump(strlen((string) $decoded)); // int(16)

关于这段代码有两点要说明白:



  • 十六进制文本并不隐藏任何信息。bin2hex(random_bytes(16))的安全性来自random_bytes()的随机性,不是来自「看起来像乱码」。不要把它当成「加密」。

  • 存令牌到数据库时,直接比较字符串更省事,不必解码后再比。要用哈希保存令牌(例如只存hash('sha256', $token)),则属于令牌管理的设计问题,和编码无关。


如果对体积敏感,base64_encode()比十六进制更紧凑(同样 16 字节,Base64 是 24 个字符,十六进制是 32 个字符),代价是多出+、/、=这几个在 URL 里需要额外编码的字符,此时可用base64url变体或rtrim(strtr(base64_encode($x), '+/', '-_'), '=')这类改写。


场景二:在日志和配置里保存二进制数据


<?php // 适用于 PHP 8.0+
$blob = random_bytes(8); // 假设这是某段二进制数据
$record = bin2hex($blob); // 存进日志/配置文件

file_put_contents(__DIR__ . '/token.log', $record . PHP_EOL, FILE_APPEND | LOCK_EX);

// 读回来还原
$line = trim((string) file_get_contents(__DIR__ . '/token.log'));
$restored = hex2bin($line);
var_dump($restored === $blob);

示例里的trim()很关键:从文件读到的内容通常带着行尾的换行符,而换行符不是合法的十六进制字符,直接丢给hex2bin()会告警并返回false。FILE_APPEND | LOCK_EX是file_put_contents()的追加与加锁标志,避免多进程同时写时相互覆盖。


常见坑点



  1. ❌ 以为hex2bin('FF')会得到'11111111'


✅ 它得到的是一个字节值 0xFF,不是二进制数字的文本。要做「十六进制数 → 二进制数字文本」的转换,用base_convert('FF', 16, 2)。



  1. ❌ 不判断返回值,直接把结果当字符串用


✅ 返回类型是string|false。失败时(奇数长度或含非法字符)只产生E_WARNING并返回false,不抛异常。必须用=== false判断。



  1. ❌ 用if (hex2bin($hex) == false)判断失败


✅ 空字符串与false松散相等,==会误判。统一用===。



  1. ❌ 把带空格、换行或0x前缀的字符串直接丢给hex2bin()


✅ 这些都是非法字符,会返回false。先清洗(去掉空白、去掉前缀、统一小写),再用ctype_xdigit()校验。



  1. ❌ 传入奇数长度的十六进制串


✅ 奇数长度必然失败,因为两个字符才构成一个字节。拼接输入时尤其容易出现这种错误,务必先判断strlen($hex) % 2 === 0。



  1. ❌ 把hex2bin()当成加密或混淆手段


✅ 十六进制只是编码。任何拿到文本的人都能一行代码解回来。要保密必须用加密(如sodium_crypto_secretbox()),要防篡改用hash_hmac()。



  1. ❌ 把解码出的二进制数据写进普通文本字段或直接输出到页面


✅ 结果可能含 NUL 和非法 UTF-8 字节,写库要用二进制字段类型,输出到 HTML 前必须转义,输出到日志要再做一次编码。



  1. ❌ 用base_convert()处理位数较多的十六进制数值


✅ 手册明确提示它在大数上会因内部浮点表示而丢失精度,长数值应改用gmp_*或bcmath系列函数。


总结




项目说明



签名`hex2bin(string $string): string\

作用把十六进制文本每两个字符解包成一个原始字节

可用版本PHP 5.4.0 起

输入要求长度为偶数;只允许0-9、a-f、A-F;不接受空格与0x前缀

失败行为产生E_WARNING级别的错误并返回false,不抛异常

长度关系输出长度恒为输入长度的一半;bin2hex()则恰好翻倍

逆函数bin2hex()

是否加密不是,只是编码

常见误用把它当成「十六进制数转二进制数」,那个需求属于base_convert()



hex2bin()的用法就这么几行,真正的风险全在「想当然」上:想当然以为它在做进制转换,想当然以为失败会抛异常,想当然以为带空格和前缀的输入也能解。记住它是bin2hex()的逆操作、返回类型带|false、输入必须是「偶数长度 + 纯十六进制字符」这三条,再配合一个先清洗后校验的包装函数,这个函数就再也不会给你添麻烦了。





需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询