Skip to content
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

Add support for Symfony 7 where able #2400

Merged
merged 1 commit into from Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 36 additions & 14 deletions .github/workflows/continuous-integration.yml
Expand Up @@ -18,38 +18,54 @@ 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
- php-version: "7.4"
composer-flags: ""
symfony-require: "5.4.*"
can-fail: false
coverage: yes
- php-version: 8.0
- php-version: "8.0"
composer-flags: ""
can-fail: false
symfony-require: "5.4.*"
- php-version: "8.1"
composer-flags: ""
can-fail: false
symfony-require: "6.4.*"
- php-version: "8.2"
composer-flags: ""
can-fail: false
symfony-require: "6.0.*"
- php-version: 8.1
symfony-require: "6.4.*"
- php-version: "8.3"
composer-flags: ""
can-fail: false
symfony-require: "6.1.*"
- php-version: 8.2
symfony-require: "6.4.*"
- php-version: "8.3"
composer-flags: ""
can-fail: false
symfony-require: "6.2.*"
- php-version: 8.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.*"
- php-version: 8.2
symfony-require: "7.0.*"
remove-sensio-bundle: yes # SensioFrameworkExtraBundle is not compatible with Symfony 7.0 or later
- php-version: "8.3"
composer-flags: ""
can-fail: true # we don't want to fail the build if we are incompatible with the next (unstable) Symfony version
remove-sensio-bundle: yes # SensioFrameworkExtraBundle is not compatible with Symfony 7.0 or later

env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}

steps:
- name: "Checkout"
Expand Down Expand Up @@ -78,11 +94,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"
Expand Down
4 changes: 2 additions & 2 deletions Controller/Annotations/FileParam.php
Expand Up @@ -22,11 +22,11 @@
*
* @Annotation
* @NamedArgumentConstructor
* @Target("METHOD")
* @Target({"CLASS", "METHOD"})
*
* @author Ener-Getick <egetick@gmail.com>
*/
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_METHOD)]
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
class FileParam extends AbstractParam
{
/** @var bool */
Expand Down
4 changes: 2 additions & 2 deletions Controller/Annotations/RequestParam.php
Expand Up @@ -18,12 +18,12 @@
*
* @Annotation
* @NamedArgumentConstructor
* @Target("METHOD")
* @Target({"CLASS", "METHOD"})
*
* @author Jordi Boggiano <j.boggiano@seld.be>
* @author Boris Guéry <guery.b@gmail.com>
*/
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_METHOD)]
#[\Attribute(\Attribute::IS_REPEATABLE | \Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD)]
class RequestParam extends AbstractScalarParam
{
/** @var bool */
Expand Down
146 changes: 77 additions & 69 deletions Controller/Annotations/Route.php
Expand Up @@ -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.
Expand All @@ -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,
Expand All @@ -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()) {
Expand Down