Skip to content

设置基础目录路径

给容器变量$instance设置基础路径

php
public function setBasePath($basePath)
{
    $this->basePath = rtrim($basePath, '\/');

    $this->bindPathsInContainer();

    return $this;
}

查看bindPathsInContainer()方法

调用bindPathsInContainer()方法

php
protected function bindPathsInContainer()
{
    // 将基础路径绑定到容器变量instance中
    $this->instance('path', $this->path());
    $this->instance('path.base', $this->basePath());
    $this->instance('path.lang', $this->langPath());
    $this->instance('path.config', $this->configPath());
    $this->instance('path.public', $this->publicPath());
    $this->instance('path.storage', $this->storagePath());
    $this->instance('path.database', $this->databasePath());
    $this->instance('path.resources', $this->resourcePath());
    $this->instance('path.bootstrap', $this->bootstrapPath());
}

查看instance()方法

php
public function instance($abstract, $instance)
{
    // 如果已经绑定了该键,则先移除该键的抽象别名
    $this->removeAbstractAlias($abstract);

    $isBound = $this->bound($abstract);

    unset($this->aliases[$abstract]);

    // 将实例绑定到容器变量中
    $this->instances[$abstract] = $instance;

    // 如果已经绑定了该键,则触发rebound事件
    if ($isBound) {
        $this->rebound($abstract);
    }

    return $instance;
}
查看removeAbstractAlias() 方法

检测abstractAliases成员变量中是否包含$searched这个键,如果包含则删除该键的别名

php
protected function removeAbstractAlias($searched)
{
    // 如果aliases中包含该键,则删除该键的别名
    if (! isset($this->aliases[$searched])) {
        return;
    }
    // Remove the alias from the list of the aliases.
    foreach ($this->abstractAliases as $abstract => $aliases) {
        foreach ($aliases as $index => $alias) {
            if ($alias == $searched) {
                unset($this->abstractAliases[$abstract][$index]);
            }
        }
    }
}
查看bound()方法

如果已经绑定了该键,则返回true

php
public function bound($abstract)
{
    return isset($this->bindings[$abstract]) ||
            isset($this->instances[$abstract]) ||
            $this->isAlias($abstract);
}

提示

第一次进来都是未绑定状态

app()方法实现

在代码中,有几个辅助函数可以快速获取容器中的服务,例如

php
if (! function_exists('config_path')) {
    /**
     * Get the configuration path.
     *
     * @param  string  $path
     * @return string
     */
    function config_path($path = '')
    {
        return app()->configPath($path);
    }
}

看一下app()辅助方法的实现

php
function app($abstract = null, array $parameters = [])
{
    // 为空时返回自身实例
    if (is_null($abstract)) {
        return Container::getInstance();
    }
    // 如果是字符串,则返回容器中的实例
    return Container::getInstance()->make($abstract, $parameters);
}

了解Container类

提示

对于容器类,还需要很多更详细的分析和了解,另外起一篇,这里就不详细分析了

Container::getInstance()方法

此处是一个单例模式,如果实例不存在,则创建一个Container实例,并返回

php
public static function getInstance()
{
    if (is_null(static::$instance)) {
        // 延迟静态绑定
        static::$instance = new static;
    }

    return static::$instance;
}

new static 使用static关键字返回时会返回调用者自身实例

延迟静态绑定

官网参考地址: 延迟静态绑定是一种特殊的绑定,它会在第一次访问时才绑定实例,并且只绑定一个实例,后续访问将返回相同的实例。

Container::make()方法

继续看传值时调用make()方法,getInstance()返回自身实例,在Container类中,找到make()方法

php
public function make($abstract, array $parameters = [])
{
    // 从container中获取实例
    return $this->resolve($abstract, $parameters);
}
resolve()方法

resolve()方法是Laravel框架容器方法make的核心实现

php
protected function resolve($abstract, $parameters = [], $raiseEvents = true)
{
    // 获取别名对应的键,如果没有返回自身
    $abstract = $this->getAlias($abstract);

    // 使用make方法解析对象时,恒调用这个方法
    // 触发执行绑定在解析对象上的回调事件
    if ($raiseEvents) {
        $this->fireBeforeResolvingCallbacks($abstract, $parameters);
    }
    // 获取具体的类名
    $concrete = $this->getContextualConcrete($abstract);
    // 判断是否需要使用上下文绑定
    $needsContextualBuild = ! empty($parameters) || ! is_null($concrete);

    // 如果已经绑定,从容器中取出实例并返回
    if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
        return $this->instances[$abstract];
    }

    // 将传递进来的参数保存在数组with中
    $this->with[] = $parameters;

    // 
    if (is_null($concrete)) {
        $concrete = $this->getConcrete($abstract);
    }

    // 如果$concrete === $abstract 或者 $concrete 是一个匿名函数,则返回ture
    if ($this->isBuildable($concrete, $abstract)) {
        // 如果$concrete是一个匿名函数,则返回匿名函数的执行结果,如果是类,则利用反射实例化一个$abstract对象
        $object = $this->build($concrete);
    } else {
        // 如果isBuildable返回false,调用make进入递归,make 再去 getConcrete 函数,去上下文绑定数组和 binding 数组
        // 查询这个时候这个・类路径下・(就是 abstruct)有没有对应的闭包或类路径。
        // 但不管怎么样。最后下来要么闭包,要么相等,他都会进入 build 函数创建对象。
        $object = $this->make($concrete);
    }

    // If we defined any extenders for this type, we'll need to spin through them
    // and apply them to the object being built. This allows for the extension
    // of services, such as changing configuration or decorating the object.
    foreach ($this->getExtenders($abstract) as $extender) {
        $object = $extender($object, $this);
    }

    // If the requested type is registered as a singleton we'll want to cache off
    // the instances in "memory" so we can return it later without creating an
    // entirely new instance of an object on each subsequent request for it.
    // 接下来,看是否是单例分享的,如果是的话就存入instance:
    if ($this->isShared($abstract) && ! $needsContextualBuild) {
        $this->instances[$abstract] = $object;
    }

    // 触发回调函数
    if ($raiseEvents) {
        $this->fireResolvingCallbacks($abstract, $object);
    }

    // 标记为已解析放在resolved中
    $this->resolved[$abstract] = true;
    // 将with数组中最后一个元素弹出
    array_pop($this->with);

    // 返回对象
    return $object;
}
getAlias()方法

获取别名对应的键

php
public function getAlias($abstract)
{
    /**
     * $this->aliases的键值对格式
     * ’Illuminate\Contracts\Filesystem\Factory‘ => ’filesystem‘
     * "Illuminate\Auth\AuthManager"=> "auth"
     */
    // 如果在aliases中存在,则返回别名对应的值
    return isset($this->aliases[$abstract])
                ? $this->getAlias($this->aliases[$abstract])
                : $abstract;
}
raiseEvents()方法

当使用make()方法时,$raiseEvents参数值为true,一定会执行fireBeforeResolvingCallbacks方法,并将$abstract $parameters两个参数值传过去

php
protected function fireBeforeResolvingCallbacks($abstract, $parameters = [])
{
    $this->fireBeforeCallbackArray($abstract, $parameters, $this->globalBeforeResolvingCallbacks);

    foreach ($this->beforeResolvingCallbacks as $type => $callbacks) {
        if ($type === $abstract || is_subclass_of($abstract, $type)) {
            $this->fireBeforeCallbackArray($abstract, $parameters, $callbacks);
        }
    }
}