diff --git a/.appveyor.yml b/.appveyor.yml index 67dcba9b569f6..383bbfdbb6493 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -18,8 +18,8 @@ install: - cd ext - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_apcu-5.1.21-8.1-ts-vs16-x86.zip - 7z x php_apcu-5.1.21-8.1-ts-vs16-x86.zip -y >nul - - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_redis-5.3.7rc1-8.1-ts-vs16-x86.zip - - 7z x php_redis-5.3.7rc1-8.1-ts-vs16-x86.zip -y >nul + - appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_redis-5.3.7-8.1-ts-vs16-x86.zip + - 7z x php_redis-5.3.7-8.1-ts-vs16-x86.zip -y >nul - cd .. - copy /Y php.ini-development php.ini-min - echo memory_limit=-1 >> php.ini-min diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 00a686580d01f..0dfc06eb7c16b 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,10 +1,10 @@ | Q | A | ------------- | --- -| Branch? | 6.2 for features / 4.4, 5.4, 6.0 or 6.1 for bug fixes +| Branch? | 6.4 for features / 5.4, or 6.3 for bug fixes | Bug fix? | yes/no | New feature? | yes/no | Deprecations? | yes/no -| Tickets | Fix #... +| Tickets | Fix #... | License | MIT | Doc PR | symfony/symfony-docs#... diff --git a/.github/expected-missing-return-types.diff b/.github/expected-missing-return-types.diff index 192369f7eb726..81c3cc9e262dd 100644 --- a/.github/expected-missing-return-types.diff +++ b/.github/expected-missing-return-types.diff @@ -3,14 +3,13 @@ sed -i 's/ *"\*\*\/Tests\/"//' composer.json composer u -o SYMFONY_PATCH_TYPE_DECLARATIONS='force=2&php=8.1' php .github/patch-types.php head=$(sed '/^diff /Q' .github/expected-missing-return-types.diff) -(echo "$head" && echo && git diff -U2 src/) > .github/expected-missing-return-types.diff +(echo "$head" && echo && git diff -U2 src/ | grep '^index ' -v) > .github/expected-missing-return-types.diff git checkout composer.json src/ diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php -index 165797504b..0c0922088a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php -@@ -87,5 +87,5 @@ abstract class KernelTestCase extends TestCase +@@ -88,5 +88,5 @@ abstract class KernelTestCase extends TestCase * @return Container */ - protected static function getContainer(): ContainerInterface @@ -18,7 +17,6 @@ index 165797504b..0c0922088a 100644 { if (!static::$booted) { diff --git a/src/Symfony/Component/BrowserKit/AbstractBrowser.php b/src/Symfony/Component/BrowserKit/AbstractBrowser.php -index b27ca37529..5b80175850 100644 --- a/src/Symfony/Component/BrowserKit/AbstractBrowser.php +++ b/src/Symfony/Component/BrowserKit/AbstractBrowser.php @@ -408,5 +408,5 @@ abstract class AbstractBrowser @@ -50,10 +48,9 @@ index b27ca37529..5b80175850 100644 { return $response; diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php -index 89d6adb70e..c569992efc 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php -@@ -108,5 +108,5 @@ class NodeBuilder implements NodeParentInterface +@@ -111,5 +111,5 @@ class NodeBuilder implements NodeParentInterface * @return NodeDefinition&ParentNodeDefinitionInterface */ - public function end() @@ -61,7 +58,6 @@ index 89d6adb70e..c569992efc 100644 { return $this->parent; diff --git a/src/Symfony/Component/Config/Definition/ConfigurationInterface.php b/src/Symfony/Component/Config/Definition/ConfigurationInterface.php -index 7b5d443fe6..d64ae0d024 100644 --- a/src/Symfony/Component/Config/Definition/ConfigurationInterface.php +++ b/src/Symfony/Component/Config/Definition/ConfigurationInterface.php @@ -26,4 +26,4 @@ interface ConfigurationInterface @@ -71,18 +67,16 @@ index 7b5d443fe6..d64ae0d024 100644 + public function getConfigTreeBuilder(): TreeBuilder; } diff --git a/src/Symfony/Component/Config/FileLocator.php b/src/Symfony/Component/Config/FileLocator.php -index 21122e52c9..206029e705 100644 --- a/src/Symfony/Component/Config/FileLocator.php +++ b/src/Symfony/Component/Config/FileLocator.php -@@ -34,5 +34,5 @@ class FileLocator implements FileLocatorInterface - * {@inheritdoc} - */ +@@ -31,5 +31,5 @@ class FileLocator implements FileLocatorInterface + } + - public function locate(string $name, string $currentPath = null, bool $first = true) + public function locate(string $name, string $currentPath = null, bool $first = true): string|array { if ('' === $name) { diff --git a/src/Symfony/Component/Config/FileLocatorInterface.php b/src/Symfony/Component/Config/FileLocatorInterface.php -index e3ca1d49c4..526d350484 100644 --- a/src/Symfony/Component/Config/FileLocatorInterface.php +++ b/src/Symfony/Component/Config/FileLocatorInterface.php @@ -31,4 +31,4 @@ interface FileLocatorInterface @@ -92,10 +86,9 @@ index e3ca1d49c4..526d350484 100644 + public function locate(string $name, string $currentPath = null, bool $first = true): string|array; } diff --git a/src/Symfony/Component/Config/Loader/FileLoader.php b/src/Symfony/Component/Config/Loader/FileLoader.php -index c479f75d34..0d16baaff7 100644 --- a/src/Symfony/Component/Config/Loader/FileLoader.php +++ b/src/Symfony/Component/Config/Loader/FileLoader.php -@@ -69,5 +69,5 @@ abstract class FileLoader extends Loader +@@ -67,5 +67,5 @@ abstract class FileLoader extends Loader * @throws FileLocatorFileNotFoundException */ - public function import(mixed $resource, string $type = null, bool $ignoreErrors = false, string $sourceResource = null, string|array $exclude = null) @@ -103,10 +96,9 @@ index c479f75d34..0d16baaff7 100644 { if (\is_string($resource) && \strlen($resource) !== ($i = strcspn($resource, '*?{[')) && !str_contains($resource, "\n")) { diff --git a/src/Symfony/Component/Config/Loader/Loader.php b/src/Symfony/Component/Config/Loader/Loader.php -index e0974fb151..f698b5271b 100644 --- a/src/Symfony/Component/Config/Loader/Loader.php +++ b/src/Symfony/Component/Config/Loader/Loader.php -@@ -50,5 +50,5 @@ abstract class Loader implements LoaderInterface +@@ -44,5 +44,5 @@ abstract class Loader implements LoaderInterface * @return mixed */ - public function import(mixed $resource, string $type = null) @@ -114,7 +106,6 @@ index e0974fb151..f698b5271b 100644 { return $this->resolve($resource, $type)->load($resource, $type); diff --git a/src/Symfony/Component/Config/Loader/LoaderInterface.php b/src/Symfony/Component/Config/Loader/LoaderInterface.php -index b94a4378f5..db502e12a7 100644 --- a/src/Symfony/Component/Config/Loader/LoaderInterface.php +++ b/src/Symfony/Component/Config/Loader/LoaderInterface.php @@ -26,5 +26,5 @@ interface LoaderInterface @@ -139,7 +130,6 @@ index b94a4378f5..db502e12a7 100644 /** diff --git a/src/Symfony/Component/Config/ResourceCheckerInterface.php b/src/Symfony/Component/Config/ResourceCheckerInterface.php -index 6b1c6c5fbe..bb80ed461e 100644 --- a/src/Symfony/Component/Config/ResourceCheckerInterface.php +++ b/src/Symfony/Component/Config/ResourceCheckerInterface.php @@ -33,5 +33,5 @@ interface ResourceCheckerInterface @@ -156,52 +146,51 @@ index 6b1c6c5fbe..bb80ed461e 100644 + public function isFresh(ResourceInterface $resource, int $timestamp): bool; } diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php -index 64068fcc23..f29aaf1b94 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php -@@ -218,5 +218,5 @@ class Application implements ResetInterface +@@ -215,5 +215,5 @@ class Application implements ResetInterface * @return int 0 if everything went fine, or an error code */ - public function doRun(InputInterface $input, OutputInterface $output) + public function doRun(InputInterface $input, OutputInterface $output): int { if (true === $input->hasParameterOption(['--version', '-V'], true)) { -@@ -454,5 +454,5 @@ class Application implements ResetInterface +@@ -464,5 +464,5 @@ class Application implements ResetInterface * @return string */ - public function getLongVersion() + public function getLongVersion(): string { if ('UNKNOWN' !== $this->getName()) { -@@ -497,5 +497,5 @@ class Application implements ResetInterface +@@ -507,5 +507,5 @@ class Application implements ResetInterface * @return Command|null */ - public function add(Command $command) + public function add(Command $command): ?Command { $this->init(); -@@ -534,5 +534,5 @@ class Application implements ResetInterface +@@ -544,5 +544,5 @@ class Application implements ResetInterface * @throws CommandNotFoundException When given command name does not exist */ - public function get(string $name) + public function get(string $name): Command { $this->init(); -@@ -641,5 +641,5 @@ class Application implements ResetInterface +@@ -651,5 +651,5 @@ class Application implements ResetInterface * @throws CommandNotFoundException When command name is incorrect or ambiguous */ - public function find(string $name) + public function find(string $name): Command { $this->init(); -@@ -751,5 +751,5 @@ class Application implements ResetInterface +@@ -761,5 +761,5 @@ class Application implements ResetInterface * @return Command[] */ - public function all(string $namespace = null) + public function all(string $namespace = null): array { $this->init(); -@@ -950,5 +950,5 @@ class Application implements ResetInterface +@@ -970,5 +970,5 @@ class Application implements ResetInterface * @return int 0 if everything went fine, or an error code */ - protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) @@ -209,24 +198,23 @@ index 64068fcc23..f29aaf1b94 100644 { foreach ($command->getHelperSet() as $helper) { diff --git a/src/Symfony/Component/Console/Command/Command.php b/src/Symfony/Component/Console/Command/Command.php -index b41e691537..34de10fa70 100644 --- a/src/Symfony/Component/Console/Command/Command.php +++ b/src/Symfony/Component/Console/Command/Command.php -@@ -189,5 +189,5 @@ class Command +@@ -192,5 +192,5 @@ class Command * @return bool */ - public function isEnabled() + public function isEnabled(): bool { return true; -@@ -215,5 +215,5 @@ class Command +@@ -218,5 +218,5 @@ class Command * @see setCode() */ - protected function execute(InputInterface $input, OutputInterface $output) + protected function execute(InputInterface $input, OutputInterface $output): int { throw new LogicException('You must override the execute() method in the concrete command class.'); -@@ -684,5 +684,5 @@ class Command +@@ -687,5 +687,5 @@ class Command * @throws InvalidArgumentException if the helper is not defined */ - public function getHelper(string $name): mixed @@ -234,18 +222,16 @@ index b41e691537..34de10fa70 100644 { if (null === $this->helperSet) { diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatter.php b/src/Symfony/Component/Console/Formatter/OutputFormatter.php -index 3c6b0efccd..121664f15a 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatter.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatter.php -@@ -137,5 +137,5 @@ class OutputFormatter implements WrappableOutputFormatterInterface - * {@inheritdoc} - */ +@@ -116,5 +116,5 @@ class OutputFormatter implements WrappableOutputFormatterInterface + } + - public function formatAndWrap(?string $message, int $width) + public function formatAndWrap(?string $message, int $width): string { if (null === $message) { diff --git a/src/Symfony/Component/Console/Formatter/WrappableOutputFormatterInterface.php b/src/Symfony/Component/Console/Formatter/WrappableOutputFormatterInterface.php -index 746cd27e79..52c61429cf 100644 --- a/src/Symfony/Component/Console/Formatter/WrappableOutputFormatterInterface.php +++ b/src/Symfony/Component/Console/Formatter/WrappableOutputFormatterInterface.php @@ -24,4 +24,4 @@ interface WrappableOutputFormatterInterface extends OutputFormatterInterface @@ -255,7 +241,6 @@ index 746cd27e79..52c61429cf 100644 + public function formatAndWrap(?string $message, int $width): string; } diff --git a/src/Symfony/Component/Console/Helper/HelperInterface.php b/src/Symfony/Component/Console/Helper/HelperInterface.php -index 1d2b7bfb84..cb1f66152d 100644 --- a/src/Symfony/Component/Console/Helper/HelperInterface.php +++ b/src/Symfony/Component/Console/Helper/HelperInterface.php @@ -34,4 +34,4 @@ interface HelperInterface @@ -264,8 +249,17 @@ index 1d2b7bfb84..cb1f66152d 100644 - public function getName(); + public function getName(): string; } +diff --git a/src/Symfony/Component/Console/Helper/Table.php b/src/Symfony/Component/Console/Helper/Table.php +--- a/src/Symfony/Component/Console/Helper/Table.php ++++ b/src/Symfony/Component/Console/Helper/Table.php +@@ -193,5 +193,5 @@ class Table + * @return $this + */ +- public function setRows(array $rows) ++ public function setRows(array $rows): static + { + $this->rows = []; diff --git a/src/Symfony/Component/Console/Input/InputInterface.php b/src/Symfony/Component/Console/Input/InputInterface.php -index 3af991a76f..742e2508f3 100644 --- a/src/Symfony/Component/Console/Input/InputInterface.php +++ b/src/Symfony/Component/Console/Input/InputInterface.php @@ -57,5 +57,5 @@ interface InputInterface @@ -290,40 +284,46 @@ index 3af991a76f..742e2508f3 100644 /** diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php -index 2f1631ed30..a4b572771e 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AbstractRecursivePass.php -@@ -71,5 +71,5 @@ abstract class AbstractRecursivePass implements CompilerPassInterface +@@ -68,5 +68,5 @@ abstract class AbstractRecursivePass implements CompilerPassInterface * @return mixed */ - protected function processValue(mixed $value, bool $isRoot = false) + protected function processValue(mixed $value, bool $isRoot = false): mixed { if (\is_array($value)) { +diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php +--- a/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php ++++ b/src/Symfony/Component/DependencyInjection/Compiler/DefinitionErrorExceptionPass.php +@@ -32,5 +32,5 @@ class DefinitionErrorExceptionPass extends AbstractRecursivePass + * @return void + */ +- public function process(ContainerBuilder $container) ++ public function process(ContainerBuilder $container): void + { + try { diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php -index 04b7022484..5d736ec754 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php -@@ -108,5 +108,5 @@ class Container implements ContainerInterface, ResetInterface - * @throws InvalidArgumentException if the parameter is not defined +@@ -110,5 +110,5 @@ class Container implements ContainerInterface, ResetInterface + * @throws ParameterNotFoundException if the parameter is not defined */ - public function getParameter(string $name) + public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null { return $this->parameterBag->get($name); diff --git a/src/Symfony/Component/DependencyInjection/ContainerInterface.php b/src/Symfony/Component/DependencyInjection/ContainerInterface.php -index cad44026c0..14cd192e07 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerInterface.php +++ b/src/Symfony/Component/DependencyInjection/ContainerInterface.php @@ -53,5 +53,5 @@ interface ContainerInterface extends PsrContainerInterface - * @throws InvalidArgumentException if the parameter is not defined + * @throws ParameterNotFoundException if the parameter is not defined */ - public function getParameter(string $name); + public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null; public function hasParameter(string $name): bool; diff --git a/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php b/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php -index a42967f4da..4e86e16f9d 100644 --- a/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php +++ b/src/Symfony/Component/DependencyInjection/Extension/ConfigurationExtensionInterface.php @@ -27,4 +27,4 @@ interface ConfigurationExtensionInterface @@ -333,32 +333,29 @@ index a42967f4da..4e86e16f9d 100644 + public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface; } diff --git a/src/Symfony/Component/DependencyInjection/Extension/Extension.php b/src/Symfony/Component/DependencyInjection/Extension/Extension.php -index d553203c43..1163f4b107 100644 --- a/src/Symfony/Component/DependencyInjection/Extension/Extension.php +++ b/src/Symfony/Component/DependencyInjection/Extension/Extension.php -@@ -32,5 +32,5 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn - * {@inheritdoc} - */ +@@ -29,10 +29,10 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn + private array $processedConfigs = []; + - public function getXsdValidationBasePath() + public function getXsdValidationBasePath(): string|false { return false; -@@ -40,5 +40,5 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn - * {@inheritdoc} - */ + } + - public function getNamespace() + public function getNamespace(): string { return 'http://example.org/schema/dic/'.$this->getAlias(); -@@ -77,5 +77,5 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn - * {@inheritdoc} - */ +@@ -68,5 +68,5 @@ abstract class Extension implements ExtensionInterface, ConfigurationExtensionIn + } + - public function getConfiguration(array $config, ContainerBuilder $container) + public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface { $class = static::class; diff --git a/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php b/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php -index 4f66f18073..e96d867296 100644 --- a/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php +++ b/src/Symfony/Component/DependencyInjection/Extension/ExtensionInterface.php @@ -35,5 +35,5 @@ interface ExtensionInterface @@ -382,7 +379,6 @@ index 4f66f18073..e96d867296 100644 + public function getAlias(): string; } diff --git a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php -index 92c4b44845..ae557d8bca 100644 --- a/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php +++ b/src/Symfony/Component/DependencyInjection/LazyProxy/Instantiator/InstantiatorInterface.php @@ -31,4 +31,4 @@ interface InstantiatorInterface @@ -392,7 +388,6 @@ index 92c4b44845..ae557d8bca 100644 + public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator): object; } diff --git a/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php b/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php -index 2085e428e9..ca0d6964e5 100644 --- a/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php +++ b/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php @@ -46,4 +46,4 @@ interface EventSubscriberInterface @@ -402,7 +397,6 @@ index 2085e428e9..ca0d6964e5 100644 + public static function getSubscribedEvents(): array; } diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionFunctionProviderInterface.php b/src/Symfony/Component/ExpressionLanguage/ExpressionFunctionProviderInterface.php -index 479aeef880..272954c082 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionFunctionProviderInterface.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionFunctionProviderInterface.php @@ -20,4 +20,4 @@ interface ExpressionFunctionProviderInterface @@ -412,17 +406,16 @@ index 479aeef880..272954c082 100644 + public function getFunctions(): array; } diff --git a/src/Symfony/Component/Form/AbstractExtension.php b/src/Symfony/Component/Form/AbstractExtension.php -index 79d61e8bc0..7f34d95d84 100644 --- a/src/Symfony/Component/Form/AbstractExtension.php +++ b/src/Symfony/Component/Form/AbstractExtension.php -@@ -114,5 +114,5 @@ abstract class AbstractExtension implements FormExtensionInterface +@@ -99,5 +99,5 @@ abstract class AbstractExtension implements FormExtensionInterface * @return FormTypeInterface[] */ - protected function loadTypes() + protected function loadTypes(): array { return []; -@@ -134,5 +134,5 @@ abstract class AbstractExtension implements FormExtensionInterface +@@ -119,5 +119,5 @@ abstract class AbstractExtension implements FormExtensionInterface * @return FormTypeGuesserInterface|null */ - protected function loadTypeGuesser() @@ -430,10 +423,9 @@ index 79d61e8bc0..7f34d95d84 100644 { return null; diff --git a/src/Symfony/Component/Form/AbstractRendererEngine.php b/src/Symfony/Component/Form/AbstractRendererEngine.php -index ada182f57b..0d2591f7a7 100644 --- a/src/Symfony/Component/Form/AbstractRendererEngine.php +++ b/src/Symfony/Component/Form/AbstractRendererEngine.php -@@ -137,5 +137,5 @@ abstract class AbstractRendererEngine implements FormRendererEngineInterface, Re +@@ -125,5 +125,5 @@ abstract class AbstractRendererEngine implements FormRendererEngineInterface, Re * @return bool */ - abstract protected function loadResourceForBlockName(string $cacheKey, FormView $view, string $blockName); @@ -441,42 +433,38 @@ index ada182f57b..0d2591f7a7 100644 /** diff --git a/src/Symfony/Component/Form/AbstractType.php b/src/Symfony/Component/Form/AbstractType.php -index 3325b8bc27..1cc22a1dab 100644 --- a/src/Symfony/Component/Form/AbstractType.php +++ b/src/Symfony/Component/Form/AbstractType.php -@@ -52,5 +52,5 @@ abstract class AbstractType implements FormTypeInterface - * {@inheritdoc} - */ +@@ -37,10 +37,10 @@ abstract class AbstractType implements FormTypeInterface + } + - public function getBlockPrefix() + public function getBlockPrefix(): string { return StringUtil::fqcnToBlockPrefix(static::class) ?: ''; -@@ -60,5 +60,5 @@ abstract class AbstractType implements FormTypeInterface - * {@inheritdoc} - */ + } + - public function getParent() + public function getParent(): ?string { return FormType::class; diff --git a/src/Symfony/Component/Form/DataTransformerInterface.php b/src/Symfony/Component/Form/DataTransformerInterface.php -index 8495905e17..1e53be60be 100644 --- a/src/Symfony/Component/Form/DataTransformerInterface.php +++ b/src/Symfony/Component/Form/DataTransformerInterface.php -@@ -60,5 +60,5 @@ interface DataTransformerInterface +@@ -65,5 +65,5 @@ interface DataTransformerInterface * @throws TransformationFailedException when the transformation fails */ - public function transform(mixed $value); + public function transform(mixed $value): mixed; /** -@@ -89,4 +89,4 @@ interface DataTransformerInterface +@@ -96,4 +96,4 @@ interface DataTransformerInterface * @throws TransformationFailedException when the transformation fails */ - public function reverseTransform(mixed $value); + public function reverseTransform(mixed $value): mixed; } diff --git a/src/Symfony/Component/Form/FormRendererEngineInterface.php b/src/Symfony/Component/Form/FormRendererEngineInterface.php -index aa249270a0..3c9d04ff9a 100644 --- a/src/Symfony/Component/Form/FormRendererEngineInterface.php +++ b/src/Symfony/Component/Form/FormRendererEngineInterface.php @@ -131,4 +131,4 @@ interface FormRendererEngineInterface @@ -486,7 +474,6 @@ index aa249270a0..3c9d04ff9a 100644 + public function renderBlock(FormView $view, mixed $resource, string $blockName, array $variables = []): string; } diff --git a/src/Symfony/Component/Form/FormTypeGuesserInterface.php b/src/Symfony/Component/Form/FormTypeGuesserInterface.php -index 61e2c5f80d..4d6b335474 100644 --- a/src/Symfony/Component/Form/FormTypeGuesserInterface.php +++ b/src/Symfony/Component/Form/FormTypeGuesserInterface.php @@ -22,5 +22,5 @@ interface FormTypeGuesserInterface @@ -510,14 +497,13 @@ index 61e2c5f80d..4d6b335474 100644 + public function guessMaxLength(string $class, string $property): ?Guess\ValueGuess; /** -@@ -50,4 +50,4 @@ interface FormTypeGuesserInterface +@@ -43,4 +43,4 @@ interface FormTypeGuesserInterface * @return Guess\ValueGuess|null */ - public function guessPattern(string $class, string $property); + public function guessPattern(string $class, string $property): ?Guess\ValueGuess; } diff --git a/src/Symfony/Component/Form/FormTypeInterface.php b/src/Symfony/Component/Form/FormTypeInterface.php -index 2b9066a511..1c9e9f5a26 100644 --- a/src/Symfony/Component/Form/FormTypeInterface.php +++ b/src/Symfony/Component/Form/FormTypeInterface.php @@ -77,5 +77,5 @@ interface FormTypeInterface @@ -534,7 +520,6 @@ index 2b9066a511..1c9e9f5a26 100644 + public function getParent(): ?string; } diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php -index 1f1740b7e2..22dc8ea26a 100644 --- a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php +++ b/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php @@ -29,4 +29,4 @@ interface CacheWarmerInterface extends WarmableInterface @@ -544,7 +529,6 @@ index 1f1740b7e2..22dc8ea26a 100644 + public function isOptional(): bool; } diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php b/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php -index 2f442cb536..d98909cfae 100644 --- a/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php +++ b/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php @@ -24,4 +24,4 @@ interface WarmableInterface @@ -554,7 +538,6 @@ index 2f442cb536..d98909cfae 100644 + public function warmUp(string $cacheDir): array; } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php -index 3a3be3af49..971560c07b 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -59,5 +59,5 @@ abstract class DataCollector implements DataCollectorInterface @@ -565,7 +548,6 @@ index 3a3be3af49..971560c07b 100644 { $casters = [ diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php -index 1cb865fd66..f6f4efe7a7 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollectorInterface.php @@ -33,4 +33,4 @@ interface DataCollectorInterface extends ResetInterface @@ -575,10 +557,9 @@ index 1cb865fd66..f6f4efe7a7 100644 + public function getName(): string; } diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php -index d97064da8b..d76c73df90 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php -@@ -463,5 +463,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface +@@ -459,5 +459,5 @@ class HttpCache implements HttpKernelInterface, TerminableInterface * @return Response */ - protected function forward(Request $request, bool $catch = false, Response $entry = null) @@ -586,17 +567,16 @@ index d97064da8b..d76c73df90 100644 { $this->surrogate?->addSurrogateCapability($request); diff --git a/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php b/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php -index 1557da575a..7e5a2a9859 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php +++ b/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php -@@ -61,5 +61,5 @@ class HttpKernelBrowser extends AbstractBrowser +@@ -59,5 +59,5 @@ class HttpKernelBrowser extends AbstractBrowser * @return Response */ - protected function doRequest(object $request) + protected function doRequest(object $request): Response { $response = $this->kernel->handle($request, HttpKernelInterface::MAIN_REQUEST, $this->catchExceptions); -@@ -79,5 +79,5 @@ class HttpKernelBrowser extends AbstractBrowser +@@ -75,5 +75,5 @@ class HttpKernelBrowser extends AbstractBrowser * @return string */ - protected function getScript(object $request) @@ -604,17 +584,16 @@ index 1557da575a..7e5a2a9859 100644 { $kernel = var_export(serialize($this->kernel), true); diff --git a/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php b/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php -index 19ff0db181..f0f4a5829f 100644 --- a/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php +++ b/src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php -@@ -30,5 +30,5 @@ interface DebugLoggerInterface - * @return array +@@ -34,5 +34,5 @@ interface DebugLoggerInterface + * }> */ - public function getLogs(Request $request = null); + public function getLogs(Request $request = null): array; /** -@@ -37,5 +37,5 @@ interface DebugLoggerInterface +@@ -41,5 +41,5 @@ interface DebugLoggerInterface * @return int */ - public function countErrors(Request $request = null); @@ -622,7 +601,6 @@ index 19ff0db181..f0f4a5829f 100644 /** diff --git a/src/Symfony/Component/Lock/LockFactory.php b/src/Symfony/Component/Lock/LockFactory.php -index 125b6eae50..ac327e8981 100644 --- a/src/Symfony/Component/Lock/LockFactory.php +++ b/src/Symfony/Component/Lock/LockFactory.php @@ -44,5 +44,5 @@ class LockFactory implements LoggerAwareInterface @@ -640,10 +618,9 @@ index 125b6eae50..ac327e8981 100644 { $lock = new Lock($key, $this->store, $ttl, $autoRelease); diff --git a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsTransport.php b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsTransport.php -index 297fccbd3f..4c47d95b38 100644 --- a/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsTransport.php +++ b/src/Symfony/Component/Messenger/Bridge/AmazonSqs/Transport/AmazonSqsTransport.php -@@ -109,5 +109,5 @@ class AmazonSqsTransport implements TransportInterface, SetupableTransportInterf +@@ -91,5 +91,5 @@ class AmazonSqsTransport implements TransportInterface, SetupableTransportInterf * @return MessageCountAwareInterface&ReceiverInterface */ - private function getReceiver(): ReceiverInterface @@ -651,7 +628,6 @@ index 297fccbd3f..4c47d95b38 100644 { return $this->receiver ??= new AmazonSqsReceiver($this->connection, $this->serializer); diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php -index 205c15b4cd..e93e460ed1 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -486,5 +486,5 @@ class OptionsResolver implements Options @@ -690,63 +666,50 @@ index 205c15b4cd..e93e460ed1 100644 { if ($this->locked) { diff --git a/src/Symfony/Component/PropertyAccess/PropertyPathInterface.php b/src/Symfony/Component/PropertyAccess/PropertyPathInterface.php -index fbb37d9f94..522e0487a9 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyPathInterface.php +++ b/src/Symfony/Component/PropertyAccess/PropertyPathInterface.php -@@ -31,5 +31,5 @@ interface PropertyPathInterface extends \Traversable +@@ -33,5 +33,5 @@ interface PropertyPathInterface extends \Traversable * @return int */ - public function getLength(); + public function getLength(): int; /** -@@ -43,5 +43,5 @@ interface PropertyPathInterface extends \Traversable +@@ -45,5 +45,5 @@ interface PropertyPathInterface extends \Traversable * @return self|null */ - public function getParent(); + public function getParent(): ?\Symfony\Component\PropertyAccess\PropertyPathInterface; /** -@@ -50,5 +50,5 @@ interface PropertyPathInterface extends \Traversable +@@ -52,5 +52,5 @@ interface PropertyPathInterface extends \Traversable * @return list */ - public function getElements(); + public function getElements(): array; /** -@@ -61,5 +61,5 @@ interface PropertyPathInterface extends \Traversable +@@ -63,5 +63,5 @@ interface PropertyPathInterface extends \Traversable * @throws Exception\OutOfBoundsException If the offset is invalid */ - public function getElement(int $index); + public function getElement(int $index): string; /** -@@ -72,5 +72,5 @@ interface PropertyPathInterface extends \Traversable +@@ -74,5 +74,5 @@ interface PropertyPathInterface extends \Traversable * @throws Exception\OutOfBoundsException If the offset is invalid */ - public function isProperty(int $index); + public function isProperty(int $index): bool; /** -@@ -83,4 +83,4 @@ interface PropertyPathInterface extends \Traversable +@@ -85,4 +85,4 @@ interface PropertyPathInterface extends \Traversable * @throws Exception\OutOfBoundsException If the offset is invalid */ - public function isIndex(int $index); + public function isIndex(int $index): bool; } -diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php -index 38e563e177..ee9c00a1db 100644 ---- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php -+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php -@@ -742,5 +742,5 @@ class PropertyAccessorTest extends TestCase - * @return mixed - */ -- public function getFoo() -+ public function getFoo(): mixed - { - return $this->foo; diff --git a/src/Symfony/Component/PropertyInfo/PropertyAccessExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyAccessExtractorInterface.php -index f9ee787130..61f8b6d5be 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyAccessExtractorInterface.php +++ b/src/Symfony/Component/PropertyInfo/PropertyAccessExtractorInterface.php @@ -24,5 +24,5 @@ interface PropertyAccessExtractorInterface @@ -763,7 +726,6 @@ index f9ee787130..61f8b6d5be 100644 + public function isWritable(string $class, string $property, array $context = []): ?bool; } diff --git a/src/Symfony/Component/PropertyInfo/PropertyListExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyListExtractorInterface.php -index 326e6cccb3..ae7c6b612b 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyListExtractorInterface.php +++ b/src/Symfony/Component/PropertyInfo/PropertyListExtractorInterface.php @@ -24,4 +24,4 @@ interface PropertyListExtractorInterface @@ -773,7 +735,6 @@ index 326e6cccb3..ae7c6b612b 100644 + public function getProperties(string $class, array $context = []): ?array; } diff --git a/src/Symfony/Component/PropertyInfo/PropertyTypeExtractorInterface.php b/src/Symfony/Component/PropertyInfo/PropertyTypeExtractorInterface.php -index 6da0bcb4c8..16e9765b1d 100644 --- a/src/Symfony/Component/PropertyInfo/PropertyTypeExtractorInterface.php +++ b/src/Symfony/Component/PropertyInfo/PropertyTypeExtractorInterface.php @@ -24,4 +24,4 @@ interface PropertyTypeExtractorInterface @@ -783,10 +744,9 @@ index 6da0bcb4c8..16e9765b1d 100644 + public function getTypes(string $class, string $property, array $context = []): ?array; } diff --git a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php -index 5f3a852d32..65a18bd924 100644 --- a/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AnnotationClassLoader.php -@@ -253,5 +253,5 @@ abstract class AnnotationClassLoader implements LoaderInterface +@@ -244,5 +244,5 @@ abstract class AnnotationClassLoader implements LoaderInterface * @return string */ - protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) @@ -794,18 +754,16 @@ index 5f3a852d32..65a18bd924 100644 { $name = str_replace('\\', '_', $class->name).'_'.$method->name; diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php -index 1142fc9714..9e4965ce06 100644 --- a/src/Symfony/Component/Routing/Router.php +++ b/src/Symfony/Component/Routing/Router.php -@@ -178,5 +178,5 @@ class Router implements RouterInterface, RequestMatcherInterface - * {@inheritdoc} - */ +@@ -175,5 +175,5 @@ class Router implements RouterInterface, RequestMatcherInterface + } + - public function getRouteCollection() + public function getRouteCollection(): RouteCollection { - if (null === $this->collection) { + return $this->collection ??= $this->loader->load($this->resource, $this->options['resource_type']); diff --git a/src/Symfony/Component/Routing/RouterInterface.php b/src/Symfony/Component/Routing/RouterInterface.php -index 6912f8a15b..caf18c886a 100644 --- a/src/Symfony/Component/Routing/RouterInterface.php +++ b/src/Symfony/Component/Routing/RouterInterface.php @@ -32,4 +32,4 @@ interface RouterInterface extends UrlMatcherInterface, UrlGeneratorInterface @@ -815,7 +773,6 @@ index 6912f8a15b..caf18c886a 100644 + public function getRouteCollection(): RouteCollection; } diff --git a/src/Symfony/Component/Security/Core/Authentication/RememberMe/TokenProviderInterface.php b/src/Symfony/Component/Security/Core/Authentication/RememberMe/TokenProviderInterface.php -index 9b32fdce31..fbbd65d8b7 100644 --- a/src/Symfony/Component/Security/Core/Authentication/RememberMe/TokenProviderInterface.php +++ b/src/Symfony/Component/Security/Core/Authentication/RememberMe/TokenProviderInterface.php @@ -28,5 +28,5 @@ interface TokenProviderInterface @@ -826,7 +783,6 @@ index 9b32fdce31..fbbd65d8b7 100644 /** diff --git a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php -index ba52c8ea65..e879a84982 100644 --- a/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php +++ b/src/Symfony/Component/Security/Core/Authorization/Voter/VoterInterface.php @@ -37,4 +37,4 @@ interface VoterInterface @@ -836,7 +792,6 @@ index ba52c8ea65..e879a84982 100644 + public function vote(TokenInterface $token, mixed $subject, array $attributes): int; } diff --git a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php -index 298bc78cd9..3ef4176dd8 100644 --- a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php +++ b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php @@ -89,5 +89,5 @@ class AuthenticationException extends RuntimeException @@ -847,7 +802,6 @@ index 298bc78cd9..3ef4176dd8 100644 { return 'An authentication exception occurred.'; diff --git a/src/Symfony/Component/Security/Core/User/UserProviderInterface.php b/src/Symfony/Component/Security/Core/User/UserProviderInterface.php -index ec90d413fa..9f1401aa91 100644 --- a/src/Symfony/Component/Security/Core/User/UserProviderInterface.php +++ b/src/Symfony/Component/Security/Core/User/UserProviderInterface.php @@ -45,5 +45,5 @@ interface UserProviderInterface @@ -865,7 +819,6 @@ index ec90d413fa..9f1401aa91 100644 /** diff --git a/src/Symfony/Component/Security/Http/EntryPoint/AuthenticationEntryPointInterface.php b/src/Symfony/Component/Security/Http/EntryPoint/AuthenticationEntryPointInterface.php -index 91271d14a3..100c2fb549 100644 --- a/src/Symfony/Component/Security/Http/EntryPoint/AuthenticationEntryPointInterface.php +++ b/src/Symfony/Component/Security/Http/EntryPoint/AuthenticationEntryPointInterface.php @@ -43,4 +43,4 @@ interface AuthenticationEntryPointInterface @@ -875,18 +828,16 @@ index 91271d14a3..100c2fb549 100644 + public function start(Request $request, AuthenticationException $authException = null): Response; } diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php -index 0c313f8f09..acfc9f4b88 100644 --- a/src/Symfony/Component/Security/Http/Firewall.php +++ b/src/Symfony/Component/Security/Http/Firewall.php -@@ -106,5 +106,5 @@ class Firewall implements EventSubscriberInterface - * {@inheritdoc} - */ +@@ -103,5 +103,5 @@ class Firewall implements EventSubscriberInterface + } + - public static function getSubscribedEvents() + public static function getSubscribedEvents(): array { return [ diff --git a/src/Symfony/Component/Security/Http/FirewallMapInterface.php b/src/Symfony/Component/Security/Http/FirewallMapInterface.php -index 480ea8ad6b..fa43d6a6e9 100644 --- a/src/Symfony/Component/Security/Http/FirewallMapInterface.php +++ b/src/Symfony/Component/Security/Http/FirewallMapInterface.php @@ -38,4 +38,4 @@ interface FirewallMapInterface @@ -896,7 +847,6 @@ index 480ea8ad6b..fa43d6a6e9 100644 + public function getListeners(Request $request): array; } diff --git a/src/Symfony/Component/Serializer/Encoder/DecoderInterface.php b/src/Symfony/Component/Serializer/Encoder/DecoderInterface.php -index 5014b9bd51..757c76f546 100644 --- a/src/Symfony/Component/Serializer/Encoder/DecoderInterface.php +++ b/src/Symfony/Component/Serializer/Encoder/DecoderInterface.php @@ -35,5 +35,5 @@ interface DecoderInterface @@ -906,31 +856,30 @@ index 5014b9bd51..757c76f546 100644 + public function decode(string $data, string $format, array $context = []): mixed; /** -@@ -45,4 +45,4 @@ interface DecoderInterface +@@ -44,4 +44,4 @@ interface DecoderInterface * @return bool */ -- public function supportsDecoding(string $format /* , array $context = [] */); -+ public function supportsDecoding(string $format /* , array $context = [] */): bool; +- public function supportsDecoding(string $format); ++ public function supportsDecoding(string $format): bool; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php -index 391cdcb39c..f637687e74 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php -@@ -213,5 +213,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn - * @return string[]|AttributeMetadataInterface[]|bool +@@ -210,5 +210,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn + * @throws LogicException if the 'allow_extra_attributes' context variable is false and no class metadata factory is provided */ - protected function getAllowedAttributes(string|object $classOrObject, array $context, bool $attributesAsString = false) + protected function getAllowedAttributes(string|object $classOrObject, array $context, bool $attributesAsString = false): array|bool { $allowExtraAttributes = $context[self::ALLOW_EXTRA_ATTRIBUTES] ?? $this->defaultContext[self::ALLOW_EXTRA_ATTRIBUTES]; -@@ -263,5 +263,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn +@@ -260,5 +260,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn * @return bool */ - protected function isAllowedAttribute(object|string $classOrObject, string $attribute, string $format = null, array $context = []) + protected function isAllowedAttribute(object|string $classOrObject, string $attribute, string $format = null, array $context = []): bool { $ignoredAttributes = $context[self::IGNORED_ATTRIBUTES] ?? $this->defaultContext[self::IGNORED_ATTRIBUTES]; -@@ -314,5 +314,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn +@@ -311,5 +311,5 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn * @throws MissingConstructorArgumentsException */ - protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, string $format = null) @@ -938,60 +887,55 @@ index 391cdcb39c..f637687e74 100644 { if (null !== $object = $this->extractObjectToPopulate($class, $context, self::OBJECT_TO_POPULATE)) { diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php -index 714fb10e30..ea3f4428bf 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php -@@ -139,5 +139,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer +@@ -139,10 +139,10 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer * @param array $context */ - public function supportsNormalization(mixed $data, string $format = null /* , array $context = [] */) + public function supportsNormalization(mixed $data, string $format = null /* , array $context = [] */): bool { return \is_object($data) && !$data instanceof \Traversable; -@@ -147,5 +147,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer - * {@inheritdoc} - */ + } + - public function normalize(mixed $object, string $format = null, array $context = []) + public function normalize(mixed $object, string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null { if (!isset($context['cache_key'])) { -@@ -230,5 +230,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer - * {@inheritdoc} - */ +@@ -225,5 +225,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer + } + - protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, string $format = null) + protected function instantiateObject(array &$data, string $class, array &$context, \ReflectionClass $reflectionClass, array|bool $allowedAttributes, string $format = null): object { - if ($this->classDiscriminatorResolver && $mapping = $this->classDiscriminatorResolver->getMappingForClass($class)) { -@@ -292,5 +292,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer + if ($class !== $mappedClass = $this->getMappedClass($data, $class, $context)) { +@@ -276,5 +276,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer * @return string[] */ - abstract protected function extractAttributes(object $object, string $format = null, array $context = []); + abstract protected function extractAttributes(object $object, string $format = null, array $context = []): array; /** -@@ -299,5 +299,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer +@@ -283,15 +283,15 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer * @return mixed */ - abstract protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = []); + abstract protected function getAttributeValue(object $object, string $attribute, string $format = null, array $context = []): mixed; /** -@@ -306,5 +306,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer * @param array $context */ - public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */) + public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */): bool { return class_exists($type) || (interface_exists($type, false) && $this->classDiscriminatorResolver && null !== $this->classDiscriminatorResolver->getMappingForClass($type)); -@@ -314,5 +314,5 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer - * {@inheritdoc} - */ + } + - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + public function denormalize(mixed $data, string $type, string $format = null, array $context = []): mixed { if (!isset($context['cache_key'])) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php -index ae3adbfe33..3a38429cf1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php @@ -45,5 +45,5 @@ interface DenormalizerInterface @@ -1008,7 +952,6 @@ index ae3adbfe33..3a38429cf1 100644 + public function supportsDenormalization(mixed $data, string $type, string $format = null /* , array $context = [] */): bool; } diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php -index 691e9c70f0..fc87f672e1 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php @@ -37,5 +37,5 @@ interface NormalizerInterface @@ -1025,7 +968,6 @@ index 691e9c70f0..fc87f672e1 100644 + public function supportsNormalization(mixed $data, string $format = null /* , array $context = [] */): bool; } diff --git a/src/Symfony/Component/Templating/Helper/HelperInterface.php b/src/Symfony/Component/Templating/Helper/HelperInterface.php -index 5dade65db5..db0d0a00ea 100644 --- a/src/Symfony/Component/Templating/Helper/HelperInterface.php +++ b/src/Symfony/Component/Templating/Helper/HelperInterface.php @@ -24,5 +24,5 @@ interface HelperInterface @@ -1036,7 +978,6 @@ index 5dade65db5..db0d0a00ea 100644 /** diff --git a/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php -index 4c088b94f9..86107a636d 100644 --- a/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php +++ b/src/Symfony/Component/Translation/Extractor/AbstractFileExtractor.php @@ -59,9 +59,9 @@ abstract class AbstractFileExtractor @@ -1052,7 +993,6 @@ index 4c088b94f9..86107a636d 100644 + abstract protected function extractFromDirectory(string|array $resource): iterable; } diff --git a/src/Symfony/Component/Validator/Constraint.php b/src/Symfony/Component/Validator/Constraint.php -index ee1d68c78f..9baaabb04c 100644 --- a/src/Symfony/Component/Validator/Constraint.php +++ b/src/Symfony/Component/Validator/Constraint.php @@ -254,5 +254,5 @@ abstract class Constraint @@ -1083,8 +1023,17 @@ index ee1d68c78f..9baaabb04c 100644 + public function getTargets(): string|array { return self::PROPERTY_CONSTRAINT; +diff --git a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php +--- a/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php ++++ b/src/Symfony/Component/Validator/Test/ConstraintValidatorTestCase.php +@@ -303,5 +303,5 @@ abstract class ConstraintValidatorTestCase extends TestCase + * @psalm-return T + */ +- abstract protected function createValidator(); ++ abstract protected function createValidator(): ConstraintValidatorInterface; + } + diff --git a/src/Symfony/Component/VarExporter/Internal/Exporter.php b/src/Symfony/Component/VarExporter/Internal/Exporter.php -index b22d6ae609..31d1a25f9d 100644 --- a/src/Symfony/Component/VarExporter/Internal/Exporter.php +++ b/src/Symfony/Component/VarExporter/Internal/Exporter.php @@ -36,5 +36,5 @@ class Exporter @@ -1094,19 +1043,7 @@ index b22d6ae609..31d1a25f9d 100644 + public static function prepare($values, $objectsPool, &$refsPool, &$objectsCount, &$valuesAreStatic): array { $refs = $values; -diff --git a/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php b/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php -index 471c1a6b91..2e19d2ab2d 100644 ---- a/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php -+++ b/src/Symfony/Component/VarExporter/Internal/GhostObjectState.php -@@ -54,5 +54,5 @@ class GhostObjectState - * @return bool Returns true when fully-initializing, false when partial-initializing - */ -- public function initialize($instance, $propertyName, $propertyScope) -+ public function initialize($instance, $propertyName, $propertyScope): bool - { - if (!$this->status) { diff --git a/src/Symfony/Component/Workflow/Event/Event.php b/src/Symfony/Component/Workflow/Event/Event.php -index cd7fab7896..b340eba38e 100644 --- a/src/Symfony/Component/Workflow/Event/Event.php +++ b/src/Symfony/Component/Workflow/Event/Event.php @@ -42,5 +42,5 @@ class Event extends BaseEvent diff --git a/.github/patch-types.php b/.github/patch-types.php index a5ff5b3293c15..6ae4b295c00b7 100644 --- a/.github/patch-types.php +++ b/.github/patch-types.php @@ -12,8 +12,15 @@ Symfony\Component\ErrorHandler\DebugClassLoader::enable(); foreach ($loader->getClassMap() as $class => $file) { + $file = realpath($file); + switch (true) { - case false !== strpos($file = realpath($file), '/vendor/'): + case false !== strpos($file, '/src/Symfony/Component/Cache/Traits/Redis'): + if (!str_ends_with($file, 'Proxy.php')) { + break; + } + // no break; + case false !== strpos($file, '/vendor/'): case false !== strpos($file, '/src/Symfony/Bridge/PhpUnit/'): case false !== strpos($file, '/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Validation/Article.php'): case false !== strpos($file, '/src/Symfony/Component/Cache/Tests/Fixtures/DriverWrapper.php'): @@ -46,6 +53,10 @@ case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/NotLoadableClass.php'): case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/ReflectionIntersectionTypeFixture.php'): case false !== strpos($file, '/src/Symfony/Component/VarDumper/Tests/Fixtures/ReflectionUnionTypeWithIntersectionFixture.php'): + case false !== strpos($file, '/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyGhost/ReadOnlyClass.php'): + case false !== strpos($file, '/src/Symfony/Component/VarExporter/Tests/Fixtures/LazyProxy/ReadOnlyClass.php'): + case false !== strpos($file, '/src/Symfony/Contracts/Service/Test/ServiceLocatorTest.php'): + case false !== strpos($file, '/src/Symfony/Contracts/Service/Test/ServiceLocatorTestCase.php'): continue 2; } diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 2d2ec7a1c271e..cf7eca61aaa21 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -18,16 +18,17 @@ permissions: jobs: tests: - name: Tests + name: Integration runs-on: Ubuntu-20.04 strategy: matrix: php: ['8.1'] + fail-fast: false services: postgres: - image: postgres:9.6-alpine + image: postgres:10.6-alpine ports: - 5432:5432 env: @@ -44,11 +45,11 @@ jobs: LDAP_USERS: a LDAP_PASSWORDS: a redis: - image: redis:6.0.0 + image: redis:6.2.8 ports: - 16379:6379 redis-cluster: - image: grokzen/redis-cluster:5.0.4 + image: grokzen/redis-cluster:6.2.8 ports: - 7000:7000 - 7001:7001 @@ -60,7 +61,7 @@ jobs: env: STANDALONE: 1 redis-sentinel: - image: bitnami/redis-sentinel:6.0 + image: bitnami/redis-sentinel:6.2.8 ports: - 26379:26379 env: @@ -106,7 +107,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install system dependencies run: | @@ -134,8 +135,8 @@ jobs: uses: shivammathur/setup-php@v2 with: coverage: "none" - extensions: "json,couchbase-3.2.2,memcached,mongodb-1.12.0,redis-5.3.4,rdkafka,xsl,ldap" - ini-values: date.timezone=Europe/Paris,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 + extensions: "json,couchbase-3.2.2,memcached,mongodb-1.12.0,redis,rdkafka,xsl,ldap" + ini-values: date.timezone=UTC,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 php-version: "${{ matrix.php }}" tools: pecl @@ -158,7 +159,7 @@ jobs: echo COMPOSER_ROOT_VERSION=$COMPOSER_ROOT_VERSION >> $GITHUB_ENV echo "::group::composer update" - composer require --dev --no-update mongodb/mongodb:"^1.11" + composer require --dev --no-update mongodb/mongodb composer update --no-progress --ansi echo "::endgroup::" diff --git a/.github/workflows/intl-data-tests.yml b/.github/workflows/intl-data-tests.yml index 72271b73638c2..58e1aa80ae0bd 100644 --- a/.github/workflows/intl-data-tests.yml +++ b/.github/workflows/intl-data-tests.yml @@ -3,10 +3,18 @@ name: Intl data on: push: paths: + - 'src/Symfony/Component/Intl/*.php' + - 'src/Symfony/Component/Intl/Util/GitRepository.php' - 'src/Symfony/Component/Intl/Resources/data/**' + - 'src/Symfony/Component/Intl/Tests/*Test.php' + - 'src/Symfony/Component/Intl/Tests/Util/GitRepositoryTest.php' pull_request: paths: + - 'src/Symfony/Component/Intl/*.php' + - 'src/Symfony/Component/Intl/Util/GitRepository.php' - 'src/Symfony/Component/Intl/Resources/data/**' + - 'src/Symfony/Component/Intl/Tests/*Test.php' + - 'src/Symfony/Component/Intl/Tests/Util/GitRepositoryTest.php' defaults: run: @@ -21,12 +29,12 @@ permissions: jobs: tests: - name: Tests + name: Intl data runs-on: Ubuntu-20.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install system dependencies run: | diff --git a/.github/workflows/package-tests.yml b/.github/workflows/package-tests.yml index 42ac6300b2118..dd2bf4c61d915 100644 --- a/.github/workflows/package-tests.yml +++ b/.github/workflows/package-tests.yml @@ -1,4 +1,4 @@ -name: Package +name: Verify Packages on: pull_request: @@ -10,7 +10,7 @@ permissions: jobs: verify: - name: Verify + name: Verify Packages runs-on: Ubuntu-20.04 steps: - name: Checkout code @@ -21,7 +21,7 @@ jobs: - name: Find packages id: find-packages - run: echo "::set-output name=packages::$(php .github/get-modified-packages.php $(find src/Symfony -mindepth 2 -type f -name composer.json -printf '%h\n' | grep -v src/Symfony/Component/Intl/Resources/emoji |jq -R -s -c 'split("\n")[:-1]') $(git diff --name-only origin/${{ github.base_ref }} HEAD | grep src/ | jq -R -s -c 'split("\n")[:-1]'))" + run: echo "packages=$(php .github/get-modified-packages.php $(find src/Symfony -mindepth 2 -type f -name composer.json -printf '%h\n' | grep -v src/Symfony/Component/Intl/Resources/emoji |jq -R -s -c 'split("\n")[:-1]') $(git diff --name-only origin/${{ github.base_ref }} HEAD | grep src/ | jq -R -s -c 'split("\n")[:-1]'))" >> $GITHUB_OUTPUT - name: Verify meta files are correct run: | @@ -42,7 +42,7 @@ jobs: } _correct_license_file() { - FIRST_LINE="Copyright (c) $(date +"%Y") Fabien Potencier" + FIRST_LINE="Copyright (c) $(date +"%Y")-present Fabien Potencier" PACKAGE_FIRST_LINE=$(head -1 ${1}) if [[ "$FIRST_LINE" != "$PACKAGE_FIRST_LINE" ]]; then echo "First line of the license file is wrong. Maybe it is the wrong year?" diff --git a/.github/workflows/phpunit-bridge.yml b/.github/workflows/phpunit-bridge.yml index 4fe5a34c703c7..2229bbc866655 100644 --- a/.github/workflows/phpunit-bridge.yml +++ b/.github/workflows/phpunit-bridge.yml @@ -1,4 +1,4 @@ -name: PhpUnitBridge +name: Lint PhpUnitBridge on: push: @@ -21,12 +21,12 @@ permissions: jobs: lint: - name: Lint + name: Lint PhpUnitBridge runs-on: Ubuntu-20.04 steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index 771aae12a4f0f..9f455cb6f8d2f 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -1,4 +1,4 @@ -name: Static analysis +name: Psalm on: pull_request: ~ @@ -19,22 +19,23 @@ jobs: name: Psalm runs-on: Ubuntu-20.04 + env: + php-version: '8.1' steps: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: '8.1' - extensions: "json,couchbase,memcached,mongodb,redis,xsl,ldap,dom" + php-version: ${{ env.php-version }} ini-values: "memory_limit=-1" coverage: none - name: Checkout target branch - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.base_ref }} - name: Checkout PR - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install dependencies run: | @@ -42,13 +43,15 @@ jobs: ([ -d "$COMPOSER_HOME" ] || mkdir "$COMPOSER_HOME") && cp .github/composer-config.json "$COMPOSER_HOME/config.json" export COMPOSER_ROOT_VERSION=$(grep ' VERSION = ' src/Symfony/Component/HttpKernel/Kernel.php | grep -P -o '[0-9]+\.[0-9]+').x-dev composer remove --dev --no-update --no-interaction symfony/phpunit-bridge - composer require --no-progress --ansi psalm/phar phpunit/phpunit:^9.5 php-http/discovery psr/event-dispatcher mongodb/mongodb + composer require --no-progress --ansi --no-plugins psalm/phar phpunit/phpunit:^9.5 php-http/discovery psr/event-dispatcher mongodb/mongodb jetbrains/phpstorm-stubs - name: Generate Psalm baseline run: | git checkout composer.json git checkout -m ${{ github.base_ref }} + # to be removed when psalm adds support for intersection types + sed -i 's/Uuid&/Uuid|/' src/Symfony/Component/Uid/Factory/TimeBasedUuidFactory.php ./vendor/bin/psalm.phar --set-baseline=.github/psalm/psalm.baseline.xml --no-progress git checkout -m FETCH_HEAD diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 7ee945d432674..b66e1b53b60f4 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -6,7 +6,7 @@ on: schedule: - cron: '34 4 * * 6' push: - branches: [ "6.2" ] + branches: [ "6.4" ] # Declare default permissions as read only. permissions: read-all @@ -23,7 +23,7 @@ jobs: # Needs for private repositories. contents: read actions: read - + steps: - name: "Checkout code" uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0 @@ -42,8 +42,8 @@ jobs: # repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} # Publish the results for public repositories to enable scorecard badges. For more details, see - # https://github.com/ossf/scorecard-action#publishing-results. - # For private repositories, `publish_results` will automatically be set to `false`, regardless + # https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories, `publish_results` will automatically be set to `false`, regardless # of the value entered here. publish_results: true @@ -55,7 +55,7 @@ jobs: name: SARIF file path: results.sarif retention-days: 5 - + # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" uses: github/codeql-action/upload-sarif@5f532563584d71fdef14ee64d17bafb34f751ce5 # v1.0.26 diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 81c1cf0fa7815..2f57845559044 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,4 +1,4 @@ -name: PHPUnit +name: Unit Tests on: push: @@ -18,20 +18,20 @@ permissions: jobs: tests: - name: Tests + name: Unit Tests env: - extensions: amqp,apcu,igbinary,intl,mbstring,memcached,redis-5.3.4 + extensions: amqp,apcu,igbinary,intl,mbstring,memcached,redis strategy: matrix: include: - php: '8.1' - - php: '8.1' + - php: '8.2' mode: high-deps - - php: '8.1' - mode: low-deps - php: '8.2' + mode: low-deps + - php: '8.3' #mode: experimental fail-fast: false @@ -39,20 +39,15 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 2 - - name: Configure for PHP >= 8.2 - if: "matrix.php >= '8.2'" - run: | - composer config platform.php 8.1.99 - - name: Setup PHP uses: shivammathur/setup-php@v2 with: coverage: "none" - ini-values: date.timezone=Europe/Paris,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 + ini-values: date.timezone=UTC,memory_limit=-1,default_socket_timeout=10,session.gc_probability=0,apc.enable_cli=1,zend.assertions=1 php-version: "${{ matrix.php }}" extensions: "${{ env.extensions }}" tools: flex @@ -69,7 +64,7 @@ jobs: echo COLUMNS=120 >> $GITHUB_ENV echo PHPUNIT="$(pwd)/phpunit --exclude-group tty,benchmark,intl-data,integration" >> $GITHUB_ENV - echo COMPOSER_UP='composer update --no-progress --ansi' >> $GITHUB_ENV + echo COMPOSER_UP='composer update --no-progress --ansi'$([[ "${{ matrix.mode }}" != low-deps ]] && echo ' --ignore-platform-req=php+') >> $GITHUB_ENV SYMFONY_VERSIONS=$(git ls-remote -q --heads | cut -f2 | grep -o '/[1-9][0-9]*\.[0-9].*' | sort -V) SYMFONY_VERSION=$(grep ' VERSION = ' src/Symfony/Component/HttpKernel/Kernel.php | cut -d "'" -f2 | cut -d '.' -f 1-2) @@ -128,7 +123,7 @@ jobs: echo SYMFONY_VERSION=$SYMFONY_VERSION >> $GITHUB_ENV echo COMPOSER_ROOT_VERSION=$SYMFONY_VERSION.x-dev >> $GITHUB_ENV - echo SYMFONY_REQUIRE=">=$([ '${{ matrix.mode }}' = low-deps ] && echo 3.4 || echo $SYMFONY_VERSION)" >> $GITHUB_ENV + echo SYMFONY_REQUIRE=">=$([ '${{ matrix.mode }}' = low-deps ] && echo 4.4 || echo $SYMFONY_VERSION)" >> $GITHUB_ENV [[ "${{ matrix.mode }}" = *-deps ]] && mv composer.json.phpunit composer.json || true - name: Install dependencies diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 2bc2c558d7b9b..db6a854c1f398 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -31,8 +31,10 @@ 'protected_to_private' => false, 'native_constant_invocation' => ['strict' => false], 'nullable_type_declaration_for_default_null_value' => ['use_nullable_type_declaration' => false], + 'no_superfluous_phpdoc_tags' => ['remove_inheritdoc' => true], 'header_comment' => ['header' => $fileHeaderComment], 'modernize_strpos' => true, + 'get_class_to_class_keyword' => true, ]) ->setRiskyAllowed(true) ->setFinder( @@ -64,6 +66,11 @@ ->notPath('Symfony/Component/ErrorHandler/Tests/DebugClassLoaderTest.php') // stop removing spaces on the end of the line in strings ->notPath('Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php') + // auto-generated proxies + ->notPath('Symfony/Component/Cache/Traits/Redis5Proxy.php') + ->notPath('Symfony/Component/Cache/Traits/Redis6Proxy.php') + ->notPath('Symfony/Component/Cache/Traits/RedisCluster5Proxy.php') + ->notPath('Symfony/Component/Cache/Traits/RedisCluster6Proxy.php') ) ->setCacheFile('.php-cs-fixer.cache') ; diff --git a/CHANGELOG-6.0.md b/CHANGELOG-6.0.md index 54219602ca6bb..07005e3077334 100644 --- a/CHANGELOG-6.0.md +++ b/CHANGELOG-6.0.md @@ -7,6 +7,236 @@ in 6.0 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v6.0.0...v6.0.1 +* 6.0.19 (2023-01-24) + + * bug #49078 [Security/Http] Check tokens before loading users from providers (nicolas-grekas) + * bug #49077 [DependencyInjection] Fix named arguments when using ContainerBuilder before compilation (nicolas-grekas) + * bug #49031 [Cache] fix collecting cache stats when nesting computations (nicolas-grekas) + * bug #49046 Fix for Windows when projects are deployed on junctions/symlinks (nerdgod) + * bug #49025 [Notifier] [OvhCloud] handle invalid receiver (seferov) + * bug #48993 [VarDumper] Fix JS to expand / collapse (nicolas-grekas) + * bug #48983 Fix BC user_identifier support after deprecation username (vtsykun) + * bug #48986 [Validator] Fix Email validator logic (fabpot) + * bug #48969 [PropertyInfo] Fixes constructor extractor for mixed type (michael.kubovic) + * bug #48978 [Serializer] use method_exists() instead of catching reflection exceptions (xabbuh) + * bug #48937 [SecurityBundle] Fix using same handler for multiple authenticators (RobertMe) + * bug #48971 [DependencyInjection] Fix dump order of inlined deps (nicolas-grekas) + * bug #48966 [HttpClient] Let curl handle content-length headers (nicolas-grekas) + * bug #48968 [VarExporter] Fix exporting enums (nicolas-grekas) + * bug #48926 [DependencyInjection] Fix support for named arguments on non-autowired services (nicolas-grekas) + * bug #48943 [FrameworkBundle] Fix deprecation when accessing a "container.private" service from the test container (nicolas-grekas) + * bug #48931 [DependencyInjection] Fix dumping inlined withers (nicolas-grekas) + * bug #48898 [HttpClient] Move Http clients data collecting at a late level (pforesi) + * bug #48896 [DoctrineBridge] Fix detecting mapping with one line annotations (franmomu) + * bug #48916 [FrameworkBundle] restore call to addGlobalIgnoredName (alexislefebvre) + * bug #48917 [Config] Fix XML dump when node example is an array (alexandre-daubois) + * bug #48904 [Validator] Allow egulias/email-validator v4 (chalasr) + * bug #48831 [Uid] Fix validating nil and max uuid (fancyweb) + +* 6.0.18 (2022-12-29) + + * bug #48823 [Cache] Fix possibly null value passed to preg_match() in RedisTrait (chalasr) + * bug #48816 [Cache] Fix for RedisAdapter without auth parameter (rikvdh) + +* 6.0.17 (2022-12-28) + + * bug #48787 [PhpUnitBridge] Use verbose deprecation output for quiet types only when it reaches the threshold (ogizanagi) + * bug #48784 [Console] Correctly overwrite progressbars with different line count per step (ncharalampidis) + * bug #48801 [Form] Make `ButtonType` handle `form_attr` option (MatTheCat) + * bug #48791 [DependencyInjection] Fix deduplicating service instances in circular graphs (nicolas-grekas) + * bug #48771 [CssSelector] Fix escape patterns (fancyweb) + * bug #48711 [Cache] RedisTrait::createConnection does not pass auth value from redis sentinel cluster DSN (evgkord) + * bug #48724 [VarExporter] Fix exporting classes with __unserialize() but not __serialize() (fancyweb) + * bug #48746 [Validator] Fix IBAN format for Tunisia and Mauritania (smelesh) + * bug #48738 [Workflow] Allow spaces in place names so the PUML dump doesn't break (Kamil Musial) + * bug #48718 Compatibility with doctrine/annotations 2 (derrabus) + * bug #48651 [HttpKernel] AbstractSessionListener should not override the cache lifetime for private responses (rodmen) + * bug #48591 [DependencyInjection] Shared private services becomes public after a public service is accessed (alexpott) + * bug #48126 [Mailer] Include all transports' debug messages in RoundRobin transport exception (mixdf) + * bug #48635 [HttpFoundation] Use relative timestamps with MemcachedSessionHandler (tvlooy) + * bug #47979 [Cache] Fix dealing with ext-redis' multi/exec returning a bool (João Nogueira) + * bug #48612 [Messenger] [Amqp] Added missing rpc_timeout option (lyrixx) + * bug #48233 [Serializer] Prevent `GetSetMethodNormalizer` from creating invalid magic method call (klaussilveira) + * bug #48628 [HttpFoundation] Fix dumping array cookies (nicolas-grekas) + * bug #48048 [WebProfilerBundle] Fix dump header not being displayed (HypeMC) + * bug #47836 [HttpClient] TraceableHttpClient: increase decorator's priority (adpeyre) + * bug #48259 [FrameworkBundle] Allow configuring `framework.exceptions` with a config builder (MatTheCat) + * bug #48314 [Mime] Fix MessagePart serialization (Amunak) + * bug #48331 [Yaml] fix dumping top-level tagged values (xabbuh) + * bug #48615 Fix getting the name of closures on PHP 8.1.11+ (nicolas-grekas) + * bug #48618 [ErrorHandler] [DebugClassLoader] Fix some new return types support (fancyweb) + * bug #48421 [HttpFoundation] IPv4-mapped IPv6 addresses incorrectly rejected (bonroyage) + * bug #48501 [RateLimiter] Add `int` to `Reservation::wait()` (DaRealFreak) + * bug #48359 [VarDumper] Ignore \Error in __debugInfo() (fancyweb) + * bug #48482 [DependencyInjection] Revert "bug #48027 Don't autoconfigure tag when it's already set with attributes" (nicolas-grekas) + * bug #48335 [TwigBridge] Amend `MoneyType` twig to include a space (mogilvie) + * bug #48046 [WebProfilerBundle] Remove redundant code from logger template (HypeMC) + * bug #48292 [Security] [LoginLink] Throw InvalidLoginLinkException on missing parameter (MatTheCat) + +* 6.0.16 (2022-11-28) + + * bug #48333 [Yaml] parse unquoted digits in tag values as integers (xabbuh) + * bug #48330 [FrameworkBundle] do not wire the MercureTransportFactory if the MercureBundle is not enabled (xabbuh) + * bug #48262 [Notifier] [SMSBiuras] `true`/`false` mismatch for `test_mode` option (StaffNowa) + * bug #48273 [HttpKernel] Fix message for unresovable arguments of invokable controllers (fancyweb) + * bug #48251 [PropertyInfo] ignore const expressions read by phpdocumentor (xabbuh) + * bug #48224 [DependencyInjection] Process bindings in `ServiceLocatorTagPass` (MatTheCat) + * bug #48179 [Console] Support completion for bash functions (Chi-teck) + * bug #48217 [Console] Improve error message when shell is not detected in completion command (GromNaN) + * bug #48222 [Translation] [Lokalize] Configure `replace_breaks` to prevent issues with multilines translations (Kocal) + * bug #48210 [Console]  Fix signal handlers called after event listeners and skip exit (GromNaN) + * bug #48198 [Messenger] Fix time-limit check exception (alamirault) + * bug #48122 [PhpUnitBridge] Fix language deprecations incorrectly marked as direct (wouterj) + * bug #47998 [Console] Fix console `ProgressBar::override()` after manual `ProgressBar::cleanup()` (maxbeckers) + * bug #48173 [HttpClient] Handle Amp HTTP client v5 incompatibility gracefully (fancyweb) + * bug #48172 [HttpKernel] Don’t try to wire Response argument with controller.service_arguments (MatTheCat) + * bug #48085 [Messenger] Tell about messenger:consume invalid limit options (MatTheCat) + * bug #48120 [Messenger] Do not throw 'no handlers' exception when skipping handlers due to duplicate handling (wouterj) + * bug #48112 [HttpFoundation] Compare cookie with null value as empty string in ResponseCookieValueSame (fancyweb) + * bug #48119 [FrameworkBundle][Lock] Allow to disable lock without defining a resource (MatTheCat) + * bug #48093 [DependencyInjection] don't move locator tag for service subscriber (RobertMe) + * bug #48075 [Mailer] Stream timeout not detected fgets returns false (Sezil) + * bug #48092 Fix the notification email theme for asynchronously dispatched emails (krisbuist) + * bug #48097 Fix search scope when performing fallback mapping driver detection (spideyfusion) + * bug #48103 [HttpClient] Do not set http_version instead of setting it to null (Tetragramat) + * bug #48027 [DependencyInjection] Don't autoconfigure tag when it's already set with attributes (nicolas-grekas) + * bug #48050 [HttpFoundation] Check IPv6 is valid before comparing it (PhilETaylor) + +* 6.0.15 (2022-10-28) + + * bug #47990 [HttpClient] Fix retrying requests when the content is used by the strategy (nicolas-grekas) + * bug #48005 [ErrorHandler] s/
/
(PhilETaylor) + * bug #47907 [Console] Update Application.php (aleksandr-shevchenko) + * bug #47955 [Security][Serializer] Add missing args to trigger_deprecation (alamirault) + * bug #47932 Throw LogicException instead of Error when trying to generate logout-… (addiks) + * bug #47918 [Intl] Update the ICU data to 72.1 - 5.4 (jderusse) + * bug #47857 [HttpKernel] Fix empty request stack when terminating with exception (krzyc) + * bug #47879 [HttpClient] Fix buffering after calling AsyncContext::passthru() (nicolas-grekas, lubo13) + * bug #47878 [HttpKernel] Remove EOL when using error_log() in HttpKernel Logger (cyve) + * bug #47883 [Console] Fix error output on windows cli (Maximilian.Beckers) + * bug #47884 [Cache] Reserve numeric keys when doing memory leak prevention (simoheinonen) + * bug #47863 [DoctrineBridge] Allow doctrine/event-manager 2 (derrabus) + * bug #47831 [Messenger] Fix amqp socket lost (GurvanVgx) + * bug #47855 [Routing] TypeError in Router when using UrlGenerator (Maximilian.Beckers) + * bug #47822 [Mailer] fix: use message object from event (rogamoore) + * bug #47858 [DoctrineBridge] Implement `EventManager::getAllListeners()` (derrabus) + +* 6.0.14 (2022-10-12) + + * bug #47621 [Serializer] Allow getting discriminated type by class name (TamasSzigeti) + * bug #47833 [TwigBridge] Remove empty spaces between choices when using checkbox-inline or checkbox-switch (simondaigre) + * bug #47808 [HttpClient] Fix seeking in not-yet-initialized requests (nicolas-grekas) + * bug #47798 [DoctrineBridge] Fix auto mapping for bundles that contain only embeddables (jorissae) + * bug #47702 [Messenger] Fix default serializer not handling DateTime objects properly (barton-webwings) + * bug #47779 [Console] Fix `Helper::removeDecoration` hyperlink bug (greew) + * bug #47753 [Mime] sync message serializer code for forward-compatibility (xabbuh) + * bug #47763 [PropertyInfo] a readonly property must not be reported as being writable (xabbuh) + * bug #47731 [WebProfiler] Fix overflow issue in Forms panel (zolikonta) + * bug #46956 [FrameworkBundle] Allow to specify `null` for exception mapping configuration values (andrew-demb) + * bug #47746 [HttpFoundation] Fix BinaryFileResponse content type detection logic (X-Coder264) + * bug #47626 [Notifier] [Expo] Throw exception on error-response from expo api (sdrewergutland) + * bug #47317 [Security] Fix login url matching when app is not run with url rewriting or from a sub folder (sgehrig) + +* 6.0.13 (2022-09-30) + + * bug #47637 [FrameworkBundle] Fix passing `serializer.default_context` option to normalizers (wuchen90) + * bug #47695 [FrameworkBundle] Filter out trans paths that are covered by a parent folder path (natewiebe13) + * bug #45554 [Serializer] Fixed framework.serializer.default_context is not working for JsonEncoder (siganushka) + * bug #47547 [Ldap] Do not run ldap_set_option on failed connection (tatankat) + * bug #47635 [DependencyInjection] EnvPlaceholderParameterBag::get() can't return UnitEnum (jack.shpartko) + * bug #47578 [Security] Fix AbstractFormLoginAuthenticator return types (AndrolGenhald) + * bug #47614 [FrameworkBundle] Fix a phpdoc in mailer assertions (HeahDude) + * bug #47516 [HttpFoundation] Prevent BinaryFileResponse::prepare from adding content type if no content is sent (naitsirch) + * bug #47533 [Messenger] decode URL-encoded characters in DSN's usernames/passwords (xabbuh) + * bug #47530 [HttpFoundation] Always return strings from accept headers (ausi) + * bug #47523 [Uid] Ensure ULIDs are monotonic even when the time goes backward (nicolas-grekas) + * bug #47528 [Form] fix UUID tranformer (nicolas-grekas) + * bug #47488 [Security] Fix valid remember-me token exposure to the second consequent request (Ivan Kurnosov) + * bug #47518 [Uid] Fix validating UUID variant bits (nicolas-grekas) + * bug #47441 [HttpClient] [HttpClientBundle] Bugfix for delayed retryableHttpClient (martkop26) + * bug #47499 [Uid][Validator] Stop to first ULID format violation (ogizanagi) + * bug #47491 [HttpKernel] Prevent exception in RequestDataCollector if request stack is empty (aschempp) + * bug #47497 [Bridge] Fix mkdir() race condition in ProxyCacheWarmer (andrey-tech) + * bug #47415 [HttpClient] Psr18Client ignore invalid HTTP headers (nuryagdym) + * bug #47394 [Console] [Completion] Make bash completion run in non interactive mode (Seldaek) + * bug #47455 [Mime] Fix TextPart broken after being serialized (fabpot) + * bug #47423 [String] CamelCase/SnakeCase on uppercase word (mpiot) + * bug #47435 [HttpKernel] lock when writting profiles (nicolas-grekas) + * bug #47417 [WebProfilerBundle] Fix profile search bar link query params (HeahDude) + * bug #47437 [Mime] Fix email rendering when having inlined parts that are not related to the content (fabpot) + * bug #47434 [HttpFoundation] move flushing outside of Response::closeOutputBuffers (nicolas-grekas) + * bug #47351 [FrameworkBundle] Do not throw when describing a factory definition (MatTheCat) + * bug #47403 [Mailer] Fix edge cases in STMP transports (fabpot) + * bug #47372 [Console] Fix OutputFormatterStyleStack::getCurrent return type (alamirault) + * bug #47391 [LokaliseBridge] Fix push command --delete-missing options when there are no missing messages (rwionczek) + * bug #47368 [Security] Count remember me cookie parts before accessing the second (MatTheCat) + * bug #47358 Fix broken request stack state if throwable is thrown. (Warxcell) + * bug #47304 [Serializer] Fix caching context-aware encoders/decoders in ChainEncoder/ChainDecoder (Guite) + * bug #47329 Email image parts: regex for single closing quote (rr-it) + * bug #47335 [Security] [AbstractToken] getUserIdentifier() must return a string (mpiot) + * bug #47283 [HttpFoundation] Prevent accepted rate limits with no remaining token to be preferred over denied ones (MatTheCat) + * bug #47128 [Serializer] Throw InvalidArgumentException if the data needed in the constructor doesn't belong to a backedEnum (allison guilhem) + * bug #47273 [HttpFoundation] Do not send Set-Cookie header twice for deleted session cookie (X-Coder264) + * bug #47255 [Serializer] Fix get accessor regex in AnnotationLoader (jsor) + * bug #47238 [HttpKernel] Fix passing `null` to `\trim()` method in LoggerDataCollector (SVillette) + * bug #47216 [Translation] Crowdin provider throw Exception when status is 50x (alamirault) + * bug #47209 Always attempt to listen for notifications (goetas) + * bug #47211 [Validator] validate nested constraints only if they are in the same group (xabbuh) + * bug #47218 [Console] fix dispatch signal event check for compatibility with the contract interface (xabbuh) + * bug #47200 [Form] ignore missing keys when mapping DateTime objects to uninitialized arrays (xabbuh) + * bug #47189 [Validator] Add additional hint when `egulias/email-validator` needs to be installed (mpdude) + * bug #47195 [FrameworkBundle] fix writes to static $kernel property (xabbuh) + * bug #47185 [String] Fix snake conversion (simPod) + * bug #47175 [DowCrawler] Fix locale-sensitivity of whitespace normalization (nicolas-grekas) + * bug #47171 [TwigBridge] suggest to install the Twig bundle when the required component is already installed (xabbuh) + * bug #47169 [Serializer] Fix throwing right exception in ArrayDenormalizer with invalid type (norkunas) + * bug #47161 [Mailer] Fix logic (fabpot) + * bug #47157 [Messenger] Fix Doctrine transport on MySQL (nicolas-grekas) + * bug #47155 [Filesystem] Remove needless `mb_*` calls (HellFirePvP) + * bug #46190 [Translation] Fix translator overlapse (Xavier RENAUDIN) + * bug #47142 [Mailer] Fix error message in case of an STMP error (fabpot) + * bug #45333 [Console] Fix ConsoleEvents::SIGNAL subscriber dispatch (GwendolenLynch) + * bug #47145 [HttpClient] Fix shared connections not being freed on PHP < 8 (nicolas-grekas) + * bug #47143 [HttpClient] Fix memory leak when using StreamWrapper (nicolas-grekas) + * bug #47130 [HttpFoundation] Fix invalid ID not regenerated with native PHP file sessions (BrokenSourceCode) + * bug #47129 [FrameworkBundle] remove the ChatterInterface alias when the chatter service is removed (xabbuh) + +* 6.0.12 (2022-08-26) + + * bug #47372 [Console] Fix OutputFormatterStyleStack::getCurrent return type (alamirault) + * bug #47391 [LokaliseBridge] Fix push command --delete-missing options when there are no missing messages (rwionczek) + * bug #47368 [Security] Count remember me cookie parts before accessing the second (MatTheCat) + * bug #47358 Fix broken request stack state if throwable is thrown. (Warxcell) + * bug #47304 [Serializer] Fix caching context-aware encoders/decoders in ChainEncoder/ChainDecoder (Guite) + * bug #47329 Email image parts: regex for single closing quote (rr-it) + * bug #47335 [Security] [AbstractToken] getUserIdentifier() must return a string (mpiot) + * bug #47283 [HttpFoundation] Prevent accepted rate limits with no remaining token to be preferred over denied ones (MatTheCat) + * bug #47128 [Serializer] Throw InvalidArgumentException if the data needed in the constructor doesn't belong to a backedEnum (allison guilhem) + * bug #47273 [HttpFoundation] Do not send Set-Cookie header twice for deleted session cookie (X-Coder264) + * bug #47255 [Serializer] Fix get accessor regex in AnnotationLoader (jsor) + * bug #47238 [HttpKernel] Fix passing `null` to `\trim()` method in LoggerDataCollector (SVillette) + * bug #47216 [Translation] Crowdin provider throw Exception when status is 50x (alamirault) + * bug #47209 Always attempt to listen for notifications (goetas) + * bug #47211 [Validator] validate nested constraints only if they are in the same group (xabbuh) + * bug #47218 [Console] fix dispatch signal event check for compatibility with the contract interface (xabbuh) + * bug #47200 [Form] ignore missing keys when mapping DateTime objects to uninitialized arrays (xabbuh) + * bug #47189 [Validator] Add additional hint when `egulias/email-validator` needs to be installed (mpdude) + * bug #47195 [FrameworkBundle] fix writes to static $kernel property (xabbuh) + * bug #47185 [String] Fix snake conversion (simPod) + * bug #47175 [DowCrawler] Fix locale-sensitivity of whitespace normalization (nicolas-grekas) + * bug #47171 [TwigBridge] suggest to install the Twig bundle when the required component is already installed (xabbuh) + * bug #47169 [Serializer] Fix throwing right exception in ArrayDenormalizer with invalid type (norkunas) + * bug #47161 [Mailer] Fix logic (fabpot) + * bug #47157 [Messenger] Fix Doctrine transport on MySQL (nicolas-grekas) + * bug #47155 [Filesystem] Remove needless `mb_*` calls (HellFirePvP) + * bug #46190 [Translation] Fix translator overlapse (Xavier RENAUDIN) + * bug #47142 [Mailer] Fix error message in case of an STMP error (fabpot) + * bug #45333 [Console] Fix ConsoleEvents::SIGNAL subscriber dispatch (GwendolenLynch) + * bug #47145 [HttpClient] Fix shared connections not being freed on PHP < 8 (nicolas-grekas) + * bug #47143 [HttpClient] Fix memory leak when using StreamWrapper (nicolas-grekas) + * bug #47130 [HttpFoundation] Fix invalid ID not regenerated with native PHP file sessions (BrokenSourceCode) + * bug #47129 [FrameworkBundle] remove the ChatterInterface alias when the chatter service is removed (xabbuh) + * 6.0.11 (2022-07-29) * bug #47069 [Security] Allow redirect after login to absolute URLs (Tim Ward) @@ -763,4 +993,3 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c * feature #41298 [Notifier] Remove deprecation in slack-notifier (jschaedl) * feature #41203 [FrameworkBundle] Add autowiring alias for `HttpCache\StoreInterface` (nicolas-grekas) * feature #41282 Bump Symfony 6 to PHP 8 (nicolas-grekas) - diff --git a/CHANGELOG-6.1.md b/CHANGELOG-6.1.md index c8755b2eab6fb..e60e17769bcc6 100644 --- a/CHANGELOG-6.1.md +++ b/CHANGELOG-6.1.md @@ -7,6 +7,221 @@ in 6.1 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v6.1.0...v6.1.1 +* 6.1.11 (2023-01-24) + + * bug #49078 [Security/Http] Check tokens before loading users from providers (nicolas-grekas) + * bug #49077 [DependencyInjection] Fix named arguments when using ContainerBuilder before compilation (nicolas-grekas) + * bug #49031 [Cache] fix collecting cache stats when nesting computations (nicolas-grekas) + * bug #49046 Fix for Windows when projects are deployed on junctions/symlinks (nerdgod) + * bug #49025 [Notifier] [OvhCloud] handle invalid receiver (seferov) + * bug #48993 [VarDumper] Fix JS to expand / collapse (nicolas-grekas) + * bug #48983 Fix BC user_identifier support after deprecation username (vtsykun) + * bug #48986 [Validator] Fix Email validator logic (fabpot) + * bug #48969 [PropertyInfo] Fixes constructor extractor for mixed type (michael.kubovic) + * bug #48978 [Serializer] use method_exists() instead of catching reflection exceptions (xabbuh) + * bug #48937 [SecurityBundle] Fix using same handler for multiple authenticators (RobertMe) + * bug #48971 [DependencyInjection] Fix dump order of inlined deps (nicolas-grekas) + * bug #48966 [HttpClient] Let curl handle content-length headers (nicolas-grekas) + * bug #48968 [VarExporter] Fix exporting enums (nicolas-grekas) + * bug #48933 [Validator] Fix bad handling of nulls when the 'fields' option of the Unique constraint is set (plfort) + * bug #48926 [DependencyInjection] Fix support for named arguments on non-autowired services (nicolas-grekas) + * bug #48943 [FrameworkBundle] Fix deprecation when accessing a "container.private" service from the test container (nicolas-grekas) + * bug #48931 [DependencyInjection] Fix dumping inlined withers (nicolas-grekas) + * bug #48898 [HttpClient] Move Http clients data collecting at a late level (pforesi) + * bug #48896 [DoctrineBridge] Fix detecting mapping with one line annotations (franmomu) + * bug #48916 [FrameworkBundle] restore call to addGlobalIgnoredName (alexislefebvre) + * bug #48917 [Config] Fix XML dump when node example is an array (alexandre-daubois) + * bug #48904 [Validator] Allow egulias/email-validator v4 (chalasr) + * bug #48831 [Uid] Fix validating nil and max uuid (fancyweb) + +* 6.1.10 (2022-12-29) + + * bug #48823 [Cache] Fix possibly null value passed to preg_match() in RedisTrait (chalasr) + * bug #48816 [Cache] Fix for RedisAdapter without auth parameter (rikvdh) + +* 6.1.9 (2022-12-28) + + * bug #48787 [PhpUnitBridge] Use verbose deprecation output for quiet types only when it reaches the threshold (ogizanagi) + * bug #48784 [Console] Correctly overwrite progressbars with different line count per step (ncharalampidis) + * bug #48801 [Form] Make `ButtonType` handle `form_attr` option (MatTheCat) + * bug #48791 [DependencyInjection] Fix deduplicating service instances in circular graphs (nicolas-grekas) + * bug #48771 [CssSelector] Fix escape patterns (fancyweb) + * bug #48711 [Cache] RedisTrait::createConnection does not pass auth value from redis sentinel cluster DSN (evgkord) + * bug #48724 [VarExporter] Fix exporting classes with __unserialize() but not __serialize() (fancyweb) + * bug #48746 [Validator] Fix IBAN format for Tunisia and Mauritania (smelesh) + * bug #48738 [Workflow] Allow spaces in place names so the PUML dump doesn't break (Kamil Musial) + * bug #48718 Compatibility with doctrine/annotations 2 (derrabus) + * bug #48681 [Console] Revert "bug #48089 Fix clear line with question in section (maxbeckers) (chalasr) + * bug #48651 [HttpKernel] AbstractSessionListener should not override the cache lifetime for private responses (rodmen) + * bug #48591 [DependencyInjection] Shared private services becomes public after a public service is accessed (alexpott) + * bug #48126 [Mailer] Include all transports' debug messages in RoundRobin transport exception (mixdf) + * bug #48089 [Console] Fix clear line with question in section (maxbeckers) + * bug #48602 [HtmlSanitizer] Fix HtmlSanitizer default configuration behavior for allowed schemes (Titouan Galopin) + * bug #48635 [HttpFoundation] Use relative timestamps with MemcachedSessionHandler (tvlooy) + * bug #47979 [Cache] Fix dealing with ext-redis' multi/exec returning a bool (João Nogueira) + * bug #48612 [Messenger] [Amqp] Added missing rpc_timeout option (lyrixx) + * bug #48233 [Serializer] Prevent `GetSetMethodNormalizer` from creating invalid magic method call (klaussilveira) + * bug #48628 [HttpFoundation] Fix dumping array cookies (nicolas-grekas) + * bug #48048 [WebProfilerBundle] Fix dump header not being displayed (HypeMC) + * bug #47836 [HttpClient] TraceableHttpClient: increase decorator's priority (adpeyre) + * bug #48259 [FrameworkBundle] Allow configuring `framework.exceptions` with a config builder (MatTheCat) + * bug #48314 [Mime] Fix MessagePart serialization (Amunak) + * bug #48331 [Yaml] fix dumping top-level tagged values (xabbuh) + * bug #48615 Fix getting the name of closures on PHP 8.1.11+ (nicolas-grekas) + * bug #48624 [ErrorHandler][HttpKernel] Fix reading the SYMFONY_IDE env var (nicolas-grekas) + * bug #48618 [ErrorHandler] [DebugClassLoader] Fix some new return types support (fancyweb) + * bug #48421 [HttpFoundation] IPv4-mapped IPv6 addresses incorrectly rejected (bonroyage) + * bug #48501 [RateLimiter] Add `int` to `Reservation::wait()` (DaRealFreak) + * bug #48359 [VarDumper] Ignore \Error in __debugInfo() (fancyweb) + * bug #48534 [FrameworkBundle] add `kernel.locale_aware` tag to `LocaleSwitcher` (kbond) + * bug #48482 [DependencyInjection] Revert "bug #48027 Don't autoconfigure tag when it's already set with attributes" (nicolas-grekas) + * bug #48346 [HttpKernel] In DateTimeValueResolver, convert previously defined date attribute to the expected class (GromNaN) + * bug #48335 [TwigBridge] Amend `MoneyType` twig to include a space (mogilvie) + * bug #48046 [WebProfilerBundle] Remove redundant code from logger template (HypeMC) + * bug #48292 [Security] [LoginLink] Throw InvalidLoginLinkException on missing parameter (MatTheCat) + +* 6.1.8 (2022-11-28) + + * bug #48333 [Yaml] parse unquoted digits in tag values as integers (xabbuh) + * bug #48330 [FrameworkBundle] do not wire the MercureTransportFactory if the MercureBundle is not enabled (xabbuh) + * bug #48262 [Notifier] [SMSBiuras] `true`/`false` mismatch for `test_mode` option (StaffNowa) + * bug #48273 [HttpKernel] Fix message for unresovable arguments of invokable controllers (fancyweb) + * bug #48251 [PropertyInfo] ignore const expressions read by phpdocumentor (xabbuh) + * bug #48224 [DependencyInjection] Process bindings in `ServiceLocatorTagPass` (MatTheCat) + * bug #48179 [Console] Support completion for bash functions (Chi-teck) + * bug #48217 [Console] Improve error message when shell is not detected in completion command (GromNaN) + * bug #48222 [Translation] [Lokalize] Configure `replace_breaks` to prevent issues with multilines translations (Kocal) + * bug #48210 [Console]  Fix signal handlers called after event listeners and skip exit (GromNaN) + * bug #48198 [Messenger] Fix time-limit check exception (alamirault) + * bug #48122 [PhpUnitBridge] Fix language deprecations incorrectly marked as direct (wouterj) + * bug #47998 [Console] Fix console `ProgressBar::override()` after manual `ProgressBar::cleanup()` (maxbeckers) + * bug #48173 [HttpClient] Handle Amp HTTP client v5 incompatibility gracefully (fancyweb) + * bug #48172 [HttpKernel] Don’t try to wire Response argument with controller.service_arguments (MatTheCat) + * bug #48085 [Messenger] Tell about messenger:consume invalid limit options (MatTheCat) + * bug #48120 [Messenger] Do not throw 'no handlers' exception when skipping handlers due to duplicate handling (wouterj) + * bug #48112 [HttpFoundation] Compare cookie with null value as empty string in ResponseCookieValueSame (fancyweb) + * bug #48119 [FrameworkBundle][Lock] Allow to disable lock without defining a resource (MatTheCat) + * bug #48110 [HttpKernel] Fix deprecation for DateTimeValueResolver with null on non-nullable argument (GromNaN) + * bug #48093 [DependencyInjection] don't move locator tag for service subscriber (RobertMe) + * bug #48075 [Mailer] Stream timeout not detected fgets returns false (Sezil) + * bug #48092 Fix the notification email theme for asynchronously dispatched emails (krisbuist) + * bug #48097 Fix search scope when performing fallback mapping driver detection (spideyfusion) + * bug #48103 [HttpClient] Do not set http_version instead of setting it to null (Tetragramat) + * bug #48027 [DependencyInjection] Don't autoconfigure tag when it's already set with attributes (nicolas-grekas) + * bug #48050 [HttpFoundation] Check IPv6 is valid before comparing it (PhilETaylor) + +* 6.1.7 (2022-10-28) + + * bug #47990 [HttpClient] Fix retrying requests when the content is used by the strategy (nicolas-grekas) + * bug #48005 [ErrorHandler] s/
/
(PhilETaylor) + * bug #47907 [Console] Update Application.php (aleksandr-shevchenko) + * bug #47955 [Security][Serializer] Add missing args to trigger_deprecation (alamirault) + * bug #47932 Throw LogicException instead of Error when trying to generate logout-… (addiks) + * bug #47918 [Intl] Update the ICU data to 72.1 - 5.4 (jderusse) + * bug #47857 [HttpKernel] Fix empty request stack when terminating with exception (krzyc) + * bug #47879 [HttpClient] Fix buffering after calling AsyncContext::passthru() (nicolas-grekas, lubo13) + * bug #47878 [HttpKernel] Remove EOL when using error_log() in HttpKernel Logger (cyve) + * bug #47854 [HttpClient] Don't override header if is x-www-form-urlencoded (Oipnet) + * bug #47883 [Console] Fix error output on windows cli (Maximilian.Beckers) + * bug #47884 [Cache] Reserve numeric keys when doing memory leak prevention (simoheinonen) + * bug #47863 [DoctrineBridge] Allow doctrine/event-manager 2 (derrabus) + * bug #47831 [Messenger] Fix amqp socket lost (GurvanVgx) + * bug #47855 [Routing] TypeError in Router when using UrlGenerator (Maximilian.Beckers) + * bug #47822 [Mailer] fix: use message object from event (rogamoore) + * bug #47858 [DoctrineBridge] Implement `EventManager::getAllListeners()` (derrabus) + +* 6.1.6 (2022-10-12) + + * bug #47621 [Serializer] Allow getting discriminated type by class name (TamasSzigeti) + * bug #47833 [TwigBridge] Remove empty spaces between choices when using checkbox-inline or checkbox-switch (simondaigre) + * bug #47808 [HttpClient] Fix seeking in not-yet-initialized requests (nicolas-grekas) + * bug #47798 [DoctrineBridge] Fix auto mapping for bundles that contain only embeddables (jorissae) + * bug #47702 [Messenger] Fix default serializer not handling DateTime objects properly (barton-webwings) + * bug #47764 [Serializer] fixed traceable decoration priorities (mtarld) + * bug #47779 [Console] Fix `Helper::removeDecoration` hyperlink bug (greew) + * bug #47753 [Mime] sync message serializer code for forward-compatibility (xabbuh) + * bug #47763 [PropertyInfo] a readonly property must not be reported as being writable (xabbuh) + * bug #47731 [WebProfiler] Fix overflow issue in Forms panel (zolikonta) + * bug #46956 [FrameworkBundle] Allow to specify `null` for exception mapping configuration values (andrew-demb) + * bug #47746 [HttpFoundation] Fix BinaryFileResponse content type detection logic (X-Coder264) + * bug #47626 [Notifier] [Expo] Throw exception on error-response from expo api (sdrewergutland) + * bug #47317 [Security] Fix login url matching when app is not run with url rewriting or from a sub folder (sgehrig) + +* 6.1.5 (2022-09-30) + + * bug #47703 [Mailer][Mailjet] Apply the default value of 512 for max depths (nurtext) + * bug #47637 [FrameworkBundle] Fix passing `serializer.default_context` option to normalizers (wuchen90) + * bug #47695 [FrameworkBundle] Filter out trans paths that are covered by a parent folder path (natewiebe13) + * bug #45554 [Serializer] Fixed framework.serializer.default_context is not working for JsonEncoder (siganushka) + * bug #47547 [Ldap] Do not run ldap_set_option on failed connection (tatankat) + * bug #47635 [DependencyInjection] EnvPlaceholderParameterBag::get() can't return UnitEnum (jack.shpartko) + * bug #47675 [HttpKernel] Use Accept-Language header even if there are no enabled locales (MatTheCat) + * bug #47578 [Security] Fix AbstractFormLoginAuthenticator return types (AndrolGenhald) + * bug #47614 [FrameworkBundle] Fix a phpdoc in mailer assertions (HeahDude) + * bug #47227 [Messenger] Support for custom handler method containing a Union type tagged with #[AsMessageHandler] (ArchitectNate) + * bug #47516 [HttpFoundation] Prevent BinaryFileResponse::prepare from adding content type if no content is sent (naitsirch) + * bug #47533 [Messenger] decode URL-encoded characters in DSN's usernames/passwords (xabbuh) + * bug #47530 [HttpFoundation] Always return strings from accept headers (ausi) + * bug #47529 [Routing] Reject v2 UUIDs (nicolas-grekas) + * bug #47523 [Uid] Ensure ULIDs are monotonic even when the time goes backward (nicolas-grekas) + * bug #47528 [Form] fix UUID tranformer (nicolas-grekas) + * bug #47488 [Security] Fix valid remember-me token exposure to the second consequent request (Ivan Kurnosov) + * bug #47518 [Uid] Fix validating UUID variant bits (nicolas-grekas) + * bug #47441 [HttpClient] [HttpClientBundle] Bugfix for delayed retryableHttpClient (martkop26) + * bug #47499 [Uid][Validator] Stop to first ULID format violation (ogizanagi) + * bug #47491 [HttpKernel] Prevent exception in RequestDataCollector if request stack is empty (aschempp) + * bug #47497 [Bridge] Fix mkdir() race condition in ProxyCacheWarmer (andrey-tech) + * bug #47415 [HttpClient] Psr18Client ignore invalid HTTP headers (nuryagdym) + * bug #47463 [Console] [Completion] Make fish completion run in non interactive mode (Seldaek) + * bug #47394 [Console] [Completion] Make bash completion run in non interactive mode (Seldaek) + * bug #47455 [Mime] Fix TextPart broken after being serialized (fabpot) + * bug #47423 [String] CamelCase/SnakeCase on uppercase word (mpiot) + * bug #47435 [HttpKernel] lock when writting profiles (nicolas-grekas) + * bug #47417 [WebProfilerBundle] Fix profile search bar link query params (HeahDude) + * bug #47437 [Mime] Fix email rendering when having inlined parts that are not related to the content (fabpot) + * bug #47434 [HttpFoundation] move flushing outside of Response::closeOutputBuffers (nicolas-grekas) + * bug #47351 [FrameworkBundle] Do not throw when describing a factory definition (MatTheCat) + * bug #47403 [Mailer] Fix edge cases in STMP transports (fabpot) + +* 6.1.4 (2022-08-26) + + * bug #47372 [Console] Fix OutputFormatterStyleStack::getCurrent return type (alamirault) + * bug #47391 [LokaliseBridge] Fix push command --delete-missing options when there are no missing messages (rwionczek) + * bug #47368 [Security] Count remember me cookie parts before accessing the second (MatTheCat) + * bug #47358 Fix broken request stack state if throwable is thrown. (Warxcell) + * bug #47304 [Serializer] Fix caching context-aware encoders/decoders in ChainEncoder/ChainDecoder (Guite) + * bug #47150 [Serializer] Revert deprecation of `ContextAwareEncoderInterface` and `ContextAwareDecoderInterface` (nicolas-grekas) + * bug #47329 Email image parts: regex for single closing quote (rr-it) + * bug #47335 [Security] [AbstractToken] getUserIdentifier() must return a string (mpiot) + * bug #47283 [HttpFoundation] Prevent accepted rate limits with no remaining token to be preferred over denied ones (MatTheCat) + * bug #47128 [Serializer] Throw InvalidArgumentException if the data needed in the constructor doesn't belong to a backedEnum (allison guilhem) + * bug #47273 [HttpFoundation] Do not send Set-Cookie header twice for deleted session cookie (X-Coder264) + * bug #47255 [Serializer] Fix get accessor regex in AnnotationLoader (jsor) + * bug #47238 [HttpKernel] Fix passing `null` to `\trim()` method in LoggerDataCollector (SVillette) + * bug #47216 [Translation] Crowdin provider throw Exception when status is 50x (alamirault) + * bug #47209 Always attempt to listen for notifications (goetas) + * bug #47211 [Validator] validate nested constraints only if they are in the same group (xabbuh) + * bug #47218 [Console] fix dispatch signal event check for compatibility with the contract interface (xabbuh) + * bug #47200 [Form] ignore missing keys when mapping DateTime objects to uninitialized arrays (xabbuh) + * bug #47189 [Validator] Add additional hint when `egulias/email-validator` needs to be installed (mpdude) + * bug #47195 [FrameworkBundle] fix writes to static $kernel property (xabbuh) + * bug #47185 [String] Fix snake conversion (simPod) + * bug #47175 [DowCrawler] Fix locale-sensitivity of whitespace normalization (nicolas-grekas) + * bug #47172 [Translation] Fix reading intl-icu domains with LocoProvider (nicolas-grekas) + * bug #47171 [TwigBridge] suggest to install the Twig bundle when the required component is already installed (xabbuh) + * bug #47169 [Serializer] Fix throwing right exception in ArrayDenormalizer with invalid type (norkunas) + * bug #47162 [Mailer] Fix error message in case of an SMTP error (fabpot) + * bug #47161 [Mailer] Fix logic (fabpot) + * bug #47157 [Messenger] Fix Doctrine transport on MySQL (nicolas-grekas) + * bug #47155 [Filesystem] Remove needless `mb_*` calls (HellFirePvP) + * bug #46190 [Translation] Fix translator overlapse (Xavier RENAUDIN) + * bug #47142 [Mailer] Fix error message in case of an STMP error (fabpot) + * bug #45333 [Console] Fix ConsoleEvents::SIGNAL subscriber dispatch (GwendolenLynch) + * bug #47145 [HttpClient] Fix shared connections not being freed on PHP < 8 (nicolas-grekas) + * bug #47143 [HttpClient] Fix memory leak when using StreamWrapper (nicolas-grekas) + * bug #47130 [HttpFoundation] Fix invalid ID not regenerated with native PHP file sessions (BrokenSourceCode) + * bug #47129 [FrameworkBundle] remove the ChatterInterface alias when the chatter service is removed (xabbuh) + * 6.1.3 (2022-07-29) * bug #47069 [Security] Allow redirect after login to absolute URLs (Tim Ward) diff --git a/CHANGELOG-6.2.md b/CHANGELOG-6.2.md new file mode 100644 index 0000000000000..1e66ed8becaa8 --- /dev/null +++ b/CHANGELOG-6.2.md @@ -0,0 +1,624 @@ +CHANGELOG for 6.2.x +=================== + +This changelog references the relevant changes (bug and security fixes) done +in 6.2 minor versions. + +To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash +To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v6.2.0...v6.2.1 + +* 6.2.14 (2023-07-31) + + * bug #51178 [Finder] Revert "Fix children condition in ExcludeDirectoryFilterIterator" (derrabus) + +* 6.2.13 (2023-07-30) + + * bug #50933 [Serializer] Fix deserializing nested arrays of objects with mixed keys (HypeMC) + * bug #51071 [VarExporter] Fix calling scope detection inside magic accessors (vtsykun) + * bug #51078 [FrameworkBundle][Workflow] Throw exception is workflow.xxx.transitions is not an array (lyrixx) + * bug #51114 [Serializer] Fix denormalizing abstract part headers in MimeMessageNormalizer (fancyweb) + * bug #50788 [Validator] Fix regression with class metadatada on parent classes (rmikalkenas) + * bug #51017 [VarExporter] Fix exporting classes with __serialize() but not __unserialize() (fancyweb) + * bug #51031 Fix deprecations on PHP 8.3 (nicolas-grekas) + * bug #51000 [WebProfilerBundle] Fix error in case of 'Content-Type' set null in dev environment with no debug (alexbuyanow) + * bug #50985 [DependencyInjection] Fix fetching lazy non-shared services multiple times (HypeMC) + * bug #50994 [ErrorHandler][Runtime] Don't mess with ini_set('assert.warning') (nicolas-grekas) + * bug #50968 [PropertyAccess] Fix access to undefined "file" key when checking stack frames (nicolas-grekas) + * bug #50552 [Security] Allow custom scheme to be used as redirection URIs (Spomky) + * bug #50945 [DebugBundle][FrameworkBundle] Fix using the framework without the Console component (HypeMC) + * bug #50913 [HttpKernel][WebProfilerBundle] Fix search feature (Cyril HERRERA) + * bug #50937 [Form] fetch all known ChoiceType values at once (xabbuh) + * bug #50944 [FrameworkBundle] Add missing monolog channel tag to the `messenger:failed:retry` command (HypeMC) + * bug #49070 [RateLimiter] fix incorrect retryAfter of FixedWindow (RobertMe) + * bug #50960 [VarDumper] Fix dumping `ArrayObject` with `DumpDataCollector` (lyrixx, HypeMC) + * bug #50943 [Intl] Taking into account bibliographic + overlong (oleg-andreyev) + * bug #50954 [PhpUnitBridge] Kill the last concurrent process when it stales for more than 60s (nicolas-grekas) + * bug #50475 [FrameworkBundle] Prevent `cache:clear` to lose files on subsequent runs (Okhoshi) + * bug #47252 [PhpUnitBridge] Use triggering class to generate baseline for deprecation messages from DebugClassLoader (leongersen) + * bug #50582 [Security/Http] Fix false-string handling in `RememberMeAuthenticator` (ossinkine) + * bug #50595 [DependencyInjection] Don't ignore attributes on the actual decorator (HypeMC) + * bug #50804 [Serializer] Fix Normalizer not utilizing converted name for index variadic param (DidierLmn) + * bug #50813 [DoctrineBridge] Load refreshed user proxy (MatTheCat) + * bug #50905 [DepdencyInjection] Fix costly logic when checking errored definitions (nicolas-grekas) + * bug #50884 [Finder] Fix initial directory is opened twice (mvorisek) + * bug #50881 [Messenger] Fix passing options set via tags to handler descriptors (nicolas-grekas) + * feature #50838 [DoctrineBridge] Remove outdated comment (HeahDude) + * bug #50837 [DependencyInjection] Fix autocasting null env values to empty string (fancyweb) + * bug #50819 [SecurityBundle] Do not translate `Bearer` header’s `error_description` (MatTheCat) + * bug #50793 [DependencyInjection] Fix resource tracking for lazy services (nicolas-grekas) + * bug #50810 [String] Fix Inflector for 'status' (evertharmeling) + +* 6.2.12 (2023-06-26) + + * bug #50763 [DependencyInjection] Skip errored definitions deep-referenced as runtime exceptions (nicolas-grekas) + * bug #50728 [HttpClient] Explicitly exclude CURLOPT_POSTREDIR (nicolas-grekas) + * bug #48961 [WebProfilerBundle] right blocks: fix display (jmsche) + * bug #50671 [HttpClient] Fix encoding some characters in query strings (Daniel Kozák) + * bug #50655 Revert "Respect isRetryable decision of the retry strategy for re-delivery" (bendavies) + * bug #50665 [FrameworkBundle] Ignore missing directories in about command (ro0NL) + * bug #50644 [VarDumper] Dumping DateTime throws error if getTimezone is false (bogdanmoza) + * bug #50656 Only update autoload_runtime.php when it changed (Seldaek) + * bug #50698 [HttpClient] Fix int conversion for `GenericRetryStrategy` with floated multiplier (francisbesset) + * bug #50611 [Clock] Fix MockClock::modify() on PHP 8.3 (nicolas-grekas) + * bug #50548 [FrameworkBundle] Show non-bundle extensions in `debug:config` & `config:dump` list view & completion (HypeMC) + * bug #50585 [Cache] Fix RedisTrait::createConnection for cluster (darkanakin41) + * bug #50546 [FrameworkBundle] Fix `debug:config` & `config:dump` in debug mode (HypeMC) + * bug #50560 [DependencyInjection] Support PHP 8.2 `true` and `null` type (ruudk) + * bug #50562 [Lock] Fix sprintf (fancyweb) + * bug #50524 Fix Doctrine deprecations (nicolas-grekas) + * bug #50539 [Validator] Remove internal from methods on non-internal interfaces (wouterj) + * bug #50534 [PhpUnitBridge] Fix support for the NO_COLOR env var (nicolas-grekas) + * bug #50517 [DependencyInjection] Fix casting scalar env vars from null (fancyweb) + * bug #50515 [Mailer] [MailPace] Fix undefined array key in errors response (Florian Heller) + * bug #50507 [Cache] Fix DBAL deprecations (MatTheCat) + * bug #50501 [Serializer] Fix discriminator map not working with `AbstractNormalizer::OBJECT_TO_POPULATE` (HypeMC) + * bug #50480 [Serializer] Fix discriminator map not working with `AbstractNormalizer::OBJECT_TO_POPULATE` (HypeMC) + * bug #50437 [Filesystem] Follow symlinks when dumping files (ausi) + * bug #50478 [DependencyInjection] Escape `` from parameter-like default values (MatTheCat) + * bug #50452 Hide definitions bearing the `container.excluded` tag (Myks92) + +* 6.2.11 (2023-05-27) + + * bug #50442 [SecurityBundle] Update security-1.0.xsd to include missing access-token definition (aegypius) + * bug #50429 [Console] block input stream if needed (joelwurtz) + * bug #50312 [Security] Skip clearing CSRF Token on stateless logout (chalasr) + * bug #50315 [Translation] Fix handling of null messages in `ArrayLoader` (rob006) + * bug #50338 [Console] Remove ``exec`` and replace it by ``shell_exec`` (maxbeckers) + * bug #50193 [Serializer] Fix `SerializedPath` not working with constructor arguments (HypeMC) + * bug #50280 [PropertyAccess] Fix nullsafe operator on array index (HypeMC) + * bug #50362 [FrameworkBundle] Fix Workflow without a marking store definition uses marking store definition of previously defined workflow (krciga22) + * bug #50309 [HttpFoundation] UrlHelper is now aware of RequestContext changes (giosh94mhz) + * bug #50309 [HttpFoundation] UrlHelper is now aware of RequestContext changes (giosh94mhz) + * bug #50352 [Notifier][TurboSMS] Fix get sender name (ZhukV) + * bug #50354 [Process] Stop the process correctly even if underlying input stream is not closed (joelwurtz) + * bug #50332 [PropertyInfo] Fix `PhpStanExtractor` when constructor has no docblock (HypeMC) + * bug #50253 [FrameworkBundle] Generate caches consistently on successive run of `cache:clear` command (Okhoshi) + * bug #49063 [Messenger] Respect `isRetryable` decision of the retry strategy for re-delivery (FlyingDR) + * bug #50251 [Serializer] Handle datetime deserialization in U format (tugmaks) + * bug #50266 [HttpFoundation] Fix file streaming after connection aborted (rlshukhov) + * bug #50277 [Messenger] Add `IS_REPEATABLE` flag to `AsMessageHandler` attribute (adrianguenter) + * bug #50269 Fix param type annotation (l-vo) + * bug #50268 Allow resources in Query::setParam (l-vo) + * bug #50256 [HttpClient] Fix setting duplicate-name headers when redirecting with AmpHttpClient (nicolas-grekas) + * bug #50214 [WebProfilerBundle] Remove legacy filters remnants (MatTheCat) + * bug #50235 [HttpClient] Fix getting through proxies via CONNECT (nicolas-grekas) + * bug #50241 [HttpKernel] Prevent initialising lazy services during services reset (tucksaun) + * bug #50244 [HttpKernel] Fix restoring surrogate content from cache (nicolas-grekas) + * bug #50246 [DependencyInjection] Do not check errored definitions’ type (MatTheCat) + * bug #49557 [PropertyInfo] Fix phpDocExtractor nullable array value type (fabpot) + * bug #50213 [ErrorHandler] Prevent conflicts with WebProfilerBundle’s JavaScript (MatTheCat) + * bug #50192 [Serializer] backed enum throw notNormalizableValueException outside construct method (alli83) + * bug #50238 [HttpKernel] Don't use eval() to render ESI/SSI (nicolas-grekas) + * bug #50226 [HttpClient] Ensure HttplugClient ignores invalid HTTP headers (nicolas-grekas) + * bug #50203 [Messenger] Fix registering message handlers (nicolas-grekas) + * bug #50204 [ErrorHandler] Skip Httplug deprecations for HttplugClient (nicolas-grekas) + +* 6.2.10 (2023-04-28) + + * bug #50143 [Console] trim(): Argument #1 () must be of type string, bool given (danepowell) + * bug #50139 Check if trace.curlCommand is defined in profiler (Toflar) + * bug #50066 [Dumper] Trim leading newlines when checking if value begins with a space (bradtreloar) + * bug #50115 [FrameworkBundle] Make service edges unique (rmikalkenas) + * bug #50111 Fix the list of supported shells for completions in a phar (stof) + * bug #50110 [Console] Fix the usage of the zsh completion through the fpath discovery (stof) + * bug #50074 [Cache] Send Predis SSL options in the $hosts parameter (magnusnordlander) + * bug #50088 [DependencyInjection] Do not ignore tags `name` attribute when it does not define their name (MatTheCat) + * bug #50099 [Cache] Fix success interpretation when pruning cache (staabm) + * bug #50092 [Security] Fix return type of AuthenticationSuccessHandlerInterface::onAuthenticationSuccess() (nicolas-grekas) + * bug #48288 [Security] Make onAuthenticationSuccess Response optional (db306) + * bug #50086 [VarExporter] Fix forwarding references to proxied classes (nicolas-grekas) + * bug #50072 [HttpClient] Fix global state preventing two CurlHttpClient instances from working together (nicolas-grekas) + * bug #50040 [Translation] Crowdin Bridge: Fix locale vs LanguageId (Peter Culka) + * bug #50061 [DependencyInjection] Fix support for inner collections when using `` (zerustech) + * bug #50022 [DependencyInjection] Fallback to default value when autowiring undefined parameters for optional arguments (radar3301) + * bug #50056 [Form] Fix "prototype_data" option creating duplicates instead of new lines (Thorry84) + * bug #50017 [Validator] Fix support of Enum to `ConstraintValidator::formatValue` (PhoneixS) + * bug #49356 [Process] Path resolution changes for PHP in the cgi-fcgi mode (isdn) + * bug #48886 [Console] Fix computing column width containing multibyte chars (cay89) + * bug #50049 [Messenger] Fix deprecation layer of RedeliveryStamp (nicolas-grekas) + * bug #47505 [Mime] Form field values with integer keys not resolved correctly (claudiu-cristea) + * bug #50048 [PhpUnitBridge] Fix PHPUnit 10.1 compatibility (enumag) + * bug #50047 [VarDumper] Make the server TCP connection sync (ogizanagi) + * bug #48837 [Messenger] [Redis] Fixed problem where worker stops handling messages on first empty message (jvmanji) + * bug #49317 [Messenger] Fix warning message on failed messenger show command (gstapinato) + * bug #49992 [Mailer] [Mailjet] Use body MessageID instead of X-MJ-Request-GUID (Starfox64) + * bug #48972 [HttpFoundation] Fix memory limit problems in BinaryFileResponse (glady) + * bug #48108 [PropertyAccess] Readonly properties must have no PropertyWriteInfo (CasvanDongen) + * bug #49009 [Form] Cast choices value callback result to string (Matth--) + * bug #49537 [Serializer] Unexpected value should throw UnexpectedValueException (ThomasTr) + * bug #49581 Avoid leading .. for temporary files from Filesystem recursive remove (giosh94mhz) + * bug #50036 [ErrorHandler] Don't throw deprecations for HttplugClient (nicolas-grekas) + * bug #50024 [Serializer] Fix denormalization of object with typed constructor arg (not castable) and with COLLECT_DENORMALIZATION_ERRORS (lyrixx) + * bug #50004 [HttpClient] fix proxied redirects in curl client (matthi4s) + * bug #50008 [Intl] Update the ICU data to 73.1 (jderusse) + * bug #50015 [Serializer] Fix serializer normalize attribute context (akalineskou) + * bug #49987 [Console] Restoring the ability to output unicode text to the Win10 console (aleksandr-shevchenko) + +* 6.2.9 (2023-04-13) + + * bug #49957 [ErrorHandler] Fix sending `Vary` header with `SerializerErrorRenderer` (nicolas-grekas) + * bug #49983 [DomCrawler] Avoid passing null to substr/strrpos methods (VincentLanglet) + * bug #49079 [DoctrineBridge] fix issue with missing stopwatch events (dmaicher) + * bug #49926 [HttpClient] Fix canceling MockResponse (fancyweb) + * bug #49889 [FrameworkBundle] Fix registering ExpressionValidator (nicolas-grekas) + +* 6.2.8 (2023-03-31) + + * bug #49835 [Form] CollectionType apply prototypeOptions to ResizeFormListener new fields (Thorry84) + * bug #49849 [FrameworkBundle] Fix services usages output for text descriptor (rmikalkenas) + * bug #49525 [Serializer] Fix serialized path for non-scalar values (boenner) + * bug #49618 [Serializer] Preserve array keys while denormalize variadic parameters (melya) + * bug #49401 [TwigBridge] Fix raw content rendering in HTML notification emails (1ed) + * bug #49679 [FrameworkBundle] enable metadata cache when annotation is disabled (bastnic) + * bug #49796 [HttpClient] Fix not calling the on progress callback when canceling a MockResponse (fancyweb) + * bug #49850 [FrameworkBundle] Fix auto-discovering validator constraints (nicolas-grekas) + * bug #49867 [DependencyInjection] Filter "container.excluded" services when using `findTaggedServiceIds()` (nicolas-grekas) + * bug #49833 [Translation] TranslatorBag::diff now iterates over catalogue domains instead of operation domains (welcoMattic) + * bug #49848 [Cache] Fix storing binary keys when using pgsql (nicolas-grekas) + * bug #49765 [Console] Add missing ZSH mention in DumpCompletionCommand help (lyrixx) + * bug #49843 [FrameworkBundle] Add missing monolog channel tag for messenger services (rmikalkenas) + * bug #49745 [FrameworkBundle] Fix wiring session.handler when handler_id is null (nicolas-grekas) + * bug #49189 [FrameworkBundle] Improve documentation about translation:extract --sort option (marien-probesys) + * bug #49274 [VarDumper] Disable links for IntelliJ platform (SerafimArts) + * bug #49682 [FrameworkBundle] Workflow - Fix LogicException about a wrong configuration of "enabled" node (adpauly) + * bug #49758 [HttpFoundation] Use separate caches for IpUtils checkIp4 and checkIp6 (danielburger1337) + * bug #49722 [HttpClient] Encode and decode curly brackets {} (pbowyer) + * bug #49720 [Serializer] GetSetMethodNormalizer::supportss should not check ignored methods (nikophil) + * bug #49681 [String] Correct inflection of 'codes' and 'names' (GwendolenLynch) + * bug #49697 [Validator] Update BIC validator IBAN mappings (maxbeckers) + * bug #49706 Stop stopwatch events in case of exception (MatTheCat) + * bug #49657 [HttpKernel] Change limit argument from string to integer for Profiler (Aliance) + * bug #49674 [FrameworkBundle] Rename limiter’s `strategy` to `policy` in XSD (MatTheCat) + * bug #49673 [VarDumper] Fixed dumping of CutStub (lyrixx) + * bug #49604 [Mailer] STDOUT blocks infinitely under Windows when STDERR is filled (TemaYud) + * bug #49651 [DependencyInjection] Fix support binary values in parameters. (vtsykun) + * bug #49548 [Messenger] Fix `TransportNamesStamp` deserialization (tucksaun) + * bug #49580 [HttpClient] Fix encoding "+" in URLs (nicolas-grekas) + * bug #49541 [Security] Remove ``@internal`` tag on `TraceableAuthenticator::getAuthenticator()` (florentdestremau) + * bug #49578 [DependencyInjection] Fix dumping array of enums parameters (fancyweb) + +* 6.2.7 (2023-02-28) + + * bug #49526 [Security] Migrate the session on login only when the user changes (nicolas-grekas) + * bug #49528 [Console] Fix fatal error when accessing Application::signalRegistry without pcntl (lyrixx) + * bug #49516 [Workflow] display label with new lines + colours properly when rendering a PUML dump (alexislefebvre) + * bug #48965 [TwigBridge] Allow floats in html5 input type number field (wimhendrikx) + * bug #48833 [Translation] Handle the translation of empty strings (javiereguiluz) + * bug #49292 [VarDumper] Fix error when reflected class has default Enum parameter in constructor (kapiwko) + * bug #49493 [FrameworkBundle] Fix denyAccessUnlessGranted for mixed attributes (delbertooo) + * bug #49484 [Validator] Fix translation of AtLeastOneOf constraint message (alexandre-daubois) + * bug #48998 [Validator] Sync IBAN formats with Swift IBAN registry (smelesh) + * bug #49488 [Mailer] Update Infobip API transport (ndousson) + * bug #49477 [WebProfilerBundle] Fix the rendering of query explanation with Postgresql (stof) + * bug #49446 [SecurityBundle] Fix `Security::login()` on specific firewall (chalasr) + * bug #49405 [MonologBridge] FirePHPHandler::onKernelResponse throws PHP 8.1 deprecation when no user agent is set (juagarc4) + * bug #49421 [TwigBridge] do not drop embed label classes (xabbuh) + * bug #49459 [Form] Skip password hashing on empty password (Seb33300) + * bug #49422 [Cache][Messenger] fixed CallbackInterface support in async expiration handler (AdamKatzDev) + * bug #49441 [Contracts] Fix setting $container before calling parent::setContainer in ServiceSubscriberTrait (edsrzf) + * bug #49272 [Workflow] remove new lines from workflow metadata (alexislefebvre) + * bug #49427 [WebProfilerBundle] Render original (not encoded) email headers (1ed) + * bug #48897 [Console] fix clear of section with question (maxbeckers) + * bug #49400 [WebProfilerBundle] Tweak Mailer panel rendering (1ed) + * bug #49368 [BC Break] Make data providers for abstract test cases static (OskarStark, alexandre-daubois) + * bug #49379 [DependencyInjection] Fix autowire attribute with nullable parameters (alamirault) + * bug #49385 [Notifier] Make `TransportTestCase` data providers static (alexandre-daubois) + * bug #49395 fix trying to load Memcached before checking we can (nicolas-grekas) + * bug #49326 [Notifier] Fix notifier profiler when transport name is null (fabpot) + * bug #49265 [HttpKernel] Fix setting the session on the main request when it's started by a subrequest (nicolas-grekas) + * bug #49353 [Cache] Only validate dbindex parameter when applicable (loevgaard) + * bug #49346 [ErrorHandler] Do not patch return statements in closures (wouterj) + * bug #49334 [DependencyInjection] keep `proxy` tag on original definition when decorating (kbond) + * bug #47946 [FrameworkBundle] Fix checkboxes check assertions (MatTheCat) + * bug #49301 [HttpClient] Fix data collector (fancyweb) + * bug #49310 [Notifier][WebProfilerBundle] Ignore messages whose `getNotification` returns `null` (MatTheCat) + * bug #49267 [Form] Check for `RepeatedType` child in `PasswordHasherListener` (MatTheCat) + * bug #49299 [HttpClient] Fix over-encoding of URL parts to match browser's behavior (nicolas-grekas) + * bug #49314 [HttpClient] Revert support for "friendsofphp/well-known-implementations" (nicolas-grekas) + * bug #49214 [Mailer] add Sender to the list of bypassed headers (xabbuh) + * bug #49282 [VarExporter] Fix lazy-proxying readonly classes on PHP 8.3 (nicolas-grekas) + * bug #49147 [Intl] Generate all emoji short name returned by slack api (adnen-chouibi) + * bug #49245 [Serializer] Fix CsvEncoder decode on empty data (cazak) + * bug #49255 [Cache] Fix Redis proxies (nicolas-grekas) + * bug #49249 [Dotenv] Fix phpdoc Dotenv (alamirault) + * bug #49247 [Translator] Replace deprecated/removed way to configure enabled_locales (chr-hertel) + * bug #49248 [Config] Fix phpdoc nullable (alamirault) + * bug #48880 [Response] `getMaxAge()` returns non-negative integer (pkruithof, fabpot) + * bug #49207 [PropertyInfo] Add meaningful message when `phpstan/phpdoc-parser` is not installed when using `PhpStanExtractor` (alexandre-daubois) + * bug #49208 [Form] Fix `PasswordHasherListener` to work with empty data (1ed) + * bug #49210 [Mailer] [MailPace] Fix undefined key in error response (OskarStark) + * bug #49220 [Validator] Make ConstraintValidatorTestCase compatible with PHPUnit 10 (gjuric) + * bug #49224 [WebProfilerBundle] Fix an accessibility issue in the search form of the header (javiereguiluz) + * bug #49226 [WebProfilerBundle] Disable Turbo for debug toolbar links (javiereguiluz) + * bug #49223 [WebProfilerBundle] Fix some minor HTML issues (javiereguiluz) + * bug #49146 [PropertyInfo] fail with a meaningful error when a needed package is missing (xabbuh) + * bug #49187 [Ldap] Allow multiple values on `extra_fields` (mvhirsch) + * bug #49128 [DependencyInjection] Fix combinatory explosion when autowiring union and intersection types (nicolas-grekas) + +* 6.2.6 (2023-02-01) + + * bug #49141 [HttpFoundation] Fix bad return type in IpUtils::checkIp4() (tristankretzer) + * bug #49126 [DependencyInjection] Fix order of arguments when mixing positional and named ones (nicolas-grekas) + * bug #49104 [HttpClient] Fix collecting data non-late for the profiler (nicolas-grekas) + * bug #49103 [Security/Http] Fix compat of persistent remember-me with legacy tokens (nicolas-grekas) + * security #cve-2022-24895 [Security/Http] Remove CSRF tokens from storage on successful login (nicolas-grekas) + * security #cve-2022-24894 [HttpKernel] Remove private headers before storing responses with HttpCache (nicolas-grekas) + +* 6.2.5 (2023-01-24) + + * bug #49078 [Security/Http] Check tokens before loading users from providers (nicolas-grekas) + * bug #49077 [DependencyInjection] Fix named arguments when using ContainerBuilder before compilation (nicolas-grekas) + * bug #49031 [Cache] fix collecting cache stats when nesting computations (nicolas-grekas) + * bug #49046 Fix for Windows when projects are deployed on junctions/symlinks (nerdgod) + * bug #49025 [Notifier] [OvhCloud] handle invalid receiver (seferov) + * bug #49034 [Security] Return default value instead of deferring to lower prio resolvers when using #[CurrentUser] and no user is found (Seldaek) + * bug #48993 [VarDumper] Fix JS to expand / collapse (nicolas-grekas) + * bug #48983 Fix BC user_identifier support after deprecation username (vtsykun) + * bug #48986 [Validator] Fix Email validator logic (fabpot) + * bug #48969 [PropertyInfo] Fixes constructor extractor for mixed type (michael.kubovic) + * bug #48978 [Serializer] use method_exists() instead of catching reflection exceptions (xabbuh) + * bug #48958 [DependencyInjection] fixes validation of non-scalar attribute values (ju1ius) + * bug #48937 [SecurityBundle] Fix using same handler for multiple authenticators (RobertMe) + * bug #48971 [DependencyInjection] Fix dump order of inlined deps (nicolas-grekas) + * bug #48966 [HttpClient] Let curl handle content-length headers (nicolas-grekas) + * bug #48968 [VarExporter] Fix exporting enums (nicolas-grekas) + * bug #48933 [Validator] Fix bad handling of nulls when the 'fields' option of the Unique constraint is set (plfort) + * bug #48926 [DependencyInjection] Fix support for named arguments on non-autowired services (nicolas-grekas) + * bug #48943 [FrameworkBundle] Fix deprecation when accessing a "container.private" service from the test container (nicolas-grekas) + * bug #48939 [VarExporter] Fix signature of `Lazy*Trait::createLazy*()` (nicolas-grekas) + * bug #48931 [DependencyInjection] Fix dumping inlined withers (nicolas-grekas) + * bug #48898 [HttpClient] Move Http clients data collecting at a late level (pforesi) + * bug #48896 [DoctrineBridge] Fix detecting mapping with one line annotations (franmomu) + * bug #48916 [FrameworkBundle] restore call to addGlobalIgnoredName (alexislefebvre) + * bug #48917 [Config] Fix XML dump when node example is an array (alexandre-daubois) + * bug #48904 [Validator] Allow egulias/email-validator v4 (chalasr) + * bug #48830 [Translation] fix PhpAstExtractor also extracts messages if t() contains both unnamed and named arguments (gassan) + * bug #48846 [Translation] Fix for resolving Constraint Validator FQCN defined as foo.bar.class parameters (gassan) + * bug #48866 [Validator] fix: Case-insensitive extensions in File-Constraint (spackmat) + * bug #48831 [Uid] Fix validating nil and max uuid (fancyweb) + +* 6.2.4 (2022-12-29) + + * bug #48822 [WebProfilerBundle] Fix the usage of web fonts (javiereguiluz) + * bug #48823 [Cache] Fix possibly null value passed to preg_match() in RedisTrait (chalasr) + * bug #48816 [Cache] Fix for RedisAdapter without auth parameter (rikvdh) + +* 6.2.3 (2022-12-28) + + * bug #48805 [DependencyInjection] Fix resolving parameters when dumping lazy proxies (nicolas-grekas) + * bug #48787 [PhpUnitBridge] Use verbose deprecation output for quiet types only when it reaches the threshold (ogizanagi) + * bug #48784 [Console] Correctly overwrite progressbars with different line count per step (ncharalampidis) + * bug #48801 [Form] Make `ButtonType` handle `form_attr` option (MatTheCat) + * bug #48791 [DependencyInjection] Fix deduplicating service instances in circular graphs (nicolas-grekas) + * bug #48790 [WebProfilerBundle] fix Mailer detail on click (Jean-Beru) + * bug #48771 [CssSelector] Fix escape patterns (fancyweb) + * bug #48774 [Translation] Fix undefined variable messages in ConstraintVisitor (alamirault) + * bug #48727 [PropertyAccess] Fix nullsafe chain like x?.y (Vincz) + * bug #48711 [Cache] RedisTrait::createConnection does not pass auth value from redis sentinel cluster DSN (evgkord) + * bug #48724 [VarExporter] Fix exporting classes with __unserialize() but not __serialize() (fancyweb) + * bug #48746 [Validator] Fix IBAN format for Tunisia and Mauritania (smelesh) + * bug #48738 [Workflow] Allow spaces in place names so the PUML dump doesn't break (Kamil Musial) + * bug #48742 [VarExporter] Generate proxies for static abstract methods (nicolas-grekas) + * bug #48735 [SecurityBundle] Prevent RuntimeException on profiler (marphi) + * bug #48718 Compatibility with doctrine/annotations 2 (derrabus) + * bug #48688 [FrameworkBundle] Add MailPace definition (skmedix) + +* 6.2.2 (2022-12-16) + + * bug #48661 [Serializer] fix context attribute with serializedName (nikophil) + * bug #48681 [Console] Revert "bug #48089 Fix clear line with question in section (maxbeckers) (chalasr) + * bug #48680 [Cache] fix lazyness of redis when using RedisTagAwareAdapter (nicolas-grekas) + * bug #48651 [HttpKernel] AbstractSessionListener should not override the cache lifetime for private responses (rodmen) + * bug #48591 [DependencyInjection] Shared private services becomes public after a public service is accessed (alexpott) + * bug #48126 [Mailer] Include all transports' debug messages in RoundRobin transport exception (mixdf) + * bug #48644 [Validator] Allow opt-out of EmailValidator deprecation when using Validation::createValidatorBuilder() (nicolas-grekas) + * bug #48606 [FrameworkBundle] container:debug CLI output improvements for excluded services (apfelbox) + * bug #48089 [Console] Fix clear line with question in section (maxbeckers) + * bug #48602 [HtmlSanitizer] Fix HtmlSanitizer default configuration behavior for allowed schemes (Titouan Galopin) + * bug #48635 [HttpFoundation] Use relative timestamps with MemcachedSessionHandler (tvlooy) + * bug #47979 [Cache] Fix dealing with ext-redis' multi/exec returning a bool (João Nogueira) + * bug #48612 [Messenger] [Amqp] Added missing rpc_timeout option (lyrixx) + * bug #48233 [Serializer] Prevent `GetSetMethodNormalizer` from creating invalid magic method call (klaussilveira) + * bug #48628 [HttpFoundation] Fix dumping array cookies (nicolas-grekas) + * bug #48559 [ExpressionLanguage] Fix BC of cached SerializedParsedExpression containing GetAttrNode (fancyweb) + * bug #48524 [HttpKernel] Fix `CacheAttributeListener` priority (HypeMC) + * bug #48451 [Translation] Fix extraction when dealing with VariadicPlaceholder parameters (Kocal) + * bug #48601 [SecurityBundle] Fix authenticator existence check in `Security::login()` (chalasr) + * bug #48587 [TwigBundle] Alias BodyRendererInterface (n3o77) + * bug #48580 [Console] Fix missing command not matching namespace error message (Titouan Galopin) + * bug #48449 [DependencyInjection] Fix bug when tag name is a text node (BrandonlinU) + * bug #48048 [WebProfilerBundle] Fix dump header not being displayed (HypeMC) + * bug #47836 [HttpClient] TraceableHttpClient: increase decorator's priority (adpeyre) + * bug #48259 [FrameworkBundle] Allow configuring `framework.exceptions` with a config builder (MatTheCat) + * bug #48314 [Mime] Fix MessagePart serialization (Amunak) + * bug #48331 [Yaml] fix dumping top-level tagged values (xabbuh) + * bug #48615 Fix getting the name of closures on PHP 8.1.11+ (nicolas-grekas) + * bug #48624 [ErrorHandler][HttpKernel] Fix reading the SYMFONY_IDE env var (nicolas-grekas) + * bug #48618 [ErrorHandler] [DebugClassLoader] Fix some new return types support (fancyweb) + * bug #48605 [VarExporter] Fix adding a key to an uninitialized array (nicolas-grekas) + * bug #48554 [Security] Fix invalid deprecation messages in Security constants (IonBazan) + * bug #48538 [Clock] Fix `usleep` deprecation warning (victor-prdh) + * bug #48421 [HttpFoundation] IPv4-mapped IPv6 addresses incorrectly rejected (bonroyage) + * bug #48501 [RateLimiter] Add `int` to `Reservation::wait()` (DaRealFreak) + * bug #48359 [VarDumper] Ignore \Error in __debugInfo() (fancyweb) + * bug #48553 [VarExporter] Fix calling `parent::__wakeup()` when unserializing with LazyProxyTrait (azjezz) + * bug #48489 [DoctrineBridge] Skip resolving entities when the corresponding request attribute is already an object (nicolas-grekas) + * bug #48534 [FrameworkBundle] add `kernel.locale_aware` tag to `LocaleSwitcher` (kbond) + * bug #48521 [FrameworkBundle] fix removing commands if console not available (kbond) + * bug #48522 [DependencyInjection] Generate different classes for ghost objects and virtual proxies (nicolas-grekas) + * bug #48482 [DependencyInjection] Revert "bug #48027 Don't autoconfigure tag when it's already set with attributes" (nicolas-grekas) + +* 6.2.1 (2022-12-06) + + * bug #48502 [DependencyInjection] Fix `ContainerBuilder` stats env usage with enum (alamirault) + * bug #48509 [HttpKernel] Fix using entities with the `#[Cache()]` attribute (HypeMC) + * bug #48505 [Mailer] Fix rendered templates for notifications (fabpot) + * bug #48476 [WebProfilerBundle] Use same color as other icons for the close toolbar btn (ogizanagi) + * bug #48483 [DependencyInjection] Remove refs that point to container.excluded services when allowed (nicolas-grekas) + * bug #48346 [HttpKernel] In DateTimeValueResolver, convert previously defined date attribute to the expected class (GromNaN) + * bug #48450 [WebProfilerBundle] Fix form panel expanders (MatTheCat) + * bug #48459 [FrameworkBundle] [Framework] Fix Infobip Mailer transport factory import (gnito-org) + * bug #48461 [VarExporter] Fix possible memory-leak when using lazy-objects (nicolas-grekas) + * bug #48335 [TwigBridge] Amend `MoneyType` twig to include a space (mogilvie) + * bug #48046 [WebProfilerBundle] Remove redundant code from logger template (HypeMC) + * bug #48428 Fixed undefined variable error (Kevin Meijer) + * bug #48416 [FrameworkBundle] don't register the MailerTestCommand symfony/console is not installed (xabbuh) + +* 6.2.0 (2022-11-30) + + * bug #48395 [String] Fix AsciiSlugger with emojis (fancyweb) + * bug #48385 [Security] Reuse `AbstractFactory`'s config tree in `AccessTokenFactory` (chalasr) + * bug #48292 [Security] [LoginLink] Throw InvalidLoginLinkException on missing parameter (MatTheCat) + +* 6.2.0-RC2 (2022-11-28) + + * bug #48366 [Mailer] Fix body renderer check (fabpot) + * bug #48347 [Clock] Autowire PSR interface (wouterj) + * bug #48341 [SecurityBundle] Fix `logout.csrf_token_generator` default value (MatTheCat) + * bug #48333 [Yaml] parse unquoted digits in tag values as integers (xabbuh) + * bug #48330 [FrameworkBundle] do not wire the MercureTransportFactory if the MercureBundle is not enabled (xabbuh) + * bug #48320 [Clock] Implement PSR-20 (nicolas-grekas) + +* 6.2.0-RC1 (2022-11-25) + + * bug #48312 [VarExporter] Improve partial-initialization API for ghost objects (nicolas-grekas) + * bug #48285 [Security] Support loading UserBadge directly from accessToken (Jeroeny) + * bug #48262 [Notifier] [SMSBiuras] `true`/`false` mismatch for `test_mode` option (StaffNowa) + * bug #48273 [HttpKernel] Fix message for unresovable arguments of invokable controllers (fancyweb) + * bug #48251 [PropertyInfo] ignore const expressions read by phpdocumentor (xabbuh) + * bug #48224 [DependencyInjection] Process bindings in `ServiceLocatorTagPass` (MatTheCat) + * bug #48271 [WebProfilerBundle] Fix form panel when there are no view vars (nicolas-grekas) + * bug #48274 Add more #[\SensitiveParameter] (fancyweb) + * bug #48179 [Console] Support completion for bash functions (Chi-teck) + +* 6.2.0-BETA3 (2022-11-19) + + * bug #48217 [Console] Improve error message when shell is not detected in completion command (GromNaN) + * bug #48222 [Translation] [Lokalize] Configure `replace_breaks` to prevent issues with multilines translations (Kocal) + * bug #48210 [Console]  Fix signal handlers called after event listeners and skip exit (GromNaN) + * bug #48198 [Messenger] Fix time-limit check exception (alamirault) + * feature #48189 [Clock] Provide `modify()` in MockClock (dbrumann) + * bug #48207 [Yaml] Restore deprecated php/const: syntax in YAML key (GromNaN) + * bug #48209 [VarExporter] Use `array` for partial initialization of lazy ghost objects (nicolas-grekas) + * bug #48186 [WebProfilerBundle] Minor tweaks in profiler redesign (javiereguiluz) + * bug #48122 [PhpUnitBridge] Fix language deprecations incorrectly marked as direct (wouterj) + * bug #47998 [Console] Fix console `ProgressBar::override()` after manual `ProgressBar::cleanup()` (maxbeckers) + * bug #48041 [FrameworkBundle] Split loggers debug compiler pass (MatTheCat) + * bug #48032 [SecurityBundle] Set `UserValueResolver`'s priority higher than `EntityValueResolver` (kbond) + * bug #48156 [Mime] When serializing File parts convert to string to allow proper unserialization (ovrflo) + * bug #48170 [Routing] Fix PSR-4 directory loader for abstract classes (derrabus) + * bug #48173 [HttpClient] Handle Amp HTTP client v5 incompatibility gracefully (fancyweb) + * bug #48172 [HttpKernel] Don’t try to wire Response argument with controller.service_arguments (MatTheCat) + * bug #48160 Adding missing argument to sprintf (weaverryan) + * bug #48085 [Messenger] Tell about messenger:consume invalid limit options (MatTheCat) + * bug #48120 [Messenger] Do not throw 'no handlers' exception when skipping handlers due to duplicate handling (wouterj) + * bug #48112 [HttpFoundation] Compare cookie with null value as empty string in ResponseCookieValueSame (fancyweb) + * bug #48119 [FrameworkBundle][Lock] Allow to disable lock without defining a resource (MatTheCat) + * bug #48110 [HttpKernel] Fix deprecation for DateTimeValueResolver with null on non-nullable argument (GromNaN) + * bug #48093 [DependencyInjection] don't move locator tag for service subscriber (RobertMe) + * bug #48075 [Mailer] Stream timeout not detected fgets returns false (Sezil) + * bug #48092 Fix the notification email theme for asynchronously dispatched emails (krisbuist) + * bug #48097 Fix search scope when performing fallback mapping driver detection (spideyfusion) + * bug #48103 [HttpClient] Do not set http_version instead of setting it to null (Tetragramat) + * bug #48077 [FrameworkBundle] Allow UUID v7 in uid configuration (achterin) + * bug #48027 [DependencyInjection] Don't autoconfigure tag when it's already set with attributes (nicolas-grekas) + * feature #48045 [DependencyInjection] Allow enum as service parameter in php config files (alexndlm) + * bug #48062 [Notifier] Mark tokens with #[SensitiveParameter] (nicolas-grekas) + * bug #48050 [HttpFoundation] Check IPv6 is valid before comparing it (PhilETaylor) + +* 6.2.0-BETA2 (2022-10-28) + + * bug #48020 [FrameworkBundle] add router cache directory option to XML schema (xabbuh) + * feature #47976 Add padding to HIBP check (rullzer) + * bug #47990 [HttpClient] Fix retrying requests when the content is used by the strategy (nicolas-grekas) + * bug #48005 [ErrorHandler] s/\/\ (PhilETaylor) + * bug #47907 [Console] Update Application.php (aleksandr-shevchenko) + * bug #47992 [Mailer] Fix BC breaking event name change (chalasr) + +* 6.2.0-BETA1 (2022-10-24) + + * feature #47364 [DependencyInjection] Allow array attributes for service tags (aschempp) + * feature #44166 [Config] Use better typehint in PHP Configuration (jderusse) + * feature #47956 [Notifier] Add support for editing Telegram messages (chr-hertel) + * feature #43534 [Serializer] Add `SerializedPath` annotation to flatten nested attributes (boenner) + * feature #47943 [Config][Routing] Nicer config syntax for PSR-4 route loading (derrabus) + * feature #46224 [Form] Add `hash_property_path` option to `PasswordType` (Seb33300) + * feature #47950 [HttpClient] Add support for "friendsofphp/well-known-implementations" (nicolas-grekas) + * feature #47936 [HttpClient] Add `withOptions()` to `HttplugClient` and `Psr18Client` (nicolas-grekas) + * feature #46053 [Messenger] Add option `allow_no_senders` to enable throwing when a message doesn't have a sender (babeuloula) + * feature #45907 [SecurityBundle] Allow specifying attributes for `RequestMatcher` (freiondrej-lmc) + * feature #47483 [HttpKernel] Make Logger implement DebugLoggerInterface (MatTheCat) + * feature #46161 [Translation] Add `PhpAstExtractor` (welcoMattic) + * feature #47872 [Validator] new email validation option to match with w3c official specification (guillemfondin) + * feature #47916 [Routing] PSR-4 directory loader (derrabus) + * feature #47890 [SecurityBundle] Deprecate the `enable_authenticator_manager` option (chalasr) + * feature #47906 [DependencyInjection] Allow injecting the current env into php config closures (HypeMC) + * feature #47902 [DependencyInjection] Add support for tagged iterators/locators exclude option to xml and yaml (HypeMC) + * feature #47801 [DependencyInjection] Allow array for the value of Autowire attribute (willemverspyck) + * feature #47864 [DoctrineBridge] Deprecate calling `ContainerAwareEventManager::getListeners()` without event name (derrabus) + * feature #47711 [Mime] deprecate attach/embed methods in favor of Email::addPart() (fabpot) + * feature #47832 [HttpClient] Make HttplugClient implement PSR-17 factories instead of Httplug's (nicolas-grekas) + * feature #47817 [Security] TraceableAccessDecisionManager: fix inspecting voters of custom access decision managers (sarbanha) + * feature #47750 [Console] Show available commands in namespace when running namespace as command (wouterj) + * feature #47730 Ban DateTime from the codebase (WebMamba) + * feature #47496 [FrameworkBundle] Make the Router `cache_dir` configurable (mpdude) + * feature #47511 [Form][PropertyAccess] Allow optional property accesses (fsoedjede) + * feature #47068 [Messenger] Deprecate MessageHandlerInterface and MessageSubscriberInterface (alamirault) + * feature #47460 [Messenger] add dedicated method for disabling instead of passing boolean flags (xabbuh) + * feature #47643 [WebProfilerBundle] Update the mailer panel (javiereguiluz) + * feature #47710 [Validator] File: add option to check extension (dunglas) + * feature #47734 [Validator] add the getCause() to the ConstraintViolationInterface (xabbuh) + * feature #47308 [Console] Allow limiting the height of a console section (wouterj) + * feature #47243 Add context option to configure the indentation of nested nodes for `YamlEncoder` (dbu) + * feature #47462 [Mime] Simplify adding Parts to an Email (fabpot) + * feature #47683 [DependencyInjection] Deprecate numeric parameter names (HeahDude) + * feature #47377 [HttpKernel] Use Accept-Language header even if there are no enabled locales (MatTheCat) + * feature #47588 Add warning for possibly truncated inputs in QuestionHelper (#47586) (pbek) + * feature #47665 [WebProfilerBundle] [WebProfilerPanel] Update the configuration panel (javiereguiluz) + * feature #47630 [FrameworkBundle] Add semantic config for new terminate_on_cache_hit HttpCache option (wouterj) + * feature #47595 [HttpFoundation] Extract request matchers for better reusability (fabpot) + * feature #47535 [TwigBridge] Expose current route in `AppVariable` (HeahDude) + * feature #47536 [TwigBundle] add option for configuring custom HTML to text converter services (xabbuh) + * feature #46064 [Security] Add a `ChainUserChecker` to allow calling multiple user checkers for a firewall (mbabker) + * feature #47445 [FrameworkBundle] Allow secrets vaults to be used directly outside Symfony (AndreasA) + * feature #47148 [WebProfilerBundle] Profiler redesign (javiereguiluz) + * feature #38996 Remove the default values from setters with a nullable parameter (derrabus, nicolas-grekas) + * feature #42593 [Validator] Add the `When` constraint and validator (wuchen90) + * feature #47525 [Uid] Add UuidV7 and UuidV8 (nicolas-grekas) + * feature #47515 [Uid] Add MaxUuid and MaxUlid (nicolas-grekas) + * feature #47407 [Console] Terminal Color Mode refactoring and force Color Mode (julien-boudry) + * feature #47507 [Uid] Add interface for `getDateTime()` and apply to relevant UIDs (shrikeh) + * feature #47236 [DependencyInjection][VarExporter] Generate lazy-loading virtual proxies for non-ghostable lazy services (nicolas-grekas) + * feature #39622 [Messenger] Be able to get raw data when a message in not decodable by the PHP Serializer (lyrixx) + * feature #47311 [FrameworkBundle] Update ContainerDebugCommand to add parial search for tags (vshevelev, BOB41K1987) + * feature #47367 [DependencyInjection] Handle INI arrays (MatTheCat) + * feature #47373 [Notifier] Add Chatwork Notifier Bridge (Ippey) + * feature #47363 [HttpKernel] Replace ArgumentValueResolverInterface by ValueResolverInterface (nicolas-grekas) + * feature #47101 [DependencyInjection] Allow service subscribers to return `SubscribedService[]` (kbond) + * feature #40152 [Messenger] Pass sender details to SendMessageToTransportsEvent (Jeroeny) + * feature #41171 [Messenger] Add simple transport based rate limiter to Messenger (bobvandevijver) + * feature #47295 [PhpUnitBridge] add ability to mock the hrtime() function (xabbuh) + * feature #47264 [String] Add support for emoji in AsciiSlugger (lyrixx) + * feature #47263 [Intl] Update EmojiTransliterator to translate emoji to github and slack short code (lyrixx) + * feature #45418 [Messenger] Add HandlerArgumentsStamp (enumag) + * feature #47094 [HttpKernel] Use xxh128 algorithm instead of sha256 for http cache store key (Pascal Woerde) + * feature #46000 [Workflow] Mark registry as internal and deprecate the service (lyrixx) + * feature #46428 [Security] Access Token Authenticator (Spomky) + * feature #47225 [Mime] Re-allow addIdHeader to be used for 'In-Reply-To' and 'References' headers (AlbinoDrought) + * feature #47190 [Mailer] Add a way to change the Bus transport dynamically (fabpot) + * feature #47201 [Mime] Add a way to control the HTML to text conversion (fabpot) + * feature #47202 [Serializer] enable JSON_PRESERVE_ZERO_FRACTION by default (dbu) + * feature #39306 [Messenger] Add `TransportNamesStamp` to change the transport while dispatching a message (asilelik, fabpot) + * feature #47196 Allow extending `#[When]` attribute (ruudk) + * feature #47191 [Mailer] Add a way to inject Stamps when sending an email via Messenger (fabpot) + * feature #47170 [Mailer] Use better error code when auth fails (fabpot) + * feature #46978 [Security] Allow using expressions with the #[IsGranted] attribute (HypeMC) + * feature #46571 [Messenger] Add new `messenger:count` command that return a list of transports with their "to be processed" message count. (ktherage, ogizanagi, EXT - THERAGE Kevin) + * feature #43865 [TwigBridge] Add support for toggle buttons in Bootstrap 5 form theme (ker0x) + * feature #46683 [Ldap] Deprecate '{username}' parameter use in favour of '{user_identifier}' in LDAP configuration (EXT - THERAGE Kevin) + * feature #46514 [HttpKernel] Add option to render Surrogate fragment with absolute URIs (Kern046) + * feature #46715 [Clock] A new component to decouple applications from the system clock (nicolas-grekas) + * feature #42355 [HttpKernel] Bugfix/last modified response strategy (aschempp) + * feature #47080 [Mailer] Add new events (fabpot) + * feature #47075 [Mime] Change the way we avoid rendering an email twice (fabpot) + * feature #46755 [Intl] Add `EmojiTransliterator` to translate emoji to many locales (lyrixx, nicolas-grekas) + * feature #47062 [Console] Don't cut Urls wrapped in SymfonyStyle block (fchris82, GromNaN) + * feature #45987 [Notifier] Add `from` in `SmsMessage` (alamirault) + * feature #46142 [ExpressionLanguage] Add support for null coalescing syntax (mytuny) + * feature #47050 [Form] Allow TranslatableInterface to the FormType help option (alamirault) + * feature #46110 [RateLimiter][Security] Improve performance of login/request rate limiter (Seldaek, wouterj) + * feature #46895 [Notifier] Introduce PHPUnit constraints and assertions for the Notifier (ismail1432) + * feature #47049 [Mailer] Throw a more specific exception when a BodyRendererInterface is needed but not configured (fabpot) + * feature #47040 Add a mailer:test command (fabpot) + * feature #46242 [Console] Add support for resuming a ProgressBar (yivi) + * feature #46962 [Mime] Add DataPart::setContentId() (fabpot) + * feature #47038 [Notifier] Add Notification::exception() (fabpot) + * feature #46944 [Console] Add Ansi8 (256 color) support, improve true color (Ansi24) support detection (julien-boudry) + * feature #47034 [Mime] Simplify code (fabpot) + * feature #47018 [Console] Zsh shell autocompletion (adhocore, GromNaN) + * feature #46591 [Finder] Add methods to sort by extension & size (sandoba) + * feature #46126 [Finder] Case insensitive file sort (hmoreau) + * feature #45034 [HttpFoundation] Rename Request::getContentType to getContentTypeFormat (MarkPedron) + * feature #46806 [Cache][WebProfilerBundle] Add adapter class to Cache `DataCollector` (Jean-Beru) + * feature #44902 Add visibility context option in PropertyNormalizer (alamirault) + * feature #46567 [Security] [LoginLink] Set custom lifetime for login link (mbrodala, fabpot) + * feature #46599 Add "negate" option to Expression constraint (fmata) + * feature #46821 [FrameworkBundle] Add `resolve-env` option to debug:config command (alexandre-daubois) + * feature #46580 [SecurityBundle] Add shortcut option to enable logout CSRF protection (wouterj) + * feature #46814 [FrameworkBundle] Add service and alias deprecation message to debug:container output (94noni) + * feature #47008 [Messenger] Add options to `FailedMessagesShowCommand` (Florian Guimier, fabpot) + * feature #45977 [Validator] Add the match option to the Choice constraint (fancyweb) + * feature #46338 [Security] Allow configuring a target url when switching user (94noni) + * feature #46326 SMTP Transport to provide the (final) Message-ID if available (Raphaël Droz) + * feature #43854 [DoctrineBridge] Add an Entity Argument Resolver (jderusse, nicolas-grekas) + * feature #46315 [Mailer] `max_per_second` option configurable via DSN (gassan) + * feature #46118 [Security] Don't allow empty username or empty password (bikalbasnet) + * feature #46229 [Messenger] Make Redis messages countable (Jean-Beru) + * feature #41406 [Security] Add a method in the security helper to ease programmatic logout (johnkrovitch, chalasr) + * feature #45404 [Mailer] allow custom hosts for ses+smtp with amazon mailer (jrushlow) + * feature #45945 [Uid] Added toHexString method to AbstractUid class (aurimasniekis) + * feature #46642 [DoctrineBridge] Add `NAME` const for UID types (marcelsiegert) + * feature #46502 [Dotenv] Variable filter added to debug command (rmikalkenas) + * feature #46211 [Mailer] Add Infobip bridge (B-Galati) + * feature #46773 [VarDumper] Add `FFI\CData` and `FFI\CType` types (SerafimArts) + * feature #46946 [Form] ChoiceType choices must support TranslatableInterface (alamirault) + * feature #38903 [FrameworkBundle] Add "Usages" to debug:container output (Bert ter Heide, bertterheide) + * feature #46901 [Console] Be explicit about the completion API version (wouterj) + * feature #46752 [DependencyInjection] Use lazy-loading ghost object proxies out of the box (nicolas-grekas) + * feature #46880 [HttpKernel] Add `#[Cache()]` to describe the default HTTP cache headers on controllers (nicolas-grekas) + * feature #46751 [VarExporter] Add trait to help implement lazy loading ghost objects (nicolas-grekas) + * feature #46906 [TwigBridge] Add `#[Template()]` to describe how to render arrays returned by controllers (nicolas-grekas) + * feature #46907 [Security] Add `#[IsGranted()]` (nicolas-grekas) + * feature #46183 Hide sensitive information with `SensitiveParameter` attribute (GromNaN) + * feature #46896 Console/SymfonyStyle: Add Multiselect to choice() (julien-boudry) + * feature #46883 [DependencyInjection] Add `shuffle` env processor (ostrolucky) + * feature #46846 [Notifier] Add Zendesk Notifier Bridge (stloyd) + * feature #46001 [HttpKernel] Add `ControllerEvent::getAttributes()` to handle attributes on controllers (nicolas-grekas) + * feature #46854 [FrameworkBundle] Make `AbstractController::render()` able to deal with forms and deprecate `renderForm()` (nicolas-grekas) + * feature #41274 [Security] Add a method in the security helper to ease programmatic login (#40662) (johnkrovitch, chalasr) + * feature #46831 Add deprecation when the session is not FlashBagAware (VincentLanglet) + * feature #46491 Introduce FlashBagAwareSessionInterface (VincentLanglet) + * feature #46813 [Form] Provide string keys when iterating on a form (VincentLanglet) + * feature #46680 [Serializer] Provide context information from attribute for promoted properties (DanielBadura) + * feature #46564 [DependencyInjection] Add Enum Env Var Processor (jack-worman) + * feature #46763 [HttpCache] Do not call terminate() on cache hit (Toflar) + * feature #45997 [FrameworkBundle][HttpKernel] Add deprecation warning to show `HttpKernel::handle()` will catch throwables (Nyholm) + * feature #46714 [Mailer] Deprecate OhMySmtp Transport, Create MailPace transport (Holicz) + * feature #46771 [Yaml] Add support for `!php/enum *->value` syntax (nicolas-grekas) + * feature #46395 [Notifier] Add Contact Everyone Bridge (franckranaivo) + * feature #46724 [Notifier] Add SMSFactor bridge (Gwemox) + * feature #46741 [DependencyInjection] Allow using ghost objects for lazy loading services (nicolas-grekas) + * feature #46675 [Serializer] Add support of true built-in type (from PHP 8.2) (bobahvas, alexandre-daubois) + * feature #46663 [Serializer] Deprecate autowiring aliases pointing to concrete normalizers (chalasr) + * feature #46584 [Security] Enforce maximum username length in UserBadge (wouterj) + * feature #46066 [Security] Add an easier way to get the current firewall configuration (Kocal) + * feature #46614 Remove Debug component leftovers (chalasr) + * feature #46566 [Serializer][WebProfilerBundle] Show serializer collector info in toolbar (ogizanagi) + * feature #46569 [Serializer][WebProfilerBundle] Collect & show caller source code (ogizanagi) + * feature #46094 [Security][SecurityBundle] Move the `Security` helper to SecurityBundle (chalasr) + * feature #46518 [Validator] deprecate the loose e-mail validation mode (xabbuh) + * feature #45985 [TwigBridge] Add form_label_content and form_help_content block to form_div_layout (alexander-schranz) + * feature #46430 [Routing] Add `Requirement::POSITIVE_INT` for common ids and pagination (HeahDude) + * feature #46279 [DependencyInjection] Optimize autowiring logic by telling it about excluded symbols (nicolas-grekas) + * feature #46452 [DependencyInjection] Add Hydrator::hydrate() and preserve PHP references when using it (nicolas-grekas) + diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 99d8c121d9fce..4b2f33954ad21 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -21,103 +21,109 @@ The Symfony Connect username in parenthesis allows to get more information - Jordi Boggiano (seldaek) - Roland Franssen (ro0) - Victor Berchet (victor) + - Javier Eguiluz (javier.eguiluz) - Yonel Ceruto (yonelceruto) + - Ryan Weaver (weaverryan) - Tobias Nyholm (tobias) - Oskar Stark (oskarstark) - - Ryan Weaver (weaverryan) - - Javier Eguiluz (javier.eguiluz) - Johannes S (johannes) - Jakub Zalas (jakubzalas) - Kris Wallsmith (kriswallsmith) - Hugo Hamon (hhamon) - Hamza Amrouche (simperfit) - Samuel ROZE (sroze) + - Jules Pietri (heah) - Pascal Borreli (pborreli) - Romain Neutron - - Jules Pietri (heah) - Joseph Bielawski (stloyd) + - Alexandre Daubois (alexandre-daubois) - Drak (drak) - Abdellatif Ait boudad (aitboudad) - - Lukas Kahwe Smith (lsmith) + - Jérôme Tamarelle (gromnan) - Jan Schädlich (jschaedl) + - Lukas Kahwe Smith (lsmith) + - Kevin Bond (kbond) - Martin Hasoň (hason) - - Jérôme Tamarelle (gromnan) - Jeremy Mikola (jmikola) - Jean-François Simon (jfsimon) - Benjamin Eberlei (beberlei) - - Kevin Bond (kbond) - Igor Wiedler + - HypeMC (hypemc) + - Antoine Lamirault (alamirault) - Valentin Udaltsov (vudaltsov) - Vasilij Duško (staff) - - HypeMC (hypemc) - Matthias Pigulla (mpdude) - Laurent VOULLEMIER (lvo) - - Pierre du Plessis (pierredup) + - Gabriel Ostrolucký (gadelat) - Antoine Makdessi (amakdessi) + - Mathieu Lechat (mat_the_cat) + - Pierre du Plessis (pierredup) - Grégoire Paris (greg0ire) - - Gabriel Ostrolucký (gadelat) - Jonathan Wage (jwage) - - David Maicher (dmaicher) - Titouan Galopin (tgalopin) + - David Maicher (dmaicher) + - Alexander Schranz (alexander-schranz) + - Gábor Egyed (1ed) + - Mathieu Santostefano (welcomattic) - Alexandre Salomé (alexandresalome) - William DURAND - - Alexander Schranz (alexander-schranz) - ornicar - Dany Maillard (maidmaid) - Eriksen Costa - Diego Saint Esteben (dosten) - stealth35 ‏ (stealth35) - Alexander Mols (asm89) - - Gábor Egyed (1ed) - Francis Besset (francisbesset) - - Alexandre Daubois (alexandre-daubois) + - Vincent Langlet (deviling) - Vasilij Dusko | CREATION - - Mathieu Santostefano (welcomattic) - Bulat Shakirzyanov (avalanche123) - Iltar van der Berg - Miha Vrhovnik (mvrhov) - - Saša Stamenković (umpirsky) - Mathieu Piot (mpiot) + - Saša Stamenković (umpirsky) - Alex Pott - Guilhem N (guilhemn) - Vladimir Reznichenko (kalessil) - Sarah Khalil (saro0h) + - Tomas Norkūnas (norkunas) - Konstantin Kudryashov (everzet) - - Vincent Langlet (deviling) - Bilal Amarni (bamarni) - - Tomas Norkūnas (norkunas) - Eriksen Costa + - Ruud Kamphuis (ruudk) - Florin Patan (florinpatan) + - Konstantin Myakshin (koc) - Peter Rehm (rpet) - Henrik Bjørnskov (henrikbjorn) - - Konstantin Myakshin (koc) + - David Buchmann (dbu) + - Massimiliano Arione (garak) - Andrej Hudec (pulzarraider) - Julien Falque (julienfalque) - - Massimiliano Arione (garak) + - Jáchym Toušek (enumag) - Douglas Greenshields (shieldo) + - Mathias Arlaud (mtarld) - Christian Raue - - David Buchmann (dbu) + - Fran Moreno (franmomu) - Graham Campbell (graham) - Michel Weimerskirch (mweimerskirch) - Eric Clemmons (ericclemmons) - Issei Murasawa (issei_m) - - Fran Moreno (franmomu) - - Jáchym Toušek (enumag) - Malte Schlüter (maltemaltesich) - - Mathias Arlaud (mtarld) - - Vasilij Dusko - Denis (yethee) + - Vasilij Dusko - Arnout Boks (aboks) - Charles Sarrazin (csarrazi) - Przemysław Bogusz (przemyslaw-bogusz) - Henrik Westphal (snc) - Dariusz Górecki (canni) - Maxime Helias (maxhelias) + - Gary PEGEOT (gary-p) - Ener-Getick + - Tugdual Saunier (tucksaun) + - Rokas Mikalkėnas (rokasm) - Sebastiaan Stok (sstok) - - Ruud Kamphuis (ruudk) - Jérôme Vasseur (jvasseur) - Ion Bazan (ionbazan) + - Yanick Witschi (toflar) - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) @@ -129,108 +135,116 @@ The Symfony Connect username in parenthesis allows to get more information - John Wards (johnwards) - Dariusz Ruminski - Lars Strojny (lstrojny) - - Yanick Witschi (toflar) - Antoine Hérault (herzult) - Konstantin.Myakshin - - Rokas Mikalkėnas (rokasm) - Arman Hosseini (arman) + - gnito-org + - Saif Eddin Gmati (azjezz) + - Simon Berger - Arnaud Le Blanc (arnaud-lb) - Maxime STEINHAUSSER - Peter Kokot (maastermedia) - - Saif Eddin Gmati (azjezz) + - jeremyFreeAgent (jeremyfreeagent) - Ahmed TAILOULOUTE (ahmedtai) - - Simon Berger + - Joel Wurtz (brouznouf) - Tim Nagel (merk) + - Allison Guilhem (a_guilhem) - Andreas Braun - Teoh Han Hui (teohhanhui) - YaFou - - Gary PEGEOT (gary-p) - Chris Wilkinson (thewilkybarkid) - Brice BERNARD (brikou) - Roman Martinuk (a2a4) - Gregor Harlan (gharlan) - - Antoine Lamirault + - Christopher Hertel (chertel) - Baptiste Clavié (talus) - Adrien Brault (adrienbrault) - Michal Piotrowski - marc.weistroff - lenar - Jesse Rushlow (geeshoe) + - Jacob Dreesen (jdreesen) - Théo FIDRY - - jeremyFreeAgent (jeremyfreeagent) + - Jeroen Spee (jeroens) + - Michael Babker (mbabker) - Włodzimierz Gajda (gajdaw) + - Hugo Alliaume (kocal) - Christian Scheb - Guillaume (guill) - - Mathieu Lechat (mat_the_cat) - - Tugdual Saunier (tucksaun) - - Jacob Dreesen (jdreesen) - - Joel Wurtz (brouznouf) - - Michael Babker (mbabker) - Olivier Dolbeau (odolbeau) - Florian Voutzinos (florianv) - zairig imad (zairigimad) - Colin Frei - Javier Spagnoletti (phansys) - excelwebzone + - Phil Taylor (prazgod) - Jérôme Parmentier (lctrs) - HeahDude - Richard van Laak (rvanlaak) + - Nicolas Philippe (nikophil) - Paráda József (paradajozsef) + - Hubert Lenoir (hubert_lenoir) - Alessandro Lai (jean85) - Alexander Schwenn (xelaris) - Fabien Pennequin (fabienpennequin) - Gordon Franke (gimler) - - Jeroen Spee (jeroens) - - Christopher Hertel (chertel) + - François-Xavier de Guillebon (de-gui_f) + - Andreas Schempp (aschempp) - Gabriel Caruso + - Joshua Thijssen - Anthony GRASSIOT (antograssiot) - Jan Rosier (rosier) + - Andreas Möller (localheinz) - Daniel Wehner (dawehner) + - Gocha Ossinkine (ossinkine) + - Chi-teck - Hugo Monteiro (monteiro) - Baptiste Leduc (korbeil) - Marco Pivetta (ocramius) - Robert Schönthal (digitalkaoz) + - Michael Voříšek + - Alexis Lefebvre - Võ Xuân Tiến (tienvx) - fd6130 (fdtvui) - Tigran Azatyan (tigranazatyan) - Eric GELOEN (gelo) - Matthieu Napoli (mnapoli) + - Ben Davies (bendavies) - Tomáš Votruba (tomas_votruba) - - Joshua Thijssen - Stefano Sala (stefano.sala) - Alessandro Chitolina (alekitto) - Valentine Boineau (valentineboineau) - Jeroen Noten (jeroennoten) - - Gocha Ossinkine (ossinkine) - - Andreas Möller (localheinz) - OGAWA Katsuhiro (fivestar) + - Dāvis Zālītis (k0d3r1s) - Jhonny Lidfors (jhonne) - Martin Hujer (martinhujer) - Wouter J + - Guilliam Xavier + - David Prévot + - Sergey (upyx) + - Antonio Pauletich (x-coder264) - Timo Bakx (timobakx) - Juti Noppornpitak (shiroyuki) - Joe Bennett (kralos) - - Hugo Alliaume (kocal) + - Nate Wiebe (natewiebe13) + - Farhad Safarov (safarov) - Anthony MARTIN - Colin O'Dell (colinodell) - Sebastian Hörl (blogsh) - - Ben Davies (bendavies) - - François-Xavier de Guillebon (de-gui_f) + - Daniel Burger - Daniel Gomes (danielcsgomes) - Michael Käfer (michael_kaefer) - Hidenori Goto (hidenorigoto) - Albert Casademont (acasademont) - Arnaud Kleinpeter (nanocom) - Guilherme Blanco (guilhermeblanco) - - Chi-teck - - Guilliam Xavier - - Nate Wiebe (natewiebe13) - - Michael Voříšek - SpacePossum - - Andreas Schempp (aschempp) - Pablo Godel (pgodel) + - Denis Brumann (dbrumann) - Romaric Drigon (romaricdrigon) - Andréia Bohner (andreia) + - Bastien Jaillot (bastnic) - Jannik Zschiesche - Rafael Dohms (rdohms) - George Mponos (gmponos) @@ -238,45 +252,43 @@ The Symfony Connect username in parenthesis allows to get more information - Aleksandar Jakovljevic (ajakov) - jwdeitch - Jurica Vlahoviček (vjurica) - - David Prévot - - Antonio Pauletich (x-coder264) - Vincent Touzet (vincenttouzet) - Fabien Bourigault (fbourigault) - - Farhad Safarov (safarov) + - soyuka - Jérémy Derussé - - Nicolas Philippe (nikophil) - - Hubert Lenoir (hubert_lenoir) + - Sébastien Alfaiate (seb33300) - Florent Mata (fmata) - mcfedr (mcfedr) - - Denis Brumann (dbrumann) - Maciej Malarz (malarzm) - Soner Sayakci - Artem Lopata - Sokolov Evgeniy (ewgraf) - Stadly - Justin Hileman (bobthecow) + - Tom Van Looy (tvlooy) - Niels Keurentjes (curry684) - Vyacheslav Pavlov - - Dāvis Zālītis (k0d3r1s) - Richard Shank (iampersistent) + - Thomas Landauer (thomas-landauer) + - Romain Monteil (ker0x) - Andre Rømcke (andrerom) - Dmitrii Poddubnyi (karser) - - soyuka - Rouven Weßling (realityking) - BoShurik - Zmey + - Maximilian Beckers (maxbeckers) - Clemens Tolboom - Oleg Voronkovich + - Alan Poulain (alanpoulain) - Helmer Aaviksoo - Michał (bambucha15) - Remon van de Kamp - Ben Hakim + - Martin Auswöger - Sylvain Fabre (sylfabre) - Filippo Tessarotto (slamdunk) - - Tom Van Looy (tvlooy) - 77web - Bohan Yang (brentybh) - - Bastien Jaillot (bastnic) - W0rma - Matthieu Ouellette-Vachon (maoueh) - Lynn van der Berg (kjarli) @@ -285,20 +297,21 @@ The Symfony Connect username in parenthesis allows to get more information - Amal Raghav (kertz) - Jonathan Ingram - Artur Kotyrba + - Jonathan Scheiber (jmsche) - Tyson Andre - - Thomas Landauer (thomas-landauer) - GDIBass - Samuel NELA (snela) - dFayet - Karoly Gossler (connorhu) - Vincent AUBERT (vincent) - Sebastien Morel (plopix) - - Sergey (upyx) - Yoann RENARD (yrenard) + - Oleg Andreyev (oleg.andreyev) - Thomas Lallement (raziel057) - Timothée Barray (tyx) - James Halsall (jaitsu) - Mikael Pajunen + - Marcin Sikoń (marphi) - Warnar Boekkooi (boekkooi) - Marco Petersen (ocrampete16) - Benjamin Leveque (benji07) @@ -314,12 +327,12 @@ The Symfony Connect username in parenthesis allows to get more information - Wojciech Kania - Alexey Kopytko (sanmai) - Sergey Linnik (linniksa) + - Warxcell (warxcell) - Richard Miller - Leo Feyer (leofeyer) - Mario A. Alvarez Garcia (nomack84) - Thomas Rabaix (rande) - D (denderello) - - Jonathan Scheiber (jmsche) - DQNEO - Andrii Bodnar - Artem (artemgenvald) @@ -331,9 +344,9 @@ The Symfony Connect username in parenthesis allows to get more information - Thibaut Cheymol (tcheymol) - Julien Pauli - Islam Israfilov (islam93) - - Oleg Andreyev (oleg.andreyev) - Daniel Gorgan - Hendrik Luup (hluup) + - Bob van de Vijver (bobvandevijver) - Martin Herndl (herndlm) - Ruben Gonzalez (rubenrua) - Benjamin Dulau (dbenjamin) @@ -350,7 +363,6 @@ The Symfony Connect username in parenthesis allows to get more information - Stepan Anchugov (kix) - bronze1man - sun (sun) - - Alan Poulain (alanpoulain) - Larry Garfield (crell) - Fabien Villepinte - SiD (plbsid) @@ -358,18 +370,22 @@ The Symfony Connect username in parenthesis allows to get more information - Edi Modrić (emodric) - Philipp Wahala (hifi) - Nikolay Labinskiy (e-moe) - - Warxcell (warxcell) - Martin Schuhfuß (usefulthink) - apetitpa - Vladyslav Loboda - Pierre Minnieur (pminnieur) - Kyle + - Frank A. Fiebig (fafiebig) - Dominique Bongiraud - Hidde Wieringa (hiddewie) + - Dane Powell + - Loïc Frémont (loic425) - Christopher Davis (chrisguitarguy) - Lukáš Holeczy (holicz) + - Michael Lee (zerustech) - Florian Lonqueu-Brochard (florianlb) - Leszek Prabucki (l3l0) + - Giorgio Premi - Emanuele Panzeri (thepanz) - Matthew Smeets - François Zaninotto (fzaninotto) @@ -382,8 +398,7 @@ The Symfony Connect username in parenthesis allows to get more information - Daniel STANCU - Markus Fasselt (digilist) - Maxime Veber (nek-) - - Marcin Sikoń (marphi) - - Martin Auswöger + - Oleksiy (alexndlm) - Sullivan SENECHAL (soullivaneuh) - Rui Marinho (ruimarinho) - Marc Weistroff (futurecat) @@ -396,11 +411,11 @@ The Symfony Connect username in parenthesis allows to get more information - Tristan Darricau (tristandsensio) - Fabien S (bafs) - Victor Bocharsky (bocharsky_bw) - - Sébastien Alfaiate (seb33300) + - Jan Sorgalla (jsor) - henrikbjorn - Alex Bowers + - Simon Podlipsky (simpod) - Marcel Beerta (mazen) - - Phil Taylor (prazgod) - flack (flack) - Craig Duncan (duncan3dc) - Mantis Development @@ -412,8 +427,8 @@ The Symfony Connect username in parenthesis allows to get more information - Rob Frawley 2nd (robfrawley) - Mohammad Emran Hasan (phpfour) - Dmitriy Mamontov (mamontovdmitriy) + - Kévin THERAGE (kevin_therage) - Nikita Konstantinov (unkind) - - Michael Lee (zerustech) - Dariusz - Francois Zaninotto - Laurent Masforné (heisenberg) @@ -426,7 +441,6 @@ The Symfony Connect username in parenthesis allows to get more information - Iker Ibarguren (ikerib) - Manuel Reinhard (sprain) - Johann Pardanaud - - Alexis Lefebvre - Indra Gunawan (indragunawan) - Tim Goudriaan (codedmonkey) - Harm van Tilborg (hvt) @@ -444,12 +458,13 @@ The Symfony Connect username in parenthesis allows to get more information - Xavier Montaña Carreras (xmontana) - Tarmo Leppänen (tarlepp) - AnneKir - - Bob van de Vijver (bobvandevijver) - Tobias Weichart - Miro Michalicka + - Peter Kruithof (pkruithof) - M. Vondano - Xavier Perez - Arjen Brouwer (arjenjb) + - Vladimir Tsykun (vtsykun) - Tavo Nieves J (tavoniievez) - Arjen van der Meijden - Patrick McDougle (patrick-mcdougle) @@ -473,13 +488,14 @@ The Symfony Connect username in parenthesis allows to get more information - Félix Labrecque (woodspire) - GordonsLondon - Roman Anasal - - Jan Sorgalla (jsor) - Piotr Kugla (piku235) - Quynh Xuan Nguyen (seriquynh) - Ray - Philipp Cordes (corphi) - - Simon Podlipsky (simpod) + - Yannick Ihmels (ihmels) + - Andrii Dembitskyi - Chekote + - Evert Harmeling (evertharmeling) - bhavin (bhavin4u) - Pavel Popov (metaer) - Thomas Adam @@ -493,21 +509,26 @@ The Symfony Connect username in parenthesis allows to get more information - Bob den Otter (bopp) - Johan Vlaar (johjohan) - Thomas Schulz (king2500) + - Anderson Müller - Benjamin Morel - Bernd Stellwag + - Philippe SEGATORI (tigitz) - Frank de Jonge + - Florent Morselli (spomky_) - Chris Tanaskoski - julien57 - - Loïc Frémont (loic425) + - Renan (renanbr) + - Ippei Sumida (ippey_s) - Ben Ramsey (ramsey) - Matthieu Auger (matthieuauger) - Josip Kruslin (jkruslin) - - Giorgio Premi - renanbr + - Maxim Dovydenok (shiftby) - Sébastien Lavoie (lavoiesl) - Alex Rock (pierstoval) - Wodor Wodorski - Beau Simensen (simensen) + - Magnus Nordlander (magnusnordlander) - Robert Kiss (kepten) - Zan Baldwin (zanbaldwin) - Antonio J. García Lagar (ajgarlag) @@ -516,16 +537,18 @@ The Symfony Connect username in parenthesis allows to get more information - Jérôme Tanghe (deuchnord) - Kim Hemsø Rasmussen (kimhemsoe) - Maximilian Reichel (phramz) - - Dane Powell - jaugustin - Dmytro Borysovskyi (dmytr0) - Mathias STRASSER (roukmoute) - Pascal Luna (skalpa) - Wouter Van Hecke - - Peter Kruithof (pkruithof) - Michael Holm (hollo) + - Yassine Guedidi (yguedidi) - Giso Stallenberg (gisostallenberg) - Blanchon Vincent (blanchonvincent) + - Quentin Devos + - William Arslett (warslett) + - Jérémy REYNAUD (babeuloula) - Christian Schmidt - Gonzalo Vilaseca (gonzalovilaseca) - Vadim Borodavko (javer) @@ -533,9 +556,13 @@ The Symfony Connect username in parenthesis allows to get more information - Soufian EZ ZANTAR (soezz) - Jan van Thoor (janvt) - Martin Kirilov (wucdbm) + - Axel Guckelsberger (guite) - Chris Smith (cs278) - Florian Klein (docteurklein) + - James Gilliland (neclimdul) - Bilge + - Phil E. Taylor (philetaylor) + - Cătălin Dan (dancatalin) - Rhodri Pugh (rodnaph) - Manuel Kiessling (manuelkiessling) - Patrick Reimers (preimers) @@ -551,31 +578,34 @@ The Symfony Connect username in parenthesis allows to get more information - Gabor Toth (tgabi333) - realmfoo - Thomas Tourlourat (armetiz) + - Gasan Guseynov (gassan) - Andrey Esaulov (andremaha) - Grégoire Passault (gregwar) - Jerzy Zawadzki (jzawadzki) - Ismael Ambrosi (iambrosi) - - Yannick Ihmels (ihmels) + - Samaël Villette (samadu61) - Saif Eddin G - Emmanuel BORGES (eborges78) + - siganushka (siganushka) - Aurelijus Valeiša (aurelijus) - - Evert Harmeling (evertharmeling) - Jan Decavele (jandc) - Gustavo Piltcher + - Joachim Løvgaard (loevgaard) + - Shakhobiddin - Grenier Kévin (mcsky_biig) + - Lee Rowlands + - Peter Bowyer (pbowyer) - Stepan Tanasiychuk (stfalcon) - Tiago Ribeiro (fixe) - Raul Fraile (raulfraile) - Adrian Rudnik (kreischweide) - Pavel Batanov (scaytrase) - Francesc Rosàs (frosas) - - Andrii Dembitskyi - Bongiraud Dominique - janschoenherr - Marko Kaznovac (kaznovac) - Emanuele Gaspari (inmarelibero) - Dariusz Rumiński - - Romain Monteil (ker0x) - Terje Bråten - Gennadi Janzen - James Hemery @@ -590,6 +620,7 @@ The Symfony Connect username in parenthesis allows to get more information - Khoo Yong Jun - Christin Gruber (christingruber) - Jeremy Livingston (jeremylivingston) + - Tobias Bönner - Julien Turby - scyzoryck - Greg Anderson @@ -599,7 +630,6 @@ The Symfony Connect username in parenthesis allows to get more information - Fractal Zombie - Gunnstein Lye (glye) - Thomas Talbot (ioni) - - Kévin THERAGE (kevin_therage) - Noémi Salaün (noemi-salaun) - Michel Hunziker - Krystian Marcisz (simivar) @@ -608,14 +638,14 @@ The Symfony Connect username in parenthesis allows to get more information - Lorenzo Millucci (lmillucci) - Jérôme Tamarelle (jtamarelle-prismamedia) - Emil Masiakowski + - Sergey Melesh (sergex) - Alexandre Parent + - Angelov Dejan (angelov) - DT Inier (gam6itko) - Matthew Lewinski (lewinski) - - Magnus Nordlander (magnusnordlander) - Ricard Clau (ricardclau) - Dmitrii Tarasov (dtarasov) - Philipp Kolesnikov - - Maxim Dovydenok (shiftby) - Carlos Pereira De Amorim (epitre) - Rodrigo Aguilera - Roumen Damianoff @@ -623,6 +653,7 @@ The Symfony Connect username in parenthesis allows to get more information - Thomas Royer (cydonia7) - Gildas Quéméner (gquemener) - Nicolas LEFEVRE (nicoweb) + - Asmir Mustafic (goetas) - Martins Sipenko - Guilherme Augusto Henschel - Mardari Dorel (dorumd) @@ -631,13 +662,13 @@ The Symfony Connect username in parenthesis allows to get more information - Andy Palmer (andyexeter) - Marko H. Tamminen (gzumba) - Francesco Levorato - - Ippei Sumida (ippey_s) - DerManoMann - David Molineus - Desjardins Jérôme (jewome62) - Vitaliy Zakharov (zakharovvi) - Tobias Sjösten (tobiassjosten) - Gyula Sallai (salla) + - Stefan Gehrig (sgehrig) - Benjamin Cremer (bcremer) - rtek - Inal DJAFAR (inalgnu) @@ -660,11 +691,12 @@ The Symfony Connect username in parenthesis allows to get more information - mondrake (mondrake) - Yaroslav Kiliba - “Filip + - FORT Pierre-Louis (plfort) - Simon Watiau (simonwatiau) - Ruben Jacobs (rubenj) - - William Arslett - Arkadius Stefanski (arkadius) - Jérémy M (th3mouk) + - Tristan Pouliquen - Terje Bråten - Pierre Rineau - Renan Gonçalves (renan_saddam) @@ -682,10 +714,10 @@ The Symfony Connect username in parenthesis allows to get more information - Jelle Raaijmakers (gmta) - Roberto Nygaard - Joshua Nye + - Jordane VASPARD (elementaire) - Dalibor Karlović - Randy Geraads - Sanpi (sanpi) - - James Gilliland (neclimdul) - Eduardo Gulias (egulias) - Andreas Leathley (iquito) - Nathanael Noblet (gnat) @@ -696,17 +728,16 @@ The Symfony Connect username in parenthesis allows to get more information - ShinDarth - Arun Philip - Stéphane PY (steph_py) - - Cătălin Dan (dancatalin) - Philipp Kräutli (pkraeutli) - Carl Casbolt (carlcasbolt) - battye + - BrokenSourceCode - Grzegorz (Greg) Zdanowski (kiler129) - Kirill chEbba Chebunin - kylekatarnls (kylekatarnls) - Steve Grunwell - - Alex (aik099) - - Axel Guckelsberger (guite) - Greg Thornton (xdissent) - BENOIT POLASZEK (bpolaszek) - Shaharia Azam @@ -717,7 +748,6 @@ The Symfony Connect username in parenthesis allows to get more information - Guillaume Verstraete - vladimir.panivko - Jason Tan (jt2k) - - Jérémy REYNAUD (babeuloula) - Costin Bereveanu (schniper) - kick-the-bucket - Marek Kalnik (marekkalnik) @@ -731,6 +761,7 @@ The Symfony Connect username in parenthesis allows to get more information - Tamas Szijarto - stlrnz - Adrien Wilmet (adrienfr) + - Mathieu Rochette (mathroc) - Alex Bacart - hugovms - Michele Locati @@ -764,12 +795,8 @@ The Symfony Connect username in parenthesis allows to get more information - Daniel Tschinder - Diego Agulló (aeoris) - Tomasz Ignatiuk - - Joachim Løvgaard (loevgaard) - vladimir.reznichenko - - Shakhobiddin - Kai - - Lee Rowlands - - siganushka (siganushka) - Alain Hippolyte (aloneh) - Karoly Negyesi (chx) - Xavier HAUSHERR @@ -782,7 +809,6 @@ The Symfony Connect username in parenthesis allows to get more information - Jordi Sala Morales (jsala) - Albert Jessurum (ajessu) - Samuele Lilli (doncallisto) - - Peter Bowyer (pbowyer) - Romain Pierre - Laszlo Korte - Gabrielle Langer @@ -808,6 +834,7 @@ The Symfony Connect username in parenthesis allows to get more information - Rimas Kudelis - Ben Scott (bpscott) - Andrii Dembitskyi + - a.dmitryuk - Pavol Tuka - Paulo Ribeiro (paulo) - Marc Laporte @@ -818,10 +845,12 @@ The Symfony Connect username in parenthesis allows to get more information - Thiago Cordeiro (thiagocordeiro) - Julien Maulny - Brian King + - Wouter van der Loop (toppy-hennie) - Paul Oms - Steffen Roßkamp - Alexandru Furculita (afurculita) - Michel Salib (michelsalib) + - Quentin Dequippe (qdequippe) - Valentin Jonovs - geoffrey - Bastien DURAND (deamon) @@ -829,14 +858,16 @@ The Symfony Connect username in parenthesis allows to get more information - Jon Gotlin (jongotlin) - Jeanmonod David (jeanmonod) - Daniel González (daniel.gonzalez) - - Renan (renanbr) - Webnet team (webnet) - Berny Cantos (xphere81) - Mátyás Somfai (smatyas) + - Simon Leblanc (leblanc_simon) - Jan Schumann + - Matheo Daninos (mathdns) - Niklas Fiekas - Mark Challoner (markchalloner) - Markus Bachmann (baachi) + - Matthieu Lempereur (mryamous) - Roger Guasch (rogerguasch) - Luis Tacón (lutacon) - Alex Hofbauer (alexhofbauer) @@ -844,31 +875,36 @@ The Symfony Connect username in parenthesis allows to get more information - lancergr - Ivan Nikolaev (destillat) - Xavier Leune (xleune) + - Matthieu Calie (matth--) - Ben Roberts (benr77) + - Benjamin Georgeault (wedgesama) - Joost van Driel (j92) - ampaze - Arturs Vonda + - Michael Hirschler (mvhirsch) - Xavier Briand (xavierbriand) - Daniel Badura - - Asmir Mustafic (goetas) - vagrant + - Maarten de Boer (mdeboer) - Asier Illarramendi (doup) - AKeeman (akeeman) - Martijn Cuppens - Restless-ET + - Robert Meijers - Vlad Gregurco (vgregurco) - Boris Vujicic (boris.vujicic) - Chris Sedlmayr (catchamonkey) + - Gwendolen Lynch - Kamil Kokot (pamil) - Seb Koelen - Christoph Mewes (xrstf) - Vitaliy Tverdokhlib (vitaliytv) - Ariel Ferrandini (aferrandini) - Niklas Keller + - BASAK Semih (itsemih) - Dirk Pahl (dirkaholic) - Cédric Lombardot (cedriclombardot) - Jonas Flodén (flojon) - - Stefan Gehrig (sgehrig) - Adrien Lucas (adrienlucas) - Dominik Zogg - Kai Dederichs @@ -880,6 +916,7 @@ The Symfony Connect username in parenthesis allows to get more information - Adam Harvey - ilyes kooli (skafandri) - Anton Bakai + - Nicolas Dousson - Sam Fleming (sam_fleming) - Alex Bakhturin - Brayden Williams (redstar504) @@ -894,14 +931,15 @@ The Symfony Connect username in parenthesis allows to get more information - Fabian Lange (codingfabian) - Yoshio HANAWA - Toon Verwerft (veewee) + - Jiri Barous - Gert de Pagter - Sebastian Bergmann - Miroslav Šustek (sustmi) - Pablo Díez (pablodip) - Damien Fa - Kevin McBride - - BrokenSourceCode - Sergio Santoro + - AndrolGenhald - Philipp Rieber (bicpi) - Dennis Væversted (srnzitcom) - Manuel de Ruiter (manuel) @@ -909,6 +947,7 @@ The Symfony Connect username in parenthesis allows to get more information - Eduardo Oliveira (entering) - Jonathan Johnson (jrjohnson) - Eugene Wissner + - aegypius - Ricardo Oliveira (ricardolotr) - Roy Van Ginneken (rvanginneken) - ondrowan @@ -916,25 +955,28 @@ The Symfony Connect username in parenthesis allows to get more information - Jon Dufresne - Chad Sikorra (chadsikorra) - Mathias Brodala (mbrodala) + - naitsirch (naitsirch) - Evan S Kaufman (evanskaufman) - Jonathan Sui Lioung Lee Slew (jlslew) - mcben - Jérôme Vieilledent (lolautruche) - Filip Procházka (fprochazka) + - Alex Kalineskou - stoccc - Markus Lanthaler (lanthaler) - Gigino Chianese (sajito) - Xav` (xavismeh) - Remi Collet - - Mathieu Rochette (mathroc) - Vicent Soria Durá (vicentgodella) - Michael Moravec - Anthony Ferrara + - Glodzienski - Christian Gripp (core23) - Marcel Hernandez - Ioan Negulescu - Jakub Škvára (jskvara) - Andrew Udvare (audvare) + - Volodymyr Panivko - alexpods - Dennis Langen (nijusan) - Adam Szaraniec @@ -946,6 +988,7 @@ The Symfony Connect username in parenthesis allows to get more information - De Cock Xavier (xdecock) - Nicolas Dewez (nicolas_dewez) - Quentin Dreyer + - Denis Kulichkin (onexhovia) - Scott Arciszewski - Xavier HAUSHERR - Achilles Kaloeridis (achilles) @@ -957,8 +1000,10 @@ The Symfony Connect username in parenthesis allows to get more information - Krzysztof Piasecki (krzysztek) - Lenard Palko - Nils Adermann (naderman) + - Roland Franssen :) - Gábor Fási - Nate (frickenate) + - Sander De la Marche (sanderdlm) - sasezaki - Kristof Van Cauwenbergh (kristofvc) - Dawid Pakuła (zulusx) @@ -967,7 +1012,6 @@ The Symfony Connect username in parenthesis allows to get more information - Rodrigo Borrego Bernabé (rodrigobb) - John Bafford (jbafford) - Emanuele Iannone - - Gasan Guseynov (gassan) - Ondrej Machulda (ondram) - Denis Gorbachev (starfall) - Martin Morávek (keeo) @@ -984,14 +1028,15 @@ The Symfony Connect username in parenthesis allows to get more information - M. (mbontemps) - Marcin Chyłek (songoq) - Ned Schwartz - - Anderson Müller - Ziumin - Matthias Schmidt - Lenar Lõhmus + - Ilija Tovilo (ilijatovilo) - Zach Badgett (zachbadgett) - Loïc Faugeron - Aurélien Fredouelle - Pavel Campr (pcampr) + - Markus Staab - Forfarle (forfarle) - Johnny Robeson (johnny) - Kai Eichinger (kai_eichinger) @@ -999,11 +1044,11 @@ The Symfony Connect username in parenthesis allows to get more information - Philipp Keck - Disquedur - Markus S. (staabm) + - Guilherme Ferreira - Geoffrey Tran (geoff) - Elan Ruusamäe (glen) - Brad Jones - Nicolas de Marqué (nicola) - - a.dmitryuk - Jannik Zschiesche - Jan Ole Behrens (deegital) - Mantas Var (mvar) @@ -1013,6 +1058,7 @@ The Symfony Connect username in parenthesis allows to get more information - Sorin Pop (sorinpop) - Piotr Stankowski - Stewart Malik + - Pierre-Emmanuel Tanguy (petanguy) - Stefan Graupner (efrane) - Gemorroj (gemorroj) - Adrien Chinour @@ -1028,9 +1074,9 @@ The Symfony Connect username in parenthesis allows to get more information - Aurélien MARTIN - Malte Schlüter - Jules Matsounga (hyoa) - - Quentin Dequippe (qdequippe) - Yewhen Khoptynskyi (khoptynskyi) - Jérôme Nadaud (jnadaud) + - wuchen90 - Alexandre Tranchant (alexandre_t) - Anthony Moutte - shreyadenny @@ -1048,7 +1094,10 @@ The Symfony Connect username in parenthesis allows to get more information - Safonov Nikita (ns3777k) - Simon DELICATA - Thibault Buathier (gwemox) + - Julien Boudry - vitaliytv + - Franck RANAIVO-HARISOA (franckranaivo) + - Andreas Hennings - Arnaud Frézet - Nicolas Martin (cocorambo) - luffy1727 @@ -1069,7 +1118,6 @@ The Symfony Connect username in parenthesis allows to get more information - Giuseppe Campanelli - Valentin - pizzaminded - - Matthieu Calie (matth--) - Stéphane Escandell (sescandell) - ivan - linh @@ -1108,29 +1156,28 @@ The Symfony Connect username in parenthesis allows to get more information - Jacek Wilczyński (jacekwilczynski) - Hany el-Kerdany - Wang Jingyu - - Benjamin Georgeault (wedgesama) - Åsmund Garfors - Maxime Douailin - Jean Pasdeloup - Laurent Moreau - - Michael Hirschler (mvhirsch) - Javier López (loalf) - tamar peled - Reinier Kip - Geoffrey Brier (geoffrey-brier) - Sofien Naas - Christophe Meneses (c77men) - - Vladimir Tsykun - Andrei O - Dustin Dobervich (dustin10) - Alejandro Diaz Torres - Karl Shea - dantleech - Valentin + - Jack Worman (jworman) - Sebastian Marek (proofek) - Łukasz Chruściel (lchrusciel) - Jan Vernieuwe (vernija) - zenmate + - Cédric Anne - j.schmitt - Georgi Georgiev - David Fuhr @@ -1139,11 +1186,14 @@ The Symfony Connect username in parenthesis allows to get more information - mwos - Aurimas Niekis (gcds) - Volker Killesreiter (ol0lll) + - Benjamin Zaslavsky (tiriel) - Vedran Mihočinec (v-m-i) + - Vincent Chalamon - creiner - RevZer0 (rav) - remieuronews - Marek Binkowski + - Benjamin Schoch (bschoch) - Rostyslav Kinash - Andrey Lebedev (alebedev) - Cristoforo Cervino (cristoforocervino) @@ -1164,7 +1214,6 @@ The Symfony Connect username in parenthesis allows to get more information - Quentin Moreau (sheitak) - Stefan Warman (warmans) - Bert Ramakers - - Angelov Dejan (angelov) - Tristan Maindron (tmaindron) - Behnoush Norouzali (behnoush) - Marc Duboc (icemad) @@ -1173,11 +1222,14 @@ The Symfony Connect username in parenthesis allows to get more information - Timothée BARRAY - Nilmar Sanchez Muguercia - Ivo Bathke (ivoba) + - Arnaud POINTET (oipnet) + - Lukas Mencl - Strate - Anton A. Sumin - Atthaphon Urairat - Jon Green (jontjs) - Mickaël Isaert (misaert) + - alexandre.lassauge - Israel J. Carberry - Julius Kiekbusch - Miquel Rodríguez Telep (mrtorrent) @@ -1188,8 +1240,8 @@ The Symfony Connect username in parenthesis allows to get more information - Quentin de Longraye (quentinus95) - Chris Heng (gigablah) - Oleksii Svitiashchuk + - Mickaël Buliard (mbuliard) - Tristan Bessoussa (sf_tristanb) - - FORT Pierre-Louis (plfort) - Richard Bradley - Nathanaël Martel (nathanaelmartel) - Nicolas Jourdan (nicolasjc) @@ -1206,9 +1258,9 @@ The Symfony Connect username in parenthesis allows to get more information - Evgeny Efimov (edefimov) - John VanDeWeghe - Oleg Mifle - - gnito-org - Michael Devery (mickadoo) - Loïc Ovigne (oviglo) + - Gregor Nathanael Meyer (spackmat) - Antoine Corcy - Markkus Millend - Clément @@ -1234,6 +1286,7 @@ The Symfony Connect username in parenthesis allows to get more information - Benjamin Zikarsky (bzikarsky) - Jason Schilling (chapterjason) - Nathan PAGE (nathix) + - Rodrigo Méndez (rodmen) - sl_toto (sl_toto) - Marek Pietrzak (mheki) - Dmitrii Lozhkin @@ -1252,13 +1305,13 @@ The Symfony Connect username in parenthesis allows to get more information - Simon Schick (simonsimcity) - Victor Macko (victor_m) - Tristan Roussel - - Quentin Devos - Jorge Vahldick (jvahldick) - Vladimir Mantulo (mantulo) - aim8604 - Aleksandr Dankovtsev - Maciej Zgadzaj - David Legatt (dlegatt) + - Alexandre parent - Cameron Porter - Hossein Bukhamsin - Oliver Hoff @@ -1297,7 +1350,6 @@ The Symfony Connect username in parenthesis allows to get more information - rtek - Maxime AILLOUD (mailloud) - Richard van den Brand (ricbra) - - Sergey Melesh (sergex) - mohammadreza honarkhah - develop - flip111 @@ -1321,7 +1373,6 @@ The Symfony Connect username in parenthesis allows to get more information - Massimiliano Braglia (massimilianobraglia) - Swen van Zanten (swenvanzanten) - Frankie Wittevrongel - - Oleksiy (alexndlm) - Richard Quadling - James Hudson (mrthehud) - Adam Prickett @@ -1341,19 +1392,20 @@ The Symfony Connect username in parenthesis allows to get more information - Grinbergs Reinis (shima5) - Ruud Arentsen - Harald Tollefsen - - Tobias Bönner - Arend-Jan Tetteroo - Mbechezi Nawo + - Klaus Silveira (klaussilveira) - Andre Eckardt (korve) - Michael Piecko (michael.piecko) - Osayawe Ogbemudia Terry (terdia) - Toni Peric (tperic) - yclian + - radar3301 - Aleksey Prilipko - - AndrolGenhald - Andrew Berry - Wybren Koelmans (wybren_koelmans) - Dmytro Dzubenko + - victor-prdh - Benjamin RICHARD - pdommelen - Cedrick Oka @@ -1377,7 +1429,6 @@ The Symfony Connect username in parenthesis allows to get more information - Serhiy Lunak (slunak) - Wojciech Błoszyk (wbloszyk) - Jeroen van den Enden (endroid) - - Jiri Barous - abunch - tamcy - Mikko Pesari @@ -1385,6 +1436,7 @@ The Symfony Connect username in parenthesis allows to get more information - Pascal Helfenstein - Malcolm Fell (emarref) - phuc vo (phucwan) + - Petar Obradović - Baldur Rensch (brensch) - Bogdan Scordaliu - Daniel Rotter (danrot) @@ -1408,13 +1460,13 @@ The Symfony Connect username in parenthesis allows to get more information - Michael Olšavský - Benny Born - Emirald Mateli - - Tristan Pouliquen - Jose Gonzalez - Claudio Zizza - Ivo Valchev - Zlatoslav Desyatnikov - Wickex - tuqqu + - Ilia (aliance) - Neagu Cristian-Doru (cristian-neagu) - Dave Marshall (davedevelopment) - Jakub Kulhan (jakubkulhan) @@ -1422,14 +1474,17 @@ The Symfony Connect username in parenthesis allows to get more information - Gladhon - Kai - Bartłomiej Zając + - Maximilian.Beckers - Grégoire Penverne (gpenverne) - Venu - Jonatan Männchen - Dennis Hotson - Andrew Tchircoff (andrewtch) - Lars Vierbergen (vierbergenlars) + - Barney Hanlon - Bart Wach - Jos Elstgeest + - Thorry84 - Kirill Lazarev - Serhii Smirnov - Martins Eglitis @@ -1453,6 +1508,7 @@ The Symfony Connect username in parenthesis allows to get more information - Anthony Massard (decap94) - Chris Maiden (matason) - Andrea Ruggiero (pupax) + - Oliver Hader - Josiah (josiah) - Alexandre Beaujour - George Yiannoulopoulos @@ -1463,6 +1519,7 @@ The Symfony Connect username in parenthesis allows to get more information - bill moll - PaoRuby - Bizley + - Edvin Hultberg - Dominik Piekarski (dompie) - Rares Sebastian Moldovan (raresmldvn) - Felds Liscia (felds) @@ -1507,6 +1564,7 @@ The Symfony Connect username in parenthesis allows to get more information - Xavier Lacot (xavier) - possum - Denis Zunke (donalberto) + - Adrien Roches (neirda24) - _sir_kane (waly) - Olivier Maisonneuve - Bruno BOUTAREL @@ -1523,7 +1581,6 @@ The Symfony Connect username in parenthesis allows to get more information - Francis Turmel (fturmel) - Nikita Nefedov (nikita2206) - Bernat Llibre - - Daniel Burger - cgonzalez - Ben - Joni Halme @@ -1532,7 +1589,6 @@ The Symfony Connect username in parenthesis allows to get more information - Vitali Tsyrkin - Juga Paazmaya - afaricamp - - Glodzienski - riadh26 - Konstantinos Alexiou - Dilek Erkut @@ -1555,6 +1611,7 @@ The Symfony Connect username in parenthesis allows to get more information - Stefano Degenkamp (steef) - James Michael DuPont - kor3k kor3k (kor3k) + - Rustam Bakeev (nommyde) - Eric Schildkamp - agaktr - Vincent CHALAMON @@ -1563,6 +1620,7 @@ The Symfony Connect username in parenthesis allows to get more information - Gennadi Janzen - SenTisso - Joe Springe + - Ivan Kurnosov - Flinsch - botbotbot - Timon van der Vorm @@ -1577,6 +1635,7 @@ The Symfony Connect username in parenthesis allows to get more information - Patrick Dawkins (pjcdawkins) - Paul Kamer (pkamer) - Rafał Wrzeszcz (rafalwrzeszcz) + - Reyo Stallenberg (reyostallenberg) - Rémi Faivre (rfv) - Nguyen Xuan Quynh - Reen Lokum @@ -1589,7 +1648,6 @@ The Symfony Connect username in parenthesis allows to get more information - Urban Suppiger - Denis Charrier (brucewouaigne) - Marcello Mönkemeyer (marcello-moenkemeyer) - - Sander De la Marche (sanderdlm) - Philipp Scheit (pscheit) - Pierre Vanliefland (pvanliefland) - Roy Klutman (royklutman) @@ -1669,8 +1727,8 @@ The Symfony Connect username in parenthesis allows to get more information - Cyril Quintin (cyqui) - Cyrille Bourgois (cyrilleb) - Gerard van Helden (drm) + - Florent Destremau (florentdestremau) - Johnny Peck (johnnypeck) - - naitsirch (naitsirch) - Geoffrey Monte (numerogeek) - Martijn Boers (plebian) - Plamen Mishev (pmishev) @@ -1696,6 +1754,7 @@ The Symfony Connect username in parenthesis allows to get more information - Jörn Lang - David Marín Carreño (davefx) - Fabien LUCAS (flucas2) + - Alex (garrett) - Hidde Boomsma (hboomsma) - Johan Wilfer (johanwilfer) - Toby Griffiths (tog) @@ -1724,7 +1783,6 @@ The Symfony Connect username in parenthesis allows to get more information - Antoine M - Frank Jogeleit - Ondřej Frei - - Volodymyr Panivko - Jenne van der Meer - Storkeus - Anton Zagorskii @@ -1737,7 +1795,6 @@ The Symfony Connect username in parenthesis allows to get more information - florian-michael-mast - Henry Snoek - Vlad Dumitrache - - Alex Kalineskou - Derek ROTH - Jeremy Benoist - Ben Johnson @@ -1759,7 +1816,6 @@ The Symfony Connect username in parenthesis allows to get more information - Shin Ohno (ganchiku) - Jaap van Otterdijk (jaapio) - Kubicki Kamil (kubik) - - Simon Leblanc (leblanc_simon) - Vladislav Nikolayev (luxemate) - Martin Mandl (m2mtech) - Maxime Pinot (maximepinot) @@ -1773,7 +1829,6 @@ The Symfony Connect username in parenthesis allows to get more information - Pierre-Olivier Vares (povares) - Ronny López (ronnylt) - Julius (sakalys) - - Samaël Villette (samadu61) - abdul malik ikhsan (samsonasik) - Dmitry (staratel) - Tito Miguel Costa (titomiguelcosta) @@ -1781,6 +1836,7 @@ The Symfony Connect username in parenthesis allows to get more information - Morgan Auchede - Christian Morgan - Alexander Miehe + - Simon (kosssi) - Sascha Dens (saschadens) - Maxime Aknin (3m1x4m) - Geordie @@ -1904,7 +1960,6 @@ The Symfony Connect username in parenthesis allows to get more information - Ronny (big-r) - Anton (bonio) - Alexandre Fiocre (demos77) - - Jordane VASPARD (elementaire) - Erwan Nader (ernadoo) - Faizan Akram Dar (faizanakram) - Greg Szczotka (greg606) @@ -1956,28 +2011,33 @@ The Symfony Connect username in parenthesis allows to get more information - Daniel Alejandro Castro Arellano (lexcast) - Aleksandar Dimitrov (netbull) - Gary Houbre (thegarious) + - Vincent Chalamon - Thomas Jarrand - Baptiste Leduc (bleduc) - Antoine Bluchet (soyuka) - Patrick Kaufmann + - Mickael Perraud (mikaelkael) - Anton Dyshkant + - Ramunas Pabreza - Kirill Nesmeyanov (serafim) - Reece Fowell (reecefowell) + - Muhammad Aakash + - Charly Goblet (_mocodo) - Guillaume Gammelin - Valérian Galliat - d-ph - Renan Taranto (renan-taranto) + - Mateusz Żyła (plotkabytes) - Rikijs Murgs - Uladzimir Tsykun - Amaury Leroux de Lens (amo__) - Christian Jul Jensen - - Franck RANAIVO-HARISOA (franckranaivo) - Alexandre GESLIN - The Whole Life to Learn - Mikkel Paulson - ergiegonzaga + - kurozumi (kurozumi) - Liverbool (liverbool) - - Julien Boudry - Dalibor Karlović - Sam Malone - Ha Phan (haphan) @@ -1989,12 +2049,14 @@ The Symfony Connect username in parenthesis allows to get more information - Mahmoud Mostafa (mahmoud) - Ahmed Abdou - Pieter + - Louis-Proffit - Michael Tibben - Billie Thompson - Ganesh Chandrasekaran (gxc4795) - Sander Marechal - Franz Wilding (killerpoke) - Ferenczi Krisztian (fchris82) + - Artyum Petrov - Oleg Golovakhin (doc_tr) - Icode4Food (icode4food) - Radosław Benkel @@ -2013,9 +2075,9 @@ The Symfony Connect username in parenthesis allows to get more information - Sander Coolen (scoolen) - Nicolas Le Goff (nlegoff) - Anne-Sophie Bachelard + - Gordienko Vladislav - Marvin Butkereit - Ben Oman - - Jack Worman (jworman) - Chris de Kok - Andreas Kleemann (andesk) - Hubert Moreau (hmoreau) @@ -2029,6 +2091,8 @@ The Symfony Connect username in parenthesis allows to get more information - Zachary Tong (polyfractal) - Ashura - Hryhorii Hrebiniuk + - Nsbx + - Alex Plekhanov - johnstevenson - hamza - dantleech @@ -2039,7 +2103,9 @@ The Symfony Connect username in parenthesis allows to get more information - Artem (digi) - boite - Silvio Ginter + - Peter Culka - MGDSoft + - joris - Vadim Tyukov (vatson) - David Wolter (davewww) - Sortex @@ -2051,8 +2117,9 @@ The Symfony Connect username in parenthesis allows to get more information - Ariel J. Birnbaum - Danijel Obradović - Pablo Borowicz + - Ondřej Frei - Máximo Cuadros (mcuadros) - - Lukas Mencl + - EXT - THERAGE Kevin - tamirvs - gauss - julien.galenski @@ -2063,27 +2130,37 @@ The Symfony Connect username in parenthesis allows to get more information - Ole Rößner (basster) - Faton (notaf) - Tom Houdmont + - mark burdett - Per Sandström (per) - Goran Juric - Laurent G. (laurentg) - Nicolas Macherey + - Asil Barkin Elik (asilelik) - Bhujagendra Ishaya - Guido Donnari - Mert Simsek (mrtsmsk0) - Lin Clark - Jeremy David (jeremy.david) + - Michał Marcin Brzuchalski (brzuchal) - Jordi Rejas - Troy McCabe - Ville Mattila + - gstapinato - gr1ev0us + - Léo VINCENT - mlazovla + - Markus Baumer - Max Beutel + - adnen chouibi + - Nathan Sepulveda - Antanas Arvasevicius - Pierre Dudoret - Michal Trojanowski - Thomas - Norbert Schultheisz - Maximilian Berghoff (electricmaxxx) + - SOEDJEDE Felix (fsoedjede) + - otsch - Piotr Antosik (antek88) - Nacho Martin (nacmartin) - Sergey Novikov (s12v) @@ -2097,23 +2174,31 @@ The Symfony Connect username in parenthesis allows to get more information - Dan Finnie - Ken Marfilla (marfillaster) - Max Grigorian (maxakawizard) + - allison guilhem - benatespina (benatespina) - Denis Kop + - Kamil Szalewski (szal1k) - Jean-Guilhem Rouel (jean-gui) - Ivan Yivoff - EdgarPE - jfcixmedia - Dominic Tubach - Martijn Evers + - Léon Gersen + - Dustin Wilson - Benjamin Paap (benjaminpaap) - Christian + - ju1ius - Denis Golubovskiy (bukashk0zzz) - Serge (nfx) - Mikkel Paulson - Michał Strzelecki - Hugo Fonseca (fonsecas72) - Martynas Narbutas + - Pierre LEJEUNE (darkanakin41) - Bailey Parker + - curlycarla2004 + - Kevin Auvinet - Antanas Arvasevicius - Kris Kelly - Eddie Abou-Jaoude (eddiejaoude) @@ -2130,35 +2215,48 @@ The Symfony Connect username in parenthesis allows to get more information - Tadas Gliaubicas (tadcka) - Thanos Polymeneas (thanos) - Benoit Garret + - HellFirePvP + - Maximilian Zumbansen - Maximilian Ruta (deltachaos) - Jakub Sacha + - Kamil Musial - Olaf Klischat - orlovv - Claude Dioudonnat - Jonathan Hedstrom - Peter Smeets (darkspartan) - Julien Bianchi (jubianchi) + - Michael Dawart (mdawart) - Robert Meijers - Tijs Verkoyen - James Sansbury - Marcin Chwedziak - hjkl - Dan Wilga + - Florian Heller - Andrew Tch - Alexander Cheprasov - Rodrigo Díez Villamuera (rodrigodiez) + - Brad Treloar - Stephen Clouse - e-ivanov + - Abderrahman DAIF (death_maker) - Yann Rabiller (einenlum) - Jochen Bayer (jocl) + - Constantine Shtompel + - VAN DER PUTTE Guillaume (guillaume_vdp) - Patrick Carlo-Hickman - Bruno MATEU - Jeremy Bush - Thomason, James - Gordienko Vladislav - Ener-Getick + - Moza Bogdan (bogdan_moza) + - johan Vlaar - Viacheslav Sychov + - Nicolas Sauveur (baishu) - Helmut Hummel (helhum) + - Andrew Neil Forster (krciga22) - Matt Brunt - Carlos Ortega Huetos - Péter Buri (burci) @@ -2174,26 +2272,34 @@ The Symfony Connect username in parenthesis allows to get more information - BRAMILLE Sébastien (oktapodia) - Artem Kolesnikov (tyomo4ka) - Gustavo Adrian + - Matthias Neid - Yannick + - Kuzia - Vladimir Luchaninov (luchaninov) - spdionis - rchoquet + - v.shevelev - gitlost - Taras Girnyk - Sergio + - Mehrdad - Eduardo García Sanz (coma) - fduch (fduch) + - Takashi Kanemoto (ttskch) - David de Boer (ddeboer) - Eno Mullaraj (emullaraj) - Stephan Vock (glaubinix) + - Guillem Fondin (guillemfondin) - Ryan Rogers - Arnaud - Klaus Purer - Gilles Doge (gido) - abulford - Philipp Kretzschmar + - Jairo Pastor - Ilya Vertakov - Brooks Boyd + - Axel Venet - Roger Webb - Dmitriy Simushev - Pawel Smolinski @@ -2201,11 +2307,11 @@ The Symfony Connect username in parenthesis allows to get more information - Oxan van Leeuwen - pkowalczyk - Soner Sayakci - - Andreas Hennings - Max Voloshin (maxvoloshin) - Nicolas Fabre (nfabre) - Raul Rodriguez (raul782) - mshavliuk + - Jesper Skytte - MightyBranch - Kacper Gunia (cakper) - Derek Lambert (dlambert) @@ -2222,8 +2328,10 @@ The Symfony Connect username in parenthesis allows to get more information - Vacheslav Silyutin - Juan Traverso - Alain Flaus (halundra) + - Ворожцов Максим (myks92) - tsufeki - Philipp Strube + - Wim Hendrikx - Clement Herreman (clemherreman) - Dan Ionut Dumitriu (danionut90) - Evgeny (disparity) @@ -2235,18 +2343,27 @@ The Symfony Connect username in parenthesis allows to get more information - gndk - Alberto Aldegheri - Dalibor Karlović + - Cyril Vermandé (cyve) + - Raul Garcia Canet (juagarc4) - Dmitri Petmanson - heccjj - Alexandre Melard + - Rafał Toboła + - AlbinoDrought - Jay Klehr - Sergey Yuferev + - Monet Emilien + - voodooism - Tobias Stöckler - Mario Young + - martkop26 + - Evan Shaw - Sander Hagen - - Ilia (aliance) - cilefen (cilefen) - Mo Di (modi) - Pablo Schläpfer + - Nikos Charalampidis + - Xavier RENAUDIN - Christian Wahler (christian) - Jelte Steijaert (jelte) - David Négrier (moufmouf) @@ -2255,6 +2372,7 @@ The Symfony Connect username in parenthesis allows to get more information - Andrea Quintino (dirk39) - Andreas Heigl (heiglandreas) - Tomasz Szymczyk (karion) + - Nadim AL ABDOU (nadim) - Peter Dietrich (xosofox) - Alex Vasilchenko - sez-open @@ -2264,13 +2382,16 @@ The Symfony Connect username in parenthesis allows to get more information - Rubén Calvo (rubencm) - Abdul.Mohsen B. A. A - Cédric Girard + - Robert Worgul - pthompson - Malaney J. Hill - Patryk Kozłowski - Alexandre Pavy + - Tim Ward - Christian Flach (cmfcmf) - Lars Ambrosius Wallenborn (larsborn) - Oriol Mangas Abellan (oriolman) + - Raphaël Geffroy (raphael-geffroy) - Sebastian Göttschkes (sgoettschkes) - Tatsuya Tsuruoka - Ross Tuck @@ -2282,6 +2403,8 @@ The Symfony Connect username in parenthesis allows to get more information - Mihai Nica (redecs) - Andrei Igna - azine + - Wojciech Zimoń + - Vladimir Melnik - Pierre Tachoire - Dawid Sajdak - Ludek Stepan @@ -2295,6 +2418,7 @@ The Symfony Connect username in parenthesis allows to get more information - karolsojko - Marco Jantke - Saem Ghani + - Claudiu Cristea - Zacharias Luiten - Sebastian Utz - Adrien Gallou (agallou) @@ -2303,6 +2427,7 @@ The Symfony Connect username in parenthesis allows to get more information - Clément LEFEBVRE (nemoneph) - Walter Dal Mut (wdalmut) - abluchet + - PabloKowalczyk - Matthieu - Albin Kerouaton - Sébastien HOUZÉ @@ -2313,6 +2438,7 @@ The Symfony Connect username in parenthesis allows to get more information - Cédric Lahouste (rapotor) - Samuel Vogel (samuelvogel) - Berat Doğan + - Christian Kolb - Guillaume LECERF - Juanmi Rodriguez Cerón - twifty @@ -2330,9 +2456,12 @@ The Symfony Connect username in parenthesis allows to get more information - Jules Lamur - Renato Mendes Figueiredo - Raphaël Droz + - Asis Pattisahusiwa - Eric Stern - ShiraNai7 - Antal Áron (antalaron) + - Alexander Grimalovsky (flying) + - Ivan Pepelko (pepelko) - Vašek Purchart (vasek-purchart) - Janusz Jabłoński (yanoosh) - Fleuv @@ -2343,6 +2472,8 @@ The Symfony Connect username in parenthesis allows to get more information - Daniel Richter (richtermeister) - Sandro Hopf (senaria) - ChrisC + - jack.shpartko + - Willem Verspyck - Kim Laï Trinh - Jason Desrosiers - m.chwedziak @@ -2357,12 +2488,15 @@ The Symfony Connect username in parenthesis allows to get more information - Ilya Biryukov (ibiryukov) - Roma (memphys) - Giorgio Premi + - Matthias Bilger + - Krzysztof Pyrkosz - ncou - Ian Carroll - caponica - Daniel Kay (danielkay-cp) - Matt Daum (daum) - Alberto Pirovano (geezmo) + - Pascal Woerde (pascalwoerde) - Pete Mitchell (peterjmit) - Tom Corrigan (tomcorrigan) - Luis Galeas @@ -2372,7 +2506,10 @@ The Symfony Connect username in parenthesis allows to get more information - Phillip Look (plook) - Max Summe - Ema Panz + - DidierLmn - Chihiro Adachi (chihiro-adachi) + - Thomas Trautner (thomastr) + - mfettig - Raphaëll Roussel - Tadcka - Abudarham Yuval @@ -2387,16 +2524,23 @@ The Symfony Connect username in parenthesis allows to get more information - Daniel González Zaballos (dem3trio) - Emmanuel Vella (emmanuel.vella) - Guillaume BRETOU (guiguiboy) + - Ibon Conesa (ibonkonesa) + - Yoann Chocteau (kezaweb) + - nuryagdy mustapayev (nueron) - Carsten Nielsen (phreaknerd) + - lepeule (vlepeule) - Jay Severson - René Kerner - Nathaniel Catchpole + - upchuk - Adrien Samson (adriensamson) - Samuel Gordalina (gordalina) - Maksym Romanowski (maxromanovsky) - Nicolas Eeckeloo (neeckeloo) - Andriy Prokopenko (sleepyboy) - Dariusz Ruminski + - Starfox64 + - Thomas Hanke - Daniel Tschinder - Arnaud CHASSEUX - Wojciech Gorczyca @@ -2408,6 +2552,7 @@ The Symfony Connect username in parenthesis allows to get more information - Mara Blaga - Rick Prent - skalpa + - Pierre Foresi - Pieter Jordaan - Tournoud (damientournoud) - Michael Dowling (mtdowling) @@ -2416,6 +2561,7 @@ The Symfony Connect username in parenthesis allows to get more information - Thomas Counsell - BilgeXA - mmokhi + - javaDeveloperKid - Robert Queck - Peter Bouwdewijn - Daniil Gentili @@ -2426,7 +2572,6 @@ The Symfony Connect username in parenthesis allows to get more information - Nouhail AL FIDI (alfidi) - Fabian Steiner (fabstei) - Felipy Amorim (felipyamorim) - - Klaus Silveira (klaussilveira) - Michael Lively (mlivelyjr) - Abderrahim (phydev) - Attila Bukor (r1pp3rj4ck) @@ -2439,28 +2584,34 @@ The Symfony Connect username in parenthesis allows to get more information - Ergie Gonzaga - Matthew J Mucklo - AnrDaemon + - Tristan Kretzer - Charly Terrier (charlypoppins) - Emre Akinci (emre) - - Rustam Bakeev (nommyde) - psampaz (psampaz) + - Stan Jansen (stanjan) - Maxwell Vandervelde - kaywalker - Sebastian Ionescu - Robert Kopera - Pablo Ogando Ferreira - Thomas Ploch + - Victor Prudhomme - Simon Neidhold - Valentin VALCIU - Jeremiah VALERIE + - Franck Ranaivo-Harisoa + - Cas van Dongen - Patrik Patie Gmitter - Yannick Snobbert - Kevin Dew - James Cowgill - sensio - Julien Menth (cfjulien) + - Lyubomir Grozdanov (lubo13) - Nicolas Schwartz (nicoschwartz) - Tim Jabs (rubinum) - Stéphane Seng (stephaneseng) + - Robert Korulczyk - Jonathan Gough - Benoit Leveque - Benjamin Bender @@ -2478,24 +2629,29 @@ The Symfony Connect username in parenthesis allows to get more information - Kirk Madera - Keith Maika - Mephistofeles + - Oleh Korneliuk - Hoffmann András - LubenZA - Flavian Sierk + - Rik van der Heijden - Michael Bessolov - Zdeněk Drahoš - Dan Harper - moldcraft - Antoine Bellion (abellion) - Ramon Kleiss (akathos) + - Alexey Buyanow (alexbuyanow) - Antonio Peric-Mazar (antonioperic) - César Suárez (csuarez) - Bjorn Twachtmann (dotbjorn) + - Goran (gog) - Tobias Genberg (lorceroth) - Michael Simonson (mikes) - Nicolas Badey (nico-b) - Olivier Scherler (oscherler) - Shane Preece (shane) - Johannes Goslar + - Mike Gladysch - Geoff - georaldc - wusuopu @@ -2508,10 +2664,11 @@ The Symfony Connect username in parenthesis allows to get more information - Alex Teterin (errogaht) - Gunnar Lium (gunnarlium) - Malte Wunsch (maltewunsch) - - Maarten de Boer (mdeboer) + - Simo Heinonen (simoheinonen) - Tiago Garcia (tiagojsag) - Artiom - Jakub Simon + - Brandon Antonio Lorenzo - Bouke Haarsma - mlievertz - Enrico Schultz @@ -2527,8 +2684,10 @@ The Symfony Connect username in parenthesis allows to get more information - Martin Schophaus (m_schophaus_adcada) - Martynas Sudintas (martiis) - Anton Sukhachev (mrsuh) + - Vitaliy Zhuk (zhukv) - Marcel Siegert - ryunosuke + - Roy de Vos Burchart - Francisco Facioni (fran6co) - Iwan van Staveren (istaveren) - Povilas S. (povilas) @@ -2539,6 +2698,7 @@ The Symfony Connect username in parenthesis allows to get more information - Boris Betzholz - Eric Caron - Arnau González + - GurvanVgx - 2manypeople - Wing - Thomas Bibb @@ -2548,6 +2708,7 @@ The Symfony Connect username in parenthesis allows to get more information - Alexandre Segura - Asier Etxebeste - Josef Cech + - AntoineDly - Andrii Boiko - Harold Iedema - Ikhsan Agustian @@ -2556,7 +2717,6 @@ The Symfony Connect username in parenthesis allows to get more information - Jakub Janata (janatjak) - Jibé Barth (jibbarth) - Matthew Foster (mfoster) - - Reyo Stallenberg (reyostallenberg) - Paul Seiffert (seiffert) - Vasily Khayrulin (sirian) - Stas Soroka (stasyan) @@ -2565,28 +2725,34 @@ The Symfony Connect username in parenthesis allows to get more information - Dan Blows - Matt Wells - Nicolas Appriou + - Javier Alfonso Bellota de Frutos - stloyd - Andreas - Chris Tickner - Andrew Coulton - Ulugbek Miniyarov - Jeremy Benoist + - sdrewergutland - Michal Gebauer + - René Landgrebe - Phil Davis - Gleb Sidora - David Stone - Jovan Perovic (jperovic) - Pablo Maria Martelletti (pmartelletti) - Sander van der Vlugt (stranding) - - Yassine Guedidi (yguedidi) + - Maxim Tugaev (tugmaks) - Florian Bogey - Waqas Ahmed - Bert Hekman - Luis Muñoz - Matthew Donadio + - Kris Buist - Houziaux mike - Phobetor + - Yoann MOROCUTTI - Markus + - Zayan Goripov - Janusz Mocek - Thomas Chmielowiec - shdev @@ -2600,25 +2766,32 @@ The Symfony Connect username in parenthesis allows to get more information - Jesper Noordsij - DerStoffel - Maciej Schmidt + - tatankat - nuncanada - Thierry Marianne - František Bereň - Jeremiah VALERIE - Mike Francis - Nil Borodulia + - Adam Katz - Almog Baku (almogbaku) + - Arrakis (arrakis) + - Benjamin Schultz (bschultz) - Gerd Christian Kunze (derdu) - Ionel Scutelnicu (ionelscutelnicu) - Kamil Madejski (kmadejski) - Nicolas Tallefourtané (nicolab) - Botond Dani (picur) + - Radek Wionczek (rwionczek) - Nick Stemerdink - David Stone + - Vincent Bouzeran - Grayson Koonce - Wissame MEKHILEF - Romain Dorgueil - Christopher Parotat - Dennis Haarbrink + - Daniel Kozák - 蝦米 - Julius Beckmann (h4cc) - Andrey Helldar (helldar) @@ -2635,6 +2808,7 @@ The Symfony Connect username in parenthesis allows to get more information - Andy Stanberry - Felix Marezki - Normunds + - Walter Doekes - Thomas Rothe - Troy Crawford - nietonfir @@ -2676,9 +2850,13 @@ The Symfony Connect username in parenthesis allows to get more information - Milos Colakovic (project2481) - Rénald Casagraude (rcasagraude) - Robin Duval (robin-duval) + - Mohammad Ali Sarbanha (sarbanha) + - Steeve Titeca (stiteca) - Artem Lopata (bumz) - alex + - evgkord - Roman Orlov + - Andreas Allacher - VolCh - Alexey Popkov - Gijs Kunze @@ -2696,6 +2874,7 @@ The Symfony Connect username in parenthesis allows to get more information - Julien Moulin (lizjulien) - Raito Akehanareru (raito) - Mauro Foti (skler) + - skmedix (skmedix) - Yannick Warnier (ywarnier) - Jörn Lang - Kevin Decherf @@ -2708,16 +2887,20 @@ The Symfony Connect username in parenthesis allows to get more information - Michael van Tricht - ReScO - Tim Strehle + - cay89 - Sam Ward - Hans N. Hjort - Walther Lalk - Adam - Ivo + - Markus Staab - Sören Bernstein + - michael.kubovic - devel - taiiiraaa - gedrox - Alan Bondarchuk + - Cyril HERRERA - dropfen - Andrey Chernykh - Edvinas Klovas @@ -2764,18 +2947,23 @@ The Symfony Connect username in parenthesis allows to get more information - Alexander Zogheb - Rémi Blaise - Nicolas Séverin + - Houssem - Joel Marcey - zolikonta - David Christmann - root - pf + - Zoli Konta - Vincent Chalnot + - Roeland Jago Douma + - Patrizio Bekerle - Tom Maguire - Mateusz Lerczak - Richard Quadling - David Zuelke - Adrian - neFAST + - Peter Gribanov - Pierre Rineau - Florian Morello - Maxim Lovchikov @@ -2792,11 +2980,15 @@ The Symfony Connect username in parenthesis allows to get more information - Omar Yepez (oyepez003) - Jonny Schmid (schmidjon) - Götz Gottwald + - Adrien Peyre + - Christoph Krapp + - andreyserdjuk - Nick Chiu - Robert Campbell - Matt Lehner - Helmut Januschka - Hein Zaw Htet™ + - Kieran - Ruben Kruiswijk - Cosmin-Romeo TANASE - Michael J @@ -2807,6 +2999,7 @@ The Symfony Connect username in parenthesis allows to get more information - Jelle Kapitein - Jochen Mandl - Marin Nicolae + - Gerrit Addiks - Albert Prat - Alessandro Loffredo - Ian Phillips @@ -2825,7 +3018,6 @@ The Symfony Connect username in parenthesis allows to get more information - David Windell - Gabriel Birke - Derek Bonner - - Ivan Kurnosov - martijn - annesosensio - NothingWeAre @@ -2833,6 +3025,7 @@ The Symfony Connect username in parenthesis allows to get more information - Alan Chen - Maerlyn - Even André Fiskvik + - tourze - Erik van Wingerden - Valouleloup - Alexis MARQUIS @@ -2884,7 +3077,6 @@ The Symfony Connect username in parenthesis allows to get more information - Kevin Verschaeve (keversc) - Kevin Herrera (kherge) - Luis Ramón López López (lrlopez) - - Matheo Daninos (mathdns) - Mehdi Mabrouk (mehdidev) - Bart Reunes (metalarend) - Muriel (metalmumu) @@ -2896,6 +3088,7 @@ The Symfony Connect username in parenthesis allows to get more information - Olivier Laviale (olvlvl) - Pablo Monterde Perez (plebs) - Jimmy Leger (redpanda) + - Sébastien JEAN (sebastien76) - Mokhtar Tlili (sf-djuba) - Marcin Szepczynski (szepczynski) - Simone Di Maulo (toretto460) @@ -2903,7 +3096,9 @@ The Symfony Connect username in parenthesis allows to get more information - Lajos Veres (vlajos) - Vladimir Chernyshev (volch) - Yorkie Chadwick (yorkie76) + - Pavel Barton - GuillaumeVerdon + - Marien Fressinaud - ureimers - akimsko - Youpie @@ -2914,9 +3109,11 @@ The Symfony Connect username in parenthesis allows to get more information - Francois Martin - Saem Ghani - Stefan Oderbolz + - Tamás Szigeti - Gabriel Moreira - Alexey Popkov - ChS + - Jannik Zschiesche - Alexis MARQUIS - Joseph Deray - Damian Sromek @@ -2934,7 +3131,6 @@ The Symfony Connect username in parenthesis allows to get more information - HADJEDJ Vincent (hadjedjvincent) - Daniele Cesarini (ijanki) - Ismail Asci (ismailasci) - - Simon (kosssi) - Ondřej Mirtes (mirtes) - Paulius Jarmalavičius (pjarmalavicius) - Ramon Ornelas (ramonornela) @@ -2942,6 +3138,7 @@ The Symfony Connect username in parenthesis allows to get more information - Thomas Dutrion (theocrite) - Till Klampaeckel (till) - Tobias Weinert (tweini) + - Valtteri R (valtzu) - Wotre - goohib - Tom Counsell @@ -2951,8 +3148,8 @@ The Symfony Connect username in parenthesis allows to get more information - Mantas Urnieža - temperatur - Paul Andrieux + - Sezil - Cas - - Gwendolen Lynch - ghazy ben ahmed - Karolis - Myke79 @@ -2967,8 +3164,10 @@ The Symfony Connect username in parenthesis allows to get more information - znerol - Christian Eikermann - Sergei Shitikov + - Steffen Keuper - Antonio Angelino - Pavel Golovin + - Tema Yud - Matt Fields - Andras Debreczeni - Vladimir Sazhin @@ -2979,7 +3178,9 @@ The Symfony Connect username in parenthesis allows to get more information - Antoine LA - Vyacheslav Slinko - Benjamin Laugueux + - Lane Shukhov - Jakub Chábek + - William Pinaud (DocFX) - Johannes - Jörg Rühl - wesleyh @@ -3009,8 +3210,11 @@ The Symfony Connect username in parenthesis allows to get more information - n-aleha - Anatol Belski - Alexis BOYER + - Shyim - Kaipi Yann + - wiseguy1394 - adam-mospan + - nerdgod - Sam Williams - Guillaume Aveline - Adrian Philipp @@ -3018,6 +3222,7 @@ The Symfony Connect username in parenthesis allows to get more information - Markus Tacker - Kasperki - Tammy D + - Adrien Foulon - Ryan Rud - Ondrej Slinták - vlechemin @@ -3027,17 +3232,20 @@ The Symfony Connect username in parenthesis allows to get more information - Skorney - Lucas Matte - fmarchalemisys + - MGatner - mieszko4 - Steve Preston - ibasaw - Wojciech Skorodecki - Kevin Frantz - Neophy7e + - Evert Jan Hakvoort - bokonet - Arrilot + - andrey-tech - Shaun Simmons - - Markus Staab - Pierre-Louis LAUNAY + - A. Pauly - djama - Michael Gwynne - Eduardo Conceição @@ -3051,6 +3259,7 @@ The Symfony Connect username in parenthesis allows to get more information - Shude - Ondřej Führer - Sema + - Ayke Halder - Thorsten Hallwas - Brian Freytag - Alex Nostadt @@ -3061,8 +3270,8 @@ The Symfony Connect username in parenthesis allows to get more information - zorn - Yuriy Potemkin - Emilie Lorenzo + - prudhomme victor - enomotodev - - Edvin Hultberg - Vincent - Benjamin Long - Ben Miller @@ -3075,6 +3284,7 @@ The Symfony Connect username in parenthesis allows to get more information - Ahmed Abdulrahman - dinitrol - Penny Leach + - Kevin Mian Kraiker - Yurii K - Richard Trebichavský - g123456789l @@ -3091,6 +3301,7 @@ The Symfony Connect username in parenthesis allows to get more information - Sam Anthony - Christian Stocker - Oussama Elgoumri + - Gert de Pagter - David Lima - Dawid Nowak - Lesnykh Ilia @@ -3101,14 +3312,18 @@ The Symfony Connect username in parenthesis allows to get more information - Alfonso Fernández García - phc - Дмитрий Пацура + - db306 - Michaël VEROUX - Julia - Lin Lu - arduanov - sualko + - Fabien + - Martin Komischke - ADmad - Nicolas Roudaire - Abdouni Karim (abdounikarim) + - Adrian Günter (adrianguenter) - Andreas Forsblom (aforsblo) - Alex Olmos (alexolmos) - Cedric BERTOLINI (alsciende) @@ -3117,6 +3332,7 @@ The Symfony Connect username in parenthesis allows to get more information - Juan Ases García (ases) - Siragusa (asiragusa) - Daniel Basten (axhm3a) + - Albert Bakker (babbert) - Bernd Matzner (bmatzner) - Bram Tweedegolf (bram_tweedegolf) - Brandon Kelly (brandonkelly) @@ -3124,6 +3340,7 @@ The Symfony Connect username in parenthesis allows to get more information - Kousuke Ebihara (co3k) - Loïc Vernet (coil) - Christoph Vincent Schaefer (cvschaefer) + - Kamil Piwowarski (cyklista) - Damon Jones (damon__jones) - David Courtey (david-crty) - Łukasz Giza (destroyer) @@ -3140,7 +3357,6 @@ The Symfony Connect username in parenthesis allows to get more information - Gerry Vandermaesen (gerryvdm) - Arash Tabrizian (ghost098) - Vladislav Krupenkin (ideea) - - Ilija Tovilo (ilijatovilo) - Peter Orosz (ill_logical) - Imangazaliev Muhammad (imangazaliev) - j0k (j0k) @@ -3158,6 +3374,7 @@ The Symfony Connect username in parenthesis allows to get more information - Sébastien Armand (khepin) - Pierre-Chanel Gauthier (kmecnin) - Krzysztof Menżyk (krymen) + - Kenjy Thiébault (kthiebault) - samuel laulhau (lalop) - Laurent Bachelier (laurentb) - Luís Cobucci (lcobucci) @@ -3166,10 +3383,12 @@ The Symfony Connect username in parenthesis allows to get more information - Moritz Borgmann (mborgmann) - Matt Drollette (mdrollette) - Adam Monsen (meonkeys) + - Steffen Persch (n3o77) - Ala Eddine Khefifi (nayzo) - emilienbouard (neime) - Nicholas Byfleet (nickbyfleet) - Nicolas Bondoux (nsbx) + - Cedric Kastner (nurtext) - ollie harridge (ollietb) - Pawel Szczepanek (pauluz) - Philippe Degeeter (pdegeeter) @@ -3192,6 +3411,7 @@ The Symfony Connect username in parenthesis allows to get more information - Schuyler Jager (sjager) - Volker (skydiablo) - Julien Sanchez (sumbobyboys) + - Sylvain BEISSIER (sylvain-beissier) - Ron Gähler (t-ronx) - Guillermo Gisinger (t3chn0r) - Tom Newby (tomnewbyau) @@ -3223,12 +3443,14 @@ The Symfony Connect username in parenthesis allows to get more information - Konstantin Scheumann - Michael - fh-github@fholzhauer.de + - rogamoore - AbdElKader Bouadjadja - DSeemiller - Jan Emrich - Mark Topper - Romain - Xavier REN + - Kevin Meijer - max - Ahmad Mayahi (ahmadmayahi) - Mohamed Karnichi (amiral) @@ -3238,6 +3460,7 @@ The Symfony Connect username in parenthesis allows to get more information - Bogdan Rancichi (devck) - Daniel Kolvik (dkvk) - Marc Lemay (flug) + - Courcier Marvin (helyakin) - Henne Van Och (hennevo) - Jeroen De Dauw (jeroendedauw) - Maxime COLIN (maximecolin) diff --git a/LICENSE b/LICENSE index 88bf75bb4d6a2..0138f8f071351 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 9882d80116fb8..78f195659fa7a 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Community --------- * [Join the Symfony Community][11] and meet other members at the [Symfony events][12]. -* [Get Symfony support][13] on Stack Overflow, Slack, IRC, etc. +* [Get Symfony support][13] on GitHub Discussions, Slack, etc. * Follow us on [GitHub][14], [Twitter][15] and [Facebook][16]. * Read our [Code of Conduct][24] and meet the [CARE Team][25]. diff --git a/UPGRADE-6.1.md b/UPGRADE-6.1.md index a3811cb502b9d..333731b4e1f83 100644 --- a/UPGRADE-6.1.md +++ b/UPGRADE-6.1.md @@ -42,8 +42,6 @@ Serializer * Deprecate `ContextAwareNormalizerInterface`, use `NormalizerInterface` instead * Deprecate `ContextAwareDenormalizerInterface`, use `DenormalizerInterface` instead - * Deprecate `ContextAwareEncoderInterface`, use `EncoderInterface` instead - * Deprecate `ContextAwareDecoderInterface`, use `DecoderInterface` instead * Deprecate supporting denormalization for `AbstractUid` in `UidNormalizer`, use one of `AbstractUid` child class instead * Deprecate denormalizing to an abstract class in `UidNormalizer` diff --git a/UPGRADE-6.2.md b/UPGRADE-6.2.md index 8271d0843738a..b263ecdeebb3a 100644 --- a/UPGRADE-6.2.md +++ b/UPGRADE-6.2.md @@ -1,6 +1,32 @@ UPGRADE FROM 6.1 to 6.2 ======================= +Config +------ + + * Deprecate calling `NodeBuilder::setParent()` without any arguments + +Console +------- + + * Deprecate calling `*Command::setApplication()`, `*FormatterStyle::setForeground/setBackground()`, `Helper::setHelpSet()`, `Input*::setDefault()`, `Question::setAutocompleterCallback/setValidator()`without any arguments + * Change the signature of `OutputFormatterStyleInterface::setForeground/setBackground()` to `setForeground/setBackground(?string)` + * Change the signature of `HelperInterface::setHelperSet()` to `setHelperSet(?HelperSet)` + +DependencyInjection +------------------- + + * Change the signature of `ContainerAwareInterface::setContainer()` to `setContainer(?ContainerInterface)` + * Deprecate calling `ContainerAwareTrait::setContainer()` without arguments + * Deprecate using numeric parameter names + +Form +---- + + * Deprecate calling `Button/Form::setParent()`, `ButtonBuilder/FormConfigBuilder::setDataMapper()`, `TransformationFailedException::setInvalidMessage()` without arguments + * Change the signature of `FormConfigBuilderInterface::setDataMapper()` to `setDataMapper(?DataMapperInterface)` + * Change the signature of `FormInterface::setParent()` to `setParent(?self)` + FrameworkBundle --------------- @@ -8,11 +34,24 @@ FrameworkBundle `Symfony\Component\Serializer\Normalizer\PropertyNormalizer` autowiring aliases, type-hint against `Symfony\Component\Serializer\Normalizer\NormalizerInterface` or implement `NormalizerAwareInterface` instead * Deprecate `AbstractController::renderForm()`, use `render()` instead + * Deprecate `FrameworkExtension::registerRateLimiter()` HttpFoundation -------------- * Deprecate `Request::getContentType()`, use `Request::getContentTypeFormat()` instead + * Deprecate calling `JsonResponse::setCallback()`, `Response::setExpires/setLastModified/setEtag()`, `MockArraySessionStorage/NativeSessionStorage::setMetadataBag()`, `NativeSessionStorage::setSaveHandler()` without arguments + +HttpClient +---------- + + * Deprecate implementing `Http\Message\RequestFactory`, `StreamFactory` and `UriFactory` on `HttplugClient` + +HttpKernel +---------- + + * Deprecate `ArgumentValueResolverInterface`, use `ValueResolverInterface` instead + * Deprecate calling `ConfigDataCollector::setKernel()`, `RouterListener::setCurrentRequest()` without arguments Ldap ---- @@ -24,20 +63,72 @@ Mailer * Deprecate the `OhMySMTP` transport, use `MailPace` instead +Messenger +-------- + + * Deprecate `MessageHandlerInterface` and `MessageSubscriberInterface`, use the `AsMessageHandler` attribute instead + +Mime +---- + + * Deprecate `Email::attachPart()`, use `addPart()` instead + * Deprecate calling `Message::setBody()` without arguments + +Notifier +-------- + + * [BC BREAK] The following data providers for `TransportTestCase` are now static: `toStringProvider()`, `supportedMessagesProvider()` and `unsupportedMessagesProvider()` + * [BC BREAK] The `TransportTestCase::createTransport()` method is now static + +PropertyAccess +-------------- + + * Deprecate calling `PropertyAccessorBuilder::setCacheItemPool()` without arguments + * Implementing the `PropertyPathInterface` without implementing the `isNullSafe()` method is deprecated + Security -------- * Add maximum username length enforcement of 4096 characters in `UserBadge` to prevent [session storage flooding](https://symfony.com/blog/cve-2016-4423-large-username-storage-in-session) - * Deprecate the `Symfony\Component\Security\Core\Security` class and service, use `Symfony\Bundle\SecurityBundle\Security\Security` instead + * Deprecate the `Symfony\Component\Security\Core\Security` class and service, use `Symfony\Bundle\SecurityBundle\Security` instead + * Deprecate the `Symfony\Bundle\SecurityBundle\Security::ACCESS_DENIED_ERROR` property, use `Symfony\Component\Security\Http\SecurityRequestAttributes::ACCESS_DENIED_ERROR` instead + * Deprecate the `Symfony\Bundle\SecurityBundle\Security::AUTHENTICATION_ERROR` property, use `Symfony\Component\Security\Http\SecurityRequestAttributes::AUTHENTICATION_ERROR` instead + * Deprecate the `Symfony\Bundle\SecurityBundle\Security::LAST_USERNAME` property, use `Symfony\Component\Security\Http\SecurityRequestAttributes::LAST_USERNAME` instead + * Deprecate the `Symfony\Bundle\SecurityBundle\Security::MAX_USERNAME_LENGTH` property, use `Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge::MAX_USERNAME_LENGTH` instead * Passing empty username or password parameter when using `JsonLoginAuthenticator` is not supported anymore * Add `$lifetime` parameter to `LoginLinkHandlerInterface::createLoginLink()` + * Change the signature of `TokenStorageInterface::setToken()` to `setToken(?TokenInterface $token)` + * Deprecate calling `TokenStorage::setToken()` or `UsageTrackingTokenStorage::setToken()` without arguments + +SecurityBundle +-------------- + + * Deprecate the `security.enable_authenticator_manager` config option + +Serializer +---------- + + * Deprecate calling `AttributeMetadata::setSerializedName()`, `ClassMetadata::setClassDiscriminatorMapping()` without arguments + * Change the signature of `AttributeMetadataInterface::setSerializedName()` to `setSerializedName(?string)` + * Change the signature of `ClassMetadataInterface::setClassDiscriminatorMapping()` to `setClassDiscriminatorMapping(?ClassDiscriminatorMapping)` + +Translation +----------- + + * Deprecate `PhpExtractor` in favor of `PhpAstExtractor` + * Add `PhpAstExtractor` (requires [nikic/php-parser](https://github.com/nikic/php-parser) to be installed) Validator --------- * Deprecate the `loose` e-mail validation mode, use `html5` instead +VarDumper +--------- + + * Deprecate calling `VarDumper::setHandler()` without arguments + Workflow -------- @@ -47,3 +138,4 @@ Workflow ``` * The first argument of `WorkflowDumpCommand` should be a `ServiceLocator` of all workflows indexed by names + * Deprecate calling `Definition::setInitialPlaces()` without arguments diff --git a/UPGRADE-7.0.md b/UPGRADE-7.0.md index 587796bbb6e9f..751ef30711d33 100644 --- a/UPGRADE-7.0.md +++ b/UPGRADE-7.0.md @@ -1,8 +1,8 @@ UPGRADE FROM 6.4 to 7.0 ======================= -Workflow --------- +Symfony 6.4 and Symfony 7.0 will be released simultaneously at the end of November 2023. According to the Symfony +release process, both versions will have the same features, but Symfony 7.0 won't include any deprecated features. +To upgrade, make sure to resolve all deprecation notices. - * The first argument of `WorkflowDumpCommand` must be a `ServiceLocator` of all - workflows indexed by names +This file will be updated on the branch 7.0 for each deprecated feature that is removed. diff --git a/composer.json b/composer.json index 59b21fd6d0c9a..18539cf8b7540 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "php-http/async-client-implementation": "*", "php-http/client-implementation": "*", "psr/cache-implementation": "2.0|3.0", + "psr/clock-implementation": "1.0", "psr/container-implementation": "1.1|2.0", "psr/event-dispatcher-implementation": "1.0", "psr/http-client-implementation": "1.0", @@ -36,10 +37,11 @@ "composer-runtime-api": ">=2.1", "ext-xml": "*", "friendsofphp/proxy-manager-lts": "^1.0.2", - "doctrine/event-manager": "~1.0", + "doctrine/event-manager": "^1.2|^2", "doctrine/persistence": "^2|^3", "twig/twig": "^2.13|^3.0.4", "psr/cache": "^2.0|^3.0", + "psr/clock": "^1.0", "psr/container": "^1.1|^2.0", "psr/event-dispatcher": "^1.0", "psr/link": "^1.1|^2.0", @@ -121,12 +123,12 @@ "async-aws/sqs": "^1.0", "async-aws/sns": "^1.0", "cache/integration-tests": "dev-master", - "doctrine/annotations": "^1.13.1", - "doctrine/collections": "~1.0", + "doctrine/annotations": "^1.13.1|^2", + "doctrine/collections": "^1.0|^2.0", "doctrine/data-fixtures": "^1.1", "doctrine/dbal": "^2.13.1|^3.0", "doctrine/orm": "^2.7.4", - "egulias/email-validator": "^2.1.10|^3.1", + "egulias/email-validator": "^2.1.10|^3.1|^4", "guzzlehttp/promises": "^1.4", "league/html-to-markdown": "^5.0", "masterminds/html5": "^2.7.2", @@ -134,13 +136,14 @@ "nyholm/psr7": "^1.0", "pda/pheanstalk": "^4.0", "php-http/httplug": "^1.0|^2.0", + "php-http/message-factory": "^1.0", "phpdocumentor/reflection-docblock": "^5.2", "phpstan/phpdoc-parser": "^1.0", "predis/predis": "~1.1", "psr/http-client": "^1.0", "psr/simple-cache": "^1.0|^2.0|^3.0", "symfony/mercure-bundle": "^0.3", - "symfony/phpunit-bridge": "^5.4|^6.0", + "symfony/phpunit-bridge": "^5.4|^6.0|^7.0", "symfony/runtime": "self.version", "symfony/security-acl": "~2.8|~3.0", "twig/cssinliner-extra": "^2.12|^3", @@ -156,11 +159,11 @@ "masterminds/html5": "<2.6", "phpdocumentor/reflection-docblock": "<5.2", "phpdocumentor/type-resolver": "<1.5.1", - "ocramius/proxy-manager": "<2.1", - "phpunit/phpunit": "<5.4.3" + "phpunit/phpunit": "<7.5|9.1.2" }, "config": { "allow-plugins": { + "php-http/discovery": false, "symfony/runtime": true } }, diff --git a/phpunit b/phpunit index e26fecd73cc9d..94baca39735ba 100755 --- a/phpunit +++ b/phpunit @@ -10,15 +10,9 @@ if (!file_exists(__DIR__.'/vendor/symfony/phpunit-bridge/bin/simple-phpunit')) { exit(1); } if (!getenv('SYMFONY_PHPUNIT_VERSION')) { - if (\PHP_VERSION_ID < 70200) { - putenv('SYMFONY_PHPUNIT_VERSION=7.5'); - } elseif (\PHP_VERSION_ID < 70300) { - putenv('SYMFONY_PHPUNIT_VERSION=8.5.26'); - } else { - putenv('SYMFONY_PHPUNIT_VERSION=9.5'); - } + putenv('SYMFONY_PHPUNIT_VERSION=9.6'); } -if (!getenv('SYMFONY_PATCH_TYPE_DECLARATIONS') && \PHP_VERSION_ID >= 70300) { +if (!getenv('SYMFONY_PATCH_TYPE_DECLARATIONS')) { putenv('SYMFONY_PATCH_TYPE_DECLARATIONS=deprecations=1'); } if (getcwd() === realpath(__DIR__.'/src/Symfony/Bridge/PhpUnit')) { diff --git a/psalm.xml b/psalm.xml index 1544a71346c41..8553f03fa117d 100644 --- a/psalm.xml +++ b/psalm.xml @@ -7,6 +7,7 @@ xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd" cacheDirectory="./.github/psalm/cache/" errorBaseline=".github/psalm/psalm.baseline.xml" + findUnusedBaselineEntry="false" > @@ -43,4 +44,8 @@ + + + + diff --git a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php index cb0c2034982a6..b531857c1422c 100644 --- a/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php +++ b/src/Symfony/Bridge/Doctrine/ArgumentResolver/EntityValueResolver.php @@ -19,7 +19,7 @@ use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -29,7 +29,7 @@ * @author Fabien Potencier * @author Jérémy Derussé */ -final class EntityValueResolver implements ArgumentValueResolverInterface +final class EntityValueResolver implements ValueResolverInterface { public function __construct( private ManagerRegistry $registry, @@ -38,61 +38,42 @@ public function __construct( ) { } - /** - * {@inheritdoc} - */ - public function supports(Request $request, ArgumentMetadata $argument): bool + public function resolve(Request $request, ArgumentMetadata $argument): array { - if (!$this->registry->getManagerNames()) { - return false; + if (\is_object($request->attributes->get($argument->getName()))) { + return []; } - $options = $this->getOptions($argument); + $options = $argument->getAttributes(MapEntity::class, ArgumentMetadata::IS_INSTANCEOF); + $options = ($options[0] ?? $this->defaults)->withDefaults($this->defaults, $argument->getType()); + if (!$options->class || $options->disabled) { - return false; + return []; } - - // Doctrine Entity? - if (!$objectManager = $this->getManager($options->objectManager, $options->class)) { - return false; + if (!$manager = $this->getManager($options->objectManager, $options->class)) { + return []; } - return !$objectManager->getMetadataFactory()->isTransient($options->class); - } - - /** - * {@inheritdoc} - */ - public function resolve(Request $request, ArgumentMetadata $argument): iterable - { - $options = $this->getOptions($argument); - $name = $argument->getName(); - $class = $options->class; - - $errorMessage = null; + $message = ''; if (null !== $options->expr) { - if (null === $object = $this->findViaExpression($class, $request, $options->expr, $options)) { - $errorMessage = sprintf('The expression "%s" returned null', $options->expr); + if (null === $object = $this->findViaExpression($manager, $request, $options)) { + $message = sprintf(' The expression "%s" returned null.', $options->expr); } // find by identifier? - } elseif (false === $object = $this->find($class, $request, $options, $name)) { + } elseif (false === $object = $this->find($manager, $request, $options, $argument->getName())) { // find by criteria - if (false === $object = $this->findOneBy($class, $request, $options)) { - if (!$argument->isNullable()) { - throw new \LogicException(sprintf('Unable to guess how to get a Doctrine instance from the request information for parameter "%s".', $name)); - } - + if (!$criteria = $this->getCriteria($request, $options, $manager)) { + return []; + } + try { + $object = $manager->getRepository($options->class)->findOneBy($criteria); + } catch (NoResultException|ConversionException) { $object = null; } } if (null === $object && !$argument->isNullable()) { - $message = sprintf('"%s" object not found by the "%s" Argument Resolver.', $class, self::class); - if ($errorMessage) { - $message .= ' '.$errorMessage; - } - - throw new NotFoundHttpException($message); + throw new NotFoundHttpException(sprintf('"%s" object not found by "%s".', $options->class, self::class).$message); } return [$object]; @@ -104,18 +85,16 @@ private function getManager(?string $name, string $class): ?ObjectManager return $this->registry->getManagerForClass($class); } - if (!isset($this->registry->getManagerNames()[$name])) { - return null; - } - try { - return $this->registry->getManager($name); + $manager = $this->registry->getManager($name); } catch (\InvalidArgumentException) { return null; } + + return $manager->getMetadataFactory()->isTransient($class) ? null : $manager; } - private function find(string $class, Request $request, MapEntity $options, string $name): false|object|null + private function find(ObjectManager $manager, Request $request, MapEntity $options, string $name): false|object|null { if ($options->mapping || $options->exclude) { return false; @@ -126,16 +105,15 @@ private function find(string $class, Request $request, MapEntity $options, strin return $id; } - $objectManager = $this->getManager($options->objectManager, $class); - if ($options->evictCache && $objectManager instanceof EntityManagerInterface) { - $cacheProvider = $objectManager->getCache(); - if ($cacheProvider && $cacheProvider->containsEntity($class, $id)) { - $cacheProvider->evictEntity($class, $id); + if ($options->evictCache && $manager instanceof EntityManagerInterface) { + $cacheProvider = $manager->getCache(); + if ($cacheProvider && $cacheProvider->containsEntity($options->class, $id)) { + $cacheProvider->evictEntity($options->class, $id); } } try { - return $objectManager->getRepository($class)->find($id); + return $manager->getRepository($options->class)->find($id); } catch (NoResultException|ConversionException) { return null; } @@ -162,21 +140,24 @@ private function getIdentifier(Request $request, MapEntity $options, string $nam } if ($request->attributes->has($name)) { - return $request->attributes->get($name); + return $request->attributes->get($name) ?? ($options->stripNull ? false : null); } if (!$options->id && $request->attributes->has('id')) { - return $request->attributes->get('id'); + return $request->attributes->get('id') ?? ($options->stripNull ? false : null); } return false; } - private function findOneBy(string $class, Request $request, MapEntity $options): false|object|null + private function getCriteria(Request $request, MapEntity $options, ObjectManager $manager): array { if (null === $mapping = $options->mapping) { - $keys = $request->attributes->keys(); - $mapping = $keys ? array_combine($keys, $keys) : []; + $mapping = $request->attributes->keys(); + } + + if ($mapping && \is_array($mapping) && array_is_list($mapping)) { + $mapping = array_combine($mapping, $mapping); } foreach ($options->exclude as $exclude) { @@ -184,18 +165,17 @@ private function findOneBy(string $class, Request $request, MapEntity $options): } if (!$mapping) { - return false; + return []; } // if a specific id has been defined in the options and there is no corresponding attribute // return false in order to avoid a fallback to the id which might be of another object if (\is_string($options->id) && null === $request->attributes->get($options->id)) { - return false; + return []; } $criteria = []; - $objectManager = $this->getManager($options->objectManager, $class); - $metadata = $objectManager->getClassMetadata($class); + $metadata = $manager->getClassMetadata($options->class); foreach ($mapping as $attribute => $field) { if (!$metadata->hasField($field) && (!$metadata->hasAssociation($field) || !$metadata->isSingleValuedAssociation($field))) { @@ -209,38 +189,22 @@ private function findOneBy(string $class, Request $request, MapEntity $options): $criteria = array_filter($criteria, static fn ($value) => null !== $value); } - if (!$criteria) { - return false; - } - - try { - return $objectManager->getRepository($class)->findOneBy($criteria); - } catch (NoResultException|ConversionException) { - return null; - } + return $criteria; } - private function findViaExpression(string $class, Request $request, string $expression, MapEntity $options): ?object + private function findViaExpression(ObjectManager $manager, Request $request, MapEntity $options): ?object { if (!$this->expressionLanguage) { throw new \LogicException(sprintf('You cannot use the "%s" if the ExpressionLanguage component is not available. Try running "composer require symfony/expression-language".', __CLASS__)); } - $repository = $this->getManager($options->objectManager, $class)->getRepository($class); + $repository = $manager->getRepository($options->class); $variables = array_merge($request->attributes->all(), ['repository' => $repository]); try { - return $this->expressionLanguage->evaluate($expression, $variables); + return $this->expressionLanguage->evaluate($options->expr, $variables); } catch (NoResultException|ConversionException) { return null; } } - - private function getOptions(ArgumentMetadata $argument): MapEntity - { - /** @var MapEntity $options */ - $options = $argument->getAttributes(MapEntity::class, ArgumentMetadata::IS_INSTANCEOF)[0] ?? $this->defaults; - - return $options->withDefaults($this->defaults, $argument->getType()); - } } diff --git a/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php b/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php index 91cc25026a8b6..0c107c066bac4 100644 --- a/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php +++ b/src/Symfony/Bridge/Doctrine/CacheWarmer/ProxyCacheWarmer.php @@ -40,8 +40,6 @@ public function isOptional(): bool } /** - * {@inheritdoc} - * * @return string[] A list of files to preload on PHP 7.4+ */ public function warmUp(string $cacheDir): array @@ -50,7 +48,7 @@ public function warmUp(string $cacheDir): array foreach ($this->registry->getManagers() as $em) { // we need the directory no matter the proxy cache generation strategy if (!is_dir($proxyCacheDir = $em->getConfiguration()->getProxyDir())) { - if (false === @mkdir($proxyCacheDir, 0777, true)) { + if (false === @mkdir($proxyCacheDir, 0777, true) && !is_dir($proxyCacheDir)) { throw new \RuntimeException(sprintf('Unable to create the Doctrine Proxy directory "%s".', $proxyCacheDir)); } } elseif (!is_writable($proxyCacheDir)) { diff --git a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php index d262c5e3591e4..bf337bf751635 100644 --- a/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php +++ b/src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php @@ -44,9 +44,6 @@ public function __construct(ContainerInterface $container, array $subscriberIds $this->subscribers = $subscriberIds; } - /** - * {@inheritdoc} - */ public function dispatchEvent($eventName, EventArgs $eventArgs = null): void { if (!$this->initializedSubscribers) { @@ -68,21 +65,29 @@ public function dispatchEvent($eventName, EventArgs $eventArgs = null): void } /** - * {@inheritdoc} - * * @return object[][] */ public function getListeners($event = null): array { + if (null === $event) { + trigger_deprecation('symfony/doctrine-bridge', '6.2', 'Calling "%s()" without an event name is deprecated. Call "getAllListeners()" instead.', __METHOD__); + + return $this->getAllListeners(); + } if (!$this->initializedSubscribers) { $this->initializeSubscribers(); } - if (null !== $event) { - if (!isset($this->initialized[$event])) { - $this->initializeListeners($event); - } + if (!isset($this->initialized[$event])) { + $this->initializeListeners($event); + } + + return $this->listeners[$event]; + } - return $this->listeners[$event]; + public function getAllListeners(): array + { + if (!$this->initializedSubscribers) { + $this->initializeSubscribers(); } foreach ($this->listeners as $event => $listeners) { @@ -94,9 +99,6 @@ public function getListeners($event = null): array return $this->listeners; } - /** - * {@inheritdoc} - */ public function hasListeners($event): bool { if (!$this->initializedSubscribers) { @@ -106,9 +108,6 @@ public function hasListeners($event): bool return isset($this->listeners[$event]) && $this->listeners[$event]; } - /** - * {@inheritdoc} - */ public function addEventListener($events, $listener): void { if (!$this->initializedSubscribers) { @@ -130,9 +129,6 @@ public function addEventListener($events, $listener): void } } - /** - * {@inheritdoc} - */ public function removeEventListener($events, $listener): void { if (!$this->initializedSubscribers) { diff --git a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php index c5318414dbf68..f0f2b65ac3f15 100644 --- a/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php +++ b/src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php @@ -53,9 +53,6 @@ public function addLogger(string $name, DebugStack $logger) $this->loggers[$name] = $logger; } - /** - * {@inheritdoc} - */ public function collect(Request $request, Response $response, \Throwable $exception = null) { $this->data = [ @@ -132,17 +129,11 @@ public function getTime() return $time; } - /** - * {@inheritdoc} - */ public function getName(): string { return 'db'; } - /** - * {@inheritdoc} - */ protected function getCasters(): array { return parent::getCasters() + [ @@ -185,9 +176,7 @@ private function sanitizeQuery(string $connectionName, array $query): array { $query['explainable'] = true; $query['runnable'] = true; - if (null === $query['params']) { - $query['params'] = []; - } + $query['params'] ??= []; if (!\is_array($query['params'])) { $query['params'] = [$query['params']]; } diff --git a/src/Symfony/Bridge/Doctrine/DataCollector/ObjectParameter.php b/src/Symfony/Bridge/Doctrine/DataCollector/ObjectParameter.php index 549a6af8bb42a..384ba0efeb869 100644 --- a/src/Symfony/Bridge/Doctrine/DataCollector/ObjectParameter.php +++ b/src/Symfony/Bridge/Doctrine/DataCollector/ObjectParameter.php @@ -23,7 +23,7 @@ public function __construct(object $object, ?\Throwable $error) $this->object = $object; $this->error = $error; $this->stringable = \is_callable([$object, '__toString']); - $this->class = \get_class($object); + $this->class = $object::class; } public function getObject(): object diff --git a/src/Symfony/Bridge/Doctrine/DataFixtures/ContainerAwareLoader.php b/src/Symfony/Bridge/Doctrine/DataFixtures/ContainerAwareLoader.php index 98acccda50ba9..95dc8825ed213 100644 --- a/src/Symfony/Bridge/Doctrine/DataFixtures/ContainerAwareLoader.php +++ b/src/Symfony/Bridge/Doctrine/DataFixtures/ContainerAwareLoader.php @@ -32,9 +32,6 @@ public function __construct(ContainerInterface $container) $this->container = $container; } - /** - * {@inheritdoc} - */ public function addFixture(FixtureInterface $fixture) { if ($fixture instanceof ContainerAwareInterface) { diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php index c576ceb48fa5b..cd5fc91cb32b7 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/AbstractDoctrineExtension.php @@ -266,8 +266,8 @@ protected function detectMetadataDriver(string $dir, ContainerBuilder $container } $container->fileExists($resource, false); - if ($container->fileExists($dir.'/'.$this->getMappingObjectDefaultName(), false)) { - return $this->detectMappingType($dir, $container); + if ($container->fileExists($discoveryPath = $dir.'/'.$this->getMappingObjectDefaultName(), false)) { + return $this->detectMappingType($discoveryPath, $container); } return null; @@ -294,10 +294,16 @@ private function detectMappingType(string $directory, ContainerBuilder $containe foreach ($glob as $file) { $content = file_get_contents($file); - if (preg_match('/^#\[.*'.$quotedMappingObjectName.'\b/m', $content)) { + if ( + preg_match('/^#\[.*'.$quotedMappingObjectName.'\b/m', $content) || + preg_match('/^#\[.*Embeddable\b/m', $content) + ) { break; } - if (preg_match('/^ \* @.*'.$quotedMappingObjectName.'\b/m', $content)) { + if ( + preg_match('/^(?: \*|\/\*\*) @.*'.$quotedMappingObjectName.'\b/m', $content) || + preg_match('/^(?: \*|\/\*\*) @.*Embeddable\b/m', $content) + ) { $type = 'annotation'; break; } diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php index 92985d89ca4ca..aa76d7d2da8e5 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/DoctrineValidationPass.php @@ -28,9 +28,6 @@ public function __construct(string $managerType) $this->managerType = $managerType; } - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { $this->updateValidatorMappingFiles($container, 'xml', 'xml'); diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php index 74a3d3200f05b..30d0fb37ced80 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -53,9 +53,6 @@ public function __construct(string $connectionsParameter, string $managerTemplat $this->tagPrefix = $tagPrefix; } - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if (!$container->hasParameter($this->connectionsParameter)) { diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php index 95a74533ba0f5..1ea0470bf8d0b 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterUidTypePass.php @@ -19,9 +19,6 @@ final class RegisterUidTypePass implements CompilerPassInterface { - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if (!class_exists(AbstractUid::class)) { diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php index e191bea3ca16d..2697022365284 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/DoctrineChoiceLoader.php @@ -50,9 +50,6 @@ public function __construct(ObjectManager $manager, string $class, IdReader $idR $this->objectLoader = $objectLoader; } - /** - * {@inheritdoc} - */ protected function loadChoices(): iterable { return $this->objectLoader @@ -60,9 +57,6 @@ protected function loadChoices(): iterable : $this->manager->getRepository($this->class)->findAll(); } - /** - * @internal to be remove in Symfony 6 - */ protected function doLoadValuesForChoices(array $choices): array { // Optimize performance for single-field identifiers. We already diff --git a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php index 3364ffa4c4edb..7582add20f907 100644 --- a/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php +++ b/src/Symfony/Bridge/Doctrine/Form/ChoiceList/ORMQueryBuilderLoader.php @@ -11,6 +11,7 @@ namespace Symfony\Bridge\Doctrine\Form\ChoiceList; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Types\ConversionException; use Doctrine\DBAL\Types\Type; @@ -38,17 +39,11 @@ public function __construct(QueryBuilder $queryBuilder) $this->queryBuilder = $queryBuilder; } - /** - * {@inheritdoc} - */ public function getEntities(): array { return $this->queryBuilder->getQuery()->execute(); } - /** - * {@inheritdoc} - */ public function getEntitiesByIds(string $identifier, array $values): array { if (null !== $this->queryBuilder->getMaxResults() || 0 < (int) $this->queryBuilder->getFirstResult()) { @@ -76,7 +71,7 @@ public function getEntitiesByIds(string $identifier, array $values): array $entity = current($qb->getRootEntities()); $metadata = $qb->getEntityManager()->getClassMetadata($entity); if (\in_array($type = $metadata->getTypeOfField($identifier), ['integer', 'bigint', 'smallint'])) { - $parameterType = Connection::PARAM_INT_ARRAY; + $parameterType = class_exists(ArrayParameterType::class) ? ArrayParameterType::INTEGER : Connection::PARAM_INT_ARRAY; // Filter out non-integer values (e.g. ""). If we don't, some // databases such as PostgreSQL fail. @@ -84,7 +79,7 @@ public function getEntitiesByIds(string $identifier, array $values): array return (string) $v === (string) (int) $v || ctype_digit($v); })); } elseif (\in_array($type, ['ulid', 'uuid', 'guid'])) { - $parameterType = Connection::PARAM_STR_ARRAY; + $parameterType = class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY; // Like above, but we just filter out empty strings. $values = array_values(array_filter($values, function ($v) { @@ -105,7 +100,7 @@ public function getEntitiesByIds(string $identifier, array $values): array unset($value); } } else { - $parameterType = Connection::PARAM_STR_ARRAY; + $parameterType = class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY; } if (!$values) { return []; diff --git a/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php b/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php index d5dc1a23b72a6..61fc5f8c6e72b 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php +++ b/src/Symfony/Bridge/Doctrine/Form/DataTransformer/CollectionToArrayTransformer.php @@ -18,6 +18,8 @@ /** * @author Bernhard Schussek + * + * @implements DataTransformerInterface */ class CollectionToArrayTransformer implements DataTransformerInterface { diff --git a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php index f2e65a7f0a8c0..1847e50eeef28 100644 --- a/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php +++ b/src/Symfony/Bridge/Doctrine/Form/DoctrineOrmTypeGuesser.php @@ -44,9 +44,6 @@ public function __construct(ManagerRegistry $registry) $this->registry = $registry; } - /** - * {@inheritdoc} - */ public function guessType(string $class, string $property): ?TypeGuess { if (!$ret = $this->getMetadata($class)) { @@ -87,9 +84,6 @@ public function guessType(string $class, string $property): ?TypeGuess }; } - /** - * {@inheritdoc} - */ public function guessRequired(string $class, string $property): ?ValueGuess { $classMetadatas = $this->getMetadata($class); @@ -127,9 +121,6 @@ public function guessRequired(string $class, string $property): ?ValueGuess return null; } - /** - * {@inheritdoc} - */ public function guessMaxLength(string $class, string $property): ?ValueGuess { $ret = $this->getMetadata($class); @@ -148,9 +139,6 @@ public function guessMaxLength(string $class, string $property): ?ValueGuess return null; } - /** - * {@inheritdoc} - */ public function guessPattern(string $class, string $property): ?ValueGuess { $ret = $this->getMetadata($class); diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php index cd4dc42393059..9f7eed373df6f 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php @@ -258,6 +258,6 @@ private function getCachedEntityLoader(ObjectManager $manager, object $queryBuil { $hash = CachingFactoryDecorator::generateHash($vary); - return $this->entityLoaders[$hash] ?? ($this->entityLoaders[$hash] = $this->getLoader($manager, $queryBuilder, $class)); + return $this->entityLoaders[$hash] ??= $this->getLoader($manager, $queryBuilder, $class); } } diff --git a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php index d50ca5c339b95..fb45f99ac2657 100644 --- a/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php +++ b/src/Symfony/Bridge/Doctrine/Form/Type/EntityType.php @@ -57,9 +57,6 @@ public function getLoader(ObjectManager $manager, object $queryBuilder, string $ return new ORMQueryBuilderLoader($queryBuilder); } - /** - * {@inheritdoc} - */ public function getBlockPrefix(): string { return 'entity'; diff --git a/src/Symfony/Bridge/Doctrine/LICENSE b/src/Symfony/Bridge/Doctrine/LICENSE index 88bf75bb4d6a2..0138f8f071351 100644 --- a/src/Symfony/Bridge/Doctrine/LICENSE +++ b/src/Symfony/Bridge/Doctrine/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php b/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php index b4cc571bb56fc..f715e782e3af7 100644 --- a/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php +++ b/src/Symfony/Bridge/Doctrine/Logger/DbalLogger.php @@ -32,9 +32,6 @@ public function __construct(LoggerInterface $logger = null, Stopwatch $stopwatch $this->stopwatch = $stopwatch; } - /** - * {@inheritdoc} - */ public function startQuery($sql, array $params = null, array $types = null): void { $this->stopwatch?->start('doctrine', 'doctrine'); @@ -44,9 +41,6 @@ public function startQuery($sql, array $params = null, array $types = null): voi } } - /** - * {@inheritdoc} - */ public function stopQuery(): void { $this->stopwatch?->stop('doctrine'); diff --git a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php index 97f96c1c9a683..bcd16dc06e6f3 100644 --- a/src/Symfony/Bridge/Doctrine/ManagerRegistry.php +++ b/src/Symfony/Bridge/Doctrine/ManagerRegistry.php @@ -15,7 +15,7 @@ use ProxyManager\Proxy\GhostObjectInterface; use ProxyManager\Proxy\LazyLoadingInterface; use Symfony\Component\DependencyInjection\Container; -use Symfony\Component\VarExporter\LazyGhostObjectInterface; +use Symfony\Component\VarExporter\LazyObjectInterface; /** * References Doctrine connections and entity/document managers. @@ -29,17 +29,11 @@ abstract class ManagerRegistry extends AbstractManagerRegistry */ protected $container; - /** - * {@inheritdoc} - */ protected function getService($name): object { return $this->container->get($name); } - /** - * {@inheritdoc} - */ protected function resetService($name): void { if (!$this->container->initialized($name)) { @@ -47,8 +41,8 @@ protected function resetService($name): void } $manager = $this->container->get($name); - if ($manager instanceof LazyGhostObjectInterface) { - if (!$manager->resetLazyGhostObject()) { + if ($manager instanceof LazyObjectInterface) { + if (!$manager->resetLazyObject()) { throw new \LogicException(sprintf('Resetting a non-lazy manager service is not supported. Declare the "%s" service as lazy.', $name)); } diff --git a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Connection.php b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Connection.php index 27eb35c004ab4..f55a0d220a874 100644 --- a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Connection.php +++ b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Connection.php @@ -42,6 +42,7 @@ public function prepare(string $sql): DriverStatement $this->debugDataHolder, $this->connectionName, $sql, + $this->stopwatch, ); } diff --git a/src/Symfony/Bridge/Doctrine/Middleware/Debug/DebugDataHolder.php b/src/Symfony/Bridge/Doctrine/Middleware/Debug/DebugDataHolder.php index 14c338e63d691..4e1052a139651 100644 --- a/src/Symfony/Bridge/Doctrine/Middleware/Debug/DebugDataHolder.php +++ b/src/Symfony/Bridge/Doctrine/Middleware/Debug/DebugDataHolder.php @@ -24,7 +24,7 @@ public function addQuery(string $connectionName, Query $query): void 'sql' => $query->getSql(), 'params' => $query->getParams(), 'types' => $query->getTypes(), - 'executionMS' => [$query, 'getDuration'], // stop() may not be called at this point + 'executionMS' => $query->getDuration(...), // stop() may not be called at this point ]; } diff --git a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php index 9813d712132f4..695a0a1535aa2 100644 --- a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php +++ b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php @@ -43,7 +43,7 @@ public function stop(): void } } - public function setParam(string|int $param, null|string|int|float|bool &$variable, int $type): void + public function setParam(string|int $param, mixed &$variable, int $type): void { // Numeric indexes start at 0 in profiler $idx = \is_int($param) ? $param - 1 : $param; diff --git a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Statement.php b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Statement.php index 0157c5db11e84..67cf2f5e3119e 100644 --- a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Statement.php +++ b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Statement.php @@ -15,6 +15,7 @@ use Doctrine\DBAL\Driver\Result as ResultInterface; use Doctrine\DBAL\Driver\Statement as StatementInterface; use Doctrine\DBAL\ParameterType; +use Symfony\Component\Stopwatch\Stopwatch; /** * @author Laurent VOULLEMIER @@ -30,6 +31,7 @@ public function __construct( private DebugDataHolder $debugDataHolder, private string $connectionName, string $sql, + private ?Stopwatch $stopwatch = null, ) { parent::__construct($statement); @@ -59,12 +61,14 @@ public function execute($params = null): ResultInterface // clone to prevent variables by reference to change $this->debugDataHolder->addQuery($this->connectionName, $query = clone $this->query); + $this->stopwatch?->start('doctrine', 'doctrine'); $query->start(); try { $result = parent::execute($params); } finally { $query->stop(); + $this->stopwatch?->stop('doctrine'); } return $result; diff --git a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php index 4f9c97186450c..75682577ed2fc 100644 --- a/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php +++ b/src/Symfony/Bridge/Doctrine/PropertyInfo/DoctrineExtractor.php @@ -38,9 +38,6 @@ public function __construct(EntityManagerInterface $entityManager) $this->entityManager = $entityManager; } - /** - * {@inheritdoc} - */ public function getProperties(string $class, array $context = []): ?array { if (null === $metadata = $this->getMetadata($class)) { @@ -60,9 +57,6 @@ public function getProperties(string $class, array $context = []): ?array return $properties; } - /** - * {@inheritdoc} - */ public function getTypes(string $class, string $property, array $context = []): ?array { if (null === $metadata = $this->getMetadata($class)) { @@ -197,17 +191,11 @@ public function getTypes(string $class, string $property, array $context = []): return null; } - /** - * {@inheritdoc} - */ public function isReadable(string $class, string $property, array $context = []): ?bool { return null; } - /** - * {@inheritdoc} - */ public function isWritable(string $class, string $property, array $context = []): ?bool { if ( diff --git a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php index 8880bea9fbdac..60f883a0e465f 100644 --- a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php @@ -50,9 +50,6 @@ public function __construct(Connection $conn) $this->conn = $conn; } - /** - * {@inheritdoc} - */ public function loadTokenBySeries(string $series): PersistentTokenInterface { // the alias for lastUsed works around case insensitivity in PostgreSQL @@ -69,9 +66,6 @@ public function loadTokenBySeries(string $series): PersistentTokenInterface throw new TokenNotFoundException('No token found.'); } - /** - * {@inheritdoc} - */ public function deleteTokenBySeries(string $series) { $sql = 'DELETE FROM rememberme_token WHERE series=:series'; @@ -84,9 +78,6 @@ public function deleteTokenBySeries(string $series) } } - /** - * {@inheritdoc} - */ public function updateToken(string $series, #[\SensitiveParameter] string $tokenValue, \DateTime $lastUsed) { $sql = 'UPDATE rememberme_token SET value=:value, lastUsed=:lastUsed WHERE series=:series'; @@ -110,9 +101,6 @@ public function updateToken(string $series, #[\SensitiveParameter] string $token } } - /** - * {@inheritdoc} - */ public function createNewToken(PersistentTokenInterface $token) { $sql = 'INSERT INTO rememberme_token (class, username, series, value, lastUsed) VALUES (:class, :username, :series, :value, :lastUsed)'; @@ -137,9 +125,6 @@ public function createNewToken(PersistentTokenInterface $token) } } - /** - * {@inheritdoc} - */ public function verifyToken(PersistentTokenInterface $token, #[\SensitiveParameter] string $tokenValue): bool { // Check if the token value matches the current persisted token @@ -174,9 +159,6 @@ public function verifyToken(PersistentTokenInterface $token, #[\SensitiveParamet return hash_equals($tmpToken->getTokenValue(), $tokenValue); } - /** - * {@inheritdoc} - */ public function updateExistingToken(PersistentTokenInterface $token, #[\SensitiveParameter] string $tokenValue, \DateTimeInterface $lastUsed): void { if (!$token instanceof PersistentToken) { diff --git a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php index f7baf6ba69b9d..c86dc55389366 100644 --- a/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php +++ b/src/Symfony/Bridge/Doctrine/Security/User/EntityUserProvider.php @@ -15,6 +15,7 @@ use Doctrine\Persistence\Mapping\ClassMetadata; use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectRepository; +use Doctrine\Persistence\Proxy; use Symfony\Component\Security\Core\Exception\UnsupportedUserException; use Symfony\Component\Security\Core\Exception\UserNotFoundException; use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; @@ -69,9 +70,6 @@ public function loadUserByIdentifier(string $identifier): UserInterface return $user; } - /** - * {@inheritdoc} - */ public function refreshUser(UserInterface $user): UserInterface { $class = $this->getClass(); @@ -100,20 +98,19 @@ public function refreshUser(UserInterface $user): UserInterface } } + if ($refreshedUser instanceof Proxy && !$refreshedUser->__isInitialized()) { + $refreshedUser->__load(); + } + return $refreshedUser; } - /** - * {@inheritdoc} - */ public function supportsClass(string $class): bool { return $class === $this->getClass() || is_subclass_of($class, $this->getClass()); } /** - * {@inheritdoc} - * * @final */ public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void diff --git a/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php b/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php index cfb010a00db05..883af01280532 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ArgumentResolver/EntityValueResolverTest.php @@ -14,9 +14,9 @@ use Doctrine\DBAL\Types\ConversionException; use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\Mapping\ClassMetadata; -use Doctrine\Persistence\Mapping\ClassMetadataFactory; use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectRepository; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\ArgumentResolver\EntityValueResolver; use Symfony\Bridge\Doctrine\Attribute\MapEntity; @@ -28,252 +28,154 @@ class EntityValueResolverTest extends TestCase { - public function testSupport() + public function testResolveWithoutClass() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $metadataFactory = $this->getMockBuilder(ClassMetadataFactory::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn(['default' => 'default']); - - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); - $manager->expects($this->once()) - ->method('getMetadataFactory') - ->with() - ->willReturn($metadataFactory); - $metadataFactory->expects($this->once()) - ->method('isTransient') - ->with('stdClass') - ->willReturn(false); - - $request = new Request(); - $argument = $this->createArgument(); - - $this->assertTrue($converter->supports($request, $argument)); - } - - public function testSupportWithoutRegistry() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn([]); - - $request = new Request(); - $argument = $this->createArgument(); - - $this->assertFalse($converter->supports($request, $argument)); - } - - public function testSupportWithoutClass() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn(['default' => 'default']); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $argument = new ArgumentMetadata('arg', null, false, false, null); - $this->assertFalse($converter->supports($request, $argument)); + $this->assertSame([], $resolver->resolve($request, $argument)); } - public function testSupportWithoutAttribute() + public function testResolveWithoutAttribute() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry, null, new MapEntity(disabled: true)); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn(['default' => 'default']); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry, null, new MapEntity(disabled: true)); $request = new Request(); $argument = $this->createArgument(); - $this->assertFalse($converter->supports($request, $argument)); + $this->assertSame([], $resolver->resolve($request, $argument)); } - public function testSupportWithoutManager() + public function testResolveWithoutManager() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $registry->expects($this->once()) - ->method('getManagerNames') - ->with() - ->willReturn(['default' => 'default']); - - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn(null); + $registry = $this->createRegistry(null); + $resolver = new EntityValueResolver($registry); $request = new Request(); $argument = $this->createArgument(); - $this->assertFalse($converter->supports($request, $argument)); - } - - public function testApplyWithNoIdAndData() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $this->expectException(\LogicException::class); - - $request = new Request(); - $argument = $this->createArgument(null, new MapEntity()); - - $converter->resolve($request, $argument); + $this->assertSame([], $resolver->resolve($request, $argument)); } - public function testApplyWithNoIdAndDataOptional() + public function testResolveWithNoIdAndDataOptional() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $argument = $this->createArgument(null, new MapEntity(), 'arg', true); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([], $resolver->resolve($request, $argument)); } - public function testApplyWithStripNulls() + public function testResolveWithStripNulls() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('arg', null); - $argument = $this->createArgument('stdClass', new MapEntity(mapping: ['arg' => 'arg'], stripNull: true), 'arg', true); + $argument = $this->createArgument('stdClass', new MapEntity(stripNull: true), 'arg', true); + + $metadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); + $metadata->expects($this->once()) + ->method('hasField') + ->with('arg') + ->willReturn(true); - $classMetadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); $manager->expects($this->once()) ->method('getClassMetadata') ->with('stdClass') - ->willReturn($classMetadata); + ->willReturn($metadata); $manager->expects($this->never()) ->method('getRepository'); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); - - $classMetadata->expects($this->once()) - ->method('hasField') - ->with($this->equalTo('arg')) - ->willReturn(true); - - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([], $resolver->resolve($request, $argument)); } /** * @dataProvider idsProvider */ - public function testApplyWithId(string|int $id) + public function testResolveWithId(string|int $id) { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('id', $id); $argument = $this->createArgument('stdClass', new MapEntity(id: 'id')); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); + $repository->expects($this->once()) + ->method('find') + ->with($id) + ->willReturn($object = new \stdClass()); $manager->expects($this->once()) ->method('getRepository') ->with('stdClass') - ->willReturn($objectRepository); - - $objectRepository->expects($this->once()) - ->method('find') - ->with($this->equalTo($id)) - ->willReturn($object = new \stdClass()); + ->willReturn($repository); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([$object], $ret); + $this->assertSame([$object], $resolver->resolve($request, $argument)); } - public function testApplyWithNullId() + public function testResolveWithNullId() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('id', null); $argument = $this->createArgument(isNullable: true); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([null], $resolver->resolve($request, $argument)); } - public function testApplyWithConversionFailedException() + public function testResolveWithConversionFailedException() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('id', 'test'); $argument = $this->createArgument('stdClass', new MapEntity(id: 'id')); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); + $repository->expects($this->once()) + ->method('find') + ->with('test') + ->will($this->throwException(new ConversionException())); $manager->expects($this->once()) ->method('getRepository') ->with('stdClass') - ->willReturn($objectRepository); - - $objectRepository->expects($this->once()) - ->method('find') - ->with($this->equalTo('test')) - ->will($this->throwException(new ConversionException())); + ->willReturn($repository); $this->expectException(NotFoundHttpException::class); - $converter->resolve($request, $argument); + $resolver->resolve($request, $argument); } public function testUsedProperIdentifier() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('id', 1); @@ -282,55 +184,43 @@ public function testUsedProperIdentifier() $argument = $this->createArgument('stdClass', new MapEntity(id: 'entity_id'), 'arg', true); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([null], $resolver->resolve($request, $argument)); } - public function idsProvider(): iterable + public static function idsProvider(): iterable { yield [1]; yield [0]; yield ['foo']; } - public function testApplyGuessOptional() + public function testResolveGuessOptional() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('guess', null); $argument = $this->createArgument('stdClass', new MapEntity(), 'arg', true); - $classMetadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $metadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); $manager->expects($this->once()) ->method('getClassMetadata') ->with('stdClass') - ->willReturn($classMetadata); - - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); + ->willReturn($metadata); $manager->expects($this->never())->method('getRepository'); - $objectRepository->expects($this->never())->method('find'); - $objectRepository->expects($this->never())->method('findOneBy'); - - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([null], $ret); + $this->assertSame([], $resolver->resolve($request, $argument)); } - public function testApplyWithMappingAndExclude() + public function testResolveWithMappingAndExclude() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $request->attributes->set('foo', 1); @@ -341,158 +231,36 @@ public function testApplyWithMappingAndExclude() new MapEntity(mapping: ['foo' => 'Foo'], exclude: ['bar']) ); - $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); $metadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - - $registry->expects($this->once()) - ->method('getManagerForClass') - ->with('stdClass') - ->willReturn($manager); + $metadata->expects($this->once()) + ->method('hasField') + ->with('Foo') + ->willReturn(true); $manager->expects($this->once()) ->method('getClassMetadata') - ->with('stdClass') - ->willReturn($metadata); - $manager->expects($this->once()) - ->method('getRepository') - ->with('stdClass') - ->willReturn($repository); - - $metadata->expects($this->once()) - ->method('hasField') - ->with($this->equalTo('Foo')) - ->willReturn(true); + ->with('stdClass') + ->willReturn($metadata); + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); $repository->expects($this->once()) ->method('findOneBy') - ->with($this->equalTo(['Foo' => 1])) + ->with(['Foo' => 1]) ->willReturn($object = new \stdClass()); - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([$object], $ret); - } - - public function testIgnoreMappingWhenAutoMappingDisabled() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry, null, new MapEntity(mapping: [])); - - $request = new Request(); - $request->attributes->set('foo', 1); - - $argument = $this->createArgument( - 'stdClass', - new MapEntity() - ); - - $metadata = $this->getMockBuilder(ClassMetadata::class)->getMock(); - - $registry->expects($this->never()) - ->method('getManagerForClass'); - - $metadata->expects($this->never()) - ->method('hasField'); - - $this->expectException(\LogicException::class); - - $ret = $converter->resolve($request, $argument); - - $this->assertYieldEquals([], $ret); - } - - public function testSupports() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $argument = $this->createArgument('stdClass', new MapEntity()); - $metadataFactory = $this->getMockBuilder(ClassMetadataFactory::class)->getMock(); - $metadataFactory->expects($this->once()) - ->method('isTransient') - ->with($this->equalTo('stdClass')) - ->willReturn(false); - - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectManager->expects($this->once()) - ->method('getMetadataFactory') - ->willReturn($metadataFactory); - - $registry->expects($this->any()) - ->method('getManagerNames') - ->willReturn(['default' => 'default']); - - $registry->expects($this->once()) - ->method('getManagerForClass') + $manager->expects($this->once()) + ->method('getRepository') ->with('stdClass') - ->willReturn($objectManager); - - $ret = $converter->supports(new Request(), $argument); - - $this->assertTrue($ret, 'Should be supported'); - } - - public function testSupportsWithConfiguredObjectManager() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $argument = $this->createArgument('stdClass', new MapEntity(objectManager: 'foo')); - $metadataFactory = $this->getMockBuilder(ClassMetadataFactory::class)->getMock(); - $metadataFactory->expects($this->once()) - ->method('isTransient') - ->with($this->equalTo('stdClass')) - ->willReturn(false); - - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectManager->expects($this->once()) - ->method('getMetadataFactory') - ->willReturn($metadataFactory); + ->willReturn($repository); - $registry->expects($this->exactly(2)) - ->method('getManagerNames') - ->willReturn(['foo' => 'foo']); - - $registry->expects($this->once()) - ->method('getManager') - ->with('foo') - ->willReturn($objectManager); - - $ret = $converter->supports(new Request(), $argument); - - $this->assertTrue($ret, 'Should be supported'); - } - - public function testSupportsWithDifferentConfiguration() - { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $argument = $this->createArgument('DateTime'); - - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectManager->expects($this->never()) - ->method('getMetadataFactory'); - - $registry->expects($this->any()) - ->method('getManagerNames') - ->willReturn(['default' => 'default']); - - $registry->expects($this->never()) - ->method('getManager'); - - $ret = $converter->supports(new Request(), $argument); - - $this->assertFalse($ret, 'Should not be supported'); + $this->assertSame([$object], $resolver->resolve($request, $argument)); } public function testExceptionWithExpressionIfNoLanguageAvailable() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); - $converter = new EntityValueResolver($registry); - - $this->expectException(\LogicException::class); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); $request = new Request(); $argument = $this->createArgument( @@ -501,14 +269,17 @@ public function testExceptionWithExpressionIfNoLanguageAvailable() 'arg1' ); - $converter->resolve($request, $argument); + $this->expectException(\LogicException::class); + + $resolver->resolve($request, $argument); } public function testExpressionFailureReturns404() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); $language = $this->getMockBuilder(ExpressionLanguage::class)->getMock(); - $converter = new EntityValueResolver($registry, $language); + $resolver = new EntityValueResolver($registry, $language); $this->expectException(NotFoundHttpException::class); @@ -519,33 +290,28 @@ public function testExpressionFailureReturns404() 'arg1' ); - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - - $objectManager->expects($this->once()) - ->method('getRepository') - ->willReturn($objectRepository); - + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); // find should not be attempted on this repository as a fallback - $objectRepository->expects($this->never()) + $repository->expects($this->never()) ->method('find'); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($objectManager); + $manager->expects($this->once()) + ->method('getRepository') + ->willReturn($repository); $language->expects($this->once()) ->method('evaluate') ->willReturn(null); - $converter->resolve($request, $argument); + $resolver->resolve($request, $argument); } public function testExpressionMapsToArgument() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); $language = $this->getMockBuilder(ExpressionLanguage::class)->getMock(); - $converter = new EntityValueResolver($registry, $language); + $resolver = new EntityValueResolver($registry, $language); $request = new Request(); $request->attributes->set('id', 5); @@ -555,41 +321,32 @@ public function testExpressionMapsToArgument() 'arg1' ); - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - - $objectManager->expects($this->once()) - ->method('getRepository') - ->willReturn($objectRepository); - + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); // find should not be attempted on this repository as a fallback - $objectRepository->expects($this->never()) + $repository->expects($this->never()) ->method('find'); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($objectManager); + $manager->expects($this->once()) + ->method('getRepository') + ->willReturn($repository); $language->expects($this->once()) ->method('evaluate') ->with('repository.findOneByCustomMethod(id)', [ - 'repository' => $objectRepository, + 'repository' => $repository, 'id' => 5, ]) ->willReturn($object = new \stdClass()); - $ret = $converter->resolve($request, $argument); - $this->assertYieldEquals([$object], $ret); + $this->assertSame([$object], $resolver->resolve($request, $argument)); } public function testExpressionSyntaxErrorThrowsException() { - $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); $language = $this->getMockBuilder(ExpressionLanguage::class)->getMock(); - $converter = new EntityValueResolver($registry, $language); - - $this->expectException(\LogicException::class); - $this->expectExceptionMessage('syntax error message around position 10'); + $resolver = new EntityValueResolver($registry, $language); $request = new Request(); $argument = $this->createArgument( @@ -598,28 +355,36 @@ public function testExpressionSyntaxErrorThrowsException() 'arg1' ); - $objectManager = $this->getMockBuilder(ObjectManager::class)->getMock(); - $objectRepository = $this->getMockBuilder(ObjectRepository::class)->getMock(); - - $objectManager->expects($this->once()) - ->method('getRepository') - ->willReturn($objectRepository); - + $repository = $this->getMockBuilder(ObjectRepository::class)->getMock(); // find should not be attempted on this repository as a fallback - $objectRepository->expects($this->never()) + $repository->expects($this->never()) ->method('find'); - $registry->expects($this->once()) - ->method('getManagerForClass') - ->willReturn($objectManager); + $manager->expects($this->once()) + ->method('getRepository') + ->willReturn($repository); $language->expects($this->once()) ->method('evaluate') ->will($this->throwException(new SyntaxError('syntax error message', 10))); - $ret = $converter->resolve($request, $argument); + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('syntax error message around position 10'); + $resolver->resolve($request, $argument); + } - $this->assertYieldEquals([null], $ret); + public function testAlreadyResolved() + { + $manager = $this->getMockBuilder(ObjectManager::class)->getMock(); + $registry = $this->createRegistry($manager); + $resolver = new EntityValueResolver($registry); + + $request = new Request(); + $request->attributes->set('arg', new \stdClass()); + + $argument = $this->createArgument('stdClass', name: 'arg'); + + $this->assertSame([], $resolver->resolve($request, $argument)); } private function createArgument(string $class = null, MapEntity $entity = null, string $name = 'arg', bool $isNullable = false): ArgumentMetadata @@ -627,13 +392,18 @@ private function createArgument(string $class = null, MapEntity $entity = null, return new ArgumentMetadata($name, $class ?? \stdClass::class, false, false, null, $isNullable, $entity ? [$entity] : []); } - private function assertYieldEquals(array $expected, iterable $generator) + private function createRegistry(ObjectManager $manager = null): ManagerRegistry&MockObject { - $args = []; - foreach ($generator as $arg) { - $args[] = $arg; - } + $registry = $this->getMockBuilder(ManagerRegistry::class)->getMock(); + + $registry->expects($this->any()) + ->method('getManagerForClass') + ->willReturn($manager); + + $registry->expects($this->any()) + ->method('getManager') + ->willReturn($manager); - $this->assertEquals($expected, $args); + return $registry; } } diff --git a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php index 1631fa8ae37e7..0ad18f0a66edd 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php @@ -14,10 +14,13 @@ use Doctrine\Common\EventSubscriber; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\ContainerAwareEventManager; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; use Symfony\Component\DependencyInjection\Container; class ContainerAwareEventManagerTest extends TestCase { + use ExpectDeprecationTrait; + private $container; private $evm; @@ -171,15 +174,29 @@ public function testGetListenersForEvent() $this->assertSame([$subscriber1, $listener1, $listener2], array_values($this->evm->getListeners('foo'))); } + /** + * @group legacy + */ public function testGetListeners() { $this->container->set('lazy', $listener1 = new MyListener()); $this->evm->addEventListener('foo', 'lazy'); $this->evm->addEventListener('foo', $listener2 = new MyListener()); + $this->expectDeprecation('Since symfony/doctrine-bridge 6.2: Calling "Symfony\Bridge\Doctrine\ContainerAwareEventManager::getListeners()" without an event name is deprecated. Call "getAllListeners()" instead.'); + $this->assertSame([$listener1, $listener2], array_values($this->evm->getListeners()['foo'])); } + public function testGetAllListeners() + { + $this->container->set('lazy', $listener1 = new MyListener()); + $this->evm->addEventListener('foo', 'lazy'); + $this->evm->addEventListener('foo', $listener2 = new MyListener()); + + $this->assertSame([$listener1, $listener2], array_values($this->evm->getAllListeners()['foo'])); + } + public function testRemoveEventListener() { $this->container->set('lazy', $listener1 = new MyListener()); diff --git a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php index 25cc33fb4ae9f..8146adb9f8e87 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorTest.php @@ -134,7 +134,7 @@ public function testSerialization($param, array $types, $expected) $this->assertTrue($collectedQueries['default'][0]['runnable']); } - public function paramProvider(): array + public static function paramProvider(): array { return [ ['some value', [], 'some value'], diff --git a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorWithDebugStackTest.php b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorWithDebugStackTest.php index f0962eff3132d..64bee1203b781 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorWithDebugStackTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DataCollector/DoctrineDataCollectorWithDebugStackTest.php @@ -107,7 +107,7 @@ public function testSerialization($param, array $types, $expected, $explainable, $this->assertSame($runnable, $collectedQueries['default'][0]['runnable']); } - public function paramProvider(): array + public static function paramProvider(): array { return [ ['some value', [], 'some value', true], diff --git a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php index c993b89610184..28e268ac7835f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DependencyInjection/DoctrineExtensionTest.php @@ -34,7 +34,7 @@ protected function setUp(): void $this->extension = $this ->getMockBuilder(AbstractDoctrineExtension::class) - ->setMethods([ + ->onlyMethods([ 'getMappingResourceConfigDirectory', 'getObjectManagerElementName', 'getMappingObjectDefaultName', @@ -83,7 +83,7 @@ public function testFixManagersAutoMappingsWithTwoAutomappings() $method->invoke($this->extension, $emConfigs, $bundles); } - public function getAutomappingData() + public static function getAutomappingData() { return [ [ @@ -187,14 +187,14 @@ public function testMappingTypeDetection() // The ordinary fixtures contain annotation $mappingType = $method->invoke($this->extension, __DIR__.'/../Fixtures', $container); - $this->assertSame($mappingType, 'annotation'); + $this->assertSame($mappingType, 'attribute'); // In the attribute folder, attributes are used $mappingType = $method->invoke($this->extension, __DIR__.'/../Fixtures/Attribute', $container); $this->assertSame($mappingType, 'attribute'); } - public function providerBasicDrivers() + public static function providerBasicDrivers() { return [ ['doctrine.orm.cache.apc.class', ['type' => 'apc']], @@ -273,17 +273,20 @@ public function testUnrecognizedCacheDriverException() $this->invokeLoadCacheDriver($objectManager, $container, $cacheName); } - public function providerBundles() + public static function providerBundles() { - yield ['AnnotationsBundle', 'annotation', '/Entity']; + yield ['AnnotationsBundle', 'attribute', '/Entity']; + yield ['AnnotationsOneLineBundle', 'attribute', '/Entity']; + yield ['FullEmbeddableAnnotationsBundle', 'attribute', '/Entity']; yield ['AttributesBundle', 'attribute', '/Entity']; + yield ['FullEmbeddableAttributesBundle', 'attribute', '/Entity']; yield ['XmlBundle', 'xml', '/Resources/config/doctrine']; yield ['PhpBundle', 'php', '/Resources/config/doctrine']; yield ['YamlBundle', 'yml', '/Resources/config/doctrine']; yield ['SrcXmlBundle', 'xml', '/Resources/config/doctrine']; - yield ['NewAnnotationsBundle', 'annotation', \DIRECTORY_SEPARATOR.'src/Entity']; + yield ['NewAnnotationsBundle', 'attribute', \DIRECTORY_SEPARATOR.'src/Entity']; yield ['NewXmlBundle', 'xml', '/config/doctrine']; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/DoctrineTestHelper.php b/src/Symfony/Bridge/Doctrine/Tests/DoctrineTestHelper.php index be18506ba96ae..2cbf416a67b26 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/DoctrineTestHelper.php +++ b/src/Symfony/Bridge/Doctrine/Tests/DoctrineTestHelper.php @@ -12,10 +12,15 @@ namespace Symfony\Bridge\Doctrine\Tests; use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Mapping\Driver\XmlDriver; +use Doctrine\ORM\ORMSetup; use Doctrine\Persistence\Mapping\Driver\MappingDriverChain; use Doctrine\Persistence\Mapping\Driver\SymfonyFileLocator; use PHPUnit\Framework\TestCase; @@ -41,17 +46,32 @@ public static function createTestEntityManager(Configuration $config = null): En 'memory' => true, ]; - return EntityManager::create($params, $config ?? self::createTestConfiguration()); + $config ??= self::createTestConfiguration(); + + if (!(new \ReflectionMethod(EntityManager::class, '__construct'))->isPublic()) { + return EntityManager::create($params, $config); + } + + $eventManager = new EventManager(); + + return new EntityManager(DriverManager::getConnection($params, $config, $eventManager), $config, $eventManager); } public static function createTestConfiguration(): Configuration { - $config = new Configuration(); + $config = class_exists(ORMSetup::class) ? ORMSetup::createConfiguration(true) : new Configuration(); $config->setEntityNamespaces(['SymfonyTestsDoctrine' => 'Symfony\Bridge\Doctrine\Tests\Fixtures']); $config->setAutoGenerateProxyClasses(true); $config->setProxyDir(sys_get_temp_dir()); $config->setProxyNamespace('SymfonyTests\Doctrine'); - $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader())); + if (class_exists(AttributeDriver::class)) { + $config->setMetadataDriverImpl(new AttributeDriver([__DIR__.'/../Tests/Fixtures' => 'Symfony\Bridge\Doctrine\Tests\Fixtures'], true)); + } else { + $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader(), null, true)); + } + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } return $config; } @@ -65,7 +85,9 @@ public static function createTestConfigurationWithXmlLoader(): Configuration new XmlDriver( new SymfonyFileLocator( [__DIR__.'/../Tests/Resources/orm' => 'Symfony\\Bridge\\Doctrine\\Tests\\Fixtures'], '.orm.xml' - ) + ), + '.orm.xml', + true ), 'Symfony\\Bridge\\Doctrine\\Tests\\Fixtures' ); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php index c6d689a96a68c..94be71ec9f153 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity.php @@ -16,6 +16,7 @@ /** * @ORM\Entity */ +#[ORM\Entity] class AssociationEntity { /** @@ -23,6 +24,7 @@ class AssociationEntity * @ORM\Id @ORM\GeneratedValue * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\GeneratedValue, ORM\Column(type: 'integer')] private $id; /** @@ -30,6 +32,7 @@ class AssociationEntity * * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity */ + #[ORM\ManyToOne(targetEntity: SingleIntIdEntity::class)] public $single; /** @@ -41,5 +44,8 @@ class AssociationEntity * * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity */ + #[ORM\ManyToOne(targetEntity: CompositeIntIdEntity::class)] + #[ORM\JoinColumn(name: 'composite_id1', referencedColumnName: 'id1')] + #[ORM\JoinColumn(name: 'composite_id2', referencedColumnName: 'id2')] public $composite; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity2.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity2.php index a0a76124583a0..df4894e76ecbc 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity2.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/AssociationEntity2.php @@ -16,6 +16,7 @@ /** * @ORM\Entity */ +#[ORM\Entity] class AssociationEntity2 { /** @@ -23,6 +24,7 @@ class AssociationEntity2 * @ORM\Id @ORM\GeneratedValue * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\GeneratedValue, ORM\Column(type: 'integer')] private $id; /** @@ -30,6 +32,7 @@ class AssociationEntity2 * * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity */ + #[ORM\ManyToOne(targetEntity: SingleIntIdNoToStringEntity::class)] public $single; /** @@ -41,5 +44,8 @@ class AssociationEntity2 * * @var \Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity */ + #[ORM\ManyToOne(targetEntity: CompositeIntIdEntity::class)] + #[ORM\JoinColumn(name: 'composite_id1', referencedColumnName: 'id1')] + #[ORM\JoinColumn(name: 'composite_id2', referencedColumnName: 'id2')] public $composite; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Attribute/UuidIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Attribute/UuidIdEntity.php index 3d28d4469c1fb..6d16ee75e54ae 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Attribute/UuidIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Attribute/UuidIdEntity.php @@ -19,7 +19,7 @@ class UuidIdEntity { #[Id] - #[Column("uuid")] + #[Column('uuid')] protected $id; public function __construct($id) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php index f03157637b256..c8be89cc760e0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/BaseUser.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Bridge\Doctrine\Tests\Fixtures; /** diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php index 0d7cc91362da3..4f33525506493 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsBundle/Entity/Person.php @@ -18,12 +18,15 @@ /** * @Entity */ +#[Entity] class Person { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/AnnotationsOneLineBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/AnnotationsOneLineBundle.php new file mode 100644 index 0000000000000..6d401bae4f987 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/AnnotationsOneLineBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AnnotationsOneLineBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AnnotationsOneLineBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php new file mode 100644 index 0000000000000..0e77dbffc6dff --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AnnotationsOneLineBundle/Entity/Person.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AnnotationsOneLineBundle\Entity; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; + +/** @Entity */ +#[Entity] +class Person +{ + /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] + protected $id; + + /** @Column(type="string") */ + #[Column(type: 'string')] + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AnnotatedEntity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AnnotatedEntity/Person.php new file mode 100644 index 0000000000000..ae6fec848d1f2 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/AttributesBundle/AnnotatedEntity/Person.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\AttributesBundle\AnnotatedEntity; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; + +/** + * @Entity + */ +#[Entity] +class Person +{ + /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] + protected $id; + + /** @Column(type="string") */ + #[Column(type: 'string')] + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString(): string + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php new file mode 100644 index 0000000000000..bad7402e51c95 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/Entity/Address.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\FullEmbeddableAnnotationsBundle\Entity; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Embeddable; + +/** + * @Embeddable + */ +#[Embeddable] +class Address +{ + + /** @Column(type="string") */ + #[Column(type: 'string')] + public $street; + + /** @Column(type="string") */ + #[Column(type: 'string')] + public $zipCode; + + /** @Column(type="string") */ + #[Column(type: 'string')] + public $city; + + public function __construct($street, $zipCode, $city) + { + $this->street = $street; + $this->zipCode = $zipCode; + $this->city = $city; + } + + public function __toString(): string + { + return sprintf('%s %s %s', $this->street, $this->zipCode, $this->city); + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/FullEmbeddableAnnotationsBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/FullEmbeddableAnnotationsBundle.php new file mode 100644 index 0000000000000..ac03c9b0ce870 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAnnotationsBundle/FullEmbeddableAnnotationsBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\FullEmbeddableAnnotationsBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class FullEmbeddableAnnotationsBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php new file mode 100644 index 0000000000000..e257442ae18e6 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/Entity/Address.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\FullEmbeddableAttributesBundle\Entity; + +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Embeddable; + +#[Embeddable] +class Address +{ + + #[Column(type: 'string')] + public $street; + + #[Column(type: 'string')] + public $zipCode; + + #[Column(type: 'string')] + public $city; + + public function __construct($street, $zipCode, $city) + { + $this->street = $street; + $this->zipCode = $zipCode; + $this->city = $city; + } + + public function __toString(): string + { + return sprintf('%s %s %s', $this->street, $this->zipCode, $this->city); + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/FullEmbeddableAttributesBundle.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/FullEmbeddableAttributesBundle.php new file mode 100644 index 0000000000000..374f63dbf5422 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/FullEmbeddableAttributesBundle/FullEmbeddableAttributesBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Fixtures\Bundles\FullEmbeddableAttributesBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class FullEmbeddableAttributesBundle extends Bundle +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php index e94a24e1a95c7..9ab508e30523f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Bundles/NewAnnotationsBundle/src/Entity/Person.php @@ -18,12 +18,15 @@ /** * @Entity */ +#[Entity] class Person { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php index 7c64cc20ad7e1..40ff64d9488b3 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeIntIdEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class CompositeIntIdEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id1; /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id2; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id1, $id2, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php index 82811b89ed8c0..67f3c4f96c07a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeObjectNoToStringIdEntity.php @@ -1,5 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + namespace Symfony\Bridge\Doctrine\Tests\Fixtures; use Doctrine\ORM\Mapping as ORM; @@ -9,6 +18,7 @@ * * @ORM\Entity */ +#[ORM\Entity] class CompositeObjectNoToStringIdEntity { /** @@ -18,6 +28,9 @@ class CompositeObjectNoToStringIdEntity * @ORM\ManyToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"persist"}) * @ORM\JoinColumn(name="object_one_id") */ + #[ORM\Id] + #[ORM\ManyToOne(targetEntity: SingleIntIdNoToStringEntity::class, cascade: ['persist'])] + #[ORM\JoinColumn(name: 'object_one_id')] protected $objectOne; /** @@ -27,6 +40,9 @@ class CompositeObjectNoToStringIdEntity * @ORM\ManyToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"persist"}) * @ORM\JoinColumn(name="object_two_id") */ + #[ORM\Id] + #[ORM\ManyToOne(targetEntity: SingleIntIdNoToStringEntity::class, cascade: ['persist'])] + #[ORM\JoinColumn(name: 'object_two_id')] protected $objectTwo; public function __construct(SingleIntIdNoToStringEntity $objectOne, SingleIntIdNoToStringEntity $objectTwo) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php index d6e8d2cd2aafa..95e687f6bfa50 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/CompositeStringIdEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class CompositeStringIdEntity { /** @Id @Column(type="string") */ + #[Id, Column(type: 'string')] protected $id1; /** @Id @Column(type="string") */ + #[Id, Column(type: 'string')] protected $id2; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id1, $id2, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/ContainerAwareFixture.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/ContainerAwareFixture.php index 6655033ab4999..fbad8e05c56f6 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/ContainerAwareFixture.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/ContainerAwareFixture.php @@ -20,7 +20,7 @@ class ContainerAwareFixture implements FixtureInterface, ContainerAwareInterface { public $container; - public function setContainer(ContainerInterface $container = null) + public function setContainer(?ContainerInterface $container) { $this->container = $container; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEmbed.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEmbed.php index fc16f1cc135bc..5912ae6b1f9ea 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEmbed.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEmbed.php @@ -16,15 +16,18 @@ /** * @ORM\Embeddable */ +#[ORM\Embeddable] class DoctrineLoaderEmbed { /** * @ORM\Column(length=25) */ + #[ORM\Column(length: 25)] public $embeddedMaxLength; /** * @ORM\Embedded(class=DoctrineLoaderNestedEmbed::class) */ + #[ORM\Embedded(class: DoctrineLoaderNestedEmbed::class)] public $nestedEmbedded; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php index d6aee2d18b0b1..c32a9ef49d472 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEntity.php @@ -21,58 +21,70 @@ * * @author Kévin Dunglas */ +#[ORM\Entity, UniqueEntity(fields: ["alreadyMappedUnique"])] class DoctrineLoaderEntity extends DoctrineLoaderParentEntity { /** * @ORM\Id * @ORM\Column */ + #[ORM\Id, ORM\Column] public $id; /** * @ORM\Column(length=20) */ + #[ORM\Column(length: 20)] public $maxLength; /** * @ORM\Column(length=20) * @Assert\Length(min=5) */ + #[ORM\Column(length: 20), Assert\Length(min: 5)] public $mergedMaxLength; /** * @ORM\Column(length=20) * @Assert\Length(min=1, max=10) */ + #[ORM\Column(length: 20), Assert\Length(min: 1, max: 10)] public $alreadyMappedMaxLength; /** * @ORM\Column(unique=true) */ + #[ORM\Column(unique: true)] public $unique; /** * @ORM\Column(unique=true) */ + #[ORM\Column(unique: true)] public $alreadyMappedUnique; /** * @ORM\Embedded(class=DoctrineLoaderEmbed::class) */ + #[ORM\Embedded(class: DoctrineLoaderEmbed::class)] public $embedded; /** @ORM\Column(type="text", nullable=true, length=1000) */ + #[ORM\Column(type: 'text', nullable: true, length: 1000)] public $textField; /** @ORM\Id @ORM\Column(type="guid", length=50) */ + #[ORM\Id, ORM\Column(type: 'guid', length: 50)] protected $guidField; /** @ORM\Column(type="simple_array", length=100) */ + #[ORM\Column(type: 'simple_array', length: 100)] public $simpleArrayField = []; /** * @ORM\Column(length=10) * @Assert\DisableAutoMapping */ + #[ORM\Column(length: 10), Assert\DisableAutoMapping] public $noAutoMapping; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php index 8ac883e89c4a2..4ba7211456902 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderEnum.php @@ -12,25 +12,31 @@ namespace Symfony\Bridge\Doctrine\Tests\Fixtures; use Doctrine\ORM\Mapping as ORM; +use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt; +use Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString; /** * @ORM\Entity */ +#[ORM\Entity] class DoctrineLoaderEnum { /** * @ORM\Id * @ORM\Column */ + #[ORM\Id, ORM\Column] public $id; /** * @ORM\Column(type="string", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString", length=1) */ + #[ORM\Column(type: 'string', enumType: EnumString::class, length: 1)] public $enumString; /** * @ORM\Column(type="integer", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") */ + #[ORM\Column(type: 'integer', enumType: EnumInt::class)] public $enumInt; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNestedEmbed.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNestedEmbed.php index fbf41555a1316..9d9424f0ece0d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNestedEmbed.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNestedEmbed.php @@ -16,10 +16,12 @@ /** * @ORM\Embeddable() */ +#[ORM\Embeddable] class DoctrineLoaderNestedEmbed { /** * @ORM\Column(length=27) */ + #[ORM\Column(length: 27)] public $nestedEmbeddedMaxLength; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNoAutoMappingEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNoAutoMappingEntity.php index 0914411431201..515ec30bc897f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNoAutoMappingEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderNoAutoMappingEntity.php @@ -20,22 +20,26 @@ * * @author Kévin Dunglas */ +#[ORM\Entity, Assert\DisableAutoMapping] class DoctrineLoaderNoAutoMappingEntity { /** * @ORM\Id * @ORM\Column */ + #[ORM\Id, ORM\Column] public $id; /** * @ORM\Column(length=20, unique=true) */ + #[ORM\Column(length: 20, unique: true)] public $maxLength; /** * @Assert\EnableAutoMapping * @ORM\Column(length=20) */ + #[Assert\EnableAutoMapping, ORM\Column(length: 20)] public $autoMappingExplicitlyEnabled; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderParentEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderParentEntity.php index 7ec0263559c71..d7d832e6af23a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderParentEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoctrineLoaderParentEntity.php @@ -16,16 +16,19 @@ /** * @ORM\MappedSuperclass */ +#[ORM\MappedSuperclass] class DoctrineLoaderParentEntity { /** * @ORM\Column(length=35) */ + #[ORM\Column(length: 35)] public $publicParentMaxLength; /** * @ORM\Column(length=30) */ + #[ORM\Column(length: 30)] private $privateParentMaxLength; public function getPrivateParentMaxLength() diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php index 3559568787bcd..2c8ac68ce17c2 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNameEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class DoubleNameEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name2; public function __construct($id, $name, $name2) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php index 20ef14fd1b578..0c37651412ab4 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/DoubleNullableNameEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class DoubleNullableNameEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name2; public function __construct($id, $name, $name2) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php index f8000dbfd9814..cdc82cf94e34a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Embeddable/Identifier.php @@ -16,6 +16,7 @@ /** * @ORM\Embeddable */ +#[ORM\Embeddable] class Identifier { /** @@ -24,5 +25,6 @@ class Identifier * @ORM\Id * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\Column(type: 'integer')] protected $value; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php index 6d7b2670962c7..c2cec427ad77e 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/EmbeddedIdentifierEntity.php @@ -12,10 +12,12 @@ namespace Symfony\Bridge\Doctrine\Tests\Fixtures; use Doctrine\ORM\Mapping as ORM; +use Symfony\Bridge\Doctrine\Tests\Fixtures\Embeddable\Identifier; /** * @ORM\Entity */ +#[ORM\Entity] class EmbeddedIdentifierEntity { /** @@ -23,5 +25,6 @@ class EmbeddedIdentifierEntity * * @ORM\Embedded(class="Symfony\Bridge\Doctrine\Tests\Fixtures\Embeddable\Identifier") */ + #[ORM\Embedded(class: Identifier::class)] protected $id; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php index 24f08b00d781a..b9888adc8beae 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php @@ -14,6 +14,7 @@ use Doctrine\ORM\Mapping\Entity; /** @Entity */ +#[Entity] class Employee extends Person { } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php index 730a9b2b1dbf8..d803ca2310d8b 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GroupableEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class GroupableEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $groupName; public function __construct($id, $name, $groupName) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GuidIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GuidIdEntity.php index 0d447ffc1e62c..d575659595010 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GuidIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/GuidIdEntity.php @@ -16,9 +16,11 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class GuidIdEntity { /** @Id @Column(type="guid") */ + #[Id, Column(type: 'guid')] protected $id; public function __construct($id) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php index b90a54ac02c61..35f3836e0d901 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php @@ -24,12 +24,15 @@ * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) */ +#[Entity, InheritanceType('SINGLE_TABLE'), DiscriminatorColumn(name: 'discr', type: 'string'), DiscriminatorMap(['person' => 'Person', 'employee' => 'Employee'])] class Person { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php index bed8bb9a51d01..b4a1ede9f0a2a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleAssociationToIntIdEntity.php @@ -17,12 +17,15 @@ use Doctrine\ORM\Mapping\OneToOne; /** @Entity */ +#[Entity] class SingleAssociationToIntIdEntity { /** @Id @OneToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"ALL"}) */ + #[Id, OneToOne(targetEntity: SingleIntIdNoToStringEntity::class, cascade: ['ALL'])] protected $entity; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; public function __construct(SingleIntIdNoToStringEntity $entity, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php index 612566b45d94b..94b47da855a37 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php @@ -16,15 +16,19 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class SingleIntIdEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; /** @Column(type="array", nullable=true) */ + #[Column(type: 'array', nullable: true)] public $phoneNumbers = []; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php index c94815ca02cad..8887c5b5a3d6c 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdNoToStringEntity.php @@ -16,12 +16,15 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class SingleIntIdNoToStringEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php index a06f4432a761a..8c774009ba530 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdStringWrapperNameEntity.php @@ -14,15 +14,17 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper; /** @Entity */ +#[Entity] class SingleIntIdStringWrapperNameEntity { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id; /** @Column(type="string_wrapper", nullable=true) */ + #[Column(type: 'string_wrapper', nullable: true)] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php index 128801a02c922..b3e952d9a8b33 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringCastableIdEntity.php @@ -17,6 +17,7 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class SingleStringCastableIdEntity { /** @@ -24,9 +25,11 @@ class SingleStringCastableIdEntity * @Column(type="string") * @GeneratedValue(strategy="NONE") */ + #[Id, Column(type: 'string'), GeneratedValue(strategy: 'NONE')] protected $id; /** @Column(type="string", nullable=true) */ + #[Column(type: 'string', nullable: true)] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php index 83f7a9f9ab39d..ce3a5526a5b15 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleStringIdEntity.php @@ -16,12 +16,15 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class SingleStringIdEntity { /** @Id @Column(type="string") */ + #[Id, Column(type: 'string')] protected $id; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapperType.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapperType.php index 217f90374b276..89edafa67bc39 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapperType.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Type/StringWrapperType.php @@ -16,25 +16,16 @@ class StringWrapperType extends StringType { - /** - * {@inheritdoc} - */ public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed { return $value instanceof StringWrapper ? $value->getString() : null; } - /** - * {@inheritdoc} - */ public function convertToPHPValue($value, AbstractPlatform $platform): mixed { return new StringWrapper($value); } - /** - * {@inheritdoc} - */ public function getName(): string { return 'string_wrapper'; diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UlidIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UlidIdEntity.php index 3ee909fe4bfc5..238a08511ebe9 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UlidIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UlidIdEntity.php @@ -16,9 +16,11 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class UlidIdEntity { /** @Id @Column(type="ulid") */ + #[Id, Column(type: 'ulid')] protected $id; public function __construct($id) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php index b8b10094dd6bd..8d63b19b0313f 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/User.php @@ -18,15 +18,19 @@ use Symfony\Component\Security\Core\User\UserInterface; /** @Entity */ +#[Entity] class User implements UserInterface, PasswordAuthenticatedUserInterface { /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id1; /** @Id @Column(type="integer") */ + #[Id, Column(type: 'integer')] protected $id2; /** @Column(type="string") */ + #[Column(type: 'string')] public $name; public function __construct($id1, $id2, $name) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UuidIdEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UuidIdEntity.php index 46084ab292d49..60271f7d15ab0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UuidIdEntity.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/UuidIdEntity.php @@ -16,9 +16,11 @@ use Doctrine\ORM\Mapping\Id; /** @Entity */ +#[Entity] class UuidIdEntity { /** @Id @Column(type="uuid") */ + #[Id, Column(type: 'uuid')] protected $id; public function __construct($id) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php index efb3b04824b6b..1605e8d32d890 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/ChoiceList/ORMQueryBuilderLoaderTest.php @@ -11,9 +11,12 @@ namespace Symfony\Bridge\Doctrine\Tests\Form\ChoiceList; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Result; use Doctrine\DBAL\Types\GuidType; use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\AbstractQuery; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader; use Symfony\Bridge\Doctrine\Tests\DoctrineTestHelper; @@ -33,20 +36,20 @@ protected function tearDown(): void public function testIdentifierTypeIsStringArray() { - $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity', Connection::PARAM_STR_ARRAY); + $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity', class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY); } public function testIdentifierTypeIsIntegerArray() { - $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', Connection::PARAM_INT_ARRAY); + $this->checkIdentifierType('Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', class_exists(ArrayParameterType::class) ? ArrayParameterType::INTEGER : Connection::PARAM_INT_ARRAY); } protected function checkIdentifierType($classname, $expectedType) { $em = DoctrineTestHelper::createTestEntityManager(); - $query = $this->getMockBuilder(\QueryMock::class) - ->setMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) + $query = $this->getMockBuilder(QueryMock::class) + ->onlyMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) ->getMock(); $query @@ -60,7 +63,7 @@ protected function checkIdentifierType($classname, $expectedType) $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) ->setConstructorArgs([$em]) - ->setMethods(['getQuery']) + ->onlyMethods(['getQuery']) ->getMock(); $qb->expects($this->once()) @@ -78,8 +81,8 @@ public function testFilterNonIntegerValues() { $em = DoctrineTestHelper::createTestEntityManager(); - $query = $this->getMockBuilder(\QueryMock::class) - ->setMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) + $query = $this->getMockBuilder(QueryMock::class) + ->onlyMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) ->getMock(); $query @@ -88,12 +91,12 @@ public function testFilterNonIntegerValues() $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [1, 2, 3, '9223372036854775808'], Connection::PARAM_INT_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [1, 2, 3, '9223372036854775808'], class_exists(ArrayParameterType::class) ? ArrayParameterType::INTEGER : Connection::PARAM_INT_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) ->setConstructorArgs([$em]) - ->setMethods(['getQuery']) + ->onlyMethods(['getQuery']) ->getMock(); $qb->expects($this->once()) @@ -114,8 +117,8 @@ public function testFilterEmptyUuids($entityClass) { $em = DoctrineTestHelper::createTestEntityManager(); - $query = $this->getMockBuilder(\QueryMock::class) - ->setMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) + $query = $this->getMockBuilder(QueryMock::class) + ->onlyMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) ->getMock(); $query @@ -124,12 +127,12 @@ public function testFilterEmptyUuids($entityClass) $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', ['71c5fd46-3f16-4abb-bad7-90ac1e654a2d', 'b98e8e11-2897-44df-ad24-d2627eb7f499'], Connection::PARAM_STR_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', ['71c5fd46-3f16-4abb-bad7-90ac1e654a2d', 'b98e8e11-2897-44df-ad24-d2627eb7f499'], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) ->setConstructorArgs([$em]) - ->setMethods(['getQuery']) + ->onlyMethods(['getQuery']) ->getMock(); $qb->expects($this->once()) @@ -159,8 +162,8 @@ public function testFilterUid($entityClass) $em = DoctrineTestHelper::createTestEntityManager(); - $query = $this->getMockBuilder(\QueryMock::class) - ->setMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) + $query = $this->getMockBuilder(QueryMock::class) + ->onlyMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) ->getMock(); $query @@ -169,12 +172,12 @@ public function testFilterUid($entityClass) $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [Uuid::fromString('71c5fd46-3f16-4abb-bad7-90ac1e654a2d')->toBinary(), Uuid::fromString('b98e8e11-2897-44df-ad24-d2627eb7f499')->toBinary()], Connection::PARAM_STR_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id', [Uuid::fromString('71c5fd46-3f16-4abb-bad7-90ac1e654a2d')->toBinary(), Uuid::fromString('b98e8e11-2897-44df-ad24-d2627eb7f499')->toBinary()], class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) ->setConstructorArgs([$em]) - ->setMethods(['getQuery']) + ->onlyMethods(['getQuery']) ->getMock(); $qb->expects($this->once()) @@ -206,7 +209,7 @@ public function testUidThrowProperException($entityClass) $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) ->setConstructorArgs([$em]) - ->setMethods(['getQuery']) + ->onlyMethods(['getQuery']) ->getMock(); $qb->expects($this->never()) @@ -227,8 +230,8 @@ public function testEmbeddedIdentifierName() { $em = DoctrineTestHelper::createTestEntityManager(); - $query = $this->getMockBuilder(\QueryMock::class) - ->setMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) + $query = $this->getMockBuilder(QueryMock::class) + ->onlyMethods(['setParameter', 'getResult', 'getSql', '_doExecute']) ->getMock(); $query @@ -237,12 +240,12 @@ public function testEmbeddedIdentifierName() $query->expects($this->once()) ->method('setParameter') - ->with('ORMQueryBuilderLoader_getEntitiesByIds_id_value', [1, 2, 3], Connection::PARAM_INT_ARRAY) + ->with('ORMQueryBuilderLoader_getEntitiesByIds_id_value', [1, 2, 3], class_exists(ArrayParameterType::class) ? ArrayParameterType::INTEGER : Connection::PARAM_INT_ARRAY) ->willReturn($query); $qb = $this->getMockBuilder(\Doctrine\ORM\QueryBuilder::class) ->setConstructorArgs([$em]) - ->setMethods(['getQuery']) + ->onlyMethods(['getQuery']) ->getMock(); $qb->expects($this->once()) ->method('getQuery') @@ -255,7 +258,7 @@ public function testEmbeddedIdentifierName() $loader->getEntitiesByIds('id.value', [1, '', 2, 3, 'foo']); } - public function provideGuidEntityClasses() + public static function provideGuidEntityClasses() { return [ ['Symfony\Bridge\Doctrine\Tests\Fixtures\GuidIdEntity'], @@ -263,7 +266,7 @@ public function provideGuidEntityClasses() ]; } - public function provideUidEntityClasses() + public static function provideUidEntityClasses() { return [ ['Symfony\Bridge\Doctrine\Tests\Fixtures\UuidIdEntity'], @@ -271,3 +274,18 @@ public function provideUidEntityClasses() ]; } } + +class QueryMock extends AbstractQuery +{ + public function __construct() + { + } + + public function getSQL(): array|string + { + } + + protected function _doExecute(): Result|int + { + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php index 652f9d67ebe18..f211f291f873a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/DoctrineOrmTypeGuesserTest.php @@ -35,7 +35,7 @@ public function testTypeGuesser(string $type, $expected) $this->assertEquals($expected, $this->getGuesser($classMetadata)->guessType('TestEntity', 'field')); } - public function requiredType() + public static function requiredType() { yield [Types::DATE_IMMUTABLE, new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', ['input' => 'datetime_immutable'], Guess::HIGH_CONFIDENCE)]; yield [Types::DATE_MUTABLE, new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateType', [], Guess::HIGH_CONFIDENCE)]; @@ -49,66 +49,63 @@ public function requiredType() yield [Types::DATETIMETZ_MUTABLE, new TypeGuess('Symfony\Component\Form\Extension\Core\Type\DateTimeType', [], Guess::HIGH_CONFIDENCE)]; } - /** - * @dataProvider requiredProvider - */ - public function testRequiredGuesser($classMetadata, $expected) - { - $this->assertEquals($expected, $this->getGuesser($classMetadata)->guessRequired('TestEntity', 'field')); - } - - public function requiredProvider() + public function testRequiredGuesserSimpleFieldNotNullable() { - $return = []; - - // Simple field, not nullable $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata->fieldMappings['field'] = true; $classMetadata->expects($this->once())->method('isNullable')->with('field')->willReturn(false); - $return[] = [$classMetadata, new ValueGuess(true, Guess::HIGH_CONFIDENCE)]; + $this->assertEquals(new ValueGuess(true, Guess::HIGH_CONFIDENCE), $this->getGuesser($classMetadata)->guessRequired('TestEntity', 'field')); + } - // Simple field, nullable + public function testRequiredGuesserSimpleFieldNullable() + { $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata->fieldMappings['field'] = true; $classMetadata->expects($this->once())->method('isNullable')->with('field')->willReturn(true); - $return[] = [$classMetadata, new ValueGuess(false, Guess::MEDIUM_CONFIDENCE)]; + $this->assertEquals(new ValueGuess(false, Guess::MEDIUM_CONFIDENCE), $this->getGuesser($classMetadata)->guessRequired('TestEntity', 'field')); + } - // One-to-one, nullable (by default) + public function testRequiredGuesserOneToOneNullable() + { $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->willReturn(true); $mapping = ['joinColumns' => [[]]]; $classMetadata->expects($this->once())->method('getAssociationMapping')->with('field')->willReturn($mapping); - $return[] = [$classMetadata, new ValueGuess(false, Guess::HIGH_CONFIDENCE)]; + $this->assertEquals(new ValueGuess(false, Guess::HIGH_CONFIDENCE), $this->getGuesser($classMetadata)->guessRequired('TestEntity', 'field')); + } - // One-to-one, nullable (explicit) + public function testRequiredGuesserOneToOneExplicitNullable() + { $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->willReturn(true); $mapping = ['joinColumns' => [['nullable' => true]]]; $classMetadata->expects($this->once())->method('getAssociationMapping')->with('field')->willReturn($mapping); - $return[] = [$classMetadata, new ValueGuess(false, Guess::HIGH_CONFIDENCE)]; + $this->assertEquals(new ValueGuess(false, Guess::HIGH_CONFIDENCE), $this->getGuesser($classMetadata)->guessRequired('TestEntity', 'field')); + } - // One-to-one, not nullable + public function testRequiredGuesserOneToOneNotNullable() + { $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->willReturn(true); $mapping = ['joinColumns' => [['nullable' => false]]]; $classMetadata->expects($this->once())->method('getAssociationMapping')->with('field')->willReturn($mapping); - $return[] = [$classMetadata, new ValueGuess(true, Guess::HIGH_CONFIDENCE)]; + $this->assertEquals(new ValueGuess(true, Guess::HIGH_CONFIDENCE), $this->getGuesser($classMetadata)->guessRequired('TestEntity', 'field')); + } - // One-to-many, no clue + public function testRequiredGuesserOneToMany() + { $classMetadata = $this->createMock(ClassMetadata::class); $classMetadata->expects($this->once())->method('isAssociationWithSingleJoinColumn')->with('field')->willReturn(false); - $return[] = [$classMetadata, null]; - - return $return; + $this->assertNull($this->getGuesser($classMetadata)->guessRequired('TestEntity', 'field')); } private function getGuesser(ClassMetadata $classMetadata) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php index 18f918dd3b522..3d752afdb3283 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php @@ -35,12 +35,12 @@ use Symfony\Component\Form\Exception\RuntimeException; use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Forms; -use Symfony\Component\Form\Tests\Extension\Core\Type\BaseTypeTest; +use Symfony\Component\Form\Tests\Extension\Core\Type\BaseTypeTestCase; use Symfony\Component\Form\Tests\Extension\Core\Type\FormTypeTest; use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; use Symfony\Component\OptionsResolver\Exception\MissingOptionsException; -class EntityTypeTest extends BaseTypeTest +class EntityTypeTest extends BaseTypeTestCase { public const TESTED_TYPE = 'Symfony\Bridge\Doctrine\Form\Type\EntityType'; @@ -158,7 +158,7 @@ public function testChoiceTranslationDomainIsDisabledByDefault($expanded) } } - public function choiceTranslationDomainProvider() + public static function choiceTranslationDomainProvider() { return [ [false], diff --git a/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php b/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php index 710e87a15e0b8..2e9ed80e3115a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Logger/DbalLoggerTest.php @@ -30,7 +30,7 @@ public function testLog($sql, $params, $logParams) $dbalLogger = $this ->getMockBuilder(DbalLogger::class) ->setConstructorArgs([$logger, null]) - ->setMethods(['log']) + ->onlyMethods(['log']) ->getMock() ; @@ -43,7 +43,7 @@ public function testLog($sql, $params, $logParams) $dbalLogger->startQuery($sql, $params); } - public function getLogFixtures() + public static function getLogFixtures() { return [ ['SQL', null, []], @@ -62,7 +62,7 @@ public function testLogNonUtf8() $dbalLogger = $this ->getMockBuilder(DbalLogger::class) ->setConstructorArgs([$logger, null]) - ->setMethods(['log']) + ->onlyMethods(['log']) ->getMock() ; @@ -85,7 +85,7 @@ public function testLogNonUtf8Array() $dbalLogger = $this ->getMockBuilder(DbalLogger::class) ->setConstructorArgs([$logger, null]) - ->setMethods(['log']) + ->onlyMethods(['log']) ->getMock() ; @@ -116,7 +116,7 @@ public function testLogLongString() $dbalLogger = $this ->getMockBuilder(DbalLogger::class) ->setConstructorArgs([$logger, null]) - ->setMethods(['log']) + ->onlyMethods(['log']) ->getMock() ; @@ -144,7 +144,7 @@ public function testLogUTF8LongString() $dbalLogger = $this ->getMockBuilder(DbalLogger::class) ->setConstructorArgs([$logger, null]) - ->setMethods(['log']) + ->onlyMethods(['log']) ->getMock() ; diff --git a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php index dd7dabcc87db1..e524ebceff0b8 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/ManagerRegistryTest.php @@ -44,7 +44,8 @@ public function testResetService() $registry->resetManager(); $this->assertSame($foo, $container->get('foo')); - $this->assertObjectNotHasAttribute('bar', $foo); + $this->assertInstanceOf(\stdClass::class, $foo); + $this->assertFalse(property_exists($foo, 'bar')); } /** diff --git a/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php b/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php index de9d537043788..b1096fbf60cb5 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Middleware/Debug/MiddlewareTest.php @@ -16,8 +16,11 @@ use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Result; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Statement; use Doctrine\DBAL\Types\Types; +use Doctrine\ORM\ORMSetup; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Middleware\Debug\DebugDataHolder; use Symfony\Bridge\Doctrine\Middleware\Debug\Middleware; @@ -48,14 +51,17 @@ private function init(bool $withStopwatch = true): void { $this->stopwatch = $withStopwatch ? new Stopwatch() : null; - $configuration = new Configuration(); + $config = class_exists(ORMSetup::class) ? ORMSetup::createConfiguration(true) : new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } $this->debugDataHolder = new DebugDataHolder(); - $configuration->setMiddlewares([new Middleware($this->debugDataHolder, $this->stopwatch)]); + $config->setMiddlewares([new Middleware($this->debugDataHolder, $this->stopwatch)]); $this->conn = DriverManager::getConnection([ 'driver' => 'pdo_sqlite', 'memory' => true, - ], $configuration); + ], $config); $this->conn->executeQuery(<< [ @@ -144,31 +150,31 @@ public function testWithParamBound(callable $executeMethod) { $this->init(); - $product = 'product1'; - $price = 12.5; - $stock = 5; + $sql = <<conn->prepare('INSERT INTO products(name, price, stock) VALUES (?, ?, ?)'); - $stmt->bindParam(1, $product); - $stmt->bindParam(2, $price); - $stmt->bindParam(3, $stock, ParameterType::INTEGER); + $expectedRes = $res = $this->getResourceFromString('mydata'); + + $stmt = $this->conn->prepare($sql); + $stmt->bindValue(1, 'product1'); + $stmt->bindValue(2, '12.5'); + $stmt->bindValue(3, 5, ParameterType::INTEGER); + $stmt->bindValue(4, $res, ParameterType::BINARY); $executeMethod($stmt); // Debug data should not be affected by these changes - $product = 'product2'; - $price = 13.5; - $stock = 4; - $debug = $this->debugDataHolder->getData()['default'] ?? []; $this->assertCount(2, $debug); - $this->assertSame('INSERT INTO products(name, price, stock) VALUES (?, ?, ?)', $debug[1]['sql']); - $this->assertSame(['product1', '12.5', 5], $debug[1]['params']); - $this->assertSame([ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER], $debug[1]['types']); + $this->assertSame($sql, $debug[1]['sql']); + $this->assertSame(['product1', '12.5', 5, $expectedRes], $debug[1]['params']); + $this->assertSame([ParameterType::STRING, ParameterType::STRING, ParameterType::INTEGER, ParameterType::BINARY], $debug[1]['types']); $this->assertGreaterThan(0, $debug[1]['executionMS']); } - public function provideEndTransactionMethod(): array + public static function provideEndTransactionMethod(): array { return [ 'commit' => [static fn (Connection $conn) => $conn->commit(), '"COMMIT"'], @@ -183,6 +189,7 @@ public function testTransaction(callable $endTransactionMethod, string $expected { $this->init(); + $this->conn->setNestTransactionsWithSavepoints(true); $this->conn->beginTransaction(); $this->conn->beginTransaction(); $this->conn->executeStatement('INSERT INTO products(name, price, stock) VALUES ("product1", 12.5, 5)'); @@ -193,22 +200,26 @@ public function testTransaction(callable $endTransactionMethod, string $expected $endTransactionMethod($this->conn); $debug = $this->debugDataHolder->getData()['default'] ?? []; - $this->assertCount(7, $debug); + $this->assertCount(9, $debug); $this->assertSame('"START TRANSACTION"', $debug[1]['sql']); $this->assertGreaterThan(0, $debug[1]['executionMS']); - $this->assertSame('INSERT INTO products(name, price, stock) VALUES ("product1", 12.5, 5)', $debug[2]['sql']); + $this->assertSame('SAVEPOINT DOCTRINE2_SAVEPOINT_2', $debug[2]['sql']); $this->assertGreaterThan(0, $debug[2]['executionMS']); - $this->assertSame($expectedEndTransactionDebug, $debug[3]['sql']); + $this->assertSame('INSERT INTO products(name, price, stock) VALUES ("product1", 12.5, 5)', $debug[3]['sql']); $this->assertGreaterThan(0, $debug[3]['executionMS']); - $this->assertSame('"START TRANSACTION"', $debug[4]['sql']); + $this->assertSame(('"ROLLBACK"' === $expectedEndTransactionDebug ? 'ROLLBACK TO' : 'RELEASE').' SAVEPOINT DOCTRINE2_SAVEPOINT_2', $debug[4]['sql']); $this->assertGreaterThan(0, $debug[4]['executionMS']); - $this->assertSame('INSERT INTO products(name, price, stock) VALUES ("product2", 15.5, 12)', $debug[5]['sql']); + $this->assertSame($expectedEndTransactionDebug, $debug[5]['sql']); $this->assertGreaterThan(0, $debug[5]['executionMS']); - $this->assertSame($expectedEndTransactionDebug, $debug[6]['sql']); + $this->assertSame('"START TRANSACTION"', $debug[6]['sql']); $this->assertGreaterThan(0, $debug[6]['executionMS']); + $this->assertSame('INSERT INTO products(name, price, stock) VALUES ("product2", 15.5, 12)', $debug[7]['sql']); + $this->assertGreaterThan(0, $debug[7]['executionMS']); + $this->assertSame($expectedEndTransactionDebug, $debug[8]['sql']); + $this->assertGreaterThan(0, $debug[8]['executionMS']); } - public function provideExecuteAndEndTransactionMethods(): array + public static function provideExecuteAndEndTransactionMethods(): array { return [ 'commit and exec' => [ @@ -219,6 +230,14 @@ public function provideExecuteAndEndTransactionMethods(): array static fn (Connection $conn, string $sql) => $conn->executeQuery($sql), static fn (Connection $conn) => $conn->rollBack(), ], + 'prepared statement' => [ + static function (Connection $conn, string $sql): Result { + return $conn->prepare($sql)->executeQuery(); + }, + static function (Connection $conn): bool { + return $conn->commit(); + }, + ], ]; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php index 31d885e059de7..225b4217ae242 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/DoctrineExtractorTest.php @@ -11,10 +11,17 @@ namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo; +use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Collections\Collection; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Types\Type as DBALType; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; +use Doctrine\ORM\ORMSetup; use Doctrine\ORM\Tools\Setup; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor; @@ -33,8 +40,24 @@ class DoctrineExtractorTest extends TestCase { private function createExtractor() { - $config = Setup::createAnnotationMetadataConfiguration([__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'], true); - $entityManager = EntityManager::create(['driver' => 'pdo_sqlite'], $config); + $config = class_exists(ORMSetup::class) + ? ORMSetup::createConfiguration(true) + : Setup::createAnnotationMetadataConfiguration([__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'], true); + if (\PHP_VERSION_ID >= 80000 && class_exists(AttributeDriver::class)) { + $config->setMetadataDriverImpl(new AttributeDriver([__DIR__.'/../Tests/Fixtures' => 'Symfony\Bridge\Doctrine\Tests\Fixtures'], true)); + } else { + $config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader(), null, true)); + } + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + + if (!(new \ReflectionMethod(EntityManager::class, '__construct'))->isPublic()) { + $entityManager = EntityManager::create(['driver' => 'pdo_sqlite'], $config); + } else { + $eventManager = new EventManager(); + $entityManager = new EntityManager(DriverManager::getConnection(['driver' => 'pdo_sqlite'], $config, $eventManager), $config, $eventManager); + } if (!DBALType::hasType('foo')) { DBALType::addType('foo', 'Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\DoctrineFooType'); @@ -132,7 +155,7 @@ public function testExtractEnum() $this->assertNull($this->createExtractor()->getTypes(DoctrineEnum::class, 'enumCustom', [])); } - public function typesProvider() + public static function typesProvider() { $provider = [ ['id', [new Type(Type::BUILTIN_TYPE_INT)]], diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php index 8190540e27566..902850c5828ba 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineDummy.php @@ -23,102 +23,122 @@ * * @author Kévin Dunglas */ +#[Entity] class DoctrineDummy { /** * @Id * @Column(type="smallint") */ + #[Id, Column(type: 'smallint')] public $id; /** * @ManyToOne(targetEntity="DoctrineRelation") */ + #[ManyToOne(targetEntity: DoctrineRelation::class)] public $foo; /** * @ManyToMany(targetEntity="DoctrineRelation") */ + #[ManyToMany(targetEntity: DoctrineRelation::class)] public $bar; /** * @ManyToMany(targetEntity="DoctrineRelation", indexBy="rguid") */ + #[ManyToMany(targetEntity: DoctrineRelation::class, indexBy: 'rguid')] protected $indexedRguid; /** * @ManyToMany(targetEntity="DoctrineRelation", indexBy="rguid_column") */ + #[ManyToMany(targetEntity: DoctrineRelation::class, indexBy: 'rguid_column')] protected $indexedBar; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="foo", indexBy="foo") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'foo', indexBy: 'foo')] protected $indexedFoo; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="baz", indexBy="baz_id") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'baz', indexBy: 'baz_id')] protected $indexedBaz; /** * @Column(type="guid") */ + #[Column(type: 'guid')] protected $guid; /** * @Column(type="time") */ + #[Column(type: 'time')] private $time; /** * @Column(type="time_immutable") */ + #[Column(type: 'time_immutable')] private $timeImmutable; /** * @Column(type="dateinterval") */ + #[Column(type: 'dateinterval')] private $dateInterval; /** * @Column(type="json_array") */ + #[Column(type: 'json_array')] private $jsonArray; /** * @Column(type="simple_array") */ + #[Column(type: 'simple_array')] private $simpleArray; /** * @Column(type="float") */ + #[Column(type: 'float')] private $float; /** * @Column(type="decimal", precision=10, scale=2) */ + #[Column(type: 'decimal', precision: 10, scale: 2)] private $decimal; /** * @Column(type="boolean") */ + #[Column(type: 'boolean')] private $bool; /** * @Column(type="binary") */ + #[Column(type: 'binary')] private $binary; /** * @Column(type="custom_foo") */ + #[Column(type: 'custom_foo')] private $customFoo; /** * @Column(type="bigint") */ + #[Column(type: 'bigint')] private $bigint; public $notMapped; @@ -126,25 +146,30 @@ class DoctrineDummy /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="dt", indexBy="dt") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'dt', indexBy: 'dt')] protected $indexedByDt; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="customType", indexBy="customType") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'customType', indexBy: 'customType')] private $indexedByCustomType; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="buzField", indexBy="buzField") */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'buzField', indexBy: 'buzField')] protected $indexedBuz; /** * @Column(type="json", nullable=true) */ + #[Column(type: 'json', nullable: true)] private $json; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="dummyRelation", indexBy="gen_value_col_id", orphanRemoval=true) */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'dummyRelation', indexBy: 'gen_value_col_id', orphanRemoval: true)] protected $dummyGeneratedValueList; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEmbeddable.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEmbeddable.php index a00856ed7331e..23609fb1827d0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEmbeddable.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEmbeddable.php @@ -19,10 +19,12 @@ * * @author Udaltsov Valentin */ +#[Embeddable] class DoctrineEmbeddable { /** * @Column(type="string") */ + #[Column(type: 'string')] protected $field; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php index fd5271fc47730..edd6b01e9f5c6 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineEnum.php @@ -11,43 +11,50 @@ namespace Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures; -use Doctrine\ORM\Mapping\Id; use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; /** * @Entity */ +#[Entity] class DoctrineEnum { /** * @Id * @Column(type="smallint") */ + #[Id, Column(type: 'smallint')] public $id; /** * @Column(type="string", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString") */ + #[Column(type: 'string', enumType: EnumString::class)] protected $enumString; /** * @Column(type="integer", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") */ + #[Column(type: 'integer', enumType: EnumInt::class)] protected $enumInt; /** * @Column(type="array", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumString") */ + #[Column(type: 'array', enumType: EnumString::class)] protected $enumStringArray; /** * @Column(type="simple_array", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") */ + #[Column(type: 'simple_array', enumType: EnumInt::class)] protected $enumIntArray; /** * @Column(type="custom_foo", enumType="Symfony\Bridge\Doctrine\Tests\PropertyInfo\Fixtures\EnumInt") */ + #[Column(type: 'custom_foo', enumType: EnumInt::class)] protected $enumCustom; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php index 312e6c51b69bf..bebe51dbc711d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineFooType.php @@ -22,25 +22,16 @@ class DoctrineFooType extends Type { private const NAME = 'foo'; - /** - * {@inheritdoc} - */ public function getName(): string { return self::NAME; } - /** - * {@inheritdoc} - */ public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string { return $platform->getClobTypeDeclarationSQL([]); } - /** - * {@inheritdoc} - */ public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed { if (null === $value) { @@ -53,9 +44,6 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform): mixe return $foo->bar; } - /** - * {@inheritdoc} - */ public function convertToPHPValue($value, AbstractPlatform $platform): mixed { if (null === $value) { @@ -71,9 +59,6 @@ public function convertToPHPValue($value, AbstractPlatform $platform): mixed return $foo; } - /** - * {@inheritdoc} - */ public function requiresSQLCommentHint(AbstractPlatform $platform): bool { return true; diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineGeneratedValue.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineGeneratedValue.php index 9e7612fa35ae4..90fec268506e8 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineGeneratedValue.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineGeneratedValue.php @@ -22,6 +22,7 @@ * * @Entity */ +#[Entity] class DoctrineGeneratedValue { /** @@ -29,21 +30,25 @@ class DoctrineGeneratedValue * @GeneratedValue(strategy="AUTO") * @Column(type="integer") */ + #[Id, GeneratedValue(strategy: 'AUTO'), Column(type: 'integer')] public $id; /** * @Column */ + #[Column] public $foo; /** * @var int * @Column(type="integer", name="gen_value_col_id") */ + #[Column(type: 'integer', name: 'gen_value_col_id')] public $valueId; /** * @OneToMany(targetEntity="DoctrineRelation", mappedBy="generatedValueRelation", indexBy="rguid_column", orphanRemoval=true) */ + #[OneToMany(targetEntity: DoctrineRelation::class, mappedBy: 'generatedValueRelation', indexBy: 'rguid_column', orphanRemoval: true)] protected $relationList; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php index 61a658096add0..3310f9e0d06a0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineRelation.php @@ -14,62 +14,75 @@ use Doctrine\ORM\Mapping\Column; use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping\Id; -use Doctrine\ORM\Mapping\ManyToOne; use Doctrine\ORM\Mapping\JoinColumn; +use Doctrine\ORM\Mapping\ManyToOne; /** * @Entity * * @author Kévin Dunglas */ +#[Entity] class DoctrineRelation { /** * @Id * @Column(type="smallint") */ + #[Id, Column(type: 'smallint')] public $id; /** * @Column(type="guid", name="rguid_column") */ + #[Column(type: 'guid', name: 'rguid_column')] protected $rguid; /** * @Column(type="guid") * @ManyToOne(targetEntity="DoctrineDummy", inversedBy="indexedFoo") */ + #[Column(type: 'guid')] + #[ManyToOne(targetEntity: DoctrineDummy::class, inversedBy: 'indexedFoo')] protected $foo; /** * @ManyToOne(targetEntity="DoctrineDummy") */ + #[ManyToOne(targetEntity: DoctrineDummy::class)] protected $baz; /** * @Column(type="datetime") */ + #[Column(type: 'datetime')] private $dt; /** * @Column(type="foo") */ + #[Column(type: 'foo')] private $customType; /** * @Column(type="guid", name="different_than_field") * @ManyToOne(targetEntity="DoctrineDummy", inversedBy="indexedBuz") */ + #[Column(type: 'guid', name: 'different_than_field')] + #[ManyToOne(targetEntity: DoctrineDummy::class, inversedBy: 'indexedBuz')] protected $buzField; /** * @ManyToOne(targetEntity="DoctrineDummy", inversedBy="dummyGeneratedValueList") */ + #[ManyToOne(targetEntity: DoctrineDummy::class, inversedBy: 'dummyGeneratedValueList')] private $dummyRelation; /** * @ManyToOne(targetEntity="DoctrineGeneratedValue", inversedBy="relationList") * @JoinColumn(name="gen_value_col_id", referencedColumnName="gen_value_col_id") */ + #[ManyToOne(targetEntity: DoctrineGeneratedValue::class, inversedBy: 'relationList')] + #[JoinColumn(name: 'gen_value_col_id', referencedColumnName: 'gen_value_col_id')] private $generatedValueRelation; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineWithEmbedded.php b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineWithEmbedded.php index aace866128b0e..053f8bec0d19b 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineWithEmbedded.php +++ b/src/Symfony/Bridge/Doctrine/Tests/PropertyInfo/Fixtures/DoctrineWithEmbedded.php @@ -21,16 +21,19 @@ * * @author Udaltsov Valentin */ +#[Entity] class DoctrineWithEmbedded { /** * @Id * @Column(type="smallint") */ + #[Id, Column(type: 'smallint')] public $id; /** * @Embedded(class="DoctrineEmbeddable") */ + #[Embedded(class: DoctrineEmbeddable::class)] protected $embedded; } diff --git a/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php index 9d8b9256f4c9d..eb387e424cd09 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php @@ -11,7 +11,10 @@ namespace Security\RememberMe; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; +use Doctrine\ORM\ORMSetup; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Security\RememberMe\DoctrineTokenProvider; use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; @@ -120,10 +123,15 @@ public function testVerifyOutdatedTokenAfterParallelRequestFailsAfter60Seconds() */ private function bootstrapProvider() { + $config = class_exists(ORMSetup::class) ? ORMSetup::createConfiguration(true) : new Configuration(); + if (class_exists(DefaultSchemaManagerFactory::class)) { + $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); + } + $connection = DriverManager::getConnection([ 'driver' => 'pdo_sqlite', - 'url' => 'sqlite:///:memory:', - ]); + 'memory' => true, + ], $config); $connection->{method_exists($connection, 'executeStatement') ? 'executeStatement' : 'executeUpdate'}(<<< 'SQL' CREATE TABLE rememberme_token ( series char(88) UNIQUE PRIMARY KEY NOT NULL, diff --git a/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php index 20d1e487a23d2..7d068d4c56307 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Security/User/EntityUserProviderTest.php @@ -16,6 +16,7 @@ use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ObjectManager; use Doctrine\Persistence\ObjectRepository; +use Doctrine\Persistence\Proxy; use PHPUnit\Framework\TestCase; use Symfony\Bridge\Doctrine\Security\User\EntityUserProvider; use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface; @@ -146,7 +147,7 @@ public function testSupportProxy() $provider = new EntityUserProvider($this->getManager($em), 'Symfony\Bridge\Doctrine\Tests\Fixtures\User', 'name'); $user2 = $em->getReference('Symfony\Bridge\Doctrine\Tests\Fixtures\User', ['id1' => 1, 'id2' => 1]); - $this->assertTrue($provider->supportsClass(\get_class($user2))); + $this->assertTrue($provider->supportsClass($user2::class)); } public function testLoadUserByUserNameShouldLoadUserWhenProperInterfaceProvided() @@ -197,6 +198,27 @@ public function testPasswordUpgrades() $provider->upgradePassword($user, 'foobar'); } + public function testRefreshedUserProxyIsLoaded() + { + $em = DoctrineTestHelper::createTestEntityManager(); + $this->createSchema($em); + + $user = new User(1, 1, 'user1'); + + $em->persist($user); + $em->flush(); + $em->clear(); + + // store a proxy in the identity map + $em->getReference(User::class, ['id1' => 1, 'id2' => 1]); + + $provider = new EntityUserProvider($this->getManager($em), User::class); + $refreshedUser = $provider->refreshUser($user); + + $this->assertInstanceOf(Proxy::class, $refreshedUser); + $this->assertTrue($refreshedUser->__isInitialized()); + } + private function getManager($em, $name = null) { $manager = $this->createMock(ManagerRegistry::class); @@ -211,7 +233,7 @@ private function getManager($em, $name = null) private function getObjectManager($repository) { $em = $this->getMockBuilder(ObjectManager::class) - ->setMethods(['getClassMetadata', 'getRepository']) + ->onlyMethods(['getClassMetadata', 'getRepository']) ->getMockForAbstractClass(); $em->expects($this->any()) ->method('getRepository') diff --git a/src/Symfony/Bridge/Doctrine/Tests/TestRepositoryFactory.php b/src/Symfony/Bridge/Doctrine/Tests/TestRepositoryFactory.php index 7b739a988a4e2..04021b7fd2ea0 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/TestRepositoryFactory.php +++ b/src/Symfony/Bridge/Doctrine/Tests/TestRepositoryFactory.php @@ -25,9 +25,6 @@ final class TestRepositoryFactory implements RepositoryFactory */ private array $repositoryList = []; - /** - * {@inheritdoc} - */ public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository { $repositoryHash = $this->getRepositoryHash($entityManager, $entityName); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php index 8fd4b8b0a04b6..c1db2bbe70124 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Types/UlidTypeTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Doctrine\Tests\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MariaDBPlatform; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; @@ -140,13 +141,15 @@ public function testGetGuidTypeDeclarationSQL(AbstractPlatform $platform, string $this->assertEquals($expectedDeclaration, $this->type->getSqlDeclaration(['length' => 36], $platform)); } - public function provideSqlDeclarations(): array + public static function provideSqlDeclarations(): \Generator { - return [ - [new PostgreSQLPlatform(), 'UUID'], - [new SqlitePlatform(), 'BLOB'], - [new MySQLPlatform(), 'BINARY(16)'], - ]; + yield [new PostgreSQLPlatform(), 'UUID']; + yield [new SqlitePlatform(), 'BLOB']; + yield [new MySQLPlatform(), 'BINARY(16)']; + + if (class_exists(MariaDBPlatform::class)) { + yield [new MariaDBPlatform(), 'BINARY(16)']; + } } public function testRequiresSQLCommentHint() diff --git a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php index 9b904b89d9d62..120887ef3653a 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Types/UuidTypeTest.php @@ -12,6 +12,7 @@ namespace Symfony\Bridge\Doctrine\Tests\Types; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Platforms\MariaDBPlatform; use Doctrine\DBAL\Platforms\MySQLPlatform; use Doctrine\DBAL\Platforms\PostgreSQLPlatform; use Doctrine\DBAL\Platforms\SqlitePlatform; @@ -152,13 +153,15 @@ public function testGetGuidTypeDeclarationSQL(AbstractPlatform $platform, string $this->assertEquals($expectedDeclaration, $this->type->getSqlDeclaration(['length' => 36], $platform)); } - public function provideSqlDeclarations(): array + public static function provideSqlDeclarations(): \Generator { - return [ - [new PostgreSQLPlatform(), 'UUID'], - [new SqlitePlatform(), 'BLOB'], - [new MySQLPlatform(), 'BINARY(16)'], - ]; + yield [new PostgreSQLPlatform(), 'UUID']; + yield [new SqlitePlatform(), 'BLOB']; + yield [new MySQLPlatform(), 'BINARY(16)']; + + if (class_exists(MariaDBPlatform::class)) { + yield [new MariaDBPlatform(), 'BINARY(16)']; + } } public function testRequiresSQLCommentHint() diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index 240e2eb976194..b1406d9aaf914 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -13,11 +13,12 @@ use Doctrine\Common\Collections\ArrayCollection; use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\EntityRepository; use Doctrine\ORM\Mapping\ClassMetadataInfo; use Doctrine\ORM\Tools\SchemaTool; use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ObjectManager; -use Doctrine\Persistence\ObjectRepository; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bridge\Doctrine\Tests\DoctrineTestHelper; use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity2; @@ -58,7 +59,7 @@ class UniqueEntityValidatorTest extends ConstraintValidatorTestCase protected $registry; /** - * @var ObjectRepository + * @var MockObject&EntityRepository */ protected $repository; @@ -95,8 +96,10 @@ protected function createRegistryMock($em = null) protected function createRepositoryMock() { - $repository = $this->getMockBuilder(ObjectRepository::class) - ->setMethods(['findByCustom', 'find', 'findAll', 'findOneBy', 'findBy', 'getClassName']) + $repository = $this->getMockBuilder(EntityRepository::class) + ->disableOriginalConstructor() + ->onlyMethods(['find', 'findAll', 'findOneBy', 'findBy', 'getClassName']) + ->addMethods(['findByCustom']) ->getMock() ; @@ -131,7 +134,7 @@ protected function createEntityManagerMock($repositoryMock) return $em; } - protected function createValidator() + protected function createValidator(): UniqueEntityValidator { return new UniqueEntityValidator($this->registry); } @@ -186,7 +189,7 @@ public function testValidateUniqueness(UniqueEntity $constraint) ->assertRaised(); } - public function provideUniquenessConstraints(): iterable + public static function provideUniquenessConstraints(): iterable { yield 'Doctrine style' => [new UniqueEntity([ 'message' => 'myMessage', @@ -219,7 +222,7 @@ public function testValidateCustomErrorPath(UniqueEntity $constraint) ->assertRaised(); } - public function provideConstraintsWithCustomErrorPath(): iterable + public static function provideConstraintsWithCustomErrorPath(): iterable { yield 'Doctrine style' => [new UniqueEntity([ 'message' => 'myMessage', @@ -278,7 +281,7 @@ public function testValidateUniquenessWithIgnoreNullDisabled(UniqueEntity $const ->assertRaised(); } - public function provideConstraintsWithIgnoreNullDisabled(): iterable + public static function provideConstraintsWithIgnoreNullDisabled(): iterable { yield 'Doctrine style' => [new UniqueEntity([ 'message' => 'myMessage', @@ -325,7 +328,7 @@ public function testNoValidationIfFirstFieldIsNullAndNullValuesAreIgnored(Unique $this->assertNoViolation(); } - public function provideConstraintsWithIgnoreNullEnabled(): iterable + public static function provideConstraintsWithIgnoreNullEnabled(): iterable { yield 'Doctrine style' => [new UniqueEntity([ 'message' => 'myMessage', @@ -424,7 +427,7 @@ function () use ($entity) { $this->assertNoViolation(); } - public function provideConstraintsWithCustomRepositoryMethod(): iterable + public static function provideConstraintsWithCustomRepositoryMethod(): iterable { yield 'Doctrine style' => [new UniqueEntity([ 'message' => 'myMessage', @@ -463,7 +466,7 @@ public function testValidateResultTypes($entity1, $result) $this->assertNoViolation(); } - public function resultTypesProvider() + public static function resultTypesProvider() { $entity = new SingleIntIdEntity(1, 'foo'); @@ -876,7 +879,7 @@ public function testValueCanBeNull() $this->assertNoViolation(); } - public function resultWithEmptyIterator(): array + public static function resultWithEmptyIterator(): array { $entity = new SingleIntIdEntity(1, 'foo'); diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php index 23c7e758505a5..e09da3634ac7c 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/DoctrineLoaderTest.php @@ -92,9 +92,6 @@ public function testLoadClassMetadata() $parentClassMetadata = $validator->getMetadataFor(new DoctrineLoaderParentEntity()); - $publicParentMaxLengthMetadata = $parentClassMetadata->getPropertyMetadata('publicParentMaxLength'); - $this->assertCount(0, $publicParentMaxLengthMetadata); - $privateParentMaxLengthMetadata = $parentClassMetadata->getPropertyMetadata('privateParentMaxLength'); $this->assertCount(1, $privateParentMaxLengthMetadata); $privateParentMaxLengthConstraints = $privateParentMaxLengthMetadata[0]->getConstraints(); @@ -198,7 +195,7 @@ public function testClassValidator(bool $expected, string $classValidatorRegexp $this->assertSame($expected, $doctrineLoader->loadClassMetadata($classMetadata)); } - public function regexpProvider() + public static function regexpProvider() { return [ [false, null], diff --git a/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php b/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php index 48f69eee850a6..6d7aac1487704 100644 --- a/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php +++ b/src/Symfony/Bridge/Doctrine/Types/AbstractUidType.php @@ -23,9 +23,6 @@ abstract class AbstractUidType extends Type */ abstract protected function getUidClass(): string; - /** - * {@inheritdoc} - */ public function getSQLDeclaration(array $column, AbstractPlatform $platform): string { if ($this->hasNativeGuidType($platform)) { @@ -39,8 +36,6 @@ public function getSQLDeclaration(array $column, AbstractPlatform $platform): st } /** - * {@inheritdoc} - * * @throws ConversionException */ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?AbstractUid @@ -61,8 +56,6 @@ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Ab } /** - * {@inheritdoc} - * * @throws ConversionException */ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string @@ -88,9 +81,6 @@ public function convertToDatabaseValue($value, AbstractPlatform $platform): ?str } } - /** - * {@inheritdoc} - */ public function requiresSQLCommentHint(AbstractPlatform $platform): bool { return true; diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php index 7d65c898ccb47..5e9048808023d 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php @@ -45,8 +45,6 @@ class UniqueEntity extends Constraint protected static $errorNames = self::ERROR_NAMES; /** - * {@inheritdoc} - * * @param array|string $fields the combination of fields that must contain unique values or a set of options */ public function __construct( @@ -92,9 +90,6 @@ public function validatedBy(): string return $this->service; } - /** - * {@inheritdoc} - */ public function getTargets(): string|array { return self::CLASS_CONSTRAINT; diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 1274607287560..71d73d88a4a44 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -75,14 +75,14 @@ public function validate(mixed $entity, Constraint $constraint) throw new ConstraintDefinitionException(sprintf('Object manager "%s" does not exist.', $constraint->em)); } } else { - $em = $this->registry->getManagerForClass(\get_class($entity)); + $em = $this->registry->getManagerForClass($entity::class); if (!$em) { throw new ConstraintDefinitionException(sprintf('Unable to find the object manager associated with an entity of class "%s".', get_debug_type($entity))); } } - $class = $em->getClassMetadata(\get_class($entity)); + $class = $em->getClassMetadata($entity::class); $criteria = []; $hasNullValue = false; @@ -136,7 +136,7 @@ public function validate(mixed $entity, Constraint $constraint) throw new ConstraintDefinitionException(sprintf('The "%s" entity repository does not support the "%s" entity. The entity should be an instance of or extend "%s".', $constraint->entityClass, $class->getName(), $supportedClass)); } } else { - $repository = $em->getRepository(\get_class($entity)); + $repository = $em->getRepository($entity::class); } $arguments = [$criteria]; @@ -203,7 +203,7 @@ private function formatWithIdentifiers(ObjectManager $em, ClassMetadata $class, return (string) $value; } - if ($class->getName() !== $idClass = \get_class($value)) { + if ($class->getName() !== $idClass = $value::class) { // non unique value might be a composite PK that consists of other entity objects if ($em->getMetadataFactory()->hasMetadataFor($idClass)) { $identifiers = $em->getClassMetadata($idClass)->getIdentifierValues($value); @@ -224,7 +224,7 @@ private function formatWithIdentifiers(ObjectManager $em, ClassMetadata $class, if (!\is_object($id) || $id instanceof \DateTimeInterface) { $idAsString = $this->formatValue($id, self::PRETTY_DATE); } else { - $idAsString = sprintf('object("%s")', \get_class($id)); + $idAsString = sprintf('object("%s")', $id::class); } $id = sprintf('%s => %s', $field, $idAsString); diff --git a/src/Symfony/Bridge/Doctrine/Validator/DoctrineInitializer.php b/src/Symfony/Bridge/Doctrine/Validator/DoctrineInitializer.php index 3e7922ec79276..c50c6e98120ab 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/DoctrineInitializer.php +++ b/src/Symfony/Bridge/Doctrine/Validator/DoctrineInitializer.php @@ -30,6 +30,6 @@ public function __construct(ManagerRegistry $registry) public function initialize(object $object) { - $this->registry->getManagerForClass(\get_class($object))?->initializeObject($object); + $this->registry->getManagerForClass($object::class)?->initializeObject($object); } } diff --git a/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php index 45758d74b8685..ce4773c5cfa4d 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php +++ b/src/Symfony/Bridge/Doctrine/Validator/DoctrineLoader.php @@ -42,9 +42,6 @@ public function __construct(EntityManagerInterface $entityManager, string $class $this->classValidatorRegexp = $classValidatorRegexp; } - /** - * {@inheritdoc} - */ public function loadClassMetadata(ClassMetadata $metadata): bool { $className = $metadata->getClassName(); @@ -108,7 +105,7 @@ public function loadClassMetadata(ClassMetadata $metadata): bool if (isset($mapping['originalClass']) && !str_contains($mapping['declaredField'], '.')) { $metadata->addPropertyConstraint($mapping['declaredField'], new Valid()); $loaded = true; - } elseif (property_exists($className, $mapping['fieldName'])) { + } elseif (property_exists($className, $mapping['fieldName']) && (!$doctrineMetadata->isMappedSuperclass || $metadata->getReflectionClass()->getProperty($mapping['fieldName'])->isPrivate())) { $metadata->addPropertyConstraint($mapping['fieldName'], new Length(['max' => $mapping['length']])); $loaded = true; } diff --git a/src/Symfony/Bridge/Doctrine/composer.json b/src/Symfony/Bridge/Doctrine/composer.json index 8de4371499e8d..2a8a509b90c17 100644 --- a/src/Symfony/Bridge/Doctrine/composer.json +++ b/src/Symfony/Bridge/Doctrine/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": ">=8.1", - "doctrine/event-manager": "~1.0", + "doctrine/event-manager": "^1.2|^2", "doctrine/persistence": "^2|^3", "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-ctype": "~1.8", @@ -29,8 +29,8 @@ "symfony/cache": "^5.4|^6.0", "symfony/config": "^5.4|^6.0", "symfony/dependency-injection": "^5.4|^6.0", - "symfony/form": "^5.4.9|^6.0.9", - "symfony/http-kernel": "^5.4|^6.0", + "symfony/form": "^5.4.21|^6.2.7", + "symfony/http-kernel": "^6.2", "symfony/messenger": "^5.4|^6.0", "symfony/doctrine-messenger": "^5.4|^6.0", "symfony/property-access": "^5.4|^6.0", @@ -39,11 +39,11 @@ "symfony/security-core": "^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/uid": "^5.4|^6.0", - "symfony/validator": "^5.4|^6.0", + "symfony/validator": "^5.4.25|~6.2.12|^6.3.1", "symfony/translation": "^5.4|^6.0", "symfony/var-dumper": "^5.4|^6.0", - "doctrine/annotations": "^1.10.4", - "doctrine/collections": "~1.0", + "doctrine/annotations": "^1.10.4|^2", + "doctrine/collections": "^1.0|^2.0", "doctrine/data-fixtures": "^1.1", "doctrine/dbal": "^2.13.1|^3.0", "doctrine/orm": "^2.7.4", @@ -53,16 +53,15 @@ "doctrine/dbal": "<2.13.1", "doctrine/lexer": "<1.1", "doctrine/orm": "<2.7.4", - "phpunit/phpunit": "<5.4.3", "symfony/cache": "<5.4", "symfony/dependency-injection": "<5.4", - "symfony/form": "<5.4", - "symfony/http-kernel": "<5.4", + "symfony/form": "<5.4.21|>=6,<6.2.7", + "symfony/http-kernel": "<6.2", "symfony/messenger": "<5.4", "symfony/property-info": "<5.4", "symfony/security-bundle": "<5.4", "symfony/security-core": "<6.0", - "symfony/validator": "<5.4" + "symfony/validator": "<5.4.25|>=6,<6.2.12|>=6.3,<6.3.1" }, "suggest": { "symfony/form": "", diff --git a/src/Symfony/Bridge/Monolog/Formatter/CompatibilityFormatter.php b/src/Symfony/Bridge/Monolog/Formatter/CompatibilityFormatter.php index 08cd70983b3ba..aa374445b08c2 100644 --- a/src/Symfony/Bridge/Monolog/Formatter/CompatibilityFormatter.php +++ b/src/Symfony/Bridge/Monolog/Formatter/CompatibilityFormatter.php @@ -26,9 +26,6 @@ trait CompatibilityFormatter { abstract private function doFormat(array|LogRecord $record): mixed; - /** - * {@inheritdoc} - */ public function format(LogRecord $record): mixed { return $this->doFormat($record); @@ -46,9 +43,6 @@ trait CompatibilityFormatter { abstract private function doFormat(array|LogRecord $record): mixed; - /** - * {@inheritdoc} - */ public function format(array $record): mixed { return $this->doFormat($record); diff --git a/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php b/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php index b8ed640e9c4aa..31f2197b75afb 100644 --- a/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php +++ b/src/Symfony/Bridge/Monolog/Formatter/ConsoleFormatter.php @@ -91,9 +91,6 @@ public function __construct(array $options = []) } } - /** - * {@inheritdoc} - */ public function formatBatch(array $records): mixed { foreach ($records as $key => $record) { diff --git a/src/Symfony/Bridge/Monolog/Formatter/VarDumperFormatter.php b/src/Symfony/Bridge/Monolog/Formatter/VarDumperFormatter.php index 92cf6c3e887b4..14b7da442b605 100644 --- a/src/Symfony/Bridge/Monolog/Formatter/VarDumperFormatter.php +++ b/src/Symfony/Bridge/Monolog/Formatter/VarDumperFormatter.php @@ -43,9 +43,6 @@ private function doFormat(array|LogRecord $record): mixed return $record; } - /** - * {@inheritdoc} - */ public function formatBatch(array $records): mixed { foreach ($records as $k => $record) { diff --git a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php index 1fa698a9390b6..c84db4d897df8 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ChromePhpHandler.php @@ -50,9 +50,6 @@ public function onKernelResponse(ResponseEvent $event) $this->headers = []; } - /** - * {@inheritdoc} - */ protected function sendHeader($header, $content): void { if (!self::$sendHeaders) { diff --git a/src/Symfony/Bridge/Monolog/Handler/CompatibilityHandler.php b/src/Symfony/Bridge/Monolog/Handler/CompatibilityHandler.php index dbeb59e4feb3b..051698f06515c 100644 --- a/src/Symfony/Bridge/Monolog/Handler/CompatibilityHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/CompatibilityHandler.php @@ -26,9 +26,6 @@ trait CompatibilityHandler { abstract private function doHandle(array|LogRecord $record): bool; - /** - * {@inheritdoc} - */ public function handle(LogRecord $record): bool { return $this->doHandle($record); @@ -46,9 +43,6 @@ trait CompatibilityHandler { abstract private function doHandle(array|LogRecord $record): bool; - /** - * {@inheritdoc} - */ public function handle(array $record): bool { return $this->doHandle($record); diff --git a/src/Symfony/Bridge/Monolog/Handler/CompatibilityProcessingHandler.php b/src/Symfony/Bridge/Monolog/Handler/CompatibilityProcessingHandler.php index c84c457859d52..e15a00286da83 100644 --- a/src/Symfony/Bridge/Monolog/Handler/CompatibilityProcessingHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/CompatibilityProcessingHandler.php @@ -26,9 +26,6 @@ trait CompatibilityProcessingHandler { abstract private function doWrite(array|LogRecord $record): void; - /** - * {@inheritdoc} - */ protected function write(LogRecord $record): void { $this->doWrite($record); @@ -46,9 +43,6 @@ trait CompatibilityProcessingHandler { abstract private function doWrite(array|LogRecord $record): void; - /** - * {@inheritdoc} - */ protected function write(array $record): void { $this->doWrite($record); diff --git a/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php b/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php index 88936ff2bfbd8..c3426bb51cb0f 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ConsoleHandler.php @@ -37,9 +37,6 @@ trait CompatibilityIsHandlingHandler { abstract private function doIsHandling(array|LogRecord $record): bool; - /** - * {@inheritdoc} - */ public function isHandling(LogRecord $record): bool { return $this->doIsHandling($record); @@ -57,9 +54,6 @@ trait CompatibilityIsHandlingHandler { abstract private function doIsHandling(array|LogRecord $record): bool; - /** - * {@inheritdoc} - */ public function isHandling(array $record): bool { return $this->doIsHandling($record); @@ -121,17 +115,11 @@ public function __construct(OutputInterface $output = null, bool $bubble = true, $this->consoleFormatterOptions = $consoleFormatterOptions; } - /** - * {@inheritdoc} - */ private function doIsHandling(array|LogRecord $record): bool { return $this->updateLevel() && parent::isHandling($record); } - /** - * {@inheritdoc} - */ private function doHandle(array|LogRecord $record): bool { // we have to update the logging level each time because the verbosity of the @@ -179,9 +167,6 @@ public function onTerminate(ConsoleTerminateEvent $event) $this->close(); } - /** - * {@inheritdoc} - */ public static function getSubscribedEvents(): array { return [ @@ -196,9 +181,6 @@ private function doWrite(array|LogRecord $record): void $this->output->write((string) $record['formatted'], false, $this->output->getVerbosity()); } - /** - * {@inheritdoc} - */ protected function getDefaultFormatter(): FormatterInterface { if (!class_exists(CliDumper::class)) { diff --git a/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php b/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php index d60b02a287558..8713f6c68b3e3 100644 --- a/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/FirePHPHandler.php @@ -37,7 +37,7 @@ public function onKernelResponse(ResponseEvent $event) } $request = $event->getRequest(); - if (!preg_match('{\bFirePHP/\d+\.\d+\b}', $request->headers->get('User-Agent')) + if (!preg_match('{\bFirePHP/\d+\.\d+\b}', $request->headers->get('User-Agent', '')) && !$request->headers->has('X-FirePHP-Version')) { self::$sendHeaders = false; $this->headers = []; @@ -52,9 +52,6 @@ public function onKernelResponse(ResponseEvent $event) $this->headers = []; } - /** - * {@inheritdoc} - */ protected function sendHeader($header, $content): void { if (!self::$sendHeaders) { diff --git a/src/Symfony/Bridge/Monolog/Handler/MailerHandler.php b/src/Symfony/Bridge/Monolog/Handler/MailerHandler.php index b75accae76a84..e0862f8f45df3 100644 --- a/src/Symfony/Bridge/Monolog/Handler/MailerHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/MailerHandler.php @@ -41,9 +41,6 @@ public function __construct(MailerInterface $mailer, callable|Email $messageTemp $this->messageTemplate = $messageTemplate instanceof Email ? $messageTemplate : $messageTemplate(...); } - /** - * {@inheritdoc} - */ public function handleBatch(array $records): void { $messages = []; @@ -65,14 +62,11 @@ public function handleBatch(array $records): void } } - if (!empty($messages)) { + if ($messages) { $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); } } - /** - * {@inheritdoc} - */ private function doWrite(array|LogRecord $record): void { $this->send((string) $record['formatted'], [$record]); diff --git a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php index 3b2319fb5812a..4ba90f7efbea3 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php @@ -29,9 +29,6 @@ class ServerLogHandler extends AbstractProcessingHandler use CompatibilityProcessingHandler; use ServerLogHandlerTrait; - /** - * {@inheritdoc} - */ protected function getDefaultFormatter(): FormatterInterface { return new VarDumperFormatter(); @@ -47,9 +44,6 @@ class ServerLogHandler extends AbstractProcessingHandler use CompatibilityProcessingHandler; use ServerLogHandlerTrait; - /** - * {@inheritdoc} - */ protected function getDefaultFormatter() { return new VarDumperFormatter(); @@ -127,9 +121,6 @@ private function doWrite(array|LogRecord $record): void } } - /** - * {@inheritdoc} - */ protected function getDefaultFormatter(): FormatterInterface { return new VarDumperFormatter(); diff --git a/src/Symfony/Bridge/Monolog/LICENSE b/src/Symfony/Bridge/Monolog/LICENSE index 88bf75bb4d6a2..0138f8f071351 100644 --- a/src/Symfony/Bridge/Monolog/LICENSE +++ b/src/Symfony/Bridge/Monolog/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Monolog/Logger.php b/src/Symfony/Bridge/Monolog/Logger.php index 2b5f312053048..4e2168a114866 100644 --- a/src/Symfony/Bridge/Monolog/Logger.php +++ b/src/Symfony/Bridge/Monolog/Logger.php @@ -22,9 +22,6 @@ */ class Logger extends BaseLogger implements DebugLoggerInterface, ResetInterface { - /** - * {@inheritdoc} - */ public function getLogs(Request $request = null): array { if ($logger = $this->getDebugLogger()) { @@ -34,9 +31,6 @@ public function getLogs(Request $request = null): array return []; } - /** - * {@inheritdoc} - */ public function countErrors(Request $request = null): int { if ($logger = $this->getDebugLogger()) { @@ -46,9 +40,6 @@ public function countErrors(Request $request = null): int return 0; } - /** - * {@inheritdoc} - */ public function clear() { if ($logger = $this->getDebugLogger()) { @@ -56,9 +47,6 @@ public function clear() } } - /** - * {@inheritdoc} - */ public function reset(): void { $this->clear(); diff --git a/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php b/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php index a033d73c3b187..5c135d10065b0 100644 --- a/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php @@ -33,7 +33,7 @@ public function __construct(RequestStack $requestStack = null) private function doInvoke(array|LogRecord $record): array|LogRecord { - $hash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : ''; + $key = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_id($request) : ''; $timestamp = $timestampRfc3339 = false; if ($record['datetime'] instanceof \DateTimeInterface) { @@ -43,7 +43,7 @@ private function doInvoke(array|LogRecord $record): array|LogRecord $timestampRfc3339 = (new \DateTimeImmutable($record['datetime']))->format(\DateTimeInterface::RFC3339_EXTENDED); } - $this->records[$hash][] = [ + $this->records[$key][] = [ 'timestamp' => $timestamp, 'timestamp_rfc3339' => $timestampRfc3339, 'message' => $record['message'], @@ -53,8 +53,8 @@ private function doInvoke(array|LogRecord $record): array|LogRecord 'channel' => $record['channel'] ?? '', ]; - if (!isset($this->errorCount[$hash])) { - $this->errorCount[$hash] = 0; + if (!isset($this->errorCount[$key])) { + $this->errorCount[$key] = 0; } switch ($record['level']) { @@ -62,19 +62,16 @@ private function doInvoke(array|LogRecord $record): array|LogRecord case Logger::CRITICAL: case Logger::ALERT: case Logger::EMERGENCY: - ++$this->errorCount[$hash]; + ++$this->errorCount[$key]; } return $record; } - /** - * {@inheritdoc} - */ public function getLogs(Request $request = null): array { if (null !== $request) { - return $this->records[spl_object_hash($request)] ?? []; + return $this->records[spl_object_id($request)] ?? []; } if (0 === \count($this->records)) { @@ -84,30 +81,21 @@ public function getLogs(Request $request = null): array return array_merge(...array_values($this->records)); } - /** - * {@inheritdoc} - */ public function countErrors(Request $request = null): int { if (null !== $request) { - return $this->errorCount[spl_object_hash($request)] ?? 0; + return $this->errorCount[spl_object_id($request)] ?? 0; } return array_sum($this->errorCount); } - /** - * {@inheritdoc} - */ public function clear() { $this->records = []; $this->errorCount = []; } - /** - * {@inheritdoc} - */ public function reset() { $this->clear(); diff --git a/src/Symfony/Bridge/Monolog/Processor/SwitchUserTokenProcessor.php b/src/Symfony/Bridge/Monolog/Processor/SwitchUserTokenProcessor.php index bb3f6ff73d0cd..22d86f0b3edb5 100644 --- a/src/Symfony/Bridge/Monolog/Processor/SwitchUserTokenProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/SwitchUserTokenProcessor.php @@ -23,17 +23,11 @@ */ class SwitchUserTokenProcessor extends AbstractTokenProcessor { - /** - * {@inheritdoc} - */ protected function getKey(): string { return 'impersonator_token'; } - /** - * {@inheritdoc} - */ protected function getToken(): ?TokenInterface { $token = $this->tokenStorage->getToken(); diff --git a/src/Symfony/Bridge/Monolog/Processor/TokenProcessor.php b/src/Symfony/Bridge/Monolog/Processor/TokenProcessor.php index c824ea1761efd..0e0085718e439 100644 --- a/src/Symfony/Bridge/Monolog/Processor/TokenProcessor.php +++ b/src/Symfony/Bridge/Monolog/Processor/TokenProcessor.php @@ -23,17 +23,11 @@ */ class TokenProcessor extends AbstractTokenProcessor { - /** - * {@inheritdoc} - */ protected function getKey(): string { return 'token'; } - /** - * {@inheritdoc} - */ protected function getToken(): ?TokenInterface { return $this->tokenStorage->getToken(); diff --git a/src/Symfony/Bridge/Monolog/Tests/Formatter/ConsoleFormatterTest.php b/src/Symfony/Bridge/Monolog/Tests/Formatter/ConsoleFormatterTest.php index 8e847c522642e..bf754f435e734 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Formatter/ConsoleFormatterTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Formatter/ConsoleFormatterTest.php @@ -28,7 +28,7 @@ public function testFormat(array|LogRecord $record, $expectedMessage) self::assertSame($expectedMessage, $formatter->format($record)); } - public function providerFormatTests(): array + public static function providerFormatTests(): array { $currentDateTime = new \DateTimeImmutable(); diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php index f7f09c389f8a4..b2f8a79f965a7 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/ConsoleHandlerTest.php @@ -65,7 +65,7 @@ public function testVerbosityMapping($verbosity, $level, $isHandling, array $map $levelName = Logger::getLevelName($level); $levelName = sprintf('%-9s', $levelName); - $realOutput = $this->getMockBuilder(Output::class)->setMethods(['doWrite'])->getMock(); + $realOutput = $this->getMockBuilder(Output::class)->onlyMethods(['doWrite'])->getMock(); $realOutput->setVerbosity($verbosity); if ($realOutput->isDebug()) { $log = "16:21:54 $levelName [app] My info message\n"; @@ -82,7 +82,7 @@ public function testVerbosityMapping($verbosity, $level, $isHandling, array $map $this->assertFalse($handler->handle($infoRecord), 'The handler finished handling the log.'); } - public function provideVerbosityMappingTests() + public static function provideVerbosityMappingTests() { return [ [OutputInterface::VERBOSITY_QUIET, Logger::ERROR, true], diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php index 81613fe21e0e0..37286d39e080c 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php @@ -56,23 +56,23 @@ public function testIsActivated($url, $record, $expected) self::assertEquals($expected, $strategy->isHandlerActivated($record)); } - public function isActivatedProvider(): array + public static function isActivatedProvider(): array { return [ ['/test', RecordFactory::create(Logger::ERROR), true], - ['/400', RecordFactory::create(Logger::ERROR, context: $this->getContextException(400)), true], - ['/400/a', RecordFactory::create(Logger::ERROR, context: $this->getContextException(400)), false], - ['/400/b', RecordFactory::create(Logger::ERROR, context: $this->getContextException(400)), false], - ['/400/c', RecordFactory::create(Logger::ERROR, context: $this->getContextException(400)), true], - ['/401', RecordFactory::create(Logger::ERROR, context: $this->getContextException(401)), true], - ['/403', RecordFactory::create(Logger::ERROR, context: $this->getContextException(403)), false], - ['/404', RecordFactory::create(Logger::ERROR, context: $this->getContextException(404)), false], - ['/405', RecordFactory::create(Logger::ERROR, context: $this->getContextException(405)), false], - ['/500', RecordFactory::create(Logger::ERROR, context: $this->getContextException(500)), true], + ['/400', RecordFactory::create(Logger::ERROR, context: self::getContextException(400)), true], + ['/400/a', RecordFactory::create(Logger::ERROR, context: self::getContextException(400)), false], + ['/400/b', RecordFactory::create(Logger::ERROR, context: self::getContextException(400)), false], + ['/400/c', RecordFactory::create(Logger::ERROR, context: self::getContextException(400)), true], + ['/401', RecordFactory::create(Logger::ERROR, context: self::getContextException(401)), true], + ['/403', RecordFactory::create(Logger::ERROR, context: self::getContextException(403)), false], + ['/404', RecordFactory::create(Logger::ERROR, context: self::getContextException(404)), false], + ['/405', RecordFactory::create(Logger::ERROR, context: self::getContextException(405)), false], + ['/500', RecordFactory::create(Logger::ERROR, context: self::getContextException(500)), true], ]; } - private function getContextException(int $code): array + private static function getContextException(int $code): array { return ['exception' => new HttpException($code)]; } diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php index f58e9afa7164e..36c448c7df0ab 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php @@ -36,22 +36,22 @@ public function testIsActivated(string $url, array|LogRecord $record, bool $expe self::assertEquals($expected, $strategy->isHandlerActivated($record)); } - public function isActivatedProvider(): array + public static function isActivatedProvider(): array { return [ ['/test', RecordFactory::create(Logger::DEBUG), false], - ['/foo', RecordFactory::create(Logger::DEBUG, context: $this->getContextException(404)), false], - ['/baz/bar', RecordFactory::create(Logger::ERROR, context: $this->getContextException(404)), false], - ['/foo', RecordFactory::create(Logger::ERROR, context: $this->getContextException(404)), false], - ['/foo', RecordFactory::create(Logger::ERROR, context: $this->getContextException(500)), true], + ['/foo', RecordFactory::create(Logger::DEBUG, context: self::getContextException(404)), false], + ['/baz/bar', RecordFactory::create(Logger::ERROR, context: self::getContextException(404)), false], + ['/foo', RecordFactory::create(Logger::ERROR, context: self::getContextException(404)), false], + ['/foo', RecordFactory::create(Logger::ERROR, context: self::getContextException(500)), true], ['/test', RecordFactory::create(Logger::ERROR), true], - ['/baz', RecordFactory::create(Logger::ERROR, context: $this->getContextException(404)), true], - ['/baz', RecordFactory::create(Logger::ERROR, context: $this->getContextException(500)), true], + ['/baz', RecordFactory::create(Logger::ERROR, context: self::getContextException(404)), true], + ['/baz', RecordFactory::create(Logger::ERROR, context: self::getContextException(500)), true], ]; } - protected function getContextException(int $code): array + protected static function getContextException(int $code): array { return ['exception' => new HttpException($code)]; } diff --git a/src/Symfony/Bridge/Monolog/Tests/Handler/FirePHPHandlerTest.php b/src/Symfony/Bridge/Monolog/Tests/Handler/FirePHPHandlerTest.php index 70d1915b64f93..3a47eb57ab123 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Handler/FirePHPHandlerTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Handler/FirePHPHandlerTest.php @@ -112,6 +112,22 @@ private function createHandler(): FirePHPHandler return $handler; } + public function testOnKernelResponseShouldNotTriggerDeprecation() + { + $handler = $this->createHandler(); + + $request = Request::create('/'); + $request->headers->remove('User-Agent'); + + $error = null; + set_error_handler(function ($type, $message) use (&$error) { $error = $message; }, \E_DEPRECATED); + + $this->dispatchResponseEvent($handler, $request); + restore_error_handler(); + + $this->assertNull($error); + } + private function dispatchResponseEvent(FirePHPHandler $handler, Request $request): Response { $dispatcher = new EventDispatcher(); diff --git a/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php b/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php index 8de9a956e7282..6e4b67e265d1d 100644 --- a/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php +++ b/src/Symfony/Bridge/Monolog/Tests/Processor/DebugProcessorTest.php @@ -46,8 +46,8 @@ public function testDatetimeRfc3339Format() public function testDebugProcessor() { $processor = new DebugProcessor(); - $processor($this->getRecord()); - $processor($this->getRecord(Logger::ERROR)); + $processor(self::getRecord()); + $processor(self::getRecord(Logger::ERROR)); $this->assertCount(2, $processor->getLogs()); $this->assertSame(1, $processor->countErrors()); @@ -65,8 +65,8 @@ public function testWithRequestStack() { $stack = new RequestStack(); $processor = new DebugProcessor($stack); - $processor($this->getRecord()); - $processor($this->getRecord(Logger::ERROR)); + $processor(self::getRecord()); + $processor(self::getRecord(Logger::ERROR)); $this->assertCount(2, $processor->getLogs()); $this->assertSame(1, $processor->countErrors()); @@ -74,8 +74,8 @@ public function testWithRequestStack() $request = new Request(); $stack->push($request); - $processor($this->getRecord()); - $processor($this->getRecord(Logger::ERROR)); + $processor(self::getRecord()); + $processor(self::getRecord(Logger::ERROR)); $this->assertCount(4, $processor->getLogs()); $this->assertSame(2, $processor->countErrors()); @@ -99,7 +99,7 @@ public function testInheritedClassCallCountErrorsWithoutArgument() $this->assertEquals(0, $debugProcessorChild->countErrors()); } - private function getRecord($level = Logger::WARNING, $message = 'test'): array|LogRecord + private static function getRecord($level = Logger::WARNING, $message = 'test'): array|LogRecord { return RecordFactory::create($level, $message); } diff --git a/src/Symfony/Bridge/PhpUnit/ClockMock.php b/src/Symfony/Bridge/PhpUnit/ClockMock.php index 4f826f1c84b44..7ec22ccd85cb3 100644 --- a/src/Symfony/Bridge/PhpUnit/ClockMock.php +++ b/src/Symfony/Bridge/PhpUnit/ClockMock.php @@ -97,7 +97,7 @@ public static function hrtime($asNumber = false) if ($asNumber) { $number = sprintf('%d%d', (int) self::$now, $ns); - return PHP_INT_SIZE === 8 ? (int) $number : (float) $number; + return \PHP_INT_SIZE === 8 ? (int) $number : (float) $number; } return [(int) self::$now, (int) $ns]; diff --git a/src/Symfony/Bridge/PhpUnit/ConstraintTrait.php b/src/Symfony/Bridge/PhpUnit/ConstraintTrait.php index 77f32e1b3753c..ceb60418ce81d 100644 --- a/src/Symfony/Bridge/PhpUnit/ConstraintTrait.php +++ b/src/Symfony/Bridge/PhpUnit/ConstraintTrait.php @@ -12,9 +12,8 @@ namespace Symfony\Bridge\PhpUnit; use PHPUnit\Framework\Constraint\Constraint; -use ReflectionClass; -$r = new ReflectionClass(Constraint::class); +$r = new \ReflectionClass(Constraint::class); if ($r->getProperty('exporter')->isProtected()) { trait ConstraintTrait { diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php index f7a85c3efe2dd..49e9f5ceb5c58 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler.php @@ -12,8 +12,9 @@ namespace Symfony\Bridge\PhpUnit; use PHPUnit\Framework\TestResult; +use PHPUnit\Runner\ErrorHandler; use PHPUnit\Util\Error\Handler; -use PHPUnit\Util\ErrorHandler; +use PHPUnit\Util\ErrorHandler as UtilErrorHandler; use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Configuration; use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation; use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\DeprecationGroup; @@ -75,7 +76,12 @@ public static function register($mode = 0) if (null !== $oldErrorHandler) { restore_error_handler(); - if ($oldErrorHandler instanceof ErrorHandler || [ErrorHandler::class, 'handleError'] === $oldErrorHandler) { + if ( + $oldErrorHandler instanceof UtilErrorHandler + || [UtilErrorHandler::class, 'handleError'] === $oldErrorHandler + || $oldErrorHandler instanceof ErrorHandler + || [ErrorHandler::class, 'handleError'] === $oldErrorHandler + ) { restore_error_handler(); self::register($mode); } @@ -132,7 +138,7 @@ public function handleError($type, $msg, $file, $line, $context = []) $msg = $trace[1]['args'][0]; } - $deprecation = new Deprecation($msg, $trace, $file); + $deprecation = new Deprecation($msg, $trace, $file, \E_DEPRECATED === $type); if ($deprecation->isMuted()) { return null; } @@ -203,7 +209,7 @@ public function shutdown() // store failing status $isFailing = !$configuration->tolerates($this->deprecationGroups); - $this->displayDeprecations($groups, $configuration, $isFailing); + $this->displayDeprecations($groups, $configuration); $this->resetDeprecationGroups(); @@ -216,7 +222,7 @@ public function shutdown() } $isFailingAtShutdown = !$configuration->tolerates($this->deprecationGroups); - $this->displayDeprecations($groups, $configuration, $isFailingAtShutdown); + $this->displayDeprecations($groups, $configuration); if ($configuration->isGeneratingBaseline()) { $configuration->writeBaseline(); @@ -292,11 +298,10 @@ private static function colorize($str, $red) /** * @param string[] $groups * @param Configuration $configuration - * @param bool $isFailing * * @throws \InvalidArgumentException */ - private function displayDeprecations($groups, $configuration, $isFailing) + private function displayDeprecations($groups, $configuration) { $cmp = function ($a, $b) { return $b->count() - $a->count(); @@ -323,7 +328,8 @@ private function displayDeprecations($groups, $configuration, $isFailing) fwrite($handle, "\n".self::colorize($deprecationGroupMessage, 'legacy' !== $group && 'indirect' !== $group)."\n"); } - if ('legacy' !== $group && !$configuration->verboseOutput($group) && !$isFailing) { + // Skip the verbose output if the group is quiet and not failing according to its threshold: + if ('legacy' !== $group && !$configuration->verboseOutput($group) && $configuration->toleratesForGroup($group, $this->deprecationGroups)) { continue; } $notices = $this->deprecationGroups[$group]->notices(); @@ -359,6 +365,8 @@ private static function getPhpUnitErrorHandler() if (!$eh = self::$errorHandler) { if (class_exists(Handler::class)) { $eh = self::$errorHandler = Handler::class; + } elseif (method_exists(UtilErrorHandler::class, '__invoke')) { + $eh = self::$errorHandler = UtilErrorHandler::class; } elseif (method_exists(ErrorHandler::class, '__invoke')) { $eh = self::$errorHandler = ErrorHandler::class; } else { diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php index a432c31183669..c220c888cb56a 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Configuration.php @@ -203,6 +203,32 @@ public function isIgnoredDeprecation(Deprecation $deprecation): bool return (bool) $result; } + /** + * @param array $deprecationGroups + * + * @return bool true if the threshold is not reached for the deprecation type nor for the total + */ + public function toleratesForGroup(string $groupName, array $deprecationGroups): bool + { + $grandTotal = 0; + + foreach ($deprecationGroups as $type => $group) { + if ('legacy' !== $type) { + $grandTotal += $group->count(); + } + } + + if ($grandTotal > $this->thresholds['total']) { + return false; + } + + if (\in_array($groupName, ['self', 'direct', 'indirect'], true) && $deprecationGroups[$groupName]->count() > $this->thresholds[$groupName]) { + return false; + } + + return true; + } + /** * @return bool */ @@ -212,7 +238,9 @@ public function isBaselineDeprecation(Deprecation $deprecation) return false; } - if ($deprecation->originatesFromAnObject()) { + if ($deprecation->originatesFromDebugClassLoader()) { + $location = $deprecation->triggeringClass(); + } elseif ($deprecation->originatesFromAnObject()) { $location = $deprecation->originatingClass().'::'.$deprecation->originatingMethod(); } else { $location = 'procedural code'; diff --git a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php index 003680a5c074b..50ef4441370d6 100644 --- a/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php +++ b/src/Symfony/Bridge/PhpUnit/DeprecationErrorHandler/Deprecation.php @@ -36,9 +36,11 @@ class Deprecation private $trace = []; private $message; + private $languageDeprecation; private $originClass; private $originMethod; private $triggeringFile; + private $triggeringClass; /** @var string[] Absolute paths to vendor directories */ private static $vendors; @@ -55,9 +57,14 @@ class Deprecation /** * @param string $message * @param string $file + * @param bool $languageDeprecation */ - public function __construct($message, array $trace, $file) + public function __construct($message, array $trace, $file, $languageDeprecation = false) { + if (isset($trace[2]['class']) && \in_array($trace[2]['class'], [DebugClassLoader::class, LegacyDebugClassLoader::class], true)) { + $this->triggeringClass = $trace[2]['args'][0]; + } + if (isset($trace[2]['function']) && 'trigger_deprecation' === $trace[2]['function']) { $file = $trace[2]['file']; array_splice($trace, 1, 1); @@ -65,6 +72,7 @@ public function __construct($message, array $trace, $file) $this->trace = $trace; $this->message = $message; + $this->languageDeprecation = $languageDeprecation; $i = \count($trace); while (1 < $i && $this->lineShouldBeSkipped($trace[--$i])) { @@ -154,6 +162,26 @@ private function lineShouldBeSkipped(array $line) return 'ReflectionMethod' === $class || 0 === strpos($class, 'PHPUnit\\'); } + /** + * @return bool + */ + public function originatesFromDebugClassLoader() + { + return isset($this->triggeringClass); + } + + /** + * @return string + */ + public function triggeringClass() + { + if (null === $this->triggeringClass) { + throw new \LogicException('Check with originatesFromDebugClassLoader() before calling this method.'); + } + + return $this->triggeringClass; + } + /** * @return bool */ @@ -238,7 +266,12 @@ public function isMuted() */ public function getType() { - if (self::PATH_TYPE_SELF === $pathType = $this->getPathType($this->triggeringFile)) { + $pathType = $this->getPathType($this->triggeringFile); + if ($this->languageDeprecation && self::PATH_TYPE_VENDOR === $pathType) { + // the triggering file must be used for language deprecations + return self::TYPE_INDIRECT; + } + if (self::PATH_TYPE_SELF === $pathType) { return self::TYPE_SELF; } if (self::PATH_TYPE_UNDETERMINED === $pathType) { diff --git a/src/Symfony/Bridge/PhpUnit/LICENSE b/src/Symfony/Bridge/PhpUnit/LICENSE index a843ec124ea70..29f72d5e95920 100644 --- a/src/Symfony/Bridge/PhpUnit/LICENSE +++ b/src/Symfony/Bridge/PhpUnit/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2022 Fabien Potencier +Copyright (c) 2014-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV7.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV7.php index fcf5c4505d3da..99a1e683525dd 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV7.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV7.php @@ -17,15 +17,10 @@ use Symfony\Bridge\PhpUnit\SymfonyTestsListener; /** - * {@inheritdoc} - * * @internal */ class CommandForV7 extends BaseCommand { - /** - * {@inheritdoc} - */ protected function createRunner(): BaseRunner { $this->arguments['listeners'] ?? $this->arguments['listeners'] = []; diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV9.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV9.php index 351f02f2230ec..6dd2767361db3 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV9.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV9.php @@ -20,15 +20,10 @@ use Symfony\Bridge\PhpUnit\SymfonyTestsListener; /** - * {@inheritdoc} - * * @internal */ class CommandForV9 extends BaseCommand { - /** - * {@inheritdoc} - */ protected function createRunner(): BaseRunner { $this->arguments['listeners'] ?? $this->arguments['listeners'] = []; diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php index 29dd75e478b5b..015bc881ae573 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php @@ -133,7 +133,7 @@ public function startTestSuite($suite) if (!class_exists(AnnotationRegistry::class, false) && class_exists(AnnotationRegistry::class)) { if (method_exists(AnnotationRegistry::class, 'registerUniqueLoader')) { AnnotationRegistry::registerUniqueLoader('class_exists'); - } else { + } elseif (method_exists(AnnotationRegistry::class, 'registerLoader')) { AnnotationRegistry::registerLoader('class_exists'); } } diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php index 2f59ae5b91e2e..e7b731152daa6 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/ConfigurationTest.php @@ -15,6 +15,7 @@ use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Configuration; use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\Deprecation; use Symfony\Bridge\PhpUnit\DeprecationErrorHandler\DeprecationGroup; +use Symfony\Component\ErrorHandler\DebugClassLoader; class ConfigurationTest extends TestCase { @@ -179,7 +180,7 @@ public function testItCanTellWhetherToDisplayAStackTrace() $this->assertTrue($configuration->shouldDisplayStackTrace('interesting')); } - public function provideItCanBeDisabled(): array + public static function provideItCanBeDisabled(): array { return [ ['disabled', false], @@ -234,6 +235,103 @@ public function testOutputIsNotVerboseInWeakMode() $this->assertFalse($configuration->verboseOutput('other')); } + /** + * @dataProvider provideDataForToleratesForGroup + */ + public function testToleratesForIndividualGroups(string $deprecationsHelper, array $deprecationsPerType, array $expected) + { + $configuration = Configuration::fromUrlEncodedString($deprecationsHelper); + + $groups = $this->buildGroups($deprecationsPerType); + + foreach ($expected as $groupName => $tolerates) { + $this->assertSame($tolerates, $configuration->toleratesForGroup($groupName, $groups), sprintf('Deprecation type "%s" is %s', $groupName, $tolerates ? 'tolerated' : 'not tolerated')); + } + } + + public static function provideDataForToleratesForGroup() { + + yield 'total threshold not reached' => ['max[total]=1', [ + 'unsilenced' => 0, + 'self' => 0, + 'legacy' => 1, // Legacy group is ignored in total threshold + 'other' => 0, + 'direct' => 1, + 'indirect' => 0, + ], [ + 'unsilenced' => true, + 'self' => true, + 'legacy' => true, + 'other' => true, + 'direct' => true, + 'indirect' => true, + ]]; + + yield 'total threshold reached' => ['max[total]=1', [ + 'unsilenced' => 0, + 'self' => 0, + 'legacy' => 1, + 'other' => 0, + 'direct' => 1, + 'indirect' => 1, + ], [ + 'unsilenced' => false, + 'self' => false, + 'legacy' => false, + 'other' => false, + 'direct' => false, + 'indirect' => false, + ]]; + + yield 'direct threshold reached' => ['max[total]=99&max[direct]=0', [ + 'unsilenced' => 0, + 'self' => 0, + 'legacy' => 1, + 'other' => 0, + 'direct' => 1, + 'indirect' => 1, + ], [ + 'unsilenced' => true, + 'self' => true, + 'legacy' => true, + 'other' => true, + 'direct' => false, + 'indirect' => true, + ]]; + + yield 'indirect & self threshold reached' => ['max[total]=99&max[direct]=0&max[self]=0', [ + 'unsilenced' => 0, + 'self' => 1, + 'legacy' => 1, + 'other' => 1, + 'direct' => 1, + 'indirect' => 1, + ], [ + 'unsilenced' => true, + 'self' => false, + 'legacy' => true, + 'other' => true, + 'direct' => false, + 'indirect' => true, + ]]; + + yield 'indirect & self threshold not reached' => ['max[total]=99&max[direct]=2&max[self]=2', [ + 'unsilenced' => 0, + 'self' => 1, + 'legacy' => 1, + 'other' => 1, + 'direct' => 1, + 'indirect' => 1, + ], [ + 'unsilenced' => true, + 'self' => true, + 'legacy' => true, + 'other' => true, + 'direct' => true, + 'indirect' => true, + ]]; + } + private function buildGroups($counts) { $groups = []; @@ -259,7 +357,7 @@ public function testBaselineGenerationEmptyFile() $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 1', $trace, ''))); $configuration->writeBaseline(); $this->assertEquals($filename, $configuration->getBaselineFile()); - $expected_baseline = [ + $expected = [ [ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest', 'message' => 'Test message 1', @@ -271,7 +369,7 @@ public function testBaselineGenerationEmptyFile() 'count' => 1, ], ]; - $this->assertEquals(json_encode($expected_baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename)); + $this->assertEquals(json_encode($expected, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename)); } public function testBaselineGenerationNoFile() @@ -286,7 +384,7 @@ public function testBaselineGenerationNoFile() $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 1', $trace, ''))); $configuration->writeBaseline(); $this->assertEquals($filename, $configuration->getBaselineFile()); - $expected_baseline = [ + $expected = [ [ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest', 'message' => 'Test message 1', @@ -298,7 +396,7 @@ public function testBaselineGenerationNoFile() 'count' => 2, ], ]; - $this->assertEquals(json_encode($expected_baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename)); + $this->assertEquals(json_encode($expected, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename)); } public function testExistingBaseline() @@ -350,7 +448,7 @@ public function testExistingBaselineAndGeneration() $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Test message 3', $trace, ''))); $configuration->writeBaseline(); $this->assertEquals($filename, $configuration->getBaselineFile()); - $expected_baseline = [ + $expected = [ [ 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest', 'message' => 'Test message 2', @@ -362,7 +460,44 @@ public function testExistingBaselineAndGeneration() 'count' => 1, ], ]; - $this->assertEquals(json_encode($expected_baseline, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename)); + $this->assertEquals(json_encode($expected, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename)); + } + + public function testBaselineGenerationWithDeprecationTriggeredByDebugClassLoader() + { + $filename = $this->createFile(); + $configuration = Configuration::fromUrlEncodedString('generateBaseline=true&baselineFile='.urlencode($filename)); + + $trace = debug_backtrace(); + $this->assertTrue($configuration->isBaselineDeprecation(new Deprecation('Regular deprecation', $trace, ''))); + + $trace[2] = [ + 'class' => DebugClassLoader::class, + 'function' => 'testBaselineGenerationWithDeprecationTriggeredByDebugClassLoader', + 'args' => [self::class] + ]; + + $deprecation = new Deprecation('Deprecation by debug class loader', $trace, ''); + + $this->assertTrue($deprecation->originatesFromDebugClassLoader()); + + $this->assertTrue($configuration->isBaselineDeprecation($deprecation)); + + $configuration->writeBaseline(); + $this->assertEquals($filename, $configuration->getBaselineFile()); + $expected = [ + [ + 'location' => 'Symfony\Bridge\PhpUnit\Tests\DeprecationErrorHandler\ConfigurationTest::runTest', + 'message' => 'Regular deprecation', + 'count' => 1, + ], + [ + 'location' => self::class, + 'message' => 'Deprecation by debug class loader', + 'count' => 1, + ], + ]; + $this->assertEquals(json_encode($expected, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES), file_get_contents($filename)); } public function testBaselineArgumentException() @@ -385,10 +520,24 @@ public function testBaselineFileWriteError() { $filename = $this->createFile(); chmod($filename, 0444); - $this->expectError(); - $this->expectErrorMessageMatches('/[Ff]ailed to open stream: Permission denied/'); $configuration = Configuration::fromUrlEncodedString('generateBaseline=true&baselineFile='.urlencode($filename)); - $configuration->writeBaseline(); + + $this->expectException(\ErrorException::class); + $this->expectExceptionMessageMatches('/[Ff]ailed to open stream: Permission denied/'); + + set_error_handler(static function (int $errno, string $errstr, string $errfile = null, int $errline = null): bool { + if ($errno & (E_WARNING | E_WARNING)) { + throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); + } + + return false; + }); + + try { + $configuration->writeBaseline(); + } finally { + restore_error_handler(); + } } public function testExistingIgnoreFile() diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php index a1d3c06ea668f..5c7cf991b3f2f 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/DeprecationTest.php @@ -97,7 +97,7 @@ public function testItMutesOnlySpecificErrorMessagesWhenTheCallingCodeIsInPhpuni $this->assertSame($muted, $deprecation->isMuted()); } - public function mutedProvider() + public static function mutedProvider() { yield 'not from phpunit, and not a whitelisted message' => [ false, @@ -147,7 +147,7 @@ public function testItTakesMutesDeprecationFromPhpUnitFiles() $this->assertTrue($deprecation->isMuted()); } - public function providerGetTypeDetectsSelf() + public static function providerGetTypeDetectsSelf() { return [ 'not_from_vendors_file' => [Deprecation::TYPE_SELF, '', 'MyClass1', __FILE__], @@ -182,7 +182,7 @@ public function testGetTypeDetectsSelf(string $expectedType, string $message, st $this->assertSame($expectedType, $deprecation->getType()); } - public function providerGetTypeUsesRightTrace() + public static function providerGetTypeUsesRightTrace() { $vendorDir = self::getVendorDir(); $fakeTrace = [ diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/PhpDeprecation.php b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/PhpDeprecation.php new file mode 100644 index 0000000000000..26a3237e77941 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/fake_vendor/acme/lib/PhpDeprecation.php @@ -0,0 +1,15 @@ + +--EXPECTF-- +Unsilenced deprecation notices (3) + +Remaining direct deprecation notices (2) + + 1x: root deprecation + + 1x: silenced bar deprecation + 1x in FooTestCase::testNonLegacyBar + +Remaining indirect deprecation notices (1) + +Legacy deprecation notices (2) diff --git a/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/php_deprecation_from_vendor_class.phpt b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/php_deprecation_from_vendor_class.phpt new file mode 100644 index 0000000000000..1ead2ef4a4013 --- /dev/null +++ b/src/Symfony/Bridge/PhpUnit/Tests/DeprecationErrorHandler/php_deprecation_from_vendor_class.phpt @@ -0,0 +1,43 @@ +--TEST-- +Test that a PHP deprecation from a vendor class autoload is considered indirect. +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Remaining indirect deprecation notices (1) + + 1x: acme\lib\PhpDeprecation implements the Serializable interface, which is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) + 1x in DebugClassLoader::loadClass from Symfony\Component\ErrorHandler diff --git a/src/Symfony/Bridge/PhpUnit/Tests/ExpectDeprecationTraitTest.php b/src/Symfony/Bridge/PhpUnit/Tests/ExpectDeprecationTraitTest.php index 5e6350e673101..224e9940b2583 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/ExpectDeprecationTraitTest.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/ExpectDeprecationTraitTest.php @@ -33,6 +33,7 @@ public function testOne() * Do not remove this test in the next major version. * * @group legacy + * * @runInSeparateProcess */ public function testOneInIsolation() diff --git a/src/Symfony/Bridge/PhpUnit/Tests/FailTests/ExpectDeprecationTraitTestFail.php b/src/Symfony/Bridge/PhpUnit/Tests/FailTests/ExpectDeprecationTraitTestFail.php index ba35b268deebe..f2eb1b1bdecf5 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/FailTests/ExpectDeprecationTraitTestFail.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/FailTests/ExpectDeprecationTraitTestFail.php @@ -39,6 +39,7 @@ public function testOne() * Do not remove this test in the next major version. * * @group legacy + * * @runInSeparateProcess */ public function testOneInIsolation() diff --git a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php index 54f981856b653..bc251877d7cdf 100644 --- a/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php +++ b/src/Symfony/Bridge/PhpUnit/bin/simple-phpunit.php @@ -12,6 +12,10 @@ // Please update when phpunit needs to be reinstalled with fresh deps: // Cache-Id: 2021-02-04 11:00 UTC +if ('cli' !== \PHP_SAPI && 'phpdbg' !== \PHP_SAPI) { + throw new Exception('This script must be run from the command line.'); +} + error_reporting(-1); global $argv, $argc; @@ -364,7 +368,7 @@ class_exists(\SymfonyExcludeListSimplePhpunit::class, false) && PHPUnit\Util\Bla } } -$cmd[0] = sprintf('%s %s --colors=always', $PHP, escapeshellarg("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit")); +$cmd[0] = sprintf('%s %s --colors=%s', $PHP, escapeshellarg("$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit"), false === $getEnvVar('NO_COLOR') ? 'always' : 'never'); $cmd = str_replace('%', '%%', implode(' ', $cmd)).' %1$s'; if ('\\' === \DIRECTORY_SEPARATOR) { @@ -394,6 +398,9 @@ class_exists(\SymfonyExcludeListSimplePhpunit::class, false) && PHPUnit\Util\Bla } } + $lastOutput = null; + $lastOutputTime = null; + while ($runningProcs) { usleep(300000); $terminatedProcs = []; @@ -406,6 +413,26 @@ class_exists(\SymfonyExcludeListSimplePhpunit::class, false) && PHPUnit\Util\Bla } } + if (!$terminatedProcs && 1 === count($runningProcs)) { + $component = key($runningProcs); + + $output = file_get_contents("$component/phpunit.stdout"); + $output .= file_get_contents("$component/phpunit.stderr"); + + if ($lastOutput !== $output) { + $lastOutput = $output; + $lastOutputTime = microtime(true); + } elseif (microtime(true) - $lastOutputTime > 60) { + echo "\033[41mTimeout\033[0m $component\n\n"; + + if ('\\' === \DIRECTORY_SEPARATOR) { + exec(sprintf('taskkill /F /T /PID %d 2>&1', $procStatus['pid']), $output, $exitCode); + } else { + proc_terminate(current($runningProcs)); + } + } + } + foreach ($terminatedProcs as $component => $procStatus) { foreach (['out', 'err'] as $file) { $file = "$component/phpunit.std$file"; @@ -431,7 +458,7 @@ class SymfonyExcludeListSimplePhpunit { } } - array_splice($argv, 1, 0, ['--colors=always']); + array_splice($argv, 1, 0, ['--colors='.(false === $getEnvVar('NO_COLOR') ? 'always' : 'never')]); $_SERVER['argv'] = $argv; $_SERVER['argc'] = ++$argc; include "$PHPUNIT_DIR/$PHPUNIT_VERSION_DIR/phpunit"; diff --git a/src/Symfony/Bridge/PhpUnit/bootstrap.php b/src/Symfony/Bridge/PhpUnit/bootstrap.php index e07c8d6cf5de8..f2064368f41a3 100644 --- a/src/Symfony/Bridge/PhpUnit/bootstrap.php +++ b/src/Symfony/Bridge/PhpUnit/bootstrap.php @@ -30,7 +30,7 @@ if (!class_exists(AnnotationRegistry::class, false) && class_exists(AnnotationRegistry::class)) { if (method_exists(AnnotationRegistry::class, 'registerUniqueLoader')) { AnnotationRegistry::registerUniqueLoader('class_exists'); - } else { + } elseif (method_exists(AnnotationRegistry::class, 'registerLoader')) { AnnotationRegistry::registerLoader('class_exists'); } } diff --git a/src/Symfony/Bridge/ProxyManager/Internal/ProxyGenerator.php b/src/Symfony/Bridge/ProxyManager/Internal/ProxyGenerator.php index b5cc2160c4019..26c95448eb2bb 100644 --- a/src/Symfony/Bridge/ProxyManager/Internal/ProxyGenerator.php +++ b/src/Symfony/Bridge/ProxyManager/Internal/ProxyGenerator.php @@ -21,9 +21,6 @@ */ class ProxyGenerator implements ProxyGeneratorInterface { - /** - * {@inheritdoc} - */ public function generate(\ReflectionClass $originalClass, ClassGenerator $classGenerator, array $proxyOptions = []): void { (new LazyLoadingValueHolderGenerator())->generate($originalClass, $classGenerator, $proxyOptions); diff --git a/src/Symfony/Bridge/ProxyManager/LICENSE b/src/Symfony/Bridge/ProxyManager/LICENSE index 88bf75bb4d6a2..0138f8f071351 100644 --- a/src/Symfony/Bridge/ProxyManager/LICENSE +++ b/src/Symfony/Bridge/ProxyManager/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php index 1db05b44dee2b..59fcdc022efce 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/Instantiator/RuntimeInstantiator.php @@ -38,9 +38,6 @@ public function __construct() $this->generator = new ProxyGenerator(); } - /** - * {@inheritdoc} - */ public function instantiateProxy(ContainerInterface $container, Definition $definition, string $id, callable $realInstantiator): object { $proxifiedClass = new \ReflectionClass($this->generator->getProxifiedClass($definition)); diff --git a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php index 308a9c7854b47..82ff95dc4cc93 100644 --- a/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php +++ b/src/Symfony/Bridge/ProxyManager/LazyProxy/PhpDumper/ProxyDumper.php @@ -37,19 +37,13 @@ public function __construct(string $salt = '') $this->classGenerator = new BaseGeneratorStrategy(); } - /** - * {@inheritdoc} - */ - public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null): bool + public function isProxyCandidate(Definition $definition, bool &$asGhostObject = null, string $id = null): bool { $asGhostObject = false; return ($definition->isLazy() || $definition->hasTag('proxy')) && $this->proxyGenerator->getProxifiedClass($definition); } - /** - * {@inheritdoc} - */ public function getProxyFactoryCode(Definition $definition, string $id, string $factoryCode): string { $instantiation = 'return'; @@ -78,10 +72,7 @@ public function getProxyFactoryCode(Definition $definition, string $id, string $ EOF; } - /** - * {@inheritdoc} - */ - public function getProxyCode(Definition $definition): string + public function getProxyCode(Definition $definition, string $id = null): string { $code = $this->classGenerator->generate($this->generateProxyClass($definition)); $code = preg_replace('/^(class [^ ]++ extends )([^\\\\])/', '$1\\\\$2', $code); diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php index 85455d222194e..095a8631784a9 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/ContainerBuilderTest.php @@ -15,7 +15,7 @@ use PHPUnit\Framework\TestCase; use ProxyManager\Proxy\LazyLoadingInterface; -use ProxyManagerBridgeFooClass; +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -29,9 +29,10 @@ class ContainerBuilderTest extends TestCase public function testCreateProxyServiceWithRuntimeInstantiator() { $builder = new ContainerBuilder(); + $builder->setProxyInstantiator(new RuntimeInstantiator()); - $builder->register('foo1', ProxyManagerBridgeFooClass::class)->setFile(__DIR__.'/Fixtures/includes/foo.php')->setPublic(true); - $builder->getDefinition('foo1')->setLazy(true)->addTag('proxy', ['interface' => ProxyManagerBridgeFooClass::class]); + $builder->register('foo1', \ProxyManagerBridgeFooClass::class)->setFile(__DIR__.'/Fixtures/includes/foo.php')->setPublic(true); + $builder->getDefinition('foo1')->setLazy(true)->addTag('proxy', ['interface' => \ProxyManagerBridgeFooClass::class]); $builder->compile(); @@ -42,7 +43,7 @@ public function testCreateProxyServiceWithRuntimeInstantiator() $this->assertSame(0, $foo1::$destructorCount); $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved on multiple subsequent calls'); - $this->assertInstanceOf(ProxyManagerBridgeFooClass::class, $foo1); + $this->assertInstanceOf(\ProxyManagerBridgeFooClass::class, $foo1); $this->assertInstanceOf(LazyLoadingInterface::class, $foo1); $this->assertFalse($foo1->isProxyInitialized()); @@ -50,7 +51,7 @@ public function testCreateProxyServiceWithRuntimeInstantiator() $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved after initialization'); $this->assertTrue($foo1->isProxyInitialized()); - $this->assertInstanceOf(ProxyManagerBridgeFooClass::class, $foo1->getWrappedValueHolderValue()); + $this->assertInstanceOf(\ProxyManagerBridgeFooClass::class, $foo1->getWrappedValueHolderValue()); $this->assertNotInstanceOf(LazyLoadingInterface::class, $foo1->getWrappedValueHolderValue()); $foo1->__destruct(); diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php index bef5e5062bb95..aedfff33c56c5 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Dumper/PhpDumperTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use ProxyManager\Proxy\LazyLoadingInterface; +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Dumper\PhpDumper; @@ -66,6 +67,7 @@ private function dumpLazyServiceProjectServiceContainer() $container->compile(); $dumper = new PhpDumper($container); + $dumper->setProxyDumper(new ProxyDumper()); return $dumper->dump(['class' => 'LazyServiceProjectServiceContainer']); } diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php index e202fad702655..fc04526ced826 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/Instantiator/RuntimeInstantiatorTest.php @@ -30,9 +30,6 @@ class RuntimeInstantiatorTest extends TestCase */ protected $instantiator; - /** - * {@inheritdoc} - */ protected function setUp(): void { $this->instantiator = new RuntimeInstantiator(); diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index e787da909bbb3..b9ad7ae1b528a 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -28,9 +28,6 @@ class ProxyDumperTest extends TestCase */ protected $dumper; - /** - * {@inheritdoc} - */ protected function setUp(): void { $this->dumper = new ProxyDumper(); @@ -93,7 +90,7 @@ public function testCorrectAssigning(Definition $definition, $access) $this->assertStringMatchesFormat('%A$this->'.$access.'[\'foo\'] = %A', $code); } - public function getPrivatePublicDefinitions() + public static function getPrivatePublicDefinitions() { return [ [ @@ -164,7 +161,7 @@ protected function createProxy(\$class, \Closure \$factory) $this->assertSame(123, @$foo->dynamicProp); } - public function getProxyCandidates(): array + public static function getProxyCandidates(): array { $definitions = [ [new Definition(__CLASS__), true], diff --git a/src/Symfony/Bridge/Twig/AppVariable.php b/src/Symfony/Bridge/Twig/AppVariable.php index 43d6e3ffc9056..6cdbaa8370811 100644 --- a/src/Symfony/Bridge/Twig/AppVariable.php +++ b/src/Symfony/Bridge/Twig/AppVariable.php @@ -158,4 +158,25 @@ public function getFlashes(string|array $types = null): array return $result; } + + public function getCurrent_route(): ?string + { + if (!isset($this->requestStack)) { + throw new \RuntimeException('The "app.current_route" variable is not available.'); + } + + return $this->getRequest()?->attributes->get('_route'); + } + + /** + * @return array + */ + public function getCurrent_route_parameters(): array + { + if (!isset($this->requestStack)) { + throw new \RuntimeException('The "app.current_route_parameters" variable is not available.'); + } + + return $this->getRequest()?->attributes->get('_route_params') ?? []; + } } diff --git a/src/Symfony/Bridge/Twig/CHANGELOG.md b/src/Symfony/Bridge/Twig/CHANGELOG.md index 1a58330efa509..8edc9b80190b4 100644 --- a/src/Symfony/Bridge/Twig/CHANGELOG.md +++ b/src/Symfony/Bridge/Twig/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * Add `form_label_content` and `form_help_content` block to form themes * Add `#[Template()]` to describe how to render arrays returned by controllers * Add support for toggle buttons in Bootstrap 5 form theme + * Add `app.current_route` and `app.current_route_parameters` variables 6.1 --- diff --git a/src/Symfony/Bridge/Twig/Command/DebugCommand.php b/src/Symfony/Bridge/Twig/Command/DebugCommand.php index d5e64859a6c20..6fae02cb9bc91 100644 --- a/src/Symfony/Bridge/Twig/Command/DebugCommand.php +++ b/src/Symfony/Bridge/Twig/Command/DebugCommand.php @@ -101,16 +101,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int throw new InvalidArgumentException(sprintf('Argument "name" not supported, it requires the Twig loader "%s".', FilesystemLoader::class)); } - switch ($input->getOption('format')) { - case 'text': - $name ? $this->displayPathsText($io, $name) : $this->displayGeneralText($io, $filter); - break; - case 'json': - $name ? $this->displayPathsJson($io, $name) : $this->displayGeneralJson($io, $filter); - break; - default: - throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format'))); - } + match ($input->getOption('format')) { + 'text' => $name ? $this->displayPathsText($io, $name) : $this->displayGeneralText($io, $filter), + 'json' => $name ? $this->displayPathsJson($io, $name) : $this->displayGeneralJson($io, $filter), + default => throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $input->getOption('format'))), + }; return 0; } @@ -384,7 +379,7 @@ private function getPrettyMetadata(string $type, mixed $entity, bool $decorated) if ('globals' === $type) { if (\is_object($meta)) { - return ' = object('.\get_class($meta).')'; + return ' = object('.$meta::class.')'; } $description = substr(@json_encode($meta), 0, 50); diff --git a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php index a26a620cfddb0..a8da618bc0738 100644 --- a/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php +++ b/src/Symfony/Bridge/Twig/DataCollector/TwigDataCollector.php @@ -38,16 +38,10 @@ public function __construct(Profile $profile, Environment $twig = null) $this->twig = $twig; } - /** - * {@inheritdoc} - */ public function collect(Request $request, Response $response, \Throwable $exception = null) { } - /** - * {@inheritdoc} - */ public function reset() { $this->profile->reset(); @@ -55,9 +49,6 @@ public function reset() $this->data = []; } - /** - * {@inheritdoc} - */ public function lateCollect() { $this->data['profile'] = serialize($this->profile); @@ -187,9 +178,6 @@ private function computeData(Profile $profile) return $data; } - /** - * {@inheritdoc} - */ public function getName(): string { return 'twig'; diff --git a/src/Symfony/Bridge/Twig/ErrorRenderer/TwigErrorRenderer.php b/src/Symfony/Bridge/Twig/ErrorRenderer/TwigErrorRenderer.php index 4dde73f62c902..9086c22d5c3cb 100644 --- a/src/Symfony/Bridge/Twig/ErrorRenderer/TwigErrorRenderer.php +++ b/src/Symfony/Bridge/Twig/ErrorRenderer/TwigErrorRenderer.php @@ -39,22 +39,19 @@ public function __construct(Environment $twig, HtmlErrorRenderer $fallbackErrorR $this->debug = \is_bool($debug) ? $debug : $debug(...); } - /** - * {@inheritdoc} - */ public function render(\Throwable $exception): FlattenException { - $exception = $this->fallbackErrorRenderer->render($exception); - $debug = \is_bool($this->debug) ? $this->debug : ($this->debug)($exception); + $flattenException = FlattenException::createFromThrowable($exception); + $debug = \is_bool($this->debug) ? $this->debug : ($this->debug)($flattenException); - if ($debug || !$template = $this->findTemplate($exception->getStatusCode())) { - return $exception; + if ($debug || !$template = $this->findTemplate($flattenException->getStatusCode())) { + return $this->fallbackErrorRenderer->render($exception); } - return $exception->setAsString($this->twig->render($template, [ - 'exception' => $exception, - 'status_code' => $exception->getStatusCode(), - 'status_text' => $exception->getStatusText(), + return $flattenException->setAsString($this->twig->render($template, [ + 'exception' => $flattenException, + 'status_code' => $flattenException->getStatusCode(), + 'status_text' => $flattenException->getStatusText(), ])); } diff --git a/src/Symfony/Bridge/Twig/Extension/AssetExtension.php b/src/Symfony/Bridge/Twig/Extension/AssetExtension.php index 3e12371ba99c3..feb25ed5c2062 100644 --- a/src/Symfony/Bridge/Twig/Extension/AssetExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/AssetExtension.php @@ -29,9 +29,6 @@ public function __construct(Packages $packages) $this->packages = $packages; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php index af6ea706b280c..dd2e0682d2901 100644 --- a/src/Symfony/Bridge/Twig/Extension/CodeExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CodeExtension.php @@ -33,9 +33,6 @@ public function __construct(string|FileLinkFormatter $fileLinkFormat, string $pr $this->charset = $charset; } - /** - * {@inheritdoc} - */ public function getFilters(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/CsrfExtension.php b/src/Symfony/Bridge/Twig/Extension/CsrfExtension.php index 646a48798ba6d..951fc31d7e06b 100644 --- a/src/Symfony/Bridge/Twig/Extension/CsrfExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/CsrfExtension.php @@ -20,9 +20,6 @@ */ final class CsrfExtension extends AbstractExtension { - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/DumpExtension.php b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php index 910fece83b9bb..c84e1e751a0f8 100644 --- a/src/Symfony/Bridge/Twig/Extension/DumpExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php @@ -35,9 +35,6 @@ public function __construct(ClonerInterface $cloner, HtmlDumper $dumper = null) $this->dumper = $dumper; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ @@ -45,9 +42,6 @@ public function getFunctions(): array ]; } - /** - * {@inheritdoc} - */ public function getTokenParsers(): array { return [new DumpTokenParser()]; @@ -74,7 +68,7 @@ public function dump(Environment $env, array $context): ?string } $dump = fopen('php://memory', 'r+'); - $this->dumper = $this->dumper ?? new HtmlDumper(); + $this->dumper ??= new HtmlDumper(); $this->dumper->setCharset($env->getCharset()); foreach ($vars as $value) { diff --git a/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php b/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php index a30499620ea0f..49e4c95cbbba2 100644 --- a/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/ExpressionExtension.php @@ -22,9 +22,6 @@ */ final class ExpressionExtension extends AbstractExtension { - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/FormExtension.php b/src/Symfony/Bridge/Twig/Extension/FormExtension.php index c041a51f9c300..827145963a8e6 100644 --- a/src/Symfony/Bridge/Twig/Extension/FormExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/FormExtension.php @@ -40,9 +40,6 @@ public function __construct(TranslatorInterface $translator = null) $this->translator = $translator; } - /** - * {@inheritdoc} - */ public function getTokenParsers(): array { return [ @@ -51,9 +48,6 @@ public function getTokenParsers(): array ]; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ @@ -77,9 +71,6 @@ public function getFunctions(): array ]; } - /** - * {@inheritdoc} - */ public function getFilters(): array { return [ @@ -88,9 +79,6 @@ public function getFilters(): array ]; } - /** - * {@inheritdoc} - */ public function getTests(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php b/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php index 104bd3231f773..938d3ddabf256 100644 --- a/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php @@ -30,9 +30,6 @@ public function __construct(UrlHelper $urlHelper) $this->urlHelper = $urlHelper; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php b/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php index 1af9ddb23cf51..072d890deb476 100644 --- a/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/HttpKernelExtension.php @@ -22,9 +22,6 @@ */ final class HttpKernelExtension extends AbstractExtension { - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php index 3ac7582b8fbeb..abced287f999c 100644 --- a/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/LogoutUrlExtension.php @@ -29,9 +29,6 @@ public function __construct(LogoutUrlGenerator $generator) $this->generator = $generator; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php index 3b3a2ac58e27b..270583f27a995 100644 --- a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php @@ -32,9 +32,6 @@ public function __construct(UrlGeneratorInterface $generator) $this->generator = $generator; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ @@ -58,7 +55,7 @@ public function getUrl(string $name, array $parameters = [], bool $schemeRelativ * saving the unneeded automatic escaping for performance reasons. * * The URL generation process percent encodes non-alphanumeric characters. So there is no risk - * that malicious/invalid characters are part of the URL. The only character within an URL that + * that malicious/invalid characters are part of the URL. The only character within a URL that * must be escaped in html is the ampersand ("&") which separates query params. So we cannot mark * the URL generation as always safe, but only when we are sure there won't be multiple query * params. This is the case when there are none or only one constant parameter given. diff --git a/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php b/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php index dd1c57fb3b36e..25d1cab2cfa9f 100644 --- a/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/SecurityExtension.php @@ -69,9 +69,6 @@ public function getImpersonateExitPath(string $exitTo = null): string return $this->impersonateUrlGenerator->generateExitPath($exitTo); } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php index cbb7394e7d848..67835e2b87e75 100644 --- a/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/TranslationExtension.php @@ -58,9 +58,6 @@ public function getTranslator(): TranslatorInterface return $this->translator; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ @@ -68,9 +65,6 @@ public function getFunctions(): array ]; } - /** - * {@inheritdoc} - */ public function getFilters(): array { return [ @@ -78,9 +72,6 @@ public function getFilters(): array ]; } - /** - * {@inheritdoc} - */ public function getTokenParsers(): array { return [ @@ -92,9 +83,6 @@ public function getTokenParsers(): array ]; } - /** - * {@inheritdoc} - */ public function getNodeVisitors(): array { return [$this->getTranslationNodeVisitor(), new TranslationDefaultDomainNodeVisitor()]; @@ -115,6 +103,10 @@ public function trans(string|\Stringable|TranslatableInterface|null $message, ar throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be a locale passed as a string when the message is a "%s", "%s" given.', __METHOD__, TranslatableInterface::class, get_debug_type($arguments))); } + if ($message instanceof TranslatableMessage && '' === $message->getMessage()) { + return ''; + } + return $message->trans($this->getTranslator(), $locale ?? (\is_string($arguments) ? $arguments : null)); } diff --git a/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php b/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php index fcc34ecb5aad6..11eca517c5d69 100644 --- a/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/WebLinkExtension.php @@ -31,9 +31,6 @@ public function __construct(RequestStack $requestStack) $this->requestStack = $requestStack; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php b/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php index 895faf457f648..71f556aead03e 100644 --- a/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/WorkflowExtension.php @@ -32,9 +32,6 @@ public function __construct(Registry $workflowRegistry) $this->workflowRegistry = $workflowRegistry; } - /** - * {@inheritdoc} - */ public function getFunctions(): array { return [ diff --git a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php index 2d0595b7e9bca..cbfcc32a7e0b1 100644 --- a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php @@ -22,9 +22,6 @@ */ final class YamlExtension extends AbstractExtension { - /** - * {@inheritdoc} - */ public function getFilters(): array { return [ @@ -37,9 +34,7 @@ public function encode(mixed $input, int $inline = 0, int $dumpObjects = 0): str { static $dumper; - if (null === $dumper) { - $dumper = new YamlDumper(); - } + $dumper ??= new YamlDumper(); if (\defined('Symfony\Component\Yaml\Yaml::DUMP_OBJECT')) { return $dumper->dump($input, $inline, 0, $dumpObjects); diff --git a/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php b/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php index 6f408ebb584ad..7f733455e824a 100644 --- a/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php +++ b/src/Symfony/Bridge/Twig/Form/TwigRendererEngine.php @@ -30,9 +30,6 @@ public function __construct(array $defaultThemes, Environment $environment) $this->environment = $environment; } - /** - * {@inheritdoc} - */ public function renderBlock(FormView $view, mixed $resource, string $blockName, array $variables = []): string { $cacheKey = $view->vars[self::CACHE_KEY_VAR]; diff --git a/src/Symfony/Bridge/Twig/LICENSE b/src/Symfony/Bridge/Twig/LICENSE index 88bf75bb4d6a2..0138f8f071351 100644 --- a/src/Symfony/Bridge/Twig/LICENSE +++ b/src/Symfony/Bridge/Twig/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php b/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php index 397b35f1569d2..d418ee2f38634 100644 --- a/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php +++ b/src/Symfony/Bridge/Twig/Mime/BodyRenderer.php @@ -59,15 +59,13 @@ public function render(Message $message): void if ($template = $message->getTextTemplate()) { $message->text($this->twig->render($template, $vars)); - $message->textTemplate(null); } if ($template = $message->getHtmlTemplate()) { $message->html($this->twig->render($template, $vars)); - $message->htmlTemplate(null); } - $message->context([]); + $message->markAsRendered(); // if text body is empty, compute one from the HTML body if (!$message->getTextBody() && null !== $html = $message->getHtmlBody()) { diff --git a/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php b/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php index 60a29304c9b20..be6fea5c9cc56 100644 --- a/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php +++ b/src/Symfony/Bridge/Twig/Mime/NotificationEmail.php @@ -14,6 +14,7 @@ use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\Mime\Header\Headers; use Symfony\Component\Mime\Part\AbstractPart; +use Symfony\Component\Mime\Part\DataPart; use Twig\Extra\CssInliner\CssInlinerExtension; use Twig\Extra\Inky\InkyExtension; use Twig\Extra\Markdown\MarkdownExtension; @@ -39,6 +40,7 @@ class NotificationEmail extends TemplatedEmail 'raw' => false, 'footer_text' => 'Notification e-mail sent by Symfony', ]; + private bool $rendered = false; public function __construct(Headers $headers = null, AbstractPart $body = null) { @@ -134,7 +136,7 @@ public function exception(\Throwable|FlattenException $exception): static $exceptionAsString = $this->getExceptionAsString($exception); $this->context['exception'] = true; - $this->attach($exceptionAsString, 'exception.txt', 'text/plain'); + $this->addPart(new DataPart($exceptionAsString, 'exception.txt', 'text/plain')); $this->importance(self::IMPORTANCE_URGENT); if (!$this->getSubject()) { @@ -177,6 +179,18 @@ public function getContext(): array return array_merge($this->context, parent::getContext()); } + public function isRendered(): bool + { + return $this->rendered; + } + + public function markAsRendered(): void + { + parent::markAsRendered(); + + $this->rendered = true; + } + public function getPreparedHeaders(): Headers { $headers = parent::getPreparedHeaders(); @@ -208,7 +222,7 @@ private function getExceptionAsString(\Throwable|FlattenException $exception): s return $exception->getAsString(); } - $message = \get_class($exception); + $message = $exception::class; if ('' !== $exception->getMessage()) { $message .= ': '.$exception->getMessage(); } @@ -224,7 +238,7 @@ private function getExceptionAsString(\Throwable|FlattenException $exception): s */ public function __serialize(): array { - return [$this->context, parent::__serialize()]; + return [$this->context, $this->theme, $this->rendered, parent::__serialize()]; } /** @@ -232,7 +246,14 @@ public function __serialize(): array */ public function __unserialize(array $data): void { - [$this->context, $parentData] = $data; + if (4 === \count($data)) { + [$this->context, $this->theme, $this->rendered, $parentData] = $data; + } elseif (3 === \count($data)) { + [$this->context, $this->theme, $parentData] = $data; + } else { + // Backwards compatibility for deserializing data structures that were serialized without the theme + [$this->context, $parentData] = $data; + } parent::__unserialize($parentData); } diff --git a/src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php b/src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php index 083b007726310..777cc06b58984 100644 --- a/src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php +++ b/src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php @@ -67,6 +67,18 @@ public function getContext(): array return $this->context; } + public function isRendered(): bool + { + return null === $this->htmlTemplate && null === $this->textTemplate; + } + + public function markAsRendered(): void + { + $this->textTemplate = null; + $this->htmlTemplate = null; + $this->context = []; + } + /** * @internal */ diff --git a/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php b/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php index e0b3bef29308f..c4e0003fac1a4 100644 --- a/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php +++ b/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php @@ -12,6 +12,8 @@ namespace Symfony\Bridge\Twig\Mime; use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Part\DataPart; +use Symfony\Component\Mime\Part\File; use Twig\Environment; /** @@ -35,26 +37,33 @@ public function toName(): string return $this->message->getTo()[0]->getName(); } + /** + * @param string $image A Twig path to the image file. It's recommended to define + * some Twig namespace for email images (e.g. '@email/images/logo.png'). + * @param string|null $contentType The media type (i.e. MIME type) of the image file (e.g. 'image/png'). + * Some email clients require this to display embedded images. + */ public function image(string $image, string $contentType = null): string { $file = $this->twig->getLoader()->getSourceContext($image); - if ($path = $file->getPath()) { - $this->message->embedFromPath($path, $image, $contentType); - } else { - $this->message->embed($file->getCode(), $image, $contentType); - } + $body = $file->getPath() ? new File($file->getPath()) : $file->getCode(); + $this->message->addPart((new DataPart($body, $image, $contentType))->asInline()); return 'cid:'.$image; } + /** + * @param string $file A Twig path to the file. It's recommended to define + * some Twig namespace for email files (e.g. '@email/files/contract.pdf'). + * @param string|null $name A custom file name that overrides the original name of the attached file + * @param string|null $contentType The media type (i.e. MIME type) of the file (e.g. 'application/pdf'). + * Some email clients require this to display attached files. + */ public function attach(string $file, string $name = null, string $contentType = null): void { $file = $this->twig->getLoader()->getSourceContext($file); - if ($path = $file->getPath()) { - $this->message->attachFromPath($path, $name, $contentType); - } else { - $this->message->attach($file->getCode(), $name, $contentType); - } + $body = $file->getPath() ? new File($file->getPath()) : $file->getCode(); + $this->message->addPart(new DataPart($body, $name, $contentType)); } /** diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php index 61c8b5ff52083..d0e3337a9239c 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationDefaultDomainNodeVisitor.php @@ -37,9 +37,6 @@ public function __construct() $this->scope = new Scope(); } - /** - * {@inheritdoc} - */ protected function doEnterNode(Node $node, Environment $env): Node { if ($node instanceof BlockNode || $node instanceof ModuleNode) { @@ -86,9 +83,6 @@ protected function doEnterNode(Node $node, Environment $env): Node return $node; } - /** - * {@inheritdoc} - */ protected function doLeaveNode(Node $node, Environment $env): ?Node { if ($node instanceof TransDefaultDomainNode) { @@ -102,9 +96,6 @@ protected function doLeaveNode(Node $node, Environment $env): ?Node return $node; } - /** - * {@inheritdoc} - */ public function getPriority(): int { return -10; diff --git a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php index c8bee150982df..29cb13d0b2e5b 100644 --- a/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php +++ b/src/Symfony/Bridge/Twig/NodeVisitor/TranslationNodeVisitor.php @@ -49,9 +49,6 @@ public function getMessages(): array return $this->messages; } - /** - * {@inheritdoc} - */ protected function doEnterNode(Node $node, Environment $env): Node { if (!$this->enabled) { @@ -101,17 +98,11 @@ protected function doEnterNode(Node $node, Environment $env): Node return $node; } - /** - * {@inheritdoc} - */ protected function doLeaveNode(Node $node, Environment $env): ?Node { return $node; } - /** - * {@inheritdoc} - */ public function getPriority(): int { return 0; diff --git a/src/Symfony/Bridge/Twig/Resources/views/Email/zurb_2/main.css b/src/Symfony/Bridge/Twig/Resources/views/Email/zurb_2/main.css index b826813ec5d76..dab0df58abecb 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Email/zurb_2/main.css +++ b/src/Symfony/Bridge/Twig/Resources/views/Email/zurb_2/main.css @@ -1,7 +1,7 @@ /* * Copyright (c) 2017 ZURB, inc. -- MIT License * - * https://raw.githubusercontent.com/foundation/foundation-emails/v2.2.1/dist/foundation-emails.css + * https://github.com/foundation/foundation-emails/blob/v2.2.1/dist/foundation-emails.css */ .wrapper { diff --git a/src/Symfony/Bridge/Twig/Resources/views/Email/zurb_2/notification/body.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Email/zurb_2/notification/body.html.twig index 0a52d36b374ed..28a62de3eed57 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Email/zurb_2/notification/body.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Email/zurb_2/notification/body.html.twig @@ -26,7 +26,7 @@ {% if markdown %} {{ include('@email/zurb_2/notification/content_markdown.html.twig') }} {% else %} - {{ (raw ? content|raw : content)|nl2br }} + {{ raw ? content|raw : content|nl2br }} {% endif %} {% endblock %} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index 865f9078a9658..f4e313b4756c8 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -90,6 +90,10 @@ {%- if required -%} {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) -%} {%- endif -%} + {%- if parent_label_class is defined -%} + {% set embed_label_classes = parent_label_class|split(' ')|filter(class => class in ['checkbox-inline', 'radio-inline']) %} + {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ embed_label_classes|join(' '))|trim}) -%} + {% endif %} {%- if label is not same as(false) and label is empty -%} {%- if label_format is not empty -%} {%- set label = label_format|replace({ diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig index 4bf2f9cde1ddd..458cc6847ed8e 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_4_layout.html.twig @@ -6,7 +6,7 @@ {%- set prepend = not (money_pattern starts with '{{') -%} {%- set append = not (money_pattern ends with '}}') -%} {%- if prepend or append -%} -
+
{%- if prepend -%}
{{ money_pattern|form_encode_currency }} @@ -261,6 +261,10 @@ {%- if required -%} {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' required')|trim}) -%} {%- endif -%} + {%- if parent_label_class is defined -%} + {% set embed_label_classes = parent_label_class|split(' ')|filter(class => class in ['checkbox-inline', 'radio-inline']) %} + {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ embed_label_classes|join(' '))|trim}) -%} + {% endif %} {{ widget|raw }} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig index a8b381ec84eab..252b95e4f3293 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_5_layout.html.twig @@ -6,7 +6,7 @@ {%- set prepend = not (money_pattern starts with '{{') -%} {%- set append = not (money_pattern ends with '}}') -%} {%- if prepend or append -%} -
+
{%- if prepend -%} {{ money_pattern|form_encode_currency }} {%- endif -%} @@ -218,10 +218,10 @@ {%- set attr = attr|merge({class: attr_class|trim}) -%} {%- set parent_label_class = parent_label_class|default(label_attr.class|default('')) -%} {%- if 'checkbox-inline' in parent_label_class %} - {% set row_class = row_class ~ ' form-check-inline' %} + {%- set row_class = row_class ~ ' form-check-inline' -%} {% endif -%} {%- if 'checkbox-switch' in parent_label_class %} - {% set row_class = row_class ~ ' form-switch' %} + {%- set row_class = row_class ~ ' form-switch' -%} {% endif -%} {%- if row_class is not empty -%}
@@ -308,33 +308,11 @@ {%- if parent_label_class is defined -%} {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|replace({'checkbox-inline': '', 'radio-inline': ''})|trim}) -%} {%- endif -%} - {%- if label is not same as(false) and label is empty -%} - {%- if label_format is not empty -%} - {%- set label = label_format|replace({ - '%name%': name, - '%id%': id, - }) -%} - {%- else -%} - {%- set label = name|humanize -%} - {%- endif -%} - {%- endif -%} {{ widget|raw }} {%- if label is not same as(false) -%} - {%- if translation_domain is same as(false) -%} - {%- if label_html is same as(false) -%} - {{- label -}} - {%- else -%} - {{- label|raw -}} - {%- endif -%} - {%- else -%} - {%- if label_html is same as(false) -%} - {{- label|trans(label_translation_parameters, translation_domain) -}} - {%- else -%} - {{- label|trans(label_translation_parameters, translation_domain)|raw -}} - {%- endif -%} - {%- endif -%} + {{- block('form_label_content') -}} {%- endif -%} {%- endif -%} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig index b8cb8c44aa832..e8b9318b3a8bf 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_base_layout.html.twig @@ -11,7 +11,7 @@ {% set prepend = not (money_pattern starts with '{{') %} {% set append = not (money_pattern ends with '}}') %} {% if prepend or append %} -
+
{% if prepend %} {{ money_pattern|form_encode_currency }} {% endif %} diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig index f8c51b83dd8ed..345695f62d684 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/foundation_5_layout.html.twig @@ -253,6 +253,10 @@ {% if errors|length > 0 -%} {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' error')|trim}) %} {% endif %} + {%- if parent_label_class is defined -%} + {% set embed_label_classes = parent_label_class|split(' ')|filter(class => class in ['checkbox-inline', 'radio-inline']) %} + {%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ embed_label_classes|join(' '))|trim}) -%} + {% endif %} {% if label is empty %} {%- if label_format is not empty -%} {% set label = label_format|replace({ diff --git a/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php b/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php index 6ba4ca77e51c4..219d47d1013d5 100644 --- a/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php +++ b/src/Symfony/Bridge/Twig/Tests/AppVariableTest.php @@ -43,7 +43,7 @@ public function testDebug($debugFlag) $this->assertEquals($debugFlag, $this->appVariable->getDebug()); } - public function debugDataProvider() + public static function debugDataProvider() { return [ 'debug on' => [true], @@ -228,6 +228,40 @@ public function testGetFlashes() ); } + public function testGetCurrentRoute() + { + $this->setRequestStack(new Request(attributes: ['_route' => 'some_route'])); + + $this->assertSame('some_route', $this->appVariable->getCurrent_route()); + } + + public function testGetCurrentRouteWithRequestStackNotSet() + { + $this->expectException(\RuntimeException::class); + $this->appVariable->getCurrent_route(); + } + + public function testGetCurrentRouteParameters() + { + $routeParams = ['some_param' => true]; + $this->setRequestStack(new Request(attributes: ['_route_params' => $routeParams])); + + $this->assertSame($routeParams, $this->appVariable->getCurrent_route_parameters()); + } + + public function testGetCurrentRouteParametersWithoutAttribute() + { + $this->setRequestStack(new Request()); + + $this->assertSame([], $this->appVariable->getCurrent_route_parameters()); + } + + public function testGetCurrentRouteParametersWithRequestStackNotSet() + { + $this->expectException(\RuntimeException::class); + $this->appVariable->getCurrent_route_parameters(); + } + protected function setRequestStack($request) { $requestStackMock = $this->createMock(RequestStack::class); diff --git a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php index f0d7d661b1ff2..3d61d6eed7458 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/DebugCommandTest.php @@ -84,7 +84,7 @@ public function testDebugTemplateName(array $input, string $output, array $paths $this->assertStringMatchesFormat($output, $tester->getDisplay(true)); } - public function getDebugTemplateNameTestData() + public static function getDebugTemplateNameTestData() { $defaultPaths = [ 'templates/' => null, @@ -311,7 +311,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions(): iterable + public static function provideCompletionSuggestions(): iterable { yield 'name' => [['email'], []]; yield 'option --format' => [['--format', ''], ['text', 'json']]; diff --git a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php index 6428f4860ba78..bae8e727fd7a0 100644 --- a/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Command/LintCommandTest.php @@ -146,7 +146,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $tester->complete($input)); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'option' => [['--format', ''], ['txt', 'json', 'github']]; } diff --git a/src/Symfony/Bridge/Twig/Tests/EventListener/TemplateAttributeListenerTest.php b/src/Symfony/Bridge/Twig/Tests/EventListener/TemplateAttributeListenerTest.php index 8f6d6a2ca4068..e1fb7f9575902 100644 --- a/src/Symfony/Bridge/Twig/Tests/EventListener/TemplateAttributeListenerTest.php +++ b/src/Symfony/Bridge/Twig/Tests/EventListener/TemplateAttributeListenerTest.php @@ -29,12 +29,18 @@ public function testAttribute() $twig = $this->createMock(Environment::class); $twig->expects($this->exactly(3)) ->method('render') - ->withConsecutive( - ['templates/foo.html.twig', ['foo' => 'bar']], - ['templates/foo.html.twig', ['bar' => 'Bar', 'buz' => 'def']], - ['templates/foo.html.twig', []], - ) - ->willReturn('Bar'); + ->willReturnCallback(function (...$args) { + static $series = [ + ['templates/foo.html.twig', ['foo' => 'bar']], + ['templates/foo.html.twig', ['bar' => 'Bar', 'buz' => 'def']], + ['templates/foo.html.twig', []], + ]; + + $this->assertSame(array_shift($series), $args); + + return 'Bar'; + }) + ; $request = new Request(); $kernel = $this->createMock(HttpKernelInterface::class); diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3HorizontalLayoutTestCase.php similarity index 98% rename from src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3HorizontalLayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3HorizontalLayoutTestCase.php index c4874c3407092..e79b0c3141e9e 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3HorizontalLayoutTestCase.php @@ -11,7 +11,7 @@ namespace Symfony\Bridge\Twig\Tests\Extension; -abstract class AbstractBootstrap3HorizontalLayoutTest extends AbstractBootstrap3LayoutTest +abstract class AbstractBootstrap3HorizontalLayoutTestCase extends AbstractBootstrap3LayoutTestCase { public function testLabelOnForm() { diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTestCase.php similarity index 99% rename from src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTestCase.php index 808352300adf4..16cb900b99522 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap3LayoutTestCase.php @@ -13,9 +13,9 @@ use Symfony\Component\Form\Extension\Core\Type\PercentType; use Symfony\Component\Form\FormError; -use Symfony\Component\Form\Tests\AbstractLayoutTest; +use Symfony\Component\Form\Tests\AbstractLayoutTestCase; -abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest +abstract class AbstractBootstrap3LayoutTestCase extends AbstractLayoutTestCase { public function testLabelOnForm() { @@ -2154,6 +2154,25 @@ public function testRenderNumberWithHtml5NumberType() $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']], '/input [@type="number"] + [@step="any"] + [@name="name"] + [@class="my&class form-control"] + [@value="1234.56"] +' + ); + } + + public function testRenderNumberWithHtml5NumberTypeAndStepAttribute() + { + $form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\NumberType', 1234.56, [ + 'html5' => true, + 'attr' => ['step' => '0.1'], + ]); + + $this->assertWidgetMatchesXpath($form->createView(), ['attr' => ['class' => 'my&class']], + '/input + [@type="number"] + [@step="0.1"] [@name="name"] [@class="my&class form-control"] [@value="1234.56"] diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4HorizontalLayoutTestCase.php similarity index 98% rename from src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4HorizontalLayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4HorizontalLayoutTestCase.php index 1ef14ecf4dc0f..ce8a733665854 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4HorizontalLayoutTestCase.php @@ -18,7 +18,7 @@ * * @author Hidde Wieringa */ -abstract class AbstractBootstrap4HorizontalLayoutTest extends AbstractBootstrap4LayoutTest +abstract class AbstractBootstrap4HorizontalLayoutTestCase extends AbstractBootstrap4LayoutTestCase { public function testRow() { diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTestCase.php similarity index 99% rename from src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTestCase.php index 36f8a50d00f13..e329b5d49ec16 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap4LayoutTestCase.php @@ -28,7 +28,7 @@ * * @author Hidde Wieringa */ -abstract class AbstractBootstrap4LayoutTest extends AbstractBootstrap3LayoutTest +abstract class AbstractBootstrap4LayoutTestCase extends AbstractBootstrap3LayoutTestCase { public function testRow() { @@ -1165,7 +1165,7 @@ public function testMoney() $this->assertWidgetMatchesXpath($form->createView(), ['id' => 'my&id', 'attr' => ['class' => 'my&class']], '/div - [@class="input-group"] + [@class="input-group "] [ ./div [@class="input-group-prepend"] diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5HorizontalLayoutTestCase.php similarity index 99% rename from src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5HorizontalLayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5HorizontalLayoutTestCase.php index e6a8846c4b127..770c18a1ba3e1 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5HorizontalLayoutTestCase.php @@ -24,7 +24,7 @@ * * @author Romain Monteil */ -abstract class AbstractBootstrap5HorizontalLayoutTest extends AbstractBootstrap5LayoutTest +abstract class AbstractBootstrap5HorizontalLayoutTestCase extends AbstractBootstrap5LayoutTestCase { public function testRow() { diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTestCase.php similarity index 99% rename from src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTest.php rename to src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTestCase.php index 7ac695ec927c3..a59e659728d50 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/AbstractBootstrap5LayoutTestCase.php @@ -36,7 +36,7 @@ * * @author Romain Monteil */ -abstract class AbstractBootstrap5LayoutTest extends AbstractBootstrap4LayoutTest +abstract class AbstractBootstrap5LayoutTestCase extends AbstractBootstrap4LayoutTestCase { public function testRow() { @@ -1465,7 +1465,7 @@ public function testMoney() $this->assertWidgetMatchesXpath($form->createView(), ['id' => 'my&id', 'attr' => ['class' => 'my&class']], '/div - [@class="input-group"] + [@class="input-group "] [ ./span [@class="input-group-text"] diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php index 33bb266cb6a2f..38983cbd697f1 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/CodeExtensionTest.php @@ -44,7 +44,7 @@ public function testGettingMethodAbbreviation($method, $abbr) $this->assertEquals($this->getExtension()->abbrMethod($method), $abbr); } - public function getClassNameProvider(): array + public static function getClassNameProvider(): array { return [ ['F\Q\N\Foo', 'Foo'], @@ -52,7 +52,7 @@ public function getClassNameProvider(): array ]; } - public function getMethodNameProvider(): array + public static function getMethodNameProvider(): array { return [ ['F\Q\N\Foo::Method', 'Foo::Method()'], diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php index d0825221663ad..8fe455e5d5706 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php @@ -53,7 +53,7 @@ public function testDumpTag($template, $debug, $expectedOutput, $expectedDumped) $this->assertSame($expectedDumped, $dumped); } - public function getDumpTags() + public static function getDumpTags() { return [ ['A{% dump %}B', true, 'AB', []], @@ -88,7 +88,7 @@ public function testDump($context, $args, $expectedOutput, $debug = true) $this->assertEquals($expectedOutput, $dump); } - public function getDumpArgs() + public static function getDumpArgs() { return [ [[], [], '', false], diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php index a0953a2d9f346..e746a267105e2 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3HorizontalLayoutTest.php @@ -21,7 +21,7 @@ use Twig\Environment; use Twig\Loader\FilesystemLoader; -class FormExtensionBootstrap3HorizontalLayoutTest extends AbstractBootstrap3HorizontalLayoutTest +class FormExtensionBootstrap3HorizontalLayoutTest extends AbstractBootstrap3HorizontalLayoutTestCase { use RuntimeLoaderProvider; diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php index a3a6c751a0718..2b7e186c0d5cd 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap3LayoutTest.php @@ -21,7 +21,7 @@ use Twig\Environment; use Twig\Loader\FilesystemLoader; -class FormExtensionBootstrap3LayoutTest extends AbstractBootstrap3LayoutTest +class FormExtensionBootstrap3LayoutTest extends AbstractBootstrap3LayoutTestCase { use RuntimeLoaderProvider; diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4HorizontalLayoutTest.php index 33e1862afd74a..f95e678440b4d 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4HorizontalLayoutTest.php @@ -26,7 +26,7 @@ * * @author Hidde Wieringa */ -class FormExtensionBootstrap4HorizontalLayoutTest extends AbstractBootstrap4HorizontalLayoutTest +class FormExtensionBootstrap4HorizontalLayoutTest extends AbstractBootstrap4HorizontalLayoutTestCase { use RuntimeLoaderProvider; diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php index b98cb3a73ab4f..2d0ca9fabab21 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap4LayoutTest.php @@ -26,7 +26,7 @@ * * @author Hidde Wieringa */ -class FormExtensionBootstrap4LayoutTest extends AbstractBootstrap4LayoutTest +class FormExtensionBootstrap4LayoutTest extends AbstractBootstrap4LayoutTestCase { use RuntimeLoaderProvider; /** @@ -102,7 +102,7 @@ public function testMoneyWidgetInIso() ; $this->assertSame(<<<'HTML' -
+
HTML diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap5HorizontalLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap5HorizontalLayoutTest.php index ef924884a4751..d6d2c3c2ecb06 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap5HorizontalLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap5HorizontalLayoutTest.php @@ -26,7 +26,7 @@ * * @author Romain Monteil */ -class FormExtensionBootstrap5HorizontalLayoutTest extends AbstractBootstrap5HorizontalLayoutTest +class FormExtensionBootstrap5HorizontalLayoutTest extends AbstractBootstrap5HorizontalLayoutTestCase { use RuntimeLoaderProvider; diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap5LayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap5LayoutTest.php index 8c0e54744f964..94174615a0ce0 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap5LayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionBootstrap5LayoutTest.php @@ -28,7 +28,7 @@ * * @author Romain Monteil */ -class FormExtensionBootstrap5LayoutTest extends AbstractBootstrap5LayoutTest +class FormExtensionBootstrap5LayoutTest extends AbstractBootstrap5LayoutTestCase { use RuntimeLoaderProvider; @@ -104,7 +104,7 @@ public function testMoneyWidgetInIso() ->createView(); self::assertSame(<<<'HTML' -
+
HTML , trim($this->renderWidget($view))); } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php index cae1a1c6e6b4d..9fa6a01316ba0 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionDivLayoutTest.php @@ -18,12 +18,12 @@ use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\FormRenderer; use Symfony\Component\Form\FormView; -use Symfony\Component\Form\Tests\AbstractDivLayoutTest; +use Symfony\Component\Form\Tests\AbstractDivLayoutTestCase; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Twig\Environment; use Twig\Loader\FilesystemLoader; -class FormExtensionDivLayoutTest extends AbstractDivLayoutTest +class FormExtensionDivLayoutTest extends AbstractDivLayoutTestCase { use RuntimeLoaderProvider; @@ -100,7 +100,7 @@ public function testThemeBlockInheritanceUsingDynamicExtend() ); } - public function isSelectedChoiceProvider() + public static function isSelectedChoiceProvider() { return [ [true, '0', '0'], @@ -150,7 +150,7 @@ public function testStartTagHasActionAttributeWhenActionIsZero() $this->assertSame('
', $html); } - public function isRootFormProvider() + public static function isRootFormProvider() { return [ [true, new FormView()], diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php index 7b75be234da3a..c0a7a9e8a8002 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/FormExtensionTableLayoutTest.php @@ -17,12 +17,12 @@ use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubTranslator; use Symfony\Component\Form\FormRenderer; use Symfony\Component\Form\FormView; -use Symfony\Component\Form\Tests\AbstractTableLayoutTest; +use Symfony\Component\Form\Tests\AbstractTableLayoutTestCase; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Twig\Environment; use Twig\Loader\FilesystemLoader; -class FormExtensionTableLayoutTest extends AbstractTableLayoutTest +class FormExtensionTableLayoutTest extends AbstractTableLayoutTestCase { use RuntimeLoaderProvider; diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php index 131cb3d040a4b..b92c3f831cb7d 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php @@ -32,7 +32,7 @@ public function testGenerateAbsoluteUrl($expected, $path, $pathinfo) $this->assertEquals($expected, $extension->generateAbsoluteUrl($path)); } - public function getGenerateAbsoluteUrlData() + public static function getGenerateAbsoluteUrlData() { return [ ['http://localhost/foo.png', '/foo.png', '/foo/bar.html'], @@ -76,7 +76,7 @@ public function testGenerateAbsoluteUrlWithoutRequestAndRequestContext($path) $this->assertEquals($path, $extension->generateAbsoluteUrl($path)); } - public function getGenerateAbsoluteUrlRequestContextData() + public static function getGenerateAbsoluteUrlRequestContextData() { return [ ['/foo.png', '/foo', 'localhost', 'http', 80, 443, 'http://localhost/foo.png'], @@ -117,7 +117,7 @@ public function testGenerateRelativePath($expected, $path, $pathinfo) $this->assertEquals($expected, $extension->generateRelativePath($path)); } - public function getGenerateRelativePathData() + public static function getGenerateRelativePathData() { return [ ['../foo.png', '/foo.png', '/foo/bar.html'], diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php index 5a995c8eeb76c..742a74f325b91 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/RoutingExtensionTest.php @@ -34,7 +34,7 @@ public function testEscaping($template, $mustBeEscaped) $this->assertSame($mustBeEscaped, $nodes->getNode('body')->getNode(0)->getNode('expr') instanceof FilterExpression); } - public function getEscapingTemplates() + public static function getEscapingTemplates() { return [ ['{{ path("foo") }}', false], diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/SerializerExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/SerializerExtensionTest.php index ef54ee2775f15..0c36c8c6b4b93 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/SerializerExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/SerializerExtensionTest.php @@ -41,7 +41,7 @@ public function testSerializeFilter(string $template, string $expectedResult) self::assertSame($expectedResult, $twig->render('template', ['object' => new SerializerModelFixture()])); } - public function serializerDataProvider(): \Generator + public static function serializerDataProvider(): \Generator { yield ['{{ object|serialize }}', '{"name":"howdy","title":"fixture"}']; yield ['{{ object|serialize(\'yaml\') }}', '{ name: howdy, title: fixture }']; diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php index 65f1bd69bff7c..d7ff03d72ff98 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/StopwatchExtensionTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Extension\StopwatchExtension; use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Stopwatch\StopwatchEvent; use Twig\Environment; use Twig\Error\RuntimeError; use Twig\Loader\ArrayLoader; @@ -41,7 +42,7 @@ public function testTiming($template, $events) } } - public function getTimingTemplates() + public static function getTimingTemplates() { return [ ['{% stopwatch "foo" %}something{% endstopwatch %}', 'foo'], @@ -67,12 +68,29 @@ protected function getStopwatch($events = []) $expectedStopCalls[] = [$this->equalTo($eventName)]; } - $startInvocationMocker = $stopwatch->expects($this->exactly($expectedCalls)) - ->method('start'); - \call_user_func_array([$startInvocationMocker, 'withConsecutive'], $expectedStartCalls); - $stopInvocationMocker = $stopwatch->expects($this->exactly($expectedCalls)) - ->method('stop'); - \call_user_func_array([$stopInvocationMocker, 'withConsecutive'], $expectedStopCalls); + $stopwatch + ->expects($this->exactly($expectedCalls)) + ->method('start') + ->willReturnCallback(function (string $name, string $category) use (&$expectedStartCalls) { + [$expectedName, $expectedCategory] = array_shift($expectedStartCalls); + + $expectedName->evaluate($name); + $this->assertSame($expectedCategory, $category); + + return $this->createMock(StopwatchEvent::class); + }) + ; + + $stopwatch + ->expects($this->exactly($expectedCalls)) + ->method('stop') + ->willReturnCallback(function (string $name) use (&$expectedStopCalls) { + [$expectedName] = array_shift($expectedStopCalls); + $expectedName->evaluate($name); + + return $this->createMock(StopwatchEvent::class); + }) + ; return $stopwatch; } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php index 86f50da0b7db8..12d6bc5e2a000 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/TranslationExtensionTest.php @@ -61,7 +61,7 @@ public function testTransComplexBody() $this->getTemplate("{% trans %}\n{{ 1 + 2 }}{% endtrans %}")->render(); } - public function getTransTests() + public static function getTransTests() { return [ // trans tag @@ -124,6 +124,7 @@ public function getTransTests() ['{{ foo|trans }}', '', ['foo' => null]], // trans object + ['{{ t("")|trans }}', ''], ['{{ t("Hello")|trans }}', 'Hello'], ['{{ t(name)|trans }}', 'Symfony', ['name' => 'Symfony']], ['{{ t(hello, { \'%name%\': \'Symfony\' })|trans }}', 'Hello Symfony', ['hello' => 'Hello %name%']], @@ -207,9 +208,7 @@ public function testDefaultTranslationDomainWithNamedArguments() private function getTemplate($template, TranslatorInterface $translator = null): TemplateWrapper { - if (null === $translator) { - $translator = new Translator('en'); - } + $translator ??= new Translator('en'); if (\is_array($template)) { $loader = new TwigArrayLoader($template); diff --git a/src/Symfony/Bridge/Twig/Tests/Mime/NotificationEmailTest.php b/src/Symfony/Bridge/Twig/Tests/Mime/NotificationEmailTest.php index b0e064e392426..ceafea1bb6b72 100644 --- a/src/Symfony/Bridge/Twig/Tests/Mime/NotificationEmailTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Mime/NotificationEmailTest.php @@ -47,6 +47,7 @@ public function testSerialize() ->importance(NotificationEmail::IMPORTANCE_HIGH) ->action('Bar', 'http://example.com/') ->context(['a' => 'b']) + ->theme('example') )); $this->assertEquals([ 'importance' => NotificationEmail::IMPORTANCE_HIGH, @@ -59,6 +60,8 @@ public function testSerialize() 'a' => 'b', 'footer_text' => 'Notification e-mail sent by Symfony', ], $email->getContext()); + + $this->assertSame('@email/example/notification/body.html.twig', $email->getHtmlTemplate()); } public function testTheme() diff --git a/src/Symfony/Bridge/Twig/Tests/Mime/TemplatedEmailTest.php b/src/Symfony/Bridge/Twig/Tests/Mime/TemplatedEmailTest.php index 77548fb119626..019be16ff4bcf 100644 --- a/src/Symfony/Bridge/Twig/Tests/Mime/TemplatedEmailTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Mime/TemplatedEmailTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Mime\TemplatedEmail; +use Symfony\Component\Mime\Part\DataPart; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; @@ -58,7 +59,7 @@ public function testSymfonySerialize() $e->textTemplate('email.txt.twig'); $e->htmlTemplate('email.html.twig'); $e->context(['foo' => 'bar']); - $e->attach('Some Text file', 'test.txt'); + $e->addPart(new DataPart('Some Text file', 'test.txt')); $expected = clone $e; $expectedJson = <<serialize($e, 'json', [ObjectNormalizer::IGNORED_ATTRIBUTES => ['cachedBody']]); - $this->assertSame($expectedJson, json_encode(json_decode($serialized), \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); + $this->assertStringMatchesFormat($expectedJson, json_encode(json_decode($serialized), \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); $n = $serializer->deserialize($serialized, TemplatedEmail::class, 'json'); $serialized = $serializer->serialize($e, 'json', [ObjectNormalizer::IGNORED_ATTRIBUTES => ['cachedBody']]); - $this->assertSame($expectedJson, json_encode(json_decode($serialized), \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); + $this->assertStringMatchesFormat($expectedJson, json_encode(json_decode($serialized), \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); $n->from('fabien@symfony.com'); $expected->from('fabien@symfony.com'); diff --git a/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php b/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php index ab45b83fecd72..cf98191233057 100644 --- a/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Node/FormThemeTest.php @@ -47,9 +47,9 @@ public function testCompile() { $form = new NameExpression('form', 0); $resources = new ArrayExpression([ - new ConstantExpression(0, 0), - new ConstantExpression('tpl1', 0), new ConstantExpression(1, 0), + new ConstantExpression('tpl1', 0), + new ConstantExpression(0, 0), new ConstantExpression('tpl2', 0), ], 0); @@ -62,7 +62,7 @@ public function testCompile() $this->assertEquals( sprintf( - '$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, [0 => "tpl1", 1 => "tpl2"], true);', + '$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, [1 => "tpl1", 0 => "tpl2"], true);', $this->getVariableGetter('form') ), trim($compiler->compile($node)->getSource()) @@ -72,7 +72,7 @@ public function testCompile() $this->assertEquals( sprintf( - '$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, [0 => "tpl1", 1 => "tpl2"], false);', + '$this->env->getRuntime("Symfony\\\\Component\\\\Form\\\\FormRenderer")->setTheme(%s, [1 => "tpl1", 0 => "tpl2"], false);', $this->getVariableGetter('form') ), trim($compiler->compile($node)->getSource()) diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php index cc2b6ef2ac39e..25b8166eae62a 100644 --- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationDefaultDomainNodeVisitorTest.php @@ -76,7 +76,7 @@ public function testNewModuleWithoutDefaultDomainTag(Node $node) $this->assertEquals([[self::$message, null]], $visitor->getMessages()); } - public function getDefaultDomainAssignmentTestData() + public static function getDefaultDomainAssignmentTestData() { return [ [TwigNodeProvider::getTransFilter(self::$message)], diff --git a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php index 069914a4fc066..bf073602583f7 100644 --- a/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/NodeVisitor/TranslationNodeVisitorTest.php @@ -51,7 +51,7 @@ public function testMessageExtractionWithInvalidDomainNode() $this->testMessagesExtraction($node, [[$message, TranslationNodeVisitor::UNDEFINED_DOMAIN]]); } - public function getMessagesExtractionTestData() + public static function getMessagesExtractionTestData() { $message = 'new key'; $domain = 'domain'; diff --git a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php index d404060091f83..41504050f74f8 100644 --- a/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php +++ b/src/Symfony/Bridge/Twig/Tests/TokenParser/FormThemeTokenParserTest.php @@ -40,7 +40,7 @@ public function testCompile($source, $expected) $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)); } - public function getTestsForFormTheme() + public static function getTestsForFormTheme() { return [ [ diff --git a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php index c93bd718bc903..7d7d65adbdbef 100644 --- a/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Translation/TwigExtractorTest.php @@ -53,7 +53,7 @@ public function testExtract($template, $messages) } } - public function getExtractData() + public static function getExtractData() { return [ ['{{ "new key" | trans() }}', ['new key' => 'messages']], @@ -101,7 +101,7 @@ public function testExtractSyntaxError($resources, array $messages) $this->assertSame($messages, $catalogue->all()); } - public function resourcesWithSyntaxErrorsProvider(): array + public static function resourcesWithSyntaxErrorsProvider(): array { return [ [__DIR__.'/../Fixtures', ['messages' => ['Hi!' => 'Hi!']]], @@ -132,7 +132,7 @@ public function testExtractWithFiles($resource) $this->assertEquals('Hi!', $catalogue->get('Hi!', 'messages')); } - public function resourceProvider(): array + public static function resourceProvider(): array { $directory = __DIR__.'/../Fixtures/extractor/'; diff --git a/src/Symfony/Bridge/Twig/TokenParser/DumpTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/DumpTokenParser.php index 341dc41855ab0..d4996dbe91cd1 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/DumpTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/DumpTokenParser.php @@ -29,9 +29,6 @@ */ final class DumpTokenParser extends AbstractTokenParser { - /** - * {@inheritdoc} - */ public function parse(Token $token): Node { $values = null; @@ -43,9 +40,6 @@ public function parse(Token $token): Node return new DumpNode($this->parser->getVarName(), $values, $token->getLine(), $this->getTag()); } - /** - * {@inheritdoc} - */ public function getTag(): string { return 'dump'; diff --git a/src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php index ef5dacb59ddd1..b95a2a05e76a4 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/FormThemeTokenParser.php @@ -24,9 +24,6 @@ */ final class FormThemeTokenParser extends AbstractTokenParser { - /** - * {@inheritdoc} - */ public function parse(Token $token): Node { $lineno = $token->getLine(); @@ -54,9 +51,6 @@ public function parse(Token $token): Node return new FormThemeNode($form, $resources, $lineno, $this->getTag(), $only); } - /** - * {@inheritdoc} - */ public function getTag(): string { return 'form_theme'; diff --git a/src/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php index 19b820497d9fa..c6d850d07cbf7 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/TransDefaultDomainTokenParser.php @@ -23,9 +23,6 @@ */ final class TransDefaultDomainTokenParser extends AbstractTokenParser { - /** - * {@inheritdoc} - */ public function parse(Token $token): Node { $expr = $this->parser->getExpressionParser()->parseExpression(); @@ -35,9 +32,6 @@ public function parse(Token $token): Node return new TransDefaultDomainNode($expr, $token->getLine(), $this->getTag()); } - /** - * {@inheritdoc} - */ public function getTag(): string { return 'trans_default_domain'; diff --git a/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php b/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php index b440931bba794..e60263a4a783f 100644 --- a/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php +++ b/src/Symfony/Bridge/Twig/TokenParser/TransTokenParser.php @@ -27,9 +27,6 @@ */ final class TransTokenParser extends AbstractTokenParser { - /** - * {@inheritdoc} - */ public function parse(Token $token): Node { $lineno = $token->getLine(); @@ -85,9 +82,6 @@ public function decideTransFork(Token $token): bool return $token->test(['endtrans']); } - /** - * {@inheritdoc} - */ public function getTag(): string { return 'trans'; diff --git a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php index 86ef269328530..0707359dd16cf 100644 --- a/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php +++ b/src/Symfony/Bridge/Twig/Translation/TwigExtractor.php @@ -45,9 +45,6 @@ public function __construct(Environment $twig) $this->twig = $twig; } - /** - * {@inheritdoc} - */ public function extract($resource, MessageCatalogue $catalogue) { foreach ($this->extractFiles($resource) as $file) { @@ -59,9 +56,6 @@ public function extract($resource, MessageCatalogue $catalogue) } } - /** - * {@inheritdoc} - */ public function setPrefix(string $prefix) { $this->prefix = $prefix; @@ -86,9 +80,6 @@ protected function canBeExtracted(string $file): bool return $this->isFile($file) && 'twig' === pathinfo($file, \PATHINFO_EXTENSION); } - /** - * {@inheritdoc} - */ protected function extractFromDirectory($directory): iterable { $finder = new Finder(); diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index be7c2d2ab31ac..a091f85808dd5 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -21,14 +21,14 @@ "twig/twig": "^2.13|^3.0.4" }, "require-dev": { - "doctrine/annotations": "^1.12", - "egulias/email-validator": "^2.1.10|^3", + "doctrine/annotations": "^1.12|^2", + "egulias/email-validator": "^2.1.10|^3|^4", "league/html-to-markdown": "^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "symfony/asset": "^5.4|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", - "symfony/form": "^6.1", + "symfony/form": "^6.2.7", "symfony/html-sanitizer": "^6.1", "symfony/http-foundation": "^5.4|^6.0", "symfony/http-kernel": "^6.2", @@ -43,7 +43,7 @@ "symfony/security-core": "^5.4|^6.0", "symfony/security-csrf": "^5.4|^6.0", "symfony/security-http": "^5.4|^6.0", - "symfony/serializer": "^5.4|^6.0", + "symfony/serializer": "^6.2", "symfony/stopwatch": "^5.4|^6.0", "symfony/console": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", @@ -57,7 +57,7 @@ "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", "symfony/console": "<5.4", - "symfony/form": "<6.1", + "symfony/form": "<6.2.7", "symfony/http-foundation": "<5.4", "symfony/http-kernel": "<6.2", "symfony/mime": "<6.2", diff --git a/src/Symfony/Bundle/DebugBundle/DebugBundle.php b/src/Symfony/Bundle/DebugBundle/DebugBundle.php index 04fd507612747..045ea5f8dbd65 100644 --- a/src/Symfony/Bundle/DebugBundle/DebugBundle.php +++ b/src/Symfony/Bundle/DebugBundle/DebugBundle.php @@ -44,9 +44,6 @@ public function boot() } } - /** - * {@inheritdoc} - */ public function build(ContainerBuilder $container) { parent::build($container); diff --git a/src/Symfony/Bundle/DebugBundle/DependencyInjection/Compiler/DumpDataCollectorPass.php b/src/Symfony/Bundle/DebugBundle/DependencyInjection/Compiler/DumpDataCollectorPass.php index 2579bf64eab13..5c531cc91dbcd 100644 --- a/src/Symfony/Bundle/DebugBundle/DependencyInjection/Compiler/DumpDataCollectorPass.php +++ b/src/Symfony/Bundle/DebugBundle/DependencyInjection/Compiler/DumpDataCollectorPass.php @@ -22,9 +22,6 @@ */ class DumpDataCollectorPass implements CompilerPassInterface { - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if (!$container->hasDefinition('data_collector.dump')) { diff --git a/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php index f645a48f8a3b1..caf7359690750 100644 --- a/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/DebugBundle/DependencyInjection/Configuration.php @@ -21,9 +21,6 @@ */ class Configuration implements ConfigurationInterface { - /** - * {@inheritdoc} - */ public function getConfigTreeBuilder(): TreeBuilder { $treeBuilder = new TreeBuilder('debug'); diff --git a/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php b/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php index 731cc62b3116d..b4d3cc776a682 100644 --- a/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php +++ b/src/Symfony/Bundle/DebugBundle/DependencyInjection/DebugExtension.php @@ -14,6 +14,7 @@ use Symfony\Bridge\Monolog\Command\ServerLogCommand; use Symfony\Bundle\DebugBundle\Command\ServerDumpPlaceholderCommand; use Symfony\Component\Config\FileLocator; +use Symfony\Component\Console\Command\Command; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; @@ -29,9 +30,6 @@ */ class DebugExtension extends Extension { - /** - * {@inheritdoc} - */ public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); @@ -92,22 +90,16 @@ public function load(array $configs, ContainerBuilder $container) ; } - if (!class_exists(ServerLogCommand::class)) { + if (!class_exists(Command::class) || !class_exists(ServerLogCommand::class)) { $container->removeDefinition('monolog.command.server_log'); } } - /** - * {@inheritdoc} - */ public function getXsdValidationBasePath(): string|false { return __DIR__.'/../Resources/config/schema'; } - /** - * {@inheritdoc} - */ public function getNamespace(): string { return 'http://symfony.com/schema/dic/debug'; diff --git a/src/Symfony/Bundle/DebugBundle/LICENSE b/src/Symfony/Bundle/DebugBundle/LICENSE index a843ec124ea70..29f72d5e95920 100644 --- a/src/Symfony/Bundle/DebugBundle/LICENSE +++ b/src/Symfony/Bundle/DebugBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2022 Fabien Potencier +Copyright (c) 2014-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig index ee009cc6fa508..04b694955051e 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig +++ b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/dump.html.twig @@ -68,7 +68,7 @@ {{ dump.data|raw }}
{% else %} -
+

No content was dumped.

{% endfor %} diff --git a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg index d6cedd8a19dc3..9ba3a7d94d8f3 100644 --- a/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg +++ b/src/Symfony/Bundle/DebugBundle/Resources/views/Profiler/icon.svg @@ -1 +1,9 @@ - + + + + + + + + + diff --git a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php index f026d4d188232..66c641de6460e 100644 --- a/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php +++ b/src/Symfony/Bundle/DebugBundle/Tests/DependencyInjection/DebugExtensionTest.php @@ -70,7 +70,7 @@ public function testUnsetClosureFileInfoShouldBeRegisteredInVarCloner() $this->assertTrue($called); } - public function provideServicesUsingDumpDestinationCreation(): array + public static function provideServicesUsingDumpDestinationCreation(): array { return [ ['tcp://localhost:1234', 'tcp://localhost:1234', null], diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index 7c503eb35d52b..1d43a095ed1c5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -4,8 +4,9 @@ CHANGELOG 6.2 --- + * Add `resolve-env` option to `debug:config` command to display actual values of environment variables in dumped configuration * Add `NotificationAssertionsTrait` - * Add option `framework.catch_all_throwables` to allow `Symfony\Component\HttpKernel\HttpKernel` to catch all kinds of `Throwable` + * Add option `framework.handle_all_throwables` to allow `Symfony\Component\HttpKernel\HttpKernel` to handle all kinds of `Throwable` * Make `AbstractController::render()` able to deal with forms and deprecate `renderForm()` * Deprecate the `Symfony\Component\Serializer\Normalizer\ObjectNormalizer` and `Symfony\Component\Serializer\Normalizer\PropertyNormalizer` autowiring aliases, type-hint against @@ -15,6 +16,13 @@ CHANGELOG * Tag all workflows services with `workflow`, those with type=workflow are tagged with `workflow.workflow`, and those with type=state_machine with `workflow.state_machine` + * Add `rate_limiter` configuration option to `messenger.transport` to allow rate limited transports using the RateLimiter component + * Remove `@internal` tag from secret vaults to allow them to be used directly outside the framework bundle and custom vaults to be added + * Deprecate `framework.form.legacy_error_messages` config node + * Add a `framework.router.cache_dir` configuration option to configure the default `Router` `cache_dir` option + * Add option `framework.messenger.buses.*.default_middleware.allow_no_senders` to enable throwing when a message doesn't have a sender + * Deprecate `AbstractController::renderForm()`, use `render()` instead + * Deprecate `FrameworkExtension::registerRateLimiter()` 6.1 --- diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AbstractPhpFileCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AbstractPhpFileCacheWarmer.php index f72df9e6542bc..47b3db6d2c9d3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AbstractPhpFileCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AbstractPhpFileCacheWarmer.php @@ -29,17 +29,12 @@ public function __construct(string $phpArrayFile) $this->phpArrayFile = $phpArrayFile; } - /** - * {@inheritdoc} - */ public function isOptional(): bool { return true; } /** - * {@inheritdoc} - * * @return string[] A list of classes to preload on PHP 7.4+ */ public function warmUp(string $cacheDir): array diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php index 2fd5f661d2dee..5ecc25b3d7078 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/AnnotationsCacheWarmer.php @@ -40,9 +40,6 @@ public function __construct(Reader $annotationReader, string $phpArrayFile, stri $this->debug = $debug; } - /** - * {@inheritdoc} - */ protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter): bool { $annotatedClassPatterns = $cacheDir.'/annotations.map'; diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/CachePoolClearerCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/CachePoolClearerCacheWarmer.php index 41041aedaed99..cad71a409dbc0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/CachePoolClearerCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/CachePoolClearerCacheWarmer.php @@ -38,8 +38,6 @@ public function __construct(Psr6CacheClearer $poolClearer, array $pools = []) } /** - * {@inheritdoc} - * * @return string[] */ public function warmUp(string $cacheDirectory): array @@ -53,9 +51,6 @@ public function warmUp(string $cacheDirectory): array return []; } - /** - * {@inheritdoc} - */ public function isOptional(): bool { // optional cache warmers are not run when handling the request diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ConfigBuilderCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ConfigBuilderCacheWarmer.php index 95f62d9202203..0ed0a93821a76 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ConfigBuilderCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ConfigBuilderCacheWarmer.php @@ -38,8 +38,6 @@ public function __construct(KernelInterface $kernel, LoggerInterface $logger = n } /** - * {@inheritdoc} - * * @return string[] */ public function warmUp(string $cacheDir): array @@ -55,7 +53,7 @@ public function warmUp(string $cacheDir): array try { $this->dumpExtension($extension, $generator); } catch (\Exception $e) { - $this->logger?->warning('Failed to generate ConfigBuilder for extension {extensionClass}.', ['exception' => $e, 'extensionClass' => \get_class($extension)]); + $this->logger?->warning('Failed to generate ConfigBuilder for extension {extensionClass}: '.$e->getMessage(), ['exception' => $e, 'extensionClass' => $extension::class]); } } @@ -79,9 +77,6 @@ private function dumpExtension(ExtensionInterface $extension, ConfigBuilderGener $generator->build($configuration); } - /** - * {@inheritdoc} - */ public function isOptional(): bool { return true; diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php index 21dd3a2728845..13daf436939e4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/RouterCacheWarmer.php @@ -34,9 +34,6 @@ public function __construct(ContainerInterface $container) $this->container = $container; } - /** - * {@inheritdoc} - */ public function warmUp(string $cacheDir): array { $router = $this->container->get('router'); @@ -48,17 +45,11 @@ public function warmUp(string $cacheDir): array throw new \LogicException(sprintf('The router "%s" cannot be warmed up because it does not implement "%s".', get_debug_type($router), WarmableInterface::class)); } - /** - * {@inheritdoc} - */ public function isOptional(): bool { return true; } - /** - * {@inheritdoc} - */ public static function getSubscribedServices(): array { return [ diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php index ccc1402e2ddfe..40093998293ce 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/SerializerCacheWarmer.php @@ -39,9 +39,6 @@ public function __construct(array $loaders, string $phpArrayFile) $this->loaders = $loaders; } - /** - * {@inheritdoc} - */ protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter): bool { if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php index 8cddae7e308ce..039658f4b721b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/TranslationsCacheWarmer.php @@ -34,8 +34,6 @@ public function __construct(ContainerInterface $container) } /** - * {@inheritdoc} - * * @return string[] */ public function warmUp(string $cacheDir): array @@ -49,17 +47,11 @@ public function warmUp(string $cacheDir): array return []; } - /** - * {@inheritdoc} - */ public function isOptional(): bool { return true; } - /** - * {@inheritdoc} - */ public static function getSubscribedServices(): array { return [ diff --git a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php index 04025a49bba66..ba0dede3948a2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php +++ b/src/Symfony/Bundle/FrameworkBundle/CacheWarmer/ValidatorCacheWarmer.php @@ -39,9 +39,6 @@ public function __construct(ValidatorBuilder $validatorBuilder, string $phpArray $this->validatorBuilder = $validatorBuilder; } - /** - * {@inheritdoc} - */ protected function doWarmUp(string $cacheDir, ArrayAdapter $arrayAdapter): bool { if (!method_exists($this->validatorBuilder, 'getLoaders')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php index ed5a47f12a0f7..3ce96a1316d8c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AboutCommand.php @@ -31,9 +31,6 @@ #[AsCommand(name: 'about', description: 'Display information about the current project')] class AboutCommand extends Command { - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -47,9 +44,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -73,7 +67,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int new TableSeparator(), ['Kernel'], new TableSeparator(), - ['Type', \get_class($kernel)], + ['Type', $kernel::class], ['Environment', $kernel->getEnvironment()], ['Debug', $kernel->isDebug() ? 'true' : 'false'], ['Charset', $kernel->getCharset()], @@ -87,8 +81,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int ['Architecture', (\PHP_INT_SIZE * 8).' bits'], ['Intl locale', class_exists(\Locale::class, false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a'], ['Timezone', date_default_timezone_get().' ('.(new \DateTime())->format(\DateTime::W3C).')'], - ['OPcache', \extension_loaded('Zend OPcache') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false'], - ['APCu', \extension_loaded('apcu') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN) ? 'true' : 'false'], + ['OPcache', \extension_loaded('Zend OPcache') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOL) ? 'true' : 'false'], + ['APCu', \extension_loaded('apcu') && filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOL) ? 'true' : 'false'], ['Xdebug', \extension_loaded('xdebug') ? 'true' : 'false'], ]; @@ -107,6 +101,10 @@ private static function formatFileSize(string $path): string if (is_file($path)) { $size = filesize($path) ?: 0; } else { + if (!is_dir($path)) { + return 'n/a'; + } + $size = 0; foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS | \RecursiveDirectoryIterator::FOLLOW_SYMLINKS)) as $file) { if ($file->isReadable()) { @@ -120,15 +118,15 @@ private static function formatFileSize(string $path): string private static function isExpired(string $date): bool { - $date = \DateTime::createFromFormat('d/m/Y', '01/'.$date); + $date = \DateTimeImmutable::createFromFormat('d/m/Y', '01/'.$date); - return false !== $date && new \DateTime() > $date->modify('last day of this month 23:59:59'); + return false !== $date && new \DateTimeImmutable() > $date->modify('last day of this month 23:59:59'); } private static function daysBeforeExpiration(string $date): string { - $date = \DateTime::createFromFormat('d/m/Y', '01/'.$date); + $date = \DateTimeImmutable::createFromFormat('d/m/Y', '01/'.$date); - return (new \DateTime())->diff($date->modify('last day of this month 23:59:59'))->format('in %R%a days'); + return (new \DateTimeImmutable())->diff($date->modify('last day of this month 23:59:59'))->format('in %R%a days'); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php index 5f79d95bf036d..6ca739a7ed6e0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AbstractConfigCommand.php @@ -54,6 +54,44 @@ protected function listBundles(OutputInterface|StyleInterface $output) } } + protected function listNonBundleExtensions(OutputInterface|StyleInterface $output): void + { + $title = 'Available registered non-bundle extension aliases'; + $headers = ['Extension alias']; + $rows = []; + + $kernel = $this->getApplication()->getKernel(); + + $bundleExtensions = []; + foreach ($kernel->getBundles() as $bundle) { + if ($extension = $bundle->getContainerExtension()) { + $bundleExtensions[\get_class($extension)] = true; + } + } + + $extensions = $this->getContainerBuilder($kernel)->getExtensions(); + + foreach ($extensions as $alias => $extension) { + if (isset($bundleExtensions[\get_class($extension)])) { + continue; + } + $rows[] = [$alias]; + } + + if (!$rows) { + return; + } + + if ($output instanceof StyleInterface) { + $output->title($title); + $output->table($headers, $rows); + } else { + $output->writeln($title); + $table = new Table($output); + $table->setHeaders($headers)->setRows($rows)->render(); + } + } + protected function findExtension(string $name): ExtensionInterface { $bundles = $this->initializeBundles(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php index aaad074daf246..2ee2d93988864 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php @@ -52,9 +52,6 @@ public function __construct(Filesystem $filesystem, string $projectDir) $this->projectDir = $projectDir; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -87,9 +84,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { /** @var KernelInterface $kernel */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php index 785027dbc8d4e..56166a197fb51 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/BuildDebugContainerTrait.php @@ -44,13 +44,20 @@ protected function getContainerBuilder(KernelInterface $kernel): ContainerBuilde $this->initializeBundles(); return $this->buildContainer(); - }, $kernel, \get_class($kernel)); + }, $kernel, $kernel::class); $container = $buildContainer(); $container->getCompilerPassConfig()->setRemovingPasses([]); $container->getCompilerPassConfig()->setAfterRemovingPasses([]); $container->compile(); } else { - (new XmlFileLoader($container = new ContainerBuilder(), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump')); + $buildContainer = \Closure::bind(function () { + $containerBuilder = $this->getContainerBuilder(); + $this->prepareContainer($containerBuilder); + + return $containerBuilder; + }, $kernel, \get_class($kernel)); + $container = $buildContainer(); + (new XmlFileLoader($container, new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump')); $locatorPass = new ServiceLocatorTagPass(); $locatorPass->process($container); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php index b7d95cfec87f2..a0a84a90007ee 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php @@ -48,9 +48,6 @@ public function __construct(CacheClearerInterface $cacheClearer, Filesystem $fil $this->filesystem = $filesystem ?? new Filesystem(); } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -69,9 +66,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $fs = $this->filesystem; @@ -135,14 +129,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($output->isVerbose()) { $io->comment('Warming up optional cache...'); } - $warmer = $kernel->getContainer()->get('cache_warmer'); - // non optional warmers already ran during container compilation - $warmer->enableOnlyOptionalWarmers(); - $preload = (array) $warmer->warmUp($realCacheDir); - - if ($preload && file_exists($preloadFile = $realCacheDir.'/'.$kernel->getContainer()->getParameter('kernel.container_class').'.preload.php')) { - Preloader::append($preloadFile, $preload); - } + $this->warmupOptionals($realCacheDir, $realBuildDir); } } else { $fs->mkdir($warmupDir); @@ -151,7 +138,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($output->isVerbose()) { $io->comment('Warming up cache...'); } - $this->warmup($warmupDir, $realCacheDir, !$input->getOption('no-optional-warmers')); + $this->warmup($warmupDir, $realBuildDir); + + if (!$input->getOption('no-optional-warmers')) { + if ($output->isVerbose()) { + $io->comment('Warming up optional cache...'); + } + $this->warmupOptionals($useBuildDir ? $realCacheDir : $warmupDir, $warmupDir); + } } if (!$fs->exists($warmupDir.'/'.$containerDir)) { @@ -225,7 +219,7 @@ private function isNfs(string $dir): bool return false; } - private function warmup(string $warmupDir, string $realBuildDir, bool $enableOptionalWarmers = true) + private function warmup(string $warmupDir, string $realBuildDir): void { // create a temporary kernel $kernel = $this->getApplication()->getKernel(); @@ -234,18 +228,6 @@ private function warmup(string $warmupDir, string $realBuildDir, bool $enableOpt } $kernel->reboot($warmupDir); - // warmup temporary dir - if ($enableOptionalWarmers) { - $warmer = $kernel->getContainer()->get('cache_warmer'); - // non optional warmers already ran during container compilation - $warmer->enableOnlyOptionalWarmers(); - $preload = (array) $warmer->warmUp($warmupDir); - - if ($preload && file_exists($preloadFile = $warmupDir.'/'.$kernel->getContainer()->getParameter('kernel.container_class').'.preload.php')) { - Preloader::append($preloadFile, $preload); - } - } - // fix references to cached files with the real cache directory name $search = [$warmupDir, str_replace('\\', '\\\\', $warmupDir)]; $replace = str_replace('\\', '/', $realBuildDir); @@ -256,4 +238,17 @@ private function warmup(string $warmupDir, string $realBuildDir, bool $enableOpt } } } + + private function warmupOptionals(string $cacheDir, string $warmupDir): void + { + $kernel = $this->getApplication()->getKernel(); + $warmer = $kernel->getContainer()->get('cache_warmer'); + // non optional warmers already ran during container compilation + $warmer->enableOnlyOptionalWarmers(); + $preload = (array) $warmer->warmUp($cacheDir); + + if ($preload && file_exists($preloadFile = $warmupDir.'/'.$kernel->getContainer()->getParameter('kernel.container_class').'.preload.php')) { + Preloader::append($preloadFile, $preload); + } + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php index 65da5d7709487..99ebf9a979595 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php @@ -45,9 +45,6 @@ public function __construct(Psr6CacheClearer $poolClearer, array $poolNames = nu $this->poolNames = $poolNames; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -63,9 +60,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolDeleteCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolDeleteCommand.php index 546bd631d492c..2c0dddd0fd9f9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolDeleteCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolDeleteCommand.php @@ -43,9 +43,6 @@ public function __construct(Psr6CacheClearer $poolClearer, array $poolNames = nu $this->poolNames = $poolNames; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -62,9 +59,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolInvalidateTagsCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolInvalidateTagsCommand.php index 8ad84bfa69503..9e6ef9330e24a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolInvalidateTagsCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolInvalidateTagsCommand.php @@ -41,9 +41,6 @@ public function __construct(ServiceProviderInterface $pools) $this->poolNames = array_keys($pools->getProvidedServices()); } - /** - * {@inheritdoc} - */ protected function configure(): void { $this @@ -59,9 +56,6 @@ protected function configure(): void ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -98,12 +92,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($errors) { $io->error('Done but with errors.'); - return self::FAILURE; + return 1; } $io->success('Successfully invalidated cache tags.'); - return self::SUCCESS; + return 0; } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolListCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolListCommand.php index 09ec8b1ef0cc7..f1e05b0db0768 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolListCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolListCommand.php @@ -37,9 +37,6 @@ public function __construct(array $poolNames) $this->poolNames = $poolNames; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -50,9 +47,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php index 1e8bb7f0338a6..53f2b01dff994 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolPruneCommand.php @@ -38,9 +38,6 @@ public function __construct(iterable $pools) $this->pools = $pools; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -53,9 +50,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php index 36c2f76e7e3cf..48f7aa391bae4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CacheWarmupCommand.php @@ -39,9 +39,6 @@ public function __construct(CacheWarmerAggregate $cacheWarmer) $this->cacheWarmer = $cacheWarmer; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -53,19 +50,11 @@ protected function configure() Before running this command, the cache must be empty. -This command does not generate the classes cache (as when executing this -command, too many classes that should be part of the cache are already loaded -in memory). Use curl or any other similar tool to warm up -the classes cache if you want. - EOF ) ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php index 33a3e458ff6bf..41321bffd3f38 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDebugCommand.php @@ -38,9 +38,6 @@ #[AsCommand(name: 'debug:config', description: 'Dump the current configuration for an extension')] class ConfigDebugCommand extends AbstractConfigCommand { - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -67,9 +64,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -77,14 +71,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (null === $name = $input->getArgument('name')) { $this->listBundles($errorIo); - - $kernel = $this->getApplication()->getKernel(); - if ($kernel instanceof ExtensionInterface - && ($kernel instanceof ConfigurationInterface || $kernel instanceof ConfigurationExtensionInterface) - && $kernel->getAlias() - ) { - $errorIo->table(['Kernel Extension'], [[$kernel->getAlias()]]); - } + $this->listNonBundleExtensions($errorIo); $errorIo->comment('Provide the name of a bundle as the first argument of this command to dump its configuration. (e.g. debug:config FrameworkBundle)'); $errorIo->comment('For dumping a specific option, add its path as the second argument of this command. (e.g. debug:config FrameworkBundle serializer to dump the framework.serializer configuration)'); @@ -173,12 +160,12 @@ private function getConfigForExtension(ExtensionInterface $extension, ContainerB // Fall back to default config if the extension has one - if (!$extension instanceof ConfigurationExtensionInterface) { + if (!$extension instanceof ConfigurationExtensionInterface && !$extension instanceof ConfigurationInterface) { throw new \LogicException(sprintf('The extension with alias "%s" does not have configuration.', $extensionAlias)); } $configs = $container->getExtensionConfig($extensionAlias); - $configuration = $extension->getConfiguration($configs, $container); + $configuration = $extension instanceof ConfigurationInterface ? $extension : $extension->getConfiguration($configs, $container); $this->validateConfiguration($extension, $configuration); return (new Processor())->processConfiguration($configuration, $configs); @@ -187,7 +174,8 @@ private function getConfigForExtension(ExtensionInterface $extension, ContainerB public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('name')) { - $suggestions->suggestValues($this->getAvailableBundles(!preg_match('/^[A-Z]/', $input->getCompletionValue()))); + $suggestions->suggestValues($this->getAvailableExtensions()); + $suggestions->suggestValues($this->getAvailableBundles()); return; } @@ -202,11 +190,23 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti } } - private function getAvailableBundles(bool $alias): array + private function getAvailableExtensions(): array + { + $kernel = $this->getApplication()->getKernel(); + + $extensions = []; + foreach ($this->getContainerBuilder($kernel)->getExtensions() as $alias => $extension) { + $extensions[] = $alias; + } + + return $extensions; + } + + private function getAvailableBundles(): array { $availableBundles = []; foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) { - $availableBundles[] = $alias ? $bundle->getContainerExtension()->getAlias() : $bundle->getName(); + $availableBundles[] = $bundle->getName(); } return $availableBundles; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php index 271ba9bf6429b..60510787af444 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ConfigDumpReferenceCommand.php @@ -23,8 +23,6 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; -use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; use Symfony\Component\Yaml\Yaml; /** @@ -39,9 +37,6 @@ #[AsCommand(name: 'config:dump-reference', description: 'Dump the default configuration for an extension')] class ConfigDumpReferenceCommand extends AbstractConfigCommand { - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -67,7 +62,7 @@ protected function configure() For dumping a specific option, add its path as second argument (only available for the yaml format): - php %command.full_name% framework profiler.matcher + php %command.full_name% framework http_client.default_options EOF ) @@ -75,8 +70,6 @@ protected function configure() } /** - * {@inheritdoc} - * * @throws \LogicException */ protected function execute(InputInterface $input, OutputInterface $output): int @@ -86,18 +79,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (null === $name = $input->getArgument('name')) { $this->listBundles($errorIo); - - $kernel = $this->getApplication()->getKernel(); - if ($kernel instanceof ExtensionInterface - && ($kernel instanceof ConfigurationInterface || $kernel instanceof ConfigurationExtensionInterface) - && $kernel->getAlias() - ) { - $errorIo->table(['Kernel Extension'], [[$kernel->getAlias()]]); - } + $this->listNonBundleExtensions($errorIo); $errorIo->comment([ 'Provide the name of a bundle as the first argument of this command to dump its default configuration. (e.g. config:dump-reference FrameworkBundle)', - 'For dumping a specific option, add its path as the second argument of this command. (e.g. config:dump-reference FrameworkBundle profiler.matcher to dump the framework.profiler.matcher configuration)', + 'For dumping a specific option, add its path as the second argument of this command. (e.g. config:dump-reference FrameworkBundle http_client.default_options to dump the framework.http_client.default_options configuration)', ]); return 0; @@ -161,6 +147,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('name')) { + $suggestions->suggestValues($this->getAvailableExtensions()); $suggestions->suggestValues($this->getAvailableBundles()); } @@ -169,13 +156,24 @@ public function complete(CompletionInput $input, CompletionSuggestions $suggesti } } + private function getAvailableExtensions(): array + { + $kernel = $this->getApplication()->getKernel(); + + $extensions = []; + foreach ($this->getContainerBuilder($kernel)->getExtensions() as $alias => $extension) { + $extensions[] = $alias; + } + + return $extensions; + } + private function getAvailableBundles(): array { $bundles = []; foreach ($this->getApplication()->getKernel()->getBundles() as $bundle) { $bundles[] = $bundle->getName(); - $bundles[] = $bundle->getContainerExtension()->getAlias(); } return $bundles; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php index a0f484db7b268..293fccc20b18b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerDebugCommand.php @@ -38,9 +38,6 @@ class ContainerDebugCommand extends Command { use BuildDebugContainerTrait; - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -114,9 +111,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -145,6 +139,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } elseif ($input->getOption('tags')) { $options = ['group_by' => 'tags']; } elseif ($tag = $input->getOption('tag')) { + $tag = $this->findProperTagName($input, $errorIo, $object, $tag); $options = ['tag' => $tag]; } elseif ($name = $input->getArgument('name')) { $name = $this->findProperServiceName($input, $errorIo, $object, $name, $input->getOption('show-hidden')); @@ -276,7 +271,7 @@ private function findProperServiceName(InputInterface $input, SymfonyStyle $io, } $matchingServices = $this->findServiceIdsContaining($builder, $name, $showHidden); - if (empty($matchingServices)) { + if (!$matchingServices) { throw new InvalidArgumentException(sprintf('No services found that match "%s".', $name)); } @@ -287,6 +282,24 @@ private function findProperServiceName(InputInterface $input, SymfonyStyle $io, return $io->choice('Select one of the following services to display its information', $matchingServices); } + private function findProperTagName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $builder, string $tagName): string + { + if (\in_array($tagName, $builder->findTags(), true) || !$input->isInteractive()) { + return $tagName; + } + + $matchingTags = $this->findTagsContaining($builder, $tagName); + if (!$matchingTags) { + throw new InvalidArgumentException(sprintf('No tags found that match "%s".', $tagName)); + } + + if (1 === \count($matchingTags)) { + return $matchingTags[0]; + } + + return $io->choice('Select one of the following tags to display its information', $matchingTags); + } + private function findServiceIdsContaining(ContainerBuilder $builder, string $name, bool $showHidden): array { $serviceIds = $builder->getServiceIds(); @@ -295,6 +308,9 @@ private function findServiceIdsContaining(ContainerBuilder $builder, string $nam if (!$showHidden && str_starts_with($serviceId, '.')) { continue; } + if (!$showHidden && $builder->hasDefinition($serviceId) && $builder->getDefinition($serviceId)->hasTag('container.excluded')) { + continue; + } if (false !== stripos(str_replace('\\', '', $serviceId), $name)) { $foundServiceIdsIgnoringBackslashes[] = $serviceId; } @@ -306,6 +322,19 @@ private function findServiceIdsContaining(ContainerBuilder $builder, string $nam return $foundServiceIds ?: $foundServiceIdsIgnoringBackslashes; } + private function findTagsContaining(ContainerBuilder $builder, string $tagName): array + { + $tags = $builder->findTags(); + $foundTags = []; + foreach ($tags as $tag) { + if (str_contains($tag, $tagName)) { + $foundTags[] = $tag; + } + } + + return $foundTags; + } + /** * @internal */ diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php index 5e3e891ee845f..8100793faf3dc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/ContainerLintCommand.php @@ -33,9 +33,6 @@ final class ContainerLintCommand extends Command { private ContainerBuilder $containerBuilder; - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -43,9 +40,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -92,7 +86,7 @@ private function getContainerBuilder(): ContainerBuilder $this->initializeBundles(); return $this->buildContainer(); - }, $kernel, \get_class($kernel)); + }, $kernel, $kernel::class); $container = $buildContainer(); $skippedIds = []; diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php index ecd507127c00c..7ac33243bb0c3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/DebugAutowiringCommand.php @@ -43,9 +43,6 @@ public function __construct(string $name = null, FileLinkFormatter $fileLinkForm parent::__construct($name); } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -68,9 +65,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -87,7 +81,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return false !== stripos(str_replace('\\', '', $serviceId), $searchNormalized) && !str_starts_with($serviceId, '.'); }); - if (empty($serviceIds)) { + if (!$serviceIds) { $errorIo->error(sprintf('No autowirable classes or interfaces found matching "%s"', $search)); return 1; @@ -106,6 +100,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $previousId = '-'; $serviceIdsNb = 0; foreach ($serviceIds as $serviceId) { + if ($builder->hasDefinition($serviceId) && $builder->getDefinition($serviceId)->hasTag('container.excluded')) { + continue; + } $text = []; $resolvedServiceId = $serviceId; if (!str_starts_with($serviceId, $previousId)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php index a8bb8303071db..41edb7505ab5c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/EventDispatcherDebugCommand.php @@ -46,9 +46,6 @@ public function __construct(ContainerInterface $dispatchers) $this->dispatchers = $dispatchers; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -72,8 +69,6 @@ protected function configure() } /** - * {@inheritdoc} - * * @throws \LogicException */ protected function execute(InputInterface $input, OutputInterface $output): int diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php index 7d2d6e9340304..a4c53beff3957 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterDebugCommand.php @@ -50,9 +50,6 @@ public function __construct(RouterInterface $router, FileLinkFormatter $fileLink $this->fileLinkFormatter = $fileLinkFormatter; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -73,8 +70,6 @@ protected function configure() } /** - * {@inheritdoc} - * * @throws InvalidArgumentException When route does not exist */ protected function execute(InputInterface $input, OutputInterface $output): int diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php index da78d510a6b4c..a654522d806f2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/RouterMatchCommand.php @@ -47,9 +47,6 @@ public function __construct(RouterInterface $router, iterable $expressionLanguag $this->expressionLanguageProviders = $expressionLanguageProviders; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -73,9 +70,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php index c2cfae702863b..f255053db3d5b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationDebugCommand.php @@ -73,9 +73,6 @@ public function __construct(TranslatorInterface $translator, TranslationReaderIn $this->enabledLocales = $enabledLocales; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -121,9 +118,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -186,7 +180,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } // No defined or extracted messages - if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) { + if (!$allMessages || null !== $domain && empty($allMessages[$domain])) { $outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale); if (null !== $domain) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php index ec399b0204c75..7a4f1d97e7b79 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/TranslationUpdateCommand.php @@ -75,9 +75,6 @@ public function __construct(TranslationWriterInterface $writer, TranslationReade $this->enabledLocales = $enabledLocales; } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -90,7 +87,7 @@ protected function configure() new InputOption('force', null, InputOption::VALUE_NONE, 'Should the extract be done'), new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'), new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'Specify the domain to extract'), - new InputOption('sort', null, InputOption::VALUE_OPTIONAL, 'Return list of messages sorted alphabetically', 'asc'), + new InputOption('sort', null, InputOption::VALUE_OPTIONAL, 'Return list of messages sorted alphabetically (only works with --dump-messages)', 'asc'), new InputOption('as-tree', null, InputOption::VALUE_OPTIONAL, 'Dump the messages as a tree-like structure: The given value defines the level where to switch to inline YAML'), ]) ->setHelp(<<<'EOF' @@ -119,16 +116,12 @@ protected function configure() You can dump a tree-like structure using the yaml format with --as-tree flag: php %command.full_name% --force --format=yaml --as-tree=3 en AcmeBundle - php %command.full_name% --force --format=yaml --sort=asc --as-tree=3 fr EOF ) ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); @@ -395,6 +388,7 @@ private function extractMessages(string $locale, array $transPaths, string $pref { $extractedCatalogue = new MessageCatalogue($locale); $this->extractor->setPrefix($prefix); + $transPaths = $this->filterDuplicateTransPaths($transPaths); foreach ($transPaths as $path) { if (is_dir($path) || is_file($path)) { $this->extractor->extract($path, $extractedCatalogue); @@ -404,6 +398,27 @@ private function extractMessages(string $locale, array $transPaths, string $pref return $extractedCatalogue; } + private function filterDuplicateTransPaths(array $transPaths): array + { + $transPaths = array_filter(array_map('realpath', $transPaths)); + + sort($transPaths); + + $filteredPaths = []; + + foreach ($transPaths as $path) { + foreach ($filteredPaths as $filteredPath) { + if (str_starts_with($path, $filteredPath.\DIRECTORY_SEPARATOR)) { + continue 2; + } + } + + $filteredPaths[] = $path; + } + + return $filteredPaths; + } + private function loadCurrentMessages(string $locale, array $transPaths): MessageCatalogue { $currentCatalogue = new MessageCatalogue($locale); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php index 14ea912a86bd9..3daa2b9ddb71a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/WorkflowDumpCommand.php @@ -66,9 +66,6 @@ public function __construct($workflows) } } - /** - * {@inheritdoc} - */ protected function configure() { $this @@ -90,9 +87,6 @@ protected function configure() ; } - /** - * {@inheritdoc} - */ protected function execute(InputInterface $input, OutputInterface $output): int { $workflowName = $input->getArgument('name'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php index e6d21f40b3dc1..0c33e2b8b9c84 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/XliffLintCommand.php @@ -43,9 +43,6 @@ public function __construct() parent::__construct(null, $directoryIteratorProvider, $isReadableProvider); } - /** - * {@inheritdoc} - */ protected function configure() { parent::configure(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php index 0686cde37ccba..42c1e795ccee6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/YamlLintCommand.php @@ -42,9 +42,6 @@ public function __construct() parent::__construct(null, $directoryIteratorProvider, $isReadableProvider); } - /** - * {@inheritdoc} - */ protected function configure() { parent::configure(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php index 8927eb1e07b1d..d73f323af7bbc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Application.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Application.php @@ -52,9 +52,6 @@ public function getKernel(): KernelInterface return $this->kernel; } - /** - * {@inheritdoc} - */ public function reset() { if ($this->kernel->getContainer()->has('services_resetter')) { @@ -80,9 +77,6 @@ public function doRun(InputInterface $input, OutputInterface $output): int return parent::doRun($input, $output); } - /** - * {@inheritdoc} - */ protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output): int { if (!$command instanceof ListCommand) { @@ -104,9 +98,6 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI return $returnCode; } - /** - * {@inheritdoc} - */ public function find(string $name): Command { $this->registerCommands(); @@ -114,9 +105,6 @@ public function find(string $name): Command return parent::find($name); } - /** - * {@inheritdoc} - */ public function get(string $name): Command { $this->registerCommands(); @@ -130,9 +118,6 @@ public function get(string $name): Command return $command; } - /** - * {@inheritdoc} - */ public function all(string $namespace = null): array { $this->registerCommands(); @@ -140,9 +125,6 @@ public function all(string $namespace = null): array return parent::all($namespace); } - /** - * {@inheritdoc} - */ public function getLongVersion(): string { return parent::getLongVersion().sprintf(' (env: %s, debug: %s) #StandWithUkraine https://sf.to/ukraine', $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php index a7ddd7ce46eb4..09128274d9903 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/Descriptor.php @@ -38,9 +38,6 @@ abstract class Descriptor implements DescriptorInterface */ protected $output; - /** - * {@inheritdoc} - */ public function describe(OutputInterface $output, mixed $object, array $options = []) { $this->output = $output; @@ -49,49 +46,22 @@ public function describe(OutputInterface $output, mixed $object, array $options (new AnalyzeServiceReferencesPass(false, false))->process($object); } - switch (true) { - case $object instanceof RouteCollection: - $this->describeRouteCollection($object, $options); - break; - case $object instanceof Route: - $this->describeRoute($object, $options); - break; - case $object instanceof ParameterBag: - $this->describeContainerParameters($object, $options); - break; - case $object instanceof ContainerBuilder && !empty($options['env-vars']): - $this->describeContainerEnvVars($this->getContainerEnvVars($object), $options); - break; - case $object instanceof ContainerBuilder && isset($options['group_by']) && 'tags' === $options['group_by']: - $this->describeContainerTags($object, $options); - break; - case $object instanceof ContainerBuilder && isset($options['id']): - $this->describeContainerService($this->resolveServiceDefinition($object, $options['id']), $options, $object); - break; - case $object instanceof ContainerBuilder && isset($options['parameter']): - $this->describeContainerParameter($object->resolveEnvPlaceholders($object->getParameter($options['parameter'])), $options); - break; - case $object instanceof ContainerBuilder && isset($options['deprecations']): - $this->describeContainerDeprecations($object, $options); - break; - case $object instanceof ContainerBuilder: - $this->describeContainerServices($object, $options); - break; - case $object instanceof Definition: - $this->describeContainerDefinition($object, $options); - break; - case $object instanceof Alias: - $this->describeContainerAlias($object, $options); - break; - case $object instanceof EventDispatcherInterface: - $this->describeEventDispatcherListeners($object, $options); - break; - case \is_callable($object): - $this->describeCallable($object, $options); - break; - default: - throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object))); - } + match (true) { + $object instanceof RouteCollection => $this->describeRouteCollection($object, $options), + $object instanceof Route => $this->describeRoute($object, $options), + $object instanceof ParameterBag => $this->describeContainerParameters($object, $options), + $object instanceof ContainerBuilder && !empty($options['env-vars']) => $this->describeContainerEnvVars($this->getContainerEnvVars($object), $options), + $object instanceof ContainerBuilder && isset($options['group_by']) && 'tags' === $options['group_by'] => $this->describeContainerTags($object, $options), + $object instanceof ContainerBuilder && isset($options['id']) => $this->describeContainerService($this->resolveServiceDefinition($object, $options['id']), $options, $object), + $object instanceof ContainerBuilder && isset($options['parameter']) => $this->describeContainerParameter($object->resolveEnvPlaceholders($object->getParameter($options['parameter'])), $options), + $object instanceof ContainerBuilder && isset($options['deprecations']) => $this->describeContainerDeprecations($object, $options), + $object instanceof ContainerBuilder => $this->describeContainerServices($object, $options), + $object instanceof Definition => $this->describeContainerDefinition($object, $options), + $object instanceof Alias => $this->describeContainerAlias($object, $options), + $object instanceof EventDispatcherInterface => $this->describeEventDispatcherListeners($object, $options), + \is_callable($object) => $this->describeCallable($object, $options), + default => throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', get_debug_type($object))), + }; if ($object instanceof ContainerBuilder) { $object->getCompiler()->getServiceReferenceGraph()->clear(); @@ -161,7 +131,7 @@ protected function formatValue(mixed $value): string } if (\is_object($value)) { - return sprintf('object(%s)', \get_class($value)); + return sprintf('object(%s)', $value::class); } if (\is_string($value)) { @@ -338,7 +308,7 @@ private function getContainerEnvVars(ContainerBuilder $container): array $getDefaultParameter = function (string $name) { return parent::get($name); }; - $getDefaultParameter = $getDefaultParameter->bindTo($bag, \get_class($bag)); + $getDefaultParameter = $getDefaultParameter->bindTo($bag, $bag::class); $getEnvReflection = new \ReflectionMethod($container, 'getEnv'); @@ -373,9 +343,9 @@ private function getContainerEnvVars(ContainerBuilder $container): array protected function getServiceEdges(ContainerBuilder $builder, string $serviceId): array { try { - return array_map(function (ServiceReferenceGraphEdge $edge) { + return array_values(array_unique(array_map(function (ServiceReferenceGraphEdge $edge) { return $edge->getSourceNode()->getId(); - }, $builder->getCompiler()->getServiceReferenceGraph()->getNode($serviceId)->getInEdges()); + }, $builder->getCompiler()->getServiceReferenceGraph()->getNode($serviceId)->getInEdges()))); } catch (InvalidArgumentException $exception) { return []; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php index 1c0cfa5c6d936..5f0588c9a6d48 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/JsonDescriptor.php @@ -78,7 +78,7 @@ protected function describeContainerService(object $service, array $options = [] } elseif ($service instanceof Definition) { $this->writeData($this->getContainerDefinitionData($service, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'], $builder, $options['id']), $options); } else { - $this->writeData(\get_class($service), $options); + $this->writeData($service::class, $options); } } @@ -106,9 +106,12 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o if ($service instanceof Alias) { $data['aliases'][$serviceId] = $this->getContainerAliasData($service); } elseif ($service instanceof Definition) { + if ($service->hasTag('container.excluded')) { + continue; + } $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments, $builder, $serviceId); } else { - $data['services'][$serviceId] = \get_class($service); + $data['services'][$serviceId] = $service::class; } } @@ -204,7 +207,7 @@ protected function getRouteData(Route $route): array 'hostRegex' => '' !== $route->getHost() ? $route->compile()->getHostRegex() : '', 'scheme' => $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY', 'method' => $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY', - 'class' => \get_class($route), + 'class' => $route::class, 'defaults' => $route->getDefaults(), 'requirements' => $route->getRequirements() ?: 'NO CUSTOM', 'options' => $route->getOptions(), @@ -252,7 +255,7 @@ private function getContainerDefinitionData(Definition $definition, bool $omitTa if ($factory[0] instanceof Reference) { $data['factory_service'] = (string) $factory[0]; } elseif ($factory[0] instanceof Definition) { - throw new \InvalidArgumentException('Factory is not describable.'); + $data['factory_service'] = sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'class not configured'); } else { $data['factory_class'] = $factory[0]; } @@ -370,7 +373,7 @@ private function getCallableData(mixed $callable): array } $data['name'] = $r->name; - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { $data['class'] = $class->name; if (!$r->getClosureThis()) { $data['static'] = true; @@ -382,7 +385,7 @@ private function getCallableData(mixed $callable): array if (method_exists($callable, '__invoke')) { $data['type'] = 'object'; - $data['name'] = \get_class($callable); + $data['name'] = $callable::class; return $data; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php index 111578a84b1d2..836f06742cd6b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/MarkdownDescriptor.php @@ -51,7 +51,7 @@ protected function describeRoute(Route $route, array $options = []) ."\n".'- Host Regex: '.('' !== $route->getHost() ? $route->compile()->getHostRegex() : '') ."\n".'- Scheme: '.($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY') ."\n".'- Method: '.($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY') - ."\n".'- Class: '.\get_class($route) + ."\n".'- Class: '.$route::class ."\n".'- Defaults: '.$this->formatRouterConfig($route->getDefaults()) ."\n".'- Requirements: '.($route->getRequirements() ? $this->formatRouterConfig($route->getRequirements()) : 'NO CUSTOM') ."\n".'- Options: '.$this->formatRouterConfig($route->getOptions()); @@ -101,7 +101,7 @@ protected function describeContainerService(object $service, array $options = [] } elseif ($service instanceof Definition) { $this->describeContainerDefinition($service, $childOptions, $builder); } else { - $this->write(sprintf('**`%s`:** `%s`', $options['id'], \get_class($service))); + $this->write(sprintf('**`%s`:** `%s`', $options['id'], $service::class)); } } @@ -162,6 +162,9 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o if ($service instanceof Alias) { $services['aliases'][$serviceId] = $service; } elseif ($service instanceof Definition) { + if ($service->hasTag('container.excluded')) { + continue; + } $services['definitions'][$serviceId] = $service; } else { $services['services'][$serviceId] = $service; @@ -188,7 +191,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $this->write("\n\nServices\n--------\n"); foreach ($services['services'] as $id => $service) { $this->write("\n"); - $this->write(sprintf('- `%s`: `%s`', $id, \get_class($service))); + $this->write(sprintf('- `%s`: `%s`', $id, $service::class)); } } } @@ -231,7 +234,7 @@ protected function describeContainerDefinition(Definition $definition, array $op if ($factory[0] instanceof Reference) { $output .= "\n".'- Factory Service: `'.$factory[0].'`'; } elseif ($factory[0] instanceof Definition) { - throw new \InvalidArgumentException('Factory is not describable.'); + $output .= "\n".sprintf('- Factory Service: inline factory service (%s)', $factory[0]->getClass() ? sprintf('`%s`', $factory[0]->getClass()) : 'not configured'); } else { $output .= "\n".'- Factory Class: `'.$factory[0].'`'; } @@ -387,7 +390,7 @@ protected function describeCallable(mixed $callable, array $options = []) } $string .= "\n".sprintf('- Name: `%s`', $r->name); - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { $string .= "\n".sprintf('- Class: `%s`', $class->name); if (!$r->getClosureThis()) { $string .= "\n- Static: yes"; @@ -399,7 +402,7 @@ protected function describeCallable(mixed $callable, array $options = []) if (method_exists($callable, '__invoke')) { $string .= "\n- Type: `object`"; - $string .= "\n".sprintf('- Name: `%s`', \get_class($callable)); + $string .= "\n".sprintf('- Name: `%s`', $callable::class); return $this->write($string."\n"); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php index 67d09b7a65b9d..8f6ef7e9a420e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/TextDescriptor.php @@ -98,7 +98,7 @@ protected function describeRoute(Route $route, array $options = []) ['Scheme', $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY'], ['Method', $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY'], ['Requirements', $route->getRequirements() ? $this->formatRouterConfig($route->getRequirements()) : 'NO CUSTOM'], - ['Class', \get_class($route)], + ['Class', $route::class], ['Defaults', $this->formatRouterConfig($defaults)], ['Options', $this->formatRouterConfig($route->getOptions())], ]; @@ -156,7 +156,7 @@ protected function describeContainerService(object $service, array $options = [] $options['output']->table( ['Service ID', 'Class'], [ - [$options['id'] ?? '-', \get_class($service)], + [$options['id'] ?? '-', $service::class], ] ); } @@ -198,6 +198,10 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o } if ($definition instanceof Definition) { + if ($definition->hasTag('container.excluded')) { + unset($serviceIds[$key]); + continue; + } if ($showTag) { $tags = $definition->getTag($showTag); foreach ($tags as $tag) { @@ -244,7 +248,7 @@ protected function describeContainerServices(ContainerBuilder $builder, array $o $alias = $definition; $tableRows[] = array_merge([$styledServiceId, sprintf('alias for "%s"', $alias)], $tagsCount ? array_fill(0, $tagsCount, '') : []); } else { - $tableRows[] = array_merge([$styledServiceId, \get_class($definition)], $tagsCount ? array_fill(0, $tagsCount, '') : []); + $tableRows[] = array_merge([$styledServiceId, $definition::class], $tagsCount ? array_fill(0, $tagsCount, '') : []); } } @@ -315,7 +319,7 @@ protected function describeContainerDefinition(Definition $definition, array $op if ($factory[0] instanceof Reference) { $tableRows[] = ['Factory Service', $factory[0]]; } elseif ($factory[0] instanceof Definition) { - throw new \InvalidArgumentException('Factory is not describable.'); + $tableRows[] = ['Factory Service', sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'class not configured')]; } else { $tableRows[] = ['Factory Class', $factory[0]]; } @@ -361,7 +365,7 @@ protected function describeContainerDefinition(Definition $definition, array $op } $inEdges = null !== $builder && isset($options['id']) ? $this->getServiceEdges($builder, $options['id']) : []; - $tableRows[] = ['Usages', $inEdges ? implode(', ', $inEdges) : 'none']; + $tableRows[] = ['Usages', $inEdges ? implode(\PHP_EOL, $inEdges) : 'none']; $options['output']->table($tableHeaders, $tableRows); } @@ -530,7 +534,7 @@ private function renderEventListenerTable(EventDispatcherInterface $eventDispatc private function formatRouterConfig(array $config): string { - if (empty($config)) { + if (!$config) { return 'NONE'; } @@ -617,7 +621,7 @@ private function formatCallable(mixed $callable): string if (str_contains($r->name, '{closure}')) { return 'Closure()'; } - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { return sprintf('%s::%s()', $class->name, $r->name); } @@ -625,7 +629,7 @@ private function formatCallable(mixed $callable): string } if (method_exists($callable, '__invoke')) { - return sprintf('%s::__invoke()', \get_class($callable)); + return sprintf('%s::__invoke()', $callable::class); } throw new \InvalidArgumentException('Callable is not describable.'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php index 414c526603d6d..ecb9b01b776a7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Console/Descriptor/XmlDescriptor.php @@ -164,7 +164,7 @@ private function getRouteDocument(Route $route, string $name = null): \DOMDocume $routeXML->setAttribute('name', $name); } - $routeXML->setAttribute('class', \get_class($route)); + $routeXML->setAttribute('class', $route::class); $routeXML->appendChild($pathXML = $dom->createElement('path')); $pathXML->setAttribute('regex', $route->compile()->getRegex()); @@ -269,7 +269,7 @@ private function getContainerServiceDocument(object $service, string $id, Contai } else { $dom->appendChild($serviceXML = $dom->createElement('service')); $serviceXML->setAttribute('id', $id); - $serviceXML->setAttribute('class', \get_class($service)); + $serviceXML->setAttribute('class', $service::class); } return $dom; @@ -294,6 +294,10 @@ private function getContainerServicesDocument(ContainerBuilder $builder, string continue; } + if ($service instanceof Definition && $service->hasTag('container.excluded')) { + continue; + } + $serviceXML = $this->getContainerServiceDocument($service, $serviceId, null, $showArguments); $containerXML->appendChild($containerXML->ownerDocument->importNode($serviceXML->childNodes->item(0), true)); } @@ -324,7 +328,7 @@ private function getContainerDefinitionDocument(Definition $definition, string $ if ($factory[0] instanceof Reference) { $factoryXML->setAttribute('service', (string) $factory[0]); } elseif ($factory[0] instanceof Definition) { - throw new \InvalidArgumentException('Factory is not describable.'); + $factoryXML->setAttribute('service', sprintf('inline factory service (%s)', $factory[0]->getClass() ?? 'not configured')); } else { $factoryXML->setAttribute('class', $factory[0]); } @@ -565,7 +569,7 @@ private function getCallableDocument(mixed $callable): \DOMDocument } $callableXML->setAttribute('name', $r->name); - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { $callableXML->setAttribute('class', $class->name); if (!$r->getClosureThis()) { $callableXML->setAttribute('static', 'true'); @@ -577,7 +581,7 @@ private function getCallableDocument(mixed $callable): \DOMDocument if (method_exists($callable, '__invoke')) { $callableXML->setAttribute('type', 'object'); - $callableXML->setAttribute('name', \get_class($callable)); + $callableXML->setAttribute('name', $callable::class); return $dom; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php index da14b738b3b97..d195d20ce0353 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php @@ -215,7 +215,7 @@ protected function denyAccessUnlessGranted(mixed $attribute, mixed $subject = nu { if (!$this->isGranted($attribute, $subject)) { $exception = $this->createAccessDeniedException($message); - $exception->setAttributes($attribute); + $exception->setAttributes([$attribute]); $exception->setSubject($subject); throw $exception; @@ -251,10 +251,7 @@ protected function renderView(string $view, array $parameters = []): string protected function render(string $view, array $parameters = [], Response $response = null): Response { $content = $this->renderView($view, $parameters); - - if (null === $response) { - $response = new Response(); - } + $response ??= new Response(); if (200 === $response->getStatusCode()) { foreach ($parameters as $v) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php index 0539c1ee734ca..2debdbb1629bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php @@ -21,9 +21,6 @@ */ class ControllerResolver extends ContainerControllerResolver { - /** - * {@inheritdoc} - */ protected function instantiateController(string $class): object { $controller = parent::instantiateController($class); diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php index 992fc802231fc..f5f42a7f77c8c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php @@ -124,9 +124,7 @@ public function urlRedirectAction(Request $request, string $path, bool $permanen return new RedirectResponse($path, $statusCode); } - if (null === $scheme) { - $scheme = $request->getScheme(); - } + $scheme ??= $request->getScheme(); if ($qs = $request->server->get('QUERY_STRING') ?: $request->getQueryString()) { if (!str_contains($path, '?')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php index d7db6ebf050a4..fbbf53d3431f2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php @@ -20,9 +20,6 @@ */ class AddAnnotationsCachedReaderPass implements CompilerPassInterface { - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { // "annotations.cached_reader" is wired late so that any passes using diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php index 3e2f2768edc1a..f7a6bcdbd710a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php @@ -22,9 +22,6 @@ */ class AddExpressionLanguageProvidersPass implements CompilerPassInterface { - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { // routing diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/EnableLoggerDebugModePass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/EnableLoggerDebugModePass.php new file mode 100644 index 0000000000000..3dad9d6b24031 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/EnableLoggerDebugModePass.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Log\Logger; + +final class EnableLoggerDebugModePass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('profiler') || !$container->hasDefinition('logger')) { + return; + } + + $loggerDefinition = $container->getDefinition('logger'); + + if (Logger::class === $loggerDefinition->getClass()) { + $loggerDefinition->setConfigurator([__CLASS__, 'configureLogger']); + } + } + + public static function configureLogger(Logger $logger): void + { + if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) && method_exists($logger, 'enableDebug')) { + $logger->enableDebug(); + } + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php index 222b5c7b75af0..942eb635b26f3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/TestServiceContainerRealRefPass.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -38,6 +39,16 @@ public function process(ContainerBuilder $container) } } + foreach ($container->getAliases() as $id => $target) { + while ($container->hasAlias($target = (string) $target)) { + $target = $container->getAlias($target); + } + + if ($definitions[$target]->hasTag('container.private')) { + $privateServices[$id] = new ServiceClosureArgument(new Reference($target)); + } + } + $privateContainer->replaceArgument(0, $privateServices); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index 02b59afad5e61..1dcee2d998a56 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -81,7 +81,6 @@ class UnusedTagsPass implements CompilerPassInterface 'routing.route_loader', 'security.authenticator.login_linker', 'security.expression_language_provider', - 'security.remember_me_aware', 'security.remember_me_handler', 'security.voter', 'serializer.encoder', @@ -89,6 +88,7 @@ class UnusedTagsPass implements CompilerPassInterface 'texter.transport_factory', 'translation.dumper', 'translation.extractor', + 'translation.extractor.visitor', 'translation.loader', 'translation.provider_factory', 'twig.extension', @@ -124,7 +124,7 @@ public function process(ContainerBuilder $container) $services = array_keys($container->findTaggedServiceIds($tag)); $message = sprintf('Tag "%s" was defined on service(s) "%s", but was never used.', $tag, implode('", "', $services)); - if (!empty($candidates)) { + if ($candidates) { $message .= sprintf(' Did you mean "%s"?', implode('", "', $candidates)); } diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php index ad62e19384976..d74fa11b2ae6b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php @@ -21,9 +21,6 @@ */ class WorkflowGuardListenerPass implements CompilerPassInterface { - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if (!$container->hasParameter('workflow.has_guard_listeners')) { diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 4e6dec875faf1..991fccbe165b9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -133,7 +133,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('error_controller') ->defaultValue('error_controller') ->end() - ->booleanNode('catch_all_throwables')->defaultFalse()->info('HttpKernel will catch all kinds of \Throwable')->end() + ->booleanNode('handle_all_throwables')->info('HttpKernel will handle all kinds of \Throwable')->end() ->end() ; @@ -161,9 +161,9 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addRequestSection($rootNode); $this->addAssetsSection($rootNode, $enableIfStandalone); $this->addTranslatorSection($rootNode, $enableIfStandalone); - $this->addValidationSection($rootNode, $enableIfStandalone, $willBeAvailable); + $this->addValidationSection($rootNode, $enableIfStandalone); $this->addAnnotationsSection($rootNode, $willBeAvailable); - $this->addSerializerSection($rootNode, $enableIfStandalone, $willBeAvailable); + $this->addSerializerSection($rootNode, $enableIfStandalone); $this->addPropertyAccessSection($rootNode, $willBeAvailable); $this->addPropertyInfoSection($rootNode, $enableIfStandalone); $this->addCacheSection($rootNode, $willBeAvailable); @@ -237,8 +237,9 @@ private function addFormSection(ArrayNodeDefinition $rootNode, callable $enableI ->scalarNode('field_name')->defaultValue('_token')->end() ->end() ->end() - // to be deprecated in Symfony 6.1 - ->booleanNode('legacy_error_messages')->end() + ->booleanNode('legacy_error_messages') + ->setDeprecated('symfony/framework-bundle', '6.2') + ->end() ->end() ->end() ->end() @@ -268,6 +269,7 @@ private function addHttpCacheSection(ArrayNodeDefinition $rootNode) ->booleanNode('allow_revalidate')->end() ->integerNode('stale_while_revalidate')->end() ->integerNode('stale_if_error')->end() + ->booleanNode('terminate_on_cache_hit')->end() ->end() ->end() ->end() @@ -350,13 +352,13 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) $workflows = []; } - if (1 === \count($workflows) && isset($workflows['workflows']) && !array_is_list($workflows['workflows']) && !empty(array_diff(array_keys($workflows['workflows']), ['audit_trail', 'type', 'marking_store', 'supports', 'support_strategy', 'initial_marking', 'places', 'transitions']))) { + if (1 === \count($workflows) && isset($workflows['workflows']) && !array_is_list($workflows['workflows']) && array_diff(array_keys($workflows['workflows']), ['audit_trail', 'type', 'marking_store', 'supports', 'support_strategy', 'initial_marking', 'places', 'transitions'])) { $workflows = $workflows['workflows']; } foreach ($workflows as $key => $workflow) { if (isset($workflow['enabled']) && false === $workflow['enabled']) { - throw new LogicException(sprintf('Cannot disable a single workflow. Remove the configuration for the workflow "%s" instead.', $workflow['name'])); + throw new LogicException(sprintf('Cannot disable a single workflow. Remove the configuration for the workflow "%s" instead.', $key)); } unset($workflows[$key]['enabled']); @@ -452,6 +454,10 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->beforeNormalization() ->always() ->then(function ($places) { + if (!\is_array($places)) { + throw new InvalidConfigurationException('The "places" option must be an array in workflow configuration.'); + } + // It's an indexed array of shape ['place1', 'place2'] if (isset($places[0]) && \is_string($places[0])) { return array_map(function (string $place) { @@ -497,6 +503,10 @@ private function addWorkflowSection(ArrayNodeDefinition $rootNode) ->beforeNormalization() ->always() ->then(function ($transitions) { + if (!\is_array($transitions)) { + throw new InvalidConfigurationException('The "transitions" option must be an array in workflow configuration.'); + } + // It's an indexed array, we let the validation occur if (isset($transitions[0]) && \is_array($transitions[0])) { return $transitions; @@ -606,6 +616,7 @@ private function addRouterSection(ArrayNodeDefinition $rootNode) ->children() ->scalarNode('resource')->isRequired()->end() ->scalarNode('type')->end() + ->scalarNode('cache_dir')->defaultValue('%kernel.cache_dir%')->end() ->scalarNode('default_uri') ->info('The default URI used to generate URLs in a non-HTTP context') ->defaultNull() @@ -869,7 +880,7 @@ private function addTranslatorSection(ArrayNodeDefinition $rootNode, callable $e ; } - private function addValidationSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone, callable $willBeAvailable) + private function addValidationSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone) { $rootNode ->children() @@ -979,7 +990,7 @@ private function addAnnotationsSection(ArrayNodeDefinition $rootNode, callable $ ; } - private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone, callable $willBeAvailable) + private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone) { $rootNode ->children() @@ -1113,7 +1124,7 @@ private function addCacheSection(ArrayNodeDefinition $rootNode, callable $willBe ->booleanNode('public')->defaultFalse()->end() ->scalarNode('default_lifetime') ->info('Default lifetime of the pool') - ->example('"600" for 5 minutes expressed in seconds, "PT5M" for five minutes expressed as ISO 8601 time interval, or "5 minutes" as a date expression') + ->example('"300" for 5 minutes expressed in seconds, "PT5M" for five minutes expressed as ISO 8601 time interval, or "5 minutes" as a date expression') ->end() ->scalarNode('provider') ->info('Overwrite the setting from the default provider for this adapter.') @@ -1186,48 +1197,48 @@ private function addExceptionsSection(ArrayNodeDefinition $rootNode) $logLevels = (new \ReflectionClass(LogLevel::class))->getConstants(); $rootNode + ->fixXmlConfig('exception') ->children() ->arrayNode('exceptions') ->info('Exception handling configuration') + ->useAttributeAsKey('class') ->beforeNormalization() + // Handle legacy XML configuration ->ifArray() ->then(function (array $v): array { if (!\array_key_exists('exception', $v)) { return $v; } - // Fix XML normalization - $data = isset($v['exception'][0]) ? $v['exception'] : [$v['exception']]; - $exceptions = []; - foreach ($data as $exception) { - $config = []; - if (\array_key_exists('log-level', $exception)) { - $config['log_level'] = $exception['log-level']; - } - if (\array_key_exists('status-code', $exception)) { - $config['status_code'] = $exception['status-code']; - } - $exceptions[$exception['name']] = $config; + $v = $v['exception']; + unset($v['exception']); + + foreach ($v as &$exception) { + $exception['class'] = $exception['name']; + unset($exception['name']); } - return $exceptions; + return $v; }) ->end() ->prototype('array') - ->fixXmlConfig('exception') ->children() ->scalarNode('log_level') ->info('The level of log message. Null to let Symfony decide.') ->validate() - ->ifTrue(function ($v) use ($logLevels) { return !\in_array($v, $logLevels); }) + ->ifTrue(function ($v) use ($logLevels) { return null !== $v && !\in_array($v, $logLevels, true); }) ->thenInvalid(sprintf('The log level is not valid. Pick one among "%s".', implode('", "', $logLevels))) ->end() ->defaultNull() ->end() ->scalarNode('status_code') - ->info('The status code of the response. Null to let Symfony decide.') + ->info('The status code of the response. Null or 0 to let Symfony decide.') + ->beforeNormalization() + ->ifTrue(function ($v) { return 0 === $v; }) + ->then(function ($v) { return null; }) + ->end() ->validate() - ->ifTrue(function ($v) { return $v < 100 || $v > 599; }) + ->ifTrue(function ($v) { return null !== $v && ($v < 100 || $v > 599); }) ->thenInvalid('The status code is not valid. Pick a value between 100 and 599.') ->end() ->defaultNull() @@ -1263,12 +1274,15 @@ private function addLockSection(ArrayNodeDefinition $rootNode, callable $enableI }) ->end() ->addDefaultsIfNotSet() + ->validate() + ->ifTrue(static function (array $config) { return $config['enabled'] && !$config['resources']; }) + ->thenInvalid('At least one resource must be defined.') + ->end() ->fixXmlConfig('resource') ->children() ->arrayNode('resources') ->normalizeKeys(false) ->useAttributeAsKey('name') - ->requiresAtLeastOneElement() ->defaultValue(['default' => [class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphore' : 'flock']]) ->beforeNormalization() ->ifString()->then(function ($v) { return ['default' => $v]; }) @@ -1492,6 +1506,10 @@ function ($a) { ->integerNode('max_delay')->defaultValue(0)->min(0)->info('Max time in ms that a retry should ever be delayed (0 = infinite)')->end() ->end() ->end() + ->scalarNode('rate_limiter') + ->defaultNull() + ->info('Rate limiter name to use when processing messages') + ->end() ->end() ->end() ->end() @@ -1510,15 +1528,36 @@ function ($a) { ->end() ->scalarNode('default_bus')->defaultNull()->end() ->arrayNode('buses') - ->defaultValue(['messenger.bus.default' => ['default_middleware' => true, 'middleware' => []]]) + ->defaultValue(['messenger.bus.default' => ['default_middleware' => ['enabled' => true, 'allow_no_handlers' => false, 'allow_no_senders' => true], 'middleware' => []]]) ->normalizeKeys(false) ->useAttributeAsKey('name') ->arrayPrototype() ->addDefaultsIfNotSet() ->children() - ->enumNode('default_middleware') - ->values([true, false, 'allow_no_handlers']) - ->defaultTrue() + ->arrayNode('default_middleware') + ->beforeNormalization() + ->ifTrue(function ($defaultMiddleware) { return \is_string($defaultMiddleware) || \is_bool($defaultMiddleware); }) + ->then(function ($defaultMiddleware): array { + if (\is_string($defaultMiddleware) && 'allow_no_handlers' === $defaultMiddleware) { + return [ + 'enabled' => true, + 'allow_no_handlers' => true, + 'allow_no_senders' => true, + ]; + } + + return [ + 'enabled' => $defaultMiddleware, + 'allow_no_handlers' => false, + 'allow_no_senders' => true, + ]; + }) + ->end() + ->canBeDisabled() + ->children() + ->booleanNode('allow_no_handlers')->defaultFalse()->end() + ->booleanNode('allow_no_senders')->defaultTrue()->end() + ->end() ->end() ->arrayNode('middleware') ->performNoDeepMerging() @@ -2100,7 +2139,7 @@ private function addUidSection(ArrayNodeDefinition $rootNode, callable $enableIf ->children() ->enumNode('default_uuid_version') ->defaultValue(6) - ->values([6, 4, 1]) + ->values([7, 6, 4, 1]) ->end() ->enumNode('name_based_uuid_version') ->defaultValue(5) @@ -2111,7 +2150,7 @@ private function addUidSection(ArrayNodeDefinition $rootNode, callable $enableIf ->end() ->enumNode('time_based_uuid_version') ->defaultValue(6) - ->values([6, 1]) + ->values([7, 6, 1]) ->end() ->scalarNode('time_based_uuid_node') ->cannotBeEmpty() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index c5855b04cd992..48f89b369a3d6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -17,15 +17,16 @@ use Http\Client\HttpClient; use phpDocumentor\Reflection\DocBlockFactoryInterface; use phpDocumentor\Reflection\Types\ContextFactory; +use PhpParser\Parser; use PHPStan\PhpDocParser\Parser\PhpDocParser; use Psr\Cache\CacheItemPoolInterface; +use Psr\Clock\ClockInterface as PsrClockInterface; use Psr\Container\ContainerInterface as PsrContainerInterface; use Psr\Http\Client\ClientInterface; use Psr\Log\LoggerAwareInterface; use Symfony\Bridge\Monolog\Processor\DebugProcessor; use Symfony\Bridge\Twig\Extension\CsrfExtension; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; -use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader; use Symfony\Bundle\FrameworkBundle\Routing\RouteLoaderInterface; use Symfony\Bundle\FullStack; use Symfony\Bundle\MercureBundle\MercureBundle; @@ -87,16 +88,20 @@ use Symfony\Component\HttpKernel\Controller\ArgumentResolver\BackedEnumValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\UidValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\Lock\LockFactory; +use Symfony\Component\Lock\LockInterface; use Symfony\Component\Lock\PersistingStoreInterface; use Symfony\Component\Lock\Store\StoreFactory; use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory; use Symfony\Component\Mailer\Bridge\Google\Transport\GmailTransportFactory; +use Symfony\Component\Mailer\Bridge\Infobip\Transport\InfobipTransportFactory as InfobipMailerTransportFactory; use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillTransportFactory; use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; +use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory; use Symfony\Component\Mailer\Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; @@ -124,6 +129,7 @@ use Symfony\Component\Mime\MimeTypes; use Symfony\Component\Notifier\Bridge\AllMySms\AllMySmsTransportFactory; use Symfony\Component\Notifier\Bridge\AmazonSns\AmazonSnsTransportFactory; +use Symfony\Component\Notifier\Bridge\Chatwork\ChatworkTransportFactory; use Symfony\Component\Notifier\Bridge\Clickatell\ClickatellTransportFactory; use Symfony\Component\Notifier\Bridge\ContactEveryone\ContactEveryoneTransportFactory; use Symfony\Component\Notifier\Bridge\Discord\DiscordTransportFactory; @@ -192,8 +198,7 @@ use Symfony\Component\RateLimiter\LimiterInterface; use Symfony\Component\RateLimiter\RateLimiterFactory; use Symfony\Component\RateLimiter\Storage\CacheStorage; -use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; -use Symfony\Component\Routing\Loader\AnnotationFileLoader; +use Symfony\Component\Routing\Loader\Psr4DirectoryLoader; use Symfony\Component\Security\Core\AuthenticationEvents; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; @@ -216,11 +221,13 @@ use Symfony\Component\Translation\Bridge\Loco\LocoProviderFactory; use Symfony\Component\Translation\Bridge\Lokalise\LokaliseProviderFactory; use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand; +use Symfony\Component\Translation\Extractor\PhpAstExtractor; use Symfony\Component\Translation\LocaleSwitcher; use Symfony\Component\Translation\PseudoLocalizationTranslator; use Symfony\Component\Translation\Translator; use Symfony\Component\Uid\Factory\UuidFactory; use Symfony\Component\Uid\UuidV4; +use Symfony\Component\Validator\Constraints\WhenValidator; use Symfony\Component\Validator\ConstraintValidatorInterface; use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader; use Symfony\Component\Validator\ObjectInitializerInterface; @@ -245,18 +252,7 @@ */ class FrameworkExtension extends Extension { - private bool $formConfigEnabled = false; - private bool $translationConfigEnabled = false; - private bool $sessionConfigEnabled = false; - private bool $annotationsConfigEnabled = false; - private bool $validatorConfigEnabled = false; - private bool $messengerConfigEnabled = false; - private bool $mailerConfigEnabled = false; - private bool $httpClientConfigEnabled = false; - private bool $notifierConfigEnabled = false; - private bool $serializerConfigEnabled = false; - private bool $propertyAccessConfigEnabled = false; - private static bool $lockConfigEnabled = false; + private array $configsEnabled = []; /** * Responds to the app.config configuration parameter. @@ -284,6 +280,7 @@ public function load(array $configs, ContainerBuilder $container) if (!ContainerBuilder::willBeAvailable('symfony/clock', ClockInterface::class, ['symfony/framework-bundle'])) { $container->removeDefinition('clock'); $container->removeAlias(ClockInterface::class); + $container->removeAlias(PsrClockInterface::class); } $container->registerAliasForArgument('parameter_bag', PsrContainerInterface::class); @@ -309,14 +306,17 @@ public function load(array $configs, ContainerBuilder $container) $configuration = $this->getConfiguration($configs, $container); $config = $this->processConfiguration($configuration, $configs); - $this->annotationsConfigEnabled = $this->isConfigEnabled($container, $config['annotations']); - $this->translationConfigEnabled = $this->isConfigEnabled($container, $config['translator']); + // warmup config enabled + $this->readConfigEnabled('annotations', $container, $config['annotations']); + $this->readConfigEnabled('translator', $container, $config['translator']); + $this->readConfigEnabled('property_access', $container, $config['property_access']); + $this->readConfigEnabled('profiler', $container, $config['profiler']); // A translator must always be registered (as support is included by // default in the Form and Validator component). If disabled, an identity // translator will be used and everything will still work as expected. - if ($this->isConfigEnabled($container, $config['translator']) || $this->isConfigEnabled($container, $config['form']) || $this->isConfigEnabled($container, $config['validation'])) { - if (!class_exists(Translator::class) && $this->isConfigEnabled($container, $config['translator'])) { + if ($this->readConfigEnabled('translator', $container, $config['translator']) || $this->readConfigEnabled('form', $container, $config['form']) || $this->readConfigEnabled('validation', $container, $config['validation'])) { + if (!class_exists(Translator::class) && $this->readConfigEnabled('translator', $container, $config['translator'])) { throw new LogicException('Translation support cannot be enabled as the Translation component is not installed. Try running "composer require symfony/translation".'); } @@ -327,7 +327,7 @@ public function load(array $configs, ContainerBuilder $container) $container->getDefinition('locale_listener')->replaceArgument(3, $config['set_locale_from_accept_language']); $container->getDefinition('response_listener')->replaceArgument(1, $config['set_content_language_from_locale']); - $container->getDefinition('http_kernel')->replaceArgument(4, $config['catch_all_throwables']); + $container->getDefinition('http_kernel')->replaceArgument(4, $config['handle_all_throwables'] ?? false); // If the slugger is used but the String component is not available, we should throw an error if (!ContainerBuilder::willBeAvailable('symfony/string', SluggerInterface::class, ['symfony/framework-bundle'])) { @@ -372,11 +372,11 @@ public function load(array $configs, ContainerBuilder $container) } } - if ($this->isConfigEnabled($container, $config['request'])) { + if ($this->readConfigEnabled('request', $container, $config['request'])) { $this->registerRequestConfiguration($config['request'], $container, $loader); } - if ($this->isConfigEnabled($container, $config['assets'])) { + if ($this->readConfigEnabled('assets', $container, $config['assets'])) { if (!class_exists(\Symfony\Component\Asset\Package::class)) { throw new LogicException('Asset support cannot be enabled as the Asset component is not installed. Try running "composer require symfony/asset".'); } @@ -384,19 +384,19 @@ public function load(array $configs, ContainerBuilder $container) $this->registerAssetsConfiguration($config['assets'], $container, $loader); } - if ($this->httpClientConfigEnabled = $this->isConfigEnabled($container, $config['http_client'])) { - $this->registerHttpClientConfiguration($config['http_client'], $container, $loader, $config['profiler']); + if ($this->readConfigEnabled('http_client', $container, $config['http_client'])) { + $this->registerHttpClientConfiguration($config['http_client'], $container, $loader); } - if ($this->mailerConfigEnabled = $this->isConfigEnabled($container, $config['mailer'])) { + if ($this->readConfigEnabled('mailer', $container, $config['mailer'])) { $this->registerMailerConfiguration($config['mailer'], $container, $loader); - } - if (!$this->mailerConfigEnabled || !class_exists(MailerTestCommand::class)) { - $container->removeDefinition('console.command.mailer_test'); + if (!$this->hasConsole() || !class_exists(MailerTestCommand::class)) { + $container->removeDefinition('console.command.mailer_test'); + } } - $propertyInfoEnabled = $this->isConfigEnabled($container, $config['property_info']); + $propertyInfoEnabled = $this->readConfigEnabled('property_info', $container, $config['property_info']); $this->registerHttpCacheConfiguration($config['http_cache'], $container, $config['http_method_override']); $this->registerEsiConfiguration($config['esi'], $container, $loader); $this->registerSsiConfiguration($config['ssi'], $container, $loader); @@ -411,7 +411,7 @@ public function load(array $configs, ContainerBuilder $container) $container->getDefinition('exception_listener')->replaceArgument(3, $config['exceptions']); - if ($this->serializerConfigEnabled = $this->isConfigEnabled($container, $config['serializer'])) { + if ($this->readConfigEnabled('serializer', $container, $config['serializer'])) { if (!class_exists(\Symfony\Component\Serializer\Serializer::class)) { throw new LogicException('Serializer support cannot be enabled as the Serializer component is not installed. Try running "composer require symfony/serializer-pack".'); } @@ -423,15 +423,15 @@ public function load(array $configs, ContainerBuilder $container) $this->registerPropertyInfoConfiguration($container, $loader); } - if (self::$lockConfigEnabled = $this->isConfigEnabled($container, $config['lock'])) { + if ($this->readConfigEnabled('lock', $container, $config['lock'])) { $this->registerLockConfiguration($config['lock'], $container, $loader); } - if ($this->isConfigEnabled($container, $config['semaphore'])) { + if ($this->readConfigEnabled('semaphore', $container, $config['semaphore'])) { $this->registerSemaphoreConfiguration($config['semaphore'], $container, $loader); } - if ($this->isConfigEnabled($container, $config['rate_limiter'])) { + if ($this->readConfigEnabled('rate_limiter', $container, $config['rate_limiter'])) { if (!interface_exists(LimiterInterface::class)) { throw new LogicException('Rate limiter support cannot be enabled as the RateLimiter component is not installed. Try running "composer require symfony/rate-limiter".'); } @@ -439,7 +439,7 @@ public function load(array $configs, ContainerBuilder $container) $this->registerRateLimiterConfiguration($config['rate_limiter'], $container, $loader); } - if ($this->isConfigEnabled($container, $config['web_link'])) { + if ($this->readConfigEnabled('web_link', $container, $config['web_link'])) { if (!class_exists(HttpHeaderSerializer::class)) { throw new LogicException('WebLink support cannot be enabled as the WebLink component is not installed. Try running "composer require symfony/weblink".'); } @@ -447,7 +447,7 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('web_link.php'); } - if ($this->isConfigEnabled($container, $config['uid'])) { + if ($this->readConfigEnabled('uid', $container, $config['uid'])) { if (!class_exists(UuidFactory::class)) { throw new LogicException('Uid support cannot be enabled as the Uid component is not installed. Try running "composer require symfony/uid".'); } @@ -460,12 +460,11 @@ public function load(array $configs, ContainerBuilder $container) // register cache before session so both can share the connection services $this->registerCacheConfiguration($config['cache'], $container); - if ($this->isConfigEnabled($container, $config['session'])) { + if ($this->readConfigEnabled('session', $container, $config['session'])) { if (!\extension_loaded('session')) { throw new LogicException('Session support cannot be enabled as the session extension is not installed. See https://php.net/session.installation for instructions.'); } - $this->sessionConfigEnabled = true; $this->registerSessionConfiguration($config['session'], $container, $loader); if (!empty($config['test'])) { // test listener will replace the existing session listener @@ -478,28 +477,27 @@ public function load(array $configs, ContainerBuilder $container) // csrf depends on session being registered if (null === $config['csrf_protection']['enabled']) { - $config['csrf_protection']['enabled'] = $this->sessionConfigEnabled && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']); + $this->writeConfigEnabled('csrf_protection', $this->readConfigEnabled('session', $container, $config['session']) && !class_exists(FullStack::class) && ContainerBuilder::willBeAvailable('symfony/security-csrf', CsrfTokenManagerInterface::class, ['symfony/framework-bundle']), $config['csrf_protection']); } $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader); // form depends on csrf being registered - if ($this->isConfigEnabled($container, $config['form'])) { + if ($this->readConfigEnabled('form', $container, $config['form'])) { if (!class_exists(Form::class)) { throw new LogicException('Form support cannot be enabled as the Form component is not installed. Try running "composer require symfony/form".'); } - $this->formConfigEnabled = true; $this->registerFormConfiguration($config, $container, $loader); if (ContainerBuilder::willBeAvailable('symfony/validator', Validation::class, ['symfony/framework-bundle', 'symfony/form'])) { - $config['validation']['enabled'] = true; + $this->writeConfigEnabled('validation', true, $config['validation']); } else { $container->setParameter('validator.translation_domain', 'validators'); $container->removeDefinition('form.type_extension.form.validator'); $container->removeDefinition('form.type_guesser.validator'); } - if (!$this->isConfigEnabled($container, $config['html_sanitizer']) || !class_exists(TextTypeHtmlSanitizerExtension::class)) { + if (!$this->readConfigEnabled('html_sanitizer', $container, $config['html_sanitizer']) || !class_exists(TextTypeHtmlSanitizerExtension::class)) { $container->removeDefinition('form.type_extension.form.html_sanitizer'); } } else { @@ -510,7 +508,7 @@ public function load(array $configs, ContainerBuilder $container) $this->registerValidationConfiguration($config['validation'], $container, $loader, $propertyInfoEnabled); // messenger depends on validation being registered - if ($this->messengerConfigEnabled = $this->isConfigEnabled($container, $config['messenger'])) { + if ($this->readConfigEnabled('messenger', $container, $config['messenger'])) { $this->registerMessengerConfiguration($config['messenger'], $container, $loader, $config['validation']); } else { $container->removeDefinition('console.command.messenger_consume_messages'); @@ -545,14 +543,14 @@ public function load(array $configs, ContainerBuilder $container) } // notifier depends on messenger, mailer being registered - if ($this->notifierConfigEnabled = $this->isConfigEnabled($container, $config['notifier'])) { + if ($this->readConfigEnabled('notifier', $container, $config['notifier'])) { $this->registerNotifierConfiguration($config['notifier'], $container, $loader); } // profiler depends on form, validation, translation, messenger, mailer, http-client, notifier, serializer being registered $this->registerProfilerConfiguration($config['profiler'], $container, $loader); - if ($this->isConfigEnabled($container, $config['html_sanitizer'])) { + if ($this->readConfigEnabled('html_sanitizer', $container, $config['html_sanitizer'])) { if (!class_exists(HtmlSanitizerConfig::class)) { throw new LogicException('HtmlSanitizer support cannot be enabled as the HtmlSanitizer component is not installed. Try running "composer require symfony/html-sanitizer".'); } @@ -590,6 +588,8 @@ public function load(array $configs, ContainerBuilder $container) ->addTag('container.service_subscriber'); $container->registerForAutoconfiguration(ArgumentValueResolverInterface::class) ->addTag('controller.argument_value_resolver'); + $container->registerForAutoconfiguration(ValueResolverInterface::class) + ->addTag('controller.argument_value_resolver'); $container->registerForAutoconfiguration(AbstractController::class) ->addTag('controller.service_arguments'); $container->registerForAutoconfiguration(DataCollectorInterface::class) @@ -702,9 +702,6 @@ public function load(array $configs, ContainerBuilder $container) ]); } - /** - * {@inheritdoc} - */ public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface { return new Configuration($container->getParameter('kernel.debug')); @@ -720,10 +717,10 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont $loader->load('form.php'); if (null === $config['form']['csrf_protection']['enabled']) { - $config['form']['csrf_protection']['enabled'] = $config['csrf_protection']['enabled']; + $this->writeConfigEnabled('form.csrf_protection', $config['csrf_protection']['enabled'], $config['form']['csrf_protection']); } - if ($this->isConfigEnabled($container, $config['form']['csrf_protection'])) { + if ($this->readConfigEnabled('form.csrf_protection', $container, $config['form']['csrf_protection'])) { if (!$container->hasDefinition('security.csrf.token_generator')) { throw new \LogicException('To use form CSRF protection, "framework.csrf_protection" must be enabled.'); } @@ -769,7 +766,7 @@ private function registerHttpCacheConfiguration(array $config, ContainerBuilder private function registerEsiConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) { - if (!$this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('esi', $container, $config)) { $container->removeDefinition('fragment.renderer.esi'); return; @@ -780,7 +777,7 @@ private function registerEsiConfiguration(array $config, ContainerBuilder $conta private function registerSsiConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) { - if (!$this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('ssi', $container, $config)) { $container->removeDefinition('fragment.renderer.ssi'); return; @@ -791,7 +788,7 @@ private function registerSsiConfiguration(array $config, ContainerBuilder $conta private function registerFragmentsConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) { - if (!$this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('fragments', $container, $config)) { $container->removeDefinition('fragment.renderer.hinclude'); return; @@ -805,7 +802,7 @@ private function registerFragmentsConfiguration(array $config, ContainerBuilder private function registerProfilerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) { - if (!$this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('profiler', $container, $config)) { // this is needed for the WebProfiler to work even if the profiler is disabled $container->setParameter('data_collector.templates', []); @@ -816,37 +813,37 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ $loader->load('collectors.php'); $loader->load('cache_debug.php'); - if ($this->formConfigEnabled) { + if ($this->isInitializedConfigEnabled('form')) { $loader->load('form_debug.php'); } - if ($this->validatorConfigEnabled) { + if ($this->isInitializedConfigEnabled('validation')) { $loader->load('validator_debug.php'); } - if ($this->translationConfigEnabled) { + if ($this->isInitializedConfigEnabled('translator')) { $loader->load('translation_debug.php'); $container->getDefinition('translator.data_collector')->setDecoratedService('translator'); } - if ($this->messengerConfigEnabled) { + if ($this->isInitializedConfigEnabled('messenger')) { $loader->load('messenger_debug.php'); } - if ($this->mailerConfigEnabled) { + if ($this->isInitializedConfigEnabled('mailer')) { $loader->load('mailer_debug.php'); } - if ($this->httpClientConfigEnabled) { + if ($this->isInitializedConfigEnabled('http_client')) { $loader->load('http_client_debug.php'); } - if ($this->notifierConfigEnabled) { + if ($this->isInitializedConfigEnabled('notifier')) { $loader->load('notifier_debug.php'); } - if ($this->serializerConfigEnabled && $config['collect_serializer_data']) { + if ($this->isInitializedConfigEnabled('serializer') && $config['collect_serializer_data']) { $loader->load('serializer_debug.php'); } @@ -885,8 +882,6 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $registryDefinition = $container->getDefinition('.workflow.registry'); - $workflow = []; - foreach ($config['workflows'] as $name => $workflow) { $type = $workflow['type']; $workflowId = sprintf('%s.%s', $type, $name); @@ -974,9 +969,8 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $definitionDefinition->addArgument($initialMarking); $definitionDefinition->addArgument(new Reference(sprintf('%s.metadata_store', $workflowId))); - $workflows[$workflowId] = $definitionDefinition; - // Create MarkingStore + $markingStoreDefinition = null; if (isset($workflow['marking_store']['type'])) { $markingStoreDefinition = new ChildDefinition('workflow.marking_store.method'); $markingStoreDefinition->setArguments([ @@ -990,7 +984,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ // Create Workflow $workflowDefinition = new ChildDefinition(sprintf('%s.abstract', $type)); $workflowDefinition->replaceArgument(0, new Reference(sprintf('%s.definition', $workflowId))); - $workflowDefinition->replaceArgument(1, $markingStoreDefinition ?? null); + $workflowDefinition->replaceArgument(1, $markingStoreDefinition); $workflowDefinition->replaceArgument(3, $name); $workflowDefinition->replaceArgument(4, $workflow['events_to_dispatch']); @@ -1115,7 +1109,7 @@ private function registerDebugConfiguration(array $config, ContainerBuilder $con private function registerRouterConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, array $enabledLocales = []) { - if (!$this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('router', $container, $config)) { $container->removeDefinition('console.command.router_debug'); $container->removeDefinition('console.command.router_match'); $container->removeDefinition('messenger.middleware.router_context'); @@ -1142,6 +1136,7 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co } $container->setParameter('router.resource', $config['resource']); + $container->setParameter('router.cache_dir', $config['cache_dir']); $router = $container->findDefinition('router.default'); $argument = $router->getArgument(2); $argument['strict_requirements'] = $config['strict_requirements']; @@ -1158,29 +1153,9 @@ private function registerRouterConfiguration(array $config, ContainerBuilder $co ->replaceArgument(0, $config['default_uri']); } - $container->register('routing.loader.annotation', AnnotatedRouteControllerLoader::class) - ->setPublic(false) - ->addTag('routing.loader', ['priority' => -10]) - ->setArguments([ - new Reference('annotation_reader', ContainerInterface::NULL_ON_INVALID_REFERENCE), - '%kernel.environment%', - ]); - - $container->register('routing.loader.annotation.directory', AnnotationDirectoryLoader::class) - ->setPublic(false) - ->addTag('routing.loader', ['priority' => -10]) - ->setArguments([ - new Reference('file_locator'), - new Reference('routing.loader.annotation'), - ]); - - $container->register('routing.loader.annotation.file', AnnotationFileLoader::class) - ->setPublic(false) - ->addTag('routing.loader', ['priority' => -10]) - ->setArguments([ - new Reference('file_locator'), - new Reference('routing.loader.annotation'), - ]); + if (!class_exists(Psr4DirectoryLoader::class)) { + $container->removeDefinition('routing.loader.psr4'); + } } private function registerSessionConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) @@ -1206,11 +1181,8 @@ private function registerSessionConfiguration(array $config, ContainerBuilder $c // session handler (the internal callback registered with PHP session management) if (null === $config['handler_id']) { - // Set the handler class to be null - $container->getDefinition('session.storage.factory.native')->replaceArgument(1, null); - $container->getDefinition('session.storage.factory.php_bridge')->replaceArgument(0, null); - - $container->setAlias('session.handler', 'session.handler.native_file'); + $config['save_path'] = null; + $container->setAlias('session.handler', 'session.handler.native'); } else { $container->resolveEnvPlaceholders($config['handler_id'], null, $usedEnvs); @@ -1321,7 +1293,7 @@ private function createVersion(ContainerBuilder $container, ?string $version, ?s private function registerTranslatorConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader, string $defaultLocale, array $enabledLocales) { - if (!$this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('translator', $container, $config)) { $container->removeDefinition('console.command.translation_debug'); $container->removeDefinition('console.command.translation_extract'); $container->removeDefinition('console.command.translation_pull'); @@ -1336,6 +1308,13 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder $container->removeDefinition('translation.locale_switcher'); } + // don't use ContainerBuilder::willBeAvailable() as these are not needed in production + if (interface_exists(Parser::class) && class_exists(PhpAstExtractor::class)) { + $container->removeDefinition('translation.extractor.php'); + } else { + $container->removeDefinition('translation.extractor.php_ast'); + } + $loader->load('translation_providers.php'); // Use the "real" translator instead of the identity default @@ -1509,7 +1488,7 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder private function registerValidationConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, bool $propertyInfoEnabled) { - if (!$this->validatorConfigEnabled = $this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('validation', $container, $config)) { $container->removeDefinition('console.command.validator_debug'); return; @@ -1545,7 +1524,7 @@ private function registerValidationConfiguration(array $config, ContainerBuilder if (\array_key_exists('enable_annotations', $config) && $config['enable_annotations']) { $validatorBuilder->addMethodCall('enableAnnotationMapping', [true]); - if ($this->annotationsConfigEnabled) { + if ($this->isInitializedConfigEnabled('annotations')) { $validatorBuilder->addMethodCall('setDoctrineAnnotationReader', [new Reference('annotation_reader')]); } } @@ -1574,6 +1553,10 @@ private function registerValidationConfiguration(array $config, ContainerBuilder if (!class_exists(ExpressionLanguage::class)) { $container->removeDefinition('validator.expression_language'); } + + if (!class_exists(WhenValidator::class)) { + $container->removeDefinition('validator.when'); + } } private function registerValidatorMapping(ContainerBuilder $container, array $config, array &$files) @@ -1640,7 +1623,7 @@ private function registerMappingFilesFromConfig(ContainerBuilder $container, arr private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader) { - if (!$this->annotationsConfigEnabled) { + if (!$this->isInitializedConfigEnabled('annotations')) { return; } @@ -1650,9 +1633,16 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde $loader->load('annotations.php'); + // registerUniqueLoader exists since doctrine/annotations v1.6 if (!method_exists(AnnotationRegistry::class, 'registerUniqueLoader')) { - $container->getDefinition('annotations.dummy_registry') - ->setMethodCalls([['registerLoader', ['class_exists']]]); + // registerLoader exists only in doctrine/annotations v1 + if (method_exists(AnnotationRegistry::class, 'registerLoader')) { + $container->getDefinition('annotations.dummy_registry') + ->setMethodCalls([['registerLoader', ['class_exists']]]); + } else { + // remove the dummy registry when doctrine/annotations v2 is used + $container->removeDefinition('annotations.dummy_registry'); + } } if ('none' === $config['cache']) { @@ -1695,7 +1685,7 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) { - if (!$this->propertyAccessConfigEnabled = $this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('property_access', $container, $config)) { return; } @@ -1721,7 +1711,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui private function registerSecretsConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) { - if (!$this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('secrets', $container, $config)) { $container->removeDefinition('console.command.secrets_set'); $container->removeDefinition('console.command.secrets_list'); $container->removeDefinition('console.command.secrets_remove'); @@ -1761,7 +1751,7 @@ private function registerSecretsConfiguration(array $config, ContainerBuilder $c private function registerSecurityCsrfConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) { - if (!$this->isConfigEnabled($container, $config)) { + if (!$this->readConfigEnabled('csrf_protection', $container, $config)) { return; } @@ -1769,7 +1759,7 @@ private function registerSecurityCsrfConfiguration(array $config, ContainerBuild throw new LogicException('CSRF support cannot be enabled as the Security CSRF component is not installed. Try running "composer require symfony/security-csrf".'); } - if (!$this->sessionConfigEnabled) { + if (!$this->isInitializedConfigEnabled('session')) { throw new \LogicException('CSRF protection needs sessions to be enabled.'); } @@ -1784,13 +1774,10 @@ private function registerSecurityCsrfConfiguration(array $config, ContainerBuild private function registerSerializerConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) { $loader->load('serializer.php'); - if ($container->getParameter('kernel.debug')) { - $container->removeDefinition('serializer.mapping.cache_class_metadata_factory'); - } $chainLoader = $container->getDefinition('serializer.mapping.chain_loader'); - if (!$this->propertyAccessConfigEnabled) { + if (!$this->isInitializedConfigEnabled('property_access')) { $container->removeAlias('serializer.property_accessor'); $container->removeDefinition('serializer.normalizer.object'); } @@ -1799,7 +1786,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $container->removeDefinition('serializer.encoder.yaml'); } - if (!class_exists(UnwrappingDenormalizer::class) || !$this->propertyAccessConfigEnabled) { + if (!class_exists(UnwrappingDenormalizer::class) || !$this->isInitializedConfigEnabled('property_access')) { $container->removeDefinition('serializer.denormalizer.unwrapping'); } @@ -1809,6 +1796,10 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder $serializerLoaders = []; if (isset($config['enable_annotations']) && $config['enable_annotations']) { + if ($container->getParameter('kernel.debug')) { + $container->removeDefinition('serializer.mapping.cache_class_metadata_factory'); + } + $annotationLoader = new Definition( AnnotationLoader::class, [new Reference('annotation_reader', ContainerInterface::NULL_ON_INVALID_REFERENCE)] @@ -1987,7 +1978,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder throw new LogicException('Messenger support cannot be enabled as the Messenger component is not installed. Try running "composer require symfony/messenger".'); } - if (!class_exists(StatsCommand::class)) { + if (!$this->hasConsole() || !class_exists(StatsCommand::class)) { $container->removeDefinition('console.command.messenger_stats'); } @@ -2032,12 +2023,9 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder foreach ($config['buses'] as $busId => $bus) { $middleware = $bus['middleware']; - if ($bus['default_middleware']) { - if ('allow_no_handlers' === $bus['default_middleware']) { - $defaultMiddleware['after'][1]['arguments'] = [true]; - } else { - unset($defaultMiddleware['after'][1]['arguments']); - } + if ($bus['default_middleware']['enabled']) { + $defaultMiddleware['after'][0]['arguments'] = [$bus['default_middleware']['allow_no_senders']]; + $defaultMiddleware['after'][1]['arguments'] = [$bus['default_middleware']['allow_no_handlers']]; // argument to add_bus_name_stamp_middleware $defaultMiddleware['before'][0]['arguments'] = [$busId]; @@ -2102,6 +2090,7 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $senderAliases = []; $transportRetryReferences = []; + $transportRateLimiterReferences = []; foreach ($config['transports'] as $name => $transport) { $serializerId = $transport['serializer'] ?? 'messenger.default_serializer'; $transportDefinition = (new Definition(TransportInterface::class)) @@ -2130,6 +2119,14 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $transportRetryReferences[$name] = new Reference($retryServiceId); } + + if ($transport['rate_limiter']) { + if (!interface_exists(LimiterInterface::class)) { + throw new LogicException('Rate limiter cannot be used within Messenger as the RateLimiter component is not installed. Try running "composer require symfony/rate-limiter".'); + } + + $transportRateLimiterReferences[$name] = new Reference('limiter.'.$transport['rate_limiter']); + } } $senderReferences = []; @@ -2184,13 +2181,22 @@ private function registerMessengerConfiguration(array $config, ContainerBuilder $container->getDefinition('messenger.retry_strategy_locator') ->replaceArgument(0, $transportRetryReferences); + if (!$transportRateLimiterReferences) { + $container->removeDefinition('messenger.rate_limiter_locator'); + } else { + $container->getDefinition('messenger.rate_limiter_locator') + ->replaceArgument(0, $transportRateLimiterReferences); + } + if (\count($failureTransports) > 0) { - $container->getDefinition('console.command.messenger_failed_messages_retry') - ->replaceArgument(0, $config['failure_transport']); - $container->getDefinition('console.command.messenger_failed_messages_show') - ->replaceArgument(0, $config['failure_transport']); - $container->getDefinition('console.command.messenger_failed_messages_remove') - ->replaceArgument(0, $config['failure_transport']); + if ($this->hasConsole()) { + $container->getDefinition('console.command.messenger_failed_messages_retry') + ->replaceArgument(0, $config['failure_transport']); + $container->getDefinition('console.command.messenger_failed_messages_show') + ->replaceArgument(0, $config['failure_transport']); + $container->getDefinition('console.command.messenger_failed_messages_remove') + ->replaceArgument(0, $config['failure_transport']); + } $failureTransportsByTransportNameServiceLocator = ServiceLocatorTagPass::register($container, $failureTransportReferencesByTransportName); $container->getDefinition('messenger.failure.send_failed_message_to_failure_transport_listener') @@ -2324,7 +2330,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con } } - private function registerHttpClientConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader, array $profilerConfig) + private function registerHttpClientConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader) { $loader->load('http_client.php'); @@ -2342,11 +2348,11 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder $container->removeDefinition(HttpClient::class); } - if ($this->isConfigEnabled($container, $retryOptions)) { + if ($this->readConfigEnabled('http_client.retry_failed', $container, $retryOptions)) { $this->registerRetryableHttpClient($retryOptions, 'http_client', $container); } - $httpClientId = ($retryOptions['enabled'] ?? false) ? 'http_client.retryable.inner' : ($this->isConfigEnabled($container, $profilerConfig) ? '.debug.http_client.inner' : 'http_client'); + $httpClientId = ($retryOptions['enabled'] ?? false) ? 'http_client.retryable.inner' : ($this->isInitializedConfigEnabled('profiler') ? '.debug.http_client.inner' : 'http_client'); foreach ($config['scoped_clients'] as $name => $scopeConfig) { if ('http_client' === $name) { throw new InvalidArgumentException(sprintf('Invalid scope name: "%s" is reserved.', $name)); @@ -2373,7 +2379,7 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder ; } - if ($this->isConfigEnabled($container, $retryOptions)) { + if ($this->readConfigEnabled('http_client.scoped_clients.'.$name.'retry_failed', $container, $retryOptions)) { $this->registerRetryableHttpClient($retryOptions, $name, $container); } @@ -2455,9 +2461,10 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co $classToServices = [ GmailTransportFactory::class => 'mailer.transport_factory.gmail', - InfobipTransportFactory::class => 'mailer.transport_factory.infobip', + InfobipMailerTransportFactory::class => 'mailer.transport_factory.infobip', MailgunTransportFactory::class => 'mailer.transport_factory.mailgun', MailjetTransportFactory::class => 'mailer.transport_factory.mailjet', + MailPaceTransportFactory::class => 'mailer.transport_factory.mailpace', MandrillTransportFactory::class => 'mailer.transport_factory.mailchimp', OhMySmtpTransportFactory::class => 'mailer.transport_factory.ohmysmtp', PostmarkTransportFactory::class => 'mailer.transport_factory.postmark', @@ -2520,14 +2527,14 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ $container->removeAlias(TexterInterface::class); } - if ($this->mailerConfigEnabled) { + if ($this->isInitializedConfigEnabled('mailer')) { $sender = $container->getDefinition('mailer.envelope_listener')->getArgument(0); $container->getDefinition('notifier.channel.email')->setArgument(2, $sender); } else { $container->removeDefinition('notifier.channel.email'); } - if ($this->messengerConfigEnabled) { + if ($this->isInitializedConfigEnabled('messenger')) { if ($config['notification_on_failed_messages']) { $container->getDefinition('notifier.failed_message_listener')->addTag('kernel.event_subscriber'); } @@ -2552,6 +2559,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ $classToServices = [ AllMySmsTransportFactory::class => 'notifier.transport_factory.all-my-sms', AmazonSnsTransportFactory::class => 'notifier.transport_factory.amazon-sns', + ChatworkTransportFactory::class => 'notifier.transport_factory.chatwork', ClickatellTransportFactory::class => 'notifier.transport_factory.clickatell', ContactEveryoneTransportFactory::class => 'notifier.transport_factory.contact-everyone', DiscordTransportFactory::class => 'notifier.transport_factory.discord', @@ -2613,7 +2621,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ } } - if (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', MercureTransportFactory::class, $parentPackages) && ContainerBuilder::willBeAvailable('symfony/mercure-bundle', MercureBundle::class, $parentPackages)) { + if (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', MercureTransportFactory::class, $parentPackages) && ContainerBuilder::willBeAvailable('symfony/mercure-bundle', MercureBundle::class, $parentPackages) && \in_array(MercureBundle::class, $container->getParameter('kernel.bundles'), true)) { $container->getDefinition($classToServices[MercureTransportFactory::class]) ->replaceArgument('$registry', new Reference(HubRegistry::class)); } elseif (ContainerBuilder::willBeAvailable('symfony/mercure-notifier', MercureTransportFactory::class, $parentPackages)) { @@ -2647,34 +2655,68 @@ private function registerRateLimiterConfiguration(array $config, ContainerBuilde $loader->load('rate_limiter.php'); foreach ($config['limiters'] as $name => $limiterConfig) { - self::registerRateLimiter($container, $name, $limiterConfig); + // default configuration (when used by other DI extensions) + $limiterConfig += ['lock_factory' => 'lock.factory', 'cache_pool' => 'cache.rate_limiter']; + + $limiter = $container->setDefinition($limiterId = 'limiter.'.$name, new ChildDefinition('limiter')); + + if (null !== $limiterConfig['lock_factory']) { + if (!interface_exists(LockInterface::class)) { + throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be installed. Try running "composer require symfony/lock".', $name)); + } + + if (!$this->isInitializedConfigEnabled('lock')) { + throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be configured.', $name)); + } + + $limiter->replaceArgument(2, new Reference($limiterConfig['lock_factory'])); + } + unset($limiterConfig['lock_factory']); + + if (null === $storageId = $limiterConfig['storage_service'] ?? null) { + $container->register($storageId = 'limiter.storage.'.$name, CacheStorage::class)->addArgument(new Reference($limiterConfig['cache_pool'])); + } + + $limiter->replaceArgument(1, new Reference($storageId)); + unset($limiterConfig['storage_service'], $limiterConfig['cache_pool']); + + $limiterConfig['id'] = $name; + $limiter->replaceArgument(0, $limiterConfig); + + $container->registerAliasForArgument($limiterId, RateLimiterFactory::class, $name.'.limiter'); } } + /** + * @deprecated since Symfony 6.2 + */ public static function registerRateLimiter(ContainerBuilder $container, string $name, array $limiterConfig) { + trigger_deprecation('symfony/framework-bundle', '6.2', 'The "%s()" method is deprecated.', __METHOD__); + // default configuration (when used by other DI extensions) $limiterConfig += ['lock_factory' => 'lock.factory', 'cache_pool' => 'cache.rate_limiter']; $limiter = $container->setDefinition($limiterId = 'limiter.'.$name, new ChildDefinition('limiter')); if (null !== $limiterConfig['lock_factory']) { - if (!self::$lockConfigEnabled) { - throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be installed and configured.', $name)); + if (!interface_exists(LockInterface::class)) { + throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be installed. Try running "composer require symfony/lock".', $name)); + } + if (!$container->hasDefinition('lock.factory.abstract')) { + throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be configured.', $name)); } $limiter->replaceArgument(2, new Reference($limiterConfig['lock_factory'])); } unset($limiterConfig['lock_factory']); - $storageId = $limiterConfig['storage_service'] ?? null; - if (null === $storageId) { + if (null === $storageId = $limiterConfig['storage_service'] ?? null) { $container->register($storageId = 'limiter.storage.'.$name, CacheStorage::class)->addArgument(new Reference($limiterConfig['cache_pool'])); } $limiter->replaceArgument(1, new Reference($storageId)); - unset($limiterConfig['storage_service']); - unset($limiterConfig['cache_pool']); + unset($limiterConfig['storage_service'], $limiterConfig['cache_pool']); $limiterConfig['id'] = $name; $limiter->replaceArgument(0, $limiterConfig); @@ -2755,10 +2797,14 @@ private function registerHtmlSanitizerConfiguration(array $config, ContainerBuil // Settings $def->addMethodCall('forceHttpsUrls', [$sanitizerConfig['force_https_urls']], true); - $def->addMethodCall('allowLinkSchemes', [$sanitizerConfig['allowed_link_schemes']], true); + if ($sanitizerConfig['allowed_link_schemes']) { + $def->addMethodCall('allowLinkSchemes', [$sanitizerConfig['allowed_link_schemes']], true); + } $def->addMethodCall('allowLinkHosts', [$sanitizerConfig['allowed_link_hosts']], true); $def->addMethodCall('allowRelativeLinks', [$sanitizerConfig['allow_relative_links']], true); - $def->addMethodCall('allowMediaSchemes', [$sanitizerConfig['allowed_media_schemes']], true); + if ($sanitizerConfig['allowed_media_schemes']) { + $def->addMethodCall('allowMediaSchemes', [$sanitizerConfig['allowed_media_schemes']], true); + } $def->addMethodCall('allowMediaHosts', [$sanitizerConfig['allowed_media_hosts']], true); $def->addMethodCall('allowRelativeMedias', [$sanitizerConfig['allow_relative_medias']], true); @@ -2792,22 +2838,20 @@ private function resolveTrustedHeaders(array $headers): int $trustedHeaders = 0; foreach ($headers as $h) { - switch ($h) { - case 'forwarded': $trustedHeaders |= Request::HEADER_FORWARDED; break; - case 'x-forwarded-for': $trustedHeaders |= Request::HEADER_X_FORWARDED_FOR; break; - case 'x-forwarded-host': $trustedHeaders |= Request::HEADER_X_FORWARDED_HOST; break; - case 'x-forwarded-proto': $trustedHeaders |= Request::HEADER_X_FORWARDED_PROTO; break; - case 'x-forwarded-port': $trustedHeaders |= Request::HEADER_X_FORWARDED_PORT; break; - case 'x-forwarded-prefix': $trustedHeaders |= Request::HEADER_X_FORWARDED_PREFIX; break; - } + $trustedHeaders |= match ($h) { + 'forwarded' => Request::HEADER_FORWARDED, + 'x-forwarded-for' => Request::HEADER_X_FORWARDED_FOR, + 'x-forwarded-host' => Request::HEADER_X_FORWARDED_HOST, + 'x-forwarded-proto' => Request::HEADER_X_FORWARDED_PROTO, + 'x-forwarded-port' => Request::HEADER_X_FORWARDED_PORT, + 'x-forwarded-prefix' => Request::HEADER_X_FORWARDED_PREFIX, + default => 0, + }; } return $trustedHeaders; } - /** - * {@inheritdoc} - */ public function getXsdValidationBasePath(): string|false { return \dirname(__DIR__).'/Resources/config/schema'; @@ -2817,4 +2861,33 @@ public function getNamespace(): string { return 'http://symfony.com/schema/dic/symfony'; } + + protected function isConfigEnabled(ContainerBuilder $container, array $config): bool + { + throw new \LogicException('To prevent using outdated configuration, you must use the "readConfigEnabled" method instead.'); + } + + private function isInitializedConfigEnabled(string $path): bool + { + if (isset($this->configsEnabled[$path])) { + return $this->configsEnabled[$path]; + } + + throw new LogicException(sprintf('Can not read config enabled at "%s" because it has not been initialized.', $path)); + } + + private function readConfigEnabled(string $path, ContainerBuilder $container, array $config): bool + { + return $this->configsEnabled[$path] ??= parent::isConfigEnabled($container, $config); + } + + private function writeConfigEnabled(string $path, bool $value, array &$config): void + { + if (isset($this->configsEnabled[$path])) { + throw new LogicException('Can not change config enabled because it has already been read.'); + } + + $this->configsEnabled[$path] = $value; + $config['enabled'] = $value; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 3b86ea1344fcf..b51e19a5a2341 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -17,6 +17,7 @@ use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AssetsContextPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ContainerBuilderDebugDumpPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\DataCollectorTranslatorPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\EnableLoggerDebugModePass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RemoveUnusedSessionMarshallingHandlerPass; @@ -165,6 +166,7 @@ public function build(ContainerBuilder $container) $container->addCompilerPass(new RemoveUnusedSessionMarshallingHandlerPass()); if ($container->getParameter('kernel.debug')) { + $container->addCompilerPass(new EnableLoggerDebugModePass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -33); $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 2); $container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING); $container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_BEFORE_REMOVING, -255); diff --git a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php index 102892b2f426c..f312a7afe4946 100644 --- a/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php +++ b/src/Symfony/Bundle/FrameworkBundle/HttpCache/HttpCache.php @@ -60,9 +60,6 @@ public function __construct(KernelInterface $kernel, string|StoreInterface $cach parent::__construct($kernel, $this->createStore(), $this->createSurrogate(), array_merge($this->options, $this->getOptions())); } - /** - * {@inheritdoc} - */ protected function forward(Request $request, bool $catch = false, Response $entry = null): Response { $this->getKernel()->boot(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php index dbf77b3bd94bd..fa04ff332433d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php @@ -104,9 +104,6 @@ private function getBundlesPath(): string return $this->getConfigDir().'/bundles.php'; } - /** - * {@inheritdoc} - */ public function getCacheDir(): string { if (isset($_SERVER['APP_CACHE_DIR'])) { @@ -116,17 +113,11 @@ public function getCacheDir(): string return parent::getCacheDir(); } - /** - * {@inheritdoc} - */ public function getLogDir(): string { return $_SERVER['APP_LOG_DIR'] ?? parent::getLogDir(); } - /** - * {@inheritdoc} - */ public function registerBundles(): iterable { $contents = require $this->getBundlesPath(); @@ -137,9 +128,6 @@ public function registerBundles(): iterable } } - /** - * {@inheritdoc} - */ public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load(function (ContainerBuilder $container) use ($loader) { diff --git a/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php b/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php index 4fb751d4bfb32..0f4a3843fb843 100644 --- a/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php +++ b/src/Symfony/Bundle/FrameworkBundle/KernelBrowser.php @@ -34,9 +34,6 @@ class KernelBrowser extends HttpKernelBrowser private bool $profiler = false; private bool $reboot = true; - /** - * {@inheritdoc} - */ public function __construct(KernelInterface $kernel, array $server = [], History $history = null, CookieJar $cookieJar = null) { parent::__construct($kernel, $server, $history, $cookieJar); @@ -119,7 +116,7 @@ public function loginUser(object $user, string $firewallContext = 'main'): stati } $token = new TestBrowserToken($user->getRoles(), $user, $firewallContext); - // required for compatibilty with Symfony 5.4 + // required for compatibility with Symfony 5.4 if (method_exists($token, 'isAuthenticated')) { $token->setAuthenticated(true, false); } @@ -147,8 +144,6 @@ public function loginUser(object $user, string $firewallContext = 'main'): stati } /** - * {@inheritdoc} - * * @param Request $request */ protected function doRequest(object $request): Response @@ -173,8 +168,6 @@ protected function doRequest(object $request): Response } /** - * {@inheritdoc} - * * @param Request $request */ protected function doRequestInProcess(object $request): Response diff --git a/src/Symfony/Bundle/FrameworkBundle/LICENSE b/src/Symfony/Bundle/FrameworkBundle/LICENSE index 88bf75bb4d6a2..0138f8f071351 100644 --- a/src/Symfony/Bundle/FrameworkBundle/LICENSE +++ b/src/Symfony/Bundle/FrameworkBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/FrameworkBundle/README.md b/src/Symfony/Bundle/FrameworkBundle/README.md index 76c7700fa03af..1b7dc2c3cf312 100644 --- a/src/Symfony/Bundle/FrameworkBundle/README.md +++ b/src/Symfony/Bundle/FrameworkBundle/README.md @@ -4,6 +4,19 @@ FrameworkBundle FrameworkBundle provides a tight integration between Symfony components and the Symfony full-stack framework. +Sponsor +------- + +The FrameworkBundle for Symfony 6.2 is [backed][1] by [alximy][2]. + +A team of passionate humans from very different backgrounds, sharing their love of +PHP, Symfony and its ecosystem. Their CTO, Expert developers, tech leads, can help +you learn or develop the tools you need, and perform audits or tailored workshops. +They value contributing to the Open Source community and are willing to mentor new +contributors in their team or yours. + +Help Symfony by [sponsoring][3] its development! + Resources --------- @@ -11,3 +24,7 @@ Resources * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) + +[1]: https://symfony.com/backers +[2]: https://alximy.io/ +[3]: https://symfony.com/sponsor diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/bin/check-unused-known-tags.php b/src/Symfony/Bundle/FrameworkBundle/Resources/bin/check-unused-known-tags.php index 4920c43ebe182..55658f5b19b76 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/bin/check-unused-known-tags.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/bin/check-unused-known-tags.php @@ -9,6 +9,10 @@ * file that was distributed with this source code. */ +if ('cli' !== \PHP_SAPI) { + throw new Exception('This script must be run from the command line.'); +} + require dirname(__DIR__, 6).'/vendor/autoload.php'; use Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\UnusedTagsPassUtils; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.php index 1b178f16a95d3..ec86fed495498 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/annotations.php @@ -25,7 +25,7 @@ ->set('annotations.reader', AnnotationReader::class) ->call('addGlobalIgnoredName', [ 'required', - service('annotations.dummy_registry'), // dummy arg to register class_exists as annotation loader only when required + service('annotations.dummy_registry')->nullOnInvalid(), // dummy arg to register class_exists as annotation loader only when required ]) ->set('annotations.dummy_registry', AnnotationRegistry::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php index 9ca75d099e5f9..b4a6e33c4de41 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/console.php @@ -160,6 +160,7 @@ [], // Receiver names service('messenger.listener.reset_services')->nullOnInvalid(), [], // Bus names + service('messenger.rate_limiter_locator')->nullOnInvalid(), ]) ->tag('console.command') ->tag('monolog.logger', ['channel' => 'messenger']) @@ -189,14 +190,17 @@ abstract_arg('Receivers'), service('messenger.routable_message_bus'), service('event_dispatcher'), - service('logger'), + service('logger')->nullOnInvalid(), + service('messenger.transport.native_php_serializer')->nullOnInvalid(), ]) ->tag('console.command') + ->tag('monolog.logger', ['channel' => 'messenger']) ->set('console.command.messenger_failed_messages_show', FailedMessagesShowCommand::class) ->args([ abstract_arg('Default failure receiver name'), abstract_arg('Receivers'), + service('messenger.transport.native_php_serializer')->nullOnInvalid(), ]) ->tag('console.command') @@ -204,6 +208,7 @@ ->args([ abstract_arg('Default failure receiver name'), abstract_arg('Receivers'), + service('messenger.transport.native_php_serializer')->nullOnInvalid(), ]) ->tag('console.command') diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php index 7e799bd1ee262..e20594093c9dd 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php @@ -17,6 +17,7 @@ use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillTransportFactory; use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; +use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory; use Symfony\Component\Mailer\Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; @@ -62,6 +63,10 @@ ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory') + ->set('mailer.transport_factory.mailpace', MailPaceTransportFactory::class) + ->parent('mailer.transport_factory.abstract') + ->tag('mailer.transport_factory') + ->set('mailer.transport_factory.postmark', PostmarkTransportFactory::class) ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory') diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php index 813d503000de4..d0d9900d2e506 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php @@ -55,6 +55,7 @@ abstract_arg('senders service locator'), ]) ->set('messenger.middleware.send_message', SendMessageMiddleware::class) + ->abstract() ->args([ service('messenger.senders_locator'), service('event_dispatcher'), @@ -140,6 +141,7 @@ ->args([ service('logger')->ignoreOnInvalid(), ]) + ->tag('monolog.logger', ['channel' => 'messenger']) ->set('messenger.transport.beanstalkd.factory', BeanstalkdTransportFactory::class) @@ -159,6 +161,11 @@ abstract_arg('max delay ms'), ]) + // rate limiter + ->set('messenger.rate_limiter_locator', ServiceLocator::class) + ->args([[]]) + ->tag('container.service_locator') + // worker event listener ->set('messenger.retry.send_failed_message_for_retry_listener', SendFailedMessageForRetryListener::class) ->args([ @@ -197,6 +204,7 @@ service('logger')->ignoreOnInvalid(), ]) ->tag('kernel.event_subscriber') + ->tag('monolog.logger', ['channel' => 'messenger']) ->set('messenger.listener.stop_worker_on_stop_exception_listener', StopWorkerOnCustomStopExceptionListener::class) ->tag('kernel.event_subscriber') diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index 691588d730d92..237ae18a59eb7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -13,6 +13,7 @@ use Symfony\Component\Notifier\Bridge\AllMySms\AllMySmsTransportFactory; use Symfony\Component\Notifier\Bridge\AmazonSns\AmazonSnsTransportFactory; +use Symfony\Component\Notifier\Bridge\Chatwork\ChatworkTransportFactory; use Symfony\Component\Notifier\Bridge\Clickatell\ClickatellTransportFactory; use Symfony\Component\Notifier\Bridge\ContactEveryone\ContactEveryoneTransportFactory; use Symfony\Component\Notifier\Bridge\Discord\DiscordTransportFactory; @@ -281,5 +282,10 @@ ->set('notifier.transport_factory.zendesk', ZendeskTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') + + ->set('notifier.transport_factory.chatwork', ChatworkTransportFactory::class) + ->parent('notifier.transport_factory.abstract') + ->tag('chatter.transport_factory') + ; }; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.php index 09e340ff8aedd..86a7cf874629c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.php @@ -15,6 +15,7 @@ use Symfony\Bundle\FrameworkBundle\CacheWarmer\RouterCacheWarmer; use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; use Symfony\Bundle\FrameworkBundle\Controller\TemplateController; +use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader; use Symfony\Bundle\FrameworkBundle\Routing\DelegatingLoader; use Symfony\Bundle\FrameworkBundle\Routing\RedirectableCompiledUrlMatcher; use Symfony\Bundle\FrameworkBundle\Routing\Router; @@ -23,10 +24,13 @@ use Symfony\Component\Routing\Generator\CompiledUrlGenerator; use Symfony\Component\Routing\Generator\Dumper\CompiledUrlGeneratorDumper; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; +use Symfony\Component\Routing\Loader\AnnotationFileLoader; use Symfony\Component\Routing\Loader\ContainerLoader; use Symfony\Component\Routing\Loader\DirectoryLoader; use Symfony\Component\Routing\Loader\GlobFileLoader; use Symfony\Component\Routing\Loader\PhpFileLoader; +use Symfony\Component\Routing\Loader\Psr4DirectoryLoader; use Symfony\Component\Routing\Loader\XmlFileLoader; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper; @@ -88,6 +92,33 @@ ]) ->tag('routing.loader') + ->set('routing.loader.annotation', AnnotatedRouteControllerLoader::class) + ->args([ + service('annotation_reader')->nullOnInvalid(), + '%kernel.environment%', + ]) + ->tag('routing.loader', ['priority' => -10]) + + ->set('routing.loader.annotation.directory', AnnotationDirectoryLoader::class) + ->args([ + service('file_locator'), + service('routing.loader.annotation'), + ]) + ->tag('routing.loader', ['priority' => -10]) + + ->set('routing.loader.annotation.file', AnnotationFileLoader::class) + ->args([ + service('file_locator'), + service('routing.loader.annotation'), + ]) + ->tag('routing.loader', ['priority' => -10]) + + ->set('routing.loader.psr4', Psr4DirectoryLoader::class) + ->args([ + service('file_locator'), + ]) + ->tag('routing.loader', ['priority' => -10]) + ->set('routing.loader', DelegatingLoader::class) ->public() ->args([ @@ -101,7 +132,7 @@ service(ContainerInterface::class), param('router.resource'), [ - 'cache_dir' => param('kernel.cache_dir'), + 'cache_dir' => param('router.cache_dir'), 'debug' => param('kernel.debug'), 'generator_class' => CompiledUrlGenerator::class, 'generator_dumper_class' => CompiledUrlGeneratorDumper::class, diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index cd53d88db00ac..a7dbde2faca93 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -30,6 +30,7 @@ + @@ -114,6 +115,7 @@ + @@ -364,14 +366,29 @@ - + - - + - + + + + + + + + + + + + + + + + + @@ -385,8 +402,6 @@ - - @@ -426,7 +441,7 @@ - + @@ -436,6 +451,12 @@ + + + + + + @@ -554,6 +575,7 @@ + @@ -566,10 +588,11 @@ + - + @@ -723,6 +746,7 @@ + @@ -750,7 +774,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php index 773016b7a662d..edf1987e32adc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php @@ -77,7 +77,8 @@ // Normalizer ->set('serializer.normalizer.constraint_violation_list', ConstraintViolationListNormalizer::class) - ->args([[], service('serializer.name_converter.metadata_aware')]) + ->args([1 => service('serializer.name_converter.metadata_aware')]) + ->autowire(true) ->tag('serializer.normalizer', ['priority' => -915]) ->set('serializer.normalizer.mime_message', MimeMessageNormalizer::class) @@ -123,7 +124,6 @@ service('property_info')->ignoreOnInvalid(), service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(), null, - [], ]) ->tag('serializer.normalizer', ['priority' => -1000]) @@ -137,7 +137,6 @@ service('property_info')->ignoreOnInvalid(), service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(), null, - [], ]) ->alias(PropertyNormalizer::class, 'serializer.normalizer.property') @@ -177,6 +176,7 @@ ->tag('serializer.encoder') ->set('serializer.encoder.json', JsonEncoder::class) + ->args([null, null]) ->tag('serializer.encoder') ->set('serializer.encoder.yaml', YamlEncoder::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer_debug.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer_debug.php index 28fc4ea48c514..45b764fdd6b7d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer_debug.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer_debug.php @@ -17,7 +17,7 @@ return static function (ContainerConfigurator $container) { $container->services() ->set('debug.serializer', TraceableSerializer::class) - ->decorate('serializer', null, 255) + ->decorate('serializer') ->args([ service('debug.serializer.inner'), service('serializer.data_collector'), diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php index 40b6a5ac6c445..a648e8fdb1e49 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Psr\Clock\ClockInterface as PsrClockInterface; use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface; use Symfony\Bundle\FrameworkBundle\CacheWarmer\ConfigBuilderCacheWarmer; use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache; @@ -119,7 +120,7 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] ->set('url_helper', UrlHelper::class) ->args([ service('request_stack'), - service('router.request_context')->ignoreOnInvalid(), + service('router')->ignoreOnInvalid(), ]) ->alias(UrlHelper::class, 'url_helper') @@ -230,6 +231,7 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] ->set('clock', NativeClock::class) ->alias(ClockInterface::class, 'clock') + ->alias(PsrClockInterface::class, 'clock') // register as abstract and excluded, aka not-autowirable types ->set(LoaderInterface::class)->abstract()->tag('container.excluded') diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.php index 30a622d02c8fb..907b1b5844441 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/session.php @@ -69,6 +69,11 @@ ->alias(\SessionHandlerInterface::class, 'session.handler') + ->set('session.handler.native', StrictSessionHandler::class) + ->args([ + inline_service(\SessionHandler::class), + ]) + ->set('session.handler.native_file', StrictSessionHandler::class) ->args([ inline_service(NativeFileSessionHandler::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php index fadaff6d2b596..dcfa2bc15716d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/translation.php @@ -26,7 +26,11 @@ use Symfony\Component\Translation\Dumper\YamlFileDumper; use Symfony\Component\Translation\Extractor\ChainExtractor; use Symfony\Component\Translation\Extractor\ExtractorInterface; +use Symfony\Component\Translation\Extractor\PhpAstExtractor; use Symfony\Component\Translation\Extractor\PhpExtractor; +use Symfony\Component\Translation\Extractor\Visitor\ConstraintVisitor; +use Symfony\Component\Translation\Extractor\Visitor\TranslatableMessageVisitor; +use Symfony\Component\Translation\Extractor\Visitor\TransMethodVisitor; use Symfony\Component\Translation\Formatter\MessageFormatter; use Symfony\Component\Translation\Loader\CsvFileLoader; use Symfony\Component\Translation\Loader\IcuDatFileLoader; @@ -149,8 +153,22 @@ ->tag('translation.dumper', ['alias' => 'res']) ->set('translation.extractor.php', PhpExtractor::class) + ->deprecate('symfony/framework-bundle', '6.2', 'The "%service_id%" service is deprecated, use "translation.extractor.php_ast" instead.') ->tag('translation.extractor', ['alias' => 'php']) + ->set('translation.extractor.php_ast', PhpAstExtractor::class) + ->args([tagged_iterator('translation.extractor.visitor')]) + ->tag('translation.extractor', ['alias' => 'php']) + + ->set('translation.extractor.visitor.trans_method', TransMethodVisitor::class) + ->tag('translation.extractor.visitor') + + ->set('translation.extractor.visitor.translatable_message', TranslatableMessageVisitor::class) + ->tag('translation.extractor.visitor') + + ->set('translation.extractor.visitor.constraint', ConstraintVisitor::class) + ->tag('translation.extractor.visitor') + ->set('translation.reader', TranslationReader::class) ->alias(TranslationReaderInterface::class, 'translation.reader') @@ -168,10 +186,11 @@ ->set('translation.locale_switcher', LocaleSwitcher::class) ->args([ param('kernel.default_locale'), - tagged_iterator('kernel.locale_aware'), + tagged_iterator('kernel.locale_aware', exclude: 'translation.locale_switcher'), service('router.request_context')->ignoreOnInvalid(), ]) ->tag('kernel.reset', ['method' => 'reset']) + ->tag('kernel.locale_aware') ->alias(LocaleAwareInterface::class, 'translation.locale_switcher') ->alias(LocaleSwitcher::class, 'translation.locale_switcher') ; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php index 3fd066ee37f1f..1c896de3a89df 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/validator.php @@ -17,6 +17,7 @@ use Symfony\Component\Validator\Constraints\EmailValidator; use Symfony\Component\Validator\Constraints\ExpressionValidator; use Symfony\Component\Validator\Constraints\NotCompromisedPasswordValidator; +use Symfony\Component\Validator\Constraints\WhenValidator; use Symfony\Component\Validator\ContainerConstraintValidatorFactory; use Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader; use Symfony\Component\Validator\Validation; @@ -27,6 +28,8 @@ $container->parameters() ->set('validator.mapping.cache.file', param('kernel.cache_dir').'/validation.php'); + $validatorsDir = \dirname((new \ReflectionClass(EmailValidator::class))->getFileName()); + $container->services() ->set('validator', ValidatorInterface::class) ->factory([service('validator.builder'), 'getValidator']) @@ -64,6 +67,12 @@ abstract_arg('Constraint validators locator'), ]) + ->load('Symfony\Component\Validator\Constraints\\', $validatorsDir.'/*Validator.php') + ->exclude($validatorsDir.'/ExpressionLanguageSyntaxValidator.php') + ->abstract() + ->tag('container.excluded') + ->tag('validator.constraint_validator') + ->set('validator.expression', ExpressionValidator::class) ->args([service('validator.expression_language')->nullOnInvalid()]) ->tag('validator.constraint_validator', [ @@ -81,9 +90,7 @@ ->args([ abstract_arg('Default mode'), ]) - ->tag('validator.constraint_validator', [ - 'alias' => EmailValidator::class, - ]) + ->tag('validator.constraint_validator') ->set('validator.not_compromised_password', NotCompromisedPasswordValidator::class) ->args([ @@ -91,9 +98,11 @@ param('kernel.charset'), false, ]) - ->tag('validator.constraint_validator', [ - 'alias' => NotCompromisedPasswordValidator::class, - ]) + ->tag('validator.constraint_validator') + + ->set('validator.when', WhenValidator::class) + ->args([service('validator.expression_language')->nullOnInvalid()]) + ->tag('validator.constraint_validator') ->set('validator.property_info_loader', PropertyInfoLoader::class) ->args([ diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php index 4b8875da73424..85d786537f031 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php @@ -41,9 +41,9 @@ ->abstract() ->set('.workflow.registry', Registry::class) ->alias(Registry::class, '.workflow.registry') - ->deprecate('symfony/workflow', '6.2', 'The "%alias_id%" alias is deprecated since Symfony 6.2 and will be removed in Symfony 7.0. Inject the workflow directly.') + ->deprecate('symfony/workflow', '6.2', 'The "%alias_id%" alias is deprecated, inject the workflow directly.') ->alias('workflow.registry', '.workflow.registry') - ->deprecate('symfony/workflow', '6.2', 'The "%alias_id%" service is deprecated since Symfony 6.2 and will be removed in Symfony 7.0. Inject the workflow directly.') + ->deprecate('symfony/workflow', '6.2', 'The "%alias_id%" alias is deprecated, inject the workflow directly.') ->set('workflow.security.expression_language', ExpressionLanguage::class) ; }; diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php index a0fefe7fd2fab..df7e9348c3f1c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/DelegatingLoader.php @@ -40,9 +40,6 @@ public function __construct(LoaderResolverInterface $resolver, array $defaultOpt parent::__construct($resolver); } - /** - * {@inheritdoc} - */ public function load(mixed $resource, string $type = null): RouteCollection { if ($this->loading) { @@ -58,7 +55,7 @@ public function load(mixed $resource, string $type = null): RouteCollection // the fatal error from occurring a second time, // otherwise the PHP process would be killed immediately; // - while rendering the exception page, the router can be required - // (by e.g. the web profiler that needs to generate an URL); + // (by e.g. the web profiler that needs to generate a URL); // - this handles the case and prevents the second fatal error // by triggering an exception beforehand. diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/RedirectableCompiledUrlMatcher.php b/src/Symfony/Bundle/FrameworkBundle/Routing/RedirectableCompiledUrlMatcher.php index dba9d6d9613d1..538427aae6cf2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/RedirectableCompiledUrlMatcher.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/RedirectableCompiledUrlMatcher.php @@ -21,9 +21,6 @@ */ class RedirectableCompiledUrlMatcher extends CompiledUrlMatcher implements RedirectableUrlMatcherInterface { - /** - * {@inheritdoc} - */ public function redirect(string $path, string $route, string $scheme = null): array { return [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php index 8fda00a771fe0..ac5db387de808 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php @@ -59,9 +59,6 @@ public function __construct(ContainerInterface $container, mixed $resource, arra $this->defaultLocale = $defaultLocale; } - /** - * {@inheritdoc} - */ public function getRouteCollection(): RouteCollection { if (null === $this->collection) { @@ -84,8 +81,6 @@ public function getRouteCollection(): RouteCollection } /** - * {@inheritdoc} - * * @return string[] A list of classes to preload on PHP 7.4+ */ public function warmUp(string $cacheDir): array @@ -193,9 +188,6 @@ private function resolve(mixed $value): mixed return str_replace('%%', '%', $escapedValue); } - /** - * {@inheritdoc} - */ public static function getSubscribedServices(): array { return [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/AbstractVault.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/AbstractVault.php index eeecbbb68b683..cfa4bab13c3a6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/AbstractVault.php +++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/AbstractVault.php @@ -13,8 +13,6 @@ /** * @author Nicolas Grekas - * - * @internal */ abstract class AbstractVault { diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php index 4fa7c40bf8d72..db4891c34b337 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php +++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/DotenvVault.php @@ -13,8 +13,6 @@ /** * @author Nicolas Grekas - * - * @internal */ class DotenvVault extends AbstractVault { diff --git a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php index ff1cd7fb87908..b6bb058b3f170 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php +++ b/src/Symfony/Bundle/FrameworkBundle/Secrets/SodiumVault.php @@ -18,8 +18,6 @@ * @author Tobias Schultze * @author Jérémy Derussé * @author Nicolas Grekas - * - * @internal */ class SodiumVault extends AbstractVault implements EnvVarLoaderInterface { @@ -32,7 +30,7 @@ class SodiumVault extends AbstractVault implements EnvVarLoaderInterface * @param $decryptionKey A string or a stringable object that defines the private key to use to decrypt the vault * or null to store generated keys in the provided $secretsDir */ - public function __construct(string $secretsDir, string|\Stringable $decryptionKey = null) + public function __construct(string $secretsDir, #[\SensitiveParameter] string|\Stringable $decryptionKey = null) { $this->pathPrefix = rtrim(strtr($secretsDir, '/', \DIRECTORY_SEPARATOR), \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR.basename($secretsDir).'.'; $this->decryptionKey = $decryptionKey; diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/DomCrawlerAssertionsTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/DomCrawlerAssertionsTrait.php index 2a692d6f5a367..6e8247cbf54e4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/DomCrawlerAssertionsTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/DomCrawlerAssertionsTrait.php @@ -15,7 +15,6 @@ use PHPUnit\Framework\Constraint\LogicalNot; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Test\Constraint as DomCrawlerConstraint; -use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorAttributeValueSame; use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorExists; /** @@ -87,18 +86,12 @@ public static function assertInputValueNotSame(string $fieldName, string $expect public static function assertCheckboxChecked(string $fieldName, string $message = ''): void { - self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new CrawlerSelectorExists("input[name=\"$fieldName\"]"), - new CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'checked', 'checked') - ), $message); + self::assertThat(self::getCrawler(), new CrawlerSelectorExists("input[name=\"$fieldName\"]:checked"), $message); } public static function assertCheckboxNotChecked(string $fieldName, string $message = ''): void { - self::assertThat(self::getCrawler(), LogicalAnd::fromConstraints( - new CrawlerSelectorExists("input[name=\"$fieldName\"]"), - new LogicalNot(new CrawlerSelectorAttributeValueSame("input[name=\"$fieldName\"]", 'checked', 'checked')) - ), $message); + self::assertThat(self::getCrawler(), new LogicalNot(new CrawlerSelectorExists("input[name=\"$fieldName\"]:checked")), $message); } public static function assertFormValue(string $formSelector, string $fieldName, string $value, string $message = ''): void diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/ExceptionSubscriber.php b/src/Symfony/Bundle/FrameworkBundle/Test/ExceptionSubscriber.php deleted file mode 100644 index b2abd5d5e08f8..0000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Test/ExceptionSubscriber.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Test; - -use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpKernel\Event\ExceptionEvent; -use Symfony\Component\HttpKernel\KernelEvents; - -/** - * This event subscriber allows you to inspect all exceptions thrown by the application. - * This is useful since the HttpKernel catches all throwables and turns them into - * an HTTP response. - * - * This class should only be used in tests. - * - * @author Tobias Nyholm - */ -class ExceptionSubscriber implements EventSubscriberInterface -{ - /** - * @var list<\Throwable> - */ - private static array $exceptions = []; - - public function onKernelException(ExceptionEvent $event) - { - self::$exceptions[] = $event->getThrowable(); - } - - /** - * @return list<\Throwable> - */ - public static function shiftAll(): array - { - $exceptions = self::$exceptions; - self::$exceptions = []; - - return $exceptions; - } - - public static function getSubscribedEvents(): array - { - return [ - KernelEvents::EXCEPTION => 'onKernelException', - ]; - } -} diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php index 32bac9c42f02b..bb5560a7b5947 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php @@ -110,9 +110,7 @@ protected static function getContainer(): ContainerInterface */ protected static function createKernel(array $options = []): KernelInterface { - if (null === static::$class) { - static::$class = static::getKernelClass(); - } + static::$class ??= static::getKernelClass(); if (isset($options['environment'])) { $env = $options['environment']; diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php b/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php index 875c84d4813da..d6b29d2b5a0c6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/MailerAssertionsTrait.php @@ -91,7 +91,7 @@ public static function assertEmailAddressContains(RawMessage $email, string $hea } /** - * @return MessageEvents[] + * @return MessageEvent[] */ public static function getMailerEvents(string $transport = null): array { @@ -123,10 +123,6 @@ private static function getMessageMailerEvents(): MessageEvents return $container->get('mailer.message_logger_listener')->getEvents(); } - if ($container->has('mailer.logger_message_listener')) { - return $container->get('mailer.logger_message_listener')->getEvents(); - } - static::fail('A client must have Mailer enabled to make email assertions. Did you forget to require symfony/mailer?'); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Test/TestContainer.php b/src/Symfony/Bundle/FrameworkBundle/Test/TestContainer.php index 321ea373d85f1..883928087ea03 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Test/TestContainer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Test/TestContainer.php @@ -37,105 +37,66 @@ public function __construct(KernelInterface $kernel, string $privateServicesLoca $this->privateServicesLocatorId = $privateServicesLocatorId; } - /** - * {@inheritdoc} - */ public function compile() { $this->getPublicContainer()->compile(); } - /** - * {@inheritdoc} - */ public function isCompiled(): bool { return $this->getPublicContainer()->isCompiled(); } - /** - * {@inheritdoc} - */ public function getParameterBag(): ParameterBagInterface { return $this->getPublicContainer()->getParameterBag(); } - /** - * {@inheritdoc} - */ public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null { return $this->getPublicContainer()->getParameter($name); } - /** - * {@inheritdoc} - */ public function hasParameter(string $name): bool { return $this->getPublicContainer()->hasParameter($name); } - /** - * {@inheritdoc} - */ public function setParameter(string $name, mixed $value) { $this->getPublicContainer()->setParameter($name, $value); } - /** - * {@inheritdoc} - */ public function set(string $id, mixed $service) { $this->getPublicContainer()->set($id, $service); } - /** - * {@inheritdoc} - */ public function has(string $id): bool { return $this->getPublicContainer()->has($id) || $this->getPrivateContainer()->has($id); } - /** - * {@inheritdoc} - */ public function get(string $id, int $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE): ?object { return $this->getPrivateContainer()->has($id) ? $this->getPrivateContainer()->get($id) : $this->getPublicContainer()->get($id, $invalidBehavior); } - /** - * {@inheritdoc} - */ public function initialized(string $id): bool { return $this->getPublicContainer()->initialized($id); } - /** - * {@inheritdoc} - */ public function reset() { // ignore the call } - /** - * {@inheritdoc} - */ public function getServiceIds(): array { return $this->getPublicContainer()->getServiceIds(); } - /** - * {@inheritdoc} - */ public function getRemovedIds(): array { return $this->getPublicContainer()->getRemovedIds(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php index 75e6ce8f7271b..1772064abc87e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php @@ -136,7 +136,7 @@ public function testWarmupRemoveCacheMisses() $cacheFile = tempnam($this->cacheDir, __FUNCTION__); $warmer = $this->getMockBuilder(AnnotationsCacheWarmer::class) ->setConstructorArgs([new AnnotationReader(), $cacheFile]) - ->setMethods(['doWarmUp']) + ->onlyMethods(['doWarmUp']) ->getMock(); $warmer->method('doWarmUp')->willReturnCallback(function ($cacheDir, ArrayAdapter $arrayAdapter) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/RouterCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/RouterCacheWarmerTest.php index 61214b039c64a..727b566e1ddb3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/RouterCacheWarmerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/RouterCacheWarmerTest.php @@ -21,9 +21,9 @@ class RouterCacheWarmerTest extends TestCase { public function testWarmUpWithWarmebleInterface() { - $containerMock = $this->getMockBuilder(ContainerInterface::class)->setMethods(['get', 'has'])->getMock(); + $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); - $routerMock = $this->getMockBuilder(testRouterInterfaceWithWarmebleInterface::class)->setMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection', 'warmUp'])->getMock(); + $routerMock = $this->getMockBuilder(testRouterInterfaceWithWarmebleInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection', 'warmUp'])->getMock(); $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); $routerCacheWarmer = new RouterCacheWarmer($containerMock); @@ -34,9 +34,9 @@ public function testWarmUpWithWarmebleInterface() public function testWarmUpWithoutWarmebleInterface() { - $containerMock = $this->getMockBuilder(ContainerInterface::class)->setMethods(['get', 'has'])->getMock(); + $containerMock = $this->getMockBuilder(ContainerInterface::class)->onlyMethods(['get', 'has'])->getMock(); - $routerMock = $this->getMockBuilder(testRouterInterfaceWithoutWarmebleInterface::class)->setMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection'])->getMock(); + $routerMock = $this->getMockBuilder(testRouterInterfaceWithoutWarmebleInterface::class)->onlyMethods(['match', 'generate', 'getContext', 'setContext', 'getRouteCollection'])->getMock(); $containerMock->expects($this->any())->method('get')->with('router')->willReturn($routerMock); $routerCacheWarmer = new RouterCacheWarmer($containerMock); $this->expectException(\LogicException::class); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/SerializerCacheWarmerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/SerializerCacheWarmerTest.php index e64196f64d5c6..85dbd88104f57 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/SerializerCacheWarmerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/CacheWarmer/SerializerCacheWarmerTest.php @@ -40,7 +40,7 @@ public function testWarmUp(array $loaders) $this->assertTrue($arrayPool->getItem('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Author')->isHit()); } - public function loaderProvider() + public static function loaderProvider() { return [ [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php index 8a5a023e3b7ac..d3f8e2c9be5a4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php @@ -126,7 +126,7 @@ public function testCacheIsWarmedWithOldContainer() \Closure::bind(function (Container $class) { unset($class->loadedDynamicParameters['kernel.build_dir']); unset($class->parameters['kernel.build_dir']); - }, null, \get_class($container))($container); + }, null, $container::class)($container); $input = new ArrayInput(['cache:clear']); $application = new Application($kernel); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePoolClearCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePoolClearCommandTest.php index 722199f227296..12ae2bd39236e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePoolClearCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePoolClearCommandTest.php @@ -44,7 +44,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'pool_name' => [ ['f'], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePoolDeleteCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePoolDeleteCommandTest.php index 24371b9f532dd..b910489abb839 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePoolDeleteCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/CachePoolDeleteCommandTest.php @@ -98,7 +98,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'pool_name' => [ ['f'], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/EventDispatcherDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/EventDispatcherDebugCommandTest.php index a506ac2d2915f..9bc054f22a942 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/EventDispatcherDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/EventDispatcherDebugCommandTest.php @@ -31,7 +31,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'event' => [[''], ['Symfony\Component\Mailer\Event\MessageEvent', 'console.command']]; yield 'event for other dispatcher' => [['--dispatcher', 'other_event_dispatcher', ''], ['other_event', 'App\OtherEvent']]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/SecretsRemoveCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/SecretsRemoveCommandTest.php index c02bc91761084..88c247ec61ebc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/SecretsRemoveCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/SecretsRemoveCommandTest.php @@ -37,7 +37,7 @@ public function testComplete(bool $withLocalVault, array $input, array $expected $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'name' => [true, [''], ['SECRET', 'OTHER_SECRET']]; yield '--local name (with local vault)' => [true, ['--local', ''], ['SECRET']]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/SecretsSetCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/SecretsSetCommandTest.php index 135907b374d26..8e8e968c8f710 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/SecretsSetCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/SecretsSetCommandTest.php @@ -32,7 +32,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'name' => [[''], ['SECRET', 'OTHER_SECRET']]; yield '--local name (with local vault)' => [['--local', ''], ['SECRET', 'OTHER_SECRET']]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php index 70f94d6a34d48..846abd44500e0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationDebugCommandTest.php @@ -269,7 +269,7 @@ function ($path, $catalogue) use ($extractedMessagesWithDomains) { $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'locale' => [ [''], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandCompletionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandCompletionTest.php index 49ba74caf6a1b..91999d288266e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandCompletionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandCompletionTest.php @@ -42,7 +42,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { $bundle = new ExtensionPresentBundle(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php index 5c6fa8ec35ea2..f883fac0c57ce 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/TranslationUpdateCommandTest.php @@ -140,6 +140,46 @@ public function testWriteMessagesForSpecificDomain() $this->assertMatchesRegularExpression('/Translation files were successfully updated./', $tester->getDisplay()); } + public function testFilterDuplicateTransPaths() + { + $transPaths = [ + $this->translationDir.'/a/test/folder/with/a/subfolder', + $this->translationDir.'/a/test/folder/', + $this->translationDir.'/a/test/folder/with/a/subfolder/and/a/file.txt', + $this->translationDir.'/a/different/test/folder', + ]; + + foreach ($transPaths as $transPath) { + if (realpath($transPath)) { + continue; + } + + if (preg_match('/\.[a-z]+$/', $transPath)) { + if (!realpath(\dirname($transPath))) { + mkdir(\dirname($transPath), 0777, true); + } + + touch($transPath); + } else { + mkdir($transPath, 0777, true); + } + } + + $command = $this->createMock(TranslationUpdateCommand::class); + + $method = new \ReflectionMethod(TranslationUpdateCommand::class, 'filterDuplicateTransPaths'); + $method->setAccessible(true); + + $filteredTransPaths = $method->invoke($command, $transPaths); + + $expectedPaths = [ + realpath($this->translationDir.'/a/different/test/folder'), + realpath($this->translationDir.'/a/test/folder'), + ]; + + $this->assertEquals($expectedPaths, $filteredTransPaths); + } + protected function setUp(): void { $this->fs = new Filesystem(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/WorkflowDumpCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/WorkflowDumpCommandTest.php index cefc5dacd8e50..284e97623ad15 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Command/WorkflowDumpCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Command/WorkflowDumpCommandTest.php @@ -32,7 +32,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions(): iterable + public static function provideCompletionSuggestions(): iterable { yield 'option --dump-format' => [['--dump-format', ''], ['puml', 'mermaid', 'dot']]; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php index d075ad75f32c1..83c8553b2706d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/ApplicationTest.php @@ -258,14 +258,31 @@ private function getKernel(array $bundles, $useDispatcher = false) $container ->expects($this->exactly(2)) ->method('hasParameter') - ->withConsecutive(['console.command.ids'], ['console.lazy_command.ids']) - ->willReturnOnConsecutiveCalls(true, true) + ->willReturnCallback(function (...$args) { + static $series = [ + ['console.command.ids'], + ['console.lazy_command.ids'], + ]; + + $this->assertSame(array_shift($series), $args); + + return true; + }) ; + $container ->expects($this->exactly(2)) ->method('getParameter') - ->withConsecutive(['console.lazy_command.ids'], ['console.command.ids']) - ->willReturnOnConsecutiveCalls([], []) + ->willReturnCallback(function (...$args) { + static $series = [ + ['console.lazy_command.ids'], + ['console.command.ids'], + ]; + + $this->assertSame(array_shift($series), $args); + + return []; + }) ; $kernel = $this->createMock(KernelInterface::class); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTestCase.php similarity index 77% rename from src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTestCase.php index fa5a2711820f3..03cd8c7628f2d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/AbstractDescriptorTestCase.php @@ -24,7 +24,7 @@ use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; -abstract class AbstractDescriptorTest extends TestCase +abstract class AbstractDescriptorTestCase extends TestCase { private $colSize; @@ -45,9 +45,9 @@ public function testDescribeRouteCollection(RouteCollection $routes, $expectedDe $this->assertDescription($expectedDescription, $routes); } - public function getDescribeRouteCollectionTestData() + public static function getDescribeRouteCollectionTestData(): array { - return $this->getDescriptionTestData(ObjectsProvider::getRouteCollections()); + return static::getDescriptionTestData(ObjectsProvider::getRouteCollections()); } /** @dataProvider getDescribeRouteTestData */ @@ -56,9 +56,9 @@ public function testDescribeRoute(Route $route, $expectedDescription) $this->assertDescription($expectedDescription, $route); } - public function getDescribeRouteTestData() + public static function getDescribeRouteTestData(): array { - return $this->getDescriptionTestData(ObjectsProvider::getRoutes()); + return static::getDescriptionTestData(ObjectsProvider::getRoutes()); } /** @dataProvider getDescribeContainerParametersTestData */ @@ -67,9 +67,9 @@ public function testDescribeContainerParameters(ParameterBag $parameters, $expec $this->assertDescription($expectedDescription, $parameters); } - public function getDescribeContainerParametersTestData() + public static function getDescribeContainerParametersTestData(): array { - return $this->getDescriptionTestData(ObjectsProvider::getContainerParameters()); + return static::getDescriptionTestData(ObjectsProvider::getContainerParameters()); } /** @dataProvider getDescribeContainerBuilderTestData */ @@ -78,9 +78,9 @@ public function testDescribeContainerBuilder(ContainerBuilder $builder, $expecte $this->assertDescription($expectedDescription, $builder, $options); } - public function getDescribeContainerBuilderTestData() + public static function getDescribeContainerBuilderTestData(): array { - return $this->getContainerBuilderDescriptionTestData(ObjectsProvider::getContainerBuilders()); + return static::getContainerBuilderDescriptionTestData(ObjectsProvider::getContainerBuilders()); } /** @@ -91,9 +91,9 @@ public function testDescribeContainerExistingClassDefinition(Definition $definit $this->assertDescription($expectedDescription, $definition); } - public function getDescribeContainerExistingClassDefinitionTestData() + public static function getDescribeContainerExistingClassDefinitionTestData(): array { - return $this->getDescriptionTestData(ObjectsProvider::getContainerDefinitionsWithExistingClasses()); + return static::getDescriptionTestData(ObjectsProvider::getContainerDefinitionsWithExistingClasses()); } /** @dataProvider getDescribeContainerDefinitionTestData */ @@ -102,9 +102,9 @@ public function testDescribeContainerDefinition(Definition $definition, $expecte $this->assertDescription($expectedDescription, $definition); } - public function getDescribeContainerDefinitionTestData() + public static function getDescribeContainerDefinitionTestData(): array { - return $this->getDescriptionTestData(ObjectsProvider::getContainerDefinitions()); + return static::getDescriptionTestData(ObjectsProvider::getContainerDefinitions()); } /** @dataProvider getDescribeContainerDefinitionWithArgumentsShownTestData */ @@ -113,7 +113,7 @@ public function testDescribeContainerDefinitionWithArgumentsShown(Definition $de $this->assertDescription($expectedDescription, $definition, ['show_arguments' => true]); } - public function getDescribeContainerDefinitionWithArgumentsShownTestData() + public static function getDescribeContainerDefinitionWithArgumentsShownTestData(): array { $definitions = ObjectsProvider::getContainerDefinitions(); $definitionsWithArgs = []; @@ -124,7 +124,7 @@ public function getDescribeContainerDefinitionWithArgumentsShownTestData() $definitionsWithArgs['definition_arguments_with_enum'] = (new Definition('definition_with_enum'))->setArgument(0, FooUnitEnum::FOO); - return $this->getDescriptionTestData($definitionsWithArgs); + return static::getDescriptionTestData($definitionsWithArgs); } /** @dataProvider getDescribeContainerAliasTestData */ @@ -133,9 +133,9 @@ public function testDescribeContainerAlias(Alias $alias, $expectedDescription) $this->assertDescription($expectedDescription, $alias); } - public function getDescribeContainerAliasTestData() + public static function getDescribeContainerAliasTestData(): array { - return $this->getDescriptionTestData(ObjectsProvider::getContainerAliases()); + return static::getDescriptionTestData(ObjectsProvider::getContainerAliases()); } /** @dataProvider getDescribeContainerDefinitionWhichIsAnAliasTestData */ @@ -144,7 +144,7 @@ public function testDescribeContainerDefinitionWhichIsAnAlias(Alias $alias, $exp $this->assertDescription($expectedDescription, $builder, $options); } - public function getDescribeContainerDefinitionWhichIsAnAliasTestData() + public static function getDescribeContainerDefinitionWhichIsAnAliasTestData(): array { $builder = current(ObjectsProvider::getContainerBuilders()); $builder->setDefinition('service_1', $builder->getDefinition('definition_1')); @@ -157,7 +157,7 @@ public function getDescribeContainerDefinitionWhichIsAnAliasTestData() } $i = 0; - $data = $this->getDescriptionTestData($aliasesWithDefinitions); + $data = static::getDescriptionTestData($aliasesWithDefinitions); foreach ($aliases as $name => $alias) { $file = array_pop($data[$i]); $data[$i][] = $builder; @@ -175,9 +175,9 @@ public function testDescribeContainerParameter($parameter, $expectedDescription, $this->assertDescription($expectedDescription, $parameter, $options); } - public function getDescribeContainerParameterTestData() + public static function getDescribeContainerParameterTestData(): array { - $data = $this->getDescriptionTestData(ObjectsProvider::getContainerParameter()); + $data = static::getDescriptionTestData(ObjectsProvider::getContainerParameter()); $file = array_pop($data[0]); $data[0][] = ['parameter' => 'database_name']; @@ -195,9 +195,9 @@ public function testDescribeEventDispatcher(EventDispatcher $eventDispatcher, $e $this->assertDescription($expectedDescription, $eventDispatcher, $options); } - public function getDescribeEventDispatcherTestData() + public static function getDescribeEventDispatcherTestData(): array { - return $this->getEventDispatcherDescriptionTestData(ObjectsProvider::getEventDispatchers()); + return static::getEventDispatcherDescriptionTestData(ObjectsProvider::getEventDispatchers()); } /** @dataProvider getDescribeCallableTestData */ @@ -206,13 +206,14 @@ public function testDescribeCallable($callable, $expectedDescription) $this->assertDescription($expectedDescription, $callable); } - public function getDescribeCallableTestData(): array + public static function getDescribeCallableTestData(): array { - return $this->getDescriptionTestData(ObjectsProvider::getCallables()); + return static::getDescriptionTestData(ObjectsProvider::getCallables()); } /** * @group legacy + * * @dataProvider getDescribeDeprecatedCallableTestData */ public function testDescribeDeprecatedCallable($callable, $expectedDescription) @@ -220,9 +221,9 @@ public function testDescribeDeprecatedCallable($callable, $expectedDescription) $this->assertDescription($expectedDescription, $callable); } - public function getDescribeDeprecatedCallableTestData(): array + public static function getDescribeDeprecatedCallableTestData(): array { - return $this->getDescriptionTestData(ObjectsProvider::getDeprecatedCallables()); + return static::getDescriptionTestData(ObjectsProvider::getDeprecatedCallables()); } /** @dataProvider getClassDescriptionTestData */ @@ -231,7 +232,7 @@ public function testGetClassDescription($object, $expectedDescription) $this->assertEquals($expectedDescription, $this->getDescriptor()->getClassDescription($object)); } - public function getClassDescriptionTestData() + public static function getClassDescriptionTestData(): array { return [ [ClassWithDocCommentOnMultipleLines::class, 'This is the first line of the description. This is the second line.'], @@ -249,14 +250,14 @@ public function testGetDeprecations(ContainerBuilder $builder, $expectedDescript $this->assertDescription($expectedDescription, $builder, ['deprecations' => true]); } - public function getDeprecationsTestData() + public static function getDeprecationsTestData(): array { - return $this->getDescriptionTestData(ObjectsProvider::getContainerDeprecations()); + return static::getDescriptionTestData(ObjectsProvider::getContainerDeprecations()); } - abstract protected function getDescriptor(); + abstract protected static function getDescriptor(); - abstract protected function getFormat(); + abstract protected static function getFormat(); private function assertDescription($expectedDescription, $describedObject, array $options = []) { @@ -278,11 +279,11 @@ private function assertDescription($expectedDescription, $describedObject, array } } - private function getDescriptionTestData(iterable $objects) + private static function getDescriptionTestData(iterable $objects): array { $data = []; foreach ($objects as $name => $object) { - $file = sprintf('%s.%s', trim($name, '.'), $this->getFormat()); + $file = sprintf('%s.%s', trim($name, '.'), static::getFormat()); $description = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); $data[] = [$object, $description, $file]; } @@ -290,7 +291,7 @@ private function getDescriptionTestData(iterable $objects) return $data; } - private function getContainerBuilderDescriptionTestData(array $objects) + private static function getContainerBuilderDescriptionTestData(array $objects): array { $variations = [ 'services' => ['show_hidden' => true], @@ -303,7 +304,7 @@ private function getContainerBuilderDescriptionTestData(array $objects) $data = []; foreach ($objects as $name => $object) { foreach ($variations as $suffix => $options) { - $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, $this->getFormat()); + $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, static::getFormat()); $description = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); $data[] = [$object, $description, $options, $file]; } @@ -312,7 +313,7 @@ private function getContainerBuilderDescriptionTestData(array $objects) return $data; } - private function getEventDispatcherDescriptionTestData(array $objects) + private static function getEventDispatcherDescriptionTestData(array $objects): array { $variations = [ 'events' => [], @@ -322,7 +323,7 @@ private function getEventDispatcherDescriptionTestData(array $objects) $data = []; foreach ($objects as $name => $object) { foreach ($variations as $suffix => $options) { - $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, $this->getFormat()); + $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, static::getFormat()); $description = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); $data[] = [$object, $description, $options, $file]; } @@ -337,13 +338,13 @@ public function testDescribeContainerBuilderWithPriorityTags(ContainerBuilder $b $this->assertDescription($expectedDescription, $builder, $options); } - public function getDescribeContainerBuilderWithPriorityTagsTestData(): array + public static function getDescribeContainerBuilderWithPriorityTagsTestData(): array { $variations = ['priority_tag' => ['tag' => 'tag1']]; $data = []; foreach (ObjectsProvider::getContainerBuildersWithPriorityTags() as $name => $object) { foreach ($variations as $suffix => $options) { - $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, $this->getFormat()); + $file = sprintf('%s_%s.%s', trim($name, '.'), $suffix, static::getFormat()); $description = file_get_contents(__DIR__.'/../../Fixtures/Descriptor/'.$file); $data[] = [$object, $description, $options]; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/JsonDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/JsonDescriptorTest.php index ee03f65391f8a..296737a923547 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/JsonDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/JsonDescriptorTest.php @@ -13,14 +13,14 @@ use Symfony\Bundle\FrameworkBundle\Console\Descriptor\JsonDescriptor; -class JsonDescriptorTest extends AbstractDescriptorTest +class JsonDescriptorTest extends AbstractDescriptorTestCase { - protected function getDescriptor() + protected static function getDescriptor() { return new JsonDescriptor(); } - protected function getFormat() + protected static function getFormat() { return 'json'; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php index fbb5aaa962689..1a653b835a3d9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php @@ -13,14 +13,14 @@ use Symfony\Bundle\FrameworkBundle\Console\Descriptor\MarkdownDescriptor; -class MarkdownDescriptorTest extends AbstractDescriptorTest +class MarkdownDescriptorTest extends AbstractDescriptorTestCase { - protected function getDescriptor() + protected static function getDescriptor() { return new MarkdownDescriptor(); } - protected function getFormat() + protected static function getFormat() { return 'md'; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php index 67c7df375dfa6..94fcbcfa3bcd3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/ObjectsProvider.php @@ -138,6 +138,7 @@ public static function getContainerDefinitions() { $definition1 = new Definition('Full\\Qualified\\Class1'); $definition2 = new Definition('Full\\Qualified\\Class2'); + $definition3 = new Definition('Full\\Qualified\\Class3'); return [ 'definition_1' => $definition1 @@ -170,6 +171,9 @@ public static function getContainerDefinitions() ->addTag('tag2') ->addMethodCall('setMailer', [new Reference('mailer')]) ->setFactory([new Reference('factory.service'), 'get']), + '.definition_3' => $definition3 + ->setFile('/path/to/file') + ->setFactory([new Definition('Full\\Qualified\\FactoryClass'), 'get']), 'definition_without_class' => new Definition(), ]; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php index b844a60e7789b..55e410597344a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/TextDescriptorTest.php @@ -15,23 +15,23 @@ use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; use Symfony\Component\Routing\Route; -class TextDescriptorTest extends AbstractDescriptorTest +class TextDescriptorTest extends AbstractDescriptorTestCase { - private $fileLinkFormatter = null; + private static $fileLinkFormatter = null; - protected function getDescriptor() + protected static function getDescriptor() { - return new TextDescriptor($this->fileLinkFormatter); + return new TextDescriptor(static::$fileLinkFormatter); } - protected function getFormat() + protected static function getFormat() { return 'txt'; } - public function getDescribeRouteWithControllerLinkTestData() + public static function getDescribeRouteWithControllerLinkTestData() { - $getDescribeData = $this->getDescribeRouteTestData(); + $getDescribeData = static::getDescribeRouteTestData(); foreach ($getDescribeData as $key => &$data) { $routeStub = $data[0]; @@ -48,7 +48,7 @@ public function getDescribeRouteWithControllerLinkTestData() /** @dataProvider getDescribeRouteWithControllerLinkTestData */ public function testDescribeRouteWithControllerLink(Route $route, $expectedDescription) { - $this->fileLinkFormatter = new FileLinkFormatter('myeditor://open?file=%f&line=%l'); + static::$fileLinkFormatter = new FileLinkFormatter('myeditor://open?file=%f&line=%l'); parent::testDescribeRoute($route, str_replace('[:file:]', __FILE__, $expectedDescription)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/XmlDescriptorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/XmlDescriptorTest.php index 8cb9a71697f6b..5b263b8171716 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/XmlDescriptorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Console/Descriptor/XmlDescriptorTest.php @@ -13,14 +13,14 @@ use Symfony\Bundle\FrameworkBundle\Console\Descriptor\XmlDescriptor; -class XmlDescriptorTest extends AbstractDescriptorTest +class XmlDescriptorTest extends AbstractDescriptorTestCase { - protected function getDescriptor() + protected static function getDescriptor() { return new XmlDescriptor(); } - protected function getFormat() + protected static function getFormat() { return 'xml'; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php index f5dd7703eacfb..efdba819cdbdc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php @@ -364,6 +364,40 @@ public function testdenyAccessUnlessGranted() $controller->denyAccessUnlessGranted('foo'); } + /** + * @dataProvider provideDenyAccessUnlessGrantedSetsAttributesAsArray + */ + public function testdenyAccessUnlessGrantedSetsAttributesAsArray($attribute, $exceptionAttributes) + { + $authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + $authorizationChecker->method('isGranted')->willReturn(false); + + $container = new Container(); + $container->set('security.authorization_checker', $authorizationChecker); + + $controller = $this->createController(); + $controller->setContainer($container); + + try { + $controller->denyAccessUnlessGranted($attribute); + $this->fail('there was no exception to check'); + } catch (AccessDeniedException $e) { + $this->assertSame($exceptionAttributes, $e->getAttributes()); + } + } + + public static function provideDenyAccessUnlessGrantedSetsAttributesAsArray() + { + $obj = new \stdClass(); + $obj->foo = 'bar'; + + return [ + 'string attribute' => ['foo', ['foo']], + 'array attribute' => [[1, 3, 3, 7], [[1, 3, 3, 7]]], + 'object attribute' => [$obj, [$obj]], + ]; + } + public function testRenderViewTwig() { $twig = $this->createMock(Environment::class); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php index 5fb3e774a709d..6f5b870e3350e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php @@ -172,7 +172,7 @@ class ContainerAwareController implements ContainerAwareInterface { private $container; - public function setContainer(ContainerInterface $container = null) + public function setContainer(?ContainerInterface $container) { $this->container = $container; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php index 70ccf7c97cf5e..de72396df6ad5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/RedirectControllerTest.php @@ -103,7 +103,7 @@ public function testRoute($permanent, $keepRequestMethod, $keepQueryParams, $ign $this->assertEquals($expectedCode, $returnResponse->getStatusCode()); } - public function provider() + public static function provider() { return [ [true, false, false, false, 301, ['additional-parameter' => 'value']], @@ -210,7 +210,7 @@ public function testUrlRedirectDefaultPorts() $this->assertRedirectUrl($returnValue, $expectedUrl); } - public function urlRedirectProvider() + public static function urlRedirectProvider() { return [ // Standard ports @@ -262,7 +262,7 @@ public function testUrlRedirect($scheme, $httpPort, $httpsPort, $requestScheme, $this->assertRedirectUrl($returnValue, $expectedUrl); } - public function pathQueryParamsProvider() + public static function pathQueryParamsProvider() { return [ ['http://www.example.com/base/redirect-path', '/redirect-path', ''], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php index 0167f55101b7b..a5d58edbb4226 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php @@ -65,7 +65,7 @@ public function testProcessKeepsDataCollectorIfTranslatorImplementsTranslatorBag $this->assertTrue($this->container->hasDefinition('data_collector.translation')); } - public function getImplementingTranslatorBagInterfaceTranslatorClassNames() + public static function getImplementingTranslatorBagInterfaceTranslatorClassNames() { return [ ['Symfony\Component\Translation\Translator'], @@ -97,7 +97,7 @@ public function testProcessRemovesDataCollectorIfTranslatorDoesNotImplementTrans $this->assertFalse($this->container->hasDefinition('data_collector.translation')); } - public function getNotImplementingTranslatorBagInterfaceTranslatorClassNames() + public static function getNotImplementingTranslatorBagInterfaceTranslatorClassNames() { return [ ['Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TranslatorWithTranslatorBag'], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php index 65f047426ae44..4844b4e9a922a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php @@ -62,7 +62,7 @@ public function testValidCollector() $this->assertEquals('add', $methodCalls[0][0]); // grab the method part of the first call } - public function provideValidCollectorWithTemplateUsingAutoconfigure(): \Generator + public static function provideValidCollectorWithTemplateUsingAutoconfigure(): \Generator { yield [new class() implements TemplateAwareDataCollectorInterface { public function collect(Request $request, Response $response, \Throwable $exception = null) @@ -105,12 +105,12 @@ public function testValidCollectorWithTemplateUsingAutoconfigure(TemplateAwareDa $profilerDefinition = $container->register('profiler', 'ProfilerClass'); $container->registerForAutoconfiguration(DataCollectorInterface::class)->addTag('data_collector'); - $container->register('mydatacollector', \get_class($dataCollector))->setAutoconfigured(true); + $container->register('mydatacollector', $dataCollector::class)->setAutoconfigured(true); (new ResolveInstanceofConditionalsPass())->process($container); (new ProfilerPass())->process($container); - $idForTemplate = \get_class($dataCollector); + $idForTemplate = $dataCollector::class; $this->assertSame(['mydatacollector' => [$idForTemplate, 'foo']], $container->getParameter('data_collector.templates')); // grab the method calls off of the "profiler" definition diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TestServiceContainerRefPassesTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TestServiceContainerRefPassesTest.php index 7dc9e6f59ec99..355b1527d64bf 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TestServiceContainerRefPassesTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/TestServiceContainerRefPassesTest.php @@ -43,6 +43,13 @@ public function testProcess() ->setPublic(true) ->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.42']) ; + $container->register('Test\soon_private_service_decorated') + ->setPublic(true) + ->addTag('container.private', ['package' => 'foo/bar', 'version' => '1.42']) + ; + $container->register('Test\soon_private_service_decorator') + ->setDecoratedService('Test\soon_private_service_decorated') + ->setArguments(['Test\soon_private_service_decorator.inner']); $container->register('Test\private_used_shared_service'); $container->register('Test\private_unused_shared_service'); @@ -55,6 +62,8 @@ public function testProcess() 'Test\private_used_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_shared_service')), 'Test\private_used_non_shared_service' => new ServiceClosureArgument(new Reference('Test\private_used_non_shared_service')), 'Test\soon_private_service' => new ServiceClosureArgument(new Reference('.container.private.Test\soon_private_service')), + 'Test\soon_private_service_decorator' => new ServiceClosureArgument(new Reference('.container.private.Test\soon_private_service_decorated')), + 'Test\soon_private_service_decorated' => new ServiceClosureArgument(new Reference('.container.private.Test\soon_private_service_decorated')), ]; $privateServices = $container->getDefinition('test.private_services_locator')->getArgument(0); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index a62c593e48a33..a9917f69cc42b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -61,7 +61,7 @@ public function testInvalidSessionName($sessionName) ); } - public function getTestInvalidSessionName() + public static function getTestInvalidSessionName() { return [ ['a.b'], @@ -115,7 +115,7 @@ public function testValidAssetsPackageNameConfiguration($packageName) $this->assertArrayHasKey($packageName, $config['assets']['packages']); } - public function provideValidAssetsPackageNameConfigurationTests() + public static function provideValidAssetsPackageNameConfigurationTests() { return [ ['foobar'], @@ -142,7 +142,7 @@ public function testInvalidAssetsConfiguration(array $assetConfig, $expectedMess ]); } - public function provideInvalidAssetConfigurationTests() + public static function provideInvalidAssetConfigurationTests() { // helper to turn config into embedded package config $createPackageConfig = function (array $packageConfig) { @@ -196,7 +196,7 @@ public function testValidLockConfiguration($lockConfig, $processedConfig) $this->assertEquals($processedConfig, $config['lock']); } - public function provideValidLockConfigurationTests() + public static function provideValidLockConfigurationTests() { yield [null, ['enabled' => true, 'resources' => ['default' => [class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphore' : 'flock']]]]; @@ -290,7 +290,7 @@ public function testValidSemaphoreConfiguration($semaphoreConfig, $processedConf $this->assertEquals($processedConfig, $config['semaphore']); } - public function provideValidSemaphoreConfigurationTests() + public static function provideValidSemaphoreConfigurationTests() { yield [null, ['enabled' => true, 'resources' => []]]; @@ -383,19 +383,19 @@ public function testBusMiddlewareDontMerge() $this->assertEquals( [ 'existing_bus' => [ - 'default_middleware' => true, + 'default_middleware' => ['enabled' => true, 'allow_no_handlers' => false, 'allow_no_senders' => true], 'middleware' => [ ['id' => 'existing_bus.middleware', 'arguments' => []], ], ], 'common_bus' => [ - 'default_middleware' => false, + 'default_middleware' => ['enabled' => false, 'allow_no_handlers' => false, 'allow_no_senders' => true], 'middleware' => [ ['id' => 'common_bus.new_middleware', 'arguments' => []], ], ], 'new_bus' => [ - 'default_middleware' => true, + 'default_middleware' => ['enabled' => true, 'allow_no_handlers' => false, 'allow_no_senders' => true], 'middleware' => [ ['id' => 'new_bus.middleware', 'arguments' => []], ], @@ -427,6 +427,37 @@ public function testItErrorsWhenDefaultBusDoesNotExist() ]); } + public function testLockCanBeDisabled() + { + $processor = new Processor(); + $configuration = new Configuration(true); + + $config = $processor->processConfiguration($configuration, [ + [ + 'http_method_override' => false, + 'lock' => ['enabled' => false], + ], + ]); + + $this->assertFalse($config['lock']['enabled']); + } + + public function testEnabledLockNeedsResources() + { + $processor = new Processor(); + $configuration = new Configuration(true); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('Invalid configuration for path "framework.lock": At least one resource must be defined.'); + + $processor->processConfiguration($configuration, [ + [ + 'http_method_override' => false, + 'lock' => ['enabled' => true], + ], + ]); + } + protected static function getBundleDefaultConfig() { return [ @@ -532,6 +563,7 @@ protected static function getBundleDefaultConfig() 'https_port' => 443, 'strict_requirements' => true, 'utf8' => true, + 'cache_dir' => '%kernel.cache_dir%', ], 'session' => [ 'enabled' => false, @@ -606,7 +638,7 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor ], ], 'default_bus' => null, - 'buses' => ['messenger.bus.default' => ['default_middleware' => true, 'middleware' => []]], + 'buses' => ['messenger.bus.default' => ['default_middleware' => ['enabled' => true, 'allow_no_handlers' => false, 'allow_no_senders' => true], 'middleware' => []]], 'reset_on_message' => true, ], 'disallow_search_engine_index' => true, @@ -656,7 +688,6 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor 'sanitizers' => [], ], 'exceptions' => [], - 'catch_all_throwables' => false, ]; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/exceptions.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/exceptions.php index 4d9fd94862377..be0663972ec5b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/exceptions.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/exceptions.php @@ -1,6 +1,9 @@ loadFromExtension('framework', [ 'http_method_override' => false, @@ -9,5 +12,17 @@ 'log_level' => 'info', 'status_code' => 422, ], + NotFoundHttpException::class => [ + 'log_level' => 'info', + 'status_code' => null, + ], + ConflictHttpException::class => [ + 'log_level' => 'info', + 'status_code' => 0, + ], + ServiceUnavailableHttpException::class => [ + 'log_level' => null, + 'status_code' => 500, + ], ], ]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_default_csrf.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_default_csrf.php index 0c491714a05a9..1f2556a5cfcbe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_default_csrf.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/form_default_csrf.php @@ -2,9 +2,6 @@ $container->loadFromExtension('framework', [ 'http_method_override' => false, - 'form' => [ - 'legacy_error_messages' => false, - ], 'session' => [ 'storage_factory_id' => 'session.storage.factory.native', 'handler_id' => null, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php index 8236fced45021..19f22f2c78c99 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/messenger_transports.php @@ -20,6 +20,7 @@ 'multiplier' => 3, 'max_delay' => 100, ], + 'rate_limiter' => 'customised_worker' ], 'failed' => 'in-memory:///', 'redis' => 'redis://127.0.0.1:6379/messages', diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping_without_annotations.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping_without_annotations.php new file mode 100644 index 0000000000000..e0d293d55f202 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping_without_annotations.php @@ -0,0 +1,15 @@ +loadFromExtension('framework', [ + 'http_method_override' => false, + 'serializer' => [ + 'enable_annotations' => false, + 'mapping' => [ + 'paths' => [ + '%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files', + '%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml', + '%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml', + ], + ], + ], +]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_not_valid.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_not_valid.php index efb5767a3b535..c70631e77f572 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_not_valid.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_not_valid.php @@ -1,6 +1,6 @@ loadFromExtension('framework', [ 'http_method_override' => false, @@ -8,7 +8,7 @@ 'my_workflow' => [ 'type' => 'state_machine', 'supports' => [ - FrameworkExtensionTest::class, + FrameworkExtensionTestCase::class, ], 'places' => [ 'first', diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_guard_expression.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_guard_expression.php index 37a1d7ac73007..bf9e56161d89b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_guard_expression.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_guard_expression.php @@ -1,6 +1,6 @@ loadFromExtension('framework', [ 'http_method_override' => false, @@ -8,7 +8,7 @@ 'article' => [ 'type' => 'workflow', 'supports' => [ - FrameworkExtensionTest::class, + FrameworkExtensionTestCase::class, ], 'initial_marking' => ['draft'], 'places' => [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php index 2ba2b3491c705..bb55be79018a6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php @@ -1,6 +1,6 @@ loadFromExtension('framework', [ 'http_method_override' => false, @@ -8,7 +8,7 @@ 'article' => [ 'type' => 'workflow', 'supports' => [ - FrameworkExtensionTest::class, + FrameworkExtensionTestCase::class, ], 'initial_marking' => ['draft'], 'places' => [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_no_events_to_dispatch.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_no_events_to_dispatch.php index 81fe174b1b2a3..b1ccc416dc6c2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_no_events_to_dispatch.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_no_events_to_dispatch.php @@ -1,6 +1,6 @@ loadFromExtension('framework', [ 'http_method_override' => false, @@ -12,7 +12,7 @@ 'property' => 'state', ], 'supports' => [ - FrameworkExtensionTest::class, + FrameworkExtensionTestCase::class, ], 'events_to_dispatch' => [], 'places' => [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_specified_events_to_dispatch.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_specified_events_to_dispatch.php index 1c434cd99c4ee..945db87a09bb3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_specified_events_to_dispatch.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_specified_events_to_dispatch.php @@ -1,6 +1,6 @@ loadFromExtension('framework', [ 'http_method_override' => false, @@ -12,7 +12,7 @@ 'property' => 'state', ], 'supports' => [ - FrameworkExtensionTest::class, + FrameworkExtensionTestCase::class, ], 'events_to_dispatch' => [ 'workflow.leave', diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php index 120bb364722dc..1443d3128315c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php @@ -1,6 +1,6 @@ loadFromExtension('framework', [ 'http_method_override' => false, @@ -8,7 +8,7 @@ 'my_workflow' => [ 'type' => 'workflow', 'supports' => [ - FrameworkExtensionTest::class, + FrameworkExtensionTestCase::class, ], 'support_strategy' => 'foobar', 'places' => [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php index 5cdb2563ac2b1..209d10184b045 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows.php @@ -1,6 +1,6 @@ loadFromExtension('framework', [ 'http_method_override' => false, @@ -8,7 +8,7 @@ 'article' => [ 'type' => 'workflow', 'supports' => [ - FrameworkExtensionTest::class, + FrameworkExtensionTestCase::class, ], 'initial_marking' => ['draft'], 'metadata' => [ @@ -44,7 +44,7 @@ ], 'pull_request' => [ 'supports' => [ - FrameworkExtensionTest::class, + FrameworkExtensionTestCase::class, ], 'initial_marking' => 'start', 'metadata' => [ @@ -103,7 +103,7 @@ 'service' => 'workflow_service', ], 'supports' => [ - FrameworkExtensionTest::class, + FrameworkExtensionTestCase::class, ], 'places' => [ ['name' => 'first'], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_explicitly_enabled.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_explicitly_enabled.php index ad71b2729853f..8da47f9f36609 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_explicitly_enabled.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_explicitly_enabled.php @@ -1,12 +1,14 @@ loadFromExtension('framework', [ 'http_method_override' => false, 'workflows' => [ 'enabled' => true, 'foo' => [ 'type' => 'workflow', - 'supports' => ['Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest'], + 'supports' => [FrameworkExtensionTestCase::class], 'initial_marking' => ['bar'], 'places' => ['bar', 'baz'], 'transitions' => [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_explicitly_enabled_named_workflows.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_explicitly_enabled_named_workflows.php index 49ab48914e1ba..440e47e1b7285 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_explicitly_enabled_named_workflows.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/workflows_explicitly_enabled_named_workflows.php @@ -1,12 +1,14 @@ loadFromExtension('framework', [ 'http_method_override' => false, 'workflows' => [ 'enabled' => true, 'workflows' => [ 'type' => 'workflow', - 'supports' => ['Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest'], + 'supports' => [FrameworkExtensionTestCase::class], 'initial_marking' => ['bar'], 'places' => ['bar', 'baz'], 'transitions' => [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml index 97104d0571e52..53566a99ed449 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml @@ -8,7 +8,6 @@ - diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml index d40de006d7316..4a877c1eb3499 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions.xml @@ -6,8 +6,25 @@ http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - - - + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml new file mode 100644 index 0000000000000..2e6048fa7c7aa --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/exceptions_legacy.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml index 55616c2d6a7ad..19245eb18c542 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml @@ -9,6 +9,5 @@ - diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml index cf88a085adc5e..d2147ae32db48 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml @@ -8,7 +8,6 @@ - diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_default_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_default_csrf.xml index c2700a6b20a76..f698e9aa8bb5f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_default_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_default_csrf.xml @@ -7,7 +7,7 @@ http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml index afb1869c5ab5d..facd62112d0e4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml @@ -7,7 +7,7 @@ http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml index 00da2a6c0f963..b2f5473d7d7df 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -10,7 +10,7 @@ fr en - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml index d78c802810ae6..28e27e380bfe0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/messenger_transports.xml @@ -10,7 +10,7 @@ - + Queue diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/rate_limiter.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/rate_limiter.xml new file mode 100644 index 0000000000000..cce1f67991177 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/rate_limiter.xml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping_without_annotations.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping_without_annotations.xml new file mode 100644 index 0000000000000..971dd1fd4fea7 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping_without_annotations.xml @@ -0,0 +1,16 @@ + + + + + + + + %kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files + %kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml + %kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_not_valid.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_not_valid.xml index 12bc1b463b68d..88fb256d55630 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_not_valid.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_not_valid.xml @@ -8,7 +8,7 @@ - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_guard_expression.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_guard_expression.xml index 1a953f96352e6..a1f489497198f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_guard_expression.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_guard_expression.xml @@ -9,7 +9,7 @@ draft - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase draft wait_for_journalist approved_by_journalist diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml index 3bf4657eccac3..cf610b6443a26 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml @@ -9,7 +9,7 @@ draft - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_no_events_to_dispatch.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_no_events_to_dispatch.xml index 4fc6a02341fd1..577613699ddc0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_no_events_to_dispatch.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_no_events_to_dispatch.xml @@ -10,7 +10,7 @@ one - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_specified_events_to_dispatch.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_specified_events_to_dispatch.xml index 48091a9b01184..219ffe87c5a8c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_specified_events_to_dispatch.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_specified_events_to_dispatch.xml @@ -10,7 +10,7 @@ one - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase workflow.leave workflow.completed diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml index 139259826af2e..6491e172bcf42 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml @@ -8,7 +8,7 @@ - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml index 5e9f1d1bff700..e9c28666098e2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml @@ -10,8 +10,7 @@ draft - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase @@ -45,7 +44,7 @@ start - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase place start title @@ -96,7 +95,7 @@ - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase first last diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_explicitly_enabled.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_explicitly_enabled.xml index a3d7f8bfa623b..226c09814c2f2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_explicitly_enabled.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_explicitly_enabled.xml @@ -7,7 +7,7 @@ - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase bar baz diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_explicitly_enabled_named_workflows.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_explicitly_enabled_named_workflows.xml index 3569297c45784..59d91e76da315 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_explicitly_enabled_named_workflows.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/workflows_explicitly_enabled_named_workflows.xml @@ -8,7 +8,7 @@ bar - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase bar baz diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/exceptions.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/exceptions.yml index 9ee4351dc760e..88e3b0da04d5a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/exceptions.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/exceptions.yml @@ -4,3 +4,12 @@ framework: Symfony\Component\HttpKernel\Exception\BadRequestHttpException: log_level: info status_code: 422 + Symfony\Component\HttpKernel\Exception\NotFoundHttpException: + log_level: info + status_code: null + Symfony\Component\HttpKernel\Exception\ConflictHttpException: + log_level: info + status_code: 0 + Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException: + log_level: null + status_code: 500 diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_default_csrf.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_default_csrf.yml index d6e9d77b93678..51a14623654be 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_default_csrf.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/form_default_csrf.yml @@ -1,7 +1,5 @@ framework: http_method_override: false - form: - legacy_error_messages: false session: storage_factory_id: session.storage.factory.native handler_id: null diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml index b16f9b6a8f09d..24471939c5435 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/messenger_transports.yml @@ -18,6 +18,7 @@ framework: delay: 7 multiplier: 3 max_delay: 100 + rate_limiter: customised_worker failed: 'in-memory:///' redis: 'redis://127.0.0.1:6379/messages' beanstalkd: 'beanstalkd://127.0.0.1:11300' diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping_without_annotations.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping_without_annotations.yml new file mode 100644 index 0000000000000..ca0647c06683d --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping_without_annotations.yml @@ -0,0 +1,9 @@ +framework: + http_method_override: false + serializer: + enable_annotations: false + mapping: + paths: + - "%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files" + - "%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml" + - "%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml" diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_not_valid.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_not_valid.yml index 29eeb8e9ad18d..f651f087936b8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_not_valid.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_not_valid.yml @@ -4,7 +4,7 @@ framework: my_workflow: type: state_machine supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase places: [first, middle, last] transitions: go: diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_guard_expression.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_guard_expression.yml index 6855c013053b2..7d56712d47b53 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_guard_expression.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_guard_expression.yml @@ -4,7 +4,7 @@ framework: article: type: workflow supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase initial_marking: [draft] places: - draft diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml index 384bd47170bfa..38f066fffa173 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml @@ -4,7 +4,7 @@ framework: article: type: workflow supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase initial_marking: [draft] places: - draft diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_no_events_to_dispatch.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_no_events_to_dispatch.yml index c511543df056d..7686463c42884 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_no_events_to_dispatch.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_no_events_to_dispatch.yml @@ -9,7 +9,7 @@ framework: type: method property: state supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase places: - one - two diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_specified_events_to_dispatch.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_specified_events_to_dispatch.yml index 703195b557664..1d6067fbc0180 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_specified_events_to_dispatch.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_specified_events_to_dispatch.yml @@ -9,7 +9,7 @@ framework: type: method property: state supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase places: - one - two diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml index a57bc555b6fb7..bab7d22a464a4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml @@ -4,7 +4,7 @@ framework: my_workflow: type: workflow supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase support_strategy: foobar places: - first diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml index ff1b926b09997..f0f57d89e236e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml @@ -4,7 +4,7 @@ framework: article: type: workflow supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase initial_marking: [draft] metadata: title: article workflow @@ -32,7 +32,7 @@ framework: to: [published] pull_request: supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase initial_marking: start metadata: title: workflow title @@ -75,7 +75,7 @@ framework: marking_store: service: workflow_service supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase places: - { name: first } - { name: last } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_explicitly_enabled.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_explicitly_enabled.yml index 1ee334125e3d1..c4bfbe3e31310 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_explicitly_enabled.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_explicitly_enabled.yml @@ -6,7 +6,7 @@ framework: foo: type: workflow supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase initial_marking: [bar] places: - bar diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_explicitly_enabled_named_workflows.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_explicitly_enabled_named_workflows.yml index 0dcfc93a1c209..24d8f17416c04 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_explicitly_enabled_named_workflows.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/workflows_explicitly_enabled_named_workflows.yml @@ -5,7 +5,7 @@ framework: workflows: type: workflow supports: - - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTestCase initial_marking: [bar] places: - bar diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php similarity index 96% rename from src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php index 320483fb0dafc..0597ee1dfb265 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php @@ -37,6 +37,7 @@ use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; @@ -82,7 +83,7 @@ use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\TagAwareCacheInterface; -abstract class FrameworkExtensionTest extends TestCase +abstract class FrameworkExtensionTestCase extends TestCase { use ExpectDeprecationTrait; @@ -579,12 +580,34 @@ public function testExceptionsConfig() { $container = $this->createContainerFromFile('exceptions'); + $configuration = $container->getDefinition('exception_listener')->getArgument(3); + $this->assertSame([ - \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class => [ - 'log_level' => 'info', - 'status_code' => 422, - ], - ], $container->getDefinition('exception_listener')->getArgument(3)); + \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class, + \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class, + \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class, + \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class, + ], array_keys($configuration)); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => 422, + ], $configuration[\Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => null, + ], $configuration[\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => null, + ], $configuration[\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => null, + 'status_code' => 500, + ], $configuration[\Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class]); } public function testRouter() @@ -637,9 +660,8 @@ public function testNullSessionHandler() { $container = $this->createContainerFromFile('session'); - $this->assertNull($container->getDefinition('session.storage.factory.native')->getArgument(1)); - $this->assertNull($container->getDefinition('session.storage.factory.php_bridge')->getArgument(0)); - $this->assertSame('session.handler.native_file', (string) $container->getAlias('session.handler')); + $this->assertNull($container->getParameter('session.save_path')); + $this->assertSame('session.handler.native', (string) $container->getAlias('session.handler')); $expected = ['session_factory', 'logger', 'session_collector']; $this->assertEquals($expected, array_keys($container->getDefinition('session_listener')->getArgument(0)->getValues())); @@ -949,6 +971,12 @@ public function testMessengerTransports() return array_shift($values); }, $failureTransports); $this->assertEquals($expectedTransportsByFailureTransports, $failureTransportsReferences); + + $rateLimitedTransports = $container->getDefinition('messenger.rate_limiter_locator')->getArgument(0); + $expectedRateLimitersByRateLimitedTransports = [ + 'customised' => new Reference('limiter.customised_worker'), + ]; + $this->assertEquals($expectedRateLimitersByRateLimitedTransports, $rateLimitedTransports); } public function testMessengerRouting() @@ -995,8 +1023,8 @@ public function testMessengerWithMultipleBuses() ['id' => 'reject_redelivered_message_middleware'], ['id' => 'dispatch_after_current_bus'], ['id' => 'failed_message_processing_middleware'], - ['id' => 'send_message'], - ['id' => 'handle_message'], + ['id' => 'send_message', 'arguments' => [true]], + ['id' => 'handle_message', 'arguments' => [false]], ], $container->getParameter('messenger.bus.commands.middleware')); $this->assertTrue($container->has('messenger.bus.events')); $this->assertSame([], $container->getDefinition('messenger.bus.events')->getArgument(0)); @@ -1006,8 +1034,8 @@ public function testMessengerWithMultipleBuses() ['id' => 'dispatch_after_current_bus'], ['id' => 'failed_message_processing_middleware'], ['id' => 'with_factory', 'arguments' => ['foo', true, ['bar' => 'baz']]], - ['id' => 'send_message'], - ['id' => 'handle_message'], + ['id' => 'send_message', 'arguments' => [true]], + ['id' => 'handle_message', 'arguments' => [false]], ], $container->getParameter('messenger.bus.events.middleware')); $this->assertTrue($container->has('messenger.bus.queries')); $this->assertSame([], $container->getDefinition('messenger.bus.queries')->getArgument(0)); @@ -1488,9 +1516,15 @@ public function testSerializerCacheActivated() $this->assertEquals(new Reference('serializer.mapping.cache.symfony'), $cache); } - public function testSerializerCacheNotActivatedDebug() + public function testSerializerCacheUsedWithoutAnnotationsAndMappingFiles() { - $container = $this->createContainerFromFile('serializer_enabled', ['kernel.debug' => true, 'kernel.container_class' => __CLASS__]); + $container = $this->createContainerFromFile('serializer_mapping_without_annotations', ['kernel.debug' => true, 'kernel.container_class' => __CLASS__]); + $this->assertTrue($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); + } + + public function testSerializerCacheNotActivatedWithAnnotations() + { + $container = $this->createContainerFromFile('serializer_mapping', ['kernel.debug' => true, 'kernel.container_class' => __CLASS__]); $this->assertFalse($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); } @@ -1693,7 +1727,7 @@ public function testAppRedisTagAwareAdapter(string $configFile) } } - public function appRedisTagAwareConfigProvider(): array + public static function appRedisTagAwareConfigProvider(): array { return [ ['cache_app_redis_tag_aware'], @@ -1908,7 +1942,7 @@ public function testHttpClientFullDefaultOptions() ], $defaultOptions['peer_fingerprint']); } - public function provideMailer(): array + public static function provideMailer(): array { return [ ['mailer_with_dsn', ['main' => 'smtp://example.com']], @@ -2049,7 +2083,9 @@ public function testLocaleSwitcherServiceRegistered() $this->markTestSkipped('LocaleSwitcher not available.'); } - $container = $this->createContainerFromFile('full'); + $container = $this->createContainerFromFile('full', compile: false); + $container->addCompilerPass(new ResolveTaggedIteratorArgumentPass()); + $container->compile(); $this->assertTrue($container->has('translation.locale_switcher')); @@ -2059,6 +2095,10 @@ public function testLocaleSwitcherServiceRegistered() $this->assertInstanceOf(TaggedIteratorArgument::class, $switcherDef->getArgument(1)); $this->assertSame('kernel.locale_aware', $switcherDef->getArgument(1)->getTag()); $this->assertEquals(new Reference('router.request_context', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE), $switcherDef->getArgument(2)); + + $localeAwareServices = array_map(fn (Reference $r) => (string) $r, $switcherDef->getArgument(1)->getValues()); + + $this->assertNotContains('translation.locale_switcher', $localeAwareServices); } public function testHtmlSanitizer() @@ -2125,7 +2165,9 @@ public function testHtmlSanitizerDefaultNullAllowedLinkMediaHost() $calls = $container->getDefinition('html_sanitizer.config.custom_default')->getMethodCalls(); $this->assertContains(['allowLinkHosts', [null], true], $calls); + $this->assertContains(['allowRelativeLinks', [false], true], $calls); $this->assertContains(['allowMediaHosts', [null], true], $calls); + $this->assertContains(['allowRelativeMedias', [false], true], $calls); } public function testHtmlSanitizerDefaultConfig() @@ -2262,26 +2304,14 @@ private function assertCachePoolServiceDefinitionIsCreated(ContainerBuilder $con $parentDefinition = $container->findDefinition($parentId); } while ($parentDefinition instanceof ChildDefinition); - switch ($adapter) { - case 'cache.adapter.apcu': - $this->assertSame(ApcuAdapter::class, $parentDefinition->getClass()); - break; - case 'cache.app': - case 'cache.adapter.filesystem': - $this->assertSame(FilesystemAdapter::class, $parentDefinition->getClass()); - break; - case 'cache.adapter.psr6': - $this->assertSame(ProxyAdapter::class, $parentDefinition->getClass()); - break; - case 'cache.adapter.redis': - $this->assertSame(RedisAdapter::class, $parentDefinition->getClass()); - break; - case 'cache.adapter.array': - $this->assertSame(ArrayAdapter::class, $parentDefinition->getClass()); - break; - default: - $this->fail('Unresolved adapter: '.$adapter); - } + match ($adapter) { + 'cache.adapter.apcu' => $this->assertSame(ApcuAdapter::class, $parentDefinition->getClass()), + 'cache.app', 'cache.adapter.filesystem' => $this->assertSame(FilesystemAdapter::class, $parentDefinition->getClass()), + 'cache.adapter.psr6' => $this->assertSame(ProxyAdapter::class, $parentDefinition->getClass()), + 'cache.adapter.redis' => $this->assertSame(RedisAdapter::class, $parentDefinition->getClass()), + 'cache.adapter.array' => $this->assertSame(ArrayAdapter::class, $parentDefinition->getClass()), + default => $this->fail('Unresolved adapter: '.$adapter), + }; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index 9c9f12a010ed1..1574377c15896 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\LogicException; @@ -18,7 +19,7 @@ use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use Symfony\Component\Workflow\Exception\InvalidDefinitionException; -class PhpFrameworkExtensionTest extends FrameworkExtensionTest +class PhpFrameworkExtensionTest extends FrameworkExtensionTestCase { protected function loadFromFile(ContainerBuilder $container, $file) { @@ -58,6 +59,36 @@ public function testAssetPackageCannotHavePathAndUrl() }); } + public function testWorkflowValidationPlacesIsArray() + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The "places" option must be an array in workflow configuration.'); + $this->createContainerFromClosure(function ($container) { + $container->loadFromExtension('framework', [ + 'workflows' => [ + 'article' => [ + 'places' => null, + ], + ], + ]); + }); + } + + public function testWorkflowValidationTransitonsIsArray() + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The "transitions" option must be an array in workflow configuration.'); + $this->createContainerFromClosure(function ($container) { + $container->loadFromExtension('framework', [ + 'workflows' => [ + 'article' => [ + 'transitions' => null, + ], + ], + ]); + }); + } + public function testWorkflowValidationStateMachine() { $this->expectException(InvalidDefinitionException::class); @@ -88,6 +119,63 @@ public function testWorkflowValidationStateMachine() }); } + public function testWorkflowDefaultMarkingStoreDefinition() + { + $container = $this->createContainerFromClosure(function ($container) { + $container->loadFromExtension('framework', [ + 'http_method_override' => false, + 'workflows' => [ + 'workflow_a' => [ + 'type' => 'state_machine', + 'marking_store' => [ + 'type' => 'method', + 'property' => 'status', + ], + 'supports' => [ + __CLASS__, + ], + 'places' => [ + 'a', + 'b', + ], + 'transitions' => [ + 'a_to_b' => [ + 'from' => ['a'], + 'to' => ['b'], + ], + ], + ], + 'workflow_b' => [ + 'type' => 'state_machine', + 'supports' => [ + __CLASS__, + ], + 'places' => [ + 'a', + 'b', + ], + 'transitions' => [ + 'a_to_b' => [ + 'from' => ['a'], + 'to' => ['b'], + ], + ], + ], + ], + ]); + }); + + $workflowA = $container->getDefinition('state_machine.workflow_a'); + $argumentsA = $workflowA->getArguments(); + $this->assertArrayHasKey('index_1', $argumentsA, 'workflow_a has a marking_store argument'); + $this->assertNotNull($argumentsA['index_1'], 'workflow_a marking_store argument is not null'); + + $workflowB = $container->getDefinition('state_machine.workflow_b'); + $argumentsB = $workflowB->getArguments(); + $this->assertArrayHasKey('index_1', $argumentsB, 'workflow_b has a marking_store argument'); + $this->assertNull($argumentsB['index_1'], 'workflow_b marking_store argument is null'); + } + public function testRateLimiterWithLockFactory() { try { @@ -103,7 +191,7 @@ public function testRateLimiterWithLockFactory() $this->fail('No LogicException thrown'); } catch (LogicException $e) { - $this->assertEquals('Rate limiter "with_lock" requires the Lock component to be installed and configured.', $e->getMessage()); + $this->assertEquals('Rate limiter "with_lock" requires the Lock component to be configured.', $e->getMessage()); } $container = $this->createContainerFromClosure(function (ContainerBuilder $container) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php index ebc37d93bed84..4a2ff788bf5c6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php @@ -14,8 +14,9 @@ use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\RateLimiter\Policy\SlidingWindowLimiter; -class XmlFrameworkExtensionTest extends FrameworkExtensionTest +class XmlFrameworkExtensionTest extends FrameworkExtensionTestCase { protected function loadFromFile(ContainerBuilder $container, $file) { @@ -32,4 +33,45 @@ public function testMessengerMiddlewareFactoryErroneousFormat() { $this->markTestSkipped('XML configuration will not allow erroneous format.'); } + + public function testLegacyExceptionsConfig() + { + $container = $this->createContainerFromFile('exceptions_legacy'); + + $configuration = $container->getDefinition('exception_listener')->getArgument(3); + + $this->assertSame([ + \Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class, + \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class, + \Symfony\Component\HttpKernel\Exception\ConflictHttpException::class, + \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class, + ], array_keys($configuration)); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => 422, + ], $configuration[\Symfony\Component\HttpKernel\Exception\BadRequestHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => null, + ], $configuration[\Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => 'info', + 'status_code' => null, + ], $configuration[\Symfony\Component\HttpKernel\Exception\ConflictHttpException::class]); + + $this->assertEqualsCanonicalizing([ + 'log_level' => null, + 'status_code' => 500, + ], $configuration[\Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException::class]); + } + + public function testRateLimiter() + { + $container = $this->createContainerFromFile('rate_limiter'); + + $this->assertTrue($container->hasDefinition('limiter.sliding_window')); + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php index 457467aa11d77..cb5a0a5e16f6f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -class YamlFrameworkExtensionTest extends FrameworkExtensionTest +class YamlFrameworkExtensionTest extends FrameworkExtensionTestCase { protected function loadFromFile(ContainerBuilder $container, $file) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ContainerExcluded.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ContainerExcluded.php new file mode 100644 index 0000000000000..2508b74276bc0 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/ContainerExcluded.php @@ -0,0 +1,7 @@ + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.json new file mode 100644 index 0000000000000..11768d0de1a45 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.json @@ -0,0 +1,16 @@ +{ + "class": "Full\\Qualified\\Class3", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "deprecated": false, + "file": "\/path\/to\/file", + "factory_service": "inline factory service (Full\\Qualified\\FactoryClass)", + "factory_method": "get", + "tags": [], + "usages": [] +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.md new file mode 100644 index 0000000000000..8a9651641d747 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.md @@ -0,0 +1,13 @@ +- Class: `Full\Qualified\Class3` +- Public: no +- Synthetic: no +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- Deprecated: no +- File: `/path/to/file` +- Factory Service: inline factory service (`Full\Qualified\FactoryClass`) +- Factory Method: `get` +- Usages: none diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.txt new file mode 100644 index 0000000000000..63cd01b592f51 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.txt @@ -0,0 +1,19 @@ + ----------------- ------------------------------------------------------ +  Option   Value  + ----------------- ------------------------------------------------------ + Service ID - + Class Full\Qualified\Class3 + Tags - + Public no + Synthetic no + Lazy no + Shared yes + Abstract no + Autowired no + Autoconfigured no + Required File /path/to/file + Factory Service inline factory service (Full\Qualified\FactoryClass) + Factory Method get + Usages none + ----------------- ------------------------------------------------------ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.xml new file mode 100644 index 0000000000000..ddd7dfb38ed26 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_3.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.json b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.json new file mode 100644 index 0000000000000..c96c06d63ad0e --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.json @@ -0,0 +1,17 @@ +{ + "class": "Full\\Qualified\\Class3", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "deprecated": false, + "arguments": [], + "file": "\/path\/to\/file", + "factory_service": "inline factory service (Full\\Qualified\\FactoryClass)", + "factory_method": "get", + "tags": [], + "usages": [] +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.md b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.md new file mode 100644 index 0000000000000..5bfafe3d0e28a --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.md @@ -0,0 +1,14 @@ +- Class: `Full\Qualified\Class3` +- Public: no +- Synthetic: no +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- Deprecated: no +- Arguments: no +- File: `/path/to/file` +- Factory Service: inline factory service (`Full\Qualified\FactoryClass`) +- Factory Method: `get` +- Usages: none diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.txt b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.txt new file mode 100644 index 0000000000000..cfde9dea018ec --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.txt @@ -0,0 +1,19 @@ + ----------------- ------------------------------------------------------ +  Option   Value  + ----------------- ------------------------------------------------------ + Service ID - + Class Full\Qualified\Class3 + Tags - + Public no + Synthetic no + Lazy no + Shared yes + Abstract no + Autowired no + Autoconfigured no + Required File /path/to/file + Factory Service inline factory service (Full\Qualified\FactoryClass) + Factory Method get + Usages none + ----------------- ------------------------------------------------------ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.xml new file mode 100644 index 0000000000000..ddd7dfb38ed26 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Descriptor/definition_arguments_3.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractAttributeRoutingTestCase.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractAttributeRoutingTestCase.php new file mode 100644 index 0000000000000..5166c8dda4384 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AbstractAttributeRoutingTestCase.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Component\HttpFoundation\Request; + +abstract class AbstractAttributeRoutingTestCase extends AbstractWebTestCase +{ + /** + * @dataProvider getRoutes + */ + public function testAnnotatedController(string $path, string $expectedValue) + { + $client = $this->createClient(['test_case' => $this->getTestCaseApp(), 'root_config' => 'config.yml']); + $client->request('GET', '/annotated'.$path); + + $this->assertSame(200, $client->getResponse()->getStatusCode()); + $this->assertSame($expectedValue, $client->getResponse()->getContent()); + + $router = self::getContainer()->get('router'); + + $this->assertSame('/annotated/create-transaction', $router->generate('symfony_framework_tests_functional_test_annotated_createtransaction')); + } + + public static function getRoutes(): array + { + return [ + ['/null_request', Request::class], + ['/null_argument', ''], + ['/null_argument_with_route_param', ''], + ['/null_argument_with_route_param/value', 'value'], + ['/argument_with_route_param_and_default', 'value'], + ['/argument_with_route_param_and_default/custom', 'custom'], + ]; + } + + abstract protected function getTestCaseApp(): string; +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AnnotatedControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AnnotatedControllerTest.php index a0be5fcef06a7..9ca913748d5b3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AnnotatedControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/AnnotatedControllerTest.php @@ -11,33 +11,10 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; -class AnnotatedControllerTest extends AbstractWebTestCase +class AnnotatedControllerTest extends AbstractAttributeRoutingTestCase { - /** - * @dataProvider getRoutes - */ - public function testAnnotatedController($path, $expectedValue) + protected function getTestCaseApp(): string { - $client = $this->createClient(['test_case' => 'AnnotatedController', 'root_config' => 'config.yml']); - $client->request('GET', '/annotated'.$path); - - $this->assertSame(200, $client->getResponse()->getStatusCode()); - $this->assertSame($expectedValue, $client->getResponse()->getContent()); - - $router = self::getContainer()->get('router'); - - $this->assertSame('/annotated/create-transaction', $router->generate('symfony_framework_tests_functional_test_annotated_createtransaction')); - } - - public function getRoutes() - { - return [ - ['/null_request', 'Symfony\Component\HttpFoundation\Request'], - ['/null_argument', ''], - ['/null_argument_with_route_param', ''], - ['/null_argument_with_route_param/value', 'value'], - ['/argument_with_route_param_and_default', 'value'], - ['/argument_with_route_param_and_default/custom', 'custom'], - ]; + return 'AnnotatedController'; } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php index f2f077786f2b7..2bee3d0b833e1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php @@ -17,42 +17,32 @@ class AnnotatedController { - /** - * @Route("/null_request", name="null_request") - */ - public function requestDefaultNullAction(Request $request = null) + #[Route('/null_request', name: 'null_request')] + public function requestDefaultNullAction(Request $request = null): Response { - return new Response($request ? \get_class($request) : null); + return new Response($request ? $request::class : null); } - /** - * @Route("/null_argument", name="null_argument") - */ - public function argumentDefaultNullWithoutRouteParamAction($value = null) + #[Route('/null_argument', name: 'null_argument')] + public function argumentDefaultNullWithoutRouteParamAction($value = null): Response { return new Response($value); } - /** - * @Route("/null_argument_with_route_param/{value}", name="null_argument_with_route_param") - */ - public function argumentDefaultNullWithRouteParamAction($value = null) + #[Route('/null_argument_with_route_param/{value}', name: 'null_argument_with_route_param')] + public function argumentDefaultNullWithRouteParamAction($value = null): Response { return new Response($value); } - /** - * @Route("/argument_with_route_param_and_default/{value}", defaults={"value": "value"}, name="argument_with_route_param_and_default") - */ - public function argumentWithoutDefaultWithRouteParamAndDefaultAction($value) + #[Route('/argument_with_route_param_and_default/{value}', defaults: ['value' => 'value'], name: 'argument_with_route_param_and_default')] + public function argumentWithoutDefaultWithRouteParamAndDefaultAction($value): Response { return new Response($value); } - /** - * @Route("/create-transaction") - */ - public function createTransaction() + #[Route('/create-transaction')] + public function createTransaction(): Response { return new Response(); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/EmailController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/EmailController.php index 1a871f79be907..4f479138cb073 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/EmailController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/EmailController.php @@ -15,6 +15,7 @@ use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\Part\DataPart; class EmailController { @@ -25,7 +26,7 @@ public function indexAction(MailerInterface $mailer) ->addCc('cc@symfony.com') ->text('Bar!') ->html('

Foo

') - ->attach(file_get_contents(__FILE__), 'foobar.php') + ->addPart(new DataPart(file_get_contents(__FILE__), 'foobar.php')) ); $mailer->send((new Email())->to('fabien@symfony.com', 'thomas@symfony.com')->from('fabien@symfony.com')->subject('Foo') @@ -33,7 +34,7 @@ public function indexAction(MailerInterface $mailer) ->addCc('cc@symfony.com') ->text('Bar!') ->html('

Foo

') - ->attach(file_get_contents(__FILE__), 'foobar.php') + ->addPart(new DataPart(file_get_contents(__FILE__), 'foobar.php')) ); return new Response(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SecurityController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SecurityController.php index d90d7512f24aa..28e3d141aaedc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SecurityController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/Controller/SecurityController.php @@ -30,9 +30,6 @@ public function profileAction() return new Response('Welcome '.$this->container->get('security.token_storage')->getToken()->getUserIdentifier().'!'); } - /** - * {@inheritdoc} - */ public static function getSubscribedServices(): array { return [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php index 2d88510520531..80cee0f1ae344 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php @@ -21,9 +21,6 @@ class TestExtension extends Extension implements PrependExtensionInterface { private $customConfig; - /** - * {@inheritdoc} - */ public function load(array $configs, ContainerBuilder $container) { $configuration = $this->getConfiguration($configs, $container); @@ -32,17 +29,11 @@ public function load(array $configs, ContainerBuilder $container) $container->setAlias('test.annotation_reader', new Alias('annotation_reader', true)); } - /** - * {@inheritdoc} - */ public function prepend(ContainerBuilder $container) { $container->prependExtensionConfig('test', ['custom' => 'foo']); } - /** - * {@inheritdoc} - */ public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface { return new Configuration($this->customConfig); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php new file mode 100644 index 0000000000000..72b2c12266d87 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CacheAttributeListenerTest.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\Cache; +use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; +use Symfony\Component\Security\Core\User\InMemoryUser; +use Symfony\Component\Security\Http\Attribute\IsGranted; + +class CacheAttributeListenerTest extends AbstractWebTestCase +{ + public function testAnonimousUserWithEtag() + { + $client = self::createClient(['test_case' => 'CacheAttributeListener']); + + $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]); + + self::assertTrue($client->getResponse()->isRedirect('http://localhost/login')); + } + + public function testAnonimousUserWithoutEtag() + { + $client = self::createClient(['test_case' => 'CacheAttributeListener']); + + $client->request('GET', '/'); + + self::assertTrue($client->getResponse()->isRedirect('http://localhost/login')); + } + + public function testLoggedInUserWithEtag() + { + $client = self::createClient(['test_case' => 'CacheAttributeListener']); + + $client->loginUser(new InMemoryUser('the-username', 'the-password', ['ROLE_USER'])); + $client->request('GET', '/', server: ['HTTP_IF_NONE_MATCH' => sprintf('"%s"', hash('sha256', '12345'))]); + + $response = $client->getResponse(); + + self::assertSame(304, $response->getStatusCode()); + self::assertSame('', $response->getContent()); + } + + public function testLoggedInUserWithoutEtag() + { + $client = self::createClient(['test_case' => 'CacheAttributeListener']); + + $client->loginUser(new InMemoryUser('the-username', 'the-password', ['ROLE_USER'])); + $client->request('GET', '/'); + + $response = $client->getResponse(); + + self::assertSame(200, $response->getStatusCode()); + self::assertSame('Hi there!', $response->getContent()); + } +} + +class TestEntityValueResolver implements ValueResolverInterface +{ + public function resolve(Request $request, ArgumentMetadata $argument): iterable + { + return Post::class === $argument->getType() ? [new Post()] : []; + } +} + +class Post +{ + public function getId(): int + { + return 1; + } + + public function getEtag(): string + { + return '12345'; + } +} + +class WithAttributesController +{ + #[IsGranted('ROLE_USER')] + #[Cache(etag: 'post.getEtag()')] + public function __invoke(Post $post): Response + { + return new Response('Hi there!'); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php index 2bebd2d77a3e9..2608966586a78 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/CachePoolsTest.php @@ -26,6 +26,7 @@ public function testCachePools() /** * @requires extension redis + * * @group integration */ public function testRedisCachePools() @@ -49,6 +50,7 @@ public function testRedisCachePools() /** * @requires extension redis + * * @group integration */ public function testRedisCustomCachePools() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDebugCommandTest.php index 09b99f82f7c64..927e616cc5ae4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDebugCommandTest.php @@ -23,36 +23,73 @@ */ class ConfigDebugCommandTest extends AbstractWebTestCase { - private $application; + /** + * @testWith [true] + * [false] + */ + public function testShowList(bool $debug) + { + $tester = $this->createCommandTester($debug); + $ret = $tester->execute([]); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertStringContainsString('Available registered bundles with their extension alias if available', $tester->getDisplay()); + $this->assertStringContainsString(' DefaultConfigTestBundle default_config_test', $tester->getDisplay()); + $this->assertStringContainsString(' ExtensionWithoutConfigTestBundle extension_without_config_test', $tester->getDisplay()); + $this->assertStringContainsString(' FrameworkBundle framework', $tester->getDisplay()); + $this->assertStringContainsString(' TestBundle test', $tester->getDisplay()); + $this->assertStringContainsString('Available registered non-bundle extension aliases', $tester->getDisplay()); + $this->assertStringContainsString(' foo', $tester->getDisplay()); + $this->assertStringContainsString(' test_dump', $tester->getDisplay()); + } - protected function setUp(): void + /** + * @testWith [true] + * [false] + */ + public function testDumpKernelExtension(bool $debug) { - $kernel = static::createKernel(['test_case' => 'ConfigDump', 'root_config' => 'config.yml']); - $this->application = new Application($kernel); - $this->application->doRun(new ArrayInput([]), new NullOutput()); + $tester = $this->createCommandTester($debug); + $ret = $tester->execute(['name' => 'foo']); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertStringContainsString('foo:', $tester->getDisplay()); + $this->assertStringContainsString(' foo: bar', $tester->getDisplay()); } - public function testDumpBundleName() + /** + * @testWith [true] + * [false] + */ + public function testDumpBundleName(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'TestBundle']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('custom: foo', $tester->getDisplay()); } - public function testDumpBundleOption() + /** + * @testWith [true] + * [false] + */ + public function testDumpBundleOption(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'TestBundle', 'path' => 'custom']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('foo', $tester->getDisplay()); } - public function testParametersValuesAreResolved() + /** + * @testWith [true] + * [false] + */ + public function testParametersValuesAreResolved(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'framework']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); @@ -60,89 +97,121 @@ public function testParametersValuesAreResolved() $this->assertStringContainsString('secret: test', $tester->getDisplay()); } - public function testParametersValuesAreFullyResolved() + /** + * @testWith [true] + * [false] + */ + public function testParametersValuesAreFullyResolved(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'framework', '--resolve-env' => true]); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('locale: en', $tester->getDisplay()); $this->assertStringContainsString('secret: test', $tester->getDisplay()); $this->assertStringContainsString('cookie_httponly: true', $tester->getDisplay()); - $this->assertStringContainsString('ide: null', $tester->getDisplay()); + $this->assertStringContainsString('ide: '.($_ENV['SYMFONY_IDE'] ?? $_SERVER['SYMFONY_IDE'] ?? 'null'), $tester->getDisplay()); } - public function testDefaultParameterValueIsResolvedIfConfigIsExisting() + /** + * @testWith [true] + * [false] + */ + public function testDefaultParameterValueIsResolvedIfConfigIsExisting(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'framework']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); - $kernelCacheDir = $this->application->getKernel()->getContainer()->getParameter('kernel.cache_dir'); + $kernelCacheDir = self::$kernel->getContainer()->getParameter('kernel.cache_dir'); $this->assertStringContainsString(sprintf("dsn: 'file:%s/profiler'", $kernelCacheDir), $tester->getDisplay()); } - public function testDumpExtensionConfigWithoutBundle() + /** + * @testWith [true] + * [false] + */ + public function testDumpExtensionConfigWithoutBundle(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'test_dump']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('enabled: true', $tester->getDisplay()); } - public function testDumpUndefinedBundleOption() + /** + * @testWith [true] + * [false] + */ + public function testDumpUndefinedBundleOption(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $tester->execute(['name' => 'TestBundle', 'path' => 'foo']); $this->assertStringContainsString('Unable to find configuration for "test.foo"', $tester->getDisplay()); } - public function testDumpWithPrefixedEnv() + /** + * @testWith [true] + * [false] + */ + public function testDumpWithPrefixedEnv(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $tester->execute(['name' => 'FrameworkBundle']); $this->assertStringContainsString("cookie_httponly: '%env(bool:COOKIE_HTTPONLY)%'", $tester->getDisplay()); } - public function testDumpFallsBackToDefaultConfigAndResolvesParameterValue() + /** + * @testWith [true] + * [false] + */ + public function testDumpFallsBackToDefaultConfigAndResolvesParameterValue(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'DefaultConfigTestBundle']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('foo: bar', $tester->getDisplay()); } - public function testDumpFallsBackToDefaultConfigAndResolvesEnvPlaceholder() + /** + * @testWith [true] + * [false] + */ + public function testDumpFallsBackToDefaultConfigAndResolvesEnvPlaceholder(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'DefaultConfigTestBundle']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString("baz: '%env(BAZ)%'", $tester->getDisplay()); } - public function testDumpThrowsExceptionWhenDefaultConfigFallbackIsImpossible() + /** + * @testWith [true] + * [false] + */ + public function testDumpThrowsExceptionWhenDefaultConfigFallbackIsImpossible(bool $debug) { $this->expectException(\LogicException::class); $this->expectExceptionMessage('The extension with alias "extension_without_config_test" does not have configuration.'); - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $tester->execute(['name' => 'ExtensionWithoutConfigTestBundle']); } /** * @dataProvider provideCompletionSuggestions */ - public function testComplete(array $input, array $expectedSuggestions) + public function testComplete(bool $debug, array $input, array $expectedSuggestions) { - $this->application->add(new ConfigDebugCommand()); - - $tester = new CommandCompletionTester($this->application->get('debug:config')); + $application = $this->createApplication($debug); + $application->add(new ConfigDebugCommand()); + $tester = new CommandCompletionTester($application->get('debug:config')); $suggestions = $tester->complete($input); foreach ($expectedSuggestions as $expectedSuggestion) { @@ -150,19 +219,30 @@ public function testComplete(array $input, array $expectedSuggestions) } } - public function provideCompletionSuggestions(): \Generator + public static function provideCompletionSuggestions(): \Generator { - yield 'name' => [[''], ['default_config_test', 'extension_without_config_test', 'framework', 'test']]; - - yield 'name (started CamelCase)' => [['Fra'], ['DefaultConfigTestBundle', 'ExtensionWithoutConfigTestBundle', 'FrameworkBundle', 'TestBundle']]; + $name = ['default_config_test', 'extension_without_config_test', 'framework', 'test', 'foo', 'test_dump']; + yield 'name, no debug' => [false, [''], $name]; + yield 'name, debug' => [true, [''], $name]; - yield 'name with existing path' => [['framework', ''], ['secret', 'router.resource', 'router.utf8', 'router.enabled', 'validation.enabled', 'default_locale']]; + $nameWithPath = ['secret', 'router.resource', 'router.utf8', 'router.enabled', 'validation.enabled', 'default_locale']; + yield 'name with existing path, no debug' => [false, ['framework', ''], $nameWithPath]; + yield 'name with existing path, debug' => [true, ['framework', ''], $nameWithPath]; } - private function createCommandTester(): CommandTester + private function createCommandTester(bool $debug): CommandTester { - $command = $this->application->find('debug:config'); + $command = $this->createApplication($debug)->find('debug:config'); return new CommandTester($command); } + + private function createApplication(bool $debug): Application + { + $kernel = static::bootKernel(['debug' => $debug, 'test_case' => 'ConfigDump', 'root_config' => 'config.yml']); + $application = new Application($kernel); + $application->doRun(new ArrayInput([]), new NullOutput()); + + return $application; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php index 4747b785f8149..8f5930faac2eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ConfigDumpReferenceCommandTest.php @@ -23,26 +23,47 @@ */ class ConfigDumpReferenceCommandTest extends AbstractWebTestCase { - private $application; - - protected function setUp(): void + /** + * @testWith [true] + * [false] + */ + public function testShowList(bool $debug) { - $kernel = static::createKernel(['test_case' => 'ConfigDump', 'root_config' => 'config.yml']); - $this->application = new Application($kernel); - $this->application->doRun(new ArrayInput([]), new NullOutput()); + $tester = $this->createCommandTester($debug); + $ret = $tester->execute([]); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertStringContainsString('Available registered bundles with their extension alias if available', $tester->getDisplay()); + $this->assertStringContainsString(' DefaultConfigTestBundle default_config_test', $tester->getDisplay()); + $this->assertStringContainsString(' ExtensionWithoutConfigTestBundle extension_without_config_test', $tester->getDisplay()); + $this->assertStringContainsString(' FrameworkBundle framework', $tester->getDisplay()); + $this->assertStringContainsString(' TestBundle test', $tester->getDisplay()); + $this->assertStringContainsString('Available registered non-bundle extension aliases', $tester->getDisplay()); + $this->assertStringContainsString(' foo', $tester->getDisplay()); + $this->assertStringContainsString(' test_dump', $tester->getDisplay()); } - public function testDumpKernelExtension() + /** + * @testWith [true] + * [false] + */ + public function testDumpKernelExtension(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'foo']); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('foo:', $tester->getDisplay()); $this->assertStringContainsString(' bar', $tester->getDisplay()); } - public function testDumpBundleName() + /** + * @testWith [true] + * [false] + */ + public function testDumpBundleName(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'TestBundle']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); @@ -50,18 +71,26 @@ public function testDumpBundleName() $this->assertStringContainsString(' custom:', $tester->getDisplay()); } - public function testDumpExtensionConfigWithoutBundle() + /** + * @testWith [true] + * [false] + */ + public function testDumpExtensionConfigWithoutBundle(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute(['name' => 'test_dump']); $this->assertSame(0, $ret, 'Returns 0 in case of success'); $this->assertStringContainsString('enabled: true', $tester->getDisplay()); } - public function testDumpAtPath() + /** + * @testWith [true] + * [false] + */ + public function testDumpAtPath(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute([ 'name' => 'test', 'path' => 'array', @@ -79,9 +108,13 @@ public function testDumpAtPath() , $tester->getDisplay(true)); } - public function testDumpAtPathXml() + /** + * @testWith [true] + * [false] + */ + public function testDumpAtPathXml(bool $debug) { - $tester = $this->createCommandTester(); + $tester = $this->createCommandTester($debug); $ret = $tester->execute([ 'name' => 'test', 'path' => 'array', @@ -95,24 +128,40 @@ public function testDumpAtPathXml() /** * @dataProvider provideCompletionSuggestions */ - public function testComplete(array $input, array $expectedSuggestions) + public function testComplete(bool $debug, array $input, array $expectedSuggestions) { - $this->application->add(new ConfigDumpReferenceCommand()); - $tester = new CommandCompletionTester($this->application->get('config:dump-reference')); - $suggestions = $tester->complete($input, 2); + $application = $this->createApplication($debug); + + $application->add(new ConfigDumpReferenceCommand()); + $tester = new CommandCompletionTester($application->get('config:dump-reference')); + $suggestions = $tester->complete($input); $this->assertSame($expectedSuggestions, $suggestions); } - public function provideCompletionSuggestions(): iterable + public static function provideCompletionSuggestions(): iterable { - yield 'name' => [[''], ['DefaultConfigTestBundle', 'default_config_test', 'ExtensionWithoutConfigTestBundle', 'extension_without_config_test', 'FrameworkBundle', 'framework', 'TestBundle', 'test']]; - yield 'option --format' => [['--format', ''], ['yaml', 'xml']]; + $name = ['foo', 'default_config_test', 'extension_without_config_test', 'framework', 'test', 'test_dump', 'DefaultConfigTestBundle', 'ExtensionWithoutConfigTestBundle', 'FrameworkBundle', 'TestBundle']; + yield 'name, no debug' => [false, [''], $name]; + yield 'name, debug' => [true, [''], $name]; + + $optionFormat = ['yaml', 'xml']; + yield 'option --format, no debug' => [false, ['--format', ''], $optionFormat]; + yield 'option --format, debug' => [true, ['--format', ''], $optionFormat]; } - private function createCommandTester(): CommandTester + private function createCommandTester(bool $debug): CommandTester { - $command = $this->application->find('config:dump-reference'); + $command = $this->createApplication($debug)->find('config:dump-reference'); return new CommandTester($command); } + + private function createApplication(bool $debug): Application + { + $kernel = static::createKernel(['debug' => $debug, 'test_case' => 'ConfigDump', 'root_config' => 'config.yml']); + $application = new Application($kernel); + $application->doRun(new ArrayInput([]), new NullOutput()); + + return $application; + } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php index d546dc2c9cf64..bb09c6aed0765 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ContainerDebugCommandTest.php @@ -13,6 +13,7 @@ use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BackslashClass; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ContainerExcluded; use Symfony\Component\Console\Tester\ApplicationTester; use Symfony\Component\Console\Tester\CommandCompletionTester; @@ -85,6 +86,19 @@ public function testDeprecatedServiceAndAlias() $this->assertStringContainsString('[WARNING] The "deprecated_alias" alias is deprecated since foo/bar 1.9 and will be removed in 2.0', $tester->getDisplay()); } + public function testExcludedService() + { + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml']); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + + $tester->run(['command' => 'debug:container']); + $this->assertStringNotContainsString(ContainerExcluded::class, $tester->getDisplay()); + } + /** * @dataProvider provideIgnoreBackslashWhenFindingService */ @@ -100,6 +114,27 @@ public function testIgnoreBackslashWhenFindingService(string $validServiceId) $this->assertStringNotContainsString('No services found', $tester->getDisplay()); } + public function testTagsPartialSearch() + { + static::bootKernel(['test_case' => 'ContainerDebug', 'root_config' => 'config.yml']); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->setInputs(['0']); + $tester->run(['command' => 'debug:container', '--tag' => 'kernel.'], ['decorated' => false]); + + $this->assertStringContainsString('Select one of the following tags to display its information', $tester->getDisplay()); + $this->assertStringContainsString('[0] kernel.event_subscriber', $tester->getDisplay()); + $this->assertStringContainsString('[1] kernel.locale_aware', $tester->getDisplay()); + $this->assertStringContainsString('[2] kernel.cache_warmer', $tester->getDisplay()); + $this->assertStringContainsString('[3] kernel.fragment_renderer', $tester->getDisplay()); + $this->assertStringContainsString('[4] kernel.reset', $tester->getDisplay()); + $this->assertStringContainsString('[5] kernel.cache_clearer', $tester->getDisplay()); + $this->assertStringContainsString('Symfony Container Services Tagged with "kernel.event_subscriber" Tag', $tester->getDisplay()); + } + public function testDescribeEnvVars() { putenv('REAL=value'); @@ -220,7 +255,7 @@ public function testGetDeprecationNoFile() $this->assertStringContainsString('[WARNING] The deprecation file does not exist', $tester->getDisplay()); } - public function provideIgnoreBackslashWhenFindingService() + public static function provideIgnoreBackslashWhenFindingService() { return [ [BackslashClass::class], @@ -248,7 +283,7 @@ public function testComplete(array $input, array $expectedSuggestions, array $no } } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { $serviceId = 'console.command.container_debug'; $hiddenServiceId = '.console.command.container_debug.lazy'; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/DebugAutowiringCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/DebugAutowiringCommandTest.php index c3110cc71dcbb..345589c1b6fc2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/DebugAutowiringCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/DebugAutowiringCommandTest.php @@ -129,7 +129,7 @@ public function testComplete(array $input, array $expectedSuggestions) } } - public function provideCompletionSuggestions(): \Generator + public static function provideCompletionSuggestions(): \Generator { yield 'search' => [[''], ['SessionHandlerInterface', 'Psr\\Log\\LoggerInterface', 'Psr\\Container\\ContainerInterface $parameterBag']]; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/FragmentTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/FragmentTest.php index b514ed3b8e042..6d8966a171ba2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/FragmentTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/FragmentTest.php @@ -37,7 +37,7 @@ public function testFragment($insulate) , $client->getResponse()->getContent()); } - public function getConfigs() + public static function getConfigs() { return [ [false], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php index 7b65ca5c276e8..d7825979536e5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/ProfilerTest.php @@ -57,7 +57,7 @@ public function testProfilerCollectParameter($insulate) $this->assertNull($client->getProfile()); } - public function getConfigs() + public static function getConfigs() { return [ [false], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Psr4RoutingTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Psr4RoutingTest.php new file mode 100644 index 0000000000000..811a99a112c0c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/Psr4RoutingTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +/** + * @requires function Symfony\Component\Routing\Loader\Psr4DirectoryLoader::__construct + */ +final class Psr4RoutingTest extends AbstractAttributeRoutingTestCase +{ + protected function getTestCaseApp(): string + { + return 'Psr4Routing'; + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/RouterDebugCommandTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/RouterDebugCommandTest.php index 7390a124c3c0a..9865e7edea6fe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/RouterDebugCommandTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/RouterDebugCommandTest.php @@ -96,7 +96,7 @@ public function testComplete(array $input, array $expectedSuggestions) $this->assertSame($expectedSuggestions, $tester->complete($input)); } - public function provideCompletionSuggestions() + public static function provideCompletionSuggestions() { yield 'option --format' => [ ['--format', ''], diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/RoutingConditionServiceTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/RoutingConditionServiceTest.php index 780060d7fc6f6..4f4caa6eb1567 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/RoutingConditionServiceTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/RoutingConditionServiceTest.php @@ -24,7 +24,7 @@ public function testCondition(int $code, string $path) $this->assertSame($code, $client->getResponse()->getStatusCode()); } - public function provideRoutes(): iterable + public static function provideRoutes(): iterable { yield 'allowed by an autoconfigured service' => [ 200, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SecurityTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SecurityTest.php index d97039562119c..c26fa717d9176 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SecurityTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SecurityTest.php @@ -33,7 +33,7 @@ public function testLoginUser(string $username, array $roles, ?string $firewallC $this->assertEquals('Welcome '.$username.'!', $client->getResponse()->getContent()); } - public function getUsers() + public static function getUsers() { yield ['the-username', ['ROLE_FOO'], null]; yield ['the-username', ['ROLE_FOO'], 'main']; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php index 019aa418901d8..9a6527b14dd62 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SerializerTest.php @@ -32,6 +32,41 @@ public function testDeserializeArrayOfObject() $this->assertEquals($expected, $result); } + + /** + * @dataProvider provideNormalizersAndEncodersWithDefaultContextOption + */ + public function testNormalizersAndEncodersUseDefaultContextConfigOption(string $normalizerId) + { + static::bootKernel(['test_case' => 'Serializer']); + + $normalizer = static::getContainer()->get($normalizerId); + + $reflectionObject = new \ReflectionObject($normalizer); + $property = $reflectionObject->getProperty('defaultContext'); + $property->setAccessible(true); + + $defaultContext = $property->getValue($normalizer); + + self::assertArrayHasKey('fake_context_option', $defaultContext); + self::assertEquals('foo', $defaultContext['fake_context_option']); + } + + public static function provideNormalizersAndEncodersWithDefaultContextOption(): array + { + return [ + ['serializer.normalizer.constraint_violation_list.alias'], + ['serializer.normalizer.dateinterval.alias'], + ['serializer.normalizer.datetime.alias'], + ['serializer.normalizer.json_serializable.alias'], + ['serializer.normalizer.problem.alias'], + ['serializer.normalizer.uid.alias'], + ['serializer.normalizer.object.alias'], + ['serializer.encoder.xml.alias'], + ['serializer.encoder.yaml.alias'], + ['serializer.encoder.csv.alias'], + ]; + } } class Foo diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php index ae8c7afafd425..4c1b92ccf539f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/SessionTest.php @@ -144,7 +144,7 @@ public function testCorrectCacheControlHeadersForCacheableAction($config, $insul $this->assertSame('public, s-maxage=100', $response->headers->get('cache-control')); } - public function getConfigs() + public static function getConfigs() { return [ // configfile, insulate diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/UidTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/UidTest.php index 28406b65bd70c..da0b1e4fc80d1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/UidTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/UidTest.php @@ -11,16 +11,11 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; -use Symfony\Bundle\FrameworkBundle\Test\ExceptionSubscriber; -use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\UidController; use Symfony\Component\Uid\Ulid; use Symfony\Component\Uid\UuidV1; use Symfony\Component\Uid\UuidV4; use Symfony\Component\Uid\UuidV6; -/** - * @see UidController - */ class UidTest extends AbstractWebTestCase { protected function setUp(): void @@ -33,15 +28,12 @@ protected function setUp(): void public function testArgumentValueResolverDisabled() { $client = $this->createClient(['test_case' => 'Uid', 'root_config' => 'config_disabled.yml']); + $client->catchExceptions(false); - $client->request('GET', '/1/uuid-v1/'.new UuidV1()); - $this->assertSame(500, $client->getResponse()->getStatusCode()); - $exceptions = ExceptionSubscriber::shiftAll(); - $this->assertCount(1, $exceptions); - $exception = reset($exceptions); + $this->expectException(\TypeError::class); + $this->expectExceptionMessage('Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\UidController::anyFormat(): Argument #1 ($userId) must be of type Symfony\Component\Uid\UuidV1, string given'); - $this->assertInstanceOf(\TypeError::class, $exception); - $this->assertStringContainsString('Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\UidController::anyFormat(): Argument #1 ($userId) must be of type Symfony\Component\Uid\UuidV1, string given', $exception->getMessage()); + $client->request('GET', '/1/uuid-v1/'.new UuidV1()); } public function testArgumentValueResolverEnabled() diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php new file mode 100644 index 0000000000000..9a26fb163a77d --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\SecurityBundle\SecurityBundle; + +return [ + new FrameworkBundle(), + new SecurityBundle(), +]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml new file mode 100644 index 0000000000000..21890451a1094 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/config.yml @@ -0,0 +1,24 @@ +imports: + - { resource: ../config/default.yml } + +services: + Symfony\Bundle\FrameworkBundle\Tests\Functional\TestEntityValueResolver: + tags: + - { name: controller.argument_value_resolver, priority: 110 } + + Symfony\Bundle\FrameworkBundle\Tests\Functional\WithAttributesController: + public: true + +security: + providers: + main: + memory: + users: + the-username: { password: the-password, roles: [ 'ROLE_USER' ] } + + firewalls: + main: + pattern: ^/ + form_login: + login_path: /login + provider: main diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml new file mode 100644 index 0000000000000..50c37b823fcbe --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/CacheAttributeListener/routing.yml @@ -0,0 +1,3 @@ +with_attributes_controller: + path: / + controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\WithAttributesController diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml index cc1a01bb8f0b5..15ddaca64356f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/ContainerDebug/config.yml @@ -33,3 +33,5 @@ services: - '%env(REAL)%' - '%env(int:key:2:json:JSON)%' - '%env(float:key:2:json:JSON)%' + Symfony\Bundle\FrameworkBundle\Tests\Fixtures\ContainerExcluded: + tags: ['container.excluded'] diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Psr4Routing/bundles.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Psr4Routing/bundles.php new file mode 100644 index 0000000000000..15ff182c6fed5 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Psr4Routing/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return [ + new FrameworkBundle(), + new TestBundle(), +]; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Psr4Routing/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Psr4Routing/config.yml new file mode 100644 index 0000000000000..377d3e7852064 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Psr4Routing/config.yml @@ -0,0 +1,2 @@ +imports: + - { resource: ../config/default.yml } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Psr4Routing/routing.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Psr4Routing/routing.yml new file mode 100644 index 0000000000000..d1dbf4abe9d5c --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Psr4Routing/routing.yml @@ -0,0 +1,6 @@ +test_bundle: + prefix: /annotated + resource: + path: "@TestBundle/Controller" + namespace: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller + type: attribute diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Security/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Security/config.yml index ff27b54bcf8d3..a223672eb9e12 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Security/config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Security/config.yml @@ -8,8 +8,6 @@ services: - container.service_subscriber security: - enable_authenticator_manager: true - providers: main: memory: diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml index 5c3c7e132339e..e51b738580255 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Serializer/config.yml @@ -7,9 +7,54 @@ framework: enabled: true default_context: enable_max_depth: true + fake_context_option: foo property_info: { enabled: true } services: serializer.alias: alias: serializer public: true + + serializer.normalizer.constraint_violation_list.alias: + alias: serializer.normalizer.constraint_violation_list + public: true + + serializer.normalizer.dateinterval.alias: + alias: serializer.normalizer.dateinterval + public: true + + serializer.normalizer.datetime.alias: + alias: serializer.normalizer.datetime + public: true + + serializer.normalizer.json_serializable.alias: + alias: serializer.normalizer.json_serializable + public: true + + serializer.normalizer.problem.alias: + alias: serializer.normalizer.problem + public: true + + serializer.normalizer.uid.alias: + alias: serializer.normalizer.uid + public: true + + serializer.normalizer.property.alias: + alias: serializer.normalizer.property + public: true + + serializer.normalizer.object.alias: + alias: serializer.normalizer.object + public: true + + serializer.encoder.xml.alias: + alias: serializer.encoder.xml + public: true + + serializer.encoder.yaml.alias: + alias: serializer.encoder.yaml + public: true + + serializer.encoder.csv.alias: + alias: serializer.encoder.csv + public: true diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Uid/config_disabled.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Uid/config_disabled.yml index 355d269440de2..5e6428618b3ee 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Uid/config_disabled.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Uid/config_disabled.yml @@ -5,8 +5,3 @@ framework: http_method_override: false uid: enabled: false - -services: - Symfony\Bundle\FrameworkBundle\Test\ExceptionSubscriber: - tags: - - { name: kernel.event_subscriber } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml index 9a353ee4eaf13..e13e9c1c3c782 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/config/framework.yml @@ -11,7 +11,6 @@ framework: enabled_locales: ['en', 'fr'] session: storage_factory_id: session.storage.factory.mock_file - catch_all_throwables: true services: logger: { class: Psr\Log\NullLogger } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php index c1b1026ac737e..def880b2319b2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/ConcreteMicroKernel.php @@ -86,16 +86,12 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load 'http_method_override' => false, 'secret' => '$ecret', 'router' => ['utf8' => true], - 'catch_all_throwables' => true, ]); $c->setParameter('halloween', 'Have a great day!'); $c->register('halloween', 'stdClass')->setPublic(true); } - /** - * {@inheritdoc} - */ public static function getSubscribedEvents(): array { return [ diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php index 3911bad73b5e6..b3f8e274ec9e5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php @@ -111,7 +111,7 @@ public function testSecretLoadedFromExtension() public function testAnonymousMicroKernel() { - $kernel = new class('anonymous_kernel') extends MinimalKernel { + $kernel = $this->kernel = new class('anonymous_kernel') extends MinimalKernel { public function helloAction(): Response { return new Response('Hello World!'); @@ -122,14 +122,13 @@ protected function configureContainer(ContainerConfigurator $c): void $c->extension('framework', [ 'http_method_override' => false, 'router' => ['utf8' => true], - 'catch_all_throwables' => true, ]); $c->services()->set('logger', NullLogger::class); } protected function configureRoutes(RoutingConfigurator $routes): void { - $routes->add('hello', '/')->controller([$this, 'helloAction']); + $routes->add('hello', '/')->controller($this->helloAction(...)); } }; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php index e4df868003e18..bf529a580489a 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/flex-style/src/FlexStyleMicroKernel.php @@ -103,7 +103,6 @@ protected function configureContainer(ContainerConfigurator $c): void $c->extension('framework', [ 'http_method_override' => false, 'router' => ['utf8' => true], - 'catch_all_throwables' => true, ]); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/KernelBrowserTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/KernelBrowserTest.php index 404a239b51282..1e462f7d0a8f6 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/KernelBrowserTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/KernelBrowserTest.php @@ -64,7 +64,7 @@ public function testRequestAfterKernelShutdownAndPerformedRequest() private function getKernelMock() { $mock = $this->getMockBuilder($this->getKernelClass()) - ->setMethods(['shutdown', 'boot', 'handle', 'getContainer']) + ->onlyMethods(['shutdown', 'boot', 'handle', 'getContainer']) ->disableOriginalConstructor() ->getMock(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php index cdcaa490ac423..2cebebed14df0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php @@ -511,7 +511,7 @@ public function testBooleanContainerParametersWithinRouteCondition() $this->assertSame('1 or 0', $route->getCondition()); } - public function getNonStringValues() + public static function getNonStringValues() { return [[null], [false], [true], [new \stdClass()], [['foo', 'bar']], [[[]]]]; } @@ -591,7 +591,7 @@ public function testResolvingMethods() $this->assertEquals(['GET', 'POST'], $route->getMethods()); } - public function getContainerParameterForRoute() + public static function getContainerParameterForRoute() { yield 'String' => ['"foo"']; yield 'Integer' => [0]; @@ -609,7 +609,7 @@ private function getServiceContainer(RouteCollection $routes): Container ->willReturn($routes) ; - $sc = $this->getMockBuilder(Container::class)->setMethods(['get'])->getMock(); + $sc = $this->getMockBuilder(Container::class)->onlyMethods(['get'])->getMock(); $sc ->expects($this->once()) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Test/WebTestCaseTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Test/WebTestCaseTest.php index 8c43917df6f5e..41c9d1bf77fa3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Test/WebTestCaseTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Test/WebTestCaseTest.php @@ -233,16 +233,18 @@ public function testAssertInputValueNotSame() public function testAssertCheckboxChecked() { $this->getCrawlerTester(new Crawler(''))->assertCheckboxChecked('rememberMe'); + $this->getCrawlerTester(new Crawler(''))->assertCheckboxChecked('rememberMe'); $this->expectException(AssertionFailedError::class); - $this->expectExceptionMessage('matches selector "input[name="rememberMe"]" and has a node matching selector "input[name="rememberMe"]" with attribute "checked" of value "checked".'); + $this->expectExceptionMessage('matches selector "input[name="rememberMe"]:checked".'); $this->getCrawlerTester(new Crawler(''))->assertCheckboxChecked('rememberMe'); } public function testAssertCheckboxNotChecked() { $this->getCrawlerTester(new Crawler(''))->assertCheckboxNotChecked('rememberMe'); + $this->getCrawlerTester(new Crawler(''))->assertCheckboxNotChecked('rememberMe'); $this->expectException(AssertionFailedError::class); - $this->expectExceptionMessage('matches selector "input[name="rememberMe"]" and does not have a node matching selector "input[name="rememberMe"]" with attribute "checked" of value "checked".'); + $this->expectExceptionMessage('does not match selector "input[name="rememberMe"]:checked".'); $this->getCrawlerTester(new Crawler(''))->assertCheckboxNotChecked('rememberMe'); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php index ff0981a71444c..0c3fb88cc9074 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Translation/TranslatorTest.php @@ -149,18 +149,21 @@ public function testResourceFilesOptionLoadsBeforeOtherAddedResources($debug, $e $loader = $this->createMock(LoaderInterface::class); + $series = [ + /* The "messages.some_locale.loader" is passed via the resource_file option and shall be loaded first */ + [['messages.some_locale.loader', 'some_locale', 'messages'], $someCatalogue], + /* This resource is added by an addResource() call and shall be loaded after the resource_files */ + [['second_resource.some_locale.loader', 'some_locale', 'messages'], $someCatalogue], + ]; + $loader->expects($this->exactly(2)) ->method('load') - ->withConsecutive( - /* The "messages.some_locale.loader" is passed via the resource_file option and shall be loaded first */ - ['messages.some_locale.loader', 'some_locale', 'messages'], - /* This resource is added by an addResource() call and shall be loaded after the resource_files */ - ['second_resource.some_locale.loader', 'some_locale', 'messages'] - ) - ->willReturnOnConsecutiveCalls( - $someCatalogue, - $someCatalogue - ); + ->willReturnCallback(function (...$args) use (&$series) { + [$expectedArgs, $return] = array_shift($series); + $this->assertSame($expectedArgs, $args); + + return $return; + }); $options = [ 'resource_files' => ['some_locale' => ['messages.some_locale.loader']], @@ -178,7 +181,7 @@ public function testResourceFilesOptionLoadsBeforeOtherAddedResources($debug, $e $translator->trans('some_message', [], null, 'some_locale'); } - public function getDebugModeAndCacheDirCombinations() + public static function getDebugModeAndCacheDirCombinations() { return [ [false, false], @@ -418,9 +421,6 @@ private function createTranslator($loader, $options, $translatorClass = Translat class TranslatorWithInvalidLocale extends Translator { - /** - * {@inheritdoc} - */ public function getLocale(): string { return 'invalid locale'; diff --git a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php index b4d9a664a5e02..3116080f042c0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php +++ b/src/Symfony/Bundle/FrameworkBundle/Translation/Translator.php @@ -95,8 +95,6 @@ public function __construct(ContainerInterface $container, MessageFormatterInter } /** - * {@inheritdoc} - * * @return string[] */ public function warmUp(string $cacheDir): array @@ -128,9 +126,6 @@ public function addResource(string $format, mixed $resource, string $locale, str $this->resources[] = [$format, $resource, $locale, $domain]; } - /** - * {@inheritdoc} - */ protected function initializeCatalogue(string $locale) { $this->initialize(); diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 98173c022f1a9..be8be43d1ca81 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -21,19 +21,19 @@ "ext-xml": "*", "symfony/cache": "^5.4|^6.0", "symfony/config": "^6.1", - "symfony/dependency-injection": "^6.2", + "symfony/dependency-injection": "^6.2.8", "symfony/deprecation-contracts": "^2.1|^3", "symfony/error-handler": "^6.1", "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/http-foundation": "^6.2", - "symfony/http-kernel": "^6.2", + "symfony/http-foundation": "^6.2.11", + "symfony/http-kernel": "^6.2.1", "symfony/polyfill-mbstring": "~1.0", "symfony/filesystem": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", "symfony/routing": "^5.4|^6.0" }, "require-dev": { - "doctrine/annotations": "^1.13.1", + "doctrine/annotations": "^1.13.1|^2", "doctrine/persistence": "^1.3|^2|^3", "symfony/asset": "^5.4|^6.0", "symfony/browser-kit": "^5.4|^6.0", @@ -49,7 +49,7 @@ "symfony/lock": "^5.4|^6.0", "symfony/mailer": "^5.4|^6.0", "symfony/messenger": "^6.2", - "symfony/mime": "^5.4|^6.0", + "symfony/mime": "^6.2", "symfony/notifier": "^5.4|^6.0", "symfony/process": "^5.4|^6.0", "symfony/rate-limiter": "^5.4|^6.0", @@ -58,7 +58,7 @@ "symfony/serializer": "^6.1", "symfony/stopwatch": "^5.4|^6.0", "symfony/string": "^5.4|^6.0", - "symfony/translation": "^5.4|^6.0", + "symfony/translation": "^6.2.8", "symfony/twig-bundle": "^5.4|^6.0", "symfony/validator": "^5.4|^6.0", "symfony/workflow": "^5.4|^6.0", @@ -74,7 +74,6 @@ "doctrine/persistence": "<1.3", "phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/type-resolver": "<1.4.0", - "phpunit/phpunit": "<5.4.3", "symfony/asset": "<5.4", "symfony/console": "<5.4", "symfony/dotenv": "<5.4", @@ -84,14 +83,14 @@ "symfony/lock": "<5.4", "symfony/mailer": "<5.4", "symfony/messenger": "<6.2", - "symfony/mime": "<5.4", + "symfony/mime": "<6.2", "symfony/property-info": "<5.4", "symfony/property-access": "<5.4", "symfony/serializer": "<6.1", "symfony/security-csrf": "<5.4", "symfony/security-core": "<5.4", "symfony/stopwatch": "<5.4", - "symfony/translation": "<5.4", + "symfony/translation": "<6.2.8", "symfony/twig-bridge": "<5.4", "symfony/twig-bundle": "<5.4", "symfony/validator": "<5.4", diff --git a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md index 7a96e86cdbe9e..dcefe374dda4c 100644 --- a/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/SecurityBundle/CHANGELOG.md @@ -5,17 +5,21 @@ CHANGELOG --- * Add the `Security` helper class - * Deprecate the `Symfony\Component\Security\Core\Security` service alias, use `Symfony\Bundle\SecurityBundle\Security\Security` instead + * Deprecate the `Symfony\Component\Security\Core\Security` service alias, use `Symfony\Bundle\SecurityBundle\Security` instead * Add `Security::getFirewallConfig()` to help to get the firewall configuration associated to the Request * Add `Security::login()` to login programmatically * Add `Security::logout()` to logout programmatically * Add `security.firewalls.logout.enable_csrf` to enable CSRF protection using the default CSRF token generator * Add RFC6750 Access Token support to allow token-based authentication + * Add `security.firewalls.switch_user.target_route` option to configure redirect target route on switch user + * Deprecate the `security.enable_authenticator_manager` config option 6.1 --- * The `security.access_control` now accepts a `RequestMatcherInterface` under the `request_matcher` option as scope configuration + * The `security.access_control` now accepts an `attributes` array to match request attributes in the `RequestMatcher` + * The `security.access_control` now accepts a `route` option to match request route in the `RequestMatcher` * Display the inherited roles of the logged-in user in the Web Debug Toolbar 6.0 diff --git a/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php b/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php index ea1e44018ea05..8a87422862287 100644 --- a/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php +++ b/src/Symfony/Bundle/SecurityBundle/Command/DebugFirewallCommand.php @@ -218,7 +218,7 @@ private function displayAuthenticators(string $name, SymfonyStyle $io): void array_map( static function ($authenticator) { return [ - \get_class($authenticator), + $authenticator::class, ]; }, $authenticators @@ -245,7 +245,7 @@ private function formatCallable(mixed $callable): string if (str_contains($r->name, '{closure}')) { return 'Closure()'; } - if ($class = $r->getClosureScopeClass()) { + if ($class = \PHP_VERSION_ID >= 80111 ? $r->getClosureCalledClass() : $r->getClosureScopeClass()) { return sprintf('%s::%s()', $class->name, $r->name); } @@ -253,7 +253,7 @@ private function formatCallable(mixed $callable): string } if (method_exists($callable, '__invoke')) { - return sprintf('%s::__invoke()', \get_class($callable)); + return sprintf('%s::__invoke()', $callable::class); } throw new \InvalidArgumentException('Callable is not describable.'); diff --git a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php index c7df65486efdf..96730d041c5fc 100644 --- a/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php +++ b/src/Symfony/Bundle/SecurityBundle/DataCollector/SecurityDataCollector.php @@ -55,9 +55,6 @@ public function __construct(TokenStorageInterface $tokenStorage = null, RoleHier $this->hasVarDumper = class_exists(ClassStub::class); } - /** - * {@inheritdoc} - */ public function collect(Request $request, Response $response, \Throwable $exception = null) { if (null === $this->tokenStorage) { @@ -122,7 +119,7 @@ public function collect(Request $request, Response $response, \Throwable $except 'impersonator_user' => $impersonatorUser, 'impersonation_exit_path' => null, 'token' => $token, - 'token_class' => $this->hasVarDumper ? new ClassStub(\get_class($token)) : \get_class($token), + 'token_class' => $this->hasVarDumper ? new ClassStub($token::class) : $token::class, 'logout_url' => $logoutUrl, 'user' => $token->getUserIdentifier(), 'roles' => $assignedRoles, @@ -140,7 +137,7 @@ public function collect(Request $request, Response $response, \Throwable $except $voter = $voter->getDecoratedVoter(); } - $this->data['voters'][] = $this->hasVarDumper ? new ClassStub(\get_class($voter)) : \get_class($voter); + $this->data['voters'][] = $this->hasVarDumper ? new ClassStub($voter::class) : $voter::class; } // collect voter details @@ -205,9 +202,6 @@ public function collect(Request $request, Response $response, \Throwable $except $this->data['authenticators'] = $this->firewall ? $this->firewall->getAuthenticatorsInfo() : []; } - /** - * {@inheritdoc} - */ public function reset() { $this->data = []; @@ -350,9 +344,6 @@ public function getAuthenticators(): array|Data return $this->data['authenticators']; } - /** - * {@inheritdoc} - */ public function getName(): string { return 'security'; diff --git a/src/Symfony/Bundle/SecurityBundle/Debug/WrappedLazyListener.php b/src/Symfony/Bundle/SecurityBundle/Debug/WrappedLazyListener.php index 5a3d0a1c609d8..a30900a5342e2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Debug/WrappedLazyListener.php +++ b/src/Symfony/Bundle/SecurityBundle/Debug/WrappedLazyListener.php @@ -38,9 +38,6 @@ public function supports(Request $request): ?bool return $this->listener->supports($request); } - /** - * {@inheritdoc} - */ public function authenticate(RequestEvent $event) { $startTime = microtime(true); diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php index 3109cf27c5ab1..607d1f06c927d 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php @@ -22,9 +22,6 @@ */ class AddExpressionLanguageProvidersPass implements CompilerPassInterface { - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if ($container->has('security.expression_language')) { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php index c3c7b86ab2f72..273a0db67fddf 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSecurityVotersPass.php @@ -29,9 +29,6 @@ class AddSecurityVotersPass implements CompilerPassInterface { use PriorityTaggedServiceTrait; - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if (!$container->hasDefinition('security.access.decision_manager')) { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSessionDomainConstraintPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSessionDomainConstraintPass.php index 461a5ad66d081..5b107b5ef0dfc 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSessionDomainConstraintPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/AddSessionDomainConstraintPass.php @@ -21,9 +21,6 @@ */ class AddSessionDomainConstraintPass implements CompilerPassInterface { - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if (!$container->hasParameter('session.storage.options') || !$container->has('security.http_utils')) { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/CleanRememberMeVerifierPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/CleanRememberMeVerifierPass.php index d959d4bda9e67..fb4b6f83dc3b8 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/CleanRememberMeVerifierPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/CleanRememberMeVerifierPass.php @@ -21,9 +21,6 @@ */ class CleanRememberMeVerifierPass implements CompilerPassInterface { - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if (!$container->hasDefinition('cache.system')) { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPass.php index 20094957cdb65..8eac2cf83a8c0 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPass.php @@ -52,9 +52,6 @@ class RegisterGlobalSecurityEventListenersPass implements CompilerPassInterface SecurityEvents::INTERACTIVE_LOGIN, ]; - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if (!$container->has('event_dispatcher') || !$container->hasParameter('security.firewalls')) { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterTokenUsageTrackingPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterTokenUsageTrackingPass.php index 2b1159eff979a..fdffab1bbf395 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterTokenUsageTrackingPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterTokenUsageTrackingPass.php @@ -27,9 +27,6 @@ */ class RegisterTokenUsageTrackingPass implements CompilerPassInterface { - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container) { if (!$container->has('security.untracked_token_storage')) { diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/ReplaceDecoratedRememberMeHandlerPass.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/ReplaceDecoratedRememberMeHandlerPass.php index 5de431c2c04c8..4727e62f7c8ff 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/ReplaceDecoratedRememberMeHandlerPass.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/ReplaceDecoratedRememberMeHandlerPass.php @@ -26,9 +26,6 @@ final class ReplaceDecoratedRememberMeHandlerPass implements CompilerPassInterfa { private const HANDLER_TAG = 'security.remember_me_handler'; - /** - * {@inheritdoc} - */ public function process(ContainerBuilder $container): void { $handledFirewalls = []; diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php index 58f6bbdac5862..25778ea851dd7 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php @@ -65,7 +65,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->booleanNode('hide_user_not_found')->defaultTrue()->end() ->booleanNode('erase_credentials')->defaultTrue()->end() - ->booleanNode('enable_authenticator_manager')->defaultTrue()->end() + ->booleanNode('enable_authenticator_manager')->setDeprecated('symfony/security-bundle', '6.2', 'The "%node%" option at "%path%" is deprecated.')->defaultTrue()->end() ->arrayNode('access_decision_manager') ->addDefaultsIfNotSet() ->children() @@ -133,6 +133,7 @@ private function addAccessControlSection(ArrayNodeDefinition $rootNode) ->prototype('array') ->fixXmlConfig('ip') ->fixXmlConfig('method') + ->fixXmlConfig('attribute') ->children() ->scalarNode('request_matcher')->defaultNull()->end() ->scalarNode('requires_channel')->defaultNull()->end() @@ -147,6 +148,11 @@ private function addAccessControlSection(ArrayNodeDefinition $rootNode) ->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end() ->prototype('scalar')->end() ->end() + ->arrayNode('attributes') + ->useAttributeAsKey('key') + ->prototype('scalar')->end() + ->end() + ->scalarNode('route')->defaultNull()->end() ->arrayNode('methods') ->beforeNormalization()->ifString()->then(function ($v) { return preg_split('/\s*,\s*/', $v); })->end() ->prototype('scalar')->end() @@ -216,7 +222,7 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto if (isset($v['csrf_token_generator'])) { $v['enable_csrf'] = true; } elseif ($v['enable_csrf']) { - $v['csrf_token_generator'] = 'security.csrf.token_generator'; + $v['csrf_token_generator'] = 'security.csrf.token_manager'; } return $v; @@ -257,7 +263,7 @@ private function addFirewallsSection(ArrayNodeDefinition $rootNode, array $facto ->scalarNode('provider')->end() ->scalarNode('parameter')->defaultValue('_switch_user')->end() ->scalarNode('role')->defaultValue('ROLE_ALLOWED_TO_SWITCH')->end() - ->scalarNode('target_url')->defaultValue(null)->end() + ->scalarNode('target_route')->defaultValue(null)->end() ->end() ->end() ->arrayNode('required_badges') diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php index b0c6b5c0ecb79..1d2121ff1616a 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AbstractFactory.php @@ -14,7 +14,6 @@ use Symfony\Component\Config\Definition\Builder\NodeDefinition; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; /** * AbstractFactory is the base class for all classes inheriting from @@ -80,7 +79,7 @@ protected function createAuthenticationSuccessHandler(ContainerBuilder $containe if (isset($config['success_handler'])) { $successHandler = $container->setDefinition($successHandlerId, new ChildDefinition('security.authentication.custom_success_handler')); - $successHandler->replaceArgument(0, new Reference($config['success_handler'])); + $successHandler->replaceArgument(0, new ChildDefinition($config['success_handler'])); $successHandler->replaceArgument(1, $options); $successHandler->replaceArgument(2, $id); } else { @@ -99,7 +98,7 @@ protected function createAuthenticationFailureHandler(ContainerBuilder $containe if (isset($config['failure_handler'])) { $failureHandler = $container->setDefinition($id, new ChildDefinition('security.authentication.custom_failure_handler')); - $failureHandler->replaceArgument(0, new Reference($config['failure_handler'])); + $failureHandler->replaceArgument(0, new ChildDefinition($config['failure_handler'])); $failureHandler->replaceArgument(1, $options); } else { $failureHandler = $container->setDefinition($id, new ChildDefinition('security.authentication.failure_handler')); diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AccessTokenFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AccessTokenFactory.php index 7034e780cc1d2..2fbf3b2f8b567 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AccessTokenFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/AccessTokenFactory.php @@ -36,14 +36,12 @@ public function __construct() public function addConfiguration(NodeDefinition $node): void { - $builder = $node->children(); + parent::addConfiguration($node); + $builder = $node->children(); $builder ->scalarNode('token_handler')->isRequired()->end() - ->scalarNode('user_provider')->defaultNull()->end() ->scalarNode('realm')->defaultNull()->end() - ->scalarNode('success_handler')->defaultNull()->end() - ->scalarNode('failure_handler')->defaultNull()->end() ->arrayNode('token_extractors') ->fixXmlConfig('token_extractors') ->beforeNormalization() @@ -64,9 +62,6 @@ public function getPriority(): int return self::PRIORITY; } - /** - * {@inheritdoc} - */ public function getKey(): string { return 'access_token'; @@ -74,7 +69,6 @@ public function getKey(): string public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string { - $userProvider = new Reference($config['user_provider'] ?? $userProviderId); $successHandler = isset($config['success_handler']) ? new Reference($this->createAuthenticationSuccessHandler($container, $firewallName, $config)) : null; $failureHandler = isset($config['failure_handler']) ? new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config)) : null; $authenticatorId = sprintf('security.authenticator.access_token.%s', $firewallName); @@ -82,9 +76,9 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal $container ->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.access_token')) - ->replaceArgument(0, $userProvider) - ->replaceArgument(1, new Reference($config['token_handler'])) - ->replaceArgument(2, new Reference($extractorId)) + ->replaceArgument(0, new Reference($config['token_handler'])) + ->replaceArgument(1, new Reference($extractorId)) + ->replaceArgument(2, new Reference($userProviderId)) ->replaceArgument(3, $successHandler) ->replaceArgument(4, $failureHandler) ->replaceArgument(5, $config['realm']) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php index 9307cba86e3f3..303ae2a183196 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/JsonLoginFactory.php @@ -39,9 +39,6 @@ public function getPriority(): int return self::PRIORITY; } - /** - * {@inheritdoc} - */ public function getKey(): string { return 'json-login'; diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php index 5b2cfe781f490..6356702cc5d0b 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php @@ -11,14 +11,16 @@ namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; -use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\NodeDefinition; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpFoundation\RateLimiter\RequestRateLimiterInterface; +use Symfony\Component\Lock\LockInterface; use Symfony\Component\RateLimiter\RateLimiterFactory; +use Symfony\Component\RateLimiter\Storage\CacheStorage; use Symfony\Component\Security\Http\RateLimiter\DefaultLoginRateLimiter; /** @@ -60,20 +62,16 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal } if (!isset($config['limiter'])) { - if (!class_exists(FrameworkExtension::class) || !method_exists(FrameworkExtension::class, 'registerRateLimiter')) { - throw new \LogicException('You must either configure a rate limiter for "security.firewalls.'.$firewallName.'.login_throttling" or install symfony/framework-bundle:^5.2.'); - } - $limiterOptions = [ 'policy' => 'fixed_window', 'limit' => $config['max_attempts'], 'interval' => $config['interval'], 'lock_factory' => $config['lock_factory'], ]; - FrameworkExtension::registerRateLimiter($container, $localId = '_login_local_'.$firewallName, $limiterOptions); + $this->registerRateLimiter($container, $localId = '_login_local_'.$firewallName, $limiterOptions); $limiterOptions['limit'] = 5 * $config['max_attempts']; - FrameworkExtension::registerRateLimiter($container, $globalId = '_login_global_'.$firewallName, $limiterOptions); + $this->registerRateLimiter($container, $globalId = '_login_global_'.$firewallName, $limiterOptions); $container->register($config['limiter'] = 'security.login_throttling.'.$firewallName.'.limiter', DefaultLoginRateLimiter::class) ->addArgument(new Reference('limiter.'.$globalId)) @@ -88,4 +86,36 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal return []; } + + private function registerRateLimiter(ContainerBuilder $container, string $name, array $limiterConfig) + { + // default configuration (when used by other DI extensions) + $limiterConfig += ['lock_factory' => 'lock.factory', 'cache_pool' => 'cache.rate_limiter']; + + $limiter = $container->setDefinition($limiterId = 'limiter.'.$name, new ChildDefinition('limiter')); + + if (null !== $limiterConfig['lock_factory']) { + if (!interface_exists(LockInterface::class)) { + throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be installed. Try running "composer require symfony/lock".', $name)); + } + if (!$container->hasDefinition('lock.factory.abstract')) { + throw new LogicException(sprintf('Rate limiter "%s" requires the Lock component to be configured.', $name)); + } + + $limiter->replaceArgument(2, new Reference($limiterConfig['lock_factory'])); + } + unset($limiterConfig['lock_factory']); + + if (null === $storageId = $limiterConfig['storage_service'] ?? null) { + $container->register($storageId = 'limiter.storage.'.$name, CacheStorage::class)->addArgument(new Reference($limiterConfig['cache_pool'])); + } + + $limiter->replaceArgument(1, new Reference($storageId)); + unset($limiterConfig['storage_service'], $limiterConfig['cache_pool']); + + $limiterConfig['id'] = $name; + $limiter->replaceArgument(0, $limiterConfig); + + $container->registerAliasForArgument($limiterId, RateLimiterFactory::class, $name.'.limiter'); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php index 2a85c2486c8f1..eac593b8c7a6c 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php @@ -111,15 +111,6 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal ->replaceArgument(3, $config['name'] ?? $this->options['name']) ; - foreach ($container->findTaggedServiceIds('security.remember_me_aware') as $serviceId => $attributes) { - // register ContextListener - if (str_starts_with($serviceId, 'security.context_listener')) { - continue; - } - - throw new \LogicException(sprintf('Symfony Authenticator Security dropped support for the "security.remember_me_aware" tag, service "%s" will no longer work as expected.', $serviceId)); - } - return $authenticatorId; } @@ -235,9 +226,6 @@ private function createTokenVerifier(ContainerBuilder $container, string $firewa return new Reference($tokenVerifierId, ContainerInterface::NULL_ON_INVALID_REFERENCE); } - /** - * {@inheritdoc} - */ public function prepend(ContainerBuilder $container) { $rememberMeSecureDefault = false; diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index b5e7a619fdc1f..e6d26f8789769 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -22,6 +22,7 @@ use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -32,7 +33,14 @@ use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; -use Symfony\Component\HttpFoundation\RequestMatcher; +use Symfony\Component\Form\Extension\PasswordHasher\PasswordHasherExtension; +use Symfony\Component\HttpFoundation\ChainRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\AttributesRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\HostRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\IpsRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\MethodRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\PathRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\PortRequestMatcher; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher; @@ -44,6 +52,7 @@ use Symfony\Component\Security\Core\Authorization\Strategy\PriorityStrategy; use Symfony\Component\Security\Core\Authorization\Strategy\UnanimousStrategy; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; +use Symfony\Component\Security\Core\User\ChainUserChecker; use Symfony\Component\Security\Core\User\ChainUserProvider; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -116,6 +125,12 @@ public function load(array $configs, ContainerBuilder $container) $container->removeDefinition('security.is_granted_attribute_expression_language'); } + if (!class_exists(PasswordHasherExtension::class)) { + $container->removeDefinition('form.listener.password_hasher'); + $container->removeDefinition('form.type_extension.form.password_hasher'); + $container->removeDefinition('form.type_extension.password.password_hasher'); + } + // set some global scalars $container->setParameter('security.access.denied_url', $config['access_denied_url']); $container->setParameter('security.authentication.manager.erase_credentials', $config['erase_credentials']); @@ -171,18 +186,13 @@ public function load(array $configs, ContainerBuilder $container) */ private function createStrategyDefinition(string $strategy, bool $allowIfAllAbstainDecisions, bool $allowIfEqualGrantedDeniedDecisions): Definition { - switch ($strategy) { - case MainConfiguration::STRATEGY_AFFIRMATIVE: - return new Definition(AffirmativeStrategy::class, [$allowIfAllAbstainDecisions]); - case MainConfiguration::STRATEGY_CONSENSUS: - return new Definition(ConsensusStrategy::class, [$allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions]); - case MainConfiguration::STRATEGY_UNANIMOUS: - return new Definition(UnanimousStrategy::class, [$allowIfAllAbstainDecisions]); - case MainConfiguration::STRATEGY_PRIORITY: - return new Definition(PriorityStrategy::class, [$allowIfAllAbstainDecisions]); - } - - throw new \InvalidArgumentException(sprintf('The strategy "%s" is not supported.', $strategy)); + return match ($strategy) { + MainConfiguration::STRATEGY_AFFIRMATIVE => new Definition(AffirmativeStrategy::class, [$allowIfAllAbstainDecisions]), + MainConfiguration::STRATEGY_CONSENSUS => new Definition(ConsensusStrategy::class, [$allowIfAllAbstainDecisions, $allowIfEqualGrantedDeniedDecisions]), + MainConfiguration::STRATEGY_UNANIMOUS => new Definition(UnanimousStrategy::class, [$allowIfAllAbstainDecisions]), + MainConfiguration::STRATEGY_PRIORITY => new Definition(PriorityStrategy::class, [$allowIfAllAbstainDecisions]), + default => throw new \InvalidArgumentException(sprintf('The strategy "%s" is not supported.', $strategy)), + }; } private function createRoleHierarchy(array $config, ContainerBuilder $container) @@ -201,24 +211,34 @@ private function createAuthorization(array $config, ContainerBuilder $container) { foreach ($config['access_control'] as $access) { if (isset($access['request_matcher'])) { - if ($access['path'] || $access['host'] || $access['port'] || $access['ips'] || $access['methods']) { + if ($access['path'] || $access['host'] || $access['port'] || $access['ips'] || $access['methods'] || $access['attributes'] || $access['route']) { throw new InvalidConfigurationException('The "request_matcher" option should not be specified alongside other options. Consider integrating your constraints inside your RequestMatcher directly.'); } $matcher = new Reference($access['request_matcher']); } else { + $attributes = $access['attributes']; + + if ($access['route']) { + if (\array_key_exists('_route', $attributes)) { + throw new InvalidConfigurationException('The "route" option should not be specified alongside "attributes._route" option. Use just one of the options.'); + } + $attributes['_route'] = $access['route']; + } + $matcher = $this->createRequestMatcher( $container, $access['path'], $access['host'], $access['port'], $access['methods'], - $access['ips'] + $access['ips'], + $attributes ); } - $attributes = $access['roles']; + $roles = $access['roles']; if ($access['allow_if']) { - $attributes[] = $this->createExpression($container, $access['allow_if']); + $roles[] = $this->createExpression($container, $access['allow_if']); } $emptyAccess = 0 === \count(array_filter($access)); @@ -228,7 +248,7 @@ private function createAuthorization(array $config, ContainerBuilder $container) } $container->getDefinition('security.access_map') - ->addMethodCall('add', [$matcher, $attributes, $access['requires_channel']]); + ->addMethodCall('add', [$matcher, $roles, $access['requires_channel']]); } // allow cache warm-up for expressions @@ -385,6 +405,10 @@ private function createFirewall(ContainerBuilder $container, string $id, array $ ])) ; + // Register Firewall-specific chained user checker + $container->register('security.user_checker.chain.'.$id, ChainUserChecker::class) + ->addArgument(new TaggedIteratorArgument('security.user_checker.'.$id)); + // Register listeners $listeners = []; $listenerKeys = []; @@ -393,11 +417,10 @@ private function createFirewall(ContainerBuilder $container, string $id, array $ $listeners[] = new Reference('security.channel_listener'); $contextKey = null; - $contextListenerId = null; // Context serializer listener if (false === $firewall['stateless']) { $contextKey = $firewall['context'] ?? $id; - $listeners[] = new Reference($contextListenerId = $this->createContextListener($container, $contextKey, $firewallEventDispatcherId)); + $listeners[] = new Reference($this->createContextListener($container, $contextKey, $firewallEventDispatcherId)); $sessionStrategyId = 'security.authentication.session_strategy'; $container @@ -466,7 +489,7 @@ private function createFirewall(ContainerBuilder $container, string $id, array $ // Authentication listeners $firewallAuthenticationProviders = []; - [$authListeners, $defaultEntryPoint] = $this->createAuthenticationListeners($container, $id, $firewall, $firewallAuthenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint, $contextListenerId); + [$authListeners, $defaultEntryPoint] = $this->createAuthenticationListeners($container, $id, $firewall, $firewallAuthenticationProviders, $defaultProvider, $providerIds, $configuredEntryPoint); // $configuredEntryPoint is resolved into a service ID and stored in $defaultEntryPoint $configuredEntryPoint = $defaultEntryPoint; @@ -572,7 +595,7 @@ private function createContextListener(ContainerBuilder $container, string $cont return $this->contextListeners[$contextKey] = $listenerId; } - private function createAuthenticationListeners(ContainerBuilder $container, string $id, array $firewall, array &$authenticationProviders, ?string $defaultProvider, array $providerIds, ?string $defaultEntryPoint, string $contextListenerId = null) + private function createAuthenticationListeners(ContainerBuilder $container, string $id, array $firewall, array &$authenticationProviders, ?string $defaultProvider, array $providerIds, ?string $defaultEntryPoint) { $listeners = []; $entryPoints = []; @@ -581,7 +604,7 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri $key = str_replace('-', '_', $factory->getKey()); if (isset($firewall[$key])) { - $userProvider = $this->getUserProvider($container, $id, $firewall, $key, $defaultProvider, $providerIds, $contextListenerId); + $userProvider = $this->getUserProvider($container, $id, $firewall, $key, $defaultProvider, $providerIds); if (!$factory instanceof AuthenticatorFactoryInterface) { throw new InvalidConfigurationException(sprintf('Authenticator factory "%s" ("%s") must implement "%s".', get_debug_type($factory), $key, AuthenticatorFactoryInterface::class)); @@ -613,7 +636,7 @@ private function createAuthenticationListeners(ContainerBuilder $container, stri return [$listeners, $defaultEntryPoint]; } - private function getUserProvider(ContainerBuilder $container, string $id, array $firewall, string $factoryKey, ?string $defaultProvider, array $providerIds, ?string $contextListenerId): string + private function getUserProvider(ContainerBuilder $container, string $id, array $firewall, string $factoryKey, ?string $defaultProvider, array $providerIds): string { if (isset($firewall[$factoryKey]['provider'])) { if (!isset($providerIds[$normalizedName = str_replace('-', '_', $firewall[$factoryKey]['provider'])])) { @@ -623,10 +646,6 @@ private function getUserProvider(ContainerBuilder $container, string $id, array return $providerIds[$normalizedName]; } - if ('remember_me' === $factoryKey && $contextListenerId) { - $container->getDefinition($contextListenerId)->addTag('security.remember_me_aware', ['id' => $id, 'provider' => 'none']); - } - if ($defaultProvider) { return $defaultProvider; } @@ -845,8 +864,8 @@ private function createSwitchUserListener(ContainerBuilder $container, string $i if (!$userProvider) { throw new InvalidConfigurationException(sprintf('Not configuring explicitly the provider for the "switch_user" listener on "%s" firewall is ambiguous as there is more than one registered provider.', $id)); } - if ($stateless && null !== $config['target_url']) { - throw new InvalidConfigurationException(sprintf('Cannot set a "target_url" for the "switch_user" listener on the "%s" firewall as it is stateless.', $id)); + if ($stateless && null !== $config['target_route']) { + throw new InvalidConfigurationException(sprintf('Cannot set a "target_route" for the "switch_user" listener on the "%s" firewall as it is stateless.', $id)); } $switchUserListenerId = 'security.authentication.switchuser_listener.'.$id; @@ -857,7 +876,7 @@ private function createSwitchUserListener(ContainerBuilder $container, string $i $listener->replaceArgument(6, $config['parameter']); $listener->replaceArgument(7, $config['role']); $listener->replaceArgument(9, $stateless); - $listener->replaceArgument(10, $config['target_url']); + $listener->replaceArgument(11, $config['target_route']); return $switchUserListenerId; } @@ -887,7 +906,7 @@ private function createRequestMatcher(ContainerBuilder $container, string $path $methods = array_map('strtoupper', $methods); } - if (null !== $ips) { + if ($ips) { foreach ($ips as $ip) { $container->resolveEnvPlaceholders($ip, null, $usedEnvs); @@ -899,22 +918,58 @@ private function createRequestMatcher(ContainerBuilder $container, string $path } } - $id = '.security.request_matcher.'.ContainerBuilder::hash([$path, $host, $port, $methods, $ips, $attributes]); + $id = '.security.request_matcher.'.ContainerBuilder::hash([ChainRequestMatcher::class, $path, $host, $port, $methods, $ips, $attributes]); if (isset($this->requestMatchers[$id])) { return $this->requestMatchers[$id]; } - // only add arguments that are necessary - $arguments = [$path, $host, $methods, $ips, $attributes, null, $port]; - while (\count($arguments) > 0 && !end($arguments)) { - array_pop($arguments); + $arguments = []; + if ($methods) { + if (!$container->hasDefinition($lid = '.security.request_matcher.'.ContainerBuilder::hash([MethodRequestMatcher::class, $methods]))) { + $container->register($lid, MethodRequestMatcher::class)->setArguments([$methods]); + } + $arguments[] = new Reference($lid); + } + + if ($path) { + if (!$container->hasDefinition($lid = '.security.request_matcher.'.ContainerBuilder::hash([PathRequestMatcher::class, $path]))) { + $container->register($lid, PathRequestMatcher::class)->setArguments([$path]); + } + $arguments[] = new Reference($lid); + } + + if ($host) { + if (!$container->hasDefinition($lid = '.security.request_matcher.'.ContainerBuilder::hash([HostRequestMatcher::class, $host]))) { + $container->register($lid, HostRequestMatcher::class)->setArguments([$host]); + } + $arguments[] = new Reference($lid); + } + + if ($ips) { + if (!$container->hasDefinition($lid = '.security.request_matcher.'.ContainerBuilder::hash([IpsRequestMatcher::class, $ips]))) { + $container->register($lid, IpsRequestMatcher::class)->setArguments([$ips]); + } + $arguments[] = new Reference($lid); + } + + if ($attributes) { + if (!$container->hasDefinition($lid = '.security.request_matcher.'.ContainerBuilder::hash([AttributesRequestMatcher::class, $attributes]))) { + $container->register($lid, AttributesRequestMatcher::class)->setArguments([$attributes]); + } + $arguments[] = new Reference($lid); + } + + if ($port) { + if (!$container->hasDefinition($lid = '.security.request_matcher.'.ContainerBuilder::hash([PortRequestMatcher::class, $port]))) { + $container->register($lid, PortRequestMatcher::class)->setArguments([$port]); + } + $arguments[] = new Reference($lid); } $container - ->register($id, RequestMatcher::class) - ->setPublic(false) - ->setArguments($arguments) + ->register($id, ChainRequestMatcher::class) + ->setArguments([$arguments]) ; return $this->requestMatchers[$id] = new Reference($id); @@ -931,9 +986,6 @@ public function addUserProviderFactory(UserProviderFactoryInterface $factory) $this->userProviderFactories[] = $factory; } - /** - * {@inheritdoc} - */ public function getXsdValidationBasePath(): string|false { return __DIR__.'/../Resources/config/schema'; diff --git a/src/Symfony/Bundle/SecurityBundle/EventListener/FirewallListener.php b/src/Symfony/Bundle/SecurityBundle/EventListener/FirewallListener.php index 98f54c8634abd..9e91f3930ab08 100644 --- a/src/Symfony/Bundle/SecurityBundle/EventListener/FirewallListener.php +++ b/src/Symfony/Bundle/SecurityBundle/EventListener/FirewallListener.php @@ -56,9 +56,6 @@ public function onKernelFinishRequest(FinishRequestEvent $event) parent::onKernelFinishRequest($event); } - /** - * {@inheritdoc} - */ public static function getSubscribedEvents(): array { return [ diff --git a/src/Symfony/Bundle/SecurityBundle/LICENSE b/src/Symfony/Bundle/SecurityBundle/LICENSE index 88bf75bb4d6a2..0138f8f071351 100644 --- a/src/Symfony/Bundle/SecurityBundle/LICENSE +++ b/src/Symfony/Bundle/SecurityBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/SecurityBundle/RememberMe/DecoratedRememberMeHandler.php b/src/Symfony/Bundle/SecurityBundle/RememberMe/DecoratedRememberMeHandler.php index 56c2886c6c607..ed6d0ed20d5be 100644 --- a/src/Symfony/Bundle/SecurityBundle/RememberMe/DecoratedRememberMeHandler.php +++ b/src/Symfony/Bundle/SecurityBundle/RememberMe/DecoratedRememberMeHandler.php @@ -31,25 +31,16 @@ public function __construct(RememberMeHandlerInterface $handler) $this->handler = $handler; } - /** - * {@inheritDoc} - */ public function createRememberMeCookie(UserInterface $user): void { $this->handler->createRememberMeCookie($user); } - /** - * {@inheritDoc} - */ public function consumeRememberMeCookie(RememberMeDetails $rememberMeDetails): UserInterface { return $this->handler->consumeRememberMeCookie($rememberMeDetails); } - /** - * {@inheritDoc} - */ public function clearRememberMeCookie(): void { $this->handler->clearRememberMeCookie(); diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/password_hasher.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/password_hasher.php index 50e1be8d981cd..2df037ad0d7ce 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/password_hasher.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/password_hasher.php @@ -11,6 +11,11 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\Form\Extension\Core\Type\PasswordType; +use Symfony\Component\Form\Extension\PasswordHasher\EventListener\PasswordHasherListener; +use Symfony\Component\Form\Extension\PasswordHasher\Type\FormTypePasswordHasherExtension; +use Symfony\Component\Form\Extension\PasswordHasher\Type\PasswordTypePasswordHasherExtension; use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory; use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface; use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher; @@ -26,5 +31,23 @@ ->args([service('security.password_hasher_factory')]) ->alias('security.password_hasher', 'security.user_password_hasher') ->alias(UserPasswordHasherInterface::class, 'security.password_hasher') + + ->set('form.listener.password_hasher', PasswordHasherListener::class) + ->args([ + service('security.password_hasher'), + service('property_accessor')->nullOnInvalid(), + ]) + + ->set('form.type_extension.form.password_hasher', FormTypePasswordHasherExtension::class) + ->args([ + service('form.listener.password_hasher'), + ]) + ->tag('form.type_extension', ['extended-type' => FormType::class]) + + ->set('form.type_extension.password.password_hasher', PasswordTypePasswordHasherExtension::class) + ->args([ + service('form.listener.password_hasher'), + ]) + ->tag('form.type_extension', ['extended-type' => PasswordType::class]) ; }; diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd index af4570669a93c..af058365cc716 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/schema/security-1.0.xsd @@ -140,6 +140,7 @@ + @@ -304,6 +305,17 @@
+ + + + + + + + + + + @@ -378,6 +390,7 @@ + @@ -386,6 +399,7 @@ + @@ -397,4 +411,12 @@ + + + + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php index 024fed6f7d3b0..5f4e693b85cbb 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.php @@ -13,11 +13,11 @@ use Symfony\Bundle\SecurityBundle\CacheWarmer\ExpressionCacheWarmer; use Symfony\Bundle\SecurityBundle\EventListener\FirewallListener; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security\FirewallConfig; use Symfony\Bundle\SecurityBundle\Security\FirewallContext; use Symfony\Bundle\SecurityBundle\Security\FirewallMap; use Symfony\Bundle\SecurityBundle\Security\LazyFirewallContext; -use Symfony\Bundle\SecurityBundle\Security\Security; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; use Symfony\Component\Ldap\Security\LdapUserProvider; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver; @@ -83,7 +83,7 @@ service_locator([ 'security.token_storage' => service('security.token_storage'), 'security.authorization_checker' => service('security.authorization_checker'), - 'security.user_authenticator' => service('security.user_authenticator')->ignoreOnInvalid(), + 'security.authenticator.managers_locator' => service('security.authenticator.managers_locator')->ignoreOnInvalid(), 'request_stack' => service('request_stack'), 'security.firewall.map' => service('security.firewall.map'), 'security.user_checker' => service('security.user_checker'), @@ -100,13 +100,16 @@ ->args([ service('security.token_storage'), ]) - ->tag('controller.argument_value_resolver', ['priority' => 40]) + ->tag('controller.argument_value_resolver', ['priority' => 120]) // Authentication related services ->set('security.authentication.trust_resolver', AuthenticationTrustResolver::class) ->set('security.authentication.session_strategy', SessionAuthenticationStrategy::class) - ->args([param('security.authentication.session_strategy.strategy')]) + ->args([ + param('security.authentication.session_strategy.strategy'), + service('security.csrf.token_storage')->ignoreOnInvalid(), + ]) ->alias(SessionAuthenticationStrategyInterface::class, 'security.authentication.session_strategy') ->set('security.authentication.session_strategy_noop', SessionAuthenticationStrategy::class) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php index 17bc916c8d314..9cad1f8e4a443 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_authenticator_access_token.php @@ -26,14 +26,13 @@ ->set('security.authenticator.access_token', AccessTokenAuthenticator::class) ->abstract() ->args([ - abstract_arg('user provider'), abstract_arg('access token handler'), abstract_arg('access token extractor'), null, null, null, + null, ]) - ->call('setTranslator', [service('translator')->ignoreOnInvalid()]) ->set('security.authenticator.access_token.chain_extractor', ChainAccessTokenExtractor::class) ->abstract() diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php index 0d4c069b43913..921aa90b8d730 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security_listeners.php @@ -151,7 +151,8 @@ 'ROLE_ALLOWED_TO_SWITCH', service('event_dispatcher')->nullOnInvalid(), false, // Stateless - abstract_arg('Target Url'), + service('router')->nullOnInvalid(), + abstract_arg('Target Route'), ]) ->tag('monolog.logger', ['channel' => 'security']) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg index 1110c10a037d2..b11d1a4637476 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg +++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/icon.svg @@ -1 +1,5 @@ - + + + + + diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig index cf2b54e27cbb7..4a5fa01a9e3bc 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig +++ b/src/Symfony/Bundle/SecurityBundle/Resources/views/Collector/security.html.twig @@ -177,7 +177,7 @@
-
+

Firewall

{% if collector.firewall %} diff --git a/src/Symfony/Bundle/SecurityBundle/Security/Security.php b/src/Symfony/Bundle/SecurityBundle/Security.php similarity index 89% rename from src/Symfony/Bundle/SecurityBundle/Security/Security.php rename to src/Symfony/Bundle/SecurityBundle/Security.php index 94d0370510d45..94e0e05e183af 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/Security.php +++ b/src/Symfony/Bundle/SecurityBundle/Security.php @@ -9,9 +9,10 @@ * file that was distributed with this source code. */ -namespace Symfony\Bundle\SecurityBundle\Security; +namespace Symfony\Bundle\SecurityBundle; use Psr\Container\ContainerInterface; +use Symfony\Bundle\SecurityBundle\Security\FirewallConfig; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; @@ -23,6 +24,7 @@ use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; use Symfony\Component\Security\Http\Event\LogoutEvent; use Symfony\Component\Security\Http\ParameterBagUtils; +use Symfony\Component\Security\Http\SecurityRequestAttributes; use Symfony\Contracts\Service\ServiceProviderInterface; /** @@ -36,6 +38,10 @@ */ class Security extends LegacySecurity { + public const ACCESS_DENIED_ERROR = SecurityRequestAttributes::ACCESS_DENIED_ERROR; + public const AUTHENTICATION_ERROR = SecurityRequestAttributes::AUTHENTICATION_ERROR; + public const LAST_USERNAME = SecurityRequestAttributes::LAST_USERNAME; + public function __construct(private readonly ContainerInterface $container, private readonly array $authenticators = []) { parent::__construct($container, false); @@ -63,7 +69,7 @@ public function login(UserInterface $user, string $authenticatorName = null, str $authenticator = $this->getAuthenticator($authenticatorName, $firewallName); $this->container->get('security.user_checker')->checkPreAuth($user); - $this->container->get('security.user_authenticator')->authenticateUser($user, $authenticator, $request); + $this->container->get('security.authenticator.managers_locator')->get($firewallName)->authenticateUser($user, $authenticator, $request); } /** @@ -110,7 +116,7 @@ public function logout(bool $validateCsrfToken = true): ?Response private function getAuthenticator(?string $authenticatorName, string $firewallName): AuthenticatorInterface { - if (!\array_key_exists($firewallName, $this->authenticators)) { + if (!isset($this->authenticators[$firewallName])) { throw new LogicException(sprintf('No authenticators found for firewall "%s".', $firewallName)); } @@ -137,7 +143,7 @@ private function getAuthenticator(?string $authenticatorName, string $firewallNa $authenticatorId = 'security.authenticator.'.$authenticatorName.'.'.$firewallName; if (!$firewallAuthenticatorLocator->has($authenticatorId)) { - throw new LogicException(sprintf('Unable to find an authenticator named "%s" for the firewall "%s". Available authenticators: "%s".', $authenticatorName, implode('", "', array_keys($firewallAuthenticatorLocator->getProvidedServices())))); + throw new LogicException(sprintf('Unable to find an authenticator named "%s" for the firewall "%s". Available authenticators: "%s".', $authenticatorName, $firewallName, implode('", "', array_keys($firewallAuthenticatorLocator->getProvidedServices())))); } return $firewallAuthenticatorLocator->get($authenticatorId); diff --git a/src/Symfony/Bundle/SecurityBundle/Security/UserAuthenticator.php b/src/Symfony/Bundle/SecurityBundle/Security/UserAuthenticator.php index 174f1d015e729..786457800367e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Security/UserAuthenticator.php +++ b/src/Symfony/Bundle/SecurityBundle/Security/UserAuthenticator.php @@ -38,9 +38,6 @@ public function __construct(FirewallMap $firewallMap, ContainerInterface $userAu $this->requestStack = $requestStack; } - /** - * {@inheritdoc} - */ public function authenticateUser(UserInterface $user, AuthenticatorInterface $authenticator, Request $request, array $badges = []): ?Response { return $this->getForFirewall()->authenticateUser($user, $authenticator, $request, $badges); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/CacheWarmer/ExpressionCacheWarmerTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/CacheWarmer/ExpressionCacheWarmerTest.php index 53b16fcdf7774..d32e2d5de560f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/CacheWarmer/ExpressionCacheWarmerTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/CacheWarmer/ExpressionCacheWarmerTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\CacheWarmer\ExpressionCacheWarmer; use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\ExpressionLanguage\ParsedExpression; use Symfony\Component\Security\Core\Authorization\ExpressionLanguage; class ExpressionCacheWarmerTest extends TestCase @@ -22,13 +23,21 @@ public function testWarmUp() { $expressions = [new Expression('A'), new Expression('B')]; + $series = [ + [$expressions[0], ['token', 'user', 'object', 'subject', 'role_names', 'request', 'trust_resolver']], + [$expressions[1], ['token', 'user', 'object', 'subject', 'role_names', 'request', 'trust_resolver']], + ]; + $expressionLang = $this->createMock(ExpressionLanguage::class); $expressionLang->expects($this->exactly(2)) ->method('parse') - ->withConsecutive( - [$expressions[0], ['token', 'user', 'object', 'subject', 'role_names', 'request', 'trust_resolver']], - [$expressions[1], ['token', 'user', 'object', 'subject', 'role_names', 'request', 'trust_resolver']] - ); + ->willReturnCallback(function (...$args) use (&$series) { + $expectedArgs = array_shift($series); + $this->assertSame($expectedArgs, $args); + + return $this->createMock(ParsedExpression::class); + }) + ; (new ExpressionCacheWarmer($expressions, $expressionLang))->warmUp(''); } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php index c3283dae5cdd7..abc2718865467 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DataCollector/SecurityDataCollectorTest.php @@ -24,6 +24,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authorization\TraceableAccessDecisionManager; use Symfony\Component\Security\Core\Authorization\Voter\TraceableVoter; @@ -32,6 +33,7 @@ use Symfony\Component\Security\Core\User\InMemoryUser; use Symfony\Component\Security\Http\FirewallMapInterface; use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator; +use Symfony\Component\VarDumper\Caster\ClassStub; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; class SecurityDataCollectorTest extends TestCase @@ -219,17 +221,36 @@ public function testGetListeners() $this->assertSame(1, $listenerCalled); } - public function providerCollectDecisionLog(): \Generator + public function testCollectCollectsDecisionLogWhenStrategyIsAffirmative() { - $voter1 = $this->getMockBuilder(VoterInterface::class)->getMockForAbstractClass(); - $voter2 = $this->getMockBuilder(VoterInterface::class)->getMockForAbstractClass(); + $voter1 = new DummyVoter(); + $voter2 = new DummyVoter(); - $eventDispatcher = $this->getMockBuilder(EventDispatcherInterface::class)->getMockForAbstractClass(); - $decoratedVoter1 = new TraceableVoter($voter1, $eventDispatcher); + $decoratedVoter1 = new TraceableVoter($voter1, new class() implements EventDispatcherInterface { + public function dispatch(object $event, string $eventName = null): object + { + return new \stdClass(); + } + }); - yield [ - MainConfiguration::STRATEGY_AFFIRMATIVE, - [[ + $strategy = MainConfiguration::STRATEGY_AFFIRMATIVE; + + $accessDecisionManager = $this->createMock(TraceableAccessDecisionManager::class); + + $accessDecisionManager + ->method('getStrategy') + ->willReturn($strategy); + + $accessDecisionManager + ->method('getVoters') + ->willReturn([ + $decoratedVoter1, + $decoratedVoter1, + ]); + + $accessDecisionManager + ->method('getDecisionLog') + ->willReturn([[ 'attributes' => ['view'], 'object' => new \stdClass(), 'result' => true, @@ -237,23 +258,74 @@ public function providerCollectDecisionLog(): \Generator ['voter' => $voter1, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], ['voter' => $voter2, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], ], - ]], - [$decoratedVoter1, $decoratedVoter1], - [\get_class($voter1), \get_class($voter2)], - [[ - 'attributes' => ['view'], - 'object' => new \stdClass(), - 'result' => true, - 'voter_details' => [ - ['class' => \get_class($voter1), 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], - ['class' => \get_class($voter2), 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], - ], - ]], + ]]); + + $dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null, true); + + $dataCollector->collect(new Request(), new Response()); + + $actualDecisionLog = $dataCollector->getAccessDecisionLog(); + + $expectedDecisionLog = [[ + 'attributes' => ['view'], + 'object' => new \stdClass(), + 'result' => true, + 'voter_details' => [ + ['class' => $voter1::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], + ['class' => $voter2::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_ABSTAIN], + ], + ]]; + + $this->assertEquals($actualDecisionLog, $expectedDecisionLog, 'Wrong value returned by getAccessDecisionLog'); + + $actualVoterClasses = array_map(static function (ClassStub $classStub): string { + return (string) $classStub; + }, $dataCollector->getVoters()); + + $expectedVoterClasses = [ + $voter1::class, + $voter2::class, ]; - yield [ - MainConfiguration::STRATEGY_UNANIMOUS, - [ + $this->assertSame( + $actualVoterClasses, + $expectedVoterClasses, + 'Wrong value returned by getVoters' + ); + + $this->assertSame($dataCollector->getVoterStrategy(), $strategy, 'Wrong value returned by getVoterStrategy'); + } + + public function testCollectCollectsDecisionLogWhenStrategyIsUnanimous() + { + $voter1 = new DummyVoter(); + $voter2 = new DummyVoter(); + + $decoratedVoter1 = new TraceableVoter($voter1, new class() implements EventDispatcherInterface { + public function dispatch(object $event, string $eventName = null): object + { + return new \stdClass(); + } + }); + + $strategy = MainConfiguration::STRATEGY_UNANIMOUS; + + $accessDecisionManager = $this->createMock(TraceableAccessDecisionManager::class); + + $accessDecisionManager + ->method('getStrategy') + ->willReturn($strategy); + + $accessDecisionManager + ->method('getVoters') + ->willReturn([ + $decoratedVoter1, + $decoratedVoter1, + ]); + + $accessDecisionManager + ->method('getDecisionLog') + ->willReturn([ [ 'attributes' => ['view', 'edit'], 'object' => new \stdClass(), @@ -274,82 +346,58 @@ public function providerCollectDecisionLog(): \Generator ['voter' => $voter2, 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], ], ], - ], - [$decoratedVoter1, $decoratedVoter1], - [\get_class($voter1), \get_class($voter2)], + ]); + + $dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null, true); + + $dataCollector->collect(new Request(), new Response()); + + $actualDecisionLog = $dataCollector->getAccessDecisionLog(); + + $expectedDecisionLog = [ [ - [ - 'attributes' => ['view', 'edit'], - 'object' => new \stdClass(), - 'result' => false, - 'voter_details' => [ - ['class' => \get_class($voter1), 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_DENIED], - ['class' => \get_class($voter1), 'attributes' => ['edit'], 'vote' => VoterInterface::ACCESS_DENIED], - ['class' => \get_class($voter2), 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_GRANTED], - ['class' => \get_class($voter2), 'attributes' => ['edit'], 'vote' => VoterInterface::ACCESS_GRANTED], - ], + 'attributes' => ['view', 'edit'], + 'object' => new \stdClass(), + 'result' => false, + 'voter_details' => [ + ['class' => $voter1::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_DENIED], + ['class' => $voter1::class, 'attributes' => ['edit'], 'vote' => VoterInterface::ACCESS_DENIED], + ['class' => $voter2::class, 'attributes' => ['view'], 'vote' => VoterInterface::ACCESS_GRANTED], + ['class' => $voter2::class, 'attributes' => ['edit'], 'vote' => VoterInterface::ACCESS_GRANTED], ], - [ - 'attributes' => ['update'], - 'object' => new \stdClass(), - 'result' => true, - 'voter_details' => [ - ['class' => \get_class($voter1), 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], - ['class' => \get_class($voter2), 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], - ], + ], + [ + 'attributes' => ['update'], + 'object' => new \stdClass(), + 'result' => true, + 'voter_details' => [ + ['class' => $voter1::class, 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], + ['class' => $voter2::class, 'attributes' => ['update'], 'vote' => VoterInterface::ACCESS_GRANTED], ], ], ]; - } - - /** - * Test the returned data when AccessDecisionManager is a TraceableAccessDecisionManager. - * - * @param string $strategy strategy returned by the AccessDecisionManager - * @param array $voters voters returned by AccessDecisionManager - * @param array $decisionLog log of the votes and final decisions from AccessDecisionManager - * @param array $expectedVoterClasses expected voter classes returned by the collector - * @param array $expectedDecisionLog expected decision log returned by the collector - * - * @dataProvider providerCollectDecisionLog - */ - public function testCollectDecisionLog(string $strategy, array $decisionLog, array $voters, array $expectedVoterClasses, array $expectedDecisionLog) - { - $accessDecisionManager = $this - ->getMockBuilder(TraceableAccessDecisionManager::class) - ->disableOriginalConstructor() - ->setMethods(['getStrategy', 'getVoters', 'getDecisionLog']) - ->getMock(); - $accessDecisionManager - ->expects($this->any()) - ->method('getStrategy') - ->willReturn($strategy); + $this->assertEquals($actualDecisionLog, $expectedDecisionLog, 'Wrong value returned by getAccessDecisionLog'); - $accessDecisionManager - ->expects($this->any()) - ->method('getVoters') - ->willReturn($voters); + $actualVoterClasses = array_map(static function (ClassStub $classStub): string { + return (string) $classStub; + }, $dataCollector->getVoters()); - $accessDecisionManager - ->expects($this->any()) - ->method('getDecisionLog') - ->willReturn($decisionLog); - - $dataCollector = new SecurityDataCollector(null, null, null, $accessDecisionManager, null, null, true); - $dataCollector->collect(new Request(), new Response()); - - $this->assertEquals($dataCollector->getAccessDecisionLog(), $expectedDecisionLog, 'Wrong value returned by getAccessDecisionLog'); + $expectedVoterClasses = [ + $voter1::class, + $voter2::class, + ]; $this->assertSame( - array_map(function ($classStub) { return (string) $classStub; }, $dataCollector->getVoters()), + $actualVoterClasses, $expectedVoterClasses, 'Wrong value returned by getVoters' ); + $this->assertSame($dataCollector->getVoterStrategy(), $strategy, 'Wrong value returned by getVoterStrategy'); } - public function provideRoles() + public static function provideRoles() { return [ // Basic roles @@ -380,3 +428,10 @@ private function getRoleHierarchy() ]); } } + +final class DummyVoter implements VoterInterface +{ + public function vote(TokenInterface $token, mixed $subject, array $attributes): int + { + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php index bc4d9fe8876be..48052e9271a08 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/AddSessionDomainConstraintPassTest.php @@ -139,7 +139,6 @@ private function createContainer($sessionStorageOptions) $config = [ 'security' => [ - 'enable_authenticator_manager' => true, 'providers' => ['some_provider' => ['id' => 'foo']], 'firewalls' => ['some_firewall' => ['security' => false]], ], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPassTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPassTest.php index bd0ea68520abe..4ec5149f7a262 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPassTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Compiler/RegisterGlobalSecurityEventListenersPassTest.php @@ -56,7 +56,6 @@ protected function setUp(): void public function testEventIsPropagated(string $configuredEvent, string $registeredEvent) { $this->container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => ['main' => ['pattern' => '/', 'http_basic' => true]], ]); @@ -70,7 +69,7 @@ public function testEventIsPropagated(string $configuredEvent, string $registere ]); } - public function providePropagatedEvents(): array + public static function providePropagatedEvents(): array { return [ [CheckPassportEvent::class, CheckPassportEvent::class], @@ -90,7 +89,6 @@ public function providePropagatedEvents(): array public function testRegisterCustomListener() { $this->container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => ['main' => ['pattern' => '/', 'http_basic' => true]], ]); @@ -111,7 +109,6 @@ public function testRegisterCustomListener() public function testRegisterCustomSubscriber() { $this->container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => ['main' => ['pattern' => '/', 'http_basic' => true]], ]); @@ -131,7 +128,6 @@ public function testRegisterCustomSubscriber() public function testMultipleFirewalls() { $this->container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => ['main' => ['pattern' => '/', 'http_basic' => true], 'api' => ['pattern' => '/api', 'http_basic' => true]], ]); @@ -161,7 +157,6 @@ public function testMultipleFirewalls() public function testListenerAlreadySpecific() { $this->container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => ['main' => ['pattern' => '/', 'http_basic' => true]], ]); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTestCase.php similarity index 89% rename from src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php rename to src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTestCase.php index e68f1250df39c..4181bac58323f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/CompleteConfigurationTestCase.php @@ -19,6 +19,11 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpFoundation\RequestMatcher\AttributesRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\HostRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\MethodRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\PathRequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\PortRequestMatcher; use Symfony\Component\PasswordHasher\Hasher\NativePasswordHasher; use Symfony\Component\PasswordHasher\Hasher\Pbkdf2PasswordHasher; use Symfony\Component\PasswordHasher\Hasher\PlaintextPasswordHasher; @@ -29,7 +34,7 @@ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge; -abstract class CompleteConfigurationTest extends TestCase +abstract class CompleteConfigurationTestCase extends TestCase { abstract protected function getLoader(ContainerBuilder $container); @@ -131,7 +136,7 @@ public function testFirewalls() [ 'simple', 'security.user_checker', - '.security.request_matcher.xmi9dcw', + '.security.request_matcher.h5ibf38', false, false, '', @@ -165,7 +170,7 @@ public function testFirewalls() [ 'parameter' => '_switch_user', 'role' => 'ROLE_ALLOWED_TO_SWITCH', - 'target_url' => null, + 'target_route' => null, ], [ 'csrf_parameter' => '_csrf_token', @@ -180,7 +185,7 @@ public function testFirewalls() [ 'host', 'security.user_checker', - '.security.request_matcher.iw4hyjb', + '.security.request_matcher.bcmu4fb', true, false, 'security.user.provider.concrete.default', @@ -248,20 +253,27 @@ public function testFirewallRequestMatchers() foreach ($arguments[1]->getValues() as $reference) { if ($reference instanceof Reference) { $definition = $container->getDefinition((string) $reference); - $matchers[] = $definition->getArguments(); + $matchers[] = $definition->getArgument(0); } } - $this->assertEquals([ - [ - '/login', - ], - [ - '/test', - 'foo\\.example\\.org', - ['GET', 'POST'], - ], - ], $matchers); + $this->assertCount(2, $matchers); + + $this->assertCount(1, $matchers[0]); + $def = $container->getDefinition((string) $matchers[0][0]); + $this->assertSame(PathRequestMatcher::class, $def->getClass()); + $this->assertSame('/login', $def->getArgument(0)); + + $this->assertCount(3, $matchers[1]); + $def = $container->getDefinition((string) $matchers[1][0]); + $this->assertSame(MethodRequestMatcher::class, $def->getClass()); + $this->assertSame(['GET', 'POST'], $def->getArgument(0)); + $def = $container->getDefinition((string) $matchers[1][1]); + $this->assertSame(PathRequestMatcher::class, $def->getClass()); + $this->assertSame('/test', $def->getArgument(0)); + $def = $container->getDefinition((string) $matchers[1][2]); + $this->assertSame(HostRequestMatcher::class, $def->getClass()); + $this->assertSame('foo\\.example\\.org', $def->getArgument(0)); } public function testUserCheckerAliasIsRegistered() @@ -294,23 +306,36 @@ public function testAccess() if (1 === $i) { $this->assertEquals(['ROLE_USER'], $attributes); $this->assertEquals('https', $channel); - $this->assertEquals( - ['/blog/524', null, ['GET', 'POST'], [], [], null, 8000], - $requestMatcher->getArguments() - ); + $this->assertCount(3, $requestMatcher->getArgument(0)); + $def = $container->getDefinition((string) $requestMatcher->getArgument(0)[0]); + $this->assertSame(MethodRequestMatcher::class, $def->getClass()); + $this->assertSame(['GET', 'POST'], $def->getArgument(0)); + $def = $container->getDefinition((string) $requestMatcher->getArgument(0)[1]); + $this->assertSame(PathRequestMatcher::class, $def->getClass()); + $this->assertSame('/blog/524', $def->getArgument(0)); + $def = $container->getDefinition((string) $requestMatcher->getArgument(0)[2]); + $this->assertSame(PortRequestMatcher::class, $def->getClass()); + $this->assertSame(8000, $def->getArgument(0)); } elseif (2 === $i) { $this->assertEquals(['IS_AUTHENTICATED_ANONYMOUSLY'], $attributes); $this->assertNull($channel); - $this->assertEquals( - ['/blog/.*'], - $requestMatcher->getArguments() - ); + $this->assertCount(1, $requestMatcher->getArgument(0)); + $def = $container->getDefinition((string) $requestMatcher->getArgument(0)[0]); + $this->assertSame(PathRequestMatcher::class, $def->getClass()); + $this->assertSame('/blog/.*', $def->getArgument(0)); } elseif (3 === $i) { $this->assertEquals('IS_AUTHENTICATED_ANONYMOUSLY', $attributes[0]); $expression = $container->getDefinition((string) $attributes[1])->getArgument(0); $this->assertEquals("token.getUserIdentifier() matches '/^admin/'", $expression); + } elseif (4 === $i) { + $this->assertEquals(['ROLE_ADMIN'], $attributes); + $def = $container->getDefinition((string) $requestMatcher->getArgument(0)[0]); + $this->assertSame(AttributesRequestMatcher::class, $def->getClass()); + $this->assertSame(['_controller' => 'AdminController::index', '_route' => 'admin'], $def->getArgument(0)); } } + + $this->assertCount(4, $matcherIds); } public function testMerge() diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_customized_config.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_customized_config.php index 6d011aebd5998..1d0a090f3f589 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_customized_config.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_customized_config.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'access_decision_manager' => [ 'allow_if_all_abstain' => true, 'allow_if_equal_granted_denied' => false, diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_default_strategy.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_default_strategy.php index cfa7751b7b4bd..1f0adbf3010f1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_default_strategy.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_default_strategy.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => [ 'memory' => [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_service.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_service.php index dee30bedc9c9f..8f615904ddf0d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_service.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_service.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'access_decision_manager' => [ 'service' => 'app.access_decision_manager', ], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_service_and_strategy.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_service_and_strategy.php index d964561c42657..bd78bdf24d578 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_service_and_strategy.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_service_and_strategy.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'access_decision_manager' => [ 'service' => 'app.access_decision_manager', 'strategy' => 'affirmative', diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_strategy_service.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_strategy_service.php index 8024e3a72f25b..6a435c252fe86 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_strategy_service.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/access_decision_manager_strategy_service.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'access_decision_manager' => [ 'strategy_service' => 'app.custom_access_decision_strategy', ], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2i_hasher.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2i_hasher.php index 6254f5747841f..341f772e87523 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2i_hasher.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/argon2i_hasher.php @@ -3,7 +3,6 @@ $this->load('container1.php'); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'password_hashers' => [ 'JMS\FooBundle\Entity\User7' => [ 'algorithm' => 'argon2i', diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/authenticator_manager.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/authenticator_manager.php index fa53fb980f67a..3dd4be36ed361 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/authenticator_manager.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/authenticator_manager.php @@ -3,7 +3,6 @@ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => [ 'main' => [ 'required_badges' => [CsrfTokenBadge::class, 'RememberMeBadge'], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/bcrypt_hasher.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/bcrypt_hasher.php index ac29b31e096e6..a416b3440d426 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/bcrypt_hasher.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/bcrypt_hasher.php @@ -3,7 +3,6 @@ $this->load('container1.php'); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'password_hashers' => [ 'JMS\FooBundle\Entity\User7' => [ 'algorithm' => 'bcrypt', diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php index f155c135a26ad..ea863ca306d75 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/container1.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'password_hashers' => [ 'JMS\FooBundle\Entity\User1' => 'plaintext', 'JMS\FooBundle\Entity\User2' => [ @@ -97,6 +96,7 @@ ['path' => '/blog/524', 'role' => 'ROLE_USER', 'requires_channel' => 'https', 'methods' => ['get', 'POST'], 'port' => 8000], ['path' => '/blog/.*', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'], ['path' => '/blog/524', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'allow_if' => "token.getUserIdentifier() matches '/^admin/'"], + ['role' => 'ROLE_ADMIN', 'attributes' => ['_controller' => 'AdminController::index'], 'route' => 'admin'], ], 'role_hierarchy' => [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_provider.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_provider.php index eeec20726a588..68b8439a7de5a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_provider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_provider.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => [ 'memory' => $memory = [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_undefined_provider.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_undefined_provider.php index dd90214810572..7c811cae1a4dd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_undefined_provider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/firewall_undefined_provider.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => [ 'memory' => [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_provider.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_provider.php index 8ddc21f13edc3..0a6a79f5f208c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_provider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_provider.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => [ 'memory' => [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_undefined_provider.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_undefined_provider.php index 10661fae2010b..cc0b776e432c4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_undefined_provider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/listener_undefined_provider.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => [ 'memory' => [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/logout_delete_cookies.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/logout_delete_cookies.php index 7a40881b655d1..8ffe12e3eb929 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/logout_delete_cookies.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/logout_delete_cookies.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php index 03a5f1d28c87c..d0bd809579e89 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge.php @@ -3,7 +3,6 @@ $this->load('merge_import.php'); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php index 198935390cfd1..c85937d6ea2c9 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/merge_import.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => [ 'main' => [ 'form_login' => [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/migrating_hasher.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/migrating_hasher.php index 3f68562c8a07c..342ea64805eff 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/migrating_hasher.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/migrating_hasher.php @@ -3,7 +3,6 @@ $this->load('container1.php'); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'password_hashers' => [ 'JMS\FooBundle\Entity\User7' => [ 'algorithm' => 'argon2i', diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/no_custom_user_checker.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/no_custom_user_checker.php index 29d93a1f2ec3e..8559ac7138825 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/no_custom_user_checker.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/no_custom_user_checker.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => [ 'memory' => [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php index 0e8963f9297e3..cfbef609a18db 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/remember_me_options.php @@ -1,7 +1,6 @@ loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/sodium_hasher.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/sodium_hasher.php index 8f17965b25bd6..3ec569ae9a6e2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/sodium_hasher.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/php/sodium_hasher.php @@ -3,7 +3,6 @@ $this->load('container1.php'); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'password_hashers' => [ 'JMS\FooBundle\Entity\User7' => [ 'algorithm' => 'sodium', diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml index 9116042908d12..012c8dac7b069 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_customized_config.xml @@ -7,7 +7,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_default_strategy.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_default_strategy.xml index 85c8050cbc70f..1011f45c4accc 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_default_strategy.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_default_strategy.xml @@ -7,7 +7,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service.xml index 3e189b8c61ff6..ebc208c057168 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service.xml @@ -7,7 +7,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service_and_strategy.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service_and_strategy.xml index 5b70a4614addb..1f2133ffe02f1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service_and_strategy.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_service_and_strategy.xml @@ -7,7 +7,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_strategy_service.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_strategy_service.xml index 94763b543f4ec..b161ddb5e671c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_strategy_service.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/access_decision_manager_strategy_service.xml @@ -7,7 +7,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2i_hasher.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2i_hasher.xml index 8168af333e13d..3dc2c685be321 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2i_hasher.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/argon2i_hasher.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/authenticator_manager.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/authenticator_manager.xml index 0185b81c440c8..54b5189a95dcb 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/authenticator_manager.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/authenticator_manager.xml @@ -7,7 +7,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge RememberMeBadge diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/bcrypt_hasher.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/bcrypt_hasher.xml index a1f784ed96761..d4c5d3ded1a11 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/bcrypt_hasher.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/bcrypt_hasher.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml index c97dd5bf7ebf0..66dd30ea8d26a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/container1.xml @@ -8,7 +8,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + @@ -76,5 +76,8 @@ + + AdminController::index + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml index 6f74984045970..52a64d2f42908 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_provider.xml @@ -8,7 +8,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml index a80f613e00331..a61d597fad573 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/firewall_undefined_provider.xml @@ -8,7 +8,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml index b45f378a5ba68..1ba3c5e5098e4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_provider.xml @@ -8,7 +8,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml index bdf9d5ec837f0..314f25d263d71 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/listener_undefined_provider.xml @@ -8,7 +8,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/logout_delete_cookies.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/logout_delete_cookies.xml index e817b48901311..e66043c359a15 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/logout_delete_cookies.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/logout_delete_cookies.xml @@ -8,7 +8,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml index 569e20e65e3b9..8caaeeb153e2c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml index c7c237f2fefa4..e518a7d9acd7a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/merge_import.xml @@ -8,7 +8,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/migrating_hasher.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/migrating_hasher.xml index d0d0b4ff91ea7..a4a9d2010dd71 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/migrating_hasher.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/migrating_hasher.xml @@ -12,7 +12,7 @@ - + bcrypt diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml index c4dea529ba452..6b51f236a50a7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/no_custom_user_checker.xml @@ -7,7 +7,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml index 9921d6c5fe6b0..767397ada3515 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/remember_me_options.xml @@ -8,7 +8,7 @@ http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/sodium_hasher.xml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/sodium_hasher.xml index 67d4d1304b31e..fd5cacef7b8a4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/sodium_hasher.xml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/xml/sodium_hasher.xml @@ -12,7 +12,7 @@ - + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_customized_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_customized_config.yml index db0f2b551cf92..a8d044f1dec5d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_customized_config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_customized_config.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true access_decision_manager: allow_if_all_abstain: true allow_if_equal_granted_denied: false diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_default_strategy.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_default_strategy.yml index adfeffa5fb8c3..f7fb5adc2c5d4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_default_strategy.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_default_strategy.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true providers: default: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_service.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_service.yml index b162a45916194..7ef3d8d93c3ab 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_service.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_service.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true access_decision_manager: service: app.access_decision_manager providers: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_service_and_strategy.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_service_and_strategy.yml index ced97bb5337b8..bd38b21ef3536 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_service_and_strategy.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_service_and_strategy.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true access_decision_manager: service: app.access_decision_manager strategy: affirmative diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_strategy_service.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_strategy_service.yml index 907cdfe8410c6..5d2afc61d78b6 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_strategy_service.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/access_decision_manager_strategy_service.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true access_decision_manager: strategy_service: app.custom_access_decision_strategy providers: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_hasher.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_hasher.yml index 0ae8214f1246e..1079d6e5f8efc 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_hasher.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/argon2i_hasher.yml @@ -2,7 +2,6 @@ imports: - { resource: container1.yml } security: - enable_authenticator_manager: true password_hashers: JMS\FooBundle\Entity\User7: algorithm: argon2i diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/authenticator_manager.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/authenticator_manager.yml index 7efae5356f0f4..34113fb910b18 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/authenticator_manager.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/authenticator_manager.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true firewalls: main: required_badges: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/bcrypt_hasher.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/bcrypt_hasher.yml index c8a4a71ce4667..8e8397486d68e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/bcrypt_hasher.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/bcrypt_hasher.yml @@ -2,7 +2,6 @@ imports: - { resource: container1.yml } security: - enable_authenticator_manager: true password_hashers: JMS\FooBundle\Entity\User7: algorithm: bcrypt diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml index 16de382cc1f2f..a3123867d8fea 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/container1.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true password_hashers: JMS\FooBundle\Entity\User1: plaintext JMS\FooBundle\Entity\User2: @@ -84,3 +83,4 @@ security: path: /blog/.* role: IS_AUTHENTICATED_ANONYMOUSLY - { path: /blog/524, role: IS_AUTHENTICATED_ANONYMOUSLY, allow_if: "token.getUserIdentifier() matches '/^admin/'" } + - { role: ROLE_ADMIN, attributes: { _controller: 'AdminController::index' }, route: 'admin' } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_provider.yml index 9aa008a75d302..11c329aa8e2fe 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_provider.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true providers: default: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_undefined_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_undefined_provider.yml index e10a2eaf398b1..ec2664054009c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_undefined_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/firewall_undefined_provider.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true providers: default: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_provider.yml index c3c1c282898a0..652f23b5f0425 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_provider.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true providers: default: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_undefined_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_undefined_provider.yml index 3cab5355ddd4e..1916df4c2e7ca 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_undefined_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/listener_undefined_provider.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true providers: default: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/logout_delete_cookies.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/logout_delete_cookies.yml index a94bc1ff8f32b..09bea8c13ab37 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/logout_delete_cookies.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/logout_delete_cookies.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true providers: default: id: foo diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml index 50ae533138613..60c0bbea558e7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge.yml @@ -2,7 +2,6 @@ imports: - { resource: merge_import.yml } security: - enable_authenticator_manager: true providers: default: { id: foo } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml index bf91f016a29c4..4f8db0a09f7b4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/merge_import.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true firewalls: main: form_login: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/migrating_hasher.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/migrating_hasher.yml index 60ac97f48fce9..8657b1ee744ad 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/migrating_hasher.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/migrating_hasher.yml @@ -2,7 +2,6 @@ imports: - { resource: container1.yml } security: - enable_authenticator_manager: true password_hashers: JMS\FooBundle\Entity\User7: algorithm: argon2i diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/no_custom_user_checker.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/no_custom_user_checker.yml index d42c45edb0a31..8b7b2e9296cbb 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/no_custom_user_checker.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/no_custom_user_checker.yml @@ -1,6 +1,4 @@ security: - enable_authenticator_manager: true - providers: default: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml index b4a1a8f6e49b5..a521c8c6a803d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/remember_me_options.yml @@ -1,6 +1,4 @@ security: - enable_authenticator_manager: true - providers: default: id: foo diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/sodium_hasher.yml b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/sodium_hasher.yml index 7c417bfe71d08..955a0b2a2059c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/sodium_hasher.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Fixtures/yml/sodium_hasher.yml @@ -2,7 +2,6 @@ imports: - { resource: container1.yml } security: - enable_authenticator_manager: true password_hashers: JMS\FooBundle\Entity\User7: algorithm: sodium diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php index c64a7b49ba56d..20bc11269fa6f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/MainConfigurationTest.php @@ -122,7 +122,7 @@ public function testLogoutCsrf() $assertions = [ 'custom_token_generator' => [true, 'a_token_generator'], - 'default_token_generator' => [true, 'security.csrf.token_generator'], + 'default_token_generator' => [true, 'security.csrf.token_manager'], 'disabled_csrf' => [false, null], 'empty' => [false, null], ]; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php index 846d01d5337ca..c4297c7b9c0e1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/PhpCompleteConfigurationTest.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; -class PhpCompleteConfigurationTest extends CompleteConfigurationTest +class PhpCompleteConfigurationTest extends CompleteConfigurationTestCase { protected function getLoader(ContainerBuilder $container) { diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php index ff4108d25cdc9..be300e7526b82 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/Security/Factory/AbstractFactoryTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\AbstractFactory; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\ContainerBuilder; class AbstractFactoryTest extends TestCase @@ -43,16 +44,23 @@ public function testDefaultFailureHandler($serviceId, $defaultHandlerInjection) $failureHandler = $this->container->getDefinition('security.authentication.failure_handler.foo.stub'); + $expectedFailureHandlerOptions = ['login_path' => '/bar']; $methodCalls = $failureHandler->getMethodCalls(); if ($defaultHandlerInjection) { $this->assertEquals('setOptions', $methodCalls[0][0]); - $this->assertEquals(['login_path' => '/bar'], $methodCalls[0][1][0]); + $this->assertEquals($expectedFailureHandlerOptions, $methodCalls[0][1][0]); } else { $this->assertCount(0, $methodCalls); + $this->assertInstanceOf(ChildDefinition::class, $failureHandler); + $this->assertEquals('security.authentication.custom_failure_handler', $failureHandler->getParent()); + $failureHandlerArguments = $failureHandler->getArguments(); + $this->assertInstanceOf(ChildDefinition::class, $failureHandlerArguments['index_0']); + $this->assertEquals($serviceId, $failureHandlerArguments['index_0']->getParent()); + $this->assertEquals($expectedFailureHandlerOptions, $failureHandlerArguments['index_1']); } } - public function getFailureHandlers() + public static function getFailureHandlers() { return [ [null, true], @@ -80,17 +88,26 @@ public function testDefaultSuccessHandler($serviceId, $defaultHandlerInjection) $successHandler = $this->container->getDefinition('security.authentication.success_handler.foo.stub'); $methodCalls = $successHandler->getMethodCalls(); + $expectedSuccessHandlerOptions = ['default_target_path' => '/bar']; + $expectedFirewallName = 'foo'; if ($defaultHandlerInjection) { $this->assertEquals('setOptions', $methodCalls[0][0]); - $this->assertEquals(['default_target_path' => '/bar'], $methodCalls[0][1][0]); + $this->assertEquals($expectedSuccessHandlerOptions, $methodCalls[0][1][0]); $this->assertEquals('setFirewallName', $methodCalls[1][0]); - $this->assertEquals(['foo'], $methodCalls[1][1]); + $this->assertEquals($expectedFirewallName, $methodCalls[1][1][0]); } else { $this->assertCount(0, $methodCalls); + $this->assertInstanceOf(ChildDefinition::class, $successHandler); + $this->assertEquals('security.authentication.custom_success_handler', $successHandler->getParent()); + $successHandlerArguments = $successHandler->getArguments(); + $this->assertInstanceOf(ChildDefinition::class, $successHandlerArguments['index_0']); + $this->assertEquals($serviceId, $successHandlerArguments['index_0']->getParent()); + $this->assertEquals($expectedSuccessHandlerOptions, $successHandlerArguments['index_1']); + $this->assertEquals($expectedFirewallName, $successHandlerArguments['index_2']); } } - public function getSuccessHandlers() + public static function getSuccessHandlers() { return [ [null, true], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php index 5323c16f3b2a1..faa31b5f27c2d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/SecurityExtensionTest.php @@ -26,7 +26,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ExpressionLanguage\Expression; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestMatcher; +use Symfony\Component\HttpFoundation\RequestMatcher\PathRequestMatcher; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; @@ -50,7 +50,6 @@ public function testInvalidCheckPath() $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -78,7 +77,6 @@ public function testFirewallWithInvalidUserProvider() $extension->addUserProviderFactory(new DummyProvider()); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'my_foo' => ['foo' => []], ], @@ -99,7 +97,6 @@ public function testDisableRoleHierarchyVoter() $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -124,7 +121,6 @@ public function testSwitchUserNotStatelessOnStatelessFirewall() $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -147,7 +143,6 @@ public function testPerListenerProvider() { $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'first' => ['id' => 'foo'], 'second' => ['id' => 'bar'], @@ -170,7 +165,6 @@ public function testMissingProviderForListener() $this->expectExceptionMessage('Not configuring explicitly the provider for the "http_basic" authenticator on "ambiguous" firewall is ambiguous as there is more than one registered provider.'); $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'first' => ['id' => 'foo'], 'second' => ['id' => 'bar'], @@ -191,7 +185,6 @@ public function testPerListenerProviderWithRememberMeAndAnonymous() { $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'first' => ['id' => 'foo'], 'second' => ['id' => 'bar'], @@ -216,7 +209,6 @@ public function testRegisterRequestMatchersWithAllowIfExpression() $rawExpression = "'foo' == 'bar' or 1 in [1, 3, 3]"; $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -256,11 +248,10 @@ public function testRegisterAccessControlWithSpecifiedRequestMatcherService() $container = $this->getRawContainer(); $requestMatcherId = 'My\Test\RequestMatcher'; - $requestMatcher = new RequestMatcher('/'); + $requestMatcher = new PathRequestMatcher('/'); $container->set($requestMatcherId, $requestMatcher); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -291,11 +282,10 @@ public function testRegisterAccessControlWithRequestMatcherAndAdditionalOptionsT $container = $this->getRawContainer(); $requestMatcherId = 'My\Test\RequestMatcher'; - $requestMatcher = new RequestMatcher('/'); + $requestMatcher = new PathRequestMatcher('/'); $container->set($requestMatcherId, $requestMatcher); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -316,20 +306,115 @@ public function testRegisterAccessControlWithRequestMatcherAndAdditionalOptionsT $container->compile(); } - public function provideAdditionalRequestMatcherConstraints() + public static function provideAdditionalRequestMatcherConstraints() { yield 'Invalid configuration with path' => [['path' => '^/url']]; yield 'Invalid configuration with host' => [['host' => 'example.com']]; yield 'Invalid configuration with port' => [['port' => 80]]; yield 'Invalid configuration with methods' => [['methods' => ['POST']]]; yield 'Invalid configuration with ips' => [['ips' => ['0.0.0.0']]]; + yield 'Invalid configuration with attributes' => [['attributes' => ['_route' => 'foo_route']]]; + yield 'Invalid configuration with route' => [['route' => 'foo_route']]; + } + + public function testRegisterAccessControlWithSpecifiedAttributes() + { + $container = $this->getRawContainer(); + $container->loadFromExtension('security', [ + 'providers' => [ + 'default' => ['id' => 'foo'], + ], + 'firewalls' => [ + 'some_firewall' => [ + 'pattern' => '/.*', + 'http_basic' => [], + ], + ], + 'access_control' => [ + ['attributes' => ['_route' => 'foo_route']], + ], + ]); + + $container->compile(); + + $accessMap = $container->getDefinition('security.access_map'); + $this->assertCount(1, $accessMap->getMethodCalls()); + $call = $accessMap->getMethodCalls()[0]; + $this->assertSame('add', $call[0]); + $args = $call[1]; + + $chainRequestMatcherDefinition = $container->getDefinition((string) $args[0]); + $chainRequestMatcherConstructorArguments = $chainRequestMatcherDefinition->getArguments(); + $attributesRequestMatcher = $container->getDefinition((string) $chainRequestMatcherConstructorArguments[0][0]); + + $this->assertCount(1, $attributesRequestMatcher->getArguments()); + $this->assertArrayHasKey('_route', $attributesRequestMatcher->getArgument(0)); + $this->assertSame('foo_route', $attributesRequestMatcher->getArgument(0)['_route']); + } + + public function testRegisterAccessControlWithSpecifiedRoute() + { + $container = $this->getRawContainer(); + $container->loadFromExtension('security', [ + 'providers' => [ + 'default' => ['id' => 'foo'], + ], + 'firewalls' => [ + 'some_firewall' => [ + 'pattern' => '/.*', + 'http_basic' => [], + ], + ], + 'access_control' => [ + ['route' => 'foo_route'], + ], + ]); + + $container->compile(); + + $accessMap = $container->getDefinition('security.access_map'); + $this->assertCount(1, $accessMap->getMethodCalls()); + $call = $accessMap->getMethodCalls()[0]; + $this->assertSame('add', $call[0]); + $args = $call[1]; + + $chainRequestMatcherDefinition = $container->getDefinition((string) $args[0]); + $chainRequestMatcherConstructorArguments = $chainRequestMatcherDefinition->getArguments(); + $attributesRequestMatcher = $container->getDefinition((string) $chainRequestMatcherConstructorArguments[0][0]); + + $this->assertCount(1, $attributesRequestMatcher->getArguments()); + $this->assertArrayHasKey('_route', $attributesRequestMatcher->getArgument(0)); + $this->assertSame('foo_route', $attributesRequestMatcher->getArgument(0)['_route']); + } + + public function testRegisterAccessControlWithSpecifiedAttributesThrowsException() + { + $container = $this->getRawContainer(); + $container->loadFromExtension('security', [ + 'providers' => [ + 'default' => ['id' => 'foo'], + ], + 'firewalls' => [ + 'some_firewall' => [ + 'pattern' => '/.*', + 'http_basic' => [], + ], + ], + 'access_control' => [ + ['route' => 'anything', 'attributes' => ['_route' => 'foo_route']], + ], + ]); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The "route" option should not be specified alongside "attributes._route" option. Use just one of the options.'); + + $container->compile(); } public function testRemovesExpressionCacheWarmerDefinitionIfNoExpressions() { $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -350,7 +435,6 @@ public function testRegisterTheUserProviderAlias() $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -373,7 +457,6 @@ public function testDoNotRegisterTheUserProviderAliasWithMultipleProviders() $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'first' => ['id' => 'foo'], 'second' => ['id' => 'bar'], @@ -400,8 +483,6 @@ public function testFirewallWithNoUserProviderTriggerDeprecation() $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, - 'providers' => [ 'first' => ['id' => 'foo'], 'second' => ['id' => 'foo'], @@ -427,7 +508,6 @@ public function testAcceptableAccessControlIps($ips) $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -453,7 +533,6 @@ public function testCustomRememberMeHandler() $container->register('custom_remember_me', \stdClass::class); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => [ 'default' => [ 'remember_me' => ['secret' => 'very', 'service' => 'custom_remember_me'], @@ -474,7 +553,6 @@ public function testSecretRememberMeHasher() $container->register('custom_remember_me', \stdClass::class); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => [ 'default' => [ 'remember_me' => ['secret' => 'very'], @@ -494,7 +572,6 @@ public function testSecretRememberMeHandler() $container->register('custom_remember_me', \stdClass::class); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => [ 'default' => [ 'remember_me' => ['secret' => 'very', 'token_provider' => 'token_provider_id'], @@ -529,7 +606,7 @@ public function sessionConfigurationProvider() ]; } - public function acceptableIpsProvider(): iterable + public static function acceptableIpsProvider(): iterable { yield [['127.0.0.1']]; yield ['127.0.0.1']; @@ -543,7 +620,6 @@ public function testSwitchUserWithSeveralDefinedProvidersButNoFirewallRootProvid { $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'first' => ['id' => 'foo'], 'second' => ['id' => 'bar'], @@ -568,7 +644,6 @@ public function testInvalidAccessControlWithEmptyRow() $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -594,7 +669,6 @@ public function testValidAccessControlWithEmptyRow() $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'default' => ['id' => 'foo'], ], @@ -625,7 +699,6 @@ public function testEntryPointRequired(array $firewall, $messageRegex) $container = $this->getRawContainer(); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'first' => ['id' => 'users'], ], @@ -638,7 +711,7 @@ public function testEntryPointRequired(array $firewall, $messageRegex) $container->compile(); } - public function provideEntryPointRequiredData() + public static function provideEntryPointRequiredData() { // more than one entry point available and not explicitly set yield [ @@ -655,7 +728,6 @@ public function testConfigureCustomAuthenticator(array $firewall, array $expecte $container = $this->getRawContainer(); $container->register(TestAuthenticator::class); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'providers' => [ 'first' => ['id' => 'users'], ], @@ -670,7 +742,7 @@ public function testConfigureCustomAuthenticator(array $firewall, array $expecte $this->assertEquals($expectedAuthenticators, array_map('strval', $container->getDefinition('security.authenticator.manager.main')->getArgument(0))); } - public function provideConfigureCustomAuthenticatorData() + public static function provideConfigureCustomAuthenticatorData() { yield [ ['custom_authenticator' => TestAuthenticator::class], @@ -689,7 +761,6 @@ public function testCompilesWithoutSessionListenerWithStatelessFirewallWithAuthe $firewallId = 'stateless_firewall'; $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => [ $firewallId => [ 'pattern' => '/.*', @@ -710,7 +781,6 @@ public function testCompilesWithSessionListenerWithStatefulllFirewallWithAuthent $firewallId = 'statefull_firewall'; $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => [ $firewallId => [ 'pattern' => '/.*', @@ -734,7 +804,6 @@ public function testUserCheckerWithAuthenticatorManager(array $config, string $e $container->register(TestUserChecker::class); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => [ 'main' => array_merge([ 'pattern' => '/.*', @@ -750,7 +819,7 @@ public function testUserCheckerWithAuthenticatorManager(array $config, string $e $this->assertEquals($expectedUserCheckerClass, $container->findDefinition($userCheckerId)->getClass()); } - public function provideUserCheckerConfig() + public static function provideUserCheckerConfig() { yield [[], InMemoryUserChecker::class]; yield [['user_checker' => TestUserChecker::class], TestUserChecker::class]; @@ -764,7 +833,6 @@ public function testConfigureCustomFirewallListener() $extension->addAuthenticatorFactory(new TestFirewallListenerFactory()); $container->loadFromExtension('security', [ - 'enable_authenticator_manager' => true, 'firewalls' => [ 'main' => [ 'custom_listener' => true, diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php index 00006a756a2ec..eccfabef77950 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/XmlCompleteConfigurationTest.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -class XmlCompleteConfigurationTest extends CompleteConfigurationTest +class XmlCompleteConfigurationTest extends CompleteConfigurationTestCase { protected function getLoader(ContainerBuilder $container) { diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php index 8e3954a057daf..4fd2d44e117c1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/DependencyInjection/YamlCompleteConfigurationTest.php @@ -15,7 +15,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -class YamlCompleteConfigurationTest extends CompleteConfigurationTest +class YamlCompleteConfigurationTest extends CompleteConfigurationTestCase { protected function getLoader(ContainerBuilder $container) { diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/EventListener/VoteListenerTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/EventListener/VoteListenerTest.php index 3406e16503f43..d262effae29ac 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/EventListener/VoteListenerTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/EventListener/VoteListenerTest.php @@ -26,7 +26,7 @@ public function testOnVoterVote() $traceableAccessDecisionManager = $this ->getMockBuilder(TraceableAccessDecisionManager::class) ->disableOriginalConstructor() - ->setMethods(['addVoterVote']) + ->onlyMethods(['addVoterVote']) ->getMock(); $traceableAccessDecisionManager diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AccessTokenTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AccessTokenTest.php index 01b205737dd59..91afad756e630 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AccessTokenTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AccessTokenTest.php @@ -113,12 +113,12 @@ public function testCustomMissingFormEncodedBodyShouldFail() $this->assertSame(401, $response->getStatusCode()); } - public function defaultFormEncodedBodyFailureData(): iterable + public static function defaultFormEncodedBodyFailureData(): iterable { yield [['access_token' => 'INVALID_ACCESS_TOKEN'], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']]; } - public function customFormEncodedBodyFailure(): iterable + public static function customFormEncodedBodyFailure(): iterable { yield [['secured_token' => 'INVALID_ACCESS_TOKEN'], ['CONTENT_TYPE' => 'application/x-www-form-urlencoded']]; } @@ -211,24 +211,24 @@ public function testCustomMissingHeaderAccessTokenShouldFail(array $headers) $this->assertSame(401, $response->getStatusCode()); } - public function defaultHeaderAccessTokenFailureData(): iterable + public static function defaultHeaderAccessTokenFailureData(): iterable { yield [['HTTP_AUTHORIZATION' => 'Bearer INVALID_ACCESS_TOKEN']]; } - public function defaultMissingHeaderAccessTokenFailData(): iterable + public static function defaultMissingHeaderAccessTokenFailData(): iterable { yield [['HTTP_AUTHORIZATION' => 'JWT INVALID_TOKEN_TYPE']]; yield [['HTTP_X_FOO' => 'Missing-Header']]; yield [['HTTP_X_AUTH_TOKEN' => 'this is not a token']]; } - public function customHeaderAccessTokenFailure(): iterable + public static function customHeaderAccessTokenFailure(): iterable { yield [['HTTP_X_AUTH_TOKEN' => 'INVALID_ACCESS_TOKEN'], 500]; } - public function customMissingHeaderAccessTokenShouldFail(): iterable + public static function customMissingHeaderAccessTokenShouldFail(): iterable { yield [[]]; yield [['HTTP_AUTHORIZATION' => 'Bearer this is not a token']]; @@ -306,12 +306,12 @@ public function testCustomMissingQueryAccessTokenShouldFail() $this->assertSame(401, $response->getStatusCode()); } - public function defaultQueryAccessTokenFailureData(): iterable + public static function defaultQueryAccessTokenFailureData(): iterable { yield ['/foo?access_token=INVALID_ACCESS_TOKEN']; } - public function customQueryAccessTokenFailure(): iterable + public static function customQueryAccessTokenFailure(): iterable { yield ['/foo?protection_token=INVALID_ACCESS_TOKEN']; } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticatorTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticatorTest.php index 10eeb39ca8c5e..ca99dbf3eadab 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticatorTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/AuthenticatorTest.php @@ -60,7 +60,7 @@ public function testWithoutUserProvider($email) $this->assertJsonStringEqualsJsonString('{"email":"'.$email.'"}', $client->getResponse()->getContent()); } - public function provideEmails() + public static function provideEmails() { yield ['jane@example.org', true]; yield ['john@example.org', false]; @@ -84,7 +84,7 @@ public function testLoginUsersWithMultipleFirewalls(string $username, string $fi $this->assertEquals('Welcome '.$username.'!', $client->getResponse()->getContent()); } - public function provideEmailsWithFirewalls() + public static function provideEmailsWithFirewalls() { yield ['jane@example.org', 'main']; yield ['john@example.org', 'custom']; @@ -102,4 +102,38 @@ public function testMultipleFirewalls() $client->request('GET', '/firewall2/profile'); $this->assertResponseRedirects('http://localhost/login'); } + + public function testCustomSuccessHandler() + { + $client = $this->createClient(['test_case' => 'Authenticator', 'root_config' => 'custom_handlers.yml']); + + $client->request('POST', '/firewall1/login', [ + '_username' => 'jane@example.org', + '_password' => 'test', + ]); + $this->assertResponseRedirects('http://localhost/firewall1/test'); + + $client->request('POST', '/firewall1/dummy_login', [ + '_username' => 'jane@example.org', + '_password' => 'test', + ]); + $this->assertResponseRedirects('http://localhost/firewall1/dummy'); + } + + public function testCustomFailureHandler() + { + $client = $this->createClient(['test_case' => 'Authenticator', 'root_config' => 'custom_handlers.yml']); + + $client->request('POST', '/firewall1/login', [ + '_username' => 'jane@example.org', + '_password' => '', + ]); + $this->assertResponseRedirects('http://localhost/firewall1/login'); + + $client->request('POST', '/firewall1/dummy_login', [ + '_username' => 'jane@example.org', + '_password' => '', + ]); + $this->assertResponseRedirects('http://localhost/firewall1/dummy_login'); + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Handler/AccessTokenHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Handler/AccessTokenHandler.php index 38bd28b4d3a96..4f94cc6936a05 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Handler/AccessTokenHandler.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Handler/AccessTokenHandler.php @@ -13,6 +13,7 @@ use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; class AccessTokenHandler implements AccessTokenHandlerInterface { @@ -20,13 +21,11 @@ public function __construct() { } - public function getUserIdentifierFrom(string $accessToken): string + public function getUserBadgeFrom(string $accessToken): UserBadge { - switch ($accessToken) { - case 'VALID_ACCESS_TOKEN': - return 'dunglas'; - default: - throw new BadCredentialsException('Invalid credentials.'); - } + return match ($accessToken) { + 'VALID_ACCESS_TOKEN' => new UserBadge('dunglas'), + default => throw new BadCredentialsException('Invalid credentials.'), + }; } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Http/JsonAuthenticationSuccessHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Http/JsonAuthenticationSuccessHandler.php index fd08a4adc96ad..d614815837439 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Http/JsonAuthenticationSuccessHandler.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AccessTokenBundle/Security/Http/JsonAuthenticationSuccessHandler.php @@ -19,7 +19,7 @@ class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface { - public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response + public function onAuthenticationSuccess(Request $request, TokenInterface $token): ?Response { return new JsonResponse(['message' => sprintf('Good game @%s!', $token->getUserIdentifier())]); } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/AuthenticatorBundle.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/AuthenticatorBundle.php new file mode 100644 index 0000000000000..730974f17f169 --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/AuthenticatorBundle.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\SecurityExtension; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class AuthenticatorBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + $this->configureSecurityExtension($container); + } + + private function configureSecurityExtension(ContainerBuilder $container): void + { + /** @var SecurityExtension $extension */ + $extension = $container->getExtension('security'); + + $extension->addAuthenticatorFactory(new DummyFormLoginFactory()); + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/DummyFormLoginFactory.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/DummyFormLoginFactory.php new file mode 100644 index 0000000000000..fc728fe5c6f4e --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/AuthenticatorBundle/DummyFormLoginFactory.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\FormLoginFactory; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class DummyFormLoginFactory extends FormLoginFactory +{ + public function getKey(): string + { + return 'dummy_form_login'; + } + + public function createAuthenticator(ContainerBuilder $container, string $firewallName, array $config, string $userProviderId): string + { + $authenticatorId = 'security.authenticator.dummy_form_login.'.$firewallName; + $options = array_intersect_key($config, $this->options); + $authenticator = $container + ->setDefinition($authenticatorId, new ChildDefinition('security.authenticator.form_login')) + ->replaceArgument(1, new Reference($userProviderId)) + ->replaceArgument(2, new Reference($this->createAuthenticationSuccessHandler($container, $firewallName, $config))) + ->replaceArgument(3, new Reference($this->createAuthenticationFailureHandler($container, $firewallName, $config))) + ->replaceArgument(4, $options); + + if ($options['use_forward'] ?? false) { + $authenticator->addMethodCall('setHttpKernel', [new Reference('http_kernel')]); + } + + return $authenticatorId; + } +} diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php index c77b1e204e0db..22f7de69b208c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Controller/LoginController.php @@ -51,9 +51,6 @@ public function secureAction() throw new \Exception('Wrapper', 0, new \Exception('Another Wrapper', 0, new AccessDeniedException())); } - /** - * {@inheritdoc} - */ public static function getSubscribedServices(): array { return [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php index 8ccd51e72d0f0..0f89b3c1b4fc8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/CsrfFormLoginBundle/Form/UserLoginType.php @@ -36,9 +36,6 @@ public function __construct(RequestStack $requestStack) $this->requestStack = $requestStack; } - /** - * {@inheritdoc} - */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder @@ -71,9 +68,6 @@ public function buildForm(FormBuilderInterface $builder, array $options) }); } - /** - * {@inheritdoc} - */ public function configureOptions(OptionsResolver $resolver) { /* Note: the form's csrf_token_id must correspond to that for the form login diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php index 9733b0788aac6..a09ca835897fd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LocalizedController.php @@ -68,9 +68,6 @@ public function homepageAction() return (new Response('Homepage'))->setPublic(); } - /** - * {@inheritdoc} - */ public static function getSubscribedServices(): array { return [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php index 7d90b2a671e86..09b0ff09ba8d1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Controller/LoginController.php @@ -60,9 +60,6 @@ public function secureAction() throw new \Exception('Wrapper', 0, new \Exception('Another Wrapper', 0, new AccessDeniedException())); } - /** - * {@inheritdoc} - */ public static function getSubscribedServices(): array { return [ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/FormLoginBundle.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/FormLoginBundle.php index c330723adff15..62490a739bdcc 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/FormLoginBundle.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/FormLoginBundle.php @@ -18,9 +18,6 @@ class FormLoginBundle extends Bundle { - /** - * {@inheritdoc} - */ public function build(ContainerBuilder $container) { parent::build($container); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php index 4aabaacd4889c..b7dd3fd361198 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/JsonLoginBundle/Security/Http/JsonAuthenticationSuccessHandler.php @@ -19,7 +19,7 @@ class JsonAuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface { - public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response + public function onAuthenticationSuccess(Request $request, TokenInterface $token): ?Response { return new JsonResponse(['message' => sprintf('Good game @%s!', $token->getUserIdentifier())]); } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php index 0cbc159ed4272..06997641c28a4 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/LoginLink/TestCustomLoginLinkSuccessHandler.php @@ -19,7 +19,7 @@ class TestCustomLoginLinkSuccessHandler implements AuthenticationSuccessHandlerInterface { - public function onAuthenticationSuccess(Request $request, TokenInterface $token): Response + public function onAuthenticationSuccess(Request $request, TokenInterface $token): ?Response { return new JsonResponse(['message' => sprintf('Welcome %s!', $token->getUserIdentifier())]); } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/RememberMeBundle/Security/StaticTokenProvider.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/RememberMeBundle/Security/StaticTokenProvider.php index 24ff10a4d0199..8a669e99bc48d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/RememberMeBundle/Security/StaticTokenProvider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/RememberMeBundle/Security/StaticTokenProvider.php @@ -23,8 +23,8 @@ class StaticTokenProvider implements TokenProviderInterface public function __construct($kernel) { // only reset the "internal db" for new tests - if (self::$kernelClass !== \get_class($kernel)) { - self::$kernelClass = \get_class($kernel); + if (self::$kernelClass !== $kernel::class) { + self::$kernelClass = $kernel::class; self::$db = []; } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/SecuredPageBundle/Security/Core/User/ArrayUserProvider.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/SecuredPageBundle/Security/Core/User/ArrayUserProvider.php index 68f896bcc68b1..42f2df9e10043 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/SecuredPageBundle/Security/Core/User/ArrayUserProvider.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/SecuredPageBundle/Security/Core/User/ArrayUserProvider.php @@ -64,7 +64,7 @@ public function refreshUser(UserInterface $user): UserInterface } $storedUser = $this->getUser($user->getUserIdentifier()); - $class = \get_class($storedUser); + $class = $storedUser::class; return new $class($storedUser->getUserIdentifier(), $storedUser->getPassword(), $storedUser->getRoles(), $storedUser->isEnabled()); } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php index ad2fc0c63d1e0..25aa013131648 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php @@ -11,6 +11,12 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional; +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\RequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; + class CsrfFormLoginTest extends AbstractWebTestCase { /** @@ -20,6 +26,10 @@ public function testFormLoginAndLogoutWithCsrfTokens($options) { $client = $this->createClient($options); + $this->callInRequestContext($client, function () { + static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); + }); + $form = $client->request('GET', '/login')->selectButton('login')->form(); $form['user_login[username]'] = 'johannes'; $form['user_login[password]'] = 'test'; @@ -40,6 +50,10 @@ public function testFormLoginAndLogoutWithCsrfTokens($options) $client->click($logoutLinks[0]); $this->assertRedirect($client->getResponse(), '/'); + + $this->callInRequestContext($client, function () { + $this->assertFalse(static::getContainer()->get('security.csrf.token_storage')->hasToken('foo')); + }); } /** @@ -49,6 +63,10 @@ public function testFormLoginWithInvalidCsrfToken($options) { $client = $this->createClient($options); + $this->callInRequestContext($client, function () { + static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); + }); + $form = $client->request('GET', '/login')->selectButton('login')->form(); $form['user_login[_token]'] = ''; $client->submit($form); @@ -57,6 +75,10 @@ public function testFormLoginWithInvalidCsrfToken($options) $text = $client->followRedirect()->text(null, true); $this->assertStringContainsString('Invalid CSRF token.', $text); + + $this->callInRequestContext($client, function () { + $this->assertTrue(static::getContainer()->get('security.csrf.token_storage')->hasToken('foo')); + }); } /** @@ -100,9 +122,27 @@ public function testFormLoginRedirectsToProtectedResourceAfterLogin($options) $this->assertStringContainsString('You\'re browsing to path "/protected-resource".', $text); } - public function provideClientOptions() + public static function provideClientOptions() { yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'config.yml']]; yield [['test_case' => 'CsrfFormLogin', 'root_config' => 'routes_as_path.yml']]; } + + private function callInRequestContext(KernelBrowser $client, callable $callable): void + { + /** @var EventDispatcherInterface $eventDispatcher */ + $eventDispatcher = static::getContainer()->get(EventDispatcherInterface::class); + $wrappedCallable = function (RequestEvent $event) use (&$callable) { + $callable(); + $event->setResponse(new Response('')); + $event->stopPropagation(); + }; + + $eventDispatcher->addListener(KernelEvents::REQUEST, $wrappedCallable); + try { + $client->request('GET', '/'.uniqid('', true)); + } finally { + $eventDispatcher->removeListener(KernelEvents::REQUEST, $wrappedCallable); + } + } } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php index 78b7f6140542a..583c0dd2336b0 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/FormLoginTest.php @@ -147,7 +147,7 @@ public function testLoginThrottling() } } - public function provideClientOptions() + public static function provideClientOptions() { yield [['test_case' => 'StandardFormLogin', 'root_config' => 'base_config.yml']]; yield [['test_case' => 'StandardFormLogin', 'root_config' => 'routes_as_path.yml']]; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php index 0c356662c39a7..99ba311a26eb8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LocalizedRoutesAsPathTest.php @@ -36,6 +36,7 @@ public function testLoginLogoutProcedure(string $locale) /** * @group issue-32995 + * * @dataProvider getLocales */ public function testLoginFailureWithLocalizedFailurePath(string $locale) @@ -73,7 +74,7 @@ public function testAccessRestrictedResourceWithForward(string $locale) $this->assertCount(1, $crawler->selectButton('login'), (string) $client->getResponse()); } - public function getLocales() + public static function getLocales() { yield ['en']; yield ['de']; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php index 5da52d9602a49..50473ed84e912 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php @@ -24,9 +24,6 @@ public function testCsrfTokensAreClearedOnLogout() { $client = $this->createClient(['test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml']); $client->disableReboot(); - $this->callInRequestContext($client, function () { - static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); - }); $client->request('POST', '/login', [ '_username' => 'johannes', @@ -34,8 +31,7 @@ public function testCsrfTokensAreClearedOnLogout() ]); $this->callInRequestContext($client, function () { - $this->assertTrue(static::getContainer()->get('security.csrf.token_storage')->hasToken('foo')); - $this->assertSame('bar', static::getContainer()->get('security.csrf.token_storage')->getToken('foo')); + static::getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar'); }); $client->request('GET', '/logout'); @@ -69,6 +65,19 @@ public function testCookieClearingOnLogout() $this->assertNull($cookieJar->get('flavor')); } + public function testEnabledCsrf() + { + $client = $this->createClient(['test_case' => 'Logout', 'root_config' => 'config_csrf_enabled.yml']); + + $cookieJar = $client->getCookieJar(); + $cookieJar->set(new Cookie('flavor', 'chocolate', strtotime('+1 day'), null, 'somedomain')); + + $client->request('POST', '/login', ['_username' => 'johannes', '_password' => 'test']); + $client->request('GET', '/logout'); + + $this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN); + } + private function callInRequestContext(KernelBrowser $client, callable $callable): void { /** @var EventDispatcherInterface $eventDispatcher */ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeCookieTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeCookieTest.php index 0bd2711f25ca0..d91b321bbc3aa 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeCookieTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeCookieTest.php @@ -31,7 +31,7 @@ public function testSessionRememberMeSecureCookieFlagAuto($https, $expectedSecur $this->assertSame($expectedSecureFlag, $cookies['']['/']['REMEMBERME']->isSecure()); } - public function getSessionRememberMeSecureCookieFlagAutoHttpsMap() + public static function getSessionRememberMeSecureCookieFlagAutoHttpsMap() { return [ [true, true], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeTest.php index 0ccc17d9c989d..2fff3a9eddc7a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeTest.php @@ -93,7 +93,7 @@ public function testSessionLessRememberMeLogout() $this->assertNull($cookieJar->get('REMEMBERME')); } - public function provideConfigs() + public static function provideConfigs() { yield [['root_config' => 'config_session.yml']]; yield [['root_config' => 'config_persistent.yml']]; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php index 7c1f3dc0679a4..362801253f305 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php @@ -151,7 +151,7 @@ private function assertRestricted($client, $path) $this->assertEquals(302, $client->getResponse()->getStatusCode()); } - public function provideConfigs() + public static function provideConfigs() { yield [['test_case' => 'StandardFormLogin', 'root_config' => 'base_config.yml']]; yield [['test_case' => 'StandardFormLogin', 'root_config' => 'routes_as_path.yml']]; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php index 95161c26ec6db..f2d6733fcf350 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityTest.php @@ -11,8 +11,8 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security\FirewallConfig; -use Symfony\Bundle\SecurityBundle\Security\Security; use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\SecuredPageBundle\Security\Core\User\ArrayUserProvider; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\JsonResponse; @@ -76,7 +76,7 @@ public function testUserWillBeMarkedAsChangedIfRolesHasChanged(UserInterface $us $this->assertEquals(302, $client->getResponse()->getStatusCode()); } - public function userWillBeMarkedAsChangedIfRolesHasChangedProvider() + public static function userWillBeMarkedAsChangedIfRolesHasChangedProvider() { return [ [ @@ -192,33 +192,21 @@ public function __toString(): string return $this->getUserIdentifier(); } - /** - * {@inheritdoc} - */ public function getRoles(): array { return $this->roles; } - /** - * {@inheritdoc} - */ public function getPassword(): ?string { return $this->password; } - /** - * {@inheritdoc} - */ public function getSalt(): string { return ''; } - /** - * {@inheritdoc} - */ public function getUsername(): string { return $this->username; @@ -229,41 +217,26 @@ public function getUserIdentifier(): string return $this->username; } - /** - * {@inheritdoc} - */ public function isAccountNonExpired(): bool { return $this->accountNonExpired; } - /** - * {@inheritdoc} - */ public function isAccountNonLocked(): bool { return $this->accountNonLocked; } - /** - * {@inheritdoc} - */ public function isCredentialsNonExpired(): bool { return $this->credentialsNonExpired; } - /** - * {@inheritdoc} - */ public function isEnabled(): bool { return $this->enabled; } - /** - * {@inheritdoc} - */ public function eraseCredentials(): void { } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php index 8b8848014d57a..f376847214913 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/SwitchUserTest.php @@ -63,7 +63,7 @@ public function testSwitchUserStateless() $this->assertSame('dunglas', $client->getProfile()->getCollector('security')->getUser()); } - public function getTestParameters() + public static function getTestParameters() { return [ 'unauthorized_user_cannot_switch' => ['user_cannot_switch_1', 'user_cannot_switch_1', 'user_cannot_switch_1', 403], diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AbstractTokenCompareRoles/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AbstractTokenCompareRoles/config.yml index 1cc13de77736f..54bfaf89cb6c7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AbstractTokenCompareRoles/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AbstractTokenCompareRoles/config.yml @@ -8,8 +8,6 @@ services: class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\SecuredPageBundle\Security\Core\User\ArrayUserProvider security: - enable_authenticator_manager: true - password_hashers: \Symfony\Component\Security\Core\User\UserInterface: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_anonymous.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_anonymous.yml index ccf7c5d052bbc..ae177caf8b3bf 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_anonymous.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_anonymous.yml @@ -6,7 +6,6 @@ framework: serializer: ~ security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_body_custom.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_body_custom.yml index 88ab6295c22b8..9c6a203331611 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_body_custom.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_body_custom.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_body_default.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_body_default.yml index da42a05d33939..c1a7a8e7b6ba8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_body_default.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_body_default.yml @@ -6,7 +6,6 @@ framework: serializer: ~ security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_header_custom.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_header_custom.yml index 2062596b1c156..befc881f3fb94 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_header_custom.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_header_custom.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_header_default.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_header_default.yml index eec1658c43867..8dea7ec2ab8da 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_header_default.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_header_default.yml @@ -6,7 +6,6 @@ framework: serializer: ~ security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_multiple_extractors.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_multiple_extractors.yml index c4bc2977b0aec..a90114f80ac72 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_multiple_extractors.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_multiple_extractors.yml @@ -6,7 +6,6 @@ framework: serializer: ~ security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_no_extractors.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_no_extractors.yml index d02b379e55676..41f1d96e5108b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_no_extractors.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_no_extractors.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_no_handler.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_no_handler.yml index a1c26742ca673..f3895738680af 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_no_handler.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_no_handler.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_query_custom.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_query_custom.yml index 9f9bca322e5be..b41be357482ae 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_query_custom.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_query_custom.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_query_default.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_query_default.yml index a4a95fdba4185..16256e32c9e69 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_query_default.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AccessToken/config_query_default.yml @@ -6,7 +6,6 @@ framework: serializer: ~ security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php index 2839d5dfaff60..d10326e676a38 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AppKernel.php @@ -46,9 +46,6 @@ public function __construct($varDir, $testCase, $rootConfig, $environment, $debu parent::__construct($environment, $debug); } - /** - * {@inheritdoc} - */ public function getContainerClass(): string { return parent::getContainerClass().substr(md5(implode('', $this->rootConfig)), -16); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/bundles.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/bundles.php index d1e9eb7e0d36a..372700495208f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/bundles.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/bundles.php @@ -12,4 +12,5 @@ return [ new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new Symfony\Bundle\SecurityBundle\SecurityBundle(), + new Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\AuthenticatorBundle(), ]; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/config.yml index 4c3c8da831336..a6adcfd0c775b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/config.yml @@ -7,7 +7,6 @@ framework: profiler: false session: storage_factory_id: session.storage.factory.mock_file - catch_all_throwables: true services: logger: { class: Psr\Log\NullLogger } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/custom_handlers.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/custom_handlers.yml new file mode 100644 index 0000000000000..df25ce3fa602d --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/custom_handlers.yml @@ -0,0 +1,33 @@ +imports: + - { resource: ./config.yml } + - { resource: ./security.yml } + +security: + firewalls: + firewall1: + pattern: /firewall1 + provider: in_memory + entry_point: form_login + form_login: + check_path: /firewall1/login + success_handler: success_handler + failure_handler: failure_handler + default_target_path: /firewall1/test + login_path: /firewall1/login + dummy_form_login: + check_path: /firewall1/dummy_login + success_handler: success_handler + failure_handler: failure_handler + default_target_path: /firewall1/dummy + login_path: /firewall1/dummy_login + +services: + success_handler: + class: Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler + arguments: + - '@security.http_utils' + failure_handler: + class: Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler + arguments: + - '@http_kernel' + - '@security.http_utils' diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/firewall_user_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/firewall_user_provider.yml index 7822396eae16a..4fb5ce880aacd 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/firewall_user_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/firewall_user_provider.yml @@ -3,7 +3,6 @@ imports: - { resource: ./security.yml } security: - enable_authenticator_manager: true firewalls: api: pattern: / diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/implicit_user_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/implicit_user_provider.yml index b2433ecd35a1c..1cb8b0c6786e7 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/implicit_user_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/implicit_user_provider.yml @@ -3,7 +3,6 @@ imports: - { resource: ./security.yml } security: - enable_authenticator_manager: true firewalls: api: pattern: / diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/multiple_firewalls.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/multiple_firewalls.yml index 655a8d83d4d5f..b424be8438419 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/multiple_firewalls.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/multiple_firewalls.yml @@ -3,7 +3,6 @@ imports: - { resource: ./security.yml } security: - enable_authenticator_manager: true firewalls: firewall1: pattern: /firewall1 diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/no_user_provider.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/no_user_provider.yml index 3983d567c5572..6be03adad7283 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/no_user_provider.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/no_user_provider.yml @@ -6,8 +6,6 @@ services: - true security: - enable_authenticator_manager: true - firewalls: api: pattern: / diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/routing.yml index 4796a3f6bc00c..76d894d9de904 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/routing.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/routing.yml @@ -22,6 +22,9 @@ security_custom_profile: firewall1_login: path: /firewall1/login +firewall_dummy_login: + path: /firewall1/dummy_login + firewall2_profile: path: /firewall2/profile defaults: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/security.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/security.yml index 2a1d748ec2fb4..484760a536f60 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/security.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Authenticator/security.yml @@ -1,6 +1,4 @@ security: - enable_authenticator_manager: true - password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/config.yml index 8be3ebc6436af..2045118e1b9f1 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/AutowiringTypes/config.yml @@ -7,7 +7,6 @@ services: class: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AutowiringBundle\AutowiredServices autowire: true security: - enable_authenticator_manager: true providers: dummy: memory: ~ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/base_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/base_config.yml index 945fd0fce3366..069fece61756f 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/base_config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/base_config.yml @@ -15,7 +15,6 @@ services: - { name: container.service_subscriber } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml index ff265cac9e27d..98ba0eb5326ad 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml @@ -2,7 +2,6 @@ imports: - { resource: ./base_config.yml } security: - enable_authenticator_manager: true firewalls: default: form_login: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routes_as_path.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routes_as_path.yml index 57abb3f2f6771..d481e6d2b7150 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routes_as_path.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/routes_as_path.yml @@ -2,7 +2,6 @@ imports: - { resource: ./config.yml } security: - enable_authenticator_manager: true firewalls: default: form_login: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml index e09a5a6c7ba3e..00939adc182b3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/FirewallEntryPoint/config.yml @@ -11,13 +11,11 @@ framework: session: storage_factory_id: session.storage.factory.mock_file profiler: { only_exceptions: false } - catch_all_throwables: true services: logger: { class: Psr\Log\NullLogger } security: - enable_authenticator_manager: true firewalls: secure: pattern: ^/secure/ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml index 2c80b9383bb28..022263a978e6d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/config.yml @@ -6,7 +6,6 @@ framework: serializer: ~ security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml index b8986de18f499..f1f1a93ab0c0b 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/custom_handlers.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/switchuser_stateless.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/switchuser_stateless.yml index e2bebd525f488..84a0493e050b2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/switchuser_stateless.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLogin/switchuser_stateless.yml @@ -2,7 +2,6 @@ imports: - { resource: ./config.yml } security: - enable_authenticator_manager: true providers: in_memory: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/config.yml index 67234430451db..5d4bc1bffcf7e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/JsonLoginLdap/config.yml @@ -12,7 +12,6 @@ services: protocol_version: 3 referrals: false security: - enable_authenticator_manager: true providers: ldap: ldap: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LoginLink/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LoginLink/config.yml index 969c669eabcfc..7feaca0cb88ce 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LoginLink/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LoginLink/config.yml @@ -2,8 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true - providers: in_memory: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_access.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_access.yml index fbcb7e6defc79..31ecfb6897c42 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_access.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_access.yml @@ -2,8 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true - password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_cookie_clearing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_cookie_clearing.yml index 974f0ab79df64..2472cec31a437 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_cookie_clearing.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_cookie_clearing.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_csrf_enabled.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_csrf_enabled.yml new file mode 100644 index 0000000000000..9d05c34a5d11c --- /dev/null +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/Logout/config_csrf_enabled.yml @@ -0,0 +1,25 @@ +imports: +- { resource: ./../config/framework.yml } + +security: + password_hashers: + Symfony\Component\Security\Core\User\InMemoryUser: plaintext + + providers: + in_memory: + memory: + users: + johannes: { password: test, roles: [ROLE_USER] } + + firewalls: + default: + form_login: + check_path: login + remember_me: true + require_previous_session: false + logout: + enable_csrf: true + + access_control: + - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: .*, roles: IS_AUTHENTICATED_FULLY } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml index 1dd8b8e507d36..f28924e4518d9 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml @@ -2,8 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true - password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/MissingUserProvider/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/MissingUserProvider/config.yml index ec3839a76adcb..501a673b4fdea 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/MissingUserProvider/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/MissingUserProvider/config.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true firewalls: default: http_basic: ~ diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config.yml index fe52f22500606..696a9041e8035 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config_persistent.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config_persistent.yml index 40ded00c5539c..a529c217f2255 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config_persistent.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config_persistent.yml @@ -4,7 +4,6 @@ services: arguments: ['@kernel'] security: - enable_authenticator_manager: true firewalls: default: remember_me: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config_session.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config_session.yml index a11750e6f60be..411de7211ebce 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config_session.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/config_session.yml @@ -1,5 +1,4 @@ security: - enable_authenticator_manager: true firewalls: default: remember_me: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/stateless_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/stateless_config.yml index 31053f856373d..2a21c54c0b206 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/stateless_config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMe/stateless_config.yml @@ -9,7 +9,6 @@ framework: cookie_samesite: lax security: - enable_authenticator_manager: true firewalls: default: stateless: true diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/config.yml index 4df09bad41582..923e15e8dfd7e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/RememberMeCookie/config.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/framework.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml index 1a93b66275a1c..3837eb5d08190 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config.yml @@ -25,8 +25,6 @@ services: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator: ~ security: - enable_authenticator_manager: true - providers: main: memory: @@ -42,7 +40,16 @@ security: custom_authenticators: - 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator' provider: main + second: + pattern: ^/second + form_login: + check_path: /second/login/check + custom_authenticators: + - 'Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator' + provider: main access_control: - { path: '^/main/login/check$', roles: IS_AUTHENTICATED_FULLY } - { path: '^/main/logged-in$', roles: IS_AUTHENTICATED_FULLY } + - { path: '^/second/login/check$', roles: IS_AUTHENTICATED_FULLY } + - { path: '^/second/logged-in$', roles: IS_AUTHENTICATED_FULLY } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config_logout_csrf.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config_logout_csrf.yml index 86cfe2b1e6d8b..c397abb9b1aa3 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config_logout_csrf.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/config_logout_csrf.yml @@ -25,8 +25,6 @@ services: Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\AuthenticatorBundle\ApiAuthenticator: ~ security: - enable_authenticator_manager: true - providers: main: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/routing.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/routing.yml index 1f74d27501207..8487bbec1a0cc 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/routing.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/SecurityHelper/routing.yml @@ -9,3 +9,11 @@ logged-in: force-logout: path: /main/force-logout controller: Symfony\Bundle\SecurityBundle\Tests\Functional\LogoutController::logout + +second-force-login: + path: /second/force-login + controller: Symfony\Bundle\SecurityBundle\Tests\Functional\ForceLoginController::welcome + +second-logged-in: + path: /second/logged-in + controller: Symfony\Bundle\SecurityBundle\Tests\Functional\LoggedInController diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/base_config.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/base_config.yml index a243ec5f0a448..4260d8aa5545d 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/base_config.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/base_config.yml @@ -6,7 +6,6 @@ parameters: env(APP_IPS): '127.0.0.1, ::1' security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml index 0f190d9b6d1e4..6b57da1eab294 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/invalid_ip_access_control.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/default.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml index 95603e583534d..f1cddb0e7f92a 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_form_failure_handler.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/default.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml index 42f18b392d020..83ceaaac81a7c 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes.yml @@ -2,7 +2,6 @@ imports: - { resource: ./../config/default.yml } security: - enable_authenticator_manager: true password_hashers: Symfony\Component\Security\Core\User\InMemoryUser: plaintext diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes_with_forward.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes_with_forward.yml index 9cbfe5dae7159..12d90d8835858 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes_with_forward.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/localized_routes_with_forward.yml @@ -2,7 +2,6 @@ imports: - { resource: ./localized_routes.yml } security: - enable_authenticator_manager: true firewalls: default: form_login: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/login_throttling.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/login_throttling.yml index 5f22f41780cd0..90e276f02d026 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/login_throttling.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/login_throttling.yml @@ -7,7 +7,6 @@ framework: rate_limiter: ~ security: - enable_authenticator_manager: true firewalls: default: login_throttling: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routes_as_path.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routes_as_path.yml index 435951968d773..fc31d7743a947 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routes_as_path.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/routes_as_path.yml @@ -2,7 +2,6 @@ imports: - { resource: ./base_config.yml } security: - enable_authenticator_manager: true firewalls: default: form_login: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/switchuser.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/switchuser.yml index 4806ed5e0cb5d..bd6f56d2c74da 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/switchuser.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/switchuser.yml @@ -2,7 +2,6 @@ imports: - { resource: ./base_config.yml } security: - enable_authenticator_manager: true providers: in_memory: memory: diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml index ad4bf4adf68d8..a86c944552644 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/config/framework.yml @@ -12,7 +12,6 @@ framework: session: storage_factory_id: session.storage.factory.mock_file profiler: { only_exceptions: false } - catch_all_throwables: true services: logger: { class: Psr\Log\NullLogger } diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Security/SecurityTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php similarity index 90% rename from src/Symfony/Bundle/SecurityBundle/Tests/Security/SecurityTest.php rename to src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php index 3eefbc8bccc68..8e1e6156d92a2 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Security/SecurityTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/SecurityTest.php @@ -9,13 +9,13 @@ * file that was distributed with this source code. */ -namespace Symfony\Bundle\SecurityBundle\Tests\Security; +namespace Symfony\Bundle\SecurityBundle\Tests; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security\FirewallConfig; use Symfony\Bundle\SecurityBundle\Security\FirewallMap; -use Symfony\Bundle\SecurityBundle\Security\Security; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -74,7 +74,7 @@ public function testGetUser($userInToken, $expectedUser) $this->assertSame($expectedUser, $security->getUser()); } - public function getUserTests() + public static function getUserTests() { yield [null, null]; @@ -115,7 +115,7 @@ public function testGetFirewallConfig(Request $request, ?FirewallConfig $expecte $this->assertSame($expectedFirewallConfig, $security->getFirewallConfig($request)); } - public function getFirewallConfigTests() + public static function getFirewallConfigTests() { $request = new Request(); @@ -141,7 +141,7 @@ public function testLogin() ->willReturnMap([ ['request_stack', $requestStack], ['security.firewall.map', $firewallMap], - ['security.user_authenticator', $userAuthenticator], + ['security.authenticator.managers_locator', new ServiceLocator(['main' => fn () => $userAuthenticator])], ['security.user_checker', $userChecker], ]) ; @@ -169,6 +169,38 @@ public function testLogin() $security->login($user); } + public function testLoginWithoutAuthenticatorThrows() + { + $request = new Request(); + $authenticator = $this->createMock(AuthenticatorInterface::class); + $requestStack = $this->createMock(RequestStack::class); + $firewallMap = $this->createMock(FirewallMap::class); + $firewall = new FirewallConfig('main', 'main'); + $user = $this->createMock(UserInterface::class); + $userChecker = $this->createMock(UserCheckerInterface::class); + + $container = $this->createMock(ContainerInterface::class); + $container + ->expects($this->atLeastOnce()) + ->method('get') + ->willReturnMap([ + ['request_stack', $requestStack], + ['security.firewall.map', $firewallMap], + ['security.user_checker', $userChecker], + ]) + ; + + $requestStack->expects($this->once())->method('getCurrentRequest')->willReturn($request); + $firewallMap->expects($this->once())->method('getFirewallConfig')->willReturn($firewall); + + $security = new Security($container, ['main' => null]); + + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('No authenticators found for firewall "main".'); + + $security->login($user); + } + public function testLogout() { $request = new Request(); diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index bd52ad5bee75f..cae9c7635a59e 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -23,14 +23,14 @@ "symfony/dependency-injection": "^6.2", "symfony/event-dispatcher": "^5.4|^6.0", "symfony/http-kernel": "^6.2", - "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-foundation": "^6.2", "symfony/password-hasher": "^5.4|^6.0", "symfony/security-core": "^6.2", "symfony/security-csrf": "^5.4|^6.0", - "symfony/security-http": "^6.2" + "symfony/security-http": "^6.2.10" }, "require-dev": { - "doctrine/annotations": "^1.10.4", + "doctrine/annotations": "^1.10.4|^2", "symfony/asset": "^5.4|^6.0", "symfony/browser-kit": "^5.4|^6.0", "symfony/console": "^5.4|^6.0", @@ -38,7 +38,7 @@ "symfony/dom-crawler": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/form": "^5.4|^6.0", - "symfony/framework-bundle": "^6.2", + "symfony/framework-bundle": "^5.4|^6.0", "symfony/ldap": "^5.4|^6.0", "symfony/process": "^5.4|^6.0", "symfony/rate-limiter": "^5.4|^6.0", @@ -53,7 +53,7 @@ "conflict": { "symfony/browser-kit": "<5.4", "symfony/console": "<5.4", - "symfony/framework-bundle": "<6.2", + "symfony/framework-bundle": "<5.4", "symfony/ldap": "<5.4", "symfony/twig-bundle": "<5.4" }, diff --git a/src/Symfony/Bundle/TwigBundle/CHANGELOG.md b/src/Symfony/Bundle/TwigBundle/CHANGELOG.md index aaa0ae400f7a1..5502812c27ec1 100644 --- a/src/Symfony/Bundle/TwigBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/TwigBundle/CHANGELOG.md @@ -1,6 +1,12 @@ CHANGELOG ========= +6.2 +--- + + * Add the `twig.mailer.html_to_text_converter` option to allow configuring custom `HtmlToTextConverterInterface` + implementations to be used by the `twig.mime_body_renderer` service + 6.1 --- diff --git a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php index 63d0227513845..d26ddf358aaba 100644 --- a/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php +++ b/src/Symfony/Bundle/TwigBundle/CacheWarmer/TemplateCacheWarmer.php @@ -36,8 +36,6 @@ public function __construct(ContainerInterface $container, iterable $iterator) } /** - * {@inheritdoc} - * * @return string[] A list of template files to preload on PHP 7.4+ */ public function warmUp(string $cacheDir): array @@ -68,17 +66,11 @@ public function warmUp(string $cacheDir): array return $files; } - /** - * {@inheritdoc} - */ public function isOptional(): bool { return true; } - /** - * {@inheritdoc} - */ public static function getSubscribedServices(): array { return [ diff --git a/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php b/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php index 5de2b2aad1358..f85dd13aa2f3a 100644 --- a/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php +++ b/src/Symfony/Bundle/TwigBundle/Command/LintCommand.php @@ -23,9 +23,6 @@ #[AsCommand(name: 'lint:twig', description: 'Lint a Twig template and outputs encountered errors')] final class LintCommand extends BaseLintCommand { - /** - * {@inheritdoc} - */ protected function configure() { parent::configure(); diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php index 45413dc93253d..3a90bce15ffaa 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/TwigEnvironmentPass.php @@ -50,7 +50,7 @@ public function process(ContainerBuilder $container) } } - if (!empty($twigBridgeExtensionsMethodCalls) || !empty($othersExtensionsMethodCalls)) { + if ($twigBridgeExtensionsMethodCalls || $othersExtensionsMethodCalls) { $definition->setMethodCalls(array_merge($twigBridgeExtensionsMethodCalls, $othersExtensionsMethodCalls, $currentMethodCalls)); } } diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php index b85a3bf4b1958..0655a51e7c159 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Configuration.php @@ -15,6 +15,7 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Mime\HtmlToTextConverter\HtmlToTextConverterInterface; /** * TwigExtension configuration structure. @@ -48,6 +49,7 @@ public function getConfigTreeBuilder(): TreeBuilder $this->addGlobalsSection($rootNode); $this->addTwigOptions($rootNode); $this->addTwigFormatOptions($rootNode); + $this->addMailerSection($rootNode); return $treeBuilder; } @@ -213,4 +215,20 @@ private function addTwigFormatOptions(ArrayNodeDefinition $rootNode) ->end() ; } + + private function addMailerSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('mailer') + ->children() + ->scalarNode('html_to_text_converter') + ->info(sprintf('A service implementing the "%s"', HtmlToTextConverterInterface::class)) + ->defaultNull() + ->end() + ->end() + ->end() + ->end() + ; + } } diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index f598524da4d64..b323bf808946e 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -54,10 +54,6 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('console.php'); } - if ($container::willBeAvailable('symfony/mailer', Mailer::class, ['symfony/twig-bundle'])) { - $loader->load('mailer.php'); - } - if (!$container::willBeAvailable('symfony/translation', Translator::class, ['symfony/twig-bundle'])) { $container->removeDefinition('twig.translation.extractor'); } @@ -79,6 +75,14 @@ public function load(array $configs, ContainerBuilder $container) $config = $this->processConfiguration($configuration, $configs); + if ($container::willBeAvailable('symfony/mailer', Mailer::class, ['symfony/twig-bundle'])) { + $loader->load('mailer.php'); + + if ($htmlToTextConverter = $config['mailer']['html_to_text_converter'] ?? null) { + $container->getDefinition('twig.mime_body_renderer')->setArgument('$converter', new Reference($htmlToTextConverter)); + } + } + $container->setParameter('twig.form.resources', $config['form_themes']); $container->setParameter('twig.default_path', $config['default_path']); $defaultTwigPath = $container->getParameterBag()->resolveValue($config['default_path']); @@ -194,9 +198,6 @@ private function normalizeBundleName(string $name): string return $name; } - /** - * {@inheritdoc} - */ public function getXsdValidationBasePath(): string|false { return __DIR__.'/../Resources/config/schema'; diff --git a/src/Symfony/Bundle/TwigBundle/LICENSE b/src/Symfony/Bundle/TwigBundle/LICENSE index 88bf75bb4d6a2..0138f8f071351 100644 --- a/src/Symfony/Bundle/TwigBundle/LICENSE +++ b/src/Symfony/Bundle/TwigBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php b/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php index 1444481f2c0ba..e43658a7da02c 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/mailer.php @@ -13,6 +13,7 @@ use Symfony\Bridge\Twig\Mime\BodyRenderer; use Symfony\Component\Mailer\EventListener\MessageListener; +use Symfony\Component\Mime\BodyRendererInterface; return static function (ContainerConfigurator $container) { $container->services() @@ -22,5 +23,6 @@ ->set('twig.mime_body_renderer', BodyRenderer::class) ->args([service('twig')]) + ->alias(BodyRendererInterface::class, 'twig.mime_body_renderer') ; }; diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd b/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd index 429c91db67b74..50eff2bc29923 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd @@ -14,6 +14,7 @@ + @@ -55,4 +56,8 @@ + + + + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/mailer.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/mailer.php new file mode 100644 index 0000000000000..2404f221ad680 --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/mailer.php @@ -0,0 +1,7 @@ +loadFromExtension('twig', [ + 'mailer' => [ + 'html_to_text_converter' => 'my_converter', + ], +]); diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/mailer.xml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/mailer.xml new file mode 100644 index 0000000000000..25c2a9453663e --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/mailer.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/mailer.yml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/mailer.yml new file mode 100644 index 0000000000000..139e2077280ba --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/mailer.yml @@ -0,0 +1,3 @@ +twig: + mailer: + html_to_text_converter: 'my_converter' diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php index cd60cbb7fd9e3..70693c0784bb8 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -24,6 +24,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer; +use Symfony\Component\Mailer\Mailer; class TwigExtensionTest extends TestCase { @@ -43,6 +44,10 @@ public function testLoadEmptyConfiguration() $this->assertEquals('%kernel.cache_dir%/twig', $options['cache'], '->load() sets default value for cache option'); $this->assertEquals('%kernel.charset%', $options['charset'], '->load() sets default value for charset option'); $this->assertEquals('%kernel.debug%', $options['debug'], '->load() sets default value for debug option'); + + if (class_exists(Mailer::class)) { + $this->assertCount(1, $container->getDefinition('twig.mime_body_renderer')->getArguments()); + } } /** @@ -199,7 +204,7 @@ public function testTwigLoaderPaths($format) ], $paths); } - public function getFormats() + public static function getFormats() { return [ ['php'], @@ -229,7 +234,7 @@ public function testStopwatchExtensionAvailability($debug, $stopwatchEnabled, $e $this->assertSame($expected, $stopwatchIsAvailable->getValue($tokenParsers[0])); } - public function stopwatchExtensionAvailabilityProvider() + public static function stopwatchExtensionAvailabilityProvider() { return [ 'debug-and-stopwatch-enabled' => [true, true, true], @@ -263,6 +268,25 @@ public function testRuntimeLoader() $this->assertEquals('foo', $args['FooClass']->getValues()[0]); } + /** + * @dataProvider getFormats + */ + public function testCustomHtmlToTextConverterService(string $format) + { + if (!class_exists(Mailer::class)) { + $this->markTestSkipped('The "twig.mime_body_renderer" service requires the Mailer component'); + } + + $container = $this->createContainer(); + $container->registerExtension(new TwigExtension()); + $this->loadFromFile($container, 'mailer', $format); + $this->compileContainer($container); + + $bodyRenderer = $container->getDefinition('twig.mime_body_renderer'); + $this->assertCount(2, $bodyRenderer->getArguments()); + $this->assertEquals(new Reference('my_converter'), $bodyRenderer->getArgument('$converter')); + } + private function createContainer() { $container = new ContainerBuilder(new ParameterBag([ diff --git a/src/Symfony/Bundle/TwigBundle/composer.json b/src/Symfony/Bundle/TwigBundle/composer.json index b587875e2529b..e7297860ff44b 100644 --- a/src/Symfony/Bundle/TwigBundle/composer.json +++ b/src/Symfony/Bundle/TwigBundle/composer.json @@ -36,7 +36,7 @@ "symfony/yaml": "^5.4|^6.0", "symfony/framework-bundle": "^5.4|^6.0", "symfony/web-link": "^5.4|^6.0", - "doctrine/annotations": "^1.10.4" + "doctrine/annotations": "^1.10.4|^2" }, "conflict": { "symfony/framework-bundle": "<5.4", diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php index 3cdec828217b5..83257e26f8c3a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -113,7 +113,7 @@ public function panelAction(Request $request, string $token): Response 'request' => $request, 'templates' => $this->getTemplateManager()->getNames($profile), 'is_ajax' => $request->isXmlHttpRequest(), - 'profiler_markup_version' => 2, // 1 = original profiler, 2 = Symfony 2.8+ profiler + 'profiler_markup_version' => 3, // 1 = original profiler, 2 = Symfony 2.8+ profiler, 3 = Symfony 6.2+ profiler ]); } @@ -157,7 +157,7 @@ public function toolbarAction(Request $request, string $token = null): Response 'templates' => $this->getTemplateManager()->getNames($profile), 'profiler_url' => $url, 'token' => $token, - 'profiler_markup_version' => 2, // 1 = original toolbar, 2 = Symfony 2.8+ toolbar + 'profiler_markup_version' => 3, // 1 = original toolbar, 2 = Symfony 2.8+ profiler, 3 = Symfony 6.2+ profiler ]); } @@ -205,6 +205,7 @@ public function searchBarAction(Request $request): Response 'end' => $end, 'limit' => $limit, 'request' => $request, + 'render_hidden_by_default' => false, ]), 200, ['Content-Type' => 'text/html'] @@ -360,7 +361,7 @@ public function openAction(Request $request): Response } return $this->renderWithCspNonces($request, '@WebProfiler/Profiler/open.html.twig', [ - 'filename' => $filename, + 'file_info' => new \SplFileInfo($filename), 'file' => $file, 'line' => $line, ]); @@ -368,11 +369,7 @@ public function openAction(Request $request): Response protected function getTemplateManager(): TemplateManager { - if (null === $this->templateManager) { - $this->templateManager = new TemplateManager($this->profiler, $this->twig, $this->templates); - } - - return $this->templateManager; + return $this->templateManager ??= new TemplateManager($this->profiler, $this->twig, $this->templates); } private function denyAccessIfProfilerDisabled() diff --git a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php index d460cf1462035..23a69bc76490c 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php +++ b/src/Symfony/Bundle/WebProfilerBundle/DependencyInjection/WebProfilerExtension.php @@ -57,9 +57,6 @@ public function load(array $configs, ContainerBuilder $container) ->replaceArgument(3, new ServiceClosureArgument(new Reference('debug.file_link_formatter.url_format'))); } - /** - * {@inheritdoc} - */ public function getXsdValidationBasePath(): string|false { return __DIR__.'/../Resources/config/schema'; diff --git a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php index 5152223669c8d..2ba1564e687a1 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php +++ b/src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php @@ -85,7 +85,7 @@ public function onKernelResponse(ResponseEvent $event) $this->urlGenerator->generate('_profiler', ['token' => $response->headers->get('X-Debug-Token')], UrlGeneratorInterface::ABSOLUTE_URL) ); } catch (\Exception $e) { - $response->headers->set('X-Debug-Error', \get_class($e).': '.preg_replace('/\s+/', ' ', $e->getMessage())); + $response->headers->set('X-Debug-Error', $e::class.': '.preg_replace('/\s+/', ' ', $e->getMessage())); } } @@ -113,7 +113,7 @@ public function onKernelResponse(ResponseEvent $event) $session->getFlashBag()->setAll($session->getFlashBag()->peekAll()); } - $response->setContent($this->twig->render('@WebProfiler/Profiler/toolbar_redirect.html.twig', ['location' => $response->headers->get('Location')])); + $response->setContent($this->twig->render('@WebProfiler/Profiler/toolbar_redirect.html.twig', ['location' => $response->headers->get('Location'), 'host' => $request->getSchemeAndHttpHost()])); $response->setStatusCode(200); $response->headers->remove('Location'); } @@ -121,7 +121,7 @@ public function onKernelResponse(ResponseEvent $event) if (self::DISABLED === $this->mode || !$response->headers->has('X-Debug-Token') || $response->isRedirection() - || ($response->headers->has('Content-Type') && !str_contains($response->headers->get('Content-Type'), 'html')) + || ($response->headers->has('Content-Type') && !str_contains($response->headers->get('Content-Type') ?? '', 'html')) || 'html' !== $request->getRequestFormat() || false !== stripos($response->headers->get('Content-Disposition', ''), 'attachment;') ) { diff --git a/src/Symfony/Bundle/WebProfilerBundle/LICENSE b/src/Symfony/Bundle/WebProfilerBundle/LICENSE index 88bf75bb4d6a2..0138f8f071351 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/LICENSE +++ b/src/Symfony/Bundle/WebProfilerBundle/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2022 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/cache.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/cache.html.twig index 8f029d51e6dc5..3f307c8b81b49 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/cache.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/cache.html.twig @@ -47,52 +47,17 @@

Cache

{% if collector.totals.calls == 0 %} -
+

No cache calls were made.

{% else %} -
-
- {{ collector.totals.calls }} - Total calls -
-
- {{ '%0.2f'|format(collector.totals.time * 1000) }} ms - Total time -
-
-
- {{ collector.totals.reads }} - Total reads -
-
- {{ collector.totals.writes }} - Total writes -
-
- {{ collector.totals.deletes }} - Total deletes -
-
-
- {{ collector.totals.hits }} - Total hits -
-
- {{ collector.totals.misses }} - Total misses -
-
- - {{ collector.totals.hit_read_ratio ?? 0 }} % - - Hits/reads -
-
+ {{ _self.render_metrics(collector.totals, true) }}

Pools

- {% for name, calls in collector.calls %} + {# the empty merge is needed to turn the iterator into an array #} + {% set cache_pools_with_calls = collector.calls|filter(calls => calls|length > 0)|merge([]) %} + {% for name, calls in cache_pools_with_calls %}

{{ name }} {{ collector.statistics[name].calls }}

@@ -111,25 +76,7 @@
{% else %}

Metrics

-
- {% for key, value in collector.statistics[name] %} -
- - {% if key == 'time' %} - {{ '%0.2f'|format(1000 * value) }} ms - {% elseif key == 'hit_read_ratio' %} - {{ value ?? 0 }} % - {% else %} - {{ value }} - {% endif %} - - {{ key == 'hit_read_ratio' ? 'Hits/reads' : key|capitalize }} -
- {% if key == 'time' or key == 'deletes' %} -
- {% endif %} - {% endfor %} -
+ {{ _self.render_metrics(collector.statistics[name]) }}

Calls

@@ -155,7 +102,77 @@ {% endif %} + + {% if loop.last %} +
+

Pools without calls {{ collector.calls|filter(calls => 0 == calls|length)|length }}

+ +
+
+ + + + + + + {% for cache_pool in collector.calls|filter(calls => 0 == calls|length)|keys|sort %} + + {% endfor %} + +
Cache pools that received no calls
{{ cache_pool }}
+
+
+ {% endif %} {% endfor %}
{% endif %} {% endblock %} + +{% macro render_metrics(pool, is_total = false) %} +
+
+ {{ pool.calls }} + {{ is_total ? 'Total calls' : 'Calls' }} +
+
+ {{ '%0.2f'|format(pool.time * 1000) }} ms + {{ is_total ? 'Total time' : 'Time' }} +
+ +
+ +
+
+ {{ pool.reads }} + {{ is_total ? 'Total reads' : 'Reads' }} +
+
+ {{ pool.writes }} + {{ is_total ? 'Total writes' : 'Writes' }} +
+
+ {{ pool.deletes }} + {{ is_total ? 'Total deletes' : 'Deletes' }} +
+
+ +
+ +
+
+ {{ pool.hits }} + {{ is_total ? 'Total hits' : 'Hits' }} +
+
+ {{ pool.misses }} + {{ is_total ? 'Total misses' : 'Misses' }} +
+
+ + {{ pool.hit_read_ratio ?? 0 }} % + + Hits/reads +
+
+
+{% endmacro %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig index 5f8cf89b2225e..b0353b87db310 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/config.html.twig @@ -118,7 +118,13 @@
- {{ collector.symfonyversion }} + + {{ collector.symfonyversion }} + + {% if collector.symfonylts %} + (LTS) + {% endif %} + Symfony version
@@ -137,33 +143,39 @@ {% endif %}
- {% set symfony_status = { dev: 'Unstable Version', stable: 'Stable Version', eom: 'Maintenance Ended', eol: 'Version Expired' } %} + {% set symfony_status = { dev: 'In Development', stable: 'Maintained', eom: 'Security Fixes Only', eol: 'Unmaintained' } %} {% set symfony_status_class = { dev: 'warning', stable: 'success', eom: 'warning', eol: 'error' } %} - - - - - - - - - - - - - - - - - -
Symfony StatusBugs {{ collector.symfonystate in ['eom', 'eol'] ? 'were' : 'are' }} fixed untilSecurity issues {{ collector.symfonystate == 'eol' ? 'were' : 'are' }} fixed until
- {{ symfony_status[collector.symfonystate]|upper }} - {% if collector.symfonylts %} -   Long-Term Support - {% endif %} - {{ collector.symfonyeom }}{{ collector.symfonyeol }} - View roadmap -
+ +
+
+
+ + {{ symfony_status[collector.symfonystate]|upper }} + + Your Symfony version status +
+ + {% if collector.symfonylts %} +
+ + {{ collector.symfonyeom }} + + Bug fixes {{ collector.symfonystate in ['eom', 'eol'] ? 'ended on' : 'until' }} +
+ {% endif %} + +
+ + {{ collector.symfonyeol }} + + + {{ collector.symfonylts ? 'Security fixes' : 'Bug fixes and security fixes' }} + {{ 'eol' == collector.symfonystate ? 'ended on' : 'until' }} +
+
+
+ + View Symfony {{ collector.symfonyversion }} release details

PHP Configuration

@@ -190,19 +202,21 @@
-
- {{ source('@WebProfiler/Icon/' ~ (collector.haszendopcache ? 'yes' : 'no') ~ '.svg') }} - OPcache -
+
+
+ {{ source('@WebProfiler/Icon/' ~ (collector.haszendopcache ? 'yes' : 'no') ~ '.svg') }} + OPcache +
-
- {{ source('@WebProfiler/Icon/' ~ (collector.hasapcu ? 'yes' : 'no-gray') ~ '.svg') }} - APCu -
+
+ {{ source('@WebProfiler/Icon/' ~ (collector.hasapcu ? 'yes' : 'no') ~ '.svg') }} + APCu +
-
- {{ source('@WebProfiler/Icon/' ~ (collector.hasxdebug ? 'yes' : 'no-gray') ~ '.svg') }} - Xdebug +
+ {{ source('@WebProfiler/Icon/' ~ (collector.hasxdebug ? 'yes' : 'no') ~ '.svg') }} + Xdebug +
diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig index af4f8b5e73ef0..33f33b235963a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/events.html.twig @@ -13,7 +13,7 @@

Event Dispatcher

{% if collector.calledlisteners is empty %} -
+

No events have been recorded. Check that debugging is enabled in the kernel.

{% else %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig index e27200d7abdf7..3d062becd3eba 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/exception.html.twig @@ -23,10 +23,18 @@ {% endblock %} {% block panel %} + {# these styles are needed to override some styles from Exception page, which wasn't + updated yet to the new style of the Symfony Profiler #} + +

Exceptions

{% if not collector.hasexception %} -
+

No exception was thrown and caught during the request.

{% else %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig index c568d894f3053..81973186f563b 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/form.html.twig @@ -46,7 +46,7 @@ #tree-menu { float: left; padding-right: 10px; - width: 230px; + width: 220px; } #tree-menu ul { list-style: none; @@ -60,11 +60,12 @@ } #tree-menu .empty { border: 0; + box-shadow: none !important; padding: 0; } #tree-details-container { - border-left: 1px solid #DDD; - margin-left: 250px; + border-left: 1px solid var(--table-border-color); + margin-left: 230px; padding-left: 20px; } .tree-details { @@ -77,74 +78,63 @@ .toggle-icon { display: inline-block; - background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QweDgwx4LcKwAAAABVQTFRFAAAA////////////////ZmZm////bvjBwAAAAAV0Uk5TABZwsuCVEUjgAAAAAWJLR0QF+G/pxwAAAE1JREFUGNNjSHMSYGBgUEljSGYAAzMGBwiDhUEBwmBiEIAwGBmwgTQgQGWgA7h2uIFwK+CWwp1BpHvYEqDuATEYkBlY3IOmBq6dCPcAAIT5Eg2IksjQAAAAAElFTkSuQmCC") no-repeat top left #5eb5e0; + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' data-icon-name='icon-tabler-square-plus' width='24' height='24' viewBox='0 0 24 24' stroke-width='2px' stroke='currentColor' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'%3E%3C/path%3E%3Crect x='4' y='4' width='16' height='16' rx='2'%3E%3C/rect%3E%3Cline x1='9' y1='12' x2='15' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='9' x2='12' y2='15'%3E%3C/line%3E%3C/svg%3E") no-repeat; + background-size: 18px 18px; } .closed .toggle-icon, .closed.toggle-icon { background-position: bottom left; } .toggle-icon.empty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAZgBmAGYHukptAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QweDhIf6CA40AAAAFRJREFUOMvtk7ENACEMA61vfx767MROWfO+AdGBHlNyTZrYUZRYDBII4NWE1pNdpFarfgLUbpDaBEgBYRiEVjsvDLa1l6O4Z3wkFWN+OfLKdpisOH/TlICzukmUJwAAAABJRU5ErkJggg=="); + background-image: none; } .tree .tree-inner { cursor: pointer; padding: 5px 7px 5px 22px; position: relative; - + overflow: hidden; + text-overflow: ellipsis; } .tree .toggle-button { - /* provide a bigger clickable area than just 10x10px */ width: 16px; height: 16px; margin-left: -18px; } .tree .toggle-icon { - width: 10px; - height: 10px; - /* position the icon in the center of the clickable area */ - margin-left: 3px; - margin-top: 3px; - background-size: 10px 20px; - background-color: #AAA; + width: 18px; + height: 18px; + vertical-align: bottom; } .tree .toggle-icon.empty { - width: 10px; - height: 10px; + width: 5px; + height: 5px; position: absolute; top: 50%; - margin-top: -5px; - margin-left: -15px; - background-size: 10px 10px; + margin-top: -2px; + margin-left: -13px; + } + .tree .tree-inner { + border-radius: 4px; } .tree ul ul .tree-inner { - padding-left: 37px; + padding-left: 32px; } .tree ul ul ul .tree-inner { - padding-left: 52px; + padding-left: 48px; } .tree ul ul ul ul .tree-inner { - padding-left: 67px; + padding-left: 64px; } .tree ul ul ul ul ul .tree-inner { - padding-left: 82px; + padding-left: 72px; } .tree .tree-inner:hover { - background: #dfdfdf; - } - .tree .tree-inner:hover span:not(.has-error) { - color: var(--base-0); + background: var(--gray-200); } .tree .tree-inner.active, .tree .tree-inner.active:hover { background: var(--tree-active-background); font-weight: bold; } - .tree .tree-inner.active .toggle-icon, .tree .tree-inner:hover .toggle-icon, .tree .tree-inner.active:hover .toggle-icon { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QweDhEYXWn+sAAAABhQTFRFAAAA39/f39/f39/f39/fZmZm39/f////gc3YPwAAAAV0Uk5TAAtAc6ZeVyCYAAAAAWJLR0QF+G/pxwAAAE1JREFUGNNjSHMSYGBgUEljSGYAAzMGBwiDhUEBwmBiEIAwGBmwgXIgQGWgA7h2uIFwK+CWwp1BpHvYC6DuATEYkBlY3IOmBq6dCPcAADqLE4MnBi/fAAAAAElFTkSuQmCC"); - background-color: #999; - } - .tree .tree-inner.active .toggle-icon.empty, .tree .tree-inner:hover .toggle-icon.empty, .tree .tree-inner.active:hover .toggle-icon.empty { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QweDhoucSey4gAAABVQTFRFAAAA39/f39/f39/f39/fZmZm39/fD5Dx2AAAAAV0Uk5TAAtAc6ZeVyCYAAAAAWJLR0QF+G/pxwAAADJJREFUCNdjSHMSYGBgUEljSGYAAzMGBwiDhUEBwmBiEIAwGBnIA3DtcAPhVsAthTkDAFOfBKW9C1iqAAAAAElFTkSuQmCC"); - } .tree-details .toggle-icon { width: 16px; height: 16px; @@ -180,26 +170,6 @@ color: inherit; text-decoration: inherit; } - h2 + h3.form-data-type { - margin-top: 0; - } - h3.form-data-type + h3 { - margin-top: 1em; - } - .theme-dark .toggle-icon { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAVUExURUdwTH+Ag0lNUZiYmGRmbP///zU5P2n9VV4AAAAFdFJOUwCv+yror0g1sQAAAE1JREFUGNNjSFM0YGBgEEpjSGEAAzcGBQiDiUEAwmBkMIAwmBmwgVAgQGWgA7h2uIFwK+CWwp1BpHtYA6DuATEYkBlY3IOmBq6dCPcAAKMtEEs3tfChAAAAAElFTkSuQmCC'); - } - .theme-dark .toggle-icon.empty { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAASUExURUdwTDI3OzQ5PS4uLjU3PzU5P4keoyIAAAAFdFJOUwBApgtzrnKGEwAAADJJREFUCNdjCFU0YGBgEAplCGEAA1cGBQiDiUEAwmBkMIAwmBnIA3DtcAPhVsAthTkDACsZBBmrTTSxAAAAAElFTkSuQmCC'); - } - .theme-dark .tree .tree-inner.active .toggle-icon, .theme-dark .tree .tree-inner:hover .toggle-icon, .theme-dark .tree .tree-inner.active:hover .toggle-icon { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAAD1BMVEVHcEx/gIOYmJiZmZn///+IJ2wIAAAAA3RSTlMAryoIUq0uAAAAUElEQVQY02NgYFQ2NjYWYGBgMAYDBgZmCMOAQRjCMGRQhjCMoEqAipAYLkCAykBXA9cONxBuBdxShDOIc4+JM9Q9IIYxMgOLe9DUwLUT4R4AznguG0qfEa0AAAAASUVORK5CYII='); - background-color: transparent; - } - .theme-dark .tree .tree-inner.active .toggle-icon.empty, .theme-dark .tree .tree-inner:hover .toggle-icon.empty, .theme-dark .tree .tree-inner.active:hover .toggle-icon.empty { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAACVBMVEVHcEwyNzuqqqrd9nIgAAAAAnRSTlMAQABPjKgAAAArSURBVAjXY2BctcqBgWvVqgUMWqtWrWDIWrVqJcMqICCGACsGawMbADIKANflJYEoGMqtAAAAAElFTkSuQmCC'); - background-color: transparent; - } {% endblock %} @@ -221,7 +191,7 @@ {% endfor %}
{% else %} -
+

No forms were submitted for this request.

{% endif %} @@ -441,7 +411,7 @@ }; tabTarget.initTabs(document.querySelectorAll('.tree .tree-inner')); - toggler.initButtons(document.querySelectorAll('a.toggle-button')); + toggler.initButtons(document.querySelectorAll('.toggle-button')); {% endblock %} @@ -449,7 +419,7 @@ {% import _self as tree %} {% set has_error = data.errors is defined and data.errors|length > 0 %}
  • -
    +
    {% if has_error %}
    {{ data.errors|length }}
    {% endif %} @@ -480,10 +450,71 @@

    {{ name|default('(no name)') }}

    {% if data.type_class is defined %} -

    {{ profiler_dump(data.type_class) }}

    +
    + Form type: + {{ profiler_dump(data.type_class) }} +
    {% endif %} - {% if data.errors is defined and data.errors|length > 0 %} + {% set form_has_errors = data.errors ?? [] is not empty %} +
    +
    +

    Errors

    + +
    + {{ _self.render_form_errors(data) }} +
    +
    + +
    +

    Default Data

    + +
    + {{ _self.render_form_default_data(data) }} +
    +
    + +
    +

    Submitted Data

    + +
    + {{ _self.render_form_submitted_data(data) }} +
    +
    + +
    +

    Passed Options

    + +
    + {{ _self.render_form_passed_options(data) }} +
    +
    + +
    +

    Resolved Options

    + +
    + {{ _self.render_form_resolved_options(data) }} +
    +
    + +
    +

    View Vars

    + +
    + {{ _self.render_form_view_variables(data) }} +
    +
    +
    +
    + + {% for childName, childData in data.children %} + {{ tree.form_tree_details(childName, childData, forms_by_hash) }} + {% endfor %} +{% endmacro %} + +{% macro render_form_errors(data) %} + {% if data.errors is defined and data.errors|length > 0 %} - {% endif %} - - {% if data.default_data is defined %} -

    - - Default Data - -

    - -
    - - - - - - + {% for error in data.errors %} - + - - - - - - - + {% endfor %}
    PropertyValue
    Model Format{{ error.message }} - {% if data.default_data.model is defined %} - {{ profiler_dump(data.default_data.seek('model')) }} + {% if error.origin is empty %} + This form. + {% elseif forms_by_hash[error.origin] is not defined %} + Unknown. {% else %} - same as normalized format + {{ forms_by_hash[error.origin].name }} {% endif %}
    Normalized Format{{ profiler_dump(data.default_data.seek('norm')) }}
    View Format - {% if data.default_data.view is defined %} - {{ profiler_dump(data.default_data.seek('view')) }} + {% if error.trace %} + Caused by: + {% for stacked in error.trace %} + {{ profiler_dump(stacked) }} + {% endfor %} {% else %} - same as normalized format + Unknown. {% endif %}
    - {% endif %} - - {% if data.submitted_data is defined %} -

    - - Submitted Data - -

    + {% else %} +
    +

    This form has no errors.

    +
    + {% endif %} +{% endmacro %} -
    - {% if data.submitted_data.norm is defined %} - - - - - - - - - - - - - - - - - - - - - -
    PropertyValue
    View Format - {% if data.submitted_data.view is defined %} - {{ profiler_dump(data.submitted_data.seek('view')) }} - {% else %} - same as normalized format - {% endif %} -
    Normalized Format{{ profiler_dump(data.submitted_data.seek('norm')) }}
    Model Format - {% if data.submitted_data.model is defined %} - {{ profiler_dump(data.submitted_data.seek('model')) }} - {% else %} - same as normalized format - {% endif %} -
    - {% else %} -
    -

    This form was not submitted.

    -
    - {% endif %} +{% macro render_form_default_data(data) %} + {% if data.default_data is defined %} + + + + + + + + + + + + + + + + + + + + + +
    PropertyValue
    Model Format + {% if data.default_data.model is defined %} + {{ profiler_dump(data.default_data.seek('model')) }} + {% else %} + same as normalized format + {% endif %} +
    Normalized Format{{ profiler_dump(data.default_data.seek('norm')) }}
    View Format + {% if data.default_data.view is defined %} + {{ profiler_dump(data.default_data.seek('view')) }} + {% else %} + same as normalized format + {% endif %} +
    + {% else %} +
    +

    This form has default data defined.

    - {% endif %} + {% endif %} +{% endmacro %} - {% if data.passed_options is defined %} -

    - - Passed Options - -

    +{% macro render_form_submitted_data(data) %} + {% if data.submitted_data.norm is defined %} + + + + + + + + + + + + + + + + + + + + + +
    PropertyValue
    View Format + {% if data.submitted_data.view is defined %} + {{ profiler_dump(data.submitted_data.seek('view')) }} + {% else %} + same as normalized format + {% endif %} +
    Normalized Format{{ profiler_dump(data.submitted_data.seek('norm')) }}
    Model Format + {% if data.submitted_data.model is defined %} + {{ profiler_dump(data.submitted_data.seek('model')) }} + {% else %} + same as normalized format + {% endif %} +
    + {% else %} +
    +

    This form was not submitted.

    +
    + {% endif %} +{% endmacro %} -
    - {% if data.passed_options|length %} - - - - - - - - - - {% for option, value in data.passed_options %} +{% macro render_form_passed_options(data) %} + {% if data.passed_options ?? [] is not empty %} +
    OptionPassed ValueResolved Value
    + + + + + + + + + {% for option, value in data.passed_options %} @@ -658,73 +677,50 @@ {% endif %} - {% endfor %} - -
    OptionPassed ValueResolved Value
    {{ option }} {{ profiler_dump(value) }}
    - {% else %} -
    -

    No options were passed when constructing this form.

    -
    - {% endif %} -
    - {% endif %} - - {% if data.resolved_options is defined %} -

    - - Resolved Options - -

    - - +{% macro render_form_resolved_options(data) %} + + + + + + + + + {% for option, value in data.resolved_options ?? [] %} + + + + + {% endfor %} + +
    OptionValue
    {{ option }}{{ profiler_dump(value) }}
    +{% endmacro %} - {% for childName, childData in data.children %} - {{ tree.form_tree_details(childName, childData, forms_by_hash) }} - {% endfor %} +{% macro render_form_view_variables(data) %} + + + + + + + + + {% for variable, value in data.view_vars ?? [] %} + + + + + {% endfor %} + +
    VariableValue
    {{ variable }}{{ profiler_dump(value) }}
    {% endmacro %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/http_client.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/http_client.html.twig index f409a2dc03fde..7fda5a93ee5df 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/http_client.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/http_client.html.twig @@ -38,7 +38,7 @@ {% block panel %}

    HTTP Client

    {% if collector.requestCount == 0 %} -
    +

    No HTTP requests were made.

    {% else %} @@ -77,33 +77,37 @@ {% endif %} {% endfor %} {% endif %} - +
    {% if profiler_token and profiler_link %} {% endif %} - {% if trace.curlCommand is not null %} + {% if trace.curlCommand is defined and trace.curlCommand %} {% endif %} + {% if trace.options is not empty %} + + + + + {% endif %} - + {% if trace.http_code >= 500 %} {% set responseStatus = 'error' %} {% elseif trace.http_code >= 400 %} @@ -111,11 +115,10 @@ {% else %} {% set responseStatus = 'success' %} {% endif %} - + {{ trace.http_code }} - - + {{ profiler_dump(trace.info, maxDepth=1) }} {% if profiler_token and profiler_link %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig index 5246dc1049f0f..937b4a6449e80 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig @@ -47,7 +47,7 @@

    Log Messages

    {% if collector.processedLogs is empty %} -
    +

    No log messages available.

    {% else %} @@ -131,8 +131,10 @@
    - - + + + + @@ -185,7 +187,7 @@

    Container Compilation Logs ({{ compilerLogTotal }})

    -

    Log messages generated during the compilation of the service container.

    + Log messages generated during the compilation of the service container.
    {% if collector.compilerLogs is empty %} @@ -253,10 +255,6 @@ {% if has_trace %} {% set trace_id = 'trace-' ~ category ~ '-' ~ log_index %} - -
    - {{ profiler_dump(log.context.exception.trace, maxDepth=1) }} -
    {% endif %} {% if has_context %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/mailer.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/mailer.html.twig index 1b898ad5397ec..a42b8b33dd32e 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/mailer.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/mailer.html.twig @@ -24,43 +24,6 @@ {% endif %} {% endblock %} -{% block head %} - {{ parent() }} - -{% endblock %} - {% block menu %} {% set events = collector.events %} @@ -78,156 +41,222 @@ {% block panel %} {% set events = collector.events %} -

    Emails

    {% if not events.messages|length %} -
    +

    No emails were sent.

    - {% endif %} + {% else %} +
    +
    +
    + {{ events.events|filter(e => e.isQueued())|length }} + Queued +
    -
    -
    - {{ events.events|filter(e => e.isQueued())|length }} - Queued +
    + {{ events.events|filter(e => not e.isQueued())|length }} + Sent +
    +
    + {% endif %} -
    - {{ events.events|filter(e => not e.isQueued())|length }} - Sent + {% if events.transports|length > 1 %} + {% for transport in events.transports %} +

    {{ transport }} transport

    + {{ _self.render_transport_details(collector, transport) }} + {% endfor %} + {% elseif events.transports is not empty %} + {{ _self.render_transport_details(collector, events.transports|first, true) }} + {% endif %} + + {% macro render_transport_details(collector, transport, show_transport_name = false) %} +
    + {% set num_emails = collector.events.events(transport)|length %} + {% if num_emails > 1 %} +
    +
    - {{ trace.method }} + {{ trace.method }} {{ trace.url }} - {% if trace.options is not empty %} - {{ profiler_dump(trace.options, maxDepth=1) }} - {% endif %} Profile - +
    Request options{{ profiler_dump(trace.options, maxDepth=1) }}
    + Response
    TimeMessage
    TimeMessage
    + + + + + + + + + + {% for event in collector.events.events(transport) %} + + + + + + + {% endfor %} + +
    #SubjectToActions
    {{ loop.index }}{{ event.message.getSubject() ?? '(No subject)' }}{{ event.message.getTo()|map(addr => addr.toString())|join(', ')|default('(empty)') }}
    +
    + + {% for event in collector.events.events(transport) %} +
    + {{ _self.render_email_details(collector, transport, event.message, event.isQueued, show_transport_name) }} +
    + {% endfor %} + + + {% else %} + {% set event = (collector.events.events(transport)|first) %} + {{ _self.render_email_details(collector, transport, event.message, event.isQueued, show_transport_name) }} + {% endif %}
    -
    - - {% for transport in events.transports %} -
    -
    - {% for event in events.events(transport) %} - {% set message = event.message %} -
    -

    Email {{ event.isQueued() ? 'queued' : 'sent via ' ~ transport }}

    -
    -
    - {% if message.headers is not defined %} - {# RawMessage instance #} -
    -
    {{ message.toString() }}
    -
    - {% else %} - {# Message instance #} -
    -
    -
    -

    Headers

    -
    - Subject -

    {{ message.headers.get('subject').bodyAsString() ?? '(empty)' }}

    -
    -
    - From -
    {{ (message.headers.get('from').bodyAsString() ?? '(empty)')|replace({'From:': ''}) }}
    - - To -
    {{ (message.headers.get('to').bodyAsString() ?? '(empty)')|replace({'To:': ''}) }}
    - - {% if event.envelope.recipients|length > 0 %} - Recipients - {% for recipient in event.envelope.recipients %} -
    {{ recipient.address }}
    - {% endfor %} - {% endif %} -
    -
    - Headers -
    {% for header in message.headers.all|filter(header => (header.name ?? '') not in ['Subject', 'From', 'To']) %}
    -                                                                {{- header.toString }}
    -                                                            {%~ endfor %}
    -
    -
    -
    -
    - {% if message.htmlBody is defined %} - {# Email instance #} - {% set htmlBody = message.htmlBody() %} - {% if htmlBody is not null %} -
    -

    HTML Preview

    -
    -
    -                                                                
    -                                                            
    -
    -
    -
    -

    HTML Content

    -
    -
    -                                                                {%- if message.htmlCharset() %}
    -                                                                    {{- htmlBody|convert_encoding('UTF-8', message.htmlCharset()) }}
    -                                                                {%- else %}
    -                                                                    {{- htmlBody }}
    -                                                                {%- endif -%}
    -                                                            
    -
    -
    - {% endif %} - {% set textBody = message.textBody() %} - {% if textBody is not null %} -
    -

    Text Content

    -
    -
    -                                                                {%- if message.textCharset() %}
    -                                                                    {{- textBody|convert_encoding('UTF-8', message.textCharset()) }}
    -                                                                {%- else %}
    -                                                                    {{- textBody }}
    -                                                                {%- endif -%}
    -                                                            
    -
    -
    - {% endif %} - {% for attachment in message.attachments %} - - {% endfor %} + {% endmacro %} + + {% macro render_email_details(collector, transport, message, message_is_queued, show_transport_name = false) %} + {% if show_transport_name %} +

    + Status: {{ message_is_queued ? 'Queued' : 'Sent' }} + • + Transport: {{ transport }} +

    + {% endif %} + + {% if message.headers is not defined %} + {# render the raw message contents #} + + {{ source('@WebProfiler/Icon/download.svg') }} + Download as EML file + + +
    {{ message.toString() }}
    + {% else %} +
    +
    +

    Email contents

    +
    +
    +

    + {{ message.getSubject() ?? '(No subject)' }} +

    +
    +

    From: {{ message.getFrom()|map(addr => addr.toString())|join(', ')|default('(empty)') }}

    +

    To: {{ message.getTo()|map(addr => addr.toString())|join(', ')|default('(empty)') }}

    + {% for header in message.headers.all|filter(header => (header.name ?? '')|lower not in ['subject', 'from', 'to']) %} +

    {{ header.toString }}

    + {% endfor %} +
    +
    + + {% if message.attachments %} +
    + {% set num_of_attachments = message.attachments|length %} + {% set total_attachments_size_in_bytes = message.attachments|reduce((total_size, attachment) => total_size + attachment.body|length) %} +

    + {{ source('@WebProfiler/Icon/attachment.svg') }} + Attachments ({{ num_of_attachments }} file{{ num_of_attachments > 1 ? 's' }} / {{ _self.render_file_size_humanized(total_attachments_size_in_bytes) }}) +

    + +
      + {% for attachment in message.attachments %} +
    • + {{ source('@WebProfiler/Icon/file.svg') }} + + {% if attachment.filename|default %} + {{ attachment.filename }} + {% else %} + (no filename) {% endif %} -
      -

      Parts Hierarchy

      -
      -
      {{ message.body().asDebugString() }}
      -
      + + ({{ _self.render_file_size_humanized(attachment.body|length) }}) + + Download +
    • + {% endfor %} +
    +
    + {% endif %} + + {% if message.htmlBody or message.textBody %} +
    +
    + {% if message.htmlBody %} + {% set htmlBody = message.htmlBody() %} +
    +

    HTML preview

    +
    +
    -
    -

    Raw

    -
    -
    {{ message.toString() }}
    -
    +
    + +
    +

    HTML content

    +
    +
    +                                                    {%- if message.htmlCharset() %}
    +                                                        {{- htmlBody|convert_encoding('UTF-8', message.htmlCharset()) }}
    +                                                    {%- else %}
    +                                                        {{- htmlBody }}
    +                                                    {%- endif -%}
    +                                                
    +
    +
    + {% endif %} + + {% if message.textBody %} + {% set textBody = message.textBody() %} +
    +

    Text content

    +
    +
    +                                                    {%- if message.textCharset() %}
    +                                                        {{- textBody|convert_encoding('UTF-8', message.textCharset()) }}
    +                                                    {%- else %}
    +                                                        {{- textBody }}
    +                                                    {%- endif -%}
    +                                                
    -
    - {% endif %} + {% endif %} +
    -
    + {% endif %}
    - {% endfor %} +
    + +
    +

    MIME parts

    +
    +
    {{ message.body().asDebugString() }}
    +
    +
    + +
    +

    Raw Message

    + +
    -
    - {% endfor %} + {% endif %} + {% endmacro %} + + {% macro render_file_size_humanized(bytes) %} + {%- if bytes < 1000 -%} + {{- bytes ~ ' bytes' -}} + {%- elseif bytes < 1000 ** 2 -%} + {{- (bytes / 1000)|number_format(2) ~ ' kB' -}} + {%- else -%} + {{- (bytes / 1000 ** 2)|number_format(2) ~ ' MB' -}} + {%- endif -%} + {% endmacro %} {% endblock %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig index 0d0f11457f408..1dc5accf1c3a4 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig @@ -57,8 +57,6 @@ .message-bus .badge.status-some-errors { line-height: 16px; border-bottom: 2px solid #B0413E; } .message-item tbody.sf-toggle-content.sf-toggle-visible { display: table-row-group; } - td.message-bus-dispatch-caller { background: #f1f2f3; } - .theme-dark td.message-bus-dispatch-caller { background: var(--base-1); } {% endblock %} @@ -68,9 +66,12 @@

    Messages

    {% if collector.messages is empty %} -
    +

    No messages have been collected.

    + {% elseif 1 == collector.buses|length %} +

    Ordered list of dispatched messages across all your buses

    + {{ helper.render_bus_messages(collector.messages, true) }} {% else %}
    @@ -112,9 +113,6 @@ data-toggle-initial="{{ loop.first ? 'display' }}" > {{ profiler_dump(dispatchCall.message.type) }} - {% if showBus %} - {{ dispatchCall.bus }} - {% endif %} {% if dispatchCall.exception is defined %} exception {% endif %} @@ -127,21 +125,21 @@ - - + {% else %} + {{ caller.name }} + {% endif %} + line {{ caller.line }}