## 入口文件 index.php ```php // [ 應用入口文件 ] namespace think; // 加載基礎文件 require __DIR__ . '/../thinkphp/base.php'; // 支持事先使用靜態方法設置Request對象和Config對象 // 執行應用並響應 Container::get('app')->run()->send(); ``` ### 1、加載基礎文件 ```php require __DIR__ . '/../thinkphp/base.php'; ``` base.php ```php namespace think; // 載入Loader類 require __DIR__ . '/library/think/Loader.php'; // 註冊自動加載 Loader::register(); // 註冊錯誤和異常處理機制 Error::register(); // 實現日誌接口 if (interface_exists('Psr\Log\LoggerInterface')) { interface LoggerInterface extends \Psr\Log\LoggerInterface {} } else { interface LoggerInterface {} } // 註冊類庫別名 Loader::addClassAlias([ 'App' => facade\App::class, 'Build' => facade\Build::class, 'Cache' => facade\Cache::class, 'Config' => facade\Config::class, 'Cookie' => facade\Cookie::class, 'Db' => Db::class, 'Debug' => facade\Debug::class, 'Env' => facade\Env::class, 'Facade' => Facade::class, 'Hook' => facade\Hook::class, 'Lang' => facade\Lang::class, 'Log' => facade\Log::class, 'Request' => facade\Request::class, 'Response' => facade\Response::class, 'Route' => facade\Route::class, 'Session' => facade\Session::class, 'Url' => facade\Url::class, 'Validate' => facade\Validate::class, 'View' => facade\View::class, ]); ``` #### 引入 think\Loader.php 類 ##### 一、調用 register 方法,註冊自動加載機制 ```php // 註冊自動加載機制 public static function register($autoload = '') { // 註冊系統自動加載 spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true); // 獲取應用根目錄末尾帶分隔符 $rootPath = self::getRootPath(); // 設置composer的目錄 self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR; // Composer自動加載支持 if (is_dir(self::$composerPath)) { if (is_file(self::$composerPath . 'autoload_static.php')) { require self::$composerPath . 'autoload_static.php'; // 返回全部定義過的類名組成的數組 $declaredClass = get_declared_classes(); // 即獲取引入的 autoload_static.php 的類名(包含命名空間) $composerClass = array_pop($declaredClass); // 把 autoload_static.php 類中的如下變量付給當前類 foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) { if (property_exists($composerClass, $attr)) { self::${$attr} = $composerClass::${$attr}; } } } else { self::registerComposerLoader(self::$composerPath); } } // 註冊命名空間定義 think,traits self::addNamespace([ 'think' => __DIR__, 'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits', ]); // 加載根目錄下runtime中的類庫映射文件 if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) { self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')); } // 自動加載extend目錄 self::addAutoLoadDir($rootPath . 'extend'); } ``` 1.1獲取應用根目錄 ```php public static function getRootPath() { // 獲取執行腳本的絕對路徑 if ('cli' == PHP_SAPI) { $scriptName = realpath($_SERVER['argv'][0]); } else { $scriptName = $_SERVER['SCRIPT_FILENAME']; } $path = realpath(dirname($scriptName)); if (!is_file($path . DIRECTORY_SEPARATOR . 'think')) { $path = dirname($path); } return $path . DIRECTORY_SEPARATOR; } ``` 1.2註冊命名空間 ```php public static function addNamespace($namespace, $path = '') { if (is_array($namespace)) { foreach ($namespace as $prefix => $paths) { self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true); } } else { self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true); } } ``` 1.3添加Psr4空間 ```php 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 ); } } ``` ![1571491243747](C:\Users\99477\AppData\Roaming\Typora\typora-user-images\1571491243747.png) 1.4註冊自動加載類庫目錄 ```php public static function addAutoLoadDir($path) { self::$fallbackDirsPsr4[] = $path; } ``` ![1571491420380](C:\Users\99477\AppData\Roaming\Typora\typora-user-images\1571491420380.png) #### 二、註冊錯誤和異常處理機制 2.1調用 Loader 類下的 autoload 方法 ```php // 自動加載 public static function autoload($class) { if (isset(self::$classAlias[$class])) { return class_alias(self::$classAlias[$class], $class); } if ($file = self::findFile($class)) { // Win環境嚴格區分大小寫 if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) { return false; } __include_file($file); return true; } } ``` 2.2查找文件 ```php private static function findFile($class) { if (!empty(self::$classMap[$class])) { // 類庫映射 return self::$classMap[$class]; } // 查找 PSR-4 $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . '.php'; $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 . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } } } } // 查找 PSR-4 fallback dirs foreach (self::$fallbackDirsPsr4 as $dir) { if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } // 查找 PSR-0 if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . '.php'; } 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 . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } } } } // 查找 PSR-0 fallback dirs foreach (self::$fallbackDirsPsr0 as $dir) { if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } return self::$classMap[$class] = false; } ``` 2.3執行自動引入 Error 類後執行 register 方法 ```php public static function register() { error_reporting(E_ALL); set_error_handler([__CLASS__, 'appError']); set_exception_handler([__CLASS__, 'appException']); register_shutdown_function([__CLASS__, 'appShutdown']); } ``` #### 三、註冊類庫別名 3.1註冊類別名 ```php public static function addClassAlias($alias, $class = null) { if (is_array($alias)) { self::$classAlias = array_merge(self::$classAlias, $alias); } else { self::$classAlias[$alias] = $class; } } ``` ![1571492267016](C:\Users\99477\AppData\Roaming\Typora\typora-user-images\1571492267016.png) ### 2、執行應用並響應 ```php Container::get('app')->run()->send(); ``` ###### 說明:Container::get('app') 獲取 think\App 對象,而後調用 run() 方法,通過一系列的處理返回 think\Response 對象,再調用 Response 對象中的 send() 方法返回輸出數據。 #### Container::get('app') 獲取 think\App 對象 1、獲取容器中的對象實例 ```php public static function get($abstract, $vars = [], $newInstance = false) { return static::getInstance()->make($abstract, $vars, $newInstance); } ``` 2、取當前容器的實例(單例) ```php public static function getInstance() { if (is_null(static::$instance)) { static::$instance = new static; } return static::$instance; } ``` ![1571495181084](C:\Users\99477\AppData\Roaming\Typora\typora-user-images\1571495181084.png) 3、建立類的實例 ```php public function make($abstract, $vars = [], $newInstance = false) { if (true === $vars) { // 老是建立新的實例化對象 $newInstance = true; $vars = []; } $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract; // 若是實例以存在且不須要從新獲取則直接返回 if (isset($this->instances[$abstract]) && !$newInstance) { return $this->instances[$abstract]; } // 是否已經有標識綁定 if (isset($this->bind[$abstract])) { $concrete = $this->bind[$abstract]; if ($concrete instanceof Closure) { // 若是是閉包直接執行 $object = $this->invokeFunction($concrete, $vars); } else { // 是類文件 $this->name[$abstract] = $concrete; return $this->make($concrete, $vars, $newInstance); } } else { $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { $this->instances[$abstract] = $object; } return $object; } ``` 4、調用反射執行類的實例化 支持依賴注入 ```php public function invokeClass($class, $vars = []) { try { $reflect = new ReflectionClass($class); // 在類中定義靜態的、共有的__make方法,自定義實例化 if ($reflect->hasMethod('__make')) { $method = new ReflectionMethod($class, '__make'); if ($method->isPublic() && $method->isStatic()) { $args = $this->bindParams($method, $vars); return $method->invokeArgs(null, $args); } } $constructor = $reflect->getConstructor(); // 獲取構造方法的參數 $args = $constructor ? $this->bindParams($constructor, $vars) : []; // 返回類的實例 return $reflect->newInstanceArgs($args); } catch (ReflectionException $e) { throw new ClassNotFoundException('class not exists: ' . $class, $class); } } ``` 5、綁定參數 ```php protected function bindParams($reflect, $vars = []) { if ($reflect->getNumberOfParameters() == 0) { return []; } // 判斷數組類型 數字數組時按順序綁定參數 1是索引數組 0是關聯數組或空 reset($vars); $type = key($vars) === 0 ? 1 : 0; $params = $reflect->getParameters(); foreach ($params as $param) { // 參數名 $name = $param->getName(); // 有大寫字母轉下劃線的形式 $lowerName = Loader::parseName($name); // 若是參數類型限制爲類類型 可獲取到 ReflectionClass object $class = $param->getClass(); if ($class) {//變量類型爲對象 $args[] = $this->getObjectParam($class->getName(), $vars); } elseif (1 == $type && !empty($vars)) {//索引數組 $args[] = array_shift($vars); } elseif (0 == $type && isset($vars[$name])) {//關聯數組 $args[] = $vars[$name]; } elseif (0 == $type && isset($vars[$lowerName])) { $args[] = $vars[$lowerName]; } elseif ($param->isDefaultValueAvailable()) {//默認值 $args[] = $param->getDefaultValue(); } else { throw new InvalidArgumentException('method param miss:' . $name); } } return $args; } ``` 6、獲取對象類型的參數值 ```php protected function getObjectParam($className, &$vars) { $array = $vars; $value = array_shift($array); if ($value instanceof $className) { $result = $value; array_shift($vars); } else { $result = $this->make($className); } return $result; } ```
Container:php