Skip to content

Commit 5e4e90a

Browse files
Make evaluation of expression arguments optional
To prevent all kinds of runtime errors, evalution of expression arguments has been made optional and should be deliberately turned on. This fixes #7.
1 parent 3d20b08 commit 5e4e90a

File tree

7 files changed

+102
-8
lines changed

7 files changed

+102
-8
lines changed

ArgumentValidator.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ class ArgumentValidator implements ArgumentValidatorInterface
1616
{
1717
private $containerBuilder;
1818
private $resultingClassResolver;
19+
private $evaluateExpressions;
1920

2021
public function __construct(
2122
ContainerBuilder $containerBuilder,
22-
ResultingClassResolverInterface $resultingClassResolver
23+
ResultingClassResolverInterface $resultingClassResolver,
24+
$evaluateExpressions = false
2325
) {
2426
$this->containerBuilder = $containerBuilder;
2527
$this->resultingClassResolver = $resultingClassResolver;
28+
$this->evaluateExpressions = $evaluateExpressions;
2629
}
2730

2831
public function validate(\ReflectionParameter $parameter, $argument)
@@ -88,10 +91,26 @@ private function validateExpressionArgument($className, Expression $expression,
8891
{
8992
$expressionLanguage = new ExpressionLanguage();
9093

94+
$this->validateExpressionSyntax($expression, $expressionLanguage);
95+
96+
if ($this->evaluateExpressions) {
97+
$this->validateExpressionResult($className, $expression, $allowsNull, $expressionLanguage);
98+
}
99+
}
100+
101+
private function validateExpressionSyntax(Expression $expression, ExpressionLanguage $expressionLanguage)
102+
{
91103
try {
92-
$result = $expressionLanguage->evaluate($expression, array('container' => $this->containerBuilder));
104+
$expressionLanguage->parse($expression, array('container'));
93105
} catch (SyntaxError $exception) {
94106
throw new InvalidExpressionSyntaxException($expression, $exception);
107+
}
108+
}
109+
110+
private function validateExpressionResult($className, Expression $expression, $allowsNull, ExpressionLanguage $expressionLanguage)
111+
{
112+
try {
113+
$result = $expressionLanguage->evaluate($expression, array('container' => $this->containerBuilder));
95114
} catch (\Exception $exception) {
96115
throw new InvalidExpressionException($expression, $exception);
97116
}

Compiler/ValidateServiceDefinitionsPass.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Matthias\SymfonyServiceDefinitionValidator\Compiler;
44

55
use Matthias\SymfonyServiceDefinitionValidator\BatchServiceDefinitionValidator;
6+
use Matthias\SymfonyServiceDefinitionValidator\Configuration;
67
use Matthias\SymfonyServiceDefinitionValidator\Error\Printer\SimpleErrorListPrinter;
78
use Matthias\SymfonyServiceDefinitionValidator\Error\ValidationErrorFactory;
89
use Matthias\SymfonyServiceDefinitionValidator\Exception\InvalidServiceDefinitionsException;
@@ -12,11 +13,18 @@
1213

1314
class ValidateServiceDefinitionsPass implements CompilerPassInterface
1415
{
16+
private $configuration;
17+
18+
public function __construct(Configuration $configuration = null)
19+
{
20+
$this->configuration = $configuration;
21+
}
22+
1523
public function process(ContainerBuilder $container)
1624
{
1725
$serviceDefinitions = $container->getDefinitions();
1826

19-
$validatorFactory = new ServiceDefinitionValidatorFactory();
27+
$validatorFactory = new ServiceDefinitionValidatorFactory($this->configuration);
2028
$validator = $validatorFactory->create($container);
2129

2230
$batchValidator = new BatchServiceDefinitionValidator(

Configuration.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Matthias\SymfonyServiceDefinitionValidator;
4+
5+
class Configuration
6+
{
7+
private $evaluateExpressions = false;
8+
9+
/**
10+
* Configure whether or not to evaluate expression arguments
11+
*
12+
* @param boolean $evaluateExpressions
13+
*/
14+
public function setEvaluateExpressions($evaluateExpressions)
15+
{
16+
$this->evaluateExpressions = (boolean) $evaluateExpressions;
17+
18+
return $this;
19+
}
20+
21+
public function getEvaluateExpressions()
22+
{
23+
return $this->evaluateExpressions;
24+
}
25+
}

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ To process multiple definitions at once, wrap the validator inside a batch valid
6767

6868
```php
6969
<?php
70+
7071
use Matthias\SymfonyServiceDefinitionValidator\BatchServiceDefinitionValidator;
7172
use Matthias\SymfonyServiceDefinitionValidator\Error\ValidationErrorFactory;
7273

@@ -107,6 +108,28 @@ class SomeBundle extends Bundle
107108
This compiler pass will throw an exception. The message of this exception will contain a list
108109
of invalid service definitions.
109110

111+
### Configure the validator
112+
113+
Both ``ValidateServiceDefinitionsPass`` and ``ServiceDefinitionValidatorFactory`` accept a ``Configuration`` object.
114+
It allows you to configure whether or not expression arguments should be evaluated. Since evaluating expressions can
115+
cause all kinds of runtime errors, it is *off by default*, but you can easily turn it on:
116+
117+
```php
118+
<?php
119+
120+
use Matthias\SymfonyServiceDefinitionValidator\Configuration;
121+
use Matthias\SymfonyServiceDefinitionValidator\Compiler\ValidateServiceDefinitionsPass;
122+
123+
$configuration = new Configuration();
124+
$configuration->setEvaluateExpressions(true);
125+
126+
$compilerPass = new ValidateServiceDefinitionsPass($configuration);
127+
128+
// or
129+
130+
$validatorFactory = new ServiceDefinitionValidatorFactory($configuration);
131+
```
132+
110133
### Fixing invalid service definitions in third-party bundles
111134

112135
When the validator finds a problem with one of the service definitions that is your own, you can of course fix the

ServiceDefinitionValidatorFactory.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,28 @@
66

77
class ServiceDefinitionValidatorFactory implements ServiceDefinitionValidatorFactoryInterface
88
{
9+
private $configuration;
10+
11+
public function __construct(Configuration $configuration = null)
12+
{
13+
if ($configuration === null) {
14+
$configuration = new Configuration();
15+
}
16+
17+
$this->configuration = $configuration;
18+
}
19+
920
public function create(ContainerBuilder $containerBuilder)
1021
{
1122
$resultingClassResolver = new ResultingClassResolver($containerBuilder);
1223

1324
$constructorResolver = new ConstructorResolver($containerBuilder, $resultingClassResolver);
1425

15-
$argumentValidator = new ArgumentValidator($containerBuilder, $resultingClassResolver);
26+
$argumentValidator = new ArgumentValidator(
27+
$containerBuilder,
28+
$resultingClassResolver,
29+
$this->configuration->getEvaluateExpressions()
30+
);
1631
$argumentsValidator = new ArgumentsValidator($argumentValidator);
1732

1833
$definitionArgumentsValidator = new DefinitionArgumentsValidator($constructorResolver, $argumentsValidator);

Tests/ArgumentValidatorTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public function testFailsWhenResultOfExpressionIsNotAnObjectOfTheExpectedClass()
9494
$containerBuilder = new ContainerBuilder();
9595
$containerBuilder->setDefinition('service_wrong_class', new Definition('stdClass'));
9696

97-
$validator = new ArgumentValidator($containerBuilder, $this->createMockResultingClassResolver());
97+
$validator = new ArgumentValidator($containerBuilder, $this->createMockResultingClassResolver(), true);
9898

9999
$this->setExpectedException('Matthias\SymfonyServiceDefinitionValidator\Exception\TypeHintMismatchException', 'ExpectedClass');
100100

@@ -108,7 +108,7 @@ public function testFailsWhenResultOfExpressionIsNotAnObject()
108108
$parameter = new \ReflectionParameter(array($class, '__construct'), 'expected');
109109
$argument = new Expression('"a string"');
110110

111-
$validator = new ArgumentValidator(new ContainerBuilder(), $this->createMockResultingClassResolver());
111+
$validator = new ArgumentValidator(new ContainerBuilder(), $this->createMockResultingClassResolver(), true);
112112

113113
$this->setExpectedException('Matthias\SymfonyServiceDefinitionValidator\Exception\TypeHintMismatchException', 'ExpectedClass');
114114

@@ -152,7 +152,7 @@ public function testFailsIfExpressionCouldNotBeEvaluated()
152152
$parameter = new \ReflectionParameter(array($class, '__construct'), 'expected');
153153
$argument = new Expression('service("invalid service")');
154154

155-
$validator = new ArgumentValidator(new ContainerBuilder(), $this->createMockResultingClassResolver());
155+
$validator = new ArgumentValidator(new ContainerBuilder(), $this->createMockResultingClassResolver(), true);
156156

157157
$this->setExpectedException('Matthias\SymfonyServiceDefinitionValidator\Exception\InvalidExpressionException');
158158

Tests/Functional/FunctionalTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Matthias\SymfonyServiceDefinitionValidator\Functional;
44

55
use Matthias\SymfonyServiceDefinitionValidator\Compiler\ValidateServiceDefinitionsPass;
6+
use Matthias\SymfonyServiceDefinitionValidator\Configuration;
67
use Symfony\Component\Config\FileLocator;
78
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
89
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -19,7 +20,10 @@ protected function setUp()
1920
{
2021
$this->container = new ContainerBuilder();
2122

22-
$compilerPass = new ValidateServiceDefinitionsPass();
23+
$configuration = new Configuration();
24+
$configuration->setEvaluateExpressions(true);
25+
26+
$compilerPass = new ValidateServiceDefinitionsPass($configuration);
2327

2428
$this->container->addCompilerPass($compilerPass, PassConfig::TYPE_AFTER_REMOVING);
2529
}

0 commit comments

Comments
 (0)