Codificar y decodificar una cadena
Las cadenas de PHP son cadenas de bytes, así que base64_encode codifica los bytes que contenga la cadena. Un archivo fuente guardado en UTF-8 te da Base64 de UTF-8 sin ningún paso de conversión:
<?php
$enc = base64_encode("héllo wörld");
echo $enc; // aMOpbGxvIHfDtnJsZA==
echo base64_decode($enc); // héllo wörld
Si el texto llega de una base de datos o de un formulario en otro juego de caracteres, conviértelo antes con mb_convert_encoding($s, 'UTF-8', 'Windows-1252'). Si no, el Base64 será correcto para esos bytes, pero en otro sistema se decodificará como mojibake. La herramienta Base64 a texto te deja probar distintos juegos de caracteres con el resultado.
Modo estricto: base64_decode($s, true)
Sin el segundo argumento, base64_decode descarta cualquier carácter que no esté en el alfabeto y decodifica el resto. Con true, devuelve false:
var_dump(base64_decode("aGk=!!")); // string(2) "hi"
var_dump(base64_decode("aGk=!!", true)); // bool(false)
Compara siempre con ===, porque una entrada vacía válida se decodifica como "", que se evalúa como falso:
$bytes = base64_decode($input, true);
if ($bytes === false) {
throw new InvalidArgumentException('Invalid Base64');
}
El modo estricto es menos estricto de lo que sugiere su nombre. Esto se probó en PHP 8.4:
| Entrada | Resultado en modo estricto |
|---|---|
aGk (sin relleno) | "hi" |
"aGk=\n" (salto de línea final) | "hi" |
aGl= (bits de relleno distintos de cero) | "hi" |
aGk== (relleno de más) | false |
a (longitud imposible) | false |
PDw_Pz8-Pg== (caracteres URL-safe) | false |
Si solo quieres Base64 canónico, quita los espacios en blanco, decodifica en modo estricto y comprueba después que base64_encode($bytes) === $input. El validador Base64 hace las mismas comprobaciones y señala el carácter incorrecto.
Base64 URL-safe con y sin relleno
PHP no tiene ninguna función integrada para Base64URL. Cambia los dos caracteres con strtr, que es más rápido que str_replace para correspondencias de un solo carácter:
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 el modo estricto tolera la falta de relleno, no hace falta volver a añadir = antes de decodificar. Más sobre las diferencias en Base64 frente a Base64URL.
Codificar y decodificar un archivo
echo base64_encode(file_get_contents("hello.txt")); // aGVsbG8K
file_put_contents("copy.txt", base64_decode("aGVsbG8K", true));
Para un data URI, antepón el tipo MIME: 'data:image/png;base64,' . base64_encode($png). mime_content_type($path) puede detectar el tipo si fileinfo está activado.
Archivos grandes con filtros de stream
file_get_contents carga el archivo entero y la copia en Base64, así que el uso de memoria es de unas 2,3 veces el tamaño del archivo. El filtro de stream convert.base64-encode codifica mientras 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);
La decodificación funciona igual, o en una línea con una URL php://filter:
copy("php://filter/read=convert.base64-decode/resource=big.b64", "big.out");
Con un archivo de prueba de 300 KB, las dos formas produjeron una salida idéntica a la de las funciones en memoria. Un bucle manual también sirve si cada bloque es múltiplo de 3 bytes, por ejemplo fread($in, 3 * 8192). Cualquier otro tamaño inserta = en mitad de la salida.
Cortes de línea MIME
Los cuerpos de correo necesitan líneas de 76 caracteres como máximo. chunk_split hace exactamente eso, con finales de línea \r\n por defecto:
echo chunk_split(base64_encode(str_repeat("x", 60)));
// eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
// eHh4
También añade un \r\n final. Las bibliotecas de correo lo hacen por ti; el codificador Base64 MIME muestra el formato.
Errores habituales
- Olvidar el modo estricto con entradas que no son de confianza. Los datos incorrectos se decodifican como bytes aleatorios y fallan en algún punto muy alejado de la causa.
- Entradas URL-safe en el modo por defecto.
base64_decode("PDw_Pz8-Pg==")devuelvestring(6) "<<���", no<<???>>. Traduce antes constrtr. - Pasar
null. En PHP 8.1+ obtienesDeprecated: base64_encode(): Passing null to parameter #1 ($string) of type string is deprecated. Pasar un array lanzaTypeError: base64_encode(): Argument #1 ($string) must be of type string, array given. - El
+que se convierte en espacio. El Base64 estándar dentro de una query string se estropea al decodificar la URL, porque ahí+significa espacio. Usa la variante URL-safe orawurlencode. - Usarlo para guardar contraseñas. Base64 no oculta nada. Usa
password_hashpara las contraseñas y consulta codificación frente a cifrado.
Notas de versión
base64_encode,base64_decodey el parámetro$strictexisten en todas las versiones de PHP con soporte (el modo estricto se añadió en la 5.2).- Desde PHP 8.0,
base64_decodedeclara que devuelvestring|false, y pasarle un array o un objeto lanzaTypeErroren lugar de un aviso. - El filtro
convert.base64-encodeacepta las opcionesline-lengthyline-break-charspara salida MIME, por ejemplostream_filter_append($out, 'convert.base64-encode', STREAM_FILTER_WRITE, ['line-length' => 76, 'line-break-chars' => "\r\n"]).
Preguntas frecuentes
¿Para qué sirve el segundo parámetro de base64_decode?
base64_decode($s, true) activa el modo estricto: la función devuelve false cuando la entrada contiene caracteres fuera del alfabeto Base64. Sin él, esos caracteres se descartan sin avisar.
¿PHP tiene una función base64url_encode?
No. Usa rtrim(strtr(base64_encode($data), '+/', '-_'), '=') para codificar y base64_decode(strtr($s, '-_', '+/'), true) para decodificar. El modo estricto acepta que falte el relleno.
¿El modo estricto de base64_decode es un validador completo?
No. Sigue aceptando espacios en blanco, la falta de relleno y bits de relleno distintos de cero, como en aGl=. Para una validación exacta, vuelve a codificar el resultado y compáralo con la entrada normalizada.
¿Por qué base64_decode devuelve basura en lugar de false?
Probablemente la llamaste sin true. En el modo por defecto, base64_decode('PDw_Pz8-Pg==') descarta _ y - y devuelve seis bytes incorrectos. Traduce antes la entrada URL-safe con strtr.