Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[DI] Inline trivial private servives #23674

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 56 additions & 4 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,50 @@ private function isSimpleInstance($id, Definition $definition)
return true;
}

/**
* Checks if the definition is a trivial instance.
*
* @param Definition $definition
*
* @return bool
*/
private function isTrivialInstance(Definition $definition)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this method name (isTrivialInstance()) which feels like an internal detail, maybe we could use a more generic and future-proof name, such as canBeInlined() ?

Copy link
Member Author

@nicolas-grekas nicolas-grekas Jul 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, as a private method, this is an internal detail :)
more importantly, the "trivial" is important here. It's not only that the instantiation can be inlined, it's also that the instantiation is trivial, this meaning that it mostly a simple new Foo().
More complex instantiation would also be inlineable (eg Factory::create(new Foo($this->get('bar')))), but we don't want to inline those (we need a limit, being trivial is the one, for what it means (it only means whatever logic is in the method, see patch :) .)
In this respect, canBeInlined() would be confusing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. My reasoning was that in the future, if you add new optimizations, the code will complicate a lot (if ($this->isTrivialInstance() || $this->isServiceLengthSmall() || $this->isDevEnvironment()) vs if ($this->canBeInlined()) and then adding more optimizations to that method).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if in the future we decide that more cases would benefit from this, we could still change the defining of what a trivial service is. And we can decide to rename the method if actually necessary.

canBeInlined has another reason to be confusing: we have another concept called inlining in the component (although a bit related)

{
if ($definition->isPublic() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) {
return false;
}
if ($definition->isDeprecated() || $definition->isLazy() || $definition->getFactory() || 3 < count($definition->getArguments())) {
return false;
}

foreach ($definition->getArguments() as $arg) {
if (!$arg || ($arg instanceof Reference && 'service_container' !== (string) $arg)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the special case about referencing the container ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because this considers new Foo($this) a trivial case ($this being the dumped container reference of course)

continue;
}
if (is_array($arg) && 3 >= count($arg)) {
foreach ($arg as $k => $v) {
if ($this->dumpValue($k) !== $this->dumpValue($k, false)) {
return false;
}
if (!$v || ($v instanceof Reference && 'service_container' !== (string) $v)) {
continue;
}
if (!is_scalar($v) || $this->dumpValue($v) !== $this->dumpValue($v, false)) {
return false;
}
}
} elseif (!is_scalar($arg) || $this->dumpValue($arg) !== $this->dumpValue($arg, false)) {
return false;
}
}

if (false !== strpos($this->dumpLiteralClass($this->dumpValue($definition->getClass())), '$')) {
return false;
}

return true;
}

/**
* Adds method calls to a service definition.
*
Expand Down Expand Up @@ -675,7 +719,7 @@ private function addServices()
foreach ($definitions as $id => $definition) {
if ($definition->isPublic()) {
$publicServices .= $this->addService($id, $definition);
} else {
} elseif (!$this->isTrivialInstance($definition)) {
$privateServices .= $this->addService($id, $definition);
}
}
Expand Down Expand Up @@ -1504,10 +1548,18 @@ private function getServiceCall($id, Reference $reference = null)
}

if ($this->container->hasDefinition($id)) {
$code = sprintf('$this->%s()', $this->generateMethodName($id));
$definition = $this->container->getDefinition($id);

if ($this->container->getDefinition($id)->isShared()) {
$code = sprintf('($this->%s[\'%s\'] ?? %s)', $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $id, $code);
if ($definition->isPublic() || !$this->isTrivialInstance($definition)) {
$code = sprintf('$this->%s()', $this->generateMethodName($id));
} else {
$code = substr($this->addNewInstance($definition, '', '', $id), 8, -2);
if ($definition->isShared()) {
$code = sprintf('($this->privates[\'%s\'] = %s)', $id, $code);
}
}
if ($definition->isShared()) {
$code = sprintf('($this->%s[\'%s\'] ?? %s)', $definition->isPublic() ? 'services' : 'privates', $id, $code);
}
} elseif (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) {
$code = sprintf('$this->get(\'%s\', ContainerInterface::NULL_ON_INVALID_REFERENCE)', $id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
$container
->register('factory_simple', 'SimpleFactoryClass')
->addArgument('foo')
->setDeprecated(true)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added this to have at least one private service that cannot be inlined.

->setPublic(false)
;
$container
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ protected function getFactoryServiceService()
*/
protected function getFactoryServiceSimpleService()
{
return $this->services['factory_service_simple'] = (new \SimpleFactoryClass('foo'))->getInstance();
return $this->services['factory_service_simple'] = ($this->privates['factory_simple'] ?? $this->getFactorySimpleService())->getInstance();
}

/**
Expand Down Expand Up @@ -372,6 +372,20 @@ protected function getServiceFromStaticMethodService()
return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance();
}

/**
* Gets the private 'factory_simple' shared service.
*
* @return \SimpleFactoryClass
*
* @deprecated The "factory_simple" service is deprecated. You should stop using it, as it will soon be removed.
*/
private function getFactorySimpleService()
{
@trigger_error('The "factory_simple" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED);

return $this->privates['factory_simple'] = new \SimpleFactoryClass('foo');
}

/**
* {@inheritdoc}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public function isCompiled()
*/
protected function getBarServiceService()
{
return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->getBazServiceService()));
return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? ($this->privates['baz_service'] = new \stdClass())));
}

/**
Expand All @@ -87,7 +87,7 @@ protected function getFooServiceService()
return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\ServiceLocator(array('bar' => function () {
return ($this->services['bar_service'] ?? $this->getBarServiceService());
}, 'baz' => function (): \stdClass {
return ($this->privates['baz_service'] ?? $this->getBazServiceService());
return ($this->privates['baz_service'] ?? ($this->privates['baz_service'] = new \stdClass()));
}, 'nil' => function () {
return NULL;
}));
Expand Down Expand Up @@ -169,14 +169,4 @@ protected function getTranslator3Service()

return $instance;
}

/**
* Gets the private 'baz_service' shared service.
*
* @return \stdClass
*/
private function getBazServiceService()
{
return $this->privates['baz_service'] = new \stdClass();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public function isCompiled()
*/
protected function getBarServiceService()
{
return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->getBazServiceService()));
return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? ($this->privates['baz_service'] = new \stdClass())));
}

/**
Expand All @@ -78,16 +78,6 @@ protected function getBarServiceService()
*/
protected function getFooServiceService()
{
return $this->services['foo_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->getBazServiceService()));
}

/**
* Gets the private 'baz_service' shared service.
*
* @return \stdClass
*/
private function getBazServiceService()
{
return $this->privates['baz_service'] = new \stdClass();
return $this->services['foo_service'] = new \stdClass(($this->privates['baz_service'] ?? ($this->privates['baz_service'] = new \stdClass())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,6 @@ public function isCompiled()
*/
protected function getPublicFooService()
{
return $this->services['public_foo'] = new \stdClass(($this->privates['private_foo'] ?? $this->getPrivateFooService()));
}

/**
* Gets the private 'private_foo' shared service.
*
* @return \stdClass
*/
private function getPrivateFooService()
{
return $this->privates['private_foo'] = new \stdClass();
return $this->services['public_foo'] = new \stdClass(($this->privates['private_foo'] ?? ($this->privates['private_foo'] = new \stdClass())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,13 @@ protected function getSymfony_Component_DependencyInjection_Tests_Fixtures_TestS
protected function getFooServiceService()
{
return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber(new \Symfony\Component\DependencyInjection\ServiceLocator(array('Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition {
return ($this->privates['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? $this->getAutowired_Symfony_Component_DependencyInjection_Tests_Fixtures_CustomDefinitionService());
return ($this->privates['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? ($this->privates['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()));
}, 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber {
return ($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] ?? $this->getSymfony_Component_DependencyInjection_Tests_Fixtures_TestServiceSubscriberService());
}, 'bar' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition {
return ($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] ?? $this->getSymfony_Component_DependencyInjection_Tests_Fixtures_TestServiceSubscriberService());
}, 'baz' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition {
return ($this->privates['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? $this->getAutowired_Symfony_Component_DependencyInjection_Tests_Fixtures_CustomDefinitionService());
return ($this->privates['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? ($this->privates['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()));
})));
}

/**
* Gets the private 'autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition' shared autowired service.
*
* @return \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition
*/
private function getAutowired_Symfony_Component_DependencyInjection_Tests_Fixtures_CustomDefinitionService()
{
return $this->privates['autowired.Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@
</service>
<service id="factory_simple" class="SimpleFactoryClass" public="false">
<argument>foo</argument>
<deprecated>The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.</deprecated>
</service>
<service id="factory_service_simple" class="Bar">
<factory service="factory_simple" method="getInstance"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ services:
factory: [Bar\FooClass, getInstance]
factory_simple:
class: SimpleFactoryClass
deprecated: The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.
public: false
arguments: ['foo']
factory_service_simple:
Expand Down