在 PHP 中处理哈希运算时,hash_init()、hash_update() 和 hash_final() 这组函数是一个非常强大的组合。它们提供了一种流式(分段)计算哈希值的方式,适合处理大文件或分块数据,比一次性用 hash() 函数更高效。
本文将详细讲解如何正确配合使用 hash_update() 和 hash_final(),并指出其中需要注意的事项。
这三个函数的基本流程如下:
初始化哈希上下文:
$context = hash_init('sha256');
多次更新数据:
hash_update($context, '部分数据1');
hash_update($context, '部分数据2');
获取最终哈希值:
$hash = hash_final($context);
完整例子:
<?php
$context = hash_init('sha256');
hash_update($context, 'Hello ');
hash_update($context, 'World!');
$finalHash = hash_final($context);
echo "最终哈希值: " . $finalHash;
?>
1?? hash_final() 调用后上下文不可再用
当你调用了:
$hash = hash_final($context);
上下文 $context 就被销毁了,不能再用它继续调用 hash_update() 或 hash_final(),否则会报错。
如果你想在同一个数据流上继续算其他哈希,你需要重新调用 hash_init()。
2?? 使用 hash_copy() 做分支
如果你需要在同一个上下文的基础上得到多个分支结果,可以用 hash_copy():
$context = hash_init('sha256');
hash_update($context, '部分数据');
$copy = hash_copy($context);
hash_update($context, '后续A');
$hashA = hash_final($context);
hash_update($copy, '后续B');
$hashB = hash_final($copy);
这样 $hashA 和 $hashB 就是不同分支的结果。
3?? 处理大文件时按块更新
对于大文件,可以分块读取并调用 hash_update():
<?php
$context = hash_init('sha256');
$handle = fopen('largefile.bin', 'rb');
while (!feof($handle)) {
$chunk = fread($handle, 8192);
hash_update($context, $chunk);
}
fclose($handle);
$finalHash = hash_final($context);
echo "大文件哈希值: " . $finalHash;
?>
这样避免了一次性读取文件带来的内存压力。
4?? URL 场景下的使用
如果你是通过 HTTP 请求流式处理数据,比如:
<?php
$url = 'https://gitbox.net/sample-file.bin';
$context = hash_init('sha256');
$handle = fopen($url, 'rb');
if ($handle) {
while (!feof($handle)) {
$chunk = fread($handle, 8192);
hash_update($context, $chunk);
}
fclose($handle);
$finalHash = hash_final($context);
echo "远程文件哈希值: " . $finalHash;
} else {
echo "无法打开 URL。";
}
?>
要注意:确保 allow_url_fopen 在 php.ini 中是启用的,否则 fopen() 无法直接打开 URL。
5?? 选择合适的算法
hash_init() 的第一个参数是算法名,推荐使用 sha256、sha512 等现代算法,而不是老旧的 md5 或 sha1,因为后者已不安全。