1 | <?php |
||
56 | */ |
||
57 | class ContainerSpec extends ObjectBehavior |
||
58 | { |
||
59 | public function it_is_initializable() |
||
60 | { |
||
61 | $this->shouldHaveType('Yapeal\Container\Container'); |
||
62 | } |
||
63 | public function it_provided_fluent_interface_from_register($provider) |
||
64 | { |
||
65 | /** |
||
66 | * @var \Yapeal\Container\ServiceProviderInterface $provider |
||
67 | */ |
||
68 | $provider->beADoubleOf('\Yapeal\Container\ServiceProviderInterface'); |
||
69 | $provider->register($this) |
||
70 | ->willReturn(); |
||
71 | $this->register($provider) |
||
72 | ->shouldReturn($this); |
||
73 | } |
||
74 | public function it_should_allow_defining_new_service_after_freezing_first() |
||
75 | { |
||
76 | $this['foo'] = function () { |
||
77 | return 'fooValue'; |
||
78 | }; |
||
79 | $this['foo']; |
||
80 | $this['bar'] = function () { |
||
81 | return 'barValue'; |
||
82 | }; |
||
83 | $this['bar']->shouldReturn('barValue'); |
||
84 | } |
||
85 | public function it_should_allow_extending_non_frozen_service() |
||
86 | { |
||
87 | $this['foo'] = function () { |
||
88 | return 'foo'; |
||
89 | }; |
||
90 | $this['foo'] = $this->extend('foo', |
||
91 | function ($foo) { |
||
92 | return "$foo.bar"; |
||
93 | }); |
||
94 | $this['foo'] = $this->extend('foo', |
||
95 | function ($foo) { |
||
96 | return "$foo.baz"; |
||
97 | }); |
||
98 | $this['foo']->shouldReturn('foo.bar.baz'); |
||
99 | } |
||
100 | public function it_should_allow_extending_other_service_after_freezing_first() |
||
101 | { |
||
102 | $this['foo'] = function () { |
||
103 | return 'foo'; |
||
104 | }; |
||
105 | $this['bar'] = function () { |
||
106 | return 'bar'; |
||
107 | }; |
||
108 | $this['foo']; |
||
109 | $this['bar'] = $this->extend('bar', |
||
110 | function ($bar, $app) { |
||
111 | return "$bar.baz"; |
||
112 | }); |
||
113 | $this['bar']->shouldReturn('bar.baz'); |
||
114 | } |
||
115 | public function it_should_allow_global_function_names_as_parameter_value() |
||
116 | { |
||
117 | $globals = ['strlen', 'count', 'strtolower']; |
||
118 | foreach ($globals as $global) { |
||
119 | $this['global_function'] = $global; |
||
120 | $this['global_function']->shouldReturn($global); |
||
121 | } |
||
122 | } |
||
123 | public function it_should_allow_removing_frozen_service_and_then_setting_again() |
||
124 | { |
||
125 | $this['foo'] = function () { |
||
126 | return 'fooValue'; |
||
127 | }; |
||
128 | $this['foo']; |
||
129 | unset($this['foo']); |
||
130 | $this['foo'] = function () { |
||
131 | return 'barValue'; |
||
132 | }; |
||
133 | } |
||
134 | public function it_should_also_unset_key_when_unsetting_offsets() |
||
135 | { |
||
136 | $this['param'] = 'value'; |
||
137 | $this['service'] = function () { |
||
138 | return new MockService(); |
||
139 | }; |
||
140 | $this->keys() |
||
141 | ->shouldHaveCount(2); |
||
142 | unset($this['param'], $this['service']); |
||
143 | $this->keys() |
||
144 | ->shouldHaveCount(0); |
||
145 | } |
||
146 | public function it_should_have_an_offset_after_one_is_set() |
||
147 | { |
||
148 | $this->keys() |
||
149 | ->shouldHaveCount(0); |
||
150 | $this['param'] = 'value'; |
||
151 | $this->keys() |
||
152 | ->shouldContain('param'); |
||
153 | $this->keys() |
||
154 | ->shouldHaveCount(1); |
||
155 | } |
||
156 | public function it_should_honor_null_values_in_offset_get() |
||
157 | { |
||
158 | $this['foo'] = null; |
||
159 | $this['foo']->shouldReturn(null); |
||
160 | } |
||
161 | public function it_should_honor_returning_null_values_from_raw() |
||
162 | { |
||
163 | $this['foo'] = null; |
||
164 | $this->raw('foo') |
||
165 | ->shouldReturn(null); |
||
166 | } |
||
167 | public function it_should_initialise_offsets_from_constructor() |
||
168 | { |
||
169 | $params = ['param' => 'value', 'param2' => false]; |
||
170 | $this->beConstructedWith($params); |
||
171 | $this->keys() |
||
172 | ->shouldHaveCount(2); |
||
173 | foreach ($params as $param => $value) { |
||
174 | $this[$param]->shouldBe($value); |
||
175 | } |
||
176 | } |
||
177 | public function it_should_not_invoke_protected_services() |
||
178 | { |
||
179 | $services = [ |
||
180 | function ($value) { |
||
181 | $service = new MockService(); |
||
182 | $service->value = $value; |
||
183 | return $service; |
||
184 | }, |
||
185 | new Invokable() |
||
186 | ]; |
||
187 | foreach ($services as $service) { |
||
188 | $this['protected'] = $this->protect($service); |
||
189 | $this['protected']->shouldReturn($service); |
||
190 | } |
||
191 | } |
||
192 | public function it_should_pass_container_as_parameter() |
||
193 | { |
||
194 | $this['service'] = function () { |
||
195 | return new MockService(); |
||
196 | }; |
||
197 | $this['container'] = function ($container) { |
||
198 | return $container; |
||
199 | }; |
||
200 | $this['service']->shouldNotEqual($this); |
||
201 | $this['container']->shouldEqual($this); |
||
202 | } |
||
203 | public function it_should_return_different_instances_of_same_type_from_factory() |
||
204 | { |
||
205 | $this['service'] = $this->factory(function () { |
||
206 | return new MockService(); |
||
207 | }); |
||
208 | $serviceOne = $this['service']->shouldHaveType('Spec\Yapeal\MockService'); |
||
209 | $this['service']->shouldNotEqual($serviceOne); |
||
210 | } |
||
211 | public function it_should_return_original_instance_from_raw_when_using_factory() |
||
212 | { |
||
213 | $definition = $this->factory(function () { |
||
214 | return 'foo'; |
||
215 | }); |
||
216 | $this['service'] = $definition; |
||
217 | $this->raw('service') |
||
218 | ->shouldReturn($definition); |
||
219 | $this['service']->shouldNotReturn($definition); |
||
220 | } |
||
221 | public function it_should_return_same_instance_and_type_as_callable_returns() |
||
222 | { |
||
223 | $this['service'] = function () { |
||
224 | return new MockService(); |
||
225 | }; |
||
226 | $serviceOne = $this['service']->shouldHaveType('Spec\Yapeal\MockService'); |
||
227 | $this['service']->shouldEqual($serviceOne); |
||
228 | } |
||
229 | public function it_should_return_same_type_and_value_for_simple_values() |
||
230 | { |
||
231 | foreach ([true, false, null, 'value', 1, 1.0, ['value']] as $item) { |
||
232 | $this['param'] = $item; |
||
233 | $this['param']->shouldBe($item); |
||
234 | } |
||
235 | } |
||
236 | public function it_should_return_true_from_offset_exists_for_any_set_key() |
||
237 | { |
||
238 | $this['param'] = 'value'; |
||
239 | $this['service'] = function () { |
||
240 | return new MockService(); |
||
241 | }; |
||
242 | $this['null'] = null; |
||
243 | $this->offsetExists('param') |
||
244 | ->shouldReturn(true); |
||
245 | $this->offsetExists('param') |
||
246 | ->shouldReturn(isset($this['param'])); |
||
247 | $this->offsetExists('service') |
||
248 | ->shouldReturn(true); |
||
249 | $this->offsetExists('service') |
||
250 | ->shouldReturn(isset($this['service'])); |
||
251 | $this->offsetExists('null') |
||
252 | ->shouldReturn(true); |
||
253 | $this->offsetExists('null') |
||
254 | ->shouldReturn(isset($this['null'])); |
||
255 | $this->offsetExists('non_existent') |
||
256 | ->shouldNotReturn(true); |
||
257 | $this->offsetExists('non_existent') |
||
258 | ->shouldNotReturn(isset($this['non_existent'])); |
||
259 | } |
||
260 | public function it_should_treat_invokable_object_like_callable() |
||
261 | { |
||
262 | $this['invokable'] = new Invokable(); |
||
263 | $invoked = $this['invokable']->shouldHaveType('Spec\Yapeal\MockService'); |
||
264 | $this['invokable']->shouldReturn($invoked); |
||
265 | } |
||
266 | public function it_should_treat_non_invokable_object_like_parameter() |
||
267 | { |
||
268 | $this['non_invokable'] = new NonInvokable(); |
||
269 | $this['non_invokable']->shouldHaveType('Spec\Yapeal\NonInvokable'); |
||
270 | } |
||
271 | public function it_throws_exception_for_non_existent_extend_offset() |
||
272 | { |
||
273 | $id = 'param'; |
||
274 | $mess = sprintf('Identifier "%s" is not defined.', $id); |
||
275 | $this->shouldThrow(new \InvalidArgumentException($mess)) |
||
276 | ->during('extend', |
||
277 | [ |
||
278 | $id, |
||
279 | function () { |
||
280 | } |
||
281 | ]); |
||
282 | } |
||
283 | public function it_throws_exception_for_non_existent_get_offset() |
||
284 | { |
||
285 | $id = 'param'; |
||
286 | $mess = sprintf('Identifier "%s" is not defined.', $id); |
||
287 | $this->shouldThrow(new \InvalidArgumentException($mess)) |
||
288 | ->during('offsetGet', [$id]); |
||
289 | } |
||
290 | public function it_throws_exception_for_non_existent_raw_offset() |
||
291 | { |
||
292 | $id = 'param'; |
||
293 | $mess = sprintf('Identifier "%s" is not defined.', $id); |
||
294 | $this->shouldThrow(new \InvalidArgumentException($mess)) |
||
295 | ->during('raw', [$id]); |
||
296 | } |
||
297 | public function it_throws_exception_when_extend_is_given_non_invokable() |
||
298 | { |
||
299 | if (version_compare(PHP_VERSION, '7.0.0', '>=')) { |
||
300 | throw new SkippingException('Unneeded on PHP 7.x or higher caught by TypeError'); |
||
301 | } |
||
302 | $this['foo'] = function () { |
||
303 | return 'foo'; |
||
304 | }; |
||
305 | $this->shouldThrow(new \InvalidArgumentException('Extension service definition is not a Closure or invokable object.')) |
||
306 | ->during('extend', ['foo', new NonInvokable()]); |
||
307 | } |
||
308 | public function it_throws_exception_when_factory_is_given_non_invokable() |
||
309 | { |
||
310 | if (version_compare(PHP_VERSION, '7.0.0', '>=')) { |
||
311 | throw new SkippingException('Unneeded on PHP 7.x or higher caught by TypeError'); |
||
312 | } |
||
313 | $this['service'] = $this->shouldThrow(new \InvalidArgumentException('Service definition is not a Closure or invokable object.')) |
||
314 | ->during('factory', [123]); |
||
315 | $this['service'] = $this->shouldThrow(new \InvalidArgumentException('Service definition is not a Closure or invokable object.')) |
||
316 | ->during('factory', [new NonInvokable()]); |
||
317 | } |
||
318 | public function it_throws_exception_when_protect_is_given_non_invokable() |
||
319 | { |
||
320 | if (version_compare(PHP_VERSION, '7.0.0', '>=')) { |
||
321 | throw new SkippingException('Unneeded on PHP 7.x or higher caught by TypeError'); |
||
322 | } |
||
323 | $this->shouldThrow(new \InvalidArgumentException('Callable is not a Closure or invokable object.')) |
||
324 | ->during('protect', [new NonInvokable()]); |
||
325 | } |
||
326 | public function it_throws_exception_when_trying_to_extend_non_invokable() |
||
327 | { |
||
328 | $this['param'] = 123; |
||
329 | $this['non_invokable'] = new NonInvokable(); |
||
330 | $this['param'] = $this->shouldThrow(new \InvalidArgumentException('Identifier "param" does not contain an object definition.')) |
||
331 | ->during('extend', |
||
332 | [ |
||
333 | 'param', |
||
334 | function () { |
||
335 | } |
||
336 | ]); |
||
337 | $this['non_invokable'] = $this->shouldThrow(new \InvalidArgumentException('Identifier "non_invokable" does not contain an object definition.')) |
||
338 | ->during('extend', |
||
339 | [ |
||
340 | 'non_invokable', |
||
341 | function () { |
||
342 | } |
||
343 | ]); |
||
344 | } |
||
345 | public function it_throws_exception_when_trying_to_extending_frozen_service() |
||
346 | { |
||
347 | $this['foo'] = function () { |
||
348 | return 'foo'; |
||
349 | }; |
||
350 | $this['foo'] = $this->extend('foo', |
||
351 | function ($foo) { |
||
352 | return "$foo.bar"; |
||
353 | }); |
||
354 | $this['foo']->shouldReturn('foo.bar'); |
||
355 | $this->shouldThrow(new \InvalidArgumentException('Identifier "foo" does not contain an object definition.')) |
||
356 | ->during('extend', |
||
357 | [ |
||
358 | 'foo', |
||
359 | function ($foo) { |
||
360 | return "$foo.baz"; |
||
361 | } |
||
362 | ]); |
||
363 | } |
||
364 | public function it_throws_exception_when_trying_to_get_offset_after_they_have_been_unset() |
||
365 | { |
||
366 | $this['param'] = 'value'; |
||
367 | $this['service'] = function () { |
||
368 | return new MockService(); |
||
369 | }; |
||
370 | $this->keys() |
||
371 | ->shouldHaveCount(2); |
||
372 | unset($this['param'], $this['service']); |
||
373 | $this->shouldThrow(new \InvalidArgumentException('Identifier "param" is not defined.')) |
||
374 | ->during('offsetGet', ['param']); |
||
375 | $this->shouldThrow(new \InvalidArgumentException('Identifier "service" is not defined.')) |
||
376 | ->during('offsetGet', ['service']); |
||
377 | } |
||
378 | public function it_throws_exception_when_trying_to_over_write_frozen_service() |
||
379 | { |
||
380 | $this['foo'] = function () { |
||
381 | return 'foo'; |
||
382 | }; |
||
383 | $this['foo']; |
||
384 | $this->shouldThrow(new \RuntimeException('Cannot override frozen service "foo".')) |
||
385 | ->during('offsetSet', |
||
386 | [ |
||
387 | 'foo', |
||
388 | function () { |
||
389 | return 'bar'; |
||
390 | } |
||
391 | ]); |
||
392 | } |
||
393 | } |
||
394 |