Base64 是一种基于64个可打印字符来表示二进制数据的表示方法,编码后的数据比原始数据略长,为原来的 4/3。编码表由64 个字符组成:26个大写英文字母 + 26个小写英语字母 + 数字0-9 + 符号 "+" + 符号 "/",其 ASCII 值为 0-63
编码过程:
1、每 3 个字节组成一组,(3*8)组成一串24个进制位;例:111001101001010110001111
2、将 24 个进制位分 4 组,每组 6 个进制位,在前补 00 扩展成 32 个进制位 = 4 个字节;例:00111001 00101001 00010110 00001111
3、将每个字节转十进制,匹配编码表对应字符,组成新的 4 个字符
Base64 编码表
满足 3 个字节情况下(utf-8 字符集一个中文字符等于 3 个字节)
原文本 Min 经过 base64 编码后等到新字符 TWlu
不满足 3 个字节,2 个字节情况下,在编码后的字符末尾填充1个 = 号
原文本 Mi 经过 base64 编码后等到新字符 TWk=
不满足 3 个字节,1 个字节情况下,在编码后的字符末尾填充 2 个 = 号
原文本 M 经过 base64 编码后等到新字符 TQ==
utf-8 中文字符『敏』,其对应的16进制是 E6958F,转换二进制为:111001101001010110001111 占三个字节
代码
<?php
namespace Patterns\Algorithm;
class Base64
{
private $_encode_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
private $_strpad = ['==', '=', ''];
/**
* 输入字符以3个字节分组,3 个字节 = base64 4个字节
* 3个字节为一组
* * 第一个字符 = 输入第一个字符右移 2 位
* * 第二个字符 = 输入第一个字符左移 4 位 + 输入第二个字符右移 4 位
* * 第三个字符 = 输入第一个字符左移 2 位 + 输入第三个字符右移 6 位
* * 第四个字符 = 取输入第三个字符右 6 位
* base64 编码
* @param string $str
* @return string
*/
public function encodebit(string $str):string
{
$strLen = strlen($str);
$subIndex = 0;
$subStr = $out = '';
while ($strLen > 2){
$subStr = substr($str, $subIndex, 3);
$fir = ord($subStr[0]) >> 2;
$sc = ((ord($subStr[0]) & 3) << 4) + ((ord($subStr[1])) >> 4);
$th = ((ord($subStr[1]) & 15) << 2) + (ord($subStr[2]) >> 6);
$end = ord($subStr[2]) & 63;
$out .= $this->_encode_table[$fir];
$out .= $this->_encode_table[$sc];
$out .= $this->_encode_table[$th];
$out .= $this->_encode_table[$end];
$subIndex += 3;
$strLen -= 3;
}
if(0 === $strLen){
return $out;
}
$str = substr($str, $subIndex);
$out .= $this->_encode_table[ord($str[0]) >> 2];
if($strLen > 1){
$out .= $this->_encode_table[((ord($str[0]) & 3) << 4) + (ord($str[1]) >> 4)];
$out .= $this->_encode_table[(ord($str[1]) & 15) << 2];
$out .= '=';
} else {
$out .= $this->_encode_table[(ord($str[0]) & 3) << 4];
$out .= '==';
}
return $out;
}
/**
* 编码
* base64 将 3 个字节转成 4 个字节
* 每 3 个字节组成一组,(3*8)组成一串24个进制位
* 将 24 个进制位分 4 组,每组 6 个进制位,在前补 00 扩展成 32 个进制位 = 4 个字节
* 将每个字节转十进制,匹配编码表对应字符,组成新的 4 个字符
*
* 不足 3 个字节情况:
* 2 个字节:按照上述步骤可组成新的 3 个字符,在末尾补充 1 个 = 号,即 xxx=
* 1 个字节:按照上述步骤可组成新的 2 个字符,在末尾补充 2 个 = 号,即 xx==
* @param string $data
* @return string
*/
public function encode(string $data):string
{
$strLen = strlen($data);
$subIndex = 0;
$out = '';
$offset = ceil($strLen / 3);
for($i = 0; $i < $offset; $i++){
$binstr = '';
$subStr = substr($data, $subIndex, 3);
$strLen = strlen($subStr);
for($k = 0; $k < $strLen; $k++){
$decbin = decbin(ord($subStr[$k]));
$binstr .= str_pad($decbin, 8, 0, STR_PAD_LEFT);
}
$out .= $this->bintoChar($strLen, $binstr);
$subIndex += 3;
}
$out .= $this->_strpad[$strLen - 1];
return $out;
}
/**
* 将8位二进制组成的进制位进行分组,6 位一组:每组在前方增 00 填充成 8 位,不足8位则在后方补 0
* @param int $strSize
* @param string $bin
* @return string
*/
private function bintoChar(int $strSize, string $bin):string
{
$subIndex = 0;
$char = '';
while ($strSize >= 0){
$substr = '00' . substr($bin, $subIndex, 6);
$substr = str_pad($substr, 8, 0, STR_PAD_RIGHT);
$char .= $this->_encode_table[bindec($substr)];
$subIndex += 6;
$strSize--;
}
return $char;
}
}
$base64 = new Base64();
$str = 'test';
echo 'base: ' . base64_encode($str),PHP_EOL;
echo 'base: ' . $base64->encodebit($str). PHP_EOL;
echo 'base: ' . $base64->encode($str) . PHP_EOL;