- IOC 容器
- 闭包绑定
- 闭包绑定单例
- 类直接生成本身
- 类单例
- 接口绑定
- 接口绑定接口作为构造器参数
- 绑定闭包第一个参数为 IOC 容器本身
- 数组访问 ArrayAccess 支持
- alias 设置别名
- make 服务容器返回对象支持参数
- bind 注册到容器支持覆盖
- instance 注册为实例
- 默认参数支持
- 必填参数校验
- 接口必须绑定服务
- call 回调自动依赖注入
- call 回调自动依赖注入支持字符串或者数组类回调
- call 回调自动依赖注入支持实例和方法数组回调
- call 回调自动依赖注入支持静态回调
- remove 删除服务和实例
- 实例数组访问 ArrayAccess.offsetUnset 支持
- 类依赖注入构造器必须为 public
- bind 注册到容器可以支持各种数据
- bind 注册到容器支持传递数组来设置别名
- 依赖注入的方法中类参数不存在例子
- instance 注册为实例支持传递数组来设置别名
- instance 注册为实例未传递第二个参数会注册自身
- 参数为类实例例子
- 参数为类实例例子和其它参数混合
- setCoroutine 注册到容器支持注册到当前协程
- removeCoroutine 删除协程上下文服务和实例
- remove 也支持删除协程上下文服务和实例
- removeCoroutine 支持删除当前协程上下文所有服务和实例
- singleton 协程例子
- bind 协程例子
- make 服务容器返回对象支持类名生成服务
- 魔术方法 __get 支持
- 魔术方法 __set 支持
- clear 清理容器
- IOC 容器禁止克隆
- makeProvider 创建服务提供者
- callProviderBootstrap 执行服务提供者 bootstrap
- registerProviders 注册服务提供者
- registerProviders 注册服务提供者支持延迟写入
IOC 容器
Testing Is Documentation
IOC 容器是整个框架最核心的部分,负责服务的管理和解耦。
目前系统所有的关键服务都接入了 IOC 容器,包括控制器、Console 命令行。
Uses
<?php
use Leevel\Di\Container;
use Leevel\Di\ICoroutine;
use stdClass;
闭包绑定
闭包属于惰性,真正使用的时候才会执行。
我们可以通过 bind
来绑定一个闭包,通过 make
来运行服务,第二次运行如果是单例则直接使用生成后的结果,否则会每次执行闭包的代码。
通常来说,系统大部分服务都是单例来提升性能和共享。
public function testBindClosure(): void
{
$container = new Container();
$container->bind('foo', function () {
return 'bar';
});
$this->assertSame('bar', $container->make('foo'));
}
闭包绑定单例
public function testSingletonClosure(): void
{
$container = new Container();
$singleton = new stdClass();
$container->singleton('singleton', function () use ($singleton) {
return $singleton;
});
$this->assertSame($singleton, $container->make('singleton'));
$this->assertSame($singleton, $container->make('singleton'));
}
类直接生成本身
一个独立的类可以直接生成,而不需要提前注册到容器中。
fixture 定义
Tests\Di\Fixtures\Test1
namespace Tests\Di\Fixtures;
class Test1
{
}
public function testClass(): void
{
$container = new Container();
$this->assertInstanceOf(Test1::class, $container->make(Test1::class));
}
类单例
类也可以注册为单例。
public function testSingletonClass(): void
{
$container = new Container();
$container->singleton(Test1::class);
$this->assertSame($container->make(Test1::class), $container->make(Test1::class));
}
接口绑定
可以为接口绑定实现。
fixture 定义
Tests\Di\Fixtures\ITest2
namespace Tests\Di\Fixtures;
interface ITest2
{
}
Tests\Di\Fixtures\Test2
namespace Tests\Di\Fixtures;
class Test2 implements ITest2
{
}
public function testInterface(): void
{
$container = new Container();
$container->bind(ITest2::class, Test2::class);
$this->assertInstanceOf(ITest2::class, $container->make(ITest2::class));
$this->assertInstanceOf(ITest2::class, $container->make(Test2::class));
}
接口绑定接口作为构造器参数
接口可以作为控制器参数来做依赖注入。
fixture 定义
Tests\Di\Fixtures\Test3
namespace Tests\Di\Fixtures;
class Test3 implements ITest3
{
public $arg1;
public function __construct(ITest2 $arg1)
{
$this->arg1 = $arg1;
}
}
通过 Test3
的构造函数注入 ITest2
的实现 Test2
,通过 IOC 容器可以实现代码解耦。
public function testInterface2(): void
{
$container = new Container();
$container->bind(ITest2::class, Test2::class);
$this->assertInstanceOf(ITest2::class, $test2 = $container->make(Test3::class)->arg1);
$this->assertInstanceOf(Test2::class, $test2);
}
绑定闭包第一个参数为 IOC 容器本身
public function testContainerAsFirstArgs(): void
{
$container = new Container();
$container->bind('test', function ($container) {
return $container;
});
$this->assertSame($container, $container->make('test'));
}
数组访问 ArrayAccess 支持
public function testArrayAccess(): void
{
$container = new Container();
$container['foo'] = function () {
return 'bar';
};
$this->assertTrue(isset($container['foo']));
$this->assertSame('bar', $container['foo']);
unset($container['foo']);
$this->assertFalse(isset($container['foo']));
}
alias 设置别名
public function testAliases(): void
{
$container = new Container();
$container['foo'] = 'bar';
$container->alias('foo', 'foo2');
$container->alias('foo', ['foo3', 'foo4']);
$container->alias(['foo' => ['foo5', 'foo6']]);
$container->alias(['foo' => 'foo7']);
$this->assertSame('bar', $container->make('foo'));
$this->assertSame('bar', $container->make('foo2'));
$this->assertSame('bar', $container->make('foo3'));
$this->assertSame('bar', $container->make('foo4'));
$this->assertSame('bar', $container->make('foo5'));
$this->assertSame('bar', $container->make('foo6'));
$this->assertSame('bar', $container->make('foo7'));
}
make 服务容器返回对象支持参数
public function testMakeWithArgs(): void
{
$container = new Container();
$container['foo'] = function ($container, $arg1, $arg2) {
return [
$arg1,
$arg2,
];
};
$this->assertSame([1, 2], $container->make('foo', [1, 2, 3]));
}
bind 注册到容器支持覆盖
public function testOverridden(): void
{
$container = new Container();
$container['foo'] = 'bar';
$this->assertSame('bar', $container['foo']);
$container['foo'] = 'bar2';
$this->assertSame('bar2', $container['foo']);
}
instance 注册为实例
public function testInstance(): void
{
$container = new Container();
$instance = new stdClass();
$container->instance('foo', $instance);
$this->assertSame($instance, $container->make('foo'));
}
默认参数支持
fixture 定义
Tests\Di\Fixtures\Test5
namespace Tests\Di\Fixtures;
class Test5 implements ITest3
{
public $arg1;
public $arg2;
public function __construct(Test3 $arg1, $arg2 = 'hello default')
{
$this->arg1 = $arg1;
$this->arg2 = $arg2;
}
}
Tests\Di\Fixtures\ITest3
namespace Tests\Di\Fixtures;
interface ITest3
{
}
public function testDefaultArgs(): void
{
$container = new Container();
$container->bind(ITest2::class, Test2::class);
$container->bind('foo', Test5::class);
$test5 = $container->make('foo');
$this->assertInstanceOf(ITest3::class, $test5);
$this->assertSame('hello default', $test5->arg2);
}
必填参数校验
fixture 定义
Tests\Di\Fixtures\Test6
namespace Tests\Di\Fixtures;
class Test6
{
public $arg1;
public $arg2;
public $arg3;
public function __construct($arg1, $arg2, $arg3)
{
$this->arg1 = $arg1;
$this->arg2 = $arg2;
$this->arg3 = $arg3;
}
}
public function testArgsRequiredContainerInvalidArgumentException(): void
{
$this->expectException(\Leevel\Di\ContainerInvalidArgumentException::class);
$this->expectExceptionMessage(
'There are 3 required args,but 0 gived.'
);
$container = new Container();
$container->make(Test6::class, []);
}
接口必须绑定服务
public function testInterfaceContainerInvalidArgumentException(): void
{
$this->expectException(\Leevel\Di\ContainerInvalidArgumentException::class);
$this->expectExceptionMessage(
'Interface Tests\\Di\\Fixtures\\ITest2 cannot be normalize because not binded.'
);
$container = new Container();
$container->make(ITest2::class, []);
}
call 回调自动依赖注入
fixture 定义
Tests\Di\Fixtures\Test7
namespace Tests\Di\Fixtures;
class Test7
{
}
Tests\Di\Fixtures\Test8
namespace Tests\Di\Fixtures;
class Test8 implements ITest8
{
public function func1()
{
return func_get_args();
}
public function func2($arg1 = 'hello')
{
return func_get_args();
}
public static function staticFunc3()
{
return func_get_args();
}
public function handle()
{
return ['call handle'];
}
}
public function testCall(): void
{
$container = new Container();
$result = $container->call(function (Test7 $arg1, array $arg2 = []) {
return func_get_args();
});
$this->assertInstanceOf(Test7::class, $result[0]);
$this->assertSame([], $result[1]);
$result = $container->call(function (Test7 $arg1, array $arg2 = [], $arg3 = null) {
return func_get_args();
}, ['arg3' => 'hello']);
$this->assertInstanceOf(Test7::class, $result[0]);
$this->assertSame([], $result[1]);
$this->assertSame('hello', $result[2]);
$test7 = new Test7();
$result = $container->call(function (Test7 $arg1, $arg2 = 'hello') {
return func_get_args();
}, [Test7::class => $test7, 'arg2' => 'hello world']);
$this->assertSame($test7, $result[0]);
$this->assertSame('hello world', $result[1]);
$test8 = new Test8();
$result = $container->call(function ($arg1, $arg2, $arg3, ?ITest8 $arg4 = null, ?Test8 $arg5 = null) {
return func_get_args();
}, ['arg1' => 'foo', 'arg3' => 'world2', Test8::class => $test8]);
$this->assertSame('foo', $result[0]);
$this->assertNull($result[1]);
$this->assertSame('world2', $result[2]);
$this->assertNull($result[3]);
$this->assertSame($result[4], $test8);
}
call 回调自动依赖注入支持字符串或者数组类回调
public function testCallWithArrayOrString(): void
{
$container = new Container();
$result = $container->call([Test8::class, 'func1'], ['foo', 'bar']);
$this->assertSame(['foo', 'bar'], $result);
$result = $container->call(Test8::class.'@func1', ['foo', 'bar']);
$this->assertSame(['foo', 'bar'], $result);
$result = $container->call([Test8::class], ['foo', 'bar']);
$this->assertSame(['call handle'], $result);
$result = $container->call(Test8::class.'@', ['foo', 'bar']);
$this->assertSame(['call handle'], $result);
$result = $container->call(Test8::class.'@func2');
$this->assertSame('hello', $result[0]);
$result = $container->call(Test8::class.'@func2', ['world', 'foo', 'bar']);
$this->assertSame('hello', $result[0]);
$this->assertSame('world', $result[1]);
$this->assertSame('foo', $result[2]);
$this->assertSame('bar', $result[3]);
$result = $container->call(Test8::class.'@func2', ['world', 'arg1' => 'foo', 'bar']);
$this->assertSame('foo', $result[0]);
$this->assertSame('world', $result[1]);
$this->assertSame('bar', $result[2]);
}
call 回调自动依赖注入支持实例和方法数组回调
public function testCallWithCallableArray(): void
{
$container = new Container();
$test8 = new Test8();
$result = $container->call([$test8, 'func1'], ['foo', 'bar']);
$this->assertSame(['foo', 'bar'], $result);
}
call 回调自动依赖注入支持静态回调
public function testCallStatic(): void
{
$container = new Container();
$result = $container->call(Test8::class.'::staticFunc3', ['hello', 'world']);
$this->assertSame(['hello', 'world'], $result);
}
remove 删除服务和实例
public function testRemove(): void
{
$container = new Container();
$test8 = new Test8();
$container->instance(Test8::class, $test8);
$this->assertTrue($container->exists(Test8::class));
$container->remove(Test8::class);
$this->assertFalse($container->exists(Test8::class));
}
实例数组访问 ArrayAccess.offsetUnset 支持
public function testUnsetInstances(): void
{
$container = new Container();
$container->instance('foo', 'bar');
$container->alias('foo', 'foo2');
$container->alias('foo', 'foo3');
$this->assertTrue(isset($container['foo']));
$this->assertTrue(isset($container['foo2']));
$this->assertTrue(isset($container['foo3']));
unset($container['foo']);
$this->assertFalse(isset($container['foo']));
$this->assertFalse(isset($container['foo2']));
$this->assertFalse(isset($container['foo3']));
}
类依赖注入构造器必须为 public
fixture 定义
Tests\Di\Fixtures\Test9
namespace Tests\Di\Fixtures;
class Test9
{
protected function __construct()
{
}
public function hello()
{
return 'world9';
}
}
public function testNotInstantiable(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage(
'Class Tests\\Di\\Fixtures\\Test9 is not instantiable.'
);
$container = new Container();
$this->assertSame('world9', $container->make(Test9::class)->hello());
}
bind 注册到容器可以支持各种数据
public function testMakeServiceBool(): void
{
$container = new Container();
$container->bind('foo', false);
$this->assertFalse($container->make('foo'));
}
bind 注册到容器支持传递数组来设置别名
public function testBindArrayAsAlias(): void
{
$container = new Container();
$container->bind(['foo' => 'bar'], false);
$this->assertFalse($container->make('foo'));
$this->assertFalse($container->make('bar'));
}
依赖注入的方法中类参数不存在例子
fixture 定义
Tests\Di\Fixtures\Test10
namespace Tests\Di\Fixtures;
class Test10
{
public function hello(TestNotFound $test)
{
return 'test10';
}
}
public function testParseReflectionException(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage(
'Class Tests\\Di\\Fixtures\\TestNotFound does not exist'
);
$container = new Container();
$container->call([new Test10(), 'hello']);
}
instance 注册为实例支持传递数组来设置别名
public function testInstanceWithArray(): void
{
$container = new Container();
$instance = new stdClass();
$container->instance(['foo' => 'bar'], $instance);
$this->assertSame($instance, $container->make('foo'));
$this->assertSame($instance, $container->make('bar'));
}
instance 注册为实例未传递第二个参数会注册自身
比如说系统中中间件注册。
# Leevel\Session\Provider\Register::middleware
/**
* 注册 middleware 服务.
*/
protected function middleware(): void;
public function testInstanceItSelf(): void
{
$container = new Container();
$container->instance('foo');
$this->assertSame('foo', $container->make('foo'));
$container->instance('Leevel\\Foo\\Middleware\\Bar');
$this->assertSame('Leevel\\Foo\\Middleware\\Bar', $container->make('Leevel\\Foo\\Middleware\\Bar'));
}
参数为类实例例子
fixture 定义
Tests\Di\Fixtures\Test20
namespace Tests\Di\Fixtures;
class Test20
{
public function handle(Test21 $arg1, Test22 $arg2)
{
return ['test21' => $arg1->prop, 'test22' => $arg2->prop];
}
}
Tests\Di\Fixtures\Test21
namespace Tests\Di\Fixtures;
class Test21
{
public $prop;
public function __construct(?string $prop = null)
{
$this->prop = $prop;
}
}
Tests\Di\Fixtures\Test22
namespace Tests\Di\Fixtures;
class Test22
{
public $prop;
public function __construct(?string $prop = null)
{
$this->prop = $prop;
}
}
public function testCallWithClassArgsAndItInstance(): void
{
$container = new Container();
$obj = new Test20();
$args = [new Test21('hello'), new Test22('world')];
$result = $container->call([$obj, 'handle'], $args);
$this->assertSame(['test21' => 'hello', 'test22' => 'world'], $result);
}
参数为类实例例子和其它参数混合
fixture 定义
Tests\Di\Fixtures\Test23
namespace Tests\Di\Fixtures;
class Test23
{
public function handle(Test24 $arg1, Test25 $arg2, string $arg3)
{
return ['test24' => $arg1->prop, 'test25' => $arg2->prop, 'three' => $arg3];
}
}
Tests\Di\Fixtures\Test24
namespace Tests\Di\Fixtures;
class Test24
{
public $prop;
public function __construct(?string $prop = null)
{
$this->prop = $prop;
}
}
Tests\Di\Fixtures\Test25
namespace Tests\Di\Fixtures;
class Test25
{
public $prop;
public function __construct(?string $prop = null)
{
$this->prop = $prop;
}
}
public function testCallWithClassArgsAndItInstanceAndMore(): void
{
$container = new Container();
$obj = new Test23();
$args = [new Test24('hello'), new Test25('world'), 'more'];
$result = $container->call([$obj, 'handle'], $args);
$this->assertSame(['test24' => 'hello', 'test25' => 'world', 'three' => 'more'], $result);
}
setCoroutine 注册到容器支持注册到当前协程
fixture 定义
Tests\Di\Fixtures\Test26
namespace Tests\Di\Fixtures;
class Test26
{
}
public function testCoroutine(): void
{
$coroutine = $this->createMock(ICoroutine::class);
$coroutine->method('context')->willReturn(true);
$this->assertTrue($coroutine->context(Test26::class));
$coroutine->method('cid')->willReturn(2);
$this->assertSame(2, $coroutine->cid());
$container = new Container();
$container->setCoroutine($coroutine);
$this->assertInstanceOf(ICoroutine::class, $container->getCoroutine());
$container->instance('test', new Test26());
$this->assertInstanceOf(Test26::class, $container->make('test'));
$this->assertTrue($container->existsCoroutine('test'));
}
removeCoroutine 删除协程上下文服务和实例
public function testRemoveCoroutine(): void
{
$coroutine = $this->createMock(ICoroutine::class);
$coroutine->method('context')->willReturn(true);
$this->assertTrue($coroutine->context(Test26::class));
$coroutine->method('cid')->willReturn(2);
$this->assertSame(2, $coroutine->cid());
$container = new Container();
$container->setCoroutine($coroutine);
$container->instance('test', new Test26());
$this->assertInstanceOf(Test26::class, $container->make('test'));
$this->assertTrue($container->existsCoroutine('test'));
$container->removeCoroutine('test');
$this->assertFalse($container->existsCoroutine('test'));
}
remove 也支持删除协程上下文服务和实例
public function testRemoveCoroutineByRemove(): void
{
$coroutine = $this->createMock(ICoroutine::class);
$coroutine->method('context')->willReturn(true);
$this->assertTrue($coroutine->context(Test26::class));
$coroutine->method('cid')->willReturn(2);
$this->assertSame(2, $coroutine->cid());
$container = new Container();
$container->setCoroutine($coroutine);
$container->instance('test', new Test26());
$this->assertInstanceOf(Test26::class, $container->make('test'));
$this->assertTrue($container->existsCoroutine('test'));
$container->remove('test');
$this->assertFalse($container->existsCoroutine('test'));
}
removeCoroutine 支持删除当前协程上下文所有服务和实例
public function testRemoveCoroutineAll(): void
{
$coroutine = $this->createMock(ICoroutine::class);
$coroutine->method('context')->willReturn(true);
$this->assertTrue($coroutine->context(Test26::class));
$coroutine->method('cid')->willReturn(2);
$this->assertSame(2, $coroutine->cid());
$container = new Container();
$container->setCoroutine($coroutine);
$container->instance('test', new Test26());
$this->assertInstanceOf(Test26::class, $container->make('test'));
$this->assertTrue($container->existsCoroutine('test'));
$container->removeCoroutine();
$this->assertFalse($container->existsCoroutine('test'));
}
singleton 协程例子
public function testCoroutineWasSingleton(): void
{
$coroutine = $this->createMock(ICoroutine::class);
$coroutine->method('context')->willReturn(true);
$this->assertTrue($coroutine->context(Test26::class));
$coroutine->method('cid')->willReturn(2);
$this->assertSame(2, $coroutine->cid());
$container = new Container();
$container->setCoroutine($coroutine);
$this->assertInstanceOf(ICoroutine::class, $container->getCoroutine());
$container->singleton('test', new Test26());
$this->assertInstanceOf(Test26::class, $container->make('test'));
$this->assertTrue($container->existsCoroutine('test'));
}
bind 协程例子
public function testBindAsCoroutine(): void
{
$coroutine = $this->createMock(ICoroutine::class);
$coroutine->method('context')->willReturn(true);
$this->assertTrue($coroutine->context('test'));
$container = new Container();
$container->setCoroutine($coroutine);
$this->assertInstanceOf(ICoroutine::class, $container->getCoroutine());
$container->bind('test', new Test26(), true, true);
$this->assertInstanceOf(Test26::class, $container->make('test'));
$this->assertTrue($container->existsCoroutine('test'));
}
make 服务容器返回对象支持类名生成服务
fixture 定义
Tests\Di\Fixtures\Test28
namespace Tests\Di\Fixtures;
class Test28
{
private $test;
public function __construct(Test27 $test)
{
$this->test = $test;
}
public function hello()
{
return $this->test->hello();
}
}
public function testClassArgsASingleClass(): void
{
$container = new Container();
$test = $container->make(Test28::class);
$this->assertSame('world', $test->hello());
}
魔术方法 __get 支持
public function testMagicGet(): void
{
$container = new Container();
$container->bind('foo', 'bar');
$this->assertSame('bar', $container->foo);
}
魔术方法 __set 支持
public function testMagicSet(): void
{
$container = new Container();
$container->foo = 'bar';
$this->assertSame('bar', $container->foo);
}
clear 清理容器
public function testClear(): void
{
$container = new Container();
$container->instance('foo', 'bar');
$this->assertSame('bar', $container->make('foo'));
$this->assertSame('notfound', $container->make('notfound'));
$container->clear();
$this->assertSame('foo', $container->make('foo'));
}
IOC 容器禁止克隆
public function testClone(): void
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage(
'IOC container disallowed clone.'
);
$container = new Container();
$container2 = clone $container;
}
makeProvider 创建服务提供者
fixture 定义
Tests\Di\Fixtures\ProviderTest1
namespace Tests\Di\Fixtures;
use Leevel\Di\IContainer;
use Leevel\Di\Provider;
class ProviderTest1 extends Provider
{
public function __construct(IContainer $container)
{
$_SERVER['testMakeProvider'] = 1;
}
public function register(): void
{
}
}
public function testMakeProvider(): void
{
$container = new Container();
$container->makeProvider(ProviderTest1::class);
$this->assertSame(1, $_SERVER['testMakeProvider']);
unset($_SERVER['testMakeProvider']);
}
callProviderBootstrap 执行服务提供者 bootstrap
fixture 定义
Tests\Di\Fixtures\ProviderTest2
namespace Tests\Di\Fixtures;
use Leevel\Di\IContainer;
use Leevel\Di\Provider;
class ProviderTest2 extends Provider
{
public function __construct(IContainer $container)
{
}
public function bootstrap()
{
$_SERVER['testCallProviderBootstrap'] = 1;
}
public function register(): void
{
}
}
public function testCallProviderBootstrap(): void
{
$container = new Container();
$container->callProviderBootstrap(new ProviderTest1($container));
$this->assertSame(1, $_SERVER['testMakeProvider']);
unset($_SERVER['testMakeProvider']);
$container->callProviderBootstrap(new ProviderTest2($container));
$this->assertSame(1, $_SERVER['testCallProviderBootstrap']);
unset($_SERVER['testCallProviderBootstrap']);
}
registerProviders 注册服务提供者
public function testRegisterProviders(): void
{
$container = new Container();
$this->assertFalse($container->isBootstrap());
$container->registerProviders([], [], []);
$this->assertTrue($container->isBootstrap());
// do nothing
$container->registerProviders([], [], []);
}
registerProviders 注册服务提供者支持延迟写入
fixture 定义
Tests\Di\Fixtures\DeferredProvider
namespace Tests\Di\Fixtures;
use Leevel\Di\IContainer;
use Leevel\Di\Provider;
class DeferredProvider extends Provider
{
public function __construct(IContainer $container)
{
$_SERVER['testDeferredProvider'] = 1;
}
public function register(): void
{
}
}
public function testDeferredProvider(): void
{
$deferredProviders = [
'test_deferred' => 'Tests\\Di\\Fixtures\\DeferredProvider',
];
$deferredAlias = [
'Tests\\Di\\Fixtures\\DeferredProvider' => [
'test_deferred' => 'bar',
],
];
$container = new Container();
$this->assertFalse($container->isBootstrap());
$container->registerProviders([], $deferredProviders, $deferredAlias);
$this->assertTrue($container->isBootstrap());
$this->assertSame('test_deferred', $container->make('bar'));
$this->assertSame('test_deferred', $container->make('test_deferred'));
$this->assertSame(1, $_SERVER['testDeferredProvider']);
unset($_SERVER['testDeferredProvider']);
}