在物件導向設計中,Proxy(代理)模式是 GoF 23 種經典設計模式之一。其核心目的是「為其他物件提供一種代理以控制對這個物件的存取」。常見的代理類型包括遠程代理、虛擬代理、保護代理與智慧代理等。然而,傳統代理模式要求開發者手動建立每個被代理類別的對應代理類別,這無疑增加了維護成本。
本文將示範如何在 PHP 4 環境下實現動態代理,利用 PHP 4 的解釋執行、弱型別與無介面特性,透過臨時檔案生成方式,自動產生代理類別。文中提供的程式碼僅為原型實作,歡迎各位高手指教。
實作策略相當簡單,核心包含兩個類別:
此外定義了 ProxyInvocationHandler 介面(實為基底類別),所有具體代理處理器需繼承此類別並實作 Invoke() 方法。
假設我們有一個 ReadFileClass 負責讀取檔案,現在需要加入使用者驗證功能,僅允許特定使用者存取。
class IReadFileClass {
function ReadMyFile() {}
}
class ReadFileClass extends IReadFileClass {
function ReadMyFile() {
$fp = fopen('test.txt', "r");
$data = fread($fp, filesize('test.txt'));
fclose($fp);
return $data;
}
}
require_once('ProxyFactory.php');
require_once('ProxyInvocationHandler.php');
require_once('Auth.php');
class ReadFileClassProxy extends ProxyInvocationHandler {
var $object;
function ReadFileClassProxy(&$obj) {
$this->object = &$obj;
}
function NewInstance(&$obj) {
$proxyFactory = ProxyFactoryInstance();
return $proxyFactory->create(new ReadFileClassProxy(&$obj), get_parent_class(&$obj));
}
function Invoke(&$proxy, $method, $parameters) {
$uname = 'Binzy'; // 測試用,實際應從 Session 取得
if (Auth::CheckAuth($uname)) {
return parent::Invoke(&$proxy, $method, $parameters);
} else {
return 'No Permission!';
}
}
}
class Auth {
function CheckAuth($username) {
return ($username == 'Binzy');
}
}
require_once('ReadFileClass.php');
require_once('ReadFileClassProxy.php');
$proxy = ReadFileClassProxy::NewInstance(new ReadFileClass());
print $proxy->ReadMyFile();
透過動態代理,我們避免了手撰代理類別的繁瑣工作,同時將横切關注點(如權限檢查、日誌記錄)與核心業務邏輯解耦。雖然 PHP 4 缺乏正式介面與反射機制,但利用其動態特性仍能實現可用的動態代理原型。PHP 5 以上版本建議使用內建的 Reflection API 與介面機制進行更優雅的實作。