[DI] fix dumping inlined services

This commit is contained in:
Nicolas Grekas 2018-10-16 18:02:29 +02:00
parent 3ed98de837
commit a97606d58a
12 changed files with 347 additions and 89 deletions

View File

@ -306,9 +306,13 @@ EOF;
if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) {
// no-op
} elseif (isset($currentPath[$id])) {
$currentId = $id;
foreach (array_reverse($currentPath) as $parentId) {
$this->circularReferences[$parentId][$id] = $id;
$id = $parentId;
$this->circularReferences[$parentId][$currentId] = $currentId;
if ($parentId === $id) {
break;
}
$currentId = $parentId;
}
} elseif (!isset($checkedNodes[$id])) {
$checkedNodes[$id] = true;
@ -591,7 +595,7 @@ EOF;
$this->definitionVariables = new \SplObjectStorage();
$this->referenceVariables = array();
$this->variableCount = 0;
$this->definitionVariables[$definition] = $this->referenceVariables[$id] = new Variable('instance');
$this->referenceVariables[$id] = new Variable('instance');
$return = array();
@ -663,22 +667,7 @@ EOF;
$code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
}
$head = $tail = '';
$arguments = array($definition->getArguments(), $definition->getFactory());
$this->addInlineVariables($head, $tail, $id, $arguments, true);
$code .= '' !== $head ? $head."\n" : '';
if ($arguments = array_filter(array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator()))) {
$this->addInlineVariables($tail, $tail, $id, $arguments, false);
$tail .= '' !== $tail ? "\n" : '';
$tail .= $this->addServiceProperties($definition);
$tail .= $this->addServiceMethodCalls($definition);
$tail .= $this->addServiceConfigurator($definition);
}
$code .= $this->addServiceInstance($id, $definition, '' === $tail)
.('' !== $tail ? "\n".$tail."\n return \$instance;\n" : '');
$code .= $this->addInlineService($id, $definition);
if ($asFile) {
$code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code)));
@ -692,35 +681,41 @@ EOF;
return $code;
}
private function addInlineVariables(&$head, &$tail, $id, array $arguments, $forConstructor)
private function addInlineVariables($id, Definition $definition, array $arguments, $forConstructor)
{
$hasSelfRef = false;
$code = '';
foreach ($arguments as $argument) {
if (\is_array($argument)) {
$hasSelfRef = $this->addInlineVariables($head, $tail, $id, $argument, $forConstructor) || $hasSelfRef;
$code .= $this->addInlineVariables($id, $definition, $argument, $forConstructor);
} elseif ($argument instanceof Reference) {
$hasSelfRef = $this->addInlineReference($head, $id, $this->container->normalizeId($argument), $forConstructor) || $hasSelfRef;
$code .= $this->addInlineReference($id, $definition, $this->container->normalizeId($argument), $forConstructor);
} elseif ($argument instanceof Definition) {
$hasSelfRef = $this->addInlineService($head, $tail, $id, $argument, $forConstructor) || $hasSelfRef;
$code .= $this->addInlineService($id, $definition, $argument, $forConstructor);
}
}
return $hasSelfRef;
return $code;
}
private function addInlineReference(&$code, $id, $targetId, $forConstructor)
private function addInlineReference($id, Definition $definition, $targetId, $forConstructor)
{
$hasSelfRef = isset($this->circularReferences[$id][$targetId]);
if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) {
return $hasSelfRef;
if ($id === $targetId) {
return $this->addInlineService($id, $definition, $definition, $forConstructor);
}
if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) {
return '';
}
$hasSelfRef = isset($this->circularReferences[$id][$targetId]);
$forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]);
list($callCount, $behavior) = $this->serviceCalls[$targetId];
if (2 > $callCount && (!$hasSelfRef || !$forConstructor)) {
return $hasSelfRef;
$code = $hasSelfRef && !$forConstructor ? $this->addInlineService($id, $definition, $definition, $forConstructor) : '';
if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) {
return $code;
}
$name = $this->getNextVariableName();
@ -730,7 +725,7 @@ EOF;
$code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($targetId, $reference));
if (!$hasSelfRef || !$forConstructor) {
return $hasSelfRef;
return $code;
}
$code .= sprintf(<<<'EOTXT'
@ -745,46 +740,56 @@ EOTXT
$id
);
return false;
return $code;
}
private function addInlineService(&$head, &$tail, $id, Definition $definition, $forConstructor)
private function addInlineService($id, Definition $definition, Definition $inlineDef = null, $forConstructor = true)
{
if (isset($this->definitionVariables[$definition])) {
return false;
$isSimpleInstance = $isRootInstance = null === $inlineDef;
if (isset($this->definitionVariables[$inlineDef = $inlineDef ?: $definition])) {
return '';
}
$arguments = array($definition->getArguments(), $definition->getFactory());
$arguments = array($inlineDef->getArguments(), $inlineDef->getFactory());
if (2 > $this->inlinedDefinitions[$definition] && !$definition->getMethodCalls() && !$definition->getProperties() && !$definition->getConfigurator()) {
return $this->addInlineVariables($head, $tail, $id, $arguments, $forConstructor);
$code = $this->addInlineVariables($id, $definition, $arguments, $forConstructor);
if ($arguments = array_filter(array($inlineDef->getProperties(), $inlineDef->getMethodCalls(), $inlineDef->getConfigurator()))) {
$isSimpleInstance = false;
} elseif ($definition !== $inlineDef && 2 > $this->inlinedDefinitions[$inlineDef]) {
return $code;
}
$name = $this->getNextVariableName();
$this->definitionVariables[$definition] = new Variable($name);
$code = '';
if ($forConstructor) {
$hasSelfRef = $this->addInlineVariables($code, $tail, $id, $arguments, $forConstructor);
if (isset($this->definitionVariables[$inlineDef])) {
$isSimpleInstance = false;
} else {
$hasSelfRef = $this->addInlineVariables($code, $code, $id, $arguments, $forConstructor);
}
$code .= $this->addNewInstance($definition, '$'.$name, ' = ', $id);
$hasSelfRef && !$forConstructor ? $tail .= ('' !== $tail ? "\n" : '').$code : $head .= ('' !== $head ? "\n" : '').$code;
$name = $definition === $inlineDef ? 'instance' : $this->getNextVariableName();
$this->definitionVariables[$inlineDef] = new Variable($name);
$code .= '' !== $code ? "\n" : '';
$code = '';
$arguments = array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator());
$hasSelfRef = $this->addInlineVariables($code, $code, $id, $arguments, false) || $hasSelfRef;
if ('instance' === $name) {
$code .= $this->addServiceInstance($id, $definition, $isSimpleInstance);
} else {
$code .= $this->addNewInstance($inlineDef, '$'.$name, ' = ', $id);
}
$code .= '' !== $code ? "\n" : '';
$code .= $this->addServiceProperties($definition, $name);
$code .= $this->addServiceMethodCalls($definition, $name);
$code .= $this->addServiceConfigurator($definition, $name);
if ('' !== $code) {
$hasSelfRef ? $tail .= ('' !== $tail ? "\n" : '').$code : $head .= $code;
if ('' !== $inline = $this->addInlineVariables($id, $definition, $arguments, false)) {
$code .= "\n".$inline."\n";
} elseif ($arguments && 'instance' === $name) {
$code .= "\n";
}
$code .= $this->addServiceProperties($inlineDef, $name);
$code .= $this->addServiceMethodCalls($inlineDef, $name);
$code .= $this->addServiceConfigurator($inlineDef, $name);
}
return $hasSelfRef;
if ($isRootInstance && !$isSimpleInstance) {
$code .= "\n return \$instance;\n";
}
return $code;
}
/**

View File

@ -1388,6 +1388,8 @@ class ContainerBuilderTest extends TestCase
$foo6 = $container->get('foo6');
$this->assertEquals((object) array('bar6' => (object) array()), $foo6);
$this->assertInstanceOf(\stdClass::class, $container->get('root'));
}
public function provideAlmostCircular()

View File

@ -833,6 +833,8 @@ class PhpDumperTest extends TestCase
$foo6 = $container->get('foo6');
$this->assertEquals((object) array('bar6' => (object) array()), $foo6);
$this->assertInstanceOf(\stdClass::class, $container->get('root'));
}
public function provideAlmostCircular()

View File

@ -0,0 +1,19 @@
<?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\Fixtures;
class FooForCircularWithAddCalls
{
public function call(\stdClass $argument)
{
}
}

View File

@ -4,6 +4,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Tests\Fixtures\FooForCircularWithAddCalls;
$public = 'public' === $visibility;
$container = new ContainerBuilder();
@ -115,4 +116,33 @@ $container->register('baz6', 'stdClass')
->setPublic(true)
->setProperty('bar6', new Reference('bar6'));
// provided by Christian Schiffler
$container
->register('root', 'stdClass')
->setArguments([new Reference('level2'), new Reference('multiuse1')])
->setPublic(true);
$container
->register('level2', FooForCircularWithAddCalls::class)
->addMethodCall('call', [new Reference('level3')]);
$container->register('multiuse1', 'stdClass');
$container
->register('level3', 'stdClass')
->addArgument(new Reference('level4'));
$container
->register('level4', 'stdClass')
->setArguments([new Reference('multiuse1'), new Reference('level5')]);
$container
->register('level5', 'stdClass')
->addArgument(new Reference('level6'));
$container
->register('level6', FooForCircularWithAddCalls::class)
->addMethodCall('call', [new Reference('level5')]);
return $container;

View File

@ -158,7 +158,6 @@ use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
$this->services['foo_with_inline'] = $instance = new \Foo();
$a = new \Bar();
$a->pub = 'pub';
$a->setBaz(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->load('getBazService.php')) && false ?: '_'});

View File

@ -264,7 +264,6 @@ class ProjectServiceContainer extends Container
$this->services['foo_with_inline'] = $instance = new \Foo();
$a = new \Bar();
$a->pub = 'pub';
$a->setBaz(${($_ = isset($this->services['baz']) ? $this->services['baz'] : $this->getBazService()) && false ?: '_'});

View File

@ -34,13 +34,26 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
'foo5' => 'getFoo5Service',
'foo6' => 'getFoo6Service',
'foobar4' => 'getFoobar4Service',
'level2' => 'getLevel2Service',
'level3' => 'getLevel3Service',
'level4' => 'getLevel4Service',
'level5' => 'getLevel5Service',
'level6' => 'getLevel6Service',
'logger' => 'getLoggerService',
'manager' => 'getManagerService',
'manager2' => 'getManager2Service',
'multiuse1' => 'getMultiuse1Service',
'root' => 'getRootService',
'subscriber' => 'getSubscriberService',
);
$this->privates = array(
'bar6' => true,
'level2' => true,
'level3' => true,
'level4' => true,
'level5' => true,
'level6' => true,
'multiuse1' => true,
);
$this->aliases = array();
@ -62,7 +75,13 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
'foobar' => true,
'foobar2' => true,
'foobar3' => true,
'level2' => true,
'level3' => true,
'level4' => true,
'level5' => true,
'level6' => true,
'logger2' => true,
'multiuse1' => true,
'subscriber2' => true,
);
}
@ -141,10 +160,10 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
$this->services['connection'] = $instance = new \stdClass($a, $b);
$a->subscriber = ${($_ = isset($this->services['subscriber']) ? $this->services['subscriber'] : $this->getSubscriberService()) && false ?: '_'};
$b->logger = ${($_ = isset($this->services['logger']) ? $this->services['logger'] : $this->getLoggerService()) && false ?: '_'};
$a->subscriber = ${($_ = isset($this->services['subscriber']) ? $this->services['subscriber'] : $this->getSubscriberService()) && false ?: '_'};
return $instance;
}
@ -157,19 +176,19 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
{
$a = new \stdClass();
$c = new \stdClass();
$b = new \stdClass();
$this->services['connection2'] = $instance = new \stdClass($a, $c);
$this->services['connection2'] = $instance = new \stdClass($a, $b);
$b = ${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'};
$c = new \stdClass($instance);
$a->subscriber2 = new \stdClass($b);
$d = ${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'};
$d = new \stdClass($instance);
$c->handler2 = new \stdClass($d);
$d->handler2 = new \stdClass($b);
$b->logger2 = $c;
$c->logger2 = $d;
$a->subscriber2 = new \stdClass($d);
return $instance;
}
@ -216,7 +235,6 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
$this->services['foo5'] = $instance = new \stdClass();
$a = new \stdClass($instance);
$a->foo = $instance;
$instance->bar = $a;
@ -306,6 +324,16 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
return $this->services['manager2'] = new \stdClass($a);
}
/**
* Gets the public 'root' shared service.
*
* @return \stdClass
*/
protected function getRootService()
{
return $this->services['root'] = new \stdClass(${($_ = isset($this->services['level2']) ? $this->services['level2'] : $this->getLevel2Service()) && false ?: '_'}, ${($_ = isset($this->services['multiuse1']) ? $this->services['multiuse1'] : $this->services['multiuse1'] = new \stdClass()) && false ?: '_'});
}
/**
* Gets the public 'subscriber' shared service.
*
@ -337,4 +365,78 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
return $this->services['bar6'] = new \stdClass($a);
}
/**
* Gets the private 'level2' shared service.
*
* @return \Symfony\Component\DependencyInjection\Tests\Fixtures\FooForCircularWithAddCalls
*/
protected function getLevel2Service()
{
$this->services['level2'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\FooForCircularWithAddCalls();
$instance->call(${($_ = isset($this->services['level3']) ? $this->services['level3'] : $this->getLevel3Service()) && false ?: '_'});
return $instance;
}
/**
* Gets the private 'level3' shared service.
*
* @return \stdClass
*/
protected function getLevel3Service()
{
return $this->services['level3'] = new \stdClass(${($_ = isset($this->services['level4']) ? $this->services['level4'] : $this->getLevel4Service()) && false ?: '_'});
}
/**
* Gets the private 'level4' shared service.
*
* @return \stdClass
*/
protected function getLevel4Service()
{
return $this->services['level4'] = new \stdClass(${($_ = isset($this->services['multiuse1']) ? $this->services['multiuse1'] : $this->services['multiuse1'] = new \stdClass()) && false ?: '_'}, ${($_ = isset($this->services['level5']) ? $this->services['level5'] : $this->getLevel5Service()) && false ?: '_'});
}
/**
* Gets the private 'level5' shared service.
*
* @return \stdClass
*/
protected function getLevel5Service()
{
$a = ${($_ = isset($this->services['level6']) ? $this->services['level6'] : $this->getLevel6Service()) && false ?: '_'};
if (isset($this->services['level5'])) {
return $this->services['level5'];
}
return $this->services['level5'] = new \stdClass($a);
}
/**
* Gets the private 'level6' shared service.
*
* @return \Symfony\Component\DependencyInjection\Tests\Fixtures\FooForCircularWithAddCalls
*/
protected function getLevel6Service()
{
$this->services['level6'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\FooForCircularWithAddCalls();
$instance->call(${($_ = isset($this->services['level5']) ? $this->services['level5'] : $this->getLevel5Service()) && false ?: '_'});
return $instance;
}
/**
* Gets the private 'multiuse1' shared service.
*
* @return \stdClass
*/
protected function getMultiuse1Service()
{
return $this->services['multiuse1'] = new \stdClass();
}
}

View File

@ -41,13 +41,26 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
'foobar2' => 'getFoobar2Service',
'foobar3' => 'getFoobar3Service',
'foobar4' => 'getFoobar4Service',
'level2' => 'getLevel2Service',
'level3' => 'getLevel3Service',
'level4' => 'getLevel4Service',
'level5' => 'getLevel5Service',
'level6' => 'getLevel6Service',
'logger' => 'getLoggerService',
'manager' => 'getManagerService',
'manager2' => 'getManager2Service',
'multiuse1' => 'getMultiuse1Service',
'root' => 'getRootService',
'subscriber' => 'getSubscriberService',
);
$this->privates = array(
'bar6' => true,
'level2' => true,
'level3' => true,
'level4' => true,
'level5' => true,
'level6' => true,
'multiuse1' => true,
);
$this->aliases = array();
@ -62,7 +75,13 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
'bar6' => true,
'config' => true,
'config2' => true,
'level2' => true,
'level3' => true,
'level4' => true,
'level5' => true,
'level6' => true,
'logger2' => true,
'multiuse1' => true,
'subscriber2' => true,
);
}
@ -176,7 +195,6 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
$this->services['connection2'] = $instance = new \stdClass(${($_ = isset($this->services['dispatcher2']) ? $this->services['dispatcher2'] : $this->getDispatcher2Service()) && false ?: '_'}, $a);
$b = new \stdClass($instance);
$b->handler2 = new \stdClass(${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'});
$a->logger2 = $b;
@ -396,6 +414,16 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
return $this->services['manager2'] = new \stdClass($a);
}
/**
* Gets the public 'root' shared service.
*
* @return \stdClass
*/
protected function getRootService()
{
return $this->services['root'] = new \stdClass(${($_ = isset($this->services['level2']) ? $this->services['level2'] : $this->getLevel2Service()) && false ?: '_'}, ${($_ = isset($this->services['multiuse1']) ? $this->services['multiuse1'] : $this->services['multiuse1'] = new \stdClass()) && false ?: '_'});
}
/**
* Gets the public 'subscriber' shared service.
*
@ -421,4 +449,78 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
return $this->services['bar6'] = new \stdClass($a);
}
/**
* Gets the private 'level2' shared service.
*
* @return \Symfony\Component\DependencyInjection\Tests\Fixtures\FooForCircularWithAddCalls
*/
protected function getLevel2Service()
{
$this->services['level2'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\FooForCircularWithAddCalls();
$instance->call(${($_ = isset($this->services['level3']) ? $this->services['level3'] : $this->getLevel3Service()) && false ?: '_'});
return $instance;
}
/**
* Gets the private 'level3' shared service.
*
* @return \stdClass
*/
protected function getLevel3Service()
{
return $this->services['level3'] = new \stdClass(${($_ = isset($this->services['level4']) ? $this->services['level4'] : $this->getLevel4Service()) && false ?: '_'});
}
/**
* Gets the private 'level4' shared service.
*
* @return \stdClass
*/
protected function getLevel4Service()
{
return $this->services['level4'] = new \stdClass(${($_ = isset($this->services['multiuse1']) ? $this->services['multiuse1'] : $this->services['multiuse1'] = new \stdClass()) && false ?: '_'}, ${($_ = isset($this->services['level5']) ? $this->services['level5'] : $this->getLevel5Service()) && false ?: '_'});
}
/**
* Gets the private 'level5' shared service.
*
* @return \stdClass
*/
protected function getLevel5Service()
{
$a = ${($_ = isset($this->services['level6']) ? $this->services['level6'] : $this->getLevel6Service()) && false ?: '_'};
if (isset($this->services['level5'])) {
return $this->services['level5'];
}
return $this->services['level5'] = new \stdClass($a);
}
/**
* Gets the private 'level6' shared service.
*
* @return \Symfony\Component\DependencyInjection\Tests\Fixtures\FooForCircularWithAddCalls
*/
protected function getLevel6Service()
{
$this->services['level6'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\FooForCircularWithAddCalls();
$instance->call(${($_ = isset($this->services['level5']) ? $this->services['level5'] : $this->getLevel5Service()) && false ?: '_'});
return $instance;
}
/**
* Gets the private 'multiuse1' shared service.
*
* @return \stdClass
*/
protected function getMultiuse1Service()
{
return $this->services['multiuse1'] = new \stdClass();
}
}

View File

@ -81,8 +81,8 @@ class Symfony_DI_PhpDumper_Test_Deep_Graph extends Container
if (isset($this->services['foo'])) {
return $this->services['foo'];
}
$b = new \stdClass();
$c = new \stdClass();
$c->p3 = new \stdClass();

View File

@ -64,14 +64,14 @@ class Symfony_DI_PhpDumper_Test_Inline_Self_Ref extends Container
*/
protected function getFooService()
{
$b = new \App\Bar();
$a = new \App\Baz($b);
$a = new \App\Bar();
$this->services['App\Foo'] = $instance = new \App\Foo($a);
$b = new \App\Baz($a);
$b->bar = $a;
$b->foo = $instance;
$this->services['App\Foo'] = $instance = new \App\Foo($b);
$a->bar = $b;
$a->foo = $instance;
return $instance;
}

View File

@ -67,22 +67,20 @@ class ProjectServiceContainer extends Container
{
$a = new \TSantos\Serializer\NormalizerRegistry();
$d = new \TSantos\Serializer\EventDispatcher\EventDispatcher();
$d->addSubscriber(new \TSantos\SerializerBundle\EventListener\StopwatchListener(new \Symfony\Component\Stopwatch\Stopwatch(true)));
$this->services['tsantos_serializer'] = $instance = new \TSantos\Serializer\EventEmitterSerializer(new \TSantos\Serializer\Encoder\JsonEncoder(), $a, $d);
$b = new \TSantos\Serializer\Normalizer\CollectionNormalizer();
$c = new \TSantos\Serializer\EventDispatcher\EventDispatcher();
$c->addSubscriber(new \TSantos\SerializerBundle\EventListener\StopwatchListener(new \Symfony\Component\Stopwatch\Stopwatch(true)));
$this->services['tsantos_serializer'] = $instance = new \TSantos\Serializer\EventEmitterSerializer(new \TSantos\Serializer\Encoder\JsonEncoder(), $a, $c);
$b->setSerializer($instance);
$c = new \TSantos\Serializer\Normalizer\JsonNormalizer();
$c->setSerializer($instance);
$d = new \TSantos\Serializer\Normalizer\JsonNormalizer();
$d->setSerializer($instance);
$a->add(new \TSantos\Serializer\Normalizer\ObjectNormalizer(new \TSantos\SerializerBundle\Serializer\CircularReferenceHandler()));
$a->add($b);
$a->add($c);
$a->add($d);
return $instance;
}