Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0d147b2

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

File tree

Expand file treeCollapse file tree

6 files changed

+125
-15
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+125
-15
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php
+6-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php
+21Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+43-7Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/LazyProxy/PhpDumper/DumperInterface.php
+2-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php
+36-6Lines 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
/**
+17Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.