文章目录
前言
在此项目中我们要实现的功能有:用户登录、用户身份验证、管理密码、记录用户个人喜好、个性化内容、基于已用用户信息,展示用户可能感兴趣的内容
一、解决方案组件
1.用户识别和个性化
用户身份验证有许多可供选择的方法,由于希望将用户和个性化信息联系起来,使用就要将用户登录名和密码保存在一个MySQL数据库中,需要时候进行验证
如果要让用户以用户名和密码登录,需要如下组件
- 用户能够注册一个用户用户名和密码。我们需要限制用户名和密码的长度和格式
- 用户应该能够用注册过程提供的详细信息进行登录
- 用户完成用户访问之后能够登出。出于隐私考虑,如果用户使用自己家中的PC,这并不是很重要,但是在公用PC上访问就相当重要了,例如图书馆。
- 网站能够检测用户是否登录,并访问登录用户的数据
- 为了安全起见,用户可以修改密码
2.保存书签
要保存用户书签,需要在MySQL数据库创建关系表。必须实现以下功能
- 用户应该能够读取和浏览书签。
- 用户应该能够添加书签。站点需要检查这些 URL 是否有效。
- 用户应该能够删除书签
因此要为以上功能编写函数。
3.推荐书签
我们可以采取不同的方式向用户推荐书签。可以推荐最流行的或者关于某个主题最流行的站点。对于这个项目,我使用的是“相似意向”推荐系统,该系统可以查找与已登录用户具有相同书签的用户,并将他们的书签推荐给用户。为了避免推荐任何个人的书签,我只推荐几个用户同时拥有的书签。
因此,需要为这个功能编写函数。
二、解决方案概述
1.系统流程图
需要将该图中每个方块创建一个模块;其中一些模块需要一个脚本,而另外一些需要更多的脚本
2.建立函数库
还需要创建函数库,函数库包括
- 用户身份验证
- 书签保存与检索
- 数据验证
- 数据库链接
- 输出至浏览器。可以由该函数库提供所有 HTML 输出功能,确保在网站里所有可视外观是一致的
3.所需文件
文件名 | 描述 |
---|---|
bookmarks.sql | 创建 PHPbookmark 数据库的 SQL 语句 |
login.php | 系统登录表单后的登录页面 |
register_form.php | 用户注册表单 |
register_new.php | 处理注册信息的脚本 |
member.php | 用户主页面,包含该用户所有当前书签 |
add_bm_form.php | 添加新书签表单 |
add_bms.php | 将书签真正添加到数据库的脚本 |
delete_bms.php | 将用户书签列表中删除选定的书签的脚本 |
recommend.php | 基于用户以前的操作,推荐用户可能感兴趣的书签的脚本 |
change_passwd_form.php | 用户修改密码时要填的表单 |
change_passwd,php | 修改数据库中用户密码的表单 |
logout.php | 用户登出脚本 |
bookmark_fns.php | 应用包含文件集合 |
data_valid_fns.php | 用户输入数据有效性验证函数 |
db_fns.php | 连接数据库的函数 |
user_auth_fns.php | 用户身份验证的函数 |
url_fns,php | 增加和删除书签以及推荐的书签 |
output_fns.php | 以 HTML 形式格式化输出的函数 |
三、实现数据库
对于 PHPbookmark 数据库来说,只需要一个非常简单的数据库模式。在程序中,要保存用户名、邮箱地址以及用户密码,还要保存书签的 URL 。一个用户可能有许多书签,许多用户也可能注册了同一个书签。因此需要两个表, user 表和 bookmark 表。
CREATE DATABASE bookmarks;
USE bookmarks;
CREATE TABLE user (
username varchar(16) not null primary key,
passwd char(40) not null,
email varchar(100) not null
);
CREATE TABLE bookmark (
username VARCHAR(16) NOT NULL,
bm_URL VARCHAR(255) NOT NULL,
index (username),
index (bm_URL),
primary key (username, bm_URL)
);
GRANT SELECT, INSERT, UPDATE, DELETE
ON bookmarks.* TO bm_user@localhost identified by 'password';
以MySQL root 用户身份运行以上命令将在系统创建的数据库。以上命令可以通过系统命令行运行,如下所示:
mysql -u youruser -p < bookmars.sql
系统将要求输入 root 用户的密码。
四、实现基本站点
创建第一页成为 login.php,因为它向用户提供登录系统的机会。该页面的代码如下所示。
<?php
require_once('bookmark_fns.php');
do_html_header('');
display_site_info();
display_login_form();
do_html_footer();
以上脚本输出如图所示
该站点调用的函数包含在 bookmark_fns.php文件中,如下所示。
<?php
// We can include this file in all our files
// this way, every file will contain all our functions and exceptions
require_once('data_valid_fns.php');
require_once('db_fns.php');
require_once('user_auth_fns.php');
require_once('output_fns.php');
require_once('url_fns.php');
可以看到,该文件就是本应用将要使用到的 5 个其他引入文件的“容器”。因为函数可以进行逻辑分组,因此可以按此方式构建书签引动。某些函数分组可能可供其他项目使用,因此将每个函数组保存在不同的文件,这样需要的时候可以找到相应的函数库。创建 bookmark_fns.php 文件是因为大部分脚本都要用到这 5 个函数文件。在每个脚本里包含这一文件比使用 5 个 require 语句更容易一些
我们使用的函数都来自 output_fns.php 文件。这些函数相当只管,其输出都是非常简单的 HTML。改文件包含了在 login.php中使用的4个函数。函数内容如下
<?php
function do_html_header($title)
{
// print an HTML header
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><?php echo $title; ?></title>
<style>
div.formblock {
background: #ccc;
width: 300px;
padding: 6px;
border: 1px solid #000;
}
</style>
</head>
<body>
<div>
<h1>PHPbookmark</h1>
</div>
<hr/>
<?php
if ($title) {
do_html_heading($title);
}
}
function do_html_footer()
{
// print footer
?>
</body>
</html>
<?php
}
function do_html_heading($heading)
{
// print heading
?>
<h2><?php echo $heading; ?></h2>
<?php
}
function display_site_info()
{
// display some marketing info
?>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<?php
}
五、实现用户身份验证
1.用户注册
要注册一个用户,需要通过一个表单获取用户详细信息,并且将这些信息保存到数据库中。
当用户点击 login.ph页面的 “Not a member?” 链接时,就会出现一个由 register_form.php产生的注册表单。该脚本如下所示
<?php
require_once('bookmark_fns.php');
do_html_header('User Registration');
display_registration_form();
do_html_footer();
?>
可以看到,该页面非常简单,只调用了来自 output_fns.php 函数库的函数。该脚本输出如图所示。
灰色表单是由 display_registration_form() 函数输出。该函数也包含在 output_fns.php中,该函数如下所示
function display_registration_form()
{
?>
<form action="register_new.php" method="post">
<div class="formblock">
<h2>Register Now</h2>
<p><label for="email">Email Address:</label><br/>
<input type="email" name="email" id="email"
size="30" maxlength="100" required /></p>
<p><label for="username">Prefeered Username<br>(max 16 chars):</label><br/>
<input type="text" name="username" id="username"
size="16" maxlength="16" required/></p>
<p><label for="passwd">Passwd <br>(between 6 and 16 chars):</label><br/>
<input type="password" name="passwd" id="passwd"
size="16" maxlength="16" required/></p>
<p><label for="passwd2">Confirm Password:</label><br/>
<input type="password" name="passwd2" id="passwd2"></p>
<button type="submit">Register</button>
</div>
</form>
<?php
}
当用户点击 “Register” 按钮时, register_new.php脚本将运行。该脚本,如下所示
<?php
// include function files for this application
require_once('bookmark_fns.php');
// create short variable names
$email = $_POST['email'];
$username = $_POST['username'];
$passwd = $_POST['passwd'];
$passwd2 = $_POST['passwd2'];
// start session which may be needed later
// start it now because it must go before headers
session_start();
try{
// check forms filled in
if (!filled_out($_POST)) {
throw new Exception('You have not filled the form out correctly - please go back and try again.');
}
// email address not valid
if (!valid_email($email)) {
throw new Exception('That is not a valid email address.Please go back and try again');
}
// password not the same
if ($passwd != $passwd2) {
throw new Exception('The passwords you entered do not match - please go back and try again.');
}
// check password length is ok
// ok if username truncates, but passwords will get
// munged if they are too long.
if ((strlen($passwd)< 6) || (strlen($passwd) > 16)) {
throw new Exception('Your password must be between 6 and 16 characters. Please go back and try again.');
}
// attempt to register
// this function can also throw an exception
register($username, $email, $passwd);
// register session variable
$_SESSION['valid_user'] = $username;
// provide link to members page
do_html_header('Registration successful');
echo 'Your registration was successful. Go to the members page to start setting up your bookmarks!';
do_html_url('member.php','Go to members page');
// end page
do_html_footer();
} catch(Exception $e) {
do_html_header('Problem:');
echo $e->getMessage();
do_html_footer();
exit;
}
脚本的主体有 try 语句块,因为需要检查许多条件。如果任何一个条件失败,执行将进入 catch 语句块。
接下来验证用户输入数据。在此过程中,需要检测修过条件,如下所示
检查表单是否完全添加。 调用 filled_out() 函数检查, 如下所示
if (!filled_out($_POST))
检查邮件地址是否有效,如下所示
if (!valid_email($email))
以上函数是自定义函数,包含在 data_valid_fns.php文件中。该代码如下所示
<?php
function filled_out($form_vars) {
// test that each variable has a value
foreach ($form_vars as $key => $value) {
if ((!isset($key)) || ($value == '')) {
return false;
}
return true;
}
}
function valid_email($address) {
// check an email address is possibly valid
if (preg_match('/^[a-zA-Z0-9_\.\-]+@[a-zA-Z0-9\-]+\.[a-zA-Z0-9\-\.]+$/',$address)) {
return true;
} else {
return false;
}
}
filled_out() 函数需要一个数组变量作为输入参数;通常是 $_POST 或 $_GET变量数组。它检查表单是否完全填写,如果完全填写返回 true,否则返回 false
验证用户两次输入的密码是否一致,如下所示
if ($passwd != $passwd2)
验证用户名和密码长度是否在规定范围之内,如下所示
if ((strlen($passwd)< 6) || (strlen($passwd) > 16))
在验证了输入数据之后,我们就可以尝试注册该用户。回顾 register_new.php脚本,会发现它是按如下方式实现的
register($username, $email, $passwd);
// register session variable
$_SESSION['valid_user'] = $username;
// provide link to members page
do_html_header('Registration successful');
echo 'Your registration was successful. Go to the members page to start setting up your bookmarks!';
do_html_url('member.php','Go to members page');
// end page
do_html_footer();
可以看到,我们使用用户输入用户名、邮件地址和密码作为参数调用了 register()函数。如果函数执行成功,我们就将用户名注册为绘画变量,并为用户提供一个指向用户主页的链接(如果函数执行失败,它将抛出一个在 catch 语句块捕获的异常)。其输出图所示
register()函数包含在 user_auth_fns.php 函数库中,如下所示
function register($username, $email, $password)
{
// register new person with db
// return true or error message
// connect to db
$conn = db_connect();
// check if username is unique
$result = $conn->query("SELECT * FROM user WHERE username='" . $username . "'");
if (!$result) {
throw new Exception('Could not execute query');
}
if ($result -> num_rows > 0) {
throw new Exception('That username is taken - go back and choose another one.');
}
// if ok, put in db
$result = $conn->query("INSERT INTO user VALUES('" . $username . "','" . $password . "','" . $email . "')");
if (!$result) {
throw new Exception('Could not register you in database - please try again later.');
}
return true;
}
该函数没有特别的新内容;只是将它链接到前面已建的数据库。如果选定用户名已经存在,或者数据库不能被更行,它将抛出一个异常。否则,它将更新数据库并返回 true。
需要注意的是,我们使用了自定义函数 db_connect() 来执行数据库连接操作。该函数提供了用户使用用户名和密码连接数据库的唯一入口。这样,如果需要修改数据库密码,只需改变应用的一个文件即可。 db_connect() 函数如下所示
<?php
function db_connect() {
$result = new mysqli('localhost', 'bm_user','password','bookmarks');
if (!$result) {
throw new Exception('Could not connect to database server');
} else {
return $result;
}
}
?>
用户注册之后,可以通过常规登录或退出页面登录和退出网站
2.登录
如果用户将其信息输入到 login.php 表单,并提交给系统,系统将运行 member.php脚本。如果用户信息正确,该脚本允许用户登录。同时显示所有登录用户相关的书签。该脚本(member.php) 如下所示
<?php
// include function files for this application
require_once('bookmark_fns.php');
session_start();
//create short variable names
if (!isset($_POST['username'])) {
//if not isset -> set with dummy value
$_POST['username'] = " ";
}
$username = $_POST['username'];
if (!isset($_POST['passwd'])) {
//if not isset -> set with dummy value
$_POST['passwd'] = " ";
}
$passwd = $_POST['passwd'];
if ($username && $passwd) {
// they have just tried logging in
try {
login($username, $passwd);
// if they are in the database register the user id
$_SESSION['valid_user'] = $username;
}
catch(Exception $e) {
// unsuccessful login
do_html_header('Problem:');
echo 'You could not be logged in.<br>
You must be logged in to view this page.';
do_html_url('login.php', 'Login');
do_html_footer();
exit;
}
}
do_html_header('Home');
check_valid_user();
// get the bookmarks this user has saved
if ($url_array = get_user_urls($_SESSION['valid_user'])) {
display_user_urls($url_array);
}
// give menu of options
display_user_menu();
do_html_footer();
?>
首先,检查用户是否来自首页,既用户是否填写了表单,然后尝试将其登录,如下所示:
if ($username && $passwd) {
// they have just tried logging in
try {
login($username, $passwd);
// if they are in the database register the user id
$_SESSION['valid_user'] = $username;
}
以上代码调用了 login() 函数登录。该函数定义在user_auth_fns.php库中,该代码如下所示
function login($username, $password ){
// check username and password with db
// if yer, return true
// else throw exception
//connect to db
$conn = db_connect();
// check if username is unique
$result = $conn->query("SELECT * FROM user WHERE username='"
.$username."' AND passwd='".$password."'");
if (!$result) {
throw new Exception('Could not log you in.');
}
if ($result->num_rows>0) {
return true;
} else {
throw new Exception('Could not log you in.');
}
}
如果用户登录成功,我们将注册其绘画。并将用户名保存到会话变量 balid_user 中。
如果一切顺利,为该用户显示会员页面,如下代码所示:
```php
do_html_header('Home');
check_valid_user();
// get the bookmarks this user has saved
if ($url_array = get_user_urls($_SESSION['valid_user'])) {
display_user_urls($url_array);
}
// give menu of options
display_user_menu();
do_html_footer();
该页面也是通过输出函数创建的。注意,上述代码使用了几个新函数。他们分别是 user_auth_fns.php 文件中的 check_valid_user() 函数。 check_valid_user()函数将检查当前用户是否拥有一个注册的会话。这是针对还没登录却处于会话中的用户。 get_user_urls() 函数将从数据库中获得该用户标签。
以上的新函数代码如下
function check_valid_user() {
// see if somebody is logged in and notify them if not
if (isset($_SESSION['valid_user'])) {
echo "Logged in as ".$_SESSION['valid_user'].".<br>";
} else {
// they are not logged in
do_html_header('Problem: ');
echo "You are not logged in.<br>";
do_html_url('login.php','Login');
do_html_footer();
exit;
}
}
function get_user_urls($username)
{
// extract from the database all the names this user has stored
$conn = db_connect();
$result = $conn->query("SELECT bm_URL FROM bookmark WHERE username='" . $username . "'");
if (!$result) {
return false;
}
// create an array of the URLs
$url_array = array();
for ($count = 1; $row = $result->fetch_row(); ++$count) {
$url_array[$count] = $row[0];
}
return $url_array;
}
function display_user_urls($url_array)
{
// display the table of USERs
// set global variable, so we can test later if this is on the page
global $bm_table;
$bm_table = true;
?>
<br/>
<form action="delete_bms.php" method="post" name="bm_table">
<table style="width: 300px; padding: 2px;margin: 0">
<?php
$color = "#cccccc";
echo "<tr style='background-color:" . $color . "'><td><strong>User</strong></td>";
echo "<td><strong>Delete?</strong></td></tr>";
if ((is_array($url_array)) && (count($url_array) > 0)) {
foreach ($url_array as $url) {
if ($color == "#cccccc") {
$color = "#ffffff";
} else {
$color = "#cccccc";
}
// remember to call htmlspecialchars() when we are displaying user data
echo "<tr style='background-color:" . $color . "'><td>" . htmlspecialchars($url) . "</td>
<td><input type=\"checkbox\" name=\"del_me[]\" value=\"" . $url . "\"></td>
</tr>";
}
} else {
echo "<tr><td>No user on record</td></tr>";
}
?>
</table>
</form>
<?php
}
登录成功后显示的页面如图所示
3.退出
你可能已经注意到,上图中的菜单选项有一个 “logout” 的链接。该链接指向 logout.php。该脚本源码如下所示
<?php
// include function files for this application
require_once('bookmark_fns.php');
session_start();
$old_user = $_SESSION['valid_user'];
// store to test if they *were* logged in
unset($_SESSION['valid_user']);
$result_dest = session_destroy();
// start output html
do_html_header('Logging Out');
if (!empty($old_user)) {
if ($result_dest) {
// if they were logged in and are now logged out
echo 'Logged out.<br>';
do_html_url('login.php', 'Login');
} else {
// they were logged in and could not be logged out
echo 'Could not log you out.<br>';
}
} else {
// if they weren't logged in but came to this page somehow
echo 'You were not logged in, and so have not logged out.<br>';
do_html_url('login.php','Login');;
}
do_html_footer();
?>
4.修改密码
如果一个用户点击“Change Password” 菜单选项,系统将打开如下图所示的表单
该表单是由脚本 change_passwd_form.php 生成,源码如下
<?php
require_once('bookmark_fns.php');
session_start();
do_html_header('Change password');
check_valid_user();
display_password_form();
display_user_menu();
do_html_footer();
?>
用户提交改表单将除法 change_passwd.php 脚本,源码如下所示
<?php
require_once('bookmark_fns.php');
session_start();
do_html_header('Change password');
// create short variable names
$old_passwd = $_POST['old_passwd'];
$new_passwd = $_POST['new_passwd'];
$new_passwd2 = $_POST['new_passwd2'];
try {
check_valid_user();
if (!filled_out($_POST)) {
throw new Exception(' You have not filled out form completely. Please try again.');
}
if($new_passwd != $new_passwd2) {
throw new Exception('Passwords entered were not the same. Not changed');
}
if ((strlen($new_passwd) > 16 || (strlen($new_passwd) < 6))) {
throw new Exception(' New password must be between 6 and 16 characters. Try again.');
}
// attempt update
change_password($_SESSION['valid_user'], $old_passwd, $new_passwd);
echo 'Password changed.';
}
catch (Exception $e) {
echo $e->getMessage();
}
display_user_menu();
do_html_footer();
?>
其中 chage_password()函数来自 user_auth_fns.php 函数库,源码如下
function change_password($username, $old_password, $new_password) {
// change password for username/old new_password
// return true of false
// if the old password is right
// change their password to new_password and return true
// else throw an exception
login($username, $old_password);
$coon=db_connect();
$result = $coon->query("UPDATE USER SET PASSWD ='".$new_password."' WHERE username='".$username."'");
if (!$result) {
throw new Exception('Password could not be changed.');
} else {
return true; // changed successfully
}
}
六、实现书签存储和读取
在实现了与用户账户相关的功能后,接下来写如何保存、读取和删除书签
1. 添加书签
用户点击用户菜单的 “Add BM” 链接可以添加书签。该链接进入如下图所示的页面
该页面(add_bm_form.php)的源码如下
<?php
// include function files for this application
require_once('bookmark_fns.php');
session_start();
// start output html
do_html_header('Add Bookmarks');
check_valid_user();
display_add_bm_form();
display_user_menu();
do_html_footer();
?>
该源码中的新函数 display_add_bm_form() 出自output_fns.php函数库,该函数源码如下
function display_add_bm_form()
{
// display the form for people to ener a new user in
?>
<form action="add_bms.php" method="post" name="bm_table">
<div class="formblock">
<h2>New Bookmark</h2>
<p>
<input type="text" name="new_url" id="new_url"
size="40" maxlength="255" value="http://" required /></p>
<button type="submit">Add Bookmark</button>
</div>
</form>
<?php
}
提交表单后,系统将调用 add_bms.php 脚本,该脚本添加新书签到个人页面,该脚本如下所示
<?php
require_once('bookmark_fns.php');
session_start();
// create short variable name
$new_url = $_POST['new_url'];
do_html_header('Adding username');
try {
check_valid_user();
if (!filled_out($_POST)) {
throw new Exception('Form not completely filled out.');
}
// check URL format
if (strstr($new_url, 'http://') === false) {
$new_url = 'http://'.$new_url;
}
// check URL is valid
if (!(@fopen($new_url, 'r'))) {
throw new Exception('Not a valid URL.');
}
// try to add bm
add_bm($new_url);
echo 'Bookmark added.';
// get the bookmarks this user has saved
if ($url_array = get_user_urls($_SESSION['valid_user'])) {
display_user_urls($url_array);
}
}
catch(Exception $e) {
echo $e->getMessage();
}
display_user_menu();
do_html_footer();
?>
该表单中的add_bm() 函数出自 url_fns.php文件,该函数将用户提交的新书签添加到数据库,函数源码如下
function add_bm($new_url)
{
// Add new bookmark to the database
echo "Attempting to add" . htmlspecialchars($new_url) . "<br/>";
$valid_user = $_SESSION['valid_user'];
$conn = db_connect();
// check not a repeat bookmark
$result = $conn->query("SELECT * FROM bookmark WHERE username='$valid_user' AND
bm_URL='" . $new_url . "'");
if ($result && ($result->num_rows > 0)) {
throw new Exception('Bookmark already exists.');
}
// insert the new bookmark
if (!$conn->query("INSERT INTO bookmark VALUES('" . $valid_user . "','" . $new_url . "')")) {
throw new Exception('Bookmark could not be inserted.');
}
return true;
}
2.显示书签
member.php 脚本和 add_bm() 函数使用了函数 get_user_urls() 和 display_user_ruls()。它们分别从数据库中读取用户书签和现实这些书签。
display_user_urls() 函数包含在 output_fns.php库中;get_user_urls() 函数包含在 url_fns.php库中
其中get_user_urls() 函数是从数据库中读取书签,如下代码所示
function get_user_urls($username)
{
// extract from the database all the names this user has stored
$conn = db_connect();
$result = $conn->query("SELECT bm_URL FROM bookmark WHERE username='" . $username . "'");
if (!$result) {
return false;
}
// create an array of the URLs
$url_array = array();
for ($count = 1; $row = $result->fetch_row(); ++$count) {
$url_array[$count] = $row[0];
}
return $url_array;
}
get_user_ursl() 函数返回一个 URL 数组, display_user_urls()函数根据这些 URL 显示相关的数据。
3.删除书签
用户将一些书签标记问删除并点击菜单选项的“Delete BM” 时,就将提交一个包含 URL 的表单。每个复选框由 display_user_urls() 函数生成,如下代码所示
echo "<tr style='background-color:" . $color . "'><td>" . htmlspecialchars($url) . "</td>
<td><input type=\"checkbox\" name=\"del_me[]\" value=\"" . $url . "\"></td>
</tr>";
每次输入框名称是 del_me[]。这就是说,在该表单触发执行的 PHP 的脚本中,我们可以访问名 $del_me 的数组,该数组改行所有要删除的书签。
点击“Delete BM” 就触发了 delete_bms.php脚本,该脚本源码如下
<?php
require_once('bookmark_fns.php');
session_start();
// create short variable names
$del_me = $_POST['del_me'];
$valid_user = $_SESSION['valid_user'];
do_html_header('Deleting bookmarks');
check_valid_user();
if(!filled_out($_POST)) {
echo '<p>You have not chosen any bookmarks to delete.<br>
Please try again.</p>';
display_user_menu();
do_html_footer();
exit;
} else {
if(count($del_me) > 0) {
foreach ($del_me as $url) {
if (delete_bm($valid_user, $url)) {
echo 'Deleted'.htmlspecialchars($url).'.<br>';
} else {
echo 'Could not delete'.htmlspecialchars($url).'.<br>';
}
}
} else {
echo 'No bookmarks selected for deletion';
}
}
// get the bookmarks this user has saved
if ($url_array = get_user_urls($valid_user)) {
display_user_urls($url_array);
}
display_user_menu();
do_html_footer();
?>
在脚本的开始,我们执行了常规验证操作。当确定用户已经删除选中的书签时,将通过循环删除书签,如下所示:
foreach ($del_me as $url) {
if (delete_bm($valid_user, $url)) {
echo 'Deleted'.htmlspecialchars($url).'.<br>';
} else {
echo 'Could not delete'.htmlspecialchars($url).'.<br>';
}
}
可以看到, delete_bm() 函数真正执行力从数据库删除书签的操作。该函数保存在url_fns.php文件中,源码如下所示
function delete_bm($user, $url) {
// delete one URL from the database
$conn = db_connect();
// delete the bookmark
if (!$conn->query("DELETE FROM bookmark WHERE username='".$user."' AND bm_url='".$url."'")) {
throw new Exception('Bookmark could not be deleted');
}
return true;
}
在系统中运行删除脚本输出结果如下图所示
与在 add_bms.php 文件中的脚本操作类似,当数据库被修改之后,我们将调用 get_user_urls() 函数和 display_user_urls() 函数来显示新书签
七、实现书签推荐
最后,我们将讨论书签推荐脚本 recommend.php。实现书签推荐有多中方法。在此我应用“相似意向”的推荐,既查找与给定用户至少有一个相同书签的其他用户,其他用户的书签也可能对特性用户有吸引力。
将“相似意向”应用到 SQL 查询最简单的办法就是使用子查询。第一个子查询如下所示:
SELECT DISTINCT(b2.username)
FROM bookmark b1, bookmark b2 WHERE b1.username
='" . $valid_user . "'
AND b1.username != b2.username
AND b1.bm_URL = b2.bm_URL
以上查询使用别名将数据库表 bookmark 进行自身链接,这是一个奇怪但是有时候有用的概念。假设我有两个书签表,b1和b2。b1查询当前用户及其书签。b2查询所有其他用户标签。我们需要查找的是用户书签中有一个 URL 与当前用户相同(b1.bm_URL = b2.bm_URL) 的其他用户 (b2.username),并且这个其他用户不是当前用户 (b1.username != b2.username)。
该查询将给出一个与当前用户意向相似的用户列表。得到这个用户列表后,就可以用下面的查询语句搜索他们的其他书签了:
SELECT bm_URL FROM bookmark WHERE username in (SELECT DISTINCT(b2.username)
FROM bookmark b1, bookmark b2 WHERE b1.username
='" . $valid_user . "'
AND b1.username != b2.username
AND b1.bm_URL = b2.bm_URL)
可以添加第二个子查询来过滤当前用户的书签;如果用户已经有了这些书签,就不必再将该书签推荐给他。最后,对$popularity 变量进行书签过滤。我们不大希望推荐太个性化的 URL ,因此只将一定数量的其他用户书签 URL 推荐给用户。
实现书签推荐的完整脚本如下
recommend.php(向用户推荐可能喜欢的书签)
<?php
require_once('bookmark_fns.php');
session_start();
do_html_header('Recommending URL');
try {
check_valid_user();
$urls = recommend_urls($_SESSION['valid_user']);
display_recommended_urls($urls);
} catch (Exception $e) {
echo $e -> getMessage();
}
display_user_menu();
do_html_footer();
?>
url_fns.php文件的recommend_url()函数
function recommend_urls($valid_user, $popularity = 1)
{
// We will provide semi intelligent recommendations to people
// If they have an URL in common with other users, they may like
// other URLs that these people like
$conn = db_connect();
// find other matching users
// with an url the same as you
// as a simple way of excluding people's private pages, and
// increasing the chance of recommending appealing URLs, we
// specify a minimum popularity level
// if $popularity = 1, then more than one person must have
// an URL before we will recommend it
$query = "SELECT bm_URL FROM bookmark WHERE username in (SELECT DISTINCT(b2.username)
FROM bookmark b1, bookmark b2 WHERE b1.username
='" . $valid_user . "'
AND b1.username != b2.username
AND b1.bm_URL = b2.bm_URL)
AND bm_URL NOT IN
(SELECT bm_URL FROM bookmark
WHERE username='" . $valid_user . "')
GROUP BY bm_URL
HAVING COUNT(bm_url)>".$popularity;
if (!($result = $conn->query($query))) {
throw new Exception('Could not find any bookmarks to remmend');
}
if ($result->num_rows==0) {
throw new Exception('Could not find any bookmarks to recommend.');
}
$urls = array();
// bulid an array of the relevant urls
for ($count = 0; $row=$result->fetch_object(); $count++) {
$urls[$count] = $row->bm_URL;
}
return $urls;
}
如果说数据库中没有两位及以上的用户收藏某网址为书签则recommend.php输入示例如图所示
以上就是本项目的基础功能,其中还有保存在output_fns.php库中的菜单栏(display_user_menu()) 函数,源码如下
function display_user_menu()
{
// display the menu options on this page
?>
<hr>
<a href="member.php">Home</a> |
<a href="add_bm_form.php">Add BM</a> |
<?php
// only offer the delete option if user table is on this page
global $bm_table;
if ($bm_table === true) {
echo "<a href=\"#\" onClick =\"bm_table.submit();\">Delete BM</a> | ";
} else {
echo "<span style=\"color: #cccccc\">Delete BM</span> | ";
}
?>
<a href="change_passwd_form.php">Change password</a><br/>
<a href="recommend.php">Recommend URLs to me</a> |
<a href="logout.php">Logout</a>
<?php
}
最后谢谢,你的收看,这是我写的第一个PHP+MySQL项目,第一次发博客,有许多不足的地方,请多多指点,谢谢观看。