PHP 會員留言板系統

延續前篇:PHP 會員登入系統

結構

  • MySQL留言資料庫(message)
  • 留言板頁面(msg.php)
  • 留言新增功能(msg-add.php)
  • 留言刪除功能(msg-del.php)

MySQL留言資料庫(message)

編碼:utf8_unicode_ci

名稱 型態 備註
id int AI
guest_id int
content text
date date

留言板頁面(msg.php)

  • 結合 message 的 guest_id 與 member 的 id
    在 message 的提取迴圈裡面,寫下指令 mysql_query( 讀取 member 資料表,WHERE條件設定 id=’$row[guest_id]’ )
<?php
mysql_connect("localhost", "root", "");
mysql_select_db("hahow");
mysql_query("SET NAMES UTF8");

//如果沒有登入的SESSION,就轉址
if (isset($_SESSION["email"])==FALSE) {
 header('Location: login.php');
}
?>
<!DOCTYPE html>
<html>
<head>
 <title>Message Board</title>
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<br><br><br><br>
<div class="container">
 <h3 class="text-center">會員留言板</h3>
 <hr>

<?php
$re=mysql_query("SELECT * FROM message");
//檢査有沒有留言數( message 資料表的資料筆數是否大於0):參考
if(mysql_num_rows($re)>0){
 //如果有留言,一筆一筆印出留言
 //使用 while 迴圈從 message 資料表一筆一筆讀取留言的方法:參考
 while($row=mysql_fetch_array($re))
 {
 //mysql_query() 括弧裡面的指令,使用變數時,要用單引號包起來,參考
 $memberRe=mysql_query("SELECT * FROM member WHERE id='$row[guest_id]'");
 $memberRow=mysql_fetch_array($memberRe);

 echo "<div class=\"panel panel-default\">
 <div class=\"panel-heading\">$memberRow[name] 
 <span class=\"pull-right\">$row[date]
 //以更改網址的方式強制讀取GET表單資料
 //正常的流程:使用者在GET表單輸入資料→送出轉址→新網址裡面有使用者輸入的資料
 //轉址到有特定 GET 表單 id 的 msg-del.php
 <a href=\"msg-del.php?id=$row[id]\" class=\"btn btn-danger btn-xs\">
 DELETE
 </a>
 </span>
 </div>
 <div class=\"panel-body\"> $row[content]
 </div>
 </div>";
 }
}

else{
 //沒有留言的話
 echo "<p class=\"text-center\">沒有任何訊息!</p>";
}
?>
 <hr>
 <p class="pull-right">以 <?php echo $_SESSION["name"]; ?> 的身份留言</p>
 <h4>新增留言</h4>
 <form action="msg-add.php" method="post">
 <textarea name="msg" class="form-control"></textarea>
 <br>
 <input type="submit" name="submit" value="送出" class="btn btn-primary btn-sm pull-right">
 </form>
</div>
</body>
</html>

留言新增功能(msg-add.php)

<?php
require("msg.php");

//設定日期格式
$date=date("Y-m-d");

$SaveNewMsg=mysql_query("INSERT INTO message(guest_id, content, date) 
//對應 msg.php 新增留言區的 POST表單
VALUES('$_SESSION[id]','$_POST[msg]','$date')");

//檢査
if(!$SaveNewMsg){
 echo "留言失敗";
}else{
 echo "留言成功";
}

//新增完畢轉回留言板
header('Location: msg.php');
?>

留言刪除功能(msg-del.php)

<?php
require("msg.php");

//抓取 msg.php 布好的GET表單的刪除ID
mysql_query("DELETE FROM message WHERE id='$_GET[id]'");

//刪除完畢轉回留言板
header('Location: msg.php');
?>

範例:會員登入網站

測試用帳號:123@123
測試用密碼:123

結語

  • 善用 Sublime + SFTP,存檔即上傳主機可以節省時間
  • bug 可能出現在各種地方,資料庫連線,上一份檔案的 session 沒有寫好,都可能有錯,必須小心

PHP 會員登入系統

結構

  • MySQL會員資料庫(member)
  • 登入頁面(login.php)
  • 註冊頁面(regist.php)
  • 會員頁面(member.php)

MySQL會員資料庫結構(member)

編碼:utf8_unicode_ci

名稱 型態 備註
id int AI
name text
email text
password text
nick text

登入頁面 login.php

<?php
//解決網頁亂碼問題
mysql_query("SET NAMES UTF8");

$error_flag = FALSE;
$notfound_flag = FALSE;

//對資料庫伺服器進行連線,並選擇對應的會員資料庫
mysql_connect("localhost", "root", "");
mysql_select_db("hahow");

//如果收到 POST 表單送來的登入資料,到資料庫檢査是否有這個人存在
//(使用 mysql_query("SELECT ...... "),然後把回傳的東西透過 mysql_fetch_array(......) 來檢査)
$result=mysql_query("SELECT * FROM member");


//如果有找到,檢査密碼是否相符
while($row = mysql_fetch_array($result)){

 //先檢査使用者有沒有輸入資料
 if(empty($_POST["email"])==FALSE && empty($_POST["pass"])==FALSE){

 //防範攻擊
 $userEmail=$_POST["email"];
 $userEmail=mysql_real_escape_string($userEmail);
 $userPassword=$_POST["pass"];
 $userPassword=mysql_real_escape_string($userPassword);
 //有輸入資料的話,再來看輸入的email跟資料庫是否一致
 if($row["email"]==$_POST["email"]){

 if($row["password"]==$_POST["pass"]){
 //如果相符合,則設定 Session(記得要先 session_start()!),並轉址到會員中心(member.php)
 session_start();
 $_SESSION["email"]=$_POST["email"];
 $_SESSION["password"]=$_POST["pass"];
 $_SESSION["name"]=$row["name"];
 $_SESSION["id"]=$row["id"];

 //讓網頁轉址的 PHP 寫法:header('Location: member.php');
 header('Location: member.php');

 }else{
 //如果不符合,則設定 $error_flag 為 TRUE,繼續顯示網頁内容
 $error_flag = TRUE;
 break;
 }

 }else{
 //如果沒有找到,則設定 $notfound_flag 為 TRUE,繼續顯示網頁内容
 $notfound_flag = TRUE;
 }

 }else{
 //如果沒收到,繼續顯示網頁内容
 }
 
}
?>

<!DOCTYPE html>
<html>
<head>
 <title>會員登入</title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<br><br><br><br>

<div class="container">
 <div class="row jumbotron">
 <div class="col-md-6 col-md-offset-3">
 <h2 class="text-center">會員登入</h2><br/>
 <form action="<?php echo htmlspecialchars($_SERVER[">" method="POST">
 <input class="form-control input-lg" id="pass" type="text" name="email" required="TRUE" placeholder="E-Mail"/><br/>
 <input class="form-control input-lg" id="pass" type="password" name="pass" required="TRUE" placeholder="密碼"/><br/>
 <input class="btn btn-primary btn-lg btn-block" type="submit" value="登入"/><a class="btn btn-default btn-lg btn-block" href="register.php">會員註冊</a>
 </form>
 <br/>
 <?php if($error_flag){ ?>
  <div class="alert alert-danger alert-dismissible" role="alert">
  <button class="close" type="button" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button> 密碼錯誤!
  </div>
 <?php }?>

 <?php if($notfound_flag){ ?>
  <div class="alert alert-danger alert-dismissible" role="alert">
  <button class="close" type="button" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button> 未找到本使用者,請重新確認!
  </div>
 <?php }?>
 </div>
 </div>
</div>
</body>
</html>

會員頁面 member.php

<?php
//關閉系統提示
error_reporting(0);
session_start();

// 檢査是否有登入(Session 有被設定)
if(isset($_SESSION["email"])==FALSE) {
 //如果沒有,轉址到登入頁面
 header('Location: login.php');
}
?>

<!DOCTYPE html>
<html>
<head>
 <title>會員中心</title>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<br><br><br><br>
<div class="container">
 <div class="row jumbotron">
 <div class="col-md-6 col-md-offset-3"> 
 <h2 class="text-center">會員登入</h2>
 <!-- 在這裡請把 HTML 修改一下,讓他可以顯示出現在登入的會員姓名! -->
 <!-- 提示:先取用 Session 中登入的人的 ID,然後再去資料庫中找出對應 ID 的姓名。 -->
 <h3>姓名:<?php echo $_SESSION["name"];?></h3>
 <a class="btn btn-primary btn-lg btn-block" href="msg.php">留言板</a>
 </div>
 </div>
</div>
</body>
</html>

註冊頁面 register.php

<?php

$noInfo_flag = false;
$duplicate_flag = false;
$success_flag=false;
$fail_flag=false;

//關閉提示
error_reporting(0);
mysql_connect("localhost", "root", "");
mysql_select_db("hahow");
mysql_query("SET NAMES UTF8");

//送出鍵按出後
if(isset($_GET['submit'])==true){
 //檢査所有欄位有沒有輸入
 if(empty($_GET['email'])==true || empty($_GET['password'])==true || empty($_GET['name'])==true || empty($_GET['nick'])==true){
 //有缺的話,叫使用者寫完
 $noInfo_flag = true;
}
}

//送出鍵按出,使用者也有輸入資料的情況
if(isset($_GET['submit'])==true && empty($_GET['email'])==false && empty($_GET['password'])==false && empty($_GET['name'])==false && empty($_GET['nick'])==false){

 //用 WHERE 檢查是否重複註冊
 //mysql_query()裡面要用'' 參考
 $result=mysql_query("SELECT * FROM member WHERE email='$_GET[email]'");
 $row=mysql_fetch_array($result);
 if($row["email"]==$_GET["email"]){
 重覆到的話,退回
 $duplicate_flag = true;
 }else{
 
 //沒有重複到,寫入資料
 $SaveNewData=mysql_query("INSERT INTO member (name, email, password, nick) VALUES('$_GET[name]','$_GET[email]','$_GET[password]','$_GET[nick]')");
 
 //檢查註冊是否成功
 if(!$SaveNewData){
 $fail_flag=true;
 }else{
 $success_flag=true;
 }
 }
}

?>

<!DOCTYPE html>
<html>
<head>
 <title>會員註冊</title>
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<br><br><br><br>
<div class="container">
 <div class="row jumbotron">
 <div class="col-md-6 col-md-offset-3"> 
 <h2 class="text-center">會員註冊</h2>
 <hr>
 <form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="GET">
 <div class="form-group">
 <label for="input-email">Email 帳號 *</label>
 <input type="email" class="form-control" id="input-email" name="email">
 </div>
 <div class="form-group">
 <label for="input-name">真實姓名 *</label>
 <input type="text" class="form-control" id="input-name" name="name">
 </div>
 <div class="form-group">
 <label for="input-nick">匿稱 *</label>
 <input type="text" class="form-control" id="input-nick" name="nick">
 </div>
 <div class="form-group">
 <label for="input-password">密碼 *</label>
 <input type="password" class="form-control" id="input-password" name="password">
 </div>
 </div>
 <br>
 <input type="submit" class="btn btn-primary btn-lg btn-block" value="註冊" name="submit">
 </form>
 <?php if($noInfo_flag){ ?>
 <div class="alert alert-danger alert-dismissible" role="alert">
 <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 請輸入所有欄位!
 </div>
 <?php }?>

 <?php if($duplicate_flag){ ?>
 <div class="alert alert-danger alert-dismissible" role="alert">
 <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 此 Eamil 帳號已經註冊過!
 </div>
 <?php }?>

 <?php if($success_flag){ ?>
 <div class="alert alert-danger alert-dismissible" role="alert">
 <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 註冊成功!
 </div>
 <?php }?>

 <?php if($fail_flag){ ?>
 <div class="alert alert-danger alert-dismissible" role="alert">
 <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 註冊失敗!
 </div>
 <?php }?>
 </div>
 </div>
</div>
</body>
</html>

範例:會員登入網站

測試用帳號:123@123
測試用密碼:123

接續後篇:會員留言板系統

PHP與MySQL的資料庫互動

PHP版本:7.3.11
MySQL版本:8.0.18
執行環境:Mac OS 10.15.3

資料庫連線 mysqli_connect()

網址127.0.0.1
登入帳號root(預設)
密碼zaq1@WSX

選擇資料庫

選擇test資料庫(帶有水庫圖示的就叫做資料庫,資料庫底下的叫做資料表)

$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');

檢查是否連線成功

if(!$connect){
    echo '連線失敗';
}else{
    echo '連線成功';
}

抓取資料與迴圈處理

test裡面有一張資料表mylist,記載了一些水果的品項與價格

⑴ 抓取資料mysqli_query(),並且存成變數$re

mysqli_query()要放2個參數,
第1個是mysqli_connect的連線資訊
第2個放MySQL的語法
$re=mysqli_query($connect, 'SELECT * FROM mylist');

⑵ 回傳資料處理 mysqli_fetch_array()

mysqli_fetch_array()在沒寫迴圈下只會回傳第一筆符合條件的資料
所以搭配while迴圈,只要條件符合,就會一筆筆將資料丟出來

while($row=mysqli_fetch_array($re)){
    echo $row['name'].'<br/>';
}

進階:處理完整資料

資料表mylist裡面有「水果名」、「序號」、「價格」這3項資訊
更進一步地將所有資料都印出來

$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');
if(!$connect){
    echo '連線失敗';
}else{
    echo '連線成功';
}

echo '<hr>';

echo '<table border="1">';

echo '<caption>水果價目表</caption>';

echo 
    '<thead>
        <tr>
            <th>序號</th>
            <th>水果名</th>
            <th>價格</th>
        </tr>
    </thead>';

echo '<tbody>';

$re=mysqli_query($connect, 'SELECT * FROM mylist');
while($row=mysqli_fetch_array($re)){
    echo '<tr>';

    echo '<td align="center">'.$row['index'].'</td>';
    echo '<td>'.$row['name'].'</td>';
    echo '<td>$'.$row['price'].'元</td>';

}

動態查詢:MySQL指令塞變數

mysqli_query()塞變數$_GET['name'],需注意引號" / '必須要包成以下格式

"SELECT * FROM mylist WHERE name='$_GET[name]'"

水果店今天要做查價網頁,使用者在網頁輸入水果名,系統回傳價格

<h2>請輸入品項名</h2>
<form action="" method="GET">
    <input type="text" name="name" />
    <input type="submit" name="submit" value="查詢" />
</form>
$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');

if(!isset($_GET['submit'])) return;

$re=mysqli_query($connect, "SELECT * FROM mylist WHERE name='$_GET[name]'");
$row=mysqli_fetch_array($re);
if(!$row){
    echo '查無此人';
}else{
    echo '查詢品項:'.$row['name'].'<br>';
    echo '價格:$'.$row['price'].'元';
}

總筆數 mysqli_num_rows()

mysqli_num_rows()會回傳$result總共抓了幾筆資料

$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');
$re=mysqli_query($connect, "SELECT * FROM mylist");
$total=mysqli_num_rows($re);
echo '總共有'.$total.'筆資料';

SQL Injection 資料庫隱碼注入攻擊

假設今天是做水果會員網站,用戶可以用水果ID登入,目前一共有4個ID

<h2>請輸入ID登入</h2>
<form action="" method="GET">
    <input type="text" name="id" />
    <input type="submit" name="submit" value="登入" />
</form>
$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');

if(!isset($_GET['submit'])) return;

$re=mysqli_query($connect, "SELECT * FROM mylist WHERE name='$_GET[id]'");
$row=mysqli_fetch_array($re);
if(!$row){
    echo '登入失敗';
}else{
    echo '您已經登入'.'<br>';
    echo '您的ID:'.$row['name'].'<br>';
}

如果有駭客在前台<input>輸入' OR '1'='1

這時,後端的MySQL搜尋語法就會變成

SELECT * FROM mylist WHERE name='' OR '1'='1'

'1'='1'一定會為true,所以駭客就能騙過程式,成功登入系統

隱碼注入對策 mysqli_escape_string()

mysqli_escape_string()可以讓值變成不具程式碼效力的普通字串
因此建議掛在任何使用者輸入的內容上,以防止被駭客攻擊

$connect=mysqli_connect('127.0.0.1', 'root', 'zaq1@WSX', 'test');

if(!isset($_GET['submit'])) return;

$safe_id=mysqli_escape_string($connect, $_GET['id']);
$re=mysqli_query($connect, "SELECT * FROM mylist WHERE name='$safe_id'");
$row=mysqli_fetch_array($re);
if(!$row){
    echo '登入失敗';
}else{
    echo '您已經登入'.'<br>';
    echo '您的ID:'.$row['name'].'<br>';
}

MySQL 基本知識

資料型別

參數名稱意義
text文字
varchar固定型態的文字
int整數數字
bool布林值

phpmyadmin介面操作:建立資料庫與資料表

點選右邊水庫圖示旁的新增後,輸入新資料庫名,編碼選uf8_unicode_ci
(避免某些中文國字出不來)

接者建立資料庫底下的資料表,輸入好資料表名後,按「建立」

建立資料欄位,AI代表Automatic Increment,自動累加
如果資料欄位有索引欄,可以勾選AI,他每新增一筆就會給+1

MySQL指令操作

MySQL指令操作可以透過端末終端機,或從phpmyadmin的SQL頁籤進入,撰寫指令

建立資料表

CREATE TABLE newsheet2(
 ID INT,
 name TEXT,
 price INT,
 weight INT
)

塞資料進去

在資料表newsheet塞一筆資料進去

欄位
nameapple
price100
weight12
INSERT INTO newsheet(name, price, weight) VALUES ('apple', 100, 12)

刪資料

要把name="badbad"這筆資料刪掉

DELETE FROM newsheet WHERE name="badbad"

撈資料

SELECT * FROM newsheet WHERE name="apple"

SELECT後面接要搜尋的「資料欄位」,*代表全部欄位
WHERE後面接搜尋條件,可省略

SELECT * FROM newsheet WHERE name="apple" AND price=100

多條件篩選,關鍵字意涵請參考下表

關鍵字意思
AND
OR
BETWEEN ★★★ AND ☆☆☆指定區間

更新(修改)資料

要把banana的價格改成500

UPDATE newsheet SET price=500 WHERE name="banana"

PHP 陣列

陣列

建立陣列

$fruits=array('apple', 'banana', 'orange', 'grape');

印出整個陣列

print_r($fruits);
( [0] => apple [1] => banana [2] => orange [3] => grape )

印出特定索引值的陣列

print_r($fruits[0]);
apple

自訂索引值

PHP的陣列可以自訂索引值,做出類似物件的效果

$fruits2=array('name'=>'apple', "price"=>100, 'weight'=>10);
print_r($fruits2['name']);
apple