看了網路上幾個用PHP 實現單例模式的作法,我寫了一個比較簡單且通用的方式。
程式碼如下:
class _單例{
final protected function __construct(&$類別, &$參數){
$函數 = array($this, '_' . $類別);
if (is_callable($函數)) {
call_user_func_array($函數, $參數);
}
}
static function &實例($類別, $參數 = array()){
static $實例 = array();
$類別 = (string)$類別;
$輸出 = &$實例[$類別];
if(false == isset($輸出) || false == is_object($輸出)){
$輸出 = new $類別($類別, $參數);
}
return $輸出;
}
}
用法範例:
class 測試 extends _單例 implements __單例{
protected function _測試($數字, $字串){
echo (string)$數字, $字串, '<br>';
}
static function &實例(){
$參數 = func_get_args();
return parent::實例(get_class(), $參數);
}
}
$結果 = 測試::實例(5, 'years');
2008/03/28
2008/03/24
2008/03/20
插件用的類別
話說 adodb lite 這套資料庫用的類別庫已經很久沒更新了,這套小巧的類別庫有一個很棒的功能,就是可以寫插件。
不過他寫插件的方法我不是很喜歡,正好我最近開始復工在寫的DB Layer也碰到這樣的難題:我想把功能分割出來,寫成可以共同合作的插件,需要什麼功能可以隨時加進去。
另外還有一點,我希望使用插件的方法時,其調用的方式等同於使用繼承的方式。
因此,我利用了PHP5的反映的功能,寫了一個插件用的抽象類別,只要繼承它,就可以簡單的寫出插件以及加入插件的功能。
底下是程式碼:
abstract class _插件{
private $_插件 = array();
private $_方法 = array();
function __construct(&$插件 = null){
$this->__插件($插件);
}
final function &__call($方法, $參數){
$輸出 = call_user_func_array($this->_方法[$方法], $參數);
return $輸出;
}
// 將插件的方法加入$this的方法中
final protected function __插件(&$插件, $更新 = false){
static $類別 = __CLASS__;
if (false == ($插件 instanceof $類別)) {
return false;
}
$名稱 = get_class($插件);
if (false == array_key_exists($名稱, $this->_插件)){
$this->_插件[$名稱] = $插件;
$this->_方法 = $插件->__方法() + $this->_方法;
}
if ($更新) {
$this->__更新();
}
return true;
}
// 取得插件的方法(除了方法名稱開頭是'__'的方法,例如一些魔術方法,或是方法本身屬於私有方法)
final protected function &__方法(){
static $_方法 = array();
if (false == empty($_方法)) {
return $_方法;
}
$反映 = new ReflectionObject($this);
$方法 = $反映->getMethods();
unset($反映);
foreach ($方法 as &$項目){
$名稱 = &$項目->getName();
if (0 !== strpos($名稱, '__') && false == $項目->isPrivate()) {
$_方法[$名稱] = array($this, $名稱);
}
}
return $_方法;
}
// 更新所有插件的方法,通常在載入所有插件後調用
final protected function __更新(){
static $類別 = __CLASS__;
foreach ($this->_插件 as &$插件){
if($插件 instanceof $類別) {
$插件->_方法 = $this->_方法 + $插件->_方法;
}
}
}
}
這裡有個簡單的範例。
plugin.php => 插件的抽象類別檔案 (內容就是上述的抽象類別)
test01.php => 主要的範例檔案
test02.php => 插件的檔案
test01.php:
require_once dirname(__FILE__) . '/plugin.php';
class 測試 extends _插件 {
function 載入插件($插件) {
require_once dirname(__FILE__) . "/$插件.php";
$類別 = __CLASS__ . ":$插件";
$插件 = new $類別($this);
$this->__插件($插件);
}
protected function 測試01(){
echo __METHOD__ , '<br>';
}
}
$測試 = new 測試();
$測試->載入插件('test02');
$測試->測試02();
test02.php:
require_once dirname(__FILE__) . '/test01.php';
class 測試:test02 extends _插件 {
function 測試02(){
echo __METHOD__ , '<br>';
$this->測試01();
}
}
不過他寫插件的方法我不是很喜歡,正好我最近開始復工在寫的DB Layer也碰到這樣的難題:我想把功能分割出來,寫成可以共同合作的插件,需要什麼功能可以隨時加進去。
另外還有一點,我希望使用插件的方法時,其調用的方式等同於使用繼承的方式。
因此,我利用了PHP5的反映的功能,寫了一個插件用的抽象類別,只要繼承它,就可以簡單的寫出插件以及加入插件的功能。
底下是程式碼:
abstract class _插件{
private $_插件 = array();
private $_方法 = array();
function __construct(&$插件 = null){
$this->__插件($插件);
}
final function &__call($方法, $參數){
$輸出 = call_user_func_array($this->_方法[$方法], $參數);
return $輸出;
}
// 將插件的方法加入$this的方法中
final protected function __插件(&$插件, $更新 = false){
static $類別 = __CLASS__;
if (false == ($插件 instanceof $類別)) {
return false;
}
$名稱 = get_class($插件);
if (false == array_key_exists($名稱, $this->_插件)){
$this->_插件[$名稱] = $插件;
$this->_方法 = $插件->__方法() + $this->_方法;
}
if ($更新) {
$this->__更新();
}
return true;
}
// 取得插件的方法(除了方法名稱開頭是'__'的方法,例如一些魔術方法,或是方法本身屬於私有方法)
final protected function &__方法(){
static $_方法 = array();
if (false == empty($_方法)) {
return $_方法;
}
$反映 = new ReflectionObject($this);
$方法 = $反映->getMethods();
unset($反映);
foreach ($方法 as &$項目){
$名稱 = &$項目->getName();
if (0 !== strpos($名稱, '__') && false == $項目->isPrivate()) {
$_方法[$名稱] = array($this, $名稱);
}
}
return $_方法;
}
// 更新所有插件的方法,通常在載入所有插件後調用
final protected function __更新(){
static $類別 = __CLASS__;
foreach ($this->_插件 as &$插件){
if($插件 instanceof $類別) {
$插件->_方法 = $this->_方法 + $插件->_方法;
}
}
}
}
這裡有個簡單的範例。
plugin.php => 插件的抽象類別檔案 (內容就是上述的抽象類別)
test01.php => 主要的範例檔案
test02.php => 插件的檔案
test01.php:
require_once dirname(__FILE__) . '/plugin.php';
class 測試 extends _插件 {
function 載入插件($插件) {
require_once dirname(__FILE__) . "/$插件.php";
$類別 = __CLASS__ . ":$插件";
$插件 = new $類別($this);
$this->__插件($插件);
}
protected function 測試01(){
echo __METHOD__ , '<br>';
}
}
$測試 = new 測試();
$測試->載入插件('test02');
$測試->測試02();
test02.php:
require_once dirname(__FILE__) . '/test01.php';
class 測試:test02 extends _插件 {
function 測試02(){
echo __METHOD__ , '<br>';
$this->測試01();
}
}
str_replace 和 strtr不同的地方
一直以來以為這兩個函數是等價的,今天心血來潮想知道這兩者在效率上有何差異,所以在網路上找了一些文章來看。
找到了CSDN的這篇討論,裡面說的很清楚,這兩個函數還是存在微妙的差異:在strtr中的替換字串長度必須等長,輸出結果才會與str_replace等價。
找到了CSDN的這篇討論,裡面說的很清楚,這兩個函數還是存在微妙的差異:在strtr中的替換字串長度必須等長,輸出結果才會與str_replace等價。
2008/03/03
PHP 檔案管理記要
今天早上我老闆問了我一個問題:為何他在IE裡面下載SugarCRM裡面有中文檔名的檔案,存檔時帶出的檔名變成亂碼?
我直覺的覺得是IE和FF對於header的解釋不同的緣故,因為上禮拜我把SugarCRM裝好後有試過,在FF中是正常的~
為了證實我的想法,我把SugarCRM的download.php翻出來看了一下,果然發現一段if else的結構證明我的想法沒錯,兩種瀏覽器對於檔名解碼的方式有不同的作法。
現在重點來了,怎麼做才是正確的?
開了MSN問了一下Kiang的意見,他給了一個提示:urlencode。
我試了幾種方式,發現在檔名編碼的部份,IE應該用urlencode,FF應該用mb_encode_mimeheader的QP編碼方式來處理檔名的部份。
因此,既然可以完成檔名的處理,我接著觀察SugarCRM處理檔案的方式。
很顯然的,他把上傳後的檔案「暗號化」,然後以此「暗號」查詢資料庫,取得原始的檔名,而上述的download.php則是統一的取檔窗口,這樣的方式便可以避免去處理Windows的檔案系統編碼的問題。
我認為這是個很好的方式,記起來以免忘記~
我直覺的覺得是IE和FF對於header的解釋不同的緣故,因為上禮拜我把SugarCRM裝好後有試過,在FF中是正常的~
為了證實我的想法,我把SugarCRM的download.php翻出來看了一下,果然發現一段if else的結構證明我的想法沒錯,兩種瀏覽器對於檔名解碼的方式有不同的作法。
現在重點來了,怎麼做才是正確的?
開了MSN問了一下Kiang的意見,他給了一個提示:urlencode。
我試了幾種方式,發現在檔名編碼的部份,IE應該用urlencode,FF應該用mb_encode_mimeheader的QP編碼方式來處理檔名的部份。
因此,既然可以完成檔名的處理,我接著觀察SugarCRM處理檔案的方式。
很顯然的,他把上傳後的檔案「暗號化」,然後以此「暗號」查詢資料庫,取得原始的檔名,而上述的download.php則是統一的取檔窗口,這樣的方式便可以避免去處理Windows的檔案系統編碼的問題。
我認為這是個很好的方式,記起來以免忘記~
訂閱:
文章 (Atom)