用 PHP 打造留言板 實作筆記


Posted by Nicolakacha on 2020-09-07

這篇實作筆記是第四期程式導師計畫第九週的留言板作業筆記,並不會重頭到尾帶著做一次留言板,而是記錄一些在實作時覺得要注意的地方和值得筆記的重點,完整的程式碼可以參考 這裡,DEMO 可以參考 這裡,那我們就開始吧!

規劃產品路由與功能

頁面
- index.php
- register.php
- login.php

功能
- handle_add_post.php
- handle_register.php
- handle_login.php
- logout.php

如何使用 Session

運用 session 可以把狀態資訊存在 Server 端,同時 sessionID 是一組加密過的亂碼,具有安全性

存取 session

//要使用 Session,都要在開頭使用 session_start()
session_start();

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

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

取用 session

//要使用 Session,都要在開頭使用 session_start()
session_start();

//如果 session 內有存過 username,
//則宣告變數 $username 為剛才存的 $_SESSION['username'] 
if(isset($_SESSION['username'])) {
    $username = $_SESSION['username'];
  }

銷毀 session

    session_start();
  session_destroy();
  header('Location: ./index.php')

連線 SQL

怎麼讓 PHP 連線 SQL

<?php

$server_name = 'localhost';
$username = 'nicolakacha';
$password = '1234';
$db_name = 'nicolakacha';

$conn = new mysqli($server_name, $username, $password, $db_name);

if ($conn->connect_error) {
  die('資料庫連線錯誤' . $conn->connect_error);
};
$conn->query('SET NAMES UTF8MB4');
$conn->query('SET time_zone = "+8:00"');
?>

SQL query 實作

怎麼利用 SQL query 和 MySQL 互動
Select

$sql = "SELECT * FROM nicolakacha_users WHERE username='$username' AND password='$password'";
  $result = $conn->query($sql);  

if (!$result) {
  die($conn->error);
}

使用 sprinf 來做 select

$sql = sprintf(
    "select * from users where username='%s' and password='%s'",
    $username,
    $password
  );

使用 sprintf 來做 insert

$sql = sprintf(
  "INSERT INTO nicolakacha_users (nickname, username, password) VALUES
  ('%s', '%s', '%s')",
$nickname,
$username,
$password
);

$result = $conn->query($sql);

檢查 SQL query 使用成功

$result = $conn->query($sql);
  if (!$result) {
    die($conn->error);
  }

比對資料是否重複

利用 errno === 1062 duplicate 來比對資料是否重複

if (!$result) {
    $code = $conn->errno;
    if ($code === 1062) {
      header('Location: register.php?errCode=2');
    }
    die($conn->error);
  }

比對資料是否存在於資料庫

利用 num_rows 來達成

//如果有找到對應的資料,就把 username 存在 session 裡,導回 index.php
  if ($result->num_rows) {
    $_SESSION['username'] = $username;
    header("Location: index.php");
  //比對失敗,,導回 index.php,顯示出錯誤
  } else {
    header('Location: login.php?errCode=2');
  }

利用 GET 回傳的代號來做對應的處理

錯誤處理

設定在某條件下,用 query string 在導向時加上 errCode

if (empty($username) || empty($password)) {
    header('Location: login.php?errCode=1');
    die('請檢查資料');
  }

導回到 login.php 時,可用 $_GET 取得 query string 的值,以做對應的處理

if (!empty($_GET['errCode'])) {
  $code = $_GET['errCode'];
  $msg = 'Error';
  if ($code === '1') {
    $msg = '請輸入內容';            
  }
  echo '<p class="error">' . $msg . '</p>';
}

利用 while 迴圈拿取 table 內所有 row 的資料

<?php while ($row = $result-> fetch_assoc()) { ?>
  <div class="comment">
    <div class="avatar"></div>
    <div class="text">
      <div class="info">
        <h3 class="nickname"><?php echo escape($row['nickname']) ?></h3>
        <h3 class="time"><?php echo escape($row['created_at']) ?></h3>
      </div>
      <div class="content"><?php echo htmlspecialchars_decode($row['content']) ?></div>
    </div>
  </div>          
<?php } ?>

利用 htmlspecialchars() 來防止 input 的內容被解析

建立函式來轉換

function escape($str) {
    return htmlspecialchars($str, ENT_QUOTES); //ENT_QUOTES可以轉換單雙引號
  }

也可以反向使用 htmlspecialchars_decode 來把資料庫內的 unicode 解析成特殊符號

<h3 class="time"><?php echo escape($row['created_at']) ?></h3>
<div class="content"><?php echo htmlspecialchars_decode($row['content']) ?></div>

正常顯示 emoji

資料庫內的 table 和欄位的編碼都要設成 UTF8MB4
連線用的 conn.php 內也要改成 $conn->query('SET NAMES UTF8MB4');

在函式內使用全域變數

需使用 global

function getUserFromUsername($username) {
    global $conn;
    $sql = "SELECT * FROM nicolakacha_users WHERE username='$username'";
    $result = $conn->query($sql);
    $row = $result->fetch_assoc();
    return $row;
  }

取得 Client 端的 IP address

function getUserIpAddr() { 
    if(!empty($_SERVER['HTTP_CLIENT_IP']))
      { $ip = $_SERVER['HTTP_CLIENT_IP']; }
    elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
      { $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; }
    else { $ip = $_SERVER['REMOTE_ADDR']; } 
      return $ip; 
  }

此方法不夠嚴謹,因為 client 端的資料可串改,實際上運用時最好存取所有相關欄位。

用 PHP 包裹 HTML 的 shorthand 方法

<?php while ($row = $result-> fetch_assoc()) { ?>
    <h3 class="nickname"><?php echo escape($row['nickname']) ?></h3>
    <h3 class="time"><?php echo escape($row['created_at']) ?></h3>      
<?php } ?>

#PHP







Related Posts

AI輔導室|製作飄逸緞帶

AI輔導室|製作飄逸緞帶

OOP - 6 關於抽象

OOP - 6 關於抽象

[Web] 轉換 [icon svg] 為 [background-image]

[Web] 轉換 [icon svg] 為 [background-image]


Comments