[TOC]
* * * * *
# 1 自動加載
>>自動加載,在php中主要包括命名空間到目錄的定位,類名到文件的定位。
>tp5的自動加載實現在Loader.php中。
## 1 啟動自動加載
### Loader::register()
>>啟動自動加載
~~~
public static function register($autoload = '')
{
// 注冊系統自動加載
spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);
// 注冊命名空間定義
self::addNamespace([
'think' => LIB_PATH . 'think' . DS,
'behavior' => LIB_PATH . 'behavior' . DS,
'traits' => LIB_PATH . 'traits' . DS,
]);
// 加載類庫映射文件
if (is_file(RUNTIME_PATH . 'classmap' . EXT)) {
self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
}
// Composer自動加載支持
if (is_dir(VENDOR_PATH . 'composer')) {
self::registerComposerLoader();
}
// 自動加載extend目錄
self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
}
~~~
>啟動自動加載,主要通過調用spl_autoload_register()注冊自動函數函數。
>php在運行過程中遇到查找失敗的類,將會自動調用上面注冊的自動加載函數
## 2 自動加載函數
### Loader::autoload()
>>tp5內置自動加載函數
~~~
public static function autoload($class)
{
// 檢測命名空間別名
if (!empty(self::$namespaceAlias)) {
$namespace = dirname($class);
if (isset(self::$namespaceAlias[$namespace])) {
$original = self::$namespaceAlias[$namespace] . '\\' . basename($class);
if (class_exists($original)) {
return class_alias($original, $class, false);
}
}
}
if ($file = self::findFile($class)) {
// Win環境嚴格區分大小寫
if (IS_WIN && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
return false;
}
__include_file($file);
return true;
}
}
~~~
>autoload()自動加載函數,將會查找需要類的文件所在目錄,然后調用__include_file()加載其內容。
### Loader::registerComposerLoader()
>>啟動composer自動加載,tp5在注冊自動加載時,
> 檢測VENDOR目錄下的composer目錄是否存在,如果存在則啟動composer自動加載
~~~
private static function registerComposerLoader()
{
if (is_file(VENDOR_PATH . 'composer/autoload_namespaces.php')) {
$map = require VENDOR_PATH . 'composer/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
self::addPsr0($namespace, $path);
}
}
if (is_file(VENDOR_PATH . 'composer/autoload_psr4.php')) {
$map = require VENDOR_PATH . 'composer/autoload_psr4.php';
foreach ($map as $namespace => $path) {
self::addPsr4($namespace, $path);
}
}
if (is_file(VENDOR_PATH . 'composer/autoload_classmap.php')) {
$classMap = require VENDOR_PATH . 'composer/autoload_classmap.php';
if ($classMap) {
self::addClassMap($classMap);
}
}
if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
$includeFiles = require VENDOR_PATH . 'composer/autoload_files.php';
foreach ($includeFiles as $fileIdentifier => $file) {
if (empty(self::$autoloadFiles[$fileIdentifier])) {
__require_file($file);
self::$autoloadFiles[$fileIdentifier] = true;
}
}
}
}
~~~
# 2 加載規則注冊
>>加載規則注冊 包括命令空間與目錄關系注冊,與類名與文件關系注冊。
## 2-1 命令空間到目錄注冊
### Loader::addNamespace()
>>注冊命名空間到目錄關系
~~~
public static function addNamespace($namespace, $path = '')
{
if (is_array($namespace)) {
foreach ($namespace as $prefix => $paths) {
self::addPsr4($prefix . '\\', rtrim($paths, DS), true);
}
} else {
self::addPsr4($namespace . '\\', rtrim($path, DS), true);
}
}
~~~
### Loader::addNamespaceAlias()
>>注冊命名空間別名
~~~
public static function addNamespaceAlias($namespace, $original = '')
{
if (is_array($namespace)) {
self::$namespaceAlias = array_merge(self::$namespaceAlias, $namespace);
} else {
self::$namespaceAlias[$namespace] = $original;
}
}
~~~
### Loader::addPsr4()
>>注冊Psr4規范命名空間與目錄關系
~~~
private static function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
self::$fallbackDirsPsr4 = array_merge(
(array) $paths,
self::$fallbackDirsPsr4
);
} else {
self::$fallbackDirsPsr4 = array_merge(
self::$fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset(self::$prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
self::$prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
self::$prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
self::$prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
self::$prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
self::$prefixDirsPsr4[$prefix] = array_merge(
self::$prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
~~~
### Loader::addPsr0()
>>注冊Psr0規范命名空間與目錄關系
~~~
private static function addPsr0($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
self::$fallbackDirsPsr0 = array_merge(
(array) $paths,
self::$fallbackDirsPsr0
);
} else {
self::$fallbackDirsPsr0 = array_merge(
self::$fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset(self::$prefixesPsr0[$first][$prefix])) {
self::$prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
self::$prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
self::$prefixesPsr0[$first][$prefix]
);
} else {
self::$prefixesPsr0[$first][$prefix] = array_merge(
self::$prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
~~~
## 2-2 類名到文件注冊
>>注冊類名到文件關系
~~~
public static function addClassMap($class, $map = '')
{
if (is_array($class)) {
self::$map = array_merge(self::$map, $class);
} else {
self::$map[$class] = $map;
}
}
~~~
# 3 加載文件查找
>>自動加載器,可以根據注冊的自動加載機制,實現目標文件所在目錄的定位,然后加載所需文件。
## 3-1 文件自動查找
>> 文件自動查找,根據上面注冊的命令空間到目錄,類名到文件的關系,查找命名空間下的類所在的文件,然后加載
### Loader::findFile()
~~~
private static function findFile($class)
{
if (!empty(self::$map[$class])) {
// 類庫映射
return self::$map[$class];
}
// 查找 PSR-4
$logicalPathPsr4 = strtr($class, '\\', DS) . EXT;
$first = $class[0];
if (isset(self::$prefixLengthsPsr4[$first])) {
foreach (self::$prefixLengthsPsr4[$first] as $prefix => $length) {
if (0 === strpos($class, $prefix)) {
foreach (self::$prefixDirsPsr4[$prefix] as $dir) {
if (is_file($file = $dir . DS . substr($logicalPathPsr4, $length))) {
return $file;
}
}
}
}
}
// 查找 PSR-4 fallback dirs
foreach (self::$fallbackDirsPsr4 as $dir) {
if (is_file($file = $dir . DS . $logicalPathPsr4)) {
return $file;
}
}
// 查找 PSR-0
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DS);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DS) . EXT;
}
if (isset(self::$prefixesPsr0[$first])) {
foreach (self::$prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (is_file($file = $dir . DS . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// 查找 PSR-0 fallback dirs
foreach (self::$fallbackDirsPsr0 as $dir) {
if (is_file($file = $dir . DS . $logicalPathPsr0)) {
return $file;
}
}
return self::$map[$class] = false;
}
~~~
>如果存在類庫映射,則直接加載類對應的文件。
>然后查找命名空間對應的目錄,類名對應的文件關系等
## 3-2 加載所需類庫
>>tp5封裝了類Java的import的加載實現
### Loader::import()
>>加載所需類庫,可以傳入目標目錄與文件后綴
~~~
public static function import($class, $baseUrl = '', $ext = EXT)
{
static $_file = [];
$key = $class . $baseUrl;
$class = str_replace(['.', '#'], [DS, '.'], $class);
if (isset($_file[$key])) {
return true;
}
if (empty($baseUrl)) {
list($name, $class) = explode(DS, $class, 2);
if (isset(self::$prefixDirsPsr4[$name . '\\'])) {
// 注冊的命名空間
$baseUrl = self::$prefixDirsPsr4[$name . '\\'];
} elseif ('@' == $name) {
//加載當前模塊應用類庫
$baseUrl = App::$modulePath;
} elseif (is_dir(EXTEND_PATH . $name)) {
$baseUrl = EXTEND_PATH;
} else {
// 加載其它模塊的類庫
$baseUrl = APP_PATH . $name . DS;
}
} elseif (substr($baseUrl, -1) != DS) {
$baseUrl .= DS;
}
// 如果類存在 則導入類庫文件
if (is_array($baseUrl)) {
foreach ($baseUrl as $path) {
$filename = $path . DS . $class . $ext;
if (is_file($filename)) {
break;
}
}
} else {
$filename = $baseUrl . $class . $ext;
}
if (!empty($filename) && is_file($filename)) {
// 開啟調試模式Win環境嚴格區分大小寫
if (IS_WIN && pathinfo($filename, PATHINFO_FILENAME) != pathinfo(realpath($filename), PATHINFO_FILENAME)) {
return false;
}
__include_file($filename);
$_file[$key] = true;
return true;
}
return false;
}
~~~
## 3-3 自動實例化對象
>>自動加載器(Loader)還可以自動實例化對象
### Loader::controller()
### Loader::model()
### Loader::validate()
### Loader::db()
### Loader::action()
## 3-4 其他方法
>>加載器還實現了其他有助于解析類名的方法和清空實例化對象的方法。
### Loader::parseName()
>>解析操作名
~~~
public static function parseName($name, $type = 0)
{
if ($type) {
return ucfirst(preg_replace_callback('/_([a-zA-Z])/', function ($match) {
return strtoupper($match[1]);
}, $name));
} else {
return strtolower(trim(preg_replace("/[A-Z]/", "_\\0", $name), "_"));
}
}
~~~
### Loader::parseClass()
>>解析類名
~~~
public static function parseClass($module, $layer, $name, $appendSuffix = false)
{
$name = str_replace(['/', '.'], '\\', $name);
$array = explode('\\', $name);
$class = self::parseName(array_pop($array), 1) . (App::$suffix || $appendSuffix ? ucfirst($layer) : '');
$path = $array ? implode('\\', $array) . '\\' : '';
return App::$namespace . '\\' . ($module ? $module . '\\' : '') . $layer . '\\' . $path . $class;
}
~~~
### Loader::clearInstance()
>>清空所有實例化的對象
~~~
public static function clearInstance()
{
self::$instance = [];
}
~~~
- 框架簡介
- 簡介
- 框架目錄
- 根目錄
- 應用目錄
- 核心目錄
- 擴展目錄
- 其他目錄
- 框架流程
- 啟動流程
- 請求流程
- 響應流程
- 框架結構
- 應用組織
- 網絡請求
- 路由組織
- 數據驗證
- 數據模型(M)
- 數據庫連接(Connection)
- 數據庫(Db)
- 查詢構造(Builder)
- 數據庫查詢(Query)
- 模型(Model)
- 模板視圖(V)
- 視圖(View)
- 模板引擎(Think)
- 模板標簽庫(TagLib)
- 控制器(C)
- 網絡響應
- 配置與緩存
- 配置操作
- 緩存操作
- cookie與session
- Cookie操作
- Session操作
- 自動加載
- 鉤子注冊
- 文件上傳
- 分頁控制
- 控制臺
- 自動構建
- 日志異常調試
- 異常處理
- 代碼調試
- 日志記錄
- 框架使用
- 1 環境搭建(Server)
- 2 網絡請求(Request)
- 3 請求路由(Route)
- 4 響應輸出(Response)
- 5 業務處理(Controller)
- 6 數據存取(Model)
- 7 Web界面(View)