0%

[week 9] 利用 PHP 實作留言板 - 初階實作篇

本篇為 [BE101] 用 PHP 與 MySQL 學習後端基礎 這門課程的學習筆記。如有錯誤歡迎指正。

課程筆記:[week 9] 後端基礎 - PHP 語法、資料庫 MySQL


前置作業

在開始實作留言板之前,需先進行前置作業:

  • 規劃產品路由與功能
  • 規劃資料結構:建立資料庫

Step1. 規劃產品路由與功能

頁面

  • 留言板首頁 index.php
  • 註冊頁面 register.php
  • 登入頁面 login.php

功能

  • 新增留言 handle_add_post.php → comments 資料庫
  • 註冊 handle_register.php → users 資料庫
  • 登入 handle_login.php
  • 登出 logout.php

Step2. 規劃資料結構:建置資料庫

建立 comments 資料庫

  • id
  • nickname
  • content
  • creat_at

建立 users 資料庫

  • id
  • nickname
  • username
  • password
  • creat_at


實作留言板

可分為前後端。通常會先切出前端頁面,再加入功能,從資料庫取出並資料串聯到頁面。

Step3. 實作前端頁面

依照設計稿切出所需頁面,可參考留言板 DEMO

Step4. 實作目標功能

接著就進入重頭戲,也就是替靜態網頁加上各種功能。

PHP 常用函式

  • -> 符號:取用物件中的變數
    • 例如:$conn->error
  • require_once();:取用資料夾中其他 library
require_once("conn.php");      // 連線到資料庫
require_once("utils.php");     // 導入常用函式

echo、print_r 與 var_dump 的區別

  • echo:印出變數、字串等
    • 例如: echo "hello World"
    • 若使用 echo 輸出引用變數時(如陣列),只會輸出陣列名
  • print_r();:印出物件、陣列
    • 例如:print_r($row);
  • var_dump:印出變數型態,作用是輸出變數的詳細資訊

讀取資料

  • fetch_array():讀取資料同時,以數字與欄位名稱各存一次在陣列中
  • fetch_assoc():讀取的資料 Key 值設定為欄位名稱的陣列
  • fetch_row():讀取的資料 Key 值設定為依序的數字
<?php
  // 把 $result 資料的 Key 值設定為欄位名稱的陣列
  while($row = $result->fetch_assoc()) {
?>
  <div class="card">
    <div class="card__avatar"></div>
    <div class="card__body">
        <div class="card__info">
          <span class="card__author"><?php echo $row['nickname']; ?></span>
          <span class="card__time"><?php echo $row['created_at']; ?></span>
        </div>
        <p class="card__content"><?php echo $row['content']; ?></p>
    </div>
  </div>
  <div class="board__hr"></div>
<?php } ?>

若以 print_r($row); 印出上述程式碼,可知 $row 為陣列:

檢查是否存在

  • isset():檢查是否有此變數
  • empty():檢查是否有值
$username = NULL;
// 如果 session 中沒有存 username,就讀取 session
if(!empty($_SESSION['username'])) {
  $username = $_SESSION['username'];
}
  • query():判斷資料庫查詢是否成功
    • 順利執行回傳 true
    • 查詢的帳密有誤、查詢的指定資料庫、資料表欄位有誤等,均回傳 false
  • exit()die():兩者幾乎相同,均為輸出消息後退出程式
// 以 id 進行 DESC(遞減)排序:"後新增的留言"會排在前面
$result = $conn->query("SELECT * FROM comments ORDER BY id DESC");
// 檢查是否查詢成功
if (!$result) {
  die('Error:' . $conn->error);
}
  • sprintf():裡面可放入替代字元

例如使用 sprintf() 做 SELECT:

// handle_login.php
  $sql = sprintf(
    "SELECT * FROM users WHERE username='%s' AND password='%s'",
    $username,
    $password
  );

// 把執行結果存在 $result 這個變數中
$result = $conn->query($sql);
// 確認是否有拿到結果
if (!$result) {
  die($conn->error);
}

sprintf() 做 INSERT INTO:

// handle_add_comment.php
$sql = sprintf(
  "INSERT INTO comments(nickname, content) VALUES('%s', '%s')",
  $nickname,
  $content
  );

// handle_register.php
$sql = sprintf(
  "INSERT INTO users(nickname, username, password) VALUES('%s', '%s', '%s')",
  $nickname,
  $username,
  $password
);

conn.php 連線資料庫

【注意】由於 conn.php 放有帳號密碼等重要資料,因此在 commit 前需加入 git.ignore,不進行版本控制。

程式碼如下:

<?php
  $server_name = 'localhost';
  $username = 'heidi';
  $password = '1234';
  $db_name = 'heidiDB';

  // `mysqli` 的四個參數分別為:伺服器名稱、帳號、密碼、資料庫名稱
  $conn = new mysqli($server_name, $username, $password, $db_name);

  // 確認是否出現連線錯誤
  if (!empty($conn->connect_error)) {
    die('資料庫連線錯誤:' . $conn->connect_error);
  }

  // 設定編碼,避免出現亂碼
  $conn->query('SET NAMES UTF8');
  // 設定成臺灣時區
  $conn->query('SET time_zone = "+8:00"');
?>

index.php 顯示所有留言

<?php
  session_start();
  // 連線到資料庫
  require_once("conn.php");
  require_once("utils.php");

  $username = NULL;
  if(!empty($_SESSION['username'])) {
    $username = $_SESSION['username'];
  }

  // 以 id 進行 desc(遞減)排序,也就是"後新增的留言"會排在前面
  $result = $conn->query("SELECT * FROM comments ORDER BY id DESC");
  // 檢查是否有資料
  if (!$result) {
    die('Error:' . $conn->error);
  }
?>

儲存狀態的方式:Cookie & Session

首先要了解 Cookie 是什麼:

  • 是一種小型純文字檔案,
  • 網站伺服器會將其儲存在 client 端,以記錄使用者的相關資訊。
  • 例如:會員登入狀態、瀏覽紀錄、購物車等。

由於 HTTP 是一個無狀態協議,會把每一次收到的請求都視為獨立的行為。但伺服器能透過 response header 的 Set-Cookie 屬性,將使用者狀態記錄在 Cookie。

瀏覽器會在每次發送請求時,自動在 request header 帶上 Cookie 資料;伺服器即可藉由檢視 Cookie 內容,得知瀏覽器使用者的狀態。

但這麼做有個缺點,儲存在 client 端的 Cookie 是能夠被竄改的,因此不適合放機密或重要的資訊。這時有兩種解法:

也就是 Cookie-based session,把狀態加密後存在 Cookie。但如果加密方式以及密鑰被破解,往後仍有安全疑慮。

2. 透過 Session ID 辨識身分

全名是 Session Identifier。如此 Server 只需在 Cookie 儲存一組亂數產生的 Session ID,其餘狀態資訊則存在 Server 端。

因此,Session 其實就是一種讓 Request 變成 stateful 的機制。

如何使用 Session

儲存 Session

$_SEESION 儲存成功,會進行下列三件事:

  1. 產生 sesseion id (token)
  2. 把 username 寫入檔案
  3. set-cookie: session-id
// 使用 Session 時,均需在開頭加上 session_start()
session_start();

$username = htmlspecialchars($_POST['username']);

// 把資料存在 Session 對應的 key 裡面
$_SEESION['username'] = $username;

讀取 Session

$_SESSION 讀取資料時,會進行下列三件事:

  1. 從 cookie 裡讀取 PHPSESSID (token)
  2. 從檔案裡面讀取 session id 的內容
  3. 把內容放到 $_SESSION
session_start();

// 若 Session 內有存過 username,則 $username 為剛才存的 $_SESSION['username'] 
if(isset($_SESSION['username'])) {
  $username = $_SESSION['username'];
}

清除 Session

session_start();

// 直接清除所有 session
session_destroy();

參考資料:

  1. 27. [WEB] Cookie & Session 是什麼?
  2. 白話 Session 與 Cookie:從經營雜貨店開始