<?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>PHP源码阅读，PHP设计模式，PHP学习笔记，项目管理-胖胖的空间</title>
	<atom:link href="http://www.phppan.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.phppan.com</link>
	<description>PHP　内核　扩展　程序　项目管理</description>
	<lastBuildDate>Thu, 10 May 2012 00:59:44 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v= 5.0.1 Final</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>在线修改MySQL大表的表结构</title>
		<link>http://www.phppan.com/2012/05/online-schema-change/</link>
		<comments>http://www.phppan.com/2012/05/online-schema-change/#comments</comments>
		<pubDate>Thu, 10 May 2012 00:57:51 +0000</pubDate>
		<dc:creator>胖胖</dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[OSC]]></category>
		<category><![CDATA[大数据量]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1674</guid>
		<description><![CDATA[问题描述
由于某个临时需求，需要给在线MySQL的某个超过千万的表增加一个字段。此表在设计之时完全按照需求实现，并没有多余的保留字段。
我们知道在MySQL中如果要执行ALTER TABLE操作，MySQL... ]]></description>
			<content:encoded><![CDATA[<p>问题描述</p>
<p>由于某个临时需求，需要给在线MySQL的某个超过千万的表增加一个字段。此表在设计之时完全按照需求实现，并没有多余的保留字段。</p>
<p>我们知道在MySQL中如果要执行ALTER TABLE操作，MySQL会通过制作原来表的一个临时副本来工作。对于表结构的修改在副本上施行，然后将新表替换原始表，此时会产生锁表，用户可以从原始表读取数据，而用户的更新和写入操作都会被lock，待新表准备好后写入新表。<br />
这对于在线的数据量较大的表来说是绝对无法容忍的，并且由于这种在线操作时间会很长，此时如果show processlist，会发现有若干的MySQL进程处于lock状态，当这种进程太多超过单台服务器允许的MySQL进程数，其它进程可能会被拒绝连接。</p>
<p>有哪些方案可以处理这个问题呢？<br />
<br />
<strong>方案1、直接ALTER TABLE</strong><br />
这个方案只能说这仅仅是一种方案，在某些非实时在线或数据量较小时有较好的表现。<br />
<br />
<strong>方案２、模拟数据库修改表结构的操作，在非数据库层实现整个过程。</strong></p>
<ol>
<li>实现业务中对于数据的读写分离</li>
<li>创建一个已经按需求修改好结构的新表</li>
<li>修改业务逻辑，将读操作指向旧表，将写操作指向新表。如果读旧表没有，再读新表，并将旧的数据写入到新表，当然这一步写入操作我们可以不用，我们可以在后台做一个定时任务将旧数据同步到新表。</li>
</ol>
<p>这种方案有一个较大的缺点，需要业务逻辑层配合实现数据的迁移，对于业务逻辑有修改，并且如果有多台机器的话，需要一台一台的修改，较费时间，但是对于MySQL的两种主要存储引擎都适用。</p>
<p> <br />
<strong>方案３、facebook <a href="http://bazaar.launchpad.net/~mysqlatfacebook/mysqlatfacebook/tools/files/head:/osc/">online schema change</a></strong><br />
facebook的OSC在整体流程上与方案2没有较大的区别，只是它在这里引入了触发器，从而不需要修改业务逻辑，在数据库层就实现了新数据的两个表的同步问题。其大概步骤如下：</p>
<ol>
<li>按需求创建新表</li>
<li>针对原始表创建触发器</li>
<li>对于原始表的更新操作都会被触发器更新到新表中</li>
<li>把原始表中的数据复制到新表中</li>
<li>将新表替换旧表</li>
</ol>
<p>fb的osc方案从数据库层解决了方案2的问题，但是它仅支持InnoDB存储引擎。</p>
<p> <br />
<strong>方案４、换一个思路，保留字段。</strong><br />
假设一切可以从头再来，我们也许可以加多一些冗余字段，各个类型都加一些，备用。只是，回不去了！<br />
<br />
<strong>方案5、再换一个思路，增加扩展表。</strong><br />
我们不在原有的表的基础上修改了，以增加扩展表的方式，将新字段的数据写入到扩展表中，修改业务逻辑，这些字段从新表中读取。<br />
<a href="http://liuzhiqiangruc.iteye.com/">志强同学</a>说这是典型的维表结构设计。<br />
暂时解决了问题，如果这些字段后续使用频率高的话，可能会有对后期维护或业务有一定的影响。<br />
<br />
<strong>后记</strong><br />
基于现有的需求，只是需要记录新的字段，所以采用了扩展表的方案。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/05/online-schema-change/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>细读PHP的生命周期</title>
		<link>http://www.phppan.com/2012/05/php-life-cycle-2/</link>
		<comments>http://www.phppan.com/2012/05/php-life-cycle-2/#comments</comments>
		<pubDate>Mon, 07 May 2012 11:02:04 +0000</pubDate>
		<dc:creator>胖胖</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单进程生命周期的图，一直也是按这个图理解其生命周期的，可是当准备一次内核分享时，却表现自己没有什么可以说的，于是就有了今天... ]]></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>http://www.phppan.com/2012/05/php-life-cycle-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从PHP的自动测试想到的</title>
		<link>http://www.phppan.com/2012/04/php-test-and-monitor/</link>
		<comments>http://www.phppan.com/2012/04/php-test-and-monitor/#comments</comments>
		<pubDate>Mon, 23 Apr 2012 00:59:52 +0000</pubDate>
		<dc:creator>胖胖</dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>
		<category><![CDATA[监控框架]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1663</guid>
		<description><![CDATA[从PHP的自动测试想到的
昨日，因TIPI项目而阅读了PHP的自动测试实现相关代码。于此，有些许感想，记录如下。
1、用自己测试自己，制定测试过程规范。
PHP的测试环境是用PHP实现的，这不得不... ]]></description>
			<content:encoded><![CDATA[<p>从PHP的自动测试想到的</p>
<p>昨日，因TIPI项目而阅读了PHP的自动测试实现相关代码。于此，有些许感想，记录如下。</p>
<p>1、用自己测试自己，制定测试过程规范。<br />
PHP的测试环境是用PHP实现的，这不得不说是一个创新之举。相对于编译型语言，作为动态语言的PHP在应对变化上有着不少的优势，而测试本来就是一个变化是非较多的地儿。其实用到了PHP的地方只是这个框架的控制器，即源码根目录下run-tests.php文件。作为控制器，它实现了整个测试过程的控制。以一个测试过程为例，总体上分为三个部分：准备、运行和显示结果。准备活动包括测试所必须的环境变量的读取与设置，对测试参数的解析，测试脚本名的解析，各种输出文件的准备 解析测试脚本中的各个段落等；运行活动包括构造测试语句，执行测试语句，得到实际运行结果；显示结果活动包括测试后的结果比对及输出，相关记录记录以及总的测试报告显示。</p>
<p>这个控制器就是PHP自动测试的规范，所有的逻辑都在这一个脚本文件中，在一个时间点上，这是一个不变的过程。对于测试中变化的内容如测试环境，测试输入数据、需要验证的内容以及针对不同输入和不同测试点应该得到的预期结果，这些都存储在PHPT文件中，以不同的标记作为段分开。这些文件按模块划分，一个用例就是一个文件，与将用例写成代码相比，优势不仅仅在于工作量，更多的是在于它的扩展性、可读性和可维护性。</p>
<p>2、简单监控框架</p>
<p>先确认我们这个监控框架的需求什么。现在我们要的是一个可以监控数据是否正常，数据的状态是否符合业务逻辑，并将监控的结果发给相关负责人。从这个简单的需求出发，我们可以发现这里变化的是监控的内容，而不变的是整个监控的流程：查询特定的数据源，根据具体业务确认数据的正确性和合理性，并将结果发送给相关责任人。</p>
<p>对于不变的因素，我们可以以公共模块的方式在代码中实现，如果汇报结果的形式有不同的分类和权限控制的话，我们可以将这些配置放到数据库，当然，我们还是需要在代码中实现这些汇报的方式。</p>
<p>对于变化的因素，我们可以学习PHP的测试过程，以某些特定的规则定义一个一个的监控，我们可以称之为监控用例。在用例中定义名称、输入、过程和预期结果。比如，我们可以定义&#8211;SQL&#8211;字段做数据源。当然，这些内容我们可以分散存储，也可以集中存储在数据库。</p>
<p>这样一种以测试的方式实现监控过程，也许可以试试。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/04/php-test-and-monitor/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP文件上传进度的实现原理</title>
		<link>http://www.phppan.com/2012/04/php-upload-progress/</link>
		<comments>http://www.phppan.com/2012/04/php-upload-progress/#comments</comments>
		<pubDate>Fri, 20 Apr 2012 00:51:38 +0000</pubDate>
		<dc:creator>胖胖</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的uploadprogress扩展。这两种方案存在本质的区别，Flash的上传进度是客户端上传的进度，它是基于本地OS的网络传输... ]]></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"><div 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></div></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"><div 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></div></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(&#8221;upload_$_POST[APC_UPLOAD_PROGRESS]&#8220;));可以得到如下结果：</p>

<div class="wp_syntax"><div 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></div></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>http://www.phppan.com/2012/04/php-upload-progress/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Form表单的enctype属性和method属性</title>
		<link>http://www.phppan.com/2012/04/form-enctype-method/</link>
		<comments>http://www.phppan.com/2012/04/form-enctype-method/#comments</comments>
		<pubDate>Thu, 19 Apr 2012 00:43:39 +0000</pubDate>
		<dc:creator>胖胖</dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[HTML]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1651</guid>
		<description><![CDATA[在WEB开发过程中，Form表单元素是一个使用频率非常高的控件，对于这样一个控件，也许我们并没有认真关注过。今天我们来解读它的enctype属性和method属性。
enctype 属性
enctype属性规定在发送到... ]]></description>
			<content:encoded><![CDATA[<p>在WEB开发过程中，Form表单元素是一个使用频率非常高的控件，对于这样一个控件，也许我们并没有认真关注过。今天我们来解读它的enctype属性和method属性。</p>
<h3><strong>enctype 属性</strong></h3>
<p>enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。它的编码方式有三种：</p>
<ul>
<li>
application/x-www-form-urlencoded编码是以name=value键值对为基础，以&#038;连接；<br />
此为默认值。如果method属性为GET，则编码后的字符串会接到url的后面（其实用其它编码方式，GET的效果也是一样的）。<br />
如果method属性为POST，则编码后的字符串会被封装到HTTP协议的请求实体中，然后发送到服务器。
</li>
<li>
text/plain编码是以name=value键值对为基础，以\r\n连接；如果服务端的程序是PHP的话，使用此编码，如果method为GET，一切和其它编码一样，如果method为POST，则无论是$_GET、$_POST还是$_REQUEST都无法获取数据，为什么呢？因为PHP对于POST方法处理方法中根本就没有针对这种编码的处理函数。当然，我们可以通过php://input或$HTTP_RAW_POST_DATA获取POST过来的原始值。
</li>
<li>
multipart/form-data编码，这是最为特殊的编码；以其Content-Type后面的boundary为分隔符，将各个控件的值包含的请求实体中。
</li>
</ul>
<p>对于POST请求，一般来说用默认的application/x-www-form-urlencoded就可以了。但是如果有文件控件（type=file）的话，就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割，并为每个部分加上 Content-Disposition(form-data或者file)，Content-Type(默认为text/plain，且没有显示),name(控件的name)等信息，并加上分割符(boundary)。</p>
<h3><strong>method 属性</strong></h3>
<p>Form的method属性支持POST和GET方法。默认为GET提交。<br />
GET方法用于信息获取，而且应该是安全的和幂等的。所谓安全指该操作用于获取信息而非修改信息。换句话说，GET请求一般不应产生副作用。相当于SQL中的SELECT操作。所谓幂等指对同一URL的多个请求应该返回同样的结果。比如sina网中点击某一个新闻页面，不同的时候返回应该是同一篇文章，如果后台有修改这条新闻，用户所看到的内容不同，但是我们还是会认为这是幂等的。</p>
<p>POST方法表示可能修改变服务器上的资源的请求。这里的修改包括在服务器上增加资源，修改已有资源或者其它修改类型的操作。</p>
<p>虽然method只支持这两个方法，但是HTTP协议还定义了一些其它的方法：<br />
比如PUT方法，它表示完全替换或更新一个已经存在的资源或创建一个新的资源。PUT与POST的差别是这是一个完整的修改，不存在只修改部分。比如DELETE，它表示删除一个资源。</p>
<p>只是，在实际应用中，为了图方便，我们经常使用GET方法实现修改操作，因为这样我们不需要创建表单，如此而已。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/04/form-enctype-method/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PHP变量的CV类型</title>
		<link>http://www.phppan.com/2012/04/php-compiled-variable/</link>
		<comments>http://www.phppan.com/2012/04/php-compiled-variable/#comments</comments>
		<pubDate>Thu, 05 Apr 2012 01:03:45 +0000</pubDate>
		<dc:creator>胖胖</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 vars:  !0 = $a，或者如果使用更加详细的 -dvld.verbosity=3参数，会看到IS_CV，IS_VAR等类型。这里所说的Comp... ]]></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"><div 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></div></div>

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

<div class="wp_syntax"><div 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></div></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"><div 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></div></div>

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

<div class="wp_syntax"><div 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; ">// 函数中的CV_OF宏定义</span>
	<span style="color: #339933;">#define CV_OF(i)     (EG(current_execute_data)-&gt;CVs[i])</span></pre></div></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>http://www.phppan.com/2012/04/php-compiled-variable/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>胖子的乱看乱想笔记三</title>
		<link>http://www.phppan.com/2012/03/evernote-03/</link>
		<comments>http://www.phppan.com/2012/03/evernote-03/#comments</comments>
		<pubDate>Tue, 27 Mar 2012 00:54:04 +0000</pubDate>
		<dc:creator>胖胖</dc:creator>
				<category><![CDATA[生活杂谈]]></category>
		<category><![CDATA[文摘]]></category>
		<category><![CDATA[时间是把杀猪刀]]></category>
		<category><![CDATA[杂记]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1634</guid>
		<description><![CDATA[1、凡事预则立，不预则废。　－　《礼记.中庸》
2、无论干什么，都要有一种归属感，甚至使命感，才能全情投入。 
3、决策者的七个根性： 沉稳，细心，有胆识，积极，大度，诚信，有担当... ]]></description>
			<content:encoded><![CDATA[<p>1、凡事预则立，不预则废。　－　《礼记.中庸》</p>
<p>2、无论干什么，都要有一种归属感，甚至使命感，才能全情投入。 </p>
<p>3、决策者的七个根性： 沉稳，细心，有胆识，积极，大度，诚信，有担当。﹣ ≪ 赢在决策≫</p>
<p>4、这个世界没有不能用的人，只有用错地方的人。 ﹣ ≪ 赢在决策≫</p>
<p>5、第一，思考你的战略，第二，计划你的工作，第三，教育好你的员工 ﹣ ≪经理人五项修炼≫</p>
<p>6、 危机 = 危险 + 机遇</p>
<p>7、为解决容量问题，需要为应用程序决定一种架构。通常要特别注意进程、网络边界和IO。－≪持续交付≫</p>
<p>8、第一次就把事情做好。﹣余世维</p>
<p>9、之前做了什么？做得怎样？有什么经验和教训？现在正在做什么？做了这些有什么用？以后准备做什么？</p>
<p>10、最理想的任务完成时间：昨天。－　≪做事做到位≫ </p>
<p>11、 上善若水。水善利万物而不争　－　《老子》　不争即争</p>
<p>12、一个人的能力分为必备能力、储备能力、进阶能力。≪中层危机≫ </p>
<p>13、爱你身边的人，他们最重要　－　于威</p>
<p>14、男人一定要像个爷们，这个世界需要你们的肩膀　－　于威</p>
<p>15、正直，勇敢，坚韧，善良，乐观，大气，有了这些，就有了一切。－　于威</p>
<p>16、四行说：你自己得行，得有人说你行，说你行的人得行，你身子骨得行 ﹣≪悟道≫</p>
<p>17、态度比能力重要</p>
<p>18、怨人不如自怨，求诸人不如求之己。 &#8212; 《文子·上德》</p>
<p>19、不要让别人等你。 </p>
<p>20、 奇技淫巧可以用，但不能什么地方都用</p>
<p>21、思路决定出路，态度决定高度</p>
<p>22、最基本的东西往往是最重要的</p>
<p>23、想得开、拿得起、放得下</p>
<p>24、我们，记得说我们，谢谢，记得说谢谢！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/03/evernote-03/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP中的字符串连接操作</title>
		<link>http://www.phppan.com/2012/03/php-string-concat/</link>
		<comments>http://www.phppan.com/2012/03/php-string-concat/#comments</comments>
		<pubDate>Mon, 26 Mar 2012 01:12:12 +0000</pubDate>
		<dc:creator>胖胖</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[上周和刘志强同学讨论字符串的连接操作：
一般情况下我们用点号做字符串的连接操作，但是如果在某个长字符串中放一个变量，通常我们会采用在字符串中直接写入一个变量的方式来实现

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

<div class="wp_syntax"><div 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; ">//或</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></div></div>

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

<div class="wp_syntax"><div 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></div></div>

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

<div class="wp_syntax"><div 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></div></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"><div 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; ">/* 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;
	memcpy<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>
	memcpy<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>
	memcpy<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></div></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"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #666666; ">// 添加字符串</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; ">//添加变量</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></div></div>

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

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; ">/* 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; ">/* {{{ */</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>
	memcpy<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; ">/* }}} */</span></pre></div></div>

<p>add_string_to_string函数的实现过程是针对即将生成的字符串的大小重新通过PHP内核的内存管理扩展内存空间（如果当前空间后续的内存够用，则天下太平，如果空间不够，则重新分配空间并执行拷贝操作），并将新的字符串复制到原始字串后面内存空间的过程。<br />
我们的示例执行了三次添加操作，也就执行了三次内存扩展操作和三次拷贝操作。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/03/php-string-concat/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于列表推导式</title>
		<link>http://www.phppan.com/2012/03/list-comprehensions/</link>
		<comments>http://www.phppan.com/2012/03/list-comprehensions/#comments</comments>
		<pubDate>Mon, 19 Mar 2012 03:40:35 +0000</pubDate>
		<dc:creator>胖胖</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[程序相关]]></category>
		<category><![CDATA[列表推导式]]></category>
		<category><![CDATA[胖子乱想的]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1626</guid>
		<description><![CDATA[列表推导式最开始是一些函数式编译语言的句法特征，比如模式匹配，它能极大提高函数式程序的读写能力。最开始并没有列表推导式，只有集合推导式（Set Comprehensions），列表推导式第一次... ]]></description>
			<content:encoded><![CDATA[<p>列表推导式最开始是一些函数式编译语言的句法特征，比如模式匹配，它能极大提高函数式程序的读写能力。最开始并没有列表推导式，只有集合推导式（Set Comprehensions），列表推导式第一次使用是Turner1982年在KRC（Kent Recursive Calculator）上。列表推导式曾经在各种函数式编程语言中出现，如Miranda(一种纯粹的函数式编程语言)，Orwell(一种lazy函数式编程语言，对Haskell有较大影响)。</p>
<p>曾经列表推导式被称为集合抽象，由于抽象(abstractions)这个词语的英文单词在若干个地方有用到，其意思太多了，所以引入了推导式(comprehensions)这样一个词语。从数学上的策梅洛-弗兰克尔集合论（Zermelo-Fraenkel Set Theory）（http://en.wikipedia.org/wiki/Zermelo-Frankel_set_theory）　来看，列表推导式和集合推导式类似。比如求集合Ａ中奇数的平方</p>

<div class="wp_syntax"><div class="code"><pre class="shell" style="font-family:monospace;">B = {square x | x ∈ A &amp; odd x}</pre></div></div>

<p>上面的这个示例，如果A集合是{1,2,3,4}，那么B集合为{1,9}</p>
<p>如果我们把这些数学符号换成常见的编程符号，如：</p>

<div class="wp_syntax"><div class="code"><pre class="shell" style="font-family:monospace;">ys = [square x | x &lt;- xs; odd x]</pre></div></div>

<p>或者我们将<-再变为for in，再加上if语句，是不是就是Python的列表推导式了。</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">vec = <span style="color: black;">&#91;</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">2</span>, <span style="color: #ff4500;">3</span>, <span style="color: #ff4500;">4</span><span style="color: black;">&#93;</span>
rs = <span style="color: black;">&#91;</span>x <span style="color: #66cc66;">*</span> x <span style="color: #ff7700;font-weight:bold;">for</span> x <span style="color: #ff7700;font-weight:bold;">in</span> vec <span style="color: #ff7700;font-weight:bold;">if</span> x <span style="color: #66cc66;">%</span> <span style="color: #ff4500;">2</span> <span style="color: #66cc66;">!</span>= <span style="color: #ff4500;">0</span><span style="color: black;">&#93;</span>
<span style="color: #ff7700;font-weight:bold;">print</span> rs</pre></div></div>

<p> 对应上面的Python示例，我们看下在Python中，列表推导式的一般形式：</p>

<div class="wp_syntax"><div class="code"><pre class="shell" style="font-family:monospace;">[表达式 for item1 in 序列1 ... for itemN in 序列N if 条件表达式]</pre></div></div>

<p>上面的表达式分为三部分，最左边是生成每个元素的表达式，然后是for 迭代过程，最右边可以设定一个if 判断作为过滤条件。</p>
<p>[]内的列表写以写为一行，也可以写为多行，一般来说多行更易读些，看个人喜好吧。 </p>
<p>对于Python而言，列表推导式（List Comprehensions）是其最强有力的语法之一，常用于从集合对象中有选择地获取并计算元素，虽然多数情况下可以使用for、if等语句组合完成同样的任务。</p>
<p>其本质是一种语法糖，它提供了一种简洁高效的方式来创建列表和迭代器, 而不必借助map(), filter(), 或者lambda。<br />
简单的列表推导可以比其它的列表创建方法更加清晰简单. 生成器表达式可以十分高效, 因为它们避免了创建整个列表。这里的优点一般是指使用简单的列表推导式时，而对于复杂的列表推导式虽然可以高效，但是生成的表达式可能难以阅读（不排除通过某些注释或排版达到优化可读性的目的）。列表推导式适用于简单情况. 每个部分应该单独置于一行: 映射表达式, for语句, 过滤器表达式. 禁止多重for语句或过滤器表达式. 复杂情况下还是使用循环吧。</p>
<p>如果我们要用PHP去实现列表推导式，应当如何表现呢？（这里假设我们需要实现这样一个语法糖）</p>
<p>有如下想法，其一般形式如下：</p>

<div class="wp_syntax"><div class="code"><pre class="shell" style="font-family:monospace;">list{表达式1, 表达式2, ... if (条件表达式),  $list1 as $key1 =&gt; $row1, $list2 as $key2 =&gt; $row2, ...}
&nbsp;
&nbsp;
//如下示例：
&nbsp;
list{echo $key1, echo $row2, if ($key1 &gt; $key2), $a as $key =&gt; $row, $b as $key2 =&gt; $row2, }</pre></div></div>

<p>在if语句前可以有多个表达式处理，以逗号隔开；<br />
在if语句后面可以有多个列表，以逗号隔开；</p>
<p>也许这个YY有点纠结，只是对于PHP来说，这个糖果也许没那么重要？</p>
<p> 参考资料：</p>
<p>Jones &#8211; 《The Implementation of Functional Programming Languages》, PH, 1987</p>
<p>http://zh-google-styleguide.googlecode.com/hg-history/2a227ce093e7b70085818bba22061d9393f3bb99/pyguide/python_language_rules.txt</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/03/list-comprehensions/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PHP内核中文件上传类型的获取过程</title>
		<link>http://www.phppan.com/2012/03/files-type/</link>
		<comments>http://www.phppan.com/2012/03/files-type/#comments</comments>
		<pubDate>Sun, 11 Mar 2012 15:18:45 +0000</pubDate>
		<dc:creator>胖胖</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1621</guid>
		<description><![CDATA[我们在做WEB应用开发时，经常会遇到文件上传的需求，文件作为一种中间介质将一些信息传递给我们。以PHP为例，如果我们需要实现一个简单的文件上传（假设我们的测试服务器为Apache），首... ]]></description>
			<content:encoded><![CDATA[<p>我们在做WEB应用开发时，经常会遇到文件上传的需求，文件作为一种中间介质将一些信息传递给我们。以PHP为例，如果我们需要实现一个简单的文件上传（假设我们的测试服务器为Apache），首先我们需要有一个前台页面让用户选择文件，这里以一个文件的上传为例：</p>

<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;form name=&quot;upload&quot; action=&quot;upload_test.php&quot; method=&quot;POST&quot; enctype=&quot;multipart/form-data&quot;&gt;
&lt;input type=&quot;hidden&quot; value=&quot;1024&quot; name=&quot;MAX_FILE_SIZE&quot; /&gt;
请选择文件:&lt;input name=&quot;ufile&quot; type=&quot;file&quot; /&gt;
&lt;input type=&quot;submit&quot; value=&quot;Just Upload it&quot; /&gt;
&lt;/form&gt;</pre></div></div>

<p>当我们选择点击提交按钮时，浏览器会将数据提交给服务器。通过Filddle我们可以看到其提交的请求头如下：</p>

<div class="wp_syntax"><div class="code"><pre class="shell" style="font-family:monospace;">POST http://localhost/test/upload_test.php HTTP/1.1
Host: localhost
Connection: keep-alive
Content-Length: 1347
Cache-Control: max-age=0
Origin: http://localhost
User-Agent: //省略若干
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBq7AMhcljN14rJrU 
&nbsp;
// 上面的是关键
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://localhost/test/test.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
&nbsp;
// 以下为POST提交的内容
&nbsp;
------WebKitFormBoundaryBq7AMhcljN14rJrU
Content-Disposition: form-data; name=&quot;MAX_FILE_SIZE&quot;
&nbsp;
10240
------WebKitFormBoundaryBq7AMhcljN14rJrU
Content-Disposition: form-data; name=&quot;ufile&quot;; filename=&quot;logo.png&quot;
Content-Type: image/png //这里就是我们想要的文件类型
&nbsp;
//以下为文件内容</pre></div></div>

<p>如果我们在upload_test.php文件中打印$_FILES，可以看到上传文件类型为image/png。</p>
<p>对应上面的请求头，image/png在文件内容输出的前面的Content-Type字段中。</p>
<p>基本上我们知道了上传的文件类型是浏览器自己识别，直接以文件的Content-Type字段传递给服务器。那么这些内容在PHP中是如何解析的呢？</p>
<h2>文件类型获取过程</h2>
<p>当客户端发起文件提交请求时，Apache会将所接收到的内容转交给mod_php5模块。<br />
当PHP接收到请求后，首先会调用sapi_activate，在此函数中程序会根据请求的方法处理数据，如我们示例的POST的方法：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>strcmp<span style="color: #009900;">&#40;</span>SG<span style="color: #009900;">&#40;</span>request_info<span style="color: #009900;">&#41;</span>.<span style="color: #202020;">request_method</span><span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;POST&quot;</span><span style="color: #009900;">&#41;</span>
<span style="color: #339933;">&amp;&amp;</span> <span style="color: #009900;">&#40;</span>SG<span style="color: #009900;">&#40;</span>request_info<span style="color: #009900;">&#41;</span>.<span style="color: #202020;">content_type</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #808080; ">/* HTTP POST -&gt; may contain form data to be read into variables
depending on content type given
*/</span>
sapi_read_post_data<span style="color: #009900;">&#40;</span>TSRMLS_C<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>sapi_read_post_data在main/SAPI.c中实现，它会根据POST内容的Content-Type类型来选择处理POST内容的方法。</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>zend_hash_find<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>SG<span style="color: #009900;">&#40;</span>known_post_content_types<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> content_type<span style="color: #339933;">,</span>
content_type_length<span style="color: #339933;">+</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span> <span style="color: #339933;">**</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span>post_entry<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> SUCCESS<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
<span style="color: #808080; ">/* found one, register it for use */</span>
SG<span style="color: #009900;">&#40;</span>request_info<span style="color: #009900;">&#41;</span>.<span style="color: #202020;">post_entry</span> <span style="color: #339933;">=</span> post_entry<span style="color: #339933;">;</span>
post_reader_func <span style="color: #339933;">=</span> post_entry<span style="color: #339933;">-&gt;</span>post_reader<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>以上代码的关键在于SG(known_post_content_types)变量在哪里初始化，其基本过程如下：</p>

<div class="wp_syntax"><div class="code"><pre class="shell" style="font-family:monospace;">sapi_startup
sapi_globals_ctor(&amp;sapi_globals);
php_setup_sapi_content_types(TSRMLS_C);
sapi_register_post_entries(php_post_entries TSRMLS_CC);</pre></div></div>

<p>这里的的php_post_entries定义在main/php_content_types.c文件。如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; ">/* {{{ php_post_entries[]
*/</span>
<span style="color: #993333;">static</span> sapi_post_entry php_post_entries<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
<span style="color: #009900;">&#123;</span> DEFAULT_POST_CONTENT_TYPE<span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>DEFAULT_POST_CONTENT_TYPE<span style="color: #009900;">&#41;</span><span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span> sapi_read_standard_form_data<span style="color: #339933;">,</span> php_std_post_handler <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
<span style="color: #009900;">&#123;</span> MULTIPART_CONTENT_TYPE<span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span>MULTIPART_CONTENT_TYPE<span style="color: #009900;">&#41;</span><span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span> NULL<span style="color: #339933;">,</span> rfc1867_post_handler <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
<span style="color: #009900;">&#123;</span> NULL<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> NULL<span style="color: #339933;">,</span> NULL <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
<span style="color: #808080; ">/* }}} */</span>
&nbsp;
<span style="color: #339933;">#define MULTIPART_CONTENT_TYPE &quot;multipart/form-data&quot;</span>
&nbsp;
<span style="color: #339933;">#define DEFAULT_POST_CONTENT_TYPE &quot;application/x-www-form-urlencoded&quot;</span></pre></div></div>

<p>嗯，这里的MULTIPART_CONTENT_TYPE（multipart/form-data）所对应的rfc1867_post_handler方法就是我们今天要找的核心函数，其定义在main/rfc1867.c文件：SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler)</p>
<p>后面获取Content-Type的过程就比较简单了：</p>
<ul>
<li>通过multipart_buffer_eof控制循环，遍历所有的multipart部分</li>
<li>通过multipart_buffer_headers获取multipart部分的头部信息</li>
<li>通过php_mime_get_hdr_value(header, &#8220;Content-Type&#8221;)获取类型</li>
<li>通过register_http_post_files_variable(lbuf, cd, http_post_files, 0 TSRMLS_CC);将数据写到$_FILES变量。</li>
</ul>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">SAPI_API SAPI_POST_HANDLER_FUNC<span style="color: #009900;">&#40;</span>rfc1867_post_handler<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
&nbsp;
<span style="color: #666666; ">//若干省略</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>multipart_buffer_eof<span style="color: #009900;">&#40;</span>mbuff TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>multipart_buffer_headers<span style="color: #009900;">&#40;</span>mbuff<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>header TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">goto</span> fileupload_done<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #666666; ">//若干省略</span>
	<span style="color: #808080; ">/* Possible Content-Type: */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>cancel_upload <span style="color: #339933;">||</span> <span style="color: #339933;">!</span><span style="color: #009900;">&#40;</span>cd <span style="color: #339933;">=</span> php_mime_get_hdr_value<span style="color: #009900;">&#40;</span>header<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Content-Type&quot;</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>
		cd <span style="color: #339933;">=</span> <span style="color: #ff0000;">&quot;&quot;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span> 
	<span style="color: #808080; ">/* fix for Opera 6.01 */</span>
		s <span style="color: #339933;">=</span> strchr<span style="color: #009900;">&#40;</span>cd<span style="color: #339933;">,</span> <span style="color: #ff0000;">';'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>s <span style="color: #339933;">!=</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			<span style="color: #339933;">*</span>s <span style="color: #339933;">=</span> <span style="color: #ff0000;">'<span style="color: #006699; font-weight: bold;">\0</span>'</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
<span style="color: #666666; ">//若干省略</span>
	<span style="color: #808080; ">/* Add $foo[type] */</span>
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>is_arr_upload<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        	snprintf<span style="color: #009900;">&#40;</span>lbuf<span style="color: #339933;">,</span> llen<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;%s[type][%s]&quot;</span><span style="color: #339933;">,</span> abuf<span style="color: #339933;">,</span> array_index<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
		snprintf<span style="color: #009900;">&#40;</span>lbuf<span style="color: #339933;">,</span> llen<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;%s[type]&quot;</span><span style="color: #339933;">,</span> param<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
	register_http_post_files_variable<span style="color: #009900;">&#40;</span>lbuf<span style="color: #339933;">,</span> cd<span style="color: #339933;">,</span> http_post_files<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span> TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #666666; ">//若干省略</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>其它的$_FILES中的size、name等字段，其实现过程与type类似</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/03/files-type/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>如何在用户中断时停止程序的运行</title>
		<link>http://www.phppan.com/2012/02/stop-program-by-user-abort/</link>
		<comments>http://www.phppan.com/2012/02/stop-program-by-user-abort/#comments</comments>
		<pubDate>Mon, 27 Feb 2012 00:59:34 +0000</pubDate>
		<dc:creator>胖胖</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[connection_status]]></category>
		<category><![CDATA[ignore_user_abort]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>
		<category><![CDATA[中断]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1612</guid>
		<description><![CDATA[当我们以WEB的方式运行PHP脚本时，默认情况下，即使你关闭当前页面，程序也会继续执行，直接程序结束或超时。如果我们想在用户关闭页面或点击了停止页面运行时就中断程序，我们需要做... ]]></description>
			<content:encoded><![CDATA[<p>当我们以WEB的方式运行PHP脚本时，默认情况下，即使你关闭当前页面，程序也会继续执行，直接程序结束或超时。如果我们想在用户关闭页面或点击了停止页面运行时就中断程序，我们需要做些什么呢？上周和小毅同学讨论了这个问题，从而也引出了今天我们这篇文章。</p>
<p>我们知道HTTP协议是基于TCP/IP协议，对于一个PHP页面的请求就是一个HTTP请求（假设我们是Apache服务器），从而会创建TCP连接，当用户中断请求时，会给服务器一个abort状态。这个abort状态就是今天我们要讲的关键点。</p>
<style>
.entry p {margin:13px 5px 0 5px;}
</style>
<p>在PHP中有一个函数与abort状态有关：ignore_user_abort函数<br />
ignore_user_abort() 函数设置与客户机断开时是否会终止脚本的执行。它返回 user-abort 之前设置的布尔值。它的参数可选。如果设置为 true，则忽略与用户的断开，如果设置为 false，会导致脚本停止运行。</p>
<p>PHP 不会检测到用户是否已断开连接，直到尝试向客户机发送信息为止。因此如果我们只是使用echo语句，可能无法如实的看到abort的效果，因为PHP在输出时会有一个缓存，如果要刷新缓存，则可以使用flush() 函数。</p>
<p>如下代码t.php：</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #990000;">ignore_user_abort</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">TRUE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">set_time_limit</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">50</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$i</span><span style="color: #339933;">++,</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>    
    <span style="color: #990000;">flush</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000088;">$fp</span> <span style="color: #339933;">=</span> <span style="color: #990000;">fopen</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;data.txt&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'a'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">fwrite</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$fp</span><span style="color: #339933;">,</span> <span style="color: #000088;">$i</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot; <span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">fclose</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$fp</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <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: #009900;">&#125;</span></pre></div></div>

<p>在浏览器中执行这段代码，过了大概两秒后，关闭请求的页面，50秒后，你会发现在data.txt文件中写入了至少50个数。这表示我们的中断操作是无效的。<br />
如果我们改一下，把第一句改为：ignore_user_abort(FALSE);，重复上面的操作，你会发现，只写入了极少的数字，这表示我们的中断操作有效了。<br />
现在通过ignore_user_abort函数，我们实现了用户中断就马上停止程序的操作。这里有一个问题，即我们需要不停的flush，通过flush函数来更新连接状态，当状态为abort时，程序根据ignore_user_abort的设置来判断是否中断程序。除此之外，我们也可以使用直接获取连接状态来check连接状态，并对特定的状态作出处理，如下代码：</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #990000;">ignore_user_abort</span><span style="color: #009900;">&#40;</span><span style="color: #009900; font-weight: bold;">FALSE</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #990000;">set_time_limit</span><span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">50</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #b1b100;">echo</span> <span style="color: #000088;">$i</span><span style="color: #339933;">++,</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">flush</span><span style="color: #009900;">&#40;</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><span style="color: #990000;">connection_status</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> CONNECTION_NORMAL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">break</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #000088;">$fp</span> <span style="color: #339933;">=</span> <span style="color: #990000;">fopen</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;data.txt&quot;</span><span style="color: #339933;">,</span> <span style="color: #0000ff;">'a'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">fwrite</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$fp</span><span style="color: #339933;">,</span> <span style="color: #000088;">$i</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot;:&quot;</span> <span style="color: #339933;">.</span> <span style="color: #990000;">connection_status</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot; <span style="color: #000099; font-weight: bold;">\r</span><span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #990000;">fclose</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$fp</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <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: #009900;">&#125;</span></pre></div></div>

<p>这里的connection_status函数的作用是获取连接的状态，当连接的状态非normal时，我们就中断循环，从而也达到了中断程序的操作。这个示例与前面的示例不同之处在于中断操作是由我们自己控制，而不是通过flush操作直接exit。如果在用户中断后还有一些其它的操作，这种方式会更合适一些。当然，这里的flush操作依旧不可少，我们还是需要通过这个函数做check操作。</p>
<p>ignore_user_abort函数和connection_status函数都实现了我们的目的，这两个函数的实现有没有关联？我们在ext/standard/basic_functions.c文件中找到这两个函数的实现如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; ">/* {{{ proto int connection_aborted(void)
&nbsp;
Returns true if client disconnected */</span>
PHP_FUNCTION<span style="color: #009900;">&#40;</span>connection_aborted<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    RETURN_LONG<span style="color: #009900;">&#40;</span>PG<span style="color: #009900;">&#40;</span>connection_status<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;</span> PHP_CONNECTION_ABORTED<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; ">/* }}} */</span>
&nbsp;
<span style="color: #808080; ">/* {{{ proto int connection_status(void)
Returns the connection status bitfield */</span>
PHP_FUNCTION<span style="color: #009900;">&#40;</span>connection_status<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    RETURN_LONG<span style="color: #009900;">&#40;</span>PG<span style="color: #009900;">&#40;</span>connection_status<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; ">/* }}} */</span>
&nbsp;
<span style="color: #808080; ">/* {{{ proto int ignore_user_abort([string value])
Set whether we want to ignore a user abort event or not */</span>
PHP_FUNCTION<span style="color: #009900;">&#40;</span>ignore_user_abort<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>arg <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> arg_len <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> old_setting<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>zend_parse_parameters<span style="color: #009900;">&#40;</span>ZEND_NUM_ARGS<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> TSRMLS_CC<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;|s&quot;</span><span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>arg<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>arg_len<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> FAILURE<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">return</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    old_setting <span style="color: #339933;">=</span> PG<span style="color: #009900;">&#40;</span>ignore_user_abort<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>arg<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        zend_alter_ini_entry_ex<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;ignore_user_abort&quot;</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;ignore_user_abort&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> arg<span style="color: #339933;">,</span> arg_len<span style="color: #339933;">,</span> PHP_INI_USER<span style="color: #339933;">,</span>     PHP_INI_STAGE_RUNTIME<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span> TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    RETURN_LONG<span style="color: #009900;">&#40;</span>old_setting<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; ">/* }}} */</span></pre></div></div>

<p>connection_status函数直接返回PG(connection_status)的值，</p>
<p>ignore_user_abort函数重新设置PG(ignore_user_abort)的值，</p>
<p>不管是因为缓存满自动调用或通过flush函数调用的flush操作，其最终都会根据连接状态判断是否执行php_handle_aborted_connection函数，如果是abort状态，则执行。</p>
<p>其代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; ">/* {{{ php_handle_aborted_connection
*/</span>
PHPAPI <span style="color: #993333;">void</span> php_handle_aborted_connection<span style="color: #009900;">&#40;</span><span style="color: #993333;">void</span><span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    TSRMLS_FETCH<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    PG<span style="color: #009900;">&#40;</span>connection_status<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> PHP_CONNECTION_ABORTED<span style="color: #339933;">;</span>
    php_output_set_status<span style="color: #009900;">&#40;</span><span style="color: #0000dd;">0</span> TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>PG<span style="color: #009900;">&#40;</span>ignore_user_abort<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        zend_bailout<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; ">/* }}} */</span></pre></div></div>

<p>在PG(ignore_user_abort)为假时，即不忽略用户的中断行为时，如果调用了此函数，则使用zend_bailout函数跳出程序直接exit。</p>
<p>在默认情况下ignore_user_abort为0，即不忽略用户的中断行为。</p>
<p>如果你是ubuntu的默认apache环境下，可能上面的代码会无效。这是由于此环境下的apache开启了zip，在没有达到预定的大小时，服务器不会与客户端通信，从而也就无法获取客户端的状态，即使使用了flush函数也是一样。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/02/stop-program-by-user-abort/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>PHP中计算字符串相似度的函数</title>
		<link>http://www.phppan.com/2012/02/php-similar-text/</link>
		<comments>http://www.phppan.com/2012/02/php-similar-text/#comments</comments>
		<pubDate>Tue, 21 Feb 2012 00:51:27 +0000</pubDate>
		<dc:creator>胖胖</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[PHP源码阅读笔记]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1602</guid>
		<description><![CDATA[上次reeze提到similar_text函数，这个真心没用过。
在手册上查找其说明如下：
similar_text — 计算两个字符串的相似度
int similar_text ( string $first , string $second [, float &#038;$percent ] )
$first 	必需。规定... ]]></description>
			<content:encoded><![CDATA[<p>上次<a href="http://reeze.cn/">reeze</a>提到similar_text函数，这个真心没用过。<br />
在手册上查找其说明如下：<br />
similar_text — 计算两个字符串的相似度<br />
int similar_text ( string $first , string $second [, float &#038;$percent ] )<br />
$first 	必需。规定要比较的第一个字符串。<br />
$second 	必需。规定要比较的第二个字符串。<br />
$percent 	可选。规定供存储百分比相似度的变量名。</p>
<style>
.entry p {margin:13px 5px 0 5px;}
</style>
<p>两个字符串的相似程度计算依据 Oliver [1993] 的描述进行。注意该实现没有使用 Oliver 虚拟码中的堆栈，但是却进行了递归调用，这个做法可能会导致整个过程变慢或变快。也请注意，该算法的复杂度是 O(N**3)，N 是最长字符串的长度。</p>
<p>比如我们想找字符串abcdefg和字符串aeg的相似度：</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$first</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;abcdefg&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$second</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;aeg&quot;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">echo</span> <span style="color: #990000;">similar_text</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$first</span><span style="color: #339933;">,</span> <span style="color: #000088;">$second</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p>结果输出3.如果想以百分比显示，则可使用它的第三个参数,如下：</p>

<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000088;">$first</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;abcdefg&quot;</span><span style="color: #339933;">;</span>
<span style="color: #000088;">$second</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;aeg&quot;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #990000;">similar_text</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$first</span><span style="color: #339933;">,</span> <span style="color: #000088;">$second</span><span style="color: #339933;">,</span> <span style="color: #000088;">$percent</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">echo</span> <span style="color: #000088;">$percent</span><span style="color: #339933;">;</span></pre></div></div>

<p>这里的相似度的算法是什么呢？本来是想看看Oliver[1993]对于这个算法的具体描述，各种google后，只找到这是从Ian Oliver1993年出版的书《Programming classics: implementing the world&#8217;s best algorithms》中记载，没有找到这本书的电子版。</p>
<p>直接代码，在string.c文件中我们找到了similar_text的实现PHP_FUNCTION(similar_text)，其最终调用php_similar_cha获取两个字符串的相似度，如下代码：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> php_similar_char<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>txt1<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> len1<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>txt2<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> len2<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">int</span> sum<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> pos1<span style="color: #339933;">,</span> pos2<span style="color: #339933;">,</span> max<span style="color: #339933;">;</span>
&nbsp;
    php_similar_str<span style="color: #009900;">&#40;</span>txt1<span style="color: #339933;">,</span> len1<span style="color: #339933;">,</span> txt2<span style="color: #339933;">,</span> len2<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>pos1<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>pos2<span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>max<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>sum <span style="color: #339933;">=</span> max<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>pos1 <span style="color: #339933;">&amp;&amp;</span> pos2<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            sum <span style="color: #339933;">+=</span> php_similar_char<span style="color: #009900;">&#40;</span>txt1<span style="color: #339933;">,</span> pos1<span style="color: #339933;">,</span> txt2<span style="color: #339933;">,</span> pos2<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span>pos1 <span style="color: #339933;">+</span> max <span style="color: #339933;">&lt;</span> len1<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #009900;">&#40;</span>pos2 <span style="color: #339933;">+</span> max <span style="color: #339933;">&lt;</span> len2<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span> 
             sum <span style="color: #339933;">+=</span> php_similar_char<span style="color: #009900;">&#40;</span>txt1 <span style="color: #339933;">+</span> pos1 <span style="color: #339933;">+</span> max<span style="color: #339933;">,</span> len1 <span style="color: #339933;">-</span> pos1 <span style="color: #339933;">-</span> max<span style="color: #339933;">,</span> 
                                               txt2 <span style="color: #339933;">+</span> pos2 <span style="color: #339933;">+</span> max<span style="color: #339933;">,</span> len2 <span style="color: #339933;">-</span> pos2 <span style="color: #339933;">-</span> max<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> sum<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>首先我们看php_similar_str函数的作用，从函数名和参数名我们可以大致猜测它的作用是求两个字符串的相似子串，具体代码如下：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> php_similar_str<span style="color: #009900;">&#40;</span><span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>txt1<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> len1<span style="color: #339933;">,</span> <span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>txt2<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> len2<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>pos1<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>pos2<span style="color: #339933;">,</span> <span style="color: #993333;">int</span> <span style="color: #339933;">*</span>max<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>p<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>q<span style="color: #339933;">;</span> 
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>end1 <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> txt1 <span style="color: #339933;">+</span> len1<span style="color: #339933;">;</span>
    <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>end2 <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> txt2 <span style="color: #339933;">+</span> len2<span style="color: #339933;">;</span>
    <span style="color: #993333;">int</span> l<span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #339933;">*</span>max <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
    <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>p <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> txt1<span style="color: #339933;">;</span> p <span style="color: #339933;">&lt;</span> end1<span style="color: #339933;">;</span> p<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>q <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> txt2<span style="color: #339933;">;</span> q <span style="color: #339933;">&lt;</span> end2<span style="color: #339933;">;</span> q<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>l <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> <span style="color: #009900;">&#40;</span>p <span style="color: #339933;">+</span> l <span style="color: #339933;">&lt;</span> end1<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #009900;">&#40;</span>q <span style="color: #339933;">+</span> l <span style="color: #339933;">&lt;</span> end2<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> <span style="color: #009900;">&#40;</span>p<span style="color: #009900;">&#91;</span>l<span style="color: #009900;">&#93;</span> <span style="color: #339933;">==</span> q<span style="color: #009900;">&#91;</span>l<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> l<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #666666; ">//我是分号</span>
            <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>l <span style="color: #339933;">&gt;</span> <span style="color: #339933;">*</span>max<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #339933;">*</span>max <span style="color: #339933;">=</span> l<span style="color: #339933;">;</span>
                <span style="color: #339933;">*</span>pos1 <span style="color: #339933;">=</span> p <span style="color: #339933;">-</span> txt1<span style="color: #339933;">;</span>
                <span style="color: #339933;">*</span>pos2 <span style="color: #339933;">=</span> q <span style="color: #339933;">-</span> txt2<span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>真心很直白的三重循环，求两个字符串的最大相似子串的长度，以及这两个子串相等的开始位置。</p>
<p>在了解了php_similar_str的作用后，回到php_similar_char函数。这是一个很直白的二分算法。以当前两个字符串的最大相似子串的位置为分隔，向两边二分查找相似子串，最终得到所有的相似子串长度的总和，这也就是我们这个函数的相似度算法：从最长子串开始，依次统计所有的子串长度。</p>
<p>那么这里的百分比是如何计算的呢？</p>
<p>在PHP_FUNCTION(similar_text)的函数体中，如下代码：</p>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;">sim <span style="color: #339933;">=</span> php_similar_char<span style="color: #009900;">&#40;</span>t1<span style="color: #339933;">,</span> t1_len<span style="color: #339933;">,</span> t2<span style="color: #339933;">,</span> t2_len<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>ac <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">2</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    Z_DVAL_PP<span style="color: #009900;">&#40;</span>percent<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> sim <span style="color: #339933;">*</span> <span style="color:#800080;">200.0</span> <span style="color: #339933;">/</span> <span style="color: #009900;">&#40;</span>t1_len <span style="color: #339933;">+</span> t2_len<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>sim是相似度的值，百分比是直接 sim * 200 / 两个字符串的长度。</p>
<p>关于那本书：</p>
<p>名称	Programming classics: implementing the world&#8217;s best algorithms<br />
作者	Ian Oliver<br />
出版商	Prentice Hall, 1993<br />
出处：	密歇根大学<br />
数字化处理时间	2007年11月15日<br />
ISBN	0131004131, 9780131004139<br />
页数	386 页<br />
这里也许可以下载到<br />
http://filecom.net/8EMrrcoyc8/<br />
http://ebooks-files.org/download/programming-classics-implementing-the-worlds-best-algorithms.html</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/02/php-similar-text/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>再读《程序员修炼之道》总结</title>
		<link>http://www.phppan.com/2012/02/the-pragmatic-programmer/</link>
		<comments>http://www.phppan.com/2012/02/the-pragmatic-programmer/#comments</comments>
		<pubDate>Sat, 18 Feb 2012 13:25:44 +0000</pubDate>
		<dc:creator>胖胖</dc:creator>
				<category><![CDATA[生活杂谈]]></category>
		<category><![CDATA[时间是把杀猪刀]]></category>
		<category><![CDATA[读书总结]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1607</guid>
		<description><![CDATA[虽然这本书的中文译名很文艺，但是内容确实值得一看，又花了一个星期的早晨将这本《程序员修炼之道》看了一遍，这次对于每个小节都写了些笔记和摘抄，也许是错的，也许没有什么道理... ]]></description>
			<content:encoded><![CDATA[<p>虽然这本书的中文译名很文艺，但是内容确实值得一看，又花了一个星期的早晨将这本《程序员修炼之道》看了一遍，这次对于每个小节都写了些笔记和摘抄，也许是错的，也许没有什么道理，只是当时的感触和想到的。内容整理如下：</p>
<p>1、我的源码让猫给吃了： 责任、风险、应急备案、不要找借口，真诚</p>
<p>2、软件的熵：破窗户理论、酒与污水理论</p>
<style>
.entry p {margin:13px 5px 0 5px;}
</style>
<p>3、石头汤与煮青蛙： 好的愿景和目标、不谋全局者不足以谋一域</p>
<p>4、足够好的软件：细化非功能性需求、过早优化是万恶之源</p>
<p>5、你的知识资产：养成学习的习惯，知识上的投资总能得到最好的回报</p>
<p>6、交流： 准备好你的交流</p>
<p>7、 重复的危害：DRY原则</p>
<p>8、正交性：高内聚，低耦合；模块化，组件化</p>
<p>9、可撤销性：良好的抽象接口让我们更灵活</p>
<p>10、曳光弹：让程序先跑起来</p>
<p>11、原型与便笺：为了学习，可看不可用。</p>
<p>12、领域语言： 语言会影响你思考问题的方式，合适的才是好的。</p>
<p>13、估算：估算会加深对需求的理解，</p>
<p>14、纯文本的威力：自描述，可读</p>
<p>15、shell游戏：GUI局限了用户的思维，但也提供了一些方便</p>
<p>16、强力编辑：选你所爱的，爱你所选的</p>
<p>17、源码控制：记住过去，人生要是有版本控制会是一个怎样的结果？</p>
<p>18、调试：调试是为了解决问题，心态很重要， 反思BUG产生的原因 </p>
<p>19、 文本操纵：懂一门脚本语言</p>
<p>20、代码生成器：参数化模板，预处理，关注变化的地方</p>
<p>21、按合约设计： DBC，鸭子类型？找出业务规则并封装规则的变化</p>
<p>22、死程序不说谎： switch语句中的default子句的存在是为了让我们发现何时发生了不可能的事情，暴露错误，早崩溃</p>
<p>23、断言式编程：有选择的使用和开启</p>
<p>24、何时使用异常：将异常用于异常的问题</p>
<p>25、怎样配平资源： 处理资源要有始有终，尽量在分配的地方释放</p>
<p>26、 解耦与得墨忒耳法则：最少知识原则，不要和陌生人说话，对象的任何方法都应该只调用它自身、传入此方法的参数、它创建的对象以及它直接持有的组件</p>
<p>27、源程序设计：配置，将变化量放到元数据</p>
<p>28、时间耦合：并发的本质问题之一是时间</p>
<p>29、它只是视图：MVC</p>
<p>30、黑板：mediator模式</p>
<p>31、靠巧合编程： 知道你在做什么，把代码写扎实</p>
<p>32、算法速率：随时记得优化代码，优化要把握度</p>
<p>33、重构：习惯重构，自动测试是比较理想的状况</p>
<p>34、易于测试的代码： 测试文化，你和用户，总有一个人测</p>
<p>35、邪恶的向导：弄清楚向导干了什么</p>
<p>36、需求之坑：将商业策略与实际的需求分开， 问下为什么！需求是需要</p>
<p>37、解开不可能解开的谜题： 确定真正的约束所在</p>
<p>38、等你准备好：构建原型</p>
<p>39、规范陷阱：需求和规范都要有一些抽象，留一些空间</p>
<p>40、圆圈与箭头：取众家之长，形成自己的工作习惯</p>
<p>41、注重实效的团队：个人的原则也适用于团队</p>
<p>42、无处不在的自动化：让计算机去重复，它会比我们做得更好</p>
<p>43、无情的测试：早测试，道是无情却有情</p>
<p>44、全都是写：文档和代码同样重要</p>
<p>45、极大的期望：步子别跨太大，否则会扯到</p>
<p>46、傲慢欲偏见：署名，打上你的标记，树立你的品牌。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.phppan.com/2012/02/the-pragmatic-programmer/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

