<?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>编码 - 她和她的猫</title>
    <link>https://her-cat.com/tags/%E7%BC%96%E7%A0%81/</link>
    <description>编码的文章列表 - 她和她的猫</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>Sun, 08 Jun 2025 11:58:52 +0800</lastBuildDate>
    <atom:link href="https://her-cat.com/tags/%E7%BC%96%E7%A0%81/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Yar 源码阅读笔记：消息编码模块</title>
      <link>https://her-cat.com/posts/2021/12/23/yar-internals-packager/</link>
      <pubDate>Thu, 23 Dec 2021 16:08:05 +0800</pubDate>
      <guid>https://her-cat.com/posts/2021/12/23/yar-internals-packager/</guid>
      <description>今天我们来了解一下，Yar 中的消息编码模块是怎样实现的。</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>在 <a href="https://her-cat.com/posts/2021/11/03/yar-internals-protocol/">上一篇文章</a> 中，我们知道了 Yar 通信协议的格式及作用，还提到了在 Yar 客户端发送请求前和收到响应后，需要先对数据进行编码与解码才能继续进一步操作。</p>
<p>今天我们来了解一下，Yar 中的消息编码模块是怎样实现的。</p>
<h2 id="什么是编码解码">什么是编码/解码</h2>
<p>在介绍 Yar 编码模块之前，我们先来了解一下，什么是编码与解码？</p>
<p>编码（encode）和解码（decode）也有人叫作序列化（serialization）和反序列化（deserialization），其实都是一个意思。</p>
<blockquote>
<p>为了统一描述，在后文中都称为编码和解码。</p></blockquote>
<p>编码是指将数据结构或对象转换成可以存储或传输的数据格式（字节流）。</p>
<p>解码是编码的反向操作，将字节流转换为数据数据结构或对象。</p>
<p>举个例子，假如要将 Person 对象写入到文件中，然后再从文件中还原该对象，应该怎么处理？</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">Person</span> 
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">public</span> <span class="nv">$name</span><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="nv">$age</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="nv">$person</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="nv">$person</span><span class="o">-&gt;</span><span class="na">name</span> <span class="o">=</span> <span class="s1">&#39;her-cat&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$person</span><span class="o">-&gt;</span><span class="na">age</span> <span class="o">=</span> <span class="mi">18</span><span class="p">;</span>
</span></span></code></pre></div><p>PHP 提供了两个函数： <a href="https://www.php.net/manual/zh/function.serialize.php">serialize</a>（编码）和 <a href="https://www.php.net/manual/zh/function.unserialize.php">unserialize</a>（解码）。</p>
<p>serialize 函数可以将除了 resource 类型以外的数据编码为字符串（字节流）。</p>
<p>unserialize 函数可以将编码后的字符串转换为 PHP 的值。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="cl"><span class="nv">$encoded</span> <span class="o">=</span> <span class="nx">file_get_contents</span><span class="p">(</span><span class="s1">&#39;person.txt&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$encoded</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">$decodedPerson</span> <span class="o">=</span> <span class="nx">unserialize</span><span class="p">(</span><span class="nv">$encoded</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$decodedPerson</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$decodedPerson</span><span class="o">-&gt;</span><span class="na">name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="nx">var_dump</span><span class="p">(</span><span class="nv">$decodedPerson</span><span class="o">-&gt;</span><span class="na">age</span><span class="p">);</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">string(57) &#34;O:6:&#34;Person&#34;:2:{s:4:&#34;name&#34;;s:7:&#34;her-cat&#34;;s:3:&#34;age&#34;;i:18;}&#34;
</span></span><span class="line"><span class="cl">object(Person)#2 (2) {
</span></span><span class="line"><span class="cl">  [&#34;name&#34;]=&gt;
</span></span><span class="line"><span class="cl">  string(7) &#34;her-cat&#34;
</span></span><span class="line"><span class="cl">  [&#34;age&#34;]=&gt;
</span></span><span class="line"><span class="cl">  int(18)
</span></span><span class="line"><span class="cl">}
</span></span><span class="line"><span class="cl">string(7) &#34;her-cat&#34;
</span></span><span class="line"><span class="cl">int(18)
</span></span></code></pre></div><h2 id="为什么需要编码解码">为什么需要编码/解码</h2>
<p>目的就是为了在不同的编程语言或不同的载体之间交换数据。</p>
<p>上面将对象写入到文件中就是不同载体之间交换数据的例子，这里再举一个不同语言之间的例子。</p>
<p>就拿 PHP 和 JavaScript 来说，我们用 PHP 写了一个 HTTP 接口给 JavaScript 调用，然后 JavaScript 解析 PHP 响应的内容。</p>
<p>PHP 和 JavaScript 是两种不同的编程语言（<del>废话</del>），两者各种数据结构的实现是不一样的，在内存上的组织方式也不一样，所以它们不能直接使用内存中的数据进行数据交换。</p>
<p>就好比两个人在交流，一个人讲蒙古语，另一个人讲粤语，最后两人都不知道对方讲的啥玩意。</p>
<p>所以，我们需要一个中间人帮忙翻译，或者使用两个人都会的一种语言进行交流，比如普通话。</p>
<p>将 “普通话” 代入到上面的例子中，实际上就是引用一种通用的数据编码格式，让 PHP 与 JavaScript 能够进行“交流”。</p>
<p>假设我们使用 JSON，PHP 将数据发送到前端之前，先调用 json_encode 将数据转换成 JSON 字符串（这个过程就是编码），
前端页面收到 PHP 返回的 JSON 字符串后，将其解析成前端的 JavaScript 的数组/对象（这个过程就是解码）。</p>
<h2 id="yar-编码模块简介">Yar 编码模块简介</h2>
<p>Yar 提供了三种编码方式：分别是 PHP、JSON、Msgpack。</p>
<p><a href="https://www.php.net/manual/zh/function.serialize.php">PHP</a> 是 PHP 内置的一种对数据结构/对象编码的方式，实际上就是使用 serialize 和 unserialize 这对函数，可以参考上面的例子。</p>
<p><a href="http://www.json.org/json-zh">JSON</a> 是目前比较通用且流行的一种数据编码格式，由于其简单、易读的特点，所以被广泛地用于 API 接口、JSON-RPC、数据存储等地方。</p>
<p>但是，JSON 也不是一点儿缺点都没有的，JSON 为了保证其可读性，需要多使用一些内存来保存相关信息，所以在数据传输的过程中，占用的内存就会比 Msgpack 这类编码格式要大一些。</p>
<p><a href="https://msgpack.org/">Msgpack</a> 一种高效的二进制编码格式，有点儿类似于 JSON，但是比 JSON 占用内存更小、效率更高。</p>
<p>前两种方式在 Yar 中已经内置支持了（直接调用相关的 PHP 函数），Msgpack 则需要自己手动安装扩展，并在编译时指定。</p>
<h3 id="编码模块结构体">编码模块结构体</h3>
<p>Yar 定义了编码模块结构体：yar_packager_t。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_packager.h
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_yar_packager</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">char</span> <span class="o">*</span><span class="n">name</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int</span>  <span class="p">(</span><span class="o">*</span><span class="n">pack</span><span class="p">)</span> <span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="n">_yar_packager</span> <span class="o">*</span><span class="n">self</span><span class="p">,</span> <span class="n">zval</span> <span class="o">*</span><span class="n">pzval</span><span class="p">,</span> <span class="n">smart_str</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">msg</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">zval</span> <span class="o">*</span> <span class="p">(</span><span class="o">*</span><span class="n">unpack</span><span class="p">)</span> <span class="p">(</span><span class="k">const</span> <span class="k">struct</span> <span class="n">_yar_packager</span> <span class="o">*</span><span class="n">self</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">content</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">msg</span><span class="p">,</span> <span class="n">zval</span> <span class="o">*</span><span class="n">ret</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kt">yar_packager_t</span><span class="p">;</span>
</span></span></code></pre></div><p>结构体中字段说明：</p>
<ul>
<li>name：编码方式的名称，比如 PHP、JSON、Msgpack。</li>
<li>pack：函数指针，对数据进行编码操作。</li>
<li>unpack：函数指针，对数据进行解码操作。</li>
</ul>
<p>yar_packager_t 结构体有点类似于 OOP 中的抽象类，为不同的编码方式提供了统一的接口，从而实现了多态。</p>
<p>如果我们想要扩展一种编码方式，只需要设置编码方式名称并实现 pack 和 unpack 函数即可。</p>
<h3 id="json-编码方式">JSON 编码方式</h3>
<p>Yar 支持的编码方式都在 packagers 目录下，这里我们用 JSON 方式进行举例。</p>
<p>在 packagers/json.c 文件中定义了一个类型为 yar_packager_t 的常量：yar_packager_json。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">const</span> <span class="kt">yar_packager_t</span> <span class="n">yar_packager_json</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="s">&#34;JSON&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">php_yar_packager_json_pack</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">php_yar_packager_json_unpack</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>首先是编码方式名称 JSON，然后是编码和解码对应的函数，这两个函数其实是对 json_encode 和 json_decode 函数的一层封装。</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">int</span> <span class="nf">php_yar_packager_json_pack</span><span class="p">(</span><span class="k">const</span> <span class="kt">yar_packager_t</span> <span class="o">*</span><span class="n">self</span><span class="p">,</span> <span class="n">zval</span> <span class="o">*</span><span class="n">pzval</span><span class="p">,</span> <span class="n">smart_str</span> <span class="o">*</span><span class="n">buf</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">php_json_encode</span><span class="p">(</span><span class="n">buf</span><span class="p">,</span> <span class="n">pzval</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">1</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="n">zval</span> <span class="o">*</span> <span class="nf">php_yar_packager_json_unpack</span><span class="p">(</span><span class="k">const</span> <span class="kt">yar_packager_t</span> <span class="o">*</span><span class="n">self</span><span class="p">,</span> <span class="kt">char</span> <span class="o">*</span><span class="n">content</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">msg</span><span class="p">,</span> <span class="n">zval</span> <span class="o">*</span><span class="n">ret</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">php_json_decode</span><span class="p">(</span><span class="n">ret</span><span class="p">,</span> <span class="n">content</span><span class="p">,</span> <span class="n">len</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">512</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">ret</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>其它两种编码方式的实现跟 JSON 差不多，这里就不赘述了。</p>
<h2 id="编码模块的生命周期">编码模块的生命周期</h2>
<p>Yar 作为 PHP 的扩展，其编码模块的生命周期肯定也是在 PHP 生命周期中的，这里我画了一张图，方便大家能够更直观的了解 Yar 编码模块的生命周期。</p>
<p><img decoding="async" height="803" loading="lazy" sizes="(max-width: 768px) 100vw, (max-width: 1024px) 768px, 1024px" src="/posts/2021/12/23/yar-internals-packager/yar-packager-life-cycle.png" srcset="/posts/2021/12/23/yar-internals-packager/yar-packager-life-cycle_hu_dd714c7b1978b3fe.png 384w, /posts/2021/12/23/yar-internals-packager/yar-packager-life-cycle_hu_93786d644e8dc225.png 768w, /posts/2021/12/23/yar-internals-packager/yar-packager-life-cycle_hu_900fb45961f1a841.png 1024w" style="max-width: 100%; height: auto; aspect-ratio: 1.3076;" width="1050"></p>
<p>接下来看看编码模块在每个阶段中都做了些什么。</p>
<h3 id="启动编码模块">启动编码模块</h3>
<p>在 PHP 模块初始化阶段，Yar 会先注册一些 Yar 相关的 PHP 常量，比如版本号、客户端相关的选项。紧接着开始初始化模块，除了编码模块以外，还会初始化传输、客户端、服务端及异常等模块。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nf">PHP_MINIT_FUNCTION</span><span class="p">(</span><span class="n">yar</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">// 注册 PHP 常量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">REGISTER_INI_ENTRIES</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nf">REGISTER_STRINGL_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_VERSION&#34;</span><span class="p">,</span> <span class="n">PHP_YAR_VERSION</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">PHP_YAR_VERSION</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">REGISTER_LONG_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_OPT_PACKAGER&#34;</span><span class="p">,</span> <span class="n">YAR_OPT_PACKAGER</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">REGISTER_LONG_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_OPT_PERSISTENT&#34;</span><span class="p">,</span> <span class="n">YAR_OPT_PERSISTENT</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">REGISTER_LONG_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_OPT_TIMEOUT&#34;</span><span class="p">,</span> <span class="n">YAR_OPT_TIMEOUT</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">REGISTER_LONG_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_OPT_CONNECT_TIMEOUT&#34;</span><span class="p">,</span> <span class="n">YAR_OPT_CONNECT_TIMEOUT</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nf">REGISTER_LONG_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_OPT_HEADER&#34;</span><span class="p">,</span> <span class="n">YAR_OPT_HEADER</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">REGISTER_LONG_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_OPT_RESOLVE&#34;</span><span class="p">,</span> <span class="n">YAR_OPT_RESOLVE</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">REGISTER_LONG_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_OPT_PROXY&#34;</span><span class="p">,</span> <span class="n">YAR_OPT_PROXY</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><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="nf">YAR_STARTUP</span><span class="p">(</span><span class="n">service</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">YAR_STARTUP</span><span class="p">(</span><span class="n">client</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">YAR_STARTUP</span><span class="p">(</span><span class="n">packager</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">YAR_STARTUP</span><span class="p">(</span><span class="n">transport</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">YAR_STARTUP</span><span class="p">(</span><span class="n">exception</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><blockquote>
<p>今天我们主要研究编码模块，其它几个模块放到后面的文章中。</p></blockquote>
<p>YAR_STARTUP 是一个宏，定义如下：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define YAR_STARTUP(module)    ZEND_MODULE_STARTUP_N(yar_##module)(INIT_FUNC_ARGS_PASSTHRU)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Name macros */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define ZEND_MODULE_STARTUP_N(module)    zm_startup_##module
</span></span></span></code></pre></div><p>YAR_STARTUP(packager) 展开之后是这样的：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="nf">zm_startup_yar_packager</span><span class="p">(</span><span class="n">type</span><span class="p">,</span> <span class="n">module_number</span><span class="p">)</span>
</span></span></code></pre></div><p>如果用 zm_startup_yar_packager 在 Yar 源码中是搜不到结果的，这是因为宏只有在编译的时候才会展开，那么为什么用 YAR_STARTUP(packager) 也搜不到呢？</p>
<p>因为在定义函数名的时候用的不是 YAR_STARTUP，而是 YAR_STARTUP_FUNCTION，先来看看定义：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define YAR_STARTUP_FUNCTION(module)    ZEND_MINIT_FUNCTION(yar_##module)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define ZEND_MINIT_FUNCTION    ZEND_MODULE_STARTUP_D
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Declaration macros */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define ZEND_MODULE_STARTUP_D(module)    zend_result ZEND_MODULE_STARTUP_N(module)(INIT_FUNC_ARGS)
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cm">/* Name macros */</span>
</span></span><span class="line"><span class="cl"><span class="cp">#define ZEND_MODULE_STARTUP_N(module)    zm_startup_##module
</span></span></span></code></pre></div><p>可以看到，最终使用的都是 ZEND_MODULE_STARTUP_N，它还有一个相似的宏：ZEND_MODULE_STARTUP_D，不同的是，前者是用于命名的宏，无参数，而后者是用于声明/定义的宏，有参数。</p>
<p>在 YAR_STARTUP_FUNCTION(packager) 函数中，先调用 php_yar_packager_register 函数往 Yar 编码模块中注册编码方式，然后往 PHP 中注册编码模块相关的 PHP 常量。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_packager.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nf">YAR_STARTUP_FUNCTION</span><span class="p">(</span><span class="n">packager</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="cp">#ifdef ENABLE_MSGPACK
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="nf">php_yar_packager_register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">yar_packager_msgpack</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="nf">php_yar_packager_register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">yar_packager_php</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nf">php_yar_packager_register</span><span class="p">(</span><span class="o">&amp;</span><span class="n">yar_packager_json</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 注册编码模块相关的 PHP 常量
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="nf">REGISTER_STRINGL_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_PACKAGER_PHP&#34;</span><span class="p">,</span> <span class="n">YAR_PACKAGER_PHP</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">YAR_PACKAGER_PHP</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="nf">REGISTER_STRINGL_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_PACKAGER_JSON&#34;</span><span class="p">,</span> <span class="n">YAR_PACKAGER_JSON</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">YAR_PACKAGER_JSON</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#ifdef ENABLE_MSGPACK
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>  <span class="nf">REGISTER_STRINGL_CONSTANT</span><span class="p">(</span><span class="s">&#34;YAR_PACKAGER_MSGPACK&#34;</span><span class="p">,</span> <span class="n">YAR_PACKAGER_MSGPACK</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">YAR_PACKAGER_MSGPACK</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">CONST_CS</span><span class="o">|</span><span class="n">CONST_PERSISTENT</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="cp">#endif
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="n">SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>接着我们来了解一下注册编码方式的逻辑，Yar 在编码模块中定义了一个结构体 yar_packagers_list，用于存储所有可用的编码方式。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_packager.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="n">_yar_packagers_list</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">size</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">num</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">yar_packager_t</span> <span class="o">**</span><span class="n">packagers</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">yar_packagers_list</span><span class="p">;</span>
</span></span></code></pre></div><p>结构体字段说明：</p>
<ul>
<li>size：packagers 数组的大小。</li>
<li>num：packagers 数组中编码方式的数量。</li>
<li>packagers：编码方式的指针数组。</li>
</ul>
<p>在 php_yar_packager_register 函数中，对 yar_packagers_list 进行初始化、扩容及注册编码方式等操作。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_packager.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">PHP_YAR_API</span> <span class="kt">int</span> <span class="nf">php_yar_packager_register</span><span class="p">(</span><span class="k">const</span> <span class="kt">yar_packager_t</span> <span class="o">*</span><span class="n">packager</span><span class="p">)</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="o">!</span><span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="c1">// size 为 0，说明未初始化
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>       <span class="c1">// 设置 size 初始值为 5，并为 packagers 分配相应的内存
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>       <span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">size</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">       <span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">packagers</span> <span class="o">=</span> <span class="p">(</span><span class="k">const</span> <span class="kt">yar_packager_t</span> <span class="o">**</span><span class="p">)</span><span class="nf">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="kt">yar_packager_t</span> <span class="o">*</span><span class="p">)</span> <span class="o">*</span> <span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">size</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="n">yar_packagers_list</span><span class="p">.</span><span class="n">num</span> <span class="o">==</span> <span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">       <span class="c1">// num 等于 size，说明数组已经满了，需要进行扩容
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>       <span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">size</span> <span class="o">+=</span> <span class="mi">5</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">       <span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">packagers</span> <span class="o">=</span> <span class="p">(</span><span class="k">const</span> <span class="kt">yar_packager_t</span> <span class="o">**</span><span class="p">)</span><span class="nf">realloc</span><span class="p">(</span><span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">packagers</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="kt">yar_packager_t</span> <span class="o">*</span><span class="p">)</span> <span class="o">*</span> <span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">size</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">// 将编码方式存储到 packagers 数组下标为 num 处
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">packagers</span><span class="p">[</span><span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">num</span><span class="p">]</span> <span class="o">=</span> <span class="n">packager</span><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">return</span> <span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">num</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="激活编码模块">激活编码模块</h3>
<p>每次请求进来到达请求初始化阶段时，就会调用 YAR_ACTIVATE_FUNCTION(packager) 函数设置本次请求使用的编码方式。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_packager.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nf">YAR_ACTIVATE_FUNCTION</span><span class="p">(</span><span class="n">packager</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="k">const</span> <span class="kt">yar_packager_t</span> <span class="o">*</span><span class="n">packager</span> <span class="o">=</span> <span class="nf">php_yar_packager_get</span><span class="p">(</span><span class="nf">YAR_G</span><span class="p">(</span><span class="n">default_packager</span><span class="p">),</span> <span class="nf">strlen</span><span class="p">(</span><span class="nf">YAR_G</span><span class="p">(</span><span class="n">default_packager</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="n">packager</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="nf">YAR_G</span><span class="p">(</span><span class="n">packager</span><span class="p">)</span> <span class="o">=</span> <span class="n">packager</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">SUCCESS</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">// 如果没有默认编码方式，则使用 PHP 作为默认的编码方式，并输出警告信息
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">YAR_G</span><span class="p">(</span><span class="n">packager</span><span class="p">)</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">yar_packager_php</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">php_error</span><span class="p">(</span><span class="n">E_WARNING</span><span class="p">,</span> <span class="s">&#34;unable to find package &#39;%s&#39;, use php instead&#34;</span><span class="p">,</span> <span class="nf">YAR_G</span><span class="p">(</span><span class="n">default_packager</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">SUCCESS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="调用编码模块">调用编码模块</h3>
<p>前面两个阶段都属于初始化的阶段，只有执行脚本的阶段才会去调用编码、解码函数，那么什么时候会调用呢？</p>
<p>上一篇文章中有提到 Yar 支持两种数据传输方式：HTTP、TCP。无论使用哪种传输方式，都会在<strong>发送请求前</strong>和<strong>收到响应后</strong>调用编码模块。</p>
<p>为此，Yar 提供了两个通用函数：php_yar_packager_pack、php_yar_packager_unpack，用于对数据进行编码或解码操作。</p>
<h4 id="php_yar_packager_pack">php_yar_packager_pack</h4>
<p>php_yar_packager_pack 函数只需要传入编码方式的名称和需要编码的数据，就可以使用对应的编码方式对数据进行编码。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_packager.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">zend_string</span> <span class="o">*</span><span class="nf">php_yar_packager_pack</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">packager_name</span><span class="p">,</span> <span class="n">zval</span> <span class="o">*</span><span class="n">pzval</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="n">header</span><span class="p">[</span><span class="mi">8</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="n">smart_str</span> <span class="n">buf</span> <span class="o">=</span> <span class="p">{</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">// 获取编码方式，如果传入的名称为 null，则使用默认的编码方式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">const</span> <span class="kt">yar_packager_t</span> <span class="o">*</span><span class="n">packager</span> <span class="o">=</span> <span class="n">packager_name</span> <span class="o">?</span>
</span></span><span class="line"><span class="cl">        <span class="nf">php_yar_packager_get</span><span class="p">(</span><span class="n">packager_name</span><span class="p">,</span> <span class="nf">strlen</span><span class="p">(</span><span class="n">packager_name</span><span class="p">))</span> <span class="o">:</span> <span class="nf">YAR_G</span><span class="p">(</span><span class="n">packager</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="o">!</span><span class="n">packager</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">php_error_docref</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="n">E_ERROR</span><span class="p">,</span> <span class="s">&#34;unsupported packager %s&#34;</span><span class="p">,</span> <span class="n">packager_name</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="mi">0</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">// 将编码方式的名称写入前 8 个字节中
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">memcpy</span><span class="p">(</span><span class="n">header</span><span class="p">,</span> <span class="n">packager</span><span class="o">-&gt;</span><span class="n">name</span><span class="p">,</span> <span class="mi">8</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">smart_str_alloc</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">,</span> <span class="n">YAR_PACKAGER_BUFFER_SIZE</span> <span class="cm">/* 1M */</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">smart_str_appendl</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">,</span> <span class="n">header</span><span class="p">,</span> <span class="mi">8</span><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="n">packager</span><span class="o">-&gt;</span><span class="nf">pack</span><span class="p">(</span><span class="n">packager</span><span class="p">,</span> <span class="n">pzval</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">buf</span><span class="p">,</span> <span class="n">msg</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="n">buf</span><span class="p">.</span><span class="n">s</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 如果编码成功，则在末尾加上 \0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="nf">smart_str_0</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">buf</span><span class="p">.</span><span class="n">s</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">// 编码失败则释放 buf 并返回 null
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">smart_str_free</span><span class="p">(</span><span class="o">&amp;</span><span class="n">buf</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>如果使用 JSON 编码方式，那么 packager-&gt;pack 实际上调用的是 php_yar_packager_json_pack 函数，将数据编码为 JSON 字符串。</p>
<h4 id="php_yar_packager_unpack">php_yar_packager_unpack</h4>
<p>php_yar_packager_unpack 函数不像 php_yar_packager_pack 函数直接传入编码方式的名称，而是传入需要解码的数据及数据的长度，通过取数据前 8 字节得到编码方式的名称，然后通过编码方式的名称获取编码方式，最后调用真正的解码函数对数据进行解码。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_packager.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="n">zval</span> <span class="o">*</span> <span class="nf">php_yar_packager_unpack</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">content</span><span class="p">,</span> <span class="kt">size_t</span> <span class="n">len</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">msg</span><span class="p">,</span> <span class="n">zval</span> <span class="o">*</span><span class="n">ret</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">char</span> <span class="o">*</span><span class="n">pack_info</span> <span class="o">=</span> <span class="n">content</span><span class="p">;</span> <span class="cm">/* 4 bytes, last byte is version */</span>
</span></span><span class="line"><span class="cl">    <span class="k">const</span> <span class="kt">yar_packager_t</span> <span class="o">*</span><span class="n">packager</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 数据往后移动 8 字节，就是之前提到的 payload
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">content</span> <span class="o">=</span> <span class="n">content</span> <span class="o">+</span> <span class="mi">8</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">len</span> <span class="o">-=</span> <span class="mi">8</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 编码方式的名称虽然占用了 8 字节，实际上只用了 7 字节，第 8 个字节存储 \0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="o">*</span><span class="p">(</span><span class="n">pack_info</span> <span class="o">+</span> <span class="mi">7</span><span class="p">)</span> <span class="o">=</span> <span class="sc">&#39;\0&#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">// 获取编码方式
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">packager</span> <span class="o">=</span> <span class="nf">php_yar_packager_get</span><span class="p">(</span><span class="n">pack_info</span><span class="p">,</span> <span class="mi">7</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="o">!</span><span class="n">packager</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">spprintf</span><span class="p">(</span><span class="n">msg</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="s">&#34;unsupported packager &#39;%s&#39;&#34;</span><span class="p">,</span> <span class="n">pack_info</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="nb">NULL</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">return</span> <span class="n">packager</span><span class="o">-&gt;</span><span class="nf">unpack</span><span class="p">(</span><span class="n">packager</span><span class="p">,</span> <span class="n">content</span><span class="p">,</span> <span class="n">len</span><span class="p">,</span> <span class="n">msg</span><span class="p">,</span> <span class="n">ret</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>如果使用 JSON 编码方式，那么 packager-&gt;unpack 实际上调用的是 php_yar_packager_json_unpack 函数，将数据（JSON 字符串）解码并存储到 ret 中。</p>
<h3 id="关闭编码模块">关闭编码模块</h3>
<p>当执行完 PHP 脚本后，进入请求关闭阶段清理执行脚本过程中使用到的资源。在此之后，如果是 PHP-FPM 模式下，则会进入请求初始化阶段等待下一次请求到来，停止 PHP-FPM 时才会进入模块关闭阶段；如果是 PHP-Cli 模式会直接进入到模块关闭阶段。</p>
<p>关闭编码模块的逻辑很简单，只需要释放 yar_packagers_list 中 packagers 使用的内存就可以了。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// source：yar_packager.c
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nf">YAR_SHUTDOWN_FUNCTION</span><span class="p">(</span><span class="n">packager</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">size</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">free</span><span class="p">(</span><span class="n">yar_packagers_list</span><span class="p">.</span><span class="n">packagers</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="n">SUCCESS</span><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>在本文中，基于 PHP 生命周期介绍了各个阶段中编码模块都做了些什么，在下一篇文章中将会介绍 Yar 的传输模块，通过该模块，我们就可以知道编码后的数据是怎样被发送出去的。</p>
<p>这篇文章断断续续花费了一个月的时间，期间经历了被裁员、离职、面试等等，心情也是跌宕起伏。之前换工作都是无缝衔接，中间也没休息过，所以这次就当放了个假，休息休息。这几天在家做做饭、搞搞卫生，感觉也还不错，在家待了几天后，想着抓紧把 Yar 系列写完，好准备下一个系列的文章。</p>
<p>至于工作，这段时间也面试了几家，如果没有合适的话，那就明年再战。</p>
<p>:)</p>
]]></content:encoded>
    </item><follow_challenge>
      <feedId>58021783493571598</feedId>
      <userId>56882619875632128</userId>
    </follow_challenge>
  </channel>
</rss>
