<?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="http://www.laruence.com/tag/php%e5%ba%94%e7%94%a8/feed" rel="self" type="application/rss+xml" />
	<link>http://www.laruence.com</link>
	<description>PHP语言, PHP扩展, Zend引擎相关的研究,技术,新闻分享 - 左手代码 右手诗</description>
	<lastBuildDate>Wed, 08 Feb 2012 05:12:49 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>深入理解PHP原理之变量生命期(一)</title>
		<link>http://www.laruence.com/2009/12/26/1198.html</link>
		<comments>http://www.laruence.com/2009/12/26/1198.html#comments</comments>
		<pubDate>Sat, 26 Dec 2009 13:11:44 +0000</pubDate>
		<dc:creator>雪候鸟</dc:creator>
				<category><![CDATA[PHP源码分析]]></category>
		<category><![CDATA[GET]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP应用]]></category>
		<category><![CDATA[SAPI]]></category>
		<category><![CDATA[variable]]></category>

		<guid isPermaLink="false">http://www.laruence.com/?p=1198</guid>
		<description><![CDATA[对于PHP的中的数据来源, 不外乎有俩种:
<coolcode lang="sh" linenum="off">
1. 来自代码中
2. 来自外部(GET/POST/DB)
</coolcode>
	
	对于代码中的变量(也就是直接量)来说, 变量分配/赋值在编译期, 活跃在执行器, 在请求关闭期被销毁.对于这些变量来说, 使用APC进行Opcode缓存, 则会缓存这部分变量的值. 

	而对于来自外部的变量, 变量分配/赋值在编译器后, 执行期前, 在请求关闭期被销毁,对于这些变量来说, 使用APC进行OpCode缓存, 是不会被缓存的.

	今天就着重关注下外部变量的一个部分,GET来的数据的整个生命周期....]]></description>
			<content:encoded><![CDATA[<div class="copyright" >
<ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;">
<li>作者: <a href="http://www.laruence.com" >Laruence</a>(<a href="http://www.twitter.com/laruence"  target="meme"  title="Twitter" ><img src="/images/ico-twitter.png" /></a> <a href="http://t.sina.com/laruence"  target="meme"  title="新浪微博" ><img src="/images/ico-sina.png" /></a> <a href="http://fusion.google.com/add?feedurl=http://www.laruence.com/feed"  target="meme"  title="Google阅读器" ><img src="/images/ico-google.png" /></a> <a href="mailto:laruence@yahoo.com.cn"  target="meme"  title="邮件" ><img src="/images/ico-mail.png" /></a>)</li>
<li>本文地址: <a href="http://www.laruence.com/2009/12/26/1198.html"  title="Permanet Link to 深入理解PHP原理之变量生命期(一)" >http://www.laruence.com/2009/12/26/1198.html</a></li>
</li>
<li>转载请注明出处 </li>
</ul></div>
<p>
	对于PHP的中的数据来源, 不外乎有俩种:</p>
<pre name="code"  class="sh_sh"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
1. 来自代码中
2. 来自外部(GET/POST/DB)
</pre>
<p>	对于代码中的变量(也就是直接量)来说, 变量分配/赋值在编译期, 活跃在执行器, 在请求关闭期被销毁.对于这些变量来说, 使用APC进行Opcode缓存, 则会缓存这部分变量的值. </p>
<p>	而对于来自外部的变量, 变量分配/赋值在编译器后, 执行期前, 在请求关闭期被销毁,对于这些变量来说, 使用APC进行OpCode缓存, 是不会被缓存的.</p>
<p>	今天就着重关注下外部变量的一个部分,GET来的数据的整个生命周期.</p>
<p>	假设, 有如下请求到来:</p>
<pre name="code"  class="sh_sh"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
	GET /index.php?name=laruence&amp;career[]=yahoo&amp;career[]=baidu
</pre>
<p>	而, 在index.php中:</p>
<pre name="code"  class="sh_php"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
&lt;?php
	$name 	= $_GET['name'];
	$career = $_GET['career']; //array
</pre>
<p>	我们知道, 在最后的执行期, $_GET数组必然包含如下片段:</p>
<pre name="code"  class="sh_php"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
	$_GET = array(
		'name'   =&gt; 'laruence',
		'career' =&gt; array(
			'yahoo', 'baidu',
		),
	)
</pre>
<p>	那么, 我们今天就重点关注下, Query String是如何构建成_GET数组的(关于GET变量的生成, 请一并阅读我之前的文章: &#8220;<a href="http://www.laruence.com/2008/11/07/581.html"  target="_blank" >PHP的GET/POST等大变量生成过程</a>&#8220;): </p>
<p>	在请求到来时刻,php_request_startup(定义在main.c)被调用,来做初始化现场. 在这个过程中包括设置超时值,调用各个模块的请求初始化函数. 当然也包括我们关心的, 创建变量环境.</p>
<p>	php_hash_environment根据php.ini中的variables_order来依次初始化各个预定义大变量, 那么对于$_GET来说:</p>
<pre name="code"  class="sh_c"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
...
case 'g':
case 'G':
	if (!_gpc_flags[2]) {
		sapi_module.treat_data(PARSE_GET, NULL, NULL TSRMLS_CC);
		_gpc_flags[2] = 1;
		if (PG(register_globals)) {
			php_autoglobal_merge(&amp;EG(symbol_table),
				Z_ARRVAL_P(PG(http_globals)[TRACK_VARS_GET]) TSRMLS_CC);
		}
	}
break;
...
</pre>
<p>	大体可以看出,这段逻辑,首先通过treat_data来生成变量hash(PG(http_globals)[TRACK_VARS_GET]), 如果打开了auto_register_globals,则再把$_GET数组中的变量加入到符号表中.</p>
<p>	treat_data是属于sapi_module_struct中的一个成员: </p>
<pre name="code"  class="sh_sh"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
 注意:	本文基于apache2handler方式的sapi, 这个启动过程和之前的文章sapi
原理中的启动过程略有不同, php5通过注册apache2的ap_hook_post_config挂钩,
在apache server启动的时候启动php(php_apache_server_startup,定义在
sapi/apache2hander/sapi_apache2.c中), 在这个函数中调用sapi_startup启动sapi,
继而通过调用php_apache2_startup来注册sapi module struct,
然后调用php_module_startup来初始化PHP, 其中又会初始化ZEND引擎,
以及填充zend_module_struct中的treat_data成员(通过php_startup_sapi_content_types)
为php_default_treat_data
</pre>
<p>	现在回过头来继续看treat_data(也就是php_default_treat_data):</p>
<pre name="code"  class="sh_c"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
....
if (arg == PARSE_GET) {     /* GET data */
	c_var = SG(request_info).query_string;
	if (c_var &amp;&amp; *c_var) {
		res = (char *) estrdup(c_var);
		free_buffer = 1;
	} else {
		free_buffer = 0;
	}
} else if (arg == PARSE_COOKIE) {       /* Cookie data */
....
</pre>
<p>	在上面的逻辑中, 给res复制为query_string, SG(request_info)是一个代表了当前请求信息的结构体, 其中query_string是在php_apache_request_ctor中通过复制apache的reqeust_rec结构体中的args而来的. </p>
<p>	对于本文的例子来说, 此时res即为&#8221;name=laruence&#038;career[]=yahoo&#038;career[]=baidu&#8221;, </p>
<p>	继续在treat_data中, 随后的逻辑是:</p>
<pre name="code"  class="sh_c"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
var = php_strtok_r(res, separator, &amp;strtok_buf);
...
while (var) {
	val = strchr(var, '=');

	if (arg == PARSE_COOKIE) {
		/* Remove leading spaces from cookie names,
			needed for multi-cookie header where ; can be followed by a space */
		while (isspace(*var)) {
			var++;
		}
		if (var == val || *var == '\0') {
			goto next_cookie;
		}
	}

	if (val) { /* have a value */
		int val_len;
		unsigned int new_val_len;

		*val++ = '\0';
		php_url_decode(var, strlen(var));
		val_len = php_url_decode(val, strlen(val));
		val = estrndup(val, val_len);
		if (sapi_module.input_filter(arg, var, &amp;val, val_len, &amp;new_val_len TSRMLS_CC)) {
			php_register_variable_safe(var, val, new_val_len, array_ptr TSRMLS_CC);
		}
		efree(val);
	} else {
...
</pre>
<p>	首先, 通过php_strtok_r把res根据&#8221;&#038;&#8221;分割成一个一个的&#8221;key=value&#8221;段, 接下来分别为var和val复制为key和value, 注意到这个过程中会分别对var和val做php_url_decode.</p>
<p>	最后通过php_register_variable_safe, 给array_ptr(此时指向PG(http_globals)[TRACK_VARS_GET], 也就是$_GET)添加一个名为var值为val的成员.</p>
<p>	到了这一步, 我们的$_GET数组中, 就包含了如下的成员:</p>
<pre name="code"  class="sh_php"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
'name'   =&gt; 'laruence',
'career' =&gt; array(
	'yahoo', 'baidu',
),
</pre>
<p>	未完待续(变量的销毁过程)&#8230;<br/>
<script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_sh.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_sh.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_php.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_php.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_c.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_sh.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_c.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_c.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_php.js" ></script></p>
<hr/><h2>Comments</h2><ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2009/12/26</a>, Robin writes: Hi，麻烦问问，如果想去掉 "[]"，改成
name=laruence&amp;career=yahoo&amp;career=baidu
$_GET数组还要不变，怎么改？

谢谢了。</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2009/12/26</a>, <a href="http://www.laruence.com"  rel="external nofollow"  class="url" >雪候鸟</a> writes: @robin 这样的话,最终career的值为baidu</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2009/12/28</a>, <a href="www.phppan.com"  rel="external nofollow"  class="url" >phppan</a> writes: 在window下用vc看也比较舒服
抚琴居主人有一篇文章有相关介绍
http://www.yanbin.org/blog/commentary-on-php-chap1_1-prep.html</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2009/12/28</a>, <a href="http://www.metsky.com"  rel="external nofollow"  class="url" >天缘</a> writes: 还没真的没深究过这些源码，佩服博主认真的研究</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2009/12/28</a>, phpcoder writes: 强悍的博主, 还有点不明白就是数组是怎么parse来的呢?</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2009/12/28</a>, <a href="http://www.laruence.com"  rel="external nofollow"  class="url" >雪候鸟</a> writes: @phpcoder 呵呵,就是简单的字符串parse了, 遇到"[]", 就知道是个数组..</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2009/12/30</a>, eve writes: 楼主，求交往</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2010/03/25</a>, Anonymous writes: 怎么没有了，写完吧，大虾</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2010/06/22</a>, 库从志 writes: 博主 写得很深入</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2010/07/25</a>, <a href="http://blog.haohtml.com/index.php/archives/4786"  rel="external nofollow"  class="url" >深入理解PHP原理之变量生命期(一) | haohtml&#39;s blog</a> writes: [...] 本文地址: http://www.laruence.com/2009/12/26/1198.html [...]</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2010/08/08</a>, <a href="http://ichenkun.org/blog/2009/11/21/%e7%be%8e%e5%91%b3%e5%88%86%e4%ba%ab1/"  rel="external nofollow"  class="url" >美味分享1 | ichenkun</a> writes: [...] 5: 深入理解PHP原理之变量 生命期(一) [...]</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2010/09/18</a>, <a href="http://chenkun.me/blog/2009/11/21/delicious-1/"  rel="external nofollow"  class="url" >美味分享1 | chenkun</a> writes: [...] 5: 深入理解PHP原理之变量 生命期(一) [...]</li><li><a href="http://www.laruence.com/2009/12/26/1198.html" >2011/03/20</a>, <a href="http://www.w3hacker.com/?p=198"  rel="external nofollow"  class="url" >深入理解PHP原理之变量生命期(一) | 万维网黑客联盟</a> writes: [...] 本文地址: http://www.laruence.com/2009/12/26/1198.html [...]</li></ul><hr/><h2>Related posts:</h2><ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2009/07/27/1020.html"  rel="bookmark"  title="Permanent Link: 深入理解PHP原理之错误抑制与内嵌HTML" >深入理解PHP原理之错误抑制与内嵌HTML</a></li><li><a href="http://www.laruence.com/2008/08/26/463.html"  rel="bookmark"  title="Permanent Link: 深入理解PHP原理之变量作用域(Scope in PHP)" >深入理解PHP原理之变量作用域(Scope in PHP)</a></li><li><a href="http://www.laruence.com/2011/03/29/1949.html"  rel="bookmark"  title="Permanent Link: 深入理解PHP原理之Session Gc的一个小概率Notice" >深入理解PHP原理之Session Gc的一个小概率Notice</a></li><li><a href="http://www.laruence.com/2010/06/20/1602.html"  rel="bookmark"  title="Permanent Link: 深入理解PHP之匿名函数" >深入理解PHP之匿名函数</a></li><li><a href="http://www.laruence.com/2010/05/04/1450.html"  rel="bookmark"  title="Permanent Link: 深入理解PHP之require/include顺序" >深入理解PHP之require/include顺序</a></li></ul><hr/><small  style="font-size:85%;font-size:85%;">Copyright &copy; 2010 <a href="http://www.laruence.com"  target="_blank" >风雪之隅</a> 版权所有, 转载务必注明. 该Feed只供个人使用, 禁止未注明的转载或商业应用. 非法应用的, 一切法律后果自负. 如有问题, 可发E-mail至my at laruence.com.(Digital Fingerprint: 73540ba0a1738d7d07d4b6038d5615e2)</small><h2 class="related_post_title" >Related Posts:</h2><ul class="related_post"   style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2009/10/15/1131.html"  title="提升PHP性能之改变Zend引擎分发方式" >提升PHP性能之改变Zend引擎分发方式</a></li><li><a href="http://www.laruence.com/2008/11/07/581.html"  title="PHP的GET/POST等大变量生成过程" >PHP的GET/POST等大变量生成过程</a></li><li><a href="http://www.laruence.com/2008/09/23/539.html"  title="使用PHP Embed SAPI实现Opcodes查看器" >使用PHP Embed SAPI实现Opcodes查看器</a></li><li><a href="http://www.laruence.com/2008/08/24/377.html"  title="PHP源码分析之Global关键字" >PHP源码分析之Global关键字</a></li><li><a href="http://www.laruence.com/2008/08/22/412.html"  title="深入理解PHP原理之变量(Variables inside PHP)" >深入理解PHP原理之变量(Variables inside PHP)</a></li><li><a href="http://www.laruence.com/2008/08/12/180.html"  title="深入理解Zend SAPIs(Zend SAPI Internals)" >深入理解Zend SAPIs(Zend SAPI Internals)</a></li><li><a href="http://www.laruence.com/2008/04/04/17.html"  title="在PHP Module中获取$_GET/$_POST/$_COOKIE的方法研究" >在PHP Module中获取$_GET/$_POST/$_COOKIE的方法研究</a></li><li><a href="http://www.laruence.com/2012/02/08/2528.html"  title="PHP-5.3.9远程执行任意代码漏洞(CVE-2012-0830)" >PHP-5.3.9远程执行任意代码漏洞(CVE-2012-0830)</a></li><li><a href="http://www.laruence.com/2012/02/02/2515.html"  title="我们什么时候应该使用异常?" >我们什么时候应该使用异常?</a></li><li><a href="http://www.laruence.com/2012/02/01/2503.html"  title="使用exit(-1)为什么得到255退出码?" >使用exit(-1)为什么得到255退出码?</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.laruence.com/2009/12/26/1198.html/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>提升PHP性能之改变Zend引擎分发方式</title>
		<link>http://www.laruence.com/2009/10/15/1131.html</link>
		<comments>http://www.laruence.com/2009/10/15/1131.html#comments</comments>
		<pubDate>Thu, 15 Oct 2009 08:21:19 +0000</pubDate>
		<dc:creator>雪候鸟</dc:creator>
				<category><![CDATA[PHP应用]]></category>
		<category><![CDATA[PHP源码分析]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[php源码分析]]></category>
		<category><![CDATA[ZE]]></category>
		<category><![CDATA[性能]]></category>
		<category><![CDATA[效率]]></category>

		<guid isPermaLink="false">http://www.laruence.com/?p=1131</guid>
		<description><![CDATA[	从PHP5.1开始,PHP提供了用户对Zend VM执行分发方式的选择接口. 

	之前的文章中, 我也提过这方面的内容, Zend虚拟机在执行的时候, 对于编译生成的op_array中的每一条opline的opcode都会分发到相应的处理器(zend_vm_def.h定义)执行, 而按照分发的方式不同, 分发过程可以分为CALL, SWITCH, 和GOTO三种类型.

默认是CALL方式, 也就是所有的opcode处理器都定义为函数, 然后虚拟机调用. 这种方式是传统的方式, 也一般被认为是最稳定的方式.
	
SWITCH方式和GOTO方式则和其命名的意义相同, 分别通过switch和goto来分发.官方给出的描述说GOTO方式最快.

   那么如果使用GOTO方式, 效率上到底能提高多少呢? 今天我就分别使用各种方式来测试一番:]]></description>
			<content:encoded><![CDATA[<div class="copyright" >
<ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;">
<li>作者: <a href="http://www.laruence.com" >Laruence</a>(<a href="http://www.twitter.com/laruence"  target="meme"  title="Twitter" ><img src="/images/ico-twitter.png" /></a> <a href="http://t.sina.com/laruence"  target="meme"  title="新浪微博" ><img src="/images/ico-sina.png" /></a> <a href="http://fusion.google.com/add?feedurl=http://www.laruence.com/feed"  target="meme"  title="Google阅读器" ><img src="/images/ico-google.png" /></a> <a href="mailto:laruence@yahoo.com.cn"  target="meme"  title="邮件" ><img src="/images/ico-mail.png" /></a>)</li>
<li>本文地址: <a href="http://www.laruence.com/2009/10/15/1131.html"  title="Permanet Link to 提升PHP性能之改变Zend引擎分发方式" >http://www.laruence.com/2009/10/15/1131.html</a></li>
</li>
<li>转载请注明出处 </li>
</ul></div>
<p>
	从PHP5.1开始,PHP提供了用户对Zend VM执行分发方式的选择接口. </p>
<p>	之前的文章中, 我也提过这方面的内容, Zend虚拟机在执行的时候, 对于编译生成的op_array中的每一条opline的opcode都会分发到相应的处理器(zend_vm_def.h定义)执行, 而按照分发的方式不同, 分发过程可以分为CALL, SWITCH, 和GOTO三种类型.</p>
<p>	默认是CALL方式, 也就是所有的opcode处理器都定义为函数, 然后虚拟机调用. 这种方式是传统的方式, 也一般被认为是最稳定的方式.</p>
<p>	SWITCH方式和GOTO方式则和其命名的意义相同, 分别通过switch和goto来分发opcode到对应的处理逻辑(段).</p>
<p>	官方给出的描述是:</p>
<blockquote><p>
<span><br/>
CALL &#8211; Uses function handlers for opcodes<br/>
SWITCH &#8211; Uses switch() statement for opcode dispatch<br/>
GOTO &#8211; Uses goto for opcode dispatch (threaded opcodes architecture)<br/>
GOTO is usually (depends on CPU and compiler) faster than SWITCH, which<br/>
tends to be slightly faster than CALL.<br/>
CALL is default because it doesn&#8217;t take very long to compile as opposed<br/>
to the other two and in general the speed is quite close to the others.<br/>
</span>
</p></blockquote>
<p>   那么如果使用GOTO方式, 效率上到底能提高多少呢?</p>
<p>   今天我就分别使用各种方式来测试一番, 测试脚本<a href="http://www.laruence.com/2009/10/15/1131.html#bench" >bench.php</a>.</p>
<p>   第一点被证明的就是, 官方说的GOTO方式编译耗时显著高于其他俩种方式, 我一开始在虚拟机上编译, 每次都Hangup(囧), 最后只好换了个强劲点的物理机, 大约3分钟后, 编译成功..</p>
<p>   测试环境:</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
PHP 5.3.0   Linux
AMD Opteron(tm) Processor 270(2G) X 4  6G Memory
</pre>
<p>   编译参数:</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
./configure --with-zend-vm=CALL/GOTO/SWITCH
</pre>
<p>  测试结果如下(都是三次取中值):</p>
<p>CALL方式:</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
laruence@dev01.tc$ sapi/cli/php bench.php
simple             0.358
simplecall         0.418
simpleucall        0.405
simpleudcall       0.424
mandel             1.011
mandel2            1.238
ackermann(7)       0.375
ary(50000)         0.083
ary2(50000)        0.075
ary3(2000)         0.561
fibo(30)           1.156
hash1(50000)       0.114
hash2(500)         0.091
heapsort(20000)    0.270
matrix(20)         0.276
nestedloop(12)     0.599
sieve(30)          0.350
strcat(200000)     0.039
------------------------
Total              7.844
</pre>
<p>SWITCH方式:</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
laruence@dev01.tc$ sapi/cli/php bench.php
simple             0.393
simplecall         0.414
simpleucall        0.424
simpleudcall       0.445
mandel             1.007
mandel2            1.254
ackermann(7)       0.392
ary(50000)         0.084
ary2(50000)        0.073
ary3(2000)         0.593
fibo(30)           1.185
hash1(50000)       0.120
hash2(500)         0.092
heapsort(20000)    0.285
matrix(20)         0.295
nestedloop(12)     0.678
sieve(30)          0.359
strcat(200000)     0.042
------------------------
Total              8.138
</pre>
<p>GOTO方式 :</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
laruence@dev01.tc$ sapi/cli/php bench.php
simple             0.306
simplecall         0.373
simpleucall        0.369
simpleudcall       0.385
mandel             0.879
mandel2            1.132
ackermann(7)       0.356
ary(50000)         0.081
ary2(50000)        0.073
ary3(2000)         0.525
fibo(30)           1.043
hash1(50000)       0.111
hash2(500)         0.088
heapsort(20000)    0.247
matrix(20)         0.247
nestedloop(12)     0.519
sieve(30)          0.331
strcat(200000)     0.037
------------------------
Total              7.103
</pre>
<p>可见, <b>GOTO方式最快, SWITCH方式最慢.</b>和官方的描述稍有不符.</p>
<p>GOTO方式比其默认的CALL方式, 性能提升还是比较明显的. </p>
<p>所以, 如果你希望让PHP发挥到机制, 改变Zend VM的分发方式, 也可以做为一个考虑因素.
</p>
<p>
附:</p>
<p>使用GOTO方式的configure选项:</p>
<pre name="code"  class="sh_bash"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
--with-zend-vm=GOTO
</pre>
<p>也可以在Zend目录下使用:</p>
<pre name="code"  class="sh_bash"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
php zend_vm_gen.php --with-vm-kind=[CALL|GOTO|SWITH]
</pre>
<p><a name="bench" > </a>测试脚本bench.php</p>
<pre name="code"  class="sh_php"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
&lt;?php
/**
 * PHP Perf Bench Test Script
 */
function simple() {
  $a = 0;
  for ($i = 0; $i &lt; 1000000; $i++)
    $a++;

  $thisisanotherlongname = 0;
  for ($thisisalongname = 0; $thisisalongname &lt; 1000000; $thisisalongname++)
    $thisisanotherlongname++;
}

/****/

function simplecall() {
  for ($i = 0; $i &lt; 1000000; $i++)
    strlen(&quot;hallo&quot;);
}

/****/

function hallo($a) {
}

function simpleucall() {
  for ($i = 0; $i &lt; 1000000; $i++)
    hallo(&quot;hallo&quot;);
}

/****/

function simpleudcall() {
  for ($i = 0; $i &lt; 1000000; $i++)
    hallo2(&quot;hallo&quot;);
}

function hallo2($a) {
}

/****/

function mandel() {
  $w1=50;
  $h1=150;
  $recen=-.45;
  $imcen=0.0;
  $r=0.7;
  $s=0;  $rec=0;  $imc=0;  $re=0;  $im=0;  $re2=0;  $im2=0;
  $x=0;  $y=0;  $w2=0;  $h2=0;  $color=0;
  $s=2*$r/$w1;
  $w2=40;
  $h2=12;
  for ($y=0 ; $y&lt;=$w1; $y=$y+1) {
    $imc=$s*($y-$h2)+$imcen;
    for ($x=0 ; $x&lt;=$h1; $x=$x+1) {
      $rec=$s*($x-$w2)+$recen;
      $re=$rec;
      $im=$imc;
      $color=1000;
      $re2=$re*$re;
      $im2=$im*$im;
      while( ((($re2+$im2)&lt;1000000) &amp;&amp; $color&gt;0)) {
        $im=$re*$im*2+$imc;
        $re=$re2-$im2+$rec;
        $re2=$re*$re;
        $im2=$im*$im;
        $color=$color-1;
      }
      if ( $color==0 ) {
        print &quot;_&quot;;
      } else {
        print &quot;#&quot;;
      }
    }
    print &quot;&lt;br&gt;&quot;;
    flush();
  }
}

/****/

function mandel2() {
  $b = &quot; .:,;!/&gt;)|&amp;IH%*#&quot;;
  //float r, i, z, Z, t, c, C;
  for ($y=30; printf(&quot;\n&quot;), $C = $y*0.1 - 1.5, $y--;){
    for ($x=0; $c = $x*0.04 - 2, $z=0, $Z=0, $x++ &lt; 75;){
      for ($r=$c, $i=$C, $k=0; $t = $z*$z - $Z*$Z + $r, $Z = 2*$z*$Z + $i, $z=$t, $k&lt;5000; $k++)
        if ($z*$z + $Z*$Z &gt; 500000) break;
      echo $b[$k%16];
    }
  }
}

/****/

function Ack($m, $n){
  if($m == 0) return $n+1;
  if($n == 0) return Ack($m-1, 1);
  return Ack($m - 1, Ack($m, ($n - 1)));
}

function ackermann($n) {
  $r = Ack(3,$n);
  print &quot;Ack(3,$n): $r\n&quot;;
}

/****/

function ary($n) {
  for ($i=0; $i&lt;$n; $i++) {
    $X[$i] = $i;
  }
  for ($i=$n-1; $i&gt;=0; $i--) {
    $Y[$i] = $X[$i];
  }
  $last = $n-1;
  print &quot;$Y[$last]\n&quot;;
}

/****/

function ary2($n) {
  for ($i=0; $i&lt;$n;) {
    $X[$i] = $i; ++$i;
    $X[$i] = $i; ++$i;
    $X[$i] = $i; ++$i;
    $X[$i] = $i; ++$i;
    $X[$i] = $i; ++$i;

    $X[$i] = $i; ++$i;
    $X[$i] = $i; ++$i;
    $X[$i] = $i; ++$i;
    $X[$i] = $i; ++$i;
    $X[$i] = $i; ++$i;
  }
  for ($i=$n-1; $i&gt;=0;) {
    $Y[$i] = $X[$i]; --$i;
    $Y[$i] = $X[$i]; --$i;
    $Y[$i] = $X[$i]; --$i;
    $Y[$i] = $X[$i]; --$i;
    $Y[$i] = $X[$i]; --$i;

    $Y[$i] = $X[$i]; --$i;
    $Y[$i] = $X[$i]; --$i;
    $Y[$i] = $X[$i]; --$i;
    $Y[$i] = $X[$i]; --$i;
    $Y[$i] = $X[$i]; --$i;
  }
  $last = $n-1;
  print &quot;$Y[$last]\n&quot;;
}

/****/

function ary3($n) {
  for ($i=0; $i&lt;$n; $i++) {
    $X[$i] = $i + 1;
    $Y[$i] = 0;
  }
  for ($k=0; $k&lt;1000; $k++) {
    for ($i=$n-1; $i&gt;=0; $i--) {
      $Y[$i] += $X[$i];
    }
  }
  $last = $n-1;
  print &quot;$Y[0] $Y[$last]\n&quot;;
}

/****/

function fibo_r($n){
    return(($n &lt; 2) ? 1 : fibo_r($n - 2) + fibo_r($n - 1));
}

function fibo($n) {
  $r = fibo_r($n);
  print &quot;$r\n&quot;;
}

/****/

function hash1($n) {
  for ($i = 1; $i &lt;= $n; $i++) {
    $X[dechex($i)] = $i;
  }
  $c = 0;
  for ($i = $n; $i &gt; 0; $i--) {
    if ($X[dechex($i)]) { $c++; }
  }
  print &quot;$c\n&quot;;
}

/****/

function hash2($n) {
  for ($i = 0; $i &lt; $n; $i++) {
    $hash1[&quot;foo_$i&quot;] = $i;
    $hash2[&quot;foo_$i&quot;] = 0;
  }
  for ($i = $n; $i &gt; 0; $i--) {
    foreach($hash1 as $key =&gt; $value) $hash2[$key] += $value;
  }
  $first = &quot;foo_0&quot;;
  $last  = &quot;foo_&quot;.($n-1);
  print &quot;$hash1[$first] $hash1[$last] $hash2[$first] $hash2[$last]\n&quot;;
}

/****/

function gen_random ($n) {
    global $LAST;
    return( ($n * ($LAST = ($LAST * IA + IC) % IM)) / IM );
}

function heapsort_r($n, &amp;$ra) {
    $l = ($n &gt;&gt; 1) + 1;
    $ir = $n;

    while (1) {
        if ($l &gt; 1) {
            $rra = $ra[--$l];
        } else {
            $rra = $ra[$ir];
            $ra[$ir] = $ra[1];
            if (--$ir == 1) {
                $ra[1] = $rra;
                return;
            }
        }
        $i = $l;
        $j = $l &lt;&lt; 1;
        while ($j &lt;= $ir) {
            if (($j &lt; $ir) &amp;&amp; ($ra[$j] &lt; $ra[$j+1])) {
                $j++;
            }
            if ($rra &lt; $ra[$j]) {
                $ra[$i] = $ra[$j];
                $j += ($i = $j);
            } else {
                $j = $ir + 1;
            }
        }
        $ra[$i] = $rra;
    }
}

function heapsort($N) {
  global $LAST;

  define(&quot;IM&quot;, 139968);
  define(&quot;IA&quot;, 3877);
  define(&quot;IC&quot;, 29573);

  $LAST = 42;
  for ($i=1; $i&lt;=$N; $i++) {
    $ary[$i] = gen_random(1);
  }
  heapsort_r($N, $ary);
  printf(&quot;%.10f\n&quot;, $ary[$N]);
}

/****/

function mkmatrix ($rows, $cols) {
    $count = 1;
    $mx = array();
    for ($i=0; $i&lt;$rows; $i++) {
        for ($j=0; $j&lt;$cols; $j++) {
            $mx[$i][$j] = $count++;
        }
    }
    return($mx);
}

function mmult ($rows, $cols, $m1, $m2) {
    $m3 = array();
    for ($i=0; $i&lt;$rows; $i++) {
        for ($j=0; $j&lt;$cols; $j++) {
            $x = 0;
            for ($k=0; $k&lt;$cols; $k++) {
                $x += $m1[$i][$k] * $m2[$k][$j];
            }
            $m3[$i][$j] = $x;
        }
    }
    return($m3);
}

function matrix($n) {
  $SIZE = 30;
  $m1 = mkmatrix($SIZE, $SIZE);
  $m2 = mkmatrix($SIZE, $SIZE);
  while ($n--) {
    $mm = mmult($SIZE, $SIZE, $m1, $m2);
  }
  print &quot;{$mm[0][0]} {$mm[2][3]} {$mm[3][2]} {$mm[4][4]}\n&quot;;
}

/****/

function nestedloop($n) {
  $x = 0;
  for ($a=0; $a&lt;$n; $a++)
    for ($b=0; $b&lt;$n; $b++)
      for ($c=0; $c&lt;$n; $c++)
        for ($d=0; $d&lt;$n; $d++)
          for ($e=0; $e&lt;$n; $e++)
            for ($f=0; $f&lt;$n; $f++)
             $x++;
  print &quot;$x\n&quot;;
}

/****/

function sieve($n) {
  $count = 0;
  while ($n-- &gt; 0) {
    $count = 0;
    $flags = range (0,8192);
    for ($i=2; $i&lt;8193; $i++) {
      if ($flags[$i] &gt; 0) {
        for ($k=$i+$i; $k &lt;= 8192; $k+=$i) {
          $flags[$k] = 0;
        }
        $count++;
      }
    }
  }
  print &quot;Count: $count\n&quot;;
}

/****/

function strcat($n) {
  $str = &quot;&quot;;
  while ($n-- &gt; 0) {
    $str .= &quot;hello\n&quot;;
  }
  $len = strlen($str);
  print &quot;$len\n&quot;;
}

/*****/

function getmicrotime()
{
  $t = gettimeofday();
  return ($t['sec'] + $t['usec'] / 1000000);
}

function start_test()
{
        ob_start();
  return getmicrotime();
}

function end_test($start, $name)
{
  global $total;
  $end = getmicrotime();
  ob_end_clean();
  $total += $end-$start;
  $num = number_format($end-$start,3);
  $pad = str_repeat(&quot; &quot;, 24-strlen($name)-strlen($num));

  echo $name.$pad.$num.&quot;\n&quot;;
        ob_start();
  return getmicrotime();
}

function total()
{
  global $total;
  $pad = str_repeat(&quot;-&quot;, 24);
  echo $pad.&quot;\n&quot;;
  $num = number_format($total,3);
  $pad = str_repeat(&quot; &quot;, 24-strlen(&quot;Total&quot;)-strlen($num));
  echo &quot;Total&quot;.$pad.$num.&quot;\n&quot;;
}

$t0 = $t = start_test();
simple();
$t = end_test($t, &quot;simple&quot;);
simplecall();
$t = end_test($t, &quot;simplecall&quot;);
simpleucall();
$t = end_test($t, &quot;simpleucall&quot;);
simpleudcall();
$t = end_test($t, &quot;simpleudcall&quot;);
mandel();
$t = end_test($t, &quot;mandel&quot;);
mandel2();
$t = end_test($t, &quot;mandel2&quot;);
ackermann(7);
$t = end_test($t, &quot;ackermann(7)&quot;);
ary(50000);
$t = end_test($t, &quot;ary(50000)&quot;);
ary2(50000);
$t = end_test($t, &quot;ary2(50000)&quot;);
ary3(2000);
$t = end_test($t, &quot;ary3(2000)&quot;);
fibo(30);
$t = end_test($t, &quot;fibo(30)&quot;);
hash1(50000);
$t = end_test($t, &quot;hash1(50000)&quot;);
hash2(500);
$t = end_test($t, &quot;hash2(500)&quot;);
heapsort(20000);
$t = end_test($t, &quot;heapsort(20000)&quot;);
matrix(20);
$t = end_test($t, &quot;matrix(20)&quot;);
nestedloop(12);
$t = end_test($t, &quot;nestedloop(12)&quot;);
sieve(30);
$t = end_test($t, &quot;sieve(30)&quot;);
strcat(200000);
$t = end_test($t, &quot;strcat(200000)&quot;);
total($t0, &quot;Total&quot;);
?&gt;
</pre>
</p>
<p><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_bash.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_bash.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_php.js" ></script></p>
<hr/><h2>Comments</h2><ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2009/10/15</a>, <a href="http://www.codytan.com"  rel="external nofollow"  class="url" >cody</a> writes: 好文，不过有些疑问
1.“对于每个Opcode都会分发执行”，这里的“个"指的是？是一条opcode语句还是一个php文件中所有的opcode语句？
2.有个问题，php的错误检查阶段是哪个？不是语法检查。如include一个不存在的问题，php会报错，这个报错是在opcode-&gt;compile-&gt;执行中的哪个环节报错的？是compile环节还是执行环节呢？
3.其实问题二是基于对php运行机制还不太清楚，我测试过一个文件 include 20个php文件和直接写成一个php文件性能上的差异很大（20文件并不执行），想知道是在哪个环境造成了这种差异。

如果能回答我，感激不尽！ :)</li><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2009/10/15</a>, <a href="http://www.laruence.com"  rel="external nofollow"  class="url" >雪候鸟</a> writes: @cody 应该是对于每条opline中的opcode. 都会通过分发对应到相应的处理器执行(zend_vm_def.h定义).
include也是一条opcode,ZEND_INCLUDE_OR_EVAL, 相应的出错检查也就是在它的处理器中.</li><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2009/10/15</a>, <a href="http://www.codytan.com"  rel="external nofollow"  class="url" >cody</a> writes: 补充一下，”opcode-&gt;compile-&gt;执行“ 我这里的compile指的其实是由opcode到c 这个过程。</li><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2009/10/15</a>, cody writes: thanks</li><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2009/10/17</a>, fybird writes: 请问你是用那个ide编程，平常。</li><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2009/10/17</a>, <a href="http://www.laruence.com"  rel="external nofollow"  class="url" >雪候鸟</a> writes: @fybird vim</li><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2009/10/24</a>, <a href="http://www.05sky.cn"  rel="external nofollow"  class="url" >CrossYou</a> writes: 我也使用了你同样的主题，呵呵

你的文章写的很深，我才刚刚接触php，学习中，楼主加油。</li><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2009/10/26</a>, zhangyufeng writes: http://twiki.laruence.com/ 打不开啦……</li><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2010/03/21</a>, <a href="http://t.sina.com.cn/liruqi"  rel="external nofollow"  class="url" >liruqi</a> writes: 建议做bench的时候，把计算代码和输出代码分离测试。因为io部分所费的时间不好预测。</li><li><a href="http://www.laruence.com/2009/10/15/1131.html" >2011/03/20</a>, <a href="http://www.w3hacker.com/?p=202"  rel="external nofollow"  class="url" >提升PHP性能之改变Zend引擎分发方式 | 万维网黑客联盟</a> writes: [...] 本文地址: http://www.laruence.com/2009/10/15/1131.html [...]</li></ul><hr/><small  style="font-size:85%;font-size:85%;">Copyright &copy; 2010 <a href="http://www.laruence.com"  target="_blank" >风雪之隅</a> 版权所有, 转载务必注明. 该Feed只供个人使用, 禁止未注明的转载或商业应用. 非法应用的, 一切法律后果自负. 如有问题, 可发E-mail至my at laruence.com.(Digital Fingerprint: 73540ba0a1738d7d07d4b6038d5615e2)</small><h2 class="related_post_title" >Related Posts:</h2><ul class="related_post"   style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2011/12/30/2435.html"  title="PHP数组的Hash冲突实例" >PHP数组的Hash冲突实例</a></li><li><a href="http://www.laruence.com/2011/03/04/1894.html"  title="深入理解PHP内存管理之谁动了我的内存" >深入理解PHP内存管理之谁动了我的内存</a></li><li><a href="http://www.laruence.com/2010/05/26/1541.html"  title="PHP类型转换相关的一个Bug" >PHP类型转换相关的一个Bug</a></li><li><a href="http://www.laruence.com/2010/05/20/1495.html"  title="Nginx + PHP CGI的一个可能的安全漏洞" >Nginx + PHP CGI的一个可能的安全漏洞</a></li><li><a href="http://www.laruence.com/2010/05/18/1482.html"  title="深入理解PHP原理之对象(一)" >深入理解PHP原理之对象(一)</a></li><li><a href="http://www.laruence.com/2009/12/26/1198.html"  title="深入理解PHP原理之变量生命期(一)" >深入理解PHP原理之变量生命期(一)</a></li><li><a href="http://www.laruence.com/2009/11/27/1164.html"  title="memory_limit的一个bug" >memory_limit的一个bug</a></li><li><a href="http://www.laruence.com/2009/09/26/1103.html"  title="PHP文件上传源码分析(RFC1867)" >PHP文件上传源码分析(RFC1867)</a></li><li><a href="http://www.laruence.com/2009/07/27/1020.html"  title="深入理解PHP原理之错误抑制与内嵌HTML" >深入理解PHP原理之错误抑制与内嵌HTML</a></li><li><a href="http://www.laruence.com/2008/08/24/377.html"  title="PHP源码分析之Global关键字" >PHP源码分析之Global关键字</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.laruence.com/2009/10/15/1131.html/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>PHP源码分析之Global关键字</title>
		<link>http://www.laruence.com/2008/08/24/377.html</link>
		<comments>http://www.laruence.com/2008/08/24/377.html#comments</comments>
		<pubDate>Sun, 24 Aug 2008 05:32:32 +0000</pubDate>
		<dc:creator>雪候鸟</dc:creator>
				<category><![CDATA[PHP应用]]></category>
		<category><![CDATA[PHP源码分析]]></category>
		<category><![CDATA[global]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.laruence.com/?p=377</guid>
		<description><![CDATA[<p>闲来无事，就系统的从PHP的词法分析，语法分析，opcodes生成，执行，整个流程，详细的分析了global关键字的实现。</p>]]></description>
			<content:encoded><![CDATA[<div class="copyright" >
<ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;">
<li>作者: <a href="http://www.laruence.com" >Laruence</a>(<a href="http://www.twitter.com/laruence"  target="meme"  title="Twitter" ><img src="/images/ico-twitter.png" /></a> <a href="http://t.sina.com/laruence"  target="meme"  title="新浪微博" ><img src="/images/ico-sina.png" /></a> <a href="http://fusion.google.com/add?feedurl=http://www.laruence.com/feed"  target="meme"  title="Google阅读器" ><img src="/images/ico-google.png" /></a> <a href="mailto:laruence@yahoo.com.cn"  target="meme"  title="邮件" ><img src="/images/ico-mail.png" /></a>)</li>
<li>本文地址: <a href="http://www.laruence.com/2008/08/24/377.html"  title="Permanet Link to PHP源码分析之Global关键字" >http://www.laruence.com/2008/08/24/377.html</a></li>
</li>
<li>转载请注明出处 </li>
</ul></div>
<p>闲来无事，就系统的从PHP的词法分析，语法分析，opcodes生成，执行，整个流程，详细的分析了global关键字的实现。</p>
<p>当你在脚本中写下:</p>
<pre name="code"  class="sh_php"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
&lt;?php
    $var = &quot;laruence&quot;;
    function sample(){
        global $var;
    }
?&gt;
</pre>
<p>的时候，你知道PHP是怎么实现在函数作用域找到全局变量的么？ </p>
<p>在我前面的文章中(<a href="http://www.laruence.com/2008/06/18/221.html" >深入理解PHP原理之Opcodes</a>)讲过, PHP的执行，经历了如下几个阶段：</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
1. Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens)

2. Parsing, 将Tokens转换成简单而有意义的表达式

3. Compilation, 将表达式编译成Opocdes

4. Execution, 顺次执行Opcodes，每次一条，从而实现PHP脚本的功能。
</pre>
<p>那么，第一阶段，自然就是Scanning了， 在词法分析阶段，我们的</p>
<pre name="code"  class="sh_php"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
global $var;
</pre>
<p>会被解析为：</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
    T_GLOBAL  var;
</pre>
<p>接下来是parsing阶段：</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
T_GLOBAL var;
</pre>
<p>yacc经过规则：</p>
<pre name="code"  class="sh_shell"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
	statement:
		|  T_GLOBAL global_var_list ';'
		....

	global_var_list:
        global_var_list ',' global_var  { zend_do_fetch_global_variable(&amp;$3, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); }
    |   global_var                      { zend_do_fetch_global_variable(&amp;$1, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); }
;
</pre>
<p>其中, zend_do_fetch_global_variable是真正生成opcode的函数：</p>
<pre name="code"  class="sh_cpp"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
      zend_op  *opline;
      ......
      opline-&gt;opcode = ZEND_FETCH_W;
      opline-&gt;result.op_type = IS_VAR;
	  ......
      opline-&gt;op2.u.EA.type = ZEND_FETCH_GLOBAL_LOCK;
</pre>
<p>而对于ZEND_FETCH_W的op_handler是：</p>
<pre name="code"  class="sh_cpp"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
	ZEND_VM_HANDLER(83, ZEND_FETCH_W, CONST|TMP|VAR|CV, ANY)
	{
    	ZEND_VM_DISPATCH_TO_HELPER_EX(zend_fetch_var_address_helper, type, BP_VAR_W);
	}
</pre>
<p>  我们来看看zend_fetch_var_adress_helper:</p>
<pre name="code"  class="sh_cpp"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
        .....
        target_symbol_table = zend_get_target_symbol_table(opline, EX(Ts), type, varname TSRMLS_CC);
/*
        if (!target_symbol_table) {
            ZEND_VM_NEXT_OPCODE();
        }
*/
        if (zend_hash_find(target_symbol_table, varname-&gt;value.str.val, varname-&gt;value.str.len+1, (void **) &amp;retval) == FAILUR
E) {
            switch (type) {
                case BP_VAR_R:
                case BP_VAR_UNSET:
                    zend_error(E_NOTICE,&quot;Undefined variable: %s&quot;, Z_STRVAL_P(varname));
                    /* break missing intentionally */
                case BP_VAR_IS:
                    retval = &amp;EG(uninitialized_zval_ptr);
                    break;
                case BP_VAR_RW:
                    zend_error(E_NOTICE,&quot;Undefined variable: %s&quot;, Z_STRVAL_P(varname));
                    /* break missing intentionally */
                case BP_VAR_W: {
                        zval *new_zval = &amp;EG(uninitialized_zval);

                        new_zval-&gt;refcount++;
                        zend_hash_update(target_symbol_table, varname-&gt;value.str.val, varname-&gt;value.str.len+1, &amp;new_zval, siz
eof(zval *), (void **) &amp;retval);
                    }
                    break;
                EMPTY_SWITCH_DEFAULT_CASE()
            }
        }
</pre>
<p>可以看出，核心就是zend_get_targer_symbol_table这个函数了：</p>
<pre name="code"  class="sh_cpp"  linenum="off"   style="background: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:Monacobackground: #333; color: #d9d9d9; border-left: 15px solid #c9c9c9; padding: 9px; font-size: 1em; overflow-x: auto;font-family:MonacoConsolasConsolasCourierCouriermonospace;monospace;">
static inline HashTable *zend_get_target_symbol_table(zend_op *opline, temp_variable *Ts, int type, zval *variable TSRMLS_DC)
{
    switch (opline-&gt;op2.u.EA.type) {
        case ZEND_FETCH_LOCAL:
            return EG(active_symbol_table);
            break;
        case ZEND_FETCH_GLOBAL:
        case ZEND_FETCH_GLOBAL_LOCK:
            return &amp;EG(symbol_table);
            break;
        case ZEND_FETCH_STATIC:
            if (!EG(active_op_array)-&gt;static_variables) {
                ALLOC_HASHTABLE(EG(active_op_array)-&gt;static_variables);
                zend_hash_init(EG(active_op_array)-&gt;static_variables, 2, NULL, ZVAL_PTR_DTOR, 0);
            }
            return EG(active_op_array)-&gt;static_variables;
            break;
        EMPTY_SWITCH_DEFAULT_CASE()
    }
    return NULL;
}
</pre>
<p>  恩，问题清楚了，也就是说， 如果你global了一个变量，那么Zend就会去全局symbol_table去寻找，如果找不到，就会在全局symbol_table中分配相应的变量。<br/>
  通过这样的机制，实现了全局变量。<script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_php.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_php.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_shell.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_cpp.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_cpp.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_cpp.js" ></script><script type="text/javascript"  src="http://www.laruence.com/wp-content/plugins/shjs-syntax-hiliter/shjs/lang/sh_cpp.js" ></script></p>
<hr/><h2>Comments</h2><ul  style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2008/08/24/377.html" >2008/08/24</a>, <a href="http://onemouse.cn"  rel="external nofollow"  class="url" >guoxiaod</a> writes: 之前我们遇到了一个问题：
关于PHP class 的顺序的问题，问题如下:
hello();

提示找不到 class b .

这个问题，是什么原因呢?</li><li><a href="http://www.laruence.com/2008/08/24/377.html" >2008/08/24</a>, <a href="http://onemouse.cn"  rel="external nofollow"  class="url" >guoxiaod</a> writes: 哎，居然，把 &lt;? 过滤掉了。</li><li><a href="http://www.laruence.com/2008/08/24/377.html" >2008/08/24</a>, <a href="http://onemouse.cn"  rel="external nofollow"  class="url" >guoxiaod</a> writes: 再发一边。
&lt;?php
class a extends b {
  public function hello(){echo "ok1";}
};
class b extends c {
  public function hello(){echo "ok2";}
};
class c {
  public function hello (){echo "ok3";}
};


$a  = new a();
$a-&gt;hello();</li><li><a href="http://www.laruence.com/2008/08/24/377.html" >2008/08/24</a>, <a href="http://www.laruence.com"  rel="external nofollow"  class="url" >雪候鸟</a> writes: 这个问题确实奇怪， 一层继承的时候就没有问题。估计是PHP5的一个bug。
回头我研究研究。</li><li><a href="http://www.laruence.com/2008/08/24/377.html" >2008/08/25</a>, <a href="http://www.blankyao.cn"  rel="external nofollow"  class="url" >blankyao</a> writes: 那篇讲多重继承的bug的文章呢？只在阅读器上看到了....</li><li><a href="http://www.laruence.com/2008/08/24/377.html" >2008/08/25</a>, <a href="http://www.laruence.com"  rel="external nofollow"  class="url" >雪候鸟</a> writes: 恩，因为刚才我发现我的思路可能有点问题，所以又重新验证了一下，这个过程中怕误导别人，就先给隐藏了。 现在好了。</li><li><a href="http://www.laruence.com/2008/08/24/377.html" >2010/10/03</a>, xinbe writes: 那請問用$GLOBALS['var']
跟global $var
哪個會比較快呢？</li><li><a href="http://www.laruence.com/2008/08/24/377.html" >2011/03/20</a>, <a href="http://www.w3hacker.com/?p=206"  rel="external nofollow"  class="url" >PHP源码分析之Global关键字 | 万维网黑客联盟</a> writes: [...] 本文地址: http://www.laruence.com/2008/08/24/377.html [...]</li></ul><hr/><small  style="font-size:85%;font-size:85%;">Copyright &copy; 2010 <a href="http://www.laruence.com"  target="_blank" >风雪之隅</a> 版权所有, 转载务必注明. 该Feed只供个人使用, 禁止未注明的转载或商业应用. 非法应用的, 一切法律后果自负. 如有问题, 可发E-mail至my at laruence.com.(Digital Fingerprint: 73540ba0a1738d7d07d4b6038d5615e2)</small><h2 class="related_post_title" >Related Posts:</h2><ul class="related_post"   style="padding-left:1em;font-size:85%;padding-left:1em;font-size:85%;"><li><a href="http://www.laruence.com/2009/12/26/1198.html"  title="深入理解PHP原理之变量生命期(一)" >深入理解PHP原理之变量生命期(一)</a></li><li><a href="http://www.laruence.com/2009/10/15/1131.html"  title="提升PHP性能之改变Zend引擎分发方式" >提升PHP性能之改变Zend引擎分发方式</a></li><li><a href="http://www.laruence.com/2012/02/08/2528.html"  title="PHP-5.3.9远程执行任意代码漏洞(CVE-2012-0830)" >PHP-5.3.9远程执行任意代码漏洞(CVE-2012-0830)</a></li><li><a href="http://www.laruence.com/2012/02/02/2515.html"  title="我们什么时候应该使用异常?" >我们什么时候应该使用异常?</a></li><li><a href="http://www.laruence.com/2012/02/01/2503.html"  title="使用exit(-1)为什么得到255退出码?" >使用exit(-1)为什么得到255退出码?</a></li><li><a href="http://www.laruence.com/2012/01/11/2482.html"  title="PHP的历史" >PHP的历史</a></li><li><a href="http://www.laruence.com/2012/01/10/2469.html"  title="如何设置一个严格30分钟过期的Session" >如何设置一个严格30分钟过期的Session</a></li><li><a href="http://www.laruence.com/2012/01/07/2453.html"  title="2012年1月全球www网站技术报告" >2012年1月全球www网站技术报告</a></li><li><a href="http://www.laruence.com/2011/12/30/2440.html"  title="PHP5.2.*防止Hash冲突拒绝服务攻击的Patch" >PHP5.2.*防止Hash冲突拒绝服务攻击的Patch</a></li><li><a href="http://www.laruence.com/2011/12/30/2435.html"  title="PHP数组的Hash冲突实例" >PHP数组的Hash冲突实例</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.laruence.com/2008/08/24/377.html/feed</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>

