From ff566ee08a72fbe119cc33b736a1c198e6657c4b Mon Sep 17 00:00:00 2001 From: Michael Babker Date: Fri, 29 Dec 2023 14:59:49 -0500 Subject: [PATCH] Add support for Symfony 7 where able Co-authored-by: Alexander Schranz --- .github/workflows/continuous-integration.yml | 33 +++- Controller/Annotations/Route.php | 146 +++++++------- DependencyInjection/FOSRestExtension.php | 65 +++--- EventListener/FormatListener.php | 2 +- EventListener/MimeTypeListener.php | 3 +- .../AllowedMethodsRouterLoader.php | 6 +- .../Controller/Annotations/FileParamTest.php | 4 +- .../FOSRestExtensionTest.php | 17 ++ Tests/EventListener/FormatListenerTest.php | 4 +- Tests/EventListener/MimeTypeListenerTest.php | 13 +- .../ResponseStatusCodeListenerTest.php | 38 ++-- .../ViewResponseListenerTest.php | 18 +- .../Controller/ParamsAnnotatedController.php | 11 +- Tests/Functional/AllowedMethodsTest.php | 6 + .../Controller/ArticleController.php | 4 + .../Controller/ParamFetcherController.php | 2 + .../Controller/SerializerErrorController.php | 1 + .../Controller/Version2Controller.php | 4 + .../Controller/VersionController.php | 1 + Tests/Functional/DependencyInjectionTest.php | 10 +- Tests/Functional/ParamFetcherTest.php | 117 +++++++---- .../RequestBodyParamConverterTest.php | 34 +--- Tests/Functional/RouteAttributesTest.php | 6 + Tests/Functional/SerializerErrorTest.php | 34 ++-- Tests/Functional/VersionTest.php | 6 + Tests/Functional/ViewResponseListenerTest.php | 11 ++ .../app/AllowedMethodsListener/bundles.php | 9 +- .../app/AllowedMethodsListener/config.yml | 2 +- Tests/Functional/app/Configuration/config.yml | 2 +- .../app/Configuration/framework.php | 4 + Tests/Functional/app/ParamFetcher/bundles.php | 9 +- Tests/Functional/app/ParamFetcher/config.yml | 2 +- .../app/RequestBodyParamConverter/bundles.php | 9 +- .../app/RequestBodyParamConverter/config.yml | 6 +- .../sensio_framework_extra.php | 22 +++ .../bundles.php | 9 +- .../config.yml | 2 +- .../config.yml | 2 +- .../app/RouteAttributes/bundles.php | 9 +- .../Functional/app/RouteAttributes/config.yml | 2 +- Tests/Functional/app/Version/bundles.php | 9 +- Tests/Functional/app/Version/config.yml | 2 +- .../app/ViewResponseListener/bundles.php | 9 +- .../app/ViewResponseListener/config.yml | 2 +- Tests/Functional/app/config/framework.php | 4 + .../app/config/sensio_framework_extra.php | 20 ++ .../app/config/sensio_framework_extra.yml | 3 - Tests/Request/ParamReaderTest.php | 186 ++++++++---------- .../Request/RequestBodyParamConverterTest.php | 20 +- composer.json | 56 +++--- phpunit.xml.dist | 3 +- 51 files changed, 595 insertions(+), 404 deletions(-) create mode 100644 Tests/Functional/app/RequestBodyParamConverter/sensio_framework_extra.php create mode 100644 Tests/Functional/app/config/sensio_framework_extra.php delete mode 100644 Tests/Functional/app/config/sensio_framework_extra.yml diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 864fc5115..19de80eb7 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -18,12 +18,14 @@ jobs: continue-on-error: ${{ matrix.can-fail }} strategy: + fail-fast: false matrix: include: - - php-version: 7.2 - composer-flags: "--prefer-lowest " + - php-version: 7.4 + composer-flags: "--prefer-lowest" + symfony-require: "5.4.*" can-fail: false - - php-version: 7.3 + - php-version: 7.4 composer-flags: "" can-fail: false - php-version: 7.4 @@ -34,23 +36,32 @@ jobs: - php-version: 8.0 composer-flags: "" can-fail: false - symfony-require: "6.0.*" + symfony-require: "5.4.*" - php-version: 8.1 composer-flags: "" can-fail: false - symfony-require: "6.1.*" + symfony-require: "6.4.*" + - php-version: 8.2 + composer-flags: "" + can-fail: false + symfony-require: "6.4.*" - php-version: 8.2 composer-flags: "" can-fail: false - symfony-require: "6.2.*" + symfony-require: "6.4.*" + remove-sensio-bundle: yes # Smoke test with SensioFrameworkExtraBundle removed on latest Symfony LTS - php-version: 8.2 composer-flags: "" can-fail: false - symfony-require: "6.3.*" + symfony-require: "7.0.*" + remove-sensio-bundle: yes # SensioFrameworkExtraBundle is not compatible with Symfony 7.0 or later - php-version: 8.2 composer-flags: "" can-fail: true # we don't want to fail the build if we are incompatible with the next (unstable) Symfony version + env: + COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: - name: "Checkout" uses: "actions/checkout@v4" @@ -78,11 +89,17 @@ jobs: key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}" restore-keys: "php-${{ matrix.php-version }}-composer-locked-" + - name: "Remove SensioFrameworkExtraBundle if required" + if: "${{ matrix.remove-sensio-bundle == 'yes' }}" + env: + SYMFONY_REQUIRE: "${{ matrix.symfony-require }}" + run: | + composer remove --no-update --dev sensio/framework-extra-bundle + - name: "Install dependencies with composer" env: SYMFONY_REQUIRE: "${{ matrix.symfony-require }}" run: | - composer remove friendsofphp/php-cs-fixer --dev --no-update composer update --no-interaction --no-progress ${{ matrix.composer-flags }} - name: "Run PHPUnit" diff --git a/Controller/Annotations/Route.php b/Controller/Annotations/Route.php index 677e0979c..3e52e72bd 100644 --- a/Controller/Annotations/Route.php +++ b/Controller/Annotations/Route.php @@ -11,7 +11,28 @@ namespace FOS\RestBundle\Controller\Annotations; -use Symfony\Component\Routing\Annotation\Route as BaseRoute; +use Symfony\Component\Routing\Annotation\Route as BaseAnnotationRoute; +use Symfony\Component\Routing\Attribute\Route as BaseAttributeRoute; + +if (class_exists(BaseAttributeRoute::class)) { + /** + * Compatibility layer for Symfony 6.4 and later. + * + * @internal + */ + class CompatRoute extends BaseAttributeRoute + { + } +} else { + /** + * Compatibility layer for Symfony 6.3 and earlier. + * + * @internal + */ + class CompatRoute extends BaseAnnotationRoute + { + } +} /** * Route annotation class. @@ -21,8 +42,17 @@ * @Target({"CLASS", "METHOD"}) */ #[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)] -class Route extends BaseRoute +class Route extends CompatRoute { + /** + * @param array|string $data + * @param array|string|null $path + * @param string[] $requirements + * @param string[]|string $methods + * @param string[]|string $schemes + * + * @throws \TypeError if the $data argument is an unsupported type + */ public function __construct( $data = [], $path = null, @@ -41,78 +71,56 @@ public function __construct( bool $stateless = null, string $env = null ) { - // BC layer for symfony < 5.2 - // Before symfony/routing 5.2 the constructor only had one parameter - $method = new \ReflectionMethod(BaseRoute::class, '__construct'); - if (1 === $method->getNumberOfParameters()) { + // Use Reflection to get the constructor from the parent class two levels up (accounting for our compat definition) + $method = (new \ReflectionClass($this))->getParentClass()->getParentClass()->getMethod('__construct'); + + // The $data constructor parameter was removed in Symfony 6.0 in favor of named arguments + if ('data' === $method->getParameters()[0]->getName()) { + parent::__construct( + $data, + $path, + $name, + $requirements, + $options, + $defaults, + $host, + $methods, + $schemes, + $condition, + $priority, + $locale, + $format, + $utf8, + $stateless, + $env + ); + } else { if (\is_string($data)) { - $path = $data; - $data = []; + $data = ['path' => $data]; } elseif (!\is_array($data)) { throw new \TypeError(sprintf('"%s": Argument $data is expected to be a string or array, got "%s".', __METHOD__, get_debug_type($data))); + } elseif (0 !== count($data) && [] === \array_intersect(\array_keys($data), ['path', 'name', 'requirements', 'options', 'defaults', 'host', 'methods', 'schemes', 'condition', 'priority', 'locale', 'format', 'utf8', 'stateless', 'env'])) { + $localizedPaths = $data; + $data = ['path' => $localizedPaths]; } - $data['path'] = $path; - $data['name'] = $name; - $data['requirements'] = $requirements; - $data['options'] = $options; - $data['defaults'] = $defaults; - $data['host'] = $host; - $data['methods'] = $methods; - $data['schemes'] = $schemes; - $data['condition'] = $condition; - - parent::__construct($data); - } else { - // BC layer for symfony < 6.0 - // The constructor parameter $data has been removed since symfony 6.0 - if ('data' === $method->getParameters()[0]->getName()) { - parent::__construct( - $data, - $path, - $name, - $requirements, - $options, - $defaults, - $host, - $methods, - $schemes, - $condition, - $priority, - $locale, - $format, - $utf8, - $stateless, - $env - ); - } else { - if (\is_string($data)) { - $data = ['path' => $data]; - } elseif (!\is_array($data)) { - throw new \TypeError(sprintf('"%s": Argument $data is expected to be a string or array, got "%s".', __METHOD__, get_debug_type($data))); - } elseif (0 !== count($data) && [] === \array_intersect(\array_keys($data), ['path', 'name', 'requirements', 'options', 'defaults', 'host', 'methods', 'schemes', 'condition', 'priority', 'locale', 'format', 'utf8', 'stateless', 'env'])) { - $localizedPaths = $data; - $data = ['path' => $localizedPaths]; - } - - parent::__construct( - $data['path'] ?? $path, - $data['name'] ?? $name, - $data['requirements'] ?? $requirements, - $data['options'] ?? $options, - $data['defaults'] ?? $defaults, - $data['host'] ?? $host, - $data['methods'] ?? $methods, - $data['schemes'] ?? $schemes, - $data['condition'] ?? $condition, - $data['priority'] ?? $priority, - $data['locale'] ?? $locale, - $data['format'] ?? $format, - $data['utf8'] ?? $utf8, - $data['stateless'] ?? $stateless, - $data['env'] ?? $env - ); - } + parent::__construct( + $data['path'] ?? $path, + $data['name'] ?? $name, + $data['requirements'] ?? $requirements, + $data['options'] ?? $options, + $data['defaults'] ?? $defaults, + $data['host'] ?? $host, + $data['methods'] ?? $methods, + $data['schemes'] ?? $schemes, + $data['condition'] ?? $condition, + $data['priority'] ?? $priority, + $data['locale'] ?? $locale, + $data['format'] ?? $format, + $data['utf8'] ?? $utf8, + $data['stateless'] ?? $stateless, + $data['env'] ?? $env + ); } if (!$this->getMethods()) { diff --git a/DependencyInjection/FOSRestExtension.php b/DependencyInjection/FOSRestExtension.php index 8d54fbcad..fa6ad25af 100644 --- a/DependencyInjection/FOSRestExtension.php +++ b/DependencyInjection/FOSRestExtension.php @@ -14,6 +14,7 @@ use FOS\RestBundle\ErrorRenderer\SerializerErrorRenderer; use FOS\RestBundle\EventListener\ResponseStatusCodeListener; use FOS\RestBundle\View\ViewHandler; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ChildDefinition; @@ -21,6 +22,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Form\Extension\Core\Type\FormType; @@ -31,13 +33,13 @@ use Symfony\Component\HttpFoundation\RequestMatcher\MethodRequestMatcher; use Symfony\Component\HttpFoundation\RequestMatcher\PathRequestMatcher; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension; use Symfony\Component\Validator\Constraint; /** * @internal */ -class FOSRestExtension extends Extension +class FOSRestExtension extends ConfigurableExtension { /** * {@inheritdoc} @@ -47,18 +49,15 @@ public function getConfiguration(array $config, ContainerBuilder $container): Co return new Configuration($container->getParameter('kernel.debug')); } - public function load(array $configs, ContainerBuilder $container): void + protected function loadInternal(array $mergedConfig, ContainerBuilder $container): void { - $configuration = new Configuration($container->getParameter('kernel.debug')); - $config = $this->processConfiguration($configuration, $configs); - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('view.xml'); $loader->load('request.xml'); $loader->load('serializer.xml'); - foreach ($config['service'] as $key => $service) { - if ('validator' === $service && empty($config['body_converter']['validate'])) { + foreach ($mergedConfig['service'] as $key => $service) { + if ('validator' === $service && empty($mergedConfig['body_converter']['validate'])) { continue; } @@ -71,20 +70,20 @@ public function load(array $configs, ContainerBuilder $container): void } } - $this->loadForm($config, $loader, $container); - $this->loadException($config, $loader, $container); - $this->loadBodyConverter($config, $loader, $container); - $this->loadView($config, $loader, $container); + $this->loadForm($mergedConfig, $loader, $container); + $this->loadException($mergedConfig, $loader, $container); + $this->loadBodyConverter($mergedConfig, $loader, $container); + $this->loadView($mergedConfig, $loader, $container); - $this->loadBodyListener($config, $loader, $container); - $this->loadFormatListener($config, $loader, $container); - $this->loadVersioning($config, $loader, $container); - $this->loadParamFetcherListener($config, $loader, $container); - $this->loadAllowedMethodsListener($config, $loader, $container); - $this->loadZoneMatcherListener($config, $loader, $container); + $this->loadBodyListener($mergedConfig, $loader, $container); + $this->loadFormatListener($mergedConfig, $loader, $container); + $this->loadVersioning($mergedConfig, $loader, $container); + $this->loadParamFetcherListener($mergedConfig, $loader, $container); + $this->loadAllowedMethodsListener($mergedConfig, $loader, $container); + $this->loadZoneMatcherListener($mergedConfig, $loader, $container); // Needs RequestBodyParamConverter and View Handler loaded. - $this->loadSerializer($config, $container); + $this->loadSerializer($mergedConfig, $container); } private function loadForm(array $config, XmlFileLoader $loader, ContainerBuilder $container): void @@ -114,7 +113,7 @@ private function loadAllowedMethodsListener(array $config, XmlFileLoader $loader private function loadBodyListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void { - if ($config['body_listener']['enabled']) { + if ($this->isConfigEnabled($container, $config['body_listener'])) { $loader->load('body_listener.xml'); $service = $container->getDefinition('fos_rest.body_listener'); @@ -149,7 +148,7 @@ private function loadBodyListener(array $config, XmlFileLoader $loader, Containe private function loadFormatListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void { - if ($config['format_listener']['enabled'] && !empty($config['format_listener']['rules'])) { + if ($this->isConfigEnabled($container, $config['format_listener']) && !empty($config['format_listener']['rules'])) { $loader->load('format_listener.xml'); if (!empty($config['format_listener']['service'])) { @@ -166,22 +165,22 @@ private function loadFormatListener(array $config, XmlFileLoader $loader, Contai private function loadVersioning(array $config, XmlFileLoader $loader, ContainerBuilder $container): void { - if (!empty($config['versioning']['enabled'])) { + if ($this->isConfigEnabled($container, $config['versioning'])) { $loader->load('versioning.xml'); $versionListener = $container->getDefinition('fos_rest.versioning.listener'); $versionListener->replaceArgument(1, $config['versioning']['default_version']); $resolvers = []; - if ($config['versioning']['resolvers']['query']['enabled']) { + if ($this->isConfigEnabled($container, $config['versioning']['resolvers']['query'])) { $resolvers['query'] = $container->getDefinition('fos_rest.versioning.query_parameter_resolver'); $resolvers['query']->replaceArgument(0, $config['versioning']['resolvers']['query']['parameter_name']); } - if ($config['versioning']['resolvers']['custom_header']['enabled']) { + if ($this->isConfigEnabled($container, $config['versioning']['resolvers']['custom_header'])) { $resolvers['custom_header'] = $container->getDefinition('fos_rest.versioning.header_resolver'); $resolvers['custom_header']->replaceArgument(0, $config['versioning']['resolvers']['custom_header']['header_name']); } - if ($config['versioning']['resolvers']['media_type']['enabled']) { + if ($this->isConfigEnabled($container, $config['versioning']['resolvers']['media_type'])) { $resolvers['media_type'] = $container->getDefinition('fos_rest.versioning.media_type_resolver'); $resolvers['media_type']->replaceArgument(0, $config['versioning']['resolvers']['media_type']['regex']); } @@ -197,7 +196,7 @@ private function loadVersioning(array $config, XmlFileLoader $loader, ContainerB private function loadParamFetcherListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void { - if ($config['param_fetcher_listener']['enabled']) { + if ($this->isConfigEnabled($container, $config['param_fetcher_listener'])) { if (!class_exists(Constraint::class)) { throw new \LogicException('Enabling the fos_rest.param_fetcher_listener option when the Symfony Validator component is not installed is not supported. Try installing the symfony/validator package.'); } @@ -221,6 +220,10 @@ private function loadBodyConverter(array $config, XmlFileLoader $loader, Contain return; } + if (!class_exists(SensioFrameworkExtraBundle::class)) { + throw new LogicException('To use the request body param converter, the "sensio/framework-extra-bundle" package is required.'); + } + $loader->load('request_body_param_converter.xml'); if (!empty($config['body_converter']['validation_errors_argument'])) { @@ -245,7 +248,7 @@ private function loadView(array $config, XmlFileLoader $loader, ContainerBuilder } } - if ($config['view']['mime_types']['enabled']) { + if ($this->isConfigEnabled($container, $config['view']['mime_types'])) { $loader->load('mime_type_listener.xml'); if (!empty($config['mime_type_listener']['service'])) { @@ -256,7 +259,11 @@ private function loadView(array $config, XmlFileLoader $loader, ContainerBuilder $container->getDefinition('fos_rest.mime_type_listener')->replaceArgument(0, $config['view']['mime_types']['formats']); } - if ($config['view']['view_response_listener']['enabled']) { + if ($this->isConfigEnabled($container, $config['view']['view_response_listener'])) { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + throw new LogicException('To use the view response listener, the "sensio/framework-extra-bundle" package is required.'); + } + $loader->load('view_response_listener.xml'); $service = $container->getDefinition('fos_rest.view_response_listener'); @@ -297,7 +304,7 @@ private function loadView(array $config, XmlFileLoader $loader, ContainerBuilder private function loadException(array $config, XmlFileLoader $loader, ContainerBuilder $container): void { - if ($config['exception']['enabled']) { + if ($this->isConfigEnabled($container, $config['exception'])) { $loader->load('exception.xml'); if ($config['exception']['map_exception_codes']) { diff --git a/EventListener/FormatListener.php b/EventListener/FormatListener.php index cff578c71..7704da380 100644 --- a/EventListener/FormatListener.php +++ b/EventListener/FormatListener.php @@ -55,7 +55,7 @@ public function onKernelRequest(RequestEvent $event): void } if (null === $format) { - if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) { + if (HttpKernelInterface::MAIN_REQUEST === $event->getRequestType()) { throw new NotAcceptableHttpException('No matching accepted Response format could be determined'); } diff --git a/EventListener/MimeTypeListener.php b/EventListener/MimeTypeListener.php index 98d758ae1..dbb235adc 100644 --- a/EventListener/MimeTypeListener.php +++ b/EventListener/MimeTypeListener.php @@ -14,7 +14,6 @@ use FOS\RestBundle\FOSRestBundle; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; -use Symfony\Component\HttpKernel\HttpKernelInterface; /** * This listener handles registering custom mime types. @@ -44,7 +43,7 @@ public function onKernelRequest(RequestEvent $event): void return; } - if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) { + if ($event->isMainRequest()) { foreach ($this->mimeTypes as $format => $mimeTypes) { $mimeTypes = array_merge($mimeTypes, Request::getMimeTypes($format)); diff --git a/Response/AllowedMethodsLoader/AllowedMethodsRouterLoader.php b/Response/AllowedMethodsLoader/AllowedMethodsRouterLoader.php index 3702c9e8e..c6d4339dd 100644 --- a/Response/AllowedMethodsLoader/AllowedMethodsRouterLoader.php +++ b/Response/AllowedMethodsLoader/AllowedMethodsRouterLoader.php @@ -38,7 +38,7 @@ public function __construct(RouterInterface $router, string $cacheDir, bool $isD public function getAllowedMethods(): array { if (!$this->cache->isFresh()) { - $this->warmUp(null); + $this->warmUp(\dirname($this->cache->getPath())); } return require $this->cache->getPath(); @@ -55,7 +55,7 @@ public function isOptional(): bool /** * {@inheritdoc} */ - public function warmUp($cacheDir): void + public function warmUp(string $cacheDir, ?string $buildDir = null): array { $processedRoutes = []; @@ -90,5 +90,7 @@ public function warmUp($cacheDir): void sprintf('getResources() ); + + return []; } } diff --git a/Tests/Controller/Annotations/FileParamTest.php b/Tests/Controller/Annotations/FileParamTest.php index c776cff3c..81f8bdd77 100644 --- a/Tests/Controller/Annotations/FileParamTest.php +++ b/Tests/Controller/Annotations/FileParamTest.php @@ -14,7 +14,7 @@ use FOS\RestBundle\Controller\Annotations\AbstractParam; use FOS\RestBundle\Controller\Annotations\FileParam; use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\FileBag; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\All; @@ -51,7 +51,7 @@ public function testValueGetter() ->willReturn('foo'); $request = $this->getMockBuilder(Request::class)->getMock(); - $parameterBag = $this->getMockBuilder(ParameterBag::class)->getMock(); + $parameterBag = $this->getMockBuilder(FileBag::class)->getMock(); $parameterBag ->expects($this->once()) ->method('get') diff --git a/Tests/DependencyInjection/FOSRestExtensionTest.php b/Tests/DependencyInjection/FOSRestExtensionTest.php index 1e43e1d5b..f99c3160a 100644 --- a/Tests/DependencyInjection/FOSRestExtensionTest.php +++ b/Tests/DependencyInjection/FOSRestExtensionTest.php @@ -15,6 +15,7 @@ use FOS\RestBundle\DependencyInjection\FOSRestExtension; use FOS\RestBundle\EventListener\ZoneMatcherListener; use PHPUnit\Framework\TestCase; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -294,6 +295,10 @@ public function testDisableViewResponseListener() public function testLoadViewResponseListener() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $config = [ 'fos_rest' => [ 'view' => [ @@ -309,6 +314,10 @@ public function testLoadViewResponseListener() public function testLoadViewResponseListenerForce() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $config = [ 'fos_rest' => [ 'view' => [ @@ -386,6 +395,10 @@ public function testViewHandlerSerializerOptions() public function testValidatorAliasWhenEnabled() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $config = [ 'fos_rest' => [ 'body_converter' => ['validate' => true], @@ -397,6 +410,10 @@ public function testValidatorAliasWhenEnabled() public function testValidatorAliasWhenDisabled() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $config = [ 'fos_rest' => [ 'body_converter' => ['validate' => false], diff --git a/Tests/EventListener/FormatListenerTest.php b/Tests/EventListener/FormatListenerTest.php index ab0e845b2..38e6f5ad7 100644 --- a/Tests/EventListener/FormatListenerTest.php +++ b/Tests/EventListener/FormatListenerTest.php @@ -126,7 +126,7 @@ public function testOnKernelControllerException() $event->expects($this->once()) ->method('getRequestType') - ->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST)); + ->will($this->returnValue(HttpKernelInterface::MAIN_REQUEST)); $requestStack = new RequestStack(); $requestStack->push($request); @@ -199,7 +199,7 @@ public function testSfFragmentFormat() $event->expects($this->any()) ->method('getRequestType') - ->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST)); + ->will($this->returnValue(HttpKernelInterface::MAIN_REQUEST)); $requestStack = new RequestStack(); $requestStack->push($request); diff --git a/Tests/EventListener/MimeTypeListenerTest.php b/Tests/EventListener/MimeTypeListenerTest.php index d60af374b..ba2e7097f 100644 --- a/Tests/EventListener/MimeTypeListenerTest.php +++ b/Tests/EventListener/MimeTypeListenerTest.php @@ -16,7 +16,6 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; -use Symfony\Component\HttpKernel\HttpKernelInterface; /** * Request listener test. @@ -43,8 +42,8 @@ public function testOnKernelRequest() $this->assertNull($request->getMimeType('jsonp')); $event->expects($this->once()) - ->method('getRequestType') - ->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST)); + ->method('isMainRequest') + ->will($this->returnValue(true)); $listener->onKernelRequest($event); @@ -64,8 +63,8 @@ public function testOnKernelRequestNoZone() ->will($this->returnValue($request)); $event->expects($this->never()) - ->method('getRequestType') - ->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST)); + ->method('isMainRequest') + ->will($this->returnValue(true)); $listener->onKernelRequest($event); @@ -85,8 +84,8 @@ public function testOnKernelRequestWithZone() ->will($this->returnValue($request)); $event->expects($this->once()) - ->method('getRequestType') - ->will($this->returnValue(HttpKernelInterface::MASTER_REQUEST)); + ->method('isMainRequest') + ->will($this->returnValue(true)); $listener->onKernelRequest($event); diff --git a/Tests/EventListener/ResponseStatusCodeListenerTest.php b/Tests/EventListener/ResponseStatusCodeListenerTest.php index d7a208081..0791b36fa 100644 --- a/Tests/EventListener/ResponseStatusCodeListenerTest.php +++ b/Tests/EventListener/ResponseStatusCodeListenerTest.php @@ -43,9 +43,9 @@ public function testResponseStatusCodeIsNotSetWhenRequestNotInRestZone() $request->attributes->set(FOSRestBundle::ZONE_ATTRIBUTE, false); if (class_exists(ExceptionEvent::class)) { - $exceptionEvent = new ExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, new \DomainException()); + $exceptionEvent = new ExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, new \DomainException()); } else { - $exceptionEvent = new GetResponseForExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, new \DomainException()); + $exceptionEvent = new GetResponseForExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, new \DomainException()); } $this->eventListener->getResponseStatusCodeFromThrowable($exceptionEvent); @@ -53,9 +53,9 @@ public function testResponseStatusCodeIsNotSetWhenRequestNotInRestZone() $response = new Response(); if (class_exists(ResponseEvent::class)) { - $responseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $response); + $responseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $response); } else { - $responseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $response); + $responseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $response); } $this->eventListener->setResponseStatusCode($responseEvent); @@ -68,9 +68,9 @@ public function testResponseStatusCodeIsNotSetWhenExceptionIsNotMapped() $request = new Request(); if (class_exists(ExceptionEvent::class)) { - $exceptionEvent = new ExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, new \LogicException()); + $exceptionEvent = new ExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, new \LogicException()); } else { - $exceptionEvent = new GetResponseForExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, new \LogicException()); + $exceptionEvent = new GetResponseForExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, new \LogicException()); } $this->eventListener->getResponseStatusCodeFromThrowable($exceptionEvent); @@ -78,9 +78,9 @@ public function testResponseStatusCodeIsNotSetWhenExceptionIsNotMapped() $response = new Response(); if (class_exists(ResponseEvent::class)) { - $responseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $response); + $responseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $response); } else { - $responseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $response); + $responseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $response); } $this->eventListener->setResponseStatusCode($responseEvent); @@ -94,9 +94,9 @@ public function testResponseStatusCodeIsSetWhenExceptionTypeIsConfigured() $exception = new \DomainException(); if (class_exists(ExceptionEvent::class)) { - $exceptionEvent = new ExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $exception); + $exceptionEvent = new ExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $exception); } else { - $exceptionEvent = new GetResponseForExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $exception); + $exceptionEvent = new GetResponseForExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $exception); } $this->eventListener->getResponseStatusCodeFromThrowable($exceptionEvent); @@ -104,9 +104,9 @@ public function testResponseStatusCodeIsSetWhenExceptionTypeIsConfigured() $response = new Response(); if (class_exists(ResponseEvent::class)) { - $responseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $response); + $responseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $response); } else { - $responseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $response); + $responseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $response); } $this->eventListener->setResponseStatusCode($responseEvent); @@ -122,14 +122,14 @@ public function testResponseStatusCodeIsSetWhenErrorTypeIsConfigured() $request = new Request(); - $this->eventListener->getResponseStatusCodeFromThrowable(new ExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, new \ParseError())); + $this->eventListener->getResponseStatusCodeFromThrowable(new ExceptionEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, new \ParseError())); $response = new Response(); if (class_exists(ResponseEvent::class)) { - $responseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $response); + $responseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $response); } else { - $responseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MASTER_REQUEST, $response); + $responseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $request, HttpKernelInterface::MAIN_REQUEST, $response); } $this->eventListener->setResponseStatusCode($responseEvent); @@ -143,9 +143,9 @@ public function testResponseStatusCodeIsNotOverriddenInSubRequests() $exception = new NotFoundHttpException(); if (class_exists(ExceptionEvent::class)) { - $masterRequestExceptionEvent = new ExceptionEvent($this->createMock(HttpKernelInterface::class), $masterRequest, HttpKernelInterface::MASTER_REQUEST, $exception); + $masterRequestExceptionEvent = new ExceptionEvent($this->createMock(HttpKernelInterface::class), $masterRequest, HttpKernelInterface::MAIN_REQUEST, $exception); } else { - $masterRequestExceptionEvent = new GetResponseForExceptionEvent($this->createMock(HttpKernelInterface::class), $masterRequest, HttpKernelInterface::MASTER_REQUEST, $exception); + $masterRequestExceptionEvent = new GetResponseForExceptionEvent($this->createMock(HttpKernelInterface::class), $masterRequest, HttpKernelInterface::MAIN_REQUEST, $exception); } $this->eventListener->getResponseStatusCodeFromThrowable($masterRequestExceptionEvent); @@ -174,9 +174,9 @@ public function testResponseStatusCodeIsNotOverriddenInSubRequests() $masterRequestResponse = new Response(); if (class_exists(ResponseEvent::class)) { - $masterRequestResponseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $masterRequest, HttpKernelInterface::MASTER_REQUEST, $masterRequestResponse); + $masterRequestResponseEvent = new ResponseEvent($this->createMock(HttpKernelInterface::class), $masterRequest, HttpKernelInterface::MAIN_REQUEST, $masterRequestResponse); } else { - $masterRequestResponseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $masterRequest, HttpKernelInterface::MASTER_REQUEST, $masterRequestResponse); + $masterRequestResponseEvent = new FilterResponseEvent($this->createMock(HttpKernelInterface::class), $masterRequest, HttpKernelInterface::MAIN_REQUEST, $masterRequestResponse); } $this->eventListener->setResponseStatusCode($masterRequestResponseEvent); diff --git a/Tests/EventListener/ViewResponseListenerTest.php b/Tests/EventListener/ViewResponseListenerTest.php index cbc6b98fd..51069c27b 100644 --- a/Tests/EventListener/ViewResponseListenerTest.php +++ b/Tests/EventListener/ViewResponseListenerTest.php @@ -17,7 +17,9 @@ use FOS\RestBundle\View\View; use FOS\RestBundle\View\ViewHandler; use FOS\RestBundle\View\ViewHandlerInterface; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; @@ -39,7 +41,7 @@ class ViewResponseListenerTest extends TestCase public $listener; /** - * @var ViewHandlerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ViewHandlerInterface|MockObject */ public $viewHandler; @@ -48,7 +50,7 @@ class ViewResponseListenerTest extends TestCase private $requestStack; /** - * @return ControllerEvent|\PHPUnit_Framework_MockObject_MockObject + * @return ControllerEvent|MockObject */ protected function getFilterEvent(Request $request) { @@ -61,13 +63,13 @@ protected function getFilterEvent(Request $request) /** * @param mixed $result * - * @return ViewEvent|\PHPUnit_Framework_MockObject_MockObject + * @return ViewEvent|MockObject */ protected function getResponseEvent(Request $request, $result) { $kernel = $this->createMock(HttpKernelInterface::class); - return new ViewEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST, $result); + return new ViewEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, $result); } public function testOnKernelViewWhenControllerResultIsNotViewObject() @@ -95,6 +97,10 @@ public static function statusCodeProvider() */ public function testStatusCode($annotationCode, $viewCode, $expectedCode) { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $this->createViewResponseListener(['json' => false]); $viewAnnotation = new ViewAnnotation([]); @@ -130,6 +136,10 @@ public static function serializerEnableMaxDepthChecksProvider() */ public function testSerializerEnableMaxDepthChecks($enableMaxDepthChecks, $expectedMaxDepth) { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $this->createViewResponseListener(['json' => false]); $viewAnnotation = new ViewAnnotation([]); diff --git a/Tests/Fixtures/Controller/ParamsAnnotatedController.php b/Tests/Fixtures/Controller/ParamsAnnotatedController.php index 7aa40e5ac..14d3fa30e 100644 --- a/Tests/Fixtures/Controller/ParamsAnnotatedController.php +++ b/Tests/Fixtures/Controller/ParamsAnnotatedController.php @@ -26,8 +26,11 @@ class ParamsAnnotatedController { /** * @QueryParam(name="page", requirements="\d+", default="1", description="Page of the overview.") + * * @RequestParam(name="byauthor", requirements="[a-z]+", description="by author", incompatibles={"search"}, strict=true) + * * @QueryParam(name="filters", map=true, requirements=@NotNull) + * * @FileParam(name="avatar", requirements={"mimeTypes"="application/json"}, image=true) * @FileParam(name="foo", requirements=@NotNull, strict=false) * @FileParam(name="bar", requirements=@NotNull, map=true) @@ -36,12 +39,12 @@ public function getArticlesAction(ParamFetcher $paramFetcher) { } - #[QueryParam(name: 'page', requirements: '\d+', default: '1', description: 'Page of the overview')] + #[QueryParam(name: 'page', requirements: '\d+', default: '1', description: 'Page of the overview.')] #[RequestParam(name: 'byauthor', requirements: '[a-z]+', description: 'by author', incompatibles: ['search'], strict: true)] - #[QueryParam(name: 'filters', requirements: '\d+', default: '1', description: 'Page of the overview')] + #[QueryParam(name: 'filters', map: true, requirements: new NotNull())] #[FileParam(name: 'avatar', requirements: ['mimeTypes' => 'application/json'], image: true)] - #[FileParam(name: 'foo', strict: false)] - #[FileParam(name: 'bar', map: true)] + #[FileParam(name: 'foo', requirements: new NotNull(), strict: false)] + #[FileParam(name: 'bar', requirements: new NotNull(), map: true)] public function getArticlesAttributesAction(ParamFetcher $paramFetcher) { } diff --git a/Tests/Functional/AllowedMethodsTest.php b/Tests/Functional/AllowedMethodsTest.php index f6f3f73de..c85eae2f8 100644 --- a/Tests/Functional/AllowedMethodsTest.php +++ b/Tests/Functional/AllowedMethodsTest.php @@ -11,6 +11,8 @@ namespace FOS\RestBundle\Tests\Functional; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; + /** * @author Ener-Getick */ @@ -18,6 +20,10 @@ class AllowedMethodsTest extends WebTestCase { public function testAllowHeader() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $client = $this->createClient(['test_case' => 'AllowedMethodsListener']); $client->request('POST', '/allowed-methods'); $this->assertEquals('GET, LOCK, POST, PUT', $client->getResponse()->headers->get('Allow')); diff --git a/Tests/Functional/Bundle/TestBundle/Controller/ArticleController.php b/Tests/Functional/Bundle/TestBundle/Controller/ArticleController.php index ac2e87dd9..78f1bb3dd 100644 --- a/Tests/Functional/Bundle/TestBundle/Controller/ArticleController.php +++ b/Tests/Functional/Bundle/TestBundle/Controller/ArticleController.php @@ -25,8 +25,10 @@ class ArticleController extends AbstractFOSRestController * @return View view instance * * @Post("/articles.{_format}", name="post_articles") + * * @View() */ + #[View] public function cpostAction(Request $request) { $view = $this->routeRedirectView('test_redirect_endpoint', ['name' => $request->request->get('name')]); @@ -40,8 +42,10 @@ public function cpostAction(Request $request) * @return View view instance * * @Get("/articles.{_format}", name="get_article", defaults={"_format": "html"}) + * * @View() */ + #[View] public function cgetAction(Request $request) { $view = $this->view(); diff --git a/Tests/Functional/Bundle/TestBundle/Controller/ParamFetcherController.php b/Tests/Functional/Bundle/TestBundle/Controller/ParamFetcherController.php index 5464e8431..e27c1fb08 100644 --- a/Tests/Functional/Bundle/TestBundle/Controller/ParamFetcherController.php +++ b/Tests/Functional/Bundle/TestBundle/Controller/ParamFetcherController.php @@ -28,6 +28,7 @@ class ParamFetcherController extends AbstractFOSRestController * @RequestParam(name="raw", requirements=@IdenticalTo({"foo"="raw", "bar"="foo"}), default="invalid", strict=false) * @RequestParam(name="map", map=true, requirements=@IdenticalTo({"foo"="map", "foobar"="foo"}), default="%invalid2% %%", strict=false) * @RequestParam(name="bar", nullable=true, requirements="%bar%\ foo") + * * @QueryParam(name="foz", requirements="[a-z]+") * @QueryParam(name="baz", requirements="[a-z]+", incompatibles={"foz"}) */ @@ -38,6 +39,7 @@ public function paramsAction(ParamFetcherInterface $fetcher) /** * @QueryParam(name="foo", default="invalid") + * * @RequestParam(name="bar", default="%foo%") */ public function testAction(Request $request, ParamFetcherInterface $fetcher) diff --git a/Tests/Functional/Bundle/TestBundle/Controller/SerializerErrorController.php b/Tests/Functional/Bundle/TestBundle/Controller/SerializerErrorController.php index a7915f0c7..0a1f7159d 100644 --- a/Tests/Functional/Bundle/TestBundle/Controller/SerializerErrorController.php +++ b/Tests/Functional/Bundle/TestBundle/Controller/SerializerErrorController.php @@ -50,6 +50,7 @@ public function customExceptionAction() /** * @View */ + #[View] public function invalidFormAction() { $form = $this->createFormBuilder(null, [ diff --git a/Tests/Functional/Bundle/TestBundle/Controller/Version2Controller.php b/Tests/Functional/Bundle/TestBundle/Controller/Version2Controller.php index f60dee566..1a506ea61 100644 --- a/Tests/Functional/Bundle/TestBundle/Controller/Version2Controller.php +++ b/Tests/Functional/Bundle/TestBundle/Controller/Version2Controller.php @@ -23,8 +23,10 @@ class Version2Controller extends AbstractFOSRestController { /** * @View() + * * @Get(path="/version", condition="request.attributes.get('version') in ['1.2']") */ + #[View] public function versionAction($version) { return ['version' => 'test annotation']; @@ -32,8 +34,10 @@ public function versionAction($version) /** * @View() + * * @Get(path="/version/{version}", requirements={"version": "1.2"}) */ + #[View] public function versionPathAction(Request $request, $version) { $versionExclusion = $this->findExclusionStrategyVersion($request); diff --git a/Tests/Functional/Bundle/TestBundle/Controller/VersionController.php b/Tests/Functional/Bundle/TestBundle/Controller/VersionController.php index 4dc489413..77afbc92a 100644 --- a/Tests/Functional/Bundle/TestBundle/Controller/VersionController.php +++ b/Tests/Functional/Bundle/TestBundle/Controller/VersionController.php @@ -23,6 +23,7 @@ class VersionController extends AbstractFOSRestController /** * @View() */ + #[View] public function versionAction(Request $request, $version) { $versionExclusion = $this->findExclusionStrategyVersion($request); diff --git a/Tests/Functional/DependencyInjectionTest.php b/Tests/Functional/DependencyInjectionTest.php index c946ce7ca..1517b7afc 100644 --- a/Tests/Functional/DependencyInjectionTest.php +++ b/Tests/Functional/DependencyInjectionTest.php @@ -64,7 +64,7 @@ public function registerBundles(): array public function registerContainerConfiguration(LoaderInterface $loader): void { $loader->load(function (ContainerBuilder $container) { - $container->loadFromExtension('framework', [ + $frameworkConfig = [ 'annotations' => [ 'enabled' => true, ], @@ -73,7 +73,13 @@ public function registerContainerConfiguration(LoaderInterface $loader): void 'resource' => '%kernel.project_dir%/config/routing.yml', 'utf8' => true, ], - ]); + ]; + + if (Kernel::VERSION_ID >= 70000) { + unset($frameworkConfig['annotations']); + } + + $container->loadFromExtension('framework', $frameworkConfig); $container->loadFromExtension('fos_rest', []); $container->setAlias('test.jms_serializer.handler_registry', new Alias('jms_serializer.handler_registry', true)); $container->setAlias('test.jms_serializer.form_error_handler', new Alias('jms_serializer.form_error_handler', true)); diff --git a/Tests/Functional/ParamFetcherTest.php b/Tests/Functional/ParamFetcherTest.php index 70cb30334..e8c40864d 100644 --- a/Tests/Functional/ParamFetcherTest.php +++ b/Tests/Functional/ParamFetcherTest.php @@ -11,9 +11,9 @@ namespace FOS\RestBundle\Tests\Functional; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Component\HttpFoundation\File\UploadedFile; -use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; /** * @author Ener-Getick @@ -36,28 +36,13 @@ class ParamFetcherTest extends WebTestCase private function createUploadedFile($path, $originalName, $mimeType = null, $error = null, $test = false) { - $ref = new \ReflectionClass(UploadedFile::class); - $params = $ref->getConstructor()->getParameters(); - - if ('error' === $params[3]->getName()) { - // symfony 4 has removed the $size param - return new UploadedFile( - $path, - $originalName, - $mimeType, - $error, - $test - ); - } else { - return new UploadedFile( - $path, - $originalName, - $mimeType, - filesize($path), - $error, - $test - ); - } + return new UploadedFile( + $path, + $originalName, + $mimeType, + $error, + $test + ); } protected function setUp(): void @@ -67,9 +52,16 @@ protected function setUp(): void public function testDefaultParameters() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $this->client->request('POST', '/params'); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $data = $this->getData(); + foreach (['raw' => 'invalid', 'map' => 'invalid2 %', 'bar' => null] as $key => $value) { $this->assertArrayHasKey($key, $data); $this->assertSame($value, $data[$key]); @@ -78,8 +70,14 @@ public function testDefaultParameters() public function testValidRawParameter() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $this->client->request('POST', '/params', ['raw' => $this->validRaw, 'map' => $this->validMap]); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $data = $this->getData(); foreach (['raw' => $this->validRaw, 'map' => 'invalid2 %', 'bar' => null] as $key => $value) { $this->assertArrayHasKey($key, $data); @@ -89,12 +87,19 @@ public function testValidRawParameter() public function testValidMapParameter() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $map = [ 'foo' => $this->validMap, 'bar' => $this->validMap, ]; + $this->client->request('POST', '/params', ['raw' => 'bar', 'map' => $map, 'bar' => 'bar foo']); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $data = $this->getData(); foreach (['raw' => 'invalid', 'map' => $map, 'bar' => 'bar foo'] as $key => $value) { $this->assertArrayHasKey($key, $data); @@ -104,8 +109,14 @@ public function testValidMapParameter() public function testWithSubRequests() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $this->client->request('POST', '/params/test?foo=quz', ['raw' => $this->validRaw]); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $expected = [ 'before' => ['foo' => 'quz', 'bar' => 'foo'], 'during' => ['raw' => $this->validRaw, 'map' => 'invalid2 %', 'bar' => null, 'foz' => '', 'baz' => ''], @@ -120,15 +131,21 @@ public function testWithSubRequests() public function testFileParamWithErrors() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $image = $this->createUploadedFile( 'Tests/Fixtures/Asset/cat.jpeg', - $singleFileName = 'cat.jpeg', + 'cat.jpeg', 'image/jpeg', 7 ); $this->client->request('POST', '/file/test', [], ['single_file' => $image]); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $this->assertEquals([ 'single_file' => 'noFile', ], $this->getData()); @@ -136,6 +153,10 @@ public function testFileParamWithErrors() public function testFileParam() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $image = $this->createUploadedFile( 'Tests/Fixtures/Asset/cat.jpeg', $singleFileName = 'cat.jpeg', @@ -144,6 +165,8 @@ public function testFileParam() $this->client->request('POST', '/file/test', [], ['single_file' => $image]); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $this->assertEquals([ 'single_file' => $singleFileName, ], $this->getData()); @@ -151,8 +174,14 @@ public function testFileParam() public function testFileParamNull() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $this->client->request('POST', '/file/test', [], []); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $this->assertEquals([ 'single_file' => 'noFile', ], $this->getData()); @@ -160,6 +189,10 @@ public function testFileParamNull() public function testFileParamArrayNullItem() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $images = [ $this->createUploadedFile( 'Tests/Fixtures/Asset/cat.jpeg', @@ -175,6 +208,8 @@ public function testFileParamArrayNullItem() $this->client->request('POST', '/file/collection/test', [], ['array_files' => $images]); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $this->assertEquals([ 'array_files' => [$imageName, $txtName], ], $this->getData()); @@ -182,6 +217,10 @@ public function testFileParamArrayNullItem() public function testFileParamImageConstraintArray() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $images = [ $this->createUploadedFile( 'Tests/Fixtures/Asset/cat.jpeg', @@ -197,6 +236,8 @@ public function testFileParamImageConstraintArray() $this->client->request('POST', '/image/collection/test', [], ['array_images' => $images]); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $this->assertEquals([ 'array_images' => [$imageName, $imageName2], ], $this->getData()); @@ -204,6 +245,10 @@ public function testFileParamImageConstraintArray() public function testFileParamImageConstraintArrayException() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $images = [ $this->createUploadedFile( 'Tests/Fixtures/Asset/cat.jpeg', @@ -219,6 +264,8 @@ public function testFileParamImageConstraintArrayException() $this->client->request('POST', '/image/collection/test', [], ['array_images' => $images]); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $this->assertEquals([ 'array_images' => 'NotAnImage', ], $this->getData()); @@ -226,8 +273,14 @@ public function testFileParamImageConstraintArrayException() public function testValidQueryParameter() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $this->client->request('POST', '/params?foz=val1'); + $this->assertTrue($this->client->getResponse()->isSuccessful(), 'The request resulted in an error.'); + $data = $this->getData(); foreach (['foz' => ''] as $key => $value) { $this->assertArrayHasKey($key, $data); @@ -237,16 +290,14 @@ public function testValidQueryParameter() public function testIncompatibleQueryParameter() { - try { - $this->client->request('POST', '/params?foz=val1&baz=val2'); - - // SF >= 4.4 - $this->assertEquals(400, $this->client->getResponse()->getStatusCode()); - $this->assertStringContainsString('\\"baz\\" param is incompatible with foz param.', $this->client->getResponse()->getContent()); - } catch (BadRequestHttpException $e) { - // SF < 4.4 - $this->assertEquals('"baz" param is incompatible with foz param.', $e->getMessage()); + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); } + + $this->client->request('POST', '/params?foz=val1&baz=val2'); + + $this->assertEquals(400, $this->client->getResponse()->getStatusCode()); + $this->assertStringContainsString('\\"baz\\" param is incompatible with foz param.', $this->client->getResponse()->getContent()); } protected function getData() diff --git a/Tests/Functional/RequestBodyParamConverterTest.php b/Tests/Functional/RequestBodyParamConverterTest.php index 1f27a4e6f..5ffcbd011 100644 --- a/Tests/Functional/RequestBodyParamConverterTest.php +++ b/Tests/Functional/RequestBodyParamConverterTest.php @@ -11,13 +11,16 @@ namespace FOS\RestBundle\Tests\Functional; -use Symfony\Bundle\FrameworkBundle\Test\BrowserKitAssertionsTrait; -use Symfony\Bundle\TwigBundle\Controller\PreviewErrorController; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; class RequestBodyParamConverterTest extends WebTestCase { public function testRequestBodyIsDeserialized() { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $client = $this->createClient(['test_case' => 'RequestBodyParamConverter']); $client->request( 'POST', @@ -32,33 +35,10 @@ public function testRequestBodyIsDeserialized() $this->assertSame('Post 1', $client->getResponse()->getContent()); } - /** - * Added to the legacy group to not trigger a deprecation. This deprecation is triggered on version 4.4 of - * the TwigBundle where the PreviewErrorController class is deprecated. Since we only make sure not to break - * that controller class, we do not have to care about the deprecations. - * - * @group legacy - * - * @see https://github.com/FriendsOfSymfony/FOSRestBundle/issues/1237 - */ - public function testErrorPageServedByTwigBundle() - { - if (!class_exists(PreviewErrorController::class)) { - $this->markTestSkipped(); - } - - $client = $this->createClient(['test_case' => 'RequestBodyParamConverterTwigBundle']); - $client->request('GET', '/_error/404.txt'); - - // Status code 200 as this page describes an error but is not the result of an error. - $this->assertEquals(200, $client->getResponse()->getStatusCode()); - $this->assertStringContainsString('The server returned a "404 Not Found".', $client->getResponse()->getContent()); - } - public function testErrorPageServedByFrameworkBundle() { - if (!trait_exists(BrowserKitAssertionsTrait::class)) { - $this->markTestSkipped(); + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); } $client = $this->createClient(['test_case' => 'RequestBodyParamConverterFrameworkBundle']); diff --git a/Tests/Functional/RouteAttributesTest.php b/Tests/Functional/RouteAttributesTest.php index 7f011f302..39e159086 100644 --- a/Tests/Functional/RouteAttributesTest.php +++ b/Tests/Functional/RouteAttributesTest.php @@ -11,6 +11,8 @@ namespace FOS\RestBundle\Tests\Functional; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; + /** * @requires PHP 8 */ @@ -21,6 +23,10 @@ class RouteAttributesTest extends WebTestCase public static function setUpBeforeClass(): void { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + self::markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + parent::setUpBeforeClass(); static::$client = static::createClient(['test_case' => self::TEST_CASE]); } diff --git a/Tests/Functional/SerializerErrorTest.php b/Tests/Functional/SerializerErrorTest.php index 3277c629f..54f05838d 100644 --- a/Tests/Functional/SerializerErrorTest.php +++ b/Tests/Functional/SerializerErrorTest.php @@ -11,7 +11,7 @@ namespace FOS\RestBundle\Tests\Functional; -use Symfony\Component\ErrorHandler\ErrorRenderer\SerializerErrorRenderer; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; /** * Test class for serialization errors and exceptions. @@ -38,8 +38,8 @@ public static function tearDownAfterClass(): void */ public function testSerializeExceptionJsonUsingErrorRenderer(string $testCase, array $expectedJson, string $expectedContentType) { - if (!class_exists(SerializerErrorRenderer::class)) { - $this->markTestSkipped(); + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); } $this->iniSet('error_log', file_exists('/dev/null') ? '/dev/null' : 'nul'); @@ -79,8 +79,8 @@ public function serializeExceptionJsonUsingErrorRendererProvider(): array public function testSerializeUnknownExceptionJsonWithDebugUsingErrorRenderer() { - if (!class_exists(SerializerErrorRenderer::class)) { - $this->markTestSkipped(); + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); } $this->iniSet('error_log', file_exists('/dev/null') ? '/dev/null' : 'nul'); @@ -93,8 +93,8 @@ public function testSerializeUnknownExceptionJsonWithDebugUsingErrorRenderer() public function testSerializeUnknownExceptionJsonWithoutDebugUsingErrorRenderer() { - if (!class_exists(SerializerErrorRenderer::class)) { - $this->markTestSkipped(); + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); } $this->iniSet('error_log', file_exists('/dev/null') ? '/dev/null' : 'nul'); @@ -110,8 +110,8 @@ public function testSerializeUnknownExceptionJsonWithoutDebugUsingErrorRenderer( */ public function testSerializeExceptionCodeMappedToResponseStatusCodeJsonUsingErrorRenderer(string $testCase, array $expectedJson) { - if (!class_exists(SerializerErrorRenderer::class)) { - $this->markTestSkipped(); + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); } $this->iniSet('error_log', file_exists('/dev/null') ? '/dev/null' : 'nul'); @@ -124,8 +124,8 @@ public function testSerializeExceptionCodeMappedToResponseStatusCodeJsonUsingErr public function testCustomExceptionSerialization() { - if (!class_exists(SerializerErrorRenderer::class)) { - $this->markTestSkipped(); + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); } $this->iniSet('error_log', file_exists('/dev/null') ? '/dev/null' : 'nul'); @@ -182,8 +182,8 @@ public function serializeExceptionCodeMappedToResponseStatusCodeJsonProvider(): */ public function testSerializeExceptionXmlUsingErrorRenderer(string $testCase, string $expectedContent, string $expectedContentType) { - if (!class_exists(SerializerErrorRenderer::class)) { - $this->markTestSkipped(); + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); } $this->iniSet('error_log', file_exists('/dev/null') ? '/dev/null' : 'nul'); @@ -234,6 +234,10 @@ public function serializeExceptionXmlUsingErrorRendererProvider(): array */ public function testSerializeInvalidFormJson($testCase) { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $client = $this->createClient(['test_case' => $testCase, 'debug' => false]); $client->request('GET', '/serializer-error/invalid-form.json'); @@ -253,6 +257,10 @@ public function invalidFormJsonProvider() */ public function testSerializeInvalidFormXml($testCase, $expectedContent) { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + $this->markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + $client = $this->createClient(['test_case' => $testCase, 'debug' => false]); $client->request('GET', '/serializer-error/invalid-form.xml'); diff --git a/Tests/Functional/VersionTest.php b/Tests/Functional/VersionTest.php index 4fcc98046..8f2e20dd5 100644 --- a/Tests/Functional/VersionTest.php +++ b/Tests/Functional/VersionTest.php @@ -11,6 +11,8 @@ namespace FOS\RestBundle\Tests\Functional; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; + /** * @author Ener-Getick */ @@ -20,6 +22,10 @@ class VersionTest extends WebTestCase public static function setUpBeforeClass(): void { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + self::markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + parent::setUpBeforeClass(); static::$client = static::createClient(['test_case' => 'Version']); } diff --git a/Tests/Functional/ViewResponseListenerTest.php b/Tests/Functional/ViewResponseListenerTest.php index afcbe7437..bcbd572aa 100644 --- a/Tests/Functional/ViewResponseListenerTest.php +++ b/Tests/Functional/ViewResponseListenerTest.php @@ -11,8 +11,19 @@ namespace FOS\RestBundle\Tests\Functional; +use Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle; + class ViewResponseListenerTest extends WebTestCase { + public static function setUpBeforeClass(): void + { + if (!class_exists(SensioFrameworkExtraBundle::class)) { + self::markTestSkipped('Test requires sensio/framework-extra-bundle'); + } + + parent::setUpBeforeClass(); + } + public static function tearDownAfterClass(): void { self::deleteTmpDir('ViewResponseListener'); diff --git a/Tests/Functional/app/AllowedMethodsListener/bundles.php b/Tests/Functional/app/AllowedMethodsListener/bundles.php index 2e6a41aa8..90086464f 100644 --- a/Tests/Functional/app/AllowedMethodsListener/bundles.php +++ b/Tests/Functional/app/AllowedMethodsListener/bundles.php @@ -9,9 +9,14 @@ * file that was distributed with this source code. */ -return [ +$bundles = [ new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new \FOS\RestBundle\FOSRestBundle(), new \FOS\RestBundle\Tests\Functional\Bundle\TestBundle\TestBundle(), - new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), ]; + +if (class_exists(\Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class)) { + $bundles[] = new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(); +} + +return $bundles; diff --git a/Tests/Functional/app/AllowedMethodsListener/config.yml b/Tests/Functional/app/AllowedMethodsListener/config.yml index 7f6103eb0..feaef7958 100644 --- a/Tests/Functional/app/AllowedMethodsListener/config.yml +++ b/Tests/Functional/app/AllowedMethodsListener/config.yml @@ -1,6 +1,6 @@ imports: - { resource: ../config/default.yml } - - { resource: ../config/sensio_framework_extra.yml } + - { resource: ../config/sensio_framework_extra.php } framework: serializer: diff --git a/Tests/Functional/app/Configuration/config.yml b/Tests/Functional/app/Configuration/config.yml index 530089633..fb4cdd571 100644 --- a/Tests/Functional/app/Configuration/config.yml +++ b/Tests/Functional/app/Configuration/config.yml @@ -1,6 +1,6 @@ imports: - { resource: ../config/default.yml } - - { resource: ../config/sensio_framework_extra.yml } + - { resource: ../config/sensio_framework_extra.php } - { resource: framework.php } - { resource: security.php } diff --git a/Tests/Functional/app/Configuration/framework.php b/Tests/Functional/app/Configuration/framework.php index 1ed19d3eb..7dd101454 100644 --- a/Tests/Functional/app/Configuration/framework.php +++ b/Tests/Functional/app/Configuration/framework.php @@ -29,4 +29,8 @@ $frameworkConfig['http_method_override'] = true; } +if (\Symfony\Component\HttpKernel\Kernel::VERSION_ID >= 70000) { + unset($frameworkConfig['annotations']); +} + $container->loadFromExtension('framework', $frameworkConfig); diff --git a/Tests/Functional/app/ParamFetcher/bundles.php b/Tests/Functional/app/ParamFetcher/bundles.php index 2e6a41aa8..90086464f 100644 --- a/Tests/Functional/app/ParamFetcher/bundles.php +++ b/Tests/Functional/app/ParamFetcher/bundles.php @@ -9,9 +9,14 @@ * file that was distributed with this source code. */ -return [ +$bundles = [ new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new \FOS\RestBundle\FOSRestBundle(), new \FOS\RestBundle\Tests\Functional\Bundle\TestBundle\TestBundle(), - new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), ]; + +if (class_exists(\Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class)) { + $bundles[] = new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(); +} + +return $bundles; diff --git a/Tests/Functional/app/ParamFetcher/config.yml b/Tests/Functional/app/ParamFetcher/config.yml index e511d0dee..63490abe2 100644 --- a/Tests/Functional/app/ParamFetcher/config.yml +++ b/Tests/Functional/app/ParamFetcher/config.yml @@ -1,6 +1,6 @@ imports: - { resource: ../config/default.yml } - - { resource: ../config/sensio_framework_extra.yml } + - { resource: ../config/sensio_framework_extra.php } parameters: bar: bar diff --git a/Tests/Functional/app/RequestBodyParamConverter/bundles.php b/Tests/Functional/app/RequestBodyParamConverter/bundles.php index 2e6a41aa8..90086464f 100644 --- a/Tests/Functional/app/RequestBodyParamConverter/bundles.php +++ b/Tests/Functional/app/RequestBodyParamConverter/bundles.php @@ -9,9 +9,14 @@ * file that was distributed with this source code. */ -return [ +$bundles = [ new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new \FOS\RestBundle\FOSRestBundle(), new \FOS\RestBundle\Tests\Functional\Bundle\TestBundle\TestBundle(), - new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), ]; + +if (class_exists(\Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class)) { + $bundles[] = new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(); +} + +return $bundles; diff --git a/Tests/Functional/app/RequestBodyParamConverter/config.yml b/Tests/Functional/app/RequestBodyParamConverter/config.yml index 6761c494d..7a6df0b89 100644 --- a/Tests/Functional/app/RequestBodyParamConverter/config.yml +++ b/Tests/Functional/app/RequestBodyParamConverter/config.yml @@ -1,6 +1,6 @@ imports: - { resource: ../config/default.yml } - - { resource: ../config/sensio_framework_extra.yml } + - { resource: ./sensio_framework_extra.php } framework: serializer: true @@ -12,10 +12,6 @@ fos_rest: body_converter: enabled: true -sensio_framework_extra: - request: - converters: true - services: get_set_method_normalizer: class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer diff --git a/Tests/Functional/app/RequestBodyParamConverter/sensio_framework_extra.php b/Tests/Functional/app/RequestBodyParamConverter/sensio_framework_extra.php new file mode 100644 index 000000000..75dd24d6b --- /dev/null +++ b/Tests/Functional/app/RequestBodyParamConverter/sensio_framework_extra.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (class_exists(\Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class)) { + $loader->load(__DIR__.'/../config/sensio_framework_extra.php'); + + $config = [ + 'request' => [ + 'converters' => true, + ], + ]; + + $container->loadFromExtension('sensio_framework_extra', $config); +} diff --git a/Tests/Functional/app/RequestBodyParamConverterFrameworkBundle/bundles.php b/Tests/Functional/app/RequestBodyParamConverterFrameworkBundle/bundles.php index 2e6a41aa8..90086464f 100644 --- a/Tests/Functional/app/RequestBodyParamConverterFrameworkBundle/bundles.php +++ b/Tests/Functional/app/RequestBodyParamConverterFrameworkBundle/bundles.php @@ -9,9 +9,14 @@ * file that was distributed with this source code. */ -return [ +$bundles = [ new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new \FOS\RestBundle\FOSRestBundle(), new \FOS\RestBundle\Tests\Functional\Bundle\TestBundle\TestBundle(), - new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), ]; + +if (class_exists(\Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class)) { + $bundles[] = new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(); +} + +return $bundles; diff --git a/Tests/Functional/app/RequestBodyParamConverterFrameworkBundle/config.yml b/Tests/Functional/app/RequestBodyParamConverterFrameworkBundle/config.yml index dffa3852e..548ce824e 100644 --- a/Tests/Functional/app/RequestBodyParamConverterFrameworkBundle/config.yml +++ b/Tests/Functional/app/RequestBodyParamConverterFrameworkBundle/config.yml @@ -1,6 +1,6 @@ imports: - { resource: ../config/default.yml } - - { resource: ../config/sensio_framework_extra.yml } + - { resource: ../config/sensio_framework_extra.php } framework: serializer: true diff --git a/Tests/Functional/app/RequestBodyParamConverterTwigBundle/config.yml b/Tests/Functional/app/RequestBodyParamConverterTwigBundle/config.yml index 7afea2132..254c0b9ef 100644 --- a/Tests/Functional/app/RequestBodyParamConverterTwigBundle/config.yml +++ b/Tests/Functional/app/RequestBodyParamConverterTwigBundle/config.yml @@ -1,6 +1,6 @@ imports: - { resource: ../config/default.yml } - - { resource: ../config/sensio_framework_extra.yml } + - { resource: ../config/sensio_framework_extra.php } framework: serializer: true diff --git a/Tests/Functional/app/RouteAttributes/bundles.php b/Tests/Functional/app/RouteAttributes/bundles.php index b0b50211e..90086464f 100644 --- a/Tests/Functional/app/RouteAttributes/bundles.php +++ b/Tests/Functional/app/RouteAttributes/bundles.php @@ -9,9 +9,14 @@ * file that was distributed with this source code. */ -return [ +$bundles = [ new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), new \FOS\RestBundle\FOSRestBundle(), new \FOS\RestBundle\Tests\Functional\Bundle\TestBundle\TestBundle(), ]; + +if (class_exists(\Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class)) { + $bundles[] = new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(); +} + +return $bundles; diff --git a/Tests/Functional/app/RouteAttributes/config.yml b/Tests/Functional/app/RouteAttributes/config.yml index def807c18..b4eb5157e 100644 --- a/Tests/Functional/app/RouteAttributes/config.yml +++ b/Tests/Functional/app/RouteAttributes/config.yml @@ -1,6 +1,6 @@ imports: - { resource: ../config/default.yml } - - { resource: ../config/sensio_framework_extra.yml } + - { resource: ../config/sensio_framework_extra.php } framework: serializer: diff --git a/Tests/Functional/app/Version/bundles.php b/Tests/Functional/app/Version/bundles.php index b0b50211e..90086464f 100644 --- a/Tests/Functional/app/Version/bundles.php +++ b/Tests/Functional/app/Version/bundles.php @@ -9,9 +9,14 @@ * file that was distributed with this source code. */ -return [ +$bundles = [ new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), new \FOS\RestBundle\FOSRestBundle(), new \FOS\RestBundle\Tests\Functional\Bundle\TestBundle\TestBundle(), ]; + +if (class_exists(\Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class)) { + $bundles[] = new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(); +} + +return $bundles; diff --git a/Tests/Functional/app/Version/config.yml b/Tests/Functional/app/Version/config.yml index 1aa744d74..c70d89da0 100644 --- a/Tests/Functional/app/Version/config.yml +++ b/Tests/Functional/app/Version/config.yml @@ -1,6 +1,6 @@ imports: - { resource: ../config/default.yml } - - { resource: ../config/sensio_framework_extra.yml } + - { resource: ../config/sensio_framework_extra.php } framework: serializer: diff --git a/Tests/Functional/app/ViewResponseListener/bundles.php b/Tests/Functional/app/ViewResponseListener/bundles.php index b0b50211e..90086464f 100644 --- a/Tests/Functional/app/ViewResponseListener/bundles.php +++ b/Tests/Functional/app/ViewResponseListener/bundles.php @@ -9,9 +9,14 @@ * file that was distributed with this source code. */ -return [ +$bundles = [ new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), new \FOS\RestBundle\FOSRestBundle(), new \FOS\RestBundle\Tests\Functional\Bundle\TestBundle\TestBundle(), ]; + +if (class_exists(\Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class)) { + $bundles[] = new \Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(); +} + +return $bundles; diff --git a/Tests/Functional/app/ViewResponseListener/config.yml b/Tests/Functional/app/ViewResponseListener/config.yml index 63096eed0..949772571 100644 --- a/Tests/Functional/app/ViewResponseListener/config.yml +++ b/Tests/Functional/app/ViewResponseListener/config.yml @@ -1,6 +1,6 @@ imports: - { resource: ../config/default.yml } - - { resource: ../config/sensio_framework_extra.yml } + - { resource: ../config/sensio_framework_extra.php } - { resource: framework.php } fos_rest: diff --git a/Tests/Functional/app/config/framework.php b/Tests/Functional/app/config/framework.php index d29de23ee..6b3ea0d80 100644 --- a/Tests/Functional/app/config/framework.php +++ b/Tests/Functional/app/config/framework.php @@ -41,4 +41,8 @@ $frameworkConfig['http_method_override'] = true; } +if (\Symfony\Component\HttpKernel\Kernel::VERSION_ID >= 70000) { + unset($frameworkConfig['annotations']); +} + $container->loadFromExtension('framework', $frameworkConfig); diff --git a/Tests/Functional/app/config/sensio_framework_extra.php b/Tests/Functional/app/config/sensio_framework_extra.php new file mode 100644 index 000000000..82d7801f2 --- /dev/null +++ b/Tests/Functional/app/config/sensio_framework_extra.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (class_exists(\Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class)) { + $config = [ + 'router' => [ + 'annotations' => false, + ], + ]; + + $container->loadFromExtension('sensio_framework_extra', $config); +} diff --git a/Tests/Functional/app/config/sensio_framework_extra.yml b/Tests/Functional/app/config/sensio_framework_extra.yml deleted file mode 100644 index 1821ccc07..000000000 --- a/Tests/Functional/app/config/sensio_framework_extra.yml +++ /dev/null @@ -1,3 +0,0 @@ -sensio_framework_extra: - router: - annotations: false diff --git a/Tests/Request/ParamReaderTest.php b/Tests/Request/ParamReaderTest.php index 70e2408b7..b929f58ca 100644 --- a/Tests/Request/ParamReaderTest.php +++ b/Tests/Request/ParamReaderTest.php @@ -11,13 +11,13 @@ namespace FOS\RestBundle\Tests\Request; +use Composer\InstalledVersions; +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\Reader; use FOS\RestBundle\Controller\Annotations\ParamInterface; -use FOS\RestBundle\Controller\Annotations\View; use FOS\RestBundle\Request\ParamReader; -use Doctrine\Common\Annotations\AnnotationReader; use FOS\RestBundle\Tests\Fixtures\Controller\ParamsAnnotatedController; use PHPUnit\Framework\TestCase; -use Symfony\Component\Validator\Constraints\NotNull; /** * @author Alexander @@ -26,89 +26,56 @@ class ParamReaderTest extends TestCase { private $paramReader; - protected function setUp(): void - { - $annotationReader = $this->getMockBuilder(AnnotationReader::class)->getMock(); - - $methodAnnotations = []; - $foo = $this->createMockedParam(); - $foo - ->expects($this->any()) - ->method('getName') - ->willReturn('foo'); - $methodAnnotations[] = $foo; - - $bar = $this->createMockedParam(); - $bar - ->expects($this->any()) - ->method('getName') - ->willReturn('bar'); - $methodAnnotations[] = $bar; - - $methodAnnotations[] = new View([]); - - $annotationReader - ->expects($this->any()) - ->method('getMethodAnnotations') - ->will($this->returnValue($methodAnnotations)); - - $classAnnotations = []; - - $baz = $this->createMockedParam(); - $baz - ->expects($this->any()) - ->method('getName') - ->willReturn('baz'); - $classAnnotations[] = $baz; - - $mikz = $this->createMockedParam(); - $mikz - ->expects($this->any()) - ->method('getName') - ->willReturn('micz'); - $classAnnotations[] = $mikz; - - $classAnnotations[] = new View([]); - - $annotationReader - ->expects($this->any()) - ->method('getClassAnnotations') - ->will($this->returnValue($classAnnotations)); - - $this->paramReader = new ParamReader($annotationReader); - } + private static $validatorSupportsAnnotations = true; - /** - * Test that only ParamInterface annotations are returned. - */ - public function testReadsOnlyParamAnnotations() + public static function setUpBeforeClass(): void { - $annotations = $this->paramReader->read(new \ReflectionClass(__CLASS__), 'setUp'); + $validatorSupportsAnnotations = true; - $this->assertCount(4, $annotations); + if (class_exists(InstalledVersions::class) && InstalledVersions::isInstalled('symfony/validator')) { + $validatorVersion = InstalledVersions::getVersion('symfony/validator'); - foreach ($annotations as $name => $annotation) { - $this->assertInstanceOf(ParamInterface::class, $annotation); - $this->assertEquals($annotation->getName(), $name); + $validatorSupportsAnnotations = null !== $validatorVersion && version_compare($validatorVersion, '7.0', '<'); } + + self::$validatorSupportsAnnotations = $validatorSupportsAnnotations; } - /** - * @requires PHP 8 - */ - public function testReadsAttributes() + protected function setUp(): void { - $paramReader = new ParamReader(); - $params = $paramReader->read(new \ReflectionClass(ParamsAnnotatedController::class), 'getArticlesAttributesAction'); + // An annotation reader is only injected when `doctrine/annotations` is installed and `symfony/validator` is installed at a version supporting annotations + if (interface_exists(Reader::class) && self::$validatorSupportsAnnotations) { + $this->paramReader = new ParamReader(new AnnotationReader()); + } else { + $this->paramReader = new ParamReader(); + } + } + + public function testReadsAnnotations() + { + if (!interface_exists(Reader::class)) { + $this->markTestSkipped('Test requires doctrine/annotations'); + } + + if (!self::$validatorSupportsAnnotations) { + $this->markTestSkipped('Test requires symfony/validator:<7.0'); + } + + $params = $this->paramReader->read(new \ReflectionClass(ParamsAnnotatedController::class), 'getArticlesAction'); $this->assertCount(6, $params); + foreach ($params as $name => $param) { + $this->assertInstanceOf(ParamInterface::class, $param); + $this->assertEquals($param->getName(), $name); + } + // Param 1 (query) $this->assertArrayHasKey('page', $params); $this->assertEquals('page', $params['page']->name); $this->assertEquals('\\d+', $params['page']->requirements); $this->assertEquals('1', $params['page']->default); - $this->assertEquals('Page of the overview', $params['page']->description); + $this->assertEquals('Page of the overview.', $params['page']->description); $this->assertFalse($params['page']->map); $this->assertFalse($params['page']->strict); @@ -124,7 +91,7 @@ public function testReadsAttributes() // Param 3 (query) $this->assertArrayHasKey('filters', $params); $this->assertEquals('filters', $params['filters']->name); - $this->assertFalse($params['filters']->map); + $this->assertTrue($params['filters']->map); // Param 4 (file) $this->assertArrayHasKey('avatar', $params); @@ -140,57 +107,62 @@ public function testReadsAttributes() $this->assertFalse($params['foo']->strict); } - public function testExceptionOnNonExistingMethod() + /** + * @requires PHP 8 + */ + public function testReadsAttributes() { - $this->expectException(\InvalidArgumentException::class); - $this->expectExceptionMessage(sprintf('Class "%s" has no method "foo".', self::class)); - - $this->paramReader->read(new \ReflectionClass(__CLASS__), 'foo'); - } + $params = $this->paramReader->read(new \ReflectionClass(ParamsAnnotatedController::class), 'getArticlesAttributesAction'); - public function testAnnotationReader() - { - $reader = new AnnotationReader(); + $this->assertCount(6, $params); - $method = new \ReflectionMethod(ParamsAnnotatedController::class, 'getArticlesAction'); - $params = $reader->getMethodAnnotations($method); + foreach ($params as $name => $param) { + $this->assertInstanceOf(ParamInterface::class, $param); + $this->assertEquals($param->getName(), $name); + } // Param 1 (query) - $this->assertEquals('page', $params[0]->name); - $this->assertEquals('\\d+', $params[0]->requirements); - $this->assertEquals('1', $params[0]->default); - $this->assertEquals('Page of the overview.', $params[0]->description); - $this->assertFalse($params[0]->map); - $this->assertFalse($params[0]->strict); + $this->assertArrayHasKey('page', $params); + $this->assertEquals('page', $params['page']->name); + $this->assertEquals('\\d+', $params['page']->requirements); + $this->assertEquals('1', $params['page']->default); + $this->assertEquals('Page of the overview.', $params['page']->description); + $this->assertFalse($params['page']->map); + $this->assertFalse($params['page']->strict); // Param 2 (request) - $this->assertEquals('byauthor', $params[1]->name); - $this->assertEquals('[a-z]+', $params[1]->requirements); - $this->assertEquals('by author', $params[1]->description); - $this->assertEquals(['search'], $params[1]->incompatibles); - $this->assertFalse($params[1]->map); - $this->assertTrue($params[1]->strict); + $this->assertArrayHasKey('byauthor', $params); + $this->assertEquals('byauthor', $params['byauthor']->name); + $this->assertEquals('[a-z]+', $params['byauthor']->requirements); + $this->assertEquals('by author', $params['byauthor']->description); + $this->assertEquals(['search'], $params['byauthor']->incompatibles); + $this->assertFalse($params['byauthor']->map); + $this->assertTrue($params['byauthor']->strict); // Param 3 (query) - $this->assertEquals('filters', $params[2]->name); - $this->assertTrue($params[2]->map); - $this->assertEquals(new NotNull(), $params[2]->requirements); + $this->assertArrayHasKey('filters', $params); + $this->assertEquals('filters', $params['filters']->name); + $this->assertTrue($params['filters']->map); // Param 4 (file) - $this->assertEquals('avatar', $params[3]->name); - $this->assertEquals(['mimeTypes' => 'application/json'], $params[3]->requirements); - $this->assertTrue($params[3]->image); - $this->assertTrue($params[3]->strict); + $this->assertArrayHasKey('avatar', $params); + $this->assertEquals('avatar', $params['avatar']->name); + $this->assertEquals(['mimeTypes' => 'application/json'], $params['avatar']->requirements); + $this->assertTrue($params['avatar']->image); + $this->assertTrue($params['avatar']->strict); // Param 5 (file) - $this->assertEquals('foo', $params[4]->name); - $this->assertEquals(new NotNull(), $params[4]->requirements); - $this->assertFalse($params[4]->image); - $this->assertFalse($params[4]->strict); + $this->assertArrayHasKey('foo', $params); + $this->assertEquals('foo', $params['foo']->name); + $this->assertFalse($params['foo']->image); + $this->assertFalse($params['foo']->strict); } - protected function createMockedParam() + public function testExceptionOnNonExistingMethod() { - return $this->getMockBuilder(ParamInterface::class)->getMock(); + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage(sprintf('Class "%s" has no method "foo".', self::class)); + + $this->paramReader->read(new \ReflectionClass(__CLASS__), 'foo'); } } diff --git a/Tests/Request/RequestBodyParamConverterTest.php b/Tests/Request/RequestBodyParamConverterTest.php index 372f29672..b0094c65b 100644 --- a/Tests/Request/RequestBodyParamConverterTest.php +++ b/Tests/Request/RequestBodyParamConverterTest.php @@ -34,23 +34,15 @@ class RequestBodyParamConverterTest extends TestCase protected $serializer; protected $converter; - protected function setUp(): void + public static function setUpBeforeClass(): void { - // skip the test if the installed version of SensioFrameworkExtraBundle - // is not compatible with the RequestBodyParamConverter class - $parameter = new \ReflectionParameter( - [ - ParamConverterInterface::class, - 'supports', - ], - 'configuration' - ); - if (ParamConverter::class != $parameter->getType()->getName()) { - $this->markTestSkipped( - 'skipping RequestBodyParamConverterTest due to an incompatible version of the SensioFrameworkExtraBundle' - ); + if (!class_exists(ParamConverterInterface::class)) { + self::markTestSkipped('Test requires sensio/framework-extra-bundle'); } + } + protected function setUp(): void + { $this->serializer = $this->getMockBuilder(Serializer::class)->getMock(); $this->converter = new RequestBodyParamConverter($this->serializer); } diff --git a/composer.json b/composer.json index 62469eea6..18947651f 100644 --- a/composer.json +++ b/composer.json @@ -29,45 +29,45 @@ ] }, "require": { - "php": "^7.2|^8.0", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/framework-bundle": "^4.4.1|^5.0|^6.0", - "symfony/http-foundation": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/routing": "^5.4|^6.0", - "symfony/security-core": "^5.4|^6.0", + "php": "^7.4|^8.0", + "symfony/config": "^5.4|^6.4|^7.0", + "symfony/dependency-injection": "^5.4|^6.4|^7.0", + "symfony/event-dispatcher": "^5.4|^6.4|^7.0", + "symfony/framework-bundle": "^5.4|^6.4|^7.0", + "symfony/http-foundation": "^5.4|^6.4|^7.0", + "symfony/http-kernel": "^5.4|^6.4|^7.0", + "symfony/routing": "^5.4|^6.4|^7.0", + "symfony/security-core": "^5.4|^6.4|^7.0", "willdurand/jsonp-callback-validator": "^1.0|^2.0", "willdurand/negotiation": "^2.0|^3.0" }, "require-dev": { - "doctrine/annotations": "^1.13.2|^2.0 ", - "friendsofphp/php-cs-fixer": "^3.0", + "doctrine/annotations": "^1.13.2|^2.0", + "friendsofphp/php-cs-fixer": "^3.43", "jms/serializer": "^1.13|^2.0|^3.0", "jms/serializer-bundle": "^2.4.3|^3.0.1|^4.0|^5.0", "psr/http-message": "^1.0", "psr/log": "^1.0|^2.0|^3.0", "sensio/framework-extra-bundle": "^6.1", - "symfony/phpunit-bridge": "^5.4|^6.0", - "symfony/asset": "^5.4|^6.0", - "symfony/browser-kit": "^5.4|^6.0", - "symfony/css-selector": "^5.4|^6.0", - "symfony/expression-language": "^5.4|^6.0", - "symfony/form": "^5.4|^6.0", - "symfony/mime": "^5.4|^6.0", - "symfony/security-bundle": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0", - "symfony/twig-bundle": "^5.4|^6.0", - "symfony/validator": "^5.4|^6.0", - "symfony/web-profiler-bundle": "^5.4|^6.0", - "symfony/yaml": "^5.4|^6.0" + "symfony/asset": "^5.4|^6.4|^7.0", + "symfony/browser-kit": "^5.4|^6.4|^7.0", + "symfony/css-selector": "^5.4|^6.4|^7.0", + "symfony/expression-language": "^5.4|^6.4|^7.0", + "symfony/form": "^5.4|^6.4|^7.0", + "symfony/mime": "^5.4|^6.4|^7.0", + "symfony/phpunit-bridge": "^5.4|^6.4|^7.0", + "symfony/security-bundle": "^5.4|^6.4|^7.0", + "symfony/serializer": "^5.4|^6.4|^7.0", + "symfony/twig-bundle": "^5.4|^6.4|^7.0", + "symfony/validator": "^5.4|^6.4|^7.0", + "symfony/web-profiler-bundle": "^5.4|^6.4|^7.0", + "symfony/yaml": "^5.4|^6.4|^7.0" }, "suggest": { - "sensio/framework-extra-bundle": "Add support for the request body converter and the view response listener, requires ^3.0", - "jms/serializer-bundle": "Add support for advanced serialization capabilities, recommended, requires ^2.0|^3.0", - "symfony/serializer": "Add support for basic serialization capabilities and xml decoding, requires ^2.7|^3.0", - "symfony/validator": "Add support for validation capabilities in the ParamFetcher, requires ^2.7|^3.0" + "jms/serializer-bundle": "Add support for advanced serialization capabilities, recommended", + "sensio/framework-extra-bundle": "Add support for the request body converter and the view response listener, not supported with Symfony >=7.0", + "symfony/serializer": "Add support for basic serialization capabilities and xml decoding", + "symfony/validator": "Add support for validation capabilities in the ParamFetcher" }, "conflict": { "doctrine/annotations": "<1.12", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7f9b085e9..a40a673fc 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -2,7 +2,8 @@ - + +