pack()
In questo articolo approfondiremo la funzione PHP pack(): panoramica, funzionamento ed esempi pratici d'uso.
Questo articolo tratta la funzione pack() di PHP: cosa fa, il mini-linguaggio delle stringhe di formato che utilizza, come l'ordine dei byte (endianness) influisce sul risultato e alcuni esempi pratici — incluso il percorso inverso con unpack().
Cosa fa pack()
pack() prende normali valori PHP (interi, float, stringhe) e li dispone byte per byte in un'unica stringa binaria — una stringa i cui caratteri sono byte grezzi anziché testo leggibile.
pack(string $format, mixed ...$values): string$format— una stringa di formato compatta che descrive, nell'ordine, come ciascun valore deve essere codificato (tipo, dimensione e ordine dei byte)....$values— uno o più valori da codificare, abbinati da sinistra a destra ai codici di formato.
Il valore restituito è una stringa binaria. Poiché quei byte di solito non sono stampabili, negli esempi di seguito il risultato viene passato a bin2hex() in modo da vedere esattamente quali byte sono stati prodotti (due cifre esadecimali = un byte).
Quando si usa?
Si ricorre a pack() ogni volta che PHP deve parlare un formato definito in byte grezzi anziché testo:
- Protocolli binari — costruire pacchetti di rete dove un header è "una lunghezza a 2 byte seguita da un id a 4 byte."
- Formati di file binari — scrivere chunk PNG, header WAV o qualsiasi layout con campi a larghezza fissa.
- Funzioni di hashing/crypto — convertire un digest esadecimale nella sua forma di byte grezzi da passare a
hash_hmac()o alle funzioniopenssl_*. - Comunicazione con sistemi C/embedded che si aspettano campi di dimensione fissa e endianness fissa.
Per la memorizzazione PHP-to-PHP, serialize() o json_encode() sono più comodi; pack() eccelle quando il layout dei byte stesso è importante.
Un primo esempio
123 in esadecimale è 0x7b. Il codice di formato N significa "unsigned long (4 byte), network byte order," quindi il valore viene riempito a quattro byte e scritto con il byte più significativo per primo: 00 00 00 7b.
Leggere la stringa di formato
Una stringa di formato è una sequenza di codici di formato, ognuno opzionalmente seguito da un contatore di ripetizione:
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 stringÈ possibile concatenare codici per descrivere un intero record. I valori passati devono corrispondere ai codici nell'ordine:
<?php
// A 2-byte short (1) followed by a 4-byte long (16909060)
$header = pack('nN', 1, 16909060);
echo bin2hex($header); // 000101020304
?>Qui n produce 00 01 (lo short 1) e N produce 01 02 03 04 (il long 16909060, ovvero 0x01020304). I byte appaiono esattamente nell'ordine in cui sono stati scritti i codici.
Ordine dei byte (endianness)
Lo stesso numero può essere memorizzato con i suoi byte in due ordini opposti, e pack() fornisce un codice per ciascuno:
- Big-endian (noto anche come network byte order) memorizza il byte più significativo per primo — codici
n(short) eN(long). - Little-endian memorizza il byte meno significativo per primo — codici
v(short) eV(long).
<?php
echo bin2hex(pack('N', 1)), "\n"; // 00000001 (big-endian)
echo bin2hex(pack('V', 1)), "\n"; // 01000000 (little-endian)
?>Questo è importante perché il destinatario deve utilizzare la stessa convenzione per leggere i dati. I protocolli di rete standardizzano sul big-endian (N/n); molti formati di file (e i dump della memoria x86) usano il little-endian (V/v). In caso di dubbio, scegliere N/n per l'interscambio — è ciò che garantisce il "network byte order."
Codici di formato più comuni
Una selezione dei codici più utilizzati (vedere il manuale PHP per l'elenco completo):
| Codice | Significato |
|---|---|
a | Stringa con padding NUL |
A | Stringa con padding di spazi |
c / C | Char con segno / senza segno (1 byte) |
s / S | Short con segno / senza segno, ordine byte nativo (2 byte) |
n / N | Short / long senza segno, big-endian |
v / V | Short / long senza segno, little-endian |
f / d | Float / double, formato macchina |
H / h | Stringa esadecimale, nibble alto / basso per primo |
I codici stringa usano il contatore di ripetizione come larghezza del campo, non come numero di valori:
<?php
echo pack('A6', 'PHP'), "|"; // PHP | (padded to 6 chars with spaces)
?>Round trip: pack() poi unpack()
pack() scrive byte; unpack() li rilegge come valori PHP. Per recuperare i dati originali è necessario descrivere lo stesso layout, e unpack() richiede inoltre un nome per ogni campo:
<?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
?>La barra (/) separa i campi con nome nel formato di unpack(). Se i layout dei due lati non coincidono, si otterranno dati corrotti — codifica e decodifica sono strettamente accoppiate.
Insidie
- Codici e valori devono essere allineati. Passare meno valori dei codici genera un avviso e restituisce
false; i valori in eccesso vengono ignorati silenziosamente (a meno che non si usi*). - L'overflow degli interi viene troncato, non segnalato.
pack('C', 300)mantiene solo il byte basso (300 & 0xFF = 44) invece di generare un errore — è necessario validare i range manualmente. - I codici nativi (
s,S,i,l, float) non sono portabili. La loro dimensione e l'ordine dei byte dipendono dalla piattaforma. Per dati che attraversano macchine diverse, preferire i codici big-/little-endian espliciti. - Il risultato è una stringa binaria. Non va usata con
echoin una pagina HTML né confrontata come testo; ispezionarla conbin2hex()o scriverla su un file/stream binario.
Per l'operazione inversa, continuare con il capitolo su unpack(). Per visualizzare i byte grezzi nei propri esperimenti, il helper bin2hex() usato in questa pagina è indispensabile.
Conclusione
pack() converte valori PHP in una stringa binaria controllata con precisione, grazie a un mini-linguaggio di formato compatto per tipo, dimensione e ordine dei byte. Si usa ogni volta che è necessario produrre byte che un altro sistema legge secondo i propri termini — protocolli binari, formati di file o routine crittografiche — abbinandola a unpack() per rileggere i dati.