HTTP 是基于无连接的网络协议, 每一次访问, 对于服务器来说, 都是全新的
有两个地方可以实现多次请求之间共享数据:
保存到浏览器中的叫: cookie
;
保存到服务器中的叫: session
;
COOKIE操作
创建COOKIE: 使用setcookie()函数.
使用语法:
bool setcookie (
string $名字
[, string $值]
[, int $过期时间 = 0]
[, string $路径]
[, string $域名]
[, bool $安全 = false]
[, bool $http只读 = false]
);
参数含义:
举例:
setcookie('loginuser', 'zhangsan', time() + 60 * 60 * 24, '/');
setcookie('manager', 'admin', time() + 60 * 20, '/admin');
获取COOKIE的值: 通过操作超全局数组$_COOKIE
实现. 假设COOKIE键名为$cookieName
, 则获取其值: $_COOKIE[$cookieName]
.
修改COOKIE的值:
方法1: 跟取值类似. 假设COOKIE键名为$cookieName
, 则修改其值: $_COOKIE[$cookieName] = 新值
.
方法2: 相当于使用setcookie()
函数再”覆盖创建”一次同键名的COOKIE.
删除COOKIE: 就是用setcookie()
给被销毁的cookie设置一个比当前时间早的时间,如:time() - 1
;
一个特殊的COOKIE: PHPSESSID
SESSION操作
在PHP中session的操作就是操作$_SESSION
超全局数组。
开始操作前要启用session:session_start().
将数据保存到SESSION中: $_SESSION['key1'] = "value1"
.
删除单个session数据:用unset
。删除全部session数据:session_destroy()
.
DROP DATABASE IF EXISTS `phpedu`;
CREATE DATABASE `phpedu` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `phpedu`;
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(50) NOT NULL,
`realname` varchar(50) NOT NULL,
`sex` int(1) NOT NULL DEFAULT '0',
`in_use` int(1) NOT NULL DEFAULT '1',
`create_time` int(11) NOT NULL,
`update_time` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
页面文件(php/html)
首页(index.php)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录小实战</title>
<style>
@import 'css/global.css';
@import 'css/index.css';
</style>
</head>
<?php
require('UserStorage.php');
// $userStorage = new UserStorage(1);
$userStorage = new UserStorage(2);
?>
<body>
<!-- <header> -->
<nav class="top-bar">
<span>LOGO</span>
<?php if ($userStorage->isLogin()) : ?>
<div class="user-info">
<span>你好, <?php echo $userStorage->getUserInfo()['realname'] ?? '没登录' ?></span>
<a href="doBusiness.php?action=logout">注销</a>
</div>
<?php else : ?>
<div class="user-opra">
<a href="login.html">登录</a>
<a href="register.html">注册</a>
</div>
<?php endif; ?>
</nav>
<!-- </header> -->
</body>
</html>
注册页(register.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户注册</title>
<style>
@import 'css/global.css';
@import 'css/register.css';
</style>
</head>
<body>
<form action="doBusiness.php?action=register" method="post">
<section class="register-box">
<div class="register-title">
<span>注册新用户</span>
</div>
<div class="register-content">
<div class="register-item">
<label for="username">用户名:</label>
<input type="text" name="username" id="username" autofocus required>
</div>
<div class="register-item">
<label for="password">密码:</label>
<input type="password" name="password" id="password" required>
</div>
<div class="register-item">
<label for="repassword">确认密码:</label>
<input type="password" name="repassword" id="repassword" required>
</div>
<div class="register-item">
<label for="realname">姓名</label>
<input type="text" name="realname" id="realname" required>
</div>
<div class="register-item">
<label for="sex_2">性别</label>
<div class="radio-item"><input type="radio" name="sex" id="sex_0" value="0"><label
for="sex_0">男</label>
</div>
<div class="radio-item"><input type="radio" name="sex" id="sex_1" value="1"><label
for="sex_1">女</label>
</div>
<div class="radio-item"><input type="radio" name="sex" id="sex_2" value="2" checked><label
for="sex_2">保密</label></div>
</div>
<div class="register-item">
<button type="submit">提交</button>
</div>
</div>
<div class="goto-login">
<a href="login.html">已有账号, 去登录 >></a>
</div>
</section>
</form>
</body>
</html>
登录页(login.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>用户登陆</title>
<style>
@import "css/global.css";
@import "css/login.css";
</style>
</head>
<body>
<form action="doBusiness.php?action=login" method="post">
<section class="login-box">
<div class="login-title">
<span>用户登录</span>
</div>
<div class="login-content">
<div class="login-item">
<label for="username">用户名:</label>
<input type="text" name="username" id="username" required autofocus />
</div>
<div class="login-item">
<label for="password">密码:</label>
<input type="password" name="password" id="password" />
</div>
<div class="login-item">
<button type="submit">登陆</button>
</div>
</div>
<div class="goto-register">
<a href="register.html">还没有账号?赶紧去注册一个吧 >></a>
</div>
</section>
</form>
</body>
</html>
css文件
global.css
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html {
font-family: Helvetica, Geneva, Verdana, Arial;
}
body {
width: 100vw;
height: 100vh;
}
index.css
.top-bar {
height: 60px;
width: 100vw;
background-color: #ccc;
padding: 0 20px;
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
}
.top-bar>:first-child {
font-size: 30px;
}
.top-bar>:last-child>a {
color: #333;
text-decoration: none;
margin-left: 20px;
}
.top-bar>:last-child>a:hover {
color: red;
}
register.css
body {
display: table-cell;
vertical-align: middle;
background: #fafafa;
}
.register-box {
border: 1px solid #ddd;
width: 340px;
height: 340px;
margin: 0 auto;
background: white;
padding: 10px;
}
.register-box>.register-title {
height: 40px;
line-height: 30px;
padding: 0 20px;
font-size: 20px;
border-bottom: 1px solid #ddd;
color: #999;
}
.register-box>.register-content {
display: flex;
flex-flow: column nowrap;
justify-content: start;
align-items: start;
padding: 10px;
}
.register-box>.register-content>.register-item {
height: 40px;
line-height: 40px;
}
.register-box>.register-content>.register-item>label:first-of-type {
display: inline-block;
width: 80px;
color: #999;
text-align: justify;
}
.register-box>.register-content>.register-item input {
border: 1px solid #ddd;
height: 32px;
border-radius: 5px;
width: 200px;
}
.register-box>.register-content>.register-item:nth-last-child(2) {
display: flex;
flex-flow: row wrap;
justify-content: left;
align-content: center;
}
.register-box>.register-content>.register-item:nth-last-child(2) input {
width: 30px;
vertical-align: middle;
color: #999;
height: unset;
}
.register-box>.register-content>.register-item:nth-last-child(2) input:hover {
cursor: pointer;
}
.register-box>.register-content>.register-item:nth-last-child(2) label {
color: #999;
}
.register-box>.register-content>.register-item:nth-last-child(2) label:hover {
cursor: pointer;
}
button {
border: none;
height: 30px;
width: 280px;
}
button:hover {
background-color: #ccc;
cursor: pointer;
color: orangered;
}
.goto-login {
margin-top: 40px;
text-align: center;
}
.goto-login>a {
color: orangered;
text-decoration: none;
}
.goto-login>a:hover {
cursor: pointer;
color: darkgreen;
}
- login.css
body {
display: table-cell;
vertical-align: middle;
background-color: #fafafa;
}
.login-box {
width: 340px;
height: 220px;
background-color: #fff;
margin: 0 auto;
padding: 10px;
}
.login-box > .login-title {
height: 40px;
line-height: 30px;
font-size: 1.5rem;
text-align: center;
color: #aaa;
border-bottom: 1px solid #ddd;
}
.login-box > .login-content {
display: flex;
flex-flow: column nowrap;
padding: 10px;
justify-content: start;
align-items: flex-start;
color: #aaa;
}
.login-box > .login-content > .login-item {
height: 40px;
line-height: 40px;
vertical-align: middle;
width: 100%;
}
.login-box > .login-content > .login-item > label {
display: inline-block;
width: 80px;
text-align-last: justify;
}
.login-box > .login-content > .login-item > input {
height: 32px;
border: 1px solid #aaa;
border-radius: 5px;
width: 200px;
}
.login-box > .login-content > .login-item > button {
border: none;
height: 30px;
line-height: 30px;
width: 100%;
margin-top: 30px;
color: #333;
}
.login-box > .login-content > .login-item > button:hover {
background-color: #aaa;
cursor: pointer;
color: orangered;
}
.goto-register {
margin-top: 50px;
text-align: center;
}
.goto-register>a {
color: orangered;
text-decoration: none;
}
.goto-register>a:hover {
cursor: pointer;
color: darkgreen;
}
PHP脚本
<?php
require('../../out.php');
require('UserStorage.php');
require('UserLogin.php');
// $userStorage = new UserStorage(1);
$userStorage = new UserStorage(2);
try {
$pdo = new PDO('mysql:host=localhost;dbname=phpedu;charset=utf8;port=3306', 'root', 'root');
} catch (Exception $ex) {
echobr('系统内部错误');
die;
}
// 处理用户表相关操作
$action = $_GET['action'];
$userLogin = new UserLogin($pdo, $userStorage);
switch ($action) {
case 'register':
$userLogin->doRegister();
break;
case 'login':
$userLogin->doLogin();
break;
case 'logout':
$userLogin->doLogout();
break;
default:
echo "<script>alert('无法处理的请求');window.location = '/0508/index.php';</script>";
}
- 会话管理类UserStorage.php
<?php
class UserStorage
{
/* 1=cookie; 2=session */
public $storageType = 1;
public function __construct(int $storageType)
{
try {
if ($storageType !== 1 && $storageType !== 2) {
throw new Exception("无效的存储类型");
}
$this->storageType = $storageType;
} catch (Exception $e) {
echobr($e->getMessage());
die;
}
}
public function getUserInfo()
{
if (!$this->isLogin()) {
return [];
}
if ($this->storageType === 1) {
return unserialize($_COOKIE['loginUser']);
}
session_start();
return unserialize($_SESSION['loginUser']);
}
public function rmUserInfo()
{
if ($this->storageType === 1) {
setcookie('loginUser', null, time() - 3600 * 24, '/');
} else {
session_start();
unset($_SESSION['loginUser']);
}
}
public function saveUserInfo($userStr)
{
if ($this->storageType === 1) {
$this->saveUserInfo2Cookie($userStr);
} else {
$this->saveUserInfo2Session($userStr);
}
}
public function saveUserInfo2Cookie($userStr)
{
setcookie('loginUser', $userStr, time() + 3600 * 24, '/');
}
public function saveUserInfo2Session($userStr)
{
session_start();
$_SESSION['loginUser'] = $userStr;
}
public function isLogin()
{
if ($this->storageType === 1) {
return $this->isLoginInCookie();
}
return $this->isLoginInSession();
}
public function isLoginInCookie()
{
if (isset($_COOKIE['loginUser']) && !empty($_COOKIE['loginUser'])) {
return true;
}
return false;
}
public function isLoginInSession()
{
session_start();
if (isset($_SESSION['loginUser']) && !empty($_SESSION['loginUser'])) {
return true;
}
return false;
}
}
- 登录相关操作类:UserLogin.php
<?php
require_once('UserStorage.php');
class UserLogin
{
private $pdo;
private $userStorage;
public function __construct(PDO $pdo, UserStorage $userStorage)
{
$this->pdo = $pdo;
$this->userStorage = $userStorage;
}
public function doRegister()
{
/* 解构post请求中的变量 */
extract($_POST);
// 数据验证
if (strlen($username) < 3 || strlen($username) > 20) {
echobr("<script>alert('用户名长度需在6-20位之间');window.history.go(-1);</script>");
exit;
}
if (strlen($password) < 6 || strlen($password) > 15) {
echobr("<script>alert('密码长度需在6-15位之间');window.history.go(-1);</script>");
exit;
}
if ($password !== $repassword) {
echobr("<script>alert('两次输入的密码不一致');window.history.go(-1);</script>");
exit;
}
// 用户名验证
$sql = 'SELECT * FROM `users` WHERE `username` = :username';
$stmt = $this->pdo->prepare($sql);
$stmt->execute(['username' => $username]);
if ($stmt->rowCount() > 0) {
echobr("<script>alert('用户名已被使用');window.history.go(-1);</script>");
exit;
}
// 保存新用户
$sql = 'INSERT `users` SET `username` = :username, `password` = :pswd, `realname` = :realname, `sex` = :sex, `update_time` = :update_time, `create_time` = :create_time';
$stmt = $this->pdo->prepare($sql);
$stmt->execute(['username' => $username, 'pswd' => md5($password), 'realname' => $realname, 'sex' => $sex, 'update_time' => time(), 'create_time' => time()]);
dumpbr($stmt->debugDumpParams());
if ($stmt->rowCount() === 1) {
echobr("<script>alert('注册成功');window.location='/0508/login/index.php'</script>");
} else {
echobr($stmt->errorInfo());
// echobr("<script>alert('注册失败, 请重试');window.history.go(-1);</script>");
echobr("<script>alert('注册失败, 请重试');</script>");
}
}
public function doLogin()
{
// 解构参数
extract($_POST);
// 判断是不是已登陆
if ($this->userStorage->isLogin()) {
echobr("<script>alert('请不要重复登陆');window.location='/0508/login/index.php'</script>");
exit;
}
// 查询用户信息
$sql = "SELECT * FROM `users` WHERE `username` = :username";
$stmt = $this->pdo->prepare($sql);
$stmt->execute(['username' => $username]);
if ($stmt->rowCount() < 1) {
echobr("<script>alert('用户名不存在');window.history.go(-1);</script>");
exit;
}
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (md5($password) !== $user['password']) {
echobr("<script>alert('密码不正确');window.history.go(-1);</script>");
exit;
}
// 去掉密码
unset($user['password']);
$userStr = serialize($user);
// 存到cookie或session中
$this->userStorage->saveUserInfo($userStr);
// 跳转回首页
echobr("<script>alert('登陆成功');window.location='/0508/login/index.php'</script>");
}
public function doLogout()
{
$this->userStorage->rmUserInfo();
echobr("<script>alert('注销成功');window.location='/0508/login/index.php';</script>");
}
}
运行效果:
cookie和session个人把它们看成是在不同的请求之间共享数据的两个池子, 一个放在客户端(浏览器), 一个放在服务器端. 可以把一些不太敏感的数据放到cookie中, 可以减少服务器的性能消耗. 而一些比较敏感的数据, 类似用户积分等, 放在session中似乎更安全一些.
登录小实战, 似乎是自己抽象能力不够强, 都是把所有的业务逻辑”按流程”写完后, 才封装整理成相应的操作类. 而数据库操作类, 感觉不封装反而使用起来更灵活一些. 属性过滤器的用法较简单, 但是因为过滤器的种类太多, 相关参数也很杂, 对于初学者, 使用它还没有使用if...else...
判断来得快