diff --git a/.phpstan/phpstan-baseline.neon b/.phpstan/phpstan-baseline.neon index 7a1ba64476..181b526437 100644 --- a/.phpstan/phpstan-baseline.neon +++ b/.phpstan/phpstan-baseline.neon @@ -33,7 +33,7 @@ parameters: - # will be fixed in v4. Code is marked as deprecated message: "#^Result of \\&\\& is always false\\.$#" - count: 1 + count: 2 path: ../src/Admin/AbstractAdmin.php - diff --git a/src/Admin/AbstractAdmin.php b/src/Admin/AbstractAdmin.php index e724129b6b..d74b15a102 100644 --- a/src/Admin/AbstractAdmin.php +++ b/src/Admin/AbstractAdmin.php @@ -52,9 +52,10 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface as RoutingUrlGeneratorInterface; use Symfony\Component\Security\Acl\Model\DomainObjectInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Component\Validator\Mapping\GenericMetadata; use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @author Thomas Rabaix @@ -309,7 +310,7 @@ abstract class AbstractAdmin implements AdminInterface, DomainObjectInterface, A * * NEXT_MAJOR: remove this property * - * @var \Symfony\Component\Translation\TranslatorInterface + * @var TranslatorInterface|LegacyTranslatorInterface * * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0 */ @@ -2443,7 +2444,11 @@ public function transChoice($id, $count, array $parameters = [], $domain = null, $domain = $domain ?: $this->getTranslationDomain(); - return $this->translator->transChoice($id, $count, $parameters, $domain, $locale); + if ($this->translator instanceof LegacyTranslatorInterface) { + return $this->translator->transChoice($id, $count, $parameters, $domain, $locale); + } + + return $this->translator->trans($id, ['%count%' => $count] + $parameters, $domain, $locale); } public function setTranslationDomain($translationDomain) @@ -2461,9 +2466,11 @@ public function getTranslationDomain() * * NEXT_MAJOR: remove this method * + * @param LegacyTranslatorInterface|TranslatorInterface $translator + * * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0 */ - public function setTranslator(TranslatorInterface $translator) + public function setTranslator(object $translator) { $args = \func_get_args(); if (isset($args[1]) && $args[1]) { @@ -2473,6 +2480,16 @@ public function setTranslator(TranslatorInterface $translator) ), E_USER_DEPRECATED); } + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf( + 'Argument 1 passed to "%s()" must be an instance of "%s" or "%s", instance of "%s" given.', + __METHOD__, + LegacyTranslatorInterface::class, + TranslatorInterface::class, + \get_class($translator) + )); + } + $this->translator = $translator; } @@ -2482,6 +2499,8 @@ public function setTranslator(TranslatorInterface $translator) * NEXT_MAJOR: remove this method * * @deprecated since sonata-project/admin-bundle 3.9, to be removed with 4.0 + * + * @return LegacyTranslatorInterface|TranslatorInterface */ public function getTranslator() { diff --git a/src/Admin/AdminInterface.php b/src/Admin/AdminInterface.php index a5205820ca..b015a61c5a 100644 --- a/src/Admin/AdminInterface.php +++ b/src/Admin/AdminInterface.php @@ -33,8 +33,9 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @author Thomas Rabaix @@ -64,6 +65,7 @@ * @method void reorderFormGroup(string $group, array $keys) * @method void defineFormBuilder(FormBuilderInterface $formBuilder) * @method string getPagerType() + * @method void setTranslator(object $translator); */ interface AdminInterface extends AccessRegistryInterface, FieldDescriptionRegistryInterface, LifecycleHookProviderInterface, MenuBuilderInterface, ParentAdminInterface, UrlGeneratorInterface { @@ -90,10 +92,8 @@ public function setDatagridBuilder(DatagridBuilderInterface $datagridBuilder); */ public function getDatagridBuilder(); - public function setTranslator(TranslatorInterface $translator); - /** - * @return TranslatorInterface + * @return TranslatorInterface|LegacyTranslatorInterface */ public function getTranslator(); diff --git a/src/Form/Type/Filter/ChoiceType.php b/src/Form/Type/Filter/ChoiceType.php index 91638fb931..e1754f7725 100644 --- a/src/Form/Type/Filter/ChoiceType.php +++ b/src/Form/Type/Filter/ChoiceType.php @@ -18,7 +18,8 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType as FormChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @final since sonata-project/admin-bundle 3.52 @@ -47,12 +48,22 @@ class ChoiceType extends AbstractType * * @deprecated since sonata-project/admin-bundle 3.5, to be removed with 4.0 * - * @var TranslatorInterface + * @var TranslatorInterface|LegacyTranslatorInterface */ protected $translator; - public function __construct(TranslatorInterface $translator) + public function __construct(object $translator) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf( + 'Argument 1 passed to "%s()" must be an instance of "%s" or "%s", instance of "%s" given.', + __METHOD__, + LegacyTranslatorInterface::class, + TranslatorInterface::class, + \get_class($translator) + )); + } + $this->translator = $translator; } diff --git a/src/Form/Type/Filter/DateRangeType.php b/src/Form/Type/Filter/DateRangeType.php index 01825fc1c0..8b8720f977 100644 --- a/src/Form/Type/Filter/DateRangeType.php +++ b/src/Form/Type/Filter/DateRangeType.php @@ -18,7 +18,8 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @final since sonata-project/admin-bundle 3.52 @@ -42,12 +43,22 @@ class DateRangeType extends AbstractType * * @deprecated since sonata-project/admin-bundle 3.5, to be removed with 4.0 * - * @var TranslatorInterface + * @var TranslatorInterface|LegacyTranslatorInterface */ protected $translator; - public function __construct(TranslatorInterface $translator) + public function __construct(object $translator) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf( + 'Argument 1 passed to "%s()" must be an instance of "%s" or "%s", instance of "%s" given.', + __METHOD__, + LegacyTranslatorInterface::class, + TranslatorInterface::class, + \get_class($translator) + )); + } + $this->translator = $translator; } diff --git a/src/Form/Type/Filter/DateTimeRangeType.php b/src/Form/Type/Filter/DateTimeRangeType.php index 66f5309c56..69a3c78039 100644 --- a/src/Form/Type/Filter/DateTimeRangeType.php +++ b/src/Form/Type/Filter/DateTimeRangeType.php @@ -18,7 +18,8 @@ use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @final since sonata-project/admin-bundle 3.52 @@ -42,12 +43,22 @@ class DateTimeRangeType extends AbstractType * * @deprecated since sonata-project/admin-bundle 3.5, to be removed with 4.0 * - * @var TranslatorInterface + * @var TranslatorInterface|LegacyTranslatorInterface */ protected $translator; - public function __construct(TranslatorInterface $translator) + public function __construct(object $translator) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf( + 'Argument 1 passed to "%s()" must be an instance of "%s" or "%s", instance of "%s" given.', + __METHOD__, + LegacyTranslatorInterface::class, + TranslatorInterface::class, + \get_class($translator) + )); + } + $this->translator = $translator; } diff --git a/src/Form/Type/Filter/DateTimeType.php b/src/Form/Type/Filter/DateTimeType.php index 4bb209c850..ba38741078 100644 --- a/src/Form/Type/Filter/DateTimeType.php +++ b/src/Form/Type/Filter/DateTimeType.php @@ -18,7 +18,8 @@ use Symfony\Component\Form\Extension\Core\Type\DateTimeType as FormDateTimeType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @final since sonata-project/admin-bundle 3.52 @@ -67,12 +68,22 @@ class DateTimeType extends AbstractType * * @deprecated since sonata-project/admin-bundle 3.5, to be removed with 4.0 * - * @var TranslatorInterface + * @var TranslatorInterface|LegacyTranslatorInterface */ protected $translator; - public function __construct(TranslatorInterface $translator) + public function __construct(object $translator) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf( + 'Argument 1 passed to "%s()" must be an instance of "%s" or "%s", instance of "%s" given.', + __METHOD__, + LegacyTranslatorInterface::class, + TranslatorInterface::class, + \get_class($translator) + )); + } + $this->translator = $translator; } diff --git a/src/Form/Type/Filter/DateType.php b/src/Form/Type/Filter/DateType.php index 24cefd185e..42adf2db57 100644 --- a/src/Form/Type/Filter/DateType.php +++ b/src/Form/Type/Filter/DateType.php @@ -18,7 +18,8 @@ use Symfony\Component\Form\Extension\Core\Type\DateType as FormDateType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @final since sonata-project/admin-bundle 3.52 @@ -67,12 +68,22 @@ class DateType extends AbstractType * * @deprecated since sonata-project/admin-bundle 3.5, to be removed with 4.0 * - * @var TranslatorInterface + * @var TranslatorInterface|LegacyTranslatorInterface */ protected $translator; - public function __construct(TranslatorInterface $translator) + public function __construct(object $translator) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf( + 'Argument 1 passed to "%s()" must be an instance of "%s" or "%s", instance of "%s" given.', + __METHOD__, + LegacyTranslatorInterface::class, + TranslatorInterface::class, + \get_class($translator) + )); + } + $this->translator = $translator; } diff --git a/src/Form/Type/Filter/NumberType.php b/src/Form/Type/Filter/NumberType.php index 4da94e8a31..81e2e1c99d 100644 --- a/src/Form/Type/Filter/NumberType.php +++ b/src/Form/Type/Filter/NumberType.php @@ -18,7 +18,8 @@ use Symfony\Component\Form\Extension\Core\Type\NumberType as FormNumberType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * @final since sonata-project/admin-bundle 3.52 @@ -57,12 +58,22 @@ class NumberType extends AbstractType * * @deprecated since sonata-project/admin-bundle 3.5, to be removed with 4.0 * - * @var TranslatorInterface + * @var TranslatorInterface|LegacyTranslatorInterface */ protected $translator; - public function __construct(TranslatorInterface $translator) + public function __construct(object $translator) { + if (!$translator instanceof LegacyTranslatorInterface && !$translator instanceof TranslatorInterface) { + throw new \TypeError(sprintf( + 'Argument 1 passed to "%s()" must be an instance of "%s" or "%s", instance of "%s" given.', + __METHOD__, + LegacyTranslatorInterface::class, + TranslatorInterface::class, + \get_class($translator) + )); + } + $this->translator = $translator; } diff --git a/src/Twig/Extension/SonataAdminExtension.php b/src/Twig/Extension/SonataAdminExtension.php index 2d4553ea50..811a28ab05 100644 --- a/src/Twig/Extension/SonataAdminExtension.php +++ b/src/Twig/Extension/SonataAdminExtension.php @@ -26,7 +26,7 @@ use Symfony\Component\Security\Acl\Voter\FieldVote; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException; -use Symfony\Component\Translation\TranslatorInterface as LegacyTranslationInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Twig\Environment; use Twig\Error\LoaderError; @@ -104,11 +104,11 @@ public function __construct( ), E_USER_DEPRECATED); } - if (!$translator instanceof TranslatorInterface && !$translator instanceof LegacyTranslationInterface) { + if (!$translator instanceof TranslatorInterface && !$translator instanceof LegacyTranslatorInterface) { throw new \TypeError(sprintf( 'Argument 2 must be an instance of "%s" or preferably "%s", "%s given"', TranslatorInterface::class, - LegacyTranslationInterface::class, + LegacyTranslatorInterface::class, \get_class($translator) )); } diff --git a/tests/Admin/AdminTest.php b/tests/Admin/AdminTest.php index b4a19b6721..e5c7a8b6d6 100644 --- a/tests/Admin/AdminTest.php +++ b/tests/Admin/AdminTest.php @@ -83,10 +83,11 @@ use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; use Symfony\Component\Validator\Mapping\MemberMetadata; use Symfony\Component\Validator\Mapping\PropertyMetadataInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class AdminTest extends TestCase { @@ -1414,13 +1415,21 @@ public function testGetObjectIdentifier(): void /** * @group legacy + * @testWith ["Symfony\\Component\\Translation\\TranslatorInterface"] + * ["Symfony\\Contracts\\Translation\\TranslatorInterface"] */ - public function testTrans(): void + public function testTrans(string $translatorClass): void { + if (!interface_exists($translatorClass)) { + $this->markTestSkipped('This test is only available using Symfony 4'); + + return; + } + $admin = new PostAdmin('sonata.post.admin.post', 'NewsBundle\Entity\Post', 'Sonata\NewsBundle\Controller\PostAdminController'); $admin->setTranslationDomain('fooMessageDomain'); - $translator = $this->createMock(TranslatorInterface::class); + $translator = $this->createMock($translatorClass); $admin->setTranslator($translator); $translator->expects($this->once()) @@ -1460,6 +1469,39 @@ public function testTransChoice(): void $translator = $this->createMock(TranslatorInterface::class); $admin->setTranslator($translator); + $translator->expects($this->once()) + ->method('trans') + ->with( + 'foo', + [ + '%count%' => 2, + '%foo%' => 'bar', + ], + 'fooMessageDomain', + 'en' + ) + ->willReturn('fooTranslated'); + + $this->assertSame('fooTranslated', $admin->transChoice('foo', 2, ['%foo%' => 'bar'], null, 'en')); + } + + /** + * @group legacy + */ + public function testTransChoiceWithLegacyTranslator(): void + { + if (!interface_exists(LegacyTranslatorInterface::class)) { + $this->markTestSkipped('This test is only available using Symfony 4'); + + return; + } + + $admin = new PostAdmin('sonata.post.admin.post', 'NewsBundle\Entity\Post', 'Sonata\NewsBundle\Controller\PostAdminController'); + $admin->setTranslationDomain('fooMessageDomain'); + + $translator = $this->createMock(LegacyTranslatorInterface::class); + $admin->setTranslator($translator); + $translator->expects($this->once()) ->method('transChoice') ->with($this->equalTo('foo'), $this->equalTo(2), $this->equalTo([]), $this->equalTo('fooMessageDomain')) @@ -1473,9 +1515,15 @@ public function testTransChoice(): void */ public function testTransChoiceWithMessageDomain(): void { + if (!interface_exists(LegacyTranslatorInterface::class)) { + $this->markTestSkipped('This test is only available using Symfony 4'); + + return; + } + $admin = new PostAdmin('sonata.post.admin.post', 'NewsBundle\Entity\Post', 'Sonata\NewsBundle\Controller\PostAdminController'); - $translator = $this->createMock(TranslatorInterface::class); + $translator = $this->createMock(LegacyTranslatorInterface::class); $admin->setTranslator($translator); $translator->expects($this->once()) diff --git a/tests/Form/Type/Filter/DateTimeRangeTypeTest.php b/tests/Form/Type/Filter/DateTimeRangeTypeTest.php index 6741e8cced..eeb1666e8e 100644 --- a/tests/Form/Type/Filter/DateTimeRangeTypeTest.php +++ b/tests/Form/Type/Filter/DateTimeRangeTypeTest.php @@ -17,7 +17,7 @@ use Sonata\Form\Type\DateTimeRangeType as FormDateTimeRangeType; use Symfony\Component\Form\Test\TypeTestCase; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class DateTimeRangeTypeTest extends TypeTestCase { diff --git a/tests/Form/Widget/FormSonataFilterChoiceWidgetTest.php b/tests/Form/Widget/FormSonataFilterChoiceWidgetTest.php index b2ab5e061a..e4b5faec8c 100644 --- a/tests/Form/Widget/FormSonataFilterChoiceWidgetTest.php +++ b/tests/Form/Widget/FormSonataFilterChoiceWidgetTest.php @@ -18,7 +18,7 @@ use Sonata\AdminBundle\Tests\Fixtures\TestExtension; use Symfony\Component\Form\Extension\Core\Type\ChoiceType as SymfonyChoiceType; use Symfony\Component\Form\FormTypeGuesserInterface; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; class FormSonataFilterChoiceWidgetTest extends BaseWidgetTest { diff --git a/tests/Translator/Extractor/JMSTranslatorBundle/AdminExtractorTest.php b/tests/Translator/Extractor/JMSTranslatorBundle/AdminExtractorTest.php index 7e56bbaa14..1b804a64ac 100644 --- a/tests/Translator/Extractor/JMSTranslatorBundle/AdminExtractorTest.php +++ b/tests/Translator/Extractor/JMSTranslatorBundle/AdminExtractorTest.php @@ -18,11 +18,13 @@ use JMS\TranslationBundle\Translation\ExtractorInterface; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; +use Sonata\AdminBundle\Admin\AbstractAdmin; use Sonata\AdminBundle\Admin\AdminInterface; use Sonata\AdminBundle\Admin\BreadcrumbsBuilderInterface; use Sonata\AdminBundle\Admin\Pool; use Sonata\AdminBundle\Translator\Extractor\JMSTranslatorBundle\AdminExtractor; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface; /** * Test for AdminExtractor. @@ -62,14 +64,18 @@ class AdminExtractorTest extends TestCase protected function setUp(): void { + if (!interface_exists(LegacyTranslatorInterface::class)) { + $this->markTestSkipped('This test is only available using Symfony 4'); + } + if (!interface_exists(ExtractorInterface::class)) { $this->markTestSkipped('JMS Translator Bundle does not exist'); } - $this->fooAdmin = $this->getMockForAbstractClass(AdminInterface::class); - $this->barAdmin = $this->getMockForAbstractClass(AdminInterface::class); + $this->fooAdmin = $this->createMock(AbstractAdmin::class); + $this->barAdmin = $this->createMock(AbstractAdmin::class); - $container = $this->getMockForAbstractClass(ContainerInterface::class); + $container = $this->createMock(ContainerInterface::class); $container ->method('get') ->willReturnCallback(function (string $id): AdminInterface { diff --git a/tests/Twig/Extension/SonataAdminExtensionTest.php b/tests/Twig/Extension/SonataAdminExtensionTest.php index 54dad24d8f..83ecad40f6 100644 --- a/tests/Twig/Extension/SonataAdminExtensionTest.php +++ b/tests/Twig/Extension/SonataAdminExtensionTest.php @@ -290,6 +290,12 @@ public function testConstructThrowsExceptionWithWrongTranslationArgument(): void */ public function testConstructWithLegacyTranslator(): void { + if (!interface_exists(LegacyTranslatorInterface::class)) { + $this->markTestSkipped('This test is only available using Symfony 4'); + + return; + } + new SonataAdminExtension( $this->pool, null,