Laravel 源码阅读 -容器

在学习过Larave的基本使用之后,会用只是简单的第一步,了解其背后的机制更为重要。Laravel 的核心就是一个 IoC 容器,框架应用程序的实例就是一个超大的容器。这个容器是一个什么样的原理呢?下面就去读一读文档~

TIP

源码版本: const VERSION = '5.4.12';

Laravel的入口文件是public/index.php,第一步先注册auto loader, 这样我们就可以在composer的帮助下非常简单的调用这种类了,比如说:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
1

就可以加载NotFoundHttpException这个类了,而不需要require等加载文件的方式加载类,非常便于管理,而且IDE友好。

1. Laravel 容器实例化

入口文件的第二步就是实例化我们的容器:

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__.'/../bootstrap/app.php';
1
2
3
4
5
6
7
8
9
10
11
12
13

注释写的非常好,”Turn On The Lights“。

bootstrap/app.php中实例化一个Application,然后在这个实例上绑定一些重要的接口(HTTP,Console和Exception),最后返回这个实例($app)到入口文件。Application实例化如下:

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
 */

$app = new Illuminate\Foundation\Application(
	realpath(__DIR__ . '/../')
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这个Application类继承自Container

class Application extends Container implements ApplicationContract, HttpKernelInterface
{
    /**
     * The Laravel framework version.
     *
     * @var string
     */
    const VERSION = '5.4.12';
1
2
3
4
5
6
7
8

本次的主角出场了,Container。laravel提供了很多服务,包括认证,数据库,缓存,消息队列等等,$app作为一个容器管理工具,负责几乎所有服务组件的实例化以及实例的生命周期管理。这种方式能够很好地对代码进行解耦,使得应用程序的业务代码不必操心服务组件的对象从何而来,当需要一个服务类来完成某个功能的时候,仅需要通过容器解析出该类型的一个实例即可。从最终的使用方式来看,laravel容器对服务实例的管理主要包括以下几个方面:

  • 服务的绑定与解析
  • 服务提供者的管理
  • 别名的作用
  • 依赖注入

source: http://www.cnblogs.com/lyzg/p/6181055.html

作为实例的管理容器,那么容器应该有一个数组用来存储这些容器,下面就来找出这个实例数组。

1.1 定义一个静态属性

<?php

namespace Illuminate\Container;
// ...

class Container implements ArrayAccess, ContainerContract
{
    /**
     * The current globally available container (if any).
     *
     * @var static
     */
    protected static $instance;
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14

1.2 静态方法创建实例

	/**
     * Set the globally available instance of the container.
     *
     * @return static
     */
    public static function getInstance()
    {
        if (is_null(static::$instance)) {
            static::$instance = new static;
        }

        return static::$instance;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13

在静态方法中调用静态属性,如果静态属性被定义的话直接返回,如果没有则新建一个实例。新建实例的方法是 new static, 通过new static实例化返回的就是调用时的类的实例,new self返回的是方法所在的类的实例,区别如下:

// self refers to the same class whose method the new operation takes place in.
// static in PHP 5.3's late static bindings refers to whatever class in the hierarchy which you call the method on.

class A {
	public static function get_self() {
		return new self;
	}

	public static function get_static() {
		return new static;
	}
}

class B extends A {}

echo get_class(B::get_self()); // A
echo get_class(B::get_static()); // B
echo get_class(A::get_static()); // A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

new self后面的可以加(),也可以省略。

1.3 构造函数

到上一步单例模式远远没有完成,因为外部还是可以直接new出一个新的实例来,而不是通过getInstance()方法。Laravel是如何设置Application的构造函数的呢?

 /**
     * Create a new Illuminate application instance.
     *
     * @param  string|null  $basePath
     * @return void
     */
    public function __construct($basePath = null)
    {
        if ($basePath) {
            $this->setBasePath($basePath);
        }

        $this->registerBaseBindings();

        $this->registerBaseServiceProviders();

        $this->registerCoreContainerAliases();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

查看Application的构造函数,竟然是public的方法?!不应该是private吗?好,接着往下看。

1.3.1 setBasePath()

   /**
     * Set the base path for the application.
     *
     * @param  string  $basePath
     * @return $this
     */
    public function setBasePath($basePath)
    {
        $this->basePath = rtrim($basePath, '\/');

        $this->bindPathsInContainer();

        return $this;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

看看bindPathsInContainer做了些什么:

/**
 * Bind all of the application paths in the container.
 *
 * @return void
 */
protected function bindPathsInContainer()
{
    $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());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

instance()方法呢?instance()方法继承自Illuminate\Container\Container,在这里对instance进行注册,上面的每一个应用的的路径都绑定到容器里了。

//  Illuminate\Container\Container
/**
* Register an existing instance as shared in the container.
*
* @param  string  $abstract
* @param  mixed   $instance
* @return void
*/
public function instance($abstract, $instance)
{
	$this->removeAbstractAlias($abstract);

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

	// We'll check to determine if this type has been bound before, and if it has
	// we will fire the rebound callbacks registered with the container and it
	// can be updated with consuming classes that have gotten resolved here.
	$this->instances[$abstract] = $instance;

	if ($this->bound($abstract)) {
		$this->rebound($abstract);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

貌似找到了,这里的instances也是个私有变量,而且是个数组类型:

/**
 * The container's shared instances.
 *
 * @var array
 */
protected $instances = [];
1
2
3
4
5
6

总结来说呢,Container不愧叫容器,里面存储着很多的单例示例,在Container中存在着大量的对instances中的instance的操作

参考

laravel 学习笔记 —— 神奇的服务容器: https://www.insp.top/learn-laravel-container

Laravel核心——Ioc服务容器: https://segmentfault.com/a/1190000009369477#articleHeader25

最近更新: 12/9/2018, 4:44:42 PM