8000 Merge branch '4.4' into 5.1 · symfony/symfony@b8afc7c · GitHub
[go: up one dir, main page]

Skip to content

Commit b8afc7c

Browse files
committed
Merge branch '4.4' into 5.1
* 4.4: Add tests on CacheDataCollector [ProxyManagerBridge] fix tests [ProxyManagerBridge] relax fixture in tests Fix circular detection with multiple paths
2 parents 9bff2e7 + 038497c commit b8afc7c

File tree

9 files changed

+284
-6
lines changed

9 files changed

+284
-6
lines changed

src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/Fixtures/proxy-implem.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public function & __get($name)
7676

7777
$targetObject = $this->valueHolder%s;
7878

79-
$backtrace = debug_backtrace(false);
79+
$backtrace = debug_backtrace(false%S);
8080
trigger_error(
8181
sprintf(
8282
'Undefined property: %s::$%s in %s on line %s',
@@ -115,8 +115,7 @@ public function __unset($name)
115115
$targetObject = $this->valueHolder%s;
116116

117117
unset($targetObject->$name);
118-
return;
119-
}
118+
%a }
120119

121120
public function __clone()
122121
{
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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+
12+
namespace Symfony\Component\Cache\Tests\Marshaller;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Cache\Adapter\TraceableAdapter;
16+
use Symfony\Component\Cache\DataCollector\CacheDataCollector;
17+
use Symfony\Component\HttpFoundation\Request;
18+
use Symfony\Component\HttpFoundation\Response;
19+
20+
class CacheDataCollectorTest extends TestCase
21+
{
22+
private const INSTANCE_NAME = 'test';
23+
24+
public function testEmptyDataCollector()
25+
{
26+
$statistics = $this->getCacheDataCollectorStatisticsFromEvents([]);
27+
28+
$this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 0, 'calls');
29+
$this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 0, 'reads');
30+
$this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits');
31+
$this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses');
32+
$this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes');
33+
}
34+
35+
public function testOneEventDataCollector()
36+
{
37+
$traceableAdapterEvent = new \stdClass();
38+
$traceableAdapterEvent->name = 'getItem';
39+
$traceableAdapterEvent->start = 0;
40+
$traceableAdapterEvent->end = 0;
41+
$traceableAdapterEvent->hits = 0;
42+
43+
$statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]);
44+
45+
$this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls');
46+
$this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 1, 'reads');
47+
$this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits');
48+
$this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 1, 'misses');
49+
$this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes');
50+
}
51+
52+
public function testHitedEventDataCollector()
53+
{
54+
$traceableAdapterEvent = new \stdClass();
55+
$traceableAdapterEvent->name = 'hasItem';
56+
$traceableAdapterEvent->start = 0;
57+
$traceableAdapterEvent->end = 0;
58+
$traceableAdapterEvent->hits = 1;
59+
$traceableAdapterEvent->misses = 0;
60+
$traceableAdapterEvent->result = ['foo' => false];
61+
62+
$statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]);
63+
64+
$this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls');
65+
$this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 1, 'reads');
66+
$this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 1, 'hits');
67+
$this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses');
68+
$this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 0, 'writes');
69+
}
70+
71+
public function testSavedEventDataCollector()
72+
{
73+
$traceableAdapterEvent = new \stdClass();
74+
$traceableAdapterEvent->name = 'save';
75+
$traceableAdapterEvent->start = 0;
76+
$traceableAdapterEvent->end = 0;
77+
78+
$statistics = $this->getCacheDataCollectorStatisticsFromEvents([$traceableAdapterEvent]);
79+
80+
$this->assertEquals($statistics[self::INSTANCE_NAME]['calls'], 1, 'calls');
81+
$this->assertEquals($statistics[self::INSTANCE_NAME]['reads'], 0, 'reads');
82+
$this->assertEquals($statistics[self::INSTANCE_NAME]['hits'], 0, 'hits');
83+
$this->assertEquals($statistics[self::INSTANCE_NAME]['misses'], 0, 'misses');
84+
$this->assertEquals($statistics[self::INSTANCE_NAME]['writes'], 1, 'writes');
85+
}
86+
87+
private function getCacheDataCollectorStatisticsFromEvents(array $traceableAdapterEvents)
88+
{
89+
$traceableAdapterMock = $this->createMock(TraceableAdapter::class);
90+
$traceableAdapterMock->method('getCalls')->willReturn($traceableAdapterEvents);
91+
92+
$cacheDataCollector = new CacheDataCollector();
93+
$cacheDataCollector->addInstance(self::INSTANCE_NAME, $traceableAdapterMock);
94+
$cacheDataCollector->collect(new Request(), new Response());
95+
96+
return $cacheDataCollector->getStatistics();
97+
}
98+
}

src/Symfony/Component/Cache/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"symfony/config": "^4.4|^5.0",
3939
"symfony/dependency-injection": "^4.4|^5.0",
4040
"symfony/filesystem": "^4.4|^5.0",
41+
"symfony/http-kernel": "^4.4|^5.0",
4142
"symfony/var-dumper": "^4.4|^5.0"
4243
},
4344
"conflict": {

src/Symfony/Component/DependencyInjection/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]);

src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

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

1400+
$pA = $container->get('pA');
1401+
$this->assertEquals(new \stdClass(), $pA);
1402+
14001403
$logger = $container->get('monolog.logger');
14011404
$this->assertEquals(new \stdClass(), $logger->handler);
14021405

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

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

10621062
$container = new $container();
10631063

1064+
$pA = $container->get('pA');
1065+
$this->assertEquals(new \stdClass(), $pA);
1066+
10641067
$logger = $container->get('monolog.logger');
10651068
$this->assertEquals(new \stdClass(), $logger->handler);
10661069

src/Symfony/Component/DependencyInjection/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)

src/Symfony/Component/DependencyInjection/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
}

0 commit comments

Comments
 (0)
0