Signature
hash(string $algo, string $data, bool $binary = false, array $options = []): stringPurpose
hash() computes a message digest of a string using an algorithm you choose by name. The same input and algorithm always give the same output, and a small change in the input produces a completely different digest. Typical uses are content fingerprints, cache keys, checksums and ETag-style identifiers.
A plain digest is not a way to store passwords. For passwords use password_hash() and password_verify(). For authenticating a message with a secret key use hash_hmac().
Signature
Parameters and defaults
$algo: the algorithm name, such as"sha256","md5"or"crc32b". Names are listed byhash_algos().$data: the message to hash. PHP hashes the raw bytes of the string, so the same text in a different encoding gives a different digest.$binary: defaults tofalse, which returns lowercase hexadecimal digits. Withtrueyou get the raw binary digest.$options: defaults to an empty array. The manual says only the"seed"option is supported, and only by the MurmurHash variants. Other algorithms do not use it.
Return value and failures
The function returns a string. In hex mode its length is twice the digest size in bytes, so SHA-256 gives 64 characters. In binary mode the string holds the raw bytes, so SHA-256 gives 32 bytes. These bytes are usually not printable.
If $algo is not a known algorithm, hash() throws a ValueError. Before PHP 8.0 it returned false and emitted a warning. The $options parameter was added in PHP 8.1.
Example 1: a basic digest
This is the manual's own example. The output is always 64 hex characters for SHA-256.
echo hash('sha256', 'The quick brown fox jumped over the lazy dog.'), "\n";68b1282b91de2c054c36629cb8dd447f12f096d3e3c587978dc2248444633483
Example 2: comparing algorithms
Different algorithms give digests of different lengths for the same input. Looping over a list of names keeps the code compact.
$data = 'abc';
$digests = [];
foreach (['md5', 'sha1', 'sha256'] as $algo) {
$digests[$algo] = hash($algo, $data);
}
echo json_encode($digests, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"md5":"900150983cd24fb0d6963f7d28e17f72","sha1":"a9993e364706816aba3e25717850c26c9cd0d89d","sha256":"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}
Example 3: hex versus binary, and unknown algorithms
The binary form is half the length of the hex form. Passing an unknown algorithm name raises a ValueError that you can catch.
$hex = hash('sha256', 'abc');
$raw = hash('sha256', 'abc', true);
try {
hash('not-an-algo', 'abc');
$error = 'none';
} catch (ValueError $e) {
$error = get_class($e);
}
echo json_encode([
'hexLength' => strlen($hex),
'binaryLength' => strlen($raw),
'sameDigest' => bin2hex($raw) === $hex,
'error' => $error,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"hexLength":64,"binaryLength":32,"sameDigest":true,"error":"ValueError"}
Edge cases
- Hashing an empty string is valid and returns the algorithm's digest of zero bytes.
- Hex output is always lowercase. Normalise case if you compare against digests from other systems.
- Binary output may contain null bytes and invalid UTF-8. Do not pass it to
json_encode()or print it into a text page. Usebin2hex()orbase64_encode()first. - Large inputs have to be in memory as one string. For streamed data use
hash_init(),hash_update()andhash_final(). For files usehash_file(). - Algorithm availability depends on the build. Check
hash_algos()if the name comes from configuration.
Common mistakes
- Using a fast digest for passwords. Digests like SHA-256 are designed to be fast, which helps attackers. Use
password_hash()instead. - Using MD5 or SHA-1 where security matters. They are fine for non-adversarial checksums, but should not be chosen for signatures or integrity against an attacker.
- Comparing secrets with
===or==. Usehash_equals()to compare a known digest with user-supplied input in constant time. - Expecting a hash to be reversible. A digest is one-way. You can only hash a guess and compare.
- Ignoring the
$binaryflag. Mixing hex and raw digests in comparisons silently fails because the strings differ in length and content. - Passing
$optionsto every algorithm. The seed is only for MurmurHash variants.
Related functions
hash_algos()lists the supported algorithm names.hash_init()starts an incremental hashing context.hash_file()hashes the contents of a file.hash_hmac()creates a keyed digest using HMAC.hash_equals()compares digests safely.
Recap
hash() takes an algorithm name and a string, and returns a lowercase hex digest by default or raw bytes when $binary is true. An unknown algorithm throws ValueError. It is the right tool for fingerprints and checksums, but not for password storage, and secrets should be compared with hash_equals().
Tested examples
echo hash('sha256', 'The quick brown fox jumped over the lazy dog.'), "\n";
68b1282b91de2c054c36629cb8dd447f12f096d3e3c587978dc2248444633483
$data = 'abc';
$digests = [];
foreach (['md5', 'sha1', 'sha256'] as $algo) {
$digests[$algo] = hash($algo, $data);
}
echo json_encode($digests, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"md5":"900150983cd24fb0d6963f7d28e17f72","sha1":"a9993e364706816aba3e25717850c26c9cd0d89d","sha256":"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"}
$hex = hash('sha256', 'abc');
$raw = hash('sha256', 'abc', true);
try {
hash('not-an-algo', 'abc');
$error = 'none';
} catch (ValueError $e) {
$error = get_class($e);
}
echo json_encode([
'hexLength' => strlen($hex),
'binaryLength' => strlen($raw),
'sameDigest' => bin2hex($raw) === $hex,
'error' => $error,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"hexLength":64,"binaryLength":32,"sameDigest":true,"error":"ValueError"}