12.3 系统功能实现
系统前端页面使用HTML语言构建,页面布局将通过CSS+DIV(层)实现。BLOG实际内容的显示,将由内嵌入HTML中的PHP代码完成。
12.3.1 实现BLOG文章的显示
这一小节将实现从文件中读取日志内容,并将其显示在页面上。
(1)为了能将日志内容显示到页面,建立一个文本文件,在该文件里存储一些日志数据,文件的内容如下。
测试日志标题| 1322862787| 这是一段日志的测试文字,一切无误的话,它应该正确显示。
这段内容中的各项以“|”分割,从左向右依次表示日志的标题、发布该日志的日期时间(以UNIX时间戳表示)、日志的实际内容。目前还没有实现添加日志文章的功能,所以先手工建立一个目录及日志内容文件,以便显示日志内容的程序可以读取该文件。将该文件以名称02-215307.txt并存储到BLOG系统contents目录的201112目录下。测试时,读者可以自行指定目录和文件名。
(2)编写PHP程序读出文件的内容,并向浏览器输出。代码12-1是实现读取文件中日志内容并显示出日志内容的程序,命名为post.php。
代码12-1 读文件并显示日志内容post.php
01 <?php 02 $file_name = 'contents/201112/02-215307.txt'; // 存储日志内容的文件 03 04 if(file_exists($file_name)) // 打开文件前判断文件是否存在 05 { 06 $fp = @fopen($file_name, 'r'); // 以只读方式打开文件 07 if($fp) 08 { 09 flock($fp, LOCK_SH); // 文件加锁 10 $result = fread($fp, 1024); // 读出文件的内容,并以字符串形式赋给变量$result 11 } 12 flock($fp, LOCK_UN); // 解锁文件 13 fclose($fp); 14 } 15 16 // 将字符串$result 的内容按“| ”分割后存入数组$content_array 17 $content_array = explode('|', $result); 18 19 // 以下代码将日志内容输出至浏览器 20 echo '<h1> 我的BLOG</h1>'; 21 echo '<b> 日志标题:</b>'.$content_array[0]; 22 echo '<br/><b> 发布时间:</b>'.date('Y-m-d H:i:s',$content_array[1]); 23 echo '<hr>'; 24 echo $content_array[2]; 25 ?>
【代码解析】代码第02行首先打开指定目录下的文本文件contents/201112/02-215307.txt,该文件保存了某天某时刻的日志内容。读出文件内容后,将其赋给变量$result,此时变量$result的值为“测试日志标题|1322862787|这是一段日志的测试文字,一切无误的话,它应该正确显示。”。接着程序第17行使用函数explode()将该字符串按竖线“|”做分割,分割后的3个部分分别是日志的标题、发布日志的时间和日志的实际内容,然后程序将这3项内容存入数组$content_array,作为其单元的值。最后使用echo语句将数组的内容输出,即将日志内容输出至页面。代码12-1的执行结果如图12-3所示。
图12-3 显示日志内容的简易界面
注意 文件中存储的时间是一个UNIX时间戳1322862787,程序中使用函数date()将这个时间戳格式化普通时间格式后输出。如果读者已经对时间戳没有印象,可参考第7.2节。
(3)代码12-1中只能读取2011年12月2日某时刻的日志文件,这肯定是不能满足实际需要的。显示BLOG内容的程序应该能够访问每一天的日志文件,这可以通过URL向程序传入参数实现,不同的参数值代表不同的日期时间,程序根据这个参数值的不同,完成访问不同目录下的日志文件,并获取该文件中的数据。比如,传入参数entry=201112-02-215307,表示访问目录201112下的02-215307.txt文件。从这个参数可以看出,它的值实际反映出了日志路径。代码12-2是使用了URL传入参数的post.php。
代码12-2 处理由URL传入的字符串参数post.php
01 <?php 02 if(!isset($_GET['entry'])) 03 { 04 echo ' 请求参数错误'; 05 exit; 06 } 07 08 $path = substr($_GET['entry'],0,6); // 日志存储目录 09 $entry = substr($_GET['entry'],7,9); // 日志文件名称 10 11 $file_name = 'contents/'.$path.'/'.$entry.'.txt'; // 拼接出完整的日志路径 12 13 if(file_exists($file_name)) // 打开文件前需要判断文件是否存在 14 { 15 $fp = @fopen($file_name, 'r'); // 以只读方式打开文件 16 if($fp) 17 { 18 flock($fp, LOCK_SH); // 文件加锁 19 $result = fread($fp, 1024); // 读出文件中的内容 20 } 21 flock($fp, LOCK_UN); // 解锁文件 22 fclose($fp); 23 } 24 25 // 将字符串$result 的内容按“| ”分割后存入数组$content_array 26 $content_array = explode('|', $result); 27 28 // 以下代码将日志内容输出 29 echo '<h1> 我的BLOG</h1>'; 30 echo '<b> 日志标题:</b>'.$content_array[0]; 31 echo '<br/><b> 发布时间:</b>'.date('Y-m-d H:i:s',$content_array[1]); 32 echo '<hr>'; 33 echo $content_array[2]; 34 ?>
【代码解析】URL中entry参数的值存放在PHP的预定义变量$_GET中。代码第02~06行首先判断是否传入了参数entry,如果通过GET方法没有得到URL参数entry,程序会输出一个提示信息,告知用户请求参数错误。如果得到了参数entry,程序会通过函数substr()将该参数的值分割,分别分割成日志的存储目录和日志文件名。字符串201112-02-215307的前6位代表的是日志的存储目录,因此通过代码第08行的“substr($_GET['entry'],0,6)”可以得到日志的存储目录。同理,代码第09行中的“substr($_GET['entry'],7,9)”获取的是日志文件名。通过浏览器,访问修改后的post.php,将看到和图12-3完全一样的结果。
图12-4 页面整体布局
至此,从文件中获取BLOG内容的基本功能已完全实现。
12.3.2 完善用户界面
12.2.1小节虽然实现了BLOG内容的显示,但页面本身还是相当简陋的。本小节将实现使用HTML构建基本页面,通过CSS+DIV技术实现对页面布局的控制和显示效果,进而完善上小节的BLOG内容显示界面。
完整的页面应该分成几大块,每块负责显示不同的内容。在这个BLOG系统中,页面的整体布局如图12-4所示。
下面就按图12-4完成页面的构建。
(1)使用HTML完成页面的创建,然后通过CSS控制页面布局和显示效果。代码12-3是用来显示日志文章内容的完整HTML文档,将其按文件名page.html保存。
代码12-3 用来显示日志文章的HTML文档page.html
01 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 02 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 03 <html> 04 <head> 05 <title>BLOG</title> 06 07 </head> 08 <body> 09 10 <div id="containor"> 11 <div id="header"> 12 BLOG 名称 13 </div> 14 <div id="title"> 15 ----I have a dream.... 16 </div> 17 <div id="left"> 18 <div id="blog_entry"> 19 <div id="blog_title"> 日志文章标题</div> 20 <div id="blog_body"> 21 <div id="blog_date">2012-12-01</div> 22 日志文章内容 23 </div> 24 </div> 25 </div> 26 27 <div id="right"> 28 <div id="sidebar"> 29 <div id="menu_title"> 关于我</div> 30 <div id="menu_body"> 我是个PHP 爱好者</div> 31 </div> 32 </div> 33 34 <div id="footer"> 35 CopyRight 2011 36 </div> 37 </div> 38 39 <body> 40 </html>
【代码解析】代码是一些简单的HTML标签,重点是一些div的布局,这里笔者不再详细介绍每个标签的意义,读者可参考前文的介绍。通过浏览器访问page.html,可以看到如图12-5所示的效果。
(2)可以肯定,这个页面仍然是不能满足需求的。下面通过CSS来控制页面的显示效果。创建一个名为style.css,在该CSS文件输入如下CSS代码并保存。
01 body{ 02 font-size:13px; 03 background-color:#C6C68C; 04 padding:0px; 05 } <! —定义 body 样式 --> 06 #header{ 07 margin-left:auto; 08 margin-right:auto; 09 padding:8px; 10 height:80px; 11 background-color:#E8F3FD; 12 border-bottom:1px solid #000; 13 } <! —定义 header 样式 --> 14 #title{ 15 margin-left:auto; 16 margin-right:auto; 17 padding:8px; 18 height:10px; 19 background-color:#E8F3FD; 20 border-bottom:1px solid #000; 21 } <! —定义 title 样式 --> 22 #container 23 {margin-left:auto; 24 margin-right:auto;margin-top:2px; 25 margin-bottom:0px; 26 padding:0; 27 width:760px; 28 height:400px; 29 background-color:#EDEDED; 30 border:1px solid #000; 31 } <! —定义 container 样式 -->
【代码解析】从代码可以看出,style.css中设定了body元素的背景色、header、title和container层的背景色、margin、padding的属性。
(3)在代码12-3所示的HTML文档中引入style.css,即在page.html的<head>与</head>标签之间加入如下代码。
<link rel="stylesheet" type="text/css" href="style.css" />
再通过浏览器查看page.html,会看到如图12-6所示的效果。
图12-5 显示文章内容的HTML页面
图12-6 在页面中加入CSS
(4)这个CSS文件已经使原始的HTML文件显示出一定的效果,但上述CSS代码仅是对页面头部的显示效果加以控制,还需要使用CSS控制整个页面的显示效果。现在继续完善这个CSS,代码12-4是完整的CSS代码。这个CSS文件会随BLOG系统功能的改变而有所改动或增加。
代码12-4 控制页面显示效果的CSS代码style.css
01 body{ 02 font-size:12px; 03 background-color:#C6C68C; 04 padding:0px; 05 font-family:Helvetica,sans-serif; 06 } <! —定义 body 样式 --> 07 08 #container{ 09 margin-left:auto; 10 margin-right:auto; 11 margin-top:2px; 12 margin-bottom:0px; 13 padding:0; 14 width:760px; 15 border:1px solid #000; 16 background-color:#F6F6F6; 17 } <! —定义 container 样式 --> 18 19 #header{ 20 margin-left:auto; 21 margin-right:auto; 22 padding:8px; 23 height:80px; 24 background-color:#E8F3FD; 25 border-bottom:1px solid #000; 26 font-size:16px; 27 font-weight:bold; 28 } <! —定义 header --> 29 30 #title{ 31 margin-left:auto; 32 margin-right:auto; 33 padding:8px; 34 height:10px; 35 background-color:#E8F3FD; 36 border-bottom:1px solid #000; 37 font-style:italic; 38 } <! —定义 title 样式 --> 39 40 #left{ 41 float:left; 42 margin-left:auto; 43 margin-right:auto; 44 margin:6px 0 4px 2px; 45 padding:5px; 46 width:530px; 47 } <! —定义 left 样式 --> 48 49 #right{ 50 float:right; 51 margin-left:auto; 52 margin-right:auto; 53 margin:6px 0 4px 2px; 54 padding:5px; 55 width:200px; 56 } <! —定义 right 样式 --> 57 58 #blog_entry{ 59 margin-left:auto; 60 margin-right:auto; 61 margin-top:4px; 62 margin-bottom:10px; 63 border:1px solid #000; 64 background-color:#FFF; 65 } <! —定义 blog_entry 样式 --> 66 67 #blog_title{ 68 border-bottom:1px solid #000; 69 background-color:#E8ECDB;magin:0px; 70 padding:4px; 71 font-weight:bold; 72 font-size:13px; 73 } <! —定义 blog_title 样式 --> 74 75 #blog_body{ 76 margin-left:auto; 77 margin-right:auto; 78 margin-top:4px; 79 padding:6px; 80 } <! —定义 blog_body 样式 --> 81 82 #blog_date{ 83 margin-left:auto; 84 margin-right:auto; 85 padding:0 0 8px 0; 86 font-size:10px; 87 } <! —定义 blog_date 样式 --> 88 89 #sidebar{ 90 margin-left:auto; 91 margin-right:auto; 92 border:1px solid #000; 93 width:180px; 94 background-color:#FFF; 95 } <! —定义 sidebar 样式 --> 96 97 #menu_title{ 98 border-bottom:1px solid #000; 99 background-color:#E8ECDB; 100 magin:0px;padding:4px; 101 height:10px; 102 font-weight:bold; 103 } <! —定义 menu_title 样式 --> 104 105 #menu_body{ 106 margin-left:auto; 107 margin-right:auto; 108 margin-top:4px; 109 padding:6px; 110 } <! —定义 menu_body 样式 --> 111 112 #footer{ 113 clear:both; 114 text-align:center; 115 margin-left:auto; 116 margin-right:auto; 117 padding:8px; 118 height:10px; 119 background-color:#E8F3FD; 120 border-top:1px solid #000; 121 } <! —定义 footer _title 样式 -->
【代码解析】关于CSS的代码,这里不做详细介绍,读者可参考注释。在后续的开发中,将看到由此CSS文件控制的各个界面效果。
12.3.3 实现BLOG文章的添加功能
为12.2.2小节显示日志文章的页面增加一个含有文本框和“提交”按钮的HTML表单,实现添加日志文章的界面,如图12-7所示。
图12-7 添加BLOG文章的界面
代码12-5实现了该页面,将代码12-5按文件名add.php进行保存。
代码12-5 添加BLOG文章的界面add.php
01 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 02 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 03 <html> 04 <head> 05 <title> 基于文本的简易BLOG</title> 06 <link rel="stylesheet" type="text/css" href="style.css" /> 07 </head> 08 <body> 09 10 <div id="container"> 11 <div id="header"> 12 <h1> 我的BLOG</h1> 13 </div> 14 <div id="title"> 15 ----i have dream.... 16 </div> 17 18 <div id="left"> 19 <div id="blog_entry"> 20 <div id="blog_title"> 添加一篇新日志</div> 21 <div id="blog_body"> 22 <div id="blog_date"></div> 23 <table border="0"> 24 <form method="POST" action="add.php"> 25 <tr><td> 日志标题:</td></tr> 26 <tr><td><input type="text" name="title" size="50"></td></tr> 27 <tr><td> 日志内容:</td></tr> 28 <tr><td><textarea name="content" cols="49" rows="10">< /textarea></td></tr> 29 <tr><td><input type="submit" value=" 提交"></td></tr> 30 </form> 31 </table> 32 </div> 33 </div> 34 </div> 35 36 <div id="right"> 37 <div id="sidebar"> 38 <div id="menu_title"> 关于我</div> 39 <div id="menu_body"> 我是个PHP 爱好者</div> 40 </div> 41 </div> 42 43 <div id="footer"> 44 CopyRight 2011 45 </div> 46 </div> 47 48 <body> 49 </html>
【代码解析】这里读者要注意的也是一些div+css的布局,读者要注意,每个div的id与前面定义的CSS代码相对应。
还需要在add.php里实现对用户提交数据的处理。该处理的主要工作是,把用户提交的数据存储到文本文件中。在完成数据存储之后,程序还应该向用户反馈一个信息,提示用户数据已经成功保存,或者提示由于某些原因,数据存储失败。代码12-6是增加了数据处理功能的add.php的完整代码。
代码12-6 添加日志文章的完整程序add.php
01 <?php 02 $ok = true; 03 if(isset($_POST['title']) && isset($_POST['content'])) // 判断变量$_POST['content'] 和$_POST['title'] 04 { 05 $ok = true; 06 07 $title = trim($_POST['title']); // 获取日志标题 08 $content = trim($_POST['content']); // 获取日志内容 09 $date = time(); // 获取日志时间 10 $blog_str = $title.'|'.$date.'|'.$content; // 将上述内容合并成字符串 11 12 $ym = date('Ym',time()); // 获取日期中的年和月 13 $d = date('d',time()); // 获取日期中的日 14 $time = date('His',time()); // 获取日期中的时间,His 可参考表7-1 15 16 $folder = 'contents/'.$ym; // 根据年和月设置目录名 17 $file = $d.'-'.$time.'.txt'; // 获取时间和日来设置文件名 18 $filename = $folder.'/'.$file; 19 $entry = $ym.'-'.$d.'-'.$time; 20 21 if(file_exists($folder) == false) 22 { 23 if(!mkdir($folder)) 24 { 25 //$ok = false; 26 //$msg = '<font color=red> 创建目录异常,添加日志失败</font>'; 27 } 28 } 29 30 $fp = @fopen($filename, 'w'); // 打开文件 31 if($fp) 32 { 33 flock($fp, LOCK_EX); 34 $result = fwrite($fp, $blog_str); // 写入文件 35 $lock = flock($fp, LOCK_UN); 36 fclose($fp); // 关闭文件 37 } 38 if(strlen($result)>0) // 判断写入是否成功 39 { 40 //$ok = false; 41 $msg = ' 日志添加成功,<a href="post.php?entry='.$entry.'"> 查看该日志文章</a>'; 42 echo $msg; 43 } 44 } 45 ?> 46 47 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 48 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 49 <html> 50 <head> 51 <title> 基于文本的简易BLOG</title> 52 <link rel="stylesheet" type="text/css" href="style.css" /> 53 </head> 54 <body> 55 56 <div id="container"> 57 <div id="header"> 58 <h1> 我的BLOG</h1> 59 </div> 60 <div id="title"> 61 ----I have dream.... 62 </div> 63 64 <div id="left"> 65 <div id="blog_entry"> 66 <div id="blog_title"> 添加一篇新日志</div> 67 68 <div id="blog_body"> 69 <div id="blog_date"></div> 70 <table border="0"> 71 <form method="POST" action="add.php"> 72 <tr><td> 日志标题:</td></tr> 73 <tr><td><input type="text" name="title" size="50"></td></tr> 74 <tr><td> 日志内容:</td></tr> 75 <tr><td><textarea name="content" cols="49" rows="10"></textarea> </td></tr> 76 <tr><td><input type="submit" value=" 提交"></td></tr> 77 </form> 78 </table> 79 </div><!-- blog_body--> 80 </div><!-- blog_entry--> 81 </div> 82 83 <div id="right"> 84 <div id="sidebar"> 85 <div id="menu_title"> 关于我</div> 86 <div id="menu_body"> 我是个PHP 爱好者</div> 87 </div> 88 </div> 89 90 <div id="footer"> 91 CopyRight 2011 92 </div> 93 </div> 94 95 <body> 96 </html>
【代码解析】代码第03行的isset()函数判断变量是否已经被赋值,如果是则返回真。第03行只有两个变量$_POST['content']和$_POST['title']都被赋值才会返回真。
下面就来添加一篇日志文章,看看add.php的实际执行效果。打开浏览器,访问add.php,输入一段文章内容,如图12-8所示。单击“提交”按钮后,如果一切正常,将会在该页的最上方看到日志文章添加成功的信息,如图12-9所示。
这个提示信息不仅告知用户日志文章添加成功,而且给出了刚刚所添加文章的链接,这是一个链接到post.php的,单击该链接,可以看到刚刚添加的日志文章,如图12-10所示。
图12-8 添加一篇新日志文章
图12-9 日志文章添加成功
图12-10 新添加的日志文章
12.3.4 实现登录功能
至此,已经完成了该BLOG系统日志文章浏览与添加的功能。通常,一个系统只有允许用户登录后,才能完成该系统相应的管理操作,本章要实现的BLOG系统也不例外。本小节将向读者介绍该BLOG系统用户登录与退出的实现。
用户登录需要用户名和密码,这里将用户名和密码配置到.php文件中,登录程序将用户输入的用户名和密码与该php文件中设置的用户名和密码进行比较,如果完全匹配,则登录成功,否则提示用户名或密码错误。建立一个名为auth.php的文件,存放在BLOG系统的config目录下,用来设置用户名和密码,程序内容如代码12-7所示。
代码12-7 用户名和密码配置文件auth.php
01 <?php 02 $AUTH = array(); 03 $AUTH['user'] = 'admin'; 04 $AUTH['passwd'] = '21232f297a57a5a743894a0e4a801fc3'; 05 ?>
【代码解析】代码第02行定义了数组$AUTH,然后第03~04行分别为数组中的用户名和用户密码赋值。
注意 auth.php定义了一个数组来存放用户名和密码,其中,密码是将某字符串经过函数md5()加密的,读者在测试时可通过使用函数md5()加密某字符串后自行设定。
然后实现用户登录程序。该程序由处理用户登录的PHP代码和显示登录界面的HTML组成,其完整代码如代码12-8所示。
代码12-8 用户登录程序login.php
01 <?php 02 include 'config/auth.php'; // 包含配置文件 03 session_start(); 04 05 if(isset($_POST['user']) && isset($_POST['passwd'])) // 判断用户的输入 06 { 07 $user = $_POST['user']; 08 $passwd = $_POST['passwd']; 09 10 $passwd = md5($passwd); // 对密码使用md5 加密 11 12 if($user != $AUTH['user'] || $passwd != $AUTH['passwd']) // 验证失败 13 { 14 echo '<strong><font color="red"> 用户名或密码错误!</font></strong>'; 15 } 16 else 17 { 18 $_SESSION['user'] = $user; // 验证成功,设置session 19 header("location: index.php"); 20 } 21 } 22 ?> 23 24 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 25 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 26 <html> 27 <head> 28 <title> 基于文本的简易BLOG</title> 29 <link rel="stylesheet" type="text/css" href="style.css" /> 30 </head> 31 <body> 32 33 <div id="container"> 34 <div id="header"> 35 <h1> 我的BLOG</h1> 36 </div> 37 <div id="title"> 38 ----I have dream.... 39 </div> 40 41 <div id="left"> 42 <div id="blog_entry"> 43 <div id="blog_title"> 用户登录</div> 44 45 <div id="blog_body"> 46 <div id="blog_date"></div> 47 <table border="0"> 48 <form method="POST" action="login.php"> 49 <tr><td> 用户名称:</td><td><input type="text" name="user" size="15"> </td></tr> 50 <tr><td> 用户密码:</td><td><input type="password" name="passwd" 51 size="15"></td></tr> 52 <tr><td><input type="submit" value=" 登录"></td></tr> 53 </form> 54 </table> 55 </div><!-- blog_body--> 56 </div><!-- blog_entry--> 57 </div> 58 59 <div id="right"> 60 <div id="sidebar"> 61 <div id="menu_title"> 关于我</div> 62 <div id="menu_body"> 我是个PHP 爱好者</div> 63 </div> 64 </div> 65 66 <div id="footer"> 67 CopyRight 2011 68 </div> 69 </div> 70 71 <body> 72 </html>
【代码解析】首先将用户名和密码设置文件config/auth.php包含到程序当中,如代码第02行所示。接着判断用户是否输入了正确的用户名和密码,如果输入有误,将提示错误信息,如代码第14行所示;如果输入正确,将用户名存入session,然后跳转到BLOG首页,如代码第18~19行所示。通过浏览器访问login.php,可以看到如图12-11所示的登录界面。
图12-11 用户登录界面
12.3.5 实现BLOG首页
用户成功登录后,会转向BLOG的首页,本小节向读者介绍BLOG首页的实现。因为用户登录后,可以完成对BLOG的各项管理操作,所以如果用户已经登录,就在首页的日志文章后添加“编辑”和“删除”链接,以便用户完成对日志文章的编辑和删除。因为登录程序设置了session,所以可以在首页中使用session对用户是否已经登录进行判断。
登录前和登录后的首页显示会稍有不同。比如,登录后应该显示“编辑”、“删除”和“退出”链接,而没有登录的情况下,用户只能看到“登录”链接。另外,BLOG首页除了显示日志文章外,还将显示日志文章按年月归档的导航列表,所以首页还应该实现日志文章的归档处理。代码12-9是实现BLOG首页的完整程序。
代码12-9 首页程序index.php
01 <?php 02 $login = false; 03 session_start(); 04 05 if(!empty($_SESSION['user']) && $_SESSION['user']=='admin') // 判断用户是否登录 06 $login = true; 07 08 $file_array = array(); 09 $folder_array = array(); 10 11 $dir = 'contents'; 12 $dh = opendir($dir); // 打开保存日志的目录 13 14 if($dh) 15 { 16 $filename = readdir($dh); // 读取目录下的文件 17 18 while($filename) // 循环处理按年月归档的日志文章 19 { 20 if($filename != '.' && $filename != '..') 21 { 22 $folder_name = $filename; 23 array_push($folder_array,$folder_name); 24 } 25 $filename = readdir($dh); 26 } 27 } 28 rsort($folder_array); // 对目录排序 29 30 $post_data = array(); 31 foreach($folder_array as $folder) 32 { 33 $dh = opendir($dir.'/'.$folder); // 处理每个目录下的日志文件 34 while(($filename = readdir($dh)) !== FALSE) 35 { 36 if(is_file($dir.'/'.$folder.'/'.$filename)) 37 { 38 $file = $filename; 39 $file_name = $dir.'/'.$folder.'/'.$file; 40 41 if(file_exists($file_name)) // 判断文件是否存在 42 { 43 $fp = @fopen($file_name, 'r'); 44 if($fp) 45 { 46 flock($fp, LOCK_SH); 47 $result = fread($fp, filesize($file_name)); // 读取文件内容 48 } 49 flock($fp, LOCK_UN); 50 fclose($fp); 51 } 52 $temp_data = array(); 53 $content_array = explode('|', $result); 54 55 $temp_data['SUBJECT'] = $content_array[0]; // 文章标题 56 $temp_data['DATE'] = date('Y-m-d H:i:s',$content_array[1]); // 发表时间 57 $temp_data['CONTENT'] = $content_array[2]; // 文章内容 58 $file = substr($file,0,9); // 日志文章所在文件夹名 59 $temp_data['FILENAME'] = $folder.'-'.$file; 60 array_push($post_data,$temp_data); 61 } 62 } 63 } 64 ?> 65 66 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 67 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 68 <html> 69 <head> 70 <title> 基于文本的简易BLOG</title> 71 <link rel="stylesheet" type="text/css" href="style.css" /> 72 </head> 73 <body> 74 75 <div id="container"> 76 <div id="header"> 77 <h1> 我的BLOG</h1> 78 </div> 79 <div id="title"> 80 ----I have dream.... 81 </div> 82 <div id="left"> 83 <?php foreach($post_data as $post) // 显示所有日志文章 84 { 85 ?> 86 <div id="blog_entry"> 87 <div id="blog_title"><?php echo $post['SUBJECT']; ?></div> 88 <div id="blog_body"> 89 <div id="blog_date"><?php echo $post['DATE']; ?></div> 90 <?php echo $post['CONTENT'];?> 91 <div> 92 <?php 93 if($login) 94 { 95 echo '<a href="edit.php?entry='.$post['FILENAME'].'"> 编辑 </a> <a 96 href="delete.php?entry='.$post['FILENAME'].'"> 删除</a>'; // 输出日志文章的“编辑”和“删除”链接 97 } 98 ?> 99 100 </div> 101 </div><!--blog_body--> 102 </div><!--blog_entry--> 103 <?php } ?> 104 </div> 105 106 <div id="right"> 107 <div id="sidebar"> 108 <div id="menu_title"> 关于我</div> 109 <div id="menu_body"> 110 我是个PHP 爱好者 111 <br/><br/> 112 <?php if($login) {echo '<a href="logout.php"> 退出</a>';} else{ echo '<a href="login.php"> 登录 113 </a>';} ?> 114 </div> 115 </div> 116 <br/> 117 <div id="sidebar"> 118 <div id="menu_title"> 日志归档</div> 119 <?php foreach($folder_array as $ym) // 输出日志按年月的归档 120 { 121 $entry = $ym; 122 $ym = substr($ym,0,4).'-'.substr($ym,4,2); 123 echo '<div id="menu_body"><a href="archives.php?ym='.$entry.'"> '.$ym.'</a></div>'; 124 } 125 ?> 126 </div> 127 </div> 128 129 <div id="footer"> 130 copyright 2011 131 </div> 132 </div> 133 134 <body> 135 </html> 136 <?php close($dh);?>
【代码解析】index.php主要实现了3大功能,一是列出所有日志文章;二是实现了日志文章按年月归档的显示;三是根据用户登录与否显示不同的链接。
如果用户已经登录,将会看到如图12-12所示的首页界面。这时,用户可以通过首页的“编辑”或“删除”链接对日志文章进行相关管理操作。因为post.php会显示某篇具体的日志文章,所以,应该在post.php中也提供“编辑”和“删除”链接,当然,这些链接只有在用户登录后才能显示出来。
图12-12 登录后的首页
说明 post.php中实现了提供“编辑”和“删除”链接的方法,与在index.php中的实现方法几乎一样,这里不再赘述,读者可以自行完成。
12.3.6 实现BLOG文章的编辑功能
用户单击“编辑”链接后,进入日志文章的编辑界面。该编辑界面可以让用户修改日志文章的标题、文章内容,修改后提交,即可完成对日志文章的编辑。BLOG的编辑界面如图12-13所示。
图12-13 日志文章的编辑界面
编辑日志文章的程序,首先应该判断用户是否登录,也就是说只有登录用户才能执行日志文章的修改操作,这可以通过使用session来完成。还需要将要编辑的日志内容显示出来,以供用户修改。编辑日志文章功能的完整程序如代码12-10所示。
代码12-10 编辑日志文章的程序edit.php
01 <?php 02 session_start(); 03 $ok = false; 04 05 if(!isset($_GET['entry'])) // 判断是否设置了$_GET['entry'] 的值 06 { 07 echo ' 请求参数错误!'; 08 exit; 09 } 10 11 if(empty($_SESSION['user']) || $_SESSION['user']!='admin') // 判断是否设置了这俩变量的值 12 { 13 echo ' 请<a href="login.php"> 登录</a> 后执行该操作。'; 14 exit; 15 } 16 17 $path = substr($_GET['entry'],0,6); // 日志存储目录 18 $entry = substr($_GET['entry'],7,9); // 日志文件名称 19 $file_name = 'contents/'.$path.'/'.$entry.'.txt'; 20 21 if(file_exists($file_name)) // 取出原文件内容 22 { 23 $fp = @fopen($file_name, 'r'); 24 if($fp) 25 { 26 flock($fp, LOCK_SH); 27 $result = fread($fp, filesize($file_name)); 28 } 29 flock($fp, LOCK_UN); 30 fclose($fp); 31 32 $content_array = explode('|', $result); // 将文件内容存放在数组中 33 } 34 35 if(isset($_POST['title']) && isset($_POST['content'])) 36 { 37 $title = trim($_POST['title']); // 获取日志主题 38 $content = trim($_POST['content']); // 获取日志内容 39 40 if(file_exists($file_name)) // 判断文件是否存在 41 { 42 // 根据用户修改时提交的内容,替换现有文件的内容,注意替换的对应关系,即标题、内容各自对应 43 做替换 44 $blog_temp = str_replace($content_array[0],$title,$result); 45 $blog_str = str_replace($content_array[2],$content,$blog_temp); 46 47 $fp = @fopen($file_name, 'w'); 48 if($fp) 49 { 50 flock($fp, LOCK_EX); 51 $result = fwrite($fp, $blog_str); // 写入文件 52 $lock = flock($fp, LOCK_UN); 53 fclose($fp); 54 } 55 } 56 57 if(strlen($result)>0) // 判断修订是否成功 58 { 59 $ok = true; 60 $msg = ' 日志修改成功,<a href="post.php?entry='.$_GET['entry'].'"> 查看该日志文 章</a>'; 61 } 62 } 63 ?> 64 65 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 66 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 67 <html> 68 <head> 69 <title> 基于文本的简易BLOG</title> 70 <link rel="stylesheet" type="text/css" href="style.css" /> 71 </head> 72 <body> 73 74 <div id="container"> 75 <div id="header"> 76 <h1> 我的BLOG</h1> 77 </div> 78 <div id="title"> 79 ----I have dream.... 80 </div> 81 82 <div id="left"> 83 <div id="blog_entry"> 84 <div id="blog_title"> 编辑日志</div> 85 86 <div id="blog_body"> 87 <?php if($ok == false) 88 { 89 ?> 90 <div id="blog_date"></div> 91 <table border="0"> 92 <form method="POST" action="edit.php?entry=<?php echo $_GET['entry'];?>"> 93 <tr><td> 日志标题:</td></tr> 94 <tr><td><input type="text" name="title" size="50" value="<?php echo 95 $content_array[0]; ?>"></td></tr> 96 <tr><td> 日志内容:</td></tr> 97 <tr><td><textarea name="content" cols="49" rows="10"><?php echo 98 $content_array[2];?></textarea></td></tr> 99 <tr><td> 创建于:<?php echo date('Y-m-d H:i:s',$content_array[1]); ?> </td></tr> 100 <tr><td><input type="submit" value=" 提交"></td></tr> 101 </form> 102 </table> 103 <?php } ?> 104 <?php if ($ok == true){ echo $msg; }?> 105 </div><!-- blog_body--> 106 </div><!-- blog_entry--> 107 </div> 108 109 <div id="right"> 110 <div id="sidebar"> 111 <div id="menu_title"> 关于我</div> 112 <div id="menu_body"> 我是个PHP 爱好者</div> 113 </div> 114 </div> 115 116 <div id="footer"> 117 CopyRight 2011 118 </div> 119 </div> 120 121 <body> 122 </html>
【代码解析】编辑操作的实质是更改文件的内容。为此,首先要从所要编辑的日志文章的文件中将文件内容全部取出,并存入数组中,如代码第21~33行所示。这个数组将在显示日志文章原有内容时使用,如代码91~102行所示。然后,程序根据用户传入的内容使用字符串替换函数str_replace()将原有内容替换为新的内容,从而完成对日志文章的修改,如代码第44、45行所示。最后,还需要将最新的内容写回到文件中,如代码第47~54行所示。如果这一切都正常完成,程序会向用户发出日志修改成功的信息,如图12-14所示。
图12-14 日志修改成功后的提示信息
12.3.7 实现BLOG文章的删除功能
删除日志文章也是BLOG系统的基本功能之一,本小节将向读者介绍删除日志文章功能的实现。删除一篇日志文章的基本操作是,找到日志文章所在路径和文件,然后执行删除操作。对于不存在的日志执行删除操作,应该给出“所要删除文章不存在”的提示信息。
当用户单击“删除”链接后,应该给用户一个提示界面,确认是否用户真的想删除该日志文章,以免用户误操作。另外,和编辑日志文章一样,只能在用户登录之后才能执行删除操作。
因为删除确认界面和执行删除操作的功能由同一个程序实现,所以这里通过向程序传入两个不同的URL参数,以便程序可以判断是显示删除确认界面还是执行删除操作,这两个参数是entry和id,其中id将作为表单隐藏域数据传给删除程序。如果程序获得id参数,则表示要执行删除操作。参数id的值和参数entry的值完全一样,它们只是用来区分不同的操作。删除日志文章的程序如代码12-11所示。
代码12-11 删除日志文章的程序delete.php
01 <?php 02 session_start(); 03 $ok = false; 04 05 if(empty($_SESSION['user']) || $_SESSION['user']!='admin') // 判断用户是否登录 06 { 07 echo ' 请<a href="login.php"> 登录</a> 后执行该操作。'; 08 exit; 09 } 10 11 if(!isset($_GET['entry'])) // 判断$_GET['entry'] 变量是否已经设置了 12 { 13 if(!isset($_POST['id'])) // 判断是否有id 参数 14 { 15 $ok = true; 16 $msg = ' 请求参数错误!<a href="index.php"> 返回首页</a>'; 17 } 18 else 19 { 20 // 做删除操作 21 $path = substr($_POST['id'],0,6); // 日志存储目录 22 $entry = substr($_POST['id'],7,9); // 日志文件名称 23 $file_name = 'contents/'.$path.'/'.$entry.'.txt'; 24 if(unlink($file_name)) 25 { 26 $ok = true; 27 $msg = ' 该日志成功删除!<a href="index.php"> 返回首页</a>'; 28 } 29 else 30 { 31 $ok = true; 32 $msg = ' 该日志删除失败!<a href="index.php"> 返回首页</a>'; 33 } 34 } 35 } 36 else 37 { 38 $form_data = ''; 39 $path = substr($_GET['entry'],0,6); // 日志存储目录 40 $entry = substr($_GET['entry'],7,9); // 日志文件名称 41 $file_name = 'contents/'.$path.'/'.$entry.'.txt'; 42 if(file_exists($file_name)) // 判断是否已经存在该文件 43 { 44 $form_data = '<input type="hidden" name="id" value="'.$_GET['entry'].'">'; 45 } 46 else 47 { 48 $ok = true; 49 $msg = ' 所要删除的日志不存在!<a href="index.php"> 返回首页</a>'; 50 } 51 } 52 ?> 53 54 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 55 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 56 <html> 57 <head> 58 <title> 基于文本的简易BLOG</title> 59 <link rel="stylesheet" type="text/css" href="style.css" /> 60 </head> 61 <body> 62 63 <div id="container"> 64 <div id="header"> 65 <h1> 我的BLOG</h1> 66 </div> 67 <div id="title"> 68 ----i have dream.... 69 </div> 70 <div id="left"> 71 <div id="blog_entry"> 72 <div id="blog_title"> 删除日志</div> 73 <div id="blog_body"> 74 <?php if($ok == false) 75 { 76 ?> 77 <form method="POST" action="delete.php"> 78 <font color="red"> 删除的日志将无法恢复,确定要删除吗?</font><br/> 79 <input type=submit value=" 确定"> 80 <?php echo $form_data; ?> 81 </form> 82 <?php } ?> 83 <?php if($ok == true) { echo $msg; } ?> 84 </div><!--blog_body--> 85 </div><!--blog_entry--> 86 </div> 87 88 <div id="right"> 89 <div id="sidebar"> 90 <div id="menu_title"> 关于我</div> 91 <div id="menu_body"> 我是个PHP 爱好者</div> 92 </div> 93 </div> 94 95 <div id="footer"> 96 copyright 2011 97 </div> 98 </div> 99 100 <body> 101 </html>
【代码解析】该程序首先根据session判断用户是否登录,没有登录则提示用户登录后再执行删除操作,如代码第05~09行所示。如果已经登录,则根据是否传入URL参数entry来判断是显示删除确认界面还是执行删除操作。如果没有传入entry参数,则判断是否传入日志文章id,程序将根据此id删除日志文章。如果传入id,则执行删除操作。当用户登录后,单击“删除”链接,将会看到如图12-15所示的确认界面。
图12-15 删除日志文章确认界面
在此确认界面中单击“确定”按钮,将会删除该日志文章。如果删除成功,将会看到如图12-16所示的“该日志成功删除!”提示信息。
图12-16 成功删除日志文章后的提示界面
12.3.8 实现BLOG归档显示的功能
BLOG系统通常会提供按年月归档显示日志的功能,本BLOG系统也将实现这一功能。该功能会将某年某月下的所有日志文章列出,方便用户按时间查看每天的日志文章。
浏览归档显示的日志文章,不需要用户登录。向浏览归档日志文章程序传入的参数是一个年月值字符串,程序根据该值找到日志所在目录,从而获取日志文章的文件内容,然后将文件内容显示到Web界面上。代码12-12是该程序的实现,程序命名为archives.php,如下所示。
代码12-12 归档显示日志文章的程序archives.php
01 <?php 02 $ok = false; 03 04 if(!isset($_GET['ym']) || empty($_GET['ym'])) // 请求参数中的目录信息 05 { 06 $ok = true; 07 $msg = ' 请求参数错误!<a href="index.php"> 返回首页</a>'; 08 } 09 10 $folder_array = array(); // 归档目录后的所有目录存在此数组中 11 $dir = 'contents'; 12 $folder = $_GET['ym']; 13 if(!is_dir($dir.'/'.$folder)) // 找到contents 目录下的所有归档目录 14 { 15 $ok = true; 16 $msg = ' 请求参数错误!<a href="index.php"> 返回首页</a>'; 17 } 18 19 $dh = opendir($dir); // 打开目录 20 if($dh) 21 { 22 $filename = readdir($dh); // 读取指定目录下的所有目录 23 while($filename) 24 { 25 if($filename != '.' && $filename != '..') 26 { 27 $folder_name = $filename; 28 array_push($folder_array,$folder_name); 29 } 30 $filename = readdir($dh); 31 } 32 } 33 rsort($folder_array); // 对目录进行排序 34 35 $post_data = array(); 36 $dh = opendir($dir.'/'.$folder); 37 38 while(($filename = readdir($dh)) !== FALSE) 39 { 40 if(is_file($dir.'/'.$folder.'/'.$filename)) 41 { 42 $file = $filename; 43 $file_name = $dir.'/'.$folder.'/'.$file; 44 45 if(file_exists($file_name)) // 判断文件是否存在 46 { 47 $fp = @fopen($file_name, 'r'); 48 if($fp) 49 { 50 flock($fp, LOCK_SH); 51 $result = fread($fp, filesize($file_name)); // 读取文件 52 } 53 flock($fp, LOCK_UN); 54 fclose($fp); 55 } 56 $temp_data = array(); 57 $content_array = explode('|', $result); 58 // 以下是文件中的3 个日志信息 59 $temp_data['SUBJECT'] = $content_array[0]; // 读取标题 60 $temp_data['DATE'] = date('Y-m-d H:i:s',$content_array[1]); // 读取日期 61 $temp_data['CONTENT'] = $content_array[2]; // 读取内容 62 array_push($post_data,$temp_data); 63 } 64 } 65 ?> 66 67 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 68 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 69 <html> 70 <head> 71 <title> 基于文本的简易BLOG</title> 72 <link rel="stylesheet" type="text/css" href="style.css" /> 73 </head> 74 <body> 75 76 <div id="container"> 77 <div id="header"> 78 <h1> 我的BLOG</h1> 79 </div> 80 <div id="title"> 81 ----I have dream.... 82 </div> 83 <div id="left"> 84 <?php 85 if($ok == false) 86 { 87 foreach($post_data as $post) 88 { 89 ?> 90 <div id="blog_entry"> 91 <div id="blog_title"><? echo $post['SUBJECT']; ?></div> 92 <div id="blog_body"> 93 <div id="blog_date"><? echo $post['DATE']; ?></div> 94 <?php echo $post['CONTENT']; ?> 95 </div><!--blog_body--> 96 </div><!--blog_entry--> 97 <?php } 98 } 99 else{ 100 echo $msg; 101 } 102 ?> 103 </div> 104 105 <div id="right"> 106 <div id="sidebar"> 107 <div id="menu_title"> 关于我</div> 108 <div id="menu_body"> 109 我是个PHP 爱好者 110 <br/><br/> 111 <a href="login.php"> 登录</a> 112 </div> 113 </div> 114 <br/> 115 <div id="sidebar"> 116 <div id="menu_title"> 日志归档</div> 117 <?php foreach($folder_array as $ym) 118 { 119 $entry = $ym; 120 $ym = substr($ym,0,4).'-'.substr($ym,4,2); 121 echo '<div id="menu_body"><a href="archives.php?ym= '.$entry.'">'.$ym.'</a></div>'; 122 } 123 ?> 124 </div> 125 </div> 126 127 <div id="footer"> 128 CopyRight 2011 129 </div> 130 </div> 131 132 <body> 133 </html> 134 <?php close($dh);?>
【代码解析】程序首先判断是否传入参数ym,该参数的值类似于201112,表示日志的归档年月。如果没有传入该参数,程序将提示错误信息,如代码第04~08行所示;如果传入的参数值没有其对应的目录,也会提示错误信息,如代码第10~17行所示。
接着,程序会在对应年月的目录下找出日志文章所在文件,这是一个循环查找的过程,如代码第38~64行所示。然后判断当前文件是目录还是普通文件,如代码第40行所示,如果是普通文件,程序将打开该文件并读取文件内容,存入数组当中;如果不是普通文件,则继续循环查找当前目录下的文件,直到找出所有普通文件(及日志文章所在文件)为止。从BLOG首页可以访问archives.php的结果如图12-17所示。
图12-17 按年月归档显示日志内容
12.3.9 实现BLOG的退出功能
这个BLOG系统的退出功能很简单,只需在程序中将用户登录时注册的session清空删除即可。代码12-13是退出程序的完整代码。
代码12-13 退出登录的程序logout.php
01 <?php 02 session_start(); 03 $info = ''; 04 05 if(isset($_SESSION['user'])) // 判断用户是否登录 06 { 07 $_SESSION['user'] = ''; 08 $msg = ' 您已经成功退出,<a href="index.php"> 返回首页</a>'; 09 } 10 else 11 { 12 $msg = ' 您未曾登录或已经超时退出,<a href="index.php"> 返回首页</a>'; 13 } 14 ?> 15 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 16 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 17 <html> 18 <head> 19 <title> 基于文本的简易BLOG</title> 20 <link rel="stylesheet" type="text/css" href="style.css" /> 21 </head> 22 <body> 23 24 <div id="container"> 25 <div id="header"> 26 <h1> 我的BLOG</h1> 27 </div> 28 <div id="title"> 29 ----i have dream.... 30 </div> 31 <div id="left"> 32 <div id="blog_entry"> 33 <div id="blog_title"> 退出登录</div> 34 <div id="blog_body"> 35 <?php echo $msg; ?> 36 </div><!--blog_body--> 37 </div><!--blog_entry--> 38 </div> 39 40 <div id="right"> 41 <div id="sidebar"> 42 <div id="menu_title"> 关于我</div> 43 <div id="menu_body"> 我是个PHP 爱好者</div> 44 </div> 45 </div> 46 47 <div id="footer"> 48 copyright 2011 49 </div> 50 </div> 51 52 <body> 53 </html>
【代码解析】必须有第05行的判断,因为只有用户在登录状态下才能退出。当用户在登录状态下单击首页的“退出”链接时,如果一切正常,将会看到如图12-18所示的成功退出提示界面。
图12-18 成功退出BLOG系统的提示界面