Skip to content

Commit ab3f9d4

Browse files
Merge branch '4.4' into 5.0
* 4.4: [Validator] fix access to uninitialized property when getting value [HttpClient] Fix regex bearer [Translator] Default value for 'sort' option in translation:update should be 'asc' [HttpKernel] Fix stale-if-error behavior, add tests [Intl] Provide more locale translations [Mailer] Fix STARTTLS support for Postmark and Mandrill [Messenger] Check for all serialization exceptions during message dec… [Messenger] Fix bug when using single route with XML config Fix exception message in Doctrine Messenger [DI] CheckTypeDeclarationsPass now checks if value is type of parameter type [SecurityBundle] fix security.authentication.provider.ldap_bind arguments Improved error message when no supported user provider is found Mysqli doesn't support the named parameters used by PdoAdapter Added debug argument to decide if debug page should be shown or not Mysqli doesn't support the named parameters used by PdoStore Properly handle phpunit arguments for configuration file [Mailer] add tests for http transports
2 parents 5a56807 + cb20e3c commit ab3f9d4

File tree

5 files changed

+98
-29
lines changed

5 files changed

+98
-29
lines changed

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')) {
175-
return;
176-
}
177-
178-
if ('object' === $type) {
179-
return;
180-
}
181-
182-
if (is_a($class, $type, true)) {
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))) {
183189
return;
184190
}
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])) {
233+
return;
234+
}
235+
236+
if ('string' === $type && \is_callable([$class, '__toString'])) {
237+
return;
238+
}
239+
240+
if ('callable' === $type && (\Closure::class === $class || \is_callable([$class, '__invoke']))) {
216241
return;
217242
}
218243

219-
if ('callable' === $type && \is_array($value) && isset($value[0]) && ($value[0] instanceof Reference || $value[0] instanceof Definition)) {
244+
if ('callable' === $type && \is_array($value) && isset($value[0]) && ($value[0] instanceof Reference || $value[0] instanceof Definition || \is_string($value[0]))) {
220245
return;
221246
}
222247

223-
if (\in_array($type, ['callable', 'Closure'], true) && $value instanceof ServiceClosureArgument) {
248+
if ('iterable' === $type && (\is_array($value) || is_subclass_of($class, \Traversable::class))) {
224249
return;
225250
}
226251

227-
if ('iterable' === $type && (\is_array($value) || $value instanceof \Traversable || $value instanceof IteratorArgument)) {
252+
if ('object' === $type && !isset(self::BUILTIN_TYPES[$class])) {
228253
return;
229254
}
230255

231-
if ('Traversable' === $type && ($value instanceof \Traversable || $value instanceof IteratorArgument)) {
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, \is_object($value) ? $class : \gettype($value), $parameter);
239264
}
240265
}
241266

Tests/Compiler/CheckTypeDeclarationsPassTest.php

Lines changed: 17 additions & 0 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
/**
@@ -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+
}
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+
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)