unpack()
Neste artigo, exploramos a função PHP unpack(), seu funcionamento e exemplos práticos de uso com dados binários.
Strings PHP são, na verdade, sequências de bytes brutos, o que as torna um contêiner natural para dados binários — cabeçalhos de imagem, pacotes de rede, formatos de arquivo e quadros de protocolo. A função unpack() lê esse fluxo de bytes brutos e o transforma em valores PHP comuns (inteiros, floats, strings) com os quais você pode trabalhar. Este artigo cobre a assinatura da função, seus códigos de formato, endianness, armadilhas comuns e como ela se combina com pack().
Sintaxe
unpack(string $format, string $data, int $offset = 0): array|false| Parâmetro | Descrição |
|---|---|
$format | Uma string de formato descrevendo como interpretar os bytes (os códigos estão listados abaixo). |
$data | A string binária a ser lida. |
$offset | Posição em bytes a partir da qual começar a leitura (adicionado no PHP 7.1). O padrão é 0. |
Retorna um array associativo com os valores desempacotados, ou false em caso de falha. unpack() é o inverso de pack(): qualquer layout que você escrever com pack() pode ser lido de volta com os mesmos códigos de formato.
Um primeiro exemplo
A string de formato é uma sequência de um ou mais códigos. Cada código é uma única letra para um tipo de dado, uma contagem de repetição opcional e um nome opcional.
Aqui "C*" significa "ler cada byte restante como um inteiro de 8 bits sem sinal". A contagem de repetição * consome todos os bytes disponíveis. Quando você não fornece um nome, unpack() numera os resultados começando em 1 (não 0):
Array
(
[1] => 1
[2] => 2
[3] => 3
[4] => 4
[5] => 5
)Códigos de formato
Cada código corresponde a um número fixo de bytes. Os mais comuns:
| Código | Tipo | Tamanho |
|---|---|---|
C / c | Char sem sinal / com sinal | 1 byte |
n | Short sem sinal, big-endian | 2 bytes |
v | Short sem sinal, little-endian | 2 bytes |
S / s | Short sem sinal / com sinal, ordem de bytes da máquina | 2 bytes |
N | Long sem sinal, big-endian | 4 bytes |
V | Long sem sinal, little-endian | 4 bytes |
L / l | Long sem sinal / com sinal, ordem de bytes da máquina | 4 bytes |
f / d | Float / double, ordem da máquina | 4 / 8 bytes |
a / A | String (preenchida com NUL / espaço) | conforme especificado |
H / h | String hexadecimal, nibble alto / baixo primeiro | por nibble |
Um número após um código o repete (C4 lê quatro chars); um * lê todos os bytes restantes.
Nomeando os campos
Formatos binários reais são compostos de campos mistos, então geralmente você dá a cada um um nome e separa os códigos com /:
"C2chars/Sint/Nlong" lê os dois primeiros bytes como chars1/chars2, os próximos dois como um short int na ordem da máquina, e os últimos quatro como um long long big-endian:
Array
(
[chars1] => 1
[chars2] => 2
[int] => 1027
[long] => 84281096
)Quando um código tem uma contagem de repetição e um nome, unpack() acrescenta um índice ao nome (chars1, chars2, …) para que os valores não colidam.
Endianness importa
Os mesmos quatro bytes significam números diferentes dependendo da ordem dos bytes. N/n são big-endian (ordem de rede); V/v são little-endian (nativo do x86); S/L seguem a máquina host e, portanto, não são portáveis. Para dados que cruzam máquinas — um formato de arquivo ou um protocolo de rede — sempre escolha um código com endianness explícito para que o resultado seja o mesmo em qualquer lugar.
<?php
$bytes = "\x01\x00\x00\x00";
print_r(unpack("Vlittle", $bytes)); // little-endian: 1
print_r(unpack("Nbig", $bytes)); // big-endian: 16777216
?>Array
(
[little] => 1
)
Array
(
[big] => 16777216
)Round-tripping com pack()
Como unpack() é o espelho de pack(), você pode serializar valores em um blob binário compacto e lê-los de volta com o formato correspondente:
<?php
$packed = pack("nN", 1027, 84281096); // build the bytes
$result = unpack("nshort/Nlong", $packed);
print_r($result);
?>Array
(
[short] => 1027
[long] => 84281096
)Armadilhas comuns
- As chaves começam em 1. Resultados sem nome são indexados a partir de 1, o que confunde ao fazer loops. Nomeie seus campos ou lembre-se do offset.
- Nomes com contagens de repetição recebem um sufixo de índice (
byte1,byte2), entãounpack("C4byte", ...)retornabyte1…byte4, não um únicobyte. - Códigos de ordem da máquina (
S,L,s,l) não são portáveis. Usen/Nouv/Vpara qualquer coisa armazenada ou transmitida. falsequando há poucos dados. Se o formato exige mais bytes do que$datacontém,unpack()retornafalsee emite um aviso — verifique o valor de retorno antes de usá-lo.
Conclusão
A função unpack() transforma bytes brutos em valores PHP usando códigos de formato compactos, e é a metade de leitura do par pack(). Domine os códigos de endianness e a sintaxe de nomenclatura de campos e você poderá analisar praticamente qualquer cabeçalho de arquivo binário ou quadro de rede. Para converter dados binários em uma string hexadecimal legível, consulte bin2hex().