簡介

製作註冊與登入的「功能」。

實作註冊功能

製作步驟

  1. register.php (實際「看得見」的註冊頁面)
    輸入要註冊的 username、nickname、password。
  2. 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

  1. 如何使用 PHP 的 errno 錯誤代碼呢?
  • $errCode = $conn->errno;
  • $errCode = $result->errno;
  1. 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. 外面的差異在哪?
我覺得可能是統一包覆「錯誤」?


待辦事項

  1. 前端後端的驗證比較差異?
  2. 增加先前所學的 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 提示密碼是否安全

實作登入功能

製作步驟

  1. register.php (實際「看得見」的登入頁面)
    輸入要登入的 username、password。
  2. 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 分別代表什麼意思呢?
回憶一下前面所得出的結果:

  1. 正確的帳密 → num_rows = 1
  2. 錯誤的帳密 → 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 寫判斷式,當發生「帳密輸錯」的狀況時,顯示「帳密輸入錯誤,請再輸入一次」諸如此類的提示訊息。

#board #MySQL #PHP







Related Posts

PHP、MySQL 語法基礎

PHP、MySQL 語法基礎

[ 筆記 ] 交換資料 - XMLHttpRequest、CORS、JSONP

[ 筆記 ] 交換資料 - XMLHttpRequest、CORS、JSONP

資料庫型態的選擇以及 cookie

資料庫型態的選擇以及 cookie






Comments