[DI] Add ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE
This commit is contained in:
parent
80ac529742
commit
0db3358ddb
@ -4,6 +4,7 @@ CHANGELOG
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* added support for ignore-on-uninitialized references
|
||||
* deprecated service auto-registration while autowiring
|
||||
* deprecated the ability to check for the initialization of a private service with the `Container::initialized()` method
|
||||
* deprecated support for top-level anonymous services in XML
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\ExpressionLanguage;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
@ -96,7 +97,8 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
|
||||
$this->getDefinitionId((string) $value),
|
||||
$targetDefinition,
|
||||
$value,
|
||||
$this->lazy || ($targetDefinition && $targetDefinition->isLazy())
|
||||
$this->lazy || ($targetDefinition && $targetDefinition->isLazy()),
|
||||
ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()
|
||||
);
|
||||
|
||||
return $value;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\DependencyInjection\Compiler;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
@ -24,14 +25,16 @@ class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass
|
||||
{
|
||||
protected function processValue($value, $isRoot = false)
|
||||
{
|
||||
if ($value instanceof Reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
|
||||
$destId = (string) $value;
|
||||
|
||||
if (!$this->container->has($destId)) {
|
||||
throw new ServiceNotFoundException($destId, $this->currentId);
|
||||
}
|
||||
if (!$value instanceof Reference) {
|
||||
return parent::processValue($value, $isRoot);
|
||||
}
|
||||
if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior() && !$this->container->has($id = (string) $value)) {
|
||||
throw new ServiceNotFoundException($id, $this->currentId);
|
||||
}
|
||||
if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior() && $this->container->has($id = (string) $value) && !$this->container->findDefinition($id)->isShared()) {
|
||||
throw new InvalidArgumentException(sprintf('Invalid ignore-on-uninitialized reference found in service "%s": target service "%s" is not shared.', $this->currentId, $id));
|
||||
}
|
||||
|
||||
return parent::processValue($value, $isRoot);
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,9 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements Repe
|
||||
|
||||
$ids = array();
|
||||
foreach ($graph->getNode($id)->getInEdges() as $edge) {
|
||||
if ($edge->isWeak()) {
|
||||
return false;
|
||||
}
|
||||
$ids[] = $edge->getSourceNode()->getId();
|
||||
}
|
||||
|
||||
|
@ -74,6 +74,9 @@ class RegisterServiceSubscribersPass extends AbstractRecursivePass
|
||||
if ($optionalBehavior = '?' === $type[0]) {
|
||||
$type = substr($type, 1);
|
||||
$optionalBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
|
||||
} elseif ($optionalBehavior = '!' === $type[0]) {
|
||||
$type = substr($type, 1);
|
||||
$optionalBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
|
||||
}
|
||||
if (is_int($key)) {
|
||||
$key = $type;
|
||||
|
@ -50,6 +50,9 @@ class RemoveUnusedDefinitionsPass implements RepeatablePassInterface
|
||||
$referencingAliases = array();
|
||||
$sourceIds = array();
|
||||
foreach ($edges as $edge) {
|
||||
if ($edge->isWeak()) {
|
||||
continue;
|
||||
}
|
||||
$node = $edge->getSourceNode();
|
||||
$sourceIds[] = $node->getId();
|
||||
|
||||
|
@ -20,6 +20,8 @@ use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
* it themselves which improves performance quite a lot.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*
|
||||
* @final since version 3.4
|
||||
*/
|
||||
class ServiceReferenceGraph
|
||||
{
|
||||
@ -85,23 +87,16 @@ class ServiceReferenceGraph
|
||||
* @param string $destValue
|
||||
* @param string $reference
|
||||
* @param bool $lazy
|
||||
* @param bool $weak
|
||||
*/
|
||||
public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null/*, bool $lazy = false*/)
|
||||
public function connect($sourceId, $sourceValue, $destId, $destValue = null, $reference = null/*, bool $lazy = false, bool $weak = false*/)
|
||||
{
|
||||
if (func_num_args() >= 6) {
|
||||
$lazy = func_get_arg(5);
|
||||
} else {
|
||||
if (__CLASS__ !== get_class($this)) {
|
||||
$r = new \ReflectionMethod($this, __FUNCTION__);
|
||||
if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
|
||||
@trigger_error(sprintf('Method %s() will have a 6th `bool $lazy = false` argument in version 4.0. Not defining it is deprecated since 3.3.', __METHOD__), E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
$lazy = false;
|
||||
}
|
||||
$lazy = func_num_args() >= 6 ? func_get_arg(5) : false;
|
||||
$weak = func_num_args() >= 7 ? func_get_arg(6) : false;
|
||||
|
||||
$sourceNode = $this->createNode($sourceId, $sourceValue);
|
||||
$destNode = $this->createNode($destId, $destValue);
|
||||
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy);
|
||||
$edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak);
|
||||
|
||||
$sourceNode->addOutEdge($edge);
|
||||
$destNode->addInEdge($edge);
|
||||
|
@ -24,19 +24,22 @@ class ServiceReferenceGraphEdge
|
||||
private $destNode;
|
||||
private $value;
|
||||
private $lazy;
|
||||
private $weak;
|
||||
|
||||
/**
|
||||
* @param ServiceReferenceGraphNode $sourceNode
|
||||
* @param ServiceReferenceGraphNode $destNode
|
||||
* @param string $value
|
||||
* @param bool $lazy
|
||||
* @param bool $weak
|
||||
*/
|
||||
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, $lazy = false)
|
||||
public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, $lazy = false, $weak = false)
|
||||
{
|
||||
$this->sourceNode = $sourceNode;
|
||||
$this->destNode = $destNode;
|
||||
$this->value = $value;
|
||||
$this->lazy = $lazy;
|
||||
$this->weak = $weak;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,4 +81,14 @@ class ServiceReferenceGraphEdge
|
||||
{
|
||||
return $this->lazy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the edge is weak, meaning it shouldn't prevent removing the target service.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isWeak()
|
||||
{
|
||||
return $this->weak;
|
||||
}
|
||||
}
|
||||
|
@ -23,26 +23,15 @@ use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
* Container is a dependency injection container.
|
||||
*
|
||||
* It gives access to object instances (services).
|
||||
*
|
||||
* Services and parameters are simple key/pair stores.
|
||||
*
|
||||
* Parameter and service keys are case insensitive.
|
||||
*
|
||||
* A service can also be defined by creating a method named
|
||||
* getXXXService(), where XXX is the camelized version of the id:
|
||||
*
|
||||
* <ul>
|
||||
* <li>request -> getRequestService()</li>
|
||||
* <li>mysql_session_storage -> getMysqlSessionStorageService()</li>
|
||||
* <li>symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()</li>
|
||||
* </ul>
|
||||
*
|
||||
* The container can have three possible behaviors when a service does not exist:
|
||||
* The container can have four possible behaviors when a service
|
||||
* does not exist (or is not initialized for the last case):
|
||||
*
|
||||
* * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
|
||||
* * NULL_ON_INVALID_REFERENCE: Returns null
|
||||
* * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference
|
||||
* (for instance, ignore a setter if the service does not exist)
|
||||
* * IGNORE_ON_UNINITIALIZED_REFERENCE: Ignores/returns null for uninitialized services or invalid references
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
@ -304,9 +293,9 @@ class Container implements ResettableContainerInterface
|
||||
|
||||
try {
|
||||
if (isset($this->fileMap[$id])) {
|
||||
return $this->load($this->fileMap[$id]);
|
||||
return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->load($this->fileMap[$id]);
|
||||
} elseif (isset($this->methodMap[$id])) {
|
||||
return $this->{$this->methodMap[$id]}();
|
||||
return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->{$this->methodMap[$id]}();
|
||||
} elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
|
||||
$id = $normalizedId;
|
||||
continue;
|
||||
@ -315,7 +304,7 @@ class Container implements ResettableContainerInterface
|
||||
// and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
|
||||
@trigger_error('Generating a dumped container without populating the method map is deprecated since 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', E_USER_DEPRECATED);
|
||||
|
||||
return $this->{$method}();
|
||||
return self::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior ? null : $this->{$method}();
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -565,6 +565,9 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
{
|
||||
$id = $this->normalizeId($id);
|
||||
|
||||
if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) {
|
||||
return parent::get($id, $invalidBehavior);
|
||||
}
|
||||
if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
|
||||
return $service;
|
||||
}
|
||||
@ -1160,6 +1163,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
foreach (self::getInitializedConditionals($v) as $s) {
|
||||
if (!$this->get($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
yield $k => $this->resolveServices($v);
|
||||
}
|
||||
@ -1171,6 +1179,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
foreach (self::getInitializedConditionals($v) as $s) {
|
||||
if (!$this->get($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
++$count;
|
||||
}
|
||||
@ -1397,6 +1410,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
* @param mixed $value An array of conditionals to return
|
||||
*
|
||||
* @return array An array of Service conditionals
|
||||
*
|
||||
* @internal since version 3.4
|
||||
*/
|
||||
public static function getServiceConditionals($value)
|
||||
{
|
||||
@ -1413,6 +1428,30 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
return $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the initialized conditionals.
|
||||
*
|
||||
* @param mixed $value An array of conditionals to return
|
||||
*
|
||||
* @return array An array of uninitialized conditionals
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function getInitializedConditionals($value)
|
||||
{
|
||||
$services = array();
|
||||
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $v) {
|
||||
$services = array_unique(array_merge($services, self::getInitializedConditionals($v)));
|
||||
}
|
||||
} elseif ($value instanceof Reference && $value->getInvalidBehavior() === ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE) {
|
||||
$services[] = (string) $value;
|
||||
}
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a reasonably unique hash of a value.
|
||||
*
|
||||
@ -1465,13 +1504,16 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
|
||||
|
||||
private function callMethod($service, $call)
|
||||
{
|
||||
$services = self::getServiceConditionals($call[1]);
|
||||
|
||||
foreach ($services as $s) {
|
||||
foreach (self::getServiceConditionals($call[1]) as $s) {
|
||||
if (!$this->has($s)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
foreach (self::getInitializedConditionals($call[1]) as $s) {
|
||||
if (!$this->get($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1]))));
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ interface ContainerInterface extends PsrContainerInterface
|
||||
const EXCEPTION_ON_INVALID_REFERENCE = 1;
|
||||
const NULL_ON_INVALID_REFERENCE = 2;
|
||||
const IGNORE_ON_INVALID_REFERENCE = 3;
|
||||
const IGNORE_ON_UNINITIALIZED_REFERENCE = 4;
|
||||
|
||||
/**
|
||||
* Sets a service.
|
||||
|
@ -277,7 +277,7 @@ EOF;
|
||||
if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id]) {
|
||||
$code .= sprintf($template, $name, $this->getServiceCall($id));
|
||||
} else {
|
||||
$code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)));
|
||||
$code .= sprintf($template, $name, $this->getServiceCall($id, new Reference($id, $behavior[$id])));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1295,12 +1295,14 @@ EOF;
|
||||
*/
|
||||
private function getServiceConditionals($value)
|
||||
{
|
||||
if (!$services = ContainerBuilder::getServiceConditionals($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$conditions = array();
|
||||
foreach ($services as $service) {
|
||||
foreach (ContainerBuilder::getInitializedConditionals($value) as $service) {
|
||||
if (!$this->container->hasDefinition($service)) {
|
||||
return 'false';
|
||||
}
|
||||
$conditions[] = sprintf("isset(\$this->services['%s'])", $service);
|
||||
}
|
||||
foreach (ContainerBuilder::getServiceConditionals($value) as $service) {
|
||||
if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) {
|
||||
continue;
|
||||
}
|
||||
@ -1335,8 +1337,8 @@ EOF;
|
||||
}
|
||||
if (!isset($behavior[$id])) {
|
||||
$behavior[$id] = $argument->getInvalidBehavior();
|
||||
} elseif (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $behavior[$id]) {
|
||||
$behavior[$id] = $argument->getInvalidBehavior();
|
||||
} else {
|
||||
$behavior[$id] = min($behavior[$id], $argument->getInvalidBehavior());
|
||||
}
|
||||
|
||||
++$calls[$id];
|
||||
@ -1665,7 +1667,9 @@ EOF;
|
||||
return '$this';
|
||||
}
|
||||
|
||||
if ($this->asFiles && $this->container->hasDefinition($id)) {
|
||||
if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) {
|
||||
$code = 'null';
|
||||
} elseif ($this->asFiles && $this->container->hasDefinition($id)) {
|
||||
if ($this->container->getDefinition($id)->isShared()) {
|
||||
$code = sprintf("\$this->load(__DIR__.'/%s.php')", $this->generateMethodName($id));
|
||||
} else {
|
||||
|
@ -309,6 +309,8 @@ class XmlDumper extends Dumper
|
||||
$element->setAttribute('on-invalid', 'null');
|
||||
} elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) {
|
||||
$element->setAttribute('on-invalid', 'ignore');
|
||||
} elseif ($behaviour == ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE) {
|
||||
$element->setAttribute('on-invalid', 'ignore_uninitialized');
|
||||
}
|
||||
} elseif ($value instanceof Definition) {
|
||||
$element->setAttribute('type', 'service');
|
||||
|
@ -304,8 +304,12 @@ class YamlDumper extends Dumper
|
||||
*/
|
||||
private function getServiceCall($id, Reference $reference = null)
|
||||
{
|
||||
if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
|
||||
return sprintf('@?%s', $id);
|
||||
if (null !== $reference) {
|
||||
switch ($reference->getInvalidBehavior()) {
|
||||
case ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE: break;
|
||||
case ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE: return sprintf('@!%s', $id);
|
||||
default: return sprintf('@?%s', $id);
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf('@%s', $id);
|
||||
|
@ -492,6 +492,8 @@ class XmlFileLoader extends FileLoader
|
||||
$invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
|
||||
if ('ignore' == $onInvalid) {
|
||||
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
|
||||
} elseif ('ignore_uninitialized' == $onInvalid) {
|
||||
$invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
|
||||
} elseif ('null' == $onInvalid) {
|
||||
$invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE;
|
||||
}
|
||||
|
@ -758,6 +758,9 @@ class YamlFileLoader extends FileLoader
|
||||
if (0 === strpos($value, '@@')) {
|
||||
$value = substr($value, 1);
|
||||
$invalidBehavior = null;
|
||||
} elseif (0 === strpos($value, '@!')) {
|
||||
$value = substr($value, 2);
|
||||
$invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE;
|
||||
} elseif (0 === strpos($value, '@?')) {
|
||||
$value = substr($value, 2);
|
||||
$invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE;
|
||||
|
@ -266,6 +266,7 @@
|
||||
<xsd:enumeration value="null" />
|
||||
<xsd:enumeration value="ignore" />
|
||||
<xsd:enumeration value="exception" />
|
||||
<xsd:enumeration value="ignore_uninitialized" />
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
|
||||
|
@ -67,6 +67,27 @@ class CheckExceptionOnInvalidReferenceBehaviorPassTest extends TestCase
|
||||
$this->process($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Invalid ignore-on-uninitialized reference found in service
|
||||
*/
|
||||
public function testProcessThrowsExceptionOnNonSharedUninitializedReference()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container
|
||||
->register('a', 'stdClass')
|
||||
->addArgument(new Reference('b', $container::IGNORE_ON_UNINITIALIZED_REFERENCE))
|
||||
;
|
||||
|
||||
$container
|
||||
->register('b', 'stdClass')
|
||||
->setShared(false)
|
||||
;
|
||||
|
||||
$this->process($container);
|
||||
}
|
||||
|
||||
private function process(ContainerBuilder $container)
|
||||
{
|
||||
$pass = new CheckExceptionOnInvalidReferenceBehaviorPass();
|
||||
|
@ -1098,6 +1098,38 @@ class ContainerBuilderTest extends TestCase
|
||||
$this->assertSame($container->get('bar_service'), $foo->get('bar'));
|
||||
}
|
||||
|
||||
public function testUninitializedReference()
|
||||
{
|
||||
$container = include __DIR__.'/Fixtures/containers/container_uninitialized_ref.php';
|
||||
$container->compile();
|
||||
|
||||
$bar = $container->get('bar');
|
||||
|
||||
$this->assertNull($bar->foo1);
|
||||
$this->assertNull($bar->foo2);
|
||||
$this->assertNull($bar->foo3);
|
||||
$this->assertNull($bar->closures[0]());
|
||||
$this->assertNull($bar->closures[1]());
|
||||
$this->assertNull($bar->closures[2]());
|
||||
$this->assertSame(array(), iterator_to_array($bar->iter));
|
||||
|
||||
$container = include __DIR__.'/Fixtures/containers/container_uninitialized_ref.php';
|
||||
$container->compile();
|
||||
|
||||
$container->get('foo1');
|
||||
$container->get('baz');
|
||||
|
||||
$bar = $container->get('bar');
|
||||
|
||||
$this->assertEquals(new \stdClass(), $bar->foo1);
|
||||
$this->assertNull($bar->foo2);
|
||||
$this->assertEquals(new \stdClass(), $bar->foo3);
|
||||
$this->assertEquals(new \stdClass(), $bar->closures[0]());
|
||||
$this->assertNull($bar->closures[1]());
|
||||
$this->assertEquals(new \stdClass(), $bar->closures[2]());
|
||||
$this->assertEquals(array('foo1' => new \stdClass(), 'foo3' => new \stdClass()), iterator_to_array($bar->iter));
|
||||
}
|
||||
|
||||
public function testRegisterForAutoconfiguration()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
@ -631,6 +631,44 @@ class PhpDumperTest extends TestCase
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_in_expression.php', $dumper->dump());
|
||||
}
|
||||
|
||||
public function testUninitializedReference()
|
||||
{
|
||||
$container = include self::$fixturesPath.'/containers/container_uninitialized_ref.php';
|
||||
$container->compile();
|
||||
$dumper = new PhpDumper($container);
|
||||
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_uninitialized_ref.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Uninitialized_Reference')));
|
||||
|
||||
require self::$fixturesPath.'/php/services_uninitialized_ref.php';
|
||||
|
||||
$container = new \Symfony_DI_PhpDumper_Test_Uninitialized_Reference();
|
||||
|
||||
$bar = $container->get('bar');
|
||||
|
||||
$this->assertNull($bar->foo1);
|
||||
$this->assertNull($bar->foo2);
|
||||
$this->assertNull($bar->foo3);
|
||||
$this->assertNull($bar->closures[0]());
|
||||
$this->assertNull($bar->closures[1]());
|
||||
$this->assertNull($bar->closures[2]());
|
||||
$this->assertSame(array(), iterator_to_array($bar->iter));
|
||||
|
||||
$container = new \Symfony_DI_PhpDumper_Test_Uninitialized_Reference();
|
||||
|
||||
$container->get('foo1');
|
||||
$container->get('baz');
|
||||
|
||||
$bar = $container->get('bar');
|
||||
|
||||
$this->assertEquals(new \stdClass(), $bar->foo1);
|
||||
$this->assertNull($bar->foo2);
|
||||
$this->assertEquals(new \stdClass(), $bar->foo3);
|
||||
$this->assertEquals(new \stdClass(), $bar->closures[0]());
|
||||
$this->assertNull($bar->closures[1]());
|
||||
$this->assertEquals(new \stdClass(), $bar->closures[2]());
|
||||
$this->assertEquals(array('foo1' => new \stdClass(), 'foo3' => new \stdClass()), iterator_to_array($bar->iter));
|
||||
}
|
||||
|
||||
public function testDumpHandlesLiteralClassWithRootNamespace()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
|
@ -12,8 +12,12 @@
|
||||
namespace Symfony\Component\DependencyInjection\Tests\Dumper;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Dumper\XmlDumper;
|
||||
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
class XmlDumperTest extends TestCase
|
||||
{
|
||||
@ -184,6 +188,18 @@ class XmlDumperTest extends TestCase
|
||||
$this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services24.xml'), $dumper->dump());
|
||||
}
|
||||
|
||||
public function testDumpLoad()
|
||||
{
|
||||
$container = new ContainerBuilder();
|
||||
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
|
||||
$loader->load('services_dump_load.xml');
|
||||
|
||||
$this->assertEquals(array(new Reference('bar', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)), $container->getDefinition('foo')->getArguments());
|
||||
|
||||
$dumper = new XmlDumper($container);
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/xml/services_dump_load.xml', $dumper->dump());
|
||||
}
|
||||
|
||||
public function testDumpAbstractServices()
|
||||
{
|
||||
$container = include self::$fixturesPath.'/containers/container_abstract.php';
|
||||
|
@ -14,9 +14,11 @@ namespace Symfony\Component\DependencyInjection\Tests\Dumper;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Dumper\YamlDumper;
|
||||
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Symfony\Component\Yaml\Parser;
|
||||
|
||||
@ -73,6 +75,8 @@ class YamlDumperTest extends TestCase
|
||||
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
|
||||
$loader->load('services_dump_load.yml');
|
||||
|
||||
$this->assertEquals(array(new Reference('bar', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)), $container->getDefinition('foo')->getArguments());
|
||||
|
||||
$dumper = new YamlDumper($container);
|
||||
$this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_dump_load.yml', $dumper->dump());
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$container
|
||||
->register('foo1', 'stdClass')
|
||||
;
|
||||
|
||||
$container
|
||||
->register('foo2', 'stdClass')
|
||||
->setPublic(false)
|
||||
;
|
||||
|
||||
$container
|
||||
->register('foo3', 'stdClass')
|
||||
->setPublic(false)
|
||||
;
|
||||
|
||||
$container
|
||||
->register('baz', 'stdClass')
|
||||
->setProperty('foo3', new Reference('foo3'))
|
||||
;
|
||||
|
||||
$container
|
||||
->register('bar', 'stdClass')
|
||||
->setProperty('foo1', new Reference('foo1', $container::IGNORE_ON_UNINITIALIZED_REFERENCE))
|
||||
->setProperty('foo2', new Reference('foo2', $container::IGNORE_ON_UNINITIALIZED_REFERENCE))
|
||||
->setProperty('foo3', new Reference('foo3', $container::IGNORE_ON_UNINITIALIZED_REFERENCE))
|
||||
->setProperty('closures', array(
|
||||
new ServiceClosureArgument(new Reference('foo1', $container::IGNORE_ON_UNINITIALIZED_REFERENCE)),
|
||||
new ServiceClosureArgument(new Reference('foo2', $container::IGNORE_ON_UNINITIALIZED_REFERENCE)),
|
||||
new ServiceClosureArgument(new Reference('foo3', $container::IGNORE_ON_UNINITIALIZED_REFERENCE)),
|
||||
))
|
||||
->setProperty('iter', new IteratorArgument(array(
|
||||
'foo1' => new Reference('foo1', $container::IGNORE_ON_UNINITIALIZED_REFERENCE),
|
||||
'foo2' => new Reference('foo2', $container::IGNORE_ON_UNINITIALIZED_REFERENCE),
|
||||
'foo3' => new Reference('foo3', $container::IGNORE_ON_UNINITIALIZED_REFERENCE),
|
||||
)))
|
||||
;
|
||||
|
||||
return $container;
|
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Exception\LogicException;
|
||||
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
||||
|
||||
/**
|
||||
* Symfony_DI_PhpDumper_Test_Uninitialized_Reference.
|
||||
*
|
||||
* This class has been auto-generated
|
||||
* by the Symfony Dependency Injection Component.
|
||||
*
|
||||
* @final since Symfony 3.3
|
||||
*/
|
||||
class Symfony_DI_PhpDumper_Test_Uninitialized_Reference extends Container
|
||||
{
|
||||
private $parameters;
|
||||
private $targetDirs = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->services = array();
|
||||
$this->methodMap = array(
|
||||
'bar' => 'getBarService',
|
||||
'baz' => 'getBazService',
|
||||
'foo1' => 'getFoo1Service',
|
||||
'foo3' => 'getFoo3Service',
|
||||
);
|
||||
$this->privates = array(
|
||||
'foo3' => true,
|
||||
);
|
||||
|
||||
$this->aliases = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function compile()
|
||||
{
|
||||
throw new LogicException('You cannot compile a dumped container that was already compiled.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isCompiled()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isFrozen()
|
||||
{
|
||||
@trigger_error(sprintf('The %s() method is deprecated since version 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public 'bar' shared service.
|
||||
*
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected function getBarService()
|
||||
{
|
||||
$this->services['bar'] = $instance = new \stdClass();
|
||||
|
||||
$instance->foo1 = ${($_ = isset($this->services['foo1']) ? $this->services['foo1'] : null) && false ?: '_'};
|
||||
$instance->foo2 = null;
|
||||
$instance->foo3 = ${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : null) && false ?: '_'};
|
||||
$instance->closures = array(0 => function () {
|
||||
return ${($_ = isset($this->services['foo1']) ? $this->services['foo1'] : null) && false ?: '_'};
|
||||
}, 1 => function () {
|
||||
return null;
|
||||
}, 2 => function () {
|
||||
return ${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : null) && false ?: '_'};
|
||||
});
|
||||
$instance->iter = new RewindableGenerator(function () {
|
||||
if (isset($this->services['foo1'])) {
|
||||
yield 'foo1' => ${($_ = isset($this->services['foo1']) ? $this->services['foo1'] : null) && false ?: '_'};
|
||||
}
|
||||
if (false) {
|
||||
yield 'foo2' => null;
|
||||
}
|
||||
if (isset($this->services['foo3'])) {
|
||||
yield 'foo3' => ${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : null) && false ?: '_'};
|
||||
}
|
||||
}, function () {
|
||||
return 0 + (int) (isset($this->services['foo1'])) + (int) (false) + (int) (isset($this->services['foo3']));
|
||||
});
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public 'baz' shared service.
|
||||
*
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected function getBazService()
|
||||
{
|
||||
$this->services['baz'] = $instance = new \stdClass();
|
||||
|
||||
$instance->foo3 = ${($_ = isset($this->services['foo3']) ? $this->services['foo3'] : $this->getFoo3Service()) && false ?: '_'};
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public 'foo1' shared service.
|
||||
*
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected function getFoo1Service()
|
||||
{
|
||||
return $this->services['foo1'] = new \stdClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the private 'foo3' shared service.
|
||||
*
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected function getFoo3Service()
|
||||
{
|
||||
return $this->services['foo3'] = new \stdClass();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
<services>
|
||||
<service id="service_container" class="Symfony\Component\DependencyInjection\ContainerInterface" synthetic="true"/>
|
||||
<service id="foo" autoconfigure="true" abstract="true">
|
||||
<argument type="service" id="bar" on-invalid="ignore_uninitialized"/>
|
||||
</service>
|
||||
<service id="Psr\Container\ContainerInterface" alias="service_container" public="false"/>
|
||||
<service id="Symfony\Component\DependencyInjection\ContainerInterface" alias="service_container" public="false"/>
|
||||
</services>
|
||||
</container>
|
@ -6,6 +6,7 @@ services:
|
||||
foo:
|
||||
autoconfigure: true
|
||||
abstract: true
|
||||
arguments: ['@!bar']
|
||||
Psr\Container\ContainerInterface:
|
||||
alias: service_container
|
||||
public: false
|
||||
|
Reference in New Issue
Block a user