Signature
hash_update(HashContext $context, string $data): truePurpose
hash_update() adds a piece of data to a hashing context that was created with hash_init(). It is the middle step of PHP's incremental hashing workflow:
hash_init()creates aHashContextfor an algorithm.hash_update()feeds the context one chunk of the message, as many times as needed.hash_final()finishes the calculation and returns the digest.
This is useful when the message arrives in pieces, or when you do not want to build one large string in memory. The digest is the same as the one-shot hash() would give for the concatenated message.
Signature
Parameters and defaults
$context: aHashContextobject returned byhash_init(). It must not have been finalized yet. The function modifies this object in place, so you do not need to reassign it.$data: the next chunk of the message. It is treated as raw bytes. Neither parameter has a default value.
Return value and failures
The function always returns true. Since PHP 8.4 the declared return type is true instead of bool. There is no false result to check for, so wrapping the call in an if adds nothing. Failures show up as errors instead. Passing a context that has already been finalized with hash_final() raises an error, and passing something that is not a HashContext raises a TypeError. Before PHP 7.2 the context was a resource rather than an object.
Example 1: Feeding a message in two chunks
The two calls below hash the same bytes as hash('md5', 'hello world').
$ctx = hash_init('md5');
hash_update($ctx, 'hello');
hash_update($ctx, ' world');
echo hash_final($ctx), "\n";5eb63bbbe01eeed093cb22bb8f5acdc3
Notice that the space belongs to the second chunk. Every byte you pass is part of the message.
Example 2: Looping over chunks and comparing with hash()
In real code the chunks usually come from a loop. Here the chunks are an in-memory array, and the result is compared with the one-shot function.
$ctx = hash_init('sha256');
foreach (['a', 'bc'] as $chunk) {
hash_update($ctx, $chunk);
}
$incremental = hash_final($ctx);
echo json_encode([
'same' => $incremental === hash('sha256', 'abc'),
'digest' => $incremental,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"same":true,"digest":"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}
How the message is split does not matter. 'a' plus 'bc' gives the same digest as 'abc' in one piece, or as three one-byte chunks.
Example 3: The return value and empty data
The return value is always true, and an empty string leaves the state unchanged.
$ctx = hash_init('md5');
var_dump(hash_update($ctx, ''));
echo hash_final($ctx), "\n";bool(true) d41d8cd98f00b204e9800998ecf8427e
The final digest is the well-known MD5 of the empty message, because no bytes were added.
Edge cases
- Empty chunks are harmless. They are accepted and do not change the digest.
- Binary data is fine. The function does not care about encodings, so multibyte text is hashed as its raw bytes. If two systems must agree on a digest, they must agree on the encoding first.
- Finalized contexts cannot be updated. Once
hash_final()has run, start again with a newhash_init(). If you need the digest of a prefix and also of a longer message, callhash_copy()before finalizing so you can continue with the copy. - HMAC contexts work the same way. A context created with
hash_init()using theHASH_HMACflag and a key accepts chunks throughhash_update()too. - Large inputs are the main reason to use this function. Feed pieces of any size, and memory use stays bounded by the chunk size rather than the message size.
Common mistakes
- Reassigning the result. Writing
$ctx = hash_update($ctx, $data);replaces the context withtrue, and the next call fails. The function does not return the context. - Forgetting
hash_final(). Updating a context never produces a digest by itself. You must callhash_final()to get the result. - Reusing a context after finalizing. A finished context is spent. Create a new one for a new message.
- Hashing a transformed chunk. If you trim, re-encode or add separators to some chunks but not to the one-shot comparison, the digests will differ. Only the exact bytes passed in matter.
- Testing the return value. Because the result is always
true, anif (!hash_update(...))branch can never run. - Using fast hashes for passwords. Incremental hashing with MD5 or SHA-256 is for checksums and integrity, not for storing passwords. Use
password_hash()for that.
Related functions
hash_init()creates the context.hash_update_file()feeds a file into a context.hash_update_stream()feeds an open stream into a context.hash_final()finishes the context and returns the digest.hash_copy()duplicates a context so you can branch the calculation.hash()is the one-shot alternative for messages that are already fully in memory.
Recap
hash_update() appends bytes to a HashContext from hash_init(). It modifies the context in place and always returns true. How you split the message does not change the digest, and you must call hash_final() to read it. Do not update a finalized context, and do not assign the return value back to your context variable.
Tested examples
$ctx = hash_init('md5');
hash_update($ctx, 'hello');
hash_update($ctx, ' world');
echo hash_final($ctx), "\n";5eb63bbbe01eeed093cb22bb8f5acdc3
$ctx = hash_init('sha256');
foreach (['a', 'bc'] as $chunk) {
hash_update($ctx, $chunk);
}
$incremental = hash_final($ctx);
echo json_encode([
'same' => $incremental === hash('sha256', 'abc'),
'digest' => $incremental,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"same":true,"digest":"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}
$ctx = hash_init('md5');
var_dump(hash_update($ctx, ''));
echo hash_final($ctx), "\n";bool(true) d41d8cd98f00b204e9800998ecf8427e