簡介
製作註冊與登入的「功能」。
實作註冊功能
製作步驟
- register.php (實際「看得見」的註冊頁面)
輸入要註冊的 username、nickname、password。 - handle_register.php (註冊「功能」頁面)
(1)require_once('conn.php');
:連結資料庫
(2)$_POST
:從 register.php 獲得輸入的值
(3)$conn->query("INSERT INTO ...")
:將資料新增至MySQL
(4)偵測錯誤
:例如:帳號已被註冊、資料填寫不齊全
方法:URL query string 特性及 GET method 獲取參數的方式。
a. 帳號已被註冊:需要在 MySQL 將 username 視為「唯一」
b. 資料填寫不齊全:三個任何一個為 empty。
(5)header();
:返回留言板頁面
思考細節
思考 1:
註冊時發生的錯誤,如:帳號已被註冊、資料填寫不完全的功能如何實作?是應該放在 register.php?還是 handle_register.php?
分析
若要把「帳號已被註冊、資料填寫不完整」等訊息提示顯示在實際頁面(register.php),就需要把提示寫在 register.php,那要怎麼判斷呢?
判斷方式
在 handle_register.php 在偵測到錯誤時,於 URL 加上 query string。在註冊時,顯示:「Duplicate entry 'aaa' for key 'username'」(重複的 username),那要怎麼抓取這個訊息錯誤,並導至(header)含 query string 的 index.pnp?
註冊時,可能出現的錯誤
- 資料填寫不完整
在按下註冊按鈕,表單的 action 會導至 handle_register.php,此時可以先判斷是否為空,如果不是空的,再新增至 MySQL 裡面,若有空,就導至 register.php?errCode=1,讓 register 可以根據 query string:errCode 來顯示「資料填寫不完整」。 - 帳號已被註冊
就是依據「按下」按鈕送出「註冊」的那個地方判斷,所以我們會使用到先前在發布留言在 handle_xxx.php 加上 URL query string 的方法,讓 register.php 可以藉由 URL query string 來判斷是否跳出錯誤訊息提示。
想法套用至實際操作
思考 1
- 如何使用 PHP 的 errno 錯誤代碼呢?
$errCode = $conn->errno;
?$errCode = $result->errno;
?
- PHP 的 errno 錯誤代碼的形式為 number 或 string?
為什麼要在意他的形式呢?因為我想要使用判斷式,來判斷 errno 的錯誤代碼,然後給出相對應錯誤的訊息提示。
以下擷取部分的 code:
// test in hangle_register.php
<?php
$result = $conn->query($sql);
$errCode = $conn->errno;
// $errCode = $result->errno; // nothing happen;
if ($errCode === 1062) {
echo 'number'; //correct
} else if($errCode ==='1062') {
echo 'string'; // incorrect
}
?>
思考 2:
「新增至 MySQL」與「判斷 errCode=1062 code」哪一段 code 放前面?有差嗎?(備註:在 MySQL 已經標 username 為唯一。
這段判斷「username 重複」的 code,到底要放在哪?
$errCode = $conn->errno;
if ($errCode === 1062) {
header("Location: register.php?errCode=2");
die();
}
以下為好幾個放置段落的參考:
<?php
require_once('conn.php');
if (
empty($_POST['nickname']) ||
empty($_POST['username']) ||
empty($_POST['password'])
) {
header("Location: register.php?errCode=1");
die();
}
/*
放置位置一:錯誤
原因:註冊的內容尚未被新增至 MySQL,所以 MySQL 還沒有辦法判斷這個新的 username 是否有與 MySQL 內的 username 重複!所以沒有辦法在 $conn->error 給出「Duplicate entry 'aaa' for key 'username'」,或者是在使用 $conn-errno 時,給出 1062 數字的錯誤代碼。
*/
$nickname = $_POST['nickname'];
$username = $_POST['username'];
$password = $_POST['password'];
$sql = sprintf(
"INSERT INTO jean_users(nickname, username, password) VALUES('%s', '%s', '%s')",
$nickname,
$username,
$password
);
$result = $conn->query($sql);
/*
放置位置 2:正確
// 原因:在放入 MySQL 後,MySQL 發現 username 重複後,會給出錯誤訊息(可以從 $conn->error 或 $conn->errno 得知),這樣一來就可以導向 URL query string 到 ?errCode=2,第二個錯誤:username 已被使用。
*/
if(!$result) {
/*
放置位置 3:好像也正確耶!
原因:利用 echo 'sucessfully 偵測到 errCode = 1062'; 取代 導向 errCode=2 的頁面,可以發現仍然可以執行。
我覺得會不會是 MySQL 發現 username 重複後,就沒有將新內容「新增」至 MySQL ,所以在 $result = $conn->query("INSERT INTO...") 是失敗的,所以$result = false,所以 !$result = true,這樣就會執行判斷 errno 為 1062 的話,就是 username 已被註冊的錯誤。
*/
echo $result->error;
die();
};
header("Location: index.php");
?>
思考 3:
衍伸另一個問題:放在 if(!result)
裡面 vs. 外面的差異在哪?
我覺得可能是統一包覆「錯誤」?
待辦事項
- 前端後端的驗證比較差異?
- 增加先前所學的 js 表單驗證
(1) * required
(2) incorrect password or username
(3) 提示:
a. this username is already registered. want to login or recover your password?
b. sign in unsuccessful. Please try again.
(4) 在輸入框右邊增加 strong / weak / safe 提示密碼是否安全
實作登入功能
製作步驟
- register.php (實際「看得見」的登入頁面)
輸入要登入的 username、password。 - handle_register.php (登入「功能」頁面)
(1)require_once('conn.php');
:連結資料庫
(2)$conn->query(SELECT * ... where username = '' and password = '')
:輸入的 username 與 password 是否符合 MySQL 內的資訊?
(3)***
怎麼得知是否符合MySQL 內的 username 和 password?
(4) 錯誤訊息提示:輸入的帳號或密碼錯誤、輸入不完整。
思考細節
思考 1:
$conn->error
跟 $result->error
的差異?或者是有使用$result->error
這種方式查錯誤的指令嗎?
思考 2:
怎麼得知是否符合 MySQL 內的 username 和 password?
不論輸入正確或錯誤的帳號密碼,頁面會跳轉為空白的 handle_login.php(如下圖)。
Code
<?php
require_once('conn.php');
// 將輸入的內容存為變數
$username = $_POST['username'];
$password = $_POST['password'];
// 在 MySQL 中尋找有沒有跟 $_POST['username'] 和 $_POST['password'] 相符合的資料,如果符合就是有這個使用者帳號與密碼,就登入成功;反之,就登入失敗。
$sql = sprintf(
"SELECT * FROM jean_users where username = '%s' and password = '%s'",
$username,
$password
);
$result = $conn->query($sql);
// 判斷在 MySQL 查詢資料有沒有出錯誤:
if (!$result) {
echo $conn->error;
echo 'failed to login';
die();
}
echo 'successfully login';
?>
那這樣到底是有沒有登入成功呢?因為在 if (!$result) {die($conn->error);}
的判斷式,也沒有執行跟顯示錯誤,那到底是哪裡出了錯呢?
現在就來嘗試看看印出 $result
或者是 $conn->error
// 嘗試增加這兩行,看有沒有辦法印出什麼資訊。
echo 'result:' . $result . '<br>';
echo 'conn->error:' . $conn->error . '<br>';
一樣輸出結果為一片空白:
這時候,靈光一閃,好像記得在 PHP 印出結果,好像有幾個方法:var_dump($var);
、echo 'string';
、print_r($var)
,既然前面使用 echo
失敗,那來嘗試簡易版的print_r($result)
。得到輸出結果:
mysqli_result Object ( [current_field] => 0 [field_count] => 5 [lengths] => [num_rows] => 0 [type] => 0 )
居然有輸出結果了耶!那來試試看正確與錯誤的帳密有沒有什麼不同好了。
正確的 username 與 password,輸出結果為:
mysqli_result Object ( [current_field] => 0 [field_count] => 5 [lengths] => [num_rows] => 1 [type] => 0 )
錯誤的 username 與 password,輸出結果為:
mysqli_result Object ( [current_field] => 0 [field_count] => 5 [lengths] => [num_rows] => 0 [type] => 0 )
我發現正確與錯誤的帳密不同之處在於 num_rows
,再來看怎麼樣把這個參數取出來,研究一下。
現在要嘗試看看,如何把 num_rows
取出,利用在 PHP 語法基礎提過的一些取參數的方法如下:
print_r($result['num_rows']); // 錯誤
print_r($result_num_rows); // 錯誤
print_r($result->nums_rows); // 正確
知道怎麼取出來 num_rows
了,但是我忽略了一個最重要的問題,那 num_rows
= 1 或 0 分別代表什麼意思呢?
回憶一下前面所得出的結果:
- 正確的帳密 →
num_rows = 1
- 錯誤的帳密 →
num_rows = 0
到底是代表 Boolean 值(1 為 true;0 為 false)呢?還是代表幾筆符合?
所以現在在資料庫建立兩筆使用者資料,其中密碼設成一樣的,來嘗試看看查詢 password = 'aaa'
。下表建立兩筆使用者的資料:
id | username | password |
---|---|---|
1 | aaa | aaa |
2 | bbb | aaa |
MySQL 指令來查詢 password = 'aaa'
得:$conn->query("SELECT * FROM jean_users where password = 'aaa'");
輸出結果如下:
mysqli_result Object ( [current_field] => 0 [field_count] => 5 [lengths] => [num_rows] => 2 [type] => 0 )
num_rows: 2
再對照上表,可以發現也是兩筆符合 passwrod = 'aaa'
,然後在 num_rows => 2
,所以可以從這裡得知: num_rows
的意思是「符合查詢 MySQL 條件的資料筆數」。
(補充)此為 code 執行後,詳細印出帳密與輸出結果
因為前面段落只有以文字的形式印出 print_r($result->num_rows);
,在此以比較詳細的印出結果來表示:
在 Error:
與 errno: 0
可以得知在 MySQL 搜尋特定條件的資料,沒有發生錯誤,但是我們的目標是找到符合的資料,而不是在搜尋時有無出錯。
<?php
require_once('conn.php');
$username = $_POST['username'];
$password = $_POST['password'];
$sql = sprintf(
"SELECT * FROM jean_users where username = '%s' and password = '%s'",
$username,
$password
);
$result = $conn->query($sql);
// 印出正確 or 錯誤帳密的輸出結果,可先跳過,直接下方圖片的看輸出結果。
echo 'correct username and passwrod is "aaa" and "aaa". <br>';
echo 'input username: ' . $username . '<br>';
echo 'input password: ' . $password . '<br>';
echo '<br>';
echo '<br> num_rows:';
print_r($result->num_rows); // num_rows=1
echo '<br>';
echo 'error:' . $conn->error . '<br>'; // nothing
echo 'errno(0=no err):' . $conn->errno; // 0 = no err occured.
if (!$result) {
echo $conn->error;
die();
}
// header("Location: index.php");
?>
正確的 username 與 password:
錯誤的 username 與 password:
從這兩張輸出結果,可以發現輸入帳號密碼正確與否的差異為 num_rows
,當正確的時候num_rows=1
,錯誤的時候num_rows=0
,也就是說在 MySQL 尋找 username ='帳號'
與 password = '密碼'
時,有符合的會回傳 1
,若不符合,則回傳 0
。
思考 3:
整理一下在製作登入及註冊功能時,需要考慮到可能會發生什麼樣的問題,以及要製作並套用該功能?
可能發生的問題:
- 登入時,可能會遇到的問題:
- 帳密其中一個輸入錯誤。
- 帳號或密碼填寫不完整。
- 註冊時,可能會遇到的問題:
- 此帳號已被註冊。
- 填寫不完整。
可能發生的問題:
想好大概會實際操作會遇到的問題後,開始判斷是需要將 action、if 寫在(以註冊為例)register.php(實際頁面) 還是 handle_register.php(功能製作)上?
在按下「註冊」按鈕時,代表送出內容,若要在輸入值上做一些判斷,就需要在handle_register.php
透過 PHP 取得參數的 $_GET['']
或 $_POST['']
來判斷。
進一步需要在實際頁面做出反應的話,就需要連接實際頁面的 php 檔案與功能製作的 php,那要怎麼連接呢?在此練習的方法是以 header
頁面導向至自己實際頁面並附上 URL query string(藉此傳回在 handle_register.php
判斷得出的結果),然後可以再實際頁面的 register.php 寫判斷式,當發生「帳密輸錯」的狀況時,顯示「帳密輸入錯誤,請再輸入一次」諸如此類的提示訊息。