1. 作业完成思路
- 下载安装验证码的composer组件
- 编写自动加载文件
- 编写自定义异常处理类
- 编写登录页面
- 编写数据验证页面
2. 数据验证逻辑
- 验证页面请求来源
- 验证数据传递方法
- 验证请求内容是否存在
- 验证输入验证码和系统生成的验证码是否匹配
- 验证数据库中是否存在登录邮箱
- 验证登录密码与数据库中的密码是否相同
3.demo
3.1 自动加载文件
namespace php\demo;
//空行
spl_autoload_register(function ($class){
// 设置项目前缀
$prefix = 'php\demo\\';
//空行
// 设置具有项目前缀的类名所对应的类的基目录
$base_dir = __DIR__ . '\src\\';
//空行
// 去掉项目前缀,获取真实的类名称
$real_class = substr($class, strlen($prefix));
// die($real_class);
//空行
// 将命名空间分隔符,替换成目录分隔符
$path = str_replace('\\', DIRECTORY_SEPARATOR, $real_class);
//空行
// 加上基目录和php的后缀'.php'
$file = $base_dir . $path . '.php';
//空行
file_exists($file) ? require $file : die('文件不存在,请重新加载');
});
3.2 自定义异常处理类
//在项目下设置src文件夹,将此类和数据库操作类放入,统一自动加载
//定义命名空间
namespace php\demo\lib;
//导入系统异常处理类
use Exception;
//从系统异常处理类派生一个自定义的子类
class MyException extends Exception{
//使用魔术方法,使得该类的实例作为字符串时输出
public function __toString()
{
//HERDOC的后面不能有空格
return <<< HERDOC
<table border="1" cellspacing="0" cellpadding="5">
<tr bgcolor="#7fffd4">
<th>错误代码</th>
<th>错误内容</th>
<th>错误文件名</th>
<th>错误代码行</th>
</tr>
<tr>
<td>{$this->getCode()}</td>
<td>{$this->getMessage()}</td>
<td>{$this->getFile()}</td>
<td>{$this->getLine()}</td>
</tr>
</table>
HERDOC;
}
}
3.3 登录页面
//定义命名空间
namespace php\demo\part2;
//引入composer中的自动加载
require '../../../../vendor/autoload.php';
//导入验证码组件
use Gregwar\Captcha\CaptchaBuilder;
//实例验证码对象
$builder = new CaptchaBuilder;
//生成一个验证码图片
$builder->build();
//开启会话,使用seeson传递验证码信息
session_start();
$_SESSION['phrase'] = $builder->getPhrase();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录页面</title>
<style>
<!-- 省略样式表 -->
</style>
</head>
<body>
<div>
<h2>用户登录</h2>
<form action="demo1.php" method="post">
<span>
<label for="email">邮箱:</label>
<input type="email" name="email" id="email" placeholder="邮箱地址">
</span>
<span>
<label for="password">密码:</label>
<input type="password" name="password" id="password">
</span>
<span>
<label for="validate">验证码:</label>
<input type="text" name="validate" id="validate" class="validate">
<img src="<?php echo $builder->inline(); ?>" alt="" onclick="location.reload()">
</span>
<button>登录</button>
</form>
</div>
</body>
</html>
3.4 数据验证页面
//定义命名空间
namespace php\demo\part2;
//
//引入自定义的自动加载文件
require 'autoload.php';
//
//导入数据库操作和异常处理类
use php\demo\iDbPaparms;
use php\demo\lib\Db;
use php\demo\lib\Db_PDO;
use php\demo\lib\MyException;
//
//开启会话
session_start();
//判断请求来源是否合法
//获取请求页面的来源,利用过滤器进行过滤,利用basename获取页面文件名
$currentName = basename(filter_input(INPUT_SERVER,'HTTP_REFERER',FILTER_VALIDATE_URL));
//设立一个白名单,在数组里的为合法的
$allowName = ['demo2.php'];
//判断请求页面是否在白名单中,如果不在抛出异常
try{
if(!in_array($currentName,$allowName)) throw (new MyException('请求来源非法',101));
} catch (MyException $e){
print $e;
}
//
//判断数据是否是通过post方式传送的
try {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') throw new MyException('传送方式非法',102);
} catch (MyException $e) {
print $e;
}
//
//检查登录邮箱、登录密码、用户输入的验证码、系统生成的验证码是否为空
try {
if (empty($_POST['email'])) throw new MyException('登录邮箱为空',103);
} catch (MyException $e) {
print $e;
}
try {
if (empty($_POST['password'])) throw new MyException('密码为空',104);
} catch (MyException $e) {
print $e;
}
try {
if (empty($_POST['validate'])) throw new MyException('验证码为空',105);
} catch (MyException $e) {
print $e;
}
try {
if (empty($_SESSION['phrase'])) throw new MyException('验证码生成错误',106);
} catch (MyException $e) {
print $e;
}
//
//过滤外部数据
//先将外部变量放入一个数组中,键名为变量名,值为过滤器
$arr = [
'validate'=>FILTER_SANITIZE_STRING,
'email'=>FILTER_VALIDATE_EMAIL,
'password'=>FILTER_SANITIZE_STRING,
];
//利用filter_input_array过滤函数过滤,该函数第一个参数为变量类型(INPUT_POST/INPUT_SERVER/INPUT_GET...),第二个参数为需要过滤的数组,返回值为过滤后的数组
$arr = filter_input_array(INPUT_POST,$arr);
//
//验证码对比
try {
//将session中保存的系统生成的验证码赋给变量phrase
$phrase = $_SESSION['phrase'];
//将输入的验证码和生成的验证码全部转为小写
$validate = strtolower($arr['validate']);
$phrase = strtolower($phrase);
//对比验证码,如果不对抛出异常
if ($validate !== $phrase) throw new MyException('验证码不符',201);
} catch (MyException $e) {
print $e;
}
//
//2.和数据库中数据进行比对
//准备数据库连接参数
$dsn = iDbPaparms::TYPE . ':host=' . iDbPaparms::HOST . ';dbname=' . iDbPaparms::DBNAME;
//生成利用PDO操作数据库操作的一个对象(这个类是基于自定义接口实现的一个类)
$links = new Db_PDO($dsn, iDbPaparms::USER_NAME, iDbPaparms::PASSWORD);
//利用数据库通用操作类获取结果集(这个类是基于自定义接口实现的一个类),
$result = Db::select($links, "`email`='{$arr['email']}'");
//查询出所有与登录email相同的数据,理论上要么不存在,如果存在应该只有一条
try {
if (empty($result)) throw new MyException('用户不存在',202);
} catch (MyException $e) {
print $e;
}
//如果用户存在,对比登录密码和数据库中的密码
try {
if (sha1($arr['password']) !== $result['password']) throw new MyException('密码错误',203);
} catch (MyException $e) {
print $e;
}
//如果整个过程没有抛出异常,说明登录成功,输出成功信息
if(empty($e)){
// echo '<pre>'.print_r($result,true).'</pre>';
echo $result['name'].',登录成功';
}
4. 运行效果图
4.1 登录页面
4.2 登录成功页面
4.3 登录出现异常页面
5.小结
5.1 自动加载的原理
- 按照PSR-4的规范,设定命名空间、文件名和文件存放路劲
- 自定义一个自动加载方法,也就是将命名空间和文件存放路径之间形成一个映射关系
- 利用spl_autoload_register()注册自定义的自动加载的方法
- 在需要的文件中引入自动加载页面
- 文件执行过程中遇到了没有定义的类,会触发自动加载方法
- 自动加载方法会按照类名在之前映射的文件中查找,因为文件名和类名是一致的,所以就是查找文件名,找到之后进行引入
5.2 composer使用
- 下载安装好composer之后,在项目中会有一个vendor的文件夹,里面存放着我们下载的composer组件
- 在vendor文件夹中有一个autoload.php的文件,这个是用来自动加载composer组件的文件,我们需要在使用的文件中引入
- 引入composer组件的文件之后,需要使用use将我们用到的类进行导入
5.3 异常处理的实现
- 新建一个继承与系统异常处理类的子类
- 在类中可以设定对异常发生时提示信息的表达方式
- 在程序中利用try和catch的组合进行检查,try字面理解就是试试接下来的代码,如果没问题就跳过catch中的代码,如果有问题就实例一个自定义异常对象,并给其赋值
- 在try中出现异常后,程序进入到catch里面的代码块,输出异常内容(自定义)、异常代码(自定义)、异常文件、异常处的代码行号等。