Symfony has just released Symfony 7.0, there are a lot of breaking changes introduced in this version.
In this post, we will use Symfony Rest Example project as an example, and show how I upgrade to v7 step by step.
Backup Existing Codes
Firstly, let's create a GIT tag on the existing codes, and back up the project.
git tag v6.x
git push origin v6.x
For those still use Symfony 6.x, please check the tag v6.x
or download a copy from the project tag page.
Create a new branch to prepare the upgrading work.
git checkout -b v7
Upgrade Symfony Packages to v7
Import the project source codes into an IDE(PHPStorm or NetBeans IDE) or VSCode( with PHP support).
Open composer.json
file, change all symfony packages version to 7.0.*
.
{
"name": "hantsy/symfony-rest-sample",
"description": "Restful APIs examples built with Symfony 7 and PHP 8",
"authors": [
{
"name": "Hantsy Bai",
"email": "hantsy@gmail.com"
}
],
"type": "project",
"license": "GPL-3.0-or-later",
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": ">=8.2",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11.99.4",
"doctrine/annotations": "^2.0",
"doctrine/doctrine-bundle": "^2.4",
"doctrine/doctrine-migrations-bundle": "^3.1",
"doctrine/orm": "^2.9",
"phpdocumentor/reflection-docblock": "^5.2",
"symfony/asset": "7.0.*",
"symfony/console": "7.0.*",
"symfony/dotenv": "7.0.*",
"symfony/expression-language": "7.0.*",
"symfony/flex": "^2.0.1",
"symfony/framework-bundle": "7.0.*",
"symfony/monolog-bundle": "^3.7",
"symfony/property-access": "7.0.*",
"symfony/property-info": "7.0.*",
"symfony/runtime": "7.0.*",
"symfony/serializer": "7.0.*",
"symfony/uid": "7.0.*",
"symfony/validator": "7.0.*",
"symfony/yaml": "7.0.*"
},
"config": {
"optimize-autoloader": true,
"preferred-install": {
"*": "dist"
},
"allow-plugins": true,
"sort-packages": true
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
"replace": {
"symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*",
"symfony/polyfill-php72": "*"
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
],
"test": "php bin/phpunit"
},
"conflict": {
"symfony/symfony": "*"
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "7.0.*"
}
},
"require-dev": {
"dama/doctrine-test-bundle": "^8.0",
"doctrine/doctrine-fixtures-bundle": "^3.4",
"phpunit/phpunit": "^9.5",
"symfony/browser-kit": "7.0.*",
"symfony/css-selector": "7.0.*",
"symfony/maker-bundle": "^1.34",
"symfony/phpunit-bridge": "7.0.*"
}
}
We remove sensio/framework-extra-bundle
and nelmio/cors-bundle
from the package list due to the compatibility with the new v7.
- The former is deprecated in v7 and not recommended in new projects. We will use new API to replace them.
- The later does not release a Symfony v7 compatible version at the moment.
Remove composer.lock
, try to run composer install
command in a terminal to install new packages and rerun the built-in recipes for this project.
Refresh Codes with New APIs
There are a few classes that need to align with the new APIs in Symfony v7.
Firstly, we used ArgumentResolver
in sensio/framework-extra-bundle
to convert parameters to Uuid
. In Symfony v7, the ValueResolver
is the replacement. More details please check Extending Action Argument Resolving.
And there is a built-in UidValueResolver can be used to convert the parameter to Uuidv4
, so we can give up our custom Uuid converter, and switch to use the official one.
Simply remove the existing src/ParamConverter/UuidParamConverter
.
Next, let's update the BodyValueResolver
and QueryParamValueResolver
. The original ArgumentValueResolverInterface
is removed in v7, the replacement is ValueResolverInterface
, which is similar to the old ArgumentValueResolverInterface
, but there is no supports
method to override.
We adjust the existing codes slightly to make it work seamlessly.
class QueryParamValueResolver implements ValueResolverInterface, LoggerAwareInterface
{
public function __construct()
{
}
private LoggerInterface $logger;
/**
* @inheritDoc
*/
public function resolve(Request $request, ArgumentMetadata $argument): iterable
{
if (!$this->supports($request, $argument)) return [];
...
}
We reuse supports
method to determine if skip the execution of argument resolving. Similarly, we adjust the BodyValueResolver
as expected.
Rerun composer install
, we get an error like the following.
!! Symfony\Component\ErrorHandler\Error\FatalError {#307
!! #message: "Compile Error: Declaration of App\Annotation\Delete::getMethods() must be compatible with Symfony\Component\Routing\Attribute\Route::getMethods(): array"
!! #code: 0
!! #file: "D:\hantsylabs\symfony5-sample\src\Annotation\Delete.php"
!! #line: 11
!! -error: array:4 [
!! "type" => 64
!! "message" => "Declaration of App\Annotation\Delete::getMethods() must be compatible with Symfony\Component\Routing\Attribute\Route::getMethods(): array"
!! "file" => "D:\hantsylabs\symfony5-sample\src\Annotation\Delete.php"
!! "line" => 11
!! ]
!! }
!! PHP Fatal error: Declaration of App\Annotation\Delete::getMethods() must be compatible with Symfony\Component\Routing\Attribute\Route::getMethods(): array in D:\hantsylabs\symfony5-sample\src\Annotation\Delete.php on line 11
In Symfony v7, all methods and properties in classes require type declaration. For developers, it is good to write type safe codes.
Symfony provides a simple script to upgrade existing codes and apply type declaration automatically.
vendor/bin/patch-type-declarations
More information about the type declaration, check Symfony 7.0 Type Declarations.
Rerun composer install
, we get an new error like the following.
!! In Loader.php line 63:
!!
!! Cannot load resource "../../src/Controller/". Make sure there is a loader
!! supporting the "annotation" type.
We should use the new attributes config to replace the legacy annotations config for loading controllers.
- Remove
config/routes/annotations.yaml
. -
Create a new file
config/routes/attributes.yaml
, fill in the following content.
controllers: resource: path: ../../src/Controller/ namespace: App\Controller type: attribute kernel: resource: App\Kernel type: attribute
More information about Route, check Creating Routes as Attributes.
Now the composer install
command should be executed successfully.
When running the tests, there are several deprecated warning info.
The Get
, Post
, Put
, Delete
, etc. that we created as examples of demonstrating attributes now raise warning info like the Route
could be final in future. Thus means in a further version, these custom attributes will not work.
- Remove the entire
src\Annotations
folder. - Open the
PostController
, use originalRoute
attribute instead.
Note, if you use Route
from Symfony\Component\Routing\Annotation\Route
, change it to use the new Symfony\Component\Routing\Attribute\Route
instead.
The left another two warning info are from Doctrine configuration.
Open config/packages/doctrine.yaml
, add the following two lines.
doctrine:
dbal:
url: '%env(resolve:DATABASE_URL)%'
use_savepoints: true // add this line
...
orm:
auto_generate_proxy_classes: true
enable_lazy_ghost_objects: true // add this line
...
I have not researched the new changes in Doctrine, these configuration is provided in the warning info.
Now run the tests, all tests should be passed.
Upgrade to PHPUnit 10
Run the following command to update PHPUnit to 10.x.
composer recipes:update symfony/phpunit-bridge
Then update the PHPUnit packages to ^10.0
.
composer install
When running the tests, there are some deprecation information in the test result.
Run the following command to migrate the configuration to PHPUnit 10 compatible format.
vendor/bin/phpunit --migrate-configuration
You will find the Symfony test listener is removed by this script, because listener
is not a valid element in PHPUnit 10 configuration. Let's wait for a new replacement in a further version.
Manually add a source
element into the phpunit.xml.dist.
<source>
<include>
<directory suffix=".php">src</directory>
</include>
</source>
Now run the tests again, all tests should be passed.
Update PHP versions on Github Actions
Finally, we should update the PHP version in Github actions workflow file, drop PHP 8.1 from the versoin list, and add PHP 8.3 as build environment. PHP 8.1 is no longer supported in the new Symfony v7.
The working codes is available via my Github, check out the symfony-rest-sample and explore it yourself.
Top comments (0)