编译环境
php 7.3.29,windows可能需要找个教程安装一下,
macOs系统自带apache,只需要把 /etc/apache2 路径下的 httpd.conf 关于php的配置的注释取消掉即可。
创建工程命令:
composer create-project topthink/think 项目名称 --prefer-dist
项目目录结构
我使用的编辑工具是phpStorm。
phpStorm的环境配置。
开发
前后端分离项目,首先把跨越问题解决了
public/index.php : 程序的入口文件
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS'){
//允许的源域名
header("Access-Control-Allow-Origin: *");
//允许的请求头信息
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization");
//允许的请求类型
header('Access-Control-Allow-Methods: GET, POST, PUT,DELETE,OPTIONS,PATCH');
exit;
}
数据库配置
application文件夹下,有一个database.php文件。在里面配置即可。
接下来是建立对象关系映射。
application创建一个模块。有创建模块的命令。
模块名称可以随便起一个合法的名字即可。
在这里我创建了一个index模块,该模块下建立了三个包:controller,model,utils。
model
model包即是用来建立对象关系映射的。
model/Users
<?php
namespace app\index\model;
use think\model;
class Users extends model
{
// 只需要把表的名字声明好即可。
protected $table = 'users';
}
controller
controller是用来实现业务逻辑的。
这里我实现了,注册,注销账户,修改密码,登录,获取所有用户的联系信息,修改某用户的联系信息。
其中,注销账户,修改密码,修改某用户的联系信息是做了权限校验的,确保是本账号才可以操作。
controller/User
<?php
namespace app\index\controller;
use app\index\utils\Token;
use think\Db;
use think\Request;
use think\Exception;
use app\index\model\Users;
class User
{
/**
* 注册
* 校验函数还可以进行拆分
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function register(){
// 获取 请求对象
$request = Request::instance();
// 从请求对象中 获取 phone,email, pwd 参数
$phone = $request->param("phone");
$email = $request->param("email");
$pwd = $request->param("pwd");
// 校验 phone, email, pwd 格式是否正确
if (!preg_match(PhoneRule, $phone)) {
return json(["msg"=>"error! phone 格式不正确"]);
} elseif (!preg_match(EmailRule, $email)){
return json(["msg"=>"error! email 格式不正确"]);
} elseif (!preg_match(PwdRule, $pwd)){
return json(["msg"=>"error! 密码至少包含数字和英文,长度6-20!"]);
}
// 查询 phone 或者 email 是否被注册过
if(Users::where('phone', $phone)->find()){
$user = Users::where('phone', $phone)->find();
return json(["msg"=>"error! phone已被注册!"]);
}elseif (Users::where('email', $email)->find()){
return json(["msg"=>"error! email已被注册!"]);
}
// 经过上述的校验都没问题,校验函数返回一个 Users对象
try {
// 插入数据
$newUser = new Users();
$newUser->phone = $phone;
$newUser->email = $email;
// 对密码加密 ===========
$newUser->pwd = md5($pwd);
//========
// 提交数据
$newUser->save();
// 注册:发不发放token?
$token = 0;
return json(["msg"=>"success! 注册成功!", "token"=>$token]);
}catch (Exception $ex){
return json(["error"=>$ex, "msg"=>"error! 注册失败!"]);
}
}
/**
* 登录
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function login(){
$request = Request::instance();
// 手机号码 或者 邮箱登陆
// 获取 登录的方式 type
$type = $request->param("type");
if ($type == "0"){
// type = 0, 手机号登录
// 查询phone
$phone = $request->param("phone");
$user = Users::where('phone', $phone)->find();
if(!$user){
return json(["msg"=>"error! 用户不存在!"]);
}
}elseif ($type==="1"){
// type = 1, 邮箱登录
// 查询email
$email = $request->param("email");
$user = Users::where('email', $email)->find();
if(!$user){
return json(["msg"=>"error! 用户不存在!"]);
}
}else{
// type 不对,直接返回提示
return json(["msg"=>"error! 请选择登录方式!"]);
}
// 获取到用户的密码,直接加密
$pwd = md5($request->param("pwd"));
// 密码 密文比对
// 根据手机号 查找 密码字段 的内容
// 然后用户提交加密后的密码进行比对
if ($user->pwd != $pwd){
return json(["msg"=>"error! 密码错误!"]);
}
// 发放token
$token = Token::getToken($user);
return json(["msg"=>"success! 登录成功!", "token"=>$token]);
}
/**
* 修改密码
* 需要加token权限
* 确保是在登录状态才能进行此操作
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function updatePwd(){
$request = Request::instance();
$checkRes = Token::checkToken($request);
if($checkRes!==true){
return json(["msg"=>$checkRes]);
}
{
// 获取当前用户的 uid 或者 phone 或者 email
// 根据这些信息查询数据库,获取数据库对象
// 这里假设根据 phone 来获进行查找
$phone = $request->param("phone");
$user = Users::where("phone",$phone)->find();
$pwd = $request->param("pwd");
// 修改的密码也需要满足注册用户时的密码规则
if (!preg_match(PwdRule, $pwd)){
return json(["msg"=>"error! 密码至少包含数字和英文,长度6-20!"]);
}
$user->pwd = md5($pwd);
// 保存提交
$user->save();
return json(["msg"=>"修改密码成功!"]);
}
}
/**
* 销毁账号
* 需要加token权限
* 确保是在登录状态才能进行此操作
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function destroyUser(){
$request = Request::instance();
// 获取当前用户的 uid 或者 phone 或者 email
// 根据这些信息查询数据库,获取数据库对象
// 这里假设根据 phone 来获进行查找
$checkRes = Token::checkToken($request);
if ($checkRes!==true){
return json(["msg"=>$checkRes]);
}
$phone = $request->param("phone");
$user = Users::where("phone",$phone)->find();
// 删除提交
$user->delete();
return json(["msg"=>"success! 销毁成功!"]);
}
/**
* 获取所有用户的联系信息
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function getUsersContactInfo(){
// select() 是选择全部, find() 是选择一条
$result=Db::name('users')
->field('uid,phone,email')
->select();
return json($result);
}
/**
* 修改某用户的联系信息
* @return \think\response\Json
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @throws \think\exception\DbException
*/
public function updateUsersContactInfo(){
// 获取 请求对象
$request = Request::instance();
$checkRes = Token::checkToken($request);
if ($checkRes!==true){
return json(["msg"=>$checkRes]);
}
// 从请求对象中 获取 phone,email, pwd 参数
$phone = $request->param("phone");
$email = $request->param("email");
$uid = $request->param("uid");
// 根据uid获取用户对象
$user = Users::where('uid', $uid)->find();
// 检查phone是否为空,为空则说明用户不需要修改phone
if($phone != ""){
// 检查 phone 的格式
if (!preg_match(PhoneRule, $phone)) {
return json(["msg"=>"error! phone 格式不正确"]);
}
// 修改用户的 phone
$user->phone = $phone;
}
if($email != ""){
// 检查 email 的格式
if (!preg_match(EmailRule, $email)) {
return json(["msg"=>"error! email 格式不正确"]);
}
// 修改用户的 email
$user->email = $email;
}
// 保存提交
$user->save();
return json(["msg"=>"success! 修改成功!"]);
}
public function controllerTest(){
test("llll");
}
}
Token发放和校验
我把Token发放和校验放在utils文件夹下,把它看作是一个工具,封装在Token类里面,但是我只需要里面的处理函数,不需要创建一个Tokne对象,把发放token和校验token用的是静态方法。直接使用类名调用即可。
// 写这个类的时候卡壳了,思路没问题,但是结果和我的预期不一样,这种是最难受的
// 为什么卡壳?
// 对token校验的时候,token的值是用户传过来的,没法预判,所以把用户传过来的token直接放进JWT::decode 里进行校验会出错,所以要做一个异常处理,但是这个异常是 \Exception 类型,我最初写的是think\Exception,一直捕获不到,这个bug找了挺久的。所以,异常的最顶层应该是\Exception吧。
<?php
namespace app\index\utils;
use app\index\model\Users;
use Firebase\JWT\JWT;
class Token
{
// 生成token的方式不公开
private static function generateToken($data){
$key = 'key'; //秘钥:自定义
$payload = array(
'iss' => 'kary', //签发人(官方字段:非必需)
'aud' => 'public', //受众(官方字段:非必需)
'iat' => time(), //签发时间
'nbf' => time(), //生效时间,立即生效
'exp' => time() + 60*60*24*7, //过期时间,一周
'uid' => $data->uid, //自定义字段
);
//加密生成token
return JWT::encode($payload, $key);
}
// 只公开获取token的方式
public static function getToken($data){
return self::generateToken($data);
}
public static function checkToken($request){
$authorization = $request->header("authorization");
// 获取token
{
// 异常捕获无效
try {
$token = substr($authorization,8,-1);
}catch (\Exception $ex){
$token = $authorization;
}
}
try {
// 1.如果当前时间大于 exp,或者小于nbf,token无效,进行拦截
$key = 'key';
JWT::$leeway = 60;//当前时间减去60,把时间留点余地
$decode = JWT::decode($token, $key, array('HS256'));
// 查数据库,用户不存在
if(Users::where('uid', $decode->uid)->find()){
// 比较当前时间大于 exp,或者小于nbf,token无效,进行拦截
if($decode->nbf > time()){
return "权限伪造!";
}elseif ($decode->exp < time()){
return "权限过期,请重新登录!";
}
}else{
return "账户不存在!";
}
}catch (\Exception $ex){
// token 无效
return "权限不足!";
}
return true;
}
}
其中在用户注册和登录的时候,需要对用户提交的手机号码,电子邮箱,密码的格式进行校验,需要用到正则表达式进行判别,正则表达是太长了,为了使代码尽可能简洁,把正则表达式放到常量文件里面定义,用的时候直接使用表达式的名字即可。响应体也可以封装在这里。
application/common.php
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: 流年 <[email protected]>
// +----------------------------------------------------------------------
// 应用公共文件
// 校验表达式
define('PhoneRule', "/^1[3456789]\d{9}$/");
define('EmailRule', "/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/");
define('PwdRule', "/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/");
// 响应体 统一封装
function writeJson($code, $data, $msg = 'ok', $errorCode = 0)
{
$data = [
'code' => $errorCode,
'result' => $data,
'message' => $msg
];
return json($data, $code);
}
路由
public文件夹下有一个route.php,
application 文件夹下也有一个route.php,
public文件夹下的route.php应该是主路由,application 文件夹下route.php的路由是子路由。
这两个路由的关系在请求接口时会体现。
public文件夹下route.php,可以不用管,把精力放在application 文件夹下route.php。
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <[email protected]>
// +----------------------------------------------------------------------
use think\Route;
// route参数的controller可以省略
// 增:注册
Route::post('/register','index/User/register');
// 删:注销账户
Route::post('/destroyUser', 'index/User/destroyUser');
// 改:修改密码
Route::post('/updatePwd', 'index/User/updatePwd');
// 查:登录
Route::post('/login', 'index/User/login');
// 查:获取所有用户的联系信息
Route::get('/getUsersContactInfo', 'index/User/getUsersContactInfo');
// 改:修改某用户的联系信息
Route::put('/updateUsersContactInfo', 'index/User/updateUsersContactInfo');
// 测试接口
Route::get('/controllerTest', 'index/User/controllerTest');
// 借口测试
Route::get('/test', function () {
// find 查找只能找到一条记录
$all = \app\index\model\IwalaCurrency::all();
echo "\njust is one case!\n";
return json($all);
});
Route::get(路由路径, 执行函数);
最近熟悉php和tp5做得一些事情。分享一下!!!