Skip to content

Commit 32ac2c7

Browse files
committed
[5.8] Container - lazy load tagged services
1 parent 440c298 commit 32ac2c7

File tree

5 files changed

+163
-14
lines changed

5 files changed

+163
-14
lines changed

src/Illuminate/Container/Container.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -454,19 +454,19 @@ public function tag($abstracts, $tags)
454454
* Resolve all of the bindings for a given tag.
455455
*
456456
* @param string $tag
457-
* @return array
457+
* @return iterable
458458
*/
459459
public function tagged($tag)
460460
{
461-
$results = [];
461+
if (! isset($this->tags[$tag])) {
462+
return [];
463+
}
462464

463-
if (isset($this->tags[$tag])) {
465+
return new RewindableGenerator(function () use ($tag) {
464466
foreach ($this->tags[$tag] as $abstract) {
465-
$results[] = $this->make($abstract);
467+
yield $this->make($abstract);
466468
}
467-
}
468-
469-
return $results;
469+
}, count($this->tags[$tag]));
470470
}
471471

472472
/**
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Illuminate\Container;
4+
5+
use Countable;
6+
use IteratorAggregate;
7+
8+
class RewindableGenerator implements Countable, IteratorAggregate
9+
{
10+
/**
11+
* @var callable
12+
*/
13+
private $generator;
14+
15+
/**
16+
* @var callable|int
17+
*/
18+
private $count;
19+
20+
/**
21+
* @param callable $generator
22+
* @param callable|int $count
23+
*/
24+
public function __construct(callable $generator, $count)
25+
{
26+
$this->generator = $generator;
27+
$this->count = $count;
28+
}
29+
30+
public function getIterator()
31+
{
32+
$generator = $this->generator;
33+
34+
return $generator();
35+
}
36+
37+
/**
38+
* @return int
39+
*/
40+
public function count()
41+
{
42+
if (is_callable($count = $this->count)) {
43+
$this->count = $count();
44+
}
45+
46+
return $this->count;
47+
}
48+
}

src/Illuminate/Contracts/Container/Container.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function tag($abstracts, $tags);
3838
* Resolve all of the bindings for a given tag.
3939
*
4040
* @param string $tag
41-
* @return array
41+
* @return iterable
4242
*/
4343
public function tagged($tag);
4444

tests/Container/ContainerTest.php

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -792,17 +792,77 @@ public function testContainerTags()
792792

793793
$this->assertCount(1, $container->tagged('bar'));
794794
$this->assertCount(2, $container->tagged('foo'));
795-
$this->assertInstanceOf(ContainerImplementationStub::class, $container->tagged('foo')[0]);
796-
$this->assertInstanceOf(ContainerImplementationStub::class, $container->tagged('bar')[0]);
797-
$this->assertInstanceOf(ContainerImplementationStubTwo::class, $container->tagged('foo')[1]);
795+
796+
$fooResults = [];
797+
foreach ($container->tagged('foo') as $foo) {
798+
$fooResults[] = $foo;
799+
}
800+
801+
$barResults = [];
802+
foreach ($container->tagged('bar') as $bar) {
803+
$barResults[] = $bar;
804+
}
805+
806+
$this->assertInstanceOf(ContainerImplementationStub::class, $fooResults[0]);
807+
$this->assertInstanceOf(ContainerImplementationStub::class, $barResults[0]);
808+
$this->assertInstanceOf(ContainerImplementationStubTwo::class, $fooResults[1]);
798809

799810
$container = new Container;
800811
$container->tag([ContainerImplementationStub::class, ContainerImplementationStubTwo::class], ['foo']);
801812
$this->assertCount(2, $container->tagged('foo'));
802-
$this->assertInstanceOf(ContainerImplementationStub::class, $container->tagged('foo')[0]);
803-
$this->assertInstanceOf(ContainerImplementationStubTwo::class, $container->tagged('foo')[1]);
804813

805-
$this->assertEmpty($container->tagged('this_tag_does_not_exist'));
814+
$fooResults = [];
815+
foreach ($container->tagged('foo') as $foo) {
816+
$fooResults[] = $foo;
817+
}
818+
819+
$this->assertInstanceOf(ContainerImplementationStub::class, $fooResults[0]);
820+
$this->assertInstanceOf(ContainerImplementationStubTwo::class, $fooResults[1]);
821+
822+
$this->assertCount(0, $container->tagged('this_tag_does_not_exist'));
823+
}
824+
825+
public function testTaggedServicesAreLazyLoaded()
826+
{
827+
$container = $this->createPartialMock(Container::class, ['make']);
828+
$container->expects($this->once())->method('make')->willReturn(new ContainerImplementationStub());
829+
830+
$container->tag(ContainerImplementationStub::class, ['foo']);
831+
$container->tag(ContainerImplementationStubTwo::class, ['foo']);
832+
833+
$fooResults = [];
834+
foreach ($container->tagged('foo') as $foo) {
835+
$fooResults[] = $foo;
836+
break;
837+
}
838+
839+
$this->assertCount(2, $container->tagged('foo'));
840+
$this->assertInstanceOf(ContainerImplementationStub::class, $fooResults[0]);
841+
}
842+
843+
public function testLazyLoadedTaggedServicesCanBeLoopedOverMultipleTimes()
844+
{
845+
$container = new Container;
846+
$container->tag(ContainerImplementationStub::class, 'foo');
847+
$container->tag(ContainerImplementationStubTwo::class, ['foo']);
848+
849+
$services = $container->tagged('foo');
850+
851+
$fooResults = [];
852+
foreach ($services as $foo) {
853+
$fooResults[] = $foo;
854+
}
855+
856+
$this->assertInstanceOf(ContainerImplementationStub::class, $fooResults[0]);
857+
$this->assertInstanceOf(ContainerImplementationStubTwo::class, $fooResults[1]);
858+
859+
$fooResults = [];
860+
foreach ($services as $foo) {
861+
$fooResults[] = $foo;
862+
}
863+
864+
$this->assertInstanceOf(ContainerImplementationStub::class, $fooResults[0]);
865+
$this->assertInstanceOf(ContainerImplementationStubTwo::class, $fooResults[1]);
806866
}
807867

808868
public function testForgetInstanceForgetsInstance()
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Container;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Illuminate\Container\RewindableGenerator;
7+
8+
class RewindableGeneratorTest extends TestCase
9+
{
10+
public function testCountUsesProvidedValue()
11+
{
12+
$generator = new RewindableGenerator(function () {
13+
yield 'foo';
14+
}, 999);
15+
16+
$this->assertSame(999, count($generator));
17+
}
18+
19+
public function testCountUsesProvidedValueAsCallback()
20+
{
21+
$called = 0;
22+
23+
$generator = new RewindableGenerator(function () {
24+
yield 'foo';
25+
}, function () use (&$called) {
26+
$called++;
27+
28+
return 500;
29+
});
30+
31+
// the count callback is called lazily
32+
$this->assertSame(0, $called);
33+
34+
$this->assertCount(500, $generator);
35+
36+
count($generator);
37+
38+
// the count callback is called only once
39+
$this->assertSame(1, $called);
40+
}
41+
}

0 commit comments

Comments
 (0)