## 环境与工具
### 框架
* ThinkPHP5
* 小程序web开发者工具
### 基础语言、环境
* PHP 5.6
* Mysql
* Apache
### 开发工具
* PHPStorm
* 微信web开发者工具(VS code)
* PostMan:接口测试
* Navicat:数据库管理
* phpstudy/XAMPP: 集成开发环境
> https://www.apachefriends.org/download.html
查看端口占用的进程,
1.cmd中输入
netstat -ano
2.找到80端口或3306端口对应的进程PID的数字
3.打开windows 任务管理器-》详细信息中找到该pid对应的进程关闭
### ThinkPHP5
* Composer安装:
* Git安装:
应用项目
https://github.com/top-think/think/tree/v5.0.7
核心框架
https://github.com/top-think/framework
* 直接下载:
### 三端分离项目命名
* 服务器端程序: Server
* 客户端小程序: Shop
* CMS: Cms
thinkphp -》 server
framework -> thinkphp
thinkphp -> 拖入到Server目录中
访问
http://localhost/personal/jiyun_classware/thinkphp5-weixin/server/public/
### ThinkPHP5目录层次结构
* 入口文件:public/index.php
* 应用: application
* 模块: application/模块名
* 控制器: application/模块名/controller/index.php
* action: application/模块名/controller/index.php中的方法
* 模型: application/模块名/model
* 服务: application/模块名/service
* vendor: composer安装目录
* thinkphp:核心类库
* runtime:缓存、日志文件
* public:外部站点的目录,可以被直接访问到的
* extend: 团队自定义的一些类库。可以被think自动加载的
> 详见 https://www.kancloud.cn/manual/thinkphp5/118008
### ThinkPHP5自带的webserver
* cd public
* php -S localhost:8080 router.php
### URL路径格式(默认不区分大小写)
#### PATH_INFO模式
* http://serverName/index.php/module/controller/action/[param/value/param2/value2...]
* http://localhost/server/public/index.php/index/index/index
#### 兼容模式 ?s=index/index/index/p/v
http://localhost:8080/index.php?s=index/index/index
> 缺点:1.太长 2.URL路径暴露了服务器文件结构 3.不能实现URL语义化
### 新建一个模块及命名空间
* 自动补全命名空间
1. settings
2. 搜索 Directories
3. 选中server/applications
4. 点source是选项卡
5. 点右侧小p 向下箭头
6. 输入app
### 安装Postman
### 动态注册式路由
> 原有的path_info模式就会失效
* 修改 application/route.php
use think\Route;
Route::rule('hello','test/Test/hello');
地址栏访问 localhost:8080/hello
### 定义路由
* Route::rule('路由表达式','路由地址','请求类型','路由参数(数组)','变量规则(数组)')
//GET\POST\DELETE\PUT
* 支持多种请求方式:
Route::rule('hello','test/Test/hello','GET|POST',['https'=>false]);
* 简写方式:
Route::get('hello','test/Test/hello');
Route::get('hello','test/Test/hello');
Route::any();
### 路由传参
* get
1.
Route::get('hello/:id','test/Test/hello');
2.不定义在地址栏里,使用?直接传,在方法参数中直接接收
Route::get('hello','test/Test/hello');
* 方法中获取参数
1. application/route.php
Route::get('hello/:id','test/Test/hello');
2. application/test/controller/Test.php/hello
public function hello($id,$name){
echo $id;
echo '|';
echo $name;
}
3.访问
http://localhost:8080/hello/123?name=yuonly
* 使用request获取参数
1. 使用
use think\Request
2.
$id = Request::instance()->param('id');
$name = Request::instance()->param('name');
$age = Request::instance()->param('age');
3. 一次性获取所有参数
$all = Request::instance()->param();
var_dump($all);
Request::instance()->get(); ? 后面的参数
Request::instance()->route(); 地址栏中的参数
Request::instance()->post(); 只获取post参数
* 助手函数获取参数
$all = input('param.');
$all = input('get.');
$all = input('post.');
* 使用依赖注入的方式获取
1. application/test/controller/Test.php/req
use think\Request;
public function hello(Request $request){
$all = $request->param();
}
2. application/route.php
Route::get('req/:id','test/Test/req');
3. 访问
http://localhost:8080/req/222?name=123&age=19
### 功能开发
---
### Banner表设计分析
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for `y1611_banner`
-- ----------------------------
DROP TABLE IF EXISTS `y1611_banner`;
CREATE TABLE `y1611_banner` (
`bid` int(11) NOT NULL AUTO_INCREMENT COMMENT 'banner主键',
`name` varchar(50) NOT NULL DEFAULT '' COMMENT 'banner名称,作为标识',
`description` varchar(255) NOT NULL DEFAULT '' COMMENT 'Banner描述',
`delete_time` int(11) NOT NULL COMMENT '删除时间',
`update_time` int(11) NOT NULL COMMENT '更新时间',
PRIMARY KEY (`bid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `y1611_banner`
-- ----------------------------
BEGIN;
INSERT INTO `y1611_banner` VALUES ('1', '首页', '首页轮播图', '0', '0');
COMMIT;
-- ----------------------------
-- Table structure for `y1611_banner_item`
-- ----------------------------
DROP TABLE IF EXISTS `y1611_banner_item`;
CREATE TABLE `y1611_banner_item` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`img_id` int(10) unsigned NOT NULL COMMENT '图片表的主键',
`keyword` varchar(120) NOT NULL DEFAULT '' COMMENT '关键字,根据不同的type跳转到不同的页面',
`type` tinyint(3) unsigned NOT NULL DEFAULT '1' COMMENT '跳转类型,可能跳转到商品页,也可能跳转到专题页',
`delete_time` int(10) unsigned NOT NULL,
`update_time` int(10) unsigned NOT NULL,
`banner_id` int(10) unsigned NOT NULL COMMENT '关联banner表',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `y1611_banner_item`
-- ----------------------------
BEGIN;
INSERT INTO `y1611_banner_item` VALUES ('1', '1', '', '1', '0', '0', '1'), ('2', '2', '', '1', '0', '0', '1'), ('3', '3', '', '1', '0', '0', '1'), ('4', '4', '', '1', '0', '0', '1');
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
### Banner接口定义及自定义控制器多级目录
* 创建Banner.php控制器 application/api/controller/v1/Banner.php
/**
* 获取指定id的banner数据
* @param $id 那个地方的banner,目前只有首页
* @url /banner/:id
* @http GET
*/
public function getBanner($id){
}
* 定义路由 application/router.php
Route::get('banner/:id','api/v1.Banner/getBanner');
### Validate:验证机制
#### 方式一:独立验证
* 控制器中引入 use think\Validate
* 代码
//定义验证规则
$validate = new Validate(([
'name'=>'require|max:10',
'email'=>'email'
]));
//执行验证
$result = $validate->check($data);
var_dump($result);
//获取验证错误信息
echo $validate->getError();
//批量验证
$result = $validate->batch()->check($data);
var_dump($result);
var_dump($validate->getError());
#### 方式二:验证器
* 创建文件夹:application/api/validate
* 创建文件: application/api/validate/TestValidate.php
* TestValiate 需要继承 Validate类
use think\Validate;
class TestValidate extends Validate
{
protected $rule = [
'name'=>'require|max:10',
'email'=>'email'
];
}
* 在控制器 Banner/valid 中使用
//2 验证器的使用
$validate = new TestValidate();
$result = $validate->batch()->check($data);
var_dump($result);
var_dump($validate->getError());
#### 自定义验证规则
* 新建 application/api/validate/IntCheck.php
use think\Validate;
class IntCheck extends Validate
{
protected $rule = [
'id'=>'require|intPositive'
];
protected function intPositive($value,$rule,$data,$field=''){
if(is_numeric($value)&& is_int($value+0)&&($value+0)>0){
return true;
}else{
return $field.'必须是正整数';
}
}
}
* 使用 application/api/controller/v1/Banner.php/valid
public function valid(Request $request){
//3 自定义校验规则
$validate = new IntCheck();
$data = [
'id'=>$request->param('id')
];
$result = $validate->batch()->check($data);
var_dump($result);
var_dump($validate->getError());
}
### 构建接口参数校验
* 创建 application/api/validate/BaseValidate.php
namespace app\api\validate;
use think\Exception;
use think\Request;
use think\Validate;
class BaseValidate extends Validate
{
public function goCheck(){
//1.获取http请求参数
$params = Request::instance()->param();
//2. 执行校验
$result = $this->check($params);
//3. 抛出异常或通过
if(!$result){
$error = $this->error;
throw new Exception($error);
}else{
return true;
}
}
}
* IntCheck 类去继承 BaseValidate
class IntCheck extends BaseValidate
{
protected $rule = [
'id'=>'require|intPositive'
];
protected function intPositive($value,$rule,$data,$field=''){
if(is_numeric($value)&& is_int($value+0)&&($value+0)>0){
return true;
}else{
return $field.'必须是正整数';
}
}
}
* 在控制器Banner中使用
//4. 验证层
(new IntCheck())->goCheck();
$cid = 123;
echo $cid;
---
### REST 与 RESTFul api的设计
> Representational State Transfer: 表述性状态转移
> SOAP Simple Object Access Protocol: 简单对象访问协议,使用XML描述数据
* RESTFul api:基于REST的API设计理念,通常使用JSON描述数据,无状态
* 基于资源,增删改查都只是对于资源状态的改变
* 使用HTTP动词来操作资源
* RESTFul API规范示例
/getGoods/:gid 错
GET: /goods/:gid 对
---
### RESTFul API最佳实践
* POST:创建
* PUT: 更新
* GET: 查询
* DELETE: 删除
#### 状态码:404、400、200、201、202、401(未授权)、403、500(服务器未知错误)
#### 错误码:自定义的错误ID号
#### 统一描述错误:错误码、错误信息、当前URL
return ['code'=>200,'error_code'=>10000,'msg'=>'服务器错误',url:'localhost']
#### 使用Token令牌来授权和验证用户身份
#### 版本控制
#### 测试与生产环境开发:
api.xxx.com
dev.api.xxx.com
#### URL语义要明确,最好可以"望文知意"
#### 最好有一份比较标准的文档
---
#### 学习RESTFul API的最佳方式
* 模仿: 比如豆瓣开放API\Github开发者API
---
### 正确处理异常处理流程
* 创建模型: application/api/model/Banner.php
namespace app\api\model;
class Banner
{
public static function getBannerById($id){
//TODO 根据id获取banner信息
return 'banner info';
}
}
### 传统方式的异常处理,一层一层抛出
* api/model/Banner.php
namespace app\api\model;
use think\Exception;
class Banner
{
public static function getBannerById($id){
//TODO 根据id获取banner信息
try{
1/0;
}catch(Exception $e){
throw $e;
return 'banner info';
}
}
}
* api/controller/v1/Banner.php
public function getBanner($id){
(new IntCheck())->goCheck();
try{
$banner = BannerModel::getBannerById($id);
}catch(Exception $e){
$errors = [
'code'=>500,
'error_code'=>10000,
'msg'=>$e->getMessage()
];
return json($errors,500);
}
return $banner;
}
#### 异常分类
* 由于用户行为导致的异常(没有通过验证、没有查询到结果):需要向用户返回具体信息,不需要记录日志
* 服务器自身异常(代码错误、调用外部接口错误):记录日志,不向客户端返回具体信息
#### 全局异常处理
* application/lib/exception/ExceptionHandler.php :自定义的全局异常处理类,到时让thinkphp的异常处理类从默认的变为这个类
> 修改 config.php
'exception_handle' => 'app\lib\exception\ExceptionHandler'
namespace app\lib\exception;
use think\Exception;
use think\exception\Handle;
use think\Request;
class ExceptionHandler extends Handle
{
private $code;
private $error_code;
private $msg;
//还需要返回客户端当前请求的url路径
//覆盖默认的异常处理的方法。所有的异常都通过它来处理
public function render(Exception $e){
//如果是BaseException 那么需要返回具体错误信息给客户端
if($e instanceof BaseException){
//处理自定义异常
$this->code = $e->code;
$this->msg = $e->msg;
$this->error_code = $e->error_code;
}else{
$this->code = 500;
$this->msg = '服务端错误';
$this->error_code = 999;
}
$request = Request::instance();
$res = [
'error_code'=>$this->error_code,
'msg'=>$this->msg,
'url'=>$request->url()
];
return json($res,$this->code);
}
}
* application/lib/exception/BaseException.php
namespace app\lib\exception;
use think\Exception;
class BaseException extends Exception
{
//http 状态码 404 200
public $code=400;
//错误信息
public $msg='参数错误';
//自定义的错误码
public $error_code=10000;
//请求的api路径
public $url;
}
* application/lib/exception/BannerMissException.php banner未找到异常类
namespace app\lib\exception;
class BannerMissException extends BaseException
{
public $code = 404;
public $msg = '请求的banner不存在';
public $error_code = 40000;
}
* 执行自定义异常: controller/v1/Banner.php
public function getBanner($id){
(new IntCheck())->goCheck();
$banner = BannerModel::getBannerById($id);//当$banner = null 时,未找到。则抛出 BannerMissException异常
if(!$banner){
throw new BannerMissException();
}
return $banner;
}
#### error_code
* 999 未知错误
* 1 开头为通用错误
* 2 商品类错误
* 3 主题类错误
* 4 Banner类错误
* 5 类目类错误
* 6 用户类错误
* 7 订单类错误
---
* 10000 通用参数错误
* 10001 资源未找到
* 10002 未授权(令牌不合法)
* 10003 尝试非法操作(自己的令牌尝试操作其他人的数据)
* 10004 授权失败(第三方应用账号登录失败)
* 10005 授权失败(服务器缓存异常)
---
* 20000 请求商品不存在
* 30000 请求主题不存在
* 40000 banner不存在
* 50000 类目不存在
* 60000 用户不存在
* 60001 用户地址不存在
* 80000 订单不存在
* 80001 订单中的商品不存在,可能被删除
* 80002 订单还未支付,却尝试发货
* 80003 订单已支付过
#### 在服务器异常中加入 记录日志功能
* 关闭系统默认的记录日志功能 config.php type=> File 改为 test
'log' => [
// 日志记录方式,内置 file socket 支持扩展
'type' => 'test',
// 日志保存目录
'path' => LOG_PATH,
// 日志记录级别
'level' => [],
],
* 在入口文件 index.php 中指定日志记录的目录
define('LOG_PATH',__DIR__.'');
* 在ExceptionHandler类中定义私有方法,用来记录日志
private function logRecord($e){
//重新开启记录日志功能
Log::init([
'type' => 'File',
// 日志保存目录
'path' => LOG_PATH,
// 日志记录级别
'level' => ['error']
]);
Log::record($e->getMessage(),'error');
}
* 在render方法中调用
else{
$this->code = 500;
$this->msg = '服务端错误';
$this->error_code = 999;
//调用记录日志方法
$this->logRecord($e);
}
#### 加入开关,当上线时使用自定义的异常处理,当调试时使用tp默认的异常处理
* 由于tp5 app_debug 配置项上线时会关闭,所以使用他作为开关变量即可
* 修改ExceptionHandler
public function render(Exception $e){
//如果是BaseException 那么需要返回具体错误信息给客户端
if($e instanceof BaseException){
//处理自定义异常
$this->code = $e->code;
$this->msg = $e->msg;
$this->error_code = $e->error_code;
}else{
if(config('app_debug')){
return parent::render($e);
}else{
$this->code = 500;
$this->msg = '服务端错误';
$this->error_code = 999;
$this->logRecord($e);
}
}
$request = Request::instance();
$res = [
'error_code'=>$this->error_code,
'msg'=>$this->msg,
'url'=>$request->url()
];
return json($res,$this->code);
}
#### 增加参数异常处理类 lib/exception/ParamException.php
namespace app\lib\exception;
use think\Exception;
class ParamException extends Exception
{
public $code = 400;
public $error_code = '10000';
public $msg = '参数错误';
}
#### 修改BaseValidate中的 goCheck方法
public function goCheck(){
//1.获取http请求参数
$params = Request::instance()->param();
//2. 执行校验
$result = $this->check($params);
//3. 抛出异常或通过
if(!$result){
$e = new ParamException();
$e->msg = $this->error;
throw $e;
//$error = $this->error;
//throw new Exception($error);
}else{
return true;
}
}
一键复制
编辑
Web IDE
原始数据
按行查看
历史