Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 538a658

Browse files
pfazzinicolas-grekas
authored andcommitted
[DI] CheckTypeDeclarationsPass now checks if value is type of parameter type
1 parent 79a7b8b commit 538a658

File tree

5 files changed

+101
-32
lines changed

5 files changed

+101
-32
lines changed

src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

1414
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
15+
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
1516
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
17+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
1618
use Symfony\Component\DependencyInjection\Container;
1719
use Symfony\Component\DependencyInjection\Definition;
1820
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
@@ -40,7 +42,23 @@
4042
*/
4143
final class CheckTypeDeclarationsPass extends AbstractRecursivePass
4244
{
43-
private const SCALAR_TYPES = ['int', 'float', 'bool', 'string'];
45+
private const SCALAR_TYPES = [
46+
'int' => true,
47+
'float' => true,
48+
'bool' => true,
49+
'string' => true,
50+
];
51+
52+
private const BUILTIN_TYPES = [
53+
'array' => true,
54+
'bool' => true,
55+
'callable' => true,
56+
'float' => true,
57+
'int' => true,
58+
'iterable' => true,
59+
'object' => true,
60+
'string' => true,
61+
];
4462

4563
private $autoload;
4664
private $skippedIds;
@@ -160,33 +178,17 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
160178
$type = $checkedDefinition->getClass();
161179
}
162180

181+
$class = null;
182+
163183
if ($value instanceof Definition) {
164184
$class = $value->getClass();
165185

166-
if (!$class || (!$this->autoload && !class_exists($class, false) && !interface_exists($class, false))) {
167-
return;
168-
}
169-
170-
if ('callable' === $type && (\Closure::class === $class || method_exists($class, '__invoke'))) {
171-
return;
172-
}
173-
174-
if ('iterable' === $type && is_subclass_of($class, 'Traversable')) {
186+
if (isset(self::BUILTIN_TYPES[strtolower($class)])) {
187+
$class = strtolower($class);
188+
} elseif (!$class || (!$this->autoload && !class_exists($class, false) && !interface_exists($class, false))) {
175189
return;
176190
}
177-
178-
if ('object' === $type) {
179-
return;
180-
}
181-
182-
if (is_a($class, $type, true)) {
183-
return;
184-
}
185-
186-
throw new InvalidParameterTypeException($this->currentId, $class, $parameter);
187-
}
188-
189-
if ($value instanceof Parameter) {
191+
} elseif ($value instanceof Parameter) {
190192
$value = $this->container->getParameter($value);
191193
} elseif ($value instanceof Expression) {
192194
$value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this->container]);
@@ -212,30 +214,53 @@ private function checkType(Definition $checkedDefinition, $value, \ReflectionPar
212214
return;
213215
}
214216

215-
if (\in_array($type, self::SCALAR_TYPES, true) && is_scalar($value)) {
217+
if (null === $class) {
218+
if ($value instanceof IteratorArgument) {
219+
$class = RewindableGenerator::class;
220+
} elseif ($value instanceof ServiceClosureArgument) {
221+
$class = \Closure::class;
222+
} elseif ($value instanceof ServiceLocatorArgument) {
223+
$class = ServiceLocator::class;
224+
} elseif (\is_object($value)) {
225+
$class = \get_class($value);
226+
} else {
227+
$class = \gettype($value);
228+
$class = ['integer' => 'int', 'double' => 'float', 'boolean' => 'bool'][$class] ?? $class;
229+
}
230+
}
231+
232+
if (isset(self::SCALAR_TYPES[$type]) && isset(self::SCALAR_TYPES[$class])) {
216233
return;
217234
}
218235

219-
if ('callable' === $type && \is_array($value) && isset($value[0]) && ($value[0] instanceof Reference || $value[0] instanceof Definition)) {
236+
if ('string' === $type && \is_callable([$class, '__toString'])) {
220237
return;
221238
}
222239

223-
if (\in_array($type, ['callable', 'Closure'], true) && $value instanceof ServiceClosureArgument) {
240+
if ('callable' === $type && (\Closure::class === $class || \is_callable([$class, '__invoke']))) {
224241
return;
225242
}
226243

227-
if ('iterable' === $type && (\is_array($value) || $value instanceof \Traversable || $value instanceof IteratorArgument)) {
244+
if ('callable' === $type && \is_array($value) && isset($value[0]) && ($value[0] instanceof Reference || $value[0] instanceof Definition || \is_string($value[0]))) {
228245
return;
229246
}
230247

231-
if ('Traversable' === $type && ($value instanceof \Traversable || $value instanceof IteratorArgument)) {
248+
if ('iterable' === $type && (\is_array($value) || is_subclass_of($class, \Traversable::class))) {
249+
return;
250+
}
251+
252+
if ('object' === $type && !isset(self::BUILTIN_TYPES[$class])) {
253+
return;
254+
}
255+
256+
if (is_a($class, $type, true)) {
232257
return;
233258
}
234259

235260
$checkFunction = sprintf('is_%s', $parameter->getType()->getName());
236261

237262
if (!$parameter->getType()->isBuiltin() || !$checkFunction($value)) {
238-
throw new InvalidParameterTypeException($this->currentId, \is_object($value) ? \get_class($value) : \gettype($value), $parameter);
263+
throw new InvalidParameterTypeException($this->currentId, $class, $parameter);
239264
}
240265
}
241266

src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarOptionalArgumentNotNull;
2727
use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Foo;
2828
use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\FooObject;
29+
use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Waldo;
30+
use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Wobble;
2931
use Symfony\Component\ExpressionLanguage\Expression;
3032

3133
/**
@@ -286,7 +288,7 @@ public function testProcessSuccessScalarType()
286288
public function testProcessFailsOnPassingScalarTypeToConstructorTypedWithClass()
287289
{
288290
$this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
289-
$this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Bar::__construct" accepts "stdClass", "integer" passed.');
291+
$this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Bar::__construct" accepts "stdClass", "int" passed.');
290292

291293
$container = new ContainerBuilder();
292294

@@ -374,7 +376,7 @@ public function testProcessSuccessWhenPassingArray()
374376
public function testProcessSuccessWhenPassingIntegerToArrayTypedParameter()
375377
{
376378
$this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException::class);
377-
$this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall::setArray" accepts "array", "integer" passed.');
379+
$this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall::setArray" accepts "array", "int" passed.');
378380

379381
$container = new ContainerBuilder();
380382

@@ -562,7 +564,7 @@ public function testProcessDoesNotThrowsExceptionOnValidTypes()
562564
public function testProcessThrowsOnIterableTypeWhenScalarPassed()
563565
{
564566
$this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
565-
$this->expectExceptionMessage('Invalid definition for service "bar_call": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setIterable" accepts "iterable", "integer" passed.');
567+
$this->expectExceptionMessage('Invalid definition for service "bar_call": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setIterable" accepts "iterable", "int" passed.');
566568

567569
$container = new ContainerBuilder();
568570

@@ -602,6 +604,21 @@ public function testProcessResolveExpressions()
602604
$this->addToAssertionCount(1);
603605
}
604606

607+
public function testProcessSuccessWhenExpressionReturnsObject()
608+
{
609+
$container = new ContainerBuilder();
610+
611+
$container->register('waldo', Waldo::class);
612+
613+
$container
614+
->register('wobble', Wobble::class)
615+
->setArguments([new Expression("service('waldo')")]);
616+
617+
(new CheckTypeDeclarationsPass(true))->process($container);
618+
619+
$this->addToAssertionCount(1);
620+
}
621+
605622
public function testProcessHandleMixedEnvPlaceholder()
606623
{
607624
$this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass;
4+
5+
class Waldo implements WaldoInterface
6+
{
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass;
4+
5+
interface WaldoInterface
6+
{
7+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass;
4+
5+
class Wobble
6+
{
7+
private $waldo;
8+
9+
public function __construct(WaldoInterface $waldo)
10+
{
11+
$this->waldo = $waldo;
12+
}
13+
}

0 commit comments

Comments
 (0)