2008年04月26日
[Web] 連結分享
最近忙著一個大案子,所以這篇的發佈時間就 Delay 了,而且以後貼網摘的時間也可能會越拉越長...Orz
另外我想說...Oracle 的表格、欄位名稱都要大寫這個限制實在很鳥...不曉得有沒有哪位 Oracle 前輩能指點一二?
PHP
-
Testing PHP/MySQL Applications with PHPUnit/DbUnit
PHPUnit 有個 DBUnit 的 extension ,主要是建立一個假的 DB 連線,然後假資料放在 XML ,然後再寫測試。
-
Developing a Modular Class For a PHP File Uploader
處理檔案上傳的類別。
-
如果不想透過別人寫好的 Library 用 YouTube ,這個方式可以參考看看。
Zend Framework
-
Front Controller Plugins in Zend Framework
這篇很詳細地介紹了 Zend Framework 的 Front Controller Plugin ,另外那個 Cache 的 Plugin 滿具有參考價值的。
-
Custom validators for Zend_Form_Element
自訂驗證器。
-
Zend Framework Articles and Tutorials
收集了兩篇文章。
-
Integrating Zend Framework and Doctrine
Doctrine 是個 ORM 套件。
-
Managing 404 errors in the Zend Framework
這篇教你如何用 ZF 處理 404 狀態。
JavaScript
-
第一次知道 uneval 這個函式,它會回傳一個建構物件用的敘述語法。
-
比較各家瀏覽器在處理元素迭代迴圈上的效能。
-
Embedding and Encoding in JavaScript
將圖片或聲音資源嵌入 JavaScript 中。
-
Dromaeo: JavaScript Performance Testing
這個是用來測試瀏覽器 JavaScript 各種演算法效能的服務。
-
Javascript: Introducing Using (.js)
這個 using 函式可以幫我們動態載入 JavaScript 。
-
Delegating the focus and blur events - example
不同瀏覽器在偵測 focus 和 blur 事件的做法。
-
60 More AJAX- and Javascript Solutions For Professional Coding
提供六十幾種 Ajax 特效。
-
A quick update on Really Simple History
RSH 是用來補足 Ajax 在瀏覽器 History 不足之處的套件。
-
Do you validate your JavaScript code?
JSLint 是一個用來驗證 JavaScript 程式碼的服務,類似 W3C 的 HTML 驗證器。
-
A quick Javascript formatting tip
這篇主要是談編碼格式...我覺得團隊裡習慣一個固定方式就好了。
-
Three Helpful JavaScript Libraries
三個還滿有用的 JavaScript 套件,分別是 Fluently 、 MOP JS 及 Collections JS 。
jQuery
-
加強很多小地方,很棒!
-
這個是有滑動效果的選單套件。
-
教你怎麼開發 jQuery 的 plugin 。
-
Some Updated and Improved Examples
幾個還不錯的 jQuery 範例。
-
Spoiler Revealer from CSS-Tricks
按一下會顯示更多訊息的效果。
-
Textbox to accept only numbers (digits) using jquery
讓文字欄位只能接受數字的效果。
CSS
-
CSS do's and dont's Part 1: CSS Selecting
CSS 在選擇器上一些注意事項,像是命名要表現出目的而不是呈現形式。
Database
MySQL
-
Sun Microsystems Announces MySQL 5.1
Sub 發佈 MySQL 5.1
Other
Software
-
IE7 easy removal tool - 完整移除IE7
用 IE7 自己的移除程式會有一些問題,這時要就借用這個工具來移除。
Programming
SCM
-
工作複本的 .svn 其實刪掉沒關係的,不會影響伺服端的 Repository 。
-
Recursively delete .svn directories
跟上面那篇一樣的目的,只是改在 Linux 上實現。
Web
Design
-
Icons, Icons, Icons… for free!
又是一堆免費的圖示,棒!
-
Web Form Design: Modern Solutions and Creative Ideas
如何設計漂亮的表單。
-
Multilevel Drop Down Navigation Menus: Examples and Tutorials
多層下拉選單設計的範例與教學。
2008年04月22日
2008年04月18日
Bridge 模式的簡單範例
今天跟 tokimeki 討論到 Bridge 模式的例子,這裡我做個簡單的記錄。
假設我們有軟體 (Software) 會透過印表機 (Printer) 做列印輸出,而軟體有文字編輯器 (輸出文字) 與影像編輯器 (輸出影像) ,然後印表機則可分做雷射、噴墨及點陣式。
在設計上,這兩者分別屬於不同的繼承體系,而 Software 又會利用到 Printer ,因此它們形成了 Bridge 關係,如下圖:

把幾個討論心得整理如下:
-
由於 Printer 抽象類封裝了實作上的不同,因此 Software 的子類別不必瞭解 Printer 的實作,它們只要知道 Printer 提供哪些介面可用。這些子類別會依照自己的需要來組合這些介面,就算用不到也沒關係。
-
在 Software 的子類別可以加入屬於自己的特殊方法,因為它們通常會依場合直接被調用,所以這裡並不需要遵守 LSP 原則。
-
對 Printer 體系來說,它不需要知道誰來用它;但反過來 Software 體系因為需要 Printer 體系的支援,所以形成了單向耦合。
-
對 Software 體系來說,只要它需要新的功能,而且這些功能所需要的實作也在 Printer 抽象類支援的範圍之內,那麼它就可以直接調用 Printer 物件的方法,而不會影響 Printer 體系。
-
如果 Software 體系需要的新功能是 Printer 抽象類所沒有的,那麼還是可以在 Printer 抽象類裡加入新的方法,而實作則交給 Printer 的子類別完成。這樣的修改完全不會影響 Software 體系。
更詳細的 Bridge 模式介紹可參考良葛格的「 Design Pattern: Bridge 模式」一文。
2008年04月17日
2008年04月15日
2008年04月14日
[PHP] 在 PHP5 中實作 AOP 的概念
這篇積在我電腦裡很久了,一直沒公開...這次趁著要幫我的 Library 加料,順便拿出來分享一下心得。
什麼是 AOP
AOP 全名為 Aspect-Oriented Programming ,基本的觀念可以參考良葛格的 AOP 入門:
這裡我簡單提一下 AOP 的基本想法:
假設當我們呼叫物件的某些方法 (或是業務流程) 之後,會想要把相關的資訊記錄到 log 檔裡,我們也許會這樣寫:
<?php
/**
* Test
*/
class Test
{
/**
* 某個方法
*/
public function doSomething()
{
// 建立 Log 物件
$logger = new Log();
// 寫入前置 Log
$logger->save('before do something.');
// 主要的動作
// ...
// 寫入 Log
$logger->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
首先我們來看看還沒有切入任何事件的目標類別:
<?php
/**
* Test class
*
*/
class TestClass
{
/**
* Method 1
*
* @param string $message
*/
public function method1($message)
{
echo "\n", __METHOD__, ":\n", $message, "\n";
}
/**
* Method 2
*
* @return int
*/
public function method2()
{
echo "\n", __METHOD__, ":\n";
return rand(1, 10);
}
/**
* Method 3
*
* @throws Exception
*/
public function method3()
{
echo "\n", __METHOD__, ":\n";
throw new Exception('Test Exception.');
}
}
這個類別提供了三個方法,其中 method1 和 method2 只是簡單的顯示資料而已,而 method3 則會丟出一個異常。
另外我們需要一個 Log 類別:
<?php
/**
* Log
*
*/
class Log
{
/**
* log message
*
* @param string $message
*/
public function save($message)
{
echo $message, "\n";
}
}
這個 Log 類別只提供一個 save() 方法,以顯示 log 訊息。
現在我們要完成的目標如下:
-
在 method1 執行前呼叫 Log::save() 。
-
在 method2 執行後呼叫 Log::save() 。
-
在 method3 發生異常時呼叫 Log::save() 。
這裡我用很簡單的方式來做,那就是直接使用一個 Aspect 類別:
<?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->_target = $target;
$this->_className = get_class($this->_target);
}
}
/**
* Register event
*
* @param string $eventName
* @param string $methodName
* @param callback $callback
*/
private function _registerEvent($eventName, $methodName, $callback, $args)
{
if (!isset($this->_eventCallbacks[$methodName])) {
$this->_eventCallbacks[$methodName] = array();
}
if (!is_callable(array($this->_target, $methodName))) {
throw new Exception(get_class($this->_target) . '::' . $methodName . ' is not exists.');
}
if (is_callable($callback)) {
$this->_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->_registerEvent('before', $methodName, $callback, (array) $args);
}
/**
* Register 'after' handler
*
* @param string $methodName
* @param callback $callback
*/
public function after($methodName, $callback, $args = array())
{
$this->_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->_registerEvent('onCatchException', $methodName, $callback, (array) $args);
}
/**
* Trigger event
*
* @param string $eventName
*/
private function _trigger($eventName, $methodName, $target)
{
if (isset($this->_eventCallbacks[$methodName][$eventName])) {
list($callback, $args) = $this->_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->_target, $methodName))) {
try {
$this->_trigger('before', $methodName, $this->_target);
$result = call_user_func_array(array($this->_target, $methodName), $args);
$this->_trigger('after', $methodName, $this->_target);
return $result ? $result : null;
} catch (Exception $e) {
$this->_trigger('onCatchException', $methodName, $e);
throw $e;
}
} else {
throw new Exception("Call to undefined method {$this->_className}::$methodName.");
}
}
/**
* Get name of callback
*
* @param callback $callback
* @return string
*/
public static function getCallbackName($callback)
{
$className = '';
$methodName = '';
if (is_array($callback) && 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 類別的測試:
<?php
require_once 'TestClass.php';
$test = new TestClass();
/* @var $test TestClass */
echo "=======\n";
$test->method1('abc');
echo "=======\n";
echo $test->method2(), "\n";
echo "=======\n";
$test->method3();
echo "=======\n";
/* 執行結果:
=======
TestClass::method1:
abc
=======
TestClass::method2:
2
=======
TestClass::method3:
Exception: Test Exception. in TestClass.php on line 38
*/
接下來我們利用 Aspect 類別來對 TestClass 物件的三個方法切入 Log::save() :
<?php
require_once 'Aspect.php';
require_once 'TestClass.php';
require_once 'Log.php';
$test = Aspect::addObject(new TestClass());
$logger = new Log();
$test->before('method1', array($logger, 'save'), 'Log saved (method1).');
$test->after('method2', array($logger, 'save'), 'Log saved (method2).');
$test->onCatchException('method3', array($logger, 'save'), 'Log saved (method3).');
/* @var $test TestClass */
echo "=======\n";
$test->method1('abc');
echo "=======\n";
echo $test->method2(), "\n";
echo "=======\n";
$test->method3();
echo "=======\n";
/* 執行結果:
=======
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 偏向於程式的整體設計,所以這裡的範例尚不能用於實戰之中,僅僅只是我個人一個概念的實作而已。
供大家參考看看吧。也歡迎一起討論~
2008年04月13日
[Web] 連結分享
朋友問我這些連結怎麼看得完?我其實沒有全部看完,只是挑重點看。其他的都是瞄個大概,然後記下先;註解也是看懂大概後寫的 (我的英文閱讀能力普普) ,所以有時候可能會錯,請大家原諒。 >_<
PHP
-
Yahoo Pipes adds support for serialized PHP
Yahoo Pipes 可以設定丟出 PHP serialized 格式的內容,我們可以直接用 unserialize 函式直接解開。
-
上面幾個連結都是介紹 SPL ,值得參考看看。
-
RoR 的創始者 DHH 大濕對 PHP 的看法。
-
Late Static Binding (LSB) forward_static_call()
重點在 get_called_class 、 parent 及 forward_static_call 上,不過那個 new static() 也是 PHP 5.3 才有的。不過範例看不懂... Table1 和 Table2 不是應該繼承 ActiveRecord 嗎? (更新:發佈前回去看了一下,作者已經修正了)
-
Why not PHP for Google's App Engine?
Google App Engine 開放了,採用的是 Python 語言。為什麼不是 PHP 呢?簡單來說, Google 就是不喜歡 PHP 。 當然未來 Google App Engine 還是有可能支持其他語言的,等吧。
-
PHP 讀取自己的 mbox 信箱內容(Mime Mail Reader)
很實用的範例,這個要學起來。
-
PHP 5.3: SPL stack, heap, queue, list
PHP 5.3 的 SPL 可能會提供 stack 、 heap 、 queue 及 list 等特色。到時候要寫一些需要資料結構的程式時,應該會很有幫助吧?
-
htmLawed 和 HTML Purifier 一樣,都是用來過濾有問題的 HTML 。
-
反過來的問題:我們該怎麼把 BIG5 所沒有的字轉成 &#xxxx; 的格式?就是先用 mb_convert_encoding($data, 'HTML-ENTITIES', 'UTF-8'); (這時 $data 內資料的編碼也應為 UTF-8 ) ,這樣編出來的碼就可以放到 ANSI (BIG5) 編碼的檔案裡用。只是 HTML-ENTITILES 會把中文字全部轉成 &#xxxx; 的格式...可以參考這篇。
-
Piwik - Open source web analytics
好物!提供像 Google Analytics 的工具。只是有個問題:我們去哪找這麼大的資料庫空間?
-
Parsing Child Nodes with the DOM XML extension in PHP 5
用 PHP5 中的 DOM XML 套件來剖析文件裡的子節點。
-
Serialize and Unserialize SimpleXML in php
將 SimpleXML 產生的物件做序列化及反序列化。
-
How to make a dynamic RSS feed in PHP
簡易的產生 RSS 教學。
-
Defining a Custom Function for File Uploaders with PHP 5
自訂函式來處理檔案上傳。
Zend Framework
-
Lifting the Skirt on Zend Framework 1.5 - Zend_Form
介紹 ZF 1.5 的 Zend_Form 。不過我個人不太喜歡完全用 PHP 碼去兜出表單,所以我還得再想個方式包裝一下。
-
介紹 Zend_Form 的進階特色,像是 i18n 及 Element Grouping 等。
-
Rob Allen 也提供一個不錯的範例。
-
這篇改用 XML 做為 Zend_Form 的設定檔。
-
Action Helpers in Zend Framework
教你使用 ZF 的 Action Helper ,還提供一個 Form Loader 範例。
-
Using Zend_Feed to Merge Multiple RSS Feeds
教你用 Zend_Feed 來串聯數個 RSS Feeds 。
-
這篇解釋如何用 Zend_Feed 來解析出文章的 Tag ,並將它們存入資料庫。
-
Zend framework map for version 1.5
好物!這張圖列出了 ZF 常用的套件。
-
好物!這篇收集了很多 ZF 的教學,值得一看。
JavaScript
-
4 ways to dynamically load external JavaScript(with source)
4 種動態載入外部 JS 的方式。 jQuery 有個 $.getScript 可用。
-
Advanced JavaScript Debugging Techniques
列出目前常見的 JS Debug 技術。
-
5 ways to redirect URL with Javascript
5 種利用 JS 導向的方式。
-
很棒的 JS 教學,還提供了很多不錯的範例。不怕英語的朋友可以去看看。
-
Base64 Encoder and Decoder with JavaScript
用 JS 實作 base64 的編碼及解碼。
-
javascript cache problem, solved
可以解決 JS Cache 的問題,可是有點小麻煩。
-
Super Mario in 14kB Javascript
用 JS 寫的瑪琍歐....我只能說:傑克,這真是太神奇了。
jQuery
-
Hacking transparent PNG support into IE6 with IE PNG Fix, CSS and jQuery Part 1 / Part 2
透過 jQuery 、 CSS 加上 IE 的 PNG fix 技術,解決 IE6 不支援 PNG Alpha 透明的問題。
-
37 More Shocking jQuery Plugins
又是一堆好用的 jQuery Plugin 。其中 Small Rich Text Editor 看起來不錯,可以用來取代 TinyMCE 。
-
Hacking the :first-child pseudo-class into IE6 with jQuery and CSS
透過 jQuery 讓 IE6 也能支援 :first-child 和 pseudo-class 。
-
Learning jQuery: Fading Menu - Replacing Content
這篇教你怎麼做出類似頁籤 (Tab) 的效果。
CSS
-
CSS vs Tables - 13 Reasons Why CSS Is Superior to Tables in Website Design
13 個為什麼 CSS 排版比 Table 排版好的理由。
-
Most Useful 50 CSS Tips And Tools For Webmasters
一些不錯的 CSS 配合 JS 的技巧。
-
如果這篇早點出來就好了,我就不用每次都要想區塊的名字 (當然常用的就不用想了) 。
-
Tomorrow's CSS Today: 8 Techniques They Don't Want You To Know
這篇告訴你一些不錯的 CSS 技巧,以及對照有支援和沒支援的瀏覽器在顯示這些技巧上的差異。我個人覺得依副檔名顯示圖示的技巧實在是很棒。
-
想要找 CSS 相關資訊與技術?來這個網站就對啦!
-
這個很棒喔!只要把 XHTML 原始碼貼上去,再按一下「 Give me my code 」,就能得到對應的 CSS 原始碼骨架 (也就是還沒有設定屬性) 。
Internet
-
[禁斷秘技] 非HiNet用戶必看! 你的ADSL被降速了嗎?
我家終於被 Hinet 看上了,所以準備要來換肛世代了。
Web
-
Google App Engine - Google 的攞你命 3000
對 Google App Engine 的簡單介紹。唉...我想要 PHP ....
-
給 Google 帳號用的 OpenID 服務。
Design
2008年04月10日
[Web] 連結分享
PHP
-
Accessing Attributes and Cloning Nodes with the DOM XML Extension in PHP 5
教你用 PHP5 的 DOM 套件存取元素屬性或用來複製節點。
-
Multithreading in PHP with CURL
用 cURL 在 PHP 中模擬出多執行緒。
-
Reading and Writing Spreadsheets with PHP
用 PHP 讀寫 Excel 。
-
Fluent interfaces and code readability
不要濫用 Fluent Interface ,要顧及程式碼的可讀性。
-
Creating an Error Handling Module for a PHP 5 File Uploader
建立一個錯誤處理器,以便在檔案上傳後可以方便處理錯誤。
-
Setting up your own on-demand video site with PHP, Part 1: Groundwork
用 PHP 建立一個影像即時播送網站。
-
教你做一個多國語言的 PHP 應用程式。
-
在 PHP 5.3 裡使用 Namespaces 時要注意的地方。
Zend Framework
-
我的實作也是基於同樣的概念,不過我是聽從 Mark 的建議,改以切換 View Object 來實現。
CSS
-
這篇跟我的「樂多Blog排版入門」很像,有興趣讀英文的朋友不妨參考看看。
-
The Amazing LI: Using CSS and Unordered List Items to Do Just About Anything
教你把無序清單轉換成各種樣式。
-
Top 12 CSS Frameworks and How to Understand Them
將常見的 12 個 CSS Frameworks 整理出來,其中 YAML 真的是滿強的。
-
The 6 Most Important CSS Techniques You Need To Know
6 個滿實用的 CSS 技巧。
-
The Highly Extensible CSS Interface
該系列的文章教你打造可隨視窗寬度延展的 CSS 樣式。
-
這篇好!幾乎把所有 CSS 屬性都列出來了!
-
Background repeat and CSS sprites
利用 CSS sprite 來製作重複的背景圖。
JavaScript
jQuery
-
主要是教你如何運用 jQuery 的 event 機制。
-
教你如何先顯示圖片的某個區域,點選後再顯示完整圖片。
-
常用的技巧!
Web
-
向 IE6 說 No !這個網站有提供一個 Script 讓使用者在以 IE6 瀏覽你的網站時,會出現一個提示升級的訊息。
-
這個也可能會用到,記下先。
Browser
-
收集了一些測試瀏覽器畫面的服務。
Design
-
Vaga 提供了一堆免費的小圖示。
Other
Software
-
也是記下先。
