Skip to content

Commit 6d029fd

Browse files
Merge branch '3.2' into 3.3
* 3.2: [DI] use assertStringEqualsFile when possible [VarDumper] Adapt to php 7.2 changes [DI] Fix using private services in expressions [Form][TwigBridge] Don't render _method in form_rest() for a child form [Form] Static call TimezoneType::getTimezones Removed references for non existent validator constraints Remove unused mocks/vars [DoctrineBridge][PropertyInfo] Added support for Doctrine Embeddables [Validator] Fix IbanValidator for ukrainian IBANs
2 parents 7984972 + 508be4b commit 6d029fd

File tree

8 files changed

+166
-8
lines changed

8 files changed

+166
-8
lines changed

Compiler/AnalyzeServiceReferencesPass.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313

1414
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1515
use Symfony\Component\DependencyInjection\Definition;
16+
use Symfony\Component\DependencyInjection\ExpressionLanguage;
1617
use Symfony\Component\DependencyInjection\Reference;
1718
use Symfony\Component\DependencyInjection\ContainerBuilder;
19+
use Symfony\Component\ExpressionLanguage\Expression;
1820

1921
/**
2022
* Run this pass before passes that need to know more about the relation of
@@ -32,6 +34,7 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
3234
private $repeatedPass;
3335
private $onlyConstructorArguments;
3436
private $lazy;
37+
private $expressionLanguage;
3538

3639
/**
3740
* @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls
@@ -79,6 +82,11 @@ protected function processValue($value, $isRoot = false)
7982

8083
return $value;
8184
}
85+
if ($value instanceof Expression) {
86+
$this->getExpressionLanguage()->compile((string) $value, array('this' => 'container'));
87+
88+
return $value;
89+
}
8290
if ($value instanceof Reference) {
8391
$targetDefinition = $this->getDefinition((string) $value);
8492

@@ -143,4 +151,27 @@ private function getDefinitionId($id)
143151

144152
return $id;
145153
}
154+
155+
private function getExpressionLanguage()
156+
{
157+
if (null === $this->expressionLanguage) {
158+
$providers = $this->container->getExpressionLanguageProviders();
159+
$this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) {
160+
if ('""' === substr_replace($arg, '', 1, -1)) {
161+
$id = stripcslashes(substr($arg, 1, -1));
162+
163+
$this->graph->connect(
164+
$this->currentId,
165+
$this->currentDefinition,
166+
$this->getDefinitionId($id),
167+
$this->getDefinition($id)
168+
);
169+
}
170+
171+
return sprintf('$this->get(%s)', $arg);
172+
});
173+
}
174+
175+
return $this->expressionLanguage;
176+
}
146177
}

Dumper/PhpDumper.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1710,7 +1710,15 @@ private function getExpressionLanguage()
17101710
throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
17111711
}
17121712
$providers = $this->container->getExpressionLanguageProviders();
1713-
$this->expressionLanguage = new ExpressionLanguage(null, $providers);
1713+
$this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) {
1714+
$id = '""' === substr_replace($arg, '', 1, -1) ? stripcslashes(substr($arg, 1, -1)) : null;
1715+
1716+
if (null !== $id && ($this->container->hasAlias($id) || $this->container->hasDefinition($id))) {
1717+
return $this->getServiceCall($id);
1718+
}
1719+
1720+
return sprintf('$this->get(%s)', $arg);
1721+
});
17141722

17151723
if ($this->container->isTrackingResources()) {
17161724
foreach ($providers as $provider) {

ExpressionLanguage.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ class ExpressionLanguage extends BaseExpressionLanguage
2525
/**
2626
* {@inheritdoc}
2727
*/
28-
public function __construct($cache = null, array $providers = array())
28+
public function __construct($cache = null, array $providers = array(), callable $serviceCompiler = null)
2929
{
3030
// prepend the default provider to let users override it easily
31-
array_unshift($providers, new ExpressionLanguageProvider());
31+
array_unshift($providers, new ExpressionLanguageProvider($serviceCompiler));
3232

3333
parent::__construct($cache, $providers);
3434
}

ExpressionLanguageProvider.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,17 @@
2424
*/
2525
class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface
2626
{
27+
private $serviceCompiler;
28+
29+
public function __construct(callable $serviceCompiler = null)
30+
{
31+
$this->serviceCompiler = $serviceCompiler;
32+
}
33+
2734
public function getFunctions()
2835
{
2936
return array(
30-
new ExpressionFunction('service', function ($arg) {
37+
new ExpressionFunction('service', $this->serviceCompiler ?: function ($arg) {
3138
return sprintf('$this->get(%s)', $arg);
3239
}, function (array $variables, $value) {
3340
return $variables['container']->get($value);

Tests/Dumper/PhpDumperTest.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,15 @@ public function testAddServiceWithoutCompilation()
138138
{
139139
$container = include self::$fixturesPath.'/containers/container9.php';
140140
$dumper = new PhpDumper($container);
141-
$this->assertEquals(str_replace('%path%', str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9.php')), $dumper->dump(), '->dump() dumps services');
141+
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services9.php', str_replace(str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), '%path%', $dumper->dump()), '->dump() dumps services');
142142
}
143143

144144
public function testAddService()
145145
{
146146
$container = include self::$fixturesPath.'/containers/container9.php';
147147
$container->compile();
148148
$dumper = new PhpDumper($container);
149-
$this->assertEquals(str_replace('%path%', str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), file_get_contents(self::$fixturesPath.'/php/services9_compiled.php')), $dumper->dump(), '->dump() dumps services');
149+
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services9_compiled.php', str_replace(str_replace('\\', '\\\\', self::$fixturesPath.DIRECTORY_SEPARATOR.'includes'.DIRECTORY_SEPARATOR), '%path%', $dumper->dump()), '->dump() dumps services');
150150

151151
$container = new ContainerBuilder();
152152
$container->register('foo', 'FooClass')->addArgument(new \stdClass());
@@ -585,6 +585,22 @@ public function testPrivateWithIgnoreOnInvalidReference()
585585
$this->assertInstanceOf('BazClass', $container->get('bar')->getBaz());
586586
}
587587

588+
public function testExpressionReferencingPrivateService()
589+
{
590+
$container = new ContainerBuilder();
591+
$container->register('private_bar', 'stdClass')
592+
->setPublic(false);
593+
$container->register('private_foo', 'stdClass')
594+
->setPublic(false);
595+
$container->register('public_foo', 'stdClass')
596+
->addArgument(new Expression('service("private_foo")'));
597+
598+
$container->compile();
599+
$dumper = new PhpDumper($container);
600+
601+
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_in_expression.php', $dumper->dump());
602+
}
603+
588604
public function testDumpHandlesLiteralClassWithRootNamespace()
589605
{
590606
$container = new ContainerBuilder();

Tests/Fixtures/php/services9.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ protected function getMethodCall1Service()
356356
if ($this->has('foobaz')) {
357357
$instance->setBar($this->get('foobaz', ContainerInterface::NULL_ON_INVALID_REFERENCE));
358358
}
359-
$instance->setBar(($this->get("foo")->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default"))));
359+
$instance->setBar((${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->get('foo')) && false ?: '_'}->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default"))));
360360

361361
return $instance;
362362
}

Tests/Fixtures/php/services9_compiled.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ protected function getMethodCall1Service()
348348

349349
$instance->setBar(${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->get('foo')) && false ?: '_'});
350350
$instance->setBar(NULL);
351-
$instance->setBar(($this->get("foo")->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default"))));
351+
$instance->setBar((${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->get('foo')) && false ?: '_'}->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default"))));
352352

353353
return $instance;
354354
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
4+
use Symfony\Component\DependencyInjection\ContainerInterface;
5+
use Symfony\Component\DependencyInjection\Container;
6+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
7+
use Symfony\Component\DependencyInjection\Exception\LogicException;
8+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
9+
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
10+
11+
/**
12+
* ProjectServiceContainer.
13+
*
14+
* This class has been auto-generated
15+
* by the Symfony Dependency Injection Component.
16+
*
17+
* @final since Symfony 3.3
18+
*/
19+
class ProjectServiceContainer extends Container
20+
{
21+
private $parameters;
22+
private $targetDirs = array();
23+
24+
/**
25+
* Constructor.
26+
*/
27+
public function __construct()
28+
{
29+
$this->services = array();
30+
$this->methodMap = array(
31+
'private_foo' => 'getPrivateFooService',
32+
'public_foo' => 'getPublicFooService',
33+
);
34+
$this->privates = array(
35+
'private_foo' => true,
36+
);
37+
38+
$this->aliases = array();
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function compile()
45+
{
46+
throw new LogicException('You cannot compile a dumped container that was already compiled.');
47+
}
48+
49+
/**
50+
* {@inheritdoc}
51+
*/
52+
public function isCompiled()
53+
{
54+
return true;
55+
}
56+
57+
/**
58+
* {@inheritdoc}
59+
*/
60+
public function isFrozen()
61+
{
62+
@trigger_error(sprintf('The %s() method is deprecated since version 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), E_USER_DEPRECATED);
63+
64+
return true;
65+
}
66+
67+
/**
68+
* Gets the 'public_foo' service.
69+
*
70+
* This service is shared.
71+
* This method always returns the same instance of the service.
72+
*
73+
* @return \stdClass A stdClass instance
74+
*/
75+
protected function getPublicFooService()
76+
{
77+
return $this->services['public_foo'] = new \stdClass(${($_ = isset($this->services['private_foo']) ? $this->services['private_foo'] : $this->getPrivateFooService()) && false ?: '_'});
78+
}
79+
80+
/**
81+
* Gets the 'private_foo' service.
82+
*
83+
* This service is shared.
84+
* This method always returns the same instance of the service.
85+
*
86+
* This service is private.
87+
* If you want to be able to request this service from the container directly,
88+
* make it public, otherwise you might end up with broken code.
89+
*
90+
* @return \stdClass A stdClass instance
91+
*/
92+
protected function getPrivateFooService()
93+
{
94+
return $this->services['private_foo'] = new \stdClass();
95+
}
96+
}

0 commit comments

Comments
 (0)