8000 feature #18167 [DependencyInjection] Fix a limitation of the PhpDumpe… · symfony/symfony@35be993 · GitHub
[go: up one dir, main page]

Skip to content

Commit 35be993

Browse files
committed
feature #18167 [DependencyInjection] Fix a limitation of the PhpDumper (Ener-Getick)
This PR was squashed before being merged into the 3.1-dev branch (closes #18167). Discussion ---------- [DependencyInjection] Fix a limitation of the PhpDumper | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #17801 | License | MIT | Doc PR | The ``PhpDumper`` cannot currently dump several services' id containing characters unsupported by php. This PR tries to solve this issue by removing the unsupported characters and by sufixing their camelized association to avoid conflicts. Commits ------- 0d147b2 [DependencyInjection] Fix a limitation of the PhpDumper
2 parents aa45949 + 0d147b2 commit 35be993

File tree

6 files changed

+125
-15
lines changed

6 files changed

+125
-15
lines changed

src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ public function getProxyFactoryCode(Definition $definition, $id)
7171
$instantiation .= " \$this->services['$id'] =";
7272
}
7373

74-
$methodName = 'get'.Container::camelize($id).'Service';
74+
if (func_num_args() >= 3) {
75+
$methodName = func_get_arg(2);
76+
} else {
77+
@trigger_error(sprintf('You must use the third argument of %s to define the method to call to construct your service since version 3.1, not using it won\'t be supported in 4.0.', __METHOD__), E_USER_DEPRECATED);
78+
$methodName = 'get'.Container::camelize($id).'Service';
79+
}
7580
$proxyClass = $this->getProxyClassName($definition);
7681

7782
$generatedClass = $this->generateProxyClass($definition);

src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,27 @@ public function testGetProxyCode()
6060
);
6161
}
6262

63+
public function testGetProxyFactoryCodeWithCustomMethod()
64+
{
65+
$definition = new Definition(__CLASS__);
66+
67+
$definition->setLazy(true);
68+
69+
$code = $this->dumper->getProxyFactoryCode($definition, 'foo', 'getFoo2Service');
70+
71+
$this->assertStringMatchesFormat(
72+
'%wif ($lazyLoad) {%wreturn $this->services[\'foo\'] =%s'
73+
.'SymfonyBridgeProxyManagerTestsLazyProxyPhpDumperProxyDumperTest_%s(%wfunction '
74+
.'(&$wrappedInstance, \ProxyManager\Proxy\LazyLoadingInterface $proxy) {'
75+
.'%w$wrappedInstance = $this->getFoo2Service(false);%w$proxy->setProxyInitializer(null);'
76+
.'%wreturn true;%w}%w);%w}%w',
77+
$code
78+
);
79+
}
80+
81+
/**
82+
* @group legacy
83+
*/
6384
public function testGetProxyFactoryCode()
6485
{
6586
$definition = new Definition(__CLASS__);

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ class PhpDumper extends Dumper
5858
private $targetDirRegex;
5959
private $targetDirMaxMatches;
6060
private $docStar;
61+
private $serviceIdToMethodNameMap;
62+
private $usedMethodNames;
6163

6264
/**
6365
* @var \Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface
@@ -106,6 +108,9 @@ public function dump(array $options = array())
106108
'namespace' => '',
107109
'debug' => true,
108110
), $options);
111+
112+
$this->initializeMethodNamesMap($options['base_class']);
113+
109114
$this->docStar = $options['debug'] ? '*' : '';
110115

111116
if (!empty($options['file']) && is_dir($dir = dirname($options['file']))) {
@@ -625,19 +630,20 @@ private function addService($id, $definition)
625630
// with proxies, for 5.3.3 compatibility, the getter must be public to be accessible to the initializer
626631
$isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition);
627632
$visibility = $isProxyCandidate ? 'public' : 'protected';
633+
$methodName = $this->generateMethodName($id);
628634
$code = <<<EOF
629635
630636
/*{$this->docStar}
631637
* Gets the '$id' service.$doc
632638
*$lazyInitializationDoc
633639
* $return
634640
*/
635-
{$visibility} function get{$this->camelize($id)}Service($lazyInitialization)
641+
{$visibility} function {$methodName}($lazyInitialization)
636642
{
637643
638644
EOF;
639645

640-
$code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id) : '';
646+
$code .= $isProxyCandidate ? $this->getProxyDumper()->getProxyFactoryCode($definition, $id, $methodName) : '';
641647

642648
if ($definition->isSynthetic()) {
643649
$code .= sprintf(" throw new RuntimeException('You have requested a synthetic service (\"%s\"). The DIC does not know how to construct this service.');\n }\n", $id);
@@ -864,7 +870,7 @@ private function addMethodMap()
864870
$code = " \$this->methodMap = array(\n";
865871
ksort($definitions);
866872
foreach ($definitions as $id => $definition) {
867-
$code .= ' '.var_export($id, true).' => '.var_export('get'.$this->camelize($id).'Service', true).",\n";
873+
$code .= ' '.var_export($id, true).' => '.var_export($this->generateMethodName($id), true).",\n";
868874
}
869875

870876
return $code." );\n";
@@ -1338,6 +1344,25 @@ private function getServiceCall($id, Reference $reference = null)
13381344
}
13391345
}
13401346

1347+
/**
1348+
* Initializes the method names map to avoid conflicts with the Container methods.
1349+
*
1350+
* @param string $class the container base class
1351+
*/
1352+
private function initializeMethodNamesMap($class)
1353+
{
1354+
$this->serviceIdToMethodNameMap = array();
1355+
$this->usedMethodNames = array();
1356+
1357+
try {
1358+
$reflectionClass = new \ReflectionClass($class);
1359+
foreach ($reflectionClass->getMethods() as $method) {
1360+
$this->usedMethodNames[strtolower($method->getName())] = true;
1361+
}
1362+
} catch (\ReflectionException $e) {
1363+
}
1364+
}
1365+
13411366
/**
13421367
* Convert a service id to a valid PHP method name.
13431368
*
@@ -1347,15 +1372,26 @@ private function getServiceCall($id, Reference $reference = null)
13471372
*
13481373
* @throws InvalidArgumentException
13491374
*/
1350-
private function camelize($id)
1375+
private function generateMethodName($id)
13511376
{
1377+
if (isset($this->serviceIdToMethodNameMap[$id])) {
1378+
return $this->serviceIdToMethodNameMap[$id];
1379+
}
1380+
13521381
$name = Container::camelize($id);
1382+
$name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name);
1383+
$methodName = 'get'.$name.'Service';
1384+
$suffix = 1;
13531385

1354-
if (!preg_match('/^[a-zA-Z0-9_\x7f-\xff]+$/', $name)) {
1355-
throw new InvalidArgumentException(sprintf('Service id "%s" cannot be converted to a valid PHP method name.', $id));
1386+
while (isset($this->usedMethodNames[strtolower($methodName)])) {
1387+
++$suffix;
1388+
$methodName = 'get'.$name.$suffix.'Service';
13561389
}
13571390

1358-
return $name;
1391+
$this->serviceIdToMethodNameMap[$id] = $methodName;
1392+
$this->usedMethodNames[strtolower($methodName)] = true;
1393+
1394+
return $methodName;
13591395
}
13601396

13611397
/**

src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ public function isProxyCandidate(Definition $definition);
3434
*
3535
* @param Definition $definition
3636
* @param string $id service identifier
37+
* @param string $methodName the method name to get the service, will be added to the interface in 4.0.
3738
*
3839
* @return string
3940
*/
40-
public function getProxyFactoryCode(Definition $definition, $id);
41+
public function getProxyFactoryCode(Definition $definition, $id/**, $methodName = null */);
4142

4243
/**
4344
* Generates the code for the lazy proxy.

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,46 @@ public function testServicesWithAnonymousFactories()
132132
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories');
133133
}
134134

135-
/**
136-
* @expectedException \InvalidArgumentException
137-
* @expectedExceptionMessage Service id "bar$" cannot be converted to a valid PHP method name.
138-
*/
139-
public function testAddServiceInvalidServiceId()
135+
public function testAddServiceIdWithUnsupportedCharacters()
140136
{
137+
$class = 'Symfony_DI_PhpDumper_Test_Unsupported_Characters';
141138
$container = new ContainerBuilder();
142139
$container->register('bar$', 'FooClass');
140+
$container->register('bar$!', 'FooClass');
143141
$dumper = new PhpDumper($container);
144-
$dumper->dump();
142+
eval('?>'.$dumper->dump(array('class' => $class)));
143+
144+
$this->assertTrue(method_exists($class, 'getBarService'));
145+
$this->assertTrue(method_exists($class, 'getBar2Service'));
146+
}
147+
148+
public function testConflictingServiceIds()
149+
{
150+
$class = 'Symfony_DI_PhpDumper_Test_Conflicting_Service_Ids';
151+
$container = new ContainerBuilder();
152+
$container->register('foo_bar', 'FooClass');
153+
$container->register('foobar', 'FooClass');
154+
$dumper = new PhpDumper($container);
155+
eval('?>'.$dumper->dump(array('class' => $class)));
156+
157+
$this->assertTrue(method_exists($class, 'getFooBarService'));
158+
$this->assertTrue(method_exists($class, 'getFoobar2Service'));
159+
}
160+
161+
public function testConflictingMethodsWithParent()
162+
{
163+
$class = 'Symfony_DI_PhpDumper_Test_Conflicting_Method_With_Parent';
164+
$container = new ContainerBuilder();
165+
$container->register('bar', 'FooClass');
166+
$container->register('foo_bar', 'FooClass');
167+
$dumper = new PhpDumper($container);
168+
eval('?>'.$dumper->dump(array(
169+
'class' => $class,
170+
'base_class' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\containers\CustomContainer',
171+
)));
172+
173+
$this->assertTrue(method_exists($class, 'getBar2Service'));
174+
$this->assertTrue(method_exists($class, 'getFoobar2Service'));
145175
}
146176

147177
/**
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\containers;
4+
5+
use Symfony\Component\DependencyInjection\Container;
6+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
7+
8+
class CustomContainer extends Container
9+
{
10+
public function getBarService()
11+
{
12+
}
13+
14+
public function getFoobarService()
15+
{
16+
}
17+
}

0 commit comments

Comments
 (0)
0