據說 TP5 已經 RC4 了,曾經在 RC3 的時候用它寫過一個小東西。官方說從 RC4 之後改動不是太大。索性讀一下它的源碼。而後順便記錄一下,若有錯漏,請路過大神多多指正!php
做爲單入口框架,就從入口文件看起,按照tp5文檔所示的規範,入口文件應該是放在public/ 下。html
那麼爲何大多數要把入口放到子文件夾下呢?這是一個小技巧。java
第一爲了動靜分離,由於如今的php框架通常都是單入口,既然是單入口,那麼必然要作rewrite,若是把靜態文件和程序文件放到一塊兒。框架路由勢必要對每個請求進行篩選。因此這些框架不約而同的把資源文件和程序文件區分開來,放在了不一樣的文件夾下。從總體來看,也就是爲何入口會在子目錄下了。node
第二是爲了安全,linux下的權限劃分很是嚴格,分別分爲讀、寫、執行,在這個基礎上又分爲文件全部組、所在組、其餘組。這樣劃分能夠更好的對文件權限進行梳理,避免上傳漏洞(用戶上傳php文件被執行)等等。linux
打開public/index.phpgit
define('APP_PATH', __DIR__ . '/../application/'); // 加載框架引導文件 require __DIR__ . '/../thinkphp/start.php';
只有兩行代碼,定義 APP_PATH
,加載 '/../thinkphp/start.php'
。APP_PATH 能夠本身修改。thinkphp
而後打開 /../thinkphp/start.php
數據庫
namespace think; // ThinkPHP 引導文件 // 加載基礎文件 require __DIR__ . '/base.php'; // 執行應用 App::run()->send();
也只有三行代碼,定義命名空間,加載基礎文件,啓動應用。這裏注意一下命名空間,全部thinkphp類都在think及其子命名空間下。程序中用到框架類的時候要先use 該類的命名空間;npm
而後咱們打開base.php
12-31行定義了一坨常量。注意裏面 defined('THINK_PATH') or define('THINK_PATH', __DIR__ . DS);
這種定義方式,先判斷時候存在,若是不存在則定義。也就是說咱們能夠在這行代碼以前(通常在index.php中)執行定義這個常量,而不會被覆蓋。php框架
36-37行
// 載入Loader類 require CORE_PATH . 'Loader.php';
載入Loader類,這個類比較重要,實現了自動加載。
39-51行
// 加載環境變量配置文件 if (is_file(ROOT_PATH . 'env' . EXT)) { $env = include ROOT_PATH . 'env' . EXT; foreach ($env as $key => $val) { $name = ENV_PREFIX . strtoupper($key); if (is_bool($val)) { $val = $val ? 1 : 0; } elseif (!is_scalar($val)) { continue; } putenv("$name=$val"); } }
加載環境變量配置文件,可能不少同窗不理解是幹什麼用的。
咱們假設一個場景,你在公司和家裏開發程序,在內網服務器上進行測試,在外網服務器上部署,全部的配置不能可能所有相同(好比數據庫賬號密碼、文件路徑等等)。總不能每次都改配置吧?若是作負載、有幾十個服務器怎麼部署?總不能都用ftp上傳,而後改配置吧?
因此如今主流的作法就是區分環境(開發環境、測試環境、生產環境),而後程序自動加載不一樣的配置。可是經過什麼區分呢?方法有不少,可是大多數都是選擇經過環境變量來區分,而後加載對應的配置文件。而後使用 git 作版本控制,而後在服務器部署同步腳本,經過 git push鉤子進行代碼同步,以達到自動化部署的模式。固然也還有其餘方式,可是大多都相似。
爲何要使用自動加載呢?由於像java、C等編譯型語言在編譯過程當中會把程序中引用的庫、包等等自動引入進來。可是php是腳本行語言啊,沒有編譯過程,怎麼辦呢?最先期的程序都是手動引入,好比早期的xxshop、xxcms,都是寫一坨require、include。又搓又不方便,對於世界上最好的語言來講這樣多丟面啊,因此咱們須要用自動加載讓咱們最好的語言看起來更有B格(至於某些性能論的同窗會說自動加載影響性能啊之類的,請用匯編!)。
咱們繼續看base.php 的 54行\think\Loader::register();
註冊自動加載,從這一行以後就可使用符合自動加載規範的任何類了。
好比56-60行,雖然沒有加載對應的文件,可是經過自動加載就能夠直接使用。
// 註冊錯誤和異常處理機制 \think\Error::register(); // 加載慣例配置文件 \think\Config::set(include THINK_PATH . 'convention' . EXT);
接下來咱們看一下自動加載的實現方法。打開Loader.php
,按照上面的執行順序,先看Loader類的register方法
核心是
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)); }
spl_autoload_register方法可能不少同窗都有了解,在咱們實例化一個當前已加載文件中不存在的類後(好比在a.php中new一個類,會先在a.php和已加載的文件中找),會執行此方法指定的函數,並把類名傳遞進去。在這個函數中若是能正確加載到該文件,那麼也能夠實例化成功,並不會報錯。因此藉助此函數能夠達到自動加載。
首先咱們知道當 new 一個不存在的類時,若是使用spl_autoload_register
定義了一個處理函數,那麼這個函數能夠得到一個參數,參數名是new 的類名。好比從前面base.php中咱們看到 \think\Error::register();
使用think命名空間下的Error類的register靜態方法,可是咱們並無引入這個文件。因而咱們能夠在spl_autoload_register
註冊的函數中獲得一個參數thinkError,若是咱們的命名空間按照文件夾格式的方法命名(這也是推薦的、經常使用的命名方式),那麼就能夠經過該參數來加載對應的文件,可是若是特殊狀況下沒有按照文件夾的格式來進行命名空間的命名,那麼就須要手動指定映射關係。self::addClassMap(__include_file(RUNTIME_PATH . 'classmap' . EXT));
加載了映射文件。而後咱們看spl_autoload_register
中指定的函數:autoload。
這個不用詳細解釋了,先處理由 addNamespace 設定的命名空間別名,而後經過 findFile 來處理映射關係,獲得真實的路徑,並加載文件。
而__autoload()
函數具備相似的功能。可是爲何用的不多呢?由於 __autoload()
只能指定一個函數,而spl_autoload_register
能夠註冊多個函數來處理這個邏輯。一旦業務複雜 __autoload()
就徹底不能勝任。好比咱們繼續看Loader類的register方法下面的內容
// Composer自動加載支持 if (is_dir(VENDOR_PATH . 'composer')) { self::registerComposerLoader(); } // 自動加載extend目錄 self::$fallbackDirsPsr4[] = rtrim(EXTEND_PATH, DS);
一個框架是否好用,很大程度取決於它的擴展能力。因此自動加載除了要處理自身類庫的加載、還要處理擴展類庫的自動加載。tp5支持使用兩種方式來擴展類庫一種是Composer,一種是手動放入 extend 目錄。
Composer不用多說,就像npm之於nodejs、yum之於Centos、apt-get之於、Ubuntu。一個php的包管理工具。Composer有一套本身的自動加載機制,tp5這裏只不過是調用了Composer本身的註冊自動加載函數的方法。有興趣的同窗能夠看一下registerComposerLoader
方法,以及vendor/composer
下幾個autoload開頭的文件。原理基本上和上面的一致。
Loader.php中核心方法已經介紹完畢,其餘的幾個方法有處理規範的方法(PSR-0,PSR-2,PSR-4,有興趣的同窗能夠谷歌、百度瞭解一下)、和根據類名處理模型和控制器的方法,這點須要看完路由才能詳解。