TinyBoard, 一个500行的PHP留言板
项目共有7个文件:install.php, index.php, util.php, cfg.php, login.php, detail.php, manage.php.
install.php实现数据库内容安装,创建配置文件(即cfg.php)。install.php内容如下:
<?php // TinyBoard 安装程序 ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"><title>TinyBoard | Install </title></head> <body> <?php if(version_compare('7.0.0', PHP_VERSION, '>=')) die('Current PHP version:' . PHP_VERSION . '. too low, requiring version higher than 7.0.' . "\n"); if(empty($_POST) || !isset($_POST['host'])) { ?> <form method = "post"> <h3>database</h3> host <input type="text" name="host" value="localhost"><br /> user <input type="text" name="user" value="root"><br /> password <input type="text" name="password" value="123"><br /> dbname <input type="text" name="dbname" value="board"><br /> <input type="submit" value="install"> </form> <?php goto End; } // step 1 显示输入内容 echo '<h3>Input</h3><br />'; echo 'dbhost ', $_POST['host'], '<br />'; echo 'dbname ', $_POST['dbname'], '<br />'; echo 'user ', $_POST['user'], '<br />'; echo 'password ', $_POST['password'], '<br />'; echo 'installing ... ', '<br />'; // step 2 安装数据库 $addUser = <<<QUERY drop table if exists user; create table user ( id int not null auto_increment, name varchar(30) not null, passwd varchar(30) not null, phone varchar(30) not null, primary key(id) ); insert into user values(1, 'Huyelei', '123', '18262289511'); insert into user values(2, 'root', '123', '133'); insert into user values(3, 'LiLei', '789', '139'); insert into user values(4, 'HanMeimei', 'ABC', '188'); QUERY; $addMessage = <<<QUERY drop table if exists message; create table message ( id int not null auto_increment, content varchar(255) not null, author int not null, authorName varchar(30) not null, date DATETIME not null, primary key(id) ); create index index_msg_author on message(author); QUERY; $host = $_POST['host'] ?? ''; $dbname = $_POST['dbname'] ?? ''; $dbusr = $_POST['user'] ?? ''; $passwd = $_POST['password'] ?? ''; try { $dsn = sprintf('mysql:host=%s;dbname=%s', $host, $dbname); $option = [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]; $pdo = new PDO($dsn, $dbusr, $passwd, $option); } catch (PDOException $e) { exit($e->getMessage()); } $pdo->query($addUser); $pdo->query($addMessage); $tables = $pdo->query('show tables')->fetchAll(PDO::FETCH_GROUP); if(!in_array('user', array_keys($tables)) || !in_array('message', array_keys($tables))) die('add table fail'); // 循环输入message table内容 $stm = $pdo->prepare('insert into message values(?, ?, ?, ?, ?)'); $stm->bindParam(1, $id); $stm->bindParam(2, $msg); $stm->bindParam(3, $author); $stm->bindParam(4, $authorName); $stm->bindParam(5, $time); $names = [1=> 'Huyelei', 2=>'root', 3=>'LiLei', 4=>'HanMeimei']; for($i=1; $i<=300; $i++) // 300 { $id = $i; $msg = sprintf('this is number %s message', $i); $author = ($i-1)%3 + 1; $authorName = $names[$author]; $time = sprintf('2018-01-03 15:00::%02d', $i%60); $stm->execute(); } $stm->rowCount() > 0 or die('set msg content fail'); // step 3 生成配置文件 $f = fopen('cfg.php', 'w') or die(__FILE__ . ' line ' . __LINE__); fwrite($f, "<?php \n"); fwrite($f, '// create time: '. date('Y-m-d H:i:s') . "\n\n"); function writeConst($f, $dst, $src) { $str = sprintf("define('%s', '%s'); \n", $dst, $src); fwrite($f, $str); } writeConst($f, 'DB_HOST', $host); writeConst($f, 'DB_USR', $dbusr); writeConst($f, 'DB_PASSWD', $passwd); writeConst($f, 'DB_NAME', $dbname); function writeConst_($f, $dst, $src) { $str = sprintf("define('%s', %s); \n", $dst, $src); fwrite($f, $str); } writeConst_($f, 'LINE_PER_PAGE', 10); writeConst_($f, 'PAGE_PER_CHAPTER', 5); writeConst_($f, 'TINY_DEBUG', true); fwrite($f, "\n"); $str = <<<CODE if(TINY_DEBUG == true) { error_reporting(E_ALL); ini_set('display_errors','On'); } else { error_reporting(E_USER_NOTICE); ini_set('display_errors','Off'); ini_set('log_errors', 'On'); } CODE; fwrite($f, $str); fwrite($f, "\n\n"); fclose($f); echo 'installation successfully finished. <br />'; End: ?> </body></html>
index.php 是入口文件,列表显示留言内容,index.php如下:
<?php /* TinyBoard, 一个500行的留言板程序 author huyelei@yeah.net 2018.01.05 QQ群619348997 使用方法: 1 所有文件拷贝到Web根目录 2 执行install.php, 实现数据库导入和配置文件cfg.php的创建 3 手工删除install.php, 访问index.php。 Enjoy. */ ?> <!DOCTYPE html> <html><head> <meta charset="UTF-8"> <title>TinyBoard</title> <style type="text/css"> .header { width:820px; margin: auto; } .sheet { display:table; border-collapse:separate; margin:auto; } .row { display:table-row; } .row div { display:table-cell; } /* row类中的 div标签 */ .row .one { width:80px; } /* row类中的第一列 */ .row .two { width:320px; max-width:320px; white-space:nowrap; overflow:hidden; text-overflow: ellipsis; } .row .three { width:96px; } .row .four { width: 200px; max-width:200px; white-space:nowrap; overflow:hidden; text-overflow: ellipsis; } .row .five { width: 120px; } .leg { /* 页码 */ width:800px; text-align:right; margin: auto; } </style> </head><body> <?php session_start(); echo '<div class="header">'; if(!isset($_SESSION['username'])) echo '<a href="login.php">登录</a>'; else echo $_SESSION['username'], ' <a href="login.php?logout=1">退出登录</a> ', ' <a href="manage.php?type=1">发帖</a> '; echo '</div><br />'; require 'cfg.php'; require 'util.php'; $pdo = getPdo(); $stat = $pdo->query('select count(1) from message'); $r = $stat->fetch(); $r['count(1)'] > 0 or die('die at line:' . __LINE__); $lineSum = $r['count(1)']; $pageSum = 1 + (int)($lineSum - 1)/LINE_PER_PAGE; $page = truncate($_GET['page'], $pageSum - 1); // 当前页, 从0开始 $lineStart = (int) $page * LINE_PER_PAGE; // 开始行,从0开始 $lineEnd = min($lineSum - 1, $lineStart + LINE_PER_PAGE - 1); $lineNum = $lineEnd - $lineStart + 1; $str = sprintf('select * from message order by id desc limit %s, %s;', $lineStart, $lineNum); $stat = $pdo->query($str); $items = $stat->fetchAll(); $horizonLine =<<<HTML <div class="row"> <div class="one">----------</div> <div class="two">----------------------------------------</div> <div class="three">------------</div> <div class="four">-------------------------</div> <div class="five">---------------</div> </div> HTML; ?> <div class="sheet"> <?php echo $horizonLine; ?> <div class="row"> <div class="one"> ID</div> <div class="two">内容</div> <div class="three"> 用户</div> <div class="four">时间</div> <div class="five">操作</div> </div> <?php echo $horizonLine; ?> <?php foreach ($items as $item): ?> <div class="row"> <div class="one"> <?php echo $item['id'] ?></div> <div class="two"><?php echo $item['content'] ?></div> <div class="three"> <?php echo $item['authorName'] ?></div> <div class="four"><?php echo $item['date'] ?></div> <div class="five"> <?php $url = 'detail.php?id=' . $item['id']; echo ' <a href="' . $url . '">详情</a> '; if(isset($_SESSION['userId']) && $_SESSION['userId'] == $item['author']) { $delete = 'manage.php?type=2&id=' . $item['id']; $update = 'manage.php?type=3&id=' . $item['id']; echo ' <a href="' . $delete . '">删除</a> '; echo ' <a href="' . $update . '">编辑</a> '; } ?></div> </div> <?php endforeach; ?> <?php echo $horizonLine; ?> </div> <div class="leg"> <?php $chapt = (int)($page/PAGE_PER_CHAPTER); // 当前chapter, 从0开始 $chaptSum = 1 + (int)(($pageSum - 1)/PAGE_PER_CHAPTER); $pageStart = (int)($chapt * PAGE_PER_CHAPTER); $pageEnd = min($pageSum - 1, $pageStart + PAGE_PER_CHAPTER - 1); $pageNum = $pageEnd - $pageStart + 1; // echo '$page:', $page, ' $chap:', $chapt, '<br />'; if($chapt > 0) { $pagePriv = (int)(($chapt - 1)*PAGE_PER_CHAPTER); $url = 'index.php?page=' . $pagePriv; echo ' <a href="' . $url . '">' . '<</a> '; } for($i=0; $i<$pageNum; $i++) { $id = $pageStart + $i; $url = 'index.php?page=' . $id; echo ' <a href="' . $url . '">' . ($id+1). '</a> '; } if($chapt < ($chaptSum - 1)) { $pageNext = (int)(($chapt + 1)*PAGE_PER_CHAPTER); $url = 'index.php?page=' . $pageNext; echo ' <a href="' . $url . '">' . '></a> '; } ?> </div> </body></html>
util.php较为简短,提供工具函数包括数据库连接等,如下:
<?php function getPdo() { static $pdo = null; if($pdo !== null) return $pdo; try { // echo 'pdo connecting <br>'; $dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8', DB_HOST, DB_NAME); $option = [PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]; return $pdo = new PDO($dsn, DB_USR, DB_PASSWD, $option); } catch (PDOException $e) { exit($e->getMessage()); } } function truncate(&$src, $max) { $dst = 0; if(isset($src)) { if($src >= $max) $dst = $max; else if($src < 0) $dst = 0; else $dst = $src; } return $dst; }
cfg.php记录项目配置信息,例如数据库用户名等,cfg.php是install.php生成,笔者的cfg.php如下:
<?php // create time: 2018-01-05 02:24:13 define('DB_HOST', 'localhost'); define('DB_USR', 'root'); define('DB_PASSWD', '123'); define('DB_NAME', 'board'); define('LINE_PER_PAGE', 10); define('PAGE_PER_CHAPTER', 5); define('TINY_DEBUG', 1); if(TINY_DEBUG == true) { error_reporting(E_ALL); ini_set('display_errors','On'); } else { error_reporting(E_USER_NOTICE); ini_set('display_errors','Off'); ini_set('log_errors', 'On'); }
login.php实现网站用户的登录和登出功能,login.php如下:
<!DOCTYPE html> <html> <head> <title>TinyBoard | login</title> </head><body> <a href="index.php">Home</a> <br /> <?php // 用户登录和登出 session_start(); if(isset($_SESSION['username'])) { isset($_GET['logout']) or die('die at line:' . __LINE__); // 退出登录 $_SESSION = []; if(isset($_COOKIE[session_name()])) setcookie(session_name(), '', time()-1, '/'); session_destroy(); echo 'logout successfully <br />'; goto End; } if(!isset($_POST['username']) || !isset($_POST['passwd'])) { ?> <form method="post"> user name: <input type="text" name="username" value="Huyelei"> <br /> password : <input type="password" name="passwd" value="123"> <br /> <input type="submit" value="Login"> </form> <?php goto End; } $username = $_POST['username']; $password = $_POST['passwd']; $query = "select * from user where name='$username' and passwd='$password'"; require 'cfg.php'; require 'util.php'; $stat = getPdo()->prepare($query); $stat->execute(); $r = $stat->fetch(PDO::FETCH_ASSOC); !empty($r) && isset($r['name']) or die('die at line:' . __LINE__); // 登录成功 $_SESSION['username'] = $r['name']; $_SESSION['userId'] = $r['id']; echo 'user:', $_SESSION['username'],' login successfully.', '<br />'; End: ?> </body></html>
detail.php描述某条留言(message)的详细信息,留言的ID通过$_GET['id']获得。detail.php如下:
<?php // 消息细节页面 ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>TinyBoard | detail</title> </head> <body> <a href="index.php">首页</a><br /><br /> <?php session_start(); require 'cfg.php'; require 'util.php'; isset($_GET['id']) or die('die at line:' . __LINE__); $stat = getPdo()->prepare('select * from message where id = ?'); $stat->execute([$_GET['id']]); $item = $stat->fetch(); extract($item); echo 'id: ', $id, '<br />'; echo 'content: ', $content, '<br />'; echo 'authorId: ', $author, '<br />'; echo 'authorName: ', $authorName, '<br />'; echo 'date: ', $date, '<br />'; if(isset($_SESSION['userId']) && $_SESSION['userId'] == $item['author']) { echo '<br />'; $delete = 'manage.php?type=2&id=' . $item['id']; $update = 'manage.php?type=3&id=' . $item['id']; echo ' <a href="' . $delete . '">删除</a> '; echo ' <a href="' . $update . '">编辑</a> '; } ?> </body> </html>
manage.php实现对message列表的增删改操作,访问manage.php采用GET方式,带有参数type和id,type表明操作类型(增删改),id表明消息id(仅删除和更改操作有意义)。manage.php如下:
<?php // 对某个消息的增删改页面, 增删改类型依次为 1 2 3 ?> <!DOCTYPE html> <html><head> <meta charset="UTF-8"> <title>TinyBoard | manage </title> </head> <body> <a href="index.php">首页</a><br /><br /> <?php session_start(); require 'cfg.php'; require 'util.php'; $type = truncate($_GET['type'], 4); $type > 0 && $type < 4 or die('die at line:' . __LINE__); if($type == 1) // 增 { if(!isset($_GET['ready'])) // 未上传参数 { ?> <form> content <input type="text" name="content"><br /> <input type="hidden" name="type" value="1"> <input type="hidden" name="ready" value="1"> <input type="submit" value="增加"> </form> <?php } else // 上传 { $content = $_GET['content'] ?? ''; !empty($content) or die('die at line:' . __LINE__); $str = sprintf("insert into message values(0, ?, %d, '%s', '%s')", $_SESSION['userId'], $_SESSION['username'], date('Y-m-d H:i:s')); $stat = getPdo()->prepare($str); $stat->execute([$content]); $stat->rowCount() > 0 or die('die at line:' . __LINE__); echo 'content input:', $content, '<br />'; echo 'message successfully created. <br />'; } } else if($type == 2) // 删 { isset($_GET['id']) or die('die at line:' . __LINE__); $stat = getPdo()->prepare('delete from message where id = ?'); $stat->execute([$_GET['id']]); $stat->rowCount() > 0 or die('line:' . __LINE__); echo 'message successfully deleted. <br />'; } else if($type == 3) // 改 { isset($_GET['id']) or die('die at line:' . __LINE__); if(!isset($_GET['ready'])) // 未上传参数 { $stat = getPdo()->prepare('select * from message where id=?'); $stat->execute([$_GET['id']]); $r = $stat->fetch(); $content = $r['content'] ?? ''; ?> <form> content <input type="text" name="content" value="<?php echo $content; ?>"><br /> <input type="hidden" name="id" value="<?php echo $_GET['id']; ?>"> <input type="hidden" name="type" value="3"> <input type="hidden" name="ready" value="1"> <input type="submit" value="编辑"> </form> <?php } else { $content = $_GET['content'] ?? ''; !empty($content) or die('die at line:' . __LINE__); $stat = getPdo()->prepare('update message set content=?,date=? where id=?;'); $stat->execute([$content, date('Y-m-d H:i:s'), $_GET['id']]); $stat->rowCount() > 0 or die('die at line:' . __LINE__); echo 'content input:', $content, '<br />'; echo 'message successfully updated. <br />'; } } ?> </body></html>
以上是所有代码。index.php执行效果如下图所示:

项目代码github地址为:https://github.com/huyl2002/TinyBoard。欢迎加入QQ群技术交流:619348997。
浙公网安备 33010602011771号