深入探討 CodeIgniter Input Class 核心程式流程

codeigniter_2
為什麼會寫到這篇呢?當然是有網友希望可以幫他解決困難,由於問題的解答需要比較長的文章解釋,就寫出這一篇啦。在我 2009 年開始推廣到現在,相信在台灣已經有不少人開始使用 CodeIgniter,自己覺得非常感動 XD,也非常欣慰,希望把好東西推廣給大家知道。廢話不多說,先來說說問題點,先前發表的一篇 [CodeIgniter] 利用 jQuery 簡易驗證使用者帳號/Email 最後有人留言針對 input->post() 在中文官網上面的 Input Class 教學有些疑慮,底下我先來說明網友的問題點。

input->post 如果為空,則塞進去資料庫竟然是 0

該網友引用了 Input Class 中文文件的內容
使用 POST, COOKIE, 或 SERVER 資料CodeIgniter 提供三個讓你取出 POST, COOKIE 或 SERVER 中項目的補助函數。使用這些函數的主要便利性在於, 它們會確認並檢視是否這些項目已被設定並且在未設定時回傳 false (boolean) , 而不是直接取出 ($_POST[‘something’]),官方範例↓這讓你可以方便地使用資料而不必預先測試它們是否存在。不然, 通常你可能會像這樣做:if ( ! isset($_POST[‘something’])) { $something = FALSE; } else { $something = $_POST[‘something’]; }
網友敘述:
但是為什麼還是為設定為0呢??這應該是檢查資料有沒有被設定而已,那我如果沒有輸入,又怎麼會出現 0 呢??
網友希望 $username = $this->input-post("username"); 能幫他判斷如果 username 沒有資料,就直接回傳 NULL,Insert 到資料庫時,應該是 NULL 而不是 0。

程式範例

網友其實沒有錯,根據文件上顯示,如果 $something = $this->input->post("something"); 取值過後,如果系統沒有 $_POST[‘something’],則會回傳 FALSE,我猜網友在跟 model 作搭配的時候使用了底下的寫法來塞值進入資料庫:
$data = array(
    "username" => $this->input->post('username'),
    "passwd" => $this->input->post('passwd'), 
    "email" => $this->input->post('email'),
);
$this->load->model('members'); 
$this->members->register($data);  
假設如果沒有 $_POST[’email’] 的話,該欄位就會被設定為 0,原因很簡單,就是出在要塞值進入資料庫的時候,程式針對資料的型態判斷啦,底下從最開始取得 $_POST 資料開始說起。

Input Class 核心程式流程($_POST)

程式剛開始是這樣寫 $username = $this->input->post("username"); 對應到系統 system/core/Input.php 程式裡面的 post method:
/**
* Fetch an item from the POST array
*
* @access	public
* @param	string
* @param	bool
* @return	string
*/
function post($index = NULL, $xss_clean = FALSE)
{
	// Check if a field has been provided
	if ($index === NULL AND ! empty($_POST))
	{
		$post = array();

		// Loop through the full _POST array and return it
		foreach (array_keys($_POST) as $key)
		{
			$post[$key] = $this->_fetch_from_array($_POST, $key, $xss_clean);
		}
		return $post;
	}
	
	return $this->_fetch_from_array($_POST, $index, $xss_clean);
}
當你傳入 username 的參數,就會直接會被送到 $this->_fetch_from_array($_POST, $index, $xss_clean); 接著我們看 _fetch_from_array 這 private method
/**
 * Fetch from array
 *
 * This is a helper function to retrieve values from global arrays
 *
 * @access	private
 * @param	array
 * @param	string
 * @param	bool
 * @return	string
 */
function _fetch_from_array(&$array, $index = '', $xss_clean = FALSE)
{
	if ( ! isset($array[$index]))
	{
		return FALSE;
	}

	if ($xss_clean === TRUE)
	{
		return $this->security->xss_clean($array[$index]);
	}

	return $array[$index];
}
有看到程式會先判斷 $_POST[‘username’] 是否存在,如果不存在則回傳 FALSE,也就是先前的 $username 就會是個 bool 函數,其值是 FALSE,那接著我們來看看 Database 怎麼處理傳入的 Value 值,先到 system/database 打開 DB_driver.php,找到 Insert 資料庫的 method:
/**
 * Generate an insert string
 *
 * @access	public
 * @param	string	the table upon which the query will be performed
 * @param	array	an associative array data of key/values
 * @return	string
 */
function insert_string($table, $data)
{
	$fields = array();
	$values = array();

	foreach ($data as $key => $val)
	{
		$fields[] = $this->_escape_identifiers($key);
		$values[] = $this->escape($val);
	}

	return $this->_insert($this->_protect_identifiers($table, TRUE, NULL, FALSE), $fields, $values);
}
會看到處理 value 是在 $values[] = $this->escape($val);,接著我們找到 escape 這 method:
/**
 * "Smart" Escape String
 *
 * Escapes data based on type
 * Sets boolean and null types
 *
 * @access	public
 * @param	string
 * @return	mixed
 */
function escape($str)
{
	if (is_string($str))
	{
		$str = "'".$this->escape_str($str)."'";
	}
	elseif (is_bool($str))
	{
		$str = ($str === FALSE) ? 0 : 1;
	}
	elseif (is_null($str))
	{
		$str = 'NULL';
	}

	return $str;
}
有看到解答了嗎?關鍵就在 is_bool($str) 這判斷式,因為 $username 值為 FALSE,所以在這裡會被設定為 0,也就是為什麼網友會說資料庫欄位怎麼都是一堆 0,原因就是這樣啦,當然也不是沒有解法阿,其實解法很容易,只是自己還是要判斷一下:
$data = array(
    "username" => ($username) ? $username : NULL,
    "passwd" => ($passwd) ? $passwd : NULL,
    "email" => ($email) ? $email : NULL,
);
這樣就可以了
  • V03071106

    恩恩….原來是這樣@@   我還以為到底是哪邊出問題呢…
    想說PHP學習已經一段時間了,結果還是第一次遇到塞 0 進資料庫的..
    害我那天差點把整個環境重新安裝0….0

    後來就自行改用
    if($this->input->post(‘name’) != false)
    有點不符合他的內建方式,不過可以動總是好的…

    現在發現CI真的好玩,根本就是懶人架構….推!!

  • XD 我不敢說其他 Framework 有沒有這麼懶人,但是 CI 就是架構不大,容易瞭解,文件清楚 …..

  • V03071106

    我學習PHP沒有很長的一段時間,所以我只能找一些讓自己可以覺得比較有成就感的方式來讓自己強迫學習,謝謝您的教學,希望未來我也能貢獻甚麼^^ 謝謝。

  • V03071106

    我學習PHP沒有很長的一段時間,所以我只能找一些讓自己可以覺得比較有成就感的方式來讓自己強迫學習,謝謝您的教學,希望未來我也能貢獻甚麼^^ 謝謝。

  • Hi, 希望此部落格可以幫助您許多,謝謝

  • Pingback: 為什麼要選擇 CodeIgniter PHP Framework? | 小惡魔 - 電腦技術 - 工作筆記 - AppleBOY()

  • Joe

    不得不说,这样的分析方法真是相当nice。。。

  • appleboy48

    感謝指教 ..