<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" 
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>網站製作學習誌- PHP</title>
<link>http://blog.roodo.com/jaceju/archives/cat_13112.html</link>
<description>
首頁
讀者留言版
管理介面












_uacct = &quot;UA-450710-1&quot;;
urchinTracker();
</description>
<language>zh-tw</language>
<generator>Roodo Blog System</generator>
<copyright>All Rights Reserved</copyright>
<atom:link href="http://blog.roodo.com/jaceju/archives/cat_13112.xml" rel="self" type="application/rss+xml" />
<item>
	<title>[PHP] 觀念教室 - 取值函式</title>
	<description><![CDATA[
	通常在物件導向的開發過程裡，我們常會將類別的屬性隱藏起來，然後透過設值及取值函式來存取它們，也就是我們常說的 setter/getter 。
例如：
&lt;?php
class SampleClass
{
    private $_attr = 0;

    public function setAttr($value)
    {
        $this-&gt;_attr = (int) $value;
    }
    
    public function getAttr()
    {
        return $this-&gt;_attr;
    }
}
不過設值函式並不是本文的重點，這裡我先略過不談。
從上面的程式碼裡可以看到，取值函式似乎是一段再簡單不過的程式碼，它只是很單純的把物件內部的屬性值回傳而已。
但如果今天我們的屬性值是需要經過計算才能夠回傳的話，怎麼辦呢？我們應該把計算的邏輯寫在取值函式裡嗎？
在討論之前，我們先來假設一個簡單的狀況：
「現在有一個購物車程式，在使用者購買金額未超過 500 元時，我們要在總金額上加上運費 100 元；然而當總金額滿 500 元時，就不需要加入運費了。」
以下就是這個簡化過的購物車程式碼：
&lt;?php
/**
 * 購物車類別
 *
 */
class Cart
{
    /**
     * 購物車項目列表
     *
     * @var array
     */
    private $_itemList = array();

    /**
     * 總金額
     *
     * @var int
     */
    private $_total = 0;

    /**
     * 加入購物車項目
     *
     * @param array $item
     * @return Cart
     */
    public function addItem($item)
    {
        $this-&gt;_itemList[] = $item;
        return $this;
    }

    /**
     * 更新購物車
     *
     * @return Cart
     */
    public function refresh()
    {
        $this-&gt;_total = 0;
        foreach ($this-&gt;_itemList as $item) {
            $this-&gt;_total += $item['subTotal'];
        }
        return $this;
    }

    /**
     * 清空購物車
     *
     * @return void
     */
    public function clearAll()
    {
        $this-&gt;_itemList = array();
        $this-&gt;_total = 0;
    }

    /**
     * 取得總金額
     *
     * @return int
     */
    public function getTotal()
    {
        if ($this-&gt;_total &lt; 500) {
            $this-&gt;_total += 100;
        }
        return $this-&gt;_total;
    }
}
註：我故意省略掉數量、單價等資訊。
在邏輯上，這段程式碼似乎沒有什麼問題，在取得總金額時判斷是否應該加上運費看起來是很合理的。
是嗎？
相信有經驗的朋友，思考一下就能看出這個程式的邏輯錯誤。
看不出來的朋友也沒關係，我簡單寫個測試程式來說明：
&lt;?php

require 'Cart.php';

// 建立購物車物件
$cart = new Cart();

// 顯示總金額 (金額為 520 元，大於 500 元，故不加上運費)
echo $cart
    -&gt;addItem(array('name' =&gt; '商品1', 'subTotal' =&gt; 120))
    -&gt;addItem(array('name' =&gt; '商品2', 'subTotal' =&gt; 100))
    -&gt;addItem(array('name' =&gt; '商品3', 'subTotal' =&gt; 50))
    -&gt;addItem(array('name' =&gt; '商品4', 'subTotal' =&gt; 70))
    -&gt;addItem(array('name' =&gt; '商品5', 'subTotal' =&gt; 80))
    -&gt;refresh()
    -&gt;getTotal(), &quot;\n&quot;;

// 再顯示一次總金額 (正確顯示 520 元)
echo $cart-&gt;getTotal(), &quot;\n&quot;;

// 先清空購物車
$cart-&gt;clearAll();

// 顯示總金額 (金額為 340 元，小於 500 元，故加上 100 元運費)
echo $cart
    -&gt;addItem(array('name' =&gt; '商品1', 'subTotal' =&gt; 120))
    -&gt;addItem(array('name' =&gt; '商品2', 'subTotal' =&gt; 100))
    -&gt;addItem(array('name' =&gt; '商品3', 'subTotal' =&gt; 50))
    -&gt;addItem(array('name' =&gt; '商品4', 'subTotal' =&gt; 70))
    -&gt;refresh()
    -&gt;getTotal(), &quot;\n&quot;;

// 再顯示一次總金額 (錯了！竟然變成 540 元！)
echo $cart-&gt;getTotal(), &quot;\n&quot;;
從上面的程式可以發現，當我們購買金額小於 500 元時， Cart::getTotal 這個取值函式會幫我們自動加上運費。但是我們第二次呼叫 Cart::getTotal 時，程式竟然又幫我們加上了一次運費！
原因我想大家都看出來了，也就是說我們根本不應該在 Cart::getTotal 中加入運費判斷這件事情。這裡正確的做法是應該把運費判斷的條件寫在 Cart::refresh 這個方法裡，而 Cart::getTotal 應該要很單純地只是回傳 $_total 這個屬性值，也就是：
   /**
     * 更新購物車
     *
     * @return Cart
     */
    public function refresh()
    {
        $this-&gt;_total = 0;
        foreach ($this-&gt;_itemList as $item) {
            $this-&gt;_total += $item['subTotal'];
        }

        // 將運費判斷改到這裡
        if ($this-&gt;_total &lt; 500) {
            $this-&gt;_total += 100;
        }
        return $this;
    }
或許有人會問，什麼狀況下你會去呼叫兩次 Cart::getTotal ？其實答案很多，例如有時候在 MVC 模式下，我們常會用 Model 的取值函式來呈現其對應的值，這時我們就可能會多次去呼叫 Cart::getTotal ；也有可能其他團隊成員並不知道 Cart::getTotal 的運作方式，他們一直很快樂地使用著 Cart::getTotal 來取得總金額，直到慘劇的發生...
所以這裡我們暫時得到一個結論：取值函式不應該參與運算邏輯。
是嗎？
其實這樣的結論還是有問題的，接下來我們來看看以下這個計時器類別：
&lt;?php

class Timer
{
    public function getTime()
    {
        return time();
    }
}

$timer = new Timer();

echo $timer-&gt;getTime(), &quot;\n&quot;;
sleep(2);
echo $timer-&gt;getTime(), &quot;\n&quot;;
很明顯的，對計時器這種物件來說，即時性是很重要的，也就是說每一次取得的時間是會變動的。在這裡，取值函式就參與了時間的計算。
因此，我們要修正剛剛的結論，為它加入一個但書：除非有即時性的需求，否則取值函式不應該參與運算邏輯。
有其他想法嗎？歡迎大家一起討論。

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>通常在物件導向的開發過程裡，我們常會將類別的屬性隱藏起來，然後透過設值及取值函式來存取它們，也就是我們常說的 setter/getter 。</p>
<p>例如：</p>
<pre><code>&lt;?php
class SampleClass
{
    private $_attr = 0;

    public function setAttr($value)
    {
        $this-&gt;_attr = (int) $value;
    }
    
    public function getAttr()
    {
        return $this-&gt;_attr;
    }
}</code></pre>
<p>不過設值函式並不是本文的重點，這裡我先略過不談。</p>
<p>從上面的程式碼裡可以看到，取值函式似乎是一段再簡單不過的程式碼，它只是很單純的把物件內部的屬性值回傳而已。</p>
<p>但如果今天我們的屬性值是需要經過計算才能夠回傳的話，怎麼辦呢？我們應該把計算的邏輯寫在取值函式裡嗎？</p>
<p>在討論之前，我們先來假設一個簡單的狀況：</p>
<p>「現在有一個購物車程式，在使用者購買金額未超過 500 元時，我們要在總金額上加上運費 100 元；然而當總金額滿 500 元時，就不需要加入運費了。」</p>
<p>以下就是這個簡化過的購物車程式碼：</p>
<pre><code>&lt;?php
/**
 * 購物車類別
 *
 */
class Cart
{
    /**
     * 購物車項目列表
     *
     * @var array
     */
    private $_itemList = array();

    /**
     * 總金額
     *
     * @var int
     */
    private $_total = 0;

    /**
     * 加入購物車項目
     *
     * @param array $item
     * @return Cart
     */
    public function addItem($item)
    {
        $this-&gt;_itemList[] = $item;
        return $this;
    }

    /**
     * 更新購物車
     *
     * @return Cart
     */
    public function refresh()
    {
        $this-&gt;_total = 0;
        foreach ($this-&gt;_itemList as $item) {
            $this-&gt;_total += $item['subTotal'];
        }
        return $this;
    }

    /**
     * 清空購物車
     *
     * @return void
     */
    public function clearAll()
    {
        $this-&gt;_itemList = array();
        $this-&gt;_total = 0;
    }

    /**
     * 取得總金額
     *
     * @return int
     */
    public function getTotal()
    {
        if ($this-&gt;_total &lt; 500) {
            $this-&gt;_total += 100;
        }
        return $this-&gt;_total;
    }
}</code></pre>
<p class="note">註：我故意省略掉數量、單價等資訊。</p>
<p>在邏輯上，這段程式碼似乎沒有什麼問題，在取得總金額時判斷是否應該加上運費看起來是很合理的。</p>
<p><strong>是嗎？</strong></p>
<p>相信有經驗的朋友，思考一下就能看出這個程式的邏輯錯誤。</p>
<p>看不出來的朋友也沒關係，我簡單寫個測試程式來說明：</p>
<pre><code>&lt;?php

require 'Cart.php';

// 建立購物車物件
$cart = new Cart();

// 顯示總金額 (金額為 520 元，大於 500 元，故不加上運費)
echo $cart
    -&gt;addItem(array('name' =&gt; '商品1', 'subTotal' =&gt; 120))
    -&gt;addItem(array('name' =&gt; '商品2', 'subTotal' =&gt; 100))
    -&gt;addItem(array('name' =&gt; '商品3', 'subTotal' =&gt; 50))
    -&gt;addItem(array('name' =&gt; '商品4', 'subTotal' =&gt; 70))
    -&gt;addItem(array('name' =&gt; '商品5', 'subTotal' =&gt; 80))
    -&gt;refresh()
    -&gt;getTotal(), &quot;\n&quot;;

// 再顯示一次總金額 (正確顯示 520 元)
echo $cart-&gt;getTotal(), &quot;\n&quot;;

// 先清空購物車
$cart-&gt;clearAll();

// 顯示總金額 (金額為 340 元，小於 500 元，故加上 100 元運費)
echo $cart
    -&gt;addItem(array('name' =&gt; '商品1', 'subTotal' =&gt; 120))
    -&gt;addItem(array('name' =&gt; '商品2', 'subTotal' =&gt; 100))
    -&gt;addItem(array('name' =&gt; '商品3', 'subTotal' =&gt; 50))
    -&gt;addItem(array('name' =&gt; '商品4', 'subTotal' =&gt; 70))
    -&gt;refresh()
    -&gt;getTotal(), &quot;\n&quot;;

// 再顯示一次總金額 (錯了！竟然變成 540 元！)
echo $cart-&gt;getTotal(), &quot;\n&quot;;</code></pre>
<p>從上面的程式可以發現，當我們購買金額小於 500 元時， Cart::getTotal 這個取值函式會幫我們自動加上運費。但是我們第二次呼叫 Cart::getTotal 時，程式竟然又幫我們加上了一次運費！</p>
<p>原因我想大家都看出來了，也就是說我們根本不應該在 Cart::getTotal 中加入運費判斷這件事情。這裡正確的做法是應該把運費判斷的條件寫在 Cart::refresh 這個方法裡，而 Cart::getTotal 應該要很單純地只是回傳 $_total 這個屬性值，也就是：</p>
<pre><code>   /**
     * 更新購物車
     *
     * @return Cart
     */
    public function refresh()
    {
        $this-&gt;_total = 0;
        foreach ($this-&gt;_itemList as $item) {
            $this-&gt;_total += $item['subTotal'];
        }

        <strong>// 將運費判斷改到這裡</strong>
        if ($this-&gt;_total &lt; 500) {
            $this-&gt;_total += 100;
        }
        return $this;
    }</code></pre>
<p>或許有人會問，什麼狀況下你會去呼叫兩次 Cart::getTotal ？其實答案很多，例如有時候在 MVC 模式下，我們常會用 Model 的取值函式來呈現其對應的值，這時我們就可能會多次去呼叫 Cart::getTotal ；也有可能其他團隊成員並不知道 Cart::getTotal 的運作方式，他們一直很快樂地使用著 Cart::getTotal 來取得總金額，直到慘劇的發生...</p>
<p>所以這裡我們暫時得到一個結論：<strong>取值函式不應該參與運算邏輯。</strong></p>
<p><strong>是嗎？</strong></p>
<p>其實這樣的結論還是有問題的，接下來我們來看看以下這個計時器類別：</p>
<pre><code>&lt;?php

class Timer
{
    public function getTime()
    {
        return time();
    }
}

$timer = new Timer();

echo $timer-&gt;getTime(), &quot;\n&quot;;
sleep(2);
echo $timer-&gt;getTime(), &quot;\n&quot;;</code></pre>
<p>很明顯的，對計時器這種物件來說，即時性是很重要的，也就是說每一次取得的時間是會變動的。在這裡，取值函式就參與了時間的計算。</p>
<p>因此，我們要修正剛剛的結論，為它加入一個但書：<strong>除非有即時性的需求，否則取值函式不應該參與運算邏輯。</strong></p>
<p>有其他想法嗎？歡迎大家一起討論。</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/7078107.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/7078107.html</guid>
	<category> PHP</category>
	<pubDate>Sun, 31 Aug 2008 12:01:30 +0800</pubDate>
</item>
<item>
	<title>[PHP] PHP 5.3 的 Lambda 和 closure ？</title>
	<description><![CDATA[
	這篇也 Lag....這是前陣子的消息。
PHP 5.3 將有可能會有 Lambda (黏巴達？) 和 closure ，來源為 Request for Comments: Lambda functions and closures 這篇文章，所以以後 call_user_func 就不必再用蹩腳的字串來做為函式內容了。而且這是不是也表示 PHP 不但向 Java 靠攏，也開始向 JavaScript 靠攏了？
註：所以 Mark 剛剛跟我說 PHP 快變四不像了。
Lambda 語法如下：
function &amp; (parameters) use (lexical vars) { body }
範例：
$lambda = function () { echo &quot;Hello World!\n&quot;; };
可以這樣用：
$lambda ();
call_user_func($lambda);
call_user_func_array($lambda, array());
所以 call_user_func 系列函式就可以用 Lambda 了。 
進階的 Lambda 範例：
function replace_spaces ($text) {
    $replacement = function ($matches) {
        return str_replace ($matches[1], ' ', '&amp;nbsp;').' ';
    };
    return preg_replace_callback ('/( +) /', $replacement, $text);
}
Closure 語法：
function (normal parameters) use ($var1, $var2, &amp;$refvar) {}
範例：
function replace_in_array ($search, $replacement, $array) {
    $map = function ($text) use ($search, $replacement) {
        if (strpos ($text, $search) &gt; 50) {
            return str_replace($search, $replacement, $text);
        } else {
            return $text;
        }
    };
    return array_map($map, $array);
}
可以看到原來可以用 callback 的函式，應該都能接受 closure 。 
其他就請參考來源文章囉。
相關文章 

php 5.3: notes about closures and lambda functions 
php 5.3将提供改进的Lambda函数 


	]]>
	</description>
	<content:encoded><![CDATA[
	<p>這篇也 Lag....這是前陣子的消息。</p>
<p>PHP 5.3 將有可能會有 Lambda (黏巴達？) 和 closure ，來源為 <a href="http://wiki.php.net/rfc/closures">Request for Comments: Lambda functions and closures</a> 這篇文章，所以以後 call_user_func 就不必再用蹩腳的字串來做為函式內容了。而且這是不是也表示 PHP 不但向 Java 靠攏，也開始向 JavaScript 靠攏了？</p>
<p class="note">註：所以 <a href="http://blog.markplace.net/" target="_blank">Mark</a> 剛剛跟我說 PHP 快變四不像了。</p>
<p>Lambda 語法如下：</p>
<pre><code>function &amp; (parameters) use (lexical vars) { body }</code></pre>
<p>範例：</p>
<pre><code>$lambda = function () { echo &quot;Hello World!\n&quot;; };</code></pre>
<p>可以這樣用：</p>
<pre><code>$lambda ();
call_user_func($lambda);
call_user_func_array($lambda, array());</code></pre>
<p>所以 call_user_func 系列函式就可以用 Lambda 了。 </p>
<p>進階的 Lambda 範例：</p>
<pre><code>function replace_spaces ($text) {
    $replacement = function ($matches) {
        return str_replace ($matches[1], ' ', '&amp;nbsp;').' ';
    };
    return preg_replace_callback ('/( +) /', $replacement, $text);
}</code></pre>
<p>Closure 語法：</p>
<pre><code>function (normal parameters) use ($var1, $var2, &amp;$refvar) {}</code></pre>
<p>範例：</p>
<pre><code>function replace_in_array ($search, $replacement, $array) {
    $map = function ($text) use ($search, $replacement) {
        if (strpos ($text, $search) &gt; 50) {
            return str_replace($search, $replacement, $text);
        } else {
            return $text;
        }
    };
    return array_map($map, $array);
}</code></pre>
<p>可以看到原來可以用 callback 的函式，應該都能接受 closure 。 </p>
<p>其他就請參考來源文章囉。</p>
<h2>相關文章 </h2>
<ul>
<li><a href="http://dev.iordanov.net/archives/9">php 5.3: notes about closures and lambda functions</a> </li>
<li><a href="http://www.ooso.net/index.php/archives/463">php 5.3将提供改进的Lambda函数</a> </li>
</ul>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/6541789.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/6541789.html</guid>
	<category> PHP</category>
	<pubDate>Tue, 22 Jul 2008 17:41:48 +0800</pubDate>
</item>
<item>
	<title>[PHP] PHP 5.3 的新特色： Phar</title>
	<description><![CDATA[
	註：以下程式我並沒有實作過，只是先分享一下給大家。
PHP 5.3 帶來很多有趣的特色，其中一個就是可以把你的 Library 打包成 phar 格式，並透過 require 敘述來引用裡面的程式碼。
首先你可以用 PHP 5.3 的新類別 Phar 來打包你想要的程式碼 (通常是類別檔案) ：
&lt;?php
$phar = new Phar('My.phar', 0, 'My.phar'); // 建立 My.phar 檔案
$phar-&gt;buildFromDirectory(
    dirname(__FILE__) . '/My', '/\.php$/'
); // 打包所有 My 目錄下的 php 檔案
$phar-&gt;compressFiles( Phar::GZ ); // 以 GZ 格式壓縮
$phar-&gt;stopBuffering(); // 壓縮完成，寫入 phar 檔
然後你可以整包引入：
require 'My.phar';
也可以單獨引入其中的某支檔案：
require 'phar://My.phar/src/Test.php';
就連 PHPUnit 3.3 也試用 Phar 來打包囉了！ 
那麼效能呢？據 Jan Schneider 的消息指出， PHP 的開發團隊已經把這個功能的效能調整到接近 native PHP 的執行進度了！
很酷吧？期待 PHP 5.3 的來臨吧！ 
參考 

 Friday afternoon toying: eZ Components as phar


	]]>
	</description>
	<content:encoded><![CDATA[
	<p class="note">註：以下程式我並沒有實作過，只是先分享一下給大家。</p>
<p>PHP 5.3 帶來很多有趣的特色，其中一個就是可以把你的 Library 打包成 phar 格式，並透過 require 敘述來引用裡面的程式碼。</p>
<p>首先你可以用 PHP 5.3 的新類別 Phar 來打包你想要的程式碼 (通常是類別檔案) ：</p>
<pre><code>&lt;?php
$phar = new Phar('My.phar', 0, 'My.phar'); // 建立 My.phar 檔案
$phar-&gt;buildFromDirectory(
    dirname(__FILE__) . '/My', '/\.php$/'
); // 打包所有 My 目錄下的 php 檔案
$phar-&gt;compressFiles( Phar::GZ ); // 以 GZ 格式壓縮
$phar-&gt;stopBuffering(); // 壓縮完成，寫入 phar 檔</code></pre>
<p>然後你可以整包引入：</p>
<pre><code>require 'My.phar';</code></pre>
<p>也可以單獨引入其中的某支檔案：</p>
<pre><code>require 'phar://My.phar/src/Test.php';</code></pre>
<p>就連 <a href="http://www.phpunit.de/">PHPUnit 3.3</a> 也<a href="http://sebastian-bergmann.de/archives/799-Phar.html">試用 Phar 來打包</a>囉了！ </p>
<p>那麼效能呢？據 Jan Schneider 的<a href="http://blog.stuartherbert.com/php/2008/06/29/where-are-the-benchmarks-for-phar/">消息</a>指出， PHP 的開發團隊已經把這個功能的效能調整到接近 native PHP 的執行進度了！</p>
<p>很酷吧？期待 PHP 5.3 的來臨吧！ </p>
<h2>參考 </h2>
<ul>
<li> <a href="http://derickrethans.nl/friday_afternoon_toying_ez_components_as_phar.php">Friday afternoon toying: eZ Components as phar</a></li>
</ul>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/6247655.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/6247655.html</guid>
	<category> PHP</category>
	<pubDate>Mon, 30 Jun 2008 10:08:45 +0800</pubDate>
</item>
<item>
	<title>[PHP] 在 PHP5 中實作 AOP 的概念</title>
	<description><![CDATA[
	這篇積在我電腦裡很久了，一直沒公開...這次趁著要幫我的 Library 加料，順便拿出來分享一下心得。 
什麼是 AOP
 AOP 全名為 Aspect-Oriented Programming ，基本的觀念可以參考良葛格的 AOP 入門：

從代理機制初探 AOP
動態代理
AOP 觀念與術語

這裡我簡單提一下 AOP 的基本想法：
假設當我們呼叫物件的某些方法 (或是業務流程) 之後，會想要把相關的資訊記錄到 log 檔裡，我們也許會這樣寫：
&lt;?php
/**
 * Test
 */
class Test
{
    /**
     * 某個方法
     */
    public function doSomething()
    {
        // 建立 Log 物件
        $logger = new Log();

        // 寫入前置 Log
        $logger-&gt;save('before do something.');

        // 主要的動作
        // ...

        // 寫入 Log
        $logger-&gt;save('before do something.');
    }
}
可是如果今天這個記錄 log 的這個動作只是臨時的，或是在未來可能會需要再加入不同的動作時 (例如寄信) ，難道我們還要在原有方法的程式碼裡修修改改嗎？有沒有什麼方式能協助我們動態地把記錄的動作插在原有動作之後呢？
AOP 就是從這個角度所延伸出來的一種觀念，它能協助我們在不侵入原有類別程式碼的狀況下，動態地為類別方法新增額外的權責；簡單來說， AOP 主要的目的就是切入類別原有方法執行之前或之後，並安插我們想要執行的動作。
註： IT 界似乎很喜歡發明深奧的名詞來詮釋一個簡單的概念，然後像我這樣不學無術的開發者就常被唬得一楞一楞的。
AOP 和 Decorator 
先介紹幾篇實作 AOP 的文章：

AOP for jQuery
Aspect Oriented Programming in PHP as a contrast to other languages.
Bunny Aspects
More on Aspect Oriented PHP
在PHP里利用魔术方法实现准AOP
AOP在PHP中的实现方式
Class: AOP Library for PHP

其實一開始我以為 AOP 和 Decorator 模式在 PHP 上的實作方式是差不多的，不過實際上還有是些許的差別。
一般在 Decorator 模式中，具體類別和 Wrapper 類別都會有個共同的祖先，亦即一個抽象類別或介面，因此所產生出來的物件對 Client 程式來說，其抽象型態可以說是一樣的。
但是在 AOP in PHP 中，我們必須透過一個代理類別來切入原有的類別方法裡，雖然這個代理類別也能夠提供原有類別中的所有方法，但是實際上它卻已經失去了與原有類別所擁有的抽象型態了。
用 PHP 實作 AOP
首先我們來看看還沒有切入任何事件的目標類別：
&lt;?php
/**
 * Test class
 *
 */
class TestClass
{
    /**
     * Method 1
     *
     * @param string $message
     */
    public function method1($message)
    {
        echo &quot;\n&quot;, __METHOD__, &quot;:\n&quot;, $message, &quot;\n&quot;;
    }

    /**
     * Method 2
     *
     * @return int
     */
    public function method2()
    {
        echo &quot;\n&quot;, __METHOD__, &quot;:\n&quot;;
        return rand(1, 10);
    }

    /**
     * Method 3
     *
     * @throws Exception
     */
    public function method3()
    {
        echo &quot;\n&quot;, __METHOD__, &quot;:\n&quot;;
        throw new Exception('Test Exception.');
    }
}
這個類別提供了三個方法，其中 method1 和 method2 只是簡單的顯示資料而已，而 method3 則會丟出一個異常。
另外我們需要一個 Log 類別：
&lt;?php
/**
 * Log
 *
 */
class Log
{
    /**
     * log message
     *
     * @param string $message
     */
    public function save($message)
    {
        echo $message, &quot;\n&quot;;
    }
}
這個 Log 類別只提供一個 save() 方法，以顯示 log 訊息。 
現在我們要完成的目標如下：


在 method1 執行前呼叫 Log::save() 。


在 method2 執行後呼叫 Log::save() 。


在 method3 發生異常時呼叫 Log::save() 。 


這裡我用很簡單的方式來做，那就是直接使用一個 Aspect 類別：
&lt;?php
/**
 * Aspect
 *
 */
class Aspect
{
    /**
     * Name of target class
     *
     * @var string
     */
    private $_className = null;

    /**
     * Target object
     *
     * @var object
     */
    private $_target = null;

    /**
     * Event callback
     *
     * @var array
     */
    private $_eventCallbacks = array();

    /**
     * Add object
     *
     * @param object $target
     * @return Aspect
     */
    public static function addObject($target)
    {
        return new Aspect($target);
    }

    /**
     * Contructor
     *
     * @param object $target
     */
    public function __construct($target)
    {
        if (is_object($target)) {
            $this-&gt;_target = $target;
            $this-&gt;_className = get_class($this-&gt;_target);
        }
    }

    /**
     * Register event
     *
     * @param string $eventName
     * @param string $methodName
     * @param callback $callback
     */
    private function _registerEvent($eventName, $methodName, $callback, $args)
    {
        if (!isset($this-&gt;_eventCallbacks[$methodName])) {
            $this-&gt;_eventCallbacks[$methodName] = array();
        }

        if (!is_callable(array($this-&gt;_target, $methodName))) {
            throw new Exception(get_class($this-&gt;_target) . '::' . $methodName . ' is not exists.');
        }

        if (is_callable($callback)) {
            $this-&gt;_eventCallbacks[$methodName][$eventName] = array($callback, $args);
        } else {
            $callbackName = Aspect::getCallbackName($callback);
            throw new Exception($callbackName . ' is not callable.');
        }
    }

    /**
     * Register 'before' handler
     *
     * @param string $methodName
     * @param callback $callback
     */
    public function before($methodName, $callback, $args = array())
    {
        $this-&gt;_registerEvent('before', $methodName, $callback, (array) $args);
    }

    /**
     * Register 'after' handler
     *
     * @param string $methodName
     * @param callback $callback
     */
    public function after($methodName, $callback, $args = array())
    {
        $this-&gt;_registerEvent('after', $methodName, $callback, (array) $args);
    }

    /**
     * Register 'on catch exception' handler
     *
     * @param string $methodName
     * @param callback $callback
     */
    public function onCatchException($methodName, $callback, $args = array())
    {
        $this-&gt;_registerEvent('onCatchException', $methodName, $callback, (array) $args);
    }

    /**
     * Trigger event
     *
     * @param string $eventName
     */
    private function _trigger($eventName, $methodName, $target)
    {
        if (isset($this-&gt;_eventCallbacks[$methodName][$eventName])) {
            list($callback, $args) = $this-&gt;_eventCallbacks[$methodName][$eventName];
            $args[] = $target;
            call_user_func_array($callback, $args);
        }
    }

    /**
     * Execute method
     *
     * @param string $methodName
     * @param array $args
     * @return mixed
     */
    public function __call($methodName, $args)
    {
        if (is_callable(array($this-&gt;_target, $methodName))) {
            try {
                $this-&gt;_trigger('before', $methodName, $this-&gt;_target);
                $result = call_user_func_array(array($this-&gt;_target, $methodName), $args);
                $this-&gt;_trigger('after', $methodName, $this-&gt;_target);
                return $result ? $result : null;
            } catch (Exception $e) {
                $this-&gt;_trigger('onCatchException', $methodName, $e);
                throw $e;
            }
        } else {
            throw new Exception(&quot;Call to undefined method {$this-&gt;_className}::$methodName.&quot;);
        }
    }

    /**
     * Get name of callback
     *
     * @param callback $callback
     * @return string
     */
    public static function getCallbackName($callback)
    {
        $className  = '';
        $methodName = '';

        if (is_array($callback) &amp;&amp; 2 == count($callback)) {
            if (is_object($callback[0])) {
                $className = get_class($callback[0]);
            } else {
                $className = (string) $callback[0];
            }
            $methodName = (string) $callback[1];
        } elseif (is_string($callback)) {
            $methodName = $callback;
        }

        return $className . (($className) ? '::' : '') . $methodName;
    }
}
這個類別有點小長，簡單說明如下：


 我們利用 Aspect::addObject() 方法來指定要被切入的物件； addObject() 方法會回傳一個透明的 Aspect 物件。


利用 before 、 after 和 onCatchException 三個方法來指定切入的時機，它們會呼叫 _registerEvent() 方法來註冊要執行的回呼函式 (callback) 。


執行原來被切入物件的方法，這時會觸動 Aspect 的 __call() 方法，並在指定的切入時機呼叫 _trigger() 方法來執行我們所切入的回呼函式。 


先來看看還沒有使用 AOP 前，我們對 TestClass 類別的測試：
&lt;?php
require_once 'TestClass.php';

$test = new TestClass();

/* @var $test TestClass */
echo &quot;=======\n&quot;;
$test-&gt;method1('abc');
echo &quot;=======\n&quot;;
echo $test-&gt;method2(), &quot;\n&quot;;
echo &quot;=======\n&quot;;
$test-&gt;method3();
echo &quot;=======\n&quot;;

/* 執行結果：
=======

TestClass::method1:
abc
=======

TestClass::method2:
2
=======

TestClass::method3:

Exception: Test Exception. in TestClass.php on line 38
*/
接下來我們利用 Aspect 類別來對 TestClass 物件的三個方法切入 Log::save() ：
&lt;?php
require_once 'Aspect.php';
require_once 'TestClass.php';
require_once 'Log.php';

$test = Aspect::addObject(new TestClass());
$logger = new Log();

$test-&gt;before('method1', array($logger, 'save'), 'Log saved (method1).');
$test-&gt;after('method2', array($logger, 'save'), 'Log saved (method2).');
$test-&gt;onCatchException('method3', array($logger, 'save'), 'Log saved (method3).');

/* @var $test TestClass */
echo &quot;=======\n&quot;;
$test-&gt;method1('abc');
echo &quot;=======\n&quot;;
echo $test-&gt;method2(), &quot;\n&quot;;
echo &quot;=======\n&quot;;
$test-&gt;method3();
echo &quot;=======\n&quot;;

/* 執行結果：
=======
Log saved (method1).

TestClass::method1:
abc
=======

TestClass::method2:
Log saved (method2).
8
=======

TestClass::method3:
Log saved (method3).

Exception: Test Exception. in TestClass.php on line 38
*/
結論
我們可以從範例看到， AOP 能幫我們在某類別的方法中插入一些額外的動作，同時又能不破壞原有類別的程式碼。而它與 Decorator 最大的不同是， Decorator 必須用很多小類別來完成相同的動作，但是 AOP 則透過 PHP 的動態特性解決了這個問題。
當然 AOP 也不是萬靈丹，像在本文的實作裡它就不能接觸目標類別的非公開屬性。而之前也跟 Mark 聊了一下，其實 AOP 偏向於程式的整體設計，所以這裡的範例尚不能用於實戰之中，僅僅只是我個人一個概念的實作而已。
供大家參考看看吧。也歡迎一起討論~

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>這篇積在我電腦裡很久了，一直沒公開...這次趁著要幫我的 Library 加料，順便拿出來分享一下心得。 </p>
<h2>什麼是 AOP</h2>
<p> AOP 全名為 Aspect-Oriented Programming ，基本的觀念可以參考良葛格的 AOP 入門：</p>
<ul>
<li><span id="text145561"><a href="http://caterpillar.onlyfun.net/Gossip/SpringGossip/FromProxyToAOP.html" target="_blank">從代理機制初探 AOP</a></span></li>
<li><span><a href="http://caterpillar.onlyfun.net/Gossip/SpringGossip/DynamicProxy.html" target="_blank">動態代理</a></span></li>
<li><span><a href="http://caterpillar.onlyfun.net/Gossip/SpringGossip/AOPConcept.html" target="_blank">AOP 觀念與術語</a></span></li>
</ul>
<p>這裡我簡單提一下 AOP 的基本想法：</p>
<p>假設當我們呼叫物件的某些方法 (或是<strong>業務流程</strong>) 之後，會想要把相關的資訊記錄到 log 檔裡，我們也許會這樣寫：</p>
<pre><code>&lt;?php
/**
 * Test
 */
class Test
{
    /**
     * 某個方法
     */
    public function doSomething()
    {
        // 建立 Log 物件
        $logger = new Log();

        // 寫入前置 Log
        $logger-&gt;save('before do something.');

        // 主要的動作
        // ...

        // 寫入 Log
        $logger-&gt;save('before do something.');
    }
}</code></pre>
<p>可是如果今天這個記錄 log 的這個動作只是臨時的，或是在未來可能會需要再加入不同的動作時 (例如寄信) ，難道我們還要在原有方法的程式碼裡修修改改嗎？有沒有什麼方式能協助我們動態地把記錄的動作插在原有動作之後呢？</p>
<p>AOP 就是從這個角度所延伸出來的一種觀念，它能協助我們在不侵入原有類別程式碼的狀況下，動態地為類別方法新增額外的權責；簡單來說， AOP 主要的目的就是<strong>切入類別原有方法執行之前或之後，並安插我們想要執行的動作</strong>。</p>
<p class="note">註： IT 界似乎很喜歡發明深奧的名詞來詮釋一個簡單的概念，然後像我這樣不學無術的開發者就常被唬得一楞一楞的。</p>
<h2>AOP 和 Decorator </h2>
<p>先介紹幾篇實作 AOP 的文章：</p>
<ul>
<li><a href="http://racklin.blogspot.com/2007/08/aop-for-jquery.html">AOP for jQuery</a></li>
<li><a href="http://blog.jonnay.net/archives/637-Aspect-Oriented-Programming-in-PHP-as-a-contrast-to-other-languages..html" target="_blank">Aspect Oriented Programming in PHP as a contrast to other languages.</a></li>
<li><a href="http://wiki.jonnay.net/bunny/bunnyaspectsphp" target="_blank">Bunny Aspects</a></li>
<li><a href="http://jaxn.org/article/2004/10/16/more-on-aspect-oriented-php/" title="Permanent Link to 'More on Aspect Oriented PHP'" target="_blank" rel="bookmark">More on Aspect Oriented PHP</a></li>
<li><a href="http://hi.baidu.com/thinkinginlamp/blog/item/864a0ef46d93b86eddc474f3.html" target="_blank">在PHP里利用魔术方法实现准AOP</a></li>
<li><a href="http://sushener.spaces.live.com/blog/cns!BB54050A5CFAFCDD!546.entry">AOP在PHP中的实现方式</a></li>
<li><a href="http://www.phpclasses.org/browse/package/2633.html">Class: AOP Library for PHP</a></li>
</ul>
<p>其實一開始我以為 AOP 和 Decorator 模式在 PHP 上的實作方式是差不多的，不過實際上還有是些許的差別。</p>
<p>一般在 Decorator 模式中，具體類別和 Wrapper 類別都會有個共同的祖先，亦即一個抽象類別或介面，因此所產生出來的物件對 Client 程式來說，其抽象型態可以說是一樣的。</p>
<p>但是在 AOP in PHP 中，我們必須透過一個代理類別來切入原有的類別方法裡，雖然這個代理類別也能夠提供原有類別中的所有方法，但是實際上它卻已經失去了與原有類別所擁有的抽象型態了。</p>
<h2>用 PHP 實作 AOP</h2>
<p>首先我們來看看還沒有切入任何事件的目標類別：</p>
<pre><code>&lt;?php
/**
 * Test class
 *
 */
class TestClass
{
    /**
     * Method 1
     *
     * @param string $message
     */
    public function method1($message)
    {
        echo &quot;\n&quot;, __METHOD__, &quot;:\n&quot;, $message, &quot;\n&quot;;
    }

    /**
     * Method 2
     *
     * @return int
     */
    public function method2()
    {
        echo &quot;\n&quot;, __METHOD__, &quot;:\n&quot;;
        return rand(1, 10);
    }

    /**
     * Method 3
     *
     * @throws Exception
     */
    public function method3()
    {
        echo &quot;\n&quot;, __METHOD__, &quot;:\n&quot;;
        throw new Exception('Test Exception.');
    }
}</code></pre>
<p>這個類別提供了三個方法，其中 method1 和 method2 只是簡單的顯示資料而已，而 method3 則會丟出一個異常。</p>
<p>另外我們需要一個 Log 類別：</p>
<pre><code>&lt;?php
/**
 * Log
 *
 */
class Log
{
    /**
     * log message
     *
     * @param string $message
     */
    public function save($message)
    {
        echo $message, &quot;\n&quot;;
    }
}</code></pre>
<p>這個 Log 類別只提供一個 save() 方法，以顯示 log 訊息。 </p>
<p>現在我們要完成的目標如下：</p>
<ol>
<li>
<p>在 method1 執行前呼叫 Log::save() 。</p>
</li>
<li>
<p>在 method2 執行後呼叫 Log::save() 。</p>
</li>
<li>
<p>在 method3 發生異常時呼叫 Log::save() 。 </p>
</li>
</ol>
<p>這裡我用很簡單的方式來做，那就是直接使用一個 Aspect 類別：</p>
<pre><code>&lt;?php
/**
 * Aspect
 *
 */
class Aspect
{
    /**
     * Name of target class
     *
     * @var string
     */
    private $_className = null;

    /**
     * Target object
     *
     * @var object
     */
    private $_target = null;

    /**
     * Event callback
     *
     * @var array
     */
    private $_eventCallbacks = array();

    /**
     * Add object
     *
     * @param object $target
     * @return Aspect
     */
    public static function addObject($target)
    {
        return new Aspect($target);
    }

    /**
     * Contructor
     *
     * @param object $target
     */
    public function __construct($target)
    {
        if (is_object($target)) {
            $this-&gt;_target = $target;
            $this-&gt;_className = get_class($this-&gt;_target);
        }
    }

    /**
     * Register event
     *
     * @param string $eventName
     * @param string $methodName
     * @param callback $callback
     */
    private function _registerEvent($eventName, $methodName, $callback, $args)
    {
        if (!isset($this-&gt;_eventCallbacks[$methodName])) {
            $this-&gt;_eventCallbacks[$methodName] = array();
        }

        if (!is_callable(array($this-&gt;_target, $methodName))) {
            throw new Exception(get_class($this-&gt;_target) . '::' . $methodName . ' is not exists.');
        }

        if (is_callable($callback)) {
            $this-&gt;_eventCallbacks[$methodName][$eventName] = array($callback, $args);
        } else {
            $callbackName = Aspect::getCallbackName($callback);
            throw new Exception($callbackName . ' is not callable.');
        }
    }

    /**
     * Register 'before' handler
     *
     * @param string $methodName
     * @param callback $callback
     */
    public function before($methodName, $callback, $args = array())
    {
        $this-&gt;_registerEvent('before', $methodName, $callback, (array) $args);
    }

    /**
     * Register 'after' handler
     *
     * @param string $methodName
     * @param callback $callback
     */
    public function after($methodName, $callback, $args = array())
    {
        $this-&gt;_registerEvent('after', $methodName, $callback, (array) $args);
    }

    /**
     * Register 'on catch exception' handler
     *
     * @param string $methodName
     * @param callback $callback
     */
    public function onCatchException($methodName, $callback, $args = array())
    {
        $this-&gt;_registerEvent('onCatchException', $methodName, $callback, (array) $args);
    }

    /**
     * Trigger event
     *
     * @param string $eventName
     */
    private function _trigger($eventName, $methodName, $target)
    {
        if (isset($this-&gt;_eventCallbacks[$methodName][$eventName])) {
            list($callback, $args) = $this-&gt;_eventCallbacks[$methodName][$eventName];
            $args[] = $target;
            call_user_func_array($callback, $args);
        }
    }

    /**
     * Execute method
     *
     * @param string $methodName
     * @param array $args
     * @return mixed
     */
    public function __call($methodName, $args)
    {
        if (is_callable(array($this-&gt;_target, $methodName))) {
            try {
                $this-&gt;_trigger('before', $methodName, $this-&gt;_target);
                $result = call_user_func_array(array($this-&gt;_target, $methodName), $args);
                $this-&gt;_trigger('after', $methodName, $this-&gt;_target);
                return $result ? $result : null;
            } catch (Exception $e) {
                $this-&gt;_trigger('onCatchException', $methodName, $e);
                throw $e;
            }
        } else {
            throw new Exception(&quot;Call to undefined method {$this-&gt;_className}::$methodName.&quot;);
        }
    }

    /**
     * Get name of callback
     *
     * @param callback $callback
     * @return string
     */
    public static function getCallbackName($callback)
    {
        $className  = '';
        $methodName = '';

        if (is_array($callback) &amp;&amp; 2 == count($callback)) {
            if (is_object($callback[0])) {
                $className = get_class($callback[0]);
            } else {
                $className = (string) $callback[0];
            }
            $methodName = (string) $callback[1];
        } elseif (is_string($callback)) {
            $methodName = $callback;
        }

        return $className . (($className) ? '::' : '') . $methodName;
    }
}</code></pre>
<p>這個類別有點小長，簡單說明如下：</p>
<ol>
<li>
<p> 我們利用 Aspect::addObject() 方法來指定要被切入的物件； addObject() 方法會回傳一個透明的 Aspect 物件。</p>
</li>
<li>
<p>利用 before 、 after 和 onCatchException 三個方法來指定切入的時機，它們會呼叫 _registerEvent() 方法來註冊要執行的回呼函式 (callback) 。</p>
</li>
<li>
<p>執行原來被切入物件的方法，這時會觸動 Aspect 的 __call() 方法，並在指定的切入時機呼叫 _trigger() 方法來執行我們所切入的回呼函式。 </p>
</li>
</ol>
<p>先來看看還沒有使用 AOP 前，我們對 TestClass 類別的測試：</p>
<pre><code>&lt;?php
require_once 'TestClass.php';

$test = new TestClass();

/* @var $test TestClass */
echo &quot;=======\n&quot;;
$test-&gt;method1('abc');
echo &quot;=======\n&quot;;
echo $test-&gt;method2(), &quot;\n&quot;;
echo &quot;=======\n&quot;;
$test-&gt;method3();
echo &quot;=======\n&quot;;

/* 執行結果：
=======

TestClass::method1:
abc
=======

TestClass::method2:
2
=======

TestClass::method3:

Exception: Test Exception. in TestClass.php on line 38
*/</code></pre>
<p>接下來我們利用 Aspect 類別來對 TestClass 物件的三個方法切入 Log::save() ：</p>
<pre><code>&lt;?php
require_once 'Aspect.php';
require_once 'TestClass.php';
require_once 'Log.php';

$test = Aspect::addObject(new TestClass());
$logger = new Log();

$test-&gt;before('method1', array($logger, 'save'), 'Log saved (method1).');
$test-&gt;after('method2', array($logger, 'save'), 'Log saved (method2).');
$test-&gt;onCatchException('method3', array($logger, 'save'), 'Log saved (method3).');

/* @var $test TestClass */
echo &quot;=======\n&quot;;
$test-&gt;method1('abc');
echo &quot;=======\n&quot;;
echo $test-&gt;method2(), &quot;\n&quot;;
echo &quot;=======\n&quot;;
$test-&gt;method3();
echo &quot;=======\n&quot;;

/* 執行結果：
=======
Log saved (method1).

TestClass::method1:
abc
=======

TestClass::method2:
Log saved (method2).
8
=======

TestClass::method3:
Log saved (method3).

Exception: Test Exception. in TestClass.php on line 38
*/</code></pre>
<h2>結論</h2>
<p>我們可以從範例看到， AOP 能幫我們在某類別的方法中插入一些額外的動作，同時又能不破壞原有類別的程式碼。而它與 Decorator 最大的不同是， Decorator 必須用很多小類別來完成相同的動作，但是 AOP 則透過 PHP 的動態特性解決了這個問題。</p>
<p>當然 AOP 也不是萬靈丹，像在本文的實作裡它就不能接觸目標類別的非公開屬性。而之前也跟 <a href="http://blog.markplace.net/">Mark</a> 聊了一下，其實 AOP 偏向於程式的整體設計，所以這裡的範例尚不能用於實戰之中，僅僅只是我個人一個概念的實作而已。</p>
<p>供大家參考看看吧。也歡迎一起討論~</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/5860329.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/5860329.html</guid>
	<category> PHP</category>
	<pubDate>Mon, 14 Apr 2008 18:29:34 +0800</pubDate>
</item>
<item>
	<title>[PHP] PHP 密技： include 與 require</title>
	<description><![CDATA[
	可以接受回傳資料？
先調查一下，知道 include 或 require 可以取得回傳資料的請舉手... (眺望)
呃...不知道的朋友也不用煩惱，我來解釋一下。
如何回傳資料呢？假設現在有個 php 檔叫做 config.php ，內容如下：
&lt;?php
return array('123', '456');
咦？那邊有人說 return 放錯地方了？不不不， PHP 能接受這樣的寫法。
好，現在我們來證明 include 或 require 能取得 config.php 所 return 回來的資料。請建立一支 test.php ，其內容是：
$config = require 'config.php';
var_dump($config);
執行看看，是不是可以跑呀？
所以我們可以在某支 PHP 程式中 return 一個資料 (任何型態) ，然後在另一支 PHP 程式中用 include 或 require 來取得這個資料。 
把 require 放在參數裡 
什麼？這不是密技？不不不，密技在底下：
function test($config) {
    var_dump($config);
}
test(require 'config.php');
對！你沒看錯！直接把 require 放在函式的參數裡！
還沒完呢，再看：
class Test
{
    public function __construct($config)
    {
        var_dump($config);
    }
}
$a = new Test(require 'config.php');
連 new 建構子的參數都可以接受 require ！
所以只要能放變數的地方，都可以放 include 或 require ，例如：
if (require 'config.php') {
    var_dump(require 'config.php');
}

if ($config = require 'config.php') {
    var_dump($config);
}
而且不僅是 include 及 require ，連 include_once 和 require_once 都可以這麼做。
我在某篇文章發現這個密技以後，分享給辦公室裡的同事們；沒想到玩了 PHP 這麼多年的他們也沒看過這個方法，看來大家對 PHP 的瞭解需要更深入一點囉！
Scope 的問題 
接著我同事問了我一個問題：如果在參數使用 require 敘述，而且被 require 的 PHP 程式裡如果有定義全域變數的話，那麼這個變數在執行的 PHP 程式裡，它的 scope 在哪裡呢？
答案是：它還是全域。
怎麼說呢？現在我們在剛剛的 config.php 的 return 敘述前加上一行程式，如下：
&lt;?php
$data = '789'; // 加上這行
return array('123', '456');
然後在 test.php 裡的 Global 部份 (也就是不在函式或類別定義裡) 的任意處加入：
var_dump($data);
是不是也可以正確顯示 config.php 中 $data 變數所指定的內容呢？這就表示在參數中使用 require 不會影響全域變數的 scope 。
還有其他 include 或 require 的密技嗎？歡迎大家一起討論囉~
什麼！你早就會了？太好了！我們非常非常需要你！
	]]>
	</description>
	<content:encoded><![CDATA[
	<h2>可以接受回傳資料？</h2>
<p>先調查一下，知道 include 或 require 可以取得回傳資料的請舉手... (眺望)</p>
<p>呃...不知道的朋友也不用煩惱，我來解釋一下。</p>
<p>如何回傳資料呢？假設現在有個 php 檔叫做 config.php ，內容如下：</p>
<pre><code>&lt;?php
return array('123', '456');</code></pre>
<p>咦？那邊有人說 return 放錯地方了？不不不， PHP 能接受這樣的寫法。</p>
<p>好，現在我們來證明 include 或 require 能取得 config.php 所 return 回來的資料。請建立一支 test.php ，其內容是：</p>
<pre><code>$config = require 'config.php';
var_dump($config);</code></pre>
<p>執行看看，是不是可以跑呀？</p>
<p>所以我們可以在某支 PHP 程式中 return 一個資料 (任何型態) ，然後在另一支 PHP 程式中用 include 或 require 來取得這個資料。 </p>
<h2>把 require 放在參數裡 </h2>
<p>什麼？這不是密技？不不不，密技在底下：</p>
<pre><code>function test($config) {
    var_dump($config);
}
test(require 'config.php');</code></pre>
<p>對！你沒看錯！直接把 require 放在函式的參數裡！</p>
<p>還沒完呢，再看：</p>
<pre><code>class Test
{
    public function __construct($config)
    {
        var_dump($config);
    }
}
$a = new Test(require 'config.php');</code></pre>
<p>連 new 建構子的參數都可以接受 require ！</p>
<p>所以只要能放變數的地方，都可以放 include 或 require ，例如：</p>
<pre><code>if (require 'config.php') {
    var_dump(require 'config.php');
}

if ($config = require 'config.php') {
    var_dump($config);
}</code></pre>
<p>而且不僅是 include 及 require ，連 include_once 和 require_once 都可以這麼做。</p>
<p>我在<a href="http://blog.astrumfutura.com/archives/340-The-Zend-Framework,-Dependency-Injection-and-Zend_Di.html">某篇文章</a>發現這個密技以後，分享給辦公室裡的同事們；沒想到玩了 PHP 這麼多年的他們也沒看過這個方法，看來大家對 PHP 的瞭解需要更深入一點囉！</p>
<h2>Scope 的問題 </h2>
<p>接著我同事問了我一個問題：如果在參數使用 require 敘述，而且被 require 的 PHP 程式裡如果有定義全域變數的話，那麼這個變數在執行的 PHP 程式裡，它的 scope 在哪裡呢？</p>
<p>答案是：它還是全域。</p>
<p>怎麼說呢？現在我們在剛剛的 config.php 的 return 敘述前加上一行程式，如下：</p>
<pre><code>&lt;?php
<strong>$data = '789';</strong> // 加上這行
return array('123', '456');</code></pre>
<p>然後在 test.php 裡的 Global 部份 (也就是不在函式或類別定義裡) 的任意處加入：</p>
<pre><code>var_dump($data);</code></pre>
<p>是不是也可以正確顯示 config.php 中 $data 變數所指定的內容呢？這就表示在參數中使用 require 不會影響全域變數的 scope 。</p>
<p>還有其他 include 或 require 的密技嗎？歡迎大家一起討論囉~</p>
<p class="note">什麼！你早就會了？太好了！<a href="http://blog.roodo.com/jaceju/archives/3282383.html">我們非常非常需要你！</a></p>		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/5579811.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/5579811.html</guid>
	<category> PHP</category>
	<pubDate>Fri, 22 Feb 2008 21:00:00 +0800</pubDate>
</item>
<item>
	<title>[PHP] mysql_query 的記憶體使用與分頁方式</title>
	<description><![CDATA[
	說明
這個實驗主要是探討在 TWPUG 上的這篇 FIEND 寫的： [原創] [分享] 小弟寫的 cakephp 換頁 排序 功能 (第一版) 。
幾個實驗重點如下：


FIEND 提到兩次 Query 不是一個好方式，他的做法是用一次 Query 配合 while + mysql_result 就能做到分頁效果。


另外 shirock 從 PHP 原始碼的部份解釋 PHP 和 MySQL 抓資料後的處理方式，但 FIEND 卻說「用屁股想都知道 PHP 不可能把 QUERY 結果 全部拉回 PHP 端記憶體」。

還有 shirock 提到：「參考文章中已經很明白指出 mysql_query 跳過 PHP 內建記憶體配置機制，而直接使用 mysql C library 的函數儲存資料在 PHP 程序這端。而 memory_limit 只會管制到 PHP 內建記憶體配置機制的使用上限。所以 mysql_query 查詢大量資料時，不會受到 memory_limit 的限制。」 

基本上我從來不知道屁股可以用來思考，所以我還是要實事求是，用 FIEND 的方法實驗一次。
註：不過我老是在上廁所時想到一些靈感...Orz
環境

Windows XP
PHP 5.2.5
MySQL 5.0.45
memory_limit = 16M (in php.ini)

另外我準備了一個資料庫，裡面包含了四個資料表：


資料表 
筆數
硬碟空間


r1000
一千筆
75KB


r10000
一萬筆
743KB


r100000
十萬筆
7,422KB


r1000000
一百萬筆
74,219KB


資料表欄位為一個 id 欄位和一個 value 欄位；而 value 欄位為 varchar(64) ，其內容存的是兩個隨機的 md5 函式結果所組合的字串。可以用以下程式產生：
&lt;?php
echo &quot;CREATE DATABASE `page_test`;\n&quot;;
echo &quot;USE `page_test`;\n&quot;;

foreach (array(1000, 10000, 100000, 1000000) as $r) {
    $sql = &lt;&lt;&lt;SQL
CREATE TABLE IF NOT EXISTS `r$r` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `value` varchar(64) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM;
SQL;
    echo $sql, &quot;\n&quot;;
    for ($i = 1; $i &lt;= $r; $i ++) {
        $value = md5(rand(0, 9999)) . md5(rand(0, 9999));
        echo &quot;INSERT INTO `r$r` (`value`) VALUES ('$value');\n&quot;;
    }
}
程式
程式部份很簡單，就是按照 FIEND 說的步驟來寫的。只是我這裡改用 CLI 模式執行，以避掉 Apache 的影響。 
&lt;?php
echo ini_get('memory_limit'), &quot;\n&quot;; // 16M
echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;Start.\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

$link = mysql_connect('localhost', 'username', 'password');
mysql_select_db('page_test', $link);

$result = mysql_query('SELECT * FROM r1000000', $link);

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;mysql_query\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

$count  = mysql_num_rows($result);
echo 'count: ', $count, &quot;\n&quot;;

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;mysql_num_rows\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

$start = rand(0, $count - 1);
$end   = $start + 10;
$i     = $start;

echo 'start: ' . $start, &quot;\n&quot;;
echo 'end: ' . $end, &quot;\n&quot;;

echo &quot;\n&quot;;
while ($id = mysql_result($result, $i, 'id')) {
   echo $i, ': ', $id, &quot;\n&quot;;
   $i ++;
   if ($i &gt;= $end || $i &gt;= $count) break;
}

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;while &amp; mysql_result\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

mysql_free_result($result);

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;mysql_free_result\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

mysql_close($link);

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;mysql_close\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);
而記憶體的觀察我是用 Windows 的工作管理員，然後查看 php.exe 所使用的記憶體大小。在什麼都沒有執行的狀況下， php.exe 會佔用掉約 8MB 的記憶體 (在我的環境下) 。 
結果如下 (每項執行 5 次後再取平均值) ：


資料表
程式啟動
mysql_query
mysql_num_rows
while &amp; mysql_result
mysql_free_result
mysql_close


r1000
8,196KB
8,592KB
8,596KB
8,656KB
8,568KB
8,556KB


r10000
8,196KB
9,496KB
9,500KB
9,560KB
8,592KB
8,580KB


r100000
8,192KB
18,840KB
18,844KB
18,904KB
8,584KB
8,596KB


r1000000
8,192KB
110,684KB
110,688KB
110,748KB
8,796KB
8,784KB


然後我比較 mysql_query 用掉的記憶體和原來資料表使用的硬碟空間： 


資料表
mysql_query
硬碟空間


r1000
396KB
75KB


r10000
1,300KB
743KB


r100000
10,648KB
7,422KB


r1000000
102,492KB
74,219KB


很明顯地 mysql_query 所得到的 resource 佔的記憶體空間比 MySQL 使用的硬碟大小還多，這證明了 mysql_query 是有把資料內容抓到 PHP 這邊來；至於為什麼 MySQL 的反而比較小，我想這應該是 MySQL 將資料做壓縮的關係。
補充：我上面在 MySQL 壓縮的部份沒有任何根據，應該是錯誤的推論；因此我想就以 normansu 給我的說明為準： 

normansu 提到： 
應該不是.
執行 mysql_store_result 的時候, 
在 result 和每一筆 record 都會多一個 Header 的空間,
大小不一定(看 field count).

所以使用的記憶體會比實際 mysql table 大.
在 source 中沒有看到任何 compress 的動作.


mysql 的這個流程讓我嚇一跳,
以往大部份用的是 mssql 和 oracle,
在 client 和 server 間的 data cache 機制都做得比較好,
看起來 mysql 像是把結果算出來後就直接全部丟出來.

所以使用 mysql 要比用 mssql 或oracle 要來得更小心一點.


然後由於在上面的實驗裡我已經將 php.ini 的 memory_limit 設為 16M ，也用過 php -i 檢查過了。但在執行一百萬筆測試時，卻沒有受到任何影響，因此也證明了 shirock 說的「所以 mysql_query 查詢大量資料時，不會受到 memory_limit 的限制。」 
結論
從上面的實驗可以看到一次 Query 並取得資料總筆數雖是可行的，但這個前提是建立在 mysql_query 已經把資料內容全部放到 php.exe 的記憶體中。除非我誤解了 FIEND 的意思，不然他的作法看起來實在不適用於他所說的「存取大量資料的環境」。 
所以一般常見的做法是採用就是 tokimeki 提到的兩次 Query 的方式，第一次先利用 SQL 的 COUNT() 指令取得我們所需要的總筆數，第二次再配合 LIMIT 去取得我們所要的資料。不用 LIMIT 的後果就是每當執行一次 script ，我們就要冒著記憶體使用量爆增的後果。
註：雖然 MySQL 有 Query Cache ，但對 php 端已經爆增的記憶體也於事無補了。
而我也贊成 tokimeki 說的：「另外之二，假設內容資料是非常龐大的，且必須利用查詢內容作某些運算（例如：矩陣運算之類的），那麼這樣的應用不該由PHP程式來完成，應由其他的方式來作計算（例如：資料庫作OLAP或是Server上某個用C/C++寫的程式定期跑），Web這邊只做顯示以及計算排程即可。」
其他不想多說了，被某人看不起也不是一天兩天的事了。我自知自己還有很多東西要學，而這些還有望其他高手前輩們給予我指教。
不過最後這個實驗也證明一件事：屁股不是用來想事情的。 

	]]>
	</description>
	<content:encoded><![CDATA[
	<h2>說明</h2>
<p>這個實驗主要是探討在 TWPUG 上的這篇 FIEND 寫的： <a href="http://twpug.net/modules/newbb/viewtopic.php?topic_id=3094&amp;forum=35">[原創] [分享] 小弟寫的 cakephp 換頁 排序 功能 (第一版)</a> 。</p>
<p>幾個實驗重點如下：</p>
<ul>
<li>
<p>FIEND 提到兩次 Query 不是一個好方式，他的做法是用一次 Query 配合 while + mysql_result 就能做到分頁效果。</p>
</li>
<li>
<p>另外 shirock 從 PHP 原始碼的部份<a href="http://blog.roodo.com/rocksaying/archives/4986803.html">解釋 PHP 和 MySQL 抓資料後的處理方式</a>，但 FIEND 卻說「<strong><a href="http://twpug.net/modules/newbb/viewtopic.php?post_id=11749#forumpost11749">用屁股想都知道 PHP 不可能把 QUERY 結果 全部拉回 PHP 端記憶體</a></strong>」。</p>
</li>
<li>還有 shirock 提到：「參考文章中已經很明白指出 mysql_query 跳過 PHP 內建記憶體配置機制，而直接使用 mysql C library 的函數儲存資料在 PHP 程序這端。而 memory_limit 只會管制到 PHP 內建記憶體配置機制的使用上限。所以 mysql_query 查詢大量資料時，不會受到 memory_limit 的限制。」 </li>
</ul>
<p>基本上我從來不知道屁股可以用來思考，所以我還是要實事求是，用 FIEND 的方法實驗一次。</p>
<p class="note">註：不過我老是在上廁所時想到一些靈感...Orz</p>
<h2>環境</h2>
<ul>
<li>Windows XP</li>
<li>PHP 5.2.5</li>
<li>MySQL 5.0.45</li>
<li>memory_limit = 16M (in php.ini)</li>
</ul>
<p>另外我準備了一個資料庫，裡面包含了四個資料表：</p>
<table border="1" cellspacing="1" cellpadding="3" summary="測試用資料表">
<tr>
<th>資料表 </th>
<th>筆數</th>
<th>硬碟空間</th>
</tr>
<tr>
<td align="right">r1000</td>
<td align="right">一千筆</td>
<td align="right">75KB</td>
</tr>
<tr>
<td align="right">r10000</td>
<td align="right">一萬筆</td>
<td align="right">743KB</td>
</tr>
<tr>
<td align="right">r100000</td>
<td align="right">十萬筆</td>
<td align="right">7,422KB</td>
</tr>
<tr>
<td align="right">r1000000</td>
<td align="right">一百萬筆</td>
<td align="right">74,219KB</td>
</tr>
</table>
<p>資料表欄位為一個 id 欄位和一個 value 欄位；而 value 欄位為 varchar(64) ，其內容存的是兩個隨機的 md5 函式結果所組合的字串。可以用以下程式產生：</p>
<pre><code>&lt;?php
echo &quot;CREATE DATABASE `page_test`;\n&quot;;
echo &quot;USE `page_test`;\n&quot;;

foreach (array(1000, 10000, 100000, 1000000) as $r) {
    $sql = &lt;&lt;&lt;SQL
CREATE TABLE IF NOT EXISTS `r$r` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `value` varchar(64) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM;
SQL;
    echo $sql, &quot;\n&quot;;
    for ($i = 1; $i &lt;= $r; $i ++) {
        $value = md5(rand(0, 9999)) . md5(rand(0, 9999));
        echo &quot;INSERT INTO `r$r` (`value`) VALUES ('$value');\n&quot;;
    }
}</code></pre>
<h2>程式</h2>
<p>程式部份很簡單，就是按照 FIEND 說的步驟來寫的。只是我這裡改用 CLI 模式執行，以避掉 Apache 的影響。 </p>
<pre><code>&lt;?php
echo ini_get('memory_limit'), &quot;\n&quot;; // 16M
echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;Start.\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

$link = mysql_connect('localhost', 'username', 'password');
mysql_select_db('page_test', $link);

$result = mysql_query('SELECT * FROM r1000000', $link);

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;mysql_query\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

$count  = mysql_num_rows($result);
echo 'count: ', $count, &quot;\n&quot;;

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;mysql_num_rows\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

$start = rand(0, $count - 1);
$end   = $start + 10;
$i     = $start;

echo 'start: ' . $start, &quot;\n&quot;;
echo 'end: ' . $end, &quot;\n&quot;;

echo &quot;\n&quot;;
while ($id = mysql_result($result, $i, 'id')) {
   echo $i, ': ', $id, &quot;\n&quot;;
   $i ++;
   if ($i &gt;= $end || $i &gt;= $count) break;
}

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;while &amp; mysql_result\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

mysql_free_result($result);

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;mysql_free_result\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);

mysql_close($link);

echo &quot;\n&quot;;
echo &quot;========================\n&quot;;
echo &quot;mysql_close\n&quot;;
echo &quot;========================\n&quot;;
sleep(10);</code></pre>
<p>而記憶體的觀察我是用 Windows 的工作管理員，然後查看 php.exe 所使用的記憶體大小。在什麼都沒有執行的狀況下， php.exe 會佔用掉約 8MB 的記憶體 (在我的環境下) 。 </p>
<p>結果如下 (每項執行 5 次後再取平均值) ：</p>
<table border="1" cellspacing="1" cellpadding="3" summary="程式執行時的記憶體用量比較">
<tr>
<th>資料表</th>
<th>程式啟動</th>
<th>mysql_query</th>
<th>mysql_num_rows</th>
<th>while &amp; mysql_result</th>
<th>mysql_free_result</th>
<th>mysql_close</th>
</tr>
<tr>
<td align="right">r1000</td>
<td align="right">8,196KB</td>
<td align="right">8,592KB</td>
<td align="right">8,596KB</td>
<td align="right">8,656KB</td>
<td align="right">8,568KB</td>
<td align="right">8,556KB</td>
</tr>
<tr>
<td align="right">r10000</td>
<td align="right">8,196KB</td>
<td align="right">9,496KB</td>
<td align="right">9,500KB</td>
<td align="right">9,560KB</td>
<td align="right">8,592KB</td>
<td align="right">8,580KB</td>
</tr>
<tr>
<td align="right">r100000</td>
<td align="right">8,192KB</td>
<td align="right">18,840KB</td>
<td align="right">18,844KB</td>
<td align="right">18,904KB</td>
<td align="right">8,584KB</td>
<td align="right">8,596KB</td>
</tr>
<tr>
<td align="right">r1000000</td>
<td align="right">8,192KB</td>
<td align="right">110,684KB</td>
<td align="right">110,688KB</td>
<td align="right">110,748KB</td>
<td align="right">8,796KB</td>
<td align="right">8,784KB</td>
</tr>
</table>
<p>然後我比較 mysql_query 用掉的記憶體和原來資料表使用的硬碟空間： </p>
<table border="1" cellspacing="1" cellpadding="3" summary="比較 mysql_query 用掉的記憶體和原來資料表使用的硬碟空間">
<tr>
<th>資料表</th>
<th>mysql_query</th>
<th>硬碟空間</th>
</tr>
<tr>
<td align="right">r1000</td>
<td align="right">396KB</td>
<td align="right">75KB</td>
</tr>
<tr>
<td align="right">r10000</td>
<td align="right">1,300KB</td>
<td align="right">743KB</td>
</tr>
<tr>
<td align="right">r100000</td>
<td align="right">10,648KB</td>
<td align="right">7,422KB</td>
</tr>
<tr>
<td align="right">r1000000</td>
<td align="right">102,492KB</td>
<td align="right">74,219KB</td>
</tr>
</table>
<p>很明顯地 mysql_query 所得到的 resource 佔的記憶體空間比 MySQL 使用的硬碟大小還多，這證明了 <strong>mysql_query 是有把資料內容抓到 PHP 這邊來</strong><del>；至於為什麼 MySQL 的反而比較小，我想這應該是 MySQL 將資料做壓縮的關係</del>。</p>
<p><strong>補充：</strong>我上面在 MySQL 壓縮的部份沒有任何根據，應該是錯誤的推論；因此我想就以 normansu 給我的說明為準： </p>
<blockquote>
<p><a href="http://twpug.net/modules/newbb/viewtopic.php?post_id=11784#forumpost11784">normansu 提到</a>： </p>
<p>應該不是.<br />
執行 mysql_store_result 的時候, <br />
在 result 和每一筆 record 都會多一個 Header 的空間,<br />
大小不一定(看 field count).<br />
<br />
所以使用的記憶體會比實際 mysql table 大.<br />
在 source 中沒有看到任何 compress 的動作.<br />
<br />
<br />
mysql 的這個流程讓我嚇一跳,<br />
以往大部份用的是 mssql 和 oracle,<br />
在 client 和 server 間的 data cache 機制都做得比較好,<br />
看起來 mysql 像是把結果算出來後就直接全部丟出來.<br />
<br />
所以使用 mysql 要比用 mssql 或oracle 要來得更小心一點.
</p>
</blockquote>
<p>然後由於在上面的實驗裡我已經將 php.ini 的 memory_limit 設為 16M ，也用過 php -i 檢查過了。但在執行一百萬筆測試時，卻沒有受到任何影響，因此也證明了 shirock 說的「<strong>所以 mysql_query 查詢大量資料時，不會受到 memory_limit 的限制。</strong>」 </p>
<h2>結論</h2>
<p>從上面的實驗可以看到一次 Query 並取得資料總筆數雖是可行的，但這個<strong>前提是建立在 mysql_query 已經把資料內容全部放到 php.exe 的記憶體中</strong>。除非我誤解了 FIEND 的意思，不然他的作法看起來實在不適用於他所說的「存取大量資料的環境」。 </p>
<p>所以一般常見的做法是採用就是 tokimeki 提到的<a href="http://twpug.net/modules/newbb/viewtopic.php?post_id=11671#forumpost11671">兩次 Query 的方式</a>，第一次先利用 SQL 的 COUNT() 指令取得我們所需要的總筆數，第二次再配合 LIMIT 去取得我們所要的資料。不用 LIMIT 的後果就是每當執行一次 script ，我們就要冒著記憶體使用量爆增的後果。</p>
<p class="note">註：雖然 MySQL 有 <a href="http://www.tblog.com.cn/index.php/archives/659">Query Cache</a> ，但對 php 端已經爆增的記憶體也於事無補了。</p>
<p>而我也贊成 tokimeki 說的：「另外之二，假設內容資料是非常龐大的，且必須利用查詢內容作某些運算（例如：矩陣運算之類的），那麼這樣的應用不該由PHP程式來完成，應由其他的方式來作計算（例如：資料庫作OLAP或是Server上某個用C/C++寫的程式定期跑），Web這邊只做顯示以及計算排程即可。」</p>
<p>其他不想多說了，被某人看不起也不是一天兩天的事了。我自知自己還有很多東西要學，而這些還有望其他高手前輩們給予我指教。</p>
<p>不過最後這個實驗也證明一件事：屁股不是用來想事情的。 </p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/4991595.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/4991595.html</guid>
	<category> PHP</category>
	<pubDate>Wed, 16 Jan 2008 16:21:01 +0800</pubDate>
</item>
<item>
	<title>[PHP] 交換兩個變數 (不使用 tmp 變數) 程式寫法</title>
	<description><![CDATA[
	在宗董的 Blog 看到這篇：交換兩個變數 (不使用 tmp 變數) 程式寫法，本來想留言，不過宗董的 Blog 系統似乎有問題。
宗董的方法是這樣的：
$a ^= $b;
$b ^= $a;
$a ^= $b;
我是想說既然是用 PHP 了，就應該好好善用一下 PHP 的原生語法：
list($a, $b) = array($b, $a);
搞定~~
這個是從 PHP 程式設計專家必備手冊一書看來的。

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>在宗董的 Blog 看到這篇：<a href="http://plog.longwin.com.tw/programming/2007/11/23/variable_swap_programming_2007">交換兩個變數 (不使用 tmp 變數) 程式寫法</a>，本來想留言，不過宗董的 Blog 系統似乎有問題。</p>
<p>宗董的方法是這樣的：</p>
<pre><code>$a ^= $b;
$b ^= $a;
$a ^= $b;</code></pre>
<p>我是想說既然是用 PHP 了，就應該好好善用一下 PHP 的原生語法：</p>
<pre><code>list($a, $b) = array($b, $a);</code></pre>
<p>搞定~~</p>
<p>這個是從 <a href="http://www.pearsoned.com.tw/chinese_show_title.asp?bkid=9867910672">PHP 程式設計專家必備手冊</a>一書看來的。</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/4528893.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/4528893.html</guid>
	<category> PHP</category>
	<pubDate>Fri, 23 Nov 2007 15:27:09 +0800</pubDate>
</item>
<item>
	<title>[PHP] 神奇的 $this</title>
	<description><![CDATA[
	今天發現了一個 PHP 5.2.4 的奇怪現象，查了官方手冊也沒發現有人特別提起 (也可能是我沒找到) 。
學過 PHP 物件導向的人都知道， $this 這個關鍵字是在生成一個物件後才能使用的。例如：
class Foo
{
    private $_foo = '_foo in class Foo.';

    public function test()
    {
        echo $this-&gt;_foo;
    }
}

$foo = new Foo();
$foo-&gt;test(); // _foo in class Foo.
而且 $this 在 Class 的程式碼裡代表的也是這個物件本身，在上例中即為 $foo 。
不過在 method 裡使用 $this 有個限制，那就是該 method 不能以 static 的方式來呼叫；也就是說，以下的執行方式是錯的：
Foo::test(); // Fatal error: Using $this when not in object context in xxx.php 
可是請看以下的程式碼：
&lt;?php

class Foo
{
    private $_foo = '_foo in class Foo.';

    public function test()
    {
        echo $this-&gt;_foo;
    }
}

class Bar
{
    public function test()
    {
        Foo::test();
    }
}

$b = new Bar();
$b-&gt;test(); // Notice: Undefined property:  Bar::$_foo in xxx.php
發現什麼問題了嗎？在 Bar::test() 裡我們竟然可以用 static 的方式呼叫 Foo::test() ！ 而且在 Foo::test() 裡的 $this-&gt;_foo 竟然變成了 Bar 類別的 $_foo 屬性！ 
至於這倒底是 PHP 的特色還是 Bug ？我也不知道，還望高手賜教。

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>今天發現了一個 PHP 5.2.4 的奇怪現象，查了官方手冊也沒發現有人特別提起 (也可能是我沒找到) 。</p>
<p>學過 PHP 物件導向的人都知道， $this 這個關鍵字是在生成一個物件後才能使用的。例如：</p>
<pre><code>class Foo
{
    private $_foo = '_foo in class Foo.';

    public function test()
    {
        echo $this-&gt;_foo;
    }
}

$foo = new Foo();
$foo-&gt;test(); // _foo in class Foo.</code></pre>
<p>而且 $this 在 Class 的程式碼裡代表的也是這個物件本身，在上例中即為 $foo 。</p>
<p>不過在 method 裡使用 $this 有個限制，那就是該 method 不能以 static 的方式來呼叫；也就是說，以下的執行方式是錯的：</p>
<pre><code>Foo::test(); // Fatal error: Using $this when not in object context in xxx.php </code></pre>
<p>可是請看以下的程式碼：</p>
<pre><code>&lt;?php

class Foo
{
    private $_foo = '_foo in class Foo.';

    public function test()
    {
        echo $this-&gt;_foo;
    }
}

class Bar
{
    public function test()
    {
        Foo::test();
    }
}

$b = new Bar();
$b-&gt;test(); // Notice: Undefined property:  Bar::$_foo in xxx.php</code></pre>
<p>發現什麼問題了嗎？在 Bar::test() 裡我們竟然可以用 static 的方式呼叫 Foo::test() ！ 而且在 Foo::test() 裡的 $this-&gt;_foo 竟然變成了 Bar 類別的 $_foo 屬性！ </p>
<p>至於這倒底是 PHP 的特色還是 Bug ？我也不知道，還望高手賜教。</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/4365549.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/4365549.html</guid>
	<category> PHP</category>
	<pubDate>Fri, 26 Oct 2007 00:14:38 +0800</pubDate>
</item>
<item>
	<title>[PHP] PHP 5.2.4 發佈</title>
	<description><![CDATA[
	

PHP 5.2.4 Released
主要是修正了很多 Bug ， 我想等一陣子再更新好了。 


獵人261 連載再開!!!!  富樫義博 終於要畫 HunterXhunter 261了!!!
富奸終於要畫新的獵人了~~真是太感動了！！！ ( TT__TT ) 
什麼？這跟網站製作沒關係喔？因為我太高興了嗎...



	]]>
	</description>
	<content:encoded><![CDATA[
	<ul>
<li>
<p><a href="http://www.php.net/index.php#2007-08-30-1">PHP 5.2.4 Released</a></p>
<p>主要是修正了很多 Bug ， 我想等一陣子再更新好了。 </p>
</li>
<li>
<p><a href="http://ookkk.blogspot.com/2007/08/261-hunterxhunter-261.html">獵人261 連載再開!!!!  富樫義博 終於要畫 HunterXhunter 261了!!!</a></p>
<p>富奸終於要畫新的獵人了~~真是太感動了！！！ ( TT__TT ) </p>
<p>什麼？這跟網站製作沒關係喔？因為我太高興了嗎...</p>
</li>
</ul>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/4054959.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/4054959.html</guid>
	<category> PHP</category>
	<pubDate>Fri, 31 Aug 2007 14:07:57 +0800</pubDate>
</item>
<item>
	<title>[PHP] 幾篇不錯的文章</title>
	<description><![CDATA[
	

Incredible Zend ZDE debugging trick for debugging CLI apps
用以下方式來 Debug PHP CLI 應用程式，我只能說作者真的很天才。
C:\&gt; set &quot;QUERY_STRING=start_debug=1&amp;debug_host=192.168.0.102&amp;debug_port=10000&amp;debug_stop=1&quot;
有機會應該來試試看才對。


AJAX and PHP End-to-End Debugging
參考上一篇文章的方式，這次用來 Debug Ajax ；也就是把上面的 QUERY_STRING 系統變數當做 Ajax 的 GET 參數。 


PHP: Arrays vs. Objects
又是一篇比較 PHP 陣列和物件效率的文章，我個人是認為效率相差不大的狀況下，應該挑選適合程序的方式來用，而不是固執於一種形式。


PHP Namespaces (Part 1: Basic usage &amp; gotchas)
這篇告訴你為什麼要用 Namespaces ，而 Mark 在中文化 dotProject 時也遇過同樣的問題。 


Namespaces FAQ
經典：

 Q: PHP 為什麼需要 Namespaces ？
A: 因為 PEAR_Form_Loader_Validate_Table_Element_Validator_Exception 實在是太 TMD 長了。 (嗯...這句是我瞎掰的 XD) 




	]]>
	</description>
	<content:encoded><![CDATA[
	<ul>
<li>
<p><a href="http://greg.chiaraquartet.net/archives/16-Incredible-Zend-ZDE-debugging-trick-for-debugging-CLI-apps.html">Incredible Zend ZDE debugging trick for debugging CLI apps</a></p>
<p>用以下方式來 Debug PHP CLI 應用程式，我只能說作者真的很天才。</p>
<pre><code>C:\&gt; set &quot;QUERY_STRING=start_debug=1&amp;debug_host=192.168.0.102&amp;debug_port=10000&amp;debug_stop=1&quot;</code></pre>
<p>有機會應該來試試看才對。</p>
</li>
<li>
<p><a href="http://pixelated-dreams.com/archives/313-AJAX-and-PHP-End-to-End-Debugging.html">AJAX and PHP End-to-End Debugging</a></p>
<p>參考上一篇文章的方式，這次用來 Debug Ajax ；也就是把上面的 QUERY_STRING 系統變數當做 Ajax 的 GET 參數。 </p>
</li>
<li>
<p><a href="http://www.rooftopsolutions.nl/article/148">PHP: Arrays vs. Objects</a></p>
<p>又是一篇比較 PHP 陣列和物件效率的文章，我個人是認為效率相差不大的狀況下，應該挑選適合程序的方式來用，而不是固執於一種形式。</p>
</li>
<li>
<p><a href="http://blog.agoraproduction.com/index.php?/archives/47-PHP-Namespaces-Part-1-Basic-usage-gotchas.html">PHP Namespaces (Part 1: Basic usage &amp; gotchas)</a></p>
<p>這篇告訴你為什麼要用 Namespaces ，而 Mark 在中文化 dotProject 時也遇過同樣的問題。 </p>
</li>
<li>
<p><a href="http://php100.wordpress.com/2007/08/17/namespaces-faq/">Namespaces FAQ</a></p>
<p>經典：</p>
<blockquote>
<p> Q: PHP 為什麼需要 Namespaces ？</p>
<p>A: 因為 PEAR_Form_Loader_Validate_Table_Element_Validator_Exception 實在是太 TMD 長了。 (嗯...這句是我瞎掰的 XD) </p>
</blockquote>
</li>
</ul>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3952367.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3952367.html</guid>
	<category> PHP</category>
	<pubDate>Sat, 18 Aug 2007 13:58:56 +0800</pubDate>
</item>
<item>
	<title>[轉載] Scraping Links With PHP</title>
	<description><![CDATA[
	剛剛看到一篇很有用的 PHP 技巧整理，主要在說明如何結合 cURL 和 DOM 來解析網頁中的連結：
Scraping Links With PHP
節錄如下：

What You'll Learn

How to use cURL to get the content from a website (URL).
Call PHP DOM functions to parse the HTML so you can extract links.
Use XPath to grab links from specific parts of a page.
Store the scraped links in a MySQL database.
Put it all together into a link scraper.
What else you could use a scraper for.
Legal issues associated with scraping content.

What You Will Need

Basic knowledge of PHP and MySQL.
A web server running PHP 5.
The cURL extension for PHP.
MySQL - if you want to store the links.



	]]>
	</description>
	<content:encoded><![CDATA[
	<p>剛剛看到一篇很有用的 PHP 技巧整理，主要在說明如何結合 cURL 和 DOM 來解析網頁中的連結：</p>
<p><a href="http://www.merchantos.com/makebeta/php/scraping-links-with-php/" target="_blank">Scraping Links With PHP</a></p>
<p>節錄如下：</p>
<blockquote>
<h3>What You'll Learn</h3>
<ol>
<li><a href="http://www.merchantos.com/makebeta/php/scraping-links-with-php/#curl_content">How to use cURL</a> to get the content from a website (URL).</li>
<li><a href="http://www.merchantos.com/makebeta/php/scraping-links-with-php/#php_dom">Call PHP DOM functions</a> to parse the HTML so you can extract links.</li>
<li><a href="http://www.merchantos.com/makebeta/php/scraping-links-with-php/#xpath_easy">Use XPath to grab links</a> from specific parts of a page.</li>
<li><a href="http://www.merchantos.com/makebeta/php/scraping-links-with-php/#store_links">Store the scraped links</a> in a MySQL database.</li>
<li><a href="http://www.merchantos.com/makebeta/php/scraping-links-with-php/#put_together">Put it all together into a link scraper.</a></li>
<li><a href="http://www.merchantos.com/makebeta/php/scraping-links-with-php/#more_uses">What else you could use a scraper for.</a></li>
<li><a href="http://www.merchantos.com/makebeta/php/scraping-links-with-php/#legal_issues">Legal issues</a> associated with scraping content.</li>
</ol>
<h3>What You Will Need</h3>
<ul>
<li>Basic knowledge of <a href="http://php.net/">PHP</a> and <a href="http://dev.mysql.com/doc/refman/5.0/en/">MySQL</a>.</li>
<li>A web server running PHP 5.</li>
<li>The <a href="http://us.php.net/curl">cURL</a> extension for PHP.</li>
<li>MySQL - if you want to store the links.</li>
</ul>
</blockquote>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3939321.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3939321.html</guid>
	<category> PHP</category>
	<pubDate>Thu, 16 Aug 2007 10:21:29 +0800</pubDate>
</item>
<item>
	<title>[PHP] Namespace in PHP5 Come Back!!</title>
	<description><![CDATA[
	不用等 PHP6 了， Frank Kleine 說 PHP 5.3 就會把 Namespace 帶回來了。原文如下：

Today I saw PHP namespaces in action. Timm Friebe, who backported the namespace patch to PHP5, showed me a namespaced version of the XP-Framework. This was very impressive! We experimented a bit with __autoload() and it was very nice to see that the function is not called on the import but actually when the class is used within the code. Additionally we played around with full and non qualified classnames, with and without the import statement. What I saw looks really promising. Keep your fingers crossed that we will get a PHP5.3 with namespaces!

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>不用等 PHP6 了， <a href="http://www.stubbles.org/archives/22-Namespaces-in-action.html">Frank Kleine</a> 說 PHP 5.3 就會把 Namespace 帶回來了。原文如下：</p>
<blockquote>
<p>Today I saw PHP namespaces in action. Timm Friebe, who <a href="http://www.stubbles.org/exit.php?url_id=182&amp;entry_id=22" onmouseover="window.status='http://news.php.net/php.internals/31143';return true;" onmouseout="window.status='';return true;" title="Announcement of the patch on php-internals.">backported the namespace patch to PHP5</a>, showed me a namespaced version of the XP-Framework. This was very impressive! We experimented a bit with __autoload() and it was very nice to see that the function is not called on the import but actually when the class is used within the code. Additionally we played around with full and non qualified classnames, with and without the import statement. What I saw looks really promising. Keep your fingers crossed that we will get a PHP5.3 with namespaces!</p>
</blockquote>		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3719267.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3719267.html</guid>
	<category> PHP</category>
	<pubDate>Thu, 26 Jul 2007 10:03:22 +0800</pubDate>
</item>
<item>
	<title>PHP4 的盡頭</title>
	<description><![CDATA[
	PHP 官方宣佈停止對 PHP4 的支援了，原文如下：

PHP 4 end of life announcement
[13-Jul-2007]
Today it is exactly three years ago since PHP 5 has been released. In those three years it has seen many improvements over PHP 4. PHP 5 is fast, stable &amp; production-ready and as PHP 6 is on the way, PHP 4 will be discontinued.
The PHP development team hereby announces that support for PHP 4 will continue until the end of this year only. After 2007-12-31 there will be no more releases of PHP 4.4. We will continue to make critical security fixes available on a case-by-case basis until 2008-08-08. Please use the rest of this year to make your application suitable to run on PHP 5.
For documentation on migration for PHP 4 to PHP 5, we would like to point you to our migration guide. There is additional information available in the PHP 5.0 to PHP 5.1 and PHP 5.1 to PHP 5.2 migration guides as well.

對某些人來說大概是壞消息，但對我而言卻是很棒的新聞。
PHP 終於醒了...

	]]>
	</description>
	<content:encoded><![CDATA[
	<p><a href="http://www.php.net">PHP 官方</a>宣佈停止對 PHP4 的支援了，原文如下：</p>
<blockquote>
<h3>PHP 4 end of life announcement</h3>
<span class="newsdate">[13-Jul-2007]</span>
<p>Today it is exactly three years ago since PHP 5 has been released. In those three years it has seen many improvements over PHP 4. PHP 5 is fast, stable &amp; production-ready and as PHP 6 is on the way, PHP 4 will be discontinued.</p>
<p>The PHP development team hereby announces that support for PHP 4 will continue until the end of this year only. After 2007-12-31 there will be no more releases of PHP 4.4. We will continue to make critical security fixes available on a case-by-case basis until 2008-08-08. Please use the rest of this year to make your application suitable to run on PHP 5.</p>
<p>For documentation on migration for PHP 4 to PHP 5, we would like to point you to our <a href="/manual/en/migration5.php">migration guide</a>. There is additional information available in the <a href="/manual/en/migration51.php">PHP 5.0 to PHP 5.1</a> and <a href="/manual/en/migration52.php">PHP 5.1 to PHP 5.2</a> migration guides as well.</p>
</blockquote>
<p>對某些人來說大概是壞消息，但對我而言卻是很棒的新聞。</p>
<p>PHP 終於醒了...</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3653075.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3653075.html</guid>
	<category> PHP</category>
	<pubDate>Fri, 13 Jul 2007 18:41:12 +0800</pubDate>
</item>
<item>
	<title>邁向 PHP5</title>
	<description><![CDATA[
	
先前我在「 PHP5 將滿 4 歲」提到許多虛擬主機商不敢升級到 PHP5 ；針對這個問題，前幾天，幾個國外知名開源 PHP 軟體公開推動了 GoPHP5 這個計畫。
以下轉錄對岸網友 lnxcym 提供的翻譯內容：

 [转]多家PHP开源项目联手推动PHP5普及
2007年7月5日消息，一个PHP开发者联盟近日宣布，几家著名的开源PHP项目将在2008年2月5日以后推出的新版软件中，它们将放弃对老版本PHP的支持，以联手推动PHP开发者社区完全向PHP 5迁移。　　包括Symfony、Typo3、phpMyAdmin、Drupal、Propel和Doctrine等在内的开源项目已经都宣布，在2008年2月5日以后发布的新版将要求PHP 5.2版本，以作为对GoPHP5.org支持的一部分，另外，它们还向其他任何PHP项目和应用程序发出了邀请，来共同推动PHP5的普及，不管是开源软件还是专有软件。　　目前大多数基于PHP的Web应用程序即可以运行在PHP4下，又可以运行在PHP5下。PHP 4在2000年发布，并迅速成为主流Web开发语言之一。版本5是在2004年发布，具有很多功能上的改进，但是由于种种原因，被采用的速度却非常慢。　　Drupal的开发者兼GoPHP5.org的创始人之一Larry Garfield表示，“和我交流过的大多数PHP开发者都想使用PHP 5，但是由于许多Web主机只默认提供对PHP 4的支持，因此实际上并没有使用。而Web主机不会升级是因为项目没有升级，而项目之所以不升级是因为Web主机没有升级。这使得许多项目在放弃对PHP4的支持的时候感到为难，因此，我们计划从自身做起，为推动PHP5的普及而努力。”　　通过预先宣布计划在2008年的软件版本中要求PHP 5.2，GoPHP5希望借此来推动Web主机开始升级他们的服务器到更新、更稳定和功能更丰富的PHP版本，并且让它们有充分的时间来完成这个工作。同时，那些使用相关项目的现在版本的用户也不会被冷落。所有相关项目将继续支持现有建立在PHP4上的版本，以保持他们的正常生活秩序，给用户和运营商时间来计划和实施升级。　　phpMyAdmin的项目领导Marc Delisle补充说，“phpMyAdmin项目对加入GoPHP5活动非常热情，我们将GoPHP5看作完善我们产品的新版本的方式，不用不得不投入精力保持其PHP4兼容性，另外还可以改善我们用户的体验。”　　PHP 5为开发者提供了大量的新功能，来帮助开发者更快速的开发现在的Web应用程序，其中包括大大提高的Web服务的XML处理能力，一个被叫做SQLLite的完整SQL数据库，更好的对时区处理，令人印象深刻的完善的安全工具，更强大的面向对象功能，等等。许多PHP项目已经要求使用PHP 5。通过推广PHP 5的安装范围的行动，也将扩大这些项目的市场范围。　　PHP是一个广泛应用的通用脚本语言，尤其适合于Web开发。PHP是当前领先的Web开发语言之一，可以运行在第三方Web服务器上，目前被包括从Yahoo到Facebook在内的众多知名IT网站所选用。    更多信息，请访问以下网站：    http://gophp5.org/    http://www.phpmyadmin.net/home_page/gophp5.php    http://drupal.org/gophp5

我個人也希望能藉由這樣的計畫，推動更多 PHP 開發者朝向 PHP5 前進；尤其目前 Zend Framework 的釋出，也讓我對 PHP5 的未來更加看好。

	]]>
	</description>
	<content:encoded><![CDATA[
	<p class="image"><a href="http://gophp5.org/" target="_blank"><img src="http://gophp5.org/sites/gophp5.org/files/gophp5_logo.png" alt="GoPHP5" border="0" /></a></p>
<p>先前我在「 <a href="http://blog.roodo.com/jaceju/archives/3517185.html" target="_blank">PHP5 將滿 4 歲</a>」提到許多虛擬主機商不敢升級到 PHP5 ；針對這個問題，前幾天，幾個國外知名開源 PHP 軟體公開推動了 <a href="http://gophp5.org/" target="_blank">GoPHP5</a> 這個計畫。</p>
<p>以下轉錄對岸網友 <a href="http://club.phpe.net/index.php?act=Profile&amp;CODE=03&amp;MID=9388">lnxcym</a> 提供的<a href="http://club.phpe.net/index.php?act=ST&amp;f=2&amp;t=15989">翻譯內容</a>：</p>
<blockquote>
<h4> <strong>[转]多家PHP开源项目联手推动PHP5普及</strong></h4>
<p>2007年7月5日消息，一个PHP开发者联盟近日宣布，几家著名的开源PHP项目将在2008年2月5日以后推出的新版软件中，它们将放弃对老版本PHP的支持，以联手推动PHP开发者社区完全向PHP 5迁移。<br /><br />　　包括Symfony、Typo3、phpMyAdmin、Drupal、Propel和Doctrine等在内的开源项目已经都宣布，在2008年2月5日以后发布的新版将要求PHP 5.2版本，以作为对GoPHP5.org支持的一部分，另外，它们还向其他任何PHP项目和应用程序发出了邀请，来共同推动PHP5的普及，不管是开源软件还是专有软件。<br /><br />　　目前大多数基于PHP的Web应用程序即可以运行在PHP4下，又可以运行在PHP5下。PHP 4在2000年发布，并迅速成为主流Web开发语言之一。版本5是在2004年发布，具有很多功能上的改进，但是由于种种原因，被采用的速度却非常慢。<br /><br />　　Drupal的开发者兼GoPHP5.org的创始人之一Larry Garfield表示，“和我交流过的大多数PHP开发者都想使用PHP 5，但是由于许多Web主机只默认提供对PHP 4的支持，因此实际上并没有使用。而Web主机不会升级是因为项目没有升级，而项目之所以不升级是因为Web主机没有升级。这使得许多项目在放弃对PHP4的支持的时候感到为难，因此，我们计划从自身做起，为推动PHP5的普及而努力。”<br /><br />　　通过预先宣布计划在2008年的软件版本中要求PHP 5.2，GoPHP5希望借此来推动Web主机开始升级他们的服务器到更新、更稳定和功能更丰富的PHP版本，并且让它们有充分的时间来完成这个工作。同时，那些使用相关项目的现在版本的用户也不会被冷落。所有相关项目将继续支持现有建立在PHP4上的版本，以保持他们的正常生活秩序，给用户和运营商时间来计划和实施升级。<br /><br />　　phpMyAdmin的项目领导Marc Delisle补充说，“phpMyAdmin项目对加入GoPHP5活动非常热情，我们将GoPHP5看作完善我们产品的新版本的方式，不用不得不投入精力保持其PHP4兼容性，另外还可以改善我们用户的体验。”<br /><br />　　PHP 5为开发者提供了大量的新功能，来帮助开发者更快速的开发现在的Web应用程序，其中包括大大提高的Web服务的XML处理能力，一个被叫做SQLLite的完整SQL数据库，更好的对时区处理，令人印象深刻的完善的安全工具，更强大的面向对象功能，等等。许多PHP项目已经要求使用PHP 5。通过推广PHP 5的安装范围的行动，也将扩大这些项目的市场范围。<br /><br />　　PHP是一个广泛应用的通用脚本语言，尤其适合于Web开发。PHP是当前领先的Web开发语言之一，可以运行在第三方Web服务器上，目前被包括从Yahoo到Facebook在内的众多知名IT网站所选用。<br /><br />    更多信息，请访问以下网站：<br /><br />    <a href="http://gophp5.org/" target="_blank">http://gophp5.org/</a><br />    <a href="http://www.phpmyadmin.net/home_page/gophp5.php" target="_blank">http://www.phpmyadmin.net/home_page/gophp5.php</a><br />    <a href="http://drupal.org/gophp5" target="_blank">http://drupal.org/gophp5</a></p>
</blockquote>
<p>我個人也希望能藉由這樣的計畫，推動更多 PHP 開發者朝向 PHP5 前進；尤其目前 Zend Framework 的釋出，也讓我對 PHP5 的未來更加看好。</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3619027.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3619027.html</guid>
	<category> PHP</category>
	<pubDate>Sun, 08 Jul 2007 17:41:09 +0800</pubDate>
</item>
<item>
	<title>Namespaces in PHP6 逆襲！ </title>
	<description><![CDATA[
	原本 PHP5 在 beta 版時有實作 Namespaces 的概念，但是因為某些因素而使得在正式推出時拿掉了。 
昨天 Dmitry Stogov 又把 Namespaces 的想法帶回 PHP 了，整理一下相關的連結：


[PHP-DEV] Simple Namespace Proposal (相關討論)



PHP namespaces


Namespaces - can we keep it  simple?


基本上我個人覺得還不錯，不過工作忙加上資歷不深，沒有什麼高明的見解，所以詳細的討論就留給各位高手們發表了。

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>原本 PHP5 在 beta 版時有實作 Namespaces 的概念，但是因為某些因素而使得在正式推出時拿掉了。 </p>
<p>昨天 Dmitry Stogov 又把 Namespaces 的想法帶回 PHP 了，整理一下相關的連結：</p>
<ul>
<li>
<p><a href="http://marc.info/?l=php-dev&amp;m=118355320225178&amp;w=2">[PHP-DEV] Simple Namespace Proposal</a> (<a href="http://marc.info/?t=118355328300005&amp;r=1&amp;w=2">相關討論</a>)
</p>
</li>
<li>
<p><a href="http://www.rooftopsolutions.nl/article/139">PHP namespaces</a></p>
</li>
<li>
<p><a href="http://php100.wordpress.com/2007/07/05/namespaces-can-we-keep-it-simple/" rel="bookmark" title="Permanent Link to Namespaces - can we keep it simple?">Namespaces - can we keep it  simple?</a></p>
</li>
</ul>
<p>基本上我個人覺得還不錯，不過工作忙加上資歷不深，沒有什麼高明的見解，所以詳細的討論就留給各位高手們發表了。</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3603131.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3603131.html</guid>
	<category> PHP</category>
	<pubDate>Fri, 06 Jul 2007 10:23:05 +0800</pubDate>
</item>
<item>
	<title>PHPUnit 3.1.0 釋出</title>
	<description><![CDATA[
	這個月 (2007/07) 好像大家都說好了，我所關注的幾個專案都釋出了新版本。剛剛 PHPUnit 也釋出了 3.1.0 版，主要特色如下：


Improvements to Mock Objects include the ability to mock classes and methods that do not exist as  well as mocking static methods. And mocked methods can raise exceptions  now.


PHPUnit's Selenium RC extension no longer uses the Testing_Selenium PEAR package but its own implementation of the Selenium RC client/server protocol. Among other benefits, this allows for the collection of code coverage data for Selenium tests.


PHPUnit can now write test result and code coverage data to a test database. Several ideas for future features depend on this data.


New template methods, PHPUnit_Framework_TestCase::sharedAssertions(), PHPUnit_Framework_TestSuite::setUp(), PHPUnit_Framework_TestSuite::tearDown(), and PHPUnit_Extensions_SeleniumTestCase::defaultAssertions(), ease the development of test cases.


PHPUnit_Framework_TestCase::assertEquals() can now operate on DOMDocument objects.


And lots of smaller improvements all over the place.


沒空翻譯了...請各位朋友自行參考 Changelog 。
時間越來越少，要學的東西越來越多....Orz 

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>這個月 (2007/07) 好像大家都說好了，我所關注的幾個專案都釋出了新版本。剛剛 <a href="http://www.phpunit.de/">PHPUnit</a> 也<a href="http://sebastian-bergmann.de/archives/682-PHPUnit-3.1.0.html">釋出了 3.1.0 版</a>，主要特色如下：</p>
<ul>
<li>
<p>Improvements to <a href="http://www.phpunit.de/pocket_guide/3.1/en/mock-objects.html">Mock Objects</a> include the ability to mock classes and methods that do not exist as  well as mocking static methods. And mocked methods can raise exceptions  now.</p>
</li>
<li>
<p><a href="http://www.phpunit.de/pocket_guide/3.1/en/selenium.html">PHPUnit's Selenium RC extension</a> no longer uses the Testing_Selenium PEAR package but its own implementation of the <a href="http://www.openqa.org/selenium-rc">Selenium RC</a> client/server protocol. Among other benefits, this allows for the collection of code coverage data for Selenium tests.</p>
</li>
<li>
<p>PHPUnit can now write test result and code coverage data to a <a href="http://www.phpunit.de/wiki/TestDatabase">test database</a>. Several <a href="http://www.phpunit.de/wiki/Ideas">ideas for future features</a> depend on this data.</p>
</li>
<li>
<p>New template methods, PHPUnit_Framework_TestCase::sharedAssertions(), PHPUnit_Framework_TestSuite::setUp(), PHPUnit_Framework_TestSuite::tearDown(), and PHPUnit_Extensions_SeleniumTestCase::defaultAssertions(), ease the development of test cases.</p>
</li>
<li>
<p>PHPUnit_Framework_TestCase::assertEquals() can now operate on DOMDocument objects.</p>
</li>
<li>
<p>And lots of smaller improvements all over the place.</p>
</li>
</ul>
<p>沒空翻譯了...請各位朋友自行參考 <a href="http://www.phpunit.de/wiki/ChangeLog#PHPUnit3.1.002-Jul-2007">Changelog</a> 。</p>
<p>時間越來越少，要學的東西越來越多....Orz </p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3567377.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3567377.html</guid>
	<category> PHP</category>
	<pubDate>Mon, 02 Jul 2007 21:02:18 +0800</pubDate>
</item>
<item>
	<title>整理最近 PHP5 的相關文章</title>
	<description><![CDATA[
	因為最近正在利用 Zend Framework 開發一個新專案，目標是減少開發的時間，也因此重新試著瞭解 PHP5 的一些特性。沒想到我對 PHP5 粗淺的見解，引出了兩位高手的熱烈討論及迴響，趁著工作閒暇整理一下：
阿土伯 (Racklin)

Iterators in PHP5
 Observer Pattern in PHP5
Get class name in static method ( Java and PHP )

石頭成 (Rocksaying) 

為什麼還不升級PHP5
Use ArrayObject and ArrayIterator to Overload Operators of Array
Stack - Example for Operators of Array Overload
PHP5 的個體導向能力問題 - magic methods 和 interface

註：我還真的是拋磚引玉呢。 XD 
如果對 PHP5 有興趣的朋友，以上文章務必要參考看看。
至於我個人所抱持的簡陋想法是： PHP 會走出屬於自己的一條路，它一定會隨著時間而進化的。 

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>因為最近正在利用 <a href="http://framework.zend.com">Zend Framework</a> 開發一個新專案，目標是減少開發的時間，也因此重新試著瞭解 PHP5 的一些特性。沒想到我對 PHP5 粗淺的見解，引出了兩位高手的熱烈討論及迴響，趁著工作閒暇整理一下：</p>
<h2><a href="http://racklin.blogspot.com/">阿土伯 (Racklin)</a></h2>
<ul>
<li><a href="http://racklin.blogspot.com/2007/06/iterators-in-php5.html">Iterators in PHP5</a></li>
<li> <a href="http://racklin.blogspot.com/2007/06/observer-pattern-in-php5.html">Observer Pattern in PHP5</a></li>
<li><a href="http://racklin.blogspot.com/search/label/PHP">Get class name in static method ( Java and PHP )</a></li>
</ul>
<h2><a href="http://blog.roodo.com/rocksaying/">石頭成 (Rocksaying)</a> </h2>
<ul>
<li><a href="http://blog.roodo.com/rocksaying/archives/3526951.html">為什麼還不升級PHP5</a></li>
<li><a href="http://blog.roodo.com/rocksaying/archives/3532653.html">Use ArrayObject and ArrayIterator to Overload Operators of Array</a></li>
<li><a href="http://blog.roodo.com/rocksaying/archives/3542135.html">Stack - Example for Operators of Array Overload</a></li>
<li><a href="http://blog.roodo.com/rocksaying/archives/3547207.html">PHP5 的個體導向能力問題 - magic methods 和 interface</a></li>
</ul>
<p class="note">註：我還真的是<a href="http://blog.roodo.com/jaceju/archives/3517185.html#comment-11054673">拋磚引玉</a>呢。 XD </p>
<p>如果對 PHP5 有興趣的朋友，以上文章務必要參考看看。</p>
<p>至於我個人所抱持的簡陋想法是： PHP 會走出屬於自己的一條路，它一定會隨著時間而進化的。 </p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3549457.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3549457.html</guid>
	<category> PHP</category>
	<pubDate>Fri, 29 Jun 2007 00:19:31 +0800</pubDate>
</item>
<item>
	<title>PHP5 將滿 4 歲</title>
	<description><![CDATA[
	PHP5 從 beta 版釋出，到今年的 6 月 29 日將滿 4 週年，也因此 Davey Shafik 感嘆大家為什麼還在使用 PHP4 ？
為什麼呢？就台灣目前的環境而言，我想到的原因有以下幾個：


很多舊的應用程式對 PHP5 這個執行環境感冒。 


承上一點，虛擬主機商多數不敢升級到 PHP5 。


很多 PHPer 不瞭解 PHP4 和 PHP5 的差異。


承上一點，台灣沒有作者敢寫 PHP5 的進階應用書籍。 (嗯...這有點用到激將法...) 


事實上，台灣不像歐洲對 PHP 有那麼多的愛好者，多數人學它只是因為簡單而且容易找到案子接。然而也是因為 PHP4 過於簡單，導致很多人常常不會深入去瞭解 PHP 這個 Script 語言，更別提 PHP5 所加入的物件導向特性對他們來說像是怪獸一樣可怕了。
老實說，我自己學習 PHP5 的時間也不過短短不到一年；這期間我督促自己儘可能地熟悉物件導向，並深入去摸索 PHP5 的進階用法。可是我自認自己還是有很多地方不甚瞭解，希望未來在眾多高手的指導下，能讓我自己對 PHP 這個工具有進一步的認識。 

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>PHP5 從 beta 版釋出，到今年的 6 月 29 日將滿 4 週年，也因此 <a href="http://pixelated-dreams.com/">Davey Shafik</a> <a href="http://pixelated-dreams.com/archives/305-Do-you-know-what-June-29th-is.html">感嘆</a>大家為什麼還在使用 PHP4 ？</p>
<p>為什麼呢？就台灣目前的環境而言，我想到的原因有以下幾個：</p>
<ul>
<li>
<p>很多舊的應用程式對 PHP5 這個執行環境感冒。 </p>
</li>
<li>
<p>承上一點，虛擬主機商多數不敢升級到 PHP5 。</p>
</li>
<li>
<p>很多 PHPer 不瞭解 PHP4 和 PHP5 的差異。</p>
</li>
<li>
<p>承上一點，台灣沒有作者敢寫 PHP5 的進階應用書籍。 (嗯...這有點用到激將法...) </p>
</li>
</ul>
<p>事實上，台灣不像歐洲對 PHP 有那麼多的愛好者，多數人學它只是因為簡單而且容易找到案子接。然而也是因為 PHP4 過於簡單，導致很多人常常不會深入去瞭解 PHP 這個 Script 語言，更別提 PHP5 所加入的物件導向特性對他們來說像是怪獸一樣可怕了。</p>
<p>老實說，我自己學習 PHP5 的時間也不過短短不到一年；這期間我督促自己儘可能地熟悉物件導向，並深入去摸索 PHP5 的進階用法。可是我自認自己還是有很多地方不甚瞭解，希望未來在眾多高手的指導下，能讓我自己對 PHP 這個工具有進一步的認識。 </p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3517185.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3517185.html</guid>
	<category> PHP</category>
	<pubDate>Sat, 23 Jun 2007 14:10:36 +0800</pubDate>
</item>
<item>
	<title>PHP 為什麼快？</title>
	<description><![CDATA[
	今天看到鳥毅老大提了一個問題：PHP 為什麼快呢？本來想回應在他的 Blog 上，不過寫一寫又太長了，就貼在自己家吧。
這個問題我想有一部份原因是因為大多數 PHP 函式是直接包裝 C 函式的關係吧？不然光只有 PHP Script 的話 (看看現在的框架應該可以略知一二) ，速度其實也就還好。所以很多時候會聽到一些 PHP 高手告誡我們：如果有現成的 PHP 函式能用的話就用，千萬不要自己寫一個。
另外 PHP 簡單我想也真的是一個重點，鳥毅老大是這麼說的：

Java Servlet或是ASP.Net的overhead都太重，因此對於簡單的需求時會像背著厚重殼的烏龜，PHP就像輕穎的兔子；這也是約耳喜歡ASP的原因吧？若只要寫個九九乘法表還需要寫一堆class、呼叫笨重的VM，實在不是聰明的做法。因此只需要在shell下執行的小程式，我也試著用不熟悉的駱駝文寫些簡單的script；只有在需要連結資料庫或做大量運算時才用Java。雖然qing老大強調OO帶來的overhead已經很小，可是我還是覺得有個肥VM的東西就是很慢呀... eg. Java v.s. VB6

例如一個 echo 指令，從解譯到把結果交給 Web Server ，其過程所要處理的東西遠比 ASP.NET 或是 Java Solution 來的少 (它們都需要類別或物件來處理) 。就算是已經編譯好的 opcode ，也是得透過 VM 來執行。而 PHP 有時候就直接利用 C 函式產生結果，然後丟給 Web Server 或 CLI 等 console ；因此少了一層包裝，其效能就好很多。 
換句話說： PHP 作弊。
 PHP 原本就是靠 Web Server Script 起家的，能儘量利用現有的東西來組合是它的特色也是優點。而現在 PHP5 及未來的版本為了兼顧維護與效能，就漸漸地靠攏 Java ，將物件導向特性包含進來；但是這只是讓 PHP5 語法在表面上看起來像是物件導向語言，但它骨子裡仍然不是。
註：昨天也和 Mark 討論到 (他希望我不要再叫他老大 XD ) ， 我們還是希望 PHP 不要完全變成 Java (|||orz 被騙得很慘)，而是要像 JavaScript (ECMAScript) ，這樣能結合靜態語言及動態語言的長處 (也許有人抱持和我不一樣的看法，歡迎指教) 。 
而提到了維護，傳統 PHP 寫法難以維護也是真的。因為沒有像 RoR 這種統一的架構，變成每個 PHPer 有自己一套開發風格；就算一律採用 Smarty 及 ADOdb 或其他類別庫來開發，每個 PHPer 在架構上的規劃還是大相逕庭。而且雖然現在 PHP 也有框架了，不過在選擇上也是令人眼花撩亂，更別提每個框架的開發者都認為自己的框架才是經過千錘百煉的。
註：大概很少有人會拿我書裡面的 Smarty 架構做專案吧？我個人強烈建議各位要自己修改，不要照抄 XD 。 
PHP 有它的優點，當然也有缺點；我想任何語言也是一樣的。其實我也不喜歡爭論孰優孰劣，只要這套語言仍是我所熟稔且能應用自如的話，那麼它就是我目前最佳的選擇了。 
以上是我個人的看法與經驗，不一定正確，希望各位先進能不吝指教。

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>今天看到<a href="http://blog.tenyi.com/">鳥毅</a>老大提了一個問題：<a href="http://blog.tenyi.com/2007/06/php.html">PHP 為什麼快呢？</a>本來想回應在他的 Blog 上，不過寫一寫又太長了，就貼在自己家吧。</p>
<p>這個問題我想有一部份原因是因為大多數 PHP 函式是直接包裝 C 函式的關係吧？不然光只有 PHP Script 的話 (看看現在的框架應該可以略知一二) ，速度其實也就還好。所以很多時候會聽到一些 PHP 高手告誡我們：如果有現成的 PHP 函式能用的話就用，千萬不要自己寫一個。</p>
<p>另外 PHP 簡單我想也真的是一個重點，鳥毅老大是這麼說的：</p>
<blockquote>
<p>Java Servlet或是ASP.Net的overhead都太重，因此對於簡單的需求時會像背著厚重殼的烏龜，PHP就像輕穎的兔子；這也是約耳喜歡ASP的原因吧？若只要寫個九九乘法表還需要寫一堆class、呼叫笨重的VM，實在不是聰明的做法。因此只需要在shell下執行的小程式，我也試著用不熟悉的駱駝文寫些簡單的script；只有在需要連結資料庫或做大量運算時才用Java。雖然qing老大強調OO帶來的overhead已經很小，可是我還是覺得有個肥VM的東西就是很慢呀... eg. Java v.s. VB6</p>
</blockquote>
<p>例如一個 echo 指令，從解譯到把結果交給 Web Server ，其過程所要處理的東西遠比 ASP.NET 或是 Java Solution 來的少 (它們都需要類別或物件來處理) 。就算是已經編譯好的 opcode ，也是得透過 VM 來執行。而 PHP 有時候就直接利用 C 函式產生結果，然後丟給 Web Server 或 CLI 等 console ；因此少了一層包裝，其效能就好很多。 </p>
<p>換句話說： <strong>PHP 作弊</strong>。</p>
<p> PHP 原本就是靠 Web Server Script 起家的，能儘量利用現有的東西來組合是它的特色也是優點。而現在 PHP5 及未來的版本為了兼顧維護與效能，就漸漸地靠攏 Java ，將物件導向特性包含進來；但是這只是讓 PHP5 語法在表面上看起來像是物件導向語言，但它骨子裡仍然不是。</p>
<p class="note">註：昨天也和 <a href="http://blog.markplace.net/">Mark</a> 討論到 (他希望我不要再叫他老大 XD ) ， 我們還是希望 PHP 不要<a href="http://mk.netgenes.org/archives/546/">完全變成 Java</a> (|||orz 被騙得很慘)，而是要像 JavaScript (<a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript</a>) ，這樣能結合靜態語言及動態語言的長處 (也許有人抱持和我不一樣的看法，歡迎指教) 。 </p>
<p>而提到了維護，傳統 PHP 寫法難以維護也是真的。因為沒有像 <a href="http://www.rubyonrails.org/">RoR</a> 這種統一的架構，變成每個 PHPer 有自己一套開發風格；就算一律採用 <a href="http://smarty.php.net">Smarty</a> 及 <a href="http://adodb.sourceforge.net/">ADOdb</a> 或其他類別庫來開發，每個 PHPer 在架構上的規劃還是大相逕庭。而且雖然現在 PHP 也有框架了，不過在選擇上也是令人眼花撩亂，更別提每個框架的開發者都認為自己的框架才是經過千錘百煉的。</p>
<p class="note">註：大概很少有人會拿我書裡面的 Smarty 架構做專案吧？我個人強烈建議各位要自己修改，不要照抄 XD 。 </p>
<p>PHP 有它的優點，當然也有缺點；我想任何語言也是一樣的。其實我也不喜歡爭論孰優孰劣，只要這套語言仍是我所熟稔且能應用自如的話，那麼它就是我目前最佳的選擇了。 </p>
<p>以上是我個人的看法與經驗，不一定正確，希望各位先進能不吝指教。</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3510483.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3510483.html</guid>
	<category> PHP</category>
	<pubDate>Fri, 22 Jun 2007 11:31:07 +0800</pubDate>
</item>
<item>
	<title>PHP 的 Application 層</title>
	<description><![CDATA[
	前陣子才在和 Mark 老大談論到 PHP 的 Application 層，今天就看到有人做出類似的東西了。
請參考： Application Server in PHP? well… Yes!
文章中主要是利用 SCGI 這個協定實作了一個 SCGI_Application 類別讓我們的應用程式繼承，這樣一來這個 Application 就會像是一個伺服器一樣，使用 9999 (預設值) 這個 Port 來回應 Web 伺服器所傳來的要求。這裡有範例可以參考，有興趣的朋友不妨試試。 
什麼是 Application 層呢？我們都知道 PHP 在處理每一次的用戶要求時，都會重頭執行一次 Script ，然後初始化每個物件 (像是資料庫、樣版引擎或是 Model 物件等等) 。當然在流量小的網站，這樣的方式並沒有什麼不妥；可是一旦流量變大時，每一次的初始化都會浪費掉些許的時間，而累積起來後就會造成伺服器的負擔。這種現象是因為 HTTP 協定的無狀態特性使然，對所有 Web 伺服端技術而言都是一樣的。 
所以如果能在 Web 伺服器的上面加上一個 Application 伺服器，就能夠利用這個伺服器所產生的抽象 Application 層來保存住這些常用物件。也就是說在 Application 層裡，我們會把共用物件常駐在記憶體裡，避免物件初始化所帶來的效能問題。例如在 Java Tomcat 或 Resin 裡，我們能把 Java Bean 持續地放在記憶體裡保持住它的狀態；如此一來在每一個要求裡所看到的 Bean 物件其實就是同一個。
然而目前 PHP 因為沒有類似 Tomcat 這種 Application Server 來協助儲存狀態，所以在效能上就會有所折扣；也因此 PHP 才會有 APC 或是 eAccelerator 這樣的加速器。
註：雖然 Resin 目前也支援 PHP ，不過據 Mark 老大的說法，在 Resin 使用 PHP 就必須採用 JDBC 來連接資料庫。這樣的話，實用價值似乎不大。 它在資料庫存取時會利用 JDBC 來模擬，使 PHP 原有的存取方式可以不用更動。
除了加速器外，在 PHP 中我們倒是有另一種保持物件的方式，那就是快取。一般來說，我們會利用序列化 (serialize) 把物件狀態存在檔案或記憶體裡，比較常見的方式就是利用 memcached 。不過就算使用了快取，我們其實還是得在 PHP 程式裡初始化相關的快取物件才行，這又回到了前面所談到的問題。
註：當然加速器也是另一種形態的快取，不過對儲存物件狀態的功效不大。
另外在 MVC 框架中，會嚴重影響效能的物件就是 Controller 了。像 Front Controller 就必須判斷使用者的要求，進一步將這些要求分派 (dispatch) 到特定的 Action Controller 裡，而每一次的分派其實就會佔用掉大部份的執行時間。所以如果 Controller 能夠編譯並結合到 Application 伺服器的話，那麼程式執行的效率就會進一步的提升。
當然我本身是希望 PHP 也能提供這樣的機制，或是將 Zend Framework 變成一個 PHP 的 extension 。嗯，也許我是痴人說夢，但我衷心希望有這一天的來臨。 

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>前陣子才在和 <a href="http://blog.markplace.net/">Mark</a> 老大談論到 PHP 的 Application 層，今天就看到有人做出類似的東西了。</p>
<p>請參考： <a href="http://blog.milkfarmsoft.com/?p=51">Application Server in PHP? well… Yes!</a></p>
<p>文章中主要是利用 <a href="http://python.ca/scgi/">SCGI</a> 這個協定實作了一個 SCGI_Application 類別讓我們的應用程式繼承，這樣一來這個 Application 就會像是一個伺服器一樣，使用 9999 (預設值) 這個 Port 來回應 Web 伺服器所傳來的要求。這裡有<a href="http://appserver-in-php.googlecode.com/svn/trunk/examples/scgi/">範例</a>可以參考，有興趣的朋友不妨試試。 </p>
<p>什麼是 Application 層呢？我們都知道 PHP 在處理每一次的用戶要求時，都會重頭執行一次 Script ，然後初始化每個物件 (像是資料庫、樣版引擎或是 Model 物件等等) 。當然在流量小的網站，這樣的方式並沒有什麼不妥；可是一旦流量變大時，每一次的初始化都會浪費掉些許的時間，而累積起來後就會造成伺服器的負擔。這種現象是因為 HTTP 協定的無狀態特性使然，對所有 Web 伺服端技術而言都是一樣的。 </p>
<p>所以如果能在 Web 伺服器的上面加上一個 Application 伺服器，就能夠利用這個伺服器所產生的抽象 Application 層來保存住這些常用物件。也就是說在 Application 層裡，我們會把共用物件常駐在記憶體裡，避免物件初始化所帶來的效能問題。例如在 Java Tomcat 或 <a href="http://www.caucho.com/resin-3.0/">Resin</a> 裡，我們能把 Java Bean 持續地放在記憶體裡保持住它的狀態；如此一來在每一個要求裡所看到的 Bean 物件其實就是同一個。</p>
<p>然而目前 PHP 因為沒有類似 <a href="http://tomcat.apache.org/">Tomcat</a> 這種 Application Server 來協助儲存狀態，所以在效能上就會有所折扣；也因此 PHP 才會有 <a href="http://pecl.php.net/package/APC">APC</a> 或是 <a href="http://eaccelerator.net/">eAccelerator</a> 這樣的加速器。</p>
<p class="note">註：<del>雖然</del> Resin 目前也<a href="http://www.caucho.com/resin-3.1/doc/quercus-resin-module.xtp">支援 PHP</a> ，<del>不過據 Mark 老大的說法，在 Resin 使用 PHP 就必須採用 JDBC 來連接資料庫。這樣的話，實用價值似乎不大。</del> 它在資料庫存取時會利用 JDBC 來模擬，使 PHP 原有的存取方式可以不用更動。</p>
<p>除了加速器外，在 PHP 中我們倒是有另一種保持物件的方式，那就是快取。一般來說，我們會利用序列化 (<a href="http://www.php.net/manual/en/function.serialize.php">serialize</a>) 把物件狀態存在檔案或記憶體裡，比較常見的方式就是利用 <a href="http://danga.com/memcached/">memcached</a> 。不過就算使用了快取，我們其實還是得在 PHP 程式裡初始化相關的快取物件才行，這又回到了前面所談到的問題。</p>
<p class="note">註：當然加速器也是另一種形態的快取，不過對儲存物件狀態的功效不大。</p>
<p>另外在 MVC 框架中，會嚴重影響效能的物件就是 Controller 了。像 Front Controller 就必須判斷使用者的要求，進一步將這些要求分派 (dispatch) 到特定的 Action Controller 裡，而每一次的分派其實就會佔用掉大部份的執行時間。所以如果 Controller 能夠編譯並結合到 Application 伺服器的話，那麼程式執行的效率就會進一步的提升。</p>
<p>當然我本身是希望 PHP 也能提供這樣的機制，或是將 Zend Framework 變成一個 PHP 的 extension 。嗯，也許我是痴人說夢，但我衷心希望有這一天的來臨。 </p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3496701.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3496701.html</guid>
	<category> PHP</category>
	<pubDate>Tue, 19 Jun 2007 23:00:05 +0800</pubDate>
</item>
<item>
	<title>Xinc - 整合 PHP 測試、發佈的工具</title>
	<description><![CDATA[
	Xinc 主要是整合 Subversion 、 PHPUnit 、 Phing 這三個工具，成為一個 PHP5  的Continuous Integration System 。簡單來說應該有點像是 Java 的 Ant ，可以協助我們在散佈套件時，進行自動化的安裝與測試。(應該吧？) 
不過我對類似的工具不甚瞭解 (畢竟我很少開發 Java 的案子) ，還望有經驗的先進能不吝指導。



	]]>
	</description>
	<content:encoded><![CDATA[
	<p><a href="http://xinc.entrypoint.biz/">Xinc</a> 主要是整合 Subversion 、 PHPUnit 、 Phing 這三個工具，成為一個 PHP5  的<a href="http://www.martinfowler.com/articles/continuousIntegration.html">Continuous Integration System</a> 。簡單來說應該有點像是 Java 的 Ant ，可以協助我們在散佈套件時，進行自動化的安裝與測試。(應該吧？) </p>
<p>不過我對類似的工具不甚瞭解 (畢竟我很少開發 Java 的案子) ，還望有經驗的先進能不吝指導。</p>


		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3452613.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3452613.html</guid>
	<category> PHP</category>
	<pubDate>Mon, 11 Jun 2007 23:05:22 +0800</pubDate>
</item>
<item>
	<title>PHPUnit 教學研討會 (國外)</title>
	<description><![CDATA[
	PHPUnit Tutorial @ ZendCon 2007
Sebastian Bergmann 老大親自授課...一整個就是好想去...|||orz
	]]>
	</description>
	<content:encoded><![CDATA[
	<p><a href="http://www.zendcon.com/schedule_tutorials.php">PHPUnit Tutorial @ ZendCon 2007</a></p>
<p><a href="http://sebastian-bergmann.de/">Sebastian Bergmann</a> 老大親自授課...一整個就是好想去...|||orz</p>		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3344359.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3344359.html</guid>
	<category> PHP</category>
	<pubDate>Fri, 25 May 2007 10:13:52 +0800</pubDate>
</item>
<item>
	<title>Anonymous functions in PHP</title>
	<description><![CDATA[
	國外最近有一些 PHP 的技術研討會，其中比較有趣的一項是：匿名函式 (Anonymous functions) 。
範例如下：
$data = array("zoo", "orange", "car", "lemon", "apple");
usort($data, function($a, $b) { return strcmp($a, $b); });
var_dump($data); # data is sorted alphabetically
沒錯，看起來就像是 JavaScript 的 function literal 。
這樣一來我們可以不需要再使用 create_function ，也不必撰寫有一堆跳脫字元的醜陋函式字串，而能更直覺地寫出我們所需要的函式。文章裡還有其他關於匿名函式的範例，有興趣的朋友可以自行參考。
話雖如此，到底哪個版本才會有這麼好用的東西出來呀？我的 mixin 類別正在找尋歸屬說。 
註：不過裡面 Jim Wilson 也提到了 (就是 Smarty 網站上常看到的  boots 本人) ，匿名函式和 closure (石頭成老大譯：封絕) 是完全不一樣的東西，而 PHP 是不需要 closure 的。可是為什麼我看感覺是一樣的？唉...大概是我等級還不夠，看不出什麼所以然吧？

	]]>
	</description>
	<content:encoded><![CDATA[
	<p>國外最近有一些 PHP 的<a href="http://www.php.net/conferences/">技術研討會</a>，其中比較有趣的一項是：<a href="http://devzone.zend.com/node/view/id/2013">匿名函式</a> (Anonymous functions) 。</p>
<p>範例如下：</p>
<pre><code><span style="color: rgb(0, 0, 0);"><span style="color: rgb(0, 0, 187);">$data </span><span style="color: rgb(0, 119, 0);">= array(</span><span style="color: rgb(221, 0, 0);">"zoo"</span><span style="color: rgb(0, 119, 0);">, </span><span style="color: rgb(221, 0, 0);">"orange"</span><span style="color: rgb(0, 119, 0);">, </span><span style="color: rgb(221, 0, 0);">"car"</span><span style="color: rgb(0, 119, 0);">, </span><span style="color: rgb(221, 0, 0);">"lemon"</span><span style="color: rgb(0, 119, 0);">, </span><span style="color: rgb(221, 0, 0);">"apple"</span><span style="color: rgb(0, 119, 0);">);
</span><span style="color: rgb(0, 0, 187);"><a href="/manual/view/page/function.usort.html">usort</a></span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$data</span><span style="color: rgb(0, 119, 0);">, function(</span><span style="color: rgb(0, 0, 187);">$a</span><span style="color: rgb(0, 119, 0);">, </span><span style="color: rgb(0, 0, 187);">$b</span><span style="color: rgb(0, 119, 0);">) { return </span><span style="color: rgb(0, 0, 187);"><a href="/manual/view/page/function.strcmp.html">strcmp</a></span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$a</span><span style="color: rgb(0, 119, 0);">, </span><span style="color: rgb(0, 0, 187);">$b</span><span style="color: rgb(0, 119, 0);">); });
</span><span style="color: rgb(0, 0, 187);"><a href="/manual/view/page/function.var-dump.html">var_dump</a></span><span style="color: rgb(0, 119, 0);">(</span><span style="color: rgb(0, 0, 187);">$data</span><span style="color: rgb(0, 119, 0);">); </span><span style="color: rgb(255, 128, 0);"># data is sorted alphabetically</span></span></code></pre>
<p>沒錯，看起來就像是 JavaScript 的 function literal 。</p>
<p>這樣一來我們可以不需要再使用 <a href="http://www.php.net/en/create_function">create_function</a> ，也不必撰寫有一堆跳脫字元的醜陋函式字串，而能更直覺地寫出我們所需要的函式。文章裡還有其他關於匿名函式的範例，有興趣的朋友可以自行參考。</p>
<p>話雖如此，到底哪個版本才會有這麼好用的東西出來呀？我的 mixin 類別正在找尋歸屬說。 </p>
<p class="note">註：不過裡面 Jim Wilson 也提到了 (就是 Smarty 網站上常看到的  boots 本人) ，匿名函式和 <a href="http://www.javaworld.com.tw/roller/page/ingramchen?entry=2007_1_1_WhyAddClosureInJava7">closure</a> (<a href="http://blog.roodo.com/rocksaying/">石頭成</a>老大譯：<a href="http://blog.roodo.com/rocksaying/archives/3088893.html">封絕</a>) 是完全不一樣的東西，而 PHP 是不需要 closure 的。可是為什麼我看感覺是一樣的？唉...大概是我等級還不夠，看不出什麼所以然吧？</p>
		]]>
	</content:encoded>
	<link>http://blog.roodo.com/jaceju/archives/3274481.html</link>
	<guid>http://blog.roodo.com/jaceju/archives/3274481.html</guid>
	<category> PHP</category>
	<pubDate>Sat, 19 May 2007 11:18:39 +0800</pubDate>
</item>
<item>
	<title>我也來實作 PHP mix-in 的概念 - Part 3</title>
	<description><![CDATA[
	說明
石頭成老大把他心目中的 mix-in 目標做出來了，他主要的實作有以下兩個重點：

物件實體生成後彼此做 mix-in 是不相干的。
類別方法在動態委派後要能遵守繼承原則，也就是說子承父、父不承子。

另外他也提到要儲存方法是一件困難的事情，因為 PHP 有三種函式的呼叫方法：一般函數、類別靜態方法、實例方法。而我在 Part 2 裡的概念實作則是用 callback 虛擬型態來儲存，不過卻忘了把一般函式給放進去。
不過 Part 2 的實作已經實現了第一個目標，所以在這次 Part 3 的實作裡，我除了決定把一般函式也納入 mix-in 的實作裡，而且還要達成石頭成老大說的第二個目標。
另外我自己也加入了以下實作重點：

所有動作都要在 Prototype 類別裡處理掉，不能夠讓子類別除了自己的工作外，還得實作不必要的部份。 (石頭成老大抱歉啦~我是覺得 $methods 不應該在子類別再次定義。)
Prototype&nbsp; 所延伸的子類別，不能覆蓋 Prototype 類別裡的任何方法，以避免功能出錯。
使用的方法要夠簡單，儘量不要讓使用者感到困惑。

經過一番努力，我終於試出來了；先來看看成果，後面我才來一一分析。

	]]>
	</description>
	<content:encoded><![CDATA[
	<h2>說明</h2>
<p>石頭成老大把他心目中的 mix-in <a href="http://blog.roodo.com/rocksaying/archives/2884871.html" title="目標">目標</a>做出來了，他主要的實作有以下兩個重點：</p>
<ol>
<li>物件實體生成後彼此做 mix-in 是不相干的。</li>
<li>類別方法在動態委派後要能遵守繼承原則，也就是說子承父、父不承子。</li>
</ol>
<p>另外他也提到要儲存方法是一件困難的事情，因為 PHP 有三種函式的呼叫方法：一般函數、類別靜態方法、實例方法。而我在 <a href="http://blog.roodo.com/jaceju/archives/2853761.html">Part 2</a> 裡的概念實作則是用 <a href="http://blog.roodo.com/jaceju/archives/409709.html" title="callback">callback</a> 虛擬型態來儲存，不過<a href="http://blog.roodo.com/jaceju/archives/2853761.html" title="前一版"></a>卻忘了把一般函式給放進去。</p>
<p>不過 Part 2 的實作已經實現了第一個目標，所以在這次 Part 3 的實作裡，我除了決定把一般函式也納入 mix-in 的實作裡，而且還要達成石頭成老大