1 Star 1 Fork 1

Laravelchen/thinkphp5.1源码研读

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
陈依- 陈依 new 3a2003c 5年前
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

thinkphp5.1源ht码研读

介绍

版本:5.1.*

序言

主要新特性

  • 引入容器和facade支持
  • 依赖注入
  • 重构的对象化路由
  • 支持注解路由
  • 跨域请求支持
  • 配置和路由目录独立
  • 取消系统常量
  • 助手函数增强
  • 类库别名机制
  • 模型和数据库增强
  • 验证类增强
  • 模板引擎改进
  • 支持PSR-3日志规范
  • 中间件支持
  • 支持 swoole / workerman 运行

基础

  • 安装

    thinkphp5.1环境要求
    • php >= 5.6
    • pdo extension
    • MBstring extension
    composer 安装
    登录composer镜像官网查看具体版本号

    composer create-project topthink/think=5.1.49 tp5

    git 安装
  • 开发规范

    命名规范
    目录和文件
    • 目录使用小写 + 下划线
    • 类库和函数文件统计使用.php为后缀
    • 类的文件名均以命名空间定义,并且命名空间路径和类库文件所在路径一致
    • 类文件采用驼峰法命名(首字母大写),其他文件采用小写+下划线命名
    • 类名和文件名保持一致,类名采用驼峰法
    函数和类、属性命名
    • 类的命名采用驼峰法(首字母大写),默认不添加后缀。如UserTypeController直接命名UserType
    • 函数命名采用小写字母加下划线(小写字母开头)。例如get_content_type
    • 方法的命名采用驼峰法(首字母小写)。例如getUserName
    • 属性的命名采用驼峰法(首字母小写)。例如tableName
    • 魔术方法采用双下划线开头。例如__autoload
    常量和配置
    • 常量以大写字母和下划线命名。例如APP_PATH
    • 配置参数以小写字母和下划线命名。例如url_route_on
    • 环境变量定义使用大写字母和下划线。例如APP_DEBUG
    数据表和字段
    • 数据表和字段采用小写和下划线方式命名,并注意字段名不能以下划线开头。例如think_user表user_name字段
  • 目录结构

www  WEB部署目录(或者子目录)
├─application           应用目录
│  ├─common             公共模块目录(可以创建、删除)
│  ├─module_name        模块目录(index/admin/api)
│  │  ├─common.php      模块函数文件
│  │  ├─controller      控制器目录
│  │  ├─model           模型目录
│  │  ├─view            视图目录
│  │  ├─config          配置目录
│  │  └─ ...            更多类库目录
│  │
│  ├─command.php        命令行定义文件
│  ├─common.php         公共函数文件
│  └─tags.php           应用行为扩展定义文件
│  
├─config                应用配置目录
│  ├─module_name        模块配置目录(index/admin/api)
│  │  ├─database.php    数据库配置
│  │  ├─cache           缓存配置
│  │  └─ ...            
│  │
│  ├─app.php            应用配置
│  ├─cache.php          缓存配置
│  ├─cookie.php         Cookie配置
│  ├─database.php       数据库配置
│  ├─log.php            日志配置
│  ├─session.php        Session配置
│  ├─template.php       模板引擎配置
│  └─trace.php          Trace配置
│
├─route                 路由定义目录
│  ├─route.php          路由定义
│  └─...                更多
│
├─public                WEB目录(对外访问目录)
│  ├─index.php          入口文件
│  ├─router.php         快速测试文件
│  └─.htaccess          用于apache的重写
│
├─thinkphp              框架系统目录
│  ├─lang               语言文件目录
│  ├─library            框架类库目录
│  │  ├─think           Think类库包目录
│  │  └─traits          系统Trait目录
│  │
│  ├─tpl                系统模板目录
│  ├─base.php           基础定义文件
│  ├─convention.php     框架惯例配置文件
│  ├─helper.php         助手函数文件
│  └─logo.png           框架LOGO文件
│
├─extend                扩展类库目录
├─runtime               应用的运行时目录(可写,可定制)
├─vendor                第三方类库目录(Composer依赖库)
├─build.php             自动生成定义文件(参考)
├─composer.json         composer 定义文件
├─LICENSE.txt           授权说明文件
├─README.md             README 文件
├─think                 命令行入口文件
  • 配置

架构

  • 架构总览

    • url访问(没有开启强制路由)
http://serverName/index.php/模块/控制器/操作/参数/值 
  • 入口文件
    默认入口文件index.php,有时按照特殊需求可能要添加新的入口文件,例如给后台模块单独设计一个admin.php的入口文件
  • 应用
    应用在ThinkPHP中是一个管理系统架构和生命周期的对象,由系统的\think\App类完成,应用通常在入口文件中被调用和执行。具有相同的应用目录的应用我们认为是同一个应用,应用具有自己独立的配置文件、公共文件、路由文件。
  • 路由
    路由用于规划(一般也用于简化)请求的访问地址,在访问地址和实际操作之间建立一个路由规则=>路由地址 的映射关系。 在tp中,如果未定义路由,则可直接使用”模块/控制器/操作“的方式访问,如果定义了路由,这种方式就失效了。 使用路由有一定的性能损失,但随之也更加安全,因为每个路由都有自己的生效条件,如果不满足条件的请求就会被过滤。远比在控制器中进行各种判断要实用的多。 路由并不仅仅是url规范这么简单,还可以实现验证、权限、参数绑定、响应设置等功能。
  • 入口文件

  • URL访问

  • 模块设计

  • 命名空间

    • 命名空间
      thinPHP5 符合PRS-4自动加载规范,只需要给类库正确的命名空间,并且命名空间路径和类库文件目录一致,就可以实现类的自动加载,真正实现惰性加载。
    • 调用PHP内置类库或第三方没有使用命名空间的类库
// 错误的用法
$class = new stdClass();
$xml  =  new SimpleXmlElement($xmlstr);
// 正确的用法
$class = new \stdClass();
$xml  =  new \SimpleXmlElement($xmlstr);
  • 根命名空间(类库包)
    系统内置的几个根命名空间:
名称 描述 类库目录
think 系统核心类库 thinkphp/library/think
traits 系统Traits类库 thinkphp/library/traits
app 应用类库 application
  • 容器和依赖注入

  • Facade

  • 钩子和行为

  • 中间件

路由

  • 路由定义

    • 注册路由规则
      route目录下的任何路由文件都是有效的,默认路由文件是route.php,可以更改名字,或者添加多个路由定义文件。
├─route                 路由定义目录
│  ├─route.php          路由定义
│  ├─api.php            路由定义
│  └─...                更多路由定义

最基本的定义路由的方法

Route::rule('路由表达式','路由地址','请求类型');
// 注册路由到index模块的News控制器的read操作
Route::rule('new/:id','index/News/read');
//我们访问 http://serverName/new/5 会自动路由到
http://serverName/index/News/redad/id5
//指定请求类型
Route::rule('new/:id','News/update','POST');
//指定多种请求类型
Route::rule('new/:id','News/read','GET|POST');

reastful api请求类型

类型 描述 快捷方法
GET get
POST post
PUT put
DELETE delete
PATCH patch
* any

快速注册路由方法:

Route::快捷方法名('路由表达式','路由地址');
Route::get('new/:id','News/read'); // 定义GET请求路由规则
Route::post('new/:id','News/update'); // 定义POST请求路由规则
Route::put('new/:id','News/update'); // 定义PUT请求路由规则
Route::delete('new/:id','News/delete'); // 定义DELETE请求路由规则
Route::any('new/:id','News/read'); // 所有请求都支持的路由规则

注册多个路由规则后,系统会一次遍历注册过的满足请求类型的路由规则,一旦匹配到正确的路由规则则后续规则不在检测

  • 路由表达式
    • 规则表达式
      规则表达式通常包含静态地址和动态地址。每个参数中以:开头的参数都表示动态变量,并且会自动绑定到操作方法的对应参数。
Route::rule('/', 'index'); // 首页访问路由
Route::rule('my', 'Member/myinfo'); // 静态地址路由
Route::rule('blog/:id', 'Blog/read'); // 静态地址和动态地址结合
Route::rule('new/:year/:month/:day', 'News/read'); // 静态地址和动态地址结合
Route::rule(':user/:blog_id', 'Blog/read'); // 全动态地址
  • 可选定义
    支持对路由参数的可选定义。
Route::get('blog/:year/[:month]','Blog/archive');
//变量由[]包裹起来,表示该变量是由路由匹配的可选变量
//下面的url地址都可以被正确的路由匹配
http://serverName/index.php/blog/2015
http://serverName/index.php/blog/2015/12
//可选参数只能放到路由规则的最后,如果在中间使用了可选参数的话,后面的变量都会变成可选参数。
  • 完全匹配
    规则匹配检测的时候默认只是对URL从头开始匹配,只要URL地址包含了定义的路由规则就会匹配成功,如果希望URL进行完全匹配,可以在路由表达式最后使用$符号。
Route::get('new/:cate$', 'News/category');
//成功
http://serverName/index.php/new/info
//失败
http://serverName/index.php/new/info/2 
//两个URL都能成功
Route::get('new/:cate', 'News/category');
//如果需要全局进行URL完全匹配,可以在app.php中设置
'route_complete_match'   => true,
  • 额外参数
    在路由跳转的时候支持额外传入参数对(额外参数指的是不在URL里面的参数,隐式传入需要的操作中,有时候能够起到一定的安全防护作用,后面我们会提到)。例如:
Route::get('blog/:id','blog/read?status=1&app_id=5');
//上面的路由规则定义中额外参数的传值方式都是等效的。status和app_id参数都是URL里面不存在的,属于隐式传值,当然并不一定需要用到,只是在需要的时候可以使用。
  • 路由标识
    如果你需要快速的根据路由生成url,可以在定义路由的时候指定生成标识(但确保唯一性)。
// 注册路由到index模块的News控制器的read操作
Route::name('new_read')->rule('new/:id','index/News/read');
//生成路由的时候可以使用
url('new_read',['id'=>10]);
//如果不定义路由标识的话,可以通过下面生成
url('index/News/read',['id'=>10]);
//第一个列子,要修改一下
// 注册路由到index模块的News控制器的read操作
Route::rule('new/:id','index/News/read')->name('new_read');
  • 变量规则

    V5.1.14+版本开始,可以在app.php配置文件中自定义默认的变量规则:
    //支持在规则路由中为变量用正则的方式指定规则
 'default_route_pattern' =>	'[\w\-]+',
  • 局部变量规则
    局部变量规则,仅在当前路由有效
// 定义GET请求路由规则 并设置name变量规则
Route::get('new/:name', 'News/read')
      ->pattern(['name' => '\w+']);
//不需要开头添加^或者在最后添加$,也不支持模式修饰符,系统会自动添加。      
  • 全局变量规则
    全部路由有效
// 设置name变量规则(采用正则定义)
Route::pattern('name', '\w+');
// 支持批量添加
Route::pattern([
    'name' => '\w+',
    'id'   => '\d+',
]);    
  • 组合变量类型
    如果你的路由规则比较特殊,可以在路由定义的时候使用组合变量。
Route::get('item-<name>-<id>', 'product/detail')
    ->pattern(['name' => '\w+', 'id' => '\d+']);

  • 动态路由
//可以把路由规则中的变量传入路由地址中,就可以实现一个动态路由
//后面部分是路由地址
Route::get('hello/:name', 'index/:name/hello');
  • 路由地址

    • 路由地址
定义方式 定义格式
1.路由->模块控制器 '[模块/控制器/操作]?参数1=值1&参数2=值2'
2.路由->重定向地址 '外部地址(默认301重定向)' 或 ['外部地址','重定向代码']
3.路由->控制器的方法 '@[模块/控制器]操作'
4.路由->类的方法 '\完整的命名空间类::静态方法' 和 '\完整的命名空间类@动态方法'
5.路由->闭包函数 闭包函数定义
6.路由->Response对象 Response对象定义和设置
7.路由->模板文件 view 方法
  • 路由到模块/控制器/操作
    最常见的一种路由方式,把满足条件的路由规则 路由到相关的模块、控制器、操作,然后由App类调度执行相关操作。 同时会进行模块的初始化操作(配置读取、公共文件载入、行为定义载入、语言包载入等)。 路由地址的格式为:
    [模块/控制器/]操作?参数1=值1&参数2=值2... 解析规则是从操作开始解析,然后控制器,最后解析模块。
// 路由到默认或者绑定模块
//如果默认绑定的是index模块,那这里就不用写index,真实路由地址index/blog/read
Route::get('blog/:id','blog/read');
// 路由到index模块
Route::get('blog/:id','index/blog/read');

//路由地址中支持多级控制器,真实路由地址 index/controller/group/Blog 这里group可以是文件夹名,实际中很实用
Route::get('blog/:id','index/group.blog/read');

//还可以支持路由到动态的模块、控制器、操作,例如:
// action变量的值作为操作方法传入
Route::get(':action/blog/:id', 'index/blog/:action');
// 变量传入index模块的控制器和操作方法
Route::get(':c/:a', 'index/:c/:a');
  • 路由到操作方法
    路由地址格式为: @[模块/控制器/]操作 这种方式看起来和第一种是一样的,本质的区别是直接执行某个控制器类的方法,而不是去解析模块/控制器/操作,同时也不会去初始化模块(因此不会调用模块的初始化方法)。
//定义如下路由,相当于直接调用\app\index\blog类的read方法
Route::get('blog/:id','@index/blog/read');
//通常在这种方法下面,由于没有定义当前模块、控制器、方法名,从而导致视图的默认模板失效,所以如果使用了视图模板渲染,则必须传入明确的参数而不能为空id
  • 路由到类的方法
    路由地址格式: 动态方法:\类的命名空间\类名@方法名 静态方法:\类的命名空间\类名::方法名 这个方式更进一步,可以支持任何类的方法,而不仅仅执行控制器里的方法,如果不是app模块,可以使用此方法
//执行的是\app\index\service\Blog类的read方法
Route::get('blog/:id','\app\index\service\Blog@read');
Route::get('blog/:id','\app\index\service\Blog::read');
  • 路由到重定向地址
    重定向的外部地址必须以‘/’或http开头的地址
Route::get('blog/:id','/blog/read/id/:id');
Route::get('blog/:id','blog/read');
//这两种方式 都是路由到同一个地址,但前者采用的是301重定向的方式路由跳转,这种方式的好处是URL可以比较随意(里面可以传递更多非标准参数),而后者只是支持模块和操作地址。例如:我们希望avatar/123重定向到/member/avatar/id/123,只能使用
Route::get('avatar/:id','/member/avatar/id/:id');
//路由地址采用重定向的话,如果要引用动态变量,直接使用动态变量即可
//采用重定向到外部地址通常对网站改版后的URL迁移过程非常有用
Route::get('blog/:id','http://blog.thinkphp.cn/read/:id');
//表示当前网站(可能是http://thinkphp.cn )的 blog/123地址会直接重定向到 http://blog.thinkphp.cn/read/123。
//5.1.3版本之后,可以使用redirect方法注册一个重定向路由
Route::redirect('blog/:id','http://blog.thinkphp.cn/read/:id',302);
  • 路由到模板
    5.1.3版本后,支持路由直接渲染模板输出。
  //表示该路由会渲染到index模块下view/hello.html模板文件输出
  Route::view('hello/:name','index@hello');
  //模板文件中可以直接输出当前请求的param变量,如果需要增加额外的模板变量,可以使用
  Route::view('hello/:name','index@hello',['city'=>'shanghai']);
  Hello,{$name}--{$city}
  • 闭包支持

    • 闭包定义
      我们可以使用闭包的方式定义一些特殊需求的路由,而不需要执行控制器的操作方法了,例如:
Route::get('hello', function () {
    return 'hello,world!';
});
Route::get('hello/:name', function ($name) {
    return 'Hello,' . $name;
});
  • 依赖注入
Route::rule('hello/:name', function (Request $request, $name) {
    //这里一般写在controller里,首先要继承think\controller基类,再引入think\facade\request,并且使用$this->request
    //5.1版本使用$this->request->method() 5.0版本使用$request->method();
    //在这里,只要引入think\facade\request即可
    $method = $this->request->method();
    return '[' . $method . '] Hello,' . $name;
});
  • 指定响应对象
    更多的情况下,是要在路由闭包中指定响应对象输出。例如:
//当然也有引入 think\facade\Response 类库
Route::get('hello/:name', function (Response $response, $name) {
    return $this->response
        ->data('Hello,' . $name)
        ->code(200)
        ->contentType('text/plain');
});
//更多的情况是直接对资源文件的请求设置404访问
// 对于不存在的static目录下的资源文件设置404访问
Route::get('static', response()->code(404));
  • 路由参数

    • 具体路由参数
      路由分组及规则定义支持指定路由参数,这些参数主要用来完成路由匹配检测和行为执行。
参数 说明 方法名
method 请求类型检测,支持多个请求类型 method
ext url后缀检测,支持匹配多个后缀 ext
deny_ext URL禁止后缀检查,支持匹配多个后缀 denyExt
https 检查是否https请求 https
domain 域名检测 domain
before 前置行为检测 before
after 后置行为执行 after
merge_extra_vars 合并额外参数 mergeExtraVars
complete_match 是否完全匹配路由 completeMatch
model 绑定模型 model
cache 请求缓存 cache
param_depr 路由参数分隔符 paramDepr
ajax ajax检测 ajax
pjax pjax检测 pjax
response 绑定response_send行为 response
validate 绑定验证器类进行数据验证 validate
header 设置response的header信息 header
append 追加额外的参数 append
middleware 注册路由中间件 middleware
merge_rule_regex 合并路由规则 mergeRuleRegex
filter 请求变量过滤 filter
例如:
Route::get('new/:id','News/read',['ext'=>'html','https'=>true]);
//也可写成链式操作
Route::get('new/:id', 'News/read')
    ->ext('html')
    ->https();
/**
*URL后缀
*URL后缀如果是全局统一的话,可以在应用配置文件app.php中设置url_html_suffix参数
*如果当前访问的URL地址中的URL后缀是允许的伪静态后缀,那么后缀本身是不会被作为参数值传入的。
*   false  => 禁止伪静态访问
*   空字符串 => 允许任何伪静态后缀
*   html   => 只允许设置的伪静态后缀
*/
// 定义GET请求路由规则 并设置URL后缀为html的时候有效,就是要以.html结尾
Route::get('new/:id', 'News/read')
    ->ext('html');
//支持匹配多个后缀    
Route::get('new/:id', 'News/read')
    ->ext('shtml|html');
//可以禁止访问的URL后缀
Route::get('new/:id', 'News/read')
    ->denyExt('jpg|png|gif');  
/**
*域名检测
*/
// 完整域名检测 只在news.thinkphp.cn访问时路由有效
Route::get('new/:id', 'News/read')
    ->domain('news.thinkphp.cn');
// 子域名检测
Route::get('new/:id', 'News/read')
    ->domain('news');
/**
*HTTPS检测
*/
// 必须使用HTTPS访问
Route::get('new/:id', 'News/read')
    ->https();
    
// 必须使用HTTP访问    
Route::get('new/:id', 'News/read')
    ->https(false);    
/**
*前置行为检测
*支持使用行为对路由进行检测是否匹配,如果行为方法返回false表示当前路由规则无效。
*/    
Route::get('user/:id', 'index/User/read')
    ->before(['\app\index\behavior\UserCheck']);
/**
*路由绑定模型
*/
Route::get('hello/:id', 'index/index/hello')
    ->model('id', '\app\index\model\User');
/**
*缓存路由请求,保留3600秒
*/
Route::get('new/:name$', 'News/read')
    ->cache(3600);
/**
*设置header信息
*/
Route::get('new/:name$', 'News/read')
    ->header('Access-Control-Allow-Origin','*');
  • 路由中间件
    从5.1.6开始,可以使用路由中间件。
Route::rule('hello/:name','hello')
	->middleware('Auth');
//对路由分组进行注册中间件
Route::group('hello', function(){
	Route::rule('hello/:name','hello');
})->middleware('Auth');
  • 路由缓存

    在定义了大量的路由规则之后,开启该缓存可以明显提升路由解析性能,而且路由规则越多越明显。 在应用配置文件app.php中设置开启:

    'route_check_cache' => true,

    开启后,会自动对每次的路由请求的调度进行缓存,第二次如果是相同的请求则无需再次经过路由解析,而是直接进行请求调度。

  • 跨域请求

    如果某个路由或者分组需要支持跨域请求,可以使用

Route::get('new/:id', 'News/read')
    ->ext('html')
    ->allowCrossDomain();
  • 路由分组

    路由分组功能允许把相同前缀的路由定义合并分组。
Route::group('分组名(字符串)或者分组路由参数(数组)','分组路由规则(数组或者闭包)');
Route::group('blog', [
    ':id'   => 'Blog/read',
    ':name' => 'Blog/read',
])->ext('html')->pattern(['id' => '\d+']);

控制器

请求

响应

数据库

模型

视图

模板

错误和日志

调试

验证

杂项

命令行

扩展库

安全和性能

空文件

简介

取消

发行版

暂无发行版

贡献者

全部

近期动态

不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
PHP
1
https://gitee.com/suxiangmendi/thinkphp5_source_code_study.git
git@gitee.com:suxiangmendi/thinkphp5_source_code_study.git
suxiangmendi
thinkphp5_source_code_study
thinkphp5.1源码研读
master

搜索帮助