ファイルを選択
  1. base64hub
  2. 各言語での Base64
  3. PHP
コード

PHP での Base64

PHP には base64_encode と base64_decode の 2 つの関数と、大きなファイル向けのストリームフィルターがあります。以下の出力はすべて PHP 8.4 のものです。

文字列のエンコードとデコード

PHP の文字列はバイト列なので、base64_encode は文字列が保持しているバイト列をそのままエンコードします。ソースファイルを UTF-8 で保存していれば、変換なしで UTF-8 の Base64 が得られます。

<?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 からテキストへの変換 ツールでは、結果に対して文字コードを切り替えて試せます。

strict モード:base64_decode($s, true)

2 番目の引数がないと、base64_decode はアルファベットにない文字をすべて捨て、残りをデコードします。true を付けると、代わりに false を返します。

var_dump(base64_decode("aGk=!!"));        // string(2) "hi"
var_dump(base64_decode("aGk=!!", true));  // bool(false)

有効な空の入力は falsy な "" にデコードされるので、チェックには必ず === を使ってください。

$bytes = base64_decode($input, true);
if ($bytes === false) {
    throw new InvalidArgumentException('Invalid Base64');
}

strict モードは、名前から想像するほど厳格ではありません。PHP 8.4 での検証結果:

入力strict モードの結果
aGk(パディングなし)"hi"
"aGk=\n"(末尾に改行)"hi"
aGl=(パディングビットが 0 でない)"hi"
aGk==(パディング過多)false
a(あり得ない長さ)false
PDw_Pz8-Pg==(URLセーフの文字)false

正規形の Base64 だけを受け付けたいなら、空白を取り除き、strict モードでデコードしてから、base64_encode($bytes) === $input を確認してください。Base64 バリデーター は同じチェックを行い、不正な文字を指摘します。

パディングあり・なしの URLセーフ Base64

PHP には Base64URL の組み込み関数がありません。2 つの文字を strtr で入れ替えます。1 文字単位の置き換えなら 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");     // <<???>>

strict モードはパディングの不足を許容するので、デコード前に = を戻す必要はありません。違いについて詳しくは Base64 と Base64URL の違い をご覧ください。

ファイルのエンコードとデコード

echo base64_encode(file_get_contents("hello.txt"));   // aGVsbG8K

file_put_contents("copy.txt", base64_decode("aGVsbG8K", true));

Data URI にするには、'data:image/png;base64,' . base64_encode($png) のように MIME タイプを前に付けます。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);

デコードも同じ方法でできますし、php://filter URL を使えば 1 行で済みます。

copy("php://filter/read=convert.base64-decode/resource=big.b64", "big.out");

300 KB のテストファイルでは、どちらもメモリ上の関数と同一の出力になりました。各チャンクが 3 バイトの倍数(たとえば fread($in, 3 * 8192))なら、手書きのループでも動作します。それ以外のサイズでは、出力の途中に = が入ります。

MIME の折り返し

メール本文では 1 行 76 文字以内にする必要があります。chunk_split はまさにそれを行い、デフォルトの行末は \r\n です。

echo chunk_split(base64_encode(str_repeat("x", 60)));
// eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
// eHh4

末尾にも \r\n が付きます。メールライブラリならこれを自動で行います。形式は MIME Base64 エンコーダー で確認できます。

よくある落とし穴

  • 信頼できない入力で strict モードを忘れる。 不正なデータがランダムなバイト列にデコードされ、原因から遠く離れた場所で失敗します。
  • URLセーフな入力をデフォルトのモードで処理する。 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セーフなバリアントか rawurlencode を使ってください。
  • パスワードの保存に使う。 Base64 は何も隠しません。パスワードには password_hash を使い、エンコードと暗号化の違い もご覧ください。

バージョンごとの注意

  • base64_encode、base64_decode、$strict フラグは、サポート中のすべての PHP バージョンにあります(strict モードは 5.2 で追加)。
  • PHP 8.0 以降、base64_decode の戻り値は string|false と宣言され、配列やオブジェクトを渡すと警告ではなく TypeError がスローされます。
  • convert.base64-encode フィルターは MIME 出力用に line-length と line-break-chars オプションを受け付けます。例:stream_filter_append($out, 'convert.base64-encode', STREAM_FILTER_WRITE, ['line-length' => 76, 'line-break-chars' => "\r\n"])。

よくある質問

base64_decode の 2 番目の引数は何をするものですか?

base64_decode($s, true) で strict モードが有効になり、入力に Base64 のアルファベット外の文字が含まれていると false を返します。指定しないと、そうした文字は黙って捨てられます。

PHP に base64url_encode 関数はありますか?

ありません。エンコードには rtrim(strtr(base64_encode($data), '+/', '-_'), '=') を、デコードには base64_decode(strtr($s, '-_', '+/'), true) を使ってください。strict モードはパディングの不足を受け付けます。

base64_decode の strict モードは完全なバリデーターですか?

いいえ。空白、パディングの不足、aGl= のような 0 でないパディングビットは受け付けてしまいます。厳密に検証するなら、結果を再エンコードして、正規化した入力と比較してください。

base64_decode が false ではなくおかしなデータを返すのはなぜですか?

おそらく true を付けずに呼び出しています。デフォルトのモードでは、base64_decode('PDw_Pz8-Pg==') は _ と - を捨てて、誤った 6 バイトを返します。URLセーフな入力は先に strtr で変換してください。

続けて読む