<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" version="2.0">
<channel>
<title><![CDATA[Gracecode.com]]></title>
<link>http://www.gracecode.com/feed/</link>
<language>zh-cn</language>
<description><![CDATA[精于心、简于形]]></description>
<pubDate>Sat, 11 Jul 2009 21:05:14 +0800</pubDate>
<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/gracecode" type="application/rss+xml" /><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Fgracecode" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fgracecode" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Fgracecode" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.bloglines.com/sub/http://feeds.feedburner.com/gracecode" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fgracecode" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fgracecode" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fgracecode" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
<title><![CDATA[PHP 的八卦两则]]></title>
<link>http://www.gracecode.com/archives/2968/</link>
<pubDate>Wed, 08 Jul 2009 16:52:40 +0000</pubDate>
<category><![CDATA[PHP]]></category>
<description><![CDATA[==PHP5.3 的 goto 语句==

[[http://www.php.net/archive/2009.php#id2009-06-30-1|PHP5.3 的正式发布]]，又重新关注起其语...]]></description>
<guid>http://www.gracecode.com/archives/2968/</guid>
<slash:comments>6</slash:comments>
<comments>http://www.gracecode.com/archives/2968/#comments</comments>
<content:encoded><![CDATA[<h2>PHP5.3 的 goto 语句</h2><p><a href="http://www.php.net/archive/2009.php#id2009-06-30-1" title="http://www.php.net/archive/2009.php#id2009-06-30-1">PHP5.3 的正式发布</a>，又重新关注起其语言本身。细心的朋友可能发现，PHP5.3 增加了 goto 语句，这在结构化语言今天似乎是有点不可思议的事情。</p>

<p><a href="http://php.net/manual/en/control-structures.goto.php" title="http://php.net/manual/en/control-structures.goto.php">按照官方的说法</a>，其实这和我们传统理解的 goto 还是有所差别。PHP 5.3 中的 goto 语句只能在脚本文件以及上下文中跳转，因此它无法从某函数或方法跳到其他的函数或者方法 -- 这不得不让我怀疑是否是“妥协”的结果。</p>

<p>看来 PHP 语言的设计者对 goto 语句应用是方便从从多重循环体中跳出（在其文档中的例子也说明了这点）。但其实这功能的本身也引发了不少的争议，个人也隐约的闻到了潘多拉身上的香水味。</p>

<p>其实“好事者”<a href="http://andy.wordpress.com/2007/06/20/dreaming-of-goto-in-php/" title="http://andy.wordpress.com/2007/06/20/dreaming-of-goto-in-php/">早在 2007 年就开始“期待”这项“新功能”</a>，相信他现在会很开心。不过有趣的是这篇文章的留言给原作者泼了盆冷水。的确，goto 语句所能完成的功能，其实善用 switch 也能做到，而且更有可读性。</p>

<p>不过不管怎么样，语言本身仅仅是工具而已。从程序的本身角度考虑（不仅仅是 PHP），如果有过多的循环等的语块嵌套，那就说明这段代码必须需要优化了。到底应不应该使用 goto 语句，其实本人和其他的 PHP 人员也有过讨论。</p>

<p>最终，大家较为统一的观点就是，避免使用。</p>

<h2>“PHP 的 10 宗罪”</h2><p>老外较起真来真的非常让人受不了，<a href="http://kuswarno.wordpress.com/2009/02/27/10-php-mistakes/" title="http://kuswarno.wordpress.com/2009/02/27/10-php-mistakes/">这不又有好事者总结了 PHP 语言本身语法的 “10 宗罪”</a>。其实，我个人这与其称为“Mistake”，还说是 PHP 提供的“美丽的陷阱”。</p>

<p>例如，文中提到的有关单引号和双引号的变量转义问题。其实这一争论的声音从学 PHP 起就在耳畔充斥。甚至记得当年还有道经典的面试题，就是考单引号和双引号的的速度孰快 -- 回过头来看，这论点就犹如此道面试题一样，是没有任何的意义的。</p>

<p>但这不代表文中的些“Mistake”都可以当作笑谈，有些是的确需要注意的。例如</p>

<pre>$i = 0;
while($i &lt; 20); {
    //some code here
    $i++;
}</pre><p>这样的写法。对应的其实还有 for 语句，我们可能“手残”多写了个分号（不要不承认），那么就只能祈祷 set_time_limit 的数字小些了 :^)</p>

<p>文中指出的其他些问题，也是我们需要纳入思考的范围中，例如数据库存取的缓存问题。为何 PHP 为何至今没有数据池这样的概念，其实这需要从 PHP 这门语言本身的设计哲学出发了。从此问题其实可以引申争为何 PHP 没有走向 Java 的套路，好吧这问题又可以争论一番了。</p>

<p>文中还有其他类似的条目，都非常的具有争议（也许搞不好这就是作者的初衷），但无论怎么说，其提出的几个问题都是值得我们去思考的。</p>

<p>深夜杂谈随想，乱语之处众位见笑了，欢迎发表您的看法。</p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2968/</wfw:commentRss>
</item>
<item>
<title><![CDATA[我们的繁体用户怎么办？]]></title>
<link>http://www.gracecode.com/archives/2967/</link>
<pubDate>Fri, 03 Jul 2009 14:54:45 +0000</pubDate>
<category><![CDATA[观点]]></category>
<description><![CDATA[今天遇到的这件事情让我感到无奈，甚至是羞辱。

作为淘宝的员工，也偶尔在淘宝处理我的闲置物品，这种既为开发人员又是用户的双重身份让人颇具有玩味。

卖了件闲置物品给在香港的同胞，什么都聊好的情...]]></description>
<guid>http://www.gracecode.com/archives/2967/</guid>
<slash:comments>22</slash:comments>
<comments>http://www.gracecode.com/archives/2967/#comments</comments>
<content:encoded><![CDATA[<p>今天遇到的这件事情让我感到无奈，甚至是羞辱。</p>

<p>作为淘宝的员工，也偶尔在淘宝处理我的闲置物品，这种既为开发人员又是用户的双重身份让人颇具有玩味。</p>

<p>卖了件闲置物品给在香港的同胞，什么都聊好的情况下，照常打开“我的淘宝”查看发货信息，傻眼了。</p>

<p><img src="http://pic.yupoo.com/feelinglucky/027227b188aa/medium.jpg" alt="http://pic.yupoo.com/feelinglucky/027227b188aa/medium.jpg" title="http://pic.yupoo.com/feelinglucky/027227b188aa/medium.jpg" /></p>

<p>这到好，连码都不用打了，买家的收货信息基本属于“火星文”，手动更换了页面的编码信息，问题还是依旧。回头再联系此买家，发现那家伙下线了，看来只能周一再沟通了。</p>

<p>正如本文开头所说的，我甚至感到羞辱。</p>

<p>羞的是我们众多的开发、测试人员历练了每个产品的每项功能，连基本的字符编码都没有处理好。</p>

<p>辱的是在淘宝那么长时间、经历了那么多的项目，发现我们竟然忽略了繁体版的用户。</p>

<p>回想起来，繁体中文用户使用淘宝其实很不容易，首先他们使用支付宝必须要有张大陆的人民币结算卡，然后他们的系统必须安装简体中文的语言包。</p>

<p>还有阿里旺旺，从<a href="http://www.taobao.com/wangwang/" title="http://www.taobao.com/wangwang/">我们的下载页面</a>中可以得知有繁体版，但让人困扰的是繁体版与简体版的下载链接是同样的，我不知道这位用户下载的文件是否和我的一致。</p>

<p>周一我必须要做两件事情，一件就是询问此买家的收货地址，并说明延迟发货的原因；另外件，就是向我们的开发人员提个醒，我们需要更慎重的测试繁体版的功能。</p>

<p>无论怎么说，这种事情都不希望再次发生。这次卖给我们的香港同胞虽是偶然，但毕竟每天是他们在使用繁体中文。</p>

<p>连基本的信息传递都无法完成，那么所谓的用户体验更是无从谈起。</p>

<p><tt>-- Split --</tt></p>

<p>感谢各位以及 <a href="http://internet.solidot.org/article.pl?sid=09/07/04/036209" title="http://internet.solidot.org/article.pl?sid=09/07/04/036209">Solidot</a> 等的关注，但我明显感到了来自不同方向的压力。评论中的很多朋友都指出了目前线上的诸多问题，我也逐一的汇报给了个产品线的负责人，但这里显然不是汇报 Bug 的地方。</p>

<p>所以，请允许我冒昧得将此篇文章的评论关闭，并<strong>谢绝任何形式的转载</strong>。<a href="http://club.china.alibaba.com/forum/iask_thread/view/4_22726032_.html" title="http://club.china.alibaba.com/forum/iask_thread/view/4_22726032_.html">阿里的信条之一就是“客户第一”</a>，因此我们有理由相信这一情况会逐渐改善的。</p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2967/</wfw:commentRss>
</item>
<item>
<title><![CDATA[PHP Tokenizer 学习笔记]]></title>
<link>http://www.gracecode.com/archives/2966/</link>
<pubDate>Mon, 29 Jun 2009 08:56:30 +0000</pubDate>
<category><![CDATA[PHP]]></category>
<description><![CDATA[==简述==

在某个项目中需要分析 PHP 代码，分离出对应的函数调用（以及源代码对应的位置）。虽然这使用正则也可以实现，但无论从效率还是代码复杂度方面考虑，这都不是最优的方式。

查询了 ...]]></description>
<guid>http://www.gracecode.com/archives/2966/</guid>
<slash:comments>7</slash:comments>
<comments>http://www.gracecode.com/archives/2966/#comments</comments>
<content:encoded><![CDATA[<h2>简述</h2><p>在某个项目中需要分析 PHP 代码，分离出对应的函数调用（以及源代码对应的位置）。虽然这使用正则也可以实现，但无论从效率还是代码复杂度方面考虑，这都不是最优的方式。</p>

<p>查询了 PHP 手册，发现其实 PHP 已经内置解析器的接口，那就是 <a href="http://cn2.php.net/manual/en/tokens.php" title="http://cn2.php.net/manual/en/tokens.php">PHP Tokenizer</a>，这工具正是我想要的。使用 PHP Tokenizer 能简单、高效、准确的分析出 PHP 源代码的组成。</p>

<h2>实例</h2><p>官方站点对 Tokenizer 的文档很少，不过这不影响我们理解它。Tokenizer 组件仅仅包含两个函数：<a href="http://cn2.php.net/manual/en/function.token-get-all.php" title="http://cn2.php.net/manual/en/function.token-get-all.php">token_get_all</a> 以及 <a href="http://cn2.php.net/manual/en/ref.tokenizer.php" title="http://cn2.php.net/manual/en/ref.tokenizer.php">token_name</a>，它们分别用于分析 PHP 代码以及获取代码对应的标识符名称。</p>

<p>下面是个简单的实例，说明如何使用这两个函数：</p>

<pre>$code = '&lt;?php echo &quot;string1&quot;.&quot;string2&quot;; ?&gt;';
$tokens = token_get_all($code);
foreach ($tokens as $token) {
    if (is_array($token)) {
        // 行号、标识符字面量、对应内容
        printf(&quot;%d - %s\t%s\n&quot;, $token[2], token_name($token[0]), $token[1]);
    }
}</pre><p>对应的输出为</p>

<pre>1 - T_OPEN_TAG    &lt;?php 
1 - T_ECHO    echo
1 - T_WHITESPACE     
1 - T_CONSTANT_ENCAPSED_STRING    &quot;string1&quot;
1 - T_CONSTANT_ENCAPSED_STRING    &quot;string2&quot;
1 - T_WHITESPACE     
1 - T_CLOSE_TAG    ?&gt;</pre><p>这里顺便说明下，<tt>$token</tt> 如果为数组，那么分别对应的三个数组成员为 token 标识符（可以用 token_name 获得字面量）、对应的源代码内容、以及对应的行号。</p>

<p>还有中情况就是 <tt>$token</tt> 为字符串，这可能的情况之一就是为 T_CONSTANT_ENCAPSED_STRING 等常量，在分析代码时要注意。如果对这点很在意，可以考虑使用<a href="http://cn2.php.net/manual/en/function.token-get-all.php#91847" title="http://cn2.php.net/manual/en/function.token-get-all.php#91847">这里的代码</a>。</p>

<p>是的，调用方式非常的简单，我们的野心当然远远要比写个简单的循环要大得多。我们可以利用这个组件做写实事，例如下面的代码用于“压缩” PHP 代码，去除不不要的换行、空白以及注释</p>

<pre>/**
 * “压缩”PHP 源代码
 *
 * @see http://c7y.phparch.com/c/entry/1/art,practical_uses_tokenizer
 */
class CompactCode
{
    static protected $out;
    static protected $tokens;

    static public function compact($source)
    {
        // 解析 PHP 源代码
        self::$tokens = token_get_all($source);   
        self::$out = '';

        reset(self::$tokens);

        // 递归判断每个标记符的类型
        while ($t = current(self::$tokens)) {
            if (is_array($t)) {
                // 过滤空白、注释
                if ($t[0] == T_WHITESPACE || $t[0] == T_DOC_COMMENT || $t[0] == T_COMMENT) {
                    self::skipWhiteAndComments();
                    continue;
                }       
                self::$out .= $t[1];
            } else {
                self::$out .= $t;
            }

            next(self::$tokens);
        }

        return self::$out;
    }

    static private function skipWhiteAndComments()
    {
        // 增加个空格，用于分割关键字
        self::$out .= ' ';
        while ($t = current(self::$tokens)) {
            // 再次贪婪查找
            if (is_array($t) &amp;&amp; ($t[0] == T_WHITESPACE || $t[0] == T_DOC_COMMENT || $t[0] == T_COMMENT)) {
                next(self::$tokens);
            } else {
                return;
            }
        }
    }
}</pre><p>调用方式很简单，只需要使用</p>

<pre>CompactCode::compact($source_code);</pre><p>即可，返回的字符串就是压缩以后的内容。在这里还有更多使用 Tokenizer 的实例，<a href="http://c7y.phparch.com/c/entry/1/art,practical_uses_tokenizer" title="http://c7y.phparch.com/c/entry/1/art,practical_uses_tokenizer">推荐阅读</a>。</p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2966/</wfw:commentRss>
</item>
<item>
<title><![CDATA[PHP SPL，遗落的宝石]]></title>
<link>http://www.gracecode.com/archives/2965/</link>
<pubDate>Sun, 14 Jun 2009 11:47:30 +0000</pubDate>
<category><![CDATA[PHP]]></category>
<description><![CDATA[[[http://www.rafaeldohms.com.br/2009/06/03/spl-a-hidden-gem/en/|Rafael Dohms 上面的篇文章]]让我惊艳了下，忍不住就翻译了下...]]></description>
<guid>http://www.gracecode.com/archives/2965/</guid>
<slash:comments>4</slash:comments>
<comments>http://www.gracecode.com/archives/2965/#comments</comments>
<content:encoded><![CDATA[<p><a href="http://www.rafaeldohms.com.br/2009/06/03/spl-a-hidden-gem/en/" title="http://www.rafaeldohms.com.br/2009/06/03/spl-a-hidden-gem/en/">Rafael Dohms 上面的篇文章</a>让我惊艳了下，忍不住就翻译了下来，同时补充了部分内容。</p>

<p><a href="http://cn.php.net/spl" title="http://cn.php.net/spl">SPL，PHP 标准库（Standard PHP Library）</a>，此从 PHP 5.0 起内置的组件和接口，并且从 PHP5.3 已逐渐的成熟。SPL 其实在所有的 PHP5 开发环境中被内置，同时无需任何设置。</p>

<p>似乎众多的 PHP 开发人员基本没有使用它，甚至闻所未闻。究其原因，可以追述到它那阳春白雪般的说明文档，使你忽略了“它的存在”。</p>

<p>SPL 这块宝石犹如铁达尼的“海洋之心”般，被沉入海底。而现在它应该被我们捞起，并将它穿戴在应有的位置 ，而这也是这篇文章所要表述的观点。</p>

<p>那么，SPL 提供了什么？</p>

<p>SPL 对 PHP 引擎进行了扩展，例如 ArrayAccess、Countable 和 SeekableIterator 等接口，它们用于以数组形式操作对象。同时，你还可以使用 RecursiveIterator、ArrayObejcts 等其他迭代器进行数据的迭代操作。</p>

<p>它还内置几个的对象例如 Exceptions、SplObserver、Spltorage 以及 spl_autoload_register、spl_classes、iterator_apply 等的帮助函数（helper functions），用于重载对应的功能。</p>

<p>这些工具聚合在一起就好比是把多功能的瑞士军刀，善用它们可以从质上提升 PHP 的代码效率。那么，我们如何发挥它的威力？</p>

<h2>重载 autoloader</h2><p>如果你是位“教科书式的程序员”，那么你保证了解如何使用 __autoload 去代替 includes/requires 操作惰性载入对应的类，对不？</p>

<p>但久之，你会发现你已经陷入了困境，首先是你要保证你的类文件必须在指定的文件路径中，例如在 Zend 框架中你必须使用“_”来分割类、方法名称（你如何解决这一问题？）。</p>

<p>另外的一个问题，就是当项目变得越来越复杂， __autoload 内的逻辑也会变得相应的复杂。到最后，甚至你会加入异常判断，以及将所有的载入类的逻辑如数写到其中。</p>

<p>大家都知道“鸡蛋不能放到一个篮子中”，利用 SPL 可以分离 __autoload 的载入逻辑。只需要写个你自己的 autoload 函数，然后利用 SPL 提供的函数重载它。</p>

<p>例如上述 Zend 框架的问题，你可以重载 Zend loader 对应的方法，如果它没有找到对应的类，那么就使用你先前定义的函数。</p>

<pre>&lt;?php
class MyLoader {
    public static function doAutoload($class) {
        // 本模块对应的 autoload 操作
    }
}

spl_autoload_register( array('MyLoader', 'doAutoload') );</pre><p>正如你所见，<a href="http://cn2.php.net/manual/en/function.spl-autoload-register.php" title="http://cn2.php.net/manual/en/function.spl-autoload-register.php">spl_autoload_register</a> 还能以数组的形式加入多个载入逻辑。同时，你还可以利用 <a href="http://cn2.php.net/manual/en/function.spl-autoload-unregister.php" title="http://cn2.php.net/manual/en/function.spl-autoload-unregister.php">spl_autoload_unregister</a> 移除已经不再需要的载入逻辑，这功能总会用到的。</p>

<h2>迭代器</h2><p>迭代是常见设计模式之一，普遍应用于一组数据中的统一的遍历操作。可以毫不夸张的说，SPL 提供了所有你需要的对应数据类型的迭代器。</p>

<p>有个非常好的案例就是遍历目录。常规的做法就是使用 scandir ，然后跳过“.“ 和 “..”，以及其它未满足条件的文件。例如你需要遍历个某个目录抽取其中的图片文件，就需要判断是否是 jpg、gif 结尾。</p>

<p>下面的代码就是使用 SPL 的迭代器执行上述递归寻找指定目录中的图片文件的例子：</p>

<pre>&lt;?php
class RecursiveFileFilterIterator extends FilterIterator {
    // 满足条件的扩展名
    protected $ext = array('jpg','gif');

    /**
     * 提供 $path 并生成对应的目录迭代器
     */
    public function __construct($path) {
        parent::__construct(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)));
    }

    /**
     * 检查文件扩展名是否满足条件
     */
    public function accept() {
        $item = $this-&gt;getInnerIterator();
        if ($item-&gt;isFile() &amp;&amp; 
                in_array(pathinfo($item-&gt;getFilename(), PATHINFO_EXTENSION), $this-&gt;ext)) {
            return TRUE;
        }
    }
}

// 实例化
foreach (new RecursiveFileFilterIterator('/path/to/something') as $item) {
    echo $item . PHP_EOL;
}</pre><p>你可能会说，这不是花了更多的代码去办同一件事情吗？那么，查看上面的代码，你不是拥有了具有高度重用而且可以测试的代码了吗 :^)</p>

<p>下面是 SPL 提供的其他的迭代器：</p>

<ul>
    <li>RecursiveIterator</li>
    <li>RecursiveIteratorIterator</li>
    <li>OuterIterator</li>
    <li>IteratorIterator</li>
    <li>FilterIterator</li>
    <li>RecursiveFilterIterator</li>
    <li>ParentIterator</li>
    <li>SeekableIterator</li>
    <li>LimitIterator</li>
    <li>GlobIterator</li>
    <li>CachingIterator</li>
    <li>RecursiveCachingIterator</li>
    <li>NoRewindIterator</li>
    <li>AppendIterator</li>
    <li>RecursiveIteratorIterator</li>
    <li>InfiniteIterator</li>
    <li>RegexIterator</li>
    <li>RecursiveRegexIterator</li>
    <li>EmptyIterator</li>
    <li>RecursiveTreeIterator</li>
    <li>ArrayIterator</li>
</ul>

<p>自 PHP5.3 开始，会内置其他更多的迭代器，我想你都可以尝试下，或许它能改变你编写传统代码的习惯。</p>

<h2>SplFixedArray</h2><p>SPL 还内置了一系列的数组操作工具，例如可以使用 <a href="http://cn2.php.net/manual/en/class.splfixedarray.php" title="http://cn2.php.net/manual/en/class.splfixedarray.php">SplFixedArray</a> 实例化一个固定长度的数组。那么为什么要使用它？因为它更快，甚至它关系着你的工资问题 :^)</p>

<p>我们知道 PHP 常规的数组包含不同类型的键，例如数字、字符串等，并且长度是可变的。正是因为这些“高级功能”，PHP 以散列（hash）的方式通过键得到对应的值  -- 其实这在特定情况这会造成性能问题。</p>

<p>而 SplFixedArray 因为是使用固定的数字键，所以它并没有使用散列存储方式。不确切的说，甚至你可以认为它就是个 C 数组。这就是为什么 SplFixedArray 会比通常数组要快的原因（仅在 PHP5.3 中）。</p>

<p>那到底有多快呢，下面的组数据可以让你窥其究竟。</p>

<p><img src="http://pic.yupoo.com/feelinglucky/02539798505b/medium.jpg" alt="http://pic.yupoo.com/feelinglucky/02539798505b/medium.jpg" title="http://pic.yupoo.com/feelinglucky/02539798505b/medium.jpg" /></p>

<p><a href="http://developer.studivz.net/2009/03/18/php-spl-data-structures-benchmark/" title="http://developer.studivz.net/2009/03/18/php-spl-data-structures-benchmark/">更详细的评测可以参考这里</a>，如果你需要大量的数组操作，那么你可以尝试下，相信它是值得信赖的。</p>

<h2>数据结构</h2><p><a href="http://cn2.php.net/manual/en/spl.datastructures.php" title="http://cn2.php.net/manual/en/spl.datastructures.php">同时 SPL 还提供了些数据结构基本类型的实现</a>。虽然我们可以使用传统的变量类型来描述数据结构，例如用数组来描述堆栈（Strack）-- 然后使用对应的方式 pop 和 push（array_pop()、array_push()），但你得时刻小心，·因为毕竟它们不是专门用于描述数据结构的 -- 一次误操作就有可能破坏该堆栈。</p>

<p>而 <a href="http://cn2.php.net/manual/en/class.splstack.php" title="http://cn2.php.net/manual/en/class.splstack.php">SPL 的 SplStack</a> 对象则严格以堆栈的形式描述数据，并提供对应的方法。同时，这样的代码应该也能理解它在操作堆栈而非某个数组，从而能让你的同伴更好的理解相应的代码，并且它更快。</p>

<p>最后，可能上述那些惨白的例子还不足矣“诱惑你”去使用 SPL。实践出真知，SPL 更多、更强大的功能需要你自己去挖掘。而它正如宝石般的慢慢雕砌，才能散发光辉。</p>

<p>PS，有关 SPL 详细的中文文档，<a href="http://www.ruanyifeng.com/blog/2008/07/php_spl_notes.html" title="http://www.ruanyifeng.com/blog/2008/07/php_spl_notes.html">阮一峰同学这里有份更详细的笔记</a>，推荐。</p>

<p><tt>-- EOF --</tt></p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2965/</wfw:commentRss>
</item>
<item>
<title><![CDATA[那些注定的软件]]></title>
<link>http://www.gracecode.com/archives/2964/</link>
<pubDate>Tue, 09 Jun 2009 16:50:12 +0000</pubDate>
<category><![CDATA[观点]]></category>
<description><![CDATA[[[http://www.cnbeta.com/articles/86103.htm|最近款某来头的软件]]，似乎又撩动了互联网的神经。不想直接说我的看法，只想举两个本人亲身经历的事情。

===...]]></description>
<guid>http://www.gracecode.com/archives/2964/</guid>
<slash:comments>16</slash:comments>
<comments>http://www.gracecode.com/archives/2964/#comments</comments>
<content:encoded><![CDATA[<p><a href="http://www.cnbeta.com/articles/86103.htm" title="http://www.cnbeta.com/articles/86103.htm">最近款某来头的软件</a>，似乎又撩动了互联网的神经。不想直接说我的看法，只想举两个本人亲身经历的事情。</p>

<h3>一</h3><p>高中时由于老师的提举，管理学校的计算机机房。某日，收到通知要全面安装“网络爸爸”这款软件（听着名字就知道干嘛用的）。当时连夜将几十台机子装上了这款软件，印象中这款软件安装非常方便 -- 甚至没有让你选择安装路径。</p>

<p>随后就是噩梦的开始，类似网页打不开等诸多的问题累坏了我们这帮“免费的网管”。最难以接受的就是每次启动系统时，其都会向服务器请求更新，而期间会造成机子假死。很多不明真相、而且耐不住性子的同学，顺手就直接按电源键了。</p>

<p>最后，我们采取的办法就是直接卸载“网络爸爸”。为了躲避老师的检查，自己用 VB 写了个“假的”系统图标。</p>

<h3>二</h3><p>大学时打暑期工，去帮电信局接宽带。当时，上级明令要求安装“星空极速”软件拨号上网。</p>

<p>开始时，我非常的“敬业”给每台机子都安装上。随着安装量的上升，问题也不断的凸显出来。陆续有用户反映弹出广告、系统拖慢等问题。老到的师哥告诉我解决办法 -- 和他们说是电脑系统的问题，让他们找装机商去。</p>

<p>直到某天，我自己终于厌倦每天安装“新空极速”、应付用户的投诉，这些重复劳动。突然我意识到，这软件对于我而言没有任何的好处：老大看不到，他们只拿装机的指标、那软件太大安装非常的慢，等待很痛苦很无意义。</p>

<p>于是，直接使用 XP 自带的 ADSL 拨号软件，几步搞定。至此以后，没有收到任何的投诉（当时最后我才知道，投诉率还和工钱挂钩），同时由于安装量上升，我比上面提到的那师哥还多拿了餐饭钱。</p>

<p><tt>-- Split --</tt></p>

<p>是母鸡都指望自己下个蛋，只要有这些母鸡在，就别指望上述软件的消失。而愚蠢、懒惰的生物迟早会被淘汰，这是我们所共知的自然规律，同时这一规律同样适用于其他领域。</p>

<p>所以，对于那些所谓软件（工具）的最后的下场，想必各位也心中自知了吧。</p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2964/</wfw:commentRss>
</item>
<item>
<title><![CDATA[innerHTML 的些摘记]]></title>
<link>http://www.gracecode.com/archives/2963/</link>
<pubDate>Sun, 31 May 2009 12:01:04 +0000</pubDate>
<category><![CDATA[Javascript]]></category>
<description><![CDATA[==异步 innerHTML==

innerHTML 插入节点的性能的问题，通常是我们最关注的。[[http://stackoverflow.com/questions/788614/ways-...]]></description>
<guid>http://www.gracecode.com/archives/2963/</guid>
<slash:comments>11</slash:comments>
<comments>http://www.gracecode.com/archives/2963/#comments</comments>
<content:encoded><![CDATA[<h2>异步 innerHTML</h2><p>innerHTML 插入节点的性能的问题，通常是我们最关注的。<a href="http://stackoverflow.com/questions/788614/ways-to-increase-performance-when-set-big-value-to-innerhtml" title="http://stackoverflow.com/questions/788614/ways-to-increase-performance-when-set-big-value-to-innerhtml">在回答这问题时</a>，<a href="http://james.padolsey.com/javascript/asynchronous-innerhtml/" title="http://james.padolsey.com/javascript/asynchronous-innerhtml/">James Padolsey 给出了他的解决方案</a>，看到上述代码不仅赞叹了下：</p>

<pre>function asyncInnerHTML(HTML, callback) {
    var temp = document.createElement('div'),
        frag = document.createDocumentFragment();
    temp.innerHTML = HTML;
    (function(){
        if(temp.firstChild) {
            frag.appendChild(temp.firstChild);
            setTimeout(arguments.callee, 0);
        } else {
            callback(frag);
        }
    })();
}</pre><ol>
    <li>充分利用闭包解决 IE6 的内存溢出问题</li>
    <li>使用<a href="http://realazy.org/blog/2008/03/29/understand-0-settimeout/" title="http://realazy.org/blog/2008/03/29/understand-0-settimeout/">延时 0 将操作从队列中拉出</a>，防止浏览器假死</li>
    <li><a href="https://developer.mozilla.org/en/DOM/DocumentFragment" title="https://developer.mozilla.org/en/DOM/DocumentFragment">Document Fragment</a> 给予我们个相当好的沙盘，只是我们经常忘记了它</li>
    <li>回调的节点可以使用 DOM 标准的手法（appendChild）插入</li>
</ol>

<p>了解了参数就很容易调用，例如</p>

<pre>var htmlStr = '&lt;div&gt;&lt;p&gt;...&lt;/p&gt;&lt;p&gt;...&lt;/p&gt;&lt;div&gt;&lt;div&gt;...&lt;/div&gt;';
asyncInnerHTML(htmlStr, function(fragment){
    document.body.appendChild(fragment);
});</pre><p>再次不禁赞叹下！</p>

<h2>组织 innerHTML 字符串</h2><p>说到 innerHTML ，通常在这操作之前会有大部分的字符串操作用于连接节点。考虑下面的三种做法，有何不同</p>

<h3>方式一</h3><pre>var arr = ['item 1', 'item 2', 'item 3', ...];
for (var i = 0, l = arr.length, list = ''; i &lt; l; i++) {
    list += '&lt;li&gt;' + arr[i] + '&lt;/li&gt;';
}
list = '&lt;ul&gt;' + list + '&lt;/ul&gt;';</pre><h3>方式二</h3><pre>var arr = ['item 1', 'item 2', 'item 3', ...];
for (var i = 0, l = arr.length, list = []; i &lt; l; i++) {
    list[list.length] = '&lt;li&gt;' + arr[i] + '&lt;/li&gt;';
}
list = '&lt;ul&gt;' + list.join('') + '&lt;/ul&gt;';</pre><h3>方式三</h3><pre>var arr = ['item 1', 'item 2', 'item 3', ...];
var list = '&lt;ul&gt;&lt;li&gt;' + arr.join('&lt;/li&gt;&lt;li&gt;') + '&lt;/li&gt;&lt;/ul&gt;';</pre><p>详细的对比<a href="http://james.padolsey.com/javascript/fastest-way-to-build-an-html-string/" title="http://james.padolsey.com/javascript/fastest-way-to-build-an-html-string/">测试在这里</a>（没错，还是 James Padolsey 那小子的 Blog）。同时，<a href="http://www.quirksmode.org/dom/innerhtml.html" title="http://www.quirksmode.org/dom/innerhtml.html">PPK 也整理了份有关 innerHTML 的速度测试报告</a>。</p>

<h2>IE 的陷阱</h2><p>对于 IE，innerHTML 有个不大不小的陷阱（<a href="http://ajaxian.com/archives/fun-anecdote-about-innerhtml" title="http://ajaxian.com/archives/fun-anecdote-about-innerhtml">via</a>），就是在 tbody 中插入 innerHTML 时，会报莫名的“未知的运行错误”。</p>

<p><a href="http://www.ericvasilik.com/table.htm" title="http://www.ericvasilik.com/table.htm">测试地址在这里</a>（经过测试，在 IE8 中仍然如此）。有兴趣的同学可以<a href="http://www.ericvasilik.com/2006/07/code-karma.html" title="http://www.ericvasilik.com/2006/07/code-karma.html">参看更详细的信息</a>。</p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2963/</wfw:commentRss>
</item>
<item>
<title><![CDATA[Farseer - Nginx for Windows 快速配置包]]></title>
<link>http://www.gracecode.com/archives/2961/</link>
<pubDate>Tue, 26 May 2009 14:36:54 +0000</pubDate>
<category><![CDATA[软件]]></category>
<description><![CDATA[http://nginx.net/nginx.gif

[[http://nginx.net/|Nginx]] 的确是 {{{*ix}}} 系统下非常优秀的 HTTP 服务器，但以前对于开发人员而...]]></description>
<guid>http://www.gracecode.com/archives/2961/</guid>
<slash:comments>22</slash:comments>
<comments>http://www.gracecode.com/archives/2961/#comments</comments>
<content:encoded><![CDATA[<p><img src="http://nginx.net/nginx.gif" alt="http://nginx.net/nginx.gif" title="http://nginx.net/nginx.gif" /></p>

<p><a href="http://nginx.net/" title="http://nginx.net/">Nginx</a> 的确是 <tt>*ix</tt> 系统下非常优秀的 HTTP 服务器，但以前对于开发人员而言搭建 Windows 平台的 Nginx + PHP 环境的确非常的繁琐而且容易出错。</p>

<p>如果您有上述的问题，那么这个名为 Farseer 的包能够帮得上您的忙。使用这软件包，能够让您在一到两分钟内完成 Nginx + PHP for Windows 平台的安装。</p>

<p>废话不多说，<a href="http://code.google.com/p/gracecode/wiki/Farseer" title="http://code.google.com/p/gracecode/wiki/Farseer">安装方式和介绍在这里</a>，<a href="http://gracecode.googlecode.com/files/Farseer-20090526-win32.zip" title="http://gracecode.googlecode.com/files/Farseer-20090526-win32.zip">文件包在这里下载</a>。</p>

<p><tt>-- Split --</tt></p>

<p>如果你很“传统”，<a href="http://www.gracecode.com/archives/2235/" title="http://www.gracecode.com/archives/2235/">这里也有个 Apache + PHP 的精简配置环境</a>，希望会对兄弟们有帮助。</p>

<p>PS，<a href="http://www.dbanotes.net/opensource/farseer_nginx_for_windows.html" title="http://www.dbanotes.net/opensource/farseer_nginx_for_windows.html">再次感谢 Fenng 兄的关注</a>，但愿 Nginx 在 Windows 平台下能够与其在其他平台下的表现一样的出众。</p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2961/</wfw:commentRss>
</item>
<item>
<title><![CDATA[IE8 的 JSON 解析 Bug]]></title>
<link>http://www.gracecode.com/archives/2960/</link>
<pubDate>Thu, 21 May 2009 08:39:16 +0000</pubDate>
<category><![CDATA[Javascript]]></category>
<description><![CDATA[使用 IE8 时发现其[[http://blogs.msdn.com/ie/archive/2008/09/10/native-json-in-ie8.aspx|原生的 JSON 解析器]]存在 Bu...]]></description>
<guid>http://www.gracecode.com/archives/2960/</guid>
<slash:comments>10</slash:comments>
<comments>http://www.gracecode.com/archives/2960/#comments</comments>
<content:encoded><![CDATA[<p>使用 IE8 时发现其<a href="http://blogs.msdn.com/ie/archive/2008/09/10/native-json-in-ie8.aspx" title="http://blogs.msdn.com/ie/archive/2008/09/10/native-json-in-ie8.aspx">原生的 JSON 解析器</a>存在 Bug，让我们先用 IE8 打开 DEMO 页面体验下。</p>

<p><a href="http://lab.gracecode.com/bug/ie8-json-stringify.html" title="http://lab.gracecode.com/bug/ie8-json-stringify.html">http://lab.gracecode.com/bug/ie8-json-stringify.html</a></p>

<p>主要的问题就是 IE8 的 JSON 组件对空的表单输入控件（input、textarea）的值检测存在类型检测错误，它会认为空的表单值为 NULL，进而造成 JSON 解析错误。</p>

<p>IE8 会将 input_value 为空（没有任何输入）的情况下，解析成</p>

<pre>{&quot;value&quot;:&quot;null&quot;}</pre><p>而实际的预期应该是</p>

<pre>{&quot;value&quot;:&quot;&quot;}</pre><p>所以如果你不幸要针对 IE8 Coding（这是不可避免的）而且胆子大想尝试其原生的 JSON 解析组件时，最好先保证类型是预期的。例如上述的 Bug，在修复之前只能使用</p>

<pre>JSON.stringify({value: input_value + ''});</pre><p>这样的方式。</p>

<p>再次赞叹 IE 系列给咱前端创造的那么多的就业机会。</p>

<p><tt>-- Update --</tt></p>

<p>找了下微软官方，发现这个 Bug 早有人提交，<a href="https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=451584" title="https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=451584">查看详细</a>。</p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2960/</wfw:commentRss>
</item>
<item>
<title><![CDATA[禁止使用 Firebug]]></title>
<link>http://www.gracecode.com/archives/2959/</link>
<pubDate>Mon, 18 May 2009 11:42:05 +0000</pubDate>
<category><![CDATA[奇技淫巧]]></category>
<description><![CDATA[相信没有人不知道 [[http://getfirebug.com/|Firebug]] 是什么东西，但有时候我们糟糕的代码不想让同行轻松的使用 F12 就能一览无遗。

那么怎么办呢？

这里...]]></description>
<guid>http://www.gracecode.com/archives/2959/</guid>
<slash:comments>25</slash:comments>
<comments>http://www.gracecode.com/archives/2959/#comments</comments>
<content:encoded><![CDATA[<p>相信没有人不知道 <a href="http://getfirebug.com/" title="http://getfirebug.com/">Firebug</a> 是什么东西，但有时候我们糟糕的代码不想让同行轻松的使用 F12 就能一览无遗。</p>

<p>那么怎么办呢？</p>

<p>这里有个猥琐的办法帮你实现这个愿望</p>

<pre>if( window.console &amp;&amp; window.console.firebug ) {
    document.body.innerHTML = '';
}</pre><p>如果还觉得不保险，那顺便连 F12 也禁止吧</p>

<pre>document.onkeydown = function(e)  {
    if (123 == (e || {}).keyCode)  return false;
}</pre><p>哦也，从此大家开始写垃圾代码吧，没人看我们代码喽</p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2959/</wfw:commentRss>
</item>
<item>
<title><![CDATA[海内的 CSRF 蠕虫攻击演示]]></title>
<link>http://www.gracecode.com/archives/2954/</link>
<pubDate>Mon, 11 May 2009 09:18:51 +0000</pubDate>
<category><![CDATA[奇技淫巧]]></category>
<description><![CDATA[http://static.hainei.com/img/logo_white.gif

我记得早先时就已经向海内报告过这个漏洞，他们也很快修复。

近期无聊再去看时，发现他们将代码回溯了，询...]]></description>
<guid>http://www.gracecode.com/archives/2954/</guid>
<slash:comments>7</slash:comments>
<comments>http://www.gracecode.com/archives/2954/#comments</comments>
<content:encoded><![CDATA[<p><img src="http://static.hainei.com/img/logo_white.gif" alt="http://static.hainei.com/img/logo_white.gif" title="http://static.hainei.com/img/logo_white.gif" /></p>

<p>我记得早先时就已经向海内报告过这个漏洞，他们也很快修复。</p>

<p>近期无聊再去看时，发现他们将代码回溯了，询问得知是在做短暂的调整。</p>

<p>那么我也刚好趁这个空档演示下 CSRF 的威力，详细的攻击步骤可以看视频演示</p>

<p><a href="http://www.gracecode.com/demo/hainei_csrf.swf" title="http://www.gracecode.com/demo/hainei_csrf.swf">http://www.gracecode.com/demo/hainei_csrf.swf</a></p>

<p>海内近期内就会封堵住这个漏洞，所以就不提供相应的代码了，而且大家也不要尝试啦。</p>

<p>有关 CSRF 的其他案例，可以<a href="http://www.gracecode.com/archives/2821" title="http://www.gracecode.com/archives/2821">参考这里</a>。</p>

]]></content:encoded>
<author><![CDATA[手气不错]]></author>
<dc:creator><![CDATA[手气不错]]></dc:creator>
<wfw:commentRss>http://www.gracecode.com/feed/archives/2954/</wfw:commentRss>
</item>
</channel>
</rss>
