<?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>Apex[有所思,有所志] &#187; PHP</title>
	<atom:link href="http://apex.ncksoft.com/archives/tag/php/feed" rel="self" type="application/rss+xml" />
	<link>http://apex.ncksoft.com</link>
	<description>日有所思，夜有所志</description>
	<lastBuildDate>Sat, 06 Feb 2010 18:12:04 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>PHP网站提速</title>
		<link>http://apex.ncksoft.com/archives/279</link>
		<comments>http://apex.ncksoft.com/archives/279#comments</comments>
		<pubDate>Fri, 11 Dec 2009 18:44:59 +0000</pubDate>
		<dc:creator>Apex</dc:creator>
				<category><![CDATA[代码人生]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[网站优化]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://apex.ncksoft.com/?p=279</guid>
		<description><![CDATA[最近在研究网站提速、搜索引擎优化等技术，打算将一些技巧逐步分享，这里先说说提速。
对于基于PHP构建的网站，一个可行的方法是将网络上传输的内容减少。一是压缩，二是如果内容没有发生变化，则不发送内容到客户端，而是让客户端使用缓存的数据。虽然说现在网络速度已经比较快了，但是考虑到网页大部分都是文本内容，压缩率还是相当可观的，一般来说，构成网页的文本内容如HTML/CSS/JS等，使用gzip压缩可以达到60%以上的压缩率。这样，一个简单的网站页面每次访问也可减少60KB以上的网络传输量。而压缩操作速度是很快的，一个100KB的页面文本，压缩耗时在3毫秒以下，基本可以忽略。
我打算先从css/javascript文件入手。下面来看一段代码：

&#19979;&#36733;: index.php&#60;?php
&#160;
// Created by Apex
// http://apex.ncksoft.com/archives/279
&#160;
define('FILE_TYPE_UNKNOWN', 0);
define('FILE_TYPE_CSS', 1);
define('FILE_TYPE_JS', 2);
&#160;
// 这个PHP文件将会放到与css和js文件同一个目录下，方便文件定位
$this_path = dirname( realpath(__FILE__) ).DIRECTORY_SEPARATOR;
&#160;
// 根据URL请求来决定后续发生什么样的header到客户端
$type = FILE_TYPE_UNKNOWN;
$fname = '';
if(isset($_REQUEST['css']))
{
&#160; &#160; $type = FILE_TYPE_CSS;
&#160; &#160; $fname = $_REQUEST['css'];
}
else if(isset($_REQUEST['js']))
{
&#160; &#160; $type = FILE_TYPE_JS;
&#160; &#160; $fname = $_REQUEST['js'];
}
else
{
&#160; &#160; exit();
}
&#160;
// 对参数进行判断
if(empty($fname))
{
&#160; &#160; exit();
}
&#160;
// 指定的文件应该存在，否则什么都不输出
$fname = $this_path . $fname;
if(!is_file($fname))
{
&#160; &#160; exit();
}
&#160;
// 得到文件内容，并获取文件最后修改时间
$output = file_get_contents($fname);
$fmtime = filemtime($fname);
&#160;
// 如果该客户端不是第一次希望获取此文件，则会有如下变量
if(isset($_SERVER[&#34;HTTP_IF_MODIFIED_SINCE&#34;]))
{
&#160; &#160; [...]]]></description>
			<content:encoded><![CDATA[<p>最近在研究网站提速、搜索引擎优化等技术，打算将一些技巧逐步分享，这里先说说提速。</p>
<p>对于基于PHP构建的网站，一个可行的方法是将网络上传输的内容减少。一是压缩，二是如果内容没有发生变化，则不发送内容到客户端，而是让客户端使用缓存的数据。虽然说现在网络速度已经比较快了，但是考虑到网页大部分都是文本内容，压缩率还是相当可观的，一般来说，构成网页的文本内容如HTML/CSS/JS等，使用gzip压缩可以达到60%以上的压缩率。这样，一个简单的网站页面每次访问也可减少60KB以上的网络传输量。而压缩操作速度是很快的，一个100KB的页面文本，压缩耗时在3毫秒以下，基本可以忽略。</p>
<p>我打算先从css/javascript文件入手。下面来看一段代码：<br />
<span id="more-279"></span></p>
<div class="hl-title">&#19979;&#36733;: <a href="http://apex.ncksoft.com/wp-content/plugins/coolcode/coolcode.php?p=279&amp;download=index.php">index.php</a></div><div class="hl-surround"><ol class="hl-main ln-show" title="Double click to hide line number." ondblclick = "linenumber(this)"><li class="hl-firstline"><span class="hl-inlinetags">&lt;?php</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// Created by Apex</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-comment">// </span><span class="hl-url">http://apex.ncksoft.com/archives/279</span><span class="hl-comment"></span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-reserved">define</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">FILE_TYPE_UNKNOWN</span><span class="hl-quotes">'</span><span class="hl-code">, </span><span class="hl-number">0</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-reserved">define</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">FILE_TYPE_CSS</span><span class="hl-quotes">'</span><span class="hl-code">, </span><span class="hl-number">1</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-reserved">define</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">FILE_TYPE_JS</span><span class="hl-quotes">'</span><span class="hl-code">, </span><span class="hl-number">2</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 这个PHP文件将会放到与css和js文件同一个目录下，方便文件定位</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-var">$this_path</span><span class="hl-code"> = </span><span class="hl-identifier">dirname</span><span class="hl-brackets">(</span><span class="hl-code"> </span><span class="hl-identifier">realpath</span><span class="hl-brackets">(</span><span class="hl-reserved">__FILE__</span><span class="hl-brackets">)</span><span class="hl-code"> </span><span class="hl-brackets">)</span><span class="hl-code">.</span><span class="hl-reserved">DIRECTORY_SEPARATOR</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 根据URL请求来决定后续发生什么样的header到客户端</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-var">$type</span><span class="hl-code"> = </span><span class="hl-identifier">FILE_TYPE_UNKNOWN</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-var">$fname</span><span class="hl-code"> = </span><span class="hl-quotes">''</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-reserved">isset</span><span class="hl-brackets">(</span><span class="hl-var">$_REQUEST</span><span class="hl-brackets">[</span><span class="hl-quotes">'</span><span class="hl-string">css</span><span class="hl-quotes">'</span><span class="hl-brackets">]))</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-var">$type</span><span class="hl-code"> = </span><span class="hl-identifier">FILE_TYPE_CSS</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-var">$fname</span><span class="hl-code"> = </span><span class="hl-var">$_REQUEST</span><span class="hl-brackets">[</span><span class="hl-quotes">'</span><span class="hl-string">css</span><span class="hl-quotes">'</span><span class="hl-brackets">]</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">else</span><span class="hl-code"> </span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-reserved">isset</span><span class="hl-brackets">(</span><span class="hl-var">$_REQUEST</span><span class="hl-brackets">[</span><span class="hl-quotes">'</span><span class="hl-string">js</span><span class="hl-quotes">'</span><span class="hl-brackets">]))</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-var">$type</span><span class="hl-code"> = </span><span class="hl-identifier">FILE_TYPE_JS</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-var">$fname</span><span class="hl-code"> = </span><span class="hl-var">$_REQUEST</span><span class="hl-brackets">[</span><span class="hl-quotes">'</span><span class="hl-string">js</span><span class="hl-quotes">'</span><span class="hl-brackets">]</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">else</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-reserved">exit</span><span class="hl-brackets">()</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 对参数进行判断</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-reserved">empty</span><span class="hl-brackets">(</span><span class="hl-var">$fname</span><span class="hl-brackets">))</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-reserved">exit</span><span class="hl-brackets">()</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 指定的文件应该存在，否则什么都不输出</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-var">$fname</span><span class="hl-code"> = </span><span class="hl-var">$this_path</span><span class="hl-code"> . </span><span class="hl-var">$fname</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-code">!</span><span class="hl-identifier">is_file</span><span class="hl-brackets">(</span><span class="hl-var">$fname</span><span class="hl-brackets">))</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-reserved">exit</span><span class="hl-brackets">()</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 得到文件内容，并获取文件最后修改时间</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-var">$output</span><span class="hl-code"> = </span><span class="hl-identifier">file_get_contents</span><span class="hl-brackets">(</span><span class="hl-var">$fname</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-var">$fmtime</span><span class="hl-code"> = </span><span class="hl-identifier">filemtime</span><span class="hl-brackets">(</span><span class="hl-var">$fname</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 如果该客户端不是第一次希望获取此文件，则会有如下变量</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-reserved">isset</span><span class="hl-brackets">(</span><span class="hl-var">$_SERVER</span><span class="hl-brackets">[</span><span class="hl-quotes">&quot;</span><span class="hl-string">HTTP_IF_MODIFIED_SINCE</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">]))</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-comment">// 这个变量里面放着客户端所缓存的这个文件的最后修改时间</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-var">$checktime</span><span class="hl-code"> = </span><span class="hl-var">$_SERVER</span><span class="hl-brackets">[</span><span class="hl-quotes">&quot;</span><span class="hl-string">HTTP_IF_MODIFIED_SINCE</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">]</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-var">$checktime</span><span class="hl-code"> = </span><span class="hl-identifier">strtotime</span><span class="hl-brackets">(</span><span class="hl-var">$checktime</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-comment">// 比较一下时间，如果文件没有改动过，则无需发送文件内容（通过发送304响应码来完成）</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-var">$fmtime</span><span class="hl-code"> &lt;= </span><span class="hl-var">$checktime</span><span class="hl-brackets">)</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="hl-identifier">header</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">HTTP/1.1 304 Not Modified</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="hl-reserved">exit</span><span class="hl-brackets">()</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 一个额外处理，将css文件中的注释全部去掉。如何去掉js的注释，还没有做。 :)</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-identifier">FILE_TYPE_CSS</span><span class="hl-code"> == </span><span class="hl-var">$type</span><span class="hl-brackets">)</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-var">$output</span><span class="hl-code"> = </span><span class="hl-identifier">preg_replace</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">!/\*[^*]*\*+([^/][^*]*\*+)*/!</span><span class="hl-quotes">'</span><span class="hl-code">, </span><span class="hl-quotes">''</span><span class="hl-code">, </span><span class="hl-var">$output</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 重要：只有服务端的PHP加载了zlib模块，且客户端支持获取gzip压缩数据的情况下，我们才进行压缩</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; !</span><span class="hl-identifier">headers_sent</span><span class="hl-brackets">()</span><span class="hl-code"> &amp;&amp;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-identifier">extension_loaded</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">zlib</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code"> &amp;&amp;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-identifier">strstr</span><span class="hl-brackets">(</span><span class="hl-var">$_SERVER</span><span class="hl-brackets">[</span><span class="hl-quotes">&quot;</span><span class="hl-string">HTTP_ACCEPT_ENCODING</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">]</span><span class="hl-code">,</span><span class="hl-quotes">&quot;</span><span class="hl-string">gzip</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">)</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-comment">// 9 代表最强压缩</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-var">$output</span><span class="hl-code"> = </span><span class="hl-identifier">gzencode</span><span class="hl-brackets">(</span><span class="hl-var">$output</span><span class="hl-code">, </span><span class="hl-number">9</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-comment">// 告诉客户端我们发送的数据是压缩过的</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-identifier">header</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Content-Encoding: gzip</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-comment">// 下面这个header不太清楚其含义，貌似用于通知缓存服务器或者代理服务器（如果有的话）做某些操作吧，请达人告诉我</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-identifier">header</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Vary: Accept-Encoding</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-comment">// 告诉客户端压缩后的数据长度，因为压缩后的数据是二进制的，客户端需要这个数据来决定接收多少数据后可以结束</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-identifier">header</span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">Content-Length: </span><span class="hl-quotes">&quot;</span><span class="hl-code">.</span><span class="hl-identifier">strlen</span><span class="hl-brackets">(</span><span class="hl-var">$output</span><span class="hl-brackets">))</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 告诉客户端我们本次发送的数据是什么内容，注意编码格式，如果你的不是utf-8编码，要进行修改</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-identifier">FILE_TYPE_CSS</span><span class="hl-code"> == </span><span class="hl-var">$type</span><span class="hl-brackets">)</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-identifier">header</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">content-type: text/css; charset: utf-8</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">else</span><span class="hl-code"> </span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-identifier">FILE_TYPE_JS</span><span class="hl-code"> == </span><span class="hl-var">$type</span><span class="hl-brackets">)</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-identifier">header</span><span class="hl-code"> </span><span class="hl-brackets">(</span><span class="hl-quotes">&quot;</span><span class="hl-string">content-type: text/javascript; charset: utf-8</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 重要：告诉客户端这个文件的最后修改时间，这样客户端下次访问此文件时，才会将这个时间再发回服务器，以便服务器进行判断</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-identifier">header</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">Last-Modified: </span><span class="hl-quotes">'</span><span class="hl-code"> . </span><span class="hl-identifier">gmdate</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">D, d M Y H:i:s</span><span class="hl-quotes">'</span><span class="hl-code">, </span><span class="hl-var">$fmtime</span><span class="hl-brackets">)</span><span class="hl-code"> . </span><span class="hl-quotes">'</span><span class="hl-string"> GMT</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-identifier">header</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">Etag: &quot;</span><span class="hl-quotes">'</span><span class="hl-code"> . </span><span class="hl-identifier">md5</span><span class="hl-brackets">(</span><span class="hl-var">$_SERVER</span><span class="hl-brackets">[</span><span class="hl-quotes">'</span><span class="hl-string">REQUEST_URI</span><span class="hl-quotes">'</span><span class="hl-brackets">])</span><span class="hl-code">.</span><span class="hl-quotes">'</span><span class="hl-string">&quot;</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 将压缩后的数据发回客户端</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">echo</span><span class="hl-code"> </span><span class="hl-var">$output</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-inlinetags">?&gt;</span></li></ol></div>
<p>我已经尽量在代码中使用注释来说明其工作了。</p>
<p>将这个文件放到保存CSS/Javascript文件的目录下，然后在需要使用原来的css/javascript文件的地方如下调用：</p>
<div class="hl-surround"><ol class="hl-main ln-show" title="Double click to hide line number." ondblclick = "linenumber(this)"><li class="hl-firstline"><span class="hl-brackets">&lt;</span><span class="hl-reserved">link</span><span class="hl-code"> </span><span class="hl-var">rel</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">stylesheet</span><span class="hl-quotes">&quot;</span><span class="hl-code"> </span><span class="hl-var">href</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">http://your.domain.com/css/index.php?css=style.css</span><span class="hl-quotes">&quot;</span><span class="hl-code"> </span><span class="hl-var">type</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">text/css</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">&gt;</span></li></ol></div>
<p>或者这样：</p>
<div class="hl-surround"><ol class="hl-main ln-show" title="Double click to hide line number." ondblclick = "linenumber(this)"><li class="hl-firstline"><span class="hl-brackets">&lt;</span><span class="hl-reserved">script</span><span class="hl-code"> </span><span class="hl-var">src</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">http://your.domain.com/css/index.php?js=common.css</span><span class="hl-quotes">&quot;</span><span class="hl-code"> </span><span class="hl-var">type</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">text/javascript</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">&gt;</span></li></ol></div>
<p>就可以了。</p>
<p>可能你会问，为什么要这么做？为什么不直接使用Apache的mod_deflate模块来做输出时的压缩呢？原因有三个。</p>
<ol>
<li>缺省安装Apache是不会安装mod_deflate模块的，如果你的代码跑在一台你不能触及Apache设置的主机上，你将不得不用这里的办法；</li>
<li>这样可以让我们在编写代码时，尽量加入注释，以便使得代码自我解释，方便多人（或者自己在将来的某个时刻）进行维护，而注释在输出的时候，可以用PHP字符串处理功能干掉；</li>
<li>更重要的是，我们可以将多个css文件或者多个js文件一次性输出到客户端；</li>
</ol>
<p>太晚了，第三点的详细解释随后补上，睡觉去了~~~zz~~ZZZ</p>
<p><strong>[2009-12-12 继续补充]</strong></p>
<p>我们知道，HTTP通讯协议其实是一个很“昂贵”的协议，每次访问服务器上的单个资源，都需要重新经过一次协议握手，其中客户端向服务端发送的请求数据，还包括cookies的内容，以及很多附加的有用数据。而一个页面一般是由多个资源构成的，例如页面文件本身、css文件、js文件、图片文件等等。我们应该尽量减少资源的数量和体积，来减少客户端与服务端的交互。</p>
<p>考虑到现今的网页，一般都会使用多个css文件来控制页面布局及外观。其实，完全可以将其合并为一个文件，这样客户端也就只需要发送一次请求即可。我们可以采用下面的方法：</p>
<div class="hl-surround"><ol class="hl-main ln-show" title="Double click to hide line number." ondblclick = "linenumber(this)"><li class="hl-firstline"><span class="hl-brackets">&lt;</span><span class="hl-reserved">link</span><span class="hl-code"> </span><span class="hl-var">rel</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">stylesheet</span><span class="hl-quotes">&quot;</span><span class="hl-code"> </span><span class="hl-var">href</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">http://your.domain.com/css/index.php?css=layout.css,style.css,color.css</span><span class="hl-quotes">&quot;</span><span class="hl-code"> </span><span class="hl-var">type</span><span class="hl-code">=</span><span class="hl-quotes">&quot;</span><span class="hl-string">text/css</span><span class="hl-quotes">&quot;</span><span class="hl-brackets">&gt;</span></li></ol></div>
<p>这样，我们一次性将服务器上的三个css文件发送给客户端了（当然，代码要略作调整，需要内部分解参数，然后读取三个文件到一个buffer中，再压缩输出，还要考虑最后更新时间采用哪个文件的，等等，不过这都是小问题）。</p>
<p>js文件也可如此处理。</p>
]]></content:encoded>
			<wfw:commentRss>http://apex.ncksoft.com/archives/279/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>网站迁移时的一个小问题</title>
		<link>http://apex.ncksoft.com/archives/159</link>
		<comments>http://apex.ncksoft.com/archives/159#comments</comments>
		<pubDate>Sat, 21 Jun 2008 16:45:32 +0000</pubDate>
		<dc:creator>Apex</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[服务器]]></category>
		<category><![CDATA[代码人生]]></category>

		<guid isPermaLink="false">http://apex.ncksoft.com/?p=159</guid>
		<description><![CDATA[因为公司外部网服务器硬件全面升级(换了一台专门的2U机架式服务器)，所以要將原来的网站和邮件服务都迁移到新的服务器上。邮件服务的迁移已经实验是没有问题了(参见从sendmail迁移到postfix)，但是在迁移网站的时候出现了一个小问题。
除了迁移网站，还需要增加一些服务，这次要增加的是一个在线演示系统，用于演示利用公司的产品完成网上登录身份认证的功能，其中一段代码是这样的：

&#60;?php
session_start();
&#160;
// 取得通过SSL认证的客户端数字证书中email信息
$email = $_SERVER['SSL_CLIENT_S_DN_Email'];
&#160;
// 如果登录的用户的email和数字证书中的email一致，说明这是一个授权用户
// 否则拒绝访问
// 在此前的登录步骤中，用户注册的email地址已经从数据库中取出并放入session变量中了。
if($email != $_SESSION['email'])
{
&#160; &#160; die('凭证不一致，您确定使用的是自己的数字证书？');
}
&#160;
// ... 其他代码...
?&#62;
令人郁闷的是，这里的判断语句条件表达式总是为真，也就是说，无论登录的用户的email地址是什么，$email的值和session中存储的email地址总是是一样的。还真是见鬼了～～～不但如此，这个页面执行完后，session中保存的email地址就变成了客户端数字证书中的email地址了。
到底什么地方出问题了呢？
经过半小时的调试，最后发现，罪魁祸首就是原来的旧网站代码。旧网站的代码是我在五六年前写的，那个时候PHP的设置中，register_globals缺省是On的，所以在原来的代码中依赖了这个特性。而在高版本的PHP中(从PHP 4.2版开始)，这个特性是被关闭了的，为了能让老代码继续工作，所以我修改了php.ini中的register_globals的设置，将其从缺省的Off改为了On，这样虽然老代码工作起来没有问题了，但是新代码却不能正常工作了。
如上述代码片段中，变量$_SESSION['email']被PHP的register_globals特性自动映射到变量$email上了，所以在给$email赋值时，其实就是给$_SESSION['email']赋值，这样，后续的判断语句当然就总是返回true，因为它们本就是同一个变量嘛。
问题确定后，修改就很容易了，將$email变量名改为$srv_email即可，测试通过。 :)
更进一步
对于追求完美的我来说，虽然这个问题可以这样解决，但总是感觉不爽，理想的情况是php.ini中register_globals特性是关闭的，对于新的代码使用更安全的方式进行处理(即对一切从客户端传递来的数据持怀疑态度，而不是自动將_POST/_GET/_ENV/_SESSION等注册成全局变量，除了安全性外，也可避免变量的冲突带来的隐蔽错误)，而老代码仍然能够享受register_globals开启的特性，在新网站上线前的过渡期，老网站能够继续提供服务。
于是上PHP的网站寻找信息。在php.net上我发现了这篇文章，其开篇就提出了register_globals特性將会在PHP 6.0中移除(而不是像PHP 5.0这样为了保持兼容而保留)，嘿嘿，毕竟register_globals已经被标记为&#8221;DEPRECATED&#8221;有好几年了，看来PHP社区认为用户都已经做好准备了。在这篇文章的回复中，有人提到了一个解决方案，即在需要register_globals特性的目录下，设置.htaccess文件，内容为

php_flag register_globals On

不过又有人提出这种做法在Apache2以上的环境中会导致Apache产生内部错误。当然这也有解决的办法：
&#8230; adding php_flag in .htaccess under apache 2 will cause internal server error. According to apache 2 manual, php_flag should goes to &#60;virtual&#62; or &#60;directory&#62; section.
嗯，明天到公司试一试。对了，对于新代码，要重新规范一下变量命名规则了，呵呵～～～
[2008-06-22 更新]
实践证明，在&#60;directory&#62;小节中加入php_flag是可行的。我没有采用设置.htaccess的方式，因为那种方式会导致每次客户端发出的HTTP请求时，Apache都需要重新读取指定目录及其父目录中的.htaccess文件(而且会逐层向上，直到站点的根目录)，即使在某个层级中.htaccess根本不存在，这多少会影响一点性能。
现在，php.ini中的register_globals特性被关闭了，但是新旧代码仍然可以共存在同一台服务器上，完美解决～～～
]]></description>
			<content:encoded><![CDATA[<p>因为公司外部网服务器硬件全面升级(换了一台专门的2U机架式服务器)，所以要將原来的网站和邮件服务都迁移到新的服务器上。邮件服务的迁移已经实验是没有问题了(参见<a href="http://apex.ncksoft.com/archives/142">从sendmail迁移到postfix</a>)，但是在迁移网站的时候出现了一个小问题。</p>
<p>除了迁移网站，还需要增加一些服务，这次要增加的是一个在线演示系统，用于演示利用公司的产品完成网上登录身份认证的功能，其中一段代码是这样的：<br />
<span id="more-159"></span></p>
<div class="hl-surround"><ol class="hl-main ln-show" title="Double click to hide line number." ondblclick = "linenumber(this)"><li class="hl-firstline"><span class="hl-inlinetags">&lt;?php</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-identifier">session_start</span><span class="hl-brackets">()</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 取得通过SSL认证的客户端数字证书中email信息</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-var">$email</span><span class="hl-code"> = </span><span class="hl-var">$_SERVER</span><span class="hl-brackets">[</span><span class="hl-quotes">'</span><span class="hl-string">SSL_CLIENT_S_DN_Email</span><span class="hl-quotes">'</span><span class="hl-brackets">]</span><span class="hl-code">;</span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 如果登录的用户的email和数字证书中的email一致，说明这是一个授权用户</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 否则拒绝访问</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-comment">// 在此前的登录步骤中，用户注册的email地址已经从数据库中取出并放入session变量中了。</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-reserved">if</span><span class="hl-brackets">(</span><span class="hl-var">$email</span><span class="hl-code"> != </span><span class="hl-var">$_SESSION</span><span class="hl-brackets">[</span><span class="hl-quotes">'</span><span class="hl-string">email</span><span class="hl-quotes">'</span><span class="hl-brackets">])</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-brackets">{</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp; &nbsp; </span><span class="hl-reserved">die</span><span class="hl-brackets">(</span><span class="hl-quotes">'</span><span class="hl-string">凭证不一致，您确定使用的是自己的数字证书？</span><span class="hl-quotes">'</span><span class="hl-brackets">)</span><span class="hl-code">;</span></li>
<li><span class="hl-code"></span><span class="hl-brackets">}</span><span class="hl-code"></span></li>
<li><span class="hl-code">&nbsp;</span></li>
<li><span class="hl-code"></span><span class="hl-comment">// ... 其他代码...</span><span class="hl-code"></span></li>
<li><span class="hl-code"></span><span class="hl-inlinetags">?&gt;</span></li></ol></div>
<p>令人郁闷的是，这里的判断语句条件表达式总是为真，也就是说，无论登录的用户的email地址是什么，$email的值和session中存储的email地址总是是一样的。还真是见鬼了～～～不但如此，这个页面执行完后，session中保存的email地址就变成了客户端数字证书中的email地址了。</p>
<p>到底什么地方出问题了呢？</p>
<p>经过半小时的调试，最后发现，罪魁祸首就是原来的旧网站代码。旧网站的代码是我在五六年前写的，那个时候PHP的设置中，register_globals缺省是On的，所以在原来的代码中依赖了这个特性。而在高版本的PHP中(从PHP 4.2版开始)，这个特性是被关闭了的，为了能让老代码继续工作，所以我修改了php.ini中的register_globals的设置，将其从缺省的Off改为了On，这样虽然老代码工作起来没有问题了，但是新代码却不能正常工作了。</p>
<p>如上述代码片段中，变量$_SESSION['email']被PHP的register_globals特性自动映射到变量$email上了，所以在给$email赋值时，其实就是给$_SESSION['email']赋值，这样，后续的判断语句当然就总是返回true，因为它们本就是同一个变量嘛。</p>
<p>问题确定后，修改就很容易了，將$email变量名改为$srv_email即可，测试通过。 :)</p>
<p><strong>更进一步</strong></p>
<p>对于追求完美的我来说，虽然这个问题可以这样解决，但总是感觉不爽，理想的情况是php.ini中register_globals特性是关闭的，对于新的代码使用更安全的方式进行处理(即对一切从客户端传递来的数据持怀疑态度，而不是自动將_POST/_GET/_ENV/_SESSION等注册成全局变量，除了安全性外，也可避免变量的冲突带来的隐蔽错误)，而老代码仍然能够享受register_globals开启的特性，在新网站上线前的过渡期，老网站能够继续提供服务。</p>
<p>于是上PHP的网站寻找信息。在php.net上我发现了<a href="http://cn.php.net/register_globals">这篇文章</a>，其开篇就提出了register_globals特性將会在PHP 6.0中移除(而不是像PHP 5.0这样为了保持兼容而保留)，嘿嘿，毕竟register_globals已经被标记为&#8221;DEPRECATED&#8221;有好几年了，看来PHP社区认为用户都已经做好准备了。在这篇文章的回复中，有人提到了一个解决方案，即在需要register_globals特性的目录下，设置.htaccess文件，内容为<br />
<code><br />
php_flag register_globals On<br />
</code><br />
不过又有人提出这种做法在Apache2以上的环境中会导致Apache产生内部错误。当然这也有解决的办法：</p>
<blockquote><p>&#8230; adding php_flag in .htaccess under apache 2 will cause internal server error. According to apache 2 manual, php_flag should goes to &lt;virtual&gt; or &lt;directory&gt; section.</p></blockquote>
<p>嗯，明天到公司试一试。对了，对于新代码，要重新规范一下变量命名规则了，呵呵～～～</p>
<p><strong>[2008-06-22 更新]</strong><br />
实践证明，在&lt;directory&gt;小节中加入php_flag是可行的。我没有采用设置.htaccess的方式，因为那种方式会导致每次客户端发出的HTTP请求时，Apache都需要重新读取指定目录及其父目录中的.htaccess文件(而且会逐层向上，直到站点的根目录)，即使在某个层级中.htaccess根本不存在，这多少会影响一点性能。</p>
<p>现在，php.ini中的register_globals特性被关闭了，但是新旧代码仍然可以共存在同一台服务器上，完美解决～～～</p>
]]></content:encoded>
			<wfw:commentRss>http://apex.ncksoft.com/archives/159/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
