8000 [DependencyInjection] Fix a limitation of the PhpDumper · symfony/symfony@0d147b2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0d147b2

Browse files
GuilhemNfabpot
authored andcommitted
[DependencyInjection] Fix a limitation of the PhpDumper
1 parent 728bb0f commit 0d147b2

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 F438 +
$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