深入探討 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,
);
這樣就可以了

See also