php学习(三):近几年来,不断发展的ThinkPhp框架(TP6)

1.thinkPhp安装

6.0版本开始,必须通过compose方式下载安装和更新。(3.2 和 5.0)

MVC是一种架构模式

软件系统分为三个基本部分:模型(Model)、视图(View)、控制器(Controller)

ThinkPhp是一个典型的MVC架构

功能:控制器,负责转发请求,对请求进行处理。 视图,界面设计人员进行图形界面设计。模型 ,程序员编写程序应有的功能(实现算法等等),数据库专家进行数据管理和数据库设计

1.创建tp项目
1.创建TpProject项目
composer create-project topthink/think=6.0.x-dev TpProject

2.运行项目
php think run --port=8001  默认是8000,但端口占用则启动不了!

该项目是以单应用模式,只有一个应用,项目。

注意:runtime是缓存文件,需要开启相关文件。

// 查看版本
php think version
// 清除缓存文件 runtime
// 1.清除文件,保留目录
php think clear
// 2.清除文件,不保留目录 
php think clear --dir
// 3.清除日志 
php think clear --log
// 4.清除日志 目录并删除空目录 
php think clear --log --dir



项目结构(单应用模式)

├─app 应用目录
│  ├─controller         控制器目录
│  ├─model              模型目录
│  ├─view               视图目录
│  └─ ...               更多类库目录
│
├─public                WEB目录(对外访问目录)
│  ├─index.php          入口文件
│  ├─router.php         快速测试文件
│  └─.htaccess          用于apache的重写
│
├─view                  视图目录
├─config                应用配置目录
├─route                 路由定义目录
├─runtime               应用的运行时目录
2.运行Git项目

3.开启提示模块

在config\app.php里面修改

return [
    // 显示错误信息, false 改为 true
    'show_error_msg'   => true,
 ]

2.thinkPhp全栈学习

1.多应用模式(?)
├─app 应用目录
│  ├─index              主应用
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录
│  │  ├─view            视图目录
│  │  ├─config          配置目录
│  │  ├─route           路由目录
│  │  └─ ...            更多类库目录
│  │ 
│  ├─admin              后台应用
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录
│  │  ├─view            视图目录
│  │  ├─config          配置目录
│  │  ├─route           路由目录
│  │  └─ ...            更多类库目录

(1.多应用模式

单应用模式,只有一组 controller, view,xxx

多应用模式,将应用模式封装成模块,有多组应用模块。

安装模块


composer require topthink/think-multi-app

config/app.php中添加多应用字段

配置文件

...
 
// 是否启用事件
'with_event'       => true,
// 自动多应用模式
'auto_multi_app'       => true,

....

在app文件夹下,可以创建index, home, 等应用, 每个应用可以包含单独的controller,

models, views

然后注意修改控制器的命名空间

namespace app\controller; 

修改成
namespace app\home\controller; 



(2.自动生成目录

在app文件夹下创建build.php文件

<?php
 
 return [
   // 需要自动创建的文件
   '__file__'=>[],
   // 需要自动创建的目录
   '__dir__'=>['controller','model','view'],
   // 需要自动创建的控制器
   'controller'=>['Index'],
   // 需要自动创建的模型
   'model'=>['User'],
   // 需要自动创建的模板 
   'view'=>['index/index']
 ]



然后执行命令:

php think  build Api

然后 就会创建Api目录文件。

教程是:使用了服务器软件,指向了public目录,

然后使用

xxxx/index.php/home/index/index

xxxx/index.php/h5/index/index

成功访问到控制器信息。

(3.获取配置信息(略)

1.get 获取配置项
2.has 判断配置项是否存在
3.set 批量设置更新配置项


// 引入Config类
use think\facade\Config;
public function index(){
   $app = Config::get('app');
   print_r($app);
   
   $app = Config::has('app.app_host');
   print_r($app);
}

2.route路由

注意:控制器名称可以写首字母小写

use think\facade\Route;

// 1.rule方法
// Route::rule('路由表达式', '路由地址', '请求类型');
// 路径参数, 并指定请求方式, 默认GET请求类型
// 路由指向 index控制器的hello方法 
//Route::rule('hello/:name', 'index/hello','GET|POST');


// 2.快捷注册路由
// 直接返回字符串
Route::get('think', function () {
    return 'hello,ThinkPHP6!';
});


// 快捷注册 方法
Route::get('hello/:name', 'index/hello');
Route::post('hello/:name', 'index/hello');


// 可选变量[]的路径参数
Route::get('hi/[:name]', 'index/hello');


// 完全匹配(不允许后面有其它路径)
Route::get('world/:test$', 'index/hello');


// 如果启动服务器软件 phpStudy, wampserver等等
// 可以使用 http://localhost:xxxx/index.php/hi/xxx
// 如果需要强制匹配路由, 则需要在 config/route.php中开启 url_route_must


// 3.路由分组
// 路由分组功能允许把相同前缀的路由定义合并分组,这样可以简化路由定义,并且提高路由匹配的效率,不必每次都去遍历完整的路由规则(尤其是开启了路由延迟解析后性能更佳)。
// group第一个参数控制路由前缀,  prefix是同一个控制器的前缀
Route::group('blog', function () {
    Route::get(':name', 'hello');
    Route::post(':name', 'hello');
//  原来: Route::post('blog:id', 'blog/update');
})->prefix('index/');



3.view视图页面

1.安装模块


composer require topthink/think-view

如果安装失败,显示包不存在,则尝试下面命令

// 取消镜像设置

composer config -g --unset repos.packagist

2.配置模板的后缀(略)

config/template.php


'view_suffix' => 'html'

以html为后缀名,也可以改成php,xml等后缀文件名

3.目录结构

├─controller
│      Index.php     控制器
│
├─model
│      User.php
│
└─view
    └─index         控制器名称(小写 或者 驼峰大写替换为'_')
            index.html   控制器内对应的方法名称

4.视图页面view

(1.助手函数view(建议)

class Index extends Vase Controller{
    public function index(){ 
        // 自动引入
        return view();
        // 指定模板
        return view('index/index');  
    }
}



(2.自动定位View类

use think\facade\View

class Index extends Vase Controller{
    public function index(){
        // 不带任何参数,自动定位当前操作的模板文件
        return View::fetch(); 
        // 调用本项目 AppApi 控制器,index.html模板
        return View::fetch('appapi/index');
        // 调用其它admin 项目 AppApi 控制器,index模板
        return View::fetch('admin@member/index');
    }
}


(3.注入方式引入(略)

use think\View;

class Index extends Vase Controller{
    public function index(View $view){
        // 注入方式
        return $view->fetch();
    }
}


**5.重定向 redirect **


return redirect('http://www.thinkphp.cn');

return redirect('/index');
4.controller控制器

控制器文件名称需要大写, 在路由中, 可以把控制器改为小写

注意:创建完整的控制器(不建议使用)


php think make:controller app\controller\User


// 路由卡路里 无法访问二级控制器

1.模板传值(助手函数)

tp6的 助手函数view, 包括模板传值都和laravel相同。

class Index extends Vase Controller{
    public function index(){ 
        return view();
        return view('index',[
    	  "id"=>'1',
    	  "name"=>'小明 '
    	]);
    	
    	// 模板页面中就可以使用 {$name} 输出变量
    }
}



2.模板传值(View类)

use think\facade\View

class Index extends Vase Controller{
    public function index(){
    	View::assign("id",1);
    	View::assign("name",'小明 '); 
    	View::assign([
    	  "id"=>'1',
    	  "name"=>'小明 '
    	]); 
    	// 模板页面中就可以使用 {$name} 输出变量
    	
        // 不带任何参数,自动定位当前操作的模板文件
        return View::fetch(); 
    }
}



模板中可以使用 {$id}   {$name} 获取数据
与 Laravel不同的是 这里使用Db获取的数据,是使用[]获取数据, {$find['name']}
5.template模板配置
  • ThinknPhp 内置了一个基于XML的性能卓越的模板引擎,这是一个专门为ThinkPhp服务的内置模板引擎,使用了 XML标签库技术 的编译型模板引擎,支持两种类型的模板标签,使用了动态编译和缓存技术,而且支持自定义标签库。

  • config/view.php文件可以进行模板设置

  • 普通标签:主要用于输出变量,函数过滤和做一些基本的运算操作

  • XML标签:也称为标签库标签,主要完成一些逻辑判断,控制和循环输出,并且可扩展

(1.模板配置

return [
    // 模板引擎类型使用Think
    'type'          => 'Think',
    // 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
    'auto_rule'     => 1,
    // 模板目录名
    'view_dir_name' => 'view',
    // 模板后缀
    'view_suffix'   => 'html',
    // 模板文件名分隔符
    'view_depr'     => DIRECTORY_SEPARATOR,
    // 模板引擎普通标签开始标记     // 模板语法会根据这个进行匹配。
    'tpl_begin'     => '{',
    // 模板引擎普通标签结束标记
    'tpl_end'       => '}',
    
    // 标签库标签开始标记  // 是xml类型标签配置
    'taglib_begin'  => '{',
    // 标签库标签结束标记
    'taglib_end'    => '}',
];

(2.加载静态资源

和 Laravel一致 ,以根路径开始 “/”
 
 <link rel="stylesheet" href="/static/lib/admin-assets/lib/layui-v2.6.3/css/layui.css" media="all">
   
6.database数据库连接

(1.全局配置

config/database.php文件下

 // 数据库连接配置信息
    'connections'     => [
        'mysql' => [
            // 数据库类型
            'type'            => env('database.type', 'mysql'),
            // 服务器地址
            'hostname'        => env('database.hostname', '127.0.0.1'),
            // 数据库名
            'database'        => env('database.database', ''),
            // 用户名
            'username'        => env('database.username', 'root'),
            // 密码
            'password'        => env('database.password', ''),
            // 端口
            'hostport'        => env('database.hostport', '3306'),
            // 数据库连接参数
            'params'          => [],
            // 数据库编码默认采用utf8
            'charset'         => env('database.charset', 'utf8'),
            // 数据库表前缀
            'prefix'          => env('database.prefix', ''),

              ... 
        ],

        // 更多的数据库配置信息
    ],

config/app.php下可以配置页面信息

// 默认时区
'default_timezone' => 'Asia/Shanghai',


// 错误显示信息,非调试模式有效
'error_message'    => '页面错误!请稍后再试~',

(2.env环境变量(也可以不设置)

注意:修改.gitignore 文件,因为.env文件不会上传

默认安装后的根目录有一个 .example.env环境变量示例文件,也可以直接修改成.env

APP_DEBUG = true

[APP]
DEFAULT_TIMEZONE = Asia/Shanghai

[DATABASE]
TYPE = mysql
HOSTNAME = 127.0.0.1
DATABASE = test
USERNAME = username
PASSWORD = password
HOSTPORT = 3306
CHARSET = utf8
DEBUG = true

[LANG]
default_lang = zh-cn

注意: 如果修改了某个模块的环境变量,需要在对应的模块下调用 .env配置

use think\facade\Env;


'error_message' => Env::get("app.error_message","您出错了")
7.model模型(服务层)
  • 模型会自动对应数据库表,模型类的命名规则 是除去表前缀的数据库表名称,首字母大写,驼峰法命名。
  • 模型自动对应的数据库表名称是遵循小写+下划线规范,如果表名有大写的情况,必须通过设置 模型的table属性
  • 类似eggjs的 服务层,数据操作由服务层处理 ,控制器层只传值。本质上说,服务层是对数据库表的操作 进行了一层封装

(1.创建模型

<?php

namespace app\model;
use  think\Model;


class User extends Model{
    // 封装成经典的ORM模型
    // 表名, ($table换成$name也可以)
    protected $table = 'user';
    // 主键id
    protected $pk = 'id';
    // 允许的字段
    protected $field = [
        'username',
        'password'
    ];

//   // 去除表的字段
//   proteced $disuse = ['phone','sex']
//   // 软删除(字段和默认值)
//   protected $deleteTime  = 'delete_time';
//   protected $defaultSoftDelete = 0
//  // 数据类型(声明数据类型)
//   proteced $chema = [
//      'id' => 'int',
//      'name'=>'string'
//   ]
//

// 作为模型的方法(数据库操作交给模型,典型的服务层方式)
//   public function index(){
//       $find = User::find(1);
//       $find = Admins::where('id',2)->find();
//
//       // 软删除
//       $res = Admins::destroy(6);
//       return $find;
//   }
}

(2.控制器中使用模型

use app\model\User;  // 引入User模型

class  Index extends BaseController{
    public function index(){
         // 方法一:调用模型内的方法(服务层方式)
         // $db = new  User();
         // $query = $db->index();

         // 方法二:直接使用orm的语法 
         // (同Laravel, 但没有get, first 方法, 只能使用select, find 方法)
         // $query = User::where('id',1)->find();
         // $query = User::where('id',1)->select();

         return json($query);
    }
}
// 检测数据集为空
$res = empty($query) 

$res = $query->isEmpty()

8.data数据获取
1.Form表单获取
use think\Request;

// name是路径参数,(这是 一个带默认值 的参数)
// 也可以定义Request对象 : Request $request
public function hello($name = 'ThinkPHP6')
{ 
    $this->request  <=>  request()  <=>  $request
    // 这三个等价,request()是助手函数, Request是请求对象 
    // 例如: http://localhost:8001/blog/22?hello=555

    // 控制器获取数据,tp6和laravel 类似 , 没有input方法,
    // 可以input('get.pid'),  request()->header();

	// 1.请求方式
	$a = $request->all();
	$a = request()->all();
	$a = $this->request->all();
	
    // 2.请求参数
    // 获取路径参数
    $a = $this->request->param('name');
    // 获取所有参数(不管post, get)
    $b = $this->request->all();
    $c = $this->request->query('hello');     // hello=555
    $c = $this->request->get('hello'); // 555
    // 获取请求头数据
    $header = $this->request->header(); // header对象
    $host = $this->request->host();     // localhost:8001
    $method = $this->request->method(); // GET
    // 获取文件
    $file = $this->request->file('file'); // 上传的二 进制文件
}

2.Mysql数据获取

(1.ORM数据查询(建议使用)

即使thinkphp是有类似于服务层的模型层,但也可以使用同 Laravel一样的orm数据查询语法。 


1.查询数据
// (同Laravel, 但没有get, first 方法, 只能使用select, find 方法)
// $query = User::where('id',1)->find();// 和first()方法同样使用
// $query = User::where('id',1)->select();

2.插入数据
$data = [ 'username' => 'darth', 'email'=> 'd.vader@theempire.com'];
$user = UserModel::insert($data) 

3.更新数据
$data = [ 'username' => 'darth', 'email'=> 'd.vader@theempire.com'];
$user = UserModel::where(id,'1')->update($data)   

4.删除数据
$user = UserModel::where('id', 12)->delete();

(2.原生方法查询

1.query查询数据
  $query = Db::query("select * from user");


2.execute 执行sql语句(插入,更新) 
  $res = Db::execute("update user set name='小小红' where name='小红'");
   
  $res = Db::execute("insert into user values(NULL,'小红','女','3')");
 

(3.table方法查询

(3.1基本查询

1.select 查询数据
  // 多条数据查询,返回一个二维数组,否则返回空数组
 $find = Db::table('user')->where('id','>',1)->select(); 
  // find查询 单条数据查询,成功则返回数组,否则返回null
 $find = Db::table('user')->where('id',1)->find(); 
 // value 单条数据 过滤查询,返回指定字段, 
 $find = Db::table('user')->where('id','>',1)->value("name"); 
 // column 多条数据 过滤查询,返回指定字段, 返回一个数组,  name是查询字段,id是键值
 $find = Db::table('user')->where('id','>',1)->column("name",'id'); 
 
 
 2.insert 插入数据
 // insert只返回结果
 $data = ['name'=>'小刚','age'=>10];
 $res = Db::table("user")->insert($data);
 // insertGetId 返回自增主键id 
 $res = Db::table("user")->insertGetId($data);
 // insertAll 添加多条数据
 $data = [
   ['name'=>'小刚21','age'=>10];
   ['name'=>'小刚2','age'=>10];
 ]
 $res = Db::table("user")->insertAll($data);
  // save修改数据(如果有主键id,则是修改, 如果没有,则是添加)
 $data = ['id'=>1,'name'=>'小刚','age'=>10];
 $res = Db::table("user")->save($data);


3.update更新数据
 $data = ['name'=>'小刚','age'=>10];
 $res = Db::table("user")->where('id',1)->update($data);
 // inc,自增,dec 自减一个字段的值
 $res = Db::table("user")->where('id',1)->inc('age',10)->update($data);
 

4.delete删除数据
   $res = Db::table("user")->where('id',1)->delete();

(3.2过滤数据


连贯操作  作用   支持的参数类型
where     用于AND查询    字符串、数组和对象
table   用于定义要操作的数据表名称  字符串和数组
alias  用于给当前数据表定义别名 字符串
field 用于定义要查询的字段(支持字段排除) 字符串和数组
order 用于对结果排序 字符串和数组
limit 用于限制查询结果数量 字符串和数字
page 用于查询分页(内部会转换成limit) 字符串和数字
join 用于对查询的join支持I 字符串和数组
union 用于对查询的union支持 字符串、数组和对象
distinct 用于查询的distinct支持 布尔值


1.where 过滤查询 , whereOr  或查询
 $select = Db::table('user')->where('id','>',1)->select(); 
 $select = Db::table('user')->where('id=1')->select(); 
 $select = Db::table('user')->where(['id'=>1,'name'=>"小明"])->select(); 
 
 
2.table 表查询,多表查询
   // u是user表的别名 ,a是admin表的别名
   $select = Db::table('user')->select();
   $select = Db::table('user u,admin a')->where('u.id',10)->select();
   $select = Db::table(['user'=>'u','admin'=>'a'])->where('u.id=a.id')->select();
  // alias 表别名
   $select = Db::table('user')->alias('a')->select();


3.order 数据排序(默认升序,  asc 升序, desc 倒序)
 $select = Db::table('user')->order('age','desc')->select();
 $select = Db::table('user')->order(['age'=>'desc'])->select();


4.limit 限制查询的个数
 $select = Db::table('user')->limit(2)->select();
 // 两个参数,从指定数据开始获取(从第3条数据开始获取10条数数据)
 $select = Db::table('user')->limit(3,10)->select();
 // page分页查询 (第1页,每页10个数据)
 $select = Db::table('user')->limit(3,10)->(1,10)->select();
 

5.join内连接 
 // 只匹配符合条件的数据
 $select = Db::table('user u')->join('admin a','a.id=u.id')->select();
 // 左连接 、右连接 只匹配符合主表条件的数据
 $select = Db::table('user u')->leftJoin('admin a','a.id=u.id')->select();
 $select = Db::table('user u')->rightJoin('admin a','a.id=u.id')->select();


6.union查询(将结果放在同一个数组中) 
 $select = Db::field('u_name')->table('user')
   ->union('select 'u_name' from user1')->select();


10. 其它过滤方法
  // field 字段过滤 
   $select = Db::table('user')->field('id,name')->select();
   $select = Db::table('user')->field(['id','name'])->select();
  // withoutField 字段去除过滤
   $select = Db::table('user')->withoutField('id,name')->select();
   // fieldRaw('MAX(age)')  取字段最大值
   $select = Db::table('user')->fieldRaw('MAX(age) as max')->select();
  // distinct 去重数据 
   $select = Db::table('user')->field('name')->distinct('name')->select();
  

(3.3聚合查询

方法   功能
count 统计数量,参数是要统计的字段名(可选)
max  获取最大值,参数是要统计的字段名(必须)
min  获取最小值,参数是要统计的字段名(必须)
avg 获取平均值,参数是要统计的字段名(必须)
sum  获取总分,参数是要统计的字段名(必须)

// 统计数量
$select = Db::table("user")->count()
// 求最大值
$select = Db::table("user")->max('age')

3.response响应数据
class Index 
{
    public function index()
    {
    	// (1. 输出页面
        return view('index/index');
    }
    
    public function hello()
    {
    	// (2. 输出字符串
        return 'hello,world!';
    }
    
    public function json()
    {
    	//(3. 输出JSON, 不能直接返回数组
        $data = ['data'=>[],'code'=>200];
    	
        return json($data);
        
        return json_decode($data)   // 字符串解析成数组(对象)
        
        return json_string($data)    // 对象转换成字符串
    }
    
    public function read()
    {
    	// (4.渲染默认模板输出
        return view();
    }
}
9.模板语法
1.$_x 输出系统变量

{$_SERVER.HTTP_HOST}  {$_GET['a']}   {$_POST['b']}

// 获取当前的域名  localhost:8001
{$Think.server.HTTP_HOST} 

{$Think.server.script_name} // 输出$_SERVER['SCRIPT_NAME']变量
{$Think.session.user_id} // 输出$_SESSION['user_id']变量
{$Think.get.pageNumber} // 输出$_GET['pageNumber']变量
{$Think.cookie.name}  // 输出$_COOKIE['name']变量
2.模板函数(过滤器)
方法    描述
default  默认值
md5     md5加密
date    日期格式化(支持各种时间类型)
format  字符串格式化
upper   转换为大写
lower   转换为小写
first   输出数组的第一个元素
last    输出数组的最后一个元素
substr  返回字符串的一部分


<div> {$name | default='小明'}  </div>
<div> {$name | md5}  </div>
<div> {$name | date='Y-m-d H:i:s'}  </div>
<div> {$number | format='%02d'}  </div>
<div> {$number | upper}  </div>
<div> {$number | substr=0,2}  </div>

3.辅助模板语法

1.不解析输出 
      {literal}  
         ... {$name}
      {/literal}
      
2.模板注释
   {// 这是单行注释 }
   {/* 这是多行注释 */}


4.{include /}模板引入

注意:view文件夹创建public_s文件夹,并且创建Public_s控制器,返回声明的视图。(可以不创建路由规则)


{include file="public_s/style" /}

{include file="public_s/script" /}

5.{foreach /} 循环遍历
{foreach $data as $key => $value}
  id:{$value.id}
  name:{$value['name']}
 {/foreach}

输出属性: 可以使用. 和 []

更强大的遍历语法

{volist name="data" key="key" id="value" (offset="1" length="2") }
   这个下标是从1开始输出, 可以截断输出遍历。
   {$value}
   
{/volist}


范围输出遍历语法

{for start="10" end="100" name="a"  step="5"}
    从10开始,步长为5,100结束。
    {$a}
{/for}


6.{switch /} 多分支条件
{switch $name}
   {case a}
       这是a
   {/case}
   {case b}
       这是b
   {/case}
   {case c}
       这是c
   {/case}
   {default/}
       这是d 
{/switch}

if条件语句

{if $number >= 90}
    优秀
{elseif $number>= 60}
     及格
{else /}
   不及格
{/if}

in / notin 范围标签

{in name="number" value="99,100,101"}
   在范围内
{/in}

{notin name="number" value="99,100,101"}
   不在范围内
{/notin}

between / nobetween 区段范围标签


{between name="number" value="99,1000"}
   在范围内
{/between}

{notbetween name="number" value="99,1000"}
   不在范围内
{/notbetween}

7.{eq /} 比较数据(三目运算)
标签  含义
eq或者equal     等于
neq或者notequal 不等于
gt             大于
egt            大于等于
lt             小于
elt            小于等于
heq            恒等于
nheq           不恒等于

{eq name="name" value="小明"}
   小明
{else/}
    else属于可省内容
{/eq}
{lt name="age" value="100"}
   大于
{/lt}


8.{:url(…)} 路由访问
在页面中设置路由 ( 控制器/方法 )

{:url('Index/verify')}


10.middleware中间件

(1.启动中间件

在app/middleware.php下开启中间件。

<?php
// 全局中间件定义文件
return [
    // 全局请求缓存
     \think\middleware\CheckRequestCache::class,
    // 多语言加载
    // \think\middleware\LoadLangPack::class,
    // Session初始化
     \think\middleware\SessionInit::class
];

(2.创建中间件


php think make:middleware AuthLogin

这个指令会 app/middleware目录下面生成一个Check中间件。(如果没有生成,则手动创建)

<?php

namespace app\middleware;

class AuthLogin
{
    public function handle($request, \Closure $next)
    {
        if (!$request->session('user')) {
            return redirect('/login');
        }

        return $next($request); 
    }
}

(3.注册中间件

在config/middleware.php下定义以下内容

return [
	// 取别名 
    'alias' => [
        'AuthLogin'=>app\middleware\AuthLogin::class,
    ],
];

中间件有四个: 全局中间件,应用中间件,路由中间 件,控制器中间件

1.全局中间件(config/middleware) 

    return [
    	'alias'=>[],
        'priority' => [],
    
        \app\middleware\Auth::class,
        'check',
        'Hello',
    ];


2.路由中间件(route)
  Route::rule('hello/:name','hello')
	->middleware('auth', 'admin')
        ->middleware('hello', 'thinkphp');  // 使用别名的方式,并且注册多个中间 件
	    ->middleware(\app\middleware\Auth::class); // 直接使用中间件
        
 Route::group('hello', function(){
	Route::rule('hello/:name','hello');
})->middleware('auth');
1.Session身份认证

(1.开启Session功能

在app/middleware.php文件开启


<?php
// 全局中间件定义文件
return [
    // 全局请求缓存
     \think\middleware\CheckRequestCache::class,
    // 多语言加载
    // \think\middleware\LoadLangPack::class,
    // Session初始化
     \think\middleware\SessionInit::class
];

(2.配置 Session

在config/session.php文件下定义以下内容

<?php
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------

return [
    // session name
    'name'           => 'PHPSESSID',
    // SESSION_ID的提交变量,解决flash上传跨域
    'var_session_id' => '',
    // 驱动方式 支持file cache
    'type'           => 'file',
    // 存储连接标识 当type使用cache的时候有效
    'store'          => null,
    // 过期时间 (秒)
    'expire'         => 1440,
    // 前缀
    'prefix'         => '',
];

(3.在控制器中使用

// 方法一: session助手函数(建议)
// 赋值
session('name', 'thinkphp');
// 取值
session('name');
// 删除
session('name', null);
// 判断是否赋值
session('?name');
// 清除session
session(null);


// 方法二: Session类访问
use think\facade\Session;
// 保存数据
Session::set('name', 'thinkphp');

// 存在性
Session::has('name');

// 获取数据
// 如果值不存在,返回null
Session::get('name');
// 如果值不存在,返回空字符串
Session::get('name', '');
// 获取全部数据
Session::all();

// 删除
Session::delete('name');

// 取舍并删除
$name = Session::pull('name');

// 清空
Session::clear();

// 清除当前请求有效的session
Session::flush();
2.Cookie身份认证2

(1.配置Cookie

无需手动初始化,系统会在调用之前自动进行Cookie初始化工作。

cookie是由服务端写入,保存在客户端上的数据交互类型

在config/cookie.php文件下配置

// cookie 保存时间
'expire'    => 0,
// cookie 保存路径
'path'      => '/',
// cookie 有效域名
'domain'    => '',
//  cookie 启用安全传输
'secure'    => false,
// httponly设置
'httponly'  => '',
// samesite 设置,支持 'strict' 'lax'
 'samesite' => '',

(2.在控制器中使用

// 方法一:cookie助手函数
// 设置
cookie('name', 'value', 3600);
// 获取
echo cookie('name');
// 删除
cookie('name', null);


// 方法二:Cookie类访问
// 设置
// 设置Cookie 有效期为 3600秒
Cookie::set('name', 'value', 3600); 
// 永久保存Cookie
Cookie::forever('name', 'value');
//删除cookie
Cookie::delete('name');
// 读取某个cookie数据
Cookie::get('name');
// 获取全部cookie数据
Cookie::get();
3.bcrpyt密码加密
public function testPass() {
    //原始密码
    $origPass = "123456"; 
    
    //加密
    $bcryptPass = password_hash($origPass, PASSWORD_DEFAULT); 

    //判断密码是否匹配
    if (password_verify($origPass,$bcryptPass)) {
    	echo "密码正确";
    } else {
    	echo "密码错误";
    }
}
11.分离项目中间件
1.cors跨域

在config/middleware.php添加以下内容


    // cors跨域
    \think\middleware\AllowCrossDomain::class
    // 默认允许所有的域访问Access-Control-Allow-Origin: *
    // 可以在config/cookie.php配置cookie 有效域名的domain
    

通过ajax,axios即可正常访问。

2.php-jwt认证

(1.安装模块

composer require firebase/php-jwt

(2.创建中间件

在 app/middleware下创建JWTMiddleware中间件,填写以下内容

注意: static 静态方法,可以使用 类名访问 JWTMiddleware::signToken

<?php

namespace app\middleware;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use think\Exception;

class JWTMiddleware
{
    /**
     * 生成验签
     * @param $uid 用户id
     * @return mixed
     */
    // 静态方法,可以使用 类名访问 JWTMiddleware::signToken

    public static function signToken($uid){
        $key='abcdefg';         //自定义的一个随机字串用户于加密中常用的 盐  salt
        $token=array(
            "iss"=>$key,        //签发者 可以为空
            "aud"=>'',          //面象的用户,可以为空
            "iat"=>time(),      //签发时间
            "nbf"=>time(),      //在什么时候jwt开始生效
            "exp"=> time()+15*24*60*60,  //token 过期时间
            "data"=>[           //记录的uid的信息
                'uid'=>$uid,
            ]
        );
        $jwt = JWT::encode($token, $key, "HS256");  //生成了 token
        return $jwt;
    }

    /**
     * 验证token
     * @param $token
     * @return array|int[]
     */
    public static function parseToken($token){
        $key='abcdefg';     //自定义的一个随机字串用户于加密中常用的 盐  salt
        $res['status'] = false;
        try {
            JWT::$leeway    = 60;//当前时间减去60,把时间留点余地
            $decoded        = JWT::decode($token, new Key($key, 'HS256')); //HS256方式,这里要和签发的时候对应
            $arr            = (array)$decoded;
            $res['status']  = true;
            $res['data'] = $arr['data'];
            return $res;

        } catch(\Firebase\JWT\SignatureInvalidException $e) { //签名不正确
            $res['info']    = "签名不正确";
        }catch(\Firebase\JWT\BeforeValidException $e) { // 签名在某个时间点之后才能用
            $res['info']    = "token失效";
        }catch(\Firebase\JWT\ExpiredException $e) { // token过期
            $res['info']    = "token过期";
        }catch(Exception $e) { //其他错误
            $res['info']    = "未知错误";
        }
        return $res;
    }
}

(3.在控制器中使用

注意:这jwt需要配合 第10条创建的AuthLogin中间件, 进行路由的拦截。

1.生成token 
use app\middleware\JWTMiddleware;

$token = JWTMiddleware::signToken($res['id']);
   

2.解析token
$token = JWTMiddleware::parseToken($token);
// 返回数组  Array ( [uid] => 123,[status]=>true ) 

(4.AuthLogin请求拦截

<?php

namespace app\middleware;

use app\model\UserMsg;
use app\middleware\JWTMiddleware;

class AuthLogin
{
    // 解析token
    // 获取用户id $user_id = AuthLogin::getAccessToken($request);
    public static function getAccessToken($request){
        // 在前后端分离的情况下,不要使用session,因为session无效。
        $access_token =  $request->header('access-token');
        // 去除bearer-
        $access_token = str_replace('bearer-','',$access_token);

        // 解析token
        $pre_user = JWTMiddleware::parseToken($access_token);

        if($pre_user['status'] && $access_token ) {
            // 返回解析的用户uid;
            // $pre_user['data']['uid']无效
            return $pre_user['data']->uid;
        }else {
            return false;
        }

    }

    public function handle($request, \Closure $next)
    {
        $pre_user_id =  AuthLogin::getAccessToken($request);
        if($pre_user_id){
            // 判断帐号是否存在,(并且没有注销)
            $res = UserMsg::where(['id'=>$pre_user_id ])->find();
            if($res && $res['isDelete'] != 1){
                // 判断帐号是否登录token
                if($res['token']){
                    $this->user = $res;
                    return $next($request);
                }else{
                    return json( ['msg'=>'当前未登录!请进行登录','code'=>403]);
                }
            }else{
                return json( ['msg'=>'帐号不存在','code'=>403]);
            }
        }else{
            return json( ['msg'=>'token错误!无法访问','code'=>403]);
        } 
    }
}
12.功能模块
1.BaseController拦截(不建议)


public function __construct(App  $app){
    $this->initialize();
  
    // 这里可以进行访问拦截操作(比如验证cookie, session)
    $this->_admin=['name'=>"666"]; // 后面可以访问
   
    //  传递公有参数
    View::assign(['empty'=>'333'])
    
    
    // 跳转到登录控制器方法, 注意,登录控制器不要继承BaseController, 否则会存在不断地 重定向
    // 跳转到登录控制器方法, 注意,登录控制器不要继承BaseController, 否则会存在不断地 重定向
    // header('Location: xxx') 类似于 redirect 重定向
    header('Location:auth/login');
    exit; 
}

扩展: cookie是服务器写入,存储在客户端浏览器
      session是服务器写入,存储在服务器端


2.Validate验证器(类)

(1.创建验证器

我们定义一个\app\validate\User验证器类用于User的验证。

可以使用下面的指令快速生成User验证器。

php think make:validate User

然后会生成以下验证器

namespace app\validate;

use think\Validate;

class User extends Validate
{
    // 验证规则
    protected $rule =   [   
        'name'  => 'require|max:25',
        'age'   => 'number|between:1,120',
        'email' => 'email',    
    ];
    
    // 提示信息
    protected $message  =   [
        'name.require' => '名称必须',
        'name.max'     => '名称最多不能超过25个字符',
        'age.number'   => '年龄必须是数字',
        'age.between'  => '年龄只能在1-120之间',
        'email'        => '邮箱格式错误',    
    ];
    
}

(2.控制器中使用


use app\validate\User;
use think\exception\ValidateException;

try {
    validate(User::class)->check([
        'name'  => 'thinkphp',
        'email' => 'thinkphp@qq.com',
    ]);
} catch (ValidateException $e) {
	// 验证失败 输出错误信息
	dump($e->getError());
}
3.captcha验证码(xx)

首先使用Composer安装think-captcha扩展包:

注意:验证码库需要开启Session才能生效。在app/middleware.php里面开启session

composer require topthink/think-captcha

(1.配置验证码

在 config/captcha.php文件定义以下

return [
    'length'    =>  4,
    'codeSet'   =>  '0123456789',
];
Captcha类带有默认的配置参数,支持自定义配置。这些参数包括:

参数	      描述	           默认
codeSet	   验证码字符集合	     略
expire	   验证码过期时间(s)	1800
math	   使用算术验证码	     false
useZh	   使用中文验证码	     false
zhSet	   中文验证码字符串  	略
useImgBg   使用背景图片	      false
fontSize   验证码字体大小(px)	 25
useCurve   是否画混淆曲线	     true
useNoise   是否添加杂点	      true
imageH	   验证码图片高度,设置为0为自动计算	0
imageW	   验证码图片宽度,设置为0为自动计算	0
length	   验证码位数	       5
fontttf	   验证码字体,不设置是随机获取	空
bg	       背景颜色         	[243, 251, 254]
reset	   验证成功后是否重置	true

(2.路由capcha


**(3.模板中使用 **

// 方法一:标签图片源 :captcha_src()
<div><img src="{:captcha_src()}" alt="captcha" /></div>

// 方法二:标签: :captcha_img()
<div>{:captcha_img()}</div>

(4.控制器中验证结果

方法一:Validate验证器的使用, 
$rule = [ 
   'captcha|验证码'=>'require|captcha'
]

方法二:调用内置的验证模块(captcha_check)
if(!captcha_check($captcha)){
 // 验证失败
};
4.download下载函数(??)

支持文件下载功能,可以更简单的读取文件进行下载操作,支持直接下载输出内容。

public function download()
{
    // download是系统封装的一个助手函数
    return download('hello/image.jpg', 'my.jpg');
    // 访问这个控制器就会下载当前文件,名称为my.jpg  (主要是返回二进制文件)
     
    $data = '这是一个测试文件';
    return download($data, 'test.txt', true);
    // 内容下载的参数,如果需要直接下载内容,可以在第三个参数传入true:
     
    return download('image.jpg', 'my.jpg')->force(false);
    // 支持设置是否强制下载,例如需要打开图像文件而不是浏览器下载的话
    
}

5.filesystem文件上传(??)

(1.配置文件保存设置

在config/filesystem.php配置以下内容(默认是这样的)

<?php

return [
    // 默认磁盘
    'default' => env('filesystem.driver', 'local'),
    // 磁盘列表
    'disks'   => [
        'local'  => [
            'type' => 'local',
            'root' => app()->getRuntimePath() . 'storage',
        ],
        'public' => [
            // 磁盘类型
            'type'       => 'local',
            // 磁盘路径
            'root'       => app()->getRootPath() . 'public/storage',
            // 磁盘路径对应的外部URL路径
            'url'        => '/storage',
            // 可见性
            'visibility' => 'public',
        ],
        // 更多的磁盘配置信息
    ],
];

(2.在控制器中使用

use think\facade\Filesystem

public function upload(){
    // 获取表单上传文件 例如上传了001.jpg
    $file = request()->file('image');
    // 上传到本地服务器
    // 直接引用,则需要在引入的命名空间添加 一个"\"
    // $savename = \think\facade\Filesystem::putFile( 'topic', $file);
    
    // 方法一:保存的位置不能被外部访问
    $savename = Filesystem::putFile( 'topic', $file);
    // 保存的结果: runtime/storage/topic/20160510/42a79759f284b767dfcb2a0197904287.jpg
    
	// 方法二:保存的位置能被外部访问
	//(第3个参数可以设置为 “md5”,"date","sha1", 也可以指定文件名,“abc.jpg”)
	$savename = Filesystem::disk('public')->putFile( 'topic', $file);
	// 保存的结果:runtime/storage/topic/72/ef580909368d824e899f77c7c98388.jpg
}

(3.验证文件大小(略)

支持使用验证类对上传文件的验证,包括文件大小、文件类型和后缀:

public function upload(){
    // 获取表单上传文件
    $files = request()->file();
    try {
        validate(['image'=>'fileSize:10240|fileExt:jpg|image:200,200,jpg'])
            ->check($files);
        $savename = [];
        foreach($files as $file) {
            $savename[] = \think\facade\Filesystem::putFile( 'topic', $file);
        }
    } catch (\think\exception\ValidateException $e) {
        echo $e->getMessage();
    }
}
验证参数	 说明
fileSize	上传文件的最大字节
fileExt	    文件后缀,多个用逗号分割或者数组
fileMime	文件MIME类型,多个用逗号分割或者数组
image	    验证图像文件的尺寸和类型
6.xx()助手函数

助手函数	             描述
abort	                中断执行并发送HTTP状态码
app	                    快速获取容器中的实例 支持依赖注入
bind	                快速绑定对象实例
cache	                缓存管理
class_basename	        获取类名(不包含命名空间)
class_uses_recursive	获取一个类里所有用到的trait
config	                获取和设置配置参数
cookie	                Cookie管理
download	            获取\think\response\File对象实例
dump	                浏览器友好的变量输出
env	                    获取环境变量
event	                触发事件
halt	                变量调试输出并中断执行
input	                获取输入数据 支持默认值和过滤
invoke	                调用反射执行callable 支持依赖注入
json	                JSON数据输出
jsonp	                JSONP数据输出
lang	                获取语言变量值
parse_name	            字符串命名风格转换
redirect	            重定向输出
request	                获取当前Request对象
response            	实例化Response对象
session	                Session管理
token	                生成表单令牌输出
trace	                记录日志信息
trait_uses_recursive	获取一个trait里所有引用到的trait
url	                    Url生成
validate	            实例化验证器
view	                渲染模板输出
display	                渲染内容输出
xml	                    XML数据输出
app_path	            当前应用目录
base_path	            应用基础目录
config_path	            应用配置目录
public_path	            web根目录
root_path	            应用根目录
runtime_path	        应用运行时目录
13.TP6和Laravel8比较
1.TP6的优势
  1. 轻量化框架,可扩展模块

  2. 开放式的文件配置

  3. 多应用模式

2.Laravel的优雅
  1. 集成化大型框架,功能完善。
  2. 安全性高(TP6容易有csrf攻击)
  3. 开发更加快捷 ,错误的时候有明确的提示。
3.安全漏洞
  1. mysql插入数据的时候
  2. 在 页面中直接渲染 html字符串

解决方法:

  1. 使用mysql 预编译查询(大量应用,会有性能问题)
  2. 采用内容安全策略(CSP)
  3. 在服务器也可以采取referer,进行限制

预编译查询:

​ 大部分数据库引擎支持某种形式的预编译语句,使你仅做一次预编译,然后在新数据集上多次查询。它消除了 SQL 注入的可能性,因为数据是以另一种形式传给数据库而非查询语句。 当你需要多次执行相同查询时,它也相当快速。然而,若你想应用于所有查询,这会极大影响性能,因为它通常要访问数据库两次。 由于查询构造器和数据库连接已经处理了转义数据,所以,安全方面已经为你解决了,但有时候,你也需要通过预编译语句或预编译查询来优化查询。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值