[DI] Allow to count on lazy collection arguments
This commit is contained in:
parent
2183f98f54
commit
f23e460fad
@ -14,13 +14,19 @@ namespace Symfony\Component\DependencyInjection\Argument;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class RewindableGenerator implements \IteratorAggregate
|
||||
class RewindableGenerator implements \IteratorAggregate, \Countable
|
||||
{
|
||||
private $generator;
|
||||
private $count;
|
||||
|
||||
public function __construct(callable $generator)
|
||||
/**
|
||||
* @param callable $generator
|
||||
* @param int|callable $count
|
||||
*/
|
||||
public function __construct(callable $generator, $count)
|
||||
{
|
||||
$this->generator = $generator;
|
||||
$this->count = $count;
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
@ -29,4 +35,13 @@ class RewindableGenerator implements \IteratorAggregate
|
||||
|
||||
return $g();
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
if (is_callable($count = $this->count)) {
|
||||
$this->count = $count();
|
||||
}
|
||||
|
||||
return $this->count;
|
||||
}
|
||||
}
|
||||
|
@ -1000,6 +1000,19 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
|
||||
yield $k => $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($v)));
|
||||
}
|
||||
}, function () use ($value) {
|
||||
$count = 0;
|
||||
foreach ($value->getValues() as $v) {
|
||||
foreach (self::getServiceConditionals($v) as $s) {
|
||||
if (!$this->has($s)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
++$count;
|
||||
}
|
||||
|
||||
return $count;
|
||||
});
|
||||
} elseif ($value instanceof ClosureProxyArgument) {
|
||||
$parameterBag = $this->getParameterBag();
|
||||
|
@ -1362,19 +1362,36 @@ EOF;
|
||||
*/
|
||||
private function wrapServiceConditionals($value, $code, &$isUnconditional = null, $containerRef = '$this')
|
||||
{
|
||||
if ($isUnconditional = !$services = ContainerBuilder::getServiceConditionals($value)) {
|
||||
if ($isUnconditional = !$condition = $this->getServiceConditionals($value, $containerRef)) {
|
||||
return $code;
|
||||
}
|
||||
|
||||
// re-indent the wrapped code
|
||||
$code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code)));
|
||||
|
||||
return sprintf(" if (%s) {\n%s }\n", $condition, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the conditions to execute for conditional services.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $containerRef
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
private function getServiceConditionals($value, $containerRef = '$this')
|
||||
{
|
||||
if (!$services = ContainerBuilder::getServiceConditionals($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$conditions = array();
|
||||
foreach ($services as $service) {
|
||||
$conditions[] = sprintf("%s->has('%s')", $containerRef, $service);
|
||||
}
|
||||
|
||||
// re-indent the wrapped code
|
||||
$code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code)));
|
||||
|
||||
return sprintf(" if (%s) {\n%s }\n", implode(' && ', $conditions), $code);
|
||||
return implode(' && ', $conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1524,9 +1541,14 @@ EOF;
|
||||
|
||||
return sprintf('array(%s)', implode(', ', $code));
|
||||
} elseif ($value instanceof IteratorArgument) {
|
||||
$countCode = array();
|
||||
$countCode[] = 'function () {';
|
||||
$operands = array(0);
|
||||
|
||||
$code = array();
|
||||
$code[] = 'new RewindableGenerator(function() {';
|
||||
$code[] = 'new RewindableGenerator(function () {';
|
||||
foreach ($value->getValues() as $k => $v) {
|
||||
($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0];
|
||||
$v = $this->wrapServiceConditionals($v, sprintf(" yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)));
|
||||
foreach (explode("\n", $v) as $v) {
|
||||
if ($v) {
|
||||
@ -1534,7 +1556,11 @@ EOF;
|
||||
}
|
||||
}
|
||||
}
|
||||
$code[] = ' })';
|
||||
|
||||
$countCode[] = sprintf(' return %s;', implode(' + ', $operands));
|
||||
$countCode[] = ' }';
|
||||
|
||||
$code[] = sprintf(' }, %s)', count($operands) > 1 ? implode("\n", $countCode) : $operands[0]);
|
||||
|
||||
return implode("\n", $code);
|
||||
} elseif ($value instanceof Definition) {
|
||||
|
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Argument;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
||||
|
||||
class RewindableGeneratorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testImplementsCountable()
|
||||
{
|
||||
$this->assertInstanceOf(\Countable::class, new RewindableGenerator(function () {
|
||||
yield 1;
|
||||
}, 1));
|
||||
}
|
||||
|
||||
public function testCountUsesProvidedValue()
|
||||
{
|
||||
$generator = new RewindableGenerator(function () {
|
||||
yield 1;
|
||||
}, 3);
|
||||
|
||||
$this->assertCount(3, $generator);
|
||||
}
|
||||
|
||||
public function testCountUsesProvidedValueAsCallback()
|
||||
{
|
||||
$called = 0;
|
||||
$generator = new RewindableGenerator(function () {
|
||||
yield 1;
|
||||
}, function () use (&$called) {
|
||||
++$called;
|
||||
|
||||
return 3;
|
||||
});
|
||||
|
||||
$this->assertSame(0, $called, 'Count callback is called lazily');
|
||||
$this->assertCount(3, $generator);
|
||||
|
||||
count($generator);
|
||||
|
||||
$this->assertSame(1, $called, 'Count callback is called only once');
|
||||
}
|
||||
}
|
@ -422,6 +422,7 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$lazyContext = $builder->get('lazy_context');
|
||||
$this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyValues);
|
||||
$this->assertCount(1, $lazyContext->lazyValues);
|
||||
|
||||
$i = 0;
|
||||
foreach ($lazyContext->lazyValues as $k => $v) {
|
||||
|
@ -314,13 +314,13 @@ class ProjectServiceContainer extends Container
|
||||
*/
|
||||
protected function getLazyContextService()
|
||||
{
|
||||
return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function() {
|
||||
return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () {
|
||||
yield 0 => 'foo';
|
||||
yield 1 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->get('foo.baz')) && false ?: '_'};
|
||||
yield 2 => array($this->getParameter('foo') => 'foo is '.$this->getParameter('foo').'', 'foobar' => $this->getParameter('foo'));
|
||||
yield 3 => true;
|
||||
yield 4 => $this;
|
||||
}));
|
||||
}, 5));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -333,11 +333,13 @@ class ProjectServiceContainer extends Container
|
||||
*/
|
||||
protected function getLazyContextIgnoreInvalidRefService()
|
||||
{
|
||||
return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function() {
|
||||
return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () {
|
||||
yield 0 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->get('foo.baz')) && false ?: '_'};
|
||||
if ($this->has('invalid')) {
|
||||
yield 1 => $this->get('invalid', ContainerInterface::NULL_ON_INVALID_REFERENCE);
|
||||
}
|
||||
}, function () {
|
||||
return 1 + (int) ($this->has('invalid'));
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -313,13 +313,13 @@ class ProjectServiceContainer extends Container
|
||||
*/
|
||||
protected function getLazyContextService()
|
||||
{
|
||||
return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function() {
|
||||
return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () {
|
||||
yield 0 => 'foo';
|
||||
yield 1 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->get('foo.baz')) && false ?: '_'};
|
||||
yield 2 => array('bar' => 'foo is bar', 'foobar' => 'bar');
|
||||
yield 3 => true;
|
||||
yield 4 => $this;
|
||||
}));
|
||||
}, 5));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -332,9 +332,9 @@ class ProjectServiceContainer extends Container
|
||||
*/
|
||||
protected function getLazyContextIgnoreInvalidRefService()
|
||||
{
|
||||
return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function() {
|
||||
return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () {
|
||||
yield 0 => ${($_ = isset($this->services['foo.baz']) ? $this->services['foo.baz'] : $this->get('foo.baz')) && false ?: '_'};
|
||||
}));
|
||||
}, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user