bug #38357 [DI] fix dumping non-shared lazy services (nicolas-grekas)

This PR was merged into the 5.1 branch.

Discussion
----------

[DI] fix dumping non-shared lazy services

| Q             | A
| ------------- | ---
| Branch?       | 5.1
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | Fix #38327
| License       | MIT
| Doc PR        | -

It took me a while to get this correct, but here we are.

Commits
-------

e33a0b0f94 [DI] fix dumping non-shared lazy services
This commit is contained in:
Fabien Potencier 2020-10-01 11:49:19 +02:00
commit e36a36ae27
4 changed files with 68 additions and 16 deletions

View File

@ -863,18 +863,45 @@ EOF;
}
}
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
$factoryCode = $definition->isShared() ? ($asFile ? "\$this->load('%s', false)" : '$this->%s(false)') : '$this->factories[%2$s](false)';
$factoryCode = $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName, $this->doExport($id)));
if (!$definition->isShared()) {
$factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id));
}
if ($isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition)) {
if (!$definition->isShared()) {
$code .= sprintf(' %s = %1$s ?? ', $factory);
if ($asFile) {
$code .= "function () {\n";
$code .= " return self::do(\$container);\n";
$code .= " };\n\n";
} else {
$code .= sprintf("\\Closure::fromCallable([\$this, '%s']);\n\n", $methodName);
}
}
$factoryCode = $asFile ? 'self::do($container, false)' : sprintf('$this->%s(false)', $methodName);
$factoryCode = $this->getProxyDumper()->getProxyFactoryCode($definition, $id, $factoryCode);
$code .= $asFile ? preg_replace('/function \(([^)]*+)\) {/', 'function (\1) use ($container) {', $factoryCode) : $factoryCode;
}
$code .= $this->addServiceInclude($id, $definition);
$c = $this->addServiceInclude($id, $definition);
if ('' !== $c && $isProxyCandidate && !$definition->isShared()) {
$c = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $c)));
$code .= " static \$include = true;\n\n";
$code .= " if (\$include) {\n";
$code .= $c;
$code .= " \$include = false;\n";
$code .= " }\n\n";
} else {
$code .= $c;
}
$c = $this->addInlineService($id, $definition);
if (!$definition->isShared()) {
if (!$isProxyCandidate && !$definition->isShared()) {
$c = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $c)));
$factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id));
$lazyloadInitialization = $definition->isLazy() ? '$lazyLoad = true' : '';
$c = sprintf(" %s = function (%s) {\n%s };\n\n return %1\$s();\n", $factory, $lazyloadInitialization, $c);

View File

@ -307,6 +307,7 @@ class PhpDumperTest extends TestCase
->setLazy(true);
$container->compile();
$dumper = new PhpDumper($container);
$dumper->setProxyDumper(new \DummyProxyDumper());
$dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'inline_factories_parameter' => false, 'inline_class_loader_parameter' => false]), true);
if ('\\' === \DIRECTORY_SEPARATOR) {

View File

@ -67,13 +67,11 @@ class ProjectServiceContainer extends Container
*/
protected function getFooService($lazyLoad = true)
{
$this->factories['service_container']['foo'] = $this->factories['service_container']['foo'] ?? \Closure::fromCallable([$this, 'getFooService']);
// lazy factory for stdClass
$this->factories['service_container']['foo'] = function ($lazyLoad = true) {
return new \stdClass();
};
return $this->factories['service_container']['foo']();
return new \stdClass();
}
}

View File

@ -28,16 +28,34 @@ class getNonSharedFooService extends ProjectServiceContainer
*/
public static function do($container, $lazyLoad = true)
{
include_once $container->targetDir.''.'/Fixtures/includes/foo_lazy.php';
$container->factories['non_shared_foo'] = function ($lazyLoad = true) use ($container) {
return new \Bar\FooLazyClass();
$container->factories['non_shared_foo'] = $container->factories['non_shared_foo'] ?? function () use ($container) {
return self::do($container);
};
return $container->factories['non_shared_foo']();
// lazy factory for Bar\FooLazyClass
static $include = true;
if ($include) {
include_once $container->targetDir.''.'/Fixtures/includes/foo_lazy.php';
$include = false;
}
return new \Bar\FooLazyClass();
}
}
[Container%s/proxy.php] => <?php
namespace Container%s;
// proxy code for Bar\FooLazyClass
if (!\class_exists('proxy', false)) {
\class_alias(__NAMESPACE__.'\\proxy', 'proxy', false);
}
[Container%s/ProjectServiceContainer.php] => <?php
namespace Container%s;
@ -105,6 +123,13 @@ class ProjectServiceContainer extends Container
return class_exists($class, false) ? $class::do($this, $lazyLoad) : $service;
}
protected function createProxy($class, \Closure $factory)
{
class_exists($class, false) || require __DIR__.'/'.$class.'.php';
return $factory();
}
}
[ProjectServiceContainer.preload.php] => <?php
@ -120,6 +145,7 @@ if (in_array(PHP_SAPI, ['cli', 'phpdbg'], true)) {
require dirname(__DIR__, %d).'%svendor/autoload.php';
require __DIR__.'/Container%s/ProjectServiceContainer.php';
require __DIR__.'/Container%s/proxy.php';
require __DIR__.'/Container%s/getNonSharedFooService.php';
$classes = [];