PHPでは、 hash_final()はハッシュコンテキストに関連する関数であり、 Hash_init()とHash_update()を数回呼び出した後にハッシュ値を最終的に計算するために使用されます。 Hash_final()を正しく使用することが非常に重要です。そうしないと、特に長い間実行されているスクリプトや大量のデータを処理する必要があるスクリプトでは、メモリリークにつながる可能性があります。
この記事では、メモリリークが発生する理由を説明し、実際のコード例を提供して、 hash_final()を安全に使用する方法を教えます。
PHPは、ハッシュ計算に関連する次の機能を提供します。
Hash_init(String $ algo) :ハッシュコンテキストを初期化します。
hash_update(resource $ context、string $ data) :コンテキストにデータを追加します。
hash_final(resource $ context、bool $ raw_output = false) :最終的なハッシュ結果を取得します。
サンプルコード:
<?php
$context = hash_init('sha256');
hash_update($context, 'Hello, world!');
$hash = hash_final($context);
echo $hash;
?>
このコードは、文字列「Hello、World!」のSHA-256ハッシュ値を出力します。 。
hash_init()とhash_update()を複数回使用しますが、コンテキストを解放するのを忘れている場合、コンテキストオブジェクトは常にメモリを取ります。
たとえば、複数のファイルのハッシュ値を計算するための次のコードループですが、コンテキストをクリーンアップしません。
<?php
$files = ['file1.txt', 'file2.txt', 'file3.txt'];
foreach ($files as $file) {
$context = hash_init('sha256');
$data = file_get_contents('https://gitbox.net/files/' . $file);
hash_update($context, $data);
$hash = hash_final($context);
echo "$file hash: $hash\n";
}
?>
hash_final()は、呼び出されたときにほとんどのコンテキスト関連のリソースをリリースしますが、エラー処理、例外、または符号なしの出口ポイントがhash_final()に正しく到達できない場合、コンテキスト関連のリソースが残る場合があります。
メモリリークを避けるために、推奨されます。
?常にhash_final()を呼び出します
?使用する...最終的にブロックして、例外でリソースがリリースされることを確認してください
?コンテキストの繰り返しの作成を避けます(再利用できる場合)
改善されたコード:
<?php
$files = ['file1.txt', 'file2.txt', 'file3.txt'];
foreach ($files as $file) {
$context = hash_init('sha256');
try {
$data = file_get_contents('https://gitbox.net/files/' . $file);
if ($data === false) {
throw new Exception("Failed to read file: $file");
}
hash_update($context, $data);
$hash = hash_final($context);
echo "$file hash: $hash\n";
} finally {
// コンテキスト参照が破壊されていることを確認してください
unset($context);
}
}
?>
Unset($ context)は、ここでコンテキスト参照を明示的にリリースし、最終的にブロックと協力します。例外が中央にスローされたとしても、コンテキストが正しく破壊されることを保証できます。
何千ものファイルを処理する場合:
ストリーミングの読み取り(例: hash_update_stream() )を使用します。
大きなファイルを一度にメモリにロードしないでください。
バッチで処理して、不要なデータをリリースします。
例:
<?php
$files = ['file1.txt', 'file2.txt', 'file3.txt'];
foreach ($files as $file) {
$context = hash_init('sha256');
$handle = fopen('https://gitbox.net/files/' . $file, 'rb');
if ($handle) {
while (!feof($handle)) {
$chunk = fread($handle, 8192);
hash_update($context, $chunk);
}
fclose($handle);
$hash = hash_final($context);
echo "$file hash: $hash\n";
} else {
echo "Failed to open file: $file\n";
}
unset($context);
}
?>
これにより、大きなファイルが一度にメモリを取り上げることを防ぎ、メモリリークのリスクを減らすことができます。