diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/TestBundle.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/TestBundle.php
index d90041213ce3..ab0b06ffb370 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/TestBundle.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/TestBundle.php
@@ -14,6 +14,8 @@
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\AnnotationReaderPass;
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\Config\CustomConfig;
use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\TranslationDebugPass;
+use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -31,5 +33,15 @@ public function build(ContainerBuilder $container)
$container->addCompilerPass(new AnnotationReaderPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new TranslationDebugPass());
+
+ $container->addCompilerPass(new class() implements CompilerPassInterface {
+ public function process(ContainerBuilder $container)
+ {
+ $container->removeDefinition('twig.controller.exception');
+ $container->removeDefinition('twig.controller.preview_error');
+ }
+ });
+
+ $container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100);
}
}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/TestBundle.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/TestBundle.php
new file mode 100644
index 000000000000..5197a16195e2
--- /dev/null
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/TestBundle.php
@@ -0,0 +1,38 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle;
+
+use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Compiler\PassConfig;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+class TestBundle extends Bundle
+{
+ public function build(ContainerBuilder $container)
+ {
+ $container->setParameter('container.build_hash', 'test_bundle');
+ $container->setParameter('container.build_time', time());
+ $container->setParameter('container.build_id', 'test_bundle');
+
+ $container->addCompilerPass(new class() implements CompilerPassInterface {
+ public function process(ContainerBuilder $container)
+ {
+ $container->removeDefinition('twig.controller.exception');
+ $container->removeDefinition('twig.controller.preview_error');
+ }
+ });
+
+ $container->addCompilerPass(new CheckTypeDeclarationsPass(true), PassConfig::TYPE_AFTER_REMOVING, -100);
+ }
+}
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AbstractTokenCompareRoles/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AbstractTokenCompareRoles/bundles.php
index bedfbb1bd82a..054405274e83 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AbstractTokenCompareRoles/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AbstractTokenCompareRoles/bundles.php
@@ -12,9 +12,11 @@
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\SecuredPageBundle\SecuredPageBundle;
+use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle;
return [
new FrameworkBundle(),
new SecurityBundle(),
new SecuredPageBundle(),
+ new TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AliasedEvents/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AliasedEvents/bundles.php
index d7b7c498f84a..115dd2c357e8 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AliasedEvents/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AliasedEvents/bundles.php
@@ -12,9 +12,11 @@
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\EventBundle\EventBundle;
+use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle;
return [
new FrameworkBundle(),
new SecurityBundle(),
new EventBundle(),
+ new TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/bundles.php
index 535a4bf517b8..794461855cb8 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/bundles.php
@@ -13,4 +13,5 @@
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AutowiringBundle\AutowiringBundle(),
+ new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/bundles.php
index 65a38200e759..81f9c48b64ca 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/bundles.php
@@ -14,4 +14,5 @@
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\CsrfFormLoginBundle\CsrfFormLoginBundle(),
+ new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/bundles.php
index 7928a468da7f..b77f03be2703 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/bundles.php
@@ -13,4 +13,5 @@
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FirewallEntryPointBundle\FirewallEntryPointBundle(),
+ new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/bundles.php
index cd367a95b477..bbb9107456b9 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/bundles.php
@@ -13,4 +13,5 @@
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\JsonLoginBundle\JsonLoginBundle(),
+ new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/bundles.php
index bcfd17425cfd..edf6dae14c06 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/bundles.php
@@ -12,4 +12,5 @@
return [
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
+ new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php
index 9a26fb163a77..a52ae15f6d9b 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutAccess/bundles.php
@@ -11,8 +11,10 @@
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
+use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle;
return [
new FrameworkBundle(),
new SecurityBundle(),
+ new TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/bundles.php
index 9a26fb163a77..a52ae15f6d9b 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/bundles.php
@@ -11,8 +11,10 @@
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
+use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle;
return [
new FrameworkBundle(),
new SecurityBundle(),
+ new TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/MissingUserProvider/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/MissingUserProvider/bundles.php
index ccff0d356cab..0e34621a35cc 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/MissingUserProvider/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/MissingUserProvider/bundles.php
@@ -12,9 +12,11 @@
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\MissingUserProviderBundle\MissingUserProviderBundle;
+use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle;
return [
new FrameworkBundle(),
new SecurityBundle(),
new MissingUserProviderBundle(),
+ new TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/bundles.php
index bcfd17425cfd..edf6dae14c06 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/PasswordEncode/bundles.php
@@ -12,4 +12,5 @@
return [
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
+ new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php
index 9a26fb163a77..a52ae15f6d9b 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeLogout/bundles.php
@@ -11,8 +11,10 @@
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
+use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle;
return [
new FrameworkBundle(),
new SecurityBundle(),
+ new TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/bundles.php
index 9a26fb163a77..a52ae15f6d9b 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/bundles.php
@@ -11,8 +11,10 @@
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
+use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle;
return [
new FrameworkBundle(),
new SecurityBundle(),
+ new TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/bundles.php
index 95041e7ad465..cef48bfcc4b4 100644
--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/bundles.php
+++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/bundles.php
@@ -12,6 +12,7 @@
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\FormLoginBundle;
+use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\TestBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
return [
@@ -19,4 +20,5 @@
new SecurityBundle(),
new TwigBundle(),
new FormLoginBundle(),
+ new TestBundle(),
];
diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json
index 77bd4a0cfb76..7ecd3a12c13e 100644
--- a/src/Symfony/Bundle/SecurityBundle/composer.json
+++ b/src/Symfony/Bundle/SecurityBundle/composer.json
@@ -19,7 +19,7 @@
"php": "^7.1.3",
"ext-xml": "*",
"symfony/config": "^4.2|^5.0",
- "symfony/dependency-injection": "^4.2|^5.0",
+ "symfony/dependency-injection": "^4.4|^5.0",
"symfony/http-kernel": "^4.4",
"symfony/security-core": "^4.4",
"symfony/security-csrf": "^4.2|^5.0",
diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md
index c90cfa747128..d1771da84656 100644
--- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md
+++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md
@@ -4,6 +4,7 @@ CHANGELOG
4.4.0
-----
+ * added `CheckTypeDeclarationsPass` to check injected parameters type during compilation
* added support for opcache.preload by generating a preloading script in the cache folder
* added support for dumping the container in one file instead of many files
* deprecated support for short factories and short configurators in Yaml
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
index 8453e4e62ada..ad3cb5295cc7 100644
--- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
+++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php
@@ -133,9 +133,12 @@ protected function getConstructor(Definition $definition, $required)
list($class, $method) = $factory;
if ($class instanceof Reference) {
$class = $this->container->findDefinition((string) $class)->getClass();
+ } elseif ($class instanceof Definition) {
+ $class = $class->getClass();
} elseif (null === $class) {
$class = $definition->getClass();
}
+
if ('__construct' === $method) {
throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId));
}
diff --git a/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php
new file mode 100644
index 000000000000..9bc57539f412
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Compiler/CheckTypeDeclarationsPass.php
@@ -0,0 +1,192 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Container;
+use Symfony\Component\DependencyInjection\Definition;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
+use Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException;
+use Symfony\Component\DependencyInjection\Parameter;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ServiceLocator;
+
+/**
+ * Checks whether injected parameters are compatible with type declarations.
+ *
+ * This pass should be run after all optimization passes.
+ *
+ * It can be added either:
+ * * before removing passes to check all services even if they are not currently used,
+ * * after removing passes to check only services are used in the app.
+ *
+ * @author Nicolas Grekas
+ * @author Julien Maulny
+ */
+final class CheckTypeDeclarationsPass extends AbstractRecursivePass
+{
+ private const SCALAR_TYPES = ['int', 'float', 'bool', 'string'];
+
+ private $autoload;
+
+ /**
+ * @param bool $autoload Whether services who's class in not loaded should be checked or not.
+ * Defaults to false to save loading code during compilation.
+ */
+ public function __construct(bool $autoload = false)
+ {
+ $this->autoload = $autoload;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function processValue($value, $isRoot = false)
+ {
+ if (!$value instanceof Definition) {
+ return parent::processValue($value, $isRoot);
+ }
+
+ if (!$this->autoload && !class_exists($class = $value->getClass(), false) && !interface_exists($class, false)) {
+ return parent::processValue($value, $isRoot);
+ }
+
+ if (ServiceLocator::class === $value->getClass()) {
+ return parent::processValue($value, $isRoot);
+ }
+
+ if ($constructor = $this->getConstructor($value, false)) {
+ $this->checkTypeDeclarations($value, $constructor, $value->getArguments());
+ }
+
+ foreach ($value->getMethodCalls() as $methodCall) {
+ $reflectionMethod = $this->getReflectionMethod($value, $methodCall[0]);
+
+ $this->checkTypeDeclarations($value, $reflectionMethod, $methodCall[1]);
+ }
+
+ return parent::processValue($value, $isRoot);
+ }
+
+ /**
+ * @throws InvalidArgumentException When not enough parameters are defined for the method
+ */
+ private function checkTypeDeclarations(Definition $checkedDefinition, \ReflectionFunctionAbstract $reflectionFunction, array $values): void
+ {
+ $numberOfRequiredParameters = $reflectionFunction->getNumberOfRequiredParameters();
+
+ if (\count($values) < $numberOfRequiredParameters) {
+ throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": "%s::%s()" requires %d arguments, %d passed.', $this->currentId, $reflectionFunction->class, $reflectionFunction->name, $numberOfRequiredParameters, \count($values)));
+ }
+
+ $reflectionParameters = $reflectionFunction->getParameters();
+ $checksCount = min($reflectionFunction->getNumberOfParameters(), \count($values));
+
+ for ($i = 0; $i < $checksCount; ++$i) {
+ if (!$reflectionParameters[$i]->hasType() || $reflectionParameters[$i]->isVariadic()) {
+ continue;
+ }
+
+ $this->checkType($checkedDefinition, $values[$i], $reflectionParameters[$i]);
+ }
+
+ if ($reflectionFunction->isVariadic() && ($lastParameter = end($reflectionParameters))->hasType()) {
+ $variadicParameters = \array_slice($values, $lastParameter->getPosition());
+
+ foreach ($variadicParameters as $variadicParameter) {
+ $this->checkType($checkedDefinition, $variadicParameter, $lastParameter);
+ }
+ }
+ }
+
+ /**
+ * @throws InvalidParameterTypeException When a parameter is not compatible with the declared type
+ */
+ private function checkType(Definition $checkedDefinition, $value, \ReflectionParameter $parameter): void
+ {
+ $type = $parameter->getType()->getName();
+
+ if ($value instanceof Reference) {
+ if (!$this->container->has($value = (string) $value)) {
+ return;
+ }
+
+ if ('service_container' === $value && is_a($type, Container::class, true)) {
+ return;
+ }
+
+ $value = $this->container->findDefinition($value);
+ }
+
+ if ('self' === $type) {
+ $type = $parameter->getDeclaringClass()->getName();
+ }
+
+ if ('static' === $type) {
+ $type = $checkedDefinition->getClass();
+ }
+
+ if ($value instanceof Definition) {
+ $class = $value->getClass();
+
+ if (!$class || (!$this->autoload && !class_exists($class, false) && !interface_exists($class, false))) {
+ return;
+ }
+
+ if ('callable' === $type && method_exists($class, '__invoke')) {
+ return;
+ }
+
+ if ('iterable' === $type && is_subclass_of($class, 'Traversable')) {
+ return;
+ }
+
+ if (is_a($class, $type, true)) {
+ return;
+ }
+
+ throw new InvalidParameterTypeException($this->currentId, $class, $parameter);
+ }
+
+ if ($value instanceof Parameter) {
+ $value = $this->container->getParameter($value);
+ } elseif (\is_string($value) && '%' === ($value[0] ?? '') && preg_match('/^%([^%]+)%$/', $value, $match)) {
+ $value = $this->container->getParameter($match[1]);
+ }
+
+ if (null === $value && $parameter->allowsNull()) {
+ return;
+ }
+
+ if (\in_array($type, self::SCALAR_TYPES, true) && is_scalar($value)) {
+ return;
+ }
+
+ if ('callable' === $type && \is_array($value) && isset($value[0]) && ($value[0] instanceof Reference || $value[0] instanceof Definition)) {
+ return;
+ }
+
+ if ('iterable' === $type && (\is_array($value) || $value instanceof \Traversable || $value instanceof IteratorArgument)) {
+ return;
+ }
+
+ if ('Traversable' === $type && ($value instanceof \Traversable || $value instanceof IteratorArgument)) {
+ return;
+ }
+
+ $checkFunction = sprintf('is_%s', $parameter->getType()->getName());
+
+ if (!$parameter->getType()->isBuiltin() || !$checkFunction($value)) {
+ throw new InvalidParameterTypeException($this->currentId, \gettype($value), $parameter);
+ }
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php b/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php
new file mode 100644
index 000000000000..206561fa95a8
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Exception/InvalidParameterTypeException.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Exception;
+
+/**
+ * Thrown when trying to inject a parameter into a constructor/method with an incompatible type.
+ *
+ * @author Nicolas Grekas
+ * @author Julien Maulny
+ */
+class InvalidParameterTypeException extends InvalidArgumentException
+{
+ public function __construct(string $serviceId, string $type, \ReflectionParameter $parameter)
+ {
+ parent::__construct(sprintf('Invalid definition for service "%s": argument %d of "%s::%s" accepts "%s", "%s" passed.', $serviceId, 1 + $parameter->getPosition(), $parameter->getDeclaringClass()->getName(), $parameter->getDeclaringFunction()->getName(), $parameter->getType()->getName(), $type));
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php
new file mode 100644
index 000000000000..51bc7c6779d2
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/CheckTypeDeclarationsPassTest.php
@@ -0,0 +1,555 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\DependencyInjection\Tests\Compiler;
+
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Compiler\CheckTypeDeclarationsPass;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Bar;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarOptionalArgument;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarOptionalArgumentNotNull;
+use Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\Foo;
+
+/**
+ * @author Nicolas Grekas
+ * @author Julien Maulny
+ */
+class CheckTypeDeclarationsPassTest extends TestCase
+{
+ public function testProcessThrowsExceptionOnInvalidTypesConstructorArguments()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Bar::__construct" accepts "stdClass", "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('foo', Foo::class);
+ $container->register('bar', Bar::class)
+ ->addArgument(new Reference('foo'));
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessThrowsExceptionOnInvalidTypesMethodCallArguments()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setFoo" accepts "stdClass", "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('foo', Foo::class);
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setFoo', [new Reference('foo')]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessFailsWhenPassingNullToRequiredArgument()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Bar::__construct" accepts "stdClass", "NULL" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('bar', Bar::class)
+ ->addArgument(null);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessThrowsExceptionWhenMissingArgumentsInConstructor()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Bar::__construct()" requires 1 arguments, 0 passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('bar', Bar::class);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessSuccessWhenPassingTooManyArgumentInConstructor()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('foo', \stdClass::class);
+ $container->register('bar', Bar::class)
+ ->addArgument(new Reference('foo'))
+ ->addArgument(new Reference('foo'));
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->addToAssertionCount(1);
+ }
+
+ public function testProcessRegisterWithClassName()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register(Foo::class, Foo::class);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(Foo::class, $container->get(Foo::class));
+ }
+
+ public function testProcessThrowsExceptionWhenMissingArgumentsInMethodCall()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setFoo()" requires 1 arguments, 0 passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('foo', \stdClass::class);
+ $container->register('bar', BarMethodCall::class)
+ ->addArgument(new Reference('foo'))
+ ->addMethodCall('setFoo', []);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessVariadicFails()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 2 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setFoosVariadic" accepts "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo", "stdClass" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('stdClass', \stdClass::class);
+ $container->register('foo', Foo::class);
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setFoosVariadic', [
+ new Reference('foo'),
+ new Reference('foo'),
+ new Reference('stdClass'),
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessVariadicFailsOnPassingBadTypeOnAnotherArgument()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setFoosVariadic" accepts "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo", "stdClass" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('stdClass', \stdClass::class);
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setFoosVariadic', [
+ new Reference('stdClass'),
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessVariadicSuccess()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('foo', Foo::class);
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setFoosVariadic', [
+ new Reference('foo'),
+ new Reference('foo'),
+ new Reference('foo'),
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(Foo::class, $container->get('bar')->foo);
+ }
+
+ public function testProcessSuccessWhenNotUsingOptionalArgument()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('foo', Foo::class);
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setFoosOptional', [
+ new Reference('foo'),
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(Foo::class, $container->get('bar')->foo);
+ }
+
+ public function testProcessSuccessWhenUsingOptionalArgumentWithGoodType()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('foo', Foo::class);
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setFoosOptional', [
+ new Reference('foo'),
+ new Reference('foo'),
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(Foo::class, $container->get('bar')->foo);
+ }
+
+ public function testProcessFailsWhenUsingOptionalArgumentWithBadType()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 2 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setFoosOptional" accepts "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo", "stdClass" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('stdClass', \stdClass::class);
+ $container->register('foo', Foo::class);
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setFoosOptional', [
+ new Reference('foo'),
+ new Reference('stdClass'),
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessSuccessWhenPassingNullToOptional()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarOptionalArgument::class)
+ ->addArgument(null);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertNull($container->get('bar')->foo);
+ }
+
+ public function testProcessSuccessWhenPassingNullToOptionalThatDoesNotAcceptNull()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarOptionalArgumentNotNull::__construct" accepts "int", "NULL" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarOptionalArgumentNotNull::class)
+ ->addArgument(null);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessFailsWhenPassingBadTypeToOptional()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarOptionalArgument::__construct" accepts "stdClass", "string" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarOptionalArgument::class)
+ ->addArgument('string instead of stdClass');
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertNull($container->get('bar')->foo);
+ }
+
+ public function testProcessSuccessScalarType()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setScalars', [
+ 1,
+ 'string',
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(BarMethodCall::class, $container->get('bar'));
+ }
+
+ public function testProcessFailsOnPassingScalarTypeToConstructorTypedWithClass()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Bar::__construct" accepts "stdClass", "integer" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('bar', Bar::class)
+ ->addArgument(1);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessFailsOnPassingScalarTypeToMethodTypedWithClass()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setFoo" accepts "stdClass", "string" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setFoo', [
+ 'builtin type instead of class',
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessFailsOnPassingClassToScalarTypedParameter()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setScalars" accepts "int", "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('foo', Foo::class);
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setScalars', [
+ new Reference('foo'),
+ new Reference('foo'),
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessSuccessOnPassingBadScalarType()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setScalars', [
+ 1,
+ true,
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(BarMethodCall::class, $container->get('bar'));
+ }
+
+ public function testProcessSuccessPassingBadScalarTypeOptionalArgument()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setScalars', [
+ 1,
+ 'string',
+ 'string instead of optional boolean',
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(BarMethodCall::class, $container->get('bar'));
+ }
+
+ public function testProcessSuccessWhenPassingArray()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setArray', [[]]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(BarMethodCall::class, $container->get('bar'));
+ }
+
+ public function testProcessSuccessWhenPassingIntegerToArrayTypedParameter()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidParameterTypeException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\Component\DependencyInjection\Tests\Fixtures\CheckTypeDeclarationsPass\BarMethodCall::setArray" accepts "array", "integer" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setArray', [1]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessSuccessWhenPassingAnIteratorArgumentToIterable()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarMethodCall::class)
+ ->addMethodCall('setIterable', [new IteratorArgument([])]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->addToAssertionCount(1);
+ }
+
+ public function testProcessFactory()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('foo', Foo::class);
+ $container->register('bar', Bar::class)
+ ->setFactory([
+ new Reference('foo'),
+ 'createBar',
+ ]);
+
+ /* Asserts that the class of Bar is well detected */
+ $container->register('bar_call', BarMethodCall::class)
+ ->addMethodCall('setBar', [new Reference('bar')]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(Bar::class, $container->get('bar'));
+ }
+
+ public function testProcessFactoryFailsOnInvalidParameterType()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo::createBarArguments" accepts "stdClass", "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('foo', Foo::class);
+ $container->register('bar', Bar::class)
+ ->addArgument(new Reference('foo'))
+ ->setFactory([
+ new Reference('foo'),
+ 'createBarArguments',
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessFactoryFailsOnInvalidParameterTypeOptional()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar": argument 2 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo::createBarArguments" accepts "stdClass", "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\Foo" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('stdClass', \stdClass::class);
+ $container->register('foo', Foo::class);
+ $container->register('bar', Bar::class)
+ ->addArgument(new Reference('stdClass'))
+ ->addArgument(new Reference('foo'))
+ ->setFactory([
+ new Reference('foo'),
+ 'createBarArguments',
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+ }
+
+ public function testProcessFactorySuccessOnValidTypes()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('stdClass', \stdClass::class);
+ $container->register('foo', Foo::class);
+ $container->register('bar', Bar::class)
+ ->addArgument(new Reference('stdClass'))
+ ->addArgument(new Reference('stdClass'))
+ ->setFactory([
+ new Reference('foo'),
+ 'createBarArguments',
+ ]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->addToAssertionCount(1);
+ }
+
+ public function testProcessFactoryCallbackSuccessOnValidType()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('bar', \DateTime::class)
+ ->setFactory('date_create');
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(\DateTime::class, $container->get('bar'));
+ }
+
+ public function testProcessDoesNotLoadCodeByDefault()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('foo', FooNotExisting::class);
+ $container->register('bar', BarNotExisting::class)
+ ->addArgument(new Reference('foo'))
+ ->addMethodCall('setFoo', [
+ new Reference('foo'),
+ 'string',
+ 1,
+ ]);
+
+ (new CheckTypeDeclarationsPass())->process($container);
+
+ $this->addToAssertionCount(1);
+ }
+
+ public function testProcessFactoryDoesNotLoadCodeByDefault()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('foo', FooNotExisting::class);
+ $container->register('bar', BarNotExisting::class)
+ ->setFactory([
+ new Reference('foo'),
+ 'notExistingMethod',
+ ]);
+
+ (new CheckTypeDeclarationsPass())->process($container);
+
+ $this->addToAssertionCount(1);
+ }
+
+ public function testProcessPassingBuiltinTypeDoesNotLoadCodeByDefault()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('bar', BarNotExisting::class)
+ ->addArgument(1);
+
+ (new CheckTypeDeclarationsPass())->process($container);
+
+ $this->addToAssertionCount(1);
+ }
+
+ public function testProcessDoesNotThrowsExceptionOnValidTypes()
+ {
+ $container = new ContainerBuilder();
+
+ $container->register('foo', \stdClass::class);
+ $container->register('bar', Bar::class)
+ ->addArgument(new Reference('foo'));
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(\stdClass::class, $container->get('bar')->foo);
+ }
+
+ public function testProcessThrowsOnIterableTypeWhenScalarPassed()
+ {
+ $this->expectException(\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Invalid definition for service "bar_call": argument 1 of "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CheckTypeDeclarationsPass\\BarMethodCall::setIterable" accepts "iterable", "integer" passed.');
+
+ $container = new ContainerBuilder();
+
+ $container->register('bar_call', BarMethodCall::class)
+ ->addMethodCall('setIterable', [2]);
+
+ (new CheckTypeDeclarationsPass(true))->process($container);
+
+ $this->assertInstanceOf(\stdClass::class, $container->get('bar')->foo);
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Bar.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Bar.php
new file mode 100644
index 000000000000..403841ce88df
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Bar.php
@@ -0,0 +1,13 @@
+foo = $foo;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarMethodCall.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarMethodCall.php
new file mode 100644
index 000000000000..c308ef954571
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarMethodCall.php
@@ -0,0 +1,39 @@
+foo = $foo;
+ }
+
+ public function setFoosVariadic(Foo $foo, Foo ...$foos)
+ {
+ $this->foo = $foo;
+ }
+
+ public function setFoosOptional(Foo $foo, Foo $fooOptional = null)
+ {
+ $this->foo = $foo;
+ }
+
+ public function setScalars(int $int, string $string, bool $bool = false)
+ {
+ }
+
+ public function setArray(array $array)
+ {
+ }
+
+ public function setIterable(iterable $iterable)
+ {
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarOptionalArgument.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarOptionalArgument.php
new file mode 100644
index 000000000000..4f348895132c
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarOptionalArgument.php
@@ -0,0 +1,13 @@
+foo = $foo;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarOptionalArgumentNotNull.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarOptionalArgumentNotNull.php
new file mode 100644
index 000000000000..07f27817c031
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/BarOptionalArgumentNotNull.php
@@ -0,0 +1,13 @@
+foo = $foo;
+ }
+}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php
new file mode 100644
index 000000000000..dde7afce91fd
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/CheckTypeDeclarationsPass/Foo.php
@@ -0,0 +1,16 @@
+
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy