Devi lavorare con il formato Base64? Allora questo sito è perfetto per te! Usa il nostro praticissimo strumento online per codificare o decodificare i tuoi dati.

Codifica Base64 in PHP: una guida completa

Ci sono dati che devono sopravvivere a un canale che non li ama. Un blob binario che deve stare in un campo JSON. Un'immagine che deve vivere dentro un tag HTML. Un certificato che appartiene a un file di configurazione. Un token che attraverserà URL, intestazioni e cookie. Questa è la vita quotidiana della codifica Base64: riscrive ogni tre byte di dati grezzi in quattro caratteri di un alfabeto di 64 lettere, con uno o due segni = che chiudono la coda, così il risultato è testo semplice che qualsiasi cosa può trasportare. La home page di questo sito spiega il formato in dettaglio, quindi questo articolo si concentra su ciò che PHP ti dà, su ciò che decide in silenzio per te e su dove si nascondono le trappole.

Sul lato PHP, la storia parte con una buona notizia: base64_encode() vive nel core dal PHP 4, prende un solo argomento, restituisce sempre una stringa e non può fallire. Nessuna modalità rigorosa, nessuna via d'errore, nessuna configurazione. La codifica è deterministica: gli stessi byte producono sempre le stesse lettere. Il tuo lavoro da sviluppatore non è far funzionare la funzione, è far comportare bene il mondo intorno: scegliere l'alfabeto giusto per la destinazione, aggiungere gli a capo giusti, convertire nel set di caratteri giusto e portare con gli occhi aperti il conto delle dimensioni del 33 percento. (Il Base64 normalmente espande i dati di circa un terzo, quattro caratteri per ogni tre byte in input; tienilo in un angolo della mente, perché torna continuamente a presentarsi.)

A fine articolo saprai produrre ogni variante di Base64 che uno sviluppatore PHP incontra davvero: output su una riga, posta spezzata in stile MIME, chiavi in armatura PEM, token sicuri per URL e data URI, più i trucchi dello streaming per quando i dati sono troppo grandi per stare in memoria.

Una funzione, zero opzioni

L'intera API, esattamente così come la riporta il PHP moderno:

base64_encode(string $string): string

Leggi di nuovo. Un parametro, un valore restituito, nessuna opzione. Il manuale lo descrive come base64 MIME, "progettato per far sopravvivere i dati binari al trasporto attraverso livelli di trasporto che non sono trasparenti a 8 bit, come i corpi della posta". Nota ciò che questa formulazione non promette: nessun a capo, nessuna spezzatura, nessuna opinione su dove l'output finirà. La funzione emette una riga lunga, e qualunque spezzatura la destinazione voglia è compito tuo con una seconda chiamata. Dal PHP 8.0, la firma porta tipi nativi; dal PHP 8.1, passare null scatena un avviso di deprecazione, quindi coalesca prima a '' qualsiasi valore che può essere null.

Le dimensioni dell'output seguono un fisso schema che puoi prevedere prima di chiamare:

Byte in input Caratteri in output Riempimento
0 0 nessuno
1 4 due =
2 4 un =
3 4 nessuno
3,000,000 4,000,000 nessuno
100,000 133,336 due =

Lo schema è quattro caratteri per ogni gruppo completo di tre byte, più un gruppo parziale finale riempito con uno o due segni =. Una conseguenza da sapere: un byte e tre byte producono entrambi quattro caratteri, quindi la lunghezza codificata nasconde le dimensioni esatte dell'input. Puoi stimarle (dividi per quattro, moltiplica per tre, sottrai i segni di riempimento) ma non puoi leggerle esattamente.

Dove finiscono gli a capo

Dato che base64_encode() non spezza mai da solo, la decisione sulla spezzatura è un problema della destinazione. Nella pratica ci sono tre risposte.

Nessun a capo. L'output grezzo della funzione, esattamente una riga. È quello che vuoi per URL, carichi utili JSON, intestazioni, valori di database e tutto il resto dove un a capo sarebbe un bug. È anche ciò che la maggior parte della gente intende con "dammi solo il Base64".

Spezzatura MIME: 76 caratteri più CRLF. La convenzione di posta di RFC 2045, sezione 6.8: le righe codificate non devono superare i 76 caratteri, e i decoder devono ignorare gli a capo. La chiamata compagna classica è chunk_split(), che il manuale affianca a base64_encode() nella sua lista "Vedi anche" per esattamente questa ragione:

$binary = file_get_contents('/var/www/uploads/report.pdf');
$wrapped = chunk_split(base64_encode($binary), 76, "\r\n");

Spezzatura PEM: 64 caratteri più LF. Chiavi e certificati usano la convenzione più vecchia di Privacy-Enhanced Mail (RFC 1421): righe più corte da 64 caratteri. Stesso strumento, numeri diversi:

$der = file_get_contents('/etc/ssl/raw-key.der');
$armor = "-----BEGIN PRIVATE KEY-----\n"
  . chunk_split(base64_encode($der), 64, "\n")
  . "-----END PRIVATE KEY-----\n";

C'è un trabocchetto di chunk_split() che vale per entrambe le spezzature: la funzione aggiunge il separatore alla fine del risultato anche quando la lunghezza dell'input è un multiplo esatto della lunghezza di riga. Se un consumatore a valle inciampa su una riga vuota finale, la causa è questa; una chiamata a rtrim() sul separatore la risolve. Nota anche l'asimmetria che ti salverà un giorno: i decoder ignorano completamente gli a capo, quindi un carico utile spezzato in stile MIME e uno non spezzato si decodificano negli stessi byte. La spezzatura è una cortesia per gli strumenti e per gli umani basati su righe, non una differenza semantica.

Rendere l'output sicuro per URL

L'alfabeto standard include + e /, e fuori da un file di testo sono entrambi guai. Un + in una stringa di query codificata come modulo diventa uno spazio prima che la tua applicazione lo veda, e / è un separatore di percorso negli URL. I nomi di file e i token hanno ognuno i propri reclami. RFC 4648, sezione 5, risolve il problema con l'alfabeto sicuro per URL e nomi di file: + diventa -, / diventa _ e il riempimento = finale viene di solito eliminato. Il RFC insiste che questo "non debba essere considerato la stessa cosa della codifica base64", quindi trattalo come un formato distinto, comunemente chiamato base64url.

Produrlo richiede due operazioni sulle stringhe:

function base64url_encode(string $data): string
{
  return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
var_dump(base64url_encode("hi>?there\x00bin")); // string(18) "aGk-P3RoZXJlAGJpbg"

Quando usarlo: le parti dei JSON Web Token, i parametri state e nonce di OAuth, gli ID delle API che metti nei percorsi degli URL, e tutto ciò che verrà copiato in una barra degli indirizzi o in un nome di file. Quando non usarlo: i corpi delle e-mail, l'armatura PEM e qualsiasi posto dove dall'altra parte c'è un consumatore dell'alfabeto standard, perché - e _ non sono nel suo vocabolario. E non mescolare i due alfabeti in silenzio: un token codificato in modo URL-safe deve essere decodificato in modo URL-safe, dappertutto, per sempre. Questa è l'intera regola di interoperabilità del base64url.

Unicode e i byte che intendevi

Le stringhe PHP sono sequenze di byte, e base64_encode() codifica qualunque byte gli venga passato senza chiedere che cosa significhino. È una caratteristica, finché un giorno il tuo "testo" non è in realtà la codifica che credi. Il fallimento classico: una stringa che nel tuo editor sembra UTF-8, ma che è arrivata da una fonte legacy come Windows-1252. Codifica quei byte così come sono, e il ricevente, che decodificherà dando per scontato l'UTF-8, si ritroverà del testo incomprensibile al posto delle tue lettere accentate.

La soluzione è normalizzare prima di codificare, con l'estensione mbstring (spedita con il sorgente PHP, ma non abilitata per impostazione predefinita):

$fromLegacy = "caf\xE9 au lait"; // byte Windows-1252: l'è è 0xE9
$utf8 = mb_convert_encoding($fromLegacy, 'UTF-8', 'Windows-1252');
$encoded = base64_encode($utf8);
var_dump($utf8); // string(13) "café au lait": adesso l'è è due byte UTF-8

Se la fonte è già UTF-8, puoi saltare la conversione, e un controllo di sana ragionevolezza a buon mercato è mb_check_encoding($utf8, 'UTF-8'). Un consiglio in una frase: non provare mai a "correggere" una stringa Base64 già codificata ricodificandola come testo. È la trappola della doppia codifica della sezione delle trappole qui sotto, ed è il bug Base64 più comune in assoluto nei codebase PHP.

File, blob e la convenzione .b64

Il lavoro di codifica più lineare: un file diventa testo. Le stringhe PHP sono byte, quindi non c'è un "modo binario" di cui preoccuparsi: file_get_contents() ti passa i byte esatti e base64_encode() ti passa il testo esatto:

$binary = file_get_contents('/var/www/uploads/photo.png');
$encoded = base64_encode($binary);
file_put_contents('/var/www/uploads/photo.png.b64', $encoded);
var_dump(strlen($binary), strlen($encoded)); // il conto del 33%, ogni volta

Due abitudini tengono al sicuro questa operazione. Prima: sapere cosa stai codificando. La classe finfo (l'estensione fileinfo, inclusa nelle build PHP standard) ti dice il tipo vero dai byte, non dal nome del file:

$mime = (new finfo(FILEINFO_MIME_TYPE))->file('/var/www/uploads/photo.png');
var_dump($mime); // string(9) "image/png"

Seconda: ricorda il conto delle dimensioni quando pianifichi lo storage: un'immagine da 500 KB diventa un file di testo da 670 KB, e un video da 1 GB diventa un file di testo da 1,33 GB. È per questo che esiste la sezione sui dati grandi qui sotto.

Data URI: mettere un'immagine nella pagina

Un data URI incorpora il carico utile direttamente nell'URL, quindi non serve una seconda richiesta per recuperarlo. RFC 2397 definisce la forma: data:, un tipo media opzionale, un flag ;base64 opzionale, una virgola e i dati. Per i media binari come le immagini l'opzione è presente, quindi il carico utile è esattamente ciò che base64_encode() ha prodotto:

function data_uri_for(string $path): string
{
  $mime = (new finfo(FILEINFO_MIME_TYPE))->file($path);
  $payload = base64_encode(file_get_contents($path));
  return 'data:' . $mime . ';base64,' . $payload;
}
$uri = data_uri_for('/var/www/uploads/avatar.png');
echo '<img src="' . $uri . '" alt="avatar" />' . "\n";

Perché Base64 qui? Perché un URI non può contenere al sicuro byte grezzi o virgole, e l'alfabeto Base64 non richiede nessun carattere di fuga. I compromessi però sono reali. Il carico utile codificato è circa il 33 percento più grande del file, e questo ingrossa il documento HTML stesso. I browser non mettono in cache un data URI come mettono in cache l'URL di un file, quindi ogni visualizzazione della pagina richiama i byte da capo. E il RFC stesso dice che i data URI sono utili solo per valori brevi: i vecchi parser HTML avevano limiti rigidi sulla lunghezza degli attributi, e i browser moderni, molto più generosi, non amano comunque i megabyte dentro un tag. Usali per avatar, icone e piccoli grafici inline; usa file veri per tutto il resto.

JWT e token delle API

I JSON Web Token sono il consumatore di punta del Base64 nelle API moderne, e usano il dialetto URL-safe senza riempimento della sezione qui sopra. Secondo RFC 7519, un JWT compatto è tre parti base64url separate da punti: intestazione, carico utile, firma. L'intestazione e il carico utile sono JSON semplice; la firma è byte grezzi. Costruirne uno a mano è un modo gradevole per vedere ogni ingranaggio:

function base64url_encode(string $data): string
{
  return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
$header = base64url_encode(json_encode(['alg' => 'HS256', 'typ' => 'JWT']));
$payload = base64url_encode(json_encode([
  'sub' => '1234567890',
  'name' => 'John Doe',
  'iat' => 1516239022,
]));
$signingInput = $header . '.' . $payload;
$signature = base64url_encode(hash_hmac('sha256', $signingInput, $secret, true));
$token = $signingInput . '.' . $signature;

Due cose da notare. La firma è la codifica base64url dei byte grezzi dell'HMAC, ed è per questo che hash_hmac() viene chiamata con true per l'output grezzo. E l'intestazione e il carico utile sono leggibili da chiunque, ma è così per progettazione: un JWT è un biglietto firmato, non un segreto. In produzione, la firma e la verifica non le fai a mano. Il pacchetto della comunità è firebase/php-jwt (v7, richiede PHP 8.0 o più recente), installato con Composer:

composer require firebase/php-jwt
use Firebase\JWT\JWT;
$secret = 'correct-horse-battery-staple-long-enough-secret';
$token = JWT::encode([
  'sub' => '1234567890',
  'name' => 'John Doe',
  'exp' => time() + 3600,
], $secret, 'HS256');
var_dump(substr_count($token, '.')); // int(2): intestazione, carico utile, firma

Nota di versione per la linea v7 della libreria: gli algoritmi HMAC impongono una lunghezza minima della chiave, quindi un segreto HS256 più corto di 32 byte viene rifiutato prima di qualsiasi codifica. I segreti lunghi sono la norma in ogni caso; qui la libreria si limita a rifiutare di essere presa alla leggera.

La libreria gestisce per te la conversione base64url, la firma e i controlli di scadenza, e lancia eccezioni tipizzate invece di restituirti dati di cui ti fidi a metà. Quando scrivi token con essa, non tocchi mai base64_encode() direttamente, ed è esattamente come deve essere.

HTTP: autenticazione Basic e handshake WebSocket

Due operazioni di costruzione di intestazioni in cui PHP fa il Base64 e il protocollo fa il resto.

Autenticazione HTTP Basic (RFC 7617): il client invia Authorization: Basic più il Base64 di username:password. Costruirla è una sola concatenazione di stringhe:

function basic_authorization_header(string $username, string $password): string
{
  return 'Basic ' . base64_encode($username . ':' . $password);
}
$headers[] = 'Authorization: ' . basic_authorization_header('alice', 'secret123');

Dillo ad alta voce una volta, perché il RFC te lo fa dire: questa è codifica, non protezione. Chiunque abbia una cattura di pacchetti recupera entrambe le metà con un tasto solo, quindi l'autenticazione Basic sta solo sulle connessioni HTTPS.

Handshake WebSocket (RFC 6455): il server dimostra di aver sentito il client ecoando una chiave trasformata. Concatena la Sec-WebSocket-Key del client con una GUID magica fissa, fa SHA-1 del risultato e codifica in Base64 il digest. Questo è Base64 standard, riempimento incluso, perché vive in un'intestazione, non in un URL:

function websocket_accept_key(string $clientKey): string
{
  return base64_encode(hash('sha1', $clientKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
}
$accept = websocket_accept_key('dGhlIHNhbXBsZSBub25jZQ==');
var_dump($accept); // string(28) "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="

Questo esempio è quello del RFC stesso, il che lo rende un comodo autotest: se la tua implementazione produce gli stessi 28 caratteri, il livello WebSocket sta parlando correttamente.

Email: il caso d'uso originale

Tutto il resto in questo articolo è un discendente di un fatto unico: SMTP era progettato per trasportare ASCII a 7 bit, e la gente voleva inviare file binari. La risposta dello standard MIME, nella sezione 6.8 di RFC 2045, è stata il Base64 come Content-Transfer-Encoding, con le due regole di casa che hai già incontrato: righe di al massimo 76 caratteri, e decoder che ignorano ogni carattere fuori dall'alfabeto. Quindi un allegato PDF viaggia così:

$pdf = file_get_contents('/var/www/uploads/report.pdf');
$attachment = chunk_split(base64_encode($pdf), 76, "\r\n");
// la libreria di posta adesso mette $attachment nella parte MIME,
// con Content-Transfer-Encoding: base64

I numeri pratici: la codifica in sé costa il 33 percento, e il CRLF ogni 76 caratteri costa un po' in più, quindi un allegato di 100 KB parte come circa 137 KB di testo. Quando scrivi e-mail da PHP, le librerie (PHPMailer e i suoi parenti stabili) fanno la spezzatura per te, e tu passi loro il binario grezzo. Se un giorno vedi una muraglia di lettere larga 76 caratteri in un file .eml grezzo, adesso conosci l'esatto algoritmo che l'ha prodotta.

Armatura PEM per chiavi e certificati

Le chiavi e i certificati hanno bisogno di più di una muraglia di lettere: hanno bisogno di etichette. L'armatura PEM è una riga BEGIN, un blocco Base64 spezzato a 64 caratteri e una riga END, una convenzione ereditata da Privacy-Enhanced Mail (RFC 1421) e tenuta in vita da OpenSSL. L'estensione openssl di PHP produce e consuma questa forma direttamente:

$res = openssl_pkey_new([
  'private_key_bits' => 2048,
  'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
openssl_pkey_export($res, $pem);
// $pem è già in armatura: etichetta BEGIN, righe da 64 caratteri, etichetta END

Il caso interessante è quando l'armatura va ricostruita a mano, per esempio quando ricevi byte DER grezzi da un'API e ti serve un file PEM per uno strumento che legge solo PEM. La convenzione è 64 caratteri per riga, a capo LF, e un'etichetta che nomina il contenuto:

$rearmored = "-----BEGIN CERTIFICATE-----\n"
  . chunk_split(base64_encode($der), 64, "\n")
  . "-----END CERTIFICATE-----\n";
var_dump(openssl_x509_parse($rearmored) !== false); // bool(true): l'armatura è valida

Fai l'etichetta sbagliata e il file è spazzatura, per quanto perfetto sia il Base64. E fai la lunghezza di riga sbagliata e la maggior parte degli strumenti lo leggerà comunque, perché i decoder ignorano gli a capo, ma gli strumenti di diff e gli umani ne soffriranno. Sessantaquattro è il numero.

File di configurazione, variabili d'ambiente e database

Base64 è un contenitore di testo, il che lo rende uno strumento di contrabbando per valori che altrimenti romperebbero il loro contenitore. Un DSN del database pieno di punti e virgola e virgolette, un JWT in un file .env, un blob binario in una colonna TEXT: tutti diventano una lunga stringa sicura.

La variante a variabile d'ambiente è un rituale in due passi. Una volta, sulla macchina che costruisce la configurazione, codifichi:

echo 'DB_DSN_B64=' . base64_encode('pg:host=db;password=qu"ote') . PHP_EOL;

Poi, a ogni avvio dell'applicazione, decodifichi e validi all'avvio, così una configurazione incollata a metà fallisce ad alta voce invece che in modo criptico:

$dsn = base64_decode(getenv('DB_DSN_B64') ?: '', true);
if ($dsn === false) {
  exit('DB_DSN_B64 is not valid Base64.');
}

Per i database, la stessa idea memorizza il binario in colonne di testo. Il conto delle dimensioni vale: il valore memorizzato è circa il 33 percento più grande del blob, quindi un file da 1 MB occupa circa 1,33 MB nella colonna, e dovresti scegliere il tipo di colonna tenendolo a mente. E lo stesso avviso di ogni altra parte: questa è sicurezza di formato, non segretezza. Chiunque può leggere la configurazione o interrogare la colonna lo può invertire in una sola chiamata. Se il valore è sensibile, cifralo; il Base64 lo rende solo portatile.

Dati grandi e memoria stabile

La codifica è la direzione che ti costa: l'output è un terzo più grande dell'input, quindi un binario da 2 GB chiede 2,66 GB di stringa codificata in memoria. In un processo web longevo o su un host con limiti di memoria stretti, questa è una ragione per fare streaming invece di ingurgitare tutto, e PHP ti dà due modi.

Il primo modo è il filtro di flusso convert.base64-encode, il gemello in streaming della funzione. Accetta i parametri come array associativo: line-length per la larghezza di spezzatura e line-break-chars per il separatore, e riproduce l'effetto di chunk_split() senza tenere l'intera stringa:

$in = fopen('/var/www/uploads/big.bin', 'rb');
$out = fopen('/var/www/uploads/big.b64', 'wb');
stream_filter_append($out, 'convert.base64-encode', STREAM_FILTER_WRITE, [
  'line-length' => 64,
  'line-break-chars' => "\n",
]);
stream_copy_to_stream($in, $out);
fclose($in);
fclose($out);

Il secondo modo è il classico trucco dei 57 byte, e è un piccolo pezzo di tradizione PHP. Una riga MIME da 76 caratteri contiene esattamente 57 byte di dati originali, quindi se leggi il file di input in blocchi che sono multipli di 57 byte, ogni blocco si codifica in modo indipendente, senza bit residui da trasportare tra un blocco e l'altro. Leggere in blocchi da 8151 byte (57 per 143: 143 righe complete da 76 caratteri in output, vicine al tradizionale buffer I/O di 8192 byte di PHP) tiene la memoria piatta mentre il file esce in streaming perfetto in MIME:

$in = fopen('/var/www/uploads/big.bin', 'rb');
$out = fopen('/var/www/uploads/big.b64', 'wb');
while (!feof($in)) {
  $plain = fread($in, 57 * 143);
  $encoded = chunk_split(base64_encode($plain), 76, "\r\n");
  fwrite($out, $encoded);
}
fclose($in);
fclose($out);

Quale scegliere? Il filtro quando vuoi che PHP si occupi dell'idraulica e non ti importa dei bordi esatti dei blocchi; il loop a 57 byte quando vuoi un output MIME deterministico, ganci di avanzamento o un limite rigido sulla dimensione del buffer. In entrambi i casi, l'impronta di memoria resta quella di un blocco, non di un file.

Trappole con accento PHP

Le trappole che compaiono nei codebase PHP reali, raccolte in un unico posto:

  • Doppia codifica. Il classico: un valore che è già Base64 (da una variabile d'ambiente, da un database, da uno script precedente) passa di nuovo da base64_encode() perché nessuno ha controllato. Il risultato si decodifica una volta e produce... ancora Base64. La cura è un controllo di andata e ritorno sul confine, o una singola funzione ben nota che possiede tutta la codifica del codebase.
  • Il + negli URL. L'output standard contiene + e /. In una stringa di query codificata come modulo, il più diventa uno spazio prima che il tuo codice lo veda; in un percorso è un separatore. Per qualsiasi cosa legata a un URL, emetti base64url o percent-encoda il valore intero con rawurlencode().
  • Il separatore finale. chunk_split() chiude il risultato con il separatore anche sui multipli esatti della lunghezza di riga. Una riga vuota finale è di solito inoffensiva (i decoder la ignorano) ma fa inciampare i contatori di righe ingenui e gli strumenti di diff. Se il consumatore è schizzinoso, rtrim() il separatore.
  • Spezzatura non in sintonia. Scrivere con righe MIME da 76 caratteri e avere un consumatore che si aspetta righe PEM da 64 caratteri (o vice versa) non è un problema di decodifica, perché i decoder ignorano gli a capo, ma è un problema per gli strumenti a righe e per la lettura umana. Scegli la convenzione che la tua destinazione si aspetta e attieniti ad essa.
  • Il cambio di riga finale fa parte dei dati. base64_encode() codifica ogni byte, compreso l'a capo alla fine di un file di testo. Quando due sistemi producono Base64 "diversi" per testo dall'aspetto uguale, l'indiziato di turno è un \n finale.
  • Base64 non è cifratura. Codificare una password prima che arrivi al database non la protegge; la formatta. La colonna "cifrata" è a una chiamata di funzione dal testo in chiaro per chiunque abbia accesso alle interrogazioni. Cifra o hasha i segreti veri; il Base64 è un costume da trasporto.
  • La memoria è un terzo più grande. Su una build PHP a 32 bit o su un host con limiti di memoria stretti, codificare un binario grande può fallire in pieno. Fallo scorrere in streaming, come mostrato qui sopra, prima di tarare memory_limit.
  • Gli a capo non vengono mai aggiunti, mai. "MIME base64" nella descrizione della funzione non significa "output spezzato in stile MIME". Se il tuo output ha bisogno di righe da 76 caratteri, le aggiungi tu con chunk_split() o con il filtro.

Una breve storia di base64_encode

Il lato codifica della storia di PHP è quasi rinfrescantemente noioso, nel migliore dei modi. base64_encode() è arrivata nel PHP 4 come funzione di core con un parametro e nessuna opzione, e da allora non ne ha guadagnato una sola. Nessuna modalità rigorosa è mai servita (quando sei tu a produrre i dati non c'è nulla su cui essere rigorosi), nessuna opzione di riempimento è mai stata aggiunta, e il lavoro di spezzatura è stato delegato a chunk_split() fin dal primo giorno, ed è per questo che le due funzioni sono ancora sedute insieme nelle liste "Vedi anche" del manuale.

Il manuale porta con sé la cifra del 33 percento da quanto a qualcuno ricorda: "I dati codificati in Base64 occupano circa il 33% di spazio in più rispetto ai dati originali". Quella frase è ancora lì oggi, ed è la ragione per cui il numero compare in questo articolo in primo luogo. Il filtro di flusso convert.base64-encode è arrivato dopo, e aveva il suo bug da cui crescere: nel 2015 PHP ha corretto un difetto (bug #68532) per cui il filtro, in modalità di lettura su flussi in memoria, poteva omettere l'ultimo carattere di riempimento, che è esattamente il tipo di corruzione silenziosa dalla quale il percorso basato sulla funzione non soffre mai. PHP 8.0 ha aggiunto i tipi nativi di parametro e restituzione string, ed è lì che il registro delle modifiche finisce. Una funzione, un parametro, vent'anni, zero opzioni: un monumento all'aver avuto la superficie giusta al primo colpo.

Fatti curiosi su PHP

Perché un riferimento non è completo senza le stranezze:

  • L'identità vuota. base64_encode('') è ''. Nessun riempimento, nessun output, nessuna sorpresa: vuoto in, vuoto fuori.
  • Un indirizzo bizzarro. Il manuale di PHP piazza base64_encode() sotto "URLs" nel libro "Altre Estensioni di Base". Non c'è un capitolo "codifica"; è sotto "URLs" che la troverai, accanto a parse_url().
  • L'alfabeto non si è mai mosso. Le stesse 64 lettere escono dall'encoder di PHP dal PHP 4. Una stringa Base64 prodotta da uno script PHP 4 su una macchina Windows nel 2001 si decodifica identicamente su PHP 8.4 su Linux oggi. È interoperabilità con un curriculum di 25 anni.
  • Un byte e tre byte hanno la stessa lunghezza. Entrambi producono quattro caratteri; solo il riempimento li distingue. È per questo che la tabella delle dimensioni in questo articolo esiste.
  • Cinquantasette è un numero magico. Una riga MIME da 76 caratteri contiene esattamente 57 byte di dati originali, ed è quella coincidenza a rendere possibile il loop di blocchi in streaming della sezione sui dati grandi senza portare stato tra una lettura e l'altra.
  • Ha un fratello dei tempi del dial-up. I "Vedi anche" di base64_encode() elencano perfino convert_uuencode(), l'avvolgente PHP per uuencode, il formato che codificava i binari per la posta prima che MIME standardizzasse Base64. Vive nel capitolo "Funzioni sulle stringhe", ma è il registro fossile dello scopo originale di questa funzione.
  • I vecchi browser erano schizzinosi sul riempimento. Una nota php.net del 2004 riferisce che Internet Explorer rifiutava nomi di cookie contenenti =, ed è per questo che il codice veterano a volte taglia i riempimenti finali dal Base64 memorizzato nei cookie. Le configurazioni moderne non hanno bisogno di quel trucco, ma spiega le strane chiamate rtrim($x, '=') che potresti ereditare.

Il rovescio della medaglia

Questo era il lato della codifica, ed è il più facile dei due: la funzione non può fallire, l'output è deterministico e il formato è uno che controlli da capo a fondo. La direzione più difficile è quella in cui ricevi il Base64 degli altri: le loro scelte di riempimento, i loro a capo, i loro dialetti URL-safe, i loro incollaggi corrotti. È lì che a un decoder servono la modalità rigorosa, una pipeline di validazione e una sana diffidenza verso tutto. La decodifica Base64 in PHP, collegata da questa pagina, copre il lato della decodifica con la stessa profondità.

Ultimo aggiornamento: 2026-09-08

Articolo correlato: Decodifica Base64 in PHP: una guida completa