menu 学神博客
search
account_circle
随手用PHP做了个论坛
学神之女
学神之女
Time:
local_offer share




废话

这是小学放暑假时的一个练手项目了,本来打算录制制作过程发到B站,但无奈电脑存不下录好的视频,只好删掉原视频,更改计划去发布到GitHub并写成博客。

其实早就在旧博客写过一个不完整版,但语文实在差劲,没写多少就勉强发布了。

现在语文还是差劲,其它科学得都可以说轻轻松松,唯独语文还在折磨我,拖我的后腿——不,已经快把整个下半身扯掉了。

之前是逐文件介绍代码的,发现并不好,现在还是逐功能介绍吧。

小声说句,怎么感觉后面的话不像地道的中文?就感觉像直接翻译的一样。

论坛简介

本文会以PHP为后端语言,实现一个简单的论坛软件,其支持以下功能:

  • 登陆注册
  • 发帖回复
  • 点赞回复
  • Markdown
  • 置顶帖
  • 响应式 本文不涉及样式
  • SQLite 本文采用JSON数据库
  • VIP用户

当然,我打算先从接近后端的部分讲起,随后再讲解前端部分。如果你就是想搭建一个论坛的话,请到GitHub直接下载完整版,连样式都已经弄得漂漂亮亮的了。

JSON数据库

由于博主在编写此论坛时未能解决自动拼接SQL语句防止SQL注入这两个问题,所以蠢蠢的写了一套JSON数据库驱动器实现了很多功能。

原理也很简单,就是用PHP自带的json_encodejson_decode函数对JSON文件进行解析。举个读的栗子:

class Database{
    public static function ls($table){
        //列出所有项,$table为表名。
        return json_decode(file_get_contents("./storage/".$table.".json"),true);
    }
}

缺点也很明显:效率低、占用高、存储空间大、功能不足……
接下来,只要根据此原理实现常规SQL中的create/drop table, select, insert, update, delete等指令就可以用了。啥?你问我咋实现?去我的GitHub项目看下initailize.php就可以了解了。

Markdown

论坛采用Markdown和HTML结合排版,由于本人无力制作Markdown引擎,所以采用了ParseDown,所以代码是酱紫的:

<?php
require("markdown.php");
$unparsed_text=<<<EOF
# 这里是一个小栗子
不要在意这些细节
EOF;
echo (new Parsedown)->text($unparsed_text);

登录注册

在聊代码之前,我认为我应该说下注册和登录的流程。注册:

  • 填写信息(并验证合法)
  • 提交到后端
  • 验证合法性
  • 存入数据库
  • 返回信息

登录:

  • 填写信息
  • 提交到后端
  • 验证合法性
  • 返回信息(如果成功创建Cookies)

从注册聊起,因为没有注册就没法登录。填写信息的话只要在网页上留几个框就可以了。

<form onsubmit="register();return false;">
    <input id="username" placeholder="用户名"/>
    <input id="password" placeholder="密码"/>
    <!--其它信息此处不做探讨-->
    <button type="submit">注册</button>
</form>

此处由于个人也厌恶那些“恶心”的跳转,所以采用AJAX提交表单:

function register(){
    var xhr = new XMLHttpRequest();
    xhr.setRequestHeader("Content-Type","application/json");//本人喜欢提交JSON信息
    xhr.onreadystatechange=function(){
        if(xhr.readyState==4) switch(xhr.status){
            case 0:
                alert("网络错误");
                break;
            case 200:
                window.location.href="./";
                break;
            case 406:
                alert(xhr.responseText);
                break;
            case 500:
            case 503:
                alert("服务器错误:"+xhr.responseText);
                break;
            default:
                alert("未知错误("+xhr.status+"):"+xhr.responseText);
                break;
        }
    }
    xhr.open("POST","api.php?operation=register",true);
    xhr.send(JSON.stringify({
        username:document.querySelector("#useranme").value,
        password:document.querySelector("#password").value
    }));
}

后端的话,在项目中个人喜欢封装成class,但这里我要拆开:

<?php
function generate_guid() {
    /*GUID是目前辨识数据库KEY的最好方式*/
    $charid = strtoupper(md5(uniqid(mt_rand(), true)));
    $hyphen = chr(45);
    $uuid = chr(123)
    .substr($charid, 0, 8).$hyphen
    .substr($charid, 8, 4).$hyphen
    .substr($charid,12, 4).$hyphen
    .substr($charid,16, 4).$hyphen
    .substr($charid,20,12)
    .chr(125);
    return $uuid;
}
$_POST=json_decode(file_get_contents("php://input"),true);//解析JSON
if(!isset($_POST['username'])||!preg_match('/[a-zA-Z0-9_-]{2,16}/ig',$_POST['username'])){
    header("HTTP/1.1 406 Unacceptable");
    die("用户名非法");
}
if(!isset($_POST['password'])){
    header("HTTP/1.1 406 Unacceptable");
    die("密码非法");
}
if(Database::select('users','username',$username)){
    header("HTTP/1.1 406 Unacceptable");
    die("用户已存在");
}
$u = Database::insert('users',[//插入数据
    'userid'=>generate_guid(),
    'username'=>$_POST['username'],
    'password'=>sha256($_POST['password']),
    'vip'=>false,//VIP信息
    'token'=>'{'.$_POST['username'].'-'.mt_rand().'-'.time().'}',//登录校验用token
    'registered'=>time()
]);

对于登录,前端同理,后端:

<?php
$_POST=json_decode(file_get_contents("php://input"),true);//解析JSON
if(!isset($_POST['username'])){
    header("HTTP/1.1 406 Unacceptable");
    die("用户名是必须的");
}
if(!isset($_POST['password'])){
    header("HTTP/1.1 406 Unacceptable");
    die("密码是必须的");
}
if(!$u = Database::select('users','username',$_POST['username'])){
    header("HTTP/1.1 404 Not Found");
    die("用户不存在");
}
if($u['password']!=sha256($_POST['password'])){
    header("HTTP/1.1 401 Unauthorized");
    die("密码错误");
}
setrawcookie("token",$u['token'],time()+2147483647,"/");//设置Cookies
echo "成功登陆";

想退出登录了,只需清除Cookies:

function logout(){
    setrawcookie("token","{undefined}",0,"/");
}

验证登录的话,就是从数据库中寻找带有相应token的用户:

<?php
function user_logged(){
    if($u=Database::select('user','token',empty($_COOKIE['token'])?null:$_COOKIE['token'])) return $u;
    else return false;
}

发帖回复

和注册的流程差不多,所以为了减小文章就不发实现代码了,不过我这里要展示一下帖子和回复的数据库结构:

/*帖子*/
{
    "postid":"ID",
    "title":"标题",
    "content":"正文",
    "sticky":false,//置顶
    "user":"发布者ID",
    "datetime":5201314,//发布日期
    "likes":["点赞者ID"],
    "replies":0,//回复量
    "views":0//阅读量
}
/*回复*/
{
    "replyid":"ID",
    "content":"正文",
    "user":"发布者ID",
    "datetime":5201314,//发布日期
    "likes":["点赞者ID"],
    "floor":2,//楼层
    "repliedTo":null,//被回复者,
    "repliedTo":{
        "user":"被回复回复者ID",
        "floor":"被回复楼层"
    }//当回复某人时
}

置顶&VIP用户

读到这里,你或许看到了,在数据库预设中,有stickyvip这样的字段,它们的确就是本小标题的功能。而判定是否置顶、VIP的话,只需要判定TF:

<?php
/*以VIP用户为例*/
if($u=user_loggeed()){
    echo $u['vip']?'VIP':'非VIP';
}else{
    echo "没登录判定个毛线";
}

前端显示如上功能

用尽量通俗的语言说完了“轶可赛艇”的后端功能后,还需要前端来显示它们,当然,此处仅输出最简单的内容,额外内容需自己幻想。
列出帖子:

<h1>帖子列表</h1>
<ul><?php 
$posts = Database::ls('posts');
foreach($posts as $key=>$value){?>
    <li><a href="<?php echo htmlspecialchars("./view/".$value['postid']);?>"><?php echo htmlspecialchars($value['title']);?></a></li>
<?php }?>
</ul>

详细内容:(回复、点赞自行添加)

<?php 
$post = Database::select('posts','postid',$_GET['post']);
if(!$post) {
    header("HTTP/1.1 404 Not Found");
    die("帖子不存在");
}
$replies = Database::select('replies','post',$post['postid']);
?>
<h1><?php htmlspecialchars($post['title']);?></h1>
<main><?php echo (new Parsedown)->text($post['content']); ?></main>
<?php 
if($replies) foreach($replies as $key=>$value){
?>
<hr />
<p><?php echo (new Parsedown)->text($value['content']);?></p>
<?php 
} else {?><hr />
无回复<?php }
?>

终于写完了2333,如果有什么问题评论或其它方式告诉我。


评论

   textsms
   account_circle
昵称不能为空
   email
邮箱格式错误
   language





message 只有 1 条评论QwQ
    学神之女
    Time: 2018-12-09 22:57




    想看看测试██是什么样子的。


    arrow_back 上一篇
    arrow_back 上一篇
    写作业的小女孩
    下一篇 arrow_forward
    A letter to my geography teacher
    下一篇 arrow_forward