Skip to content

Commit 104d775

Browse files
committed
Merge branch '5.1' into 5.2
* 5.1: Add tests on CacheDataCollector [ProxyManagerBridge] fix tests [ProxyManagerBridge] relax fixture in tests Fix circular detection with multiple paths
2 parents ff71914 + 1bfc66e commit 104d775

File tree

6 files changed

+183
-3
lines changed

6 files changed

+183
-3
lines changed

Dumper/PhpDumper.php

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,32 +433,66 @@ private function analyzeReferences()
433433
$this->singleUsePrivateIds = array_diff_key($this->singleUsePrivateIds, $this->circularReferences);
434434
}
435435

436-
private function collectCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array $path = [], bool $byConstructor = true): void
436+
private function collectCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array &$loops = [], array $path = [], bool $byConstructor = true): void
437437
{
438438
$path[$sourceId] = $byConstructor;
439439
$checkedNodes[$sourceId] = true;
440440
foreach ($edges as $edge) {
441441
$node = $edge->getDestNode();
442442
$id = $node->getId();
443-
444443
if (!($definition = $node->getValue()) instanceof Definition || $sourceId === $id || ($edge->isLazy() && ($this->proxyDumper ?? $this->getProxyDumper())->isProxyCandidate($definition)) || $edge->isWeak()) {
445444
continue;
446445
}
447446

448447
if (isset($path[$id])) {
449448
$loop = null;
450449
$loopByConstructor = $edge->isReferencedByConstructor();
450+
$pathInLoop = [$id, []];
451451
foreach ($path as $k => $pathByConstructor) {
452452
if (null !== $loop) {
453453
$loop[] = $k;
454+
$pathInLoop[1][$k] = $pathByConstructor;
455+
$loops[$k][] = &$pathInLoop;
454456
$loopByConstructor = $loopByConstructor && $pathByConstructor;
455457
} elseif ($k === $id) {
456458
$loop = [];
457459
}
458460
}
459461
$this->addCircularReferences($id, $loop, $loopByConstructor);
460462
} elseif (!isset($checkedNodes[$id])) {
461-
$this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes, $path, $edge->isReferencedByConstructor());
463+
$this->collectCircularReferences($id, $node->getOutEdges(), $checkedNodes, $loops, $path, $edge->isReferencedByConstructor());
464+
} elseif (isset($loops[$id])) {
465+
// we already had detected loops for this edge
466+
// let's check if we have a common ancestor in one of the detected loops
467+
foreach ($loops[$id] as [$first, $loopPath]) {
468+
if (!isset($path[$first])) {
469+
continue;
470+
}
471+
// We have a common ancestor, let's fill the current path
472+
$fillPath = null;
473+
foreach ($loopPath as $k => $pathByConstructor) {
474+
if (null !== $fillPath) {
475+
$fillPath[$k] = $pathByConstructor;
476+
} elseif ($k === $id) {
477+
$fillPath = $path;
478+
$fillPath[$k] = $pathByConstructor;
479+
}
480+
}
481+
482+
// we can now build the loop
483+
$loop = null;
484+
$loopByConstructor = $edge->isReferencedByConstructor();
485+
foreach ($fillPath as $k => $pathByConstructor) {
486+
if (null !== $loop) {
487+
$loop[] = $k;
488+
$loopByConstructor = $loopByConstructor && $pathByConstructor;
489+
} elseif ($k === $first) {
490+
$loop = [];
491+
}
492+
}
493+
$this->addCircularReferences($first, $loop, true);
494+
break;
495+
}
462496
}
463497
}
464498
unset($path[$sourceId]);

Tests/ContainerBuilderTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,6 +1395,9 @@ public function testAlmostCircular($visibility)
13951395
$container = include __DIR__.'/Fixtures/containers/container_almost_circular.php';
13961396
$container->compile();
13971397

1398+
$pA = $container->get('pA');
1399+
$this->assertEquals(new \stdClass(), $pA);
1400+
13981401
$logger = $container->get('monolog.logger');
13991402
$this->assertEquals(new \stdClass(), $logger->handler);
14001403

Tests/Dumper/PhpDumperTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,9 @@ public function testAlmostCircular($visibility)
10541054

10551055
$container = new $container();
10561056

1057+
$pA = $container->get('pA');
1058+
$this->assertEquals(new \stdClass(), $pA);
1059+
10571060
$logger = $container->get('monolog.logger');
10581061
$this->assertEquals(new \stdClass(), $logger->handler);
10591062

Tests/Fixtures/containers/container_almost_circular.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@
99
$public = 'public' === $visibility;
1010
$container = new ContainerBuilder();
1111

12+
// multiple path detection
13+
14+
$container->register('pA', 'stdClass')->setPublic(true)
15+
->addArgument(new Reference('pB'))
16+
->addArgument(new Reference('pC'));
17+
18+
$container->register('pB', 'stdClass')->setPublic($public)
19+
->setProperty('d', new Reference('pD'));
20+
$container->register('pC', 'stdClass')->setPublic($public)
21+
->setLazy(true)
22+
->setProperty('d', new Reference('pD'));
23+
24+
$container->register('pD', 'stdClass')->setPublic($public)
25+
->addArgument(new Reference('pA'));
26+
1227
// monolog-like + handler that require monolog
1328

1429
$container->register('monolog.logger', 'stdClass')->setPublic(true)

Tests/Fixtures/php/services_almost_circular_private.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public function __construct()
3737
'manager2' => 'getManager2Service',
3838
'manager3' => 'getManager3Service',
3939
'monolog.logger' => 'getMonolog_LoggerService',
40+
'pA' => 'getPAService',
4041
'root' => 'getRootService',
4142
'subscriber' => 'getSubscriberService',
4243
];
@@ -84,6 +85,9 @@ public function getRemovedIds(): array
8485
'manager4' => true,
8586
'monolog.logger_2' => true,
8687
'multiuse1' => true,
88+
'pB' => true,
89+
'pC' => true,
90+
'pD' => true,
8791
'subscriber2' => true,
8892
];
8993
}
@@ -371,6 +375,28 @@ protected function getMonolog_LoggerService()
371375
return $instance;
372376
}
373377

378+
/**
379+
* Gets the public 'pA' shared service.
380+
*
381+
* @return \stdClass
382+
*/
383+
protected function getPAService()
384+
{
385+
$a = new \stdClass();
386+
387+
$b = ($this->privates['pC'] ?? $this->getPCService());
388+
389+
if (isset($this->services['pA'])) {
390+
return $this->services['pA'];
391+
}
392+
393+
$this->services['pA'] = $instance = new \stdClass($a, $b);
394+
395+
$a->d = ($this->privates['pD'] ?? $this->getPDService());
396+
397+
return $instance;
398+
}
399+
374400
/**
375401
* Gets the public 'root' shared service.
376402
*
@@ -478,4 +504,34 @@ protected function getManager4Service($lazyLoad = true)
478504

479505
return $instance;
480506
}
507+
508+
/**
509+
* Gets the private 'pC' shared service.
510+
*
511+
* @return \stdClass
512+
*/
513+
protected function getPCService($lazyLoad = true)
514+
{
515+
$this->privates['pC'] = $instance = new \stdClass();
516+
517+
$instance->d = ($this->privates['pD'] ?? $this->getPDService());
518+
519+
return $instance;
520+
}
521+
522+
/**
523+
* Gets the private 'pD' shared service.
524+
*
525+
* @return \stdClass
526+
*/
527+
protected function getPDService()
528+
{
529+
$a = ($this->services['pA'] ?? $this->getPAService());
530+
531+
if (isset($this->privates['pD'])) {
532+
return $this->privates['pD'];
533+
}
534+
535+
return $this->privates['pD'] = new \stdClass($a);
536+
}
481537
}

Tests/Fixtures/php/services_almost_circular_public.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public function __construct()
5050
'manager3' => 'getManager3Service',
5151
'monolog.logger' => 'getMonolog_LoggerService',
5252
'monolog.logger_2' => 'getMonolog_Logger2Service',
53+
'pA' => 'getPAService',
54+
'pB' => 'getPBService',
55+
'pC' => 'getPCService',
56+
'pD' => 'getPDService',
5357
'root' => 'getRootService',
5458
'subscriber' => 'getSubscriberService',
5559
];
@@ -559,6 +563,71 @@ protected function getMonolog_Logger2Service()
559563
return $instance;
560564
}
561565

566+
/**
567+
* Gets the public 'pA' shared service.
568+
*
569+
* @return \stdClass
570+
*/
571+
protected function getPAService()
572+
{
573+
$a = ($this->services['pB'] ?? $this->getPBService());
574+
575+
if (isset($this->services['pA'])) {
576+
return $this->services['pA'];
577+
}
578+
$b = ($this->services['pC'] ?? $this->getPCService());
579+
580+
if (isset($this->services['pA'])) {
581+
return $this->services['pA'];
582+
}
583+
584+
return $this->services['pA'] = new \stdClass($a, $b);
585+
}
586+
587+
/**
588+
* Gets the public 'pB' shared service.
589+
*
590+
* @return \stdClass
591+
*/
592+
protected function getPBService()
593+
{
594+
$this->services['pB'] = $instance = new \stdClass();
595+
596+
$instance->d = ($this->services['pD'] ?? $this->getPDService());
597+
598+
return $instance;
599+
}
600+
601+
/**
602+
* Gets the public 'pC' shared service.
603+
*
604+
* @return \stdClass
605+
*/
606+
protected function getPCService($lazyLoad = true)
607+
{
608+
$this->services['pC'] = $instance = new \stdClass();
609+
610+
$instance->d = ($this->services['pD'] ?? $this->getPDService());
611+
612+
return $instance;
613+
}
614+
615+
/**
616+
* Gets the public 'pD' shared service.
617+
*
618+
* @return \stdClass
619+
*/
620+
protected function getPDService()
621+
{
622+
$a = ($this->services['pA'] ?? $this->getPAService());
623+
624+
if (isset($this->services['pD'])) {
625+
return $this->services['pD'];
626+
}
627+
628+
return $this->services['pD'] = new \stdClass($a);
629+
}
630+
562631
/**
563632
* Gets the public 'root' shared service.
564633
*

0 commit comments

Comments
 (0)