<?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; PHP内核</title>
	<atom:link href="https://www.phppan.com/tag/php%e5%86%85%e6%a0%b8/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sat, 04 Apr 2026 01:19:58 +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/2014/02/php-var-compare/</link>
		<comments>https://www.phppan.com/2014/02/php-var-compare/#comments</comments>
		<pubDate>Sun, 09 Feb 2014 13:15:55 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[PHP源码]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1890</guid>
		<description><![CDATA[有如下4个代码示例，你认为他们创建对象，并获得成员变量的速度排序是怎样的？ 1：将成员变量设置为public， [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>有如下4个代码示例，你认为他们创建对象，并获得成员变量的速度排序是怎样的？</p>
<p>1：将成员变量设置为public，通过赋值操作给成员变量赋值，直接获取变量</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">	<span style="color: #000000; font-weight: bold;">class</span> Foo <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000088;">$id</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #000088;">$data</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Foo<span style="color: #339933;">;</span>
	<span style="color: #000088;">$data</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">10</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$data</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>2：将成员变量设置为public，通过构造函数设置成员变量的值，直接获取变量</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">        <span style="color: #000000; font-weight: bold;">class</span> Foo2 <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000088;">$id</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> __construct<span style="color: #009900;">&#40;</span><span style="color: #000088;">$id</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$id</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #000088;">$data</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Foo2<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">10</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$data</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>3：将成员变量设置为protected，通过构造函数设置成员变量的值，通过成员方法获取变量</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">&nbsp;
     <span style="color: #000000; font-weight: bold;">class</span> Foo3 <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #000088;">$id</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> __construct<span style="color: #009900;">&#40;</span><span style="color: #000088;">$id</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$id</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> getId<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #000088;">$data</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Foo3<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">10</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$data</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getId</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>4：将成员变量设置为protected，通过构造函数设置成员变量的值，通过魔术方法获取变量</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">&nbsp;
     <span style="color: #000000; font-weight: bold;">class</span> Foo4 <span style="color: #009900;">&#123;</span>
		<span style="color: #000000; font-weight: bold;">protected</span> <span style="color: #000088;">$id</span><span style="color: #339933;">;</span>
		<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> __construct<span style="color: #009900;">&#40;</span><span style="color: #000088;">$id</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$id</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
&nbsp;
		<span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> __get<span style="color: #009900;">&#40;</span><span style="color: #000088;">$key</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #b1b100;">return</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #000088;">$data</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Foo4<span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">10</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$data</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">id</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>按执行速度快慢排序： 1243<br />
咱们先看其opcode:</p>
<p>1:</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">   	<span style="color: #cc66cc;">1</span>  ZEND_FETCH_CLASS	<span style="color: #cc66cc;">4</span> 	<span style="color: #339933;">:</span><span style="color: #cc66cc;">4</span> 	<span style="color: #0000ff;">'Foo'</span>
	<span style="color: #cc66cc;">2</span>  <span style="color: #000000; font-weight: bold;">NEW</span>      			$<span style="color:#800080;">5</span>	<span style="color: #339933;">:</span><span style="color: #cc66cc;">4</span>
	<span style="color: #cc66cc;">3</span>  DO_FCALL_BY_NAME			<span style="color: #cc66cc;">0</span>          
	<span style="color: #cc66cc;">4</span>  ASSIGN     				<span style="color: #339933;">!</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> $<span style="color:#800080;">5</span>
	<span style="color: #cc66cc;">5</span>  ZEND_ASSIGN_OBJ			<span style="color: #339933;">!</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'id'</span>
	<span style="color: #cc66cc;">6</span>  ZEND_OP_DATA				<span style="color: #cc66cc;">10</span>
	<span style="color: #cc66cc;">7</span>  FETCH_OBJ_R			$<span style="color:#800080;">9</span>	<span style="color: #339933;">!</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'id'</span>
	<span style="color: #cc66cc;">8</span>  <span style="color: #b1b100;">ECHO</span>        				$<span style="color:#800080;">9</span></pre></td></tr></table></div>

<p>2: </p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">	<span style="color: #cc66cc;">1</span>  ZEND_FETCH_CLASS	<span style="color: #cc66cc;">4</span> 	<span style="color: #339933;">:</span><span style="color: #cc66cc;">10</span>	<span style="color: #0000ff;">'Foo2'</span>
	<span style="color: #cc66cc;">2</span>  <span style="color: #000000; font-weight: bold;">NEW</span>             		$<span style="color:#800080;">11</span>	<span style="color: #339933;">:</span><span style="color: #cc66cc;">10</span>
	<span style="color: #cc66cc;">3</span>  SEND_VAL        			<span style="color: #cc66cc;">10</span>
	<span style="color: #cc66cc;">4</span>  DO_FCALL_BY_NAME		<span style="color: #cc66cc;">1</span> 
	<span style="color: #cc66cc;">5</span>  ASSIGN    				<span style="color: #339933;">!</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> $<span style="color:#800080;">11</span>
	<span style="color: #cc66cc;">6</span>  FETCH_OBJ_R			$<span style="color:#800080;">14</span>	<span style="color: #339933;">!</span><span style="color: #cc66cc;">1</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'id'</span>
	<span style="color: #cc66cc;">7</span>  <span style="color: #b1b100;">ECHO</span>        				$<span style="color:#800080;">14</span></pre></td></tr></table></div>

<p>3: </pre>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">	<span style="color: #cc66cc;">1</span>  ZEND_FETCH_CLASS	<span style="color: #cc66cc;">4</span> 	<span style="color: #339933;">:</span><span style="color: #cc66cc;">15</span>	<span style="color: #0000ff;">'Foo3'</span>
	<span style="color: #cc66cc;">2</span>  <span style="color: #000000; font-weight: bold;">NEW</span>         			$<span style="color:#800080;">16</span>	<span style="color: #339933;">:</span><span style="color: #cc66cc;">15</span>
	<span style="color: #cc66cc;">3</span>  SEND_VAL     			<span style="color: #cc66cc;">10</span>
	<span style="color: #cc66cc;">4</span>  DO_FCALL_BY_NAME			<span style="color: #cc66cc;">1</span>          
	<span style="color: #cc66cc;">5</span>  ASSIGN  	   			<span style="color: #339933;">!</span><span style="color: #cc66cc;">2</span><span style="color: #339933;">,</span> $<span style="color:#800080;">16</span>
	<span style="color: #cc66cc;">6</span>  ZEND_INIT_METHOD_CALL	<span style="color: #339933;">!</span><span style="color: #cc66cc;">2</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'getId'</span>
	<span style="color: #cc66cc;">7</span>  DO_FCALL_BY_NAME		<span style="color: #cc66cc;">0</span> 	$<span style="color:#800080;">20</span>     
	<span style="color: #cc66cc;">8</span>  <span style="color: #b1b100;">ECHO</span>       				$<span style="color:#800080;">20</span></pre></td></tr></table></div>

<p>4: </p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">	<span style="color: #cc66cc;">1</span>  ZEND_FETCH_CLASS	<span style="color: #cc66cc;">4</span>  <span style="color: #339933;">:</span><span style="color: #cc66cc;">21</span>	<span style="color: #0000ff;">'Foo4'</span>
	<span style="color: #cc66cc;">2</span>  <span style="color: #000000; font-weight: bold;">NEW</span>          		$<span style="color:#800080;">22</span>	<span style="color: #339933;">:</span><span style="color: #cc66cc;">21</span>
	<span style="color: #cc66cc;">3</span>  END_VAL      			<span style="color: #cc66cc;">10</span>
	<span style="color: #cc66cc;">4</span>  DO_FCALL_BY_NAME		<span style="color: #cc66cc;">1</span>          
	<span style="color: #cc66cc;">5</span>  ASSIGN        			<span style="color: #339933;">!</span><span style="color: #cc66cc;">3</span><span style="color: #339933;">,</span> $<span style="color:#800080;">22</span>
	<span style="color: #cc66cc;">6</span>  FETCH_OBJ_R  		$<span style="color:#800080;">25</span> <span style="color: #339933;">!</span><span style="color: #cc66cc;">3</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'id'</span>
	<span style="color: #cc66cc;">7</span>   <span style="color: #b1b100;">ECHO</span>  				$<span style="color:#800080;">25</span></pre></td></tr></table></div>

<p>根据上面的opcode，参照其在zend_vm_execute.h文件对应的opcode实现，我们可以发现什么？</p>
<p>一、PHP内核创建对象的过程分为三步：</p>
<ol>
<li>ZEND_FETCH_CLASS 根据类名获取存储类的变量，其实现为一个hashtalbe EG(class_table) 的查找操作</li>
<li>NEW 初始化对象，将EX(call)->fbc指向构造函数指针。</li>
<li>调用构造函数，其调用和其它的函数调用是一样，都是调用zend_do_fcall_common_helper_SPEC</li>
</ol>
<p>二、魔术方法的调用是通过条件触发的，并不是直接调用，如我们示例中的成员变量id的获取（zend_std_read_property），其步骤为:</p>
<ol>
<li>获取对象的属性，如果存在，转第二步；如果没有相关属性，转第三步</li>
<li>从对象的properties查找是否存在与名称对应的属性存在，如果存在返回结果，如果不存在，转第三步</li>
<li>如果存在__get魔术方法，则调用此方法获取变量，如果不存在，报错</li>
</ol>
<p>回到排序的问题：</p>
<p>一、第一个和第二个的区别是什么？</p>
<p>第二个的opcode比第一个要少，反而比第一个要慢一些，因为构造函数多了参数，多了一个参数处理的opcode。参数处理是一个比较费时的操作，当我们在做代码优化时，一些不必要的参数能去掉就去掉；当一个函数有多个参数时，可以考虑通过一个数组将其封装后传递进来。</p>
<p>二、为啥第三个最慢？</p>
<p>因为其获取参数其本质上是一次对象成员方法的调用，方法的调用成本高于变量的获取</p>
<p>三、为啥第四个比第三个要快？</p>
<p>因为第四个的操作实质上获取变量，只不过其内部实现了魔术方法的调用，相对于用户定义的方法，内部函数的调用的效率会高。因此，当我们有一些PHP内核实现的方法可以调用时就不要重复发明轮子了。</p>
<p>四、为啥第四个比第二个要慢？</p>
<p>因为在PHP的对象获取变量的过程中，当成员变量在类的定义不在在时，会去调用PHP特有的魔术方法__get，多了一次魔术方法的调用。</p>
<p>总结一下：</p>
<ol>
<li>使用PHP内置函数</li>
<li>并不是事必面向对象(OOP)，面向对象往往开销很大，每个方法和对象调用都会消耗很多内存。</li>
<li>尽量少用魔术方法 &#8212; 除非有必要，不要用框架，因为框架都有大量的魔术方法使用。</li>
<li>在性能优先的应用场景中，将成员变量设置为public，不失为一种比较好的方法，当你需要用到OOP时。</li>
<li>能使用PHP语法结构的不要用函数，能使用内置函数的不要自己写，能用函数的不要用对象</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2014/02/php-var-compare/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP的$this变量</title>
		<link>https://www.phppan.com/2013/10/php-this-var/</link>
		<comments>https://www.phppan.com/2013/10/php-this-var/#comments</comments>
		<pubDate>Sun, 13 Oct 2013 01:56:24 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1869</guid>
		<description><![CDATA[手册上的一个有意思的小示例。 http://www.php.net/manual/zh/language.va [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>手册上的一个有意思的小示例。</p>
<p>http://www.php.net/manual/zh/language.variables.basics.php</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">	<span style="color: #000088;">$this</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'text'</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// error</span>
	<span style="color: #000088;">$name</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'this'</span><span style="color: #339933;">;</span>
	<span style="color: #000088;">$$name</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">'text'</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// sets $this to 'text'</span>
	<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$$name</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>在PHP的词法分析时，$this变量是符合其规则的，在语法解析生成中间代码时，PHP内核会根据变量类型在生成赋值的中间代码时判断是否为$this变量，如果是则报错。这里为什么要报错呢？因为this作为一个特殊的变量，在对象的成员方法等调用初始化时会将this变量添加到活动符号表。</p>
<p>在类的成员方法里面，可以用 -&gt;（对象运算符）：$this-&gt;property（其中 property 是该属性名）这种方式来访问非静态属性。</p>
<p>当一个方法在类定义内部被调用时，有一个可用的伪变量 $this。$this 是一个到主叫对象的引用（通常是该方法所从属的对象，但如果是从第二个对象静态调用时也可能是另一个对象）。</p>
<p>在词法分析、语法分析并生成中间代码时，$this作为一个特殊的变量存在，特别是在生成中间代码时，代码中充斥着对于this的特殊处理。这些都是为后面的运行做准备，如识别标记出某处使用this变量，在存储opcode的zend_op_array结构体中专门有一个变量this_var标识是否有this变量。一个函数或一个类方法都会生成一个新的zend_op_array，在生成中间代码时，判断当前变量是否为this变量。</p>
<p>this变量在执行过程中会有两种存在状态，一种是全局传递的状态，存储在EG(This)，一种是当前作用域状态，以this变量存储在EG(active_symbol_table)（当前执行环境的活动符号表）。<br />
在我们执行一个 op_array 时，比如一个对象的方法，PHP内核会给这个 op_array 生成一个 zend<em>execute</em>data ,在生成初始化时，EG(This) 会添加到EG(active_symbol_table) 。<br />
在方法调用过程中，如果有用到this变量，则会直接取EG(active_symbol_table)的值。</p>
<p>那么一个对象中的EG(This)在哪里初始化呢？<br />
就EG(This)变量本身来说，在我们初始化PHP的执行环境时，它和其它全局变量(如EG(scope)等)一样都会被初始化为NULL。<br />
对于一个对象来说，当我们创建了一个对象，调用时，PHP内核会将当前获得的对象直接赋值给EG(This)，而这个当前获得的对象是在通过new操作生成对象时创建的对象本身。</p>
<p>如下这个简单示例：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">	<span style="color: #000000; font-weight: bold;">class</span> Foo <span style="color: #009900;">&#123;</span>
	     <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000088;">$var</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">10</span><span style="color: #339933;">;</span>
&nbsp;
	     <span style="color: #000000; font-weight: bold;">function</span> t<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	          <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #000000; font-weight: bold;">var</span><span style="color: #339933;">;</span>     
	     <span style="color: #009900;">&#125;</span>
&nbsp;
	     <span style="color: #000000; font-weight: bold;">function</span> t2<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
	     	<span style="color: #b1b100;">echo</span> <span style="color: #cc66cc;">33</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #000088;">$foo</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Foo<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000088;">$foo</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">t</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>其主程序流程生成的中间代码如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">        <span style="color: #000000; font-weight: bold;">function</span> name<span style="color: #339933;">:</span>  <span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span>
	number of ops<span style="color: #339933;">:</span>  <span style="color: #0000dd;">8</span>
	compiled vars<span style="color: #339933;">:</span>  <span style="color: #339933;">!</span><span style="color: #0000dd;">0</span> <span style="color: #339933;">=</span> $foo
	line     <span style="color: #339933;"># *  op                           fetch          ext  return  operands</span>
	<span style="color: #339933;">---------------------------------------------------------------------------------</span>
	   <span style="color: #0000dd;">2</span>     <span style="color: #0000dd;">0</span>  <span style="color: #339933;">&gt;</span>   NOP                                                      
	  <span style="color: #0000dd;">15</span>     <span style="color: #0000dd;">1</span>      ZEND_FETCH_CLASS                              <span style="color: #0000dd;">4</span>  <span style="color: #339933;">:</span><span style="color: #0000dd;">1</span>      <span style="color: #ff0000;">'Foo'</span>
	         <span style="color: #0000dd;">2</span>      NEW                                              $<span style="color:#800080;">2</span>      <span style="color: #339933;">:</span><span style="color: #0000dd;">1</span>
	         <span style="color: #0000dd;">3</span>      DO_FCALL_BY_NAME                              <span style="color: #0000dd;">0</span>          
	         <span style="color: #0000dd;">4</span>      ASSIGN                                                   <span style="color: #339933;">!</span><span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> $<span style="color:#800080;">2</span>
	  <span style="color: #0000dd;">16</span>     <span style="color: #0000dd;">5</span>      ZEND_INIT_METHOD_CALL                                    <span style="color: #339933;">!</span><span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #ff0000;">'t'</span>
	         <span style="color: #0000dd;">6</span>      DO_FCALL_BY_NAME                              <span style="color: #0000dd;">0</span>          
	         <span style="color: #0000dd;">7</span>    <span style="color: #339933;">&gt;</span> RETURN                                                   <span style="color: #0000dd;">1</span></pre></td></tr></table></div>

<p>this变量原始的对象值出生在 opcode NEW，经过了赋值（ASSIGN）后，在方法初始化时，将变量本身传递给执行环境的调用者，调用者又在执行调用（DO_FCALL_BY_NAME）时将变量传递给EG(This)，当执行这个方法的op_array时，初始化当前作用域的环境（zend_execute_data）时，会将EG(This)作为$this变量添加到活动符号表，后续方法中的$this变量的使用就会直接取符号表的变量。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2013/10/php-this-var/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>re2c中文手册</title>
		<link>https://www.phppan.com/2013/09/re2c-chinese-manual/</link>
		<comments>https://www.phppan.com/2013/09/re2c-chinese-manual/#comments</comments>
		<pubDate>Sun, 08 Sep 2013 09:50:25 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[flex]]></category>
		<category><![CDATA[Lex]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[re2c]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>
		<category><![CDATA[词法解析]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1850</guid>
		<description><![CDATA[re2c中文手册 在PHP的实现过程中，包括PHP语言本身的词法分析，一共有多达8处的地方使用了re2c，如果 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h1>re2c中文手册</h1>
<p>在PHP的实现过程中，包括PHP语言本身的词法分析，一共有多达8处的地方使用了re2c，如果我们常用的时间函数、pdo扩展等。对re2c的了解更能促进我们进PHP内核实现的认知。</p>
<p>本手册是re2c官网的manual.html文件翻译稿，仅适用于对re2c的初步了解，更多的资料见<a href="http://sourceforge.net/projects/re2c/">re2c项目</a>中lessons目录和doc目录。</p>
<h2>Name</h2>
<p>re2c &#8211; 将正则表达式转化成C/C++代码</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">Synopsisre2c <span style="color: #009900;">&#91;</span><span style="color: #339933;">-</span>bdDefFghisuvVw1<span style="color: #009900;">&#93;</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">-</span>o output<span style="color: #009900;">&#93;</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">-</span>c <span style="color: #009900;">&#91;</span><span style="color: #339933;">-</span>t header<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span> file</pre></td></tr></table></div>

<h2>Description</h2>
<p>re2c是一个将正则表达式转化成基于C语言标识的预处理器。</p>
<p>re2c的输入包含C/C++代码，并且以/*!re2c&#8230; */注释的格式将扫描标识交错嵌入到这些代码中。在它的输出中，这些注释将会被生成的代码替换掉，当执行时，它将会查找到下一个token，并且执行用户提供的针对该token的特定代码。<br />
如下示例：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">char</span> <span style="color: #339933;">*</span>scan<span style="color: #009900;">&#40;</span><span style="color: #993333;">char</span> <span style="color: #339933;">*</span>p<span style="color: #009900;">&#41;</span>
	<span style="color: #009900;">&#123;</span>
	<span style="color: #808080; font-style: italic;">/*!re2c
	        re2c:define:YYCTYPE  = &quot;unsigned char&quot;;
	        re2c:define:YYCURSOR = p;
	        re2c:yyfill:enable   = 0;
	        re2c:yych:conversion = 1;
	        re2c:indent:top      = 1;
	        [0-9]+          {return p;}
	        [^]             {return (char*)0;}
	*/</span>
	<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>re2c将生成如下代码：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* Generated by re2c on Sat Apr 16 11:40:58 1994 */</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>scan<span style="color: #009900;">&#40;</span><span style="color: #993333;">char</span> <span style="color: #339933;">*</span>p<span style="color: #009900;">&#41;</span>
	<span style="color: #009900;">&#123;</span>
	    <span style="color: #009900;">&#123;</span>
	        <span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span> yych<span style="color: #339933;">;</span>
&nbsp;
	       yych <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span>p<span style="color: #339933;">;</span>
	        <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>yych <span style="color: #339933;">&lt;=</span> <span style="color: #ff0000;">'/'</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">goto</span> yy4<span style="color: #339933;">;</span>
	        <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>yych <span style="color: #339933;">&gt;=</span> <span style="color: #ff0000;">':'</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">goto</span> yy4<span style="color: #339933;">;</span>
	        <span style="color: #339933;">++</span>p<span style="color: #339933;">;</span>
	        yych <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span>p<span style="color: #339933;">;</span>
	        <span style="color: #b1b100;">goto</span> yy7<span style="color: #339933;">;</span>
	yy3<span style="color: #339933;">:</span>
	        <span style="color: #009900;">&#123;</span><span style="color: #b1b100;">return</span> p<span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
	yy4<span style="color: #339933;">:</span>
	        <span style="color: #339933;">++</span>p<span style="color: #339933;">;</span>
	        yych <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span>p<span style="color: #339933;">;</span>
	        <span style="color: #009900;">&#123;</span><span style="color: #b1b100;">return</span> <span style="color: #993333;">char</span><span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span><span style="color: #009900;">&#125;</span>
	yy6<span style="color: #339933;">:</span>
	        <span style="color: #339933;">++</span>p<span style="color: #339933;">;</span>
	        yych <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">unsigned</span> <span style="color: #993333;">char</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span>p<span style="color: #339933;">;</span>
	yy7<span style="color: #339933;">:</span>
	        <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>yych <span style="color: #339933;">&lt;=</span> <span style="color: #ff0000;">'/'</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">goto</span> yy3<span style="color: #339933;">;</span>
	        <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span>yych <span style="color: #339933;">&lt;=</span> <span style="color: #ff0000;">'9'</span><span style="color: #009900;">&#41;</span> <span style="color: #b1b100;">goto</span> yy6<span style="color: #339933;">;</span>
	        <span style="color: #b1b100;">goto</span> yy3<span style="color: #339933;">;</span>
	    <span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>你可以通过添加注释：/*!max:re2c<em>/ 来输出一个宏定义 YYMAXFILL 来保存输入解析时字符的最大个数。如果使用了-1,  YYMAXFILL 只能在最后的 /*!re2c</em>/ 后触发一次。同时，你也可以使用 /*!ignore:re2c */ 来为扫描代码添加注释文档，它们被输出。</p>
<h2>Options</h2>
<p>re2c提供如下的选项：</p>
<ul>
<li>-?</li>
<li>-h  帮助</li>
<li>-b  当指定-b参数时，-s参数也会被默认同时指定。 使用位向量尝试着从编译器捣鼓出更好的代码。它对于关键字比较多的规则很有用，比如大部分的编程语言。re2c的实现是通过生成256个ascii字符的映射表，直接判断对应的字符串是否应该跳转到下一个字符，从而实现优化。</li>
<li>-c 支持类lex或flex的表达式</li>
<li>-d 创建一个解析器用来打印当前位置的信息，这对于调试非常有用。如果你要使用它，你需要定义一个供解析器调用的YYDEBUG宏，它像一个函数一样，接受两个参数：void YYDEBUG(int state,char current)。第一个参数是state或者-1，第二个参数是当前所解析的代码位置。在每个++YYCURSOR、不同的goto跳转变化处，re2c自动添加YYDEBUG宏调用。如果在规则文件中没定义YYDEUBG宏，在编译C文件时会出错。</li>
<li>-D 输出Graphviz dot 格式的数据，比如可以使用&#8221; dot -Tpng input.dot &gt; output.png&#8221;来处理生成图片。注意扫描器中如果包含太多的状态可能会让dot程序崩溃</li>
<li>-e 从ASCII平台交叉编译EBCDIC</li>
<li>-f  生成带可存储状态的扫描器。更多详情见下面的可存储的扫描器小节。</li>
<li>-F 部分支持flex语法。当-F标记有效时，flex的变量用大括号括起来，并且在定义时不需要等号，在结束时不需要用分号。否则，名字被认为是直接被引号的字符串。</li>
<li>-g 使用GCC的goto特性生成扫描器。当决策复杂时re2c会生成决策跳转表，使用goto针对不同的情况做不同的跳转。仅适用于GCC编译器。注意，这里默认指定了-b参数。re2c的实现中，-g参数会生成yytarget决策跳转表，其实就是一个256个元素的一维数据，针对不同的字符，直接跳转，以优化扫描器。</li>
<li>-i 不输出行信息，当你的用户从你的代码编译，而你又不要求他们拥有re2c环境，此时你可以使用CMS工具管理re2c的输出文件时，此参数就有用武之地了。-o参数指定输出文件。<br />
指在生成的.c文件中不使用#line宏。</li>
<li>-r  允许扫描器在每个 &#8216;/<em>!use:re2c&#8217;块后面重用定义的 &#8216;/</em>!use:re2c&#8217; 块。这些块可以包含适当的配置，特别是 &#8216;re2c:flags:w&#8217;和re2c:flags:u&#8217;。这种方法可能会为不同的字符类型，不同的输入机制或不同的输出机制多次创建相同的扫描器。&#8217;/<em>!use:re2c&#8217; 块也可以在 &#8216;/</em>!rules:re2c&#8217;中的规则集中包含额外的规则。</li>
<li>-s 为一些switch语句生成嵌套的if语句。许多编译器需要这个参数的辅助以便生成更好的代码。</li>
<li>-t  生成一个类型定义的头文件，以支持类(f)lex条件，当需要使用-t参数时，需同时指定-c参数，-t参数后面接生成的头文件名称。如果只指定re2c会报错：re2c: error: Can only output a header file when using -c switch</li>
<li>-u 生成一个支持Unicode编码的解析器。这意味着生成的代码能处理任何有效的Unicode字符，直到x10FFFF。当需要支持UTF-8或UTF-16时，你需要自己将输入的数据转化成UTF-32编码。</li>
<li>-v 查看版本信息。如：re2c 0.13.6</li>
<li>-V 以数字格式查看版本信息。如：001306</li>
<li>-w 创建支持宽字符格式的解析器，默认指定-s参数，不能和-e参数共存。</li>
<li>-1 强制一次生成，它不能和-f组合在一起使用，并且在re2c块结束之前不能禁用YYMAXFILL。</li>
<li>&#8211;no-generation-date   禁止输出生成日志，所以只会输出re2c的版本信息。</li>
<li>&#8211;case-insensitive          所有字符串不区分大小写，所以，双引号中的字符和单引号的意义一样。</li>
<li>&#8211;case-inverted          颠倒单引号和双引号包含的字符中的意思，比如，有了这个开关，单引号内的字符串区分大小写，双引号内的字符串不区分大小写。</li>
</ul>
<h2>Interface Code接口代码</h2>
<p>不像其他的扫描器程序，re2c 不会生成完整的扫描器：用户必须提供一些接口代码。用户必须定义下面的宏或者是其他相应的配置。</p>
<ul>
<li>YYCONDTYPE 用-c 模式你可以使用-t参数来生成一个包含了会被作为条件使用的枚举类型的文件。枚举类型中的每个值都会在规则集合里面作为条件来使用。</li>
<li>YYCTYPE 用来维持一个输入符号。通常是 char 或者unsigned char。</li>
<li>YYCTXMARKER  *YYCTYPE类型的表达式，生成的代码回溯信息的上下文会保存在</li>
<li>YYCTXMARKER。如果扫描器规则需要使用上下文中的一个或多个正则表达式，则用户需要定义这个宏。</li>
<li>YYCURSOR  *YYCTYPE类型的表达式指针指向当前输入的符号，生成的代码作为符号相匹配，在开始的地方，YYCURSOR假定指向当前token的第一个字符。在结束时，YYCURSOR将会指向下一个token的第一个字符。</li>
<li>YYDEBUG(state,current) 这个只有指定-d标记的时候才会需要。调用用户定义的函数时可以非常容易的调试生成的代码。<br />
这个函数应该有以下签名：void YYDEBUG(int state,char current)。第一个参数接受 state ，默认值为-1第二个参数接受输入的当前位置。</li>
<li>YYFILL(n) 当缓冲器需要填充的时候，生成的代码将会调用YYFILL(n)：至少提供n个字符。YYFILL(n)将会根据需要调整YYCURSOR,YYLIMIT,YYMARKER 和 YYCTXMARKER。注意在典型的程序语言当中，n等于最长的关键词的长度加一。用户可以在/*!max:re2c<em>/一次定义YYMAXFILL来指定最长长度。如果使用了-1，YYMAXFILL将会在/*!re2c</em>/之后调用一次阻塞。</li>
<li>YYGETCONDITION() 如果使用了-c模式，这个定义将会在扫描器代码之前获取条件集。这个值必须初始化为枚举YYCONDTYPE的类型。</li>
<li>YYGETSTATE() 如果指定了-f模式，用户就需要定义这个宏。此种情况下，扫描器在开始时为了获取保存的状态，生成的代码将会调用YYGETSTATE()。YYGETSTATE()必须返回一个有符号的整数，这个值如果是-1，告诉扫描器这是第一次执行，否则这个值等于以前YYSETSTATE(s) 保存的状态。否则，扫描器将会恢复操作之后立即调用YYFILL(n)。</li>
<li>YYLIMIT 这是一个类型为*YYCTYPE的表达式，它标记了缓冲器的结尾（YYLIMIT[-1]是缓冲区的最后一个字符）。生成的代码将会不断的比较YYCORSUR 和 YYLIMIT 以决定 什么时候填充缓冲区。</li>
<li>YYSETCONDITION(c) 这个宏用来在转换规则中设置条件，它只有在指定-c模式和使用转换规则时有用。</li>
<li>YYSETSTATE(s) 用户只需要在指定-f模式时定义这个宏，如果是这样，生成的代码将会在YYFILL(n)之前调用YYSETSTATE(s)，YYSETSTATE的参数是一个有符号整型，被称为唯一的标示特定的YYFILL(n)实例。</li>
<li>YYMARKER 类型为*YYCTYPE的表达式，生成的代码保存回溯信息到YYMARKER。一些简单的扫描器可能用不到。</li>
<li>解析器支持条件 当使用-c参数时，你可以使用正则表达式条件列表。这样re2c会为每个条件生成扫描块，在每一个生成的扫描器都有自己的先决条件。先决条件是定义YYGETCONDETION ，而且类型必须是YYCONDTYPE。</li>
<li>YYSETSTATE(s) 用户只需要在指定-f模式时定义这个宏。在此种情况下，生成的代码将会在YYFILL(n)之前调用YYSETSTATE(s)，YYSETSTATE的参数是一个有符号整型，被称为唯一的标示特定的YYFILL(n)实例。如果用户希望保存扫描器的状态并用YYFILL(n) 将状态返回给调用 者，他所需要做的是在变量中保存这个唯一的标识。然后，当再次调用扫描器时，它将调用</li>
<li>YYGETSTATE()并在恢复到之前离开的地方继续执行。即使禁用了 YYFILL(n) ，生成的代码也会包含YYSETSTATE(s)和YYGETSTATE。</li>
</ul>
<h2>Scanner With Storable States可存储状态的扫描器</h2>
<p>当指定-f标记时，re2c会生成一个存储了它当前状态的扫描器，它能精确的恢复到之前离开的位置，并返回给调用者。</p>
<p>re2c的默认行为是拉模式，无论何时需要，它都可以要求额外的输入，然而，这种操作模式是基于扫描器可以控制解析循环这一前提的，而这个前提并不一定会存在。<br />
通常情况下，如果有一个预处理过程或其它相关的源程序数据在扫描器之前先执行，则扫描器无法再要求更多的数据，除非他们都在独立的线程之中。</p>
<p>-f标记刚好可以解决这个问题：它让用户设计的扫描器以拉模式工作，即数据一块一块的输入到扫描器中。当扫描器运行数据时，它仅存储它的状态，并返回给调用者。当更多的输入数据输入到扫描器时，它能很精确的恢复到之前离开的位置。</p>
<p>当re2c使用-f选项时，它不能接收标准输入，因为它必须做两次完整的全局扫描，而两次扫描就需要读取两次。这就意味着，如果不能打开输入两次或第一次输入影响第二次输入，re2c会执行失败。</p>
<p>相对于拉模式，可存储的扫描器有以下不同：</p>
<ol>
<li>用户必须提供YYSETSTATE() 宏和YYGETSTATE(state)宏</li>
<li>-f参数禁止了yych和yyaccept的声明。因此用户必须声明这些，并且必须能够保存和恢复他们。在example/push.re文件的示例中，这些都被声明为C++类的字段，因此他们不再需要明确的保存或恢复。对于C语言来说，我们可以通过宏，以参数传递的方式从结构体中获取这些字段。或者，可以将他们声明为局部变量，当它决定返回并将之作为函数的一个项保存在 YYFILL(n)中。此外， 使用YYFILL(n)保存的效率更高，因为可以无条件的调用YYSETSTATE(state)。然而，YYFILL(n) 并不能将state作为参数，因此，我们必须通过YYSETSTATE(state)将state保存到局部变量中。</li>
<li>如果需要更多的输入，需要修改YYFILL(n) ，使之可以从调用它的函数处返回。</li>
<li>修改调用者的逻辑，使其在需要更多的输入时做出相应的应答。</li>
<li>生成的代码中将包含一个选择逻辑块，这个选择逻辑会被用来通过跳转到相应的YYFILL(n)调用处，以恢复最后的状态。这个代码块会在第一个 &#8220;/*!re2c */&#8221;块收尾的地方自动生成。通过放置 &#8220;/*!getstate:re2c */&#8221;注释，可能会触发YYGETSTATE() 的生成操作。这对于被包含在循环中的扫描器非常有用。</li>
</ol>
<p>请查看 examples/push.re文件中的推模式示例扫描器。它生成的代码可以通过&#8221;state:abort&#8221;和&#8221;state:nextlabel&#8221;调整。</p>
<h2>Scanner With Condition Support 可判断条件的扫描</h2>
<p>当使用-c参数时，你可以在正则表达式之前优先一系统的条件名。在这种情况下，re2c会针对每个条件生成扫描代码块。这些代码块都有它自己的前置条件，这此前置条件都是通过接口定义YYGETCONDITON实现，并且必须为YYCONDTYPE类型。</p>
<p>其中有两个特别的类型，一个是‘*’，它表示满足所有条件；另一个是空条件，它提供一个没有扫描内容的代码块，这意味着不需要任何正则表达式。这个特殊的块始终有一个固定的枚举值0。这些特殊的规则可以被用来初始化一个扫描器。这些特殊的规则并不是必须的，但是有时可以用它来声明一些没有初始化的状态。</p>
<p>非空规则允许指定新的条件，这些条件将导致规则的变化。它会生成定义的YYSETCONDTITION，除此之外再无其它。</p>
<p>还有另一种特殊的规则，它允许在所有的有规则和没有规则代码前添加代码。例如，它可以用来保存扫描的字符串的长度。这个特殊的规则以感叹号开始，后面可以接条件 &lt;! condition, &#8230; &gt; 或星号&lt;!*&gt;。当re2c为这个规则生成代码时，如果这个规则的状态没有起始规则或已经在在一个星号规则，那么这个代码将作为起始代码。</p>
<h2>Scanner Specifications 扫描器规则</h2>
<p>每个扫描器规格都由 规则集、命名定义和配置构成。</p>
<p>规则由正则以及紧跟其后面的C/C++代码构成，当正则匹配时，其后的C/C++代码会被执行。你可以以大括号或：=开始代码。当用大括号开始代码时，re2c会根据大括号判断其尝试并自动结束代码的查找。如果不使用大括号开始代码，则re2c会在第一行不为空时停止查找。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">	regular<span style="color: #339933;">-</span>expression <span style="color: #009900;">&#123;</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code <span style="color: #009900;">&#125;</span>
	regular<span style="color: #339933;">-</span>expression <span style="color: #339933;">:=</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code</pre></td></tr></table></div>

<p>如果指定-c参数，则每个正则前面都会有一系列的由逗号分隔的条件名称。除了正常命名的规则以外，有两种特殊的情况。一个规则可能包含一个单独的条件名称&#8217;*&#8217;和没有条件名称。对于没有条件名称的情况，其后面不能接正则表达式。非空规则可能会进一步指定新的条件。在这种情况下，re2c可能会自动生成必要的代码来改变条件。如上所示代码，其以大括号和&#8217;:=&#8217;开始代码。更进一步，更多的规则可以使用&#8217;:=&gt;&#8217;快捷方式来自动生成代码，它不仅仅可以设置新的状态，还可以继续执行新的状态。一个快捷规则不应该在循环中使用，这些循环代码在循环开始和re2c块之间，除非用 re2c:cond:goto使之 &#8216;continue;&#8217;如果一段代码必须放在所有的规则之前，你可以使用&lt;! 伪规则。</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">    <span style="color: #339933;">&lt;</span>condition<span style="color: #339933;">-</span>list<span style="color: #339933;">&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #009900;">&#123;</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code <span style="color: #009900;">&#125;</span>
    <span style="color: #339933;">&lt;</span>condition<span style="color: #339933;">-</span>list<span style="color: #339933;">&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #339933;">:=</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code
    <span style="color: #339933;">&lt;</span>condition<span style="color: #339933;">-</span>list<span style="color: #339933;">&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #339933;">=&gt;</span> condition <span style="color: #009900;">&#123;</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code <span style="color: #009900;">&#125;</span>
    <span style="color: #339933;">&lt;</span>condition<span style="color: #339933;">-</span>list<span style="color: #339933;">&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #339933;">=&gt;</span> condition <span style="color: #339933;">:=</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code
    <span style="color: #339933;">&lt;</span>condition<span style="color: #339933;">-</span>list<span style="color: #339933;">&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #339933;">:=&gt;</span> condition
    <span style="color: #339933;">&lt;*&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #009900;">&#123;</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code <span style="color: #009900;">&#125;</span>
    <span style="color: #339933;">&lt;*&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #339933;">:=</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code
    <span style="color: #339933;">&lt;*&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #339933;">=&gt;</span> condition <span style="color: #009900;">&#123;</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code <span style="color: #009900;">&#125;</span>
    <span style="color: #339933;">&lt;*&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #339933;">=&gt;</span> condition <span style="color: #339933;">:=</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code
    <span style="color: #339933;">&lt;*&gt;</span> regular<span style="color: #339933;">-</span>expression <span style="color: #339933;">:=&gt;</span> condition
    <span style="color: #339933;">&lt;&gt;</span> <span style="color: #009900;">&#123;</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code <span style="color: #009900;">&#125;</span>
    <span style="color: #339933;">&lt;&gt;</span> <span style="color: #339933;">:=</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code
    <span style="color: #339933;">&lt;&gt;</span> <span style="color: #339933;">=&gt;</span> condition <span style="color: #009900;">&#123;</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code <span style="color: #009900;">&#125;</span>
    <span style="color: #339933;">&lt;&gt;</span> <span style="color: #339933;">=&gt;</span> condition <span style="color: #339933;">:=</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code
    <span style="color: #339933;">&lt;&gt;</span> <span style="color: #339933;">:=&gt;</span> condition
    <span style="color: #339933;">&lt;!</span>condition<span style="color: #339933;">-</span>list<span style="color: #339933;">&gt;</span> <span style="color: #009900;">&#123;</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code <span style="color: #009900;">&#125;</span>
    <span style="color: #339933;">&lt;!</span>condition<span style="color: #339933;">-</span>list<span style="color: #339933;">&gt;</span> <span style="color: #339933;">:=</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code
    <span style="color: #339933;">&lt;!*&gt;</span> <span style="color: #009900;">&#123;</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code <span style="color: #009900;">&#125;</span>
    <span style="color: #339933;">&lt;!*&gt;</span> <span style="color: #339933;">:=</span> C<span style="color: #339933;">/</span>C<span style="color: #339933;">++</span> code</pre></td></tr></table></div>

<p>命名定义格式如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">name <span style="color: #339933;">=</span> regular<span style="color: #339933;">-</span>expression<span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>如果使用了-F 模式，可以使用如下命名定义方法：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">name regular<span style="color: #339933;">-</span>expression<span style="color: #339933;">&amp;</span>lt<span style="color: #339933;">;</span> <span style="color: #339933;">/</span>pre<span style="color: #339933;">&amp;</span>gt<span style="color: #339933;">;</span>
以<span style="color: #ff0000;">&quot;re2c&quot;</span>开始的命名定义配置如下所示：
<span style="color: #339933;">&lt;</span>pre lang<span style="color: #339933;">=</span><span style="color: #ff0000;">&quot;c&quot;</span><span style="color: #339933;">&gt;</span>re2c<span style="color: #339933;">:</span>name <span style="color: #339933;">=</span> value<span style="color: #339933;">;</span>
re2c<span style="color: #339933;">:</span>name <span style="color: #339933;">=</span> <span style="color: #ff0000;">&quot;value&quot;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<h2>Summary Of Re2c Regular-expressionsre2c正则表达式小结</h2>
<ul>
<li>&#8220;foo&#8221; 字符串foo。可以使用ANSI-C转义序列。</li>
<li>[xyz] 字符集；此种情况匹配字符x,y或z</li>
<li>[abj-oZ] 包含区间的字符集，此种情况匹配a,b,j到o之间的任一字符，或z</li>
<li>[^class]  字符集否定匹配，匹配没有在方括号中定义的字符。</li>
<li>r\s 匹配非s的正则，r和s都必须是可以表示为字符集的正则表达式</li>
<li>r* 零次或多次匹配，r是任一正则表达式</li>
<li>r+ 一次或多次匹配（至少一次）</li>
<li>r? 零次或一次匹配</li>
<li>name  这里name就是在前面的定义段给出的名字</li>
<li>(r) 匹配规则表达式r，圆括号可以提高其优先级。</li>
<li>rs 匹配规则表达式r，其后紧跟着表达式s。这称为联接(concatenation)。</li>
<li>r|s 或者匹配规则表达式r，或者匹配表达式s。</li>
<li>r/s 匹配模式r，但是要求其后紧跟着模式s。s并不会参与文本的匹配。这种正则表达式的匹配称之为“尾部上下文”</li>
<li>r{n}  n次匹配</li>
<li>r{n,} 至少n次匹配</li>
<li>r{n,m} 至少n次，至多m次匹配；匹配除换行符外的任意字符</li>
<li>def 当没有使用-F参数时，匹配的命名定义通过def定义。当-F参数指定时，def语名和双引号包含的效果一样，直接匹配def字符串。字符集和字符串可能包含有八进制或十六进制或如下的转义字符 (\n, \t, \v, \b, \r, \f, \a, \)。一个八进制字符由一个反斜杠和紧跟着它的三个八进制数字组成，一个十六进制字符由一个反斜杠，一个小写的x，以及两个十六进制数字组成，或由一个反斜杠，一个大写的X，以及四个十六进制数字组成。re2c进一步会支持更多的C/C++的unicode符号。这些unicode符号由一个反斜杠+u+四个十六进制数字或一个反斜杠+U+八个十六进制的数字组成。然后，仅当-u模式下才能处理这些uincode字符。</li>
</ul>
<p>在非unicode模式下，大于\X00FF的字符是无法直接匹配的，除非使用”万金油“类型的 (.|&#8221;\n&#8221;)和[^]正则表达式匹配所有的字符时，包含它们。</p>
<p>如上所示的正则表达式列表按优先级分组，从最上面的最高优先级到最下面的最低优先级。这些组合之间的优先级相同。</p>
<h2>Inplace Configuration现场配置</h2>
<p>它可能在re2c块中配置并生成代码，如下所示为可用的配置项：</p>
<ul>
<li>re2c:condprefix = yyc_ ;<br />
允许指定条件标签的前缀。它将在生成的输出文件中的所有条件标签前添加指定的前缀。</li>
<li>re2c:condenumprefix = yyc ;<br />
允许指定条件值的前缀。它将在生成的输出文件中的所有条件枚举值前添加指定的前缀。</li>
<li>re2c:cond:divider = &#8220;/* *********************************** */&#8221; ;<br />
允许为条件块自定义分隔符。你可以使用&#8217;@@&#8217;输出条件的名字或使用</li>
<li>re2c:cond:divider@cond = @@ ;<br />
指定即将被 re2c:cond:divider中的条件名替换的占位符。</li>
<li>re2c:cond:goto = &#8220;goto @@;&#8221; ;<br />
允许使用 &#8216;:=&gt;&#8217; 规则自定义条件跳转语句。你可以使用&#8217;@@&#8217;输出条件的名字或使用re2c:cond:divider@cond自定义占位符，同时你也可以使用此语句继续下一个循环周期，这个循环周期包括循环开始到re2c块之间的任何代码。</li>
<li>re2c:cond:goto@cond = @@ ;<br />
指定即将在 re2c:cond:goto语句中被替换的条件标签占位符</li>
<li>re2c:indent:top = 0 ;<br />
指定最小的缩进，大于或等于0</li>
<li>re2c:indent:string = &#8220;\t&#8221; ;<br />
指定缩进用的字符串。除非你想使用外部工具，否则就需要只包含空白字符串。最简单的方法就是用单引号或双引号包含它们。如果你不需要任何缩进，直接使用&#8221;&#8221;即可。</li>
<li>re2c:yych:conversion = 0 ;<br />
当此设置非零时，re2c会在读取yych时自动生成转换代码。此时的类型必须使用re2c:define:YYCTYPE定义。</li>
<li>re2c:yych:emit = 1 ;<br />
设置为0可以禁止yych的生成。</li>
<li>re2c:yybm:hex = 0 ;<br />
如果设置为0，则生成一个十进制表格，否则将生成一个十六进制表格</li>
<li>re2c:yyfill:enable = 1 ;<br />
将此设置为0可以禁止YYFILL(n)的生成。当使用它时请确认生成的扫描器在输入之后不再读取。允许此行为将给你的程序引入服务安全问题。</li>
<li>re2c:yyfill:check = 1 ;<br />
当YYLIMIT + max(YYFILL)一直可用时，把此设置为0可以禁止使用YYCURSOR和YYLIMIT的先决条件的输出。</li>
<li>re2c:yyfill:parameter = 1 ;<br />
允许禁止YYFILL调用的参数传递。如果设置为0，将没有任何参数传递到YYFILL。然而，define:YYFILL@LEN允许指定一个字符串替换实际字符中的长度。如果设置为非0，除非设置re2c:define:YYFILL:naked，否则YYFILL将使用紧跟其后的大括号内的所要求的字符数。其它请参照：re2c:define:YYFILL:naked和re2c:define:YYFILL@LEN.</li>
<li>re2c:startlabel = 0 ;如果设置为0的整数，即使没有扫描器本身，下一个扫描块的开始标签也会被生成。否则仅在需要的时候生成常规的yy0开始标签。如果设置为一个文本值，不管常规的开始标签生成是否，包含当前文本的标签都会被生成。在开始标签生成后，当前设置会被重置为0。</li>
<li>re2c:labelprefix = yy ;<br />
允许修改数字标签的前缀，默认为yy，任何有效的标签都是可以的。</li>
<li>re2c:state:abort = 0 ;<br />
当设置为非零，并且开启-f模式时，YYGETSTATE 块会包含一个默认的情况，初始化时设置为-1</li>
<li>re2c:state:nextlabel = 0 ;<br />
当开启-f模式时，使用此设置可以控制是否在YYGETSTATE块后面接yyNext标签行。通常，你可以用startlabel配置强制指定开始标签或用默认的yy0作为开始标签，而不是用yyNext。通常我们通过放置&#8221;/*!getstate:re2c */&#8221; 注释来分隔实际扫描器的YYGETSTATE 代码，而不是专用的标签。</li>
<li>re2c:cgoto:threshold = 9 ;<br />
当启用-g模式时，这个值指定生成的跳转表的复杂度阈值，而不是使用嵌套的if语句和决策位字段。</li>
<li>re2c:yych:conversion = 0 ;<br />
当输入使用有符号字符时，并且开启-s和-b械时，re2c会自动将其转化为无符号类型。当设置为0时会禁用空字符串转化。设置为非零时，转化将在YYCTYPE处进行。如果这个值通过现场配置，则使用该值。否则，将会变成(YYCTYPE)，并且不能再修改成配置。当设置为一个字符串时，必须用括号括起来。现在，假设你的输入为char*并且使用上述的设置，你可以设置YYCTYPE为unsigned char，并且当前值设置为1或者&#8221;(unsigned char)&#8221;</li>
<li>re2c:define:define:YYCONDTYPE = YYCONDTYPE ;枚举用于支持-c模式的条件</li>
<li>re2c:define:YYCTXMARKER = YYCTXMARKER ;<br />
允许覆盖定义的YYCTXMARKER ，从而避免将其设置为实际所需的代码。</li>
<li>re2c:define:YYCTYPE = YYCTYPE ;<br />
允许覆盖定义的YYCTYPE ，从而避免将其设置为实际所需的代码。</li>
<li>re2c:define:YYCURSOR = YYCURSOR ;<br />
允许覆盖定义的YYCURSOR ，从而避免将其设置为实际所需的代码。</li>
<li>re2c:define:YYDEBUG = YYDEBUG ;<br />
允许覆盖定义的YYDEBUG ，从而避免将其设置为实际所需的代码。</li>
<li>re2c:define:YYFILL = YYFILL ;<br />
允许覆盖定义的YYFILL ，从而避免将其设置为实际所需的代码。</li>
<li>re2c:define:YYFILL:naked = 0 ;<br />
当设置为1时，括号、参数、分号都会被发出。</li>
<li>re2c:define:YYFILL@len = @@ ;<br />
当使用 re2c:define:YYFILL 时，并且re2c:yyfill:parameter 为0时，YYFILL 中的任何文本将会被新的实际的长度值替换。</li>
<li>re2c:define:YYGETCONDITION = YYGETCONDITION ;<br />
允许覆盖定义的YYGETCONDITION</li>
<li>re2c:define:YYGETCONDITION:naked = ;<br />
当设置为1时，括号、参数、分号都会被发出。</li>
<li>re2c:define:YYGETSTATE = YYGETSTATE ;<br />
允许覆盖定义的YYGETSTATE ，从而避免将其设置为实际所需的代码。</li>
<li>re2c:define:YYGETSTATE:naked = 0 ;<br />
当设置为1时，括号、参数、分号都会被发出。</li>
<li>re2c:define:YYLIMIT = YYLIMIT ;<br />
允许覆盖定义的YYLIMIT ，从而避免将其设置为实际所需的代码。</li>
<li>re2c:define:YYMARKER = YYMARKER ;<br />
允许覆盖定义YYMARKER，从而避免将其设置为实际所需的代码。</li>
<li>re2c:define:YYSETCONDITION = YYSETCONDITION ;<br />
允许覆盖定义的YYSETCONDITION</li>
<li>re2c:define:YYSETCONDITION@cond = @@ ;<br />
当使用 re2c:define:YYSETCONDITION时，YYSETCONDITION中的任何文本将会被新的实际的<br />
条件值替换。</li>
<li>re2c:define:YYSETSTATE = YYSETSTATE ;<br />
允许覆盖定义的YYSETSTATE，从而避免将其设置为实际所需的代码。</li>
<li>re2c:define:YYSETSTATE:naked = 0 ;<br />
当设置为1时，括号、参数、分号都会被发出。</li>
<li>re2c:define:YYSETSTATE@state = @@ ;<br />
当使用re2c:define:YYSETSTATE时，YYSETCONDITION中的任何文本将会被新的实际的状态值替换</li>
<li>re2c:label:yyFillLabel = yyFillLabel ;<br />
允许覆盖标签yyFillLabel，即可以自定义生成的yyFillLabel 变量名。</li>
<li>re2c:label:yyNext = yyNext ;<br />
允许覆盖标签yyNext ，即可以自定义生成的yyNext变量名。</li>
<li>re2c:variable:yyaccept = yyaccept ;<br />
允许覆盖变量yyaccept，即可以自定义生成的yyaccept变量名。</li>
<li>re2c:variable:yybm = yybm ;<br />
允许覆盖变量yybm，即可以自定义生成的yybm变量名。</li>
<li>re2c:variable:yych = yych ;<br />
允许覆盖变量yych，即可以自定义生成的yych变量名。</li>
<li>re2c:variable:yyctable = yyctable ;<br />
当指定-c参数和-g参数时，re2c会使用此变量为YYGETCONDITION生成静态跳转表。</li>
<li>re2c:variable:yystable = yystable ;<br />
当指定-f参数和-g参数时，re2c会使用此变量为YYGETSTATE生成静态跳转表。</li>
<li>re2c:variable:yytarget = yytarget ;<br />
允许覆盖变量yytarget，即可以自定义生成的yytarget变量名。</li>
</ul>
<h2>Understanding Re2c 理解re2c</h2>
<p>re2c的子目录中包含各种例子教你一步一步的如何开启re2c的世界，所有的例子都是可编译运行的。</p>
<h3>Features特点</h3>
<p>re2c不提供默认的动作：生成的代码假定输入包含一系列token。通常，可以通过添加一条规则实现，例如上面示例中的异常字符</p>
<p>因为re2c不提供结束表达式，所以用户必须安排一个输入结束符并让一个规则匹配并捕获它。<br />
如果来源是一个以空字符串结尾的字符串，则匹配一个空字符串就可以了。如果来源是一个文件，你可以在文件后添加一个换行（或其它不会出现的标记）；通过识别这个字符，以检查这是否为一个标记点并执行相应的操作。同样，你也可以使用YYFILL(n)判断是否没有足够的字符可用时结束扫描。</p>
<p>BugsDifference only works for character sets.<br />
The re2c internal algorithms need documentation.<br />
See Alsoflex(1), lex(1).<br />
More information on re2c can be found here:</p>
<p>http://re2c.org/</p>
<p>Authors</p>
<p>Peter Bumbulis <a href="mailto:peter@csg.uwaterloo.ca">peter@csg.uwaterloo.ca</a><br />
Brian Young <a href="mailto:bayoung@acm.org">bayoung@acm.org</a><br />
Dan Nuffer <a href="mailto:nuffer@users.sourceforge.net">nuffer@users.sourceforge.net</a><br />
Marcus Boerger <a href="mailto:helly@users.sourceforge.net">helly@users.sourceforge.net</a><br />
Hartmut Kaiser <a href="mailto:hkaiser@users.sourceforge.net">hkaiser@users.sourceforge.net</a><br />
Emmanuel Mogenet <a href="mailto:mgix@mgix.com">mgix@mgix.com</a> added storable state</p>
<p>英文原地址：http://re2c.org/manual.html<br />
译者：胖子（http://www.phppan.com/）<br />
友情协助：吴帅（http://www.imsiren.com/）<br />
校验：reeze(http://www.reeze.cn)<br />
特别鸣谢：老婆大人的亲自指点和崽崽的听话。</pre>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2013/09/re2c-chinese-manual/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP的ticks机制</title>
		<link>https://www.phppan.com/2013/02/php-ticks/</link>
		<comments>https://www.phppan.com/2013/02/php-ticks/#comments</comments>
		<pubDate>Thu, 07 Feb 2013 09:18:38 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1790</guid>
		<description><![CDATA[PHP的ticks机制 要过年了，在年前完成这篇文章，如果有缘可以看到，祝福看到的朋友新年快乐，在新的一年里， [&#8230;]]]></description>
				<content:encoded><![CDATA[<h1>PHP的ticks机制</h1>
<p>要过年了，在年前完成这篇文章，如果有缘可以看到，祝福看到的朋友新年快乐，在新的一年里，万事顺意！</p>
<p>按今年的计划每个月至少有两篇文章，而一月份因为各种理由而只有一篇2012的总结，无论什么原因，总归是不对的。这篇算是补上的，也作为今年的开始。</p>
<p>回正题，今天要研究的是PHP的ticks机制。</p>
<p>PHP提供declare关键字和ticks关键字来声明ticks机制。如：declare(ticks = N);  这表示：在当前scope内，每执行N句internal  statements（opcodes），就会中断当前的业务语句，去执行通过register_tick_function注册的函数（如果存在的话），然后再继续之前的代码。需要注意的是这里的N是指的PHP的一些OPCODE，而OPCODE与我们见到的PHP语句却不是一一对应的。</p>
<p>最开始我以为PHP内核是在编译时记录是否有ticks机制，在真正执行中间代码时插入判断代码，实现此机制。但是事实上却不是这样滴。</p>
<p>看PHP代码示例1：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">    <span style="color: #000088;">$name</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;phppan&quot;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$name</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">class</span> Tipi <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> test<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;test&quot;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">function</span> f_tipi<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>如上代码包括了我们常见的几种语句，赋值，输出，定义类，定义函数。通常我们用VLD查看PHP生成的中间代码，上面的代码通过 <strong>php  -dvld.active=1 t.php</strong> 我们会看到 ECHO、ASSIGN、NOP等中间代码。</p>
<p>现在我们在示例1的代码上添加上ticks机制。如PHP代码示例2：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">    <span style="color: #000000; font-weight: bold;">declare</span><span style="color: #009900;">&#40;</span>ticks<span style="color: #339933;">=</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #000088;">$name</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;phppan&quot;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$name</span><span style="color: #339933;">;</span>
    <span style="color: #000000; font-weight: bold;">class</span> Tipi <span style="color: #009900;">&#123;</span>
        <span style="color: #000000; font-weight: bold;">public</span> <span style="color: #000000; font-weight: bold;">function</span> test<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;test&quot;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
    <span style="color: #000000; font-weight: bold;">function</span> f_tipi<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>示例2与示例1相比也就是多了第一条语句： declare(ticks=1);  如果我们此时再次通过VLD查看中间代码，会发现在每个中间代码的后面都多了一句中间代码：<strong>TICKS</strong> 。</p>
<p>是否因为ticks=1的原因而在每个中间代码的后面添加了TICKS？将declare(ticks=1);换成declare(ticks=100);，再次VLD，结果没有变化。从以上的结果可以看出，PHP内核在语法分析过程中实现了ticks机制。</p>
<p>从实现过程来说定义ticks机制分为两个过程：一个是定义是否需要执行ticks或者说声明ticks机制，另一个实现在声明了ticks机制的情况下控制语句的执行。</p>
<h3>声明ticks机制过程</h3>
<p>声明的过程就是调用declare(ticks = N);  在语法分析时根据declare关键字和参数中的ticks关键字来声明ticks机制。通过zend_compile.c文件中的zend_do_declare_begin、declare_statement、zend_do_declare_end三个函数来编译声明ticks机制。在declare_statement函数中我们可以看到：declare除了可以声明ticks之外，还可以声明encoding，这在代码里面就是一个if  else的判断。</p>
<p>ticks机制的声明仅在编译过程有用，它为后面的声明控制语句服务。其编译过程中的全局变量为：CG(declarables)。这是一个结构体，它仅有一个成员：ticks。当然后面应该还会有更多的成员出现。</p>
<h3>声明控制语句</h3>
<p>示例1和示例2已经充分说明在每条语句的语法分析时，会根据是否声明了ticks机制来添加TICKS中间代码，其实现在于每条语句在语法解析时都会添加一条函数调用：zend_do_ticks。从zend_language_parser.y文件中可以看出：zend_do_ticks函数添加在类定义语句，函数定义语句和常规语句的后面。  zend_compile.c文件中的zend_do_ticks函数会根据前面提到的 CG(declarables).ticks 来判断是否生成  ZEND_TICKS 中间代码（在VLD中看到的中间代码都是没有ZEND开头）。</p>
<p>除了声明ticks机制，还有执行。执行过程中关键的变量是在声明时的ticks=N。其实这里的N可以换个角度去理解：ticks指定的数字是指执行了多少次TICKS语句。在TICKS中间代码的执行函数ZEND_TICKS_SPEC_CONST_HANDLER中，会统计执行当前函数的次数，存储变量为EG(ticks_count)。当达到当初声明的界限，就会调用一次所有通过register_tick_function注册的函数，并计数清零。</p>
<p>与当初自己设想的实现相比，PHP内核对ticks机制的实现满足了功能单一原则和松耦合原则。将ticks机制作为一个中间代码添加到整个中间代码的执行体系中，包括状态的转移，函数的切换这些都是直接使用原有的机制。</p>
<h2>ticks机制的应用场景</h2>
<p>手册上说：Ticks 很适合用来做调试，以及实现简单的多任务，后台 I/O 和很多其它任务。</p>
<p>在调试过程中，对于定位一段特定代码中速度慢的语句比较有用，我们可以每执行两条低级语句就记录一次时间。虽然这个过程也可以用其它方法完成，但用 tick  更方便也更容易实现。</p>
<p>PCNTL也使用ticks机制来作为信号处理机制（signal handle callback  mechanism），可以最小程度地降低处理异步事件时的负载。这里的关键在于PCNTL扩展的模块初始化函数（PHP_MINIT_FUNCTION(pcntl)）。在此模块做模块初始化时，它会调用：  php_add_tick_function(pcntl_signal_dispatch);将pcntl的分发执行函数添加到ticks机制的调用函数中去，从而当ticks触发时就会调用PCNTL扩展函数中指定的所有方法。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2013/02/php-ticks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP脚本运行超时管理机制</title>
		<link>https://www.phppan.com/2012/06/php-timeout/</link>
		<comments>https://www.phppan.com/2012/06/php-timeout/#comments</comments>
		<pubDate>Mon, 25 Jun 2012 00:44:51 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[timeout]]></category>
		<category><![CDATA[安全模式]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>
		<category><![CDATA[超时]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1708</guid>
		<description><![CDATA[PHP脚本运行超时管理机制 在我们平常的开发中，也许曾经都遇到过PHP脚本运行超时的情况，此时PHP会显示错误 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2>PHP脚本运行超时管理机制</h2>
<p>在我们平常的开发中，也许曾经都遇到过PHP脚本运行超时的情况，此时PHP会显示错误说： “Fatal error: Maximum execution  time of XXX seconds exceeded in XXX”，并终止脚本的运行。当遇到这种情况我们经常会通过使用  set_time_limit（非安全模式），或修改配置文件并重启服务器，或者修改程序减少程序的执行时间，使其在允许的范围之内，以解决此问题。但是，这些都是在应用层上我们可以看到的的表象，在PHP内核中有一套这样的机制支撑这样一个表象。</p>
<p>这是PHP为防止某些业务脚本长时间执行而阻塞其它脚本的处理或耗尽服务器资源，从而实现的脚本运行的超时管理机制。其本质上是PHP通过针对不同的平台实现定时器，依赖运行时的超时全局变量（EG(timeout_seconds)）管理并控制定时器的运行。所有对脚本运行时长的管理，包括接口函数和配置文件对于最大运行时长的配置，最终都是通过管理超时全局变量并重启定时器来实现的。</p>
<p></p>
<h3>初始化和超时配置项</h3>
<p>在PHP内核的核心层文件/main/main.c文件中，定义了PHP的核心配置项以及每个配置项对应的on_modify方法。在模块初始化（php_module_startup）时，PHP内核会调用ini配置的注册函数，将定义的核心配置项添加到ini配置的指令集中，并且会调用每个配置项对应的on_modify方法。</p>
<p>用于定义脚本运行最长时间的max_execution_time配置项也是这些核心配置项的一员，它的默认值为30秒，对应的on_modify方法是OnUpdateTimeout。当注册这些核心配置项时，max_execution_time的on_modify方法将被调用，此时配置项的值将传递给超时全局变量:EG(timeout_seconds)，并通过zend_set_timeout方法启动定时器。</p>
<p>针对WIN平台和类unix平台，PHP内核实现了不同的定时器。  Win32平台的定时器是在WM_TIME的基础上封装了一个计时器。通过创建一个独立线程控制计时器，并创建一个消息环，WaitForSingleObject用来阻塞zend_init_timeout_thread  返回。当接收到WM_REGISTER_ZEND_TIMEOUT时开始计时，实际上此时计时的任务是SetTimer(timeout_window, wParam,  lParam*1000, NULL); 系统会在 seconds *　1000 后发个 WM_TIMER，这个时候就结束计时，中间可以被  WM_UNREGISTER_ZEND_TIMEOUT 打断。</p>
<p>类unix平台使用Linux的API函数setitimer，指定SIGPROF信号为超时处理信号，对应超时处理函数zend_timeout，当发生超时时，会发送此信号并触发函数zend_timeout显示错误信息并中止程序。</p>
<p>如果需要取消定时器，Win平台通过PostThreadMessage发送WM_UNREGISTER_ZEND_TIMEOUT给线程即可，类unix平台会重置定时器的时长为0。</p>
<p></p>
<h3>超时管理</h3>
<p>超时机制的管理非常灵活，有三种修改运行时长的方法。</p>
<p>1、  修改配置项。默认情况下PHP脚本的最长运行时长为30s。如果需要调整此项，可以通过修改php.ini文件中的max_execution_time项并重启动服务器达到修改最长运行时长的目的。此种方法适用于最开始的默认配置修改，或在其它方法无效的情况下使用。</p>
<p>2、  使用set_time_limit接口函数。此函数的作用是设置脚本最大执行时间。当此函数被调用时，set_time_limit()会从零开始重新启动超时计数器。比如，每一次设置是5秒，待脚本运行4秒后，脚本中又设置了5秒，那么，脚本在超时之前可运行总共时间为10秒。如下脚本示例：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;">    <span style="color: #000000; font-weight: bold;">&lt;?php</span>
    <span style="color: #990000;">set_time_limit</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">5</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$i</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> <span style="color: #000088;">$i</span> <span style="color: #339933;">&lt;</span> <span style="color: #cc66cc;">4</span><span style="color: #339933;">;</span> <span style="color: #000088;">$i</span><span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #990000;">sleep</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$i</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;&lt;br /&gt;&quot;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #990000;">set_time_limit</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">5</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span><span style="color: #000088;">$i</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span><span style="color: #339933;">;</span> <span style="color: #000088;">$i</span> <span style="color: #339933;">&lt;</span> <span style="color: #cc66cc;">4</span><span style="color: #339933;">;</span> <span style="color: #000088;">$i</span><span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #990000;">sleep</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$i</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">&quot;&lt;br /&gt;&quot;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>如上的代码，程序会执行完两个循环，都输出0,1,2,3。如果我们注释掉中间的set_time_limit(5)，程序再运行一次，此时就会在第二个循环输出0后报错。</p>
<p>在安全模式下，无法通过set_time_limit和ini_set重新设置max_execution_time，只有关闭安全模式或改变php.ini中的时间限制才能达到修改此参数的目的。</p>
<p>3、 通过ini_set修改max_execution_time参数。</p>
<p>以上的三种方法，其实现过程基本类似，前一种是在初始化时调用on_modify指针函数。后两种在处理了参数后，调用zend_alter_ini_entry_ex函数，触发on_modify函数。于是，管理超时机制的所有操作最终都汇集到OnUpdateTimeout函数。在此函数中，通过zend_set_timeout重新设置脚本的超时时间。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/06/php-timeout/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>细读PHP的生命周期</title>
		<link>https://www.phppan.com/2012/05/php-life-cycle-2/</link>
		<comments>https://www.phppan.com/2012/05/php-life-cycle-2/#comments</comments>
		<pubDate>Mon, 07 May 2012 11:02:04 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[PHP生命周期]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1670</guid>
		<description><![CDATA[在《Extending and Embedding PHP》中，有一张经典的描述PHP单进程生命周期的图，一直 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>在《Extending and Embedding PHP》中，有一张经典的描述PHP单进程生命周期的图，一直也是按这个图理解其生命周期的，可是当准备一次内核分享时，却表现自己没有什么可以说的，于是就有了今天的这篇文章：细读PHP的生命周期。这里，我们会详细说明在CLI模式下PHP一个生命周期中做了哪些事情。</p>
<p><strong>启动</strong></p>
<p>在调用每个模块的模块初始化前，会有一个初始化的过程，它包括：</p>
<ul>
<li><strong>初始化若干全局变量</strong></li>
</ul>
<p>这里的初始化全局变量大多数情况下是将其设置为NULL，有一些除外，比如设置zuf（zend_utility_functions），以zuf.printf_function  =  php_printf为例，这里的php_printf在zend_startup函数中会被赋值给zend_printf作为全局函数指针使用，而zend_printf函数通常会作为常规字符串输出使用，比如显示程序调用栈的debug_print_backtrace就是使用它打印相关信息。</p>
<ul>
<li><strong>初始化若干常量</strong></li>
</ul>
<p>这里的常量是PHP自己的一些常量，这些常量要么是硬编码在程序中,比如PHP_VERSION，要么是写在配置头文件中，比如PEAR_EXTENSION_DIR，这些是写在config.w32.h文件中。</p>
<ul>
<li><strong>初始化ZEND引擎和核心组件</strong></li>
</ul>
<p>前面提到的zend_startup函数的作用就是初始化ZEND引擎，这里的初始化操作包括内存管理初始化、全局使用的函数指针初始化（如前面所说的zend_printf等），对PHP源文件进行词法分析、语法分析、中间代码执行的函数指针的赋值，初始化若干HashTable（比如函数表，常量表等等），为ini文件解析做准备，为PHP源文件解析做准备，注册内置函数（如strlen、define等），注册标准常量（如E_ALL、TRUE、NULL等）、注册GLOBALS全局变量等。</p>
<ul>
<li><strong>解析php.ini</strong></li>
</ul>
<p>php_init_config函数的作用是读取php.ini文件，设置配置参数，加载zend扩展并注册PHP扩展函数。此函数分为如下几步：初始化参数配置表，调用当前模式下的ini初始化配置，比如CLI模式下，会做如下初始化：</p>
<pre>INI_DEFAULT<span>(</span><span>"report_zend_debug"</span><span>,</span> <span>"0"</span><span>)</span><span>;</span>
INI_DEFAULT<span>(</span><span>"display_errors"</span><span>,</span> <span>"1"</span><span>)</span><span>;</span></pre>
<p>不过在其它模式下却没有这样的初始化操作。接下来会的各种操作都是查找ini文件：</p>
<ol>
<li>判断是否有php_ini_path_override，在CLI模式下可以通过-c参数指定此路径（在php的命令参数中-c表示在指定的路径中查找ini文件）。</li>
<li>如果没有php_ini_path_override，判断php_ini_ignore是否为非空（忽略php.ini配置，这里也就CLI模式下有用，使用-n参数）。</li>
<li>如果不忽略ini配置，则开始处理php_ini_search_path（查找ini文件的路径），这些路径包括CWD(当前路径，不过这种不适用CLI模式)、执行脚本所在目录、环境变量PATH和PHPRC和配置文件中的PHP_CONFIG_FILE_PATH的值。</li>
<li>在准备完查找路径后，PHP会判断现在的ini路径（php_ini_file_name）是否为文件和是否可打开。如果这里ini路径是文件并且可打开，则会使用此文件，  也就是CLI模式下通过-c参数指定的ini文件的优先级是最高的，其次是PHPRC指定的文件，第三是在搜索路径中查找php-%sapi-module-name%.ini文件（如CLI模式下应该是查找php-cli.ini文件），最后才是搜索路径中查找php.ini文件。</li>
</ol>
<ul>
<li><strong>全局操作函数的初始化</strong></li>
</ul>
<p>php_startup_auto_globals函数会初始化在用户空间所使用频率很高的一些全局变量，如：$_GET、$_POST、$_FILES等。这里只是初始化，所调用的zend_register_auto_global函数也只是将这些变量名添加到CG(auto_globals)这个变量表。</p>
<p>php_startup_sapi_content_types函数用来初始化SAPI对于不同类型内容的处理函数，这里的处理函数包括POST数据默认处理函数、默认数据处理函数等。</p>
<ul>
<li><strong>初始化静态构建的模块和共享模块(MINIT)</strong></li>
</ul>
<p>php_register_internal_extensions_func函数用来注册静态构建的模块，也就是默认加载的模块，我们可以将其认为为内置模块。在PHP5.3.0版本中内置的模块包括PHP标准扩展模块（/ext/standard/目录，这里是我们用的最频繁的函数，比如字符串函数，数学函数，数组操作函数等等），日历扩展模块、FTP扩展模块、  session扩展模块等。这些内置模块并不是一成不变的，在不同的PHP模板中，由于不同时间的需求或其它影响因素会导致这些默认加载的模块会变化，比如从代码中我们就可以看到mysql、xml等扩展模块曾经或将来会作为内置模块出现。</p>
<p>模块初始化会执行两个操作： 1. 将这些模块注册到已注册模块列表（module_registry），如果注册的模块已经注册过了，PHP会报Module  XXX already loaded的错误。 1. 将每个模块中包含的函数注册到函数表（ CG(function_table) ），如果函数无法添加，则会报  Unable to register functions, unable to load。</p>
<p>在注册了静态构建的模块后，PHP会注册附加的模块，不同的模式下可以加载不同的模块集，比如在CLI模式下是没有这些附加的模块的。</p>
<p>在内置模块和附加模块后，接下来是注册通过共享对象（比如DLL）和php.ini文件灵活配置的扩展。</p>
<p>在所有的模块都注册后，PHP会马上执行模块初始化操作（zend_startup_modules）。它的整个过程就是依次遍历每个模块，调用每个模块的模块初始化函数，也就是在本小节前面所说的用宏PHP_MINIT_FUNCTION包含的内容。</p>
<ul>
<li><strong>禁用函数和类</strong></li>
</ul>
<p>php_disable_functions函数用来禁用PHP的一些函数。这些被禁用的函数来自PHP的配置文件的disable_functions变量。其禁用的过程是调用zend_disable_function函数将指定的函数名从CG(function_table)函数表中删除。</p>
<p>php_disable_classes函数用来禁用PHP的一些类。这些被禁用的类来自PHP的配置文件的disable_classes变量。其禁用的过程是调用zend_disable_class函数将指定的类名从CG(class_table)类表中删除。</p>
<p><strong>ACTIVATION</strong></p>
<p>在处理了文件相关的内容，PHP会调用php_request_startup做请求初始化操作。请求初始化操作，除了图中显示的调用每个模块的请求初始化函数外，还做了较多的其它工作，其主要内容如下：</p>
<ul>
<li><strong>激活ZEND引擎</strong></li>
</ul>
<p>gc_reset函数用来重置垃圾收集机制，当然这是在PHP5.3之后才有的。</p>
<p>init_compiler函数用来初始化编译器，比如将编译过程中在放opcode的数组清空，准备编译时用来的数据结构等等。</p>
<p>init_executor函数用来初始化中间代码执行过程。在编译过程中，函数列表、类列表等都存放在编译时的全局变量中，在准备执行过程时，会将这些列表赋值给执行的全局变量中，如：EG(function_table)  = CG(function_table);  中间代码执行是在PHP的执行虚拟栈中，初始化时这些栈等都会一起被初始化。除了栈，还有存放变量的符号表(EG(symbol_table))会被初始化为50个元素的hashtable，存放对象的EG(objects_store)被初始化了1024个元素。  PHP的执行环境除了上面的一些变量外，还有错误处理，异常处理等等，这些都是在这里被初始化的。通过php.ini配置的zend_extensions也是在这里被遍历调用activate函数。</p>
<ul>
<li><strong>激活SAPI</strong></li>
</ul>
<p>sapi_activate函数用来初始化SG(sapi_headers)和SG(request_info)，并且针对HTTP请求的方法设置一些内容，比如当请求方法为HEAD时，设置SG(request_info).headers_only=1；此函数最重要的一个操作是处理请求的数据，其最终都会调用sapi_module.default_post_reader。而sapi_module.default_post_reader在前面的模块初始化是通过php_startup_sapi_content_types函数注册了默认处理函数为main/php_content_types.c文件中php_default_post_reader函数。此函数会将POST的原始数据写入$HTTP_RAW_POST_DATA变量。</p>
<p>在处理了post数据后，PHP会通过sapi_module.read_cookies读取cookie的值，在CLI模式下，此函数的实现为sapi_cli_read_cookies，而在函数体中却只有一个return  NULL;</p>
<p>如果当前模式下有设置activate函数，则运行此函数，激活SAPI，在CLI模式下此函数指针被设置为NULL。</p>
<ul>
<li><strong>环境初始化</strong></li>
</ul>
<p>这里的环境初始化是指在用户空间中需要用到的一些环境变量初始化，这里的环境包括服务器环境、请求数据环境等。实际到我们用到的变量，就是$_POST、$_GET、$_COOKIE、$_SERVER、$_ENV、$_FILES。和sapi_module.default_post_reader一样，sapi_module.treat_data的值也是在模块初始化时，通过php_startup_sapi_content_types函数注册了默认数据处理函数为main/php_variables.c文件中php_default_treat_data函数。</p>
<p>以$_COOKIE为例，php_default_treat_data函数会对依据分隔符，将所有的cookie拆分并赋值给对应的变量。</p>
<ul>
<li><strong>模块请求初始化</strong></li>
</ul>
<p>PHP通过zend_activate_modules函数实现模块的请求初始化，也就是我们在图中看到Call each extension&#8217;s  RINIT。此函数通过遍历注册在module_registry变量中的所有模块，调用其RINIT方法实现模块的请求初始化操作。</p>
<p><strong>运行</strong></p>
<p>php_execute_script函数包含了运行PHP脚本的全部过程。</p>
<p>当一个PHP文件需要解析执行时，它可能会需要执行三个文件，其中包括一个前置执行文件、当前需要执行的主文件和一个后置执行文件。非当前的两个文件可以在php.ini文件通过auto_prepend_file参数和auto_append_file参数设置。如果将这两个参数设置为空，则禁用对应的执行文件。</p>
<p>对于需要解析执行的文件，通过zend_compile_file（compile_file函数）做词法分析、语法分析和中间代码生成操作，返回此文件的所有中间代码。如果解析的文件有生成有效的中间代码，则调用zend_execute（execute函数）执行中间代码。如果在执行过程中出现异常并且用户有定义对这些异常的处理，则调用这些异常处理函数。在所有的操作都处理完后，PHP通过EG(return_value_ptr_ptr)返回结果。</p>
<p><strong>DEACTIVATION</strong></p>
<p>PHP关闭请求的过程是一个若干个关闭操作的集合，这个集合存在于php_request_shutdown函数中。这个集合包括如下内容：</p>
<ol>
<li>调用所有通过register_shutdown_function()注册的函数。这些在关闭时调用的函数是在用户空间添加进来的。一个简单的例子，我们可以在脚本出错时调用一个统一的函数，给用户一个友好一些的页面，这个有点类似于网页中的404页面。</li>
<li>执行所有可用的__destruct函数。这里的析构函数包括在对象池（EG(objects_store）中的所有对象的析构函数以及EG(symbol_table)中各个元素的析构方法。</li>
<li>将所有的输出刷出去。</li>
<li>发送HTTP应答头。这也是一个输出字符串的过程，只是这个字符串可能符合某些规范。</li>
<li>遍历每个模块的关闭请求方法，执行模块的请求关闭操作，这就是我们在图中看到的Call each extension&#8217;s RSHUTDOWN。</li>
<li>销毁全局变量表（PG(http_globals)）的变量。</li>
<li>通过zend_deactivate函数，关闭词法分析器、语法分析器和中间代码执行器。</li>
<li>调用每个扩展的post-RSHUTDOWN函数。只是基本每个扩展的post_deactivate_func函数指针都是NULL。</li>
<li>关闭SAPI，通过sapi_deactivate销毁SG(sapi_headers)、SG(request_info)等的内容。</li>
<li>关闭流的包装器、关闭流的过滤器。</li>
<li>关闭内存管理。</li>
<li>重新设置最大执行时间</li>
</ol>
<p><strong>结束</strong></p>
<p>最终到了要收尾的地方了。</p>
<ul>
<li><strong>flush</strong></li>
</ul>
<p>sapi_flush将最后的内容刷新出去。其调用的是sapi_module.flush，在CLI模式下等价于fflush函数。</p>
<ul>
<li><strong>关闭ZEND引擎</strong></li>
</ul>
<p>zend_shutdown将关闭ZEND引擎。</p>
<p>此时对应图中的流程，我们应该是执行每个模块的关闭模块操作。在这里只有一个zend_hash_graceful_reverse_destroy函数将module_registry销毁了。当然，它最终也是调用了关闭模块的方法的，其根源在于在初始化module_registry时就设置了这个hash表析构时调用ZEND_MODULE_DTOR宏。而ZEND_MODULE_DTOR宏对应的是module_destructor函数。在此函数中会调用模块的module_shutdown_func方法，即PHP_RSHUTDOWN_FUNCTION宏产生的那个函数。</p>
<p>在关闭所有的模块后，PHP继续销毁全局函数表，销毁全局类表、销售全局变量表等。通过zend_shutdown_extensions遍历zend_extensions所有元素，调用每个扩展的shutdown函数。</p>
<p>PS: 最近有同学问到TIPI项目的进度问题，主编说:在七月份会有一次版本发布，更多的内容可以查看项目的github。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/05/php-life-cycle-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP文件上传进度的实现原理</title>
		<link>https://www.phppan.com/2012/04/php-upload-progress/</link>
		<comments>https://www.phppan.com/2012/04/php-upload-progress/#comments</comments>
		<pubDate>Fri, 20 Apr 2012 00:51:38 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1648</guid>
		<description><![CDATA[在PHP5.4之前，如果我们要获取文件上传的进度，可以选择的方案有Flash或使用PHP的uploadprog [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>在PHP5.4之前，如果我们要获取文件上传的进度，可以选择的方案有Flash或使用PHP的<a href="http://pecl.php.net/package/uploadprogress" target="_blank">uploadprogress扩展</a>。这两种方案存在本质的区别，Flash的上传进度是客户端上传的进度，它是基于本地OS的网络传输，最终其本质上也是一次HTTP的multipart/form-data编码的POST请求；uploadprogress扩展需要依靠JS获取服务器提供的进度，这里的进度是服务器接收的文件进度。</p>
<p>而在PHP5.4之后，我们可以在不添加扩展的情况下，从session数据中获取了文件上传的进度。uploadprogress扩展和PHP5.4的session扩展都能获取上传的进度，其是否有相同的地方呢？</p>
<p>我们先来看uploadprogress扩展，下载源码包，解圧，直接打开文件，我们可以在example中找到一个简单的示例。在info.php文件中，uploadprogress_get_info函数用来获取上传文件进度。upploadprogress.c文件存储了扩展的实现过程。uploadprogress扩展实现的关键在于其模块寝化函数：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">PHP_MINIT_FUNCTION<span style="color: #009900;">&#40;</span>uploadprogress<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
	REGISTER_INI_ENTRIES<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	php_rfc1867_callback <span style="color: #339933;">=</span> uploadprogress_php_rfc1867_file<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> SUCCESS<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>此函数的核心就是设置php_rfc1867_callback为uploadprogress_php_rfc1867_file。<br />
设置这个函数指针有什么用呢？<br />
在前面的文章<a href="http://www.phppan.com/2012/03/files-type/" target="_blank">PHP内核中文件上传类型的获取过程</a>中我们了解到PHP处理POST请求的函数是SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)(main/rfc1867.c)。在这里， 我们发现了若干个php_rfc1867_callback的调用，从调用的第一个参数来看，它可以分为六个事件，或者说有六个回调更新点。</p>
<p>如果此时我们查看PHP5.4的的session扩展的实现文件session.c时，搜索php_rfc1867_callback，你会发现在模块初始化函数中也有与扩展类似的赋值操作：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;">	php_rfc1867_callback <span style="color: #339933;">=</span> php_session_rfc1867_callback<span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>同样，在php_session_rfc1867_callback函数中有与uploadprogress同样的六个事件的处理，这六个事件相当于六个钩子程序，分别对应POST请求的处理的六个不同的位置，在PHP5.4中他们的作用分别是：</p>
<ul>
<li>
1、MULTIPART_EVENT_START 在处理所有的请求实体之前，初始化上传进度信息，比用于记录上传进度相关信息的progress结构体信息（如content-length）
</li>
<li>
2、MULTIPART_EVENT_FORMDATA 对于每个multipart包含的控制，执行此步初始化操作，以此之前会解析Content-Disposition相关属性，并初始化progress的其它信息，如session_id,以及整个上传活动的key，这里表示整个上传进度准备好了。
</li>
<li>
3、MULTIPART_EVENT_FILE_START 开始处理上传的文件信息，如果progress的data不存在，则会创建此结构，并初始化session中存储的对于此次文件上传的start_time、content_length、bytes_processed、files等信息。然后处理单个文件的上传属性，如field_name、tmp_name等。对于tmp_name等字段这里是执行初始化操作。这一步的时候获取session 的值才会开始有上传进度的相关信息。
</li>
<li>
4、MULTIPART_EVENT_FILE_DATA 更新上传文件的长度，在一堆的文件相关信息检测和临时文件写入之前，也是在将数据写入到$_FILES之前。
</li>
<li>
5、MULTIPART_EVENT_FILE_END 单个文件上传结束，此时会更新这个文件相关的一些信息，比如error, tmp_name，tmp_name字段在start时是null。当然这里还有针对当前文件的done字段的更新。
</li>
<li>
6、MULTIPART_EVENT_END 更新session数组的最后的一些结信息 比如done字段 并清空progress的信息，
</li>
</ul>
<p>这里的六个事件是相同的，而uploadprogress扩展和PHP5.4的session扩展在事件处理过程中中间存储结构和最后的返回内容与方式上存在一些差异。uploadprogress扩展的存储结构为一个按照扩展制定的规则生成的临时文件，最后是通过扩展函数uploadprogress_get_info返回上传进度的数组。PHP5.4的存储结构为SESSION的存储方式，或者是文件，或者是memcache，这个按session的设置来，其最终是通过$_SESSION返回相关数组。</p>
<p>除了uploadprogress扩展外，APC也以设置php_rfc1867_callback = apc_rfc1867_progress，提供了类似的解决方案，启动此功能需要在php.ini中设置apc.rfc1867项为启用，并且在表单中加一个隐藏域 APC_UPLOAD_PROGRESS，这个域的值可以随机生成一个hash，以确定此次上传操作的唯一性。通过Ajax调用服务端显示进度的接口，在接口中通过apc_fetch函数获取APC缓存的文件上传进度。比如print_r(apc_fetch(&#8220;upload_$_POST[APC_UPLOAD_PROGRESS]&#8220;));可以得到如下结果：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;"><span style="color: #990000;">Array</span>
<span style="color: #009900;">&#40;</span>
    <span style="color: #009900;">&#91;</span>total<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> <span style="color: #cc66cc;">1142543</span>
    <span style="color: #009900;">&#91;</span><span style="color: #990000;">current</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> <span style="color: #cc66cc;">1142543</span>
    <span style="color: #009900;">&#91;</span>rate<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> <span style="color:#800080;">1828068.8</span>
    <span style="color: #009900;">&#91;</span>filename<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> test
    <span style="color: #009900;">&#91;</span>name<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">file</span>
    <span style="color: #009900;">&#91;</span>temp_filename<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> <span style="color: #339933;">/</span>tmp<span style="color: #339933;">/</span>php8F
    <span style="color: #009900;">&#91;</span>cancel_upload<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> <span style="color: #cc66cc;">0</span>
    <span style="color: #009900;">&#91;</span>done<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> <span style="color: #cc66cc;">1</span>
<span style="color: #009900;">&#41;</span></pre></td></tr></table></div>

<p>apc.rfc1867相关更加详细的内容猛击 <a href="http://cn2.php.net/manual/en/apc.configuration.php#ini.apc.rfc1867" target="_blank">APC Runtime Configuration</a></p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/04/php-upload-progress/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/03/php-string-concat/</link>
		<comments>https://www.phppan.com/2012/03/php-string-concat/#comments</comments>
		<pubDate>Mon, 26 Mar 2012 01:12:12 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP内核]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>
		<category><![CDATA[字符串]]></category>
		<category><![CDATA[深入理解PHP内核]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1630</guid>
		<description><![CDATA[上周和刘志强同学讨论字符串的连接操作： 一般情况下我们用点号做字符串的连接操作，但是如果在某个长字符串中放一个 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>上周和<a href="http://liuzhiqiangruc.iteye.com/">刘志强同学</a>讨论字符串的连接操作：<br />
一般情况下我们用点号做字符串的连接操作，但是如果在某个长字符串中放一个变量，通常我们会采用在字符串中直接写入一个变量的方式来实现</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$var</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">10</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$str</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;test string begin &quot;</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$var</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot; end&quot;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">//或</span>
<span style="color: #000088;">$var</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">10</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$str</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;test string begin <span style="color: #006699; font-weight: bold;">$var</span> end&quot;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>这二者有什么区别呢？</p>
<p>以VLD扩展直接查看这两段代码生成的中间代码：<br />
点号连接：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="shell" style="font-family:monospace;">number of ops:  7
compiled vars:  !0 = $var, !1 = $str
line     # *  op         ext  return  operands
------------------------------------------------
   2     0  &gt;   EXT_STMT
         1      ASSIGN                  !0, 10
   3     2      EXT_STMT
         3      CONCAT          ~1      'test+string+begin+', !0
         4      CONCAT          ~2      ~1, '+end'
         5      ASSIGN                  !1, ~2
         6    &gt; RETURN                  1</pre></td></tr></table></div>

<p>直接在字符串中插入变量：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="shell" style="font-family:monospace;">number of ops:  8
compiled vars:  !0 = $var, !1 = $str
line     # *  op             ext  return  operands
----------------------------------------------------
   2     0  &gt;   EXT_STMT
         1      ASSIGN                      !0, 10
   3     2      EXT_STMT
         3      ADD_STRING          ~1      'test+string+begin+'
         4      ADD_VAR             ~1      ~1, !0
         5      ADD_STRING          ~1      ~1, '+end'
         6      ASSIGN                      !1, ~1
         7    &gt; RETURN                      1</pre></td></tr></table></div>

<p>对比这段生成的中间码，其原理完全不一样：</p>
<p>点号是典型的连接操作（当然，它本来就是连接操作），<br />
当使用多个点号是，每两个点号的结果都会使用一个临时变量存储起来，并作为下一个操作的一个操作数。如在我们的示例中，首先是执行第一个连接操作，将“test string begin ”和$var连接起来，得到“test string begin 10”，然后再执行第二个连接操作，将上一个操作得到的结果“test string begin 10”和&#8221; end&#8221;连接起来，并将结果存储在另一个临时变量，最后将第二个连接操作的结果赋值给$str。</p>
<p>连接操作对应的opcode为ZEND_CONCAT，对于所给的两个操作数，其最终通过concat_function函数将两个字符串连接起来，如果所给的变量的类型不是字符串，则会通过zend_make_printable_zval将其转换成字符串。concat_function函数会根据两个字符串的长度重新分配内存，并执行两次拷贝操作，将两个字符串拷贝到新的内存空间。<br />
这里针对两个字符串相同的情况有一个特殊处理。<br />
如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>result<span style="color: #339933;">==</span>op1<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>	<span style="color: #808080; font-style: italic;">/* special case, perform operations on result */</span>
	uint res_len <span style="color: #339933;">=</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op1<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op2<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> erealloc<span style="color: #009900;">&#40;</span>Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> res_len<span style="color: #339933;">+</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #000066;">memcpy</span><span style="color: #009900;">&#40;</span>Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span>Z_STRLEN_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> Z_STRVAL_P<span style="color: #009900;">&#40;</span>op2<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op2<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span>res_len<span style="color: #009900;">&#93;</span><span style="color: #339933;">=</span><span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
	Z_STRLEN_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> res_len<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
	Z_STRLEN_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op1<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op2<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">char</span> <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> emalloc<span style="color: #009900;">&#40;</span>Z_STRLEN_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000066;">memcpy</span><span style="color: #009900;">&#40;</span>Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> Z_STRVAL_P<span style="color: #009900;">&#40;</span>op1<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op1<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000066;">memcpy</span><span style="color: #009900;">&#40;</span>Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span>Z_STRLEN_P<span style="color: #009900;">&#40;</span>op1<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> Z_STRVAL_P<span style="color: #009900;">&#40;</span>op2<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op2<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span>Z_STRLEN_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
	Z_TYPE_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> IS_STRING<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>示例执行了两次连接操作，则执行了两次内存分配操作和四次拷贝操作。</p>
<p>而直接在字符串中插入变量，其所有的操作都是添加操作，将字符串添加到返回值，将变量添加到返回值，<br />
所有的结果返回都是在一个临时变量中，如我们的示例，首先会将&#8221;test string begin &#8220;添加到临时变量，然后将临时变量和$var变量添加到临时变量，之后将临时变量和&#8221; end&#8221;添加到临时变量，最后将此此时变量赋值给$str。这里添加将字符串添加到临时变量，其对应的opcode为ZEND_ADD_STRING，将变量添加到临时变量，其对应的opcode为ZEND_ADD_VAR，虽然这两个操作的opcode不同，但其最终调用都是add_string_to_string，他们所不同的调用此函数的第三个参数，一个是操作码存储的ZVAL变量，一个是通过变更列表获取的ZVAL变量。<br />
其调用结构如下：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">// 添加字符串</span>
zval <span style="color: #339933;">*</span>str <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>EX_T<span style="color: #009900;">&#40;</span>opline<span style="color: #339933;">-&gt;</span>result.<span style="color: #202020;">u</span>.<span style="color: #202020;">var</span><span style="color: #009900;">&#41;</span>.<span style="color: #202020;">tmp_var</span><span style="color: #339933;">;</span>
add_string_to_string<span style="color: #009900;">&#40;</span>str<span style="color: #339933;">,</span> str<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: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">//添加变量</span>
zval <span style="color: #339933;">*</span>str <span style="color: #339933;">=</span> <span style="color: #339933;">&amp;</span>EX_T<span style="color: #009900;">&#40;</span>opline<span style="color: #339933;">-&gt;</span>result.<span style="color: #202020;">u</span>.<span style="color: #202020;">var</span><span style="color: #009900;">&#41;</span>.<span style="color: #202020;">tmp_var</span><span style="color: #339933;">;</span>
zval <span style="color: #339933;">*</span>var <span style="color: #339933;">=</span> _get_zval_ptr_tmp<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>opline<span style="color: #339933;">-&gt;</span>op2<span style="color: #339933;">,</span> EX<span style="color: #009900;">&#40;</span>Ts<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>free_op2 TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
add_string_to_string<span style="color: #009900;">&#40;</span>str<span style="color: #339933;">,</span> str<span style="color: #339933;">,</span> var<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>在添加变量时，如果添加的变量不是字符串，会通过zend_make_printable_zval将变量转换成字符串输出，如数组会转换成Array。<br />
add_string_to_string的实现在Zend/zend_operators.c文件中：</p>

<div class="wp_syntax"><table><tr><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* must support result==op1 */</span>
ZEND_API <span style="color: #993333;">int</span> add_string_to_string<span style="color: #009900;">&#40;</span>zval <span style="color: #339933;">*</span>result<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> zval <span style="color: #339933;">*</span>op1<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> zval <span style="color: #339933;">*</span>op2<span style="color: #009900;">&#41;</span> <span style="color: #808080; font-style: italic;">/* {{{ */</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span> length <span style="color: #339933;">=</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op1<span style="color: #009900;">&#41;</span> <span style="color: #339933;">+</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op2<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">char</span> <span style="color: #339933;">*</span><span style="color: #009900;">&#41;</span> erealloc<span style="color: #009900;">&#40;</span>Z_STRVAL_P<span style="color: #009900;">&#40;</span>op1<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> length<span style="color: #339933;">+</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #000066;">memcpy</span><span style="color: #009900;">&#40;</span>Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span>Z_STRLEN_P<span style="color: #009900;">&#40;</span>op1<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> Z_STRVAL_P<span style="color: #009900;">&#40;</span>op2<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> Z_STRLEN_P<span style="color: #009900;">&#40;</span>op2<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	Z_STRVAL_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span>length<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
	Z_STRLEN_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> length<span style="color: #339933;">;</span>
	Z_TYPE_P<span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> IS_STRING<span style="color: #339933;">;</span>
	<span style="color: #b1b100;">return</span> SUCCESS<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* }}} */</span></pre></td></tr></table></div>

<p>add_string_to_string函数的实现过程是针对即将生成的字符串的大小重新通过PHP内核的内存管理扩展内存空间（如果当前空间后续的内存够用，则天下太平，如果空间不够，则重新分配空间并执行拷贝操作），并将新的字符串复制到原始字串后面内存空间的过程。<br />
我们的示例执行了三次添加操作，也就执行了三次内存扩展操作和三次拷贝操作。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2012/03/php-string-concat/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>
	</channel>
</rss>
