pack()
In this article, we will focus on the PHP pack() function. We will provide you with an overview of the function, how it works, and examples of its use.
This article covers PHP's pack() function: what it does, the format-string mini-language it uses, how byte order (endianness) affects the result, and practical examples you can run — including the round trip back with unpack().
What pack() does
pack() takes ordinary PHP values (integers, floats, strings) and lays them out byte-by-byte into a single binary string — a string whose characters are raw bytes rather than readable text.
pack(string $format, mixed ...$values): string$format— a compact format string that describes, in order, how each value should be encoded (its type, size, and byte order)....$values— one or more values to encode, matched left-to-right against the format codes.
The return value is a binary string. Because those bytes are usually not printable, examples below pipe the result through bin2hex() so you can see exactly which bytes were produced (two hex digits = one byte).
When would you use it?
You reach for pack() whenever PHP has to speak a format defined in raw bytes rather than text:
- Binary protocols — building network packets where a header is "a 2-byte length followed by a 4-byte id."
- Binary file formats — writing PNG chunks, WAV headers, or any layout with fixed-width fields.
- Hashing/crypto helpers — turning a hex digest into its raw-byte form to pass to
hash_hmac()oropenssl_*. - Talking to C/embedded systems that expect fixed-size, fixed-endian fields.
For plain PHP-to-PHP storage, serialize() or json_encode() are friendlier; pack() shines when the byte layout itself matters.
A first example
123 in hexadecimal is 0x7b. The format code N means "unsigned long (4 bytes), network byte order," so the value is padded to four bytes and written most-significant-byte first: 00 00 00 7b.
Reading the format string
A format string is a sequence of format codes, each optionally followed by a repeat count:
N one unsigned long
N4 four unsigned longs in a row
N* as many unsigned longs as there are remaining values
A10 a 10-character space-padded stringYou can concatenate codes to describe a whole record. The values you pass must line up with the codes in order:
<?php
// A 2-byte short (1) followed by a 4-byte long (16909060)
$header = pack('nN', 1, 16909060);
echo bin2hex($header); // 000101020304
?>Here n produces 00 01 (the short 1) and N produces 01 02 03 04 (the long 16909060, which is 0x01020304). The bytes appear in exactly the order the codes were written.
Byte order (endianness)
The same number can be stored with its bytes in two opposite orders, and pack() gives you a code for each:
- Big-endian (a.k.a. network byte order) stores the most-significant byte first — codes
n(short) andN(long). - Little-endian stores the least-significant byte first — codes
v(short) andV(long).
<?php
echo bin2hex(pack('N', 1)), "\n"; // 00000001 (big-endian)
echo bin2hex(pack('V', 1)), "\n"; // 01000000 (little-endian)
?>This matters because the receiver must use the same convention to read the data back. Network protocols standardize on big-endian (N/n); many file formats (and x86 memory dumps) use little-endian (V/v). When in doubt, pick N/n for interchange — that is what "network byte order" guarantees.
Common format codes
A selection of the most-used codes (see the PHP manual for the full list):
| Code | Meaning |
|---|---|
a | NUL-padded string |
A | Space-padded string |
c / C | Signed / unsigned char (1 byte) |
s / S | Signed / unsigned short, native byte order (2 bytes) |
n / N | Unsigned short / long, big-endian |
v / V | Unsigned short / long, little-endian |
f / d | Float / double, machine format |
H / h | Hex string, high / low nibble first |
String codes use the repeat count as a field width, not a value count:
<?php
echo pack('A6', 'PHP'), "|"; // PHP | (padded to 6 chars with spaces)
?>Round trip: pack() then unpack()
pack() writes bytes; unpack() reads them back into PHP values. To recover the original data you must describe the same layout, and unpack() additionally needs a name for each field:
<?php
// Encode two fields
$binary = pack('Nn', 65536, 7);
// Decode using the same layout, naming each field
$values = unpack('Nfirst/nsecond', $binary);
echo $values['first'], ' ', $values['second']; // 65536 7
?>The slash (/) separates named fields in the unpack() format. If the layouts on the two sides don't match, you get garbage — encoding and decoding are tightly coupled.
Gotchas
- Codes vs. values must align. Passing fewer values than codes triggers a warning and returns
false; extra values are silently ignored (unless you used*). - Integer overflow is truncated, not flagged.
pack('C', 300)keeps only the low byte (300 & 0xFF = 44) instead of raising an error — validate ranges yourself. - Native codes (
s,S,i,l, floats) are not portable. Their size and byte order depend on the platform. For data that crosses machines, prefer the explicit big-/little-endian codes. - The result is a binary string. Don't
echoit to an HTML page or compare it as text; inspect it withbin2hex()or write it to a binary file/stream.
For the inverse operation, continue with the unpack() chapter. To see the raw bytes in your own experiments, the bin2hex() helper used throughout this page is invaluable.
Conclusion
pack() converts PHP values into a precisely controlled binary string, with a compact format mini-language for the field type, size, and byte order. Use it whenever you must produce bytes that another system reads on its own terms — binary protocols, file formats, or crypto routines — and pair it with unpack() to read the data back.