<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; TIPI</title>
	<atom:link href="https://www.phppan.com/tag/tipi/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sun, 10 May 2026 02:26:45 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>PHP内核中的异常基类</title>
		<link>https://www.phppan.com/2012/10/php-exception-class/</link>
		<comments>https://www.phppan.com/2012/10/php-exception-class/#comments</comments>
		<pubDate>Sun, 28 Oct 2012 23:03:52 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>
		<category><![CDATA[TIPI]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1748</guid>
		<description><![CDATA[PHP内核中的异常基类 在&#60;&#60;思考PHP语言三：异常处理&#62;&#62;中有说到异常的定义：异常处 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2>PHP内核中的异常基类</h2>
<p>在<a href="http://www.phppan.com/2010/12/thinkinphp-3-exception/">&lt;&lt;思考PHP语言三：异常处理&gt;&gt;</a>中有说到异常的定义：异常处理是指在语言中能够使程序按照一种标准的方法对于某些运行时错误和其他程序所检测到的异常事件做出反应。异常发生的时间是不可以确定的，如果一种语言不包括异常处理机制，这就会给语言带来额外的复杂性。对于异常的处理有三种方案，而PHP5使用的是将一个异常处理独立出来，作为专门的子程序或类存在。</p>
<p><a href="http://cn2.php.net/manual/zh/class.exception.php">Exception</a>是PHP中所有异常的基类，自从PHP5.1.0开始引入，自此，我们可以以面向对象的方式处理错误。  Exception类的声明如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">&nbsp;
 Exception <span style="color: #009900;">&#123;</span>
        <span style="color: #666666; font-style: italic;">/* 属性 */</span>
        <span style="color: #000000; font-weight: bold;">protected</span> string <span style="color: #000088;">$message</span> <span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">protected</span> int <span style="color: #000088;">$code</span> <span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">protected</span> string <span style="color: #000088;">$file</span> <span style="color: #339933;">;</span>
        <span style="color: #000000; font-weight: bold;">protected</span> int <span style="color: #000088;">$line</span> <span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #666666; font-style: italic;">/* 方法 */</span>
        <span style="color: #000000; font-weight: bold;">public</span> __construct <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span> string <span style="color: #000088;">$message</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;&quot;</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">,</span> int <span style="color: #000088;">$code</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">,</span> Exception <span style="color: #000088;">$previous</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">NULL</span> <span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span> <span style="color: #009900;">&#41;</span>
        final <span style="color: #000000; font-weight: bold;">public</span> string getMessage <span style="color: #009900;">&#40;</span> void <span style="color: #009900;">&#41;</span>
        final <span style="color: #000000; font-weight: bold;">public</span> Exception getPrevious <span style="color: #009900;">&#40;</span> void <span style="color: #009900;">&#41;</span>
        final <span style="color: #000000; font-weight: bold;">public</span> int getCode <span style="color: #009900;">&#40;</span> void <span style="color: #009900;">&#41;</span>
        final <span style="color: #000000; font-weight: bold;">public</span> string getFile <span style="color: #009900;">&#40;</span> void <span style="color: #009900;">&#41;</span>
        final <span style="color: #000000; font-weight: bold;">public</span> int getLine <span style="color: #009900;">&#40;</span> void <span style="color: #009900;">&#41;</span>
        final <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #990000;">array</span> getTrace <span style="color: #009900;">&#40;</span> void <span style="color: #009900;">&#41;</span>
        final <span style="color: #000000; font-weight: bold;">public</span> string getTraceAsString <span style="color: #009900;">&#40;</span> void <span style="color: #009900;">&#41;</span>
        <span style="color: #000000; font-weight: bold;">public</span> string __toString <span style="color: #009900;">&#40;</span> void <span style="color: #009900;">&#41;</span>
        final <span style="color: #000000; font-weight: bold;">private</span> void __clone <span style="color: #009900;">&#40;</span> void <span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>其中message表示异常消息内容，code表示异常代码，file表示抛出异常的文件名，line表示抛出异常在该文件中的行号。下面从  PHP内核的角度说明这些属性及对应的方法。</p>
<p>message表示异常的消息内容，其对应getMessage方法。message是自定义的异常消息，默认为空字符串。对于PHP内核来说，创建Exception对象时，有无message参数会影响  getMessage方法的返回值，以及显示异常时是否有with message  %s等字样。message成员变量的作用是为了让用户更好的定义说明异常类。</p>
<p>code表示异常代码，其对应getCode方法。和meesage成员变量一样，code也是用户自定义的内容，默认为0。</p>
<p>file表示抛出异常的文件名，其对应getFile方法，返回值为执行文件的文件名，在PHP内核中存储此文件名的字段为  EG(active_op_array)-&gt;filename  此字段的值在生成一个opcode列表时，PHP的内核会将此前正在编译文件的文件名赋值给opcode的filename属性，如生成一个函数的op_array，在初始化op_array时，会执行上面所说的赋值操作，这里的赋值是通过编译的全局变量来传递的。当代码执行时，EG(active_op_array)表示正在执行的opcode列表。</p>
<p>line表示抛出异常在该文件中的行号，其对应getLine方法，返回整数，即EG(opline_ptr)-&gt;lineno。对于每条PHP脚本生成的opcode，在编译时都会执行一次初始化操作，在这次初始化操作中，PHP内核会将当前正在编译的行号赋值给opcode的lineno属性。  EG(opline_ptr)是PHP内核执行的当前opcode，抛出异常时对应的行号即为此对象的lineno属性。</p>
<p>除了上面四个属性，异常类还包括一个非常重要的内容：异常的追踪信息。在异常类中，通过getTrace方法可以获取这些信息。此方法的作用相当于PHP的内置函数debug_backtrace。在代码实现层面他们最终都是调用zend_fetch_debug_backtrace函数。在此函数中通过回溯PHP的调用栈，返回代码追踪信息。与getTrace方法对应还有一个返回被串化值的方法getTraceAsString，以字符串替代数组返回异常追踪信息。</p>
<p>在构造函数中，从PHP5.3.0增加$previous参数，表示异常链中的前一个异常。在catch块中可以抛出一个新的异常，并引用原始的异常，为调试提供更多的信息。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/10/php-exception-class/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP变量的CV类型</title>
		<link>https://www.phppan.com/2012/04/php-compiled-variable/</link>
		<comments>https://www.phppan.com/2012/04/php-compiled-variable/#comments</comments>
		<pubDate>Thu, 05 Apr 2012 01:03:45 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[TIPI]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1640</guid>
		<description><![CDATA[PHP变量的CV类型 在我们使用VLD扩展查看PHP生成的中间代码时，经常会看到有这样一项：compiled  [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>PHP变量的CV类型</p>
<p>在我们使用VLD扩展查看PHP生成的中间代码时，经常会看到有这样一项：compiled vars:  !0 = $a，或者如果使用更加详细的 -dvld.verbosity=3参数，会看到IS_CV，IS_VAR等类型。这里所说的Compiled vars和IS_CV都与今天我们所要了解的CV类型有莫大的关系。</p>
<p>CV者，Compiled variable也。<br />
CV类型是PHP编译过程中产生的一种变量类型，以类似于缓存的方式，提高某些变量的存储速度。<br />
与CV类型同一级别的类型还有：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">	<span style="color: #339933;">#define IS_CONST (1&lt;&lt;0)</span>
	<span style="color: #339933;">#define IS_TMP_VAR (1&lt;&lt;1)</span>
	<span style="color: #339933;">#define IS_VAR (1&lt;&lt;2)</span>
	<span style="color: #339933;">#define IS_UNUSED (1&lt;&lt;3) /* Unused variable */</span>
	<span style="color: #339933;">#define IS_CV (1&lt;&lt;4) /* Compiled variable */</span></pre></td></tr></table></div>

<p>比如最常见的赋值语句：$a = 10;</p>
<p>以php -dvld.active=1 -dvld.verbosity=3 test.php(这段代码在放在test.php文件中)查看。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="shell" style="font-family:monospace;">&nbsp;
	function name:  (null)
	number of ops:  3
	compiled vars:  !0 = $a
	line     # *  op            return  operands
	----------------------------------------------
	   2     0  &gt;   EXT_STMT       RES[  IS_UNUSED  ]         OP1[  IS_UNUSED  ] OP2[  IS_UNUSED  ]
		 1      ASSIGN                 OP1[IS_CV !0 ] OP2[ ,  IS_CONST (0) 10 ]
	  13     2    &gt; RETURN                 OP1[IS_CONST (0) 1 ]</pre></td></tr></table></div>

<p>如上我们可以知道10的类型为IS_CONST，$a的类型为IS_CV。</p>
<p>这里的CV类型在代码运行时存储在哪呢？在什么时候能起到性能优化的作用？</p>
<p>我们知道PHP中间代码运行的数据大部分都放在全局变量execute_data中，对于这个变量我们通常以EX(element)的方式调用，如EX(CVs)、EX(opline)等等。通过对execute_data所有字段的查阅我们可以大概知道CV类型变量存放在EX(CVs)中。</p>
<p>如何判别呢？<br />
同样，以上面的简单赋值语句为例，依据其中间代码(ZEND_ASSIGN)，操作数的类型(IS_CV和IS_CONST)，可以得出其中间代码最终执行的函数为：ZEND_ASSIGN_SPEC_CV_CONST_HANDLE。在此函数中，对于操作数据处理：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">	zval <span style="color: #339933;">*</span>value <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>opline<span style="color: #339933;">-&gt;</span>op2.<span style="color: #202020;">u</span>.<span style="color: #202020;">constant</span><span style="color: #339933;">;</span>
	zval <span style="color: #339933;">**</span>variable_ptr_ptr <span style="color: #339933;">=</span> _get_zval_ptr_ptr_cv<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>opline<span style="color: #339933;">-&gt;</span>op1<span style="color: #339933;">,</span> EX<span style="color: #009900;">&#40;</span>Ts<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> BP_VAR_W TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>op2为右值，op1为左值，左值的查找处理是_get_zval_ptr_ptr_cv。如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">	<span style="color: #993333;">static</span> zend_always_inline zval <span style="color: #339933;">**</span>_get_zval_ptr_ptr_cv<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> znode <span style="color: #339933;">*</span>node<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> temp_variable <span style="color: #339933;">*</span>Ts<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> type TSRMLS_DC<span style="color: #009900;">&#41;</span>
	<span style="color: #009900;">&#123;</span>
		zval <span style="color: #339933;">***</span>ptr <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>CV_OF<span style="color: #009900;">&#40;</span>node<span style="color: #339933;">-&gt;</span>u.<span style="color: #202020;">var</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>UNEXPECTED<span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>ptr <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">return</span> _get_zval_cv_lookup<span style="color: #009900;">&#40;</span>ptr<span style="color: #339933;">,</span> node<span style="color: #339933;">-&gt;</span>u.<span style="color: #202020;">var</span><span style="color: #339933;">,</span> type TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">return</span> <span style="color: #339933;">*</span>ptr<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #666666; font-style: italic;">// 函数中的CV_OF宏定义</span>
	<span style="color: #339933;">#define CV_OF(i)     (EG(current_execute_data)-&gt;CVs[i])</span></pre></td></tr></table></div>

<p>上面的函数和宏定义道出了CV这个类缓存机制的实现过程和CV类型的存储位置。</p>
<p>在函数中，程序会先判断变量是否存在于EX(CVs) &#8211; 这就是存储位置，如果存在则直接返回，否则调用_get_zval_cv_lookup，通过HashTable操作在EG(active_symbol_table)表中查找变量。虽然HashTable的查找操作已经比较快了，但是与原始的数组操作相比还是不在一个数量级。这就是CV类型变量的性能优化点所在。</p>
<p>以上是变量的赋值操作，也是变量的创建过程，与此对应，在变量销毁时PHP内核应该会对此种类型的变量执行清除HashTable和数组两个操作，以unset操作为例，针对IS_CV类型的变量，其中间代码最终会执行ZEND_UNSET_VAR_SPEC_CV_HANDLER。在此函数中不管是ZEND_QUICK_SET类型的操作，还是常规的unset操作，都会对HashTable（EG(active_symbol_table)或target_symbol_table）和数组（EX(CVs)和ex->CVs）执行清除操作。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/04/php-compiled-variable/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP执行过程中的数据</title>
		<link>https://www.phppan.com/2012/02/php-execute-data/</link>
		<comments>https://www.phppan.com/2012/02/php-execute-data/#comments</comments>
		<pubDate>Mon, 13 Feb 2012 00:48:48 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[TIPI]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1598</guid>
		<description><![CDATA[PHP脚本在内核中一般会经过词法解析，语法解析、编译生成中间代码，执行中间代码这样四个大的步骤。其中，第四个步 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>PHP脚本在内核中一般会经过词法解析，语法解析、编译生成中间代码，执行中间代码这样四个大的步骤。其中，第四个步骤，执行中间代码PHP内核默认情况下是通过zend/zend_vm_execute.h文件中的execute函数调用执行完成，对于所有的中间代码，默认实现是以按顺序执行，当遇到函数等情况时跳出去执行，执行完后再回到跳出的位置继续执行。</p>
<style>
.entry p {margin:13px 5px 0 5px;}
</style>
<p>与过程相比，过程中的数据会更加重要，那么在执行过程中的核心数据结构有哪些呢？ 在Zend/zend_vm_execute.h文件中的execute函数实现中，zend_execute_data类型的execute_data变量贯穿整个中间代码的执行过程， 其在调用时并没有直接使用execute_data，而是使用EX宏代替，其定义在Zend/zend_compile.h文件中，如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define EX(element) execute_data.element</span></pre></td></tr></table></div>

<p>因此我们在execute函数或在opcode的实现函数中会看到EX(fbc)，EX(object)等宏调用， 它们是调用函数局部变量execute_data的元素：execute_data.fbc和execute_data.object。 execute_data不仅仅只有fbc、object等元素，它包含了执行过程中的中间代码，上一次执行的函数，函数执行的当前作用域，类等信息。 其结构如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> _zend_execute_data zend_execute_data<span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">struct</span> _zend_execute_data <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">struct</span> _zend_op <span style="color: #339933;">*</span>opline<span style="color: #339933;">;</span>
    zend_function_state function_state<span style="color: #339933;">;</span>
    zend_function <span style="color: #339933;">*</span>fbc<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* Function Being Called */</span>
    zend_class_entry <span style="color: #339933;">*</span>called_scope<span style="color: #339933;">;</span> 
    zend_op_array <span style="color: #339933;">*</span>op_array<span style="color: #339933;">;</span>  <span style="color: #808080; font-style: italic;">/* 当前执行的中间代码 */</span>
    zval <span style="color: #339933;">*</span>object<span style="color: #339933;">;</span>
    <span style="color: #993333;">union</span> _temp_variable <span style="color: #339933;">*</span>Ts<span style="color: #339933;">;</span>
    zval <span style="color: #339933;">***</span>CVs<span style="color: #339933;">;</span>
    HashTable <span style="color: #339933;">*</span>symbol_table<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* 符号表 */</span>
    <span style="color: #993333;">struct</span> _zend_execute_data <span style="color: #339933;">*</span>prev_execute_data<span style="color: #339933;">;</span>   <span style="color: #808080; font-style: italic;">/* 前一条中间代码执行的环境*/</span>
    zval <span style="color: #339933;">*</span>old_error_reporting<span style="color: #339933;">;</span>
    zend_bool nested<span style="color: #339933;">;</span>
    zval <span style="color: #339933;">**</span>original_return_value<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* */</span>
    zend_class_entry <span style="color: #339933;">*</span>current_scope<span style="color: #339933;">;</span>
    zend_class_entry <span style="color: #339933;">*</span>current_called_scope<span style="color: #339933;">;</span>
    zval <span style="color: #339933;">*</span>current_this<span style="color: #339933;">;</span>
    zval <span style="color: #339933;">*</span>current_object<span style="color: #339933;">;</span>
    <span style="color: #993333;">struct</span> _zend_op <span style="color: #339933;">*</span>call_opline<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>在前面的中间代码执行过程中有介绍：中间代码的执行最终是通过EX(opline)->handler(execute_data TSRMLS_CC)来调用最终的中间代码程序。 在这里会将主管中间代码执行的execute函数中初始化好的execture_data传递给执行程序。</p>
<p>zend_execute_data结构体部分字段说明如下：</p>
<ul>
<li>
opline字段：struct _zend_op类型，当前执行的中间代码</li>
<li>
op_array字段： zend_op_array类型，当前执行的中间代码队列</li>
<li>
fbc字段：zend_function类型，已调用的函数<br />
called_scope字段：zend_class_entry类型，当前调用对象作用域，常用操作是EX(called_scope) = Z_OBJCE_P(EX(object))， 即将刚刚调用的对象赋值给它。</li>
<li>
symbol_table字段： 符号表，存放局部变量，这在前面的<< 第六节 变量的生命周期 » 变量的作用域 >>有过说明。 在execute_data初始时，EX(symbol_table) = EG(active_symbol_table);</li>
<li>
prev_execute_data字段：前一条中间代码执行的中间数据，用于函数调用等操作的运行环境恢复。<br />
在execute函数中初始化时，会调用zend_vm_stack_alloc函数分配内存。 这是一个栈的分配操作，对于一段PHP代码的上下文环境，它存在于这样一个分配的空间作放置中间数据用，并作为栈顶元素。 当有其它上下文环境的切换（如函数调用），此时会有一个新的元素生成，上一个上下文环境会被新的元素压下去， 新的上下文环境所在的元素作为栈顶元素存在。
</li>
</ul>
<p>在zend_vm_stack_alloc函数中我们可以看到一些PHP内核中的优化。 比如在分配时，这里会存在一个最小分配单元，在zend_vm_stack_extend函数中， 分配的最小单位是ZEND_VM_STACK_PAGE_SIZE((64 * 1024) &#8211; 64)，这样可以在一定范围内控制内存碎片的大小。 又比如判断栈元素是否为空，在PHP5.3.1之前版本(如5.3.0)是通过第四个元素elelments与top的位置比较来实现， 而从PHP5.3.1版本开始，struct _zend_vm_stack结构就没有第四个元素，直接通过在当前地址上增加整个结构体的长度与top的地址比较实现。 两个版本结构代码及比较代码如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">// PHP5.3.0</span>
<span style="color: #993333;">struct</span> _zend_vm_stack <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">**</span>top<span style="color: #339933;">;</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">**</span>end<span style="color: #339933;">;</span>
    zend_vm_stack prev<span style="color: #339933;">;</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>elements<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>UNEXPECTED<span style="color: #009900;">&#40;</span>EG<span style="color: #009900;">&#40;</span>argument_stack<span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span>top <span style="color: #339933;">==</span> EG<span style="color: #009900;">&#40;</span>argument_stack<span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span>elements<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">//  PHP5.3.1</span>
<span style="color: #993333;">struct</span> _zend_vm_stack <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">**</span>top<span style="color: #339933;">;</span>
    <span style="color: #993333;">void</span> <span style="color: #339933;">**</span>end<span style="color: #339933;">;</span>
    zend_vm_stack prev<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>UNEXPECTED<span style="color: #009900;">&#40;</span>EG<span style="color: #009900;">&#40;</span>argument_stack<span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span>top <span style="color: #339933;">==</span> ZEND_VM_STACK_ELEMETS<span style="color: #009900;">&#40;</span>EG<span style="color: #009900;">&#40;</span>argument_stack<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #339933;">#define ZEND_VM_STACK_ELEMETS(stack) \
((void**)(((char*)(stack)) + ZEND_MM_ALIGNED_SIZE(sizeof(struct _zend_vm_stack))))</span></pre></td></tr></table></div>

<p>当一个上下文环境结束其生命周期后，如果回收这段内存呢？ 还是以函数为例，我们在前面的函数章节中<< 函数的返回 >>中我们知道每个函数都会有一个函数返回， 即使没有在函数的实现中定义，也会默认返回一个NULL。以ZEND_RETURN_SPEC_CONST_HANDLER实现为例， 在函数的返回最后都会调用一个函数zend_leave_helper_SPEC。</p>
<p>在zend_leave_helper_SPEC函数中，对于执行过程中的函数处理有几个关键点：</p>
<ul>
<li>
上下文环境的切换：这里的关键代码是：EG(current_execute_data) = EX(prev_execute_data);。 EX(prev_execute_data)用于保留当前函数调用前的上下文环境，从而达到恢复和切换的目的。</li>
<li>
当前上下文环境所占用内存空间的释放：这里的关键代码是：zend_vm_stack_free(execute_data TSRMLS_CC);。 zend_vm_stack_free函数的实现存在于Zend/zend_execute.h文件，它的作用就是释放栈元素所占用的内存。</li>
<li>
返回到之前的中间代码执行路径中：这里的关键代码是：ZEND_VM_LEAVE();。 我们从zend_vm_execute.h文件的开始部分就知道ZEND_VM_LEAVE宏的效果是返回3。 在执行中间代码的while循环当中，当ret=3时，这个执行过程就会恢复之前上下文环境，继续执行。
</li>
</ul>
<p>更多内容请请移步<a href="http://www.php-internal.com/"　target="_blank">TIPI项目</a></p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/02/php-execute-data/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP的语法分析器-Bison入门</title>
		<link>https://www.phppan.com/2011/10/php-yacc-bison/</link>
		<comments>https://www.phppan.com/2011/10/php-yacc-bison/#comments</comments>
		<pubDate>Mon, 17 Oct 2011 02:03:55 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[TIPI]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1482</guid>
		<description><![CDATA[Bison是一种通用目的的分析器生成器。它将LALR(1)上下文无关文法的描述转化成分析该文法的C程序。 使用 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">Bison是一种通用目的的分析器生成器。它将LALR(1)上下文无关文法的描述转化成分析该文法的C程序。 使用它可以生成解释器，编译器，协议实现等多种程序。 Bison向上兼容Yacc，所有书写正确的Yacc语法都应该可以不加修改地在Bison下工作。 它不但与Yacc兼容还具有许多Yacc不具备的特性。</p>
<p style="text-indent: 2em;">Bison分析器文件是定义了名为yyparse并且实现了某个语法的函数的C代码。 这个函数并不是一个可以完成所有的语法分析任务的C程序。 除此这外我们还必须提供额外的一些函数： 如词法分析器、分析器报告错误时调用的错误报告函数等等。 我们知道一个完整的C程序必须以名为main的函数开头，如果我们要生成一个可执行文件，并且要运行语法解析器， 那么我们就需要有main函数，并且在某个地方直接或间接调用yyparse，否则语法分析器永远都不会运行。</p>
<p style="text-indent: 2em;">先看下bison的示例：<a style="color: #1299da; text-decoration: underline;" href="http://www.gnu.org/software/bison/manual/html_node/RPN-Calc.html#RPN-Calc">逆波兰记号计算器</a></p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; border-top-left-radius: 4px 4px; border-top-right-radius: 4px 4px; border-bottom-right-radius: 4px 4px; border-bottom-left-radius: 4px 4px; -webkit-box-shadow: #a0a0a0 2px 2px 5px; box-shadow: #a0a0a0 2px 2px 5px; padding: 10px; border: 1px solid #eeeeee;">%{
#define YYSTYPE double
#include &lt;stdio.h&gt;
#include &lt;math.h&gt;
#include &lt;ctype.h&gt;
int yylex (void);
void yyerror (char const *);
%}

%token NUM

%%
input:    /* empty */
     | input line
    ;

line:     '\n'
    | exp '\n'      { printf ("\t%.10g\n", $1); }
;

exp:      NUM           { $$ = $1;           }
   | exp exp '+'   { $$ = $1 + $2;      }
    | exp exp '-'   { $$ = $1 - $2;      }
    | exp exp '*'   { $$ = $1 * $2;      }
    | exp exp '/'   { $$ = $1 / $2;      }
     /* Exponentiation */
    | exp exp '^'   { $$ = pow($1, $2); }
    /* Unary minus    */
    | exp 'n'       { $$ = -$1;          }
;
%%

#include &lt;ctype.h&gt;

int yylex (void) {
       int c;

/* Skip white space.  */
       while ((c = getchar ()) == ' ' || c == '\t') ;

/* Process numbers.  */
       if (c == '.' || isdigit (c)) {
       ungetc (c, stdin);
       scanf ("%lf", &amp;yylval);
       return NUM;
     }

       /* Return end-of-input.  */
       if (c == EOF) return 0;

       /* Return a single char.  */
       return c;
}

void yyerror (char const *s) {
    fprintf (stderr, "%s\n", s);
}

int main (void) {
    return yyparse ();
}</pre>
<p style="text-indent: 2em;">我们先看下运行的效果：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; border-top-left-radius: 4px 4px; border-top-right-radius: 4px 4px; border-bottom-right-radius: 4px 4px; border-bottom-left-radius: 4px 4px; -webkit-box-shadow: #a0a0a0 2px 2px 5px; box-shadow: #a0a0a0 2px 2px 5px; padding: 10px; border: 1px solid #eeeeee;">bison demo.y
gcc -o test -lm test.tab.c
chmod +x test
./test</pre>
<blockquote style="margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-left: 60px; padding-right: 20px; background-image: url(http://localhost/git/tipi/web/images/quote.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #ededed; border-top-width: 1px; border-bottom-width: 1px; border-top-style: solid; border-bottom-style: solid; border-top-color: #aaaaaa; border-bottom-color: #aaaaaa; border-left-width: 0px; border-left-style: initial; border-left-color: initial; border-right-width: 0px; border-right-style: initial; border-right-color: initial; background-position: 15px 8px; background-repeat: no-repeat no-repeat;">
<p style="text-indent: 0px;">NOTE gcc命令需要添加-lm参数。因为头文件仅对接口进行描述，但头文件不是负责进行符号解析的实体。此时需要告诉编译器应该使用哪个函数库来完成对符号的解析。 　GCC的命令参数中，-l参数就是用来指定程序要链接的库，-l参数紧接着就是库名，这里我们在-l后面接的是m，即数学库，他的库名是m，他的库文件名是libm.so。</p>
</blockquote>
<p style="text-indent: 2em;">这是一个逆波兰记号计算器的示例，在命令行中输入 3 7 + 回车，输出10</p>
<p style="text-indent: 2em;">一般来说，使用Bison设计语言的流程，从语法描述到编写一个编译器或者解释器,有三个步骤:</p>
<ul>
<li>以Bison可识别的格式正式地描述语法。对每一个语法规则，描述当这个规则被识别时相应的执行动作，动作由C语句序列。即我们在示例中看到的%%和%%这间的内容。</li>
<li>描述编写一个词法分析器处理输入并将记号传递给语法分析器（即yylex函数一定要存在）。词法分析器既可是手工编写的C代码, 也可以由lex产生，后面我们会讨论如何将re2c与bison结合使用。上面的示例中是直接手工编写C代码实现一个命令行读取内容的词法分析器。</li>
<li>编写一个调用Bison产生的分析器的控制函数，在示例中是main函数直接调用。编写错误报告函数（即yyerror函数）。</li>
</ul>
<p style="text-indent: 2em;">将这些源代码转换成可执行程序，需要按以下步骤进行：</p>
<ul>
<li>按语法运行Bison产生分析器。对应示例中的命令，bison demo.y</li>
<li>同其它源代码一样编译Bison输出的代码，链接目标文件以产生最终的产品。即对应示例中的命令　gcc -o test -lm test.tab.c</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/10/php-yacc-bison/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TIPI第六章发布</title>
		<link>https://www.phppan.com/2011/07/tipi-chapter6/</link>
		<comments>https://www.phppan.com/2011/07/tipi-chapter6/#comments</comments>
		<pubDate>Fri, 29 Jul 2011 03:41:26 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[程序相关]]></category>
		<category><![CDATA[TIPI]]></category>
		<category><![CDATA[内存管理]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1431</guid>
		<description><![CDATA[人生在世，如身处荆棘之中！心不动，人不妄动，不动则不伤。如心动，则人妄动，伤其身，痛其骨，于是体会到世间诸般痛 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">人生在世，如身处荆棘之中！心不动，人不妄动，不动则不伤。如心动，则人妄动，伤其身，痛其骨，于是体会到世间诸般痛苦。</p>
<p style="text-indent: 2em;">选择在这个宜嫁娶、宜开光、宜安床的日子里，我们将第六章发布了。 没有华丽的出场，只有深夜的辗转，我们在努力。 虽然过程中有一些纷纷扰扰，但是经历了风雨的彩虹会更加美丽。</p>
<p style="text-indent: 2em;">这次发布更新的主要内容有:</p>
<ol>
<li>新增加第六章内存管理</li>
<li>增加CHM格式的支持</li>
<li>部分内容调整</li>
</ol>
<p style="text-indent: 2em;">TIPI团队</p>
<p style="text-indent: 2em;">这次发布时我们提供了CHM版本的<a style="color: #1299da; text-decoration: underline;" href="http://www.php-internal.com/downloads/">下载</a>，</p>
<p style="text-indent: 2em;"><a style="color: #1299da; text-decoration: underline;" href="http://www.php-internal.com/book/">TIPI入口&gt;&gt;&gt;</a></p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/07/tipi-chapter6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TIPI儿童节版发布</title>
		<link>https://www.phppan.com/2011/06/tipi-children/</link>
		<comments>https://www.phppan.com/2011/06/tipi-children/#comments</comments>
		<pubDate>Wed, 01 Jun 2011 01:00:05 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[TIPI]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1389</guid>
		<description><![CDATA[少年智则国智，少年富则国富，少年强则国强，少年独立则国独立，少年自由则国自由，少年进步则国进步&#8230;&#038; [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">少年智则国智，少年富则国富，少年强则国强，少年独立则国独立，少年自由则国自由，少年进步则国进步&#8230;&#8230;</p>
<p style="text-indent: 2em;">各位自认为是儿童的，不是儿童的；扮萌的或已成为大叔的同学，儿童节快乐！ <a style="color: #1299da; text-decoration: underline;" href="http://www.php-internal.com/team/">TIPI团队</a>选择在这个应该纯粹一点的节日里，发布努力了两个月的成果。 自最近一次的版本发布（2011-04-01）以来，关注的同学可能会注意到网站上 没有新的变化，其实这段时间我们并没有减缓TIPI项目的进度，在第一次项目发布之后我们收到了很多的反馈， 经过团队的讨论，我们决定暂缓新章节的编写，把精力集中在现有章节的改良上。 总体来讲有如下变化：</p>
<ul style="list-style-type: none; list-style-position: initial; list-style-image: initial; padding: 0px; margin: 0px;">
<li style="padding-left: 22px; background-image: url(http://localhost/git/tipi/web/images/list.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: initial; background-position: 0% 4px; background-repeat: no-repeat no-repeat;">重构现有章节。将现有一些章节的内容进行了丰富。当然这并不是终点，我们会持续对内容进行优化。</li>
<li style="padding-left: 22px; background-image: url(http://localhost/git/tipi/web/images/list.png); background-attachment: initial; background-origin: initial; background-clip: initial; background-color: initial; background-position: 0% 4px; background-repeat: no-repeat no-repeat;">完成了<a style="color: #1299da; text-decoration: underline;" href="http://www.php-internal.com/book/?p=chapt05/05-00-class-and-oop">第五章</a>的编写。上次发布时发布了第五章类的第一二小节，这次我们完成了所有的内容。</li>
</ul>
<p style="text-indent: 2em;">上次发布时我们提供了PDF版本的<a style="color: #1299da; text-decoration: underline;" href="http://www.php-internal.com/releases/RELEASE_2011-06-01_V0.5.9.pdf">下载</a>， 这次的发布没有带来新格式的下载，不过不要着急， CHM版本的下载将会在近期提供。</p>
<p style="text-indent: 2em;"><a style="color: #1299da; text-decoration: underline;" href="http://www.php-internal.com/book/">TIPI入口&gt;&gt;&gt;</a></p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/06/tipi-children/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TIPI0203-PHP脚本的执行</title>
		<link>https://www.phppan.com/2011/01/tipi0203/</link>
		<comments>https://www.phppan.com/2011/01/tipi0203/#comments</comments>
		<pubDate>Fri, 28 Jan 2011 06:24:27 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP脚本的执行]]></category>
		<category><![CDATA[TIPI]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1256</guid>
		<description><![CDATA[在前面的章节介绍了PHP的生命周期，PHP的SAPI，这些内容都是处于上层的，在这个下面是对于PHP本身的解析 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">在前面的章节介绍了PHP的生命周期，PHP的SAPI，这些内容都是处于上层的，在这个下面是对于PHP本身的解析和执行。这一小节我们介绍PHP脚本的执行。</p>
<p style="text-indent: 2em;">目前的编程语言可以分为两大类:</p>
<ul>
<li>第一类是像C/C++, .NET, Java之类的编译型语言, 它们的共性是: 运行之前必须对源代码进行编译,然后运行编译后的目标文件.</li>
<li>第二类比如:PHP, Javascript, Ruby, Python这些解释型语言, 他们都无需经过编译即可&#8221;运行&#8221;. 虽然可以理解为直接运行, 但它们并不是真的直接就被能被机器理解, 机器只能理解机器语言,那这些语言是怎么被执行的呢, 一般这些语言都需要一个<strong>解释器</strong>, 由解释器来执行这些源码, 实际上这些语言还是会经过编译环节, 只不过它们一般会在运行的时候实时进行编译. 为了效率,并不是所有语言在每次执行的时候都会重新编译一遍, 比如PHP的各种opcode缓存扩展(如APC, xcache, eAccelerator等),比如Python会将编译的中间文件保存成pyc/pyo文件,避免每次运行重新进行编译所带来的性能损失.</li>
</ul>
<p style="text-indent: 2em;">PHP的脚本的执行也需要一个解释器, 比如命令行下的php程序,或者apache的mod_php模块等等. 前一节提到了PHP的SAPI接口, 下面就以PHP命令行程序为例解释PHP脚本是怎么被执行的. 例如如下的这段PHP脚本:</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #cc7833;">&lt;?php</span>
<span style="color: #6d9cbe;">$str</span> <span style="color: #e0882f;">=</span> <span style="color: #99ff00;">"Hello, Tipi!<span>\n</span>"</span><span style="color: #e0882f;">;</span>
<a style="color: #1299da; text-decoration: none;" href="http://www.php.net/echo"><span style="color: #e2392d;">echo</span></a> <span style="color: #6d9cbe;">$str</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">假设上面的代码保存在名为hello.php的文件中, 用PHP命令行程序执行这个脚本:</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">$ php <span>--help</span>  <span style="color: #bd48b3;"># 显示php程序可以接受的参数</span>
$ php .<span style="color: #e0882f;">/</span>hello.php</pre>
<p style="text-indent: 2em;">这段代码的输出显然是Hello, Tipi!, 那么在执行脚本的时候PHP/Zend都做了些什么呢? 这些语句是怎么样让php输出这段话的呢? 下面将一步一步的进行介绍.</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">程序的执行</h2>
<ol>
<li>如上例中, 传递给php程序需要执行的文件, php程序完成基本的准备工作后启动PHP及Zend引擎, 加载注册的扩展模块.</li>
<li>初始化完成后读取脚本文件,Zend引擎对脚本文件进行词法分析,语法分析. 然后编译成opcode 执行. 如果安装了apc之类的opcode缓存, 编译环节可能会被跳过而直接从缓存中读取opcode执行.</li>
</ol>
<h3 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.1em; color: #333333;">脚本的编译执行</h3>
<p style="text-indent: 2em;">PHP在读取到脚本文件后首先对代码进行词法分析, PHP的词法分析器是通过lex生成的, 词法规则文件在$PHP_SRC/Zend/zend_language_scanner.l, 这一阶段lex会会将源代码按照词法规则切分一个一个的标记(token). PHP中提供了一个函数token_get_all(), 该函数接收一个字符串参数, 返回一个按照词法规则切分好的数组. 例如将上面的php代码作为参数传递给这个函数:</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #cc7833;">&lt;?php</span>
<span style="color: #6d9cbe;">$code</span> <span style="color: #e0882f;">=</span><span>&lt;&lt;&lt;PHP_CODE
&lt;?php
$str = "Hello, Tipi\n";
echo $str;
PHP</span>_CODE<span style="color: #e0882f;">;</span>

<a style="color: #1299da; text-decoration: none;" href="http://www.php.net/var_dump"><span style="color: #e2392d;">var_dump</span></a><span style="color: #ffffff;">(</span><a style="color: #1299da; text-decoration: none;" href="http://www.php.net/token_get_all"><span style="color: #e2392d;">token_get_all</span></a><span style="color: #ffffff;">(</span><span style="color: #6d9cbe;">$code</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">运行上面的脚本你将会看到一如下的输出</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">array (
  0 =&gt;
  array (
    0 =&gt; 368,       // 脚本开始标记
    1 =&gt; '&lt;?php     // 匹配到的字符串
',
    2 =&gt; 1,
  ),
  1 =&gt;
  array (
    0 =&gt; 371,
    1 =&gt; ' ',
    2 =&gt; 2,
  ),
  2 =&gt; '=',
  3 =&gt;
  array (
    0 =&gt; 371,
    1 =&gt; ' ',
    2 =&gt; 2,
  ),
  4 =&gt;
  array (
    0 =&gt; 315,
    1 =&gt; '"Hello, Tipi
"',
    2 =&gt; 2,
  ),
  5 =&gt; ';',
  6 =&gt;
  array (
    0 =&gt; 371,
    1 =&gt; '
',
    2 =&gt; 3,
  ),
  7 =&gt;
  array (
    0 =&gt; 316,
    1 =&gt; 'echo',
    2 =&gt; 4,
  ),
  8 =&gt;
  array (
    0 =&gt; 371,
    1 =&gt; ' ',
    2 =&gt; 4,
  ),
  9 =&gt; ';',</pre>
<p style="text-indent: 2em;">这也是Zend引擎词法分析做的事情,将代码切分为一个个的标记,然后使用语法分析器(PHP使用yacc生成语法分析器, 规则见$PHP_SRC/Zend/zend_language_parser.y), yacc根据规则进行相应的处理, 如果代码找不到匹配的规则,也就是语法错误时Zend引擎会停止,并输出错误信息. 比如缺少括号,或者不符合语法规则的情况都会在这个环节检查. 在匹配到相应的语法规则后,Zend引擎还会进行编译, 将代码编译为opcode, 完成后,Zend引擎会执行这些opcode, 在执行opcode的过程中还有可能会继续重复进行编译-执行, 例如执行eval,include/require等语句, 因为这些语句还会包含或者执行其他文件或者字符串中的脚本.</p>
<p style="text-indent: 2em;">例如上例中的echo语句会编译为一条ZEND_ECHO指令, 执行过程中,该指令由C函数zend_print_variable(zval* z)执行,将传递进来的字符串打印出来. 为了方便理解, 本例中省去了一些细节,例如opcode指令和处理函数之间的映射关系等. 后面的章节将会详细介绍.</p>
<p style="text-indent: 2em;">如果想直接查看生成的Opcode，可以使用php的vld扩展查看。扩展下载地址: <a style="color: #1299da; text-decoration: underline;" href="http://pecl.php.net/package/vld">http://pecl.php.net/package/vld</a>。Win下需要自己编译生成dll文件。</p>
<p style="text-indent: 2em;">作者：TIPI团队</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/01/tipi0203/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TIPI020203-FastCGI</title>
		<link>https://www.phppan.com/2011/01/tipi020203-fastcgi/</link>
		<comments>https://www.phppan.com/2011/01/tipi020203-fastcgi/#comments</comments>
		<pubDate>Fri, 28 Jan 2011 06:22:17 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[CGI]]></category>
		<category><![CDATA[FastCGI]]></category>
		<category><![CDATA[TIPI]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1253</guid>
		<description><![CDATA[FastCGI简介 什么是CGI CGI全称是“通用网关接口”(Common Gateway Interfac [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">FastCGI简介</h2>
<hr style="border: 1px dashed #cccccc;" />
<h3 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.1em; color: #333333;">什么是CGI</h3>
<p style="text-indent: 2em;">CGI全称是“通用网关接口”(Common Gateway Interface)， 它可以让一个客户端，从网页浏览器向执行在Web服务器上的程序，请求数据。 CGI描述了客户端和这个程序之间传输数据的一种标准。 CGI的一个目的是要独立于任何语言的，所以CGI可以用任何一种语言编写，只要这种语言具有标准输入、输出和环境变量。如php,perl,tcl等</p>
<h3 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.1em; color: #333333;">什么是FastCGI</h3>
<p style="text-indent: 2em;">FastCGI像是一个常驻(long-live)型的CGI，它可以一直执行着，只要激活后，不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。它还支持分布式的运算, 即 FastCGI 程序可以在网站服务器以外的主机上执行并且接受来自其它网站服务器来的请求。</p>
<p style="text-indent: 2em;">FastCGI是语言无关的、可伸缩架构的CGI开放扩展，其主要行为是将CGI解释器进程保持在内存中并因此获得较高的性能。众所周知，CGI解释器的反复加载是CGI性能低下的主要原因，如果CGI解释器保持在内存中并接受FastCGI进程管理器调度，则可以提供良好的性能、伸缩性、Fail- Over特性等等。</p>
<h3 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.1em; color: #333333;">FastCGI的工作原理</h3>
<ol>
<li>Web Server启动时载入FastCGI进程管理器（IIS ISAPI或Apache Module)</li>
<li>FastCGI进程管理器自身初始化，启动多个CGI解释器进程(可见多个php-cgi)并等待来自Web Server的连接。</li>
<li>当客户端请求到达Web Server时，FastCGI进程管理器选择并连接到一个CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。</li>
<li>FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时，请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 在CGI模式中，php-cgi在此便退出了。</li>
</ol>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">PHP中的CGI实现</h2>
<hr style="border: 1px dashed #cccccc;" />
<p style="text-indent: 2em;">PHP的cgi实现本质是是以socket编程实现一个tcp或udp协议的服务器，当启动时，创建tcp/udp协议的服务器的socket监听，并接收相关请求进行处理。这只是请求的处理，在此基础上添加模块初始化，sapi初始化，模块关闭，sapi关闭等就构成了整个cgi的生命周期。 程序是从cgi_main.c文件的main函数开始，而在main函数中调用了定义在fastcgi.c文件中的初始化，监听等函数。我们从main函数开始，看看PHP对于fastcgi的实现。</p>
<p style="text-indent: 2em;">这里将整个流程分为初始化操作，请求处理，关闭操作三个部分。 我们就整个流程进行简单的说明，并在其中穿插介绍一些用到的重要函数。</p>
<h3 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.1em; color: #333333;">初始化操作</h3>
<p style="text-indent: 2em;">过程说明见代码注释</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #bd48b3; font-style: italic;">/* {{{ main
 */</span>
<span style="color: #1299da;">int</span> main<span style="color: #ffffff;">(</span><span style="color: #1299da;">int</span> argc<span style="color: #e0882f;">,</span> <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>argv<span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span><span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
...
<span style="color: #ffffff;">sapi_startup</span><span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>cgi_sapi_module<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span> <span style="color: #bc9458; font-style: italic;">//  1512行 启动sapi,调用sapi全局构造函数，初始化sapi_globals_struct结构体</span>
... <span style="color: #bc9458; font-style: italic;">//  根据启动参数，初始化信息</span>

<span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>cgi_sapi_module.<span style="color: #ffffff;">startup</span><span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>cgi_sapi_module<span style="color: #ffffff;">)</span> <span style="color: #e0882f;">==</span> FAILURE<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span> <span style="color: #bc9458; font-style: italic;">//  模块初始化 调用php_cgi_startup方法</span>
...
<span style="color: #ffffff;">}</span>

...
<span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>bindpath<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
    fcgi_fd <span style="color: #e0882f;">=</span> fcgi_listen<span style="color: #ffffff;">(</span>bindpath<span style="color: #e0882f;">,</span> <span style="color: #1299da;">128</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  实现socket监听，调用fcgi_init初始化</span>
    ...
<span style="color: #ffffff;">}</span>

<span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>fastcgi<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
    ...
    <span style="color: #bd48b3; font-style: italic;">/* library is already initialized, now init our request */</span>
    fcgi_init_request<span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>request<span style="color: #e0882f;">,</span> fcgi_fd<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  request内存分配，初始化变量</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">fcgi_listen函数主要用于创建、绑定socket并开始监听</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">    <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span><span style="color: #ffffff;">(</span>listen_socket <span style="color: #e0882f;">=</span> socket<span style="color: #ffffff;">(</span>sa.<span style="color: #ffffff;">sa</span>.<span style="color: #ffffff;">sa_family</span><span style="color: #e0882f;">,</span> SOCK_STREAM<span style="color: #e0882f;">,</span> <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span> <span style="color: #e0882f;">&lt;</span> <span style="color: #1299da;">0</span> <span style="color: #e0882f;">||</span>
        ...
        <span style="color: #ffffff;">bind</span><span style="color: #ffffff;">(</span>listen_socket<span style="color: #e0882f;">,</span> <span style="color: #ffffff;">(</span><span style="color: #1299da;">struct</span> sockaddr <span style="color: #e0882f;">*</span><span style="color: #ffffff;">)</span> <span style="color: #e0882f;">&amp;</span>sa<span style="color: #e0882f;">,</span> sock_len<span style="color: #ffffff;">)</span> <span style="color: #e0882f;">&lt;</span> <span style="color: #1299da;">0</span> <span style="color: #e0882f;">||</span>
        listen<span style="color: #ffffff;">(</span>listen_socket<span style="color: #e0882f;">,</span> backlog<span style="color: #ffffff;">)</span> <span style="color: #e0882f;">&lt;</span> <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        ...
    <span style="color: #ffffff;">}</span></pre>
<h3 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.1em; color: #333333;">请求处理操作流程</h3>
<p style="text-indent: 2em;">过程说明见代码注释</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">    <span style="color: #ff8400;">while</span> <span style="color: #ffffff;">(</span>parent<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <span style="color: #ff8400;">do</span> <span style="color: #ffffff;">{</span>
            pid <span style="color: #e0882f;">=</span> fork<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  生成新的子进程</span>
            <span style="color: #ff8400;">switch</span> <span style="color: #ffffff;">(</span>pid<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
            <span style="color: #ff8400;">case</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">:</span> <span style="color: #bc9458; font-style: italic;">//  子进程</span>
                parent <span style="color: #e0882f;">=</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span>

                <span style="color: #bd48b3; font-style: italic;">/* don't catch our signals */</span>
                sigaction<span style="color: #ffffff;">(</span>SIGTERM<span style="color: #e0882f;">,</span> <span style="color: #e0882f;">&amp;</span>old_term<span style="color: #e0882f;">,</span> <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  终止信号</span>
                sigaction<span style="color: #ffffff;">(</span>SIGQUIT<span style="color: #e0882f;">,</span> <span style="color: #e0882f;">&amp;</span>old_quit<span style="color: #e0882f;">,</span> <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  终端退出符</span>
                sigaction<span style="color: #ffffff;">(</span>SIGINT<span style="color: #e0882f;">,</span>  <span style="color: #e0882f;">&amp;</span>old_int<span style="color: #e0882f;">,</span>  <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  终端中断符</span>
                <span style="color: #cc7833;">break</span><span style="color: #e0882f;">;</span>
                ...
                <span style="color: #ff8400;">default</span><span style="color: #e0882f;">:</span>
                <span style="color: #bd48b3; font-style: italic;">/* Fine */</span>
                running<span style="color: #e0882f;">++;</span>
                <span style="color: #cc7833;">break</span><span style="color: #e0882f;">;</span>
        <span style="color: #ffffff;">}</span> <span style="color: #ff8400;">while</span> <span style="color: #ffffff;">(</span>parent <span style="color: #e0882f;">&amp;&amp;</span> <span style="color: #ffffff;">(</span>running <span style="color: #e0882f;">&lt;</span> children<span style="color: #ffffff;">)</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

    ...
        <span style="color: #ff8400;">while</span> <span style="color: #ffffff;">(</span><span style="color: #e0882f;">!</span>fastcgi <span style="color: #e0882f;">||</span> fcgi_accept_request<span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>request<span style="color: #ffffff;">)</span> <span style="color: #e0882f;">&gt;=</span> <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        SG<span style="color: #ffffff;">(</span>server_context<span style="color: #ffffff;">)</span> <span style="color: #e0882f;">=</span> <span style="color: #ffffff;">(</span><span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span><span style="color: #ffffff;">)</span> <span style="color: #e0882f;">&amp;</span>request<span style="color: #e0882f;">;</span>
        init_request_info<span style="color: #ffffff;">(</span>TSRMLS_C<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        CG<span style="color: #ffffff;">(</span>interactive<span style="color: #ffffff;">)</span> <span style="color: #e0882f;">=</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span>
                    ...
            <span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">在fcgi_accept_request函数中，处理连接请求，忽略受限制客户的请求，调用fcgi_read_request函数（定义在fastcgi.c文件），分析请求的信息，将相关的变量写到对应的变量中。 其中在读取请求内容时调用了safe_read方法。如下所示： <strong>[main() -&gt; fcgi_accept_request() -&gt; fcgi_read_request() -&gt; safe_read()]</strong></p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #1299da;">static</span> <span style="color: #cc7833;">inline</span> ssize_t safe_read<span style="color: #ffffff;">(</span>fcgi_request <span style="color: #e0882f;">*</span>req<span style="color: #e0882f;">,</span> <span style="color: #1299da;">const</span> <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span>buf<span style="color: #e0882f;">,</span> size_t count<span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
    size_t n <span style="color: #e0882f;">=</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span>
    <span style="color: #ff8400;">do</span> <span style="color: #ffffff;">{</span>
    ... <span style="color: #bc9458; font-style: italic;">//  省略  对win32的处理</span>
        ret <span style="color: #e0882f;">=</span> read<span style="color: #ffffff;">(</span>req<span style="color: #e0882f;">-&gt;</span>fd<span style="color: #e0882f;">,</span> <span style="color: #ffffff;">(</span><span style="color: #ffffff;">(</span><span style="color: #1299da;">char</span><span style="color: #e0882f;">*</span><span style="color: #ffffff;">)</span>buf<span style="color: #ffffff;">)</span><span style="color: #e0882f;">+</span>n<span style="color: #e0882f;">,</span> count<span style="color: #e0882f;">-</span>n<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  非win版本的读操作</span>
    ... <span style="color: #bc9458; font-style: italic;">//  省略</span>
    <span style="color: #ffffff;">}</span> <span style="color: #ff8400;">while</span> <span style="color: #ffffff;">(</span>n <span style="color: #e0882f;">!=</span> count<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">在请求初始化完成，读取请求完毕后，就该处理请求的PHP文件了。假设此次请求为PHP_MODE_STANDARD则会调用php_execute_script执行PHP文件。 在此函数中它先初始化此文件相关的一些内容，然后再调用zend_execute_scripts函数，对PHP文件进行词法分析和语法分析，生成中间代码， 并执行zend_execute函数，从而执行这些中间代码。关于整个脚本的执行请参见第三节 脚本的执行。</p>
<h3 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.1em; color: #333333;">关闭操作流程</h3>
<p style="text-indent: 2em;">过程说明代码注释</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">...
<span style="color: #ffffff;">php_request_shutdown</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">(</span><span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span><span style="color: #ffffff;">)</span> <span style="color: #1299da;">0</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>   <span style="color: #bc9458; font-style: italic;">//  php请求关闭函数</span>
...
<span style="color: #ffffff;">fcgi_shutdown</span><span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>    <span style="color: #bc9458; font-style: italic;">//  fcgi的关闭 销毁fcgi_mgmt_vars变量</span>
php_module_shutdown<span style="color: #ffffff;">(</span>TSRMLS_C<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>  <span style="color: #bc9458; font-style: italic;">//  模块关闭    清空sapi,关闭zend引擎 销毁内存，清除垃圾等</span>
sapi_shutdown<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>    <span style="color: #bc9458; font-style: italic;">//  sapi关闭  sapi全局变量关闭等</span>
...</pre>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">参考资料</h2>
<hr style="border: 1px dashed #cccccc;" />
<p style="text-indent: 2em;">以下为本篇文章对于一些定义引用的参考资料：</p>
<p>http://www.fastcgi.com/drupal/node/2</p>
<p>http://baike.baidu.com/view/641394.htm</p>
<p style="text-indent: 2em;">作者：TIPI团队</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/01/tipi020203-fastcgi/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TIPI020202-嵌入式PHP</title>
		<link>https://www.phppan.com/2011/01/tipi020202-embedphp/</link>
		<comments>https://www.phppan.com/2011/01/tipi020202-embedphp/#comments</comments>
		<pubDate>Fri, 28 Jan 2011 06:20:57 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[TIPI]]></category>
		<category><![CDATA[嵌入式PHP]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1250</guid>
		<description><![CDATA[从第一章中对PHP源码目录结构的介绍以及PHP生命周期章节中可以看出，嵌入式PHP类似CLI,是SAPI接口的 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">从第一章中对PHP源码目录结构的介绍以及PHP生命周期章节中可以看出，嵌入式PHP类似CLI,是SAPI接口的另一种实现。 一般情况下，它的一个请求的生命周期也会和其它的SAPI一样：模块初始化=&gt;请求初始化=&gt;处理请求=&gt;关闭请求=&gt;关闭模块。当然，这只是理想情况。 但是嵌入式PHP的请求可能包括一段或多段代码，这就导致了嵌入式PHP的特殊性。</p>
<p style="text-indent: 2em;">对于嵌入式PHP或许我们了解比较少，或者说根本用不到，甚至在网上相关的资料也不多， 所幸我们在《Extending and Embedding PHP》这本书上找到了一些资料。 这一小节，我们从这本书的一个示例说起，介绍PHP对于嵌入式PHP的支持以及PHP为嵌入式提供了哪些接口或功能。 首先我们看下所要用到的示例源码：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #bd48b3; font-style: italic;">#include &lt;sapi/embed/php_embed.h&gt;</span>
<span style="color: #bd48b3; font-style: italic;">#ifdef ZTS</span>
    <span style="color: #1299da;">void</span> <span style="color: #e0882f;">***</span>tsrm_ls<span style="color: #e0882f;">;</span>
<span style="color: #bd48b3; font-style: italic;">#endif</span>
<span style="color: #bd48b3; font-style: italic;">/* Extension bits */</span>
zend_module_entry php_mymod_module_entry <span style="color: #e0882f;">=</span> <span style="color: #ffffff;">{</span>
    STANDARD_MODULE_HEADER<span style="color: #e0882f;">,</span>
    <span style="color: #99ff00;">"mymod"</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* extension name */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* function entries */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* MINIT */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* MSHUTDOWN */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* RINIT */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* RSHUTDOWN */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* MINFO */</span>
    <span style="color: #99ff00;">"1.0"</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* version */</span>
    STANDARD_MODULE_PROPERTIES
<span style="color: #ffffff;">}</span><span style="color: #e0882f;">;</span>
<span style="color: #bd48b3; font-style: italic;">/* Embedded bits */</span>
<span style="color: #1299da;">static</span> <span style="color: #1299da;">void</span> startup_php<span style="color: #ffffff;">(</span><span style="color: #1299da;">void</span><span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
    <span style="color: #1299da;">int</span> argc <span style="color: #e0882f;">=</span> <span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>argv<span style="color: #ffffff;">[</span><span style="color: #1299da;">2</span><span style="color: #ffffff;">]</span> <span style="color: #e0882f;">=</span> <span style="color: #ffffff;">{</span> <span style="color: #99ff00;">"embed5"</span><span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span> <span style="color: #ffffff;">}</span><span style="color: #e0882f;">;</span>
    php_embed_init<span style="color: #ffffff;">(</span>argc<span style="color: #e0882f;">,</span> argv PTSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    zend_startup_module<span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>php_mymod_module_entry<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span>
<span style="color: #1299da;">static</span> <span style="color: #1299da;">void</span> execute_php<span style="color: #ffffff;">(</span><span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>filename<span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
    zend_first_try <span style="color: #ffffff;">{</span>
        <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>include_script<span style="color: #e0882f;">;</span>
        spprintf<span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>include_script<span style="color: #e0882f;">,</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"include '%s'"</span><span style="color: #e0882f;">,</span> filename<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        zend_eval_string<span style="color: #ffffff;">(</span>include_script<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> filename TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        efree<span style="color: #ffffff;">(</span>include_script<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span> zend_end_try<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span>
<span style="color: #1299da;">int</span> main<span style="color: #ffffff;">(</span><span style="color: #1299da;">int</span> argc<span style="color: #e0882f;">,</span> <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>argv<span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span><span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
    <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>argc <span style="color: #e0882f;">&lt;=</span> <span style="color: #1299da;">1</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <a style="color: #1299da; text-decoration: none;" href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #e2392d;">printf</span></a><span style="color: #ffffff;">(</span><span style="color: #99ff00;">"Usage: embed4 scriptfile"</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        <span style="color: #ff8400;">return</span> <span style="color: #e0882f;">-</span><span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>
    startup_php<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    execute_php<span style="color: #ffffff;">(</span>argv<span style="color: #ffffff;">[</span><span style="color: #1299da;">1</span><span style="color: #ffffff;">]</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    php_embed_shutdown<span style="color: #ffffff;">(</span>TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #ff8400;">return</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">以上的代码可以在《Extending and Embedding PHP》在第20章找到（原始代码有一个符号错误，有兴趣的童鞋可以去围观下）。 上面的代码是一个嵌入式PHP运行器（我们权当其为运行器吧），在这个运行器上我们可以运行PHP代码。 这段代码包括了对于PHP嵌入式支持的声明，启动嵌入式PHP运行环境，运行PHP代码，关闭嵌入式PHP运行环境。 下面我们就这段代码分析PHP对于嵌入式的支持做了哪些工作。 首先看下第一行：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #bd48b3; font-style: italic;">#include &lt;sapi/embed/php_embed.h&gt;</span></pre>
<p style="text-indent: 2em;">在sapi目录下的embed目录是PHP对于嵌入式的抽象层所在。在这里有我们所要用到的函数或宏定义。如示例中所使用的php_embed_init，php_embed_shutdown等函数。</p>
<p style="text-indent: 2em;">第2到4行：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"> <span style="color: #bd48b3; font-style: italic;">#ifdef ZTS</span>
    <span style="color: #1299da;">void</span> <span style="color: #e0882f;">***</span>tsrm_ls<span style="color: #e0882f;">;</span>
<span style="color: #bd48b3; font-style: italic;">#endif</span></pre>
<p style="text-indent: 2em;">ZTS是Zend Thread Safety的简写，与这个相关的有一个TSRM（线程安全资源管理）的东东，这个后面的章节会有详细介绍，这里就不再作阐述。</p>
<p style="text-indent: 2em;">第6到17行：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"> zend_module_entry php_mymod_module_entry <span style="color: #e0882f;">=</span> <span style="color: #ffffff;">{</span>
    STANDARD_MODULE_HEADER<span style="color: #e0882f;">,</span>
    <span style="color: #99ff00;">"mymod"</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* extension name */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* function entries */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* MINIT */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* MSHUTDOWN */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* RINIT */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* RSHUTDOWN */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* MINFO */</span>
    <span style="color: #99ff00;">"1.0"</span><span style="color: #e0882f;">,</span> <span style="color: #bd48b3; font-style: italic;">/* version */</span>
    STANDARD_MODULE_PROPERTIES
<span style="color: #ffffff;">}</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">以上PHP内部的模块结构声明，此处对于模块初始化，请求初始化等函数指针均为NULL, 也就是模块在初始化及请求开始结束等事件发生的时候不执行任何操作。不过这些操作在sapi/embed/php_embed.c文件中的php_embed_shutdown等函数中有体现。 关于模块结构的定义在zend/zend_modules.h中。</p>
<p style="text-indent: 2em;">startup_php函数:</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #1299da;">static</span> <span style="color: #1299da;">void</span> startup_php<span style="color: #ffffff;">(</span><span style="color: #1299da;">void</span><span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
    <span style="color: #1299da;">int</span> argc <span style="color: #e0882f;">=</span> <span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>argv<span style="color: #ffffff;">[</span><span style="color: #1299da;">2</span><span style="color: #ffffff;">]</span> <span style="color: #e0882f;">=</span> <span style="color: #ffffff;">{</span> <span style="color: #99ff00;">"embed5"</span><span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span> <span style="color: #ffffff;">}</span><span style="color: #e0882f;">;</span>
    php_embed_init<span style="color: #ffffff;">(</span>argc<span style="color: #e0882f;">,</span> argv PTSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    zend_startup_module<span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>php_mymod_module_entry<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">这个函数调用了两个函数php_embed_init和zend_startup_module完成初始化工作。 php_embed_init函数定义在sapi/embed/php_embed.c文件中。它完成了PHP对于嵌入式的初始化支持。 zend_startup_module函数是PHP的内部API函数，它的作用是注册定义的模块，这里是注册mymod模块。 这个注册过程仅仅是将所定义的zend_module_entry结构添加到注册模块列表中。</p>
<p style="text-indent: 2em;">execute_php函数:</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #1299da;">static</span> <span style="color: #1299da;">void</span> execute_php<span style="color: #ffffff;">(</span><span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>filename<span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
    zend_first_try <span style="color: #ffffff;">{</span>
        <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>include_script<span style="color: #e0882f;">;</span>
        spprintf<span style="color: #ffffff;">(</span><span style="color: #e0882f;">&amp;</span>include_script<span style="color: #e0882f;">,</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"include '%s'"</span><span style="color: #e0882f;">,</span> filename<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        zend_eval_string<span style="color: #ffffff;">(</span>include_script<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> filename TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        efree<span style="color: #ffffff;">(</span>include_script<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span> zend_end_try<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">从函数的名称来看，这个函数的功能是执行PHP代码的。 它通过调用sprrintf函数构造一个include语句，然后再调用zend_eval_string函数执行这个include语句。 zend_eval_string最终是调用zend_eval_stringl函数，这个函数是流程是一个编译PHP代码，生成zend_op_array类型数据，并执行opcode的过程。 这段程序相当于下面的这段php程序, 这段程序可以用php命令来执行，虽然下面这段程序没有实际意义，而通过嵌入式PHP中，你可以在一个用C实现的 系统中嵌入PHP, 然后用PHP来实现功能。</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #cc7833;">&lt;?php</span>
<span style="color: #ff8400;">if</span><span style="color: #ffffff;">(</span><span style="color: #6d9cbe;">$argc</span> <span style="color: #e0882f;">&lt;</span> <span style="color: #1299da;">2</span><span style="color: #ffffff;">)</span> <a style="color: #1299da; text-decoration: none;" href="http://www.php.net/die"><span style="color: #e2392d;">die</span></a><span style="color: #ffffff;">(</span><span style="color: #99ff00;">"Usage: embed4 scriptfile"</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>

<span style="color: #ff8400;">include</span> <span style="color: #6d9cbe;">$argv</span><span style="color: #ffffff;">[</span><span style="color: #1299da;">1</span><span style="color: #ffffff;">]</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">main函数：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #1299da;">int</span> main<span style="color: #ffffff;">(</span><span style="color: #1299da;">int</span> argc<span style="color: #e0882f;">,</span> <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>argv<span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span><span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
    <span style="color: #ff8400;">if</span> <span style="color: #ffffff;">(</span>argc <span style="color: #e0882f;">&lt;=</span> <span style="color: #1299da;">1</span><span style="color: #ffffff;">)</span> <span style="color: #ffffff;">{</span>
        <a style="color: #1299da; text-decoration: none;" href="http://www.opengroup.org/onlinepubs/009695399/functions/printf.html"><span style="color: #e2392d;">printf</span></a><span style="color: #ffffff;">(</span><span style="color: #99ff00;">"Usage: embed4 scriptfile"</span><span style="color: #e0882f;">;</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
        <span style="color: #ff8400;">return</span> <span style="color: #e0882f;">-</span><span style="color: #1299da;">1</span><span style="color: #e0882f;">;</span>
    <span style="color: #ffffff;">}</span>
    startup_php<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    execute_php<span style="color: #ffffff;">(</span>argv<span style="color: #ffffff;">[</span><span style="color: #1299da;">1</span><span style="color: #ffffff;">]</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    php_embed_shutdown<span style="color: #ffffff;">(</span>TSRMLS_CC<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #ff8400;">return</span> <span style="color: #1299da;">0</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">这个函数是主函数，执行初始化操作，根据输入的参数执行PHP的include语句，最后执行关闭操作，返回。 其中php_embed_shutdown函数定义在sapi/embed/php_embed.c文件中。它完成了PHP对于嵌入式的关闭操作支持。包括请求关闭操作，模块关闭操作等。</p>
<h3 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.1em; color: #333333;">其它宏</h3>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #bd48b3; font-style: italic;">#define PHP_EMBED_START_BLOCK(x,y) { \</span>
    php_embed_init<span style="color: #ffffff;">(</span>x<span style="color: #e0882f;">,</span> y<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span> \
    zend_first_try <span style="color: #ffffff;">{</span>

<span style="color: #bd48b3; font-style: italic;">#endif</span>

<span style="color: #bd48b3; font-style: italic;">#define PHP_EMBED_END_BLOCK() \</span>
  <span style="color: #ffffff;">}</span> zend_catch <span style="color: #ffffff;">{</span> \
    <span style="color: #bd48b3; font-style: italic;">/* int exit_status = EG(exit_status); */</span> \
  <span style="color: #ffffff;">}</span> zend_end_try<span style="color: #ffffff;">(</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span> \
  php_embed_shutdown<span style="color: #ffffff;">(</span>TSRMLS_C<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span> \
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">如上两个宏可能会用到你的嵌入式PHP中，从代码中可以看出，它包含了在示例代码中的php_embed_init，zend_first_try，zend_end_try，php_embed_shutdown等 嵌入式PHP中常用的方法。 大量的使用宏也算是PHP源码的一大特色吧，</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">参与资料</h2>
<p style="text-indent: 2em;">《Extending and Embedding PHP》</p>
<p style="text-indent: 2em;">作者：TIPI团队</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/01/tipi020202-embedphp/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TIPI020201-PHP以模块方式注册到Apache</title>
		<link>https://www.phppan.com/2011/01/tipi020201-apache/</link>
		<comments>https://www.phppan.com/2011/01/tipi020201-apache/#comments</comments>
		<pubDate>Fri, 28 Jan 2011 06:18:47 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[php模块]]></category>
		<category><![CDATA[TIPI]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1247</guid>
		<description><![CDATA[为了让Apache支持php,我们通常的做法是编译一个apche的php模块, 在配置中配置让mod_php来 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p style="text-indent: 2em;">为了让Apache支持php,我们通常的做法是编译一个apche的php模块, 在配置中配置让mod_php来处理php文件的请求. php模块通过注册apache2的ap_hook_post_config挂钩, 在apache启动的时候启动php模块以接受php的请求.</p>
<p style="text-indent: 2em;">下面介绍apache模块加载的基本知识以及PHP对于apache的实现</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">Apache模块加载机制简介</h2>
<hr style="border: 1px dashed #cccccc;" />
<p style="text-indent: 2em;">Apache的模块可以在运行的时候动态装载，这意味着对服务器可以进行功能扩展而不需要重新对源代码进行编译，甚至根本不需要停止服务器。 我们所需要做的仅仅是给服务器发送信号HUP或者AP_SIG_GRACEFUL通知服务器重新载入模块。 但是在动态加载之前，我们需要将模块编译成为动态链接库。此时的动态加载就是加载动态链接库。<br />
Apache中对动态链接库的处理是通过模块mod_so来完成的，因此mod_so模块不能被动态加载， 它只能被静态编译进Apache的核心。这意味着它是随着Apache一起启动的。<br />
比如我们要加载PHP模块，那么首先我们需要在httpd.conf文件中添加一行：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">LoadModule php5_module modules<span style="color: #e0882f;">/</span>mod_php5.<span style="color: #ffffff;">so</span></pre>
<p style="text-indent: 2em;">该命令的第一个参数是模块的名称，名称可以在模块实现的源码中找到。第二个选项是该模块所处的路径。 如果需要在服务器运行时加载模块，可以通过发送信号HUP或者AP_SIG_GRACEFUL给服务器，一旦接受到该信号，Apache将重新装载模块，而不需要重新启动服务器。</p>
<p style="text-indent: 2em;">下面我们以PHP模块的加载为例，分析Apache的模块加载过程。在配置文件中添加了所上所示的指令后，Apache在加载模块时会根据模块名查找模块并加载， 对于每一个模块，Apache必须保证其文件名是以“mod_”开始的，如php的mod_php5.c。如果命名格式不对，Apache将认为此模块不合法。 module结构的name属性在最后是通过宏STANDARD20_MODULE_STUFF以__FILE__体现。 关于这点可以在后面介绍mod_php5模块时有看到。 通过之前指令中指定的路径找到相关的动态链接库文件，Apache通过内部的函数获取动态链接库中的内容，并将模块的内容加载到内存中的指定变量中。<br />
在真正激活模块之前，Apache会检查所加载的模块是否为真正的Apache模块，这个检测是通过检查magic字段进行的。而magic字段是通过宏STANDARD20_MODULE_STUFF体现，在这个宏中magic的值为MODULE_MAGIC_COOKIE，MODULE_MAGIC_COOKIE定义如下：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #bd48b3; font-style: italic;">#define MODULE_MAGIC_COOKIE 0x41503232UL /* "AP22" */</span></pre>
<p style="text-indent: 2em;">最后Apache会调用相关函数(ap_add_loaded_module)将模块激活，此处的激活就是将模块放入相应的链表中(ap_top_modules链表：ap_top_modules链表用来保存Apache中所有的被激活的模块，包括默认的激活模块和激活的第三方模块。）</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">Apache2的mod_php5模块说明</h2>
<hr style="border: 1px dashed #cccccc;" />
<p style="text-indent: 2em;">Apache2的mod_php5模块包括sapi/apache2handler和sapi/apache2filter两个目录 在apache2_handle/mod_php5.c文件中，模块定义的相关代码如下：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;">AP_MODULE_DECLARE_DATA module php5_module <span style="color: #e0882f;">=</span> <span style="color: #ffffff;">{</span>
    STANDARD20_MODULE_STUFF<span style="color: #e0882f;">,</span>
        <span style="color: #bd48b3; font-style: italic;">/* 宏，包括版本，小版本，模块索引，模块名，下一个模块指针等信息，其中模块名以__FILE__体现 */</span>
    create_php_config<span style="color: #e0882f;">,</span>      <span style="color: #bd48b3; font-style: italic;">/* create per-directory config structure */</span>
    merge_php_config<span style="color: #e0882f;">,</span>       <span style="color: #bd48b3; font-style: italic;">/* merge per-directory config structures */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span>                   <span style="color: #bd48b3; font-style: italic;">/* create per-server config structure */</span>
    <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span>                   <span style="color: #bd48b3; font-style: italic;">/* merge per-server config structures */</span>
    php_dir_cmds<span style="color: #e0882f;">,</span>           <span style="color: #bd48b3; font-style: italic;">/* 模块定义的所有的指令 */</span>
    php_ap2_register_hook
        <span style="color: #bd48b3; font-style: italic;">/* 注册钩子，此函数通过ap_hoo_开头的函数在一次请求处理过程中对于指定的步骤注册钩子 */</span>
<span style="color: #ffffff;">}</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">它所对应的是apache的module结构，module的结构定义如下：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #1299da;">typedef</span> <span style="color: #1299da;">struct</span> module_struct module<span style="color: #e0882f;">;</span>
<span style="color: #1299da;">struct</span> module_struct <span style="color: #ffffff;">{</span>
    <span style="color: #1299da;">int</span> version<span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">int</span> minor_version<span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">int</span> module_index<span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">const</span> <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>name<span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span>dynamic_load_handle<span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">struct</span> module_struct <span style="color: #e0882f;">*</span>next<span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">unsigned</span> <span style="color: #1299da;">long</span> magic<span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">void</span> <span style="color: #ffffff;">(</span><span style="color: #e0882f;">*</span>rewrite_args<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">(</span>process_rec <span style="color: #e0882f;">*</span>process<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span><span style="color: #ffffff;">(</span><span style="color: #e0882f;">*</span>create_dir_config<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">(</span>apr_pool_t <span style="color: #e0882f;">*</span>p<span style="color: #e0882f;">,</span> <span style="color: #1299da;">char</span> <span style="color: #e0882f;">*</span>dir<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span><span style="color: #ffffff;">(</span><span style="color: #e0882f;">*</span>merge_dir_config<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">(</span>apr_pool_t <span style="color: #e0882f;">*</span>p<span style="color: #e0882f;">,</span> <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span>base_conf<span style="color: #e0882f;">,</span> <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span>new_conf<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span><span style="color: #ffffff;">(</span><span style="color: #e0882f;">*</span>create_server_config<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">(</span>apr_pool_t <span style="color: #e0882f;">*</span>p<span style="color: #e0882f;">,</span> server_rec <span style="color: #e0882f;">*</span>s<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span><span style="color: #ffffff;">(</span><span style="color: #e0882f;">*</span>merge_server_config<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">(</span>apr_pool_t <span style="color: #e0882f;">*</span>p<span style="color: #e0882f;">,</span> <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span>base_conf<span style="color: #e0882f;">,</span> <span style="color: #1299da;">void</span> <span style="color: #e0882f;">*</span>new_conf<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">const</span> command_rec <span style="color: #e0882f;">*</span>cmds<span style="color: #e0882f;">;</span>
    <span style="color: #1299da;">void</span> <span style="color: #ffffff;">(</span><span style="color: #e0882f;">*</span>register_hooks<span style="color: #ffffff;">)</span> <span style="color: #ffffff;">(</span>apr_pool_t <span style="color: #e0882f;">*</span>p<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">上面的模块结构与我们在mod_php5.c中所看到的结构有一点不同，这是由于STANDARD20_MODULE_STUFF的原因，这个宏它包含了前面8个字段的定义。 STANDARD20_MODULE_STUFF宏的定义如下：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #bd48b3; font-style: italic;">/** Use this in all standard modules */</span>
<span style="color: #bd48b3; font-style: italic;">#define STANDARD20_MODULE_STUFF MODULE_MAGIC_NUMBER_MAJOR, \</span>
                MODULE_MAGIC_NUMBER_MINOR<span style="color: #e0882f;">,</span> \
                <span style="color: #e0882f;">-</span><span style="color: #1299da;">1</span><span style="color: #e0882f;">,</span> \
                __FILE__<span style="color: #e0882f;">,</span> \
                <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> \
                <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> \
                MODULE_MAGIC_COOKIE<span style="color: #e0882f;">,</span> \
                                <span style="color: #cc7833;">NULL</span>      <span style="color: #bd48b3; font-style: italic;">/* rewrite args spot */</span></pre>
<p style="text-indent: 2em;">php_dir_cmds所定义的内容如下：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #1299da;">const</span> command_rec php_dir_cmds<span style="color: #ffffff;">[</span><span style="color: #ffffff;">]</span> <span style="color: #e0882f;">=</span>
<span style="color: #ffffff;">{</span>
    AP_INIT_TAKE2<span style="color: #ffffff;">(</span><span style="color: #99ff00;">"php_value"</span><span style="color: #e0882f;">,</span> php_apache_value_handler<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> OR_OPTIONS<span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"PHP Value Modifier"</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">,</span>
    AP_INIT_TAKE2<span style="color: #ffffff;">(</span><span style="color: #99ff00;">"php_flag"</span><span style="color: #e0882f;">,</span> php_apache_flag_handler<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> OR_OPTIONS<span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"PHP Flag Modifier"</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">,</span>
    AP_INIT_TAKE2<span style="color: #ffffff;">(</span><span style="color: #99ff00;">"php_admin_value"</span><span style="color: #e0882f;">,</span> php_apache_admin_value_handler<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> ACCESS_CONF<span style="color: #e0882f;">|</span>RSRC_CONF<span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"PHP Value Modifier (Admin)"</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">,</span>
    AP_INIT_TAKE2<span style="color: #ffffff;">(</span><span style="color: #99ff00;">"php_admin_flag"</span><span style="color: #e0882f;">,</span> php_apache_admin_flag_handler<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> ACCESS_CONF<span style="color: #e0882f;">|</span>RSRC_CONF<span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"PHP Flag Modifier (Admin)"</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">,</span>
    AP_INIT_TAKE1<span style="color: #ffffff;">(</span><span style="color: #99ff00;">"PHPINIDir"</span><span style="color: #e0882f;">,</span> php_apache_phpini_set<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> RSRC_CONF<span style="color: #e0882f;">,</span> <span style="color: #99ff00;">"Directory containing the php.ini file"</span><span style="color: #ffffff;">)</span><span style="color: #e0882f;">,</span>
    <span style="color: #ffffff;">{</span><span style="color: #cc7833;">NULL</span><span style="color: #ffffff;">}</span>
<span style="color: #ffffff;">}</span><span style="color: #e0882f;">;</span></pre>
<p style="text-indent: 2em;">以上为php模块定义的指令表。它实际上是一个command_rec结构的数组。 当Apache遇到指令的时候将逐一遍历各个模块中的指令表，查找是否有哪个模块能够处理该指令， 如果找到，则调用相应的处理函数，如果所有指令表中的模块都不能处理该指令，那么将报错。 如上可见，php模块仅提供php_value等5个指令。</p>
<p style="text-indent: 2em;">php_ap2_register_hook函数的定义如下：</p>
<pre style="background-color: #333333; color: #ffffff; font: normal normal normal 13px/normal 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Courier New', monospace; overflow-x: auto; overflow-y: auto; padding: 10px;"><span style="color: #1299da;">void</span> php_ap2_register_hook<span style="color: #ffffff;">(</span>apr_pool_t <span style="color: #e0882f;">*</span>p<span style="color: #ffffff;">)</span>
<span style="color: #ffffff;">{</span>
    ap_hook_pre_config<span style="color: #ffffff;">(</span>php_pre_config<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> APR_HOOK_MIDDLE<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    ap_hook_post_config<span style="color: #ffffff;">(</span>php_apache_server_startup<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> APR_HOOK_MIDDLE<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    ap_hook_handler<span style="color: #ffffff;">(</span>php_handler<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> APR_HOOK_MIDDLE<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
    ap_hook_child_init<span style="color: #ffffff;">(</span>php_apache_child_init<span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> <span style="color: #cc7833;">NULL</span><span style="color: #e0882f;">,</span> APR_HOOK_MIDDLE<span style="color: #ffffff;">)</span><span style="color: #e0882f;">;</span>
<span style="color: #ffffff;">}</span></pre>
<p style="text-indent: 2em;">以上代码声明了pre_config,post_config,handler和child_init 4个挂钩以及对应的处理函数。 其中pre_config,post_config,child_init是启动挂钩，它们在服务器启动时调用。 handler挂钩是请求挂钩，它在服务器处理请求时调用。其中在post_config挂钩中启动php。 它通过php_apache_server_startup函数实现。php_apache_server_startup函数通过调用sapi_startup启动sapi, 并通过调用php_apache2_startup来注册sapi module struct（此结构在本节开头中有说明）, 最后调用php_module_startup来初始化PHP, 其中又会初始化ZEND引擎,以及填充zend_module_struct中 的treat_data成员(通过php_startup_sapi_content_types)等。</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">Apache的运行过程</h2>
<hr style="border: 1px dashed #cccccc;" />
<p style="text-indent: 2em;">Apache的运行分为启动阶段和运行阶段。 在启动阶段，Apache为了获得系统资源最大的使用权限，将以特权用户root（*nix系统）或超级管理员Administrator(Windows系统)完成启动，并且整个过程处于一个单进程单线程的环境中，。 这个阶段包括配置文件解析(如http.conf文件)、模块加载(如mod_php,mod_perl)和系统资源初始化（例如日志文件、共享内存段、数据库连接等）等工作。</p>
<p style="text-indent: 2em;">Apache的启动阶段执行了大量的初始化操作，并且将许多比较慢或者花费比较高的操作都集中在这个阶段完成，以减少了后面处理请求服务的压力。</p>
<p style="text-indent: 2em;">在运行阶段，Apache主要工作是处理用户的服务请求。 在这个阶段，Apache放弃特权用户级别，使用普通权限，这主要是基于安全性的考虑，防止由于代码的缺陷引起的安全漏洞。 Apache对HTTP的请求可以分为连接、处理和断开连接三个大的阶段。同时也可以分为11个小的阶段，依次为： Post-Read-Request，URI Translation，Header Parsing，Access Control，Authentication，Authorization，MIME Type Checking，FixUp，Response，Logging，CleanUp</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">Apache Hook机制</h2>
<hr style="border: 1px dashed #cccccc;" />
<p style="text-indent: 2em;">Apache 的Hook机制是指：Apache 允许模块(包括内部模块和外部模块，例如mod_php5.so,mod_perl.so等)将自定义的函数注入到请求处理循环中。换句话说，模块可以在 Apache的任何一个处理阶段中挂接(Hook)上自己的处理函数，从而参与Apache的请求处理过程。 mod_php5.so/ php5apache2.dll就是将所包含的自定义函数，通过Hook机制注入到Apache中，在Apache处理流程的各个阶段负责处理php请求。 关于Hook机制在Windows系统开发也经常遇到，在Windows开发既有系统级的钩子，又有应用级的钩子。</p>
<p style="text-indent: 2em;">以上介绍了apache的加载机制，hook机制，apache的运行过程以及php5模块的相关知识，下面简单的说明在查看源码中的一些常用对象。</p>
<h2 style="font-weight: bold; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.2em; color: #333333;">Apache常用对象</h2>
<hr style="border: 1px dashed #cccccc;" />
<p style="text-indent: 2em;">在说到Apache的常用对象时，我们不得不先说下httpd.h文件。httpd.h文件包含了Apache的所有模块都需要的核心API。 它定义了许多系统常量。但是更重要的是它包含了下面一些对象的定义。</p>
<p style="text-indent: 2em;"><strong>request_rec对象</strong><br />
当一个客户端请求到达Apache时，就会创建一个request_rec对象，当Apache处理完一个请求后，与这个请求对应的request_rec对象也会随之被释放。 request_rec对象包括与一个HTTP请求相关的所有数据，并且还包含一些Apache自己要用到的状态和客户端的内部字段。</p>
<p style="text-indent: 2em;"><strong>server_rec对象</strong><br />
server_rec定义了一个逻辑上的WEB服务器。如果有定义虚拟主机，每一个虚拟主机拥有自己的server_rec对象。 server_rec对象在Apache启动时创建，当整个httpd关闭时才会被释放。 它包括服务器名称，连接信息，日志信息，针对服务器的配置，事务处理相关信息等 server_rec对象是继request_rec对象之后第二重要的对象。</p>
<p style="text-indent: 2em;"><strong>conn_rec对象</strong><br />
conn_rec对象是TCP连接在Apache的内部体现。 它在客户端连接到服务器时创建，在连接断开时释放。</p>
<h1 style="font-weight: normal; font-family: 'Microsoft YaHei', Helvetica, Arial, sans-serif; font-size: 1.4em; color: #006ea3; margin-top: 10px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; padding-bottom: 10px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #aaaaaa;">参考资料</h1>
<hr style="border: 1px dashed #cccccc;" />
<p style="text-indent: 2em;">《The Apache Modules Book&#8211;Application Development with Apache》</p>
<p style="text-indent: 2em;">作者：TIPI团队</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2011/01/tipi020201-apache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
