{ 程式小心得 }

  • Enable HTTPBuilder debug message

    | /

    在用 groovy & cucumber 寫 API test 時,如果發生問題只會噴個 error 的 body 而已,這樣非常難 debug 。

    而且有一些參數是設在 http header 上,如果把每個 API 的 header 都印出來,要到處塞 debug logger。如果是 https 的話,也沒辦法使用 tcpdump 來看。
    這時可以用下列方法簡單的把 logger 設定打開,就可以直接噴在營幕上了,這樣比較好 debug。

    1
    $ mvn -B integration-test -Dcucumber.options="--tags @test_only" \ -Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog \ -Dorg.apache.commons.logging.simplelog.showdatetime=true \ -Dorg.apache.commons.logging.simplelog.log.org.apache.http=DEBUG

    還有其他的參數可以參考後附的文章內容。

    reference

  • [php] grapheme_strlen vs mb_strlen vs strlen

    | /

    前言

    小時候在寫 PHP 時要看字串長度,常常不懂事的用 strlen 來作判斷,但是在遇到中日韓這種會把 2 bytes 當一個字的長度時,這時候字串的長度又不準了。這時學會了使用 mb_strlen 這個 function,感覺長度這樣用,就準了。但有趣的事是,竟然近期又出現了 grapheme_strlen 。崩潰,不過就是算個長度而已,為什麼有這麼多的方法。

    讓我們來寫個小程式來實驗一下

    [code lang=”php”] <?php $str = “這是中文 english 混合測試”; var_dump($str); var_dump( mb_strlen($str, ‘UTF-8’), grapheme_strlen($str), strlen($str) ); ?> [/code] 結果如下: [code lang=”bash”] $ php /tmp/test.php string(33) “這是中文 english 混合測試” int(17) int(17) int(33) [/code] 看起來很合理啊,用 strlen 結果是 33 個字元,因為他是算 bytes。mb_strlen 17 個字,即使是中英文混合,看起來算字數也是沒問題的!mb_strlen 和 grapheme_strlen 目前看起來結果是一樣的。

    接下來再一個小實驗

    [code lang=”php”] <?php $str = json_decode(‘“e\u0301 = \u00E9”‘); var_dump($str); var_dump( mb_strlen($str, ‘UTF-8’), grapheme_strlen($str), strlen($str) ); ?> [/code] 結果如下: [code lang=”bash”] $ php /tmp/test.php string(8) “é = é” int(6) int(5) int(8) [/code] WTF,竟然有三個不同的數字,畫面上來看,明明就只有五個字,為什麼 mb_strlen 會算出來六個字呢!

    原來是 Grapheme Cluster 在作怪

    Grapheme Cluster 就類似中文一樣,會把二個字當作一個字。所以畫面上看起來是一個字,但實際上在 unicode 是可以用二個字來表示,所以可以看見範例二中顯示的字是一樣,但 unicode 是不同的。而這樣就會造成 mutlibyte 的 function 計算長度有問題。

    結論就是….

    如果有考慮中日韓以外的語系的話,可以使用 grapheme_strlen 。另外必須要注意,如果傳進來的參數不是 utf8 的字的話,他的回傳值是 null ,而這部份與 mb_strlen 的結果不一致。

    參考文章

  • [php] mock 有 reference parameter 的 method

    | /

    前言

    雖然盡量不要在 php 裡面寫 call by reference 的參數,但是有一些舊有的code 還是有人這樣寫。如果這時又想要mock 他就麻煩了,因為值並不會隨著自已想要的內容作改變。

    實作

    原先的 class 設計如下

    [code lang=”php”] <?php class NeedToMockClass { function testReference(&$str) { // 也許會有很多很複雜的計算,不過這邊簡化成一行就好 $str = “test String”; return; } } class testClass{ function test($str) { $t = new NeedToMockClass(); $t->testReference($str); return __METHOD__ .’->’ . $str; } } // $t = new testClass(); // $str = $t->test(‘not a test string’); // var_dump($str); // string(28) “testClass::test->test String” [/code]

    How To Test?

    如果這時我要測 testClass 我該如何測?因為我單純的只想測 testClass 裡的邏輯對不對,我不想管 NeedToMockClass 的邏輯,所以這時我該把 class mock 掉,並且讓他可以自定 $str 的 output。 首先,會先改寫 testClass 讓他可以從外面傳 NeedToMockClass 的 object 進去。 [code lang=”php”] <?php class testClass{ private $_mockClass = null; // 由 construct 傳進來,讓他可以取代掉預設的 mockClass function __construct($mockClass == null) { if ($mockClass = null) { $mockClass = new NeedToMockClass(); } $this->_mockClass = $mockClass; } function test($str) { // 接著用mock 後的class 來執行 $this->_mockClass->testReference($str); return __METHOD__ .’->’ . $str; } } [/code] 最後測試程式會用下面這個方法寫,主要是用 returnCallback 的方式來 mock: [code lang=”php”] <?php require_once ‘./sample.php’; class testTest extends PHPUnit_Framework_TestCase { function mockTestReference(&$str) { $str = ‘3345678’; } function testTestClass() { $mockClass = $this->getMock(‘NeedToMockClass’, array(), array(), “” ,false); // 這裡是用 returnCallback 的方式呼叫 testTest::mockTestReference 這樣就可以改 $str 的內容了 $mockClass->expects($this->any()) ->method(‘testReference’) ->will($this->returnCallback(array(‘testTest’, ‘mockTestReference’))); $t = new testClass($mockClass); $str = ‘one test’; $ret = $t->test($str); // 最後會如預期的產生 testClass::test->3345678 的字樣 $this->assertEquals(‘testClass::test->3345678’, $ret); } } [/code] 順利達成目標,其結果如下: [code lang=”bash”] $ phpunit testTest.php PHPUnit 3.7.35 by Sebastian Bergmann. . Time: 22 ms, Memory: 3.50Mb OK (1 test, 2 assertions) [/code]

    參考文件

  • 使用 Codeigniter 的 Pagination 且帶入不定長度查詢參數的方法

    | /

    在官方的Pagination Class 中, 用 page 的參數來控制本頁目前的起始行數。但範例裡面使用的方法如下:

    1
    2
    3
    4
    5
    $this->load->library('pagination'); 
    $config['base_url'] = 'http://example.com/index.php/test/page/';
    $config['total_rows'] = 200;
    $config['per_page'] = 20;
    $this->pagination->initialize($config);

    所以原始的方法所產生的 url 就是 http://example.com/index.php/test/page/{row number} 這種格式。在參數是固定長度下,你可以輕易的知道在第幾個位置是分頁的依據,即可以使用 $config[‘uri_segment’] 來指定位置。

    但如果參數為不固定數量,哪怎麼辦?使用 uri_segment 就沒有作用了,因為你也不知道第幾個位置是起始行數,而且起始行數只會加在 url 的最後面。

    下面提供一個方法可以 override 掉原先的目前所抓取的起始行數的方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $this->load->library('pagination'); // 中間可以加很多的東西,但 page 一定要放在最後面
    $config['base_url'] = 'http://example.com/index.php/test/'.blalbala.'/page/';
    $config['total_rows'] = 200;
    $config['per_page'] = 20;
    // 新增下面的程式。
    $config['uri_segment'] = 0;
    // 不指定 row 的 url 位置
    $q = $this->uri->uri\_to\_assoc(3);
    // 把目前的參數轉成array
    $config['cur_page'] = $q['page'];
    // override 目前的 row
    $this->pagination->initialize($config);
    echo $this->pagination->create\_links();

    這樣你的參數的長度就不需要固定了。這對於帶入參數的 search ,或是 list 的頁面,在使用上較為方便,也不用作什麼 dirty hack。

    actos price

  • PHP 5.3 已經 deprecate zend_get_parameters_ex

    | /

    這幾天在把一個程式作 PHP 5.2 轉換到 PHP 5.3 的環境下,發現以前有一個 zend_get_parameters_ex 已經被 deprecated 了。 Example: [code lang=”c”] zend_get_parameters_ex( 1 , &arg1 ) [/code] 簡單一點的改法就是把他改使用 zend_parse_parameters 這個 method 。 Example : [code lang=”c”] zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC , “sz” , &arg1, &arg1_len, &arg2) [/code] 這個 function 比較方便,也可以不用把 zval 轉成 c 所需要的型態,可以直接放入原有的參數型態,像使用 char or long 一些原生參數就方便多了。 Old Method Example: [code lang=”c”] zval **arg1; std::string str; zend_get_parameters_ex( 1 , &arg1 ) ; convert_to_string_ex( arg1 ); str = Z_STRVAL_PP( arg1 ); [/code] New Method Example: [code lang=”c”] char *arg1; int arg1_len; std::string str; zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC , “s” , &arg1, &arg1_len); str = string(arg1); [/code]

  • java & php 的 timestamp 為不同單位

    /

    一般以來,我以為 timestamp 應該是要從 1970 年零點零分零秒開始,至目前時刻所經過的「秒」數,才算是 timestamp 。不過後來查了一下 java 的 timestamp 的說明,他是從 1970 年零點零分零秒開始到現在的「微秒」數。所以二者之間相差了 1000 倍。 Java 的 timestamp的說明如下: long getTime() Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Timestamp object. PHP 的 timestamp 說明如下: int time ( void ) Returns the current time measured in the number of seconds since the Unix Epoch (January 1 1970 00:00:00 GMT). 所以如果 php & java 要作 web service 的服務的話,記得要處理這部份的轉換。 Java 部份的程式碼: [code lang=”java”] import java.util.Calendar; class Time { public static void main(String arg[]) { Calendar cal = Calendar.getInstance(); System.out.println(cal.getTime().getTime()); } } [/code] 結果如下: [code] whatup@whatup:/tmp$ java Time 1257688227061 [/code] PHP 部份的程式碼: [code] echo time(); [/code] 結果如下: [code] whatup@whatup:/tmp$ php time.php 1257688308 [/code]

  • 利用 curl 平行抓取多個網頁

    /

    如果一次要抓多個網頁,如果使用一個一個 curl 慢慢抓的話,實在是太慢了。假設一個網頁要 0.3 sec 的話,十個網頁需要抓時就要 3 sec 了。利用這個 curl 的 mutli handle 功能,可以同時抓取 N 個網頁,這樣抓取網頁速度會有明顯的提昇。

    其實也可以利用這個方法,來試試自已的網頁的存取速度怎麼樣。不過如果一次開 50 個以上,感覺抓取速度會受到影響。所以還是別同時抓取太多網頁比較好。

    範例程式碼如下: [code lang=”php”] 0); foreach($curl_obs as $curl_ob) { $content = curl_multi_getcontent($curl_ob); var_dump(strlen($content)); } foreach($curl_obs as $curl_ob) { curl_multi_remove_handle($mh, $curl_ob); } curl_multi_close($mh); ?> [/code]

    執行結果: [code] whatup@whatup:/tmp$ php curl_multi.php int(222) int(9490) [/code]

  • google app enging for java 用來跑 PHP

    /

    前幾天 google 發布了 google app engine 支援了 java 後,就有聽說有人可以利用 Quercus 專案,來支援 PHP ,這是一個令人興奮的消息啊。像我不太會寫 python 的人,有 PHP 的支援,更可以讓更多的 web developer 快速上手。

    幾天後,有一家公司的 blog 上發表了一篇「Run PHP on the Google App Engine」,他們已經成功的在 app engine 上執行 php 了。

    他所列的步驟如下:

    1. Register a free account.
    2. Download this file to your computer.
    3. Edit application XML tag in the file war\WEB-INF\appengine-web.xml to the name of the application you have registered.
    4. Finally upload your application. I downloaded Google App Engine SDK for Java and use the following command in windows.

    但我在本地端測試的時候,因為少了一些 package 所以沒辦法在本地端執行,你可以到 http://www.codehaus.org/ 下載 jetty package ,並且解壓縮到 app-engine 的 lib 目錄,即可以使用 dev_appserver.sh 這個指令在本地端測試了。Depoy 到 google 的 GAE 上的話,就不用這這個 package 了。

    另外他沒有支援 Mysql 之類的關聯性資料庫,所以如果要 port 一些跟資料庫相關的東西的話,應該要自行改寫其資料庫的部份。

  • jquery 判定 browser 種類

    /

    在工作中,有一些需要為 IE 作特別的 hack ,例如像 IE 的 option 不吃 onclick 的事件之類的。但又不想讓他影響到 IE 之外的 browser 。在 jquery 裡面,可以使用下列的參數來判定是不是使用 IE 。

    [code lang=”javascript”] if(jQuery.browser.msie) alert(“這個 browser 是 IE”); [/code]

    當然其他的參數也可以判定 browser 的板本與種類,可以參考Utilities/jQuery.browser這份文件。

  • 於 mysql 裡使用 custom sort

    /

    通常在 mysql 下作排序的時候,會用「 ORDER BY 欄位」來排序內容,但這樣的方法的排序的動作只能依其大小來排。假設目前有下面的 table 內容想要顯示,可是球隊內容都會依照其防守位置作排序依據,這時就不能用單純的大小來比較了。 [code lang=”sql”] SELECT * FROM batters ORDER BY 守位; [/code] 結果如下:

    守位 名字
    1B 潘忠韋
    2B 蔣智聰
    3B 石志偉
    C 陳峰民
    CF 黃龍義
    DH 陳金鋒
    LF 曾豪駒
    RF 霸漢
    SS 林智勝

    可以透過 FIELD 來幫忙,他可以依照傳入的值找出順序,例如: field(欄位,’spring’,’summer’,’fall’,’winter’),如果傳入值是 winter 的話,他會回傳數字,可以利用這個特性作為排序依據。依上面的例子來說: [code lang=”sql”] SELECT * FROM batters ORDER BY FIELD(守位,’C’,’1B’,’2B’,’3B’,’SS’,’LF’,’CF’,’RF’,’DH’); [/code] 此時結果就會變成:

    守位 名字
    C 陳峰民
    1B 潘忠韋
    2B 蔣智聰
    3B 石志偉
    SS 林智勝
    LF 曾豪駒
    CF 黃龍義
    RF 霸漢
    DH 陳金鋒

    一定有人說,為什麼剛開始就不設計用數字作為欄位內容,在有些情況之下,如果想在不更動 database 內容的話,即可使用這個方法排序。