要入门web安全,首先就要实战一下,所以就跟着51CTO的孙胜利老师学习PHP好长时间了,终于将这一项目(小项目)给完成了,虽然遇到了很多error,但最后还是做出来了,就记录一下开发过程,顺便再理一遍思路。
全部源码已放在GitHub上,如有需要可以自己下载,可以的话点Star,Thanks
源码地址
开发好后的小型论坛
看起来还挺清新的,那么接下来就开始整理我的开发之路了。
开发思路:
创建后台-父板块
首先,我们需要创建一个数据库来保存我们的所添加的父板块
数据库就取名为bbs
,创建父板块father_module
表,设置三个字段,id
,module_name
,sort
。
数据库创建完成,就开始编写PHP代码,在此之前,我们需要HTML代码用来展示。
大致就是这样,写父板块功能的时候,中间那一部分可以不要,我们只要HTML代码的头部,左边和尾部。为了不让HTML代码重复出现,我们可以将相同的HTML代码防在一个文件中,利用PHP调用即可。
在admin目录下创建一个inc文件夹,里面放入后台经常要调用的文件,可以节省代码量。
这里把头部的HTML代码放入header.inc.php
,左边的HTML代码放入left.inc.php
,尾部的HTML代码放入 footer.inc.php
。
现在页面搞好了,但我们还需要编写一些数据库相关的函数,比如连接数据库等,如果没有这些函数,创建的数据库也毫无意义。
因为前台和后台都要用到,所以在根目录下再建立一个inc
文件夹。
下面就开始编写PHP代码,我们首先得连接数据库。
连接数据库就取名为config.in.php
<?php
date_default_timezone_set('Asia/Shanghai');//设置时区
session_start();//开启SESSION,验证后台登陆
header('Content-type:text/html;charset=utf-8');//设置编码
define('DB_HOST','localhost');//定义为常量
define('DB_USER','用户名');
define('DB_PASSWORD','密码');
define('DB_DATABASE','数据库名');
define('SA_PATH',dirname(dirname(__FILE__)));
define('SUB_URL',str_replace($_SERVER['DOCUMENT_ROOT'],'',str_replace('\\','/',SA_PATH)).'/');
?>
写好之后,我们还需要编写一些MYSQL常用的函数,方便后面开发使用。
<?php
//连接数据库
function connect($host=DB_HOST,$user=DB_USER,$password=DB_PASSWORD,$database=DB_DATABASE){
$link=@mysqli_connect($host, $user, $password, $database);
if(mysqli_connect_errno()){
exit(mysqli_connect_error());
}
mysqli_set_charset($link,'utf8');
return $link;
}
//执行一条SQL语句,返回结果
function execute($link,$query){
$result=mysqli_query($link,$query);
if(mysqli_errno($link)){
exit(mysqli_error($link));
}
return $result;
}
//获取记录数
function num($link,$sql_count){
$result=execute($link,$sql_count);
$count=mysqli_fetch_row($result);
return $count[0];
}
//数据入库之前进行转义
function escape($link,$data){
if(is_string($data)){
return mysqli_real_escape_string($link,$data);
}
if(is_array($data)){
foreach ($data as $key=>$val){
$data[$key]=escape($link,$val);
}
}
return $data;
}
//一次性执行多条SQL语句
/*
一次性执行多条SQL语句
$link:连接
$arr_sqls:数组形式的多条sql语句
$error:传入一个变量,里面会存储语句执行的错误信息
*/
function execute_multi($link,$arr_sqls,&$error){
$sqls=implode(';',$arr_sqls).';';
if(mysqli_multi_query($link,$sqls)){
$data=array();
$i=0;//计数
do {
if($result=mysqli_store_result($link)){
$data[$i]=mysqli_fetch_all($result);
mysqli_free_result($result);
}else{
$data[$i]=null;
}
$i++;
if(!mysqli_more_results($link)) break;
}while (mysqli_next_result($link));
if($i==count($arr_sqls)){
return $data;
}else{
$error="sql语句执行失败:<br /> 数组下标为{
$i}的语句:{
$arr_sqls[$i]}执行错误<br /> 错误原因:".mysqli_error($link);
return false;
}
}else{
$error='执行失败!请检查首条语句是否正确!<br />可能的错误原因:'.mysqli_error($link);
return false;
}
}
//关闭连接
function close($link){
mysqli_close($link);
}
?>
这些函数都编号后,就开始我们第一个板块-父板块。
我们首先要引用这两个文件,才能调用他们。
接下来就开始编写father_module.php
<?php
include_once '../inc/config.inc.php';
include_once '../inc/mysql.inc.php';
$link=connect();//连接数据库
?>
<?php include 'inc/header.inc.php'?>//引入头部
<?php include 'inc/left.inc.php'?>//引入左边
<?php include 'inc/footer.inc.php'?>//引入右边
这样大致框架就做好了,要求的父板块页面至少包括编辑、排序、删除、这些功能,页面是这样的(有点丑)。
那么接下来就开始在HTML代码中嵌入PHP代码,实现相应的功能。
father_module.php
完整代码如下:
<?php
include_once '../inc/config.inc.php';
include_once '../inc/mysql.inc.php';
include_once '../inc/tool.inc.php';
$link=connect();
//判断用户是否点击排序按钮
if(isset($_POST['submit'])){
foreach($_POST['sort'] as $key=>$val){
//is_numeric() 函数用于检测变量是否为数字或数字字符串
if(!is_numeric($val) || !is_numeric($key)){
skip('father_module.php','error','排序错误!');
}
//更新sort中的默认值
$query[]="update father_module set sort={
$val} where id={
$key}";
}
//mysqli_multi_query() 函数可用来执行多条SQL语句
if(execute_multi($link,$query,$error)){
skip('father_module.php','ok','排序修改成功');
}else{
skip('father_module.php','error',$error);
}
}
$template['title']='父板块列表页';
?>
<?php include 'inc/header.inc.php'?>
<div id="bg_wrapper">
<div id="main">
<div id="content">
<div class="jquery_tab">
<div class="content_block">
<h2 class="jquery_tab_title">父板块列表</h2>
<div class="explain">
<ul>
<li>添加你所喜欢的φ(* ̄0 ̄)</li>
<li>(o゜▽゜)o☆"( ̄y▽, ̄)╭ "</li>
</ul>
</div>
<form method="POST">
<table class="list">
<tr>
<th>排序</th>
<th>版块名称</th>
<th>版主</th>
<th>操作</th>
</tr>
<?php
//查询
$query="select * from father_module";
//执行
$result=execute($link,$query);
while ($data=mysqli_fetch_assoc($result)){
//将字符串以URL编码,用于编码处理
$url=urlencode("father_module_delete.php?id={
$data['id']}");
$return_url=urlencode($_SERVER['REQUEST_URI']);
$message="要删除父版块 {
$data['module_name']} 吗?";
//跳转确认页面,给用户更好的用户体验
$delete_url="confirm.php?url={
$url}&return_url={
$return_url}&message={
$message}";
$html=<<<A
<tr>
<td><input class="sort" type="text" name="sort[{
$data['id']}]" value="{
$data['sort']}" /></td>
<td>{
$data['module_name']}[id:{
$data['id']}]</td>
<td>且听风吟</td>
<td><a href="#">[访问]</a> <a href="father_module_update.php?id={
$data['id']}">[编辑]</a> <a href="$delete_url">[删除]</a></td>
</tr>
A;
echo $html;
}
?>
</table>
<input style="margin-top:20px;cursor:pointer;" class="btn" type="submit" name="submit" value="排序" />
</form>
</div><!--end content_block-->
</div><!-- end jquery_tab -->
我们还需要做confirm.php
删除确认页面、father_module_update.php
编辑功能、father_module_delete.php
删除功能
先来做一下删除确认页面
confirm.php
代码
<?php
include_once '../inc/config.inc.php';
//先判断是否传过来有参数
if(!isset($_GET['message']) || !isset($_GET['url']) || !isset($_GET['return_url'])){
exit();
}
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>确认界面</title>
<style>
@charset "UTF-8";
@use postcss-cssnext;
:root {
--windmill-dark: #14222D;
--windmill-medium-dark: #508BB3;
--windmill-medium: #6096C4;
--windmill-medium-light: #649ECD;
--windmill-light: #B6E2FF;
}
.windmill {
position: relative;
width: 800px;
height: 500px;
margin: auto;
overflow: hidden;
background: var(--windmill-medium-light);
}
....//css代码太多了,这里就不展示了,只把重要的代码展示出来
<link rel="stylesheet" type="text/css" href="style/remind.css" />
<div class="notice"><span class="pic ask"></span> <?php echo $_GET['message']?> <a style="color:red;" href="<?php echo $_GET['url']?>">确定</a> | <a style="color:#666;" href="<?php echo $_GET['return_url']?>">取消</a></div>
</body>
</html>
实现效果:
但现在没有删除功能,所以再做删除功能。
father_module_delete.php
代码
<?php
include_once '../inc/config.inc.php';
include_once '../inc/mysql.inc.php';
include_once '../inc/tool.inc.php';
$link=connect();
if(!isset($_GET['id'])||!is_numeric($_GET['id'])){
skip('father_module.php','error','id参数错误!');
}
//从子版块中查询是否有相同的id,如果有的话就不能直接删除
$query="select * from son_module where father_module_id={
$_GET['id']}";
$result=execute($link,$query);
if(mysqli_num_rows($result)){
skip('father_module_add.php','error','板块下有子板块,无法直接删除');
}
//查询数据库中的id是否和传过来的相同
$query="delete from father_module where id={
$_GET['id']}";
execute($link,$query);
//mysqli_affected_rows() 函数返回前一次 MySQL 操作(SELECT、INSERT、UPDATE、REPLACE、DELETE)所影响的记录行数
if(mysqli_affected_rows($link)==1){
skip('father_module.php','ok','删除成功');
}else{
skip('father_module.php','error','对不起删除失败,请重试!');
}
?>
下面就是编辑页面了
father_module_update.php
编辑页面
<?php
include_once '../inc/config.inc.php';
include_once '../inc/mysql.inc.php';
include_once '../inc/tool.inc.php';
$link=connect();
$template['title']='管理员编辑页';
//验证
if(!isset($_GET['id'])||!is_numeric($_GET['id'])){
skip('father_module.php','error','id参数错误!');
}
$query="select * from father_module where id={
$_GET['id']}";
$result=execute($link,$query);
if(!mysqli_num_rows($result)){
skip('father_module.php','error','这条版块信息不存在!');
}
if(isset($_POST['submit'])){
//因为涉及修改,所以我们需要验证,在创建inc中check_father_module.inc.php文件来验证。
$check_flag='update';
include 'inc/check_father_module.inc.php';
//更新数据
$query="update father_module set module_name='{
$_POST['module_name']}',sort={
$_POST['sort']} where id={
$_GET['id']}";
execute($link,$query);
if(mysqli_affected_rows($link)==1){
skip('father_module.php','ok','恭喜你,修改成功!');
}else{
skip('father_module.php',