概述
基于最新ThinkPHP5.0.8制定,原搞是xmind思维导图制作,如果觉得图片看不太清楚,可以下载xmind源文件,用xmind软件打开。
另外,思维导图中使用的源码,全部在导图备注中,只有下载了xmind源文件,才可以看到。
想自学ThinkPHP5开发的同学,一定不要错过这个福利。正在从事ThinkPHP5项目开发的程序员,可以收藏,以备查询
一、思维导图:
二、框架架构整体描述文字版:
框架架构
1 架构总览
1.1 基于MVC设计模式
1.1.1 Model:模型
1.1.2 View:视图
1.1.3 Controller:控制器
1.2 基于MVC的URL访问路由
1.2.1 http://域名/入口文件/模块/控制器/操作/参数/值...
1.3 入口文件
1.3.1 最常见:index.php
1.3.2 可以为模块绑定其它入口文件,如后:admin.php
1.4 应用App
1.4.1 管理框架生命周期的对象:thinkApp类
1.4.2 由入口文件调用并执行
1.4.3 具有相同的APP_PATH应用目录的,视为同一个应用
1.4.4 一个应用可以有多个入口,例如前台为index.php,后台为admin.php
1.4.5 应用有自己独立的配置文件与公共函数文件
1.5 模块Module
1.5.1 一个应用通常是由多个模块组成
1.5.2 一个模块通常是应用目录中一个子目录: app/index/
1.5.3 模块通常由多个控制器类文件组成,可以将这些类以目录方式管理
1.5.4 简单应用,可不创建模块目录,使用单模块架构,在应用配置中关闭多模块支持: 'app_multi_module'=>false
1.5.5 模块也可有自己独立的配置文件和公共文件、类库文件
1.6 控制器Controller
1.6.1 控制器负责响应用户的请求,调用模型处理,选择视图输出,不应介入业务处理
1.6.2 每个控制器其实就是一个类文件: Index.php
1.6.3 5.0开始,控制器不用继承任何父类就可以正常工作
<?php
namespace appindexcontroller; class Index {
public function index()
{
return 'hello,thinkphp!';
}
}
登录后复制
1.7 操作Action
1.7.1 一个控制器包括多个操作方法,操作方法是URL访问的最小单元
1.7.2 操作方法如果要求参数,则必须传入:$_GET或$_POST
<?php
namespace appindexcontroller; class Index {
public function index() {
return 'index'; } public function hello($name) {
return 'Hello,'.$name; }
}
登录后复制
1.8 模型Model
1.8.1 完成实际的业务逻辑和数据封装,并返回与格式 无关的数据
1.8.2 模型支持分层设计:逻辑层/服务层/事件层
1.8.3 模型类不一定要访问数据库,只有CURD操作时才连接,是真正的惰性连接
1.9 视图View
1.9.1 控制器调用模型类返回的数据,要通视图类包装成不同的格式返回。
1.9.2 可根据需求确定是直接渲染还是用模板输出
1.9.3 视图有一系列的模板文件与控制器中的操作对应
1.9.4 可以在操作方法中动态设置模板目录
1.10 命名空间NameSpace
1.10.1 命名空间主要用在框架的类库文件中
1.10.2 命名空间必须符合PSR-4的自动加载机制:空间名与类的路径对应
2 生命周期
2.1 入口文件index.php
2.1.1 位置: public/index.php
2.1.2 入口文件中通常只定义常量和加载框架引导文件
<?php
// 应用入口文件
// 定义项目路径
define('APP_PATH', __DIR__ . '/../application/');
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';
登录后复制
2.2 引导文件:start.php
<?php
namespace think;
// ThinkPHP 引导文件
// 加载基础文件
require __DIR__ . '/base.php';
// 执行应用
App::run()->send();
登录后复制
2.2.1 加载base.php
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2017 http://thinkuoften.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
define('THINK_VERSION', '5.0.5');
define('THINK_START_TIME', microtime(true));
define('THINK_START_MEM', memory_get_usage());
define('EXT', '.php');
define('DS', DIRECTORY_SEPARATOR);
defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS);
define('LIB_PATH', THINK_PATH . 'library' . DS);
define('CORE_PATH', LIB_PATH . 'think' . DS);
define('TRAIT_PATH', LIB_PATH . 'traits' . DS);
defined('APP_PATH') or define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']) . DS);
defined('ROOT_PATH') or define('ROOT_PATH', dirname(realpath(APP_PATH)) . DS);
defined('EXTEND_PATH') or define('EXTEND_PATH', ROOT_PATH . 'extend' . DS);
defined('VENDOR_PATH') or define('VENDOR_PATH', ROOT_PATH . 'vendor' . DS);
defined('RUNTIME_PATH') or define('RUNTIME_PATH', ROOT_PATH . 'runtime' . DS);
defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH . 'log' . DS);
defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH . 'cache' . DS);
defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH . 'temp' . DS);
defined('CONF_PATH') or define('CONF_PATH', APP_PATH); // 配置文件目录
defined('CONF_EXT') or define('CONF_EXT', EXT); // 配置文件后缀
defined('ENV_PREFIX') or define('ENV_PREFIX', 'PHP_'); // 环境变量的配置前缀
// 环境常量
define('IS_CLI', PHP_SAPI == 'cli' ? true : false);
define('IS_WIN', strpos(PHP_OS, 'WIN') !== false);
// 载入Loader类
require CORE_PATH . 'Loader.php';
// 加载环境变量配置文件
if (is_file(ROOT_PATH . '.env')) {
$env = parse_ini_file(ROOT_PATH . '.env', true);
foreach ($env as $key => $val) {
$name = ENV_PREFIX . strtoupper($key);
if (is_array($val)) {
foreach ($val as $k => $v) {
$item = $name . '_' . strtoupper($k);
putenv("$item=$v");
}
} else {
putenv("$name=$val");
}
}
}
// 注册自动加载
thinkLoader::register();
// 注册错误和异常处理机制
thinkError::register();
// 加载惯例配置文件
thinkConfig::set(include THINK_PATH . 'convention' . EXT);
登录后复制
2.2.1.1 加载系统常量定义
2.2.1.2 加载环境变量定义文件
2.2.1.3 注册自动加载机制
2.2.1.3.1 调用Loader::register(),加载符合规范类库及第三方类库
2.2.1.3.2 自动加载的内容
2.2.1.3.2.1 注册系统的自动加载方法 thinkLoader::autoload
2.2.1.3.2.2 注册系统命名空间定义
2.2.1.3.2.3 加载类库映射文件(如果存在)
2.2.1.3.2.4 如果存在 Composer 安装,则注册 Composer 自动加载
2.2.1.3.2.5 注册 extend 扩展目录
2.2.1.3.3 类库自动加载的检测顺序
2.2.1.3.3.1 是否定义类库映射
2.2.1.3.3.2 PSR-4 自动加载检测
2.2.1.3.3.3 PSR-0 自动加载检测
2.2.1.4 注册错误和异常处理机制
2.2.1.4.1 执行 Error::register() 注册,由三部分组成
2.2.1.4.1.1 应用关闭方法: thinkError::appShutdown
2.2.1.4.1.2 错误处理方法: thinkError::appError
2.2.1.4.1.3 异常处理方法: thinkError::appException
2.2.1.5 加载惯例配置文件
2.2.1.5.1 thinkConfig::set(include THINK_PATH . 'convention' . EXT);
2.2.2 执行应用:App::run()->send();
2.2.2.1 App::run()输入Request对象,返回Response对象
2.2.2.2 Response::send():发送数据到客户端
2.3 应用初始化:App::initCommon()和init()方法
/**
* 初始化应用
*/
public static function initCommon()
{
if (empty(self::$init)) {
// 初始化应用
$config = self::init();
self::$suffix = $config['class_suffix'];
// 应用调试模式
self::$debug = Env::get('app_debug', Config::get('app_debug'));
if (!self::$debug) {
ini_set('display_errors', 'Off');
} elseif (!IS_CLI) {
//重新申请一块比较大的buffer
if (ob_get_level() > 0) {
$output = ob_get_clean();
}
ob_start();
if (!empty($output)) {
echo $output;
}
}
// 注册应用命名空间
self::$namespace = $config['app_namespace'];
Loader::addNamespace($config['app_namespace'], APP_PATH);
if (!empty($config['root_namespace'])) {
Loader::addNamespace($config['root_namespace']);
}
// 加载额外文件
if (!empty($config['extra_file_list'])) {
foreach ($config['extra_file_list'] as $file) {
$file = strpos($file, '.') ? $file : APP_PATH . $file . EXT;
if (is_file($file) && !isset(self::$file[$file])) {
include $file;
self::$file[$file] = true;
}
}
}
// 设置系统时区
date_default_timezone_set($config['default_timezone']);
// 监听app_init
Hook::listen('app_init');
self::$init = true;
}
return Config::get();
}
/**
* 初始化应用或模块
* @access public
* @param string $module 模块名
* @return array
*/
private static function init($module = '')
{
// 定位模块目录
$module = $module ? $module . DS : '';
// 加载初始化文件
if (is_file(APP_PATH . $module . 'init' . EXT)) {
include APP_PATH . $module . 'init' . EXT;
} elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) {
include RUNTIME_PATH . $module . 'init' . EXT;
} else {
$path = APP_PATH . $module;
// 加载模块配置
$config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT);
// 读取数据库配置文件
$filename = CONF_PATH . $module . 'database' . CONF_EXT;
Config::load($filename, 'database');
// 读取扩展配置文件
if (is_dir(CONF_PATH . $module . 'extra')) {
$dir = CONF_PATH . $module . 'extra';
$files = scandir($dir);
foreach ($files as $file) {
if (strpos($file, CONF_EXT)) {
$filename = $dir . DS . $file;
Config::load($filename, pathinfo($file, PATHINFO_FILENAME));
}
}
}
// 加载应用状态配置
if ($config['app_status']) {
$config = Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT);
}
// 加载行为扩展文件
if (is_file(CONF_PATH . $module . 'tags' . EXT)) {
Hook::import(include CONF_PATH . $module . 'tags' . EXT);
}
// 加载公共文件
if (is_file($path . 'common' . EXT)) {
include $path . 'common' . EXT;
}
// 加载当前模块语言包
if ($module) {
Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT);
}
}
return Config::get();
}
登录后复制
2.3.1.1 定位模块目录
2.3.1.2 加载初始化文件
2.3.1.2.1 加载模块配置
2.3.1.2.2 读取数据库配置文件
2.3.1.2.3 读取扩展配置文件
2.3.1.2.4 加载应用状态配置
2.3.1.2.5 加载行为扩展文件
2.3.1.2.6 加载公共文件
2.3.1.2.7 加载当前模块语言包
2.3.2 检测应用调试模式:self::$debug = Env::get('app_debug', Config::get('app_debug'));
2.3.3 注册应用命名空间:self::$namespace = $config['app_namespace'];
2.3.4 加载额外文件:'extra_file_list'
2.3.5 设置系统时区:date_default_timezone_set($config['default_timezone']);
2.3.6 监听app_init: Hook::listen('app_init');
2.3.7 返回所有配置项:return Config::get();
2.4 URL访问检测:
2.4.1 PATH_INFO:http://serverName/index.php/index/index/hello/val/value
2.4.2 兼容方式:http://serverName/index.php?s=/index/index/hello&val=value
2.5 路由检测:App::routeCheck($request, array $config)与设置路由机制:route($route, $must = false)
/**
* URL路由检测(根据PATH_INFO)
* @access public
* @param thinkRequest $request
* @param array $config
* @return array
* @throws thinkException
*/
public static function routeCheck($request, array $config)
{
$path = $request->path();
$depr = $config['pathinfo_depr'];
$result = false;
// 路由检测
$check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
if ($check) {
// 开启路由
if (is_file(RUNTIME_PATH . 'route.php')) {
// 读取路由缓存
$rules = include RUNTIME_PATH . 'route.php';
if (is_array($rules)) {
Route::rules($rules);
}
} else {
$files = $config['route_config_file'];
foreach ($files as $file) {
if (is_file(CONF_PATH . $file . CONF_EXT)) {
// 导入路由配置
$rules = include CONF_PATH . $file . CONF_EXT;
if (is_array($rules)) {
Route::import($rules);
}
}
}
}
// 路由检测(根据路由定义返回不同的URL调度)
$result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
$must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
if ($must && false === $result) {
// 路由无效
throw new RouteNotFoundException();
}
}
if (false === $result) {
// 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
$result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
}
return $result;
}
/**
* 设置应用的路由检测机制
* @access public
* @param bool $route 是否需要检测路由
* @param bool $must 是否强制检测路由
* @return void
*/
public static function route($route, $must = false)
{
self::$routeCheck = $route;
self::$routeMust = $must;
}
}
登录后复制
2.5.1 路由到模块/控制器/操作
2.5.2 路由到外部重定向地址;
2.5.3 路由到控制器方法;
2.5.4 路由到闭包函数;
2.5.5 路由到类的方法;
2.6 请求分发与响应输出:Response::send()
2.6.1 控制器的所有操作方法都是 return 返回而不是直接输出
2.6.2 自动转换成 default_return_type 参数配置的格式
2.7 应用结束,写入日志
2.7.1 系统的日志包括用户调试输出的和系统自动生成的日志,统一会在应用结束的时候进行写入操作
2.7.2 日志的写入操作受日志初始化的影响
3 入口文件
3.1 采用单一入口模式进行项目部署(并非唯一入口)
3.2 不同应用对应不同入口,但入口文件内容和功能基本一致
3.3 入口文件内容
<?php
// 定义应用目录
define('APP_PATH', __DIR__ . '/../application/');
// 加载框架引导文件
require __DIR__ . '/../thinkphp/start.php';
登录后复制
3.3.1 定义应用目录:define('APP_PATH', __DIR__ . '/../application/');
3.3.2 定义系统常量:define('CONF_PATH', __DIR__ . '/../config/');
3.3.3 加载框架引导文件: require __DIR__ . '/../thinkphp/start.php';
3.4 入口文件位于public目录下
3.4.1 这是为了让应用部署更安全
3.4.2 public必须是Web可访问目录
3.4.3 其实文件或目录应该放在非Web访问目录下面
4 URL访问
4.1 URL设计
4.1.1 PATH_INFO:http://index.php/模块/控制器/操作/[参数名/参数值...]
4.1.2 兼容模型:http://index.php?s=/模块/控制器/操作/[参数名/参数值...]
4.1.3 不支持普通URL模式,但传参可以:http://index.php/module/controller/action?id=10
4.1.4 URL默认不区分大小写,都会转为小写的,控制器部分自动转为驼峰法处理
4.2 隐藏入口文件
4.2.1 隐藏入口文件,可优化URL,网站更安全
4.2.2 在入口文件同级:public/.htaccess
4.2.3 Apache配置文件httpd.conf加载:mod_rewrite.so模块,支持URL重写
4.2.4 httpd.conf:AllowOverride None 将None设置为 All
5 模块设计
5.1 默认为多模块,支持单一模块设计
5.2 模块命名空间均为app为根空间
5.3 模块可以看作是类库的集合:控制器类,模型类
5.4 模块类库:app模块名类库类名
5.5 入口文件中隐藏模块和控制器
由于默认是采用多模块的支持,所以多个模块的情况下必须在URL地址中标识当前模块,如果只有一个模块的 话,可以进行模块绑定,方法是应用的入口文件中添加如下代码:
// 绑定当前访问到index模块 define('BIND_MODULE','index');
绑定后,我们的URL访问地址则变成:
http://serverName/index.php/控制器/操作/[参数名/参数值...]
访问的模块是 index 模块。 如果你的应用比较简单,模块和控制器都只有一个,那么可以在应用公共文件中绑定模块和控制器,如下:
// 绑定当前访问到index模块的index控制器 define('BIND_MODULE','index/index');
设置后,我们的URL访问地址则变成:
http://serverName/index.php/操作/[参数名/参数值...]
访问的模块是 index 模块,控制器是 Index 控制器。
5.5.1 绑定当前访问的模块:define('BIND_MODULE','index');
5.5.2 绑定当前访问的模块和控制器:define('BIND_MODULE','userlogin/getname');
5.6 单一模块设计:'app_multi_module'=>false,
5.6.1 可以把应用目录当作模块目录
5.6.2 模块中的控制器命名空间也要调整
6 命名空间
6.1 命名空间的路径与类库文件的目录一致,可以实现类的自动加载(惰性加载)
6.2 根命名空间:类库包
6.2.1 think:系统核心类库 (think/library/think)
6.2.2 traits:系统trait类库(think/library/traits)
6.2.3 app:应用类库(application)
6.2.4 自定义根命名空间
6.2.4.1 默认加载EXTEND_APTH目录中的类库,目录名为根
我们只需要把自己的类库包目录放入 EXTEND_PATH 目录(默认为 extend ,可配置),就可以自动注册对 应的命名空间,例如:
我们在 extend 目录下面新增一个 my 目录,然后定义一个 myHello 类( 类文件位于 extend/my/Hello.php )如下:
<?php
namespace my;
class Hello
{
public function index()
{
return 'hello tp5';
}
}
登录后复制
我们就可以在控制器中,直接实例化和调用:
<?php
namespace appindexcontroller;
class Index
{
public function index()
{
$obj = new myHello();
return $obj->index();
}
}
登录后复制
6.2.4.2 可在入口文件中重新定义:define('EXTEND_PATH','../vendor/');
6.2.4.3 手动注册根命名空间
6.2.4.3.1 应用公共文件:common.php中添加如代码:
在应用公共文件中添加下面的代码:
thinkLoader::addNamespace('my','../application/extend/my/');
登录后复制
如果要同时注册多个根命名空间,可以使用:
thinkLoader::addNamespace([ 'my' => '../application/extend/my/', 'org' => '../application/extend/org/', ]);
登录后复制
6.2.4.3.2 应用配置文件:config.php中添加:
可以直接在应用的配置文件中添加配置,系统会在应用执行的时候自动注册。
'root_namespace' => [ 'my' => '../application/extend/my/', 'org' => '../application/extend/org/', ]
登录后复制
6.3 可以给命名空间创建别名
6.3.1 应用公共文件common.php
7 trait引入
7.1 trait提供了一种代码复用机制,是类的公共方法集,与继承相比,相当于横向扩展了类的功能
7.2 PHP5.4使用load_trait()引入,PHP5.5以上可以直接自动加载
但由于PHP5.4版本不支持 trait 的自动加载,因此如果是PHP5.4版本,必须手动导入 trait 类库,系统 提供了一个助手函数 load_trait ,用于自动加载 trait 类库,例如,可以这样正确引入 trait 类库。
namespace appindexcontroller; load_trait('controller/Jump'); class index {
// 引入traitscontrollerJump
use traitscontrollerJump; public function index() {
$this->assign('name','value');
$this->show('index'); }
}
登录后复制
如果你的PHP版本大于 5.5 的话,则可以省略 load_trait 函数引入 trait 。
namespace appindexcontroller; class index {
use traitscontrollerJump;
public function index()
{
}
}
登录后复制
可以支持同时引入多个 trait 类库,例如:
namespace appindexcontroller; load_trait('controller/Other'); load_trait('controller/Jump'); class index
{
use traitscontrollerOther; use traitscontrollerJump; public function index() { }
}
登录后复制
或者使用
namespace appindexcontroller; load_trait('controller/Other'); load_trait('controller/Jump'); class index {
use traitscontrollerOther,traitscontrollerJump;
public function index()
{
}
}
登录后复制
7.3 trait命名冲突的解决方案
7.3.1 insteadof:冲突时指定使用哪一个trait类
7.3.2 as:将另一个冲突的trait类以别名的方式访问
8 API友好
8.1 数据输出
8.1.1 控制器中数据输出统一用Response类处理,并不直接输出
8.1.2 设置 default_return_type 或者动态设置不同类型的 Response 输出就可以自动进行数据转换
8.1.3 大多数情况下,你只需要在控制器中返回字符串或者数组即可
8.1.4 默认为html,可在配置文件中:'default_return_type'=>'json'
8.2 错误调试
8.2.1 Trace 调试功能支持 Socket 在内的方 式,可以实现远程的开发调试
三、ThinkPHP5框架思维导图下载地址:
2框架架构.xmind.zip
【相关推荐】
1. 图解ThinkPHP5框架(一):基础知识,开发规范与目录结构
2. 图解ThinkPHP5框架(三):配置类Config.php源码解读
3. 2017年最新的10个thinkphp视频教程推荐
最后
以上就是单身板凳为你收集整理的图解ThinkPHP5框架(二):应用运行流程与生命周期的全部内容,希望文章能够帮你解决图解ThinkPHP5框架(二):应用运行流程与生命周期所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复