<?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>Some reminiscences, some memories &#187; PHP</title>
	<atom:link href="http://www.mikespook.com/index.php/category/php/feed" rel="self" type="application/rss+xml" />
	<link>http://www.mikespook.com</link>
	<description>Just another boring day</description>
	<lastBuildDate>Tue, 10 Jan 2012 03:14:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>关于 QQ OAuth 封装出现 T_PAAMAYIM_NEKUDOTAYIM 的解决办法</title>
		<link>http://www.mikespook.com/2011/01/%e5%85%b3%e4%ba%8e-oauth-%e5%b0%81%e8%a3%85%e5%87%ba%e7%8e%b0-t_paamayim_nekudotayim-%e7%9a%84%e4%b8%80%e4%ba%9b%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95/</link>
		<comments>http://www.mikespook.com/2011/01/%e5%85%b3%e4%ba%8e-oauth-%e5%b0%81%e8%a3%85%e5%87%ba%e7%8e%b0-t_paamayim_nekudotayim-%e7%9a%84%e4%b8%80%e4%ba%9b%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95/#comments</comments>
		<pubDate>Sun, 30 Jan 2011 06:16:15 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[error]]></category>
		<category><![CDATA[OAuth]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=832</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
许多网友在使用我的 OAuth 的腾讯微博封装（腾讯微博开放平台的PECL的OAuth封装）时都遇到了 T_PAAMAYIM_NEKUDOTAYIM 的错误。为了方便大家，干脆这里统一说明一下吧。

这个错误的责任在我。

由于我使用的开发和部署环境都是 Ubuntu 环境，PHP 版本 5.3.3。我在编码的时候使用了一个 5.2.x 不支持的特性。在 XY/QQ/Store.php 的 28 行：

<span class="readmore"><a href="http://www.mikespook.com/2011/01/%e5%85%b3%e4%ba%8e-oauth-%e5%b0%81%e8%a3%85%e5%87%ba%e7%8e%b0-t_paamayim_nekudotayim-%e7%9a%84%e4%b8%80%e4%ba%9b%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95/" title="关于 QQ OAuth 封装出现 T_PAAMAYIM_NEKUDOTAYIM 的解决办法">阅读全文——共380字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>许多网友在使用我的 OAuth 的腾讯微博封装（<a href="http://www.mikespook.com/index.php/archives/813">腾讯微博开放平台的PECL的OAuth封装</a>）时都遇到了 T_PAAMAYIM_NEKUDOTAYIM 的错误。为了方便大家，干脆这里统一说明一下吧。</p>
<p>这个错误的责任在我。<br />
由于我使用的开发和部署环境都是 Ubuntu 环境，PHP 版本 5.3.3。我在编码的时候使用了一个 5.2.x 不支持的特性。在 XY/QQ/Store.php 的 28 行：</p>
<pre class="brush: php; title: ; notranslate">
$class::setParams($params);
</pre>
<p>经不完全验证在 5.2.x 及以下版本会报  T_PAAMAYIM_NEKUDOTAYIM 错误。</p>
<p>解决办法其实很简单，将 28 行代码替换为：</p>
<pre class="brush: php; title: ; notranslate">
call_user_func(array($class, 'setParams'), $params);
</pre>
<p>仅此即可。</p>
<p>真是抱歉了！同时感谢 <a href="http://52q.net/" target="_blank">ahu</a>、<a href="http://sjolzy.cn/" target="_blank">sjolzy</a> 提供的关键信息！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/01/%e5%85%b3%e4%ba%8e-oauth-%e5%b0%81%e8%a3%85%e5%87%ba%e7%8e%b0-t_paamayim_nekudotayim-%e7%9a%84%e4%b8%80%e4%ba%9b%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>腾讯微博开放平台的PECL的OAuth封装</title>
		<link>http://www.mikespook.com/2011/01/%e8%85%be%e8%ae%af%e5%be%ae%e5%8d%9a%e5%bc%80%e6%94%be%e5%b9%b3%e5%8f%b0%e7%9a%84pecl%e7%9a%84oauth%e5%b0%81%e8%a3%85/</link>
		<comments>http://www.mikespook.com/2011/01/%e8%85%be%e8%ae%af%e5%be%ae%e5%8d%9a%e5%bc%80%e6%94%be%e5%b9%b3%e5%8f%b0%e7%9a%84pecl%e7%9a%84oauth%e5%b0%81%e8%a3%85/#comments</comments>
		<pubDate>Tue, 04 Jan 2011 05:58:52 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[OAuth]]></category>
		<category><![CDATA[Open]]></category>
		<category><![CDATA[PECL]]></category>
		<category><![CDATA[QQ]]></category>
		<category><![CDATA[tencent]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=813</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
这是腾讯微博开放平台使用 PECL OAuth 扩展例子，我在“微博擂台”中已经使用的。现在抽取出来作为一个独立的库。

Token 的存储提供了 Session 和 Memcache 两种方式。Session 方式是默认方式，也就是说不进行任何设置默认使用 Session 存储 OAuth 的 Token。

如果想用后台进程，例如 Gearman 之类的异步调用腾讯的 API ，建议使用 Memcache。当然，也可以自己扩展存储接口，实现 Mysql 之类的存储方式。

<span class="readmore"><a href="http://www.mikespook.com/2011/01/%e8%85%be%e8%ae%af%e5%be%ae%e5%8d%9a%e5%bc%80%e6%94%be%e5%b9%b3%e5%8f%b0%e7%9a%84pecl%e7%9a%84oauth%e5%b0%81%e8%a3%85/" title="腾讯微博开放平台的PECL的OAuth封装">阅读全文——共218字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>这是腾讯微博开放平台使用 PECL OAuth 扩展例子，我在“<a href="http://xxiyy.com/qqt/vs/" target="_blank">微博擂台</a>”中已经使用的。现在抽取出来作为一个独立的库。</p>
<p>Token 的存储提供了 Session 和 Memcache 两种方式。Session 方式是默认方式，也就是说不进行任何设置默认使用 Session 存储 OAuth 的 Token。</p>
<p>如果想用后台进程，例如 Gearman 之类的异步调用腾讯的 API ，建议使用 Memcache。当然，也可以自己扩展存储接口，实现 Mysql 之类的存储方式。</p>
<p><a href="http://www.mikespook.com/wp-content/uploads/2011/01/QQ-OAuth.zip">下载代码</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2011/01/%e8%85%be%e8%ae%af%e5%be%ae%e5%8d%9a%e5%bc%80%e6%94%be%e5%b9%b3%e5%8f%b0%e7%9a%84pecl%e7%9a%84oauth%e5%b0%81%e8%a3%85/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>使用 PECL 的 OAuth 库访问 QQ 微博 API</title>
		<link>http://www.mikespook.com/2010/12/%e4%bd%bf%e7%94%a8-pecl-%e7%9a%84-oauth-%e5%ba%93%e8%ae%bf%e9%97%ae-qq-%e5%be%ae%e5%8d%9a-api/</link>
		<comments>http://www.mikespook.com/2010/12/%e4%bd%bf%e7%94%a8-pecl-%e7%9a%84-oauth-%e5%ba%93%e8%ae%bf%e9%97%ae-qq-%e5%be%ae%e5%8d%9a-api/#comments</comments>
		<pubDate>Tue, 28 Dec 2010 14:28:49 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[OAuth]]></category>
		<category><![CDATA[PECL]]></category>
		<category><![CDATA[QQ]]></category>
		<category><![CDATA[微博]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=798</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
大势所趋，QQ 也在自家门上开了个小洞让诸位看客过过瘾。不过 API 文档不给力，疏漏多、讲得粗，没有 SDK，没有 Step by step，关键细节交代不清……幸而，摸索两日，总算是探得一个靠谱的办法——PECL 的 OAuth 库访问。

特别记录于此，供众玩家观赏。



<span class="readmore"><a href="http://www.mikespook.com/2010/12/%e4%bd%bf%e7%94%a8-pecl-%e7%9a%84-oauth-%e5%ba%93%e8%ae%bf%e9%97%ae-qq-%e5%be%ae%e5%8d%9a-api/" title="使用 PECL 的 OAuth 库访问 QQ 微博 API">阅读全文——共3506字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>大势所趋，QQ 也在自家门上开了个小洞让诸位看客过过瘾。不过 API 文档不给力，疏漏多、讲得粗，没有 SDK，没有 Step by step，关键细节交代不清……幸而，摸索两日，总算是探得一个靠谱的办法——PECL 的 OAuth 库访问。</p>
<p>特别记录于此，供众玩家观赏。<br />
<span id="more-798"></span></p>
<p>在使用 <a href="http://pecl.php.net/package/oauth" target="_blank">PECL 的 OAuth</a> 库之前，尝试过包括 <a href="http://code.google.com/p/oauth-php/" target="_blank">oauth-php</a> 在内的一些第三方库。有些跟 QQ 的 API 不兼容（不要信什么大家都是严格按照 RFC 实现之类的浮云）；有些自己正常运行都有问题；还有些我觉得作为一个 API 调用界面，本身设计的用法就太过复杂，封装层次太高……本想用熟悉的 <a href="http://framework.zend.com/manual/en/zend.oauth.html" target="_blank">Zend_OAuth</a>，但是想到 Zend 的那个大块头……幸有 PECL！</p>
<p>不过 PECL 也不是人人都能用的，毕竟这个玩意是个要额外安装的家伙。大家酌情选择吧……</p>
<p>先讲讲我的运行环境。我使用的是 <a href="http://www.linode.com/?r=ca32e7b3df04fe291d4ce354dcf3b2f8f4ce93ef" target="_blank">linode 的 VPS</a> 主机，最便宜的那种。装的是 Ubuntu 10.4，Apache2、PHP5 这些都是 apt 装的。个人觉得，我投入的那点精力优化出来的编译参数不一定会比 ubuntu 的团队做得更好。</p>
<p>所以 OAuth 扩展的安装是非常的简单，首先安装 php 的编译依赖和 pear 包。libpcre3 是 OAuth 编译依赖的一个包，主要用来支持正则表达式：</p>
<pre class="brush: bash; title: ; notranslate">
sudo apt-get install php5-dev php-pear libpcre3-dev
</pre>
<p>使用 pecl 安装 OAuth：</p>
<pre class="brush: bash; title: ; notranslate">
sudo pecl install oauth
</pre>
<p>在 /etc/php5/apache2/conf.d/ 下建立文件 oauth.ini，加内容：</p>
<pre class="brush: bash; title: ; notranslate">
extension=oauth.so
</pre>
<p>重启 web 服务器：</p>
<pre class="brush: bash; title: ; notranslate">
sudo apache2ctl restart
</pre>
<p>好了，OAuth 扩展加载完成。</p>
<p>在演示中，只做两件事情：1、利用 OAuth 完成身份验证。2、取得身份验证后获取大厅消息。</p>
<p>下面是代码，不罗嗦了，注释里说明。</p>
<p>define.php</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
define('OAUTH_KEY', '应用信息里的 App Key');
define('OAUTH_SECRET', '应用信息里的 App Secret');

define('REQUEST_TOKEN', 'https://open.t.qq.com/cgi-bin/request_token');
define('AUTHORIZE', 'https://open.t.qq.com/cgi-bin/authorize');
define('ACCESS_TOKEN', 'https://open.t.qq.com/cgi-bin/access_token');

define('CALLBACK', 'http://xxiyy.com/qqt/callback.php');

session_id('XY');
session_start();
</pre>
<p>index.php</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
include('define.php');
try {
    $oauth = new OAuth(OAUTH_KEY, OAUTH_SECRET, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
    $oauth-&gt;enableDebug();
    // 很重要！！！在 OAuth 标准里是没有规定 nonce 的长度的，但是 QQ 对 nonce 的长度做了要求——32 字节长。如果不设置一下，会返回 400 错误。我为此纠结了一天。
    $oauth-&gt;setNonce(md5(rand()));
    // CALLBACK 一定要设置，OAuth 扩展的文档上是没设置的，但是 QQ 这里不设会报错
    $requestTokenInfo = $oauth-&gt;getRequestToken(REQUEST_TOKEN, CALLBACK);
    $_SESSION['oauth_token_secret'] = $requestTokenInfo['oauth_token_secret'];
    // header(&quot;Location: ……&quot;) 亦可
    echo &quot;&lt;p&gt;&lt;a href='&quot; . AUTHORIZE . &quot;?oauth_token=&quot; . $requestTokenInfo['oauth_token'] . &quot;'&gt;authorize&lt;/a&gt;&lt;/p&gt;&quot;;
} catch (OAuthException $e) {
    var_dump($e);
}
</pre>
<p>callback.php</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
include('define.php');
try {
    $oauth = new OAuth(OAUTH_KEY, OAUTH_SECRET, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
    $oauth-&gt;enableDebug();
    // 很重要！！！如果不设置一下，会返回 401 错误。
    $oauth-&gt;setNonce(md5(rand()));
    $oauth-&gt;setToken($_GET['oauth_token'], $_SESSION['oauth_token_secret']);
    $accessTokenInfo = $oauth-&gt;getAccessToken(ACCESS_TOKEN, null, $_GET['oauth_verifier']);
    $_SESSION['access_token'] = $accessTokenInfo['oauth_token'];
    $_SESSION['access_secret'] = $accessTokenInfo['oauth_token_secret'];
    header('Location: room.php');
} catch (OAuthException $e) {
    var_dump($e);
}
</pre>
<p>room.php</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
include('define.php');
try {
    $url = &quot;http://open.t.qq.com/api/statuses/public_timeline?format=json&amp;pos=0&amp;reqnum=10&quot;;

    $oauth = new OAuth(OAUTH_KEY, OAUTH_SECRET, OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
    $oauth-&gt;enableDebug();
    $oauth-&gt;setToken($_SESSION['access_token'], $_SESSION['access_secret']);
    $oauth-&gt;fetch($url);
    $json = json_decode($oauth-&gt;getLastResponse());
    var_dump($json);
} catch (OAuthException $e) {
    var_dump($e);
}
</pre>
<p>完整代码：<a href='http://www.mikespook.com/wp-content/uploads/2010/12/qqt.zip'>下载</a></p>
<p>PS：多说两句。今天微博上看到关于 QQ 开放的 API 有一些讨论，似乎认可度不高。不过，虽然腾讯这次开放的微博 API 还有很多不足，尤其是开发者支持和文档方面。但说句公道话，还远未达到“令人畏惧”的程度（你们想看看更“令人畏惧”的吗？）。按照腾讯一贯的风格：持续迭代、快速改进，应该很快会有新的支持。</p>
<p>尝试一下这个：<br />
<a href="javascript:void(0)" onclick="postToWb();" class="tmblog"><img src="http://v.t.qq.com/share/images/s/b32.png"></a><script type="text/javascript">
	function postToWb(){
		var _t = encodeURI(document.title);
		var _url = encodeURIComponent(document.location);
		var _appkey = encodeURI("appkey");//你从腾讯获得的appkey
		var _pic = encodeURI('');//（例如：var _pic='图片url1|图片url2|图片url3....）
		var _site = 'http://www.mikespook.com';//你的网站地址
		var _u = 'http://v.t.qq.com/share/share.php?title='+_t+'&#038;url='+_url+'&#038;appkey='+_appkey+'&#038;site='+_site+'&#038;pic='+_pic;
		window.open( _u,'', 'width=700, height=680, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=yes, resizable=no, status=no' );
	}
</script><br />
貌似有问题，带不上地址呢？我又搞错什么了？</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2010/12/%e4%bd%bf%e7%94%a8-pecl-%e7%9a%84-oauth-%e5%ba%93%e8%ae%bf%e9%97%ae-qq-%e5%be%ae%e5%8d%9a-api/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>使用 PECL 的 OAuth 访问腾讯微薄 API 的一点麻烦</title>
		<link>http://www.mikespook.com/2010/12/%e4%bd%bf%e7%94%a8-pecl-%e7%9a%84-oauth-%e8%ae%bf%e9%97%ae%e8%85%be%e8%ae%af%e5%be%ae%e8%96%84api/</link>
		<comments>http://www.mikespook.com/2010/12/%e4%bd%bf%e7%94%a8-pecl-%e7%9a%84-oauth-%e8%ae%bf%e9%97%ae%e8%85%be%e8%ae%af%e5%be%ae%e8%96%84api/#comments</comments>
		<pubDate>Tue, 28 Dec 2010 09:33:49 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[OAuth]]></category>
		<category><![CDATA[PECL]]></category>
		<category><![CDATA[微博]]></category>
		<category><![CDATA[腾讯]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=795</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
尝试用 PECL 的 OAuth 访问腾讯微薄，到 Access Token 那步总是有问题。 5％ 的成功率。在 Request Token 的时候，也总有不成功的情况发生。

捕捉到异常：“Invalid auth/bad request (got a 401, expected HTTP/1.1 20X or a redirect)”，服务器返回“Invalid / expired Token”。

奇怪的是同样的代码，那 5％ 的成功率是哪里来的。上 Q 一问，腾讯某大牛提示检查检查 nonce 或者 timestamp 是不是正确。于是乎，检查了一下 OAuth 的代码：

<span class="readmore"><a href="http://www.mikespook.com/2010/12/%e4%bd%bf%e7%94%a8-pecl-%e7%9a%84-oauth-%e8%ae%bf%e9%97%ae%e8%85%be%e8%ae%af%e5%be%ae%e8%96%84api/" title="使用 PECL 的 OAuth 访问腾讯微薄 API 的一点麻烦">阅读全文——共834字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>尝试用 PECL 的 OAuth 访问腾讯微薄，到 Access Token 那步总是有问题。 5％ 的成功率。在 Request Token 的时候，也总有不成功的情况发生。</p>
<p>捕捉到异常：“Invalid auth/bad request (got a 401, expected HTTP/1.1 20X or a redirect)”，服务器返回“Invalid / expired Token”。</p>
<p>奇怪的是同样的代码，那 5％ 的成功率是哪里来的。上 Q 一问，腾讯某大牛提示检查检查 nonce 或者 timestamp 是不是正确。于是乎，检查了一下 <a href="http://svn.php.net/viewvc/pecl/oauth/trunk/oauth.c?view=co&#038;content-type=text/plain" target="_blank">OAuth 的代码</a>：</p>
<pre class="brush: cpp; title: ; notranslate">
	if (soo-&gt;nonce) {
		nonce = estrdup(soo-&gt;nonce);
	} else {
		struct timeval tv;
		int sec, usec;
		/* XXX maybe find a better way to generate a nonce... */
		gettimeofday((struct timeval *) &amp;tv, (struct timezone *) NULL);
		sec = (int) tv.tv_sec;
		usec = (int) (tv.tv_usec % 0x100000);
		spprintf(&amp;nonce, 0, &quot;%ld%08x%05x%.8f&quot;, php_rand(TSRMLS_C), sec, usec, php_combined_lcg(TSRMLS_C) * 10);
	}
</pre>
<p>看到“spprintf(&#038;nonce, 0, “%ld%08x%05x%.8f”, php_rand(TSRMLS_C), sec, usec, php_combined_lcg(TSRMLS_C) * 10);”了吗？！悲剧啊！</p>
<p>现在明白腾讯文档上那句“随机串（32个字符长度）”是什么意思了，RFC 5849 完全没提 nonce 需要 32 字符长度。腾讯自己说自己复合 OAuth 1.0a 标准，然后在标准上搞出了小标准⋯⋯</p>
<p>我比较懒，简单搞掂：</p>
<pre class="brush: php; title: ; notranslate">
$oauth-&gt;setNonce(md5(rand()));
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2010/12/%e4%bd%bf%e7%94%a8-pecl-%e7%9a%84-oauth-%e8%ae%bf%e9%97%ae%e8%85%be%e8%ae%af%e5%be%ae%e8%96%84api/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Web编程异步模型的 Gearman 实现（残）</title>
		<link>http://www.mikespook.com/2010/06/web%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84-gearman-%e5%ae%9e%e7%8e%b0%ef%bc%88%e6%ae%8b%ef%bc%89/</link>
		<comments>http://www.mikespook.com/2010/06/web%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84-gearman-%e5%ae%9e%e7%8e%b0%ef%bc%88%e6%ae%8b%ef%bc%89/#comments</comments>
		<pubDate>Tue, 08 Jun 2010 02:34:35 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[Gearman]]></category>
		<category><![CDATA[web]]></category>
		<category><![CDATA[异步]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=597</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
写了 PHP 原生的二段式异步模型的实现，我就想着用 Gearman 实现一个 callback 方式的异步。还没准备好怎么去写，就看到了靓文一篇《Gearman 心得》。

看过之后，甚感压力：好文！！于是，弃笔不写，洗洗睡罢了……

补充一下，在“心得”文中仅仅说明了不阻塞的后台作业。对于异步获取数据并未说明。所以我这里罗嗦一下……

<span class="readmore"><a href="http://www.mikespook.com/2010/06/web%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84-gearman-%e5%ae%9e%e7%8e%b0%ef%bc%88%e6%ae%8b%ef%bc%89/" title="Web编程异步模型的 Gearman 实现（残）">阅读全文——共373字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>写了 PHP 原生的二段式异步模型的实现，我就想着用 Gearman 实现一个 callback 方式的异步。还没准备好怎么去写，就看到了靓文一篇<a href="http://www.jaceju.net/blog/?p=1211" target="_blank">《Gearman 心得》</a>。</p>
<p>看过之后，甚感压力：<strong>好文！！</strong>于是，弃笔不写，洗洗睡罢了……</p>
<p>补充一下，在“心得”文中仅仅说明了不阻塞的后台作业。对于异步获取数据并未说明。所以我这里罗嗦一下……</p>
<p>worker 如果用 php 来实现，并且不用<a href="http://www.mikespook.com/index.php/archives/587" target="_blank">《Web编程异步模型的PHP 原生实现》</a>中的异步方式，是无法实现 php 的 client 的异步的。比较好的实现方式是 worker 不使用 php，用 python、perl 或者 c，实现一个线程池来执行 job。当然，私下觉得用 stackless python 可能是更好的选择。</p>
<p>2010年07月18日补充：<br />
好吧，终于有人撰文，正好可以补充完整这个异步思路：<br />
<a href="http://blog.ez2learn.com/2010/07/17/talk-about-coroutine-and-gevent/" target="_blank">淺談coroutine與gevent</a><br />
就他了，太棒了！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2010/06/web%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84-gearman-%e5%ae%9e%e7%8e%b0%ef%bc%88%e6%ae%8b%ef%bc%89/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Web编程异步模型的PHP原生实现</title>
		<link>http://www.mikespook.com/2010/06/web%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84php%e5%8e%9f%e7%94%9f%e5%ae%9e%e7%8e%b0/</link>
		<comments>http://www.mikespook.com/2010/06/web%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84php%e5%8e%9f%e7%94%9f%e5%ae%9e%e7%8e%b0/#comments</comments>
		<pubDate>Sat, 05 Jun 2010 03:01:31 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[异步]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=587</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
这是基于上一篇随笔：关于Web编程异步模型的白日梦的实现。这一思路我记得在 05 年还是 07 年的时候就在 ChinaUnix 上有高人所讨论，只是自己当时愚钝未能明晰本质，纠结于 PHP 的多线程之中……

这个实现写好有段时间了，最近琐碎的事情很多，一直没有整理出来。今日得闲记录下来。

利用PHP自带的 stream_select 函数实现异步，利用这个函数使得 PHP 原生支持的异步调用实现，无须第三方服务或库。不过只能实现二段式异步调用，就是说会有明显的 Begin 和 End 两个阶段。

<span class="readmore"><a href="http://www.mikespook.com/2010/06/web%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84php%e5%8e%9f%e7%94%9f%e5%ae%9e%e7%8e%b0/" title="Web编程异步模型的PHP原生实现">阅读全文——共2511字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>这是基于上一篇随笔：<a href="http://www.mikespook.com/index.php/archives/580" target="_blank">关于Web编程异步模型的白日梦</a>的实现。这一思路我记得在 05 年还是 07 年的时候就在 ChinaUnix 上有高人所讨论，只是自己当时愚钝未能明晰本质，纠结于 PHP 的多线程之中……</p>
<p>这个实现写好有段时间了，最近琐碎的事情很多，一直没有整理出来。今日得闲记录下来。</p>
<p>利用PHP自带的 stream_select 函数实现异步，利用这个函数使得 PHP 原生支持的异步调用实现，无须第三方服务或库。不过只能实现二段式异步调用，就是说会有明显的 Begin 和 End 两个阶段。<br />
<span id="more-587"></span><br />
代码比较容易理解，大家自己看吧。</p>
<p>先看看，封装好的方法，如何异步调用：</p>
<pre class="brush: php; title: ; notranslate">
/**
 * DEMO
 */

/**
 * 请求集合
 */
$requestSet = array(
    'foobar' =&gt; array(
        'stream'    =&gt;  '127.0.0.1:80',
        'path'      =&gt;  '/api.php',
        'params'    =&gt;  array('a'=&gt;1),
        'callback'  =&gt;  'echoArray',
    ),
    'foo-bar' =&gt; array(
        'stream'    =&gt;  '127.0.0.1:80',
        'path'      =&gt;  '/api.php',
        'params'    =&gt;  array('method'=&gt;'get', 'table'=&gt;'user', 'id'=&gt;1),
        'callback'  =&gt;  'echoArray',
    ),
    'another' =&gt; array(
        'stream'    =&gt;  '127.0.0.1:80',
        'path'      =&gt;  '/api.php',
        'params'    =&gt;  array(),
        'callback'  =&gt;  'echoArray',
    ),
);

/**
 * 演示处理函数，这里只作打印
 * @param $a array 输入数组
 */
function echoArray($a) {
    var_dump($a);
}

/**
 * 二段异步调用
 */

/**
 * 第一阶段，发起请求
 */
$fps = beginRequest($requestSet);
/**
 * 第二阶段，处理返回
 */
endReponse($requestSet, $fps);
</pre>
<p>封装的异步函数：</p>
<pre class="brush: php; title: ; notranslate">
/**
 * 发起一个请求
 * @param $stream string 主机、端口
 * @param $path string api路径
 * @param $params array 参数
 * @return resource 文件描述符
 */
function request($stream, $path, $params) {
    $fp = stream_socket_client(&quot;tcp://{$stream}&quot;, $errno, $errstr, 30);
    if (!$fp) {
        throw new Exception($errstr, $errno);
    } else {
        $params = http_build_query($params);
        $host = explode(':', $stream);
        $str = &quot;GET {$path}?{$params} HTTP/1.0\r\nHost: {$host[0]}\r\nAccept: */*\r\n\r\n&quot;;
        fwrite($fp, $str);
    }
    return $fp;
}

/**
 * 获取请求的返回
 * @param $fp resource 文件描述符
 * @return array 取得的返回值
 */
function response($fp) {
    $r = stream_get_contents($fp);
    // 简陋的 HTTP 协议解析
    $r = explode(&quot;\n&quot;, $r);
    return json_decode($r[count($r) - 1]);
}

/**
 * 发起请求
 * @param $requestSet array 请求队列数组
 * @return array 文件描述符数组
 */
function beginRequest($requestSet) {
    $fps = array();
    foreach($requestSet as $key =&gt; $request) {
        try {
            $fps[$key] = request($request['stream'], $request['path'], $request['params']);
        } catch (Exception $e) {
            echo $e;
        }
    }
    return $fps;
}

/**
 * 处理返回
 * @param $requestSet array 请求队列数组
 * @param $fps array 文件描述符数组
 */
function endReponse($requestSet, $fps) {
    $r = $fps;

    while(count($fps) &gt; 0) {
        // 这里是重点，stream_select 保证了先返回的先处理。
        $n = stream_select($r, $w, $e, 0);
        if ($n === false) {
            echo $e;
    	    continue;
        }
        if ($n &gt; 0) {
            foreach($r as $socket) {
                // 读取返回值，并调用回调
                $r = response($socket);
                $key = array_search($socket, $fps);
                $callback = $requestSet[$key]['callback'];
                $callback($r);
                fclose($socket);
	        unset($fps[$key]);
	    }
        }
        $r = $fps;
    }
}
</pre>
<p>api.php 接口：</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
$a = array(
    'hello' =&gt; 'world',
    'foobar' =&gt; array(0,1,2,3,4),
    'GET'   =&gt;  $_GET,
    );
echo json_encode($a);
</pre>
<p>为了演示的需要剔除了面向对象、异常处理的一些内容。同时硬性规定 api 接口使用 json 通过 http 协议传递数据。<br />
实际上，由于 php 的 stream 的功能可以处理包括 udp 协议在内的I/O。可利用其他协议和开销更小的报文格式，以便能获得更好的性能。</p>
<p>由于模型的限制，callback 异步无法直接实现，可将存在数据读取依赖关系的数据读取过程拆解为若干个二段异步调用。</p>
<p>PS：这里只是一个实现方法的演示，并不涉及该方法是否在真实环境可用。也就是说，虽然实现了PHP的异步调用，但是实际运行可能并不比顺序阻塞读取的性能高。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2010/06/web%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84php%e5%8e%9f%e7%94%9f%e5%ae%9e%e7%8e%b0/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>关于Web编程异步模型的白日梦</title>
		<link>http://www.mikespook.com/2010/05/%e5%85%b3%e4%ba%8eweb%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84%e7%99%bd%e6%97%a5%e6%a2%a6/</link>
		<comments>http://www.mikespook.com/2010/05/%e5%85%b3%e4%ba%8eweb%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84%e7%99%bd%e6%97%a5%e6%a2%a6/#comments</comments>
		<pubDate>Thu, 27 May 2010 03:04:26 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Stardy & Research]]></category>
		<category><![CDATA[golang]]></category>
		<category><![CDATA[异步]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=580</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
早上刷牙，处于半睡状态。突然想起昨天晚上看到的那个 go-lang 的 MVC 框架，若使用 go func() 方式异步获取数据，应当是不错的。窃喜……梦醒……

在地铁上被前前后后那些特种男女逼到车角，无奈。又想起早上那个白日梦，遂上网搜索了一番。得老赵的佳作一篇《F# 与ASP.NET（1）：基于事件的异步模式与异步Action》。之前看过，由于对微软无爱，未能细品。今日一读，如醍醐灌顶，豁然开朗。

遂整理思路如下，以待后用。

<span class="readmore"><a href="http://www.mikespook.com/2010/05/%e5%85%b3%e4%ba%8eweb%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84%e7%99%bd%e6%97%a5%e6%a2%a6/" title="关于Web编程异步模型的白日梦">阅读全文——共1987字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>早上刷牙，处于半睡状态。突然想起昨天晚上看到的那个 go-lang 的 MVC 框架，若使用 go func() 方式异步获取数据，应当是不错的。窃喜……梦醒……</p>
<p>在地铁上被前前后后那些特种男女逼到车角，无奈。又想起早上那个白日梦，遂上网搜索了一番。得老赵的佳作一篇<a href="http://blog.zhaojie.me/2010/04/fsharp-for-asp-net-1-event-based-asynchronous-pattern-and-async-action.html" target="_blank">《F# 与ASP.NET（1）：基于事件的异步模式与异步Action》</a>。之前看过，由于对微软无爱，未能细品。今日一读，如醍醐灌顶，豁然开朗。</p>
<p>遂整理思路如下，以待后用。<br />
<span id="more-580"></span></p>
<p>在说异步模型之前，先说说最常见的同步模型吧。例如下面的 PHP 代码：</p>
<pre class="brush: php; title: ; notranslate">
// 获取数据
$userInfo = getUserInfo();
$newsList = getNewsList();
$topRateNewsList = getNewsList('DESC `rate`');
// 创建模板，绑定变量
$tmp = CreateTemplate();
$tmp-&gt;bind('userInfo', $userInfo);
$tmp-&gt;bind('newsList', $newsList);
$tmp-&gt;bind('topRateNewsList', $topRateNewsList);
// 渲染模板，输出
$tmp-&gt;render();
echo $tmp;
</pre>
<p>在这段代码中，所有调用都是顺序的。也就是说，如果 getUserInfo($userId) 没有返回用户信息的话，getNewsList(5) 永远都不会被调用。实际情况，可能是用户信息是从用户库取数据，新闻是从新闻库取数据。假设新闻库运行良好，而由于某种原因用户库宕机了，那么这次请求也就废掉了。故障也从一个库的宕机扩散到整个站点。</p>
<p>现在，我白日做梦的创建一种新语言 go-php，引入 go-lang 的关键字 go 到 php 中：</p>
<pre class="brush: php; title: ; notranslate">
// 创建一个数据通道
$chan = CreateChan();
// 获取数据，并放到数据通道上去
go getUserInfo($chan);
go getNewsList($chan);
$params = array('DESC `rate`');
go getNewsList($chan, $params);
// 创建模板
$tmp = CreateTemplate();
// 从通道上取数据
while($d = $chan-&gt;read()) {
    if ($d['error']) {
        // 数据有错误 ……处理一下吧
    } else {
        $tmp-&gt;bind($d['key'], $d['data']);
    }
}
// 渲染模板，输出
$tmp-&gt;render();
echo $tmp;
</pre>
<p>好了，这其实不是什么新奇创造，这只是一个<strong>二段式异步调用（Begin/End）</strong>。这有点像大宗采购，采购商并不一件一件的商品进行采购，而是拿着清单说：“好了，兄弟，这是我要的货，你们帮我找齐，放到码头406号仓库去……”，然后他就在 406 号仓库等着点货了。这段代码还可以改进，就像采购清单一样，将这个清单推到数据层，数据层把数据返回到数据通道上去。恩，应该不少人在自己的应用中使用 MQ，Memcache 甚至 pipe 实现了这种异步了吧。</p>
<p>再来看另外一段 go-php 代码：</p>
<pre class="brush: php; title: ; notranslate">
/**
 * 回调函数
 * @param $tmp 模板
 * @param $d 返回的数据
 */
function callbackBind($tmp, $d) {
    if ($d['error']) {
        // 数据有错误 ……处理一下吧
    } else {
        $tmp-&gt;bind($d['key'], $d['data']);
    }
}
// 创建模板
$tmp = CreateTemplate();
// 获取数据，并设置数据回调
go getUserInfo(callbackBind, $tmp);
go getNewsList(callbackBind, $tmp);
$params = array('DESC `rate`');
go getNewsList(callbackBind, $tmp, $params);
// 如果不是所有数据都回调了，则阻塞
$tmp-&gt;wait();
// 渲染模板，输出
$tmp-&gt;render();
echo $tmp;
</pre>
<p>这就是老赵的<strong>事件回调</strong>的异步处理的 go-php 版本。这有点像渠道商订货（或者淘宝上的无货代理？）：“我需要XXXX，你帮我送到XXXX去”。然后坐等，所有的内容都送到了，就收钱走人。</p>
<p>对于数据读取的错误，只要处理得当也不是致命的。最多在渲染页面的时候，少某块数据，用户只会奇怪：“这次怎么打开没有新闻那部分的内容了呢……刷新一下看看……”。而不会说：“烂网站，又打不开了……”用户体验直线上升啊！</p>
<p>好了，梦就做到这里。相信这两种方式其实已经有实际案例了。我比较孤陋寡闻一些，了解的不多。而且有的东西，未经许可也不好多说……大家私下打听吧。</p>
<p>总之呢，两种异步模型各自有各自的好处。并行的数据存取，提高 I/O 利用率是其本质。王道啊……</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2010/05/%e5%85%b3%e4%ba%8eweb%e7%bc%96%e7%a8%8b%e5%bc%82%e6%ad%a5%e6%a8%a1%e5%9e%8b%e7%9a%84%e7%99%bd%e6%97%a5%e6%a2%a6/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>关于昨日爆出的 Nginx + PHP CGI 漏洞的一点点补充</title>
		<link>http://www.mikespook.com/2010/05/%e5%85%b3%e4%ba%8e%e6%98%a8%e6%97%a5%e7%88%86%e5%87%ba%e7%9a%84-nginx-php-cgi-%e6%bc%8f%e6%b4%9e%e7%9a%84%e4%b8%80%e7%82%b9%e7%82%b9%e8%a1%a5%e5%85%85/</link>
		<comments>http://www.mikespook.com/2010/05/%e5%85%b3%e4%ba%8e%e6%98%a8%e6%97%a5%e7%88%86%e5%87%ba%e7%9a%84-nginx-php-cgi-%e6%bc%8f%e6%b4%9e%e7%9a%84%e4%b8%80%e7%82%b9%e7%82%b9%e8%a1%a5%e5%85%85/#comments</comments>
		<pubDate>Fri, 21 May 2010 06:14:52 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[cgi]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[漏洞]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=575</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
我第一次看到这个漏洞是在 Laruence 的博客。看完之后，我赶紧评估了一下我们正在开发的产品出现这个漏洞的可能性。还不错，在我们当前架构下，这个漏洞被成功利用的可能性为 0 ……

结果，今天在大嘴巴 cnbeta 看到了这篇很标题党的新闻《80后发现nginx 0day漏洞，上传图片可入侵100万服务器》。然后引用的出处是这里。

好了，我认为我提供的背景资料足够详细了。现在说说为什么我们的产品不会出现这个被利用的可能吧。

<span class="readmore"><a href="http://www.mikespook.com/2010/05/%e5%85%b3%e4%ba%8e%e6%98%a8%e6%97%a5%e7%88%86%e5%87%ba%e7%9a%84-nginx-php-cgi-%e6%bc%8f%e6%b4%9e%e7%9a%84%e4%b8%80%e7%82%b9%e7%82%b9%e8%a1%a5%e5%85%85/" title="关于昨日爆出的 Nginx + PHP CGI 漏洞的一点点补充">阅读全文——共484字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>我第一次看到这个漏洞是在 <a href="http://www.laruence.com/2010/05/20/1495.html" target="_blank">Laruence 的博客</a>。看完之后，我赶紧评估了一下我们正在开发的产品出现这个漏洞的可能性。还不错，在我们当前架构下，这个漏洞被成功利用的可能性为 0 ……</p>
<p>结果，今天在大嘴巴 cnbeta 看到了这篇很标题党的新闻<a href="http://www.cnbeta.com/articles/111711.htm" target="_blank">《80后发现nginx 0day漏洞，上传图片可入侵100万服务器》</a>。然后引用的出处是<a href="http://www.80sec.com/nginx-securit.html" target="_blank">这里</a>。</p>
<p>好了，我认为我提供的背景资料足够详细了。现在说说为什么我们的产品不会出现这个被利用的可能吧。</p>
<p>其实很简单，将资源文件和 php 脚本文件放在不同的域名下面。然后将资源文件（含产品自身的和用户贡献的）的访问限于只作文件传输，不作任何的脚本解析。</p>
<p>例如 PHP 脚本执行的主机名是 www.mikespook.com。而上传文件和图像、js、css 等放在 static.mikespook.com 主机名下。</p>
<p>其实，就是这么简单的一个隔离措施，就避免了出现这种上传并解析的漏洞。</p>
<p>即使想用同一个域名，通过对 nginx 的配置禁止资源文件目录下的文件被当作脚本解析也是很容易的。</p>
<p>这个故事教育我们：细节是基石，架构是王道！！！</p>
<p>另，根据来自高春辉的可靠消息，手机之家也不存在此问题……</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2010/05/%e5%85%b3%e4%ba%8e%e6%98%a8%e6%97%a5%e7%88%86%e5%87%ba%e7%9a%84-nginx-php-cgi-%e6%bc%8f%e6%b4%9e%e7%9a%84%e4%b8%80%e7%82%b9%e7%82%b9%e8%a1%a5%e5%85%85/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于 php 的 count 函数</title>
		<link>http://www.mikespook.com/2009/11/%e5%85%b3%e4%ba%8e-php-%e7%9a%84-count-%e5%87%bd%e6%95%b0/</link>
		<comments>http://www.mikespook.com/2009/11/%e5%85%b3%e4%ba%8e-php-%e7%9a%84-count-%e5%87%bd%e6%95%b0/#comments</comments>
		<pubDate>Tue, 24 Nov 2009 14:38:06 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=424</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
今天一个同事在Q群上考大家关于 php 的 count 函数的一些东东，大意是 count(&#8216;some string&#8217;) 会输出什么……印象中，以前跟谁讨论过这个。不过日子久远，已经有些模糊了。所以还是写下来，just4fun。

大部分实践者都知道 count(string) 输出的结果是 1，而不是有的人期望的 strlen(string)。如果输出 count(callback)，会惊奇的发现结果也是 1。这是为什么呢？

代码说明一切（php-5.2.11/ext/standard/array.c）：

<span class="readmore"><a href="http://www.mikespook.com/2009/11/%e5%85%b3%e4%ba%8e-php-%e7%9a%84-count-%e5%87%bd%e6%95%b0/" title="关于 php 的 count 函数">阅读全文——共1700字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>今天一个同事在Q群上考大家关于 php 的 count 函数的一些东东，大意是 count(&#8216;some string&#8217;) 会输出什么……印象中，以前跟谁讨论过这个。不过日子久远，已经有些模糊了。所以还是写下来，just4fun。</p>
<p>大部分实践者都知道 count(string) 输出的结果是 1，而不是有的人期望的 strlen(string)。如果输出 count(callback)，会惊奇的发现结果也是 1。这是为什么呢？<span id="more-424"></span></p>
<p>代码说明一切（php-5.2.11/ext/standard/array.c）：</p>
<pre lang="c">/* {{{ proto int count(mixed var [, int mode])
   Count the number of elements in a variable (usually an array) */
PHP_FUNCTION(count)
{
	zval *array;
	long mode = COUNT_NORMAL;

	if (zend_parse_parameters (ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &amp;array, &amp;mode) == FAILURE)
		return;

	switch (Z_TYPE_P(array)) {
		case IS_NULL:
			RETURN_LONG(0);
			break;
		case IS_ARRAY:
			RETURN_LONG (php_count_recursive (array, mode TSRMLS_CC));
			break;
		case IS_OBJECT: {
#ifdef HAVE_SPL
			/* it the object implements Countable we call its count() method */
			zval *retval;

			if (Z_OBJ_HT_P(array)-&gt;get_class_entry &amp;&amp; instanceof_function(Z_OBJCE_P(array), spl_ce_Countable TSRMLS_CC)) {
				zend_call_method_with_0_params(&amp;array, NULL, NULL, "count", &amp;retval);
				if (retval) {
					convert_to_long_ex(&amp;retval);
					RETVAL_LONG(Z_LVAL_P(retval));
					zval_ptr_dtor(&amp;retval);
				}
				return;
			}
#endif
			/* if not we return the number of properties (not taking visibility into account) */
			if (Z_OBJ_HT_P(array)-&gt;count_elements) {
				RETVAL_LONG(1);
				if (SUCCESS == Z_OBJ_HT(*array)-&gt;count_elements(array, &amp;Z_LVAL_P(return_value) TSRMLS_CC)) {
					return;
				}
			}
		}
		default:
			RETURN_LONG(1);
			break;
	}
}
/* }}} */</pre>
<p>可以发现在 319 行的 switch 分别判断了NULL（IS_NULL）、数组（IS_ARRAY）、对象（IS_OBJECT）和其他（default）。不论是 string 还是 callback 都被归类到了 default 里，所以在 350 行返回了长整形 1。也就是说除了 NULL、Array、Object 其他任何类型的数据 count 都只会返回 1。</p>
<p>通常，用 count 函数来计算数组的机会远大于对 NULL 值使用 count 函数。如果优化一下 case 的顺序，例如将 IS_ARRAY 放在 IS_NULL 的前面，可以减少那么一点点判断……当然了，现在的计算机性能，可能这点点优化是完全可以不必考虑的了。</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211; Just for fun 分割线 &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>如果期望 count(string) 返回 strlen(string) 的结果，其实只要在 switch 中增加一个 case 即可：</p>
<pre lang="c">		case IS_STRING:
			RETURN_LONG (strlen(array));
			break;</pre>
<p>这个探讨没什么意义，好玩罢了……</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2009/11/%e5%85%b3%e4%ba%8e-php-%e7%9a%84-count-%e5%87%bd%e6%95%b0/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>脚本语言的配置文件</title>
		<link>http://www.mikespook.com/2009/09/%e8%84%9a%e6%9c%ac%e8%af%ad%e8%a8%80%e7%9a%84%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6/</link>
		<comments>http://www.mikespook.com/2009/09/%e8%84%9a%e6%9c%ac%e8%af%ad%e8%a8%80%e7%9a%84%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6/#comments</comments>
		<pubDate>Mon, 21 Sep 2009 02:30:26 +0000</pubDate>
		<dc:creator>mikespook</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[xml]]></category>
		<category><![CDATA[配置文件]]></category>

		<guid isPermaLink="false">http://www.mikespook.com/?p=405</guid>
		<description><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
关于配置文件，在 PHP 的 Zend Framework 中我做过一些简单的关于性能的测试：http://www.mikespook.com/index.php/archives/36。将 ninnypro 的配置文件从 ini 修改为 xml ，并且声称能提高传说中的性能。

最近被调到另外一个在用 python 的组帮忙，阅读了他们的实现服务器端的 python 代码之，配置文件近二十余个，全是 xml 文件。为了使用着些配置文件，从 XMLFile 继承，实现了二十余个 Config 类。

<span class="readmore"><a href="http://www.mikespook.com/2009/09/%e8%84%9a%e6%9c%ac%e8%af%ad%e8%a8%80%e7%9a%84%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6/" title="脚本语言的配置文件">阅读全文——共1350字</a></span>]]></description>
			<content:encoded><![CDATA[<style type="text/css">
#leftcontainerBox {
	float:left;
	position: fixed;
	top: 60%;
	left: 70px;
}
#leftcontainerBox .buttons {
	float:left;
	clear:both;
	margin:4px 4px 4px 4px;
	padding-bottom:2px;
}
#bottomcontainerBox {
	width: 50%;
	padding-top: 1px;
}
#bottomcontainerBox .buttons {
	float: left;
	margin: 4px 4px 4px 4px;
}
</style>
<p>关于配置文件，在 PHP 的 Zend Framework 中我做过一些简单的关于性能的测试：<a href="http://www.mikespook.com/index.php/archives/36" target="_blank">http://www.mikespook.com/index.php/archives/36</a>。将 ninnypro 的配置文件从 ini 修改为 xml ，并且声称能提高传说中的性能。</p>
<p>最近被调到另外一个在用 python 的组帮忙，阅读了他们的实现服务器端的 python 代码之，配置文件近二十余个，全是 xml 文件。为了使用着些配置文件，从 XMLFile 继承，实现了二十余个 Config 类。</p>
<p><strong>这看起来似乎没什么问题。<span id="more-405"></span></strong></p>
<p>是的，一点问题也没有。</p>
<ol>
<li>XML是高度结构化的描述方式。</li>
<li>XML有大量丰富的包、库支持。</li>
<li>XML的跨语言能力也是相当优异。你可以轻松的用市面上常见的所有语言轻松的掌控 XML。</li>
</ol>
<p>这些都决定了 XML 作为配置文件，肯定是比 ini 或者自定义结构的配置文件更加优异的结构。同时，大量的实践也证明 XML 的优异性。例如 j2ee 的实践、SOA的实现、Jabber 的应用都说明 XML: is the lord of the configuration。</p>
<p><strong>但是真得一点问题都没有么？</strong></p>
<p>首先，来看一下 Zend Framework 的 Zend_Config。由于 PHP 自身的数据结构特点，Zend_Config_Xml 和 Zend_Config_Ini 两个类都会加载相应的配置文件，解析后作为数组传递到 Zend_Config 类中。也就是说，最终殊途同归，所有的配置都变成了 PHP 的数组。</p>
<p>然后，再来看一下那个项目中的配置文件。结构化的 XML 被 XMLFile 读取，然后转化为 XMLFile 对象。再统一放入 ConfigManagement 中，经过转换，将部分结构转为 dict 使用。</p>
<p>使用配置文件的原因，我猜测是因为以前，编译语言开发的程序经过编译后，不具有动态改变参数的能力。虽然可以从命令行通过传参来在每次运行时修改配置，但是无论是谁需要通过命令行传递成百上千的参数，都会疯掉。而且，命令行传参，不具有结构化的特征。在一些场景下，需要快速修改部分配置，就变得极为困难。</p>
<p>于是聪明的程序员发明了对人友好的、结构化的配置文件，通过读取配置文件来改变程序内部参数的使用。</p>
<p>这一习惯一直沿用至今。伴随 XML 的问世，配置文件的地位再次上升。更有甚者，在开发中不做编码，只写配置。</p>
<p><strong>看出问题了么？</strong></p>
<p>如果还是没有想法的话，那就再来看看脚本语言：</p>
<ol>
<li>脚本语言具有动态性。</li>
<li>脚本语言自身就是结构化的。</li>
<li>脚本语言可以增强编译语言的动态能力。</li>
</ol>
<p>在各个脚本语言应用 XML 作为配置文件的实践中，不论是 PHP 的 array，还是 Python 的 dict，无一例外的将配置文件转换为了脚本语言内部的一种数据结构。在多数情况下，这也是 XML 作为脚本语言配置文件唯一的解决方案。</p>
<p>再来看一个问题：在项目中使用 XML 作为配置文件的时候，是否有使用 DTD 作为 XML 的效验工具？</p>
<p>如果答案是否定的，那么 XML作为脚本语言就没有任何优势。没有应用 DTD 的 XML 就如同 PHP 的 array，Python 的 dict 一样。</p>
<p>那又何必要苦苦的追随 XML，不用脚本语言的数据结构来直接编写配置文件呢？</p>
<p>本来，早就像对这个话题写点什么。星期六（2009-09-19）的时候参加了广州技术沙龙，听了朱童鞋的 nginx 代码剖析后，突然有所顿悟：<strong></strong></p>
<p><strong>成功在于细节，性能在于细节。</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://www.mikespook.com/2009/09/%e8%84%9a%e6%9c%ac%e8%af%ad%e8%a8%80%e7%9a%84%e9%85%8d%e7%bd%ae%e6%96%87%e4%bb%b6/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

