David's Blog

[CodeIgniter] 我的膝蓋被 CI 射了一箭之談 Input Class 的 get_post 方法需要注意的地方

| 4 Comments

今天一個正在進行中的案子,接到開發伙伴來的訊息,說他用POST來的資料,進去資料庫都是空白。看到這個訊息,我的心頭小鹿禁不住一陣亂撞,這下不得了,趕快測試看看:Model寫入資料庫沒問題,Controller丟資料給Model沒問題,難道是接收POST資料有問題?

於是趕緊打開心愛的Notepadd++看看程式,由於我是負責寫API接收Client丟資料來處理,然後回傳處理結果,而且Client端有可能用GET也可能用POST丟來,資料欄位多少不一定(這是我們訂好的規格),所以接收資料我是這樣寫的:

$data_array = $this->input->get_post(NULL,TRUE);

這樣我可以接收整個POST資料或GET資料,而且CI會幫我把整個$_POST或$_GET陣列過濾掉XSS然後傳回陣列給我。至於順序呢,依照CI的官方手冊說,它會先看POST再看GET。好了,這時候我們一般人的理解應該就是:如果有POST資料,它就用POST,然後傳回給我,如果沒有POST,它會再去看GET,然後一樣處理好傳回給我。

但,真的是這樣嗎? 當我單獨用$this->input->get(NULL,TRUE)或$this->input->post(NULL,TRUE)都沒問題,可以正常接收到整個GET或POST來的陣列,但是get_post這樣用就挫賽了。舉個例子,譬如我的某個API的URL點是這樣的:

http://api.example.com/v1/user/add

他可以接受POST或GET丟任意數量的資料來,我就接收起來,處理好存入資料庫。好了,如果Client端是這樣丟來的:

http://api.example.com/v1/user/add?token=9c6b99753ff8c41cda0331d634d2a4eac8e53a34

然後POST內容是這樣

name=David+Pai&mobile=0999123456

那這樣透過$this->input->get_post(NULL,TRUE)會得到什麼呢? 你以為$data_array會是這樣:

array(3) {
  ["token"]=>
  string(40) "9c6b99753ff8c41cda0331d634d2a4eac8e53a34"
  ["name"]=>
  string(9) "David Pai"
  ["mobile"]=>
  string(10) "0999123456"
}

結果不是,只剩下這樣:

array(1) {
  ["token"]=>
  string(40) "9c6b99753ff8c41cda0331d634d2a4eac8e53a34"
}

只剩下GET來的資料,POST全都不見了。

怎麼會這樣,不是說會先看POST再看GET,那至少給我POST資料吧,怎麼反而只給GET資料呢? 好的,這是我的膝蓋第一次被CI射了一箭,直到我去追了Input Class的原始碼…讓我們看看get_post這個方法:

function get_post($index = '', $xss_clean = FALSE)
{
    if ( ! isset($_POST[$index]) )
    {
        return $this->get($index, $xss_clean);
    }
    else
    {
        return $this->post($index, $xss_clean);
    }
}

看出來了嗎? 這下真相終於大白,如果你這樣用$this->input->get_post(NULL,TRUE),它會因為$_POST[NULL]判斷為false而直接跑去用$this->get(NULL,TRUE),所以得到的結果就只剩下GET來的東西了。

所以,對於CI的get_post,有幾個地方要注意:

  • 它沒有像get或post那樣,第一個參數指定為NULL就可以處理整個$_POST或$_GET
  • 如果第一個參數指定為NULL,只會得到跟get一樣的結果
  • 如果要像手冊說的那樣,第一個參數一定要指定

那麼,如果要像上面那個例子那樣,我POST和GET都要接收,然後碰到相同的以POST為先,這樣怎麼做呢? 既然CI沒有這樣的方法,那我們只好自力救濟啦,分開來接,然後再自己merge囉:

$data_get = $this->input->get(NULL, TRUE);
$data_post = $this->input->post(NULL, TRUE);
           
$data_get = is_array($data_get) ? $data_get : array();
$data_post = is_array($data_post) ? $data_post : array();

$data_array = array_merge($data_get, $data_post);

於是,這個Bug解決了,而我的膝蓋也復原了…

參考資料
CI官方手冊對於Input Class的說明

4 Comments

  1. 又学习了,不过我在项目中大多都是指定post或get的name然后接收的。

    • 呵呵,其實我平常也都是指定name接收的,只是碰到這個案子特殊,name是不知道的情況下才會這樣用
      套您的話,我也透過這次學習了^^

  2. Hey! Do you use Twitter? I’d like to follow you if that would be okay. I’m undoubtedly enjoying your blog and look forward to new posts.

    • I am adding Twitter to my social link on top right block of this page. You can follow that.

Leave a Reply

Required fields are marked *.