## 入口文件 index.php ```php // [ 应用入口文件 ] namespace think; // 加载基础文件 require __DIR__ . '/../thinkphp/base.php'; // 支持事先使用静态方法设置Request对象和Config对象 // 执行应用并响应 Container::get('app')->run()->send(); ``` ### 一、加载基础文件 ```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 类 ##### 1、调用 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、注册错误和异常处理机制 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、注册类库别名 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) ### 二、执行应用并响应 ```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; } ```