介绍

可以理解为一个盒子,事先将项目中可能用到的类扔进去,在项目中直接从容器中拿,也就是避免了直接在项目中到处new,造成大量耦合。取而代之的是在项目类里面增设setDi、getDi方法,通过Di(依赖注入)统一管理类。

应用场景

需要灵活管理一大堆具有依赖关系的对象时,比如一个PHP框架需要管理众多服务时。

例子

1.定义了一个存储接口,以及两个类实现

<?php 

interface Storage{
	public function open();
}

class DatabaseStorage implements Storage {
	public function open() {
		echo 'open db Storage';
	}
}

class FileStorage implements Storage {
	public function open() {
		echo 'open file Storage';
	}
}

2.创建一个容器

// 容器
class Container {

	protected $binds; // 绑定闭包

	protected $instances; // 绑定实例

	/**
	 * 向容器中注册服务
	 * 
	 * @param $abstract 服务名称
	 * @param $conctete 服务体
	 */
	public function bind($abstract, $concrete) {

		// 如果是个闭包则绑定到binds中
		if ($concrete instanceof Closure) {
			$this->binds[$abstract] = $concrete;
		} else {
			// 否则绑定到实例数组中
			$this->instances[$abstract] = $concrete;
		}
	}

	/**
	 * 从容器中获取服务
	 * 
	 * @param $abstract 服务名称
	 * @param $params 	实例化服务所需要传递的参数
	 */
	public function make($abstract, $params = []) {

		// 如果是个实例就返回这个实例
		if (isset($this->instances[$abstract])) {
			return $this->instances[$abstract];
		}
		
		array_unshift($params, $this);
		
		// 返回闭包执行结果
		return call_user_func_array($this->binds[$abstract], $params);
	}
}

3.使用容器,注册服务到容器中,并拿出来使用


$Container = new Container;

// 把名称为FileStorage,内容为闭包的服务,注册到容器
$Container->bind('FileStorage', function($Container) {
	return new FileStorage;
});
// 把名称为DatabaseStorage,内容为DatabaseStorage实例的服务注册到容器
$Container->bind('DatabaseStorage', new DatabaseStorage);



// 从容器获取服务并使用
$FileStorage = $Container->make('FileStorage');
$FileStorage->open(); // open file Storage

$DatabaseStorage = $Container->make('DatabaseStorage');
$DatabaseStorage->open(); // open db Storage

Laravel框架底层对容器的使用

1.看bootstrap/app.php部分代码。

<?php 

// 该Application类继承了Container容器类,这里实例化了Application这个容器
// 传递的path参数,是为了把程序的绝对路径绑定到容器
$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

...

2.看new Illuminate\Foundation\Application发生的事情。

<?php
...
public function __construct($basePath = null)
{
    $this->registerBaseBindings();

    $this->registerBaseServiceProviders();

    $this->registerCoreContainerAliases();

    if ($basePath) {
    	// 设置基本目录这个方法中又调用了bindPathsInContainer方法
        $this->setBasePath($basePath);
    }
}
...

// 把基础的path的绑定,放到容器中
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());
}

...

3.由上可见,已经把一些path绑定到了容器中,那么比如我们用public_path()时,底层是如何返回给我们的呢?


当然了,就是从这个容器中再取出来!

// vendor/laravel/framework/src/Illuminate/Foundation/helpers.php
...
if (! function_exists('public_path')) {
    /**
     * Get the path to the public folder.
     *
     * @param  string  $path
     * @return string
     */
    function public_path($path = '')
    {
        return app()->make('path.public').($path ? DIRECTORY_SEPARATOR.$path : $path);
    }
}
...

参考资料

https://blog.csdn.net/qq_39647045/article/details/83659028
https://segmentfault.com/a/1190000015449325
https://www.jianshu.com/p/e0583692521c

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐