1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\DependencyInjection\Tests\Compiler; 13 14use PHPUnit\Framework\TestCase; 15use Symfony\Component\DependencyInjection\ChildDefinition; 16use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; 17use Symfony\Component\DependencyInjection\Compiler\ResolveDefinitionTemplatesPass; 18use Symfony\Component\DependencyInjection\ContainerBuilder; 19 20class ResolveChildDefinitionsPassTest extends TestCase 21{ 22 public function testProcess() 23 { 24 $container = new ContainerBuilder(); 25 $container->register('parent', 'foo')->setArguments(['moo', 'b'])->setProperty('foo', 'moo'); 26 $container->setDefinition('child', new ChildDefinition('parent')) 27 ->replaceArgument(0, 'a') 28 ->setProperty('foo', 'bar') 29 ->setClass('bar') 30 ; 31 32 $this->process($container); 33 34 $def = $container->getDefinition('child'); 35 $this->assertNotInstanceOf(ChildDefinition::class, $def); 36 $this->assertEquals('bar', $def->getClass()); 37 $this->assertEquals(['a', 'b'], $def->getArguments()); 38 $this->assertEquals(['foo' => 'bar'], $def->getProperties()); 39 } 40 41 public function testProcessAppendsMethodCallsAlways() 42 { 43 $container = new ContainerBuilder(); 44 45 $container 46 ->register('parent') 47 ->addMethodCall('foo', ['bar']) 48 ; 49 50 $container 51 ->setDefinition('child', new ChildDefinition('parent')) 52 ->addMethodCall('bar', ['foo']) 53 ; 54 55 $this->process($container); 56 57 $def = $container->getDefinition('child'); 58 $this->assertEquals([ 59 ['foo', ['bar']], 60 ['bar', ['foo']], 61 ], $def->getMethodCalls()); 62 } 63 64 public function testProcessDoesNotCopyAbstract() 65 { 66 $container = new ContainerBuilder(); 67 68 $container 69 ->register('parent') 70 ->setAbstract(true) 71 ; 72 73 $container 74 ->setDefinition('child', new ChildDefinition('parent')) 75 ; 76 77 $this->process($container); 78 79 $def = $container->getDefinition('child'); 80 $this->assertFalse($def->isAbstract()); 81 } 82 83 public function testProcessDoesNotCopyShared() 84 { 85 $container = new ContainerBuilder(); 86 87 $container 88 ->register('parent') 89 ->setShared(false) 90 ; 91 92 $container 93 ->setDefinition('child', new ChildDefinition('parent')) 94 ; 95 96 $this->process($container); 97 98 $def = $container->getDefinition('child'); 99 $this->assertTrue($def->isShared()); 100 } 101 102 public function testProcessDoesNotCopyTags() 103 { 104 $container = new ContainerBuilder(); 105 106 $container 107 ->register('parent') 108 ->addTag('foo') 109 ; 110 111 $container 112 ->setDefinition('child', new ChildDefinition('parent')) 113 ; 114 115 $this->process($container); 116 117 $def = $container->getDefinition('child'); 118 $this->assertEquals([], $def->getTags()); 119 } 120 121 public function testProcessDoesNotCopyDecoratedService() 122 { 123 $container = new ContainerBuilder(); 124 125 $container 126 ->register('parent') 127 ->setDecoratedService('foo') 128 ; 129 130 $container 131 ->setDefinition('child', new ChildDefinition('parent')) 132 ; 133 134 $this->process($container); 135 136 $def = $container->getDefinition('child'); 137 $this->assertNull($def->getDecoratedService()); 138 } 139 140 public function testProcessDoesNotDropShared() 141 { 142 $container = new ContainerBuilder(); 143 144 $container 145 ->register('parent') 146 ; 147 148 $container 149 ->setDefinition('child', new ChildDefinition('parent')) 150 ->setShared(false) 151 ; 152 153 $this->process($container); 154 155 $def = $container->getDefinition('child'); 156 $this->assertFalse($def->isShared()); 157 } 158 159 public function testProcessHandlesMultipleInheritance() 160 { 161 $container = new ContainerBuilder(); 162 163 $container 164 ->register('parent', 'foo') 165 ->setArguments(['foo', 'bar', 'c']) 166 ; 167 168 $container 169 ->setDefinition('child2', new ChildDefinition('child1')) 170 ->replaceArgument(1, 'b') 171 ; 172 173 $container 174 ->setDefinition('child1', new ChildDefinition('parent')) 175 ->replaceArgument(0, 'a') 176 ; 177 178 $this->process($container); 179 180 $def = $container->getDefinition('child2'); 181 $this->assertEquals(['a', 'b', 'c'], $def->getArguments()); 182 $this->assertEquals('foo', $def->getClass()); 183 } 184 185 public function testSetLazyOnServiceHasParent() 186 { 187 $container = new ContainerBuilder(); 188 189 $container->register('parent', 'stdClass'); 190 191 $container->setDefinition('child1', new ChildDefinition('parent')) 192 ->setLazy(true) 193 ; 194 195 $this->process($container); 196 197 $this->assertTrue($container->getDefinition('child1')->isLazy()); 198 } 199 200 public function testSetLazyOnServiceIsParent() 201 { 202 $container = new ContainerBuilder(); 203 204 $container->register('parent', 'stdClass') 205 ->setLazy(true) 206 ; 207 208 $container->setDefinition('child1', new ChildDefinition('parent')); 209 210 $this->process($container); 211 212 $this->assertTrue($container->getDefinition('child1')->isLazy()); 213 } 214 215 public function testSetAutowiredOnServiceHasParent() 216 { 217 $container = new ContainerBuilder(); 218 219 $container->register('parent', 'stdClass') 220 ->setAutowired(true) 221 ; 222 223 $container->setDefinition('child1', new ChildDefinition('parent')) 224 ->setAutowired(false) 225 ; 226 227 $this->process($container); 228 229 $this->assertFalse($container->getDefinition('child1')->isAutowired()); 230 } 231 232 public function testSetAutowiredOnServiceIsParent() 233 { 234 $container = new ContainerBuilder(); 235 236 $container->register('parent', 'stdClass') 237 ->setAutowired(true) 238 ; 239 240 $container->setDefinition('child1', new ChildDefinition('parent')); 241 242 $this->process($container); 243 244 $this->assertTrue($container->getDefinition('child1')->isAutowired()); 245 } 246 247 public function testDeepDefinitionsResolving() 248 { 249 $container = new ContainerBuilder(); 250 251 $container->register('parent', 'parentClass'); 252 $container->register('sibling', 'siblingClass') 253 ->setConfigurator(new ChildDefinition('parent'), 'foo') 254 ->setFactory([new ChildDefinition('parent'), 'foo']) 255 ->addArgument(new ChildDefinition('parent')) 256 ->setProperty('prop', new ChildDefinition('parent')) 257 ->addMethodCall('meth', [new ChildDefinition('parent')]) 258 ; 259 260 $this->process($container); 261 262 $configurator = $container->getDefinition('sibling')->getConfigurator(); 263 $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($configurator)); 264 $this->assertSame('parentClass', $configurator->getClass()); 265 266 $factory = $container->getDefinition('sibling')->getFactory(); 267 $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($factory[0])); 268 $this->assertSame('parentClass', $factory[0]->getClass()); 269 270 $argument = $container->getDefinition('sibling')->getArgument(0); 271 $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($argument)); 272 $this->assertSame('parentClass', $argument->getClass()); 273 274 $properties = $container->getDefinition('sibling')->getProperties(); 275 $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($properties['prop'])); 276 $this->assertSame('parentClass', $properties['prop']->getClass()); 277 278 $methodCalls = $container->getDefinition('sibling')->getMethodCalls(); 279 $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($methodCalls[0][1][0])); 280 $this->assertSame('parentClass', $methodCalls[0][1][0]->getClass()); 281 } 282 283 public function testSetDecoratedServiceOnServiceHasParent() 284 { 285 $container = new ContainerBuilder(); 286 287 $container->register('parent', 'stdClass'); 288 289 $container->setDefinition('child1', new ChildDefinition('parent')) 290 ->setDecoratedService('foo', 'foo_inner', 5) 291 ; 292 293 $this->process($container); 294 295 $this->assertEquals(['foo', 'foo_inner', 5], $container->getDefinition('child1')->getDecoratedService()); 296 } 297 298 public function testDecoratedServiceCopiesDeprecatedStatusFromParent() 299 { 300 $container = new ContainerBuilder(); 301 $container->register('deprecated_parent') 302 ->setDeprecated(true) 303 ; 304 305 $container->setDefinition('decorated_deprecated_parent', new ChildDefinition('deprecated_parent')); 306 307 $this->process($container); 308 309 $this->assertTrue($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); 310 } 311 312 public function testDecoratedServiceCanOverwriteDeprecatedParentStatus() 313 { 314 $container = new ContainerBuilder(); 315 $container->register('deprecated_parent') 316 ->setDeprecated(true) 317 ; 318 319 $container->setDefinition('decorated_deprecated_parent', new ChildDefinition('deprecated_parent')) 320 ->setDeprecated(false) 321 ; 322 323 $this->process($container); 324 325 $this->assertFalse($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); 326 } 327 328 /** 329 * @group legacy 330 */ 331 public function testProcessMergeAutowiringTypes() 332 { 333 $container = new ContainerBuilder(); 334 335 $container 336 ->register('parent') 337 ->addAutowiringType('Foo') 338 ; 339 340 $container 341 ->setDefinition('child', new ChildDefinition('parent')) 342 ->addAutowiringType('Bar') 343 ; 344 345 $this->process($container); 346 347 $childDef = $container->getDefinition('child'); 348 $this->assertEquals(['Foo', 'Bar'], $childDef->getAutowiringTypes()); 349 350 $parentDef = $container->getDefinition('parent'); 351 $this->assertSame(['Foo'], $parentDef->getAutowiringTypes()); 352 } 353 354 public function testProcessResolvesAliases() 355 { 356 $container = new ContainerBuilder(); 357 358 $container->register('parent', 'ParentClass'); 359 $container->setAlias('parent_alias', 'parent'); 360 $container->setDefinition('child', new ChildDefinition('parent_alias')); 361 362 $this->process($container); 363 364 $def = $container->getDefinition('child'); 365 $this->assertSame('ParentClass', $def->getClass()); 366 } 367 368 public function testProcessSetsArguments() 369 { 370 $container = new ContainerBuilder(); 371 372 $container->register('parent', 'ParentClass')->setArguments([0]); 373 $container->setDefinition('child', (new ChildDefinition('parent'))->setArguments([ 374 1, 375 'index_0' => 2, 376 'foo' => 3, 377 ])); 378 379 $this->process($container); 380 381 $def = $container->getDefinition('child'); 382 $this->assertSame([2, 1, 'foo' => 3], $def->getArguments()); 383 } 384 385 public function testBindings() 386 { 387 $container = new ContainerBuilder(); 388 389 $container->register('parent', 'stdClass') 390 ->setBindings(['a' => '1', 'b' => '2']) 391 ; 392 393 $container->setDefinition('child', new ChildDefinition('parent')) 394 ->setBindings(['b' => 'B', 'c' => 'C']) 395 ; 396 397 $this->process($container); 398 399 $bindings = []; 400 foreach ($container->getDefinition('child')->getBindings() as $k => $v) { 401 $bindings[$k] = $v->getValues()[0]; 402 } 403 $this->assertEquals(['b' => 'B', 'c' => 'C', 'a' => '1'], $bindings); 404 } 405 406 public function testSetAutoconfiguredOnServiceIsParent() 407 { 408 $container = new ContainerBuilder(); 409 410 $container->register('parent', 'stdClass') 411 ->setAutoconfigured(true) 412 ; 413 414 $container->setDefinition('child1', new ChildDefinition('parent')); 415 416 $this->process($container); 417 418 $this->assertFalse($container->getDefinition('child1')->isAutoconfigured()); 419 } 420 421 /** 422 * @group legacy 423 */ 424 public function testAliasExistsForBackwardsCompatibility() 425 { 426 $this->assertInstanceOf(ResolveChildDefinitionsPass::class, new ResolveDefinitionTemplatesPass()); 427 } 428 429 protected function process(ContainerBuilder $container) 430 { 431 $pass = new ResolveChildDefinitionsPass(); 432 $pass->process($container); 433 } 434 435 public function testProcessDetectsChildDefinitionIndirectCircularReference() 436 { 437 $this->expectException('Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException'); 438 $this->expectExceptionMessageMatches('/^Circular reference detected for service "c", path: "c -> b -> a -> c"./'); 439 $container = new ContainerBuilder(); 440 441 $container->register('a'); 442 443 $container->setDefinition('b', new ChildDefinition('a')); 444 $container->setDefinition('c', new ChildDefinition('b')); 445 $container->setDefinition('a', new ChildDefinition('c')); 446 447 $this->process($container); 448 } 449} 450