﻿<?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>Gavin&#039;s Blog &#187; PHP</title>
	<atom:link href="http://laigw.name/cat/tech/php/feed" rel="self" type="application/rss+xml" />
	<link>http://laigw.name</link>
	<description>Keep it simple, stupid. Simplicity is beauty.</description>
	<lastBuildDate>Sun, 29 Jan 2012 07:14:51 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>浅谈PHP中include文件时的作用域问题</title>
		<link>http://laigw.name/post/319.html</link>
		<comments>http://laigw.name/post/319.html#comments</comments>
		<pubDate>Fri, 10 Apr 2009 13:46:47 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[include]]></category>
		<category><![CDATA[require]]></category>
		<category><![CDATA[作用域]]></category>

		<guid isPermaLink="false">http://laigw.name/?p=319</guid>
		<description><![CDATA[    大多数情况下，PHP程序员都是只需使用include或require的基本功能，即在一个php脚本里include(require)另外一个php脚本，但往往容易忽略在一些情况下include(require)的作用域问题，并造成一些难以发觉的错误。该文就是想理清这样一个问题：在一个函数里使用include(require)时、被包含进来的变量、函数和类的作用域问题。]]></description>
		<wfw:commentRss>http://laigw.name/post/319.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PHP继承构造函数的可见性不能声明为private</title>
		<link>http://laigw.name/post/311.html</link>
		<comments>http://laigw.name/post/311.html#comments</comments>
		<pubDate>Mon, 06 Apr 2009 08:55:27 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[private]]></category>
		<category><![CDATA[构造函数]]></category>
		<category><![CDATA[继承]]></category>

		<guid isPermaLink="false">http://laigw.name/?p=311</guid>
		<description><![CDATA[近日在编写继承性的PHP代码时，遇到一个怪异的问题，就是继承于另外一个基类的继承类的构造函数不能声明为private（但可以是protected和public，没有继承时也是可以使用private的），问题详细描述请看正文。]]></description>
		<wfw:commentRss>http://laigw.name/post/311.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>PHP的cookie和session学习总结</title>
		<link>http://laigw.name/post/283.html</link>
		<comments>http://laigw.name/post/283.html#comments</comments>
		<pubDate>Thu, 27 Nov 2008 05:25:49 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[COOKIE]]></category>
		<category><![CDATA[Session]]></category>

		<guid isPermaLink="false">http://www.laigw.name/article/283.html</guid>
		<description><![CDATA[在网上看到这篇文章，觉得其对php cookie和session分析阐述得比较到位，特转载于此。]]></description>
		<wfw:commentRss>http://laigw.name/post/283.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PHP沉思录之六：Drupal的性能问题</title>
		<link>http://laigw.name/post/202.html</link>
		<comments>http://laigw.name/post/202.html#comments</comments>
		<pubDate>Wed, 19 Nov 2008 03:49:55 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Drupal]]></category>
		<category><![CDATA[沉思录]]></category>

		<guid isPermaLink="false">http://www.laigw.name/?p=202</guid>
		<description><![CDATA[本文发表在《程序员》杂志2008年第11期 
PHP沉思录之六：Drupal的性能问题
左轻侯
    Drupal是一个基于PHP的开源CMS系统，也是我认为技术上实现得最好的一个PHP应用。Drupal的架构非常优秀，通过微内核+plugin的方式，实现了极佳的扩展性，从而使Drupal远远超出一般的CMS这一范畴。从这个意义上来说，把Drupal称为Web OS似乎更加合适一些。关于Drupal，有太多的话可以说，也许我会在以后的时间里写一篇文章对它进行专门的讨论。但是在本文中，我想讨论的，是Drupal社区中的每一个人都会面对，但不是每一个人都对其有清晰认识的问题，即Drupal的性能问题。

    因为客户需求，我曾经对Drupal做过比较全面的测试。当时的环境是双服务器（DB server+Web Server），硬件配置都是单CPU+4G。数据库里面有几千条Node记录。用JMeter对各种情况下（开/关各种cache模块，logged user/anonymous user）不同页面的读取和写入操作都进行过测试。
测试的结果可能和很多人印象中不一样。两个主要的结果如下：
    1. Logged user和anonymous user的性能差距非常大。同一个页面，logged user的RPS(Requests per second)一般不超过20，而启用了cache的anonymous user的RPS在100多，当使用了file-based cache以后，甚至能超过300。
    2. 数据库压力相对较小。由于Drupal把大量可配置的内容都放在数据库中，因此往往容易产生这样一种印象，即Drupal对数据库要求应该是很高的。但事实上，无论是cache还是非cache模式，DB server的压力都相当小（CPU在10%以下），而Web Server的CPU在80%以上。跟踪所有的db query的执行时间后，也证明了这一点（全部db query的执行时间只占页面生成时间的一小部分）。 
    经过反复的测试和思考，我得出了一些结论。很明显，Drupal在大量logged user并发情况下的瓶颈，在于执行Drupal代码的CPU时间，而不是在于数据库或者其他地方。之所以出现这样的情况，和PHP本身的执行机制和Drupal的实现方式有关。Drupal在生成一个非cached的页面时，不管这个页面多么简单，都要执行一个完整的bootstrap过程，即使只启用了最少的模块，这个过程也要调用几十个PHP文件，执行成千上万行PHP代码。而PHP的机制又决定了没有任何PHP代码或者对象能够驻留内存，每次响应请求都必须执行完整的初始化工作。而anonymous user之所以快，是因为Drupal在执行cached page的时候，不会执行完整的bootstrap过程，它先检查是否cached page，是的话就读取缓存，然后结束工作。这样当然就快了。
以这个结论为前提，可以解释一些事情：
    1. 为什么Drupal的性能在各种环境下相差并不多。无论是双服务器，单服务器，甚至内存非常小的虚拟机，logged user的RPS值往往总是在10~20之间。数据库里面有几百条，或者几十万条记录，影响也不大。因为瓶颈并不在于DB或者内存，而是在于执行代码的过程。
    2. [...]]]></description>
		<wfw:commentRss>http://laigw.name/post/202.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP沉思录之五：Session有效期问题</title>
		<link>http://laigw.name/post/198.html</link>
		<comments>http://laigw.name/post/198.html#comments</comments>
		<pubDate>Tue, 18 Nov 2008 15:37:50 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Session]]></category>
		<category><![CDATA[沉思录]]></category>

		<guid isPermaLink="false">http://www.laigw.name/?p=198</guid>
		<description><![CDATA[本文发表在《程序员》杂志第10期 PHP沉思录之五：Session有效期问题 左轻侯 2008.9.07
Session处理是所有的Web应用都必须面对的问题。PHP中对session有效期的处理，和其他的解决方案有着很大的不同，这是和PHP的工作机制相关的。在传统的client/server应用中，对于session失效的情况，可以交给网络协议自己来处理。无论是client端主动关闭连接，还是因为网络异常而导致的连接中断，server端都能够得到通知，触发连接中断的事件。只要编程响应这一事件，执行指定的操作即可。但对于web应用来说，情况却完全不一样。HTTP协议本身是无状态的，也就是说，每当client/server完成一次请求/响应的过程后，连接就会被断开。在断开连接以后，server并不知道client是否继续“在线”，还会继续发送下一次请求。换句话说，无论client端的用户已经关闭了浏览器窗口，还是用户仅仅在阅读当前网页并准备在下一秒钟继续浏览，或者用户因为Windows崩溃/停电/硬盘坏掉/网线被拔/地球爆炸而彻底无法再发送下一个请求，server都一无所知。（在HTTP 1.1中，浏览器可以通过keep-alive参数，来通知server不要在响应请求后主动断开连接，从而实现物理上的长连接。但是，这只是为了提高网络传输的性能而采取的措施，HTTP在逻辑上仍然是无状态的。）因此，只能通过某种模拟的方式来判断当前session是否有效。如果某个session在超过一段时间后没有对server端发出请求，server都会判断用户已经“离线”，当前session失效，并触发连接中断的事件。要做到这一点，server需要运行一个后台线程，定时扫描所有的session信息，判断session是否已经超时。

PHP处理session的原理也不例外，但是在具体的实现方式上，却与众不同。这是因为，由于PHP的工作机制，它并没有一个后台线程，来定时地扫描session信息并判断其是否失效。它的解决之道是，当一个有效请求发生时，PHP会根据某个概率，来决定是否调用一个GC（Garbage Collector）。GC的工作，就是扫描所有的session信息，用当前时间减去session的最后修改时间（modified date），同配置参数（configuration option）session.gc_maxlifetime的值进行比较，如果生存时间已经超过gc_maxlifetime，就把该session删除。这是很容易理解的，因为如果每次请求都要调用GC代码，那么PHP的效率就会低得令人吃不消了。这个概率取决于配置参数 session.gc_probability/session.gc_divisor的值（可以通过php.ini或者ini_set()函数来修改）。默认情下，session.gc_probability = 1，session.gc_divisor=100，也就是说有1%的可能性会启动GC。这三个参数，session.gc_maxlifetime/session.gc_probability/session.gc_divisor都可以通过php.ini或者ini_set()函数来修改。但要记得，如果使用ini_set()函数的话，必须在每一个页面的开始处都调用ini_set()。
这又导致了另外一个问题，gc_maxlifetime只能保证session生存的最短时间，并不能够保存在超过这一时间之后session信息立即会得到删除。因为GC是按概率启动的，可能在某一个长时间内都没有被启动，那么大量的session在超过gc_maxlifetime以后仍然会有效。当然，发生这种情况的概率很小，但是如果你的应用对session的失效期要求很精确的话，这会导致很严重的问题。解决这个问题的一个方法是，把session.gc_probability/session.gc_divisor的机率提高，如果提到100%，就会彻底解决这个问题，但显然会对性能造成严重的影响。另一个方法是放弃PHP的GC，自己在代码中判断当前session的生存时间，如果超出了 gc_maxlifetime，就清空当前session。
PHP中的session有效期默认是1440秒（24分钟），也就是说，客户端超过24分钟没有刷新，当前session就会失效。要修改这个默认值，正确的解决办法是修改配置参数session.gc_maxlifetime。我曾经在网上搜索过这个问题的解决方式，找到的结果千奇百怪。有的说要设置“session_life_time”，据我知所，PHP中没有这个参数。有的说要调用session_set_cookie_params，或者设置session.cookie_lifetime，这仅仅用于设置client端cookie的生存时间，换言之，只当client端cookie的生存时间小于server端的session生存期时，修改这个值才有效，并且最长不能超过server端的session生存期，原因很简单，当server端的session已经失效时，client端cookie的生存时间再长也是没有意义的。还有的说要调用 session_cache_expire，这个参数用于通知浏览器和proxy，当前页面的内容应该被缓存多长时间，和session的生存期并没有直接关系。
听起来，这种解决方案很完美。但是，当你在实际中尝试修改session.gc_maxlifetime的值的时候，你很可能会发现，这个参数基本不起作用，session有效期仍然保持24分钟的默认值。甚至可能出现，在开发环境下工作正常，在服务器上却无效！
为了彻底解决这个问题，需要对PHP的工作细节进行进一步的分析。
在默认情况下，PHP 中的session信息会以文本文件的形式，被保存在系统的临时文件目录中。这个路径由配置参数session.save_path指定。在Linux下，这一路径通常为\tmp，在 Windows下通常为C:\Windows\Temp。当服务器上有多个PHP应用时，它们会把自己的session文件都保存在同一个目录中（因为它们使用同一个session.save_path参数）。同样地，这些PHP应用也会按一定机率启动GC，扫描所有的session文件。
问题在于，GC在工作时，并不会区分不同站点的session。举例言之，站点A的gc_maxlifetime设置为2小时，站点B的 gc_maxlifetime设置为默认的24分钟。当站点B的GC启动时，它会扫描公用的临时文件目录，把所有超过24分钟的session文件全部删除掉，而不管它们来自于站点A或B。这样，站点A的gc_maxlifetime设置就形同虚设了。
找到问题所在，解决起来就很简单了。在页面的开始处调用session_save_path()函数，它能够修改session.save_path参数，把保存session的目录指向一个专用的目录，例如\tmp\myapp\。这样，gc_maxlifetime参数就工作正常了。使用公用的session.save_path还会导致安全性问题，因为这意味着，同一台服务器上的其它PHP程序也可以读取你的站点的session文件，这可能被用于黑客攻击。另一个问题是效率：在一个繁忙的站点中，可能存在成千上万个session文件，而把许多不同网站的session文件都放在同一个目录下，无论是对单个文件的读写，还是遍历所有文件进行GC，都无疑会导致性能的降低。因此，如果你的PHP应用和别的PHP应用运行在同一台服务器上的话，强烈建议你使用自己的session.save_path。
严格地来说，这算是PHP的一个bug。当PHP在进行GC时，它应该区别来自不同站点的session文件，并应用不同的gc_maxlifetime值。目前，最新的PHP 5.2.X仍然存在这个问题。
上文说到，在一个繁忙的站点中，可能存在成千上万个session文件，即使区分了不同站点的session.save_path目录，单个站点的session文件数目仍然可能导致效率问题。为了解决这一问题，可行的几种方法有：
1、如果PHP运行在Linux系统下，使用ReiserFS文件系统取代默认的ext2/ext3文件系统。ReiserFS对于大量小文件的存取性能，比ext2/ext3有极大的提高。
2、将session.save_path指向一个内存路径。这意味着，session文件的读写只在内存中进行，而不执行磁盘操作。
3、session.save_path接受一个额外的N参数，用于指定目录的级数。例如，“5;/tmp” 将导致创建类似这样的session文件：/tmp/4/b/1/e/3/sess_4b1e384ad74619bd212e236e52a5a174If。具体的说明，请参见：http://cn.php.net/manual/en/session.configuration.php#ini.session.save-path
4、终极的解决方案，是放弃PHP的session处理机制，自己编码接管所有的session处理操作，通过session_set_save_handler()函数来实现。通过自己接管session处理，可以将所有的session保存在专门的数据库（往往使用内存表）中，从而彻底解决session文件带来的问题，并且可以方便地实现session的共享和复制。这也是大型的PHP应用一般会使用的方式。关于session_set_save_handler()函数的使用，网上和相关图书都有详细的说明，这里不再赘述。值得一提的是，即使在这种方式下，启动GC的概率仍然取决于session.gc_probability/session.gc_divisor。
来源：http://www.bloggern.com/3835.html
]]></description>
		<wfw:commentRss>http://laigw.name/post/198.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP沉思录之四：Zend Framework</title>
		<link>http://laigw.name/post/194.html</link>
		<comments>http://laigw.name/post/194.html#comments</comments>
		<pubDate>Tue, 18 Nov 2008 12:05:37 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[沉思录]]></category>

		<guid isPermaLink="false">http://www.laigw.name/?p=194</guid>
		<description><![CDATA[PHP沉思录之四：Zend Framework 左轻侯 2007.11.11 
    从理论上来说，PHP是一种通用的动态语言，它可以替代Perl实现通用的脚本，甚至可以创建客户端GUI程序(通过GTK+)。但是，在实际应用中，PHP在绝大多数情况下都被用来开发Web应用。即使在Java和.NET这样有软件业界巨头支持的重量级竞争对手面前，出身草莽的PHP也毫不逊色，尤其是在应用最为广泛的轻量级web开发领域，PHP一直牢牢占据着领先的位置。在这一领域参与竞争的其他语言，例如Python和Perl，虽然各具特色，但是仍然无法撼动PHP的地位。PHP是当之无愧的“Web开发第一语言”，而且似乎没有什么能对它构成挑战。随着web应用在软件界的地位越来越重要，PHP也逐渐从脚本小子手中的玩具，转变成重要的工业语言，并获得了IBM这样的巨人的支持。
    但是，就在近两三年，一种新的Web开发解决方案的迅速崛起，震动了整个业界，让PHP开始感到王座不稳。这个解决方案，当然就是Ruby on Rails。
    本文无意对PHP和Ruby这两种语言进行全面的比较（虽然这的确是一个非常有意思的话题）。无论读者对Ruby和ROR持什么看法，有一点却是不争的事实：Ruby作为一种新的语言，虽然极具特色，但是并没有得到业界的普遍接受；只有在ROR诞生以后，以其惊人的生产力征服了无数开发者，Ruby这才一飞冲天。也就是说，Ruby至于在很大程度上是依靠ROR这个框架，才能起迅速崛起。

    对于感到严重威胁的PHP社区，如果要对Ruby进行有效的反击，这似乎是一条可行的道路：基于PHP语言，实现一个新的web开发框架，能够达到甚至超过ROR那样的生产力。这既是对Ruby的反击，其实也是PHP自身发展的必然结果。因为PHP在其发展过程中，早已经出现了数十个各种各样的框架，只是尚没有一个能具有ROR那样的生产力和影响力。
但是，实现这样一个框架，也面临着不少的问题：
    1、PHP语言本身能否完成这一任务？PHP原本只是用于快速解决web应用的简单脚本，虽然经过不断的发展，已经具有了许多高级的语言特征，但是是否能够实现象ROR那样精巧和强大的框架，能够达到ROR那样的生产力？
    2、PHP社群能否接受这样一个框架？PHP的使用者，大多数是脚本小子出身，习惯于快速、敏捷、直观的开发方式，对于重量级的框架，未必能够普遍认同。
　　
    无论这些问题的答案如何，重要的是，有人这么做了。Zend Framework就是这一思路的结果。
    如上所述，Zend Framework就是这样一个完全基于PHP语言的、向ROR学习的、针对web应用开发的框架。与其它同类的框架相比，Zend Framework有两个特点让它显得与众不同：
    1、Zend Framework由Zend公司开发，因此它是一个“官方的”框架。众所周知，Zend公司是PHP编译器的维护者，也是Zend Optimizer、Zend Studio等一系列PHP相关产品的拥有者。由于这一关系，Zend Framework虽然没有被内置到PHP发行包中，但也算得上PHP的官方解决方案了。（顺便说句题外话，Zend公司最近发布了Zend Studio for Eclipse的beta版本，有兴趣的读者不妨尝试一下。）
   [...]]]></description>
		<wfw:commentRss>http://laigw.name/post/194.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP沉思录之三：Smarty</title>
		<link>http://laigw.name/post/189.html</link>
		<comments>http://laigw.name/post/189.html#comments</comments>
		<pubDate>Tue, 18 Nov 2008 09:34:37 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Smarty]]></category>
		<category><![CDATA[沉思录]]></category>

		<guid isPermaLink="false">http://www.laigw.name/?p=189</guid>
		<description><![CDATA[PHP沉思录之三：Smarty 左轻侯 2007.8.11 
    在任何Web应用中，如何将程序代码和界面设计，或者说，将逻辑层和表现层分离开来，都会是一个问题。对于PHP这种类型的嵌入网页的脚本语言，这一问题尤其突出。在新手编写的代码中，把访问数据库的代码和操纵HTML元素的代码写在同一个页面里，是很常见的情况。为了避免这一问题，开发者倾向于将涉及业务逻辑的代码封装在某些单独的库文件中，再在负责显示界面的文件中将它们include进来。但是，这仍然无法避免在显示界面的文件中包含大量的PHP代码。究其所以然，是因为除了涉及业务逻辑的代码以外，即使仅仅在显示层，也往往涉及到复杂的显示逻辑。在一个典型的显示页面中，程序需要先包含所有必需的库文件，初始化上下文环境，创建相关业务逻辑对象（假如数据库访问代码已经被业务逻辑对象封装，可以节省数据库相关的代码），最后在HTML的空隙中把对象格式化为HTML元素进行显示。于是我们看到了无数这样的页面，在第一行HTML开始之前，就已经包含了数十行甚至更多的PHP代码，在HTML的内部，仍然充满了各种各样的PHP代码。因此，PHP代码和HTML代码搅和在一块的问题仍然无法解决，对HTML的修改仍然可能导致整个PHP程序崩溃。更加麻烦的是，这种不清晰的结构妨碍了PHP应用在规模上的进一步扩张。

    为了解决这一问题，模板（template）技术应运而生。模板技术的基本原理是，通过一个解析器（parser），读取指定的模板文件（包含了某些特定标签的HTML文件），将这些标签替换为相关的PHP变量，再输出为标准的HTML。通过这种方式，不但分离了业务逻辑层和表现层，而且也尽可能地分离了显示逻辑和HTML代码。通过替换不同的模板文件，可以方便地生成各种格式的输出，例如HTML，XML，WML，后期稍加处理甚至可以生成PDF和Flash。早期较为著名的PHP模板引擎有PHPLib中的Template和FastTemplate。
但是，模板技术也有其先天的缺陷：
	1、无法彻底分离逻辑。显示逻辑和HTML代码很难通过简单的标签替换，实现彻底的分离。例如，遍历并显示一个数组，在PHP中可以用简单的foreach语句实现，但是使用模板时，就需要进行对整个模板文件进行多次替换操作，造成效率的极大降低；或者根据不同的数据值显示不同的格式，如果模板文件完全不包含PHP代码，那么将很难做到这一点。
	2、解析导致的性能损失。由于每次PHP页面被访问时，解析器都必须对模板文件进行替换操作，无疑会降低PHP应用的性能。尤其在多次的替换操作时更是如此。因此，不使用模板比使用模板往往更加快速，这也是许多PHP程序员摒弃模板技术的原因之一。 
    在经过数年的发展之后，“编译型”的模板技术渐渐占据了主流。所谓“编译型”，是指解析器读取模板文件以后，并不直接生成静态HTML，而是“编译”成一个新的PHP文件，并将它保存起来。以后访问该页面时，模板引擎会直接执行“编译”后的PHP文件。Smarty是这种模板引擎的代表。
针对以上的两个问题，Smarty作了如下处理： 
	1、独立语法。Smarty实现了一套自己的语法，这套语法不但支持变量替换和简单的判断，而且支持循环，修饰符（modifier），内置了很多功能强大的函数，而且还支持自定义函数。这套系统保证Smarty能够完全独立地处理显示输出，无须再和PHP有什么瓜葛。事实上，在Smarty模板中，是不可以直接使用PHP代码的（通过显式定义可以使用），这也是一种强制分离逻辑层和表现层的方式。(理论上来说，Smarty的模板文件也可以应用于其它语言。)但是，这种解决方式也受到了指责，因为Smarty的语法过于强大，几乎变成了一门新的语言，指责者认为，这反而增加了复杂性。但是，根据作者的实际经验，Smarty的语法不但非常简单直观，而且只需要掌握一些最初级的语法，就足可以应付绝大多数的应用。即使是不懂编程的网页设计师，也很容易就能够掌握。
	2、编译机制。Smarty的“编译”机制，节省了用于反复解析模板文件的时间，极大地提高了速度。由于“编译”后生成的是标准的PHP文件，因此从理论上来说，执行的速度不会低于没有模板的PHP应用的速度。在一些和解析型模板引擎进行的对比测试中，Smarty在第一次访问时落后，但是在以后的访问中速度远远超出竞争对手。而且，这种编译过程是智能的，在模板文件的内容被改变后，Smarty会自动重新编译，因此对于开发者来说，编译过程完全无需人工干预。另外，如果你愿意的话，生成的PHP文件还可以方便地应用于Zend Accelerator这样的工具，进行二次编译。 
除此之外，Smarty还拥有其他一些优秀的特性： 
	1、缓存机制。由于实现了编译机制，在接收到对某个模板文件的访问请求时，Smarty会自动将它重定向到编译后的PHP文件。但是，这也意味着，Smarty也可以将它重定向到任何其他的文件——例如静态的HTML文件。在此基础之上，Smarty实现了自己的基于页面的缓存机制。Smarty能够将编译后的PHP文件产生的结果——静态HTML——保存起来，将重复发送的请求直接重定向给它，这意味着对于第一次之后的请求，不需要执行任何PHP代码（Smarty本身的代码当然除外）。对于不需要频繁更新的页面（我们知道这种网页往往在整个网站中占大多数），通过这种缓存机制获取的性能提升是惊人的。而且，由于它是在页面级实现的，因此完全无须涉及到复杂的对象级缓存问题，保持了逻辑上的简单性。
	2、可配置性。Smarty在开发之初就将高度的可配置性作为自己的一个设计目标。它本身以100%的PHP编写，以源代码的方式发行，只需要将Smarty简单地拷贝到你的文件路径中，就可以使用了。Smarty的各项配置变量，都可以通过修改config文件或者手动编码进行定制。例如，Smarty默认的定界符是花括号（{}），但是这往往和Javascript以及CSS中的花括号冲突。为了解决这一问题，可以简单地将默认定界符修改为其他的字符（例如ASP风格的“”）。
	3、可扩展性。Smart的实现基于面向对象的架构，并且提供了插件机制，非常便于用户修改和扩展其默认的认为。当然，你也可以直接修改它的源代码来达到目的。（对于基于脚本语言的开源应用来说，这是非常惬意的，因为你甚至不需要重新编译。） 
让我们来看一个最简单的Smarty应用。这个应用包括两个文件：
	TestSmarty.php 调用Smarty类库，初始化变量，并解析相应的模板文件
	TestSmarty.tpl 模板文件，其实就是包含了Smarty标签的HTML，放在指定的模板目录下，默认是./templates
　　
	TestSmarty.php的内容如下：

&#160;查看代码 PHP1
2
3
4
5
6
	&#60;?php 
	include_once&#40;&#34;./smarty/Smarty.class.php&#34;&#41;; 
	$Smarty = new Smarty&#40;&#41;; 
	$Smarty-&#62;assign&#40;&#34;HelloStr&#34;, &#34;Hello, world&#34;&#41;; 
	$Smarty-&#62;display&#40;&#34;TestSmarty.tpl&#34;&#41;; 
	?&#62;

这个文件的内容非常简单，任何有过PHP经验的程序员都应该能够理解：首先将Smarty类库所在的文件include进来，然后创建一个新的Smarty对象，并对HelloStr变量进行赋值，最后解析TestSmarty.tpl文件。
	TestSmarty.php的内容如下：

&#160;查看代码 PHP1
2
　　 
	This is a string from Smarty: &#123;$HelloStr&#125;

　　 
解析的结果为：
　

&#160;查看代码 PHP1
2
　 
	This is a string from Smarty: Hello, world

此时检查存放编译后的PHP文件的子目录（默认是./templates_c）,可以找到一个名叫%%65^650^65099D8B%%TestSmarty.tpl.php的文件，内容如下：
　

&#160;查看代码 PHP1
2
3
4
　 
	&#60;?php [...]]]></description>
		<wfw:commentRss>http://laigw.name/post/189.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP沉思录之二：PME模型</title>
		<link>http://laigw.name/post/185.html</link>
		<comments>http://laigw.name/post/185.html#comments</comments>
		<pubDate>Tue, 18 Nov 2008 08:37:59 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PME]]></category>
		<category><![CDATA[PME模型]]></category>
		<category><![CDATA[沉思录]]></category>

		<guid isPermaLink="false">http://www.laigw.name/?p=185</guid>
		<description><![CDATA[本文发表在《程序员》第7期 PHP沉思录之二：PME模型 左轻侯 
在大规模的程序设计中，组件（component）已经成为一种非常流行的技术。常见的组件技术都基于PME模型，即属性（Property）、方法（Method）和事件（Event）。基于PME的组件技术可以方便地实现IoC（Inversion of Control，控制反转），是从IDE的plugin到应用服务器的“热发布”等许多技术的基础。
PHP从版本5开始，大大完善了对OO的支持，以前不能被应用的许多pattern现在都可以在PHP5中实现。因此，是否能够实现基于PHP的组件技术，也就成了一个值得讨论的问题。
下面对PHP对于PME模型的支持，逐一进行讨论：

1、属性（Property）
PHP并不支持类似Delphi或者C#的property语法，但这并不是问题。Java也不支持property语法，但是通过getXXX()和setXXX()的命名约定，同样可以支持属性。PHP也可以通过这一方式来支持属性。但是，PHP提供了另一种也许更好的方法，那就是__set()和__get()方法。
在PHP中，每一个class都会自动继承__set()和__get()方法。它们的定义如下：

&#160;查看代码 PHP1
2
void __set &#40; string name, mixed value &#41; 
mixed __get &#40; string name &#41;

这两个方法将在下列情况下被触发：当程序访问一个当前类没有显式定义的属性时。在这个时候，被访问的属性名称作为参数被传入相应的方法。任何类都可以重载__set()和__get()方法，以实现自己的功能。
如下例：

&#160;查看代码 PHP1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PropertyTester &#123; 
&#160;
  public function __get&#40;$PropName&#41; &#123; 
    echo &#34;Getting Property $PropNamen&#34;; 
  &#125; 
&#160;
  public function __set&#40;$PropName, $Value&#41; &#123; 
    echo &#34;Setting Property $PropName [...]]]></description>
		<wfw:commentRss>http://laigw.name/post/185.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP沉思录之一：工作模型与数据库访问接口</title>
		<link>http://laigw.name/post/177.html</link>
		<comments>http://laigw.name/post/177.html#comments</comments>
		<pubDate>Tue, 18 Nov 2008 07:58:31 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PAO]]></category>
		<category><![CDATA[工作原理]]></category>
		<category><![CDATA[工作模型]]></category>
		<category><![CDATA[数据库]]></category>
		<category><![CDATA[沉思录]]></category>
		<category><![CDATA[访问接口]]></category>

		<guid isPermaLink="false">http://www.laigw.name/?p=177</guid>
		<description><![CDATA[本文发表于《程序员》5月号，是一个系列的第一篇，目前想到的其他一些主题是：
　　
　　SQL注入问题
　　事件模型
　　AOP模型
　　UI Framework的实现
　　Template机制
　　
PHP沉思录&#8211;工作模型
　　
     PHP的工作模型非常特殊。从某种程度上说，PHP和ASP、ASP.NET、JSP/Servlet等流行的Web技术，有着本质上的区别。
     以Java为例，Java在Web应用领域，有两种技术：Java Servlet和JSP（Java Server Page）。Java Servlet是一种特殊类型的Java程序，它通过实现相关接口，处理Web服务器发送过来的请求，完成相应的工作。JSP在形式上是一种类似于PHP的脚本，但是事实上，它最后也被编译成Servlet。也就是说，在Java解决方案中，JSP和Servlet是作为独立的Java应用程序执行的，它们在初始化之后就驻留内存，通过特定的接口和Web服务器通信，完成相应工作。除非被显式地重启，否则它们不会终止。因此，可以在JSP和Servlet中使用各种缓存技术，例如数据库连接池。

     ASP.NET的机制与此类似。至于ASP，虽然也是一种解释型语言，但是仍然提供了Application对象来存放应用程序级的全局变量，它依托于ASP解释器在IIS中驻留的进程，在整个应用程序的生命期有效。
     PHP却完全不是这样。作为一种纯解释型语言，PHP脚本在每次被解释时进行初始化，在解释完毕后终止运行。这种运行是互相独立的，每一次请求都会创建一个单独的进程或线程，来解释相应的页面文件。页面创建的变量和其他对象，都只在当前的页面内部可见，无法跨越页面访问。在终止运行后，页面中申请的、没有被代码显式释放的外部资源，包括内存、数据库连接、文件句柄、Socket连接等，都会被强行释放。也就是说，PHP无法在语言级别直接访问跨越页面的变量，也无法创建驻留内存的对象。
     见下例：
　　

&#160;查看代码 PHP1
2
3
4
5
6
7
8
9
10
11
12
13
14
　　&#60;?php 
　　class StaticVarTester &#123; 
　　 public static $StaticVar = 0; 
　　&#125; 
　　 
　　function TestStaticVar&#40;&#41; &#123; 
　　 StaticVarTester :: $StaticVar += 1; 
　　 echo &#34;StaticVarTester [...]]]></description>
		<wfw:commentRss>http://laigw.name/post/177.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>国内著名CMS:PHPCMS整站源码分析(四)</title>
		<link>http://laigw.name/post/127.html</link>
		<comments>http://laigw.name/post/127.html#comments</comments>
		<pubDate>Sat, 15 Nov 2008 09:44:38 +0000</pubDate>
		<dc:creator>Gavin</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHPCMS]]></category>
		<category><![CDATA[源码分析]]></category>

		<guid isPermaLink="false">http://www.laigw.name/?p=127</guid>
		<description><![CDATA[四、phpcms的模版引擎
函数 template函数是在global.func.php 里面定义的。 在前面的phpcms 的首页 index.php 里就见到了。用法： include template() 用法很熟， 呵呵其实和 dz 的模板引擎一样的用法。 但DZ的模板引擎比 PHPCMS 的简单很多，因为没有用到模板的标签技术。 大家有空可以研究下DZ的模板引擎。这里不说。 好分析下上面这个 模板的主要函数吧。他的作用是返回编译好的模板文件路径。也就是把模板 X.html(模板文件) 用正则替换成 x.php(编译后的PHP文件).然后使用 include 函数。懂了吧！ php的模板引擎都一个鸟样。 然后剩下的就是正则的东西了。


&#160;查看代码 PHP1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
&#60;?php
/**
函数 template函数是在global.func.php 里面定义的。 在前面的phpcms 的首页 index.php 里就见到了。用法： include template() 用法很熟， 呵呵其实和 dz 的模板引擎一样的用法。 但DZ的模板引擎比 PHPCMS 的简单很多，因为没有用到模板的标签技术。 大家有空可以研究下DZ的模板引擎。这里不说。 好分析下上面这个 模板的主要函数吧。他的作用是返回编译好的模板文件路径。也就是把模板 X.html(模板文件) 用正则替换成 x.php(编译后的PHP文件).然后使用 include 函数。懂了吧！ php的模板引擎都一个鸟样。 然后剩下的就是正则的东西了。等下再说。
*/
function template&#40;$module = 'phpcms', [...]]]></description>
		<wfw:commentRss>http://laigw.name/post/127.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

