Закодировать и декодировать строку
Строки в PHP – это строки байтов, так что base64_encode кодирует те байты, которые в строке лежат. Если исходный файл сохранён в UTF-8, вы получите Base64 от UTF-8 без всяких преобразований:
<?php
$enc = base64_encode("héllo wörld");
echo $enc; // aMOpbGxvIHfDtnJsZA==
echo base64_decode($enc); // héllo wörld
Если текст пришёл из базы данных или формы в другой кодировке, сначала преобразуйте его через mb_convert_encoding($s, 'UTF-8', 'Windows-1252'). Иначе Base64 будет верным для этих байтов, но в других местах декодируется в кракозябры. В инструменте Base64 в текст можно применить к результату разные кодировки.
Строгий режим: base64_decode($s, true)
Без второго аргумента base64_decode выбрасывает все символы не из алфавита и декодирует остальное. С true функция вместо этого возвращает false:
var_dump(base64_decode("aGk=!!")); // string(2) "hi"
var_dump(base64_decode("aGk=!!", true)); // bool(false)
Всегда проверяйте через ===: корректная пустая строка декодируется в "", а это ложное значение:
$bytes = base64_decode($input, true);
if ($bytes === false) {
throw new InvalidArgumentException('Invalid Base64');
}
Строгий режим не так строг, как обещает название. Проверено на PHP 8.4:
| Вход | Результат в строгом режиме |
|---|---|
aGk (нет дополнения) | "hi" |
"aGk=\n" (перевод строки в конце) | "hi" |
aGl= (ненулевые биты дополнения) | "hi" |
aGk== (лишнее дополнение) | false |
a (невозможная длина) | false |
PDw_Pz8-Pg== (URL-safe символы) | false |
Если нужен только канонический Base64, удалите пробельные символы, декодируйте в строгом режиме и проверьте base64_encode($bytes) === $input. Валидатор Base64 выполняет те же проверки и указывает на плохой символ.
URL-safe Base64 с дополнением и без
Встроенной функции для Base64URL в PHP нет. Поменяйте два символа через strtr (для посимвольных замен он быстрее, чем str_replace):
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"); // <<???>>
Поскольку строгий режим терпит отсутствие дополнения, возвращать = перед декодированием не нужно. Подробнее о различиях в статье Base64 и Base64URL.
Закодировать и декодировать файл
echo base64_encode(file_get_contents("hello.txt")); // aGVsbG8K
file_put_contents("copy.txt", base64_decode("aGVsbG8K", true));
Для data URI добавьте впереди MIME-тип: 'data:image/png;base64,' . base64_encode($png). Если включено расширение fileinfo, тип определит mime_content_type($path).
Большие файлы через потоковые фильтры
file_get_contents загружает в память и весь файл, и его копию в Base64, так что расход памяти получается около 2,3 размера файла. Потоковый фильтр convert.base64-encode кодирует данные прямо при копировании:
$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);
Декодирование устроено так же, а можно уложиться в одну строку через URL php://filter:
copy("php://filter/read=convert.base64-decode/resource=big.b64", "big.out");
На тестовом файле в 300 КБ оба способа дали тот же результат, что и функции в памяти. Ручной цикл тоже подойдёт, если каждая часть кратна 3 байтам, например fread($in, 3 * 8192). Любой другой размер вставит = в середину вывода.
Перенос строк MIME
В теле письма строки должны быть не длиннее 76 символов. Ровно это делает chunk_split, по умолчанию с окончаниями строк \r\n:
echo chunk_split(base64_encode(str_repeat("x", 60)));
// eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
// eHh4
В конце она тоже добавляет \r\n. Почтовые библиотеки делают это за вас; формат показывает кодировщик MIME Base64.
Частые подводные камни
- Забытый строгий режим для недоверенных данных. Плохие данные декодируются в случайные байты и ломают что-то далеко от места, где возникла проблема.
- URL-safe строка в режиме по умолчанию.
base64_decode("PDw_Pz8-Pg==")возвращаетstring(6) "<<���", а не<<???>>. Сначала переведите её черезstrtr. - Передача
null. В PHP 8.1+ вы получитеDeprecated: base64_encode(): Passing null to parameter #1 ($string) of type string is deprecated. Передача массива бросаетTypeError: base64_encode(): Argument #1 ($string) must be of type string, array given. +превращается в пробел. Стандартный Base64 в строке запроса портится при URL-декодировании, потому что+там означает пробел. Используйте URL-safe вариант илиrawurlencode.- Base64 как хранилище паролей. Base64 ничего не скрывает. Для паролей используйте
password_hash, а подробнее читайте в статье кодирование и шифрование.
Версии
base64_encode,base64_decodeи флаг$strictесть во всех поддерживаемых версиях PHP (строгий режим появился в 5.2).- С PHP 8.0
base64_decodeобъявлена как возвращающаяstring|false, а передача массива или объекта бросаетTypeErrorвместо предупреждения. - Фильтр
convert.base64-encodeпринимает параметрыline-lengthиline-break-charsдля вывода в стиле MIME, напримерstream_filter_append($out, 'convert.base64-encode', STREAM_FILTER_WRITE, ['line-length' => 76, 'line-break-chars' => "\r\n"]).
Вопросы и ответы
Что делает второй параметр base64_decode?
base64_decode($s, true) включает строгий режим: функция возвращает false, если во входной строке есть символы вне алфавита Base64. Без него такие символы молча отбрасываются.
Есть ли в PHP функция base64url_encode?
Нет. Для кодирования используйте rtrim(strtr(base64_encode($data), '+/', '-_'), '='), для декодирования – base64_decode(strtr($s, '-_', '+/'), true). Строгий режим допускает отсутствие дополнения.
Строгий режим base64_decode – это полноценная проверка?
Нет. Он всё равно пропускает пробелы, отсутствие дополнения и ненулевые биты дополнения вроде aGl=. Для точной проверки закодируйте результат заново и сравните с нормализованной входной строкой.
Почему base64_decode возвращает мусор, а не false?
Скорее всего, вы вызвали функцию без true. В режиме по умолчанию base64_decode('PDw_Pz8-Pg==') выбрасывает _ и - и возвращает шесть неправильных байтов. Сначала переведите URL-safe строку через strtr.