PHP框架设计之 ThinkPHP5 源码解析

ThinkPHP是一个快速、兼容而且简单的轻量级国产PHP开发框架,也是国内使用最为广泛的国产框架。诞生于2006年,历经FCS0.6.0到 ThinkPHP 0.9.5,历经1.0到3.0的发展。2015年发布ThinkPHP5.0版本。采用全新的架构思想,引入了更多的PHP新特性,优化了核心,减少了依赖,实现了真正的惰性加载,支持composer,并针对API开发做了大量的优化,包括路由、日志、异常、模型、数据库、模板引擎和验证等模块都已经重构。兼容PHP最新版本。这使得他成为框架的首选。下面也就其框架对一定的解析。

运行机制及流程

在这里插入图片描述

1.入口文件index.php

独立模式

define('APP_PATH', __DIR__ . '/../application/');
define('APP_DEBUG', true);require __DIR__ . '/../thinkphp/start.php';

composer模式

define('APP_PATH', __DIR__ . '/../application/');
 
define('APP_DEBUG', true);if (is_file(__DIR__ . '/../vendor/autoload.php')) {    require_once __DIR__ . '/../vendor/autoload.php';
} else {    require __DIR__ . '/../thinkphp/start.php';
}

2.框架引导文件start.php

namespace think;
 
// ThinkPHP 引导文件
// 加载基础文件
require __DIR__ . '/base.php';
// 执行应用
App::run()->send();

3.全局变量文件base.php

define('THINK_VERSION', '5.0.2');
//TP版本号define('THINK_START_TIME', microtime(true));
//启动时间define('THINK_START_MEM', memory_get_usage());
//获取分配给 PHP 的内存量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");
 }
 }
}
 
// 注册自动加载
\think\Loader::register();
 
// 注册错误和异常处理机制
\think\Error::register();
 
// 加载惯例配置文件
\think\Config::set(include THINK_PATH . 'convention' . EXT);

4.自动加载器文件Loader.php

Loader.php是框架的类的自动加载实现文件。可以使用autoload()自动挡加载类,import()手动加载指定目录的类。

protected static $map = []; 系统的类名映射缓存数组,在下面的addMap()使用

protected static $load = []; 系统的加载列表缓存数组,在下面的autoload()使用

protected static $namespace = []; 系统的命名空间缓存数组,在下面的addNamespace()使用

private static $prefixLengthsPsr4 = [];private static $prefixDirsPsr4    = [];

系统的PSR-4缓存数组,在下面的registerComposerLoader()使用

private static $prefixesPsr0 = []; 系统的PSR-0缓存数组,在下面的registerComposerLoader()使用。

上面四个静态变量用作加载器的缓存数组,保证加载效率。

1 autoload($class)

根据类名自动加载。

public static function autoload($class){}

使用类库映射加载对应的类。

if (isset(self::$map[$class])) {    if (is_file(self::$map[$class])) {
        APP_DEBUG && self::$load[] = self::$map[$class];        include self::$map[$class];
    }
}

使用composer加载类

elseif ($file = self::findFileInComposer($class)) {
    APP_DEBUG && self::$load[] = $file;    include $file;
}

根据命名空间自动加载类

else {    if (!strpos($class, '\\')) {        return;
    }    list($name, $class) = explode('\\', $class, 2);   
    if (isset(self::$namespace[$name])) {        $path = self::$namespace[$name];
    } elseif (is_dir(EXTEND_PATH . $name)) {        $path = EXTEND_PATH . $name . DS;
    } else {        return;
    }    $filename = $path . str_replace('\\', DS, $class) . EXT;   
    if (is_file($filename)) {        if (APP_DEBUG && IS_WIN && false === strpos(realpath($filename), $class . EXT)) {            return;
        }
 
        APP_DEBUG && self::$load[] = $filename;        include $filename;
    } else {
        Log::record('autoloader error : ' . $filename, 'notice');
    }
}

2 addMap(class,map='')

注册类名映射

public static function addMap(class,map = ''){} 数组合并到map,字符串关联到class.

if (is_array($class)) {    self::$map = array_merge(self::$map, $class);
} else {    self::$map[$class] = $map;
}

3 addNamespace(namespace,path)

注册命名空间和路径的关联

public static function addNamespace(namespace,path = ''){} 数组合并到$namespace,字符串关联到path.

if (is_array($namespace)) {    self::$namespace = array_merge(self::$namespace, $namespace);
} else {    self::$namespace[$namespace] = $path;
}

4 register($autoload='')

注册自动加载机制

public static function register($autoload = ''){} think\Loader::autoload()系统自动加载

self::registerComposerLoader() composer自动加载

spl_autoload_register($autoload ? $autoload : 'think\\Loader::autoload');self::registerComposerLoader();

5 composer自动加载的注册

private static function registerComposerLoader(){} 关于composer自动加载机制见 基础原理的php的composer自动加载

6 composer自动加载的两个私有方法

private static function composerRequire($fileIdentifier, $file){}private static function findFileInComposer($class, $ext = '.php'){}

7 import(class,baseUrl='',$ext=EXT)

手动加载baseUrl下的class命名的$ext后缀文件

public static function import(class,baseUrl = '', ext = EXT){} 首先分析baseUrl,

然后查找baserUrl.class.$ext 文件名,然后加载对应文件

点关注,不迷路

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,192评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,858评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,517评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,148评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,162评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,905评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,537评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,439评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,956评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,083评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,218评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,899评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,565评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,093评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,201评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,539评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,215评论 2 358

推荐阅读更多精彩内容