IOC 容器

Testing Is Documentation

tests/Di/ContainerTest.phpIOC 容器 - 图1

IOC 容器是整个框架最核心的部分,负责服务的管理和解耦。

目前系统所有的关键服务都接入了 IOC 容器,包括控制器、Console 命令行。

Uses

  1. <?php
  2. use Leevel\Di\Container;
  3. use Leevel\Di\ICoroutine;
  4. use stdClass;

闭包绑定

闭包属于惰性,真正使用的时候才会执行。

我们可以通过 bind 来绑定一个闭包,通过 make 来运行服务,第二次运行如果是单例则直接使用生成后的结果,否则会每次执行闭包的代码。

通常来说,系统大部分服务都是单例来提升性能和共享。

  1. public function testBindClosure(): void
  2. {
  3. $container = new Container();
  4. $container->bind('foo', function () {
  5. return 'bar';
  6. });
  7. $this->assertSame('bar', $container->make('foo'));
  8. }

闭包绑定单例

  1. public function testSingletonClosure(): void
  2. {
  3. $container = new Container();
  4. $singleton = new stdClass();
  5. $container->singleton('singleton', function () use ($singleton) {
  6. return $singleton;
  7. });
  8. $this->assertSame($singleton, $container->make('singleton'));
  9. $this->assertSame($singleton, $container->make('singleton'));
  10. }

类直接生成本身

一个独立的类可以直接生成,而不需要提前注册到容器中。

fixture 定义

Tests\Di\Fixtures\Test1

  1. namespace Tests\Di\Fixtures;
  2. class Test1
  3. {
  4. }
  1. public function testClass(): void
  2. {
  3. $container = new Container();
  4. $this->assertInstanceOf(Test1::class, $container->make(Test1::class));
  5. }

类单例

类也可以注册为单例。

  1. public function testSingletonClass(): void
  2. {
  3. $container = new Container();
  4. $container->singleton(Test1::class);
  5. $this->assertSame($container->make(Test1::class), $container->make(Test1::class));
  6. }

接口绑定

可以为接口绑定实现。

fixture 定义

Tests\Di\Fixtures\ITest2

  1. namespace Tests\Di\Fixtures;
  2. interface ITest2
  3. {
  4. }

Tests\Di\Fixtures\Test2

  1. namespace Tests\Di\Fixtures;
  2. class Test2 implements ITest2
  3. {
  4. }
  1. public function testInterface(): void
  2. {
  3. $container = new Container();
  4. $container->bind(ITest2::class, Test2::class);
  5. $this->assertInstanceOf(ITest2::class, $container->make(ITest2::class));
  6. $this->assertInstanceOf(ITest2::class, $container->make(Test2::class));
  7. }

接口绑定接口作为构造器参数

接口可以作为控制器参数来做依赖注入。

fixture 定义

Tests\Di\Fixtures\Test3

  1. namespace Tests\Di\Fixtures;
  2. class Test3 implements ITest3
  3. {
  4. public $arg1;
  5. public function __construct(ITest2 $arg1)
  6. {
  7. $this->arg1 = $arg1;
  8. }
  9. }

通过 Test3 的构造函数注入 ITest2 的实现 Test2,通过 IOC 容器可以实现代码解耦。

  1. public function testInterface2(): void
  2. {
  3. $container = new Container();
  4. $container->bind(ITest2::class, Test2::class);
  5. $this->assertInstanceOf(ITest2::class, $test2 = $container->make(Test3::class)->arg1);
  6. $this->assertInstanceOf(Test2::class, $test2);
  7. }

绑定闭包第一个参数为 IOC 容器本身

  1. public function testContainerAsFirstArgs(): void
  2. {
  3. $container = new Container();
  4. $container->bind('test', function ($container) {
  5. return $container;
  6. });
  7. $this->assertSame($container, $container->make('test'));
  8. }

数组访问 ArrayAccess 支持

  1. public function testArrayAccess(): void
  2. {
  3. $container = new Container();
  4. $container['foo'] = function () {
  5. return 'bar';
  6. };
  7. $this->assertTrue(isset($container['foo']));
  8. $this->assertSame('bar', $container['foo']);
  9. unset($container['foo']);
  10. $this->assertFalse(isset($container['foo']));
  11. }

alias 设置别名

  1. public function testAliases(): void
  2. {
  3. $container = new Container();
  4. $container['foo'] = 'bar';
  5. $container->alias('foo', 'foo2');
  6. $container->alias('foo', ['foo3', 'foo4']);
  7. $container->alias(['foo' => ['foo5', 'foo6']]);
  8. $container->alias(['foo' => 'foo7']);
  9. $this->assertSame('bar', $container->make('foo'));
  10. $this->assertSame('bar', $container->make('foo2'));
  11. $this->assertSame('bar', $container->make('foo3'));
  12. $this->assertSame('bar', $container->make('foo4'));
  13. $this->assertSame('bar', $container->make('foo5'));
  14. $this->assertSame('bar', $container->make('foo6'));
  15. $this->assertSame('bar', $container->make('foo7'));
  16. }

make 服务容器返回对象支持参数

  1. public function testMakeWithArgs(): void
  2. {
  3. $container = new Container();
  4. $container['foo'] = function ($container, $arg1, $arg2) {
  5. return [
  6. $arg1,
  7. $arg2,
  8. ];
  9. };
  10. $this->assertSame([1, 2], $container->make('foo', [1, 2, 3]));
  11. }

bind 注册到容器支持覆盖

  1. public function testOverridden(): void
  2. {
  3. $container = new Container();
  4. $container['foo'] = 'bar';
  5. $this->assertSame('bar', $container['foo']);
  6. $container['foo'] = 'bar2';
  7. $this->assertSame('bar2', $container['foo']);
  8. }

instance 注册为实例

  1. public function testInstance(): void
  2. {
  3. $container = new Container();
  4. $instance = new stdClass();
  5. $container->instance('foo', $instance);
  6. $this->assertSame($instance, $container->make('foo'));
  7. }

默认参数支持

fixture 定义

Tests\Di\Fixtures\Test5

  1. namespace Tests\Di\Fixtures;
  2. class Test5 implements ITest3
  3. {
  4. public $arg1;
  5. public $arg2;
  6. public function __construct(Test3 $arg1, $arg2 = 'hello default')
  7. {
  8. $this->arg1 = $arg1;
  9. $this->arg2 = $arg2;
  10. }
  11. }

Tests\Di\Fixtures\ITest3

  1. namespace Tests\Di\Fixtures;
  2. interface ITest3
  3. {
  4. }
  1. public function testDefaultArgs(): void
  2. {
  3. $container = new Container();
  4. $container->bind(ITest2::class, Test2::class);
  5. $container->bind('foo', Test5::class);
  6. $test5 = $container->make('foo');
  7. $this->assertInstanceOf(ITest3::class, $test5);
  8. $this->assertSame('hello default', $test5->arg2);
  9. }

必填参数校验

fixture 定义

Tests\Di\Fixtures\Test6

  1. namespace Tests\Di\Fixtures;
  2. class Test6
  3. {
  4. public $arg1;
  5. public $arg2;
  6. public $arg3;
  7. public function __construct($arg1, $arg2, $arg3)
  8. {
  9. $this->arg1 = $arg1;
  10. $this->arg2 = $arg2;
  11. $this->arg3 = $arg3;
  12. }
  13. }
  1. public function testArgsRequiredContainerInvalidArgumentException(): void
  2. {
  3. $this->expectException(\Leevel\Di\ContainerInvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'There are 3 required args,but 0 gived.'
  6. );
  7. $container = new Container();
  8. $container->make(Test6::class, []);
  9. }

接口必须绑定服务

  1. public function testInterfaceContainerInvalidArgumentException(): void
  2. {
  3. $this->expectException(\Leevel\Di\ContainerInvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Interface Tests\\Di\\Fixtures\\ITest2 cannot be normalize because not binded.'
  6. );
  7. $container = new Container();
  8. $container->make(ITest2::class, []);
  9. }

call 回调自动依赖注入

fixture 定义

Tests\Di\Fixtures\Test7

  1. namespace Tests\Di\Fixtures;
  2. class Test7
  3. {
  4. }

Tests\Di\Fixtures\Test8

  1. namespace Tests\Di\Fixtures;
  2. class Test8 implements ITest8
  3. {
  4. public function func1()
  5. {
  6. return func_get_args();
  7. }
  8. public function func2($arg1 = 'hello')
  9. {
  10. return func_get_args();
  11. }
  12. public static function staticFunc3()
  13. {
  14. return func_get_args();
  15. }
  16. public function handle()
  17. {
  18. return ['call handle'];
  19. }
  20. }
  1. public function testCall(): void
  2. {
  3. $container = new Container();
  4. $result = $container->call(function (Test7 $arg1, array $arg2 = []) {
  5. return func_get_args();
  6. });
  7. $this->assertInstanceOf(Test7::class, $result[0]);
  8. $this->assertSame([], $result[1]);
  9. $result = $container->call(function (Test7 $arg1, array $arg2 = [], $arg3 = null) {
  10. return func_get_args();
  11. }, ['arg3' => 'hello']);
  12. $this->assertInstanceOf(Test7::class, $result[0]);
  13. $this->assertSame([], $result[1]);
  14. $this->assertSame('hello', $result[2]);
  15. $test7 = new Test7();
  16. $result = $container->call(function (Test7 $arg1, $arg2 = 'hello') {
  17. return func_get_args();
  18. }, [Test7::class => $test7, 'arg2' => 'hello world']);
  19. $this->assertSame($test7, $result[0]);
  20. $this->assertSame('hello world', $result[1]);
  21. $test8 = new Test8();
  22. $result = $container->call(function ($arg1, $arg2, $arg3, ?ITest8 $arg4 = null, ?Test8 $arg5 = null) {
  23. return func_get_args();
  24. }, ['arg1' => 'foo', 'arg3' => 'world2', Test8::class => $test8]);
  25. $this->assertSame('foo', $result[0]);
  26. $this->assertNull($result[1]);
  27. $this->assertSame('world2', $result[2]);
  28. $this->assertNull($result[3]);
  29. $this->assertSame($result[4], $test8);
  30. }

call 回调自动依赖注入支持字符串或者数组类回调

  1. public function testCallWithArrayOrString(): void
  2. {
  3. $container = new Container();
  4. $result = $container->call([Test8::class, 'func1'], ['foo', 'bar']);
  5. $this->assertSame(['foo', 'bar'], $result);
  6. $result = $container->call(Test8::class.'@func1', ['foo', 'bar']);
  7. $this->assertSame(['foo', 'bar'], $result);
  8. $result = $container->call([Test8::class], ['foo', 'bar']);
  9. $this->assertSame(['call handle'], $result);
  10. $result = $container->call(Test8::class.'@', ['foo', 'bar']);
  11. $this->assertSame(['call handle'], $result);
  12. $result = $container->call(Test8::class.'@func2');
  13. $this->assertSame('hello', $result[0]);
  14. $result = $container->call(Test8::class.'@func2', ['world', 'foo', 'bar']);
  15. $this->assertSame('hello', $result[0]);
  16. $this->assertSame('world', $result[1]);
  17. $this->assertSame('foo', $result[2]);
  18. $this->assertSame('bar', $result[3]);
  19. $result = $container->call(Test8::class.'@func2', ['world', 'arg1' => 'foo', 'bar']);
  20. $this->assertSame('foo', $result[0]);
  21. $this->assertSame('world', $result[1]);
  22. $this->assertSame('bar', $result[2]);
  23. }

call 回调自动依赖注入支持实例和方法数组回调

  1. public function testCallWithCallableArray(): void
  2. {
  3. $container = new Container();
  4. $test8 = new Test8();
  5. $result = $container->call([$test8, 'func1'], ['foo', 'bar']);
  6. $this->assertSame(['foo', 'bar'], $result);
  7. }

call 回调自动依赖注入支持静态回调

  1. public function testCallStatic(): void
  2. {
  3. $container = new Container();
  4. $result = $container->call(Test8::class.'::staticFunc3', ['hello', 'world']);
  5. $this->assertSame(['hello', 'world'], $result);
  6. }

remove 删除服务和实例

  1. public function testRemove(): void
  2. {
  3. $container = new Container();
  4. $test8 = new Test8();
  5. $container->instance(Test8::class, $test8);
  6. $this->assertTrue($container->exists(Test8::class));
  7. $container->remove(Test8::class);
  8. $this->assertFalse($container->exists(Test8::class));
  9. }

实例数组访问 ArrayAccess.offsetUnset 支持

  1. public function testUnsetInstances(): void
  2. {
  3. $container = new Container();
  4. $container->instance('foo', 'bar');
  5. $container->alias('foo', 'foo2');
  6. $container->alias('foo', 'foo3');
  7. $this->assertTrue(isset($container['foo']));
  8. $this->assertTrue(isset($container['foo2']));
  9. $this->assertTrue(isset($container['foo3']));
  10. unset($container['foo']);
  11. $this->assertFalse(isset($container['foo']));
  12. $this->assertFalse(isset($container['foo2']));
  13. $this->assertFalse(isset($container['foo3']));
  14. }

类依赖注入构造器必须为 public

fixture 定义

Tests\Di\Fixtures\Test9

  1. namespace Tests\Di\Fixtures;
  2. class Test9
  3. {
  4. protected function __construct()
  5. {
  6. }
  7. public function hello()
  8. {
  9. return 'world9';
  10. }
  11. }
  1. public function testNotInstantiable(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Class Tests\\Di\\Fixtures\\Test9 is not instantiable.'
  6. );
  7. $container = new Container();
  8. $this->assertSame('world9', $container->make(Test9::class)->hello());
  9. }

bind 注册到容器可以支持各种数据

  1. public function testMakeServiceBool(): void
  2. {
  3. $container = new Container();
  4. $container->bind('foo', false);
  5. $this->assertFalse($container->make('foo'));
  6. }

bind 注册到容器支持传递数组来设置别名

  1. public function testBindArrayAsAlias(): void
  2. {
  3. $container = new Container();
  4. $container->bind(['foo' => 'bar'], false);
  5. $this->assertFalse($container->make('foo'));
  6. $this->assertFalse($container->make('bar'));
  7. }

依赖注入的方法中类参数不存在例子

fixture 定义

Tests\Di\Fixtures\Test10

  1. namespace Tests\Di\Fixtures;
  2. class Test10
  3. {
  4. public function hello(TestNotFound $test)
  5. {
  6. return 'test10';
  7. }
  8. }
  1. public function testParseReflectionException(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Class Tests\\Di\\Fixtures\\TestNotFound does not exist'
  6. );
  7. $container = new Container();
  8. $container->call([new Test10(), 'hello']);
  9. }

instance 注册为实例支持传递数组来设置别名

  1. public function testInstanceWithArray(): void
  2. {
  3. $container = new Container();
  4. $instance = new stdClass();
  5. $container->instance(['foo' => 'bar'], $instance);
  6. $this->assertSame($instance, $container->make('foo'));
  7. $this->assertSame($instance, $container->make('bar'));
  8. }

instance 注册为实例未传递第二个参数会注册自身

比如说系统中中间件注册。

  1. # Leevel\Session\Provider\Register::middleware
  2. /**
  3. * 注册 middleware 服务.
  4. */
  5. protected function middleware(): void;
  1. public function testInstanceItSelf(): void
  2. {
  3. $container = new Container();
  4. $container->instance('foo');
  5. $this->assertSame('foo', $container->make('foo'));
  6. $container->instance('Leevel\\Foo\\Middleware\\Bar');
  7. $this->assertSame('Leevel\\Foo\\Middleware\\Bar', $container->make('Leevel\\Foo\\Middleware\\Bar'));
  8. }

参数为类实例例子

fixture 定义

Tests\Di\Fixtures\Test20

  1. namespace Tests\Di\Fixtures;
  2. class Test20
  3. {
  4. public function handle(Test21 $arg1, Test22 $arg2)
  5. {
  6. return ['test21' => $arg1->prop, 'test22' => $arg2->prop];
  7. }
  8. }

Tests\Di\Fixtures\Test21

  1. namespace Tests\Di\Fixtures;
  2. class Test21
  3. {
  4. public $prop;
  5. public function __construct(?string $prop = null)
  6. {
  7. $this->prop = $prop;
  8. }
  9. }

Tests\Di\Fixtures\Test22

  1. namespace Tests\Di\Fixtures;
  2. class Test22
  3. {
  4. public $prop;
  5. public function __construct(?string $prop = null)
  6. {
  7. $this->prop = $prop;
  8. }
  9. }
  1. public function testCallWithClassArgsAndItInstance(): void
  2. {
  3. $container = new Container();
  4. $obj = new Test20();
  5. $args = [new Test21('hello'), new Test22('world')];
  6. $result = $container->call([$obj, 'handle'], $args);
  7. $this->assertSame(['test21' => 'hello', 'test22' => 'world'], $result);
  8. }

参数为类实例例子和其它参数混合

fixture 定义

Tests\Di\Fixtures\Test23

  1. namespace Tests\Di\Fixtures;
  2. class Test23
  3. {
  4. public function handle(Test24 $arg1, Test25 $arg2, string $arg3)
  5. {
  6. return ['test24' => $arg1->prop, 'test25' => $arg2->prop, 'three' => $arg3];
  7. }
  8. }

Tests\Di\Fixtures\Test24

  1. namespace Tests\Di\Fixtures;
  2. class Test24
  3. {
  4. public $prop;
  5. public function __construct(?string $prop = null)
  6. {
  7. $this->prop = $prop;
  8. }
  9. }

Tests\Di\Fixtures\Test25

  1. namespace Tests\Di\Fixtures;
  2. class Test25
  3. {
  4. public $prop;
  5. public function __construct(?string $prop = null)
  6. {
  7. $this->prop = $prop;
  8. }
  9. }
  1. public function testCallWithClassArgsAndItInstanceAndMore(): void
  2. {
  3. $container = new Container();
  4. $obj = new Test23();
  5. $args = [new Test24('hello'), new Test25('world'), 'more'];
  6. $result = $container->call([$obj, 'handle'], $args);
  7. $this->assertSame(['test24' => 'hello', 'test25' => 'world', 'three' => 'more'], $result);
  8. }

setCoroutine 注册到容器支持注册到当前协程

fixture 定义

Tests\Di\Fixtures\Test26

  1. namespace Tests\Di\Fixtures;
  2. class Test26
  3. {
  4. }
  1. public function testCoroutine(): void
  2. {
  3. $coroutine = $this->createMock(ICoroutine::class);
  4. $coroutine->method('context')->willReturn(true);
  5. $this->assertTrue($coroutine->context(Test26::class));
  6. $coroutine->method('cid')->willReturn(2);
  7. $this->assertSame(2, $coroutine->cid());
  8. $container = new Container();
  9. $container->setCoroutine($coroutine);
  10. $this->assertInstanceOf(ICoroutine::class, $container->getCoroutine());
  11. $container->instance('test', new Test26());
  12. $this->assertInstanceOf(Test26::class, $container->make('test'));
  13. $this->assertTrue($container->existsCoroutine('test'));
  14. }

removeCoroutine 删除协程上下文服务和实例

  1. public function testRemoveCoroutine(): void
  2. {
  3. $coroutine = $this->createMock(ICoroutine::class);
  4. $coroutine->method('context')->willReturn(true);
  5. $this->assertTrue($coroutine->context(Test26::class));
  6. $coroutine->method('cid')->willReturn(2);
  7. $this->assertSame(2, $coroutine->cid());
  8. $container = new Container();
  9. $container->setCoroutine($coroutine);
  10. $container->instance('test', new Test26());
  11. $this->assertInstanceOf(Test26::class, $container->make('test'));
  12. $this->assertTrue($container->existsCoroutine('test'));
  13. $container->removeCoroutine('test');
  14. $this->assertFalse($container->existsCoroutine('test'));
  15. }

remove 也支持删除协程上下文服务和实例

  1. public function testRemoveCoroutineByRemove(): void
  2. {
  3. $coroutine = $this->createMock(ICoroutine::class);
  4. $coroutine->method('context')->willReturn(true);
  5. $this->assertTrue($coroutine->context(Test26::class));
  6. $coroutine->method('cid')->willReturn(2);
  7. $this->assertSame(2, $coroutine->cid());
  8. $container = new Container();
  9. $container->setCoroutine($coroutine);
  10. $container->instance('test', new Test26());
  11. $this->assertInstanceOf(Test26::class, $container->make('test'));
  12. $this->assertTrue($container->existsCoroutine('test'));
  13. $container->remove('test');
  14. $this->assertFalse($container->existsCoroutine('test'));
  15. }

removeCoroutine 支持删除当前协程上下文所有服务和实例

  1. public function testRemoveCoroutineAll(): void
  2. {
  3. $coroutine = $this->createMock(ICoroutine::class);
  4. $coroutine->method('context')->willReturn(true);
  5. $this->assertTrue($coroutine->context(Test26::class));
  6. $coroutine->method('cid')->willReturn(2);
  7. $this->assertSame(2, $coroutine->cid());
  8. $container = new Container();
  9. $container->setCoroutine($coroutine);
  10. $container->instance('test', new Test26());
  11. $this->assertInstanceOf(Test26::class, $container->make('test'));
  12. $this->assertTrue($container->existsCoroutine('test'));
  13. $container->removeCoroutine();
  14. $this->assertFalse($container->existsCoroutine('test'));
  15. }

singleton 协程例子

  1. public function testCoroutineWasSingleton(): void
  2. {
  3. $coroutine = $this->createMock(ICoroutine::class);
  4. $coroutine->method('context')->willReturn(true);
  5. $this->assertTrue($coroutine->context(Test26::class));
  6. $coroutine->method('cid')->willReturn(2);
  7. $this->assertSame(2, $coroutine->cid());
  8. $container = new Container();
  9. $container->setCoroutine($coroutine);
  10. $this->assertInstanceOf(ICoroutine::class, $container->getCoroutine());
  11. $container->singleton('test', new Test26());
  12. $this->assertInstanceOf(Test26::class, $container->make('test'));
  13. $this->assertTrue($container->existsCoroutine('test'));
  14. }

bind 协程例子

  1. public function testBindAsCoroutine(): void
  2. {
  3. $coroutine = $this->createMock(ICoroutine::class);
  4. $coroutine->method('context')->willReturn(true);
  5. $this->assertTrue($coroutine->context('test'));
  6. $container = new Container();
  7. $container->setCoroutine($coroutine);
  8. $this->assertInstanceOf(ICoroutine::class, $container->getCoroutine());
  9. $container->bind('test', new Test26(), true, true);
  10. $this->assertInstanceOf(Test26::class, $container->make('test'));
  11. $this->assertTrue($container->existsCoroutine('test'));
  12. }

make 服务容器返回对象支持类名生成服务

fixture 定义

Tests\Di\Fixtures\Test28

  1. namespace Tests\Di\Fixtures;
  2. class Test28
  3. {
  4. private $test;
  5. public function __construct(Test27 $test)
  6. {
  7. $this->test = $test;
  8. }
  9. public function hello()
  10. {
  11. return $this->test->hello();
  12. }
  13. }
  1. public function testClassArgsASingleClass(): void
  2. {
  3. $container = new Container();
  4. $test = $container->make(Test28::class);
  5. $this->assertSame('world', $test->hello());
  6. }

魔术方法 __get 支持

  1. public function testMagicGet(): void
  2. {
  3. $container = new Container();
  4. $container->bind('foo', 'bar');
  5. $this->assertSame('bar', $container->foo);
  6. }

魔术方法 __set 支持

  1. public function testMagicSet(): void
  2. {
  3. $container = new Container();
  4. $container->foo = 'bar';
  5. $this->assertSame('bar', $container->foo);
  6. }

clear 清理容器

  1. public function testClear(): void
  2. {
  3. $container = new Container();
  4. $container->instance('foo', 'bar');
  5. $this->assertSame('bar', $container->make('foo'));
  6. $this->assertSame('notfound', $container->make('notfound'));
  7. $container->clear();
  8. $this->assertSame('foo', $container->make('foo'));
  9. }

IOC 容器禁止克隆

  1. public function testClone(): void
  2. {
  3. $this->expectException(\RuntimeException::class);
  4. $this->expectExceptionMessage(
  5. 'IOC container disallowed clone.'
  6. );
  7. $container = new Container();
  8. $container2 = clone $container;
  9. }

makeProvider 创建服务提供者

fixture 定义

Tests\Di\Fixtures\ProviderTest1

  1. namespace Tests\Di\Fixtures;
  2. use Leevel\Di\IContainer;
  3. use Leevel\Di\Provider;
  4. class ProviderTest1 extends Provider
  5. {
  6. public function __construct(IContainer $container)
  7. {
  8. $_SERVER['testMakeProvider'] = 1;
  9. }
  10. public function register(): void
  11. {
  12. }
  13. }
  1. public function testMakeProvider(): void
  2. {
  3. $container = new Container();
  4. $container->makeProvider(ProviderTest1::class);
  5. $this->assertSame(1, $_SERVER['testMakeProvider']);
  6. unset($_SERVER['testMakeProvider']);
  7. }

callProviderBootstrap 执行服务提供者 bootstrap

fixture 定义

Tests\Di\Fixtures\ProviderTest2

  1. namespace Tests\Di\Fixtures;
  2. use Leevel\Di\IContainer;
  3. use Leevel\Di\Provider;
  4. class ProviderTest2 extends Provider
  5. {
  6. public function __construct(IContainer $container)
  7. {
  8. }
  9. public function bootstrap()
  10. {
  11. $_SERVER['testCallProviderBootstrap'] = 1;
  12. }
  13. public function register(): void
  14. {
  15. }
  16. }
  1. public function testCallProviderBootstrap(): void
  2. {
  3. $container = new Container();
  4. $container->callProviderBootstrap(new ProviderTest1($container));
  5. $this->assertSame(1, $_SERVER['testMakeProvider']);
  6. unset($_SERVER['testMakeProvider']);
  7. $container->callProviderBootstrap(new ProviderTest2($container));
  8. $this->assertSame(1, $_SERVER['testCallProviderBootstrap']);
  9. unset($_SERVER['testCallProviderBootstrap']);
  10. }

registerProviders 注册服务提供者

  1. public function testRegisterProviders(): void
  2. {
  3. $container = new Container();
  4. $this->assertFalse($container->isBootstrap());
  5. $container->registerProviders([], [], []);
  6. $this->assertTrue($container->isBootstrap());
  7. // do nothing
  8. $container->registerProviders([], [], []);
  9. }

registerProviders 注册服务提供者支持延迟写入

fixture 定义

Tests\Di\Fixtures\DeferredProvider

  1. namespace Tests\Di\Fixtures;
  2. use Leevel\Di\IContainer;
  3. use Leevel\Di\Provider;
  4. class DeferredProvider extends Provider
  5. {
  6. public function __construct(IContainer $container)
  7. {
  8. $_SERVER['testDeferredProvider'] = 1;
  9. }
  10. public function register(): void
  11. {
  12. }
  13. }
  1. public function testDeferredProvider(): void
  2. {
  3. $deferredProviders = [
  4. 'test_deferred' => 'Tests\\Di\\Fixtures\\DeferredProvider',
  5. ];
  6. $deferredAlias = [
  7. 'Tests\\Di\\Fixtures\\DeferredProvider' => [
  8. 'test_deferred' => 'bar',
  9. ],
  10. ];
  11. $container = new Container();
  12. $this->assertFalse($container->isBootstrap());
  13. $container->registerProviders([], $deferredProviders, $deferredAlias);
  14. $this->assertTrue($container->isBootstrap());
  15. $this->assertSame('test_deferred', $container->make('bar'));
  16. $this->assertSame('test_deferred', $container->make('test_deferred'));
  17. $this->assertSame(1, $_SERVER['testDeferredProvider']);
  18. unset($_SERVER['testDeferredProvider']);
  19. }