tutorials > 裏所有文章列表

unit test 與 AsUnit 3.0 – 關於 testing 與 test-driven的兩三事 (3/3)

In engineering, flash, tutorials   November 16, 2005 - 11:33 am

testing這件事對許多工程師來說都是:知道它很重要,但從來不覺得應該去做。

在瞭解如何使用 AsUnit這樣一個 test framework後,接者就可以來談談目前一般人進行testing的方式。

比較傳統的做法是先寫程式,然後再做 unit test,也就是程式碼寫好後再用一堆 test case去驗証它的正確性,這種做法初看是非常合理的,也符合一般常見的專案開發流程,先規畫、再寫code、最後測試,然後上線(我們就叫這種為 waterfall開發法)

但這種做法也有不少的缺點,最明顯的一個就是等到全部的code都寫好時才要進行測試,往往會覺得無從下手,因此此時可能已經有數十個class,而且彼此的關連極深,根本不知道該怎麼測起,搞不好連當初寫的工程師自已都忘了某個class是做什麼用的(別笑,你試試看自已寫個class不加註解,然後半年後再拿出來,看看你還能記得多少內容或理解它在做什麼)。

另一個麻煩就是此時才進行測試,即使發現了錯誤,恐怕也很難修改,或是能改但成本變的很大,因為這時一改恐怕就要牽動好幾個其它class,天知道改了這裏其它地方會不會爆炸。

有鑑於此,90年代未期開始出現一種新的聲音,強調不如在寫程式前先把 test case寫好,然後再以通過測試為目標去開發程式,這就是所謂的 test-driven design。

舉個簡單的例子,以前面的money class為例,傳統的劇情是這樣:

1、我希望這個class可以加總兩筆金額,例如原本3元,加上5元後得到結果 8元

2、然後開始寫 Money.addMoney() 這支method:function addMoney(){....}

3、寫好後用幾組不同的數字傳入 addMoney() 去看看結果是否正確

而在 test-driven的劇情則是這樣:

1、我希望這個class可以加總兩筆金額,例如原本3元,加上5元後得到結果 8元

2、接者我先寫些測試的case 做為將來測試使用,例如

Actionscript:
  1. var money2:Money = new Money( pounds2, pence2 );
  2. var money3:Money = money.addMoney( money2 )
  3. assertEquals( "Pounds should be 6", 6, money3.pounds );

3、然後開始寫 Money.addMoney() 這支method:function addMoney(){....}

但由於我已經知道將來會怎麼測試它,因此在寫的過程中就會特別留意傳入的參數與回傳的資料型態等細節要符合這個測試的期盼。

在 test-driven 的開發手法下,工程師寫程式是為了能百分之百通過 test case,因此在整個開發過程中就有了明確的依歸,只要test case有要求,就要在程式碼裏面完成(而且一定要完成),而 test case 沒提到的事,就沒有那麼高的priority可以丟一邊先不管,因此只要 test case 寫的夠週全,將來成品就一定可用。

另一方面,在寫 test case的過程中,工程師也會有機會先想清楚整個過程,有點類似 forsee/preview 將來程式完成的樣子,然後根據他在腦子中的觀察,去寫下所有功能的 test case,這很像是讓建築師在還沒動手蓋房子前,就在腦中冥想出未來成屋的樣子,然後照者在腦中看到的畫面去建這棟房子;這與傳統先動手蓋房子等到落成那天才能看到全貌是完全不一樣的兩回事。

最後再仔細想想,反正不論早晚都是要寫 test case (當然如果你壓根就沒打算做 testing那就另當別論),如果早寫可以帶來這些額外的好處,而晚寫卻要面臨種種風險,任何聰明快樂又有遠見的工程師應該都知道該怎麼選吧?

而 test-driven正巧也是近年來漸成主流的 agile development 重心之一,其中最為人所知的方法論就是 eXtreme Programmg (XP編程),當然 agile development 是個大題目,值得另外撰文聊聊,但這裏的重點只有一個:越來越多的工程師在經歷了幾十年的專案開發歲月後,終於發現先寫 test case帶來的效益是何其之大。

現在flash coders也有了 asUnit 3這個重量級的協力工具,要怎麼發揮它的妙用就看個人囉 :-)

note: 寫完這篇心中已經浮起另一個題目 > toward an agile flash development 聽起來還挺炫的吧?(只是有沒有時間寫就看老天了)

comments(2) | by admin

unit test 與 AsUnit 3.0 – tutorial (2/3)

In engineering, flash, tutorials   November 15, 2005 - 11:02 am

目前 asUnit的最新版是 asUnit 3.0.1 alpha,據兩位負責人說這是多年以來最大的改版,完全使用 actionscirpt 3 語法,並且忠實的照者 JUnit 去刻出一樣的功能,因此對大部份熟悉 JUnit使用方式的工程師來說,只要記得把 type 改成放後面並加上 : 就可以直接開工了。

下面簡單說明一下 asUnit 3的用法。

假設你有一個叫做 Money的class,它的功能是用來加減運算金額的數目
(note:這個程式是從 as2unit裏面借來的,我將它改寫成AS3 語法並修掉一些錯誤以通過測試)

Actionscript:
  1. package{
  2.    
  3.     class Money {
  4.        
  5.         private var __pounds:Number;
  6.         private var __pence:Number
  7.        
  8.         public function Money( pounds:Number, pence:Number )
  9.         {
  10.             /*
  11.             if ( pounds == undefined || pence == undefined )
  12.                 throw new Error( "Invalid constructor arguments" );
  13.             */
  14.            
  15.             if ( pence> 100 )
  16.             {
  17.                pounds = pounds + Math.floor( pence / 100 );
  18.                 pence = pence % 100;
  19.             }
  20.             //it's using setters here, for updating __pounds indeed.
  21.             this.pounds = pounds;
  22.             this.pence = pence;
  23.         }
  24.    
  25.    
  26.     //------------------------------------------------------------------------------
  27.    
  28.         public function addMoney( money:Money ):Money
  29.         {
  30.             var pounds:Number = this.pounds;
  31.             var pence:Number = this.pence;
  32.    
  33.             pounds = pounds + money.pounds;
  34.             pence = pence + money.pence;
  35.    
  36.             return flatten( pounds, pence );
  37.         }
  38.       }
  39. }

在這個class裏目前只有一個method,叫做 addMoney。

以往我們要測試這個class會不會通過,可能會用類似下面的程式:

Actionscript:
  1. var m:Money = new Money(5,10);
  2. m.addMoney(new Money(2,10));
  3. trace(m)

然後看結果是不是如預期中的金額。

這很明顯的,這樣做非常沒有效率,而且當class結構越長越大時,就很難維護這些test code。

因此比較好的做法是寫一個獨立的 test class,像這樣:

Actionscript:
  1. package{
  2.  
  3.     /*
  4.     這裏是真正的 test class, 測試每一支 method
  5.     */
  6.    
  7.     import flash.util.*;
  8.     import Money;
  9.     import asunit.framework.TestCase;
  10.     import asunit.framework.TestSuite;
  11.    
  12.     public class TestMoney extends TestCase{
  13.        
  14.         private var money:Money;
  15.        
  16.         /**/
  17.         public function TestMoney( ... args:Array )
  18.         {
  19.             trace("abba:"+ args);
  20.             super( args[0] );
  21.         }
  22.        
  23.        
  24.     //------------------------------------------------------------------------------
  25.    
  26.         public function testAddMoney()
  27.         {
  28.             var pounds2:Number = 3;
  29.             var pence2:Number = 20;
  30.            
  31.             money = new Money( 3, 50 );
  32.            
  33.            
  34.             var money2:Money = new Money( pounds2, pence2 );
  35.            
  36.             var money3:Money = money.addMoney( money2 )
  37.             /*
  38.             //assertNotNull( "money was null", money3 );
  39.             //assertNotUndefined( "money was undefined", money3 );
  40.             */
  41.             assertEquals( "Pounds should be 6", 6, money3.pounds );
  42.             assertEquals( "Pence should be 70", 70, money3.pence );
  43.            
  44.         }
  45.      }
  46. }

在這個class裏可以看到許多 assertEquals(), assertNotNull()之類的method, 這些就是 asUnit提供的測試功能,以 testAddMoney()為例,我們用一個新數字加上原本的數字後,透過
assertEquals( "Pounds should be 6", 6, money3.pounds ); 來檢查加總的結果是否為 6,也就是將 money3.pounds 當做參數傳入,然後看看程式跑完後跳出來的數字是否為6,如果兩者相等,那這個測試就通過,如果不是,那就要開始debug囉~

由上面兩個例子可以類推,如果未來 Money class裏又增加了下列功能:

subtractMoney()
greaterThan()
lessThan()
equals()

勢必每個method都可以透過一個 testXXXX()來進行測試,透過這樣系統化的撰寫test code,不但可以確保程式如預期般的運作,更重要的是未來不論要修改,或是人事交替必需由新進工程師接手維護程式碼,都可以透過這組 test code來確保程式功能完整無誤。

asUnit另一個更棒的功能就是 test suite (唸做 sweet),可以將一堆test class集合起來一次執行,請看範例:

Actionscript:
  1. package{
  2.     import asunit.framework.TestSuite;
  3.  
  4.     import TestMoney, TestCar, TestObject;
  5.     import flash.util.*;
  6.    
  7.     /*
  8.     aggregates all test classes here to run at once
  9.     */
  10.     public  class MyTestSuite extends TestSuite{
  11.        
  12.         public function MyTestSuite(){
  13.             addTest(new TestMoney());
  14.                         addTest(new TestCar());
  15.                         addTest(new TestObject());
  16.         }
  17.     }
  18. }

這裏我們建了一個 MyTestSuite,並且將三個要測試的class (別別是 TestMoney, TestCar, TestObject) 都丟進去,將來只要執行這個 MyTestSuite就可以一次測試完三個class裏所有的method。

測試語法如下:

Actionscript:
  1. mt:MyTestSuite = new MyTestSuite();
  2. mt.run();

不過這樣做有個小缺點,就是除非執行過程中有發生錯誤,才會在eclipse的console中顯示錯誤訊息,但如果全部通過的話就完全不會顯示任何資訊。

為此asUnit很貼心的提供了兩種選擇,第一個是用 flex 畫出來的測試介面,使用方法如下:

Actionscript:
  1. package {
  2.     import asunit.textui.TestRunner;
  3.     import TestMoney;
  4.  
  5.     public class AsUnit extends TestRunner {
  6.  
  7.         public function AsUnit() {
  8.             start(MyTestSuite)//this is for multiple test sets
  9.             //start(TestMoney);  //this is for single test set
  10.         }
  11.     }
  12. }

只要把MyTestSuite傳進去,它就會自動執行並開啟一個小swf視窗,最後把結果顯示在裏面,如下圖:

從圖中可以看出只花了0.025秒就完成了12項測試並且全部通過。

另一個方法則是用xul ui。xul是一個可以在 firefox裏執行的 GUI app,使用方式也差不多,只是他得在browser裏面執行,但由於我不需要這種玩意所以抓回來後從來沒執行過。

Add comment | by admin

unit test 與 AsUnit 3.0 – 介紹 (1/3)

In engineering, flash, tutorials   November 14, 2005 - 11:30 am

unit test在專業的軟體開發過程中一直是非常非常重要的一環,透過一個有系統的方式來檢測程式碼的正確度可說是軟體開發最低限度的要求。

但到目前為止,10 個flash coders裏大概有 11 個都是用唯一的一種方式來做所謂的 test : CTRL-Enter,也就是 test movie來執行電影,然後看看output panel裏有無出現錯誤。(呃,如果你不屬於這11個,並且已經在用某種 unit test framework,那請直接跳過這篇文章去做更有意義的事,例如補個眠)

傳統用 test movie做測試對付小型的專案還行有餘力,但當案子發展的越來越大,並且完全走向 class (OO)導向的開發方式時,這種做法就顯的費時費力且不切實際,另一方面,這種測試方法往往要等UI好了才比較能整合起來測試,但往往這時有些根本的錯誤已經種的太深很難再改了。

在java的世界裏,這個問題很早就獲得了解決,他們可以靠一個叫 JUnit的 testing framework 來自動化所有的測試工作,只要簡單的設定與編輯,就可以完整的測試所有class中的每一個method運作是否正常,並且更重要的是,這些test class可以做為日後改版或 refactoring時重要依據,例如每次改完程式碼或refactor一大段code blcok, 就重跑一次test,只要測試能過,就代表剛才做的改變沒問題。

在Flash的世界中其實也有類似JUnit的東西,幾個檯面上比較知名的(其實是也就只有這幾個啦)

-as2unit:

這是英國的 iteration:two顧問公司所發展的test framework,,as2unit的基礎架構是照者 JUnit翻版而來,但它只保留了幾個重要的部份,因此功能上不是非常完整。btw, 這家公司日前才剛被macromedia收購成為 Macromedia Europe Consulting (MEC)。

-as2lib

這是最早期出現的一個全能型 library,裏面也包含了一個小型的 unit test library,但由於太冷門一直無法成為主流。

-flex unit

這也是 iteration:two推出的project,但主要是針對 flex 做 unit test,只是隨者 flex 2的出現(與伴隨而來的大改良),這個project結束的日子應該不遠了。

而本文要介紹的,則是由macromedia官方支援(金援)發展的正宗JUnit flash-port版:

AsUnit

asUnit也是一個open source的testing framework, 並且也存了在相當長一段時間,但由於一年前macromedia正式加入這個proejct,並且提供足夠的經費供他們做研發,因此他們順利的成為第一個支援Actionscript 3 的 testing framework。

Add comment | by admin

Flash Remoting 教學 4 – 使用flash remoting Authenticate

In tutorials   August 1, 2005 - 4:29 pm

◆flash remoting的安全考量

Flash remoting是很快很方便的client/server連線方式,但由於flash swf本身很容易被破解,因此基本上任何人都可以知道server端的business delegate class名稱與method。

大家可以假想下列情況,如果在server上有一個MyClass 的component, 裏面有 deleteUserById($id)這支function,而現在hacker透過反組譯swf檔知道下列資訊:

Server ip: http://xxx.xxx.xxx.xxx
Component: /Test/MyClass.php
Function: deleteUserById()

他是否就可以開始跑迴圈將資料庫內的資料一筆筆刪除呢?

理論上這是可行的,即使你的程式設計中已經有login的手續,還是可以很輕易的繞過這個檢查而直攻server上的components.

以往在coldfusion/jrun使用flash remoting時,我們可以使用內建的role與authenticate功能,配合flash 端的setCredential來做每次method call的認証,但在amfphp v1.0之前這個功能一直是很穩定的維持爛掉的狀態,不論怎麼設定role或credential都不會有反應,好在這次patrick終於將整個功能都弄好了。

本文將介紹如何使用 flash remoting的 Authenticate class做安全認証。

◆Authentication 的方式

Amfphp進行authentication 的方式一樣是透過method table來設定,請看下列程式碼。

PHP:
  1. function AuthClass()
  2.     {
  3.         $this->methodTable = array(
  4.  
  5.             "getPerson" => array(
  6.                 "description" => "Describe here your method",
  7.                 "access" => "remote",
  8.                 "roles" => "admin"   // this method will be available only if authenticate method returns "admin"
  9.             ),
  10.  
  11.             "doLogin" => array(
  12.                 "description" => "Describe here your method",
  13.                 "access" => "remote",
  14.                 "roles" => "admin"   // this method will be available only if authenticate method returns "admin"
  15.             )
  16.         );
  17.     }

在這個method table中可看到 roles => “admin”這樣的參數,這就是在設定 doLogin()這支method只有身份為 role的人才能存取,當然這裏也可以放多個參數,例如roles => “admin”, “supervisor”, “dbadmin”這樣就有三種身份的人可以存取這個method。

而在flash端,則是透過一個可能大部份人都不曾知道的指令 setCredential()來搭配。它的語法如下。

Actionscript:
  1. setCredentials(inputName.text, inputPwd.text);

這是一個內建於 flash remoting NetConnection class的指令,一旦設定好日後每個method call之前都會在AMF封包頭加上這個資訊,因此php端一收到這個資訊就會自動進行認証。

聽起來很簡單對不對?amfphp可是搞了兩年才有完整的功能呀~

◆php程式

接下來我們開始寫server端的程式。

PHP:
  1. <?php
  2. /*
  3. @author: jeremy@richtechmedia.com
  4. @website: http://ria.richtechmedia.com
  5. released under CC 2.5.
  6. */
  7.  
  8. class AuthClass
  9. {
  10.     function AuthClass()
  11.     {
  12.         $this->methodTable = array(
  13.  
  14.             "getPerson" => array(
  15.                 "description" => "Describe here your method",
  16.                 "access" => "remote",
  17.                 "roles" => "admin"   // this method will be available only if authenticate method returns "admin"
  18.             ),
  19.  
  20.             "doLogin" => array(
  21.                 "description" => "Describe here your method",
  22.                 "access" => "remote",
  23.                 "roles" => "admin"   // this method will be available only if authenticate method returns "admin"
  24.             )
  25.         );
  26.     }
  27.  
  28.     function getPerson()
  29.     {
  30.         $person = array();
  31.         $person['first_name'] = "Alessandro";
  32.         $person['last_name']  = "Crugnola";
  33.         return $person;
  34.     }
  35.    
  36.     function doLogin()
  37.     {
  38.         return true;
  39.     }
  40.  
  41.     //
  42.     function _authenticate($name, $pwd){
  43.         //mysql settings
  44.         $host = "localhost";
  45.     $link = mysql_connect($host, "root", "");
  46.     mysql_select_db("test", $link);
  47.     //
  48.     $sql="select role from tbl_users where name='$name' && pwd='$pwd'
  49.     ";
  50.     $result = mysql_query($sql);
  51.     //
  52.     if(mysql_num_rows($result)>=1){
  53.         $rows = mysql_fetch_assoc($result);
  54.         $role= $rows["role"];
  55.         //now we know the role of this user, just return it
  56.         return $role;
  57.     }else {
  58.             return false;
  59.         }
  60.     }
  61. }
  62. ?>

這裏面需要注意的是最後一個_authenticate(),它是amfphp內定的名稱,任務就是在接收到AMF封包中的 credential資訊時進行認証。這個function的內容是可以由我們自由撰寫的,從範例中可以看到我們是跟資料庫取得name 與password做比較,這是很典型的認証手法。

如果帳號/密碼這關通過,我們就將前面順便query出的role傳回amfphp,從此這個user的身份就確認下來了。

附帶一提,這個role 的資訊是記錄在session中,每次flash 與 amfphp連線時,都會自動產生一個session key,因此不用擔心每次都會跟db要一次資料或是搞錯role的身份。

◆flash程式

接下來看一下flash的程式

Actionscript:
  1. /*
  2. @author: jeremy@richtechmedia.com
  3. @website: http://ria.richtechmedia.com
  4. released under CC 2.5.
  5. */
  6.  
  7. //remoting related classes
  8. import mx.remoting.Service;
  9. import mx.remoting.PendingCall;
  10. import mx.remoting.RecordSet;
  11. import mx.remoting.debug.NetDebug;
  12. import mx.rpc.RelayResponder;
  13. import mx.rpc.ResultEvent;
  14. import mx.rpc.FaultEvent;
  15.  
  16. //start debugging so we can see debug info in NCD.
  17. NetDebug.initialize();
  18.  
  19. //gateway settings
  20. var gateway:String = "http://localhost/auth/gateway.php";
  21. respGeneral = new RelayResponder(this, "resultReceived", "resultFailure");
  22. _service = new Service(gateway, null, "AuthClass", null, respGeneral);
  23.  
  24. //callback when result was returned from server
  25. function resultReceived(re:ResultEvent):Void{
  26.     //if login success, we can branch to other method calls
  27.     if(re.result){
  28.         trace("login result: " + re.result);
  29.         //other method calls here...
  30.     }
  31. };
  32.  
  33. //if authentication failed, FaultEvent will show the error message
  34. function resultFailure(fe:FaultEvent):Void{
  35.     trace("failed: " + fe.fault.description);
  36. }
  37.  
  38. //send message to server
  39. btnLogin.clickHandler = function(){
  40.     _service.connection.setCredentials(inputName.text, inputPwd.text);
  41.     var pc:PendingCall = _service.doLogin();
  42. }

這段程式碼其它部份都是很標準的remoting 程序,唯一要特別注意第40行這部份,當使用者在login form裏填好name, password按下 login button時,我們先用 setCredentials(inputName.text, inputPwd.text)將他/她輸入的帳號密碼設成credential,然後進行_service.doLogin(),這樣做的目地是因為setCredential()只會在下一次的remoting call才會發揮功能,因此不能太早設定。

另外,請特別注意最後這個 doLogin()它實際的功能並不是真的在負責處理login,我們只是用它去測試前面的 setCredential()是否正確,因為如果前面的credential設定是錯誤的,那接下來一執行 doLogin()時,就會產生類似下面的錯誤訊息:

This user is not does not have access to {doLogin}.

我們就可以依此做 if..else..判斷,看是要提醒user再試一次,或是進行下一步的動作。而如果不用doLogin當helper的話,我們就必需真的直接執行其它指令,這樣會增加method call曝光的機會,能免則免。

有了以上的認知,應該可以瞭解為何我們需要 resultReceived 與 resultFailure 兩個callback分別處理正確與錯誤的訊息,這部份要小心判斷免的讓駭客偷偷溜進去了。

◆ 測試

接者我們來測試看看。資料庫內已內建兩組帳號(在下載檔案裏有sql schema可直接套用),帳號|密碼|身份 分別是:
John, a, admin
Mary, a, client

我們先故意不輸入資料就按button,結果是下圖

圖中可以看到出現status訊息,說明User Error,自然就進不去囉!

然後我們輸入 john, a 試試

大家可以看到此時amfphp已知道john的身份是admin, 因此它有權限可以執行某些method了。

◆結論

Web app的安全性說實話再怎麼做都是不夠的,永遠有backdoor可能被駭客發現,但最低層次至少基本的防護是該先做好的,同樣的問題在寫flashcom的應用程式時其實更嚴重,而且要付出的成本也更大,畢竟flashcom是論人頭與頻寬來算license, 如果不好好保護被人隨便hijacking損失可就大了。

另一方面,除了上述的 role-based authentication外,也可考慮使用SSL做為連線方式,並搭配MD5將資料編碼,這樣可以連傳輸端這一塊也保護起來。

最後,關於flash安全性的問題要有一個基本的認知:只要swf發佈出去就是不安全的,就算你用三道編碼器反覆加密過,也總有被破解的一天,因此重要的business logic與data絕不可放在 client端。

據說在不久的將來當Actionscirpt 3出來後,flash會支援 binary-code downloads,屆時或許就有比較完善的保護方案,不過那又是後話等真的實現時再說吧。

檔案下載

comments(6) | by admin

Flash Remoting 教學 3 – 透過flash remoting使用 web service

In tutorials   August 1, 2005 - 3:06 pm

◆flash使用web service 的三種方式

Web service是近五年來非常火紅的資料交換方式,幾乎各大技術平台都有完整的支援方案,flash自然也不例外。

從早期 5.0時又慢又不穩定的xml object到現在7.0快速又穩定的內建核心,flash支援Web service的苦心大家都看得見。

在flash裏使用web service一般有三種方式:

1、 使用web service connector: 在pro版裏有內建這個連線元件,只要簡單在面板上勾選設並填入wsdl就可以進行連線,並且傳回值也可透過data binding或DataSet等元件做進一步處理再bind到UI元件上。

2、 直接使用 web service classes, 一般進行專案時為了能完全掌控Web service object的運作,通常都會選擇直接操作web service class並且建立個ws object來進行連線,然後手工處理傳回值;這個方法聽起來似乎很累人不過由於彈性大選擇多所以還是有好處的。

3、最後一種方式就是透過flash remoting當媒人,先將Web service request傳給server端的remoting gateway, 然後由server去delegate轉向遠端發出Web service請求。這樣做主要的好處有兩個:

-flash player有所謂的sandbox security限制,如果要連線到不同domain的位址往往會出現錯誤訊息,此時要不得一個個手工設定allowDomain,要不就是使用policy xml,但如果透過server端的remoting gateway當媒人的話就不會有domain相異的問題。

-另一個好處就是remoting gateway會自動將傳回值轉換成flash native object,例如最常見的就是 Array, Associative Array,方便直接使用。

當然使用第三種方法也是有缺點的,最明顯的就是連線時多了一道手續,因此會拖慢一些時間,第二個則是所有的request都會透過server端先處理,一來增加server的loading,二來也消耗server的頻寬,因此是否要採用此方法變成是一個高層次的design decision。

本文的目地是教會大家使用這種技巧,但實務上是否採用就請自行判斷囉!

◆透過 flash remoting 使用web service

要透過remoting gateway使用web service 有幾個地方要特別注意:

-由於所有的web service都是過server去轉介,因此server端一定要俱備web service client的能力。這對使用Java, Php平台的人來說更為重要,因為它不像.NET是天生俱備的。

-php 5(windows版)開始正式內建一個 Soap dll,此dll是用C語言撰寫,因此理論上比之前使用NuSoap或其它Soap Library的方案更為快速穩定,但它需要經過一些設定手續才能正式啟用。

◆php的設定

本文使用的平台為 windows xp + apache 2 + php 5.0.4,一般來說安裝好時是沒有將soal_dll掛進去的,大家可以寫一個簡單的 phpinfo() 去看看系統設定,如果沒有找到下面的文字,就代表soap並未啟動。

啟動soap的方式很簡單,只要在php.ini(一般應該都在php的目錄下,例如我的路徑為 c:\php5\php.ini)裏加入下面兩句即可:

extension=php_soap.dll
extension=php_xsl.dll

第一句是掛入 php_soap.dll這個module, 第二句則是支援xslt的功能,雖然remoting用不到,不過如果將來你需要在php裏處理web service的話就很有用,所以順便掛一下吧。

另外請確定php.ini裏面的 extension_dir = “/ext” 的路徑有指向正確資料夾,不然絕對找不到上面兩個module。設定好後存檔將apache 重開一次,如果沒出現錯誤訊息(例如 php_soap.dll找不到之類的)就代表安裝成功了,當然為了保險起見,可以再執行一次 phpinfo(),這次就應該會看到上圖的文字了。

◆php程式碼

當透過 flash remoting來連接web service時,其實php端是不需要寫任何程式的,只要在專案資料夾內有一個 gateway.php 做媒人即可。

不過這裏我們寫一個小程式來測試php是否真的已俱備當soap client的能力。

PHP:
  1. <?
  2. $wsdl_url = "http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl";
  3. $client = new SoapClient($wsdl_url);
  4. $return = $client->getQuote("MACR");
  5. echo("price is: " . $return);
  6. ?>

寫好後將它存成 testsoap.php然後透過browser執行一次,不久後就會看到macromedia的股價傳回來,今天是40美元一股,當年第一次寫這範例時約略是18美元左右,可見是值得長期投資的績優股,建議買進 (笑)。

這段程式碼比較值得注意的地方是第3行,我們建立了一個 new SoapClient()並給定遠端wsdl的位址,然後下一行透過 $client->getQuote("MACR")去取回macromedia的股價。

◆flash程式碼

接下來我們準備透過flash 呼叫web service吧。

Actionscript:
  1. /*
  2. @author: jeremy@richtechmedia.com
  3. @website: http://ria.richtechmedia.com
  4. released under CC 2.5.
  5. */
  6.  
  7. import mx.remoting.Service;
  8. import mx.rpc.RelayResponder;
  9. import mx.rpc.FaultEvent;
  10. import mx.rpc.ResultEvent;
  11. import mx.remoting.PendingCall;
  12. import mx.remoting.RecordSet;
  13. import mx.remoting.DataGlue;
  14. import mx.utils.Delegate;
  15.  
  16. //webservie is a folder under /htdoc/ it serveed as a gateway to delegate remote web service call
  17. gatewayUrl= "http://localhost/webservice/gateway.php?";
  18.  
  19. //initialize NetDebug
  20. mx.remoting.debug.NetDebug.initialize();
  21.  
  22. //this is where we assign remote web service WSDL
  23. service = new Service(gatewayUrl, null, "http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl", null, null);
  24. btnSearch.addEventListener("click", Delegate.create(this, doSearch));
  25.  
  26. //call remote method to check for current rate between US and Taiwan dollars
  27. function doSearch() {
  28.     var pc:PendingCall = service.getRate({country1:"taiwan", country2:"us"});
  29.     pc.responder = new RelayResponder(this, "handleSearch", "handleError");
  30. }
  31.  
  32. //
  33. function handleSearch(re:ResultEvent) {
  34.     trace(re.result);
  35. }
  36.  
  37. //
  38. function handleError(fault:FaultEvent):Void {
  39.     trace("fault: " + fault.fault.faultstring);
  40. }

請注意第17行設定gatewayURL的語法,在gateway.php後面多了一個問號,其實這個gateway.php只是要負責幫忙轉送method call到遠方,因此任何一資料下有這個檔案都可以。

接者第23行我們將要用的web service填進去,這裏我們是使用xmethods.com提供的一個匯率轉換程式。

最後在第28行我們透過 service.getRate()傳出兩個參數,一個是taiwan, 一個是us,表示我們想知道今天一美元可換多少台幣。特別傳參數時使用的格式,裏面是一個anonymous object,包含兩個properties,分別是 country1, country2,這是xmethods.com當時提供web service所規定的參數格式,因此我們一定要遵守,不然call了之就再也不會有回音了…

◆執行

接者來執行看看。

按下button後可透過NCD看到下列訊息

其中第二行是傳送兩個參數給flash remoting,然後它會幫忙轉送給遠端的method。

最後一行則是結果回來了,答案是今天一美元可以換32元台幣,看來是上cdnow血拼的好時機(當初教的時後匯率是令人心痛的35.xxx)

◆結論

這個教學裏我們並沒有使用太複雜的例子,但大家可以到 xmethods.com試試其它的web service, 裏面有許多服務是會傳回複雜的資料型態,例如array甚至是多維的陣列,此時就可以體會使用flash remoting的好處,因為可以省掉自已操作xml(childNode之類的語法)的麻煩。

範例檔案下載

comments(3) | by admin

Previous Posts

mobile phone