Codificar e decodificar uma string
As strings do PHP são strings de bytes, então o base64_encode codifica os bytes que a string tiver. Um arquivo-fonte salvo em UTF-8 dá Base64 de UTF-8 sem nenhuma etapa de conversão:
<?php
$enc = base64_encode("héllo wörld");
echo $enc; // aMOpbGxvIHfDtnJsZA==
echo base64_decode($enc); // héllo wörld
Se o texto vem de um banco de dados ou de um formulário em outro charset, converta-o antes com mb_convert_encoding($s, 'UTF-8', 'Windows-1252'). Caso contrário, o Base64 fica correto para aqueles bytes, mas vira mojibake ao ser decodificado em outro lugar. A ferramenta Base64 para texto permite testar charsets diferentes no resultado.
Modo estrito: base64_decode($s, true)
Sem o segundo argumento, o base64_decode joga fora qualquer caractere que não esteja no alfabeto e decodifica o resto. Com true, ele retorna false:
var_dump(base64_decode("aGk=!!")); // string(2) "hi"
var_dump(base64_decode("aGk=!!", true)); // bool(false)
Sempre compare com ===, porque uma entrada vazia válida decodifica para "", que é avaliado como falso:
$bytes = base64_decode($input, true);
if ($bytes === false) {
throw new InvalidArgumentException('Invalid Base64');
}
O modo estrito é menos rígido do que o nome sugere. Testado no PHP 8.4:
| Entrada | Resultado no modo estrito |
|---|---|
aGk (sem preenchimento) | "hi" |
"aGk=\n" (quebra de linha no final) | "hi" |
aGl= (bits de preenchimento diferentes de zero) | "hi" |
aGk== (preenchimento demais) | false |
a (comprimento impossível) | false |
PDw_Pz8-Pg== (caracteres URL-safe) | false |
Se você só aceita Base64 canônico, remova os espaços em branco, decodifique no modo estrito e depois confira base64_encode($bytes) === $input. O validador de Base64 faz as mesmas verificações e aponta o caractere ruim.
Base64 URL-safe com e sem preenchimento
O PHP não tem função Base64URL embutida. Troque os dois caracteres com strtr, que é mais rápido que str_replace para mapeamentos de um caractere:
function b64url_encode(string $data, bool $pad = false): string {
$s = strtr(base64_encode($data), '+/', '-_');
return $pad ? $s : rtrim($s, '=');
}
function b64url_decode(string $s): string|false {
return base64_decode(strtr($s, '-_', '+/'), true);
}
echo b64url_encode("<<???>>"); // PDw_Pz8-Pg
echo b64url_encode("<<???>>", true); // PDw_Pz8-Pg==
echo b64url_decode("PDw_Pz8-Pg"); // <<???>>
Como o modo estrito tolera a falta de preenchimento, não é preciso recolocar os = antes de decodificar. Mais sobre as diferenças em Base64 vs Base64URL.
Codificar e decodificar um arquivo
echo base64_encode(file_get_contents("hello.txt")); // aGVsbG8K
file_put_contents("copy.txt", base64_decode("aGVsbG8K", true));
Para um data URI, acrescente o tipo MIME na frente: 'data:image/png;base64,' . base64_encode($png). O mime_content_type($path) detecta o tipo se o fileinfo estiver habilitado.
Arquivos grandes com filtros de stream
O file_get_contents carrega o arquivo inteiro e a cópia em Base64, então o uso de memória fica em cerca de 2,3 vezes o tamanho do arquivo. O filtro de stream convert.base64-encode codifica enquanto copia:
$in = fopen("big.bin", "rb");
$out = fopen("big.b64", "wb");
stream_filter_append($out, "convert.base64-encode");
stream_copy_to_stream($in, $out);
fclose($in);
fclose($out);
A decodificação funciona do mesmo jeito, ou numa linha só, por uma URL php://filter:
copy("php://filter/read=convert.base64-decode/resource=big.b64", "big.out");
Os dois geraram saída idêntica à das funções em memória num arquivo de teste de 300 KB. Um laço manual também funciona se cada bloco tiver um múltiplo de 3 bytes, por exemplo fread($in, 3 * 8192). Qualquer outro tamanho insere = no meio da saída.
Quebra de linha MIME
Corpos de e-mail precisam de linhas de no máximo 76 caracteres. O chunk_split faz exatamente isso, com finais de linha \r\n por padrão:
echo chunk_split(base64_encode(str_repeat("x", 60)));
// eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
// eHh4
Ele também acrescenta um \r\n no final. As bibliotecas de e-mail fazem isso por você; o codificador Base64 MIME mostra o formato.
Armadilhas comuns
- Esquecer o modo estrito com entrada não confiável. Dados ruins viram bytes aleatórios e falham num lugar bem longe da causa.
- Entrada URL-safe no modo padrão.
base64_decode("PDw_Pz8-Pg==")retornastring(6) "<<���", não<<???>>. Traduza comstrtrantes. - Passar
null. No PHP 8.1+ você recebeDeprecated: base64_encode(): Passing null to parameter #1 ($string) of type string is deprecated. Passar um array lançaTypeError: base64_encode(): Argument #1 ($string) must be of type string, array given. +virando espaço. Base64 padrão numa query string é estragado pela decodificação de URL, porque ali+significa espaço. Use a variante URL-safe ourawurlencode.- Usar como armazenamento de senhas. Base64 não esconde nada. Use
password_hashpara senhas e veja codificação vs criptografia.
Notas de versão
base64_encode,base64_decodee a flag$strictexistem em todas as versões suportadas do PHP (o modo estrito chegou no 5.2).- Desde o PHP 8.0, o
base64_decodeé declarado com retornostring|false, e passar um array ou objeto lançaTypeErrorem vez de um aviso. - O filtro
convert.base64-encodeaceita as opçõesline-lengtheline-break-charspara saída MIME, por exemplostream_filter_append($out, 'convert.base64-encode', STREAM_FILTER_WRITE, ['line-length' => 76, 'line-break-chars' => "\r\n"]).
Perguntas frequentes
Para que serve o segundo parâmetro do base64_decode?
base64_decode($s, true) ativa o modo estrito: a função retorna false quando a entrada contém caracteres fora do alfabeto Base64. Sem ele, esses caracteres são descartados em silêncio.
O PHP tem uma função base64url_encode?
Não. Use rtrim(strtr(base64_encode($data), '+/', '-_'), '=') para codificar e base64_decode(strtr($s, '-_', '+/'), true) para decodificar. O modo estrito aceita a falta de preenchimento.
O modo estrito do base64_decode é um validador completo?
Não. Ele ainda aceita espaços em branco, preenchimento ausente e bits de preenchimento diferentes de zero, como em aGl=. Para uma validação exata, recodifique o resultado e compare com a entrada normalizada.
Por que o base64_decode retorna lixo em vez de false?
Provavelmente você o chamou sem o true. No modo padrão, base64_decode('PDw_Pz8-Pg==') descarta o _ e o - e retorna seis bytes errados. Traduza a entrada URL-safe com strtr antes.