'使用PHP编写留言板Version2.0'

1.改进

在上一篇使用PHP编写留言板的结尾处,写了留言板的不足之处。经过一周的PHP学习,打算重新写一遍留言板,对之前的不足做出改进。

1.1删除功能

新的留言板添加了编辑选项,可以修改留言内容或删除留言。

1.2重复提交

新的留言板添加了session,而且在提交表单之后会自动跳转到留言板的主页,从而解决了重复提交的Bug。

1.3时间显示问题

在上一篇其实已经写出了解决方案,新的留言板会使用上次的方案来解决时间显示问题。

2.功能介绍

2.1删除留言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 
session_start();
if(empty($_SESSION['userName'])){
header("location:login_page.php");
}
$userName=$_SESSION['userName'];
$message_ID=$_GET['id'];
$ip="localhost";
$account="root";
$password="root";
$connect=new pdo("mysql:host=$ip;dbname=MessageBoard",$account,$password);
$query="delete from messages where ID=$message_ID;";
$res=$connect->prepare($query);
$res->execute();
?>

删除功能的实现,是通过在编辑页面点击链接时使用GET方式传入留言的ID,获得留言的ID后,通过PDO操作数据库删除留言。

2.2登陆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
session_start();
header("Content-Type:text/html; charset=utf-8");
$ip="localhost";
$account="root";
$password="root";
//连接MySQL数据库
try{
//连接数据库
$connect=new pdo("mysql:host=$ip;dbname=MessageBoard",$account,$password);
echo "Connect successed."."<br>";
//用户提交登陆信息
$userName=$_POST['userName'];
$pwd=$_POST['password'];
//数据库查询语句
$query="select Password from user where userName='$userName'";
//
$res=$connect->prepare($query);
$res->execute();
$result=$res->fetch(PDO::FETCH_ASSOC);
if($pwd==$result['Password']){
echo "登陆成功";
$_SESSION['userName']=$userName;
header("location:home.php");
}else{
echo "登陆失败";
}
}catch(PDOException $error){
echo "Connect failed.".$error->getmessage();
}
$connect=NULL;
?>

2.3退出

1
2
3
4
5
6
<?php 
session_start();
unset($_SESSION['userName']);
session_destroy();
header("location:login_page.php");
?>

2.4发布留言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php 
session_start();
if(empty($_SESSION['userName'])){
header("location:login_page.php");
}
$userName=$_SESSION['userName'];
?>
<?php
$ip="localhost";
$account="root";
$password="root";
$connect=new pdo("mysql:host=$ip;dbname=MessageBoard",$account,$password);
$receiver=$_POST['receiver'];
$time=date("Y-m-d H-i-s");
$content=$_POST['content'];
$query="insert into `messages` (sender,receiver,time,content) values('$userName','$receiver','$time','$content');";
$res=$connect->prepare($query);
$res->execute();
$connect=NULL;
header("location:home.php");
?>

2.5注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
header("Content-Type:text/html; charset=utf-8");
$ip="localhost";
$account="root";
$password="root";
//连接MySQL数据库
try{
//连接数据库
$connect=new pdo("mysql:host=$ip;dbname=MessageBoard",$account,$password);
echo "Connect successed."."<br>";
//用户提交登陆信息
$userName=$_POST['userName'];
$pwd=$_POST['password'];
$Name=$_POST['Name'];
//数据库查询语句
$query="insert into `user` (userName,Password,Name) value('$userName','$pwd','$Name');";
//
if($_POST['userName']==""or$_POST['password']==""or$_POST['Name']==""){
echo "不能为空";
}else{
$res=$connect->exec($query);
if($res!=0){
echo "注册成功";
}else{
echo "注册失败";
}
}
}catch(PDOException $error){
echo "Connect failed.".$error->getmessage();
}
$connect=NULL;
?>

2.6编辑留言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php 
session_start();
if(empty($_SESSION['userName'])){
header("location:login_page.php");
}
$userName=$_SESSION['userName'];
$message_ID=$_POST['ID'];
$ip="localhost";
$account="root";
$password="root";
$receiver=$_POST['receiver'];
$time=date("Y-m-d H-i-s");
$content=$_POST['content'];
$ID=$_POST['ID'];
$connect=new pdo("mysql:host=$ip;dbname=MessageBoard",$account,$password);
$query="update `messages` set receiver='$receiver',time='$time',content='$content' where ID='$ID';";
$res=$connect->prepare($query);
$res->execute();
$connect=NULL;
header("location:home.php");
?>

编辑留言功能实现的方式和删除留言的一样的,都是通过在编辑页面使用GET方式将留言ID传入到编辑留言页面,再通过一个隐藏的表单将留言ID所使用POST方式传入到编辑页面功能中,然后再使用PDO操作数据库编辑留言。

2.7Session

1
2
3
4
5
6
7
session_start();
$_SESSEION['userName']=$userName;
if(empty($_SESSION['userName'])){
header("location:login_page.php");
}
unset($_SESSION['userName'];
session_destory();

2.7.1session_start()

在把用户信息存储在$_SESSION数组之前,必须先启动会话。
session_start()就是启动会话的函数。session_start()这个函数一般写在最开始的地方,在<html>标签之前即可。

2.7.2存储或取回session变量

存储或取回都需要使用到$_SESSION变量。
上面的代码中,将用户登录名(userName)存储到了$_SESSION中,在后面比对是也使用的是userName。

2.7.3简单的登陆判断

使用if判断$_SESSION[‘userName’]是否为空来判断用户是否已经登陆,如果$_SESSION[‘userName’]为空,则跳转到登陆页面。

2.7.4删除session数据

unset($_SESSION[‘userName’])和session_destory()都可以实现删除session数据的操作。在退出功能中写入,可以确保用户退出留言板。

3.数据库

MessageBoard数据库有2个表,user表存放用户的注册信息,messages存放用户发布的留言。

3.1user表结构

3.2messages表结构

4.显示页面

4.1编辑页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<?php
session_start();
if(empty($_SESSION['userName'])){
header("location:login_page.php");
}
$userName=$_SESSION['userName'];
?>
<html>
<head>
<title>编辑留言</title>
<meta charset="utf-8">
</head>
<body>
<h1>编辑留言</h1>
<?php echo "欢迎".$userName; ?>
<a href="home.php">返回留言板</a>
<a href="send_message_page.php">发送留言</a>
<a href="logout.php">退出留言板</a>
<table>
<tr>
<th>发送人</th>
<th>接收人</th>
<th>发送时间</th>
<th>留言内容</th>
<th>删除</th>
<th>编辑</th>
</tr>
<?php
$ip="localhost";
$account="root";
$password="root";
$connect=new pdo("mysql:host=$ip;dbname=MessageBoard",$account,$password);
$query="select * from messages where sender='$userName';";
$res=$connect->prepare($query);
$res->execute();
while ($result=$res->fetch(PDO::FETCH_ASSOC)) {
$message_ID=$result['ID'];
echo "<tr>";
echo "<td>".$result['sender']."</td>";
echo "<td>".$result['receiver']."</td>";
echo "<td>".$result['time']."</td>";
echo "<td>".$result['content']."</td>";
echo "<td><a href='delete_message.php?id=$message_ID'>删除此条留言</a></td>";
echo "<td><a href='update_message_page.php?id=$message_ID'>编辑此条留言</a></td>";
echo "</tr>";
}
?>
</table>
</body>
</html>

使用GET方式通过<a>标签将留言ID发送到对应功能或页面。

4.2主页面(显示留言)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<!DOCTYPE html>
<?php
session_start();
if(empty($_SESSION['userName'])){
header("location:login_page.php");
}
$userName=$_SESSION['userName'];
?>
<html>
<head>
<title>留言板</title>
<meta charset="utf-8">
</head>
<body>
<h1>留言板</h1>
<?php echo "欢迎".$userName; ?>
<a href="logout.php">退出留言板</a>
<a href="send_message_page.php">发送留言</a>
<a href="edit_message_page.php">编辑留言</a>
<table border=1>
<tr>
<th>发送人</th>
<th>接收人</th>
<th>发送时间</th>
<th>留言内容</th>
</tr>
<?php
$ip="localhost";
$account="root";
$password="root";
$connect=new pdo("mysql:host=$ip;dbname=MessageBoard",$account,$password);
$query="select * from messages where sender='$userName' or receiver='$userName' or receiver='all';";
$res=$connect->prepare($query);
$res->execute();
while ($result=$res->fetch(PDO::FETCH_ASSOC)) {
echo "<tr>";
echo "<td>".$result['sender']."</td>";
echo "<td>".$result['receiver']."</td>";
echo "<td>".$result['time']."</td>";
echo "<td>".$result['content']."</td>";
echo "</tr>";
}
?>
</table>
</body>
</html>

4.3登陆页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<?php session_start(); ?>
<html>
<head>
<title>留言板</title>
<meta charset="utf-8">
</head>
<body>
<h1>留言板</h1>
<a href="signup_page.php">注册</a>
<form action="login.php" method="POST">
用户名:<input type="text" name="userName">
<br>
<br>
密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password">
<br>
<br>
<input type="submit" value="登陆">
</form>
</body>
</html>

4.4发布留言页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>
<?php
session_start();
if(empty($_SESSION['userName'])){
header("location:login_page.php");
}
$userName=$_SESSION['userName'];
?>
<html>
<head>
<title>发送留言</title>
<meta charset="utf-8">
</head>
<body>
<?php
$ip="localhost";
$account="root";
$password="root";
$connect=new pdo("mysql:host=$ip;dbname=MessageBoard",$account,$password);
$query="select Name from user where Name!='$userName';";
$res=$connect->prepare($query);
$res->execute();
?>
<h1>留言</h1>
<?php echo "欢迎".$userName; ?>
<br>
<a href="home.php">返回留言板</a>
<form action="send_message.php" method="POST">
接收人:
<select name="receiver">
<option value="all">所有人</option>
<?php
while ($result=$res->fetch(PDO::FETCH_ASSOC)) {
$name=$result['Name'];
echo "<option value='$name'>".$name."</option>";
}
?>
</select>
<br><br>
<textarea name="content"></textarea>
<br><br>
<input type="submit">
</form>
</body>
</html>

4.5注册页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html>
<head>
<title>留言板注册</title>
<meta charset="utf-8">
</head>
<body>
<h1>留言板注册</h1>
<form action="signup.php" method="POST">
用户名:<input type="text" name="userName">
<br>
<br>
昵&nbsp;&nbsp;&nbsp;称:<input type="text" name="Name">
<br>
<br>
密&nbsp;&nbsp;&nbsp;码:<input type="password" name="password">
<br>
<br>
<input type="submit" value="注册">
</form>
</body>
</html>

4.6修改留言页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<?php
session_start();
if(empty($_SESSION['userName'])){
header("location:login_page.php");
}
$userName=$_SESSION['userName'];
$message_ID=$_GET['id'];
?>
<html>
<head>
<title>编辑留言</title>
<meta charset="utf-8">
</head>
<body>
<?php
$ip="localhost";
$account="root";
$password="root";
$connect=new pdo("mysql:host=$ip;dbname=MessageBoard",$account,$password);
$query="select Name from user where Name!='$userName';";
$res=$connect->prepare($query);
$res->execute();
?>
<h1>编辑留言</h1>
<?php echo "欢迎".$userName; ?>
<a href="home.php">返回留言板</a>
<a href="send_message_page.php">发送留言</a>
<a href="logout.php">退出留言板</a>
<form action="update_message.php" method="POST">
接收人:
<select name="receiver">
<option value="all">所有人</option>
<?php
while ($result=$res->fetch(PDO::FETCH_ASSOC)) {
$name=$result['Name'];
echo "<option value='$name'>".$name."</option>";
}
?>
</select>
<br><br>
<textarea name="content"></textarea>
<br><br>
<input type="hidden" name="ID" value="<?php echo $message_ID; ?>">
<input type="submit">
</body>
</html>

5.PDO

PDO是 PHP Date Object (PHP数据对象)的简称,目前支持的数据库包括Firebird, FreeTDS,Interbase ,MySQL,MS SQL Server, ODBC, Oracle,Postgre SQL,SQLite 以及 Sybase等。有了PDO,用户就不必再使用 mysql_*函数,aco_函数或者 mssql_\函数,也不必再将他们封装到数据库操作类,只需要使用PDO接口中的方法就可以对不同的数据库进行操作,在选择不同数据库时,只需要修改PDO的 DNS(数据库名)就可以了。

5.1PDO构造函数连接数据库

PDO构造函数的形式:

1
_construct(string $dsn[,string $username[,string $password[,array $driver_options]]])

$dsn:主机IP地址及数据库名称

1
$dsn="mysql:host=$host;dbName=$dbName"

$username:数据库用户名
$password:数据库密码
$driver_options:连接数据库的其他选项

通过PDO连接数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php 
header("Content-Type:text/html;charset=utf-8");
$host="localhost";
$account="root";
$password="root";
$dbname="MessageBoard";
try{
$connect=new PDO("mysql:host=$host;dbname=$dbname,$account,$password);
echo "Connect successed.";
}catch (Exception $error){
echo $error->getMessage()."<br>";
}
?>

5.2使用PDO执行SQL语句

在PDO中,可以使用3中方式执行SQL语句,分别是exec()方法,query方法,以及预处理语句prepare()和execute()方法

5.2.1PDO::exec()方法

exec()方法形式如下:

1
int PDO::exec(string $statement)

PDO::exec()执行一条SQL语句,并返回受影响的行数。
$statement是要执行的SQL语句。

通过exec()方法执行SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 
header("Content-Type:text/html;charset=utf-8");
$host="localhost";
$account="root";
$password="root";
$dbname="MessageBoard";
try{
$connect=new PDO("mysql:host=$host;dbname=$dbname,$account,$password);
$query="insert into `user` (userName,Password,Name) values('chessur','chessur','chessur');"
$result=$connect->exec($query);
echo "数据插入成功,受影响行数为:".$result;
}catch (Exception $error){
echo $error->getMessage()."<br>";
}
?>

5.2.2PDO::query()方法

query()方法形式如下:

1
PDOStatement PDO::query(string $statement)

PDO::query()可执行多条SQL语句,并以PDOStatement对象形式返回结果集。

通过PDO::query()执行SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 
header("Content-Type:text/html;charset=utf-8");
$host="localhost";
$account="root";
$password="root";
$dbname="MessageBoard";
try{
$connect=new PDO("mysql:host=$host;dbname=$dbname,$account,$password);
$query="insert into `user` (userName,Password,Name) values('chessur','chessur','chessur');"
$result=$connect->query($query);
print_r($result);
}catch (Exception $error){
echo $error->getMessage()."<br>";
}
?>

5.2.3预处理语句:PDO::prepare()语句和execute()语句

PDO::prepare()方法形式如下:

1
PDOStatement PDO::prepare(string $statement[,array $driver_options])

PDOStatement::execute方法形式如下:

1
bool PDOStatement::execute([array $input_parameters])

PDO::prepare()方法为PDOStatement::execute()方法准备待执行的SQL语句。SQL 语句可以包含零个或多个参数占位标记,格式是命名(:name)或问号(?)的形式,当它执行时将用真实数据取代。 在同一个 SQL 语句里,命名形式和问号形式不能同时使用;只能选择其中一种参数形式。
PDOStatement::execute()方法执行一条预处理语句。

使用预处理语句执行SQL语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php 
header("Content-Type:text/html;charset=utf-8");
$host="localhost";
$account="root";
$password="root";
$dbname="MessageBoard";
try{
$connect=new PDO("mysql:host=$host;dbname=$dbname,$account,$password);
$query="insert into `user` (userName,Password,Name) values('chessur','chessur','chessur');"
$result=$connect->prepare($query);
$res->execute();
while($result=$result->fetch(PDO::FETCH_ASSOC)){
echo $result['userName'].$result['Password'].$result[Name];
}
}catch (Exception $error){
echo $error->getMessage()."<br>";
}
?>

5.3PDOStatement::fetch

5.3.1PDOStatement::fetch

PDOStatement::fetch形式如下:

1
mixed PDOStatement::fetch ([ int $fetch_style [, int $cursor_orientation = PDO::FETCH_ORI_NEXT [, int $cursor_offset = 0 ]]] )

5.3.1.1参数-$fetch_style

控制下一行如何返回给调用者。此值必须是 PDO::FETCH_* 系列常量中的一个,缺省为 PDO::ATTR_DEFAULT_FETCH_MODE的值 (默认为 PDO::FETCH_BOTH)。

参数 效果
PDO::FETCH_ASSOC 返回一个索引为结果集列名的数组
PDO::FETCH_BOTH(默认) 返回一个索引为结果集列名和以0开始的列号的数组
PDO::FETCH_BOUND 返回 TRUE ,并分配结果集中的列值给 PDOStatement::bindColumn() 方法绑定的 PHP 变量。
PDO::FETCH_CLASS 返回一个请求类的新实例,映射结果集中的列名到类中对应的属性名。如果 fetch_style 包含 PDO::FETCH_CLASSTYPE(例如:PDO::FETCH_CLASS
PDO::FETCH_INTO 更新一个被请求类已存在的实例,映射结果集中的列到类中命名的属性
PDO::FETCH_LAZY 结合使用 PDO::FETCH_BOTH 和 PDO::FETCH_OBJ,创建供用来访问的对象变量名
PDO::FETCH_NUM 返回一个索引为以0开始的结果集列号的数组
PDO::FETCH_OBJ 返回一个属性名对应结果集列名的匿名对象

5.3.1.2参数-$cursor_orientation

对于 一个 PDOStatement 对象表示的可滚动游标,该值决定了哪一行将被返回给调用者。此值必须是 PDO::FETCH_ORI_* 系列常量中的一个,默认为 PDO::FETCH_ORI_NEXT。要想让 PDOStatement 对象使用可滚动游标,必须在用 PDO::prepare() 预处理SQL语句时,设置 PDO::ATTR_CURSOR 属性为 PDO::CURSOR_SCROLL。

5.3.1.3参数-$offset

对于一个 cursor_orientation 参数设置为 PDO::FETCH_ORI_ABS 的PDOStatement 对象代表的可滚动游标,此值指定结果集中想要获取行的绝对行号。

对于一个 cursor_orientation 参数设置为 PDO::FETCH_ORI_REL 的PDOStatement 对象代表的可滚动游标,此值指定想要获取行相对于调用 PDOStatement::fetch() 前游标的位置

5.3.2PDOStatement::fetchAll

PDOStatement::fetchAll形式如下

1
array PDOStatement::fetchAll ([ int $fetch_style [, mixed $fetch_argument [, array $ctor_args = array() ]]] )

5.3.2.1参数-$fetch_style

控制返回数组的内容如同 PDOStatement::fetch() 文档中记载的一样。默认为 PDO::ATTR_DEFAULT_FETCH_MODE 的值( 其缺省值为 PDO::FETCH_BOTH )

想要返回一个包含结果集中单独一列所有值的数组,需要指定 PDO::FETCH_COLUMN 。通过指定 column-index 参数获取想要的列。

想要获取结果集中单独一列的唯一值,需要将 PDO::FETCH_COLUMN 和 PDO::FETCH_UNIQUE 按位或。

想要返回一个根据指定列把值分组后的关联数组,需要将 PDO::FETCH_COLUMN 和 PDO::FETCH_GROUP 按位或。

5.3.2.2参数-$fetch_argument

参数 效果
PDO::FETCH_COLUMN 返回指定以0开始索引的列
PDO::FETCH_CLASS 返回指定类的实例,映射每行的列到类中对应的属性名
PDO::FETCH_FUNC 将每行的列作为参数传递给指定的函数,并返回调用函数后的结果

5.3.2.3参数-$ctor_args

当 fetch_style 参数为 PDO::FETCH_CLASS 时,自定义类的构造函数的参数。

5.3.3PDOStatement::fetchColumn

PDOStatement::fetchColumn形式如下:

1
string PDOStatement::fetchColumn ([ int $column_number = 0 ] )

5.3.3.1参数-column_number

你想从行里取回的列的索引数字(以0开始的索引)。如果没有提供值,则 PDOStatement::fetchColumn() 获取第一列。

5.3.4PDOStatement::fetchObject

PDOStatement::fetchObject形式如下:

1
mixed PDOStatement::fetchObject ([ string $class_name = "stdClass" [, array $ctor_args ]] )

5.3.4.1参数-class_name

创建类的名称。

5.3.4.2参数-ctor_args

此数组的元素被传递给构造函数。

6.缺陷

没有考虑防御手段。

7.参考

[1] php实现留言板功能

[2] PDO中执行SQL语句的三种方法

[3] PDO中获取结果集之fetch()方法详解

[4] T-SQL教程

[5] PHP Sessions

[6] PHP:PDO-Manual