Skip to content

Add Symfony 6 support #2340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Nov 22, 2021
Merged
18 changes: 14 additions & 4 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,37 @@ jobs:
phpunit:
name: "PHPUnit"
runs-on: "ubuntu-20.04"
continue-on-error: ${{ matrix.can-fail }}

strategy:
matrix:
include:
- php-version: 7.2
composer-flags: "--prefer-lowest --prefer-stable"
can-fail: false
- php-version: 7.3
composer-flags: "--prefer-stable"
can-fail: false
- php-version: 7.4
composer-flags: "--prefer-stable"
symfony-require: "4.4.*"
can-fail: false
- php-version: 7.4
composer-flags: "--prefer-stable"
symfony-require: "5.1.*"
symfony-require: "5.3.*"
can-fail: false
coverage: yes
- php-version: 7.4
composer-flags: "--prefer-stable"
symfony-require: "5.2.*"
can-fail: true
symfony-require: "5.4.*@dev"
- php-version: 8.0
composer-flags: "--ignore-platform-reqs --prefer-stable"
composer-flags: "--prefer-stable"
can-fail: true
symfony-require: "6.0.*@dev"
- php-version: 8.1
composer-flags: "--prefer-stable"
can-fail: false

steps:
- name: "Checkout"
Expand All @@ -48,13 +57,15 @@ jobs:
with:
php-version: "${{ matrix.php-version }}"
coverage: "xdebug"
tools: "composer:v2,flex"

- name: "Install PHP without coverage"
uses: "shivammathur/setup-php@v2"
if: "${{ matrix.coverage == '' }}"
with:
php-version: "${{ matrix.php-version }}"
coverage: "none"
tools: "composer:v2,flex"

- name: "Cache dependencies installed with composer"
uses: "actions/cache@v2"
Expand All @@ -67,7 +78,6 @@ jobs:
env:
SYMFONY_REQUIRE: "${{ matrix.symfony-require }}"
run: |
composer global require --no-progress --no-scripts --no-plugins symfony/flex
composer update --no-interaction --no-progress ${{ matrix.composer-flags }}

- name: "Run PHPUnit"
Expand Down
24 changes: 10 additions & 14 deletions Controller/AbstractFOSRestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,22 @@
namespace FOS\RestBundle\Controller;

use FOS\RestBundle\View\ViewHandlerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Contracts\Service\ServiceSubscriberInterface;

$ref = new \ReflectionMethod(ServiceSubscriberInterface::class, 'getSubscribedServices');

// Has the ServiceSubscriberInterface a return type hint
if ($ref->getReturnType() !== null) {
class_alias(PostSymfony6AbstractFOSRestController::class, 'FOS\RestBundle\Controller\BaseAbstractFOSRestController');
} else {
class_alias(PreSymfony6AbstractFOSRestController::class, 'FOS\RestBundle\Controller\BaseAbstractFOSRestController');
}
/**
* Controllers using the View functionality of FOSRestBundle.
*/
abstract class AbstractFOSRestController extends AbstractController
abstract class AbstractFOSRestController extends BaseAbstractFOSRestController
{
use ControllerTrait;

/**
* @return ViewHandlerInterface
*/
Expand All @@ -32,15 +39,4 @@ protected function getViewHandler()

return $this->viewhandler;
}

/**
* {@inheritdoc}
*/
public static function getSubscribedServices()
{
$subscribedServices = parent::getSubscribedServices();
$subscribedServices['fos_rest.view_handler'] = ViewHandlerInterface::class;

return $subscribedServices;
}
}
32 changes: 32 additions & 0 deletions Controller/PostSymfony6AbstractFOSRestController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/*
* This file is part of the FOSRestBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FOS\RestBundle\Controller;

use FOS\RestBundle\View\ViewHandlerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

/**
* @internal
*/
abstract class PostSymfony6AbstractFOSRestController extends AbstractController
{
/**
* {@inheritdoc}
*/
public static function getSubscribedServices(): array
{
$subscribedServices = parent::getSubscribedServices();
$subscribedServices['fos_rest.view_handler'] = ViewHandlerInterface::class;

return $subscribedServices;
}
}
34 changes: 34 additions & 0 deletions Controller/PreSymfony6AbstractFOSRestController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of the FOSRestBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FOS\RestBundle\Controller;

use FOS\RestBundle\View\ViewHandlerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

/**
* @internal
*/
abstract class PreSymfony6AbstractFOSRestController extends AbstractController
{
use ControllerTrait;

/**
* {@inheritdoc}
*/
public static function getSubscribedServices()
{
$subscribedServices = parent::getSubscribedServices();
$subscribedServices['fos_rest.view_handler'] = ViewHandlerInterface::class;

return $subscribedServices;
}
}
14 changes: 12 additions & 2 deletions EventListener/BodyListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use FOS\RestBundle\FOSRestBundle;
use FOS\RestBundle\Normalizer\ArrayNormalizerInterface;
use FOS\RestBundle\Normalizer\Exception\NormalizationException;
use Symfony\Component\HttpFoundation\InputBag;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
Expand Down Expand Up @@ -88,7 +89,12 @@ public function onKernelRequest(RequestEvent $event): void
$decoder = $this->decoderProvider->getDecoder($format);
$data = $decoder->decode($content);
if (is_array($data)) {
$request->request = new ParameterBag($data);
if (class_exists(InputBag::class)) {
$request->request = new InputBag($data);
} else {
$request->request = new ParameterBag($data);
}

$normalizeRequest = true;
} else {
throw new BadRequestHttpException('Invalid '.$format.' message received');
Expand All @@ -105,7 +111,11 @@ public function onKernelRequest(RequestEvent $event): void
throw new BadRequestHttpException($e->getMessage());
}

$request->request = new ParameterBag($data);
if (class_exists(InputBag::class)) {
$request->request = new InputBag($data);
} else {
$request->request = new ParameterBag($data);
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions Serializer/Normalizer/FlattenExceptionNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function __construct(ExceptionValueMap $statusCodeMap, ExceptionValueMap
$this->rfc7807 = $rfc7807;
}

public function normalize($exception, $format = null, array $context = [])
public function normalize($exception, $format = null, array $context = []): array
{
if (isset($context['status_code'])) {
$statusCode = $context['status_code'];
Expand Down Expand Up @@ -74,7 +74,7 @@ public function normalize($exception, $format = null, array $context = [])
}
}

public function supportsNormalization($data, $format = null, array $context = [])
public function supportsNormalization($data, $format = null, array $context = []): bool
{
return $data instanceof FlattenException && ($context[Serializer::FOS_BUNDLE_SERIALIZATION_CONTEXT] ?? false);
}
Expand Down
13 changes: 10 additions & 3 deletions Tests/Controller/Annotations/QueryParamTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use FOS\RestBundle\Controller\Annotations\AbstractScalarParam;
use FOS\RestBundle\Controller\Annotations\QueryParam;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\InputBag;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;

Expand Down Expand Up @@ -45,9 +46,15 @@ public function testValueGetter()
->willReturn('foo');

$request = $this->getMockBuilder(Request::class)->getMock();
$parameterBag = new ParameterBag();
$parameterBag->set('foo', 'foobar');
$request->query = $parameterBag;

if (class_exists(InputBag::class)) {
$bag = new InputBag();
} else {
$bag = new ParameterBag();
}

$bag->set('foo', 'foobar');
$request->query = $bag;

$this->assertEquals('foobar', $this->param->getValue($request, 'bar'));
}
Expand Down
13 changes: 10 additions & 3 deletions Tests/Controller/Annotations/RequestParamTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use FOS\RestBundle\Controller\Annotations\AbstractScalarParam;
use FOS\RestBundle\Controller\Annotations\RequestParam;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\InputBag;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;

Expand Down Expand Up @@ -45,9 +46,15 @@ public function testValueGetter()
->willReturn('foo');

$request = $this->getMockBuilder(Request::class)->getMock();
$parameterBag = new ParameterBag();
$parameterBag->set('foo', 'foobar');
$request->request = $parameterBag;

if (class_exists(InputBag::class)) {
$bag = new InputBag();
} else {
$bag = new ParameterBag();
}

$bag->set('foo', 'foobar');
$request->request = $bag;

$this->assertEquals('foobar', $this->param->getValue($request, 'bar'));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function versionPathAction(Request $request, $version)
private function findExclusionStrategyVersion(Request $request)
{
$view = $this->view([]);
$response = $this->get('fos_rest.view_handler')->createResponse($view, $request, 'json');
$response = $this->getViewHandler()->createResponse($view, $request, 'json');

return $view->getContext()->getVersion();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function versionAction(Request $request, $version)
private function findExclusionStrategyVersion(Request $request)
{
$view = $this->view([]);
$response = $this->get('fos_rest.view_handler')->createResponse($view, $request, 'json');
$response = $this->getViewHandler()->createResponse($view, $request, 'json');

return $view->getContext()->getVersion();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

/*
* This file is part of the FOSRestBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace FOS\RestBundle\Tests\Functional\Bundle\TestBundle\Security;

use FOS\RestBundle\Tests\Functional\Bundle\TestBundle\Entity\User;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;

class ApiToken53Authenticator extends AbstractAuthenticator
{
protected $headerName = 'x-foo';
protected $tokenValue = 'FOOBAR';

public function authenticate(Request $request): PassportInterface
{
$credentials = $request->headers->get($this->headerName);

if (!$credentials || $credentials !== $this->tokenValue) {
throw new BadCredentialsException();
}

$userBadge = new UserBadge($this->tokenValue, function () {
$user = new User();
$user->username = 'foo';
$user->roles[] = 'ROLE_API';

return $user;
});

return new SelfValidatingPassport($userBadge);
}

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
return null;
}

public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
{
return new JsonResponse(null, 401);
}

public function supports(Request $request): ?bool
{
if (!$request->headers->has($this->headerName)) {
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;

class ApiTokenAuthenticator extends AbstractAuthenticator
{
protected $headerName = 'x-foo';
protected $tokenValue = 'FOOBAR';

public function authenticate(Request $request): PassportInterface
/**
* @return Passport
*/
public function authenticate(Request $request)
{
$credentials = $request->headers->get($this->headerName);

Expand Down
Loading