|
1
|
|
|
<?php |
|
|
|
|
|
|
2
|
|
|
namespace Slince\Di\Tests; |
|
3
|
|
|
|
|
4
|
|
|
use Slince\Di\Container; |
|
5
|
|
|
use Slince\Di\Definition; |
|
6
|
|
|
use Slince\Di\Exception\DependencyInjectionException; |
|
7
|
|
|
use Slince\Di\Reference; |
|
8
|
|
|
use Slince\Di\Tests\TestClass\Actor; |
|
9
|
|
|
use Slince\Di\Tests\TestClass\ActorInterface; |
|
10
|
|
|
use Slince\Di\Tests\TestClass\Actress; |
|
11
|
|
|
use Slince\Di\Tests\TestClass\Director; |
|
12
|
|
|
use Slince\Di\Tests\TestClass\Movie; |
|
13
|
|
|
|
|
14
|
|
|
error_reporting(E_ALL ^ E_USER_DEPRECATED); |
|
15
|
|
|
|
|
16
|
|
|
class ContainerTest extends \PHPUnit_Framework_TestCase |
|
17
|
|
|
{ |
|
18
|
|
|
public function getContainer() |
|
19
|
|
|
{ |
|
20
|
|
|
return new Container(); |
|
21
|
|
|
} |
|
22
|
|
|
|
|
23
|
|
|
/** |
|
24
|
|
|
* 自定义闭包或者工厂方法代理 |
|
25
|
|
|
*/ |
|
26
|
|
|
public function testDelegate() |
|
27
|
|
|
{ |
|
28
|
|
|
$container = $this->getContainer(); |
|
29
|
|
|
$container->delegate('director1', function () { |
|
30
|
|
|
return new Director('James', 26); |
|
31
|
|
|
}); |
|
32
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director1')); |
|
33
|
|
|
$container->delegate('director2', [Director::class, 'factory']); //或者 'Slince\Di\Tests\TestClass\Director::factory' |
|
34
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director2')); |
|
35
|
|
|
} |
|
36
|
|
|
|
|
37
|
|
|
/** |
|
38
|
|
|
* 测试对象绑定,对象绑定结果是单例 |
|
39
|
|
|
*/ |
|
40
|
|
|
public function testInstance() |
|
41
|
|
|
{ |
|
42
|
|
|
$container = $this->getContainer(); |
|
43
|
|
|
$director = new Director(); |
|
44
|
|
|
$container->instance('director', $director); |
|
45
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director')); |
|
46
|
|
|
$this->assertTrue($container->get('director') === $director); |
|
47
|
|
|
//instance只能是单例 |
|
48
|
|
|
$this->assertTrue($container->get('director') === $container->get('director')); |
|
49
|
|
|
} |
|
50
|
|
|
|
|
51
|
|
|
public function testDefine() |
|
52
|
|
|
{ |
|
53
|
|
|
$container = $this->getContainer(); |
|
54
|
|
|
$container->define('director', Director::class, [0 => 'Bob', 1 => 45]); |
|
55
|
|
|
$this->assertInstanceOf(Director::class, $director = $container->get('director')); |
|
56
|
|
|
$this->assertEquals('Bob', $director->getName()); |
|
57
|
|
|
$this->assertEquals(45, $director->getAge()); |
|
58
|
|
|
} |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* @deprecated |
|
62
|
|
|
*/ |
|
63
|
|
|
public function testSetDefinition() |
|
64
|
|
|
{ |
|
65
|
|
|
$container = $this->getContainer(); |
|
66
|
|
|
$container->setDefinition('director', new Definition(Director::class, [ |
|
|
|
|
|
|
67
|
|
|
'name' => 'Bob', |
|
68
|
|
|
'age' => 45 |
|
69
|
|
|
])); |
|
70
|
|
|
$this->assertInstanceOf(Director::class, $director = $container->get('director')); |
|
71
|
|
|
$this->assertEquals('Bob', $director->getName()); |
|
72
|
|
|
$this->assertEquals(45, $director->getAge()); |
|
73
|
|
|
} |
|
74
|
|
|
|
|
75
|
|
|
/** |
|
76
|
|
|
* 类名<=>别名,interface <=> implement绑定 |
|
77
|
|
|
*/ |
|
78
|
|
|
public function testSimpleBind() |
|
79
|
|
|
{ |
|
80
|
|
|
$container = $this->getContainer(); |
|
81
|
|
|
//简单的别名绑定 |
|
82
|
|
|
$container->bind('director', Director::class); |
|
83
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director')); |
|
84
|
|
|
} |
|
85
|
|
|
|
|
86
|
|
|
/** |
|
87
|
|
|
* {@deprecated} |
|
88
|
|
|
*/ |
|
89
|
|
|
public function alias() |
|
90
|
|
|
{ |
|
91
|
|
|
$container = $this->getContainer(); |
|
92
|
|
|
//兼容旧版本别名绑定 |
|
93
|
|
|
$container->alias('director', Director::class); |
|
|
|
|
|
|
94
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director')); |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
/** |
|
98
|
|
|
* 绑定接口与实现 |
|
99
|
|
|
*/ |
|
100
|
|
|
public function testInterfaceBind() |
|
101
|
|
|
{ |
|
102
|
|
|
$container = $this->getContainer(); |
|
103
|
|
|
//接口与实现;类绑定 |
|
104
|
|
|
$container->bind(ActorInterface::class, Actor::class); |
|
105
|
|
|
//直接获取接口实例 |
|
106
|
|
|
$this->assertInstanceOf(ActorInterface::class, $container->get(ActorInterface::class)); |
|
107
|
|
|
$this->assertInstanceOf(Actor::class, $container->get(ActorInterface::class)); |
|
108
|
|
|
|
|
109
|
|
|
//获取依赖该接口的类实例 |
|
110
|
|
|
$movie = $container->get(Movie::class); |
|
111
|
|
|
$this->assertInstanceOf(Movie::class, $movie); |
|
112
|
|
|
$this->assertInstanceOf(Actor::class, $movie->getActor()); |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
|
|
/** |
|
116
|
|
|
* 为类设置接口依赖 |
|
117
|
|
|
*/ |
|
118
|
|
|
public function testInterfaceBindForClassContext() |
|
119
|
|
|
{ |
|
120
|
|
|
$container = $this->getContainer(); |
|
121
|
|
|
//为Movie类声明接口实际指向 |
|
122
|
|
|
$container->bind(ActorInterface::class, Actor::class, Movie::class); |
|
123
|
|
|
//获取依赖该接口的类实例,由于构造方法与setter皆是类依赖故container可以自动解决 |
|
124
|
|
|
$container->define('movie', Movie::class, [], [ |
|
125
|
|
|
'setActress' => [] |
|
126
|
|
|
]); |
|
127
|
|
|
|
|
128
|
|
|
$movie = $container->get('movie'); |
|
129
|
|
|
$this->assertInstanceOf(Movie::class, $movie); |
|
130
|
|
|
$this->assertInstanceOf(Actor::class, $movie->getActor()); |
|
131
|
|
|
$this->assertInstanceOf(Actor::class, $movie->getActress()); |
|
132
|
|
|
|
|
133
|
|
|
//直接获取接口实例,会报出异常 |
|
134
|
|
|
$this->setExpectedException(DependencyInjectionException::class); |
|
135
|
|
|
$this->assertInstanceOf(Actor::class, $container->get(ActorInterface::class)); |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
|
|
/** |
|
139
|
|
|
* 为类方法设置接口依赖 |
|
140
|
|
|
*/ |
|
141
|
|
|
public function testInterfaceBindForClassMethodContext() |
|
142
|
|
|
{ |
|
143
|
|
|
$container = $this->getContainer(); |
|
144
|
|
|
//为Movie类声明接口依赖 |
|
145
|
|
|
$container->bind(ActorInterface::class, Actor::class, [Movie::class, '__construct']); //构造函数 |
|
146
|
|
|
$container->bind(ActorInterface::class, Actress::class, [Movie::class, 'setActress']); //setter方法 |
|
147
|
|
|
|
|
148
|
|
|
//获取依赖该接口的类实例,由于构造方法与setter皆是类依赖故container可以自动解决 |
|
149
|
|
|
$container->define('movie', Movie::class, [], [ |
|
150
|
|
|
'setActress' => [] |
|
151
|
|
|
]); |
|
152
|
|
|
$movie = $container->get('movie'); |
|
153
|
|
|
$this->assertInstanceOf(Movie::class, $movie); |
|
154
|
|
|
$this->assertInstanceOf(Actor::class, $movie->getActor()); |
|
155
|
|
|
$this->assertInstanceOf(Actress::class, $movie->getActress()); |
|
156
|
|
|
} |
|
157
|
|
|
|
|
158
|
|
|
public function testShare() |
|
159
|
|
|
{ |
|
160
|
|
|
$container = $this->getContainer(); |
|
161
|
|
|
$container->delegate('director', function () { |
|
162
|
|
|
return new Director('James', 26); |
|
163
|
|
|
}); |
|
164
|
|
|
$container->share('director'); |
|
165
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director')); |
|
166
|
|
|
$this->assertTrue($container->get('director') === $container->get('director')); |
|
167
|
|
|
|
|
168
|
|
|
//兼容旧的api,已提示废除 |
|
169
|
|
|
$container->share('director2', function () { |
|
170
|
|
|
return new Director('James', 26); |
|
171
|
|
|
}); |
|
172
|
|
|
$this->assertTrue($container->get('director2') === $container->get('director2')); |
|
173
|
|
|
} |
|
174
|
|
|
|
|
175
|
|
|
public function testSet() |
|
176
|
|
|
{ |
|
177
|
|
|
$container = $this->getContainer(); |
|
178
|
|
|
//Similar to delegate |
|
179
|
|
|
$container->set('director', function () { |
|
180
|
|
|
return new Director('James', 26); |
|
181
|
|
|
}); |
|
182
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director')); |
|
183
|
|
|
$this->assertFalse($container->get('director') === $container->get('director')); |
|
184
|
|
|
|
|
185
|
|
|
//Similar to bind |
|
186
|
|
|
$container->set('director2', Director::class); |
|
187
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director2')); |
|
188
|
|
|
$this->assertFalse($container->get('director2') === $container->get('director2')); |
|
189
|
|
|
|
|
190
|
|
|
//Similar to instance |
|
191
|
|
|
$container->set('director3', new Director()); |
|
192
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director3')); |
|
193
|
|
|
$this->assertTrue($container->get('director3') === $container->get('director3')); |
|
194
|
|
|
|
|
195
|
|
|
//Similar to define |
|
196
|
|
|
$container->set('director4', new Definition(Director::class, [ |
|
197
|
|
|
'name' => 'Bob', |
|
198
|
|
|
'age' => 45 |
|
199
|
|
|
])); |
|
200
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director4')); |
|
201
|
|
|
$this->assertFalse($container->get('director4') === $container->get('director4')); |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
|
|
public function testSetWithShare() |
|
205
|
|
|
{ |
|
206
|
|
|
$container = $this->getContainer(); |
|
207
|
|
|
//Similar to delegate |
|
208
|
|
|
$container->set('director', function () { |
|
209
|
|
|
return new Director('James', 26); |
|
210
|
|
|
}, true); |
|
211
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director')); |
|
212
|
|
|
$this->assertTrue($container->get('director') === $container->get('director')); |
|
213
|
|
|
|
|
214
|
|
|
//Similar to bind |
|
215
|
|
|
$container->set('director2', Director::class, true); |
|
216
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director2')); |
|
217
|
|
|
$this->assertTrue($container->get('director2') === $container->get('director2')); |
|
218
|
|
|
|
|
219
|
|
|
//Similar to instance |
|
220
|
|
|
$container->set('director3', new Director(), true); |
|
221
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director3')); |
|
222
|
|
|
$this->assertTrue($container->get('director3') === $container->get('director3')); |
|
223
|
|
|
|
|
224
|
|
|
//Similar to define |
|
225
|
|
|
$container->set('director4', new Definition(Director::class, [ |
|
226
|
|
|
'name' => 'Bob', |
|
227
|
|
|
'age' => 45 |
|
228
|
|
|
]), true); |
|
229
|
|
|
$this->assertInstanceOf(Director::class, $container->get('director4')); |
|
230
|
|
|
$this->assertTrue($container->get('director4') === $container->get('director4')); |
|
231
|
|
|
} |
|
232
|
|
|
|
|
233
|
|
|
public function testGetWithArguments() |
|
234
|
|
|
{ |
|
235
|
|
|
$container = $this->getContainer(); |
|
236
|
|
|
$director = $container->get(Director::class, [ |
|
237
|
|
|
'age' => 26 |
|
238
|
|
|
]); |
|
239
|
|
|
//变量名索引 |
|
240
|
|
|
$this->assertEquals(26, $director->getAge()); |
|
241
|
|
|
//数字索引 |
|
242
|
|
|
$director = $container->get(Director::class, [ |
|
243
|
|
|
1 => 18 |
|
244
|
|
|
]); |
|
245
|
|
|
$this->assertEquals(18, $director->getAge()); |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
public function testGetWithReference() |
|
249
|
|
|
{ |
|
250
|
|
|
$container = $this->getContainer(); |
|
251
|
|
|
$container->set('director', new Director('Bob', 45)); |
|
252
|
|
|
$container->bind(ActorInterface::class, Actor::class); |
|
253
|
|
|
|
|
254
|
|
|
$movie = $container->get(Movie::class); |
|
255
|
|
|
$this->assertEquals(18, $movie->getDirector()->getAge()); //Defaults age |
|
256
|
|
|
|
|
257
|
|
|
$movie = $container->get(Movie::class, [ |
|
258
|
|
|
new Reference('director') |
|
259
|
|
|
]); |
|
260
|
|
|
$this->assertEquals('Bob', $movie->getDirector()->getName()); |
|
261
|
|
|
$this->assertEquals(45, $movie->getDirector()->getAge()); |
|
262
|
|
|
} |
|
263
|
|
|
|
|
264
|
|
|
public function testSimpleGlobalParameter() |
|
265
|
|
|
{ |
|
266
|
|
|
$container = $this->getContainer(); |
|
267
|
|
|
$container->setParameters([ |
|
268
|
|
|
'directorName' => 'James' |
|
269
|
|
|
]); |
|
270
|
|
|
$container->delegate('director', function (Container $container) { |
|
271
|
|
|
return new Director($container->getParameter('directorName'), 26); |
|
272
|
|
|
}); |
|
273
|
|
|
$this->assertEquals('James', $container->get('director')->getName()); |
|
274
|
|
|
} |
|
275
|
|
|
|
|
276
|
|
|
public function testGlobalParameter() |
|
277
|
|
|
{ |
|
278
|
|
|
$container = $this->getContainer(); |
|
279
|
|
|
$container->setParameters([ |
|
280
|
|
|
'directorName' => 'James', |
|
281
|
|
|
'director' => [ //支持点号获取深度数据结构 |
|
282
|
|
|
'age' => 26 |
|
283
|
|
|
] |
|
284
|
|
|
]); |
|
285
|
|
|
//支持点号访问 |
|
286
|
|
|
$container->define('director', Director::class, [ |
|
287
|
|
|
'%directorName%', |
|
288
|
|
|
'%director.age%' |
|
289
|
|
|
]); |
|
290
|
|
|
$this->assertEquals('James', $container->get('director')->getName()); |
|
291
|
|
|
$this->assertEquals(26, $container->get('director')->getAge()); |
|
292
|
|
|
|
|
293
|
|
|
//在get操作时传入参数 |
|
294
|
|
|
$this->assertEquals(26, $container->get(Director::class, [ |
|
295
|
|
|
'age' => '%director.age%' |
|
296
|
|
|
])->getAge()); |
|
297
|
|
|
} |
|
298
|
|
|
} |
|
299
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.