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

Skip to content

[DI] Generate one file per service factory #23741

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
Aug 7, 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
8 changes: 6 additions & 2 deletions src/Symfony/Bridge/Doctrine/ManagerRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,12 @@ function (&$wrappedInstance, LazyLoadingInterface $manager) use ($name) {
if (isset($this->aliases[$name])) {
$name = $this->aliases[$name];
}
$method = !isset($this->methodMap[$name]) ? 'get'.strtr($name, $this->underscoreMap).'Service' : $this->methodMap[$name];
$wrappedInstance = $this->{$method}(false);
if (isset($this->fileMap[$name])) {
$wrappedInstance = $this->load($this->fileMap[$name], false);
} else {
$method = !isset($this->methodMap[$name]) ? 'get'.strtr($name, $this->underscoreMap).'Service' : $this->methodMap[$name];
$wrappedInstance = $this->{$method}(false);
}

$manager->setProxyInitializer(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,24 +81,21 @@ public function getProxyFactoryCode(Definition $definition, $id, $factoryCode =
}
$proxyClass = $this->getProxyClassName($definition);

$generatedClass = $this->generateProxyClass($definition);
$hasStaticConstructor = $this->generateProxyClass($definition)->hasMethod('staticProxyConstructor');

$constructorCall = $generatedClass->hasMethod('staticProxyConstructor')
? $proxyClass.'::staticProxyConstructor'
: 'new '.$proxyClass;
$constructorCall = sprintf($hasStaticConstructor ? '%s::staticProxyConstructor' : 'new %s', '\\'.$proxyClass);

return <<<EOF
if (\$lazyLoad) {

$instantiation $constructorCall(
function (&\$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) {
$instantiation \$this->createProxy('$proxyClass', function () {
return $constructorCall(function (&\$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface \$proxy) {
\$wrappedInstance = $factoryCode;

\$proxy->setProxyInitializer(null);

return true;
}
);
});
});
}


Expand All @@ -122,7 +119,7 @@ public function getProxyCode(Definition $definition)
*/
private function getProxyClassName(Definition $definition)
{
return str_replace('\\', '', $definition->getClass()).'_'.spl_object_hash($definition).$this->salt;
return preg_replace('/^.*\\\\/', '', $definition->getClass()).'_'.substr(hash('sha256', spl_object_hash($definition).$this->salt), -7);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@
use %a
class LazyServiceProjectServiceContainer extends Container
{%a
p%s function getFooService($lazyLoad = true)
protected function getFooService($lazyLoad = true)
{
if ($lazyLoad) {

return $this->services['foo'] =%sstdClass_%s(
function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {
return $this->services['foo'] = $this->createProxy('stdClass_%s', function () {
return %S\stdClass_%s(function (&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {
$wrappedInstance = $this->getFooService(false);

$proxy->setProxyInitializer(null);

return true;
}
);
});
});
}

return new \stdClass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ public function testGetProxyCode()
$code = $this->dumper->getProxyCode($definition);

$this->assertStringMatchesFormat(
'%Aclass SymfonyBridgeProxyManagerTestsLazyProxyPhpDumperProxyDumperTest%aextends%w'
'%Aclass ProxyDumperTest%aextends%w'
.'\Symfony\Bridge\ProxyManager\Tests\LazyProxy\PhpDumper\ProxyDumperTest%a',
$code
);
}

public function testGetProxyFactoryCodeWithCustomMethod()
public function testGetProxyFactoryCode()
{
$definition = new Definition(__CLASS__);

Expand All @@ -70,19 +70,15 @@ public function testGetProxyFactoryCodeWithCustomMethod()
$code = $this->dumper->getProxyFactoryCode($definition, 'foo', '$this->getFoo2Service(false)');

$this->assertStringMatchesFormat(
'%wif ($lazyLoad) {%wreturn $this->services[\'foo\'] =%s'
Copy link
Member Author

Choose a reason for hiding this comment

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

the format is already tested elsewhere, no need to duplicate that effort, it just makes updating tests more complex (same below)

.'SymfonyBridgeProxyManagerTestsLazyProxyPhpDumperProxyDumperTest_%s(%wfunction '
.'(&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {'
.'%w$wrappedInstance = $this->getFoo2Service(false);%w$proxy->setProxyInitializer(null);'
.'%wreturn true;%w}%w);%w}%w',
$code
'%A$wrappedInstance = $this->getFoo2Service(false);%w$proxy->setProxyInitializer(null);%A',
$code
);
}

/**
* @group legacy
*/
public function testGetProxyFactoryCode()
public function testLegacyGetProxyFactoryCode()
{
$definition = new Definition(__CLASS__);

Expand All @@ -91,11 +87,7 @@ public function testGetProxyFactoryCode()
$code = $this->dumper->getProxyFactoryCode($definition, 'foo');

$this->assertStringMatchesFormat(
'%wif ($lazyLoad) {%wreturn $this->services[\'foo\'] =%s'
.'SymfonyBridgeProxyManagerTestsLazyProxyPhpDumperProxyDumperTest_%s(%wfunction '
.'(&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {'
.'%w$wrappedInstance = $this->getFooService(false);%w$proxy->setProxyInitializer(null);'
.'%wreturn true;%w}%w);%w}%w',
'%A$wrappedInstance = $this->getFooService(false);%w$proxy->setProxyInitializer(null);%A',
$code
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Bridge/ProxyManager/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
],
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/dependency-injection": "~2.8|~3.0|~4.0",
"symfony/dependency-injection": "~3.4|~4.0",
"ocramius/proxy-manager": "~0.4|~1.0|~2.0"
},
"require-dev": {
Expand Down
25 changes: 21 additions & 4 deletions src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
use Symfony\Component\HttpKernel\KernelInterface;
Expand Down Expand Up @@ -125,6 +126,9 @@ protected function execute(InputInterface $input, OutputInterface $output)

$this->filesystem->remove($oldCacheDir);

// The current event dispatcher is stale, let's not use it anymore
$this->getApplication()->setDispatcher(new EventDispatcher());

if ($output->isVerbose()) {
$io->comment('Finished');
}
Expand Down Expand Up @@ -213,13 +217,19 @@ protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = tr
}

// fix references to container's class
$tempContainerClass = get_class($tempKernel->getContainer());
$realContainerClass = get_class($realKernel->getContainer());
$tempContainerClass = $tempKernel->getContainerClass();
$realContainerClass = $tempKernel->getRealContainerClass();
foreach (Finder::create()->files()->depth('<2')->name($tempContainerClass.'*')->in($warmupDir) as $file) {
$content = str_replace($tempContainerClass, $realContainerClass, file_get_contents($file));
file_put_contents($file, $content);
rename($file, str_replace(DIRECTORY_SEPARATOR.$tempContainerClass, DIRECTORY_SEPARATOR.$realContainerClass, $file));
}
if (is_dir($tempContainerDir = $warmupDir.'/'.get_class($tempKernel->getContainer()))) {
foreach (Finder::create()->files()->in($tempContainerDir) as $file) {
$content = str_replace($tempContainerClass, $realContainerClass, file_get_contents($file));
file_put_contents($file, $content);
}
}

// remove temp kernel file after cache warmed up
@unlink($tempKernelFile);
Expand All @@ -245,7 +255,9 @@ protected function getTempKernel(KernelInterface $parent, $namespace, $parentCla
// to avoid the many problems in serialized resources files
$class = substr($parentClass, 0, -1).'_';
// the temp container class must be changed too
$containerClass = var_export(substr(get_class($parent->getContainer()), 0, -1).'_', true);
$container = $parent->getContainer();
$realContainerClass = var_export($container->hasParameter('kernel.container_class') ? $container->getParameter('kernel.container_class') : get_class($parent->getContainer()), true);
$containerClass = substr_replace($realContainerClass, '_', -2, 1);

if (method_exists($parent, 'getProjectDir')) {
$projectDir = var_export(realpath($parent->getProjectDir()), true);
Expand Down Expand Up @@ -281,7 +293,12 @@ public function getLogDir()
return $logDir;
}

protected function getContainerClass()
public function getRealContainerClass()
{
return $realContainerClass;
}

public function getContainerClass()
{
return $containerClass;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ public function testCacheIsFreshAfterCacheClearedWithWarmup()
}

// check that app kernel file present in meta file of container's cache
$containerRef = new \ReflectionObject($this->kernel->getContainer());
$containerFile = $containerRef->getFileName();
$containerClass = $this->kernel->getContainer()->getParameter('kernel.container_class');
$containerRef = new \ReflectionClass($containerClass);
$containerFile = dirname(dirname($containerRef->getFileName())).'/'.$containerClass.'.php';
$containerMetaFile = $containerFile.'.meta';
$kernelRef = new \ReflectionObject($this->kernel);
$kernelFile = $kernelRef->getFileName();
Expand All @@ -83,6 +84,12 @@ public function testCacheIsFreshAfterCacheClearedWithWarmup()
}
}
$this->assertTrue($found, 'Kernel file should present as resource');
$this->assertRegExp(sprintf('/\'kernel.container_class\'\s*=>\s*\'%s\'/', get_class($this->kernel->getContainer())), file_get_contents($containerFile), 'kernel.container_class is properly set on the dumped container');

if (defined('HHVM_VERSION')) {
return;
}
$containerRef = new \ReflectionClass(require $containerFile);
$containerFile = str_replace('tes_'.DIRECTORY_SEPARATOR, 'test'.DIRECTORY_SEPARATOR, $containerRef->getFileName());
$this->assertRegExp(sprintf('/\'kernel.container_class\'\s*=>\s*\'%s\'/', $containerClass), file_get_contents($containerFile), 'kernel.container_class is properly set on the dumped container');
}
}
80 changes: 45 additions & 35 deletions src/Symfony/Component/DependencyInjection/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class Container implements ResettableContainerInterface
protected $parameterBag;

protected $services = array();
protected $fileMap = array();
protected $methodMap = array();
protected $aliases = array();
protected $loading = array();
Expand Down Expand Up @@ -203,7 +204,7 @@ public function set($id, $service)
} else {
@trigger_error(sprintf('Setting the "%s" private service is deprecated since Symfony 3.2 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED);
}
} elseif (isset($this->methodMap[$id])) {
} elseif (isset($this->fileMap[$id]) || isset($this->methodMap[$id])) {
if (null === $service) {
@trigger_error(sprintf('Unsetting the "%s" pre-defined service is deprecated since Symfony 3.3 and won\'t be supported anymore in Symfony 4.0.', $id), E_USER_DEPRECATED);
} else {
Expand Down Expand Up @@ -235,7 +236,7 @@ public function has($id)
return true;
}

if (isset($this->methodMap[$id])) {
if (isset($this->fileMap[$id]) || isset($this->methodMap[$id])) {
return true;
}

Expand Down Expand Up @@ -299,49 +300,48 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE
throw new ServiceCircularReferenceException($id, array_keys($this->loading));
}

if (isset($this->methodMap[$id])) {
Copy link
Member Author

Choose a reason for hiding this comment

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

the diff in this method is better viewed ignoring whitespaces

$method = $this->methodMap[$id];
} elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
$id = $normalizedId;
continue;
} elseif (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) {
// We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
// 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);
// $method is set to the right value, proceed
} else {
if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
if (!$id) {
throw new ServiceNotFoundException($id);
}

$alternatives = array();
foreach ($this->getServiceIds() as $knownId) {
$lev = levenshtein($id, $knownId);
if ($lev <= strlen($id) / 3 || false !== strpos($knownId, $id)) {
$alternatives[] = $knownId;
}
}

throw new ServiceNotFoundException($id, null, null, $alternatives);
}

return;
}

$this->loading[$id] = true;

try {
$service = $this->$method();
if (isset($this->fileMap[$id])) {
return $this->load($this->fileMap[$id]);
} elseif (isset($this->methodMap[$id])) {
return $this->{$this->methodMap[$id]}();
} elseif (--$i && $id !== $normalizedId = $this->normalizeId($id)) {
$id = $normalizedId;
continue;
} elseif (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this, $method = 'get'.strtr($id, $this->underscoreMap).'Service')) {
// We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
// 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}();
}

break;
} catch (\Exception $e) {
unset($this->services[$id]);

throw $e;
} finally {
unset($this->loading[$id]);
}
}

if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
if (!$id) {
throw new ServiceNotFoundException($id);
}

$alternatives = array();
foreach ($this->getServiceIds() as $knownId) {
$lev = levenshtein($id, $knownId);
if ($lev <= strlen($id) / 3 || false !== strpos($knownId, $id)) {
$alternatives[] = $knownId;
}
}

return $service;
throw new ServiceNotFoundException($id, null, null, $alternatives);
}
}

Expand Down Expand Up @@ -401,7 +401,7 @@ public function getServiceIds()
}
$ids[] = 'service_container';

return array_unique(array_merge($ids, array_keys($this->methodMap), array_keys($this->services)));
return array_unique(array_merge($ids, array_keys($this->methodMap), array_keys($this->fileMap), array_keys($this->services)));
}

/**
Expand All @@ -428,6 +428,16 @@ public static function underscore($id)
return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), str_replace('_', '.', $id)));
}

/**
* Creates a service by requiring its factory file.
*
* @return object The service created by the file
*/
protected function load($file)
{
return require $file;
}

/**
* Fetches a variable from the environment.
*
Expand Down
Loading