<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Base64 - 她和她的猫</title>
    <link>https://her-cat.com/tags/base64/</link>
    <description>Base64的文章列表 - 她和她的猫</description>
    <image>
      <title>她和她的猫</title>
      <url>https://her-cat.com/assets/favorite.jpeg</url>
      <link>https://her-cat.com/assets/favorite.jpeg</link>
    </image>
    <generator>Hugo -- 0.148.1</generator>
    <language>zh</language>
    <lastBuildDate>Mon, 10 Feb 2020 04:04:20 +0800</lastBuildDate>
    <atom:link href="https://her-cat.com/tags/base64/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>《PHP 实现 Base64 编码/解码》笔记</title>
      <link>https://her-cat.com/posts/2020/02/10/php-implements-base64-encoding-decoding-notes/</link>
      <pubDate>Mon, 10 Feb 2020 04:04:20 +0800</pubDate>
      <guid>https://her-cat.com/posts/2020/02/10/php-implements-base64-encoding-decoding-notes/</guid>
      <description>放假前，在《C Primer Plus》一书中阅读了关于位运算的章节，对于位运算的一些概念有了基本的认识，所以当静下心来阅读《PHP 实现 Base64 编码/解码》文中的</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>早在去年 11 月底就已经看过《PHP 实现 Base64 编码/解码》这篇文章了，由于当时所掌握的位运算知识过于薄弱，所以就算是看过几遍也是囫囵吞枣一般，不出几日便忘记了其滋味。</p>
<p>只得其形，不知其意。</p>
<p>所以暗下决心写一篇阅读笔记，以此来较量是否掌握了其原理及位运算相关知识。但是作为一名拖延症患者，导致此事一再拖延，直至今日。</p>
<p>人总是趋利避害的。</p>
<p>记得刚出来实习的时候，室友大牛会截图问我一些代码是什么意思。我跟他说，你一行行的读，一边读一边写注释，等你读完就知道这些代码是什么意思。</p>
<p>对于一坨不认识的代码，首先是抗拒，但是为了生活又不得不做，于是开始烦躁起来，选择寻求他人或者搁置一旁。遇到这种情况，按照上面的方法一行行的写注释，写着写着心就静了下来，代码也理解了，既不麻烦他人也完成了任务。</p>
<p>放假前，在《C Primer Plus》一书中阅读了关于位运算的章节，对于位运算的一些概念有了基本的认识，所以当静下心来阅读《PHP 实现 Base64 编码/解码》文中的代码时，也还算是顺畅。</p>
<p>由于文中一些位运算代码十分巧妙，所以在阅读代码的时候也是一边写注释一边读，为了便于查看，带注释的代码放在文末。</p>
<h2 id="base64-字符映射表">Base64 字符映射表</h2>
<p>这张表包含了 64 个字符，Base64 编码后的结果也是取自于这 64 个字符，每个字符用 6 位的二进制来表示。</p>
<p><img alt="PHP实现Base64加密算法" decoding="async" height="467" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2020/02/10/php-implements-base64-encoding-decoding-notes/base64.png" srcset="/posts/2020/02/10/php-implements-base64-encoding-decoding-notes/base64_hu_8346a2dd8a2b35cf.png 384w" style="max-width: 100%; height: auto; aspect-ratio: 1.4582;" width="681"></p>
<p>通过这张映射表，可以根据 <strong>Binary</strong> 找到对应的 <strong>Char</strong>，<strong>Index</strong> 的二进制去掉左侧两位就是 <strong>Binary</strong> 了。</p>
<p>举个例子，Index <strong>51</strong> 的二进制是 <strong>00110011</strong>，一共有 8 位，去掉左侧两位就是 <strong>110011</strong>，对应的 Char 是 <strong>z</strong>;</p>
<h2 id="base64-编码的过程">Base64 编码的过程</h2>
<p>假设现有字符串 123 需要编码，首先将每个字符的 ASCII 转成二进制后排列在一起。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// 1 的 ASCII 为 49，49 的二进制为 00110001
</span></span><span class="line"><span class="cl">// 2 的 ASCII 为 50，50 的二进制为 00110010
</span></span><span class="line"><span class="cl">// 3 的 ASCII 为 60，60 的二进制为 00110011
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">// 将二进制排列在一起
</span></span><span class="line"><span class="cl">001100010011001000110011
</span></span></code></pre></div><p>上面的二进制为 24 位，正好可以拆分为 4 个 6 位的 Base64 字符。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001100 010011 001000 110011
</span></span></code></pre></div><p>再根据上面映射表中的 <strong>Binary</strong> 找到对应的 <strong>Char</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001100 010011 001000 110011
</span></span><span class="line"><span class="cl">   M     T       I     z
</span></span></code></pre></div><p>所以字符串 123 经过 Base64 之后就是 MTIz 了。</p>
<p>虽然上面已经将 Base64 编码的过程基本上说完了，但是还有个很重要的问题：如果字符串的长度不是 3 的倍数怎么办？</p>
<p>举个例子，需要加密的字符串为 1234，长度为 4，不是 3 的倍数，多出了一个字符。</p>
<p>排列后的二进制位为 32 位，组成 5 个 6 位的 Base64 字符后还多出 2 位，多出来的总不能丢掉不管吧？所以需要对多出来的位进行 <strong>补齐</strong> 处理。</p>
<p>怎么补呢？上面已经说过，6 位可以组成一个 Base64 字符，那么只要再补上 4 位就可以组成一个完整的Base64 字符。</p>
<p>在这里我们偷偷的给字符串加了 4 位，怎么在解码时候知道编码时加了 4 位呢？很简单，只需要在编码结果后面加上两个 <strong>=</strong> 号。</p>
<p>所以字符串 1234 的编码结果为 MTIzNA==。</p>
<p>上面举的例子是多出一个字符的情况，如果多出两个字符呢？还是一样做补齐处理，不过只需要补上 2 位，在编码结果后面加上一个 <strong>=</strong> 号。</p>
<h2 id="base64-编码的代码实现">Base64 编码的代码实现</h2>
<blockquote>
<p>为了突出重点，这里会将每部分的代码单独提出来，补充一些源码中并不存在的代码，使得代码块能够单独运行。</p></blockquote>
<h3 id="排列字符二进制">排列字符二进制</h3>
<p>首先将每个字符的 ASCII 转为二进制并排列在一起。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 需要编码的字符串
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nv">$content</span> <span class="o">=</span> <span class="s1">&#39;123&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 先将第一个字符左移16位，为剩下2个字符（每个字符8位）腾出16位的空间
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nv">$int_24</span> <span class="o">=</span> <span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 再将第二个字符左移8位，紧跟第一个字符后面
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="o">|</span> <span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 最后一个字符放在剩下的8位里面
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="o">|</span> <span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span></code></pre></div><p>先理解一下上面的注释，在脑海中留一个大致的印象，然后再往后看。</p>
<p><strong>$content[0]</strong> 的值为 1，通过 <strong>ord()</strong> 函数获取到 ASCII 值为 49，49 的二进制值为 <strong>00110001</strong>，然后将其左移 16 位，得到的二进制为 <strong>001100010000000000000000</strong>。</p>
<p>左移多少位是在二进制的右边加多少个 0 ，可以数一下二进制 <strong>00110001</strong> 的右边是不是多了 16 个 0，这 16 个 0 就是为剩下的两个字符留的。</p>
<p><strong>$content[1]</strong> 的值为 2，ASCII 值为 50，50 的二进制为 <strong>00110010</strong>，左移 8 位后的二进制为 <strong>0011001000000000</strong>，然后通过位或运算将其放入二进制 <strong>001100010000000000000000</strong> 中，得到二进制 <strong>001100010011001000000000</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001100010000000000000000 // 第一个字符 ASCII 码左移16位后
</span></span><span class="line"><span class="cl">        0011001000000000 // 第二个字符 ASCII 码左移8位后
</span></span><span class="line"><span class="cl">001100010011001000000000 // 位或运算后
</span></span></code></pre></div><p>这样第二个字符的二进制就紧跟在第一个字符的二进制后面，这时后面还有空闲的 8 个 0 留给最后一个字符。</p>
<p><strong>$content[2]</strong> 的值为 3，ASCII 为 60，60 的二进制为 <strong>00110011</strong>，然后使用位或运算直接放入上一步得到的二进制中。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001100010011001000000000 // 上一步得到的二进制
</span></span><span class="line"><span class="cl">                00110011 // 60 的二进制
</span></span><span class="line"><span class="cl">001100010011001000110011 // 位或后
</span></span></code></pre></div><p>此时，<strong>$int_24</strong> 的二进制值为 <strong>001100010011001000110011</strong>。</p>
<blockquote>
<p>觉得看不清的话可以 ctrl f 分别搜索一下三个字符 ASCII 的二进制。</p></blockquote>
<h3 id="转换为-base64-字符">转换为 Base64 字符</h3>
<p>接下来将 <strong>$int_24</strong> 的二进制分为 4 个 6 位的二进制，然后再根据二进制转换为 Base64 字符。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// 通过 normalToBase64Char() 方法将6位的二进制转换为 base64 字符
</span></span><span class="line"><span class="cl">$ret .= self::normalToBase64Char($int_24 &gt;&gt; 18);
</span></span><span class="line"><span class="cl">$ret .= self::normalToBase64Char(($int_24 &gt;&gt; 12) &amp; 0x3f);
</span></span><span class="line"><span class="cl">$ret .= self::normalToBase64Char(($int_24 &gt;&gt; 6) &amp; 0x3f);
</span></span><span class="line"><span class="cl">$ret .= self::normalToBase64Char($int_24 &amp; 0x3f);
</span></span></code></pre></div><p>先来看一下 4 个 6 位二进制获取的过程。</p>
<p>第一个 6 位二进制，将 <strong>$int_24</strong> 右移 18 位得到了二进制 <strong>001100</strong>，右移就是移除右侧多少个位。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001100010011001000110011 // $int_24 的二进制
</span></span><span class="line"><span class="cl">001100                   // 右移 18 位后  
</span></span></code></pre></div><p>第二个 6 位二进制，将 <strong>$int_24</strong> 右移 12 位，再通过位与 <strong>0x3f</strong> 保留右侧的 6 位，得到二进制 <strong>010011</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001100010011001000110011 // $int_24 的二进制
</span></span><span class="line"><span class="cl">001100010011             // 右移 12 位后
</span></span><span class="line"><span class="cl">      111111             // 0x3f 的十进制是63，二进制值是 111111
</span></span><span class="line"><span class="cl">      010011             // 位与运算后
</span></span></code></pre></div><p>第三个 6 位二进制，将 <strong>$int_24</strong> 右移 6 位，再通过位与 <strong>0x3f</strong> 保留右侧的 6 位，得到二进制 <strong>001000</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001100010011001000110011 // $int_24 的二进制
</span></span><span class="line"><span class="cl">001100010011001000       // 右移 6 位后
</span></span><span class="line"><span class="cl">            111111       // 0x3f 的十进制是63，二进制值是 111111
</span></span><span class="line"><span class="cl">            001000       // 位与运算后
</span></span></code></pre></div><p>第四个 6 位二进制，将 <strong>$int_24</strong> 位与 <strong>0x3f</strong> 保留右侧的 6 位，得到二进制 <strong>110011</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001100010011001000110011 // $int_24 的二进制
</span></span><span class="line"><span class="cl">                  111111 // 0x3f 的十进制是63，二进制值是 111111
</span></span><span class="line"><span class="cl">                  110011 // 位与运算后
</span></span></code></pre></div><p>再来看看 <strong>normalToBase64Char()</strong> 方法，这个函数的作用就是<strong>将 6 位二进制表示的值转为 Base64 字符</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">private static function normalToBase64Char($num)
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    if ($num &gt;= 0 &amp;&amp; $num &lt;= 25) {
</span></span><span class="line"><span class="cl">        return chr(ord(&#39;A&#39;) + $num);
</span></span><span class="line"><span class="cl">    } else if ($num &gt;= 26 &amp;&amp; $num &lt;= 51) {
</span></span><span class="line"><span class="cl">        return chr(ord(&#39;a&#39;) + ($num - 26));
</span></span><span class="line"><span class="cl">    } else if ($num &gt;= 52 &amp;&amp; $num &lt;= 61) {
</span></span><span class="line"><span class="cl">        return chr(ord(&#39;0&#39;) + ($num - 52));
</span></span><span class="line"><span class="cl">    } else if ($num == 62) {
</span></span><span class="line"><span class="cl">        return &#39;+&#39;;
</span></span><span class="line"><span class="cl">    } else {
</span></span><span class="line"><span class="cl">        return &#39;/&#39;;
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>需要注意的是，这里的 <strong>$num</strong> 是上面分割出来的 6 位二进制表示的值，比如 <strong>001100</strong> 表示的值就是 12。<strong>$num</strong> 就是映射表中的 Base64 数值（<strong>Index</strong>）。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 0b 表示001100是二进制的
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="mi">0</span><span class="nx">b001100</span><span class="p">;</span> <span class="c1">// 12
</span></span></span></code></pre></div><p>通过映射表可以知道 <strong>001100</strong> 对应的是 <strong>M</strong>，那怎么给它们建立映射关系呢？</p>
<p>还是得从映射表中找规律，A 的 ASCII 值为 65，M 的 ASCII 值为 77，77 减 65 等于 12，正好是 <strong>001100</strong> 所表示的值。所以当 <strong>$num</strong> 的值大于等于 0，小于等于 25 时，<strong>$num</strong> 对应的 Base64 字符在 A ~ Z 之间，只需要将 <strong>$num</strong> 加上 A 的 ASCII 值 65 就可以得到对应的 Base64 字符了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 当 $num &gt;= 0 &amp;&amp; $num &lt;= 25
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 0 的 6 位二进制为 000000
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;A&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="mi">0</span><span class="nx">b000000</span><span class="p">)</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// A
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 1 的 6 位二进制为 000001
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;A&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="mi">0</span><span class="nx">b000001</span><span class="p">)</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// B
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 2 的 6 位二进制位 000010
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;A&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="mi">0</span><span class="nx">b000010</span><span class="p">)</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// C
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 3 的 6 位二进制位 000011
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;A&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="mi">0</span><span class="nx">b000011</span><span class="p">)</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// D
</span></span></span></code></pre></div><p>通过上面我们可以知道 0 ~ 25 之间的 26 个数字分别对应 26 个 Base64 字符 A ~ Z，所以当 <strong>$num</strong> 大于 25 时，需要减去 26，得到的结果再加上 a 的 ASCII 值 97 就是对应的 Base64 字符的 ASCII 值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 当 $num &gt;= 26 &amp;&amp; $num &lt;= 51
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 26 的 6 位二进制为 011010
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">0</span><span class="nx">b011010</span> <span class="o">-</span> <span class="mi">26</span><span class="p">))</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// a
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 27 的 6 位二进制为 011011
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">0</span><span class="nx">b011011</span> <span class="o">-</span> <span class="mi">26</span><span class="p">))</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// b
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 28 的 6 位二进制位 011100
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">0</span><span class="nx">b011100</span> <span class="o">-</span> <span class="mi">26</span><span class="p">))</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 29 的 6 位二进制位 011101
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">0</span><span class="nx">b011101</span> <span class="o">-</span> <span class="mi">26</span><span class="p">))</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// d
</span></span></span></code></pre></div><p>通过上面我们可以知道 26 ~ 51 之间的 26 个数字分别对应 26 个 Base64 字符 a ~ z, 所以在当 <strong>$num</strong> 大于 51 时，需要减去 52（26 个大写字母 + 26 个小写字母），得到的结果再加上 0 的 ASCII 值 48 就是对应的 Base64 字符的 ASCII 值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 当 $num &gt;= 52 &amp;&amp; $num &lt;= 61
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 52 的 6 位二进制为 110100
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;0&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">0</span><span class="nx">b110100</span> <span class="o">-</span> <span class="mi">52</span><span class="p">))</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// 0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 53 的 6 位二进制为 110101
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;0&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">0</span><span class="nx">b110101</span> <span class="o">-</span> <span class="mi">52</span><span class="p">))</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// 1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 54 的 6 位二进制位 110110
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;0&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">0</span><span class="nx">b110110</span> <span class="o">-</span> <span class="mi">52</span><span class="p">))</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// 2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 55 的 6 位二进制位 110111
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">echo</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;0&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">0</span><span class="nx">b110111</span> <span class="o">-</span> <span class="mi">52</span><span class="p">))</span><span class="o">.</span><span class="nx">PHP_EOL</span><span class="p">;</span> <span class="c1">// 3
</span></span></span></code></pre></div><p>通过上面我们可以知道 52 ~ 61 之间的 10 个数字分别对应 10 个 Base64 字符 0 ~ 9。</p>
<p>当 <strong>$num</strong> 等于 62 时对应的 Base64 字符为 <strong>+</strong>，等于 63 时对应的 Base64 字符为 <strong>/</strong>。</p>
<p>到这里 <strong>normalToBase64Char()</strong> 方法就讲完了，将上面的 4 个 6 位二进制 <strong>001100</strong>、<strong>010011</strong>、<strong>001000</strong>、<strong>110011</strong>，传入方法中得到 M、T、I、z，所以 123 编码后就是 MTIz。</p>
<h3 id="base64-字符补齐">Base64 字符补齐</h3>
<p>先看一下补齐处理的代码。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="c1">// 字符串长度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nv">$len</span> <span class="o">=</span> <span class="nx">strlen</span><span class="p">(</span><span class="nv">$content</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">// 完整组合
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nv">$loop</span> <span class="o">=</span> <span class="nx">intval</span><span class="p">(</span><span class="nv">$len</span> <span class="o">/</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="c1">//剩余字符数，是否需要补齐
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nv">$rest</span> <span class="o">=</span> <span class="nv">$len</span> <span class="o">%</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nv">$rest</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$rest</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 如果多出1个字符，将其左移4位进行补齐
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nv">$int_12</span> <span class="o">=</span> <span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$loop</span> <span class="o">*</span> <span class="mi">3</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 右移 6 位，剩余左侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_12</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 通过 0x3f (111111) 以掩码的方式取出右侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_12</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$ret</span> <span class="o">.=</span> <span class="s2">&#34;==&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 如果多出 2 个字符，需要补齐 2 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 先将多出来的第一个字符左移 8 位，为多出来的第二个字符腾出位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 然后再将整体向左移 2 位，使其可以拆分为 3 个 6 位的 base64 字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nv">$int_18</span> <span class="o">=</span> <span class="p">((</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$loop</span> <span class="o">*</span> <span class="mi">3</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$loop</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 右移 12 位，剩余左侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_18</span> <span class="o">&gt;&gt;</span> <span class="mi">12</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 右移 6 位，通过 0x3f (111111) 以掩码的方式取出剩余的右侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">normalToBase64Char</span><span class="p">((</span><span class="nv">$int_18</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 通过 0x3f (111111) 以掩码的方式取出右侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_18</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$ret</span> <span class="o">.=</span> <span class="s2">&#34;=&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nv">$ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>如果 <strong>$rest</strong> 等于 0，说明字符串长度是 3 的倍数，不需要补齐。</p>
<p>如果 <strong>$rest</strong> 等于 1，说明多出一个字符，将其 ASCII 值左移 4 位，得到二进制位长度为 12 位，正好可以拆分为 2 个 6 位的二进制。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// 这里用 1234 进行举例，多出来的字符为 4
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">00110100     // 4 的 ASCII 值为 52，二进制为 00110100
</span></span><span class="line"><span class="cl">001101000000 // 左移 4 位后
</span></span></code></pre></div><p>将 <strong>$int_12</strong> 右移 6 位，得到剩下的 6 位。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001101000000 // $int_12 的二进制
</span></span><span class="line"><span class="cl">001101       // 右移 6 位后
</span></span></code></pre></div><p><strong>$int_12</strong> 位与 <strong>0x3f</strong> 得到右侧的 6 位。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">001101000000 // $int_12 的二进制
</span></span><span class="line"><span class="cl">      111111 // 0x3f 的十进制是63，二进制值是 111111
</span></span><span class="line"><span class="cl">      000000 // 位与运算后
</span></span></code></pre></div><p>最后在编码结果后面加上 <strong>==</strong>，表示多出一个字符。</p>
<p>如果 <strong>$rest</strong> 等于 2，说明多出两个字符，先将第一个字符左移 8 位，为第二个字符腾出位置，再通过位或运算将第二个字符放在第一个字符后面，两个字符的 ASCII 值排列后将整体左移 2 位，得到二进制位长度为 18 位，可以拆分为 3 个 6 位的二进制。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// 这里用 12345 进行举例，多出来的字符为 45
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">00110100            // 4 的 ASCII 值为 52，二进制为 00110100
</span></span><span class="line"><span class="cl">0011010000000000    // 将第一个字符左移 8 位后
</span></span><span class="line"><span class="cl">        00110101    // 5 的 ASCII 值位 53，二进制为 00110101
</span></span><span class="line"><span class="cl">0011010000110101    // 位或运算后
</span></span><span class="line"><span class="cl">001101000011010100  // 左移 2 位后
</span></span></code></pre></div><p>然后就是按 6 位一组二进制取出来，跟上面的操作差不多，就略过了，最后在编码结果后面加上 <strong>=</strong>，表示多出两个字符。</p>
<h2 id="base64-解码的过程">Base64 解码的过程</h2>
<p>解码其实是编码的逆操作。</p>
<p>编码时：</p>
<ul>
<li>将每个字符的 ASCII 的二进制排列在一起</li>
<li>以 6 位一组二进制进行拆分</li>
<li>将 6 位二进制转为 Base64 字符</li>
</ul>
<p>解码时：</p>
<ul>
<li>将每个 Base64 字符 的 ASCII 值转为对应的 Base64 数值（Index）</li>
<li>将转换后的数值的二进制排列在一起</li>
<li>以 8 位一组二进制进行拆分</li>
<li>将 8 位二进制（其值是 ASCII 值）通过 chr() 函数转为字符</li>
</ul>
<p>先根据末尾的 <strong>=</strong> 来判断补齐了几位，如果进行了补齐处理，将末尾的 4 个字符截取出来，在最后进行处理，使得前面剩余的字符可以 4 个字符一组。</p>
<p>比如 MTIzNA==，截取末尾的 4 个字符 <strong>NA==</strong>，剩余的 <strong>MTIz</strong> 可以组成一组。MTIzNDU= 截取末尾的 4 个字符 <strong>NDU=</strong>，剩余的 <strong>MTIz</strong> 组成一组。</p>
<p>假设现有 MTIzNA== 需要解码，截取末尾后剩余 MTIz。首先将每个字符的 ASCII 通过 <strong>base64CharToInt()</strong> 方法进行转换成对应的 Base64 数值（Index），再将转换后的 数值的二进制排列起来，排列时去除了每个素质的二进制的左侧两位，最终得到 <strong>00001100010011001000110011</strong>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// M 的 ASCII 为 77，转换后的 ASCII 为 12，12 的二进制为 00001100
</span></span><span class="line"><span class="cl">// T 的 ASCII 为 84，转换后的 ASCII 为 19，19 的二进制为 00010011
</span></span><span class="line"><span class="cl">// I 的 ASCII 为 73，转换后的 ASCII 为 8，8 的二进制为 00001000
</span></span><span class="line"><span class="cl">// z 的 ASCII 为 122，转换后的 ASCII 为 51，51 的二进制为 00110011
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">00001100000000000000000000 // 将 12 的二进制左移 18 位，
</span></span><span class="line"><span class="cl">      00010011000000000000 // 将 19 的二进制左移 12 位
</span></span><span class="line"><span class="cl">00001100010011000000000000 // 位或后
</span></span><span class="line"><span class="cl">            00001000000000 // 将 8 的二进制左移 6 位
</span></span><span class="line"><span class="cl">00001100010011001000000000 // 位或后
</span></span><span class="line"><span class="cl">                  00110011 // 51 的二进制
</span></span><span class="line"><span class="cl">00001100010011001000110011 // 位与后
</span></span></code></pre></div><p>这里的 <strong>00001100010011001000110011</strong> 共有 26 位，因为第一个字符的左侧两位并未被移除，将其与 16777215（二进制为 24 个 1）进行位与运算，使其变成 24 位的二进制 <strong>001100010011001000110011</strong>，但是操作前后的编码结果并未发生改变，所以这里猜测左侧的 00 可以忽略（或者说每个位的默认值就是 0）。</p>
<p>最终得到的 24 位二进制与编码时排列后的的二进制是一样的，所以接下来只需要按照 8 位一组进行分割就可以得到原文的 ASCII 值，再通过 <strong>chr()</strong> 函数获取 ASCII 值对应的字符。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// 排列后的二进制
</span></span><span class="line"><span class="cl">001100010011001000110011
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">00110001 00110010 00110011
</span></span><span class="line"><span class="cl">    1       2        3
</span></span></code></pre></div><p>接下来处理补齐的部分 NA==，有两个 = 说明编码时补齐了 4 位，多出了一个字符。</p>
<p>将第一个字符左移 6 位，为第二个字符腾出位置，将第二个字符或放在第一个字符后面。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">00001101        // N 对应的 Base64 数值为 13，13 的二进制为 00001101
</span></span><span class="line"><span class="cl">00001101000000  // 左移 6 位后
</span></span><span class="line"><span class="cl">      00000000  // A 对应的 Base64 数值为 0，0 的二进制为 00000000
</span></span><span class="line"><span class="cl">00001101000000  // 位或后
</span></span><span class="line"><span class="cl">0000110100      // 右移 4 位后
</span></span></code></pre></div><p>同样得到的二进制 <strong>0000110100</strong> 左侧多了两个 0，原因与上面一样，这里最终得到的是 <strong>00110100</strong>，通过 <strong>chr()</strong> 函数获取对应的字符为 4。</p>
<p>所以 MTIzNA== 解码后的结果是 1234。</p>
<p>补齐 2 位的解码处理跟补齐4位差不多，这里就不重复了。</p>
<h2 id="base64-解码的代码实现">Base64 解码的代码实现</h2>
<p>碍于篇幅长度这里就不讲解码的代码实现了，如果搞懂了上面的编码实现，那么阅读解码的代码也是没什么问题的，不懂的话可以配合文末的带注释的代码进行理解。</p>
<p>这里着重说一下 <strong>base64CharToInt()</strong> 方法。</p>
<p>在编码时，我们通过 <strong>normalToBase64Char()</strong> 方法将一个 6 位的二进制转成了 Base64 字符，这里的 6 位二进制所表示的值就是 Base64 数值，也就是映射表中的 <strong>Index</strong>。</p>
<p>所以 <strong>base64CharToInt()</strong> 方法就是将 Base64 字符转成 Base64 数值（Index）。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">private static function base64CharToInt($num)
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    // 因为在转换为 base64 字符时加了相应的值
</span></span><span class="line"><span class="cl">    // 在解码时需要再减去
</span></span><span class="line"><span class="cl">    if ($num &gt;= 65 &amp;&amp; $num &lt;= 90) {
</span></span><span class="line"><span class="cl">        // 65 == A
</span></span><span class="line"><span class="cl">        return ($num - 65);
</span></span><span class="line"><span class="cl">    } else if ($num &gt;= 97 &amp;&amp; $num &lt;= 122) {
</span></span><span class="line"><span class="cl">        // 97 == a
</span></span><span class="line"><span class="cl">        return ($num - 97) + 26;
</span></span><span class="line"><span class="cl">    } else if ($num &gt;= 48 &amp;&amp; $num &lt;= 57) {
</span></span><span class="line"><span class="cl">        // 48 == 0
</span></span><span class="line"><span class="cl">        return ($num - 48)+52;
</span></span><span class="line"><span class="cl">    } else if ($num == 43) {
</span></span><span class="line"><span class="cl">        // 43 == +
</span></span><span class="line"><span class="cl">        return 62;
</span></span><span class="line"><span class="cl">    } else {
</span></span><span class="line"><span class="cl">        return 63;
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>这里是根据 Base64 字符的 ASCII 值 (<strong>$num</strong>) 来判断加/减多少数值才能得到原来的值。</p>
<ul>
<li>65 ~ 90 对应的是 A ~ Z。</li>
<li>97 ~ 122 对应的是 a ~ z。</li>
<li>48 ~ 57 对应的是 0 ~ 9。</li>
<li>43 对应的是 +，编码时 $num 为 62 就返回 +，所以解码时返回 62。</li>
<li>47 对应的是 /，编码时 $num 为 63 就返回 /，所以解码时返回 63。</li>
</ul>
<blockquote>
<p>可以对照着 <strong>normalToBase64Char()</strong> 部分来理解。</p></blockquote>
<h2 id="带注释的源码">带注释的源码</h2>
<p>注释中一些关于 <strong>字符</strong> 的描述需要联系代码来理解其本意。</p>
<p>比如在编码的注释中：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"> 先将第一个字符左移16位，为剩下2个字符（每个字符8位）腾出16位的空间
</span></span></code></pre></div><p>实际上左移 16 位的值是第一个字符的 ASCII 值，并不是字符本身。</p>
<p>在解码的注释中：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">将第一个字符左移 18 位，为后面的 3 个字符腾出位置
</span></span></code></pre></div><p>跟编码时一样，左移 18 位的并不是字符本身，而是第一个字符的 Base64 数值 的 ASCII 值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Base64</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * 将 6 位二进制表示的值转为 Base64 字符
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param $num
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return string
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">normalToBase64Char</span><span class="p">(</span><span class="nv">$num</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">&gt;=</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nv">$num</span> <span class="o">&lt;=</span> <span class="mi">25</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;A&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="nv">$num</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">&gt;=</span> <span class="mi">26</span> <span class="o">&amp;&amp;</span> <span class="nv">$num</span> <span class="o">&lt;=</span> <span class="mi">51</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;a&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">-</span> <span class="mi">26</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">&gt;=</span> <span class="mi">52</span> <span class="o">&amp;&amp;</span> <span class="nv">$num</span> <span class="o">&lt;=</span> <span class="mi">61</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nx">chr</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="s1">&#39;0&#39;</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">-</span> <span class="mi">52</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">==</span> <span class="mi">62</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="s1">&#39;+&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="s1">&#39;/&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="sd">/**
</span></span></span><span class="line"><span class="cl"><span class="sd">     * 将 Base64 字符 的 ASCII 值转为对应的 Base64 数值(Index)
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @param $num
</span></span></span><span class="line"><span class="cl"><span class="sd">     * @return int
</span></span></span><span class="line"><span class="cl"><span class="sd">     */</span>
</span></span><span class="line"><span class="cl">    <span class="k">private</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">base64CharToInt</span><span class="p">(</span><span class="nv">$num</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 因为在转换为 base64字符时加了相应的值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 在解码时需要再减去
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">&gt;=</span> <span class="mi">65</span> <span class="o">&amp;&amp;</span> <span class="nv">$num</span> <span class="o">&lt;=</span> <span class="mi">90</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 65 == A
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">return</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">-</span> <span class="mi">65</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">&gt;=</span> <span class="mi">97</span> <span class="o">&amp;&amp;</span> <span class="nv">$num</span> <span class="o">&lt;=</span> <span class="mi">122</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 97 == a
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">return</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">-</span> <span class="mi">97</span><span class="p">)</span> <span class="o">+</span> <span class="mi">26</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">&gt;=</span> <span class="mi">48</span> <span class="o">&amp;&amp;</span> <span class="nv">$num</span> <span class="o">&lt;=</span> <span class="mi">57</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 48 == 0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">return</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">-</span> <span class="mi">48</span><span class="p">)</span><span class="o">+</span><span class="mi">52</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$num</span> <span class="o">==</span> <span class="mi">43</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 43 == +
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">return</span> <span class="mi">62</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="mi">63</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">encode</span><span class="p">(</span><span class="nv">$content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 字符串长度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nv">$len</span> <span class="o">=</span> <span class="nx">strlen</span><span class="p">(</span><span class="nv">$content</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 完整组合
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nv">$loop</span> <span class="o">=</span> <span class="nx">intval</span><span class="p">(</span><span class="nv">$len</span> <span class="o">/</span> <span class="mi">3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="c1">//剩余字符数，是否需要补齐
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nv">$rest</span> <span class="o">=</span> <span class="nv">$len</span> <span class="o">%</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1">//首先计算完整组合
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">for</span> <span class="p">(</span><span class="nv">$i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nv">$i</span> <span class="o">&lt;</span> <span class="nv">$loop</span><span class="p">;</span> <span class="nv">$i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$base_offset</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">*</span> <span class="nv">$i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 每次取3个字符，一个字符占8位，总共24位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 先将第一个字符左移16位，为剩下2个字符（每个字符8位）腾出16位的空间
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$int_24</span> <span class="o">=</span> <span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$base_offset</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">16</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 再将第二个字符左移8位，紧跟第一个字符后面
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="o">|</span> <span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$base_offset</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 最后一个字符放在剩下的8位里面
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="o">|</span> <span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$base_offset</span> <span class="o">+</span> <span class="mi">2</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 0x3f 转为十进制是63，二进制值是 111111，这里的 0x3f 相当于是掩码
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 后面的每次位移运算都只取6位，就得到了4个数字
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 将 $int_24 向右移 18 位，得到 base64 第一个字符的二进制，也就是  $int_24 最左侧的 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 再通过 normalToBase64Char 方法将4个数字转成 base64 那张表对应的字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 通过 normalToBase64Char() 方法将6位的二进制转换为 base64 字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_24</span> <span class="o">&gt;&gt;</span> <span class="mi">18</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">normalToBase64Char</span><span class="p">((</span><span class="nv">$int_24</span> <span class="o">&gt;&gt;</span> <span class="mi">12</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">normalToBase64Char</span><span class="p">((</span><span class="nv">$int_24</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_24</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 如果字符串长度刚好是 3 的整数倍时，上面的 for 循环已经将字符串处理完了
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="c1">// 不需要进行补齐处理
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nv">$rest</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nv">$ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$rest</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 如果多出1个字符，此时需要补齐4位，使其可以拆分为两个6位的 base64 字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$int_12</span> <span class="o">=</span> <span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$loop</span> <span class="o">*</span> <span class="mi">3</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 向右移 6 位，剩余左侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_12</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 通过 0x3f (111111) 以掩码的方式取出右侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_12</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$ret</span> <span class="o">.=</span> <span class="s2">&#34;==&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nv">$ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 如果多出 2 个字符，需要补齐 2 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 先将多出来的第一个字符左移 8 位，为多出来的第二个字符腾出位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 然后再将整体向左移 2 位，使其可以拆分为 3 个 6 位的 base64 字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$int_18</span> <span class="o">=</span> <span class="p">((</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$loop</span> <span class="o">*</span> <span class="mi">3</span><span class="p">])</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="nx">ord</span><span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$loop</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 右移 12 位，剩余左侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_18</span> <span class="o">&gt;&gt;</span> <span class="mi">12</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 右移 6 位，通过 0x3f (111111) 以掩码的方式取出剩余的右侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">normalToBase64Char</span><span class="p">((</span><span class="nv">$int_18</span> <span class="o">&gt;&gt;</span> <span class="mi">6</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 通过 0x3f (111111) 以掩码的方式取出右侧 6 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">self</span><span class="o">::</span><span class="na">normalToBase64Char</span><span class="p">(</span><span class="nv">$int_18</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$ret</span> <span class="o">.=</span> <span class="s2">&#34;=&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="k">return</span> <span class="nv">$ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">decode</span><span class="p">(</span><span class="nv">$content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$len</span> <span class="o">=</span> <span class="nx">strlen</span><span class="p">(</span><span class="nv">$content</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$len</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;=&#39;</span> <span class="o">&amp;&amp;</span> <span class="nv">$content</span><span class="p">[</span><span class="nv">$len</span> <span class="o">-</span> <span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;=&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">//说明加密的时候，剩余1个字节，补齐了4位，也就是左移了4位，所以除了最后包含的2个字符，前面的所有字符可以4个字符一组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$last_chars</span> <span class="o">=</span> <span class="nx">substr</span><span class="p">(</span><span class="nv">$content</span><span class="p">,</span> <span class="o">-</span><span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$full_chars</span> <span class="o">=</span> <span class="nx">substr</span><span class="p">(</span><span class="nv">$content</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$len</span> <span class="o">-</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$type</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$content</span><span class="p">[</span><span class="nv">$len</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;=&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">//说明加密的时候，剩余2个字节，补齐了2位，也就是左移了2位，所以除了最后包含的3个字符，前面的所有字符可以4个字符一组
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$last_chars</span> <span class="o">=</span> <span class="nx">substr</span><span class="p">(</span><span class="nv">$content</span><span class="p">,</span> <span class="o">-</span><span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$full_chars</span> <span class="o">=</span> <span class="nx">substr</span><span class="p">(</span><span class="nv">$content</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$len</span> <span class="o">-</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$type</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$type</span> <span class="o">=</span> <span class="mi">3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$full_chars</span> <span class="o">=</span> <span class="nv">$content</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">//首先处理完整的部分
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nv">$loop</span> <span class="o">=</span> <span class="nx">strlen</span><span class="p">(</span><span class="nv">$full_chars</span><span class="p">)</span> <span class="o">/</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$ret</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">for</span> <span class="p">(</span><span class="nv">$i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nv">$i</span> <span class="o">&lt;</span> <span class="nv">$loop</span><span class="p">;</span> <span class="nv">$i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$base_offset</span> <span class="o">=</span> <span class="mi">4</span> <span class="o">*</span> <span class="nv">$i</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 每次取 4 个 base64 字符，一个字符占 6 位，总共 24 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 将第一个字符左移 18 位，为后面的 3 个字符腾出位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$int_24</span> <span class="o">=</span> <span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">base64CharToInt</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$full_chars</span><span class="p">[</span><span class="nv">$base_offset</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">18</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 将第二个字符左移 12 位，紧跟在第一个字符后面
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="o">|</span> <span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">base64CharToInt</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$full_chars</span><span class="p">[</span><span class="nv">$base_offset</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">12</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 将第三个字符左移 8 位，紧跟在第二个字符后面
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="o">|</span> <span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">base64CharToInt</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$full_chars</span><span class="p">[</span><span class="nv">$base_offset</span> <span class="o">+</span> <span class="mi">2</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                <span class="c1">// 将第四个字符放在第三个字符后面
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                <span class="o">|</span> <span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">base64CharToInt</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$full_chars</span><span class="p">[</span><span class="nv">$base_offset</span> <span class="o">+</span> <span class="mi">3</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">            <span class="c1">// 右移 16 位，得到解码后第一个字符（24 - 16 = 8）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">chr</span><span class="p">(</span><span class="nv">$int_24</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 右移 8 位，再通过掩码 0xff(11111111) 得到解码后的第二个字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">chr</span><span class="p">((</span><span class="nv">$int_24</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 通过掩码 0xff(11111111) 得到最后一个字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">chr</span><span class="p">(</span><span class="nv">$int_24</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">//紧接着处理补齐的部分
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="nv">$type</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 多出一个字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 先将补齐的第一个字符左移 6 位，给第二个字符腾出位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$int_12</span> <span class="o">=</span> <span class="nx">self</span><span class="o">::</span><span class="na">base64CharToInt</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$last_chars</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">6</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 将第二个字符放入刚腾出来位置中，再将整体右移 4 位，保留 8 位，正好一个十进制数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$int_8</span> <span class="o">=</span> <span class="p">(</span><span class="nv">$int_12</span> <span class="o">|</span> <span class="nx">self</span><span class="o">::</span><span class="na">base64CharToInt</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$last_chars</span><span class="p">[</span><span class="mi">1</span><span class="p">])))</span> <span class="o">&gt;&gt;</span> <span class="mi">4</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 再根据 ASCII 值获取字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">chr</span><span class="p">(</span><span class="nv">$int_8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$type</span> <span class="o">==</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 多处两个字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="c1">// 首先将补齐的第一个字符左移 12 位，为剩余的两个字符腾出位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$l_two_chars</span> <span class="o">=</span> <span class="p">((</span><span class="nx">self</span><span class="o">::</span><span class="na">base64CharToInt</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$last_chars</span><span class="p">[</span><span class="mi">0</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">12</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 将第二个字符左移 6 位，放在第一个字符的后面
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="o">|</span> <span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">base64CharToInt</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$last_chars</span><span class="p">[</span><span class="mi">1</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">6</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                    <span class="c1">// 将第三个字符放在剩余的 6 位中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="c1">// 将整体右移 2 位，此时正好 16 位，两个字符的长度
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                    <span class="o">|</span> <span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">base64CharToInt</span><span class="p">(</span><span class="nx">ord</span><span class="p">(</span><span class="nv">$last_chars</span><span class="p">[</span><span class="mi">2</span><span class="p">]))</span> <span class="o">&lt;&lt;</span> <span class="mi">0</span><span class="p">))</span> <span class="o">&gt;&gt;</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 左移 8 位得到解码的第一个字符
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">chr</span><span class="p">(</span><span class="nv">$l_two_chars</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 通过 0xff(11111111) 作为掩码，得到右侧剩余的 8 位
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nv">$ret</span> <span class="o">.=</span> <span class="nx">chr</span><span class="p">(</span><span class="nv">$l_two_chars</span> <span class="o">&amp;</span> <span class="mh">0xff</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nv">$ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h2 id="最后">最后</h2>
<p>原本在写完注释后，觉得对于 Base64 的实现已经理解的差不多了，但是在写这篇文章的时候，发现之前一些自认为理解的逻辑没办法写出来，主要原因还是没有理解透彻，所以在写的时候不能行云流水。</p>
<hr>
<p>因为疫情的原因延期上班，公司是内网开发，没办法远程办公，所以就变成了延长放假时间。</p>
<p>每天日夜颠倒，看电影打游戏，三四个小时放不下手机。整个人变成了废柴状态，食不知味，玩不尽兴，内心极度焦虑。</p>
<p>直到我拿出笔记本，绞尽脑汁的写着这篇文章，我的焦虑、迷茫、空虚才找到了出口。</p>
<h2 id="相关文章">相关文章</h2>
<ul>
<li><a href="https://learnku.com/articles/36655">PHP 实现 Base64 加密算法
</a></li>
<li><a href="https://learnku.com/articles/22312">PHP 位运算使用
</a></li>
<li><a href="https://weibo.com/ttarticle/p/show?id=2309404469838071333071">在别人傻玩猛睡的时候，你要学习</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>【转载】PHP 实现 Base64 编码/解码</title>
      <link>https://her-cat.com/posts/2020/01/08/php-implements-base64-encoding-decoding/</link>
      <pubDate>Wed, 08 Jan 2020 18:47:56 +0800</pubDate>
      <guid>https://her-cat.com/posts/2020/01/08/php-implements-base64-encoding-decoding/</guid>
      <description>对于现在很多的 PHP 程序员而言，绝大部分时间都是在做业务有关的代码，其它方面可能涉及的比较少，因此今天准备和大家讲讲不一样的知识，Base64 加密算法，上午花了一点儿时间用 PHP</description>
      <content:encoded><![CDATA[<blockquote>
<p>作者：<a href="https://learnku.com/users/39566">Dennis_Ritchie</a> 原文地址：<a href="https://learnku.com/articles/36655">https://learnku.com/articles/36655</a></p></blockquote>
<h2 id="多看看外面的世界">多看看外面的世界</h2>
<p>对于现在很多的 PHP 程序员而言，绝大部分时间都是在做业务有关的代码，其它方面可能涉及的比较少，因此今天准备和大家讲讲不一样的知识，<strong>Base64加密算法</strong>，上午花了一点儿时间用PHP重新实现了一遍，因为之前使用c写的，中间也出现了一些bug，但是很快修复了，代码我已经上传到了码云<a href="https://gitee.com/obamajs/php-base64-implemention/blob/master/Base64.php">php-base64-implemention</a>，希望大家下载下来仔细的分析一哈。</p>
<h2 id="知识储备">知识储备</h2>
<p>如果对位操作不熟悉的读者，建议先看一下这方面的内容，非常简单，几分钟就可以了。</p>
<h2 id="友情链接">友情链接</h2>
<p><a href="https://baike.baidu.com/pic/ASCII/309296/0/e850352ac65c103880a07b53bc119313b17e8941?fr=lemma&amp;ct=single#aid=0&amp;pic=e850352ac65c103880a07b53bc119313b17e8941">ASCII图</a></p>
<h2 id="base64作用">Base64作用</h2>
<p>base64的作用是把任意的字符序列转换为只包含特殊字符集的序列，那么base64加密之后的文本包含哪些字符呢？</p>
<ul>
<li>A-Z</li>
<li>a-z</li>
<li>0-9</li>
<li>+和/</li>
</ul>
<p>上面总共包含64个字符，所以每个字符都使用6位来表示，下面有一张表，可以清晰的说明这个问题</p>
<p><img alt="PHP实现Base64加密算法" decoding="async" height="467" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2020/01/08/php-implements-base64-encoding-decoding/base64.png" srcset="/posts/2020/01/08/php-implements-base64-encoding-decoding/base64_hu_8346a2dd8a2b35cf.png 384w" style="max-width: 100%; height: auto; aspect-ratio: 1.4582;" width="681"></p>
<p>这个是我在维基百科的截图，举个例子，对于Base64加密之后的字符A，对应的数值为0，二进制表示就是000000，如果你现在不懂，没关系，后面我会仔细的讲解加密和解密的过程。</p>
<h2 id="base64加密">Base64加密</h2>
<p>上面我已经提到了，每个Base64字符用6位来表示，但是一个字节是8位，所以3个字节刚好可以生成4个Base64字符，这应该很容易计算出来，下面我给大家举个例子，假如说现在有个字符串为&quot;123&quot;，1的ASCII为49，那么转换为二进制就是 00110001，2的ASCII为50，那么转换为二进制就是00110010，3的ASCII为51，那么转换为二进制就是00110011，这三个二进制组合在一起就是这样：001100010011001000110011
上面的二进制位总共24位，从左到右依次取6位，对应关系如下：</p>
<ul>
<li>第一个6位，001100，查阅上面的图标，对应M</li>
<li>第二个6位，010011，同样的操作，对应T</li>
<li>第三个6位，001000，对应I</li>
<li>第四个6位，110011，对应z</li>
</ul>
<p>所以经过上面的分析，123转换为Base64之后，就是MTIz，是不是很简单？正常情况下都是很美好的，但是我们刚才的分析建立在加密之前的字节数是3的倍数，那么如果不是呢，比如剩下一个字节，或者是2个，别急，下面来一一分析。</p>
<h3 id="补齐">补齐</h3>
<p>如果剩下一个字节，那么也就是说剩下8位，因为6位才能组合成一组啊，所以我们需要给它补上，补多少呢？只要4位就行了，12位刚好可以凑成2个Base64字符，那么补什么呢？很简单，补0000就可以了，还是以上面的123为例，但是我们给它加上一个4，所以现在是“1234”，根据上面的分析，123刚好可以转换为4个Base64字符，所以不管它，和上面的一模一样,。现在我们只需要分析后面的4，4的ASCII为52，转换为二进制就是00110100，我们给它加上4个0，那么结果就是001101000000，再对它进行6位分割，001101和000000，查表得到N和A，没错，这就是正确答案，但是为了后面的解码，我们需要在加密后的字符串末尾加上2个“=”，就是“MTIzNA==”。</p>
<p>如果剩下2个字节的话，2个字节刚好16位，6位一组的话，也就是说，少了2位，这样就可以组合成18位了（3个Base64字符），这里我们以字符串“12”为例，1的ASCII转换为二进制是00110001，2的ASCII转换为二进制是00110010，我们将它组合在一起然后补齐之后（加上2个0），就是001100010011001000，按照6位一组进行分割，然后查表求得，结果是MTI，但是为了后面的解码，我们需要在加密后的字符串末尾加上1个“=”，就是“MTI=”。</p>
<h2 id="base64解密">Base64解密</h2>
<p>有了加密的基础，解密就很简单了，以上面的加密结果为例 “MTIzNA==”，下面我们分别分析：</p>
<ul>
<li>我们首先判断字符串末尾是否有“=”，如果没有的话，那么也就是说，原始字符串没有补位操作，按照4个Base64字符转换为3个8位的字节算法就可以了，4个字符组合起来就是24位，按照8位一个字节，就是三个字节。</li>
<li>如果末尾有2个等于号“==”，也就是说之前进行了补位操作，通过上面加密的流程可以知道，原始字节流中，剩余1个字节，补了4个0，得到了2个Base64字符，所以加密字符串中，除了最后2个字符，其余按照没有补位的转换操作就可以了，对于最后的2个Base64字符，我们把他们对应的二进制位组合起来，然后再进行 右移 4位，就得到了一个8位的字节。</li>
<li>如果末尾有一个等于号“=”，也就是说未加密之前，剩余2个字节，所以按照上面所说的，加密的时候，需要补齐2个0，这样就形成了三个Base64字符，那么除了最后的三个字符，其余的按照正常的转换就可以了，对于最后的三个Base64字符，我们把他们的二进制位组合起来总共18位，然后右移2位，就得到了16位的2个字节。</li>
</ul>
<p><strong>Base64解密的时候，需要查上面的表，进行反向操作，举个例子，对于Base64字符M，查表得到它对应的6位二进制位为001100，一定要谨记这一点</strong>。</p>
<h2 id="base64-代码实现">Base64 代码实现</h2>
<p>上面讲解了Base64的加密和解密方法，说起来容易做起来难啊，在PHP里面尤其如此</p>
<h3 id="6位数字-转换为base64字符参考上图">6位数字 转换为Base64字符（参考上图）</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">function normalToBase64Char($num)
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    if ($num &gt;= 0 &amp;&amp; $num &lt;= 25) {
</span></span><span class="line"><span class="cl">        return chr(ord(&#39;A&#39;) + $num);
</span></span><span class="line"><span class="cl">    } else if ($num &gt;= 26 &amp;&amp; $num &lt;= 51) {
</span></span><span class="line"><span class="cl">        return chr(ord(&#39;a&#39;) + ($num - 26));
</span></span><span class="line"><span class="cl">    } else if ($num &gt;= 52 &amp;&amp; $num &lt;= 61) {
</span></span><span class="line"><span class="cl">        return chr(ord(&#39;0&#39;) + ($num - 52));
</span></span><span class="line"><span class="cl">    } else if ($num == 62) {
</span></span><span class="line"><span class="cl">        return &#39;+&#39;;
</span></span><span class="line"><span class="cl">    } else {
</span></span><span class="line"><span class="cl">        return &#39;/&#39;;
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>上面的代码就是截图的PHP代码实现，这里我提醒大家不要把Base64的a字符和ASCII的a字符混淆起来，两种情况下存在着上图的映射关系，再次提醒一下，这个函数传入的是6位的数据。</p>
<h3 id="base64字符转换为-6位数字">Base64字符转换为 6位数字</h3>
<p>这个过程就是 <strong>6位数字 转换为Base64字符</strong>的逆过程，代码如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">function base64CharToInt($num)
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    if ($num &gt;= 65 &amp;&amp; $num &lt;= 90) {
</span></span><span class="line"><span class="cl">        return ($num - 65);
</span></span><span class="line"><span class="cl">    } else if ($num &gt;= 97 &amp;&amp; $num &lt;= 122) {
</span></span><span class="line"><span class="cl">        return ($num - 97) + 26;
</span></span><span class="line"><span class="cl">    } else if ($num &gt;= 48 &amp;&amp; $num &lt;= 57) {
</span></span><span class="line"><span class="cl">        return ($num - 48) + 52;
</span></span><span class="line"><span class="cl">    } else if ($num == 43) {
</span></span><span class="line"><span class="cl">        return 62;
</span></span><span class="line"><span class="cl">    } else {
</span></span><span class="line"><span class="cl">        return 63;
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>对于任意一个Base64字符，我们首先要获取到它对应的ASCII值，再根据这个值，通过上面的表的映射关系，求出它对应Base64数值，这个数据就是未加密数据的真实字节数据。</p>
<h3 id="加密代码实现">加密代码实现</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">function encode($content)
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    $len = strlen($content);
</span></span><span class="line"><span class="cl">    $loop = intval($len / 3);//完整组合
</span></span><span class="line"><span class="cl">    $rest = $len % 3;//剩余字节数，需要补齐
</span></span><span class="line"><span class="cl">    $ret = &#34;&#34;;
</span></span><span class="line"><span class="cl">    //首先计算完整组合
</span></span><span class="line"><span class="cl">    for ($i = 0; $i &lt; $loop; $i++) {
</span></span><span class="line"><span class="cl">        $base_offset = 3 * $i;
</span></span><span class="line"><span class="cl">        //每三个字节组合成一个无符号的24位的整数
</span></span><span class="line"><span class="cl">        $int_24 = (ord($content[$base_offset]) &lt;&lt; 16)
</span></span><span class="line"><span class="cl">            | (ord($content[$base_offset + 1]) &lt;&lt; 8)
</span></span><span class="line"><span class="cl">            | (ord($content[$base_offset + 2]) &lt;&lt; 0);
</span></span><span class="line"><span class="cl">        //6位一组，每一组都进行Base64字符串转换
</span></span><span class="line"><span class="cl">        $ret .= self::normalToBase64Char($int_24 &gt;&gt; 18);
</span></span><span class="line"><span class="cl">        $ret .= self::normalToBase64Char(($int_24 &gt;&gt; 12) &amp; 0x3f);
</span></span><span class="line"><span class="cl">        $ret .= self::normalToBase64Char(($int_24 &gt;&gt; 6) &amp; 0x3f);
</span></span><span class="line"><span class="cl">        $ret .= self::normalToBase64Char($int_24 &amp; 0x3f);
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">    //需要补齐的情况
</span></span><span class="line"><span class="cl">    if ($rest == 0) {
</span></span><span class="line"><span class="cl">        return $ret;
</span></span><span class="line"><span class="cl">    } else if ($rest == 1) {
</span></span><span class="line"><span class="cl">        //剩余1个字节，此时需要补齐4位
</span></span><span class="line"><span class="cl">        $int_12 = ord($content[$loop * 3]) &lt;&lt; 4;
</span></span><span class="line"><span class="cl">        $ret .= self::normalToBase64Char($int_12 &gt;&gt; 6);
</span></span><span class="line"><span class="cl">        $ret .= self::normalToBase64Char($int_12 &amp; 0x3f);
</span></span><span class="line"><span class="cl">        $ret .= &#34;==&#34;;
</span></span><span class="line"><span class="cl">        return $ret;
</span></span><span class="line"><span class="cl">    } else {
</span></span><span class="line"><span class="cl">        //剩余2个字节，需要补齐2位
</span></span><span class="line"><span class="cl">        $int_18 = ((ord($content[$loop * 3]) &lt;&lt; 8) | ord($content[$loop * 3 + 1])) &lt;&lt; 2;
</span></span><span class="line"><span class="cl">        $ret .= self::normalToBase64Char($int_18 &gt;&gt; 12);
</span></span><span class="line"><span class="cl">        $ret .= self::normalToBase64Char(($int_18 &gt;&gt; 6) &amp; 0x3f);
</span></span><span class="line"><span class="cl">        $ret .= self::normalToBase64Char($int_18 &amp; 0x3f);
</span></span><span class="line"><span class="cl">        $ret .= &#34;=&#34;;
</span></span><span class="line"><span class="cl">        return $ret;
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><p>上面的代码和我之前分析的一模一样。</p>
<h3 id="解密代码实现">解密代码实现</h3>
<p>解密的过程复杂一点儿，但是只要你看懂上面我所说的，肯定没问题。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">function decode($content)
</span></span><span class="line"><span class="cl">{
</span></span><span class="line"><span class="cl">    $len = strlen($content);
</span></span><span class="line"><span class="cl">    if ($content[$len - 1] == &#39;=&#39; &amp;&amp; $content[$len - 2] == &#39;=&#39;) {
</span></span><span class="line"><span class="cl">        //说明加密的时候，剩余1个字节，补齐了4位，也就是左移了4位，所以除了最后包含的2个字符，前面的所有字符可以4个字符一组
</span></span><span class="line"><span class="cl">        $last_chars = substr($content, -4);
</span></span><span class="line"><span class="cl">        $full_chars = substr($content, 0, $len - 4);
</span></span><span class="line"><span class="cl">        $type = 1;
</span></span><span class="line"><span class="cl">    } else if ($content[$len - 1] == &#39;=&#39;) {
</span></span><span class="line"><span class="cl">        //说明加密的时候，剩余2个字节，补齐了2位，也就是左移了2位，所以除了最后包含的3个字符，前面的所有字符可以4个字符一组
</span></span><span class="line"><span class="cl">        $last_chars = substr($content, -4);
</span></span><span class="line"><span class="cl">        $full_chars = substr($content, 0, $len - 4);
</span></span><span class="line"><span class="cl">        $type = 2;
</span></span><span class="line"><span class="cl">    } else {
</span></span><span class="line"><span class="cl">        $type = 3;
</span></span><span class="line"><span class="cl">        $full_chars = $content;
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    //首先处理完整的部分
</span></span><span class="line"><span class="cl">    $loop = strlen($full_chars) / 4;
</span></span><span class="line"><span class="cl">    $ret = &#34;&#34;;
</span></span><span class="line"><span class="cl">    for ($i = 0; $i &lt; $loop; $i++) {
</span></span><span class="line"><span class="cl">        $base_offset = 4 * $i;
</span></span><span class="line"><span class="cl">        $int_24 = (self::base64CharToInt(ord($full_chars[$base_offset])) &lt;&lt; 18)
</span></span><span class="line"><span class="cl">            | (self::base64CharToInt(ord($full_chars[$base_offset + 1])) &lt;&lt; 12)
</span></span><span class="line"><span class="cl">            | (self::base64CharToInt(ord($full_chars[$base_offset + 2])) &lt;&lt; 6)
</span></span><span class="line"><span class="cl">            | (self::base64CharToInt(ord($full_chars[$base_offset + 3])) &lt;&lt; 0);
</span></span><span class="line"><span class="cl">        $ret .= chr($int_24 &gt;&gt; 16);
</span></span><span class="line"><span class="cl">        $ret .= chr(($int_24 &gt;&gt; 8) &amp; 0xff);
</span></span><span class="line"><span class="cl">        $ret .= chr($int_24 &amp; 0xff);
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">    //紧接着处理补齐的部分
</span></span><span class="line"><span class="cl">    if ($type == 1) {
</span></span><span class="line"><span class="cl">        $l_char = chr(((self::base64CharToInt(ord($last_chars[0])) &lt;&lt; 6)
</span></span><span class="line"><span class="cl">                | (self::base64CharToInt(ord($last_chars[1])))) &gt;&gt; 4);
</span></span><span class="line"><span class="cl">        $ret .= $l_char;
</span></span><span class="line"><span class="cl">    } else if ($type == 2) {
</span></span><span class="line"><span class="cl">        $l_two_chars = ((self::base64CharToInt(ord($last_chars[0])) &lt;&lt; 12)
</span></span><span class="line"><span class="cl">                | (self::base64CharToInt(ord($last_chars[1])) &lt;&lt; 6)
</span></span><span class="line"><span class="cl">                | (self::base64CharToInt(ord($last_chars[2])) &lt;&lt; 0)) &gt;&gt; 2;
</span></span><span class="line"><span class="cl">        $ret .= chr($l_two_chars &gt;&gt; 8);
</span></span><span class="line"><span class="cl">        $ret .= chr($l_two_chars &amp; 0xff);
</span></span><span class="line"><span class="cl">    }
</span></span><span class="line"><span class="cl">    return $ret;
</span></span><span class="line"><span class="cl">}
</span></span></code></pre></div><h2 id="告诫">告诫</h2>
<p>任何代码都不能缺少理论的支撑，所以在看代码前，请仔细的阅读Base64的基本原理，一旦原理看懂了，阅读代码就不是那么难了，任何时候阅读别人的代码，这都是应该谨记的地方，之前就已经告诉大家了，代码已经上传到码云，<a href="https://gitee.com/obamajs/php-base64-implemention/blob/master/Base64.php">php-base64-implemention</a>，代码没有问题，完全可以运行，如果有问题可以找我，博文的最后面有我的联系方式，祝您假期愉快。</p>
<h2 id="交流学习">交流学习</h2>
<p>我建了一个qq群，大家平时可以交流学习，我也会给大家讲解Laravel的底层知识和其它编程知识。</p>
]]></content:encoded>
    </item>
    <item>
      <title>C# Base64 加密及解密</title>
      <link>https://her-cat.com/posts/2017/04/10/c-sharp-base64/</link>
      <pubDate>Mon, 10 Apr 2017 20:23:57 +0800</pubDate>
      <guid>https://her-cat.com/posts/2017/04/10/c-sharp-base64/</guid>
      <description>今天准备采集一个网站，在分析他的数据的接口的时候，发现提交及返回的数据都是经过 js 加密的。然后我就开始把 js 转成 C# 的代码，运行后发现返回的数</description>
      <content:encoded><![CDATA[<p>今天准备采集一个网站，在分析他的数据的接口的时候，发现提交及返回的数据都是经过 js 加密的。然后我就开始把 js 转成 C# 的代码，运行后发现返回的数据不一样，我就在想他是不是 Base64 过的，然后就解开了&hellip;</p>
<p>附上代码：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl">    <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// Base64编码及解码</span>
</span></span><span class="line"><span class="cl">    <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="k">class</span> <span class="nc">Base64</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">Encode</span><span class="p">(</span><span class="kt">string</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">try</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="kt">byte</span><span class="p">[]</span> <span class="n">dataByte</span> <span class="p">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="p">[</span><span class="n">data</span><span class="p">.</span><span class="n">Length</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">                <span class="n">dataByte</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="n">GetBytes</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="kt">string</span> <span class="n">encodedData</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">ToBase64String</span><span class="p">(</span><span class="n">dataByte</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">encodedData</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                <span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">Message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="cs">/// &lt;summary&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="cs">/// 解码(UTF8)</span>
</span></span><span class="line"><span class="cl">        <span class="cs">/// &lt;/summary&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="cs">/// &lt;param name=&#34;data&#34;&gt;&lt;/param&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="cs">/// &lt;returns&gt;&lt;/returns&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="kd">public</span> <span class="kd">static</span> <span class="kt">string</span> <span class="n">Decode</span><span class="p">(</span><span class="kt">string</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="k">try</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="kt">byte</span><span class="p">[]</span> <span class="n">outputb</span> <span class="p">=</span> <span class="n">Convert</span><span class="p">.</span><span class="n">FromBase64String</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="kt">string</span> <span class="n">result</span> <span class="p">=</span> <span class="n">System</span><span class="p">.</span><span class="n">Text</span><span class="p">.</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="n">GetString</span><span class="p">(</span><span class="n">outputb</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">                <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">e</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="k">throw</span> <span class="k">new</span> <span class="n">Exception</span><span class="p">(</span><span class="n">se</span><span class="p">.</span><span class="n">Message</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span></code></pre></div><p>使用方法：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c#" data-lang="c#"><span class="line"><span class="cl"><span class="kt">string</span> <span class="n">encodeStr</span> <span class="p">=</span> <span class="n">Base64</span><span class="p">.</span><span class="n">Encode</span><span class="p">(</span><span class="s">&#34;她和她的猫&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">string</span> <span class="n">decodeStr</span> <span class="p">=</span> <span class="n">Base64</span><span class="p">.</span><span class="n">Decode</span><span class="p">(</span><span class="n">encodeStr</span><span class="p">);</span>
</span></span></code></pre></div><blockquote>
<p>这是一篇过去很久的文章，其中的信息可能已经有所发展或是发生改变。</p></blockquote>
]]></content:encoded>
    </item><follow_challenge>
      <feedId>58021783493571598</feedId>
      <userId>56882619875632128</userId>
    </follow_challenge>
  </channel>
</rss>
