thinkphp5.1 入口文件一二事

## 入口文件 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;
}
```

 

你可能感兴趣的:(thinkphp5.1 入口文件一二事)