diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8325f4dac0..b06b9acd8b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
+## [4.34.0](https://github.com/sonata-project/SonataAdminBundle/compare/4.33.0...4.34.0) - 2024-12-07
+### Changed
+- [[#8225](https://github.com/sonata-project/SonataAdminBundle/pull/8225)] Drop Symfony < 6.4 ([@VincentLanglet](https://github.com/VincentLanglet))
+- [[#8225](https://github.com/sonata-project/SonataAdminBundle/pull/8225)] Drop PHP 8 ([@VincentLanglet](https://github.com/VincentLanglet))
+
## [4.33.0](https://github.com/sonata-project/SonataAdminBundle/compare/4.32.0...4.33.0) - 2024-12-04
### Added
- [[#8219](https://github.com/sonata-project/SonataAdminBundle/pull/8219)] Support editable mode on `FieldDescriptionInterface::TYPE_ENUM` ([@onEXHovia](https://github.com/onEXHovia))
diff --git a/composer.json b/composer.json
index ba0943f39f..9fec2da779 100644
--- a/composer.json
+++ b/composer.json
@@ -24,11 +24,11 @@
],
"homepage": "https://docs.sonata-project.org/projects/SonataAdminBundle",
"require": {
- "php": "^8.0",
+ "php": "^8.1",
"ext-json": "*",
"doctrine/collections": "^1.6 || ^2.0",
"doctrine/common": "^3.0",
- "knplabs/knp-menu": "^3.1",
+ "knplabs/knp-menu": "^3.2",
"knplabs/knp-menu-bundle": "^3.0",
"psr/container": "^1.0 || ^2.0",
"psr/log": "^2.0 || ^3.0",
@@ -37,32 +37,32 @@
"sonata-project/exporter": "^2.14 || ^3.1.1",
"sonata-project/form-extensions": "^1.15 || ^2.0",
"sonata-project/twig-extensions": "^1.4.1 || ^2.0",
- "symfony/asset": "^5.4 || ^6.2 || ^7.0",
- "symfony/config": "^5.4 || ^6.2 || ^7.0",
- "symfony/console": "^5.4 || ^6.2 || ^7.0",
- "symfony/dependency-injection": "^5.4 || ^6.2 || ^7.0",
- "symfony/doctrine-bridge": "^5.4 || ^6.2 || ^7.0",
- "symfony/event-dispatcher": "^5.4 || ^6.2 || ^7.0",
+ "symfony/asset": "^6.4 || ^7.1",
+ "symfony/config": "^6.4 || ^7.1",
+ "symfony/console": "^6.4 || ^7.1",
+ "symfony/dependency-injection": "^6.4 || ^7.1",
+ "symfony/doctrine-bridge": "^6.4 || ^7.1",
+ "symfony/event-dispatcher": "^6.4 || ^7.1",
"symfony/event-dispatcher-contracts": "^2.0 || ^3.0",
- "symfony/expression-language": "^5.4 || ^6.2 || ^7.0",
- "symfony/form": "^5.4 || ^6.2 || ^7.0",
- "symfony/framework-bundle": "^5.4 || ^6.2 || ^7.0",
- "symfony/http-foundation": "^5.4 || ^6.2 || ^7.0",
- "symfony/http-kernel": "^5.4 || ^6.2 || ^7.0",
- "symfony/options-resolver": "^5.4 || ^6.2 || ^7.0",
- "symfony/property-access": "^5.4 || ^6.2 || ^7.0",
- "symfony/routing": "^5.4 || ^6.2 || ^7.0",
+ "symfony/expression-language": "^6.4 || ^7.1",
+ "symfony/form": "^6.4 || ^7.1",
+ "symfony/framework-bundle": "^6.4 || ^7.1",
+ "symfony/http-foundation": "^6.4 || ^7.1",
+ "symfony/http-kernel": "^6.4 || ^7.1",
+ "symfony/options-resolver": "^6.4 || ^7.1",
+ "symfony/property-access": "^6.4 || ^7.1",
+ "symfony/routing": "^6.4 || ^7.1",
"symfony/security-acl": "^3.1",
- "symfony/security-bundle": "^5.4 || ^6.2 || ^7.0",
- "symfony/security-core": "^5.4 || ^6.2 || ^7.0",
- "symfony/security-csrf": "^5.4 || ^6.2 || ^7.0",
- "symfony/serializer": "^5.4 || ^6.2 || ^7.0",
- "symfony/string": "^5.4 || ^6.2 || ^7.0",
- "symfony/translation": "^5.4 || ^6.2 || ^7.0",
+ "symfony/security-bundle": "^6.4 || ^7.1",
+ "symfony/security-core": "^6.4 || ^7.1",
+ "symfony/security-csrf": "^6.4 || ^7.1",
+ "symfony/serializer": "^6.4 || ^7.1",
+ "symfony/string": "^6.4 || ^7.1",
+ "symfony/translation": "^6.4 || ^7.1",
"symfony/translation-contracts": "^2.3 || ^3.0",
- "symfony/twig-bridge": "^5.4 || ^6.2 || ^7.0",
- "symfony/twig-bundle": "^5.4 || ^6.2 || ^7.0",
- "symfony/validator": "^5.4 || ^6.2 || ^7.0",
+ "symfony/twig-bridge": "^6.4 || ^7.1",
+ "symfony/twig-bundle": "^6.4 || ^7.1",
+ "symfony/validator": "^6.4 || ^7.1",
"twig/string-extra": "^3.0",
"twig/twig": "^3.0"
},
@@ -76,17 +76,17 @@
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpstan/phpstan-symfony": "^1.0",
- "phpunit/phpunit": "^9.5",
+ "phpunit/phpunit": "^9.6",
"psalm/plugin-phpunit": "^0.18",
"psalm/plugin-symfony": "^5.0",
"psr/event-dispatcher": "^1.0",
"rector/rector": "^1.1",
- "symfony/browser-kit": "^5.4 || ^6.2 || ^7.0",
- "symfony/css-selector": "^5.4 || ^6.2 || ^7.0",
- "symfony/filesystem": "^5.4 || ^6.2 || ^7.0",
+ "symfony/browser-kit": "^6.4 || ^7.1",
+ "symfony/css-selector": "^6.4 || ^7.1",
+ "symfony/filesystem": "^6.4 || ^7.1",
"symfony/maker-bundle": "^1.25",
- "symfony/phpunit-bridge": "^6.2 || ^7.0",
- "symfony/yaml": "^5.4 || ^6.2 || ^7.0",
+ "symfony/phpunit-bridge": "^6.4 || ^7.1",
+ "symfony/yaml": "^6.4 || ^7.1",
"vimeo/psalm": "^5.8.0"
},
"suggest": {
diff --git a/rector.php b/rector.php
index d57c8f4916..254341a3b4 100644
--- a/rector.php
+++ b/rector.php
@@ -19,6 +19,8 @@
use Rector\Config\RectorConfig;
use Rector\Php70\Rector\FunctionLike\ExceptionHandlerTypehintRector;
+use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector;
+use Rector\Php81\Rector\Property\ReadOnlyPropertyRector;
use Rector\PHPUnit\CodeQuality\Rector\Class_\NarrowUnusedSetUpDefinedPropertyRector;
use Rector\PHPUnit\CodeQuality\Rector\Class_\PreferPHPUnitThisCallRector;
use Rector\PHPUnit\Set\PHPUnitSetList;
@@ -40,6 +42,8 @@
$rectorConfig->importShortClasses(false);
$rectorConfig->skip([
ExceptionHandlerTypehintRector::class,
+ ReadOnlyPropertyRector::class,
+ NullToStrictStringFuncCallArgRector::class,
PreferPHPUnitThisCallRector::class,
NarrowUnusedSetUpDefinedPropertyRector::class,
]);
diff --git a/src/Command/GenerateObjectAclCommand.php b/src/Command/GenerateObjectAclCommand.php
index 4ab81ce901..97512ea7ef 100644
--- a/src/Command/GenerateObjectAclCommand.php
+++ b/src/Command/GenerateObjectAclCommand.php
@@ -94,7 +94,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
$securityIdentity = null;
if ($input->getOption('step') && $this->askConfirmation($input, $output, "Set an object owner?\n", 'no')) {
- $username = $this->askAndValidate($input, $output, 'Please enter the username: ', '', [Validators::class, 'validateUsername']);
+ $username = $this->askAndValidate($input, $output, 'Please enter the username: ', '', Validators::validateUsername(...));
$securityIdentity = new UserSecurityIdentity($username, $this->getUserModelClass($input, $output));
}
diff --git a/src/EventListener/ConfigureCRUDControllerListener.php b/src/EventListener/ConfigureCRUDControllerListener.php
index cc3179abfc..651cf80245 100644
--- a/src/EventListener/ConfigureCRUDControllerListener.php
+++ b/src/EventListener/ConfigureCRUDControllerListener.php
@@ -29,6 +29,13 @@ public function onKernelController(ControllerEvent $event): void
if (\is_array($controller)) {
$controller = $controller[0];
+ } else {
+ try {
+ $reflection = new \ReflectionFunction($controller(...));
+ $controller = $reflection->getClosureThis();
+ } catch (\ReflectionException) {
+ return;
+ }
}
if (!$controller instanceof CRUDController) {
diff --git a/src/Maker/AdminMaker.php b/src/Maker/AdminMaker.php
index 8447b9cfa0..401a8d0c54 100644
--- a/src/Maker/AdminMaker.php
+++ b/src/Maker/AdminMaker.php
@@ -122,14 +122,14 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
$this->modelClass = $io->ask(
'The fully qualified model class',
$input->getArgument('model'),
- [Validators::class, 'validateClass']
+ Validators::validateClass(...)
);
- $this->modelClassBasename = \array_slice(explode('\\', $this->modelClass), -1)[0];
+ $this->modelClassBasename = \array_slice(explode('\\', (string) $this->modelClass), -1)[0];
$this->adminClassBasename = $io->ask(
'The admin class basename',
$input->getOption('admin') ?? \sprintf('%sAdmin', $this->modelClassBasename),
- [Validators::class, 'validateAdminClassBasename']
+ Validators::validateAdminClassBasename(...)
);
if (\count($this->availableModelManagers) > 1) {
$managerTypes = array_keys($this->availableModelManagers);
@@ -141,7 +141,7 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
$this->controllerClassBasename = $io->ask(
'The controller class basename',
$input->getOption('controller') ?? \sprintf('%sAdminController', $this->modelClassBasename),
- [Validators::class, 'validateControllerClassBasename']
+ Validators::validateControllerClassBasename(...)
);
$input->setOption('controller', $this->controllerClassBasename);
}
@@ -150,12 +150,12 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
$servicesFile = $io->ask(
'The services YAML configuration file',
$input->getOption('services') ?? (is_file($path.'admin.yaml') ? 'admin.yaml' : 'services.yaml'),
- [Validators::class, 'validateServicesFile']
+ Validators::validateServicesFile(...)
);
$id = $io->ask(
'The admin service ID',
$this->getAdminServiceId($this->adminClassBasename),
- [Validators::class, 'validateServiceId']
+ Validators::validateServiceId(...)
);
$input->setOption('services', $servicesFile);
$input->setOption('id', $id);
diff --git a/src/Util/AdminObjectAclData.php b/src/Util/AdminObjectAclData.php
index 1417308e29..bcfd0304e4 100644
--- a/src/Util/AdminObjectAclData.php
+++ b/src/Util/AdminObjectAclData.php
@@ -31,11 +31,6 @@ final class AdminObjectAclData
*/
private static array $ownerPermissions = ['MASTER', 'OWNER'];
- /**
- * @var \Traversable Roles to set ACL for
- */
- private \Traversable $aclRoles;
-
/**
* @var array Cache of masks
*/
@@ -50,7 +45,7 @@ final class AdminObjectAclData
/**
* @param AdminInterface