Strings7 min readExamples run on PHP 8.5.10

bin2hex()

Learn how PHP 8.4 bin2hex() turns every byte of a string into two lowercase hexadecimal characters, with examples, edge cases and pitfalls.

Signature

bin2hex(string $string): string

Purpose

bin2hex() converts a string into its hexadecimal representation. PHP strings are sequences of bytes, and the function reads them one byte at a time. Each byte becomes two hexadecimal characters, with the high nibble (the upper four bits) written first. The result contains only ASCII characters, so it is safe to print, log, put in JSON or compare visually even when the original data contains NUL bytes, control characters or invalid UTF-8.

Typical uses include displaying binary values such as hashes or tokens, debugging what a string really contains, and building text-safe identifiers from raw bytes.

Signature

Parameters and defaults

  • $string: the data to encode. It has no default and is required. The function treats the value as raw bytes and ignores any character encoding.

There are no optional parameters. You cannot choose uppercase output, a separator or a grouping size; if you want those, post-process the result with functions such as strtoupper() or str_split().

Return value and failure cases

The function returns a string whose length is always exactly twice the byte length of the input. The digits a to f are produced in lowercase. An empty string produces an empty string.

There is no false return for normal input, so you do not need to check for failure. The only practical error is a type problem: with declare(strict_types=1), passing a non-string such as an integer throws a TypeError. Without strict types, scalar values are coerced to strings first, so the integer 255 is treated as the three characters "255", not as the single byte 0xFF. That surprises many people.

Example 1: basic text

Each ASCII character is one byte, so the output has two hex digits per character. An empty string gives an empty result, shown here between a line start and a pipe character.

Example
<?php
echo bin2hex('Hello'), "\n";
echo bin2hex('AB'), "\n";
echo bin2hex(''), "|\n";
Output
48656c6c6f
4142
|

H is byte 0x48, e is 0x65, and so on. A is 0x41 and B is 0x42.

Example 2: bytes, not characters

The function does not know about characters. A single accented letter in UTF-8 uses two bytes, so you get four hex digits. Bytes that are not printable, such as NUL, still produce readable output.

Example
<?php
$text = "\u{00E9}";
echo bin2hex($text), "\n";
echo strlen($text), "\n";
echo bin2hex("\x00\xFF\x10"), "\n";
Output
c3a9
2
00ff10

The escape \u{00E9} creates the UTF-8 encoding of the letter, which is the byte pair c3 a9. This is why strlen() reports 2, and why the length of a bin2hex() result is a count of bytes times two, never a count of characters times two.

Example 3: formatting and round trip

Because the output is always made of two-digit pairs, you can split it into bytes with str_split($hex, 2). The inverse function hex2bin() restores the original bytes.

Example
<?php
$bytes = chr(1) . chr(127) . chr(128) . chr(255);
$hex = bin2hex($bytes);
echo json_encode([
    'hex' => $hex,
    'length' => strlen($hex),
    'pairs' => str_split($hex, 2),
    'roundTrip' => hex2bin($hex) === $bytes,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"hex":"017f80ff","length":8,"pairs":["01","7f","80","ff"],"roundTrip":true}

Note that single-digit byte values are padded with a leading zero, so 0x01 appears as 01. That fixed width is what makes the encoding reversible and easy to split.

Edge cases

  • Empty input: returns "", and hex2bin('') also gives an empty string.
  • Binary safety: NUL bytes and bytes above 0x7F are handled like any other byte.
  • Multibyte text: the output reflects the bytes actually stored in the string, which depends on how the string was created or encoded.
  • Size: the result needs twice as much memory as the input, which matters for very large strings.
  • Case: output is lowercase. Hex comparisons against uppercase values should normalize case first, for example with strtolower().

Common mistakes

  1. Using it to convert numbers. bin2hex(255) encodes the text "255". To write an integer in base 16, use dechex() instead.
  2. Treating the result as encryption. Hex is only a reversible representation. It hides nothing and adds no security.
  3. Counting characters. The length of the result equals twice the number of bytes, not characters. Use strlen() for bytes and a multibyte function when you really need characters.
  4. Expecting separators or uppercase. There is no option for either; format the string yourself afterwards.
  5. Feeding odd-length hex to the inverse. A valid bin2hex() result always has an even length, but hand-written hex may not, and hex2bin() will reject it with a warning.
  • hex2bin() decodes a hexadecimal string back into bytes.
  • pack() builds binary strings from typed values.
  • dechex() converts an integer to a hexadecimal number string.
  • base64_encode() is a more compact text-safe encoding for binary data.
  • str_split() helps group the hex pairs for display.

Recap

bin2hex(string $string): string encodes every byte as two lowercase hex digits, high nibble first. The output is ASCII, always twice as long as the input in bytes, and reversible with hex2bin(). Remember that it works on bytes rather than characters or numbers, and that it is an encoding, not protection.

Tested examples

Basic text and empty string
<?php
echo bin2hex('Hello'), "\n";
echo bin2hex('AB'), "\n";
echo bin2hex(''), "|\n";
Output
48656c6c6f
4142
|
Bytes, not characters
<?php
$text = "\u{00E9}";
echo bin2hex($text), "\n";
echo strlen($text), "\n";
echo bin2hex("\x00\xFF\x10"), "\n";
Output
c3a9
2
00ff10
Formatting and round trip
<?php
$bytes = chr(1) . chr(127) . chr(128) . chr(255);
$hex = bin2hex($bytes);
echo json_encode([
    'hex' => $hex,
    'length' => strlen($hex),
    'pairs' => str_split($hex, 2),
    'roundTrip' => hex2bin($hex) === $bytes,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"hex":"017f80ff","length":8,"pairs":["01","7f","80","ff"],"roundTrip":true}