From 877b5cde285a0f5a571d496be51f7c2a8dd1dd06 Mon Sep 17 00:00:00 2001 From: Artem Lopata Date: Fri, 17 Oct 2025 00:35:35 +0200 Subject: [PATCH 01/91] The BrowserKit history with parameter separator without slash. --- src/Symfony/Component/BrowserKit/AbstractBrowser.php | 2 +- .../Component/BrowserKit/Tests/AbstractBrowserTest.php | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/BrowserKit/AbstractBrowser.php b/src/Symfony/Component/BrowserKit/AbstractBrowser.php index c534e9e16c146..29a2c441ea281 100644 --- a/src/Symfony/Component/BrowserKit/AbstractBrowser.php +++ b/src/Symfony/Component/BrowserKit/AbstractBrowser.php @@ -656,7 +656,7 @@ protected function getAbsoluteUri(string $uri): string $uri = $path.$uri; } - return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri; + return preg_replace('#^(.*?//[^/?]+)[/?].*$#', '$1', $currentUri).$uri; } /** diff --git a/src/Symfony/Component/BrowserKit/Tests/AbstractBrowserTest.php b/src/Symfony/Component/BrowserKit/Tests/AbstractBrowserTest.php index 504cc95878ef2..9c4bf050c7463 100644 --- a/src/Symfony/Component/BrowserKit/Tests/AbstractBrowserTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/AbstractBrowserTest.php @@ -889,4 +889,13 @@ public function testInternalRequestNull() $client->getInternalRequest(); } + + public function testHistoryWithParametersAndNoSlash() + { + $client = $this->getBrowser(); + $client->request('GET', 'https://www.example.com?the=value'); + $client->request('GET', '/path/?parameter=value'); + + $this->assertSame('https://www.example.com/path/?parameter=value', $client->getRequest()->getUri(), '->request() history provides proper base.'); + } } From 2a618c51c62f23126e105cc1ddce3ad0fd385481 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Thu, 30 Oct 2025 17:28:23 +0100 Subject: [PATCH 02/91] [Form] Fix EnumType choice_label logic for grouped choices --- .../Form/Extension/Core/Type/EnumType.php | 12 +- .../Extension/Core/Type/EnumTypeTest.php | 111 ++++++++++++++++++ 2 files changed, 119 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/Type/EnumType.php b/src/Symfony/Component/Form/Extension/Core/Type/EnumType.php index b529a5869cec5..106cb341d7003 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/EnumType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/EnumType.php @@ -31,11 +31,15 @@ public function configureOptions(OptionsResolver $resolver): void ->setAllowedValues('class', enum_exists(...)) ->setDefault('choices', static fn (Options $options): array => $options['class']::cases()) ->setDefault('choice_label', static function (Options $options) { - if (\is_array($options['choices']) && !array_is_list($options['choices'])) { - return null; - } + return static function (\UnitEnum $choice, int|string $key): string|TranslatableInterface { + if (\is_int($key)) { + // Key is an integer, use the enum's name (or translatable) + return $choice instanceof TranslatableInterface ? $choice : $choice->name; + } - return static fn (\UnitEnum $choice) => $choice instanceof TranslatableInterface ? $choice : $choice->name; + // Key is a string, use it as the label + return $key; + }; }) ->setDefault('choice_value', static function (Options $options): ?\Closure { if (!is_a($options['class'], \BackedEnum::class, true)) { diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/EnumTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/EnumTypeTest.php index 3916d86ebca22..dfd162070d280 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/EnumTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/EnumTypeTest.php @@ -11,6 +11,8 @@ namespace Extension\Core\Type; +use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView; +use Symfony\Component\Form\ChoiceList\View\ChoiceView; use Symfony\Component\Form\Extension\Core\Type\EnumType; use Symfony\Component\Form\Tests\Extension\Core\Type\BaseTypeTestCase; use Symfony\Component\Form\Tests\Fixtures\Answer; @@ -311,6 +313,115 @@ public function testChoicesWithLabels() $this->assertSame('no', $view->children[1]->vars['label']); } + public function testGroupedEnumChoices() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choices' => [ + 'Group 1' => [Answer::Yes, Answer::No], + 'Group 2' => [Answer::FourtyTwo], + ], + ]); + $view = $form->createView(); + $this->assertCount(2, $view->vars['choices']['Group 1']->choices); + $this->assertSame('Yes', $view->vars['choices']['Group 1']->choices[0]->label); + $this->assertSame('No', $view->vars['choices']['Group 1']->choices[1]->label); + $this->assertCount(1, $view->vars['choices']['Group 2']->choices); + $this->assertSame('FourtyTwo', $view->vars['choices']['Group 2']->choices[2]->label); + } + + public function testGroupedEnumChoicesWithCustomLabels() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choices' => [ + 'Group 1' => [ + 'Custom Yes' => Answer::Yes, + 'Custom No' => Answer::No, + ], + 'Group 2' => [ + 'Custom 42' => Answer::FourtyTwo, + ], + ], + ]); + $view = $form->createView(); + + // Test Group 1 + $this->assertCount(2, $view->vars['choices']['Group 1']->choices); + $this->assertSame('Custom Yes', $view->vars['choices']['Group 1']->choices[0]->label); + $this->assertSame('Custom No', $view->vars['choices']['Group 1']->choices[1]->label); + + // Test Group 2 + $this->assertCount(1, $view->vars['choices']['Group 2']->choices); + $this->assertSame('Custom 42', $view->vars['choices']['Group 2']->choices[2]->label); + } + + public function testMixedGroupedAndSingleChoices() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choices' => [ + 'Group 1' => [Answer::Yes, Answer::No], + 'Custom 42' => Answer::FourtyTwo, + ], + ]); + $view = $form->createView(); + + // Group 1 (simple list) → enum names + $this->assertInstanceOf(ChoiceGroupView::class, $view->vars['choices']['Group 1']); + $this->assertCount(2, $view->vars['choices']['Group 1']->choices); + $this->assertSame('Yes', $view->vars['choices']['Group 1']->choices[0]->label); + $this->assertSame('No', $view->vars['choices']['Group 1']->choices[1]->label); + + // Single custom → custom label (treated as flat choice) + $customChoice = $view->vars['choices'][2]; + $this->assertInstanceOf(ChoiceView::class, $customChoice); + $this->assertSame('Custom 42', $customChoice->label); + } + + public function testMixedLabeledAndUnlabeledChoices() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choices' => [ + Answer::Yes, + Answer::No, + 'Custom 42' => Answer::FourtyTwo, + ], + ]); + $view = $form->createView(); + // Assertions: names for unlabeled, custom for labeled + $children = array_values($view->children); // Numeric access + $this->assertSame('Yes', $children[0]->vars['label']); + $this->assertSame('No', $children[1]->vars['label']); + $this->assertSame('Custom 42', $children[2]->vars['label']); + } + + public function testEnumChoicesWithNumericCustomLabels() + { + $form = $this->factory->create($this->getTestedType(), null, [ + 'multiple' => false, + 'expanded' => true, + 'class' => Answer::class, + 'choice_label' => null, // Explicitly override to use keys as labels for numeric customs + 'choices' => [ + '34' => Answer::Yes, + '2' => Answer::No, + ], + ]); + $view = $form->createView(); + $this->assertSame('34', $view->children[0]->vars['label']); + $this->assertSame('2', $view->children[1]->vars['label']); + } + protected function getTestOptions(): array { return ['class' => Suit::class]; From 3605dd9333d05e4eaee744b0105228bce4dc3486 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 31 Oct 2025 17:42:50 +0100 Subject: [PATCH 03/91] [Routing] Fix matching the "0" URL --- .../Matcher/Dumper/CompiledUrlMatcherTrait.php | 6 +++--- .../Routing/Matcher/RedirectableUrlMatcher.php | 2 +- .../Component/Routing/Matcher/TraceableUrlMatcher.php | 2 +- src/Symfony/Component/Routing/Matcher/UrlMatcher.php | 5 +++-- .../Routing/Tests/Matcher/UrlMatcherTest.php | 11 +++++++++++ 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherTrait.php b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherTrait.php index db754e6de05ed..5177c269ae39e 100644 --- a/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +++ b/src/Symfony/Component/Routing/Matcher/Dumper/CompiledUrlMatcherTrait.php @@ -57,7 +57,7 @@ public function match(string $pathinfo): array } finally { $this->context->setScheme($scheme); } - } elseif ('/' !== $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/') { + } elseif ('' !== $trimmedPathinfo = rtrim($pathinfo, '/')) { $pathinfo = $trimmedPathinfo === $pathinfo ? $pathinfo.'/' : $trimmedPathinfo; if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { return $this->redirect($pathinfo, $ret['_route']) + $ret; @@ -73,8 +73,8 @@ public function match(string $pathinfo): array private function doMatch(string $pathinfo, array &$allow = [], array &$allowSchemes = []): array { $allow = $allowSchemes = []; - $pathinfo = rawurldecode($pathinfo) ?: '/'; - $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/'; + $pathinfo = '' === ($pathinfo = rawurldecode($pathinfo)) ? '/' : $pathinfo; + $trimmedPathinfo = '' === ($trimmedPathinfo = rtrim($pathinfo, '/')) ? '/' : $trimmedPathinfo; $context = $this->context; $requestMethod = $canonicalMethod = $context->getMethod(); diff --git a/src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php b/src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php index 8d1ad4f90ad8b..3e7b78b5e6099 100644 --- a/src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php @@ -41,7 +41,7 @@ public function match(string $pathinfo): array } finally { $this->context->setScheme($scheme); } - } elseif ('/' === $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/') { + } elseif ('' === $trimmedPathinfo = rtrim($pathinfo, '/')) { throw $e; } else { try { diff --git a/src/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php b/src/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php index 20fb064687c9e..8ff018309c987 100644 --- a/src/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php @@ -63,7 +63,7 @@ protected function matchCollection(string $pathinfo, RouteCollection $routes): a $method = 'GET'; } $supportsTrailingSlash = 'GET' === $method && $this instanceof RedirectableUrlMatcherInterface; - $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/'; + $trimmedPathinfo = '' === ($trimmedPathinfo = rtrim($pathinfo, '/')) ? '/' : $trimmedPathinfo; foreach ($routes as $name => $route) { $compiledRoute = $route->compile(); diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php index ec281fde739e4..f3ab414f04ed7 100644 --- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php +++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -78,8 +78,9 @@ public function getContext(): RequestContext public function match(string $pathinfo): array { $this->allow = $this->allowSchemes = []; + $pathinfo = '' === ($pathinfo = rawurldecode($pathinfo)) ? '/' : $pathinfo; - if ($ret = $this->matchCollection(rawurldecode($pathinfo) ?: '/', $this->routes)) { + if ($ret = $this->matchCollection($pathinfo, $this->routes)) { return $ret; } @@ -125,7 +126,7 @@ protected function matchCollection(string $pathinfo, RouteCollection $routes): a $method = 'GET'; } $supportsTrailingSlash = 'GET' === $method && $this instanceof RedirectableUrlMatcherInterface; - $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/'; + $trimmedPathinfo = '' === ($trimmedPathinfo = rtrim($pathinfo, '/')) ? '/' : $trimmedPathinfo; foreach ($routes as $name => $route) { $compiledRoute = $route->compile(); diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index fcc2eda113cf2..12e09bdf13985 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -22,6 +22,17 @@ class UrlMatcherTest extends TestCase { + public function testZero() + { + $coll = new RouteCollection(); + $coll->add('index', new Route('/')); + + $matcher = $this->getUrlMatcher($coll); + + $this->expectException(ResourceNotFoundException::class); + $matcher->match('0'); + } + public function testNoMethodSoAllowed() { $coll = new RouteCollection(); From 5c79069e9931be9e3a06e44ef647d27c10b10973 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Fri, 31 Oct 2025 20:10:14 +0100 Subject: [PATCH 04/91] [ErrorHandler] Improve PHPDoc precision in SerializerErrorRenderer --- .../ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php index 3cc6b8e2b00df..9748a20167a6b 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php @@ -29,9 +29,9 @@ class SerializerErrorRenderer implements ErrorRendererInterface private bool|\Closure $debug; /** - * @param string|callable(FlattenException) $format The format as a string or a callable that should return it - * formats not supported by Request::getMimeTypes() should be given as mime types - * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it + * @param string|callable(FlattenException): string $format The format as a string or a callable that should return it + * formats not supported by Request::getMimeTypes() should be given as mime types + * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it */ public function __construct( private SerializerInterface $serializer, From 436ab1179601df9367793cab4390016e2d8930d3 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Fri, 31 Oct 2025 22:07:53 +0100 Subject: [PATCH 05/91] [JsonPath] Remove unused "nothing" property from JsonCrawler --- src/Symfony/Component/JsonPath/JsonCrawler.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/JsonPath/JsonCrawler.php b/src/Symfony/Component/JsonPath/JsonCrawler.php index 8c47cfdae5e4e..cd0aaf963b484 100644 --- a/src/Symfony/Component/JsonPath/JsonCrawler.php +++ b/src/Symfony/Component/JsonPath/JsonCrawler.php @@ -32,8 +32,6 @@ */ final class JsonCrawler implements JsonCrawlerInterface { - private static \stdClass $nothing; - private const RFC9535_FUNCTIONS = [ 'length' => true, 'count' => true, @@ -833,8 +831,8 @@ private function compare(mixed $left, mixed $right, string $operator): bool private function compareEquality(mixed $left, mixed $right): bool { - $leftIsNothing = $left === Nothing::Nothing; - $rightIsNothing = $right === Nothing::Nothing; + $leftIsNothing = Nothing::Nothing === $left; + $rightIsNothing = Nothing::Nothing === $right; if ( $leftIsNothing && $rightIsNothing From 8326390b31a6a9ae3447b20600e2f2cac868732f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 2 Nov 2025 10:12:28 +0100 Subject: [PATCH 06/91] Update CHANGELOG for 7.4.0-BETA2 --- CHANGELOG-7.4.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG-7.4.md b/CHANGELOG-7.4.md index dc41d50350174..aff44ff9cfb22 100644 --- a/CHANGELOG-7.4.md +++ b/CHANGELOG-7.4.md @@ -7,6 +7,32 @@ in 7.4 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/v7.4.0...v7.4.1 +* 7.4.0-BETA2 (2025-11-02) + + * feature #62270 [Lock][DynamoDB] Allow symfony/lock 8.0 (DavidPrevot) + * bug #62267 [Config] Use the empty string instead of null as an array offset (santysisi) + * feature #62230 [Messenger] Support signing messages per handler (nicolas-grekas) + * bug #62246 [HttpFoundation] Allow Request::setFormat() to override predefined formats (longwave) + * feature #60153 [Serializer] Allow forcing timezone in `DateTimeNormalizer` during denormalization (frankdekker) + * bug #62240 [DomCrawler] Handle malformed tags in HTML5 parser (longwave) + * bug #62242 [MonologBridge] Accept HttpExceptionInterface in HttpCodeActivationStrategy (GromNaN) + * bug #62222 [Cache] fix ext-redis 6.2.0 compatibility (xabbuh) + * bug #62211 [Workflow] State contamination due to class-based setter cache (nicolas-grekas) + * bug #62184 [EventDispatcher][FrameworkBundle] Rework union types on `#[AsEventListener]` (HypeMC) + * bug #62199 [Workflow] State Contamination in Marking Stores due to Class-based Getter Cache (siganushka) + * bug #62197 [Validator] Fix call to undefined getParser() in YamlValidator (yoeunes) + * bug #62203 [Mailer] Fix contentId assignment for inline attachments (webflo) + * bug #62201 [HtmlSanitizer] Remove `srcdoc` from allowed attributes (Spomky) + * feature #62190 [Runtime] Expose `project_dir` as `APP_PROJECT_DIR` env var (nicolas-grekas) + * bug #62191 [FrameworkBundle] Change HttpCache directory to use new getShareDir (alexander-schranz) + * bug #62169 [HttpClient] Fix caching client decorating scoping client (pierredup) + * bug #62185 [FrameworkBundle] Add missing `container.excluded` to `serializer.attribute_metadata` (HypeMC) + * bug #62186 [DomCrawler] Fix converting HTML5 trees to DOM nodes (nicolas-grekas) + * bug #62182 [VarDumper] Fix dumping CurlHttpClient instances (nicolas-grekas) + * bug #62180 [DomCrawler] Properly ignore errors when using the native HTML5 parser (nicolas-grekas) + * bug #62178 [FrameworkBundle] Expose share directory in AboutCommand (ro0NL) + * bug #62167 [Lock][DynamoDB] Enable `http_client` injection and fix tests using AmpV5 client (GromNaN) + * 7.4.0-BETA1 (2025-10-27) * feature #62170 [HttpKernel] Add `KernelInterface::getShareDir()`, `APP_SHARE_DIR` and `kernel.share_dir` (nicolas-grekas) From 342204ffd3a5509482b104622202a39a12ebf12f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 2 Nov 2025 10:12:33 +0100 Subject: [PATCH 07/91] Update VERSION for 7.4.0-BETA2 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 0420885a5919a..e3e2676f30bad 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '7.4.0-DEV'; + public const VERSION = '7.4.0-BETA2'; public const VERSION_ID = 70400; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = 'BETA2'; public const END_OF_MAINTENANCE = '11/2028'; public const END_OF_LIFE = '11/2029'; From bb4878fa846544775f6109ff8a9086206c352217 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 2 Nov 2025 10:29:48 +0100 Subject: [PATCH 08/91] Bump Symfony version to 7.4.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index e3e2676f30bad..0420885a5919a 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '7.4.0-BETA2'; + public const VERSION = '7.4.0-DEV'; public const VERSION_ID = 70400; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'BETA2'; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '11/2028'; public const END_OF_LIFE = '11/2029'; From 71034afca6d28e7e6bf2e0656e2dfced1c782ca5 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 2 Nov 2025 10:46:10 +0100 Subject: [PATCH 09/91] Bump Symfony version to 8.0.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index c126531d55002..765f2c731f1d2 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -71,12 +71,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '8.0.0-BETA2'; + public const VERSION = '8.0.0-DEV'; public const VERSION_ID = 80000; public const MAJOR_VERSION = 8; public const MINOR_VERSION = 0; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'BETA2'; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '07/2026'; public const END_OF_LIFE = '07/2026'; From 5fb6a8ab64eaed11f41904819cfc97512d246e3b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 2 Nov 2025 12:34:53 +0100 Subject: [PATCH 10/91] simplify test --- .../Tests/Kernel/MicroKernelTraitTest.php | 39 ++++++------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php index ac663f3753f5d..14adc2a43ade9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Kernel; +use PHPUnit\Framework\Attributes\BackupGlobals; use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; use Symfony\Bundle\FrameworkBundle\Console\Application; @@ -46,47 +47,31 @@ protected function tearDown(): void } } + #[BackupGlobals(true)] public function testGetShareDirDisabledByEnv() { - $previous = $_SERVER['APP_SHARE_DIR'] ?? null; $_SERVER['APP_SHARE_DIR'] = 'false'; - try { - $kernel = $this->kernel = new ConcreteMicroKernel('test', false); + $kernel = $this->kernel = new ConcreteMicroKernel('test', false); - $this->assertNull($kernel->getShareDir()); + $this->assertNull($kernel->getShareDir()); - $parameters = $kernel->getKernelParameters(); - $this->assertArrayNotHasKey('kernel.share_dir', $parameters); - } finally { - if (null === $previous) { - unset($_SERVER['APP_SHARE_DIR']); - } else { - $_SERVER['APP_SHARE_DIR'] = $previous; - } - } + $parameters = $kernel->getKernelParameters(); + $this->assertArrayNotHasKey('kernel.share_dir', $parameters); } + #[BackupGlobals(true)] public function testGetShareDirCustomPathFromEnv() { - $previous = $_SERVER['APP_SHARE_DIR'] ?? null; $_SERVER['APP_SHARE_DIR'] = sys_get_temp_dir(); - try { - $kernel = $this->kernel = new ConcreteMicroKernel('test', false); + $kernel = $this->kernel = new ConcreteMicroKernel('test', false); - $expected = rtrim(sys_get_temp_dir(), '/').'/test'; - $this->assertSame($expected, $kernel->getShareDir()); + $expected = rtrim(sys_get_temp_dir(), '/').'/test'; + $this->assertSame($expected, $kernel->getShareDir()); - $parameters = $kernel->getKernelParameters(); - $this->assertSame($expected, $parameters['kernel.share_dir'] ?? null); - } finally { - if (null === $previous) { - unset($_SERVER['APP_SHARE_DIR']); - } else { - $_SERVER['APP_SHARE_DIR'] = $previous; - } - } + $parameters = $kernel->getKernelParameters(); + $this->assertSame($expected, $parameters['kernel.share_dir'] ?? null); } public function test() From e4ff7976b4fd00f8a8b08b38846b2970353c95a0 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 2 Nov 2025 12:49:59 +0100 Subject: [PATCH 11/91] fix test on Windows --- .../FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php index 14adc2a43ade9..c57594f4b2c33 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Kernel/MicroKernelTraitTest.php @@ -71,7 +71,9 @@ public function testGetShareDirCustomPathFromEnv() $this->assertSame($expected, $kernel->getShareDir()); $parameters = $kernel->getKernelParameters(); - $this->assertSame($expected, $parameters['kernel.share_dir'] ?? null); + $this->assertArrayHasKey('kernel.share_dir', $parameters); + $this->assertNotNull($parameters['kernel.share_dir']); + $this->assertSame(realpath($expected), realpath($parameters['kernel.share_dir'])); } public function test() From 037ce90fe53c80be30f1c89cf60761a391d0529b Mon Sep 17 00:00:00 2001 From: Santiago San Martin Date: Sun, 2 Nov 2025 15:11:54 -0300 Subject: [PATCH 12/91] [ProxyManagerBridge] Remove comment that reference github discussion --- .../ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php index 51ae459d10f0f..7a6789d133417 100644 --- a/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php +++ b/src/Symfony/Bridge/ProxyManager/Tests/LazyProxy/PhpDumper/ProxyDumperTest.php @@ -142,7 +142,6 @@ protected function createProxy(\$class, \Closure \$factory) $implem = preg_replace('#\n /\*\*.*?\*/#s', '', $implem); $implem = str_replace("array(\n \n );", "[\n \n ];", $implem); - // https://github.com/symfony/symfony/pull/62269#issuecomment-3476843062 $implem = str_replace('() : bool', '(): bool', $implem); $this->assertStringMatchesFormatFile(__DIR__.'/Fixtures/proxy-implem.php', $implem); From fb158becd0d6db1a5b689dfd11102df18b8d0fe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Pr=C3=A9vot?= Date: Mon, 3 Nov 2025 08:29:56 +0100 Subject: [PATCH 13/91] [Serializer] Use Asia/Tokyo instead of Japan in tests --- .../Tests/Normalizer/DateTimeNormalizerTest.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php index 52cb04833e3ec..c3673c7ffe3dc 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/DateTimeNormalizerTest.php @@ -303,13 +303,13 @@ public function testDenormalizeUsingPreserveContextTimezoneAndFormatPassedInCons { $normalizer = new DateTimeNormalizer( [ - DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('Japan'), + DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('Asia/Tokyo'), DateTimeNormalizer::FORMAT_KEY => 'Y-m-d\\TH:i:sO', DateTimeNormalizer::FORCE_TIMEZONE_KEY => true, ] ); $actual = $normalizer->denormalize('2016-12-01T12:34:56+0000', \DateTimeInterface::class); - $this->assertEquals(new \DateTimeZone('Japan'), $actual->getTimezone()); + $this->assertEquals(new \DateTimeZone('Asia/Tokyo'), $actual->getTimezone()); } public function testDenormalizeUsingPreserveContextTimezoneAndFormatPassedInContext() @@ -319,12 +319,12 @@ public function testDenormalizeUsingPreserveContextTimezoneAndFormatPassedInCont \DateTimeInterface::class, null, [ - DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('Japan'), + DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('Asia/Tokyo'), DateTimeNormalizer::FORMAT_KEY => 'Y-m-d\\TH:i:sO', DateTimeNormalizer::FORCE_TIMEZONE_KEY => true, ] ); - $this->assertEquals(new \DateTimeZone('Japan'), $actual->getTimezone()); + $this->assertEquals(new \DateTimeZone('Asia/Tokyo'), $actual->getTimezone()); } public function testDenormalizeUsingPreserveContextTimezoneWithoutFormat() @@ -334,11 +334,11 @@ public function testDenormalizeUsingPreserveContextTimezoneWithoutFormat() \DateTimeInterface::class, null, [ - DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('Japan'), + DateTimeNormalizer::TIMEZONE_KEY => new \DateTimeZone('Asia/Tokyo'), DateTimeNormalizer::FORCE_TIMEZONE_KEY => true, ] ); - $this->assertEquals(new \DateTimeZone('Japan'), $actual->getTimezone()); + $this->assertEquals(new \DateTimeZone('Asia/Tokyo'), $actual->getTimezone()); } public function testDenormalizeUsingPreserveContextShouldBeIgnoredWithoutTimezoneInContext() From ccdf3cbe64afca44f7341e51bc41d1d6cfa52003 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 3 Nov 2025 09:42:57 +0100 Subject: [PATCH 14/91] [HttpClient] Reject 3xx pushed responses --- src/Symfony/Component/HttpClient/CurlHttpClient.php | 4 +++- src/Symfony/Component/HttpClient/Response/CurlResponse.php | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index b3d6f2adb8ae4..3461119744c3d 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -378,7 +378,9 @@ private static function acceptPushForRequest(string $method, array $options, Pus } } - return true; + $statusCode = $pushedResponse->response->getInfo('http_code') ?: 200; + + return $statusCode < 300 || 400 <= $statusCode; } /** diff --git a/src/Symfony/Component/HttpClient/Response/CurlResponse.php b/src/Symfony/Component/HttpClient/Response/CurlResponse.php index 7e95bbcfb9a1c..0c8a5ef146ae2 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -396,7 +396,7 @@ private static function parseHeaderLine($ch, string $data, array &$info, array & $info['peer_certificate_chain'] = array_map('openssl_x509_read', array_column($certinfo, 'Cert')); } - if (300 <= $info['http_code'] && $info['http_code'] < 400) { + if (300 <= $info['http_code'] && $info['http_code'] < 400 && null !== $options) { if (curl_getinfo($ch, \CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) { curl_setopt($ch, \CURLOPT_FOLLOWLOCATION, false); } elseif (303 === $info['http_code'] || ('POST' === $info['http_method'] && \in_array($info['http_code'], [301, 302], true))) { @@ -418,7 +418,7 @@ private static function parseHeaderLine($ch, string $data, array &$info, array & $info['redirect_url'] = null; - if (300 <= $statusCode && $statusCode < 400 && null !== $location) { + if (300 <= $statusCode && $statusCode < 400 && null !== $location && null !== $options) { if ($noContent = 303 === $statusCode || ('POST' === $info['http_method'] && \in_array($statusCode, [301, 302], true))) { $info['http_method'] = 'HEAD' === $info['http_method'] ? 'HEAD' : 'GET'; curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, $info['http_method']); @@ -433,7 +433,7 @@ private static function parseHeaderLine($ch, string $data, array &$info, array & if (401 === $statusCode && isset($options['auth_ntlm']) && 0 === strncasecmp($headers['www-authenticate'][0] ?? '', 'NTLM ', 5)) { // Continue with NTLM auth - } elseif ($statusCode < 300 || 400 <= $statusCode || null === $location || curl_getinfo($ch, \CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) { + } elseif ($statusCode < 300 || 400 <= $statusCode || null === $location || null === $options || curl_getinfo($ch, \CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) { // Headers and redirects completed, time to get the response's content $multi->handlesActivity[$id][] = new FirstChunk(); From aca2c4c299f5e4fc0af1818fbfc249e8a0779fd6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 3 Nov 2025 09:52:05 +0100 Subject: [PATCH 15/91] fix merge --- .../Tests/DependencyInjection/PhpFrameworkExtensionTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php index 08e96eb6a75b1..815d1e1068c91 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php @@ -377,10 +377,6 @@ public function testMessengerSigningSerializerWiring() ->addTag('messenger.message_handler', ['handles' => DummyMessage::class, 'sign' => true]); $container->loadFromExtension('framework', [ - 'annotations' => false, - 'http_method_override' => false, - 'handle_all_throwables' => true, - 'php_errors' => ['log' => true], 'messenger' => [ 'transports' => [ 'async' => ['dsn' => 'in-memory://'], From d80d1858e199ba0262f5e8ade5dac5aaf96abe84 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Fri, 31 Oct 2025 23:33:28 +0100 Subject: [PATCH 16/91] [Translation] Remove an unused argument passed to parseNode() method --- .../Component/Translation/PseudoLocalizationTranslator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Translation/PseudoLocalizationTranslator.php b/src/Symfony/Component/Translation/PseudoLocalizationTranslator.php index 3632dcf98362d..1d470c4112ea0 100644 --- a/src/Symfony/Component/Translation/PseudoLocalizationTranslator.php +++ b/src/Symfony/Component/Translation/PseudoLocalizationTranslator.php @@ -183,7 +183,7 @@ private function parseNode(\DOMNode $node): array $parts[] = [false, false, '>']; - $parts = array_merge($parts, $this->parseNode($childNode, $parts)); + $parts = array_merge($parts, $this->parseNode($childNode)); $parts[] = [false, false, 'tagName.'>']; } From 8014c5dd31412db60ea44a563c998eedc3991227 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Sat, 1 Nov 2025 17:31:58 +0100 Subject: [PATCH 17/91] [Notifier] Remove unused $transportName argument in EmailChannel::notify() --- src/Symfony/Component/Notifier/Channel/EmailChannel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Notifier/Channel/EmailChannel.php b/src/Symfony/Component/Notifier/Channel/EmailChannel.php index 7d1813a22746d..16eafa2a4d564 100644 --- a/src/Symfony/Component/Notifier/Channel/EmailChannel.php +++ b/src/Symfony/Component/Notifier/Channel/EmailChannel.php @@ -54,7 +54,7 @@ public function notify(Notification $notification, RecipientInterface $recipient $message = $notification->asEmailMessage($recipient, $transportName); } - $message ??= EmailMessage::fromNotification($notification, $recipient, $transportName); + $message ??= EmailMessage::fromNotification($notification, $recipient); $email = $message->getMessage(); if ($email instanceof Email) { if (!$email->getFrom()) { From 9962b91b12bb791322fa73836b350836b6db7cac Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 31 Oct 2025 17:43:49 +0100 Subject: [PATCH 18/91] [HttpFoundation] Fix parsing pathinfo with no leading slash --- src/Symfony/Component/HttpFoundation/Request.php | 5 ++--- .../Component/HttpFoundation/Tests/RequestTest.php | 10 ++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index d1103cf8a0a57..393b5472cdd21 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -1983,9 +1983,8 @@ protected function preparePathInfo() } $pathInfo = substr($requestUri, \strlen($baseUrl)); - if (false === $pathInfo || '' === $pathInfo) { - // If substr() returns false then PATH_INFO is set to an empty string - return '/'; + if (false === $pathInfo || '' === $pathInfo || '/' !== $pathInfo[0]) { + return '/'.$pathInfo; } return $pathInfo; diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 789119b6a7c68..bb91a2da087a2 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -1876,6 +1876,16 @@ public static function getBaseUrlData() '', '/foo/api/bar', ], + [ + '/api/index.phpfoo', + [ + 'SCRIPT_FILENAME' => '/var/www/api/index.php', + 'SCRIPT_NAME' => '/api/index.php', + 'PHP_SELF' => '/api/index.php', + ], + '/api/index.php', + '/foo', + ], ]; } From 4c44765b9e2505e54411bc8f88568b325a011147 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Tue, 4 Nov 2025 01:59:30 +0100 Subject: [PATCH 19/91] [Console] Add missing VERBOSITY_SILENT case in CommandDataCollector --- .../Component/Console/DataCollector/CommandDataCollector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/Console/DataCollector/CommandDataCollector.php b/src/Symfony/Component/Console/DataCollector/CommandDataCollector.php index 6dcac66bb03ee..724af54a06ad3 100644 --- a/src/Symfony/Component/Console/DataCollector/CommandDataCollector.php +++ b/src/Symfony/Component/Console/DataCollector/CommandDataCollector.php @@ -43,6 +43,7 @@ public function collect(Request $request, Response $response, ?\Throwable $excep 'duration' => $command->duration, 'max_memory_usage' => $command->maxMemoryUsage, 'verbosity_level' => match ($command->output->getVerbosity()) { + OutputInterface::VERBOSITY_SILENT => 'silent', OutputInterface::VERBOSITY_QUIET => 'quiet', OutputInterface::VERBOSITY_NORMAL => 'normal', OutputInterface::VERBOSITY_VERBOSE => 'verbose', From 9e74a12ac062261cb54f132158dab01d3bf29945 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Tue, 4 Nov 2025 03:21:26 +0100 Subject: [PATCH 20/91] [PHPDoc] Fix various PHPDoc syntax errors --- src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php | 2 +- src/Symfony/Component/Runtime/GenericRuntime.php | 2 +- .../Component/Translation/Command/TranslationLintCommand.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php index 7bbc8dbceddee..d4f3611bbdb15 100644 --- a/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php +++ b/src/Symfony/Bridge/Doctrine/Middleware/Debug/Query.php @@ -79,7 +79,7 @@ public function getSql(): string } /** - * @return array + * @return array */ public function getParams(): array { diff --git a/src/Symfony/Component/Runtime/GenericRuntime.php b/src/Symfony/Component/Runtime/GenericRuntime.php index c6e30c06102f1..eb76f1dbf2851 100644 --- a/src/Symfony/Component/Runtime/GenericRuntime.php +++ b/src/Symfony/Component/Runtime/GenericRuntime.php @@ -49,7 +49,7 @@ class GenericRuntime implements RuntimeInterface protected array $options; /** - * @param array { + * @param array{ * debug?: ?bool, * runtimes?: ?array, * error_handler?: string|false, diff --git a/src/Symfony/Component/Translation/Command/TranslationLintCommand.php b/src/Symfony/Component/Translation/Command/TranslationLintCommand.php index 612dd840fca71..e38466ea6fb0d 100644 --- a/src/Symfony/Component/Translation/Command/TranslationLintCommand.php +++ b/src/Symfony/Component/Translation/Command/TranslationLintCommand.php @@ -70,7 +70,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $locales = $input->getOption('locale'); - /** @var array> $errors */ + /** @var array>> $errors */ $errors = []; $domainsByLocales = []; From 1ff1ac394410d688eb1a4ef80b40c2db8f3638dd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Nov 2025 11:36:25 +0100 Subject: [PATCH 21/91] [DependendcyInjection] Improve shape for "from_callable" definitions --- .../DependencyInjection/Loader/Configurator/AppReference.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AppReference.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AppReference.php index 5c681856db73d..c6f5b1db67c45 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AppReference.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AppReference.php @@ -42,7 +42,7 @@ * type?: string|null, * ignore_errors?: bool, * }> - * @psalm-type ParametersConfig = array|null> + * @psalm-type ParametersConfig = array|null>|null> * @psalm-type ArgumentsType = list|array * @psalm-type CallType = array|array{0:string, 1?:ArgumentsType, 2?:bool}|array{method:string, arguments?:ArgumentsType, returns_clone?:bool} * @psalm-type TagsType = list>> // arrays inside the list must have only one element, with the tag name as the key @@ -94,7 +94,7 @@ * autoconfigure?: bool, * bind?: array, * constructor?: string, - * from_callable?: mixed, + * from_callable?: CallbackType, * } * @psalm-type AliasType = string|array{ * alias: string, From 8ceced30e9e74ccd74250fbdf795cfabb3deb32b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Nov 2025 14:42:38 +0100 Subject: [PATCH 22/91] Fix tests --- .../Bundle/FrameworkBundle/Tests/Fixtures/reference.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php index 4830b3e1f2f82..b806b93406b4d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php @@ -31,7 +31,7 @@ * type?: string|null, * ignore_errors?: bool, * }> - * @psalm-type ParametersConfig = array|null> + * @psalm-type ParametersConfig = array|null>|null> * @psalm-type ArgumentsType = list|array * @psalm-type CallType = array|array{0:string, 1?:ArgumentsType, 2?:bool}|array{method:string, arguments?:ArgumentsType, returns_clone?:bool} * @psalm-type TagsType = list>> // arrays inside the list must have only one element, with the tag name as the key @@ -83,7 +83,7 @@ * autoconfigure?: bool, * bind?: array, * constructor?: string, - * from_callable?: mixed, + * from_callable?: CallbackType, * } * @psalm-type AliasType = string|array{ * alias: string, From 13e25caae7da2b7690dc8b231ddac78bd985806e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 4 Nov 2025 14:31:53 +0100 Subject: [PATCH 23/91] [Routing] Simplify importing routes defined on controller services --- .../Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php | 6 ++---- .../Bundle/FrameworkBundle/Tests/Fixtures/reference.php | 3 +-- .../Component/Routing/Loader/AttributeServicesLoader.php | 2 +- .../Routing/Loader/Configurator/RoutesReference.php | 3 +-- .../Routing/Tests/Loader/AttributeServicesLoaderTest.php | 6 +++--- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php index 70bc3f28d624d..16f723c7a6cff 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php @@ -79,15 +79,13 @@ private function configureRoutes(RoutingConfigurator $routes): void $routes->import($configDir.'/{routes}/'.$this->environment.'/*.{php,yaml}'); $routes->import($configDir.'/{routes}/*.{php,yaml}'); + $routes->import('routing.controllers'); + if (is_file($this->getConfigDir().'/routes.yaml')) { $routes->import($configDir.'/routes.yaml'); } else { $routes->import($configDir.'/{routes}.php'); } - - if ($fileName = (new \ReflectionObject($this))->getFileName()) { - $routes->import($fileName, 'attribute'); - } } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php index b806b93406b4d..4336766314e1e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php @@ -184,8 +184,7 @@ public static function config(array $config): array * * return Routes::config([ * 'controllers' => [ - * 'resource' => 'attributes', - * 'type' => 'tagged_services', + * 'resource' => 'routing.controllers', * ], * ]); * ``` diff --git a/src/Symfony/Component/Routing/Loader/AttributeServicesLoader.php b/src/Symfony/Component/Routing/Loader/AttributeServicesLoader.php index a3c8f493e37a7..b2eea81887aaf 100644 --- a/src/Symfony/Component/Routing/Loader/AttributeServicesLoader.php +++ b/src/Symfony/Component/Routing/Loader/AttributeServicesLoader.php @@ -42,6 +42,6 @@ public function load(mixed $resource, ?string $type = null): RouteCollection public function supports(mixed $resource, ?string $type = null): bool { - return 'tagged_services' === $type && 'attributes' === $resource; + return 'routing.controllers' === $resource; } } diff --git a/src/Symfony/Component/Routing/Loader/Configurator/RoutesReference.php b/src/Symfony/Component/Routing/Loader/Configurator/RoutesReference.php index 3d82d05b9d37f..4a48ffc9a3166 100644 --- a/src/Symfony/Component/Routing/Loader/Configurator/RoutesReference.php +++ b/src/Symfony/Component/Routing/Loader/Configurator/RoutesReference.php @@ -26,8 +26,7 @@ * * return Routes::config([ * 'controllers' => [ - * 'resource' => 'attributes', - * 'type' => 'tagged_services', + * 'resource' => 'routing.controllers', * ], * ]); * ``` diff --git a/src/Symfony/Component/Routing/Tests/Loader/AttributeServicesLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AttributeServicesLoaderTest.php index 31d63ec708dd5..90591b0d0dc00 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AttributeServicesLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AttributeServicesLoaderTest.php @@ -26,8 +26,8 @@ public function testSupports() $this->assertFalse($loader->supports('attributes', null)); $this->assertFalse($loader->supports('attributes', 'attribute')); - $this->assertFalse($loader->supports('other', 'tagged_services')); - $this->assertTrue($loader->supports('attributes', 'tagged_services')); + $this->assertFalse($loader->supports('other', 'routing.controllers')); + $this->assertTrue($loader->supports('routing.controllers')); } public function testDelegatesToAttributeLoaderAndMergesCollections() @@ -47,7 +47,7 @@ public function testDelegatesToAttributeLoaderAndMergesCollections() $attributeLoader->setResolver($resolver); $servicesLoader->setResolver($resolver); - $collection = $servicesLoader->load('attributes', 'tagged_services'); + $collection = $servicesLoader->load('routing.controllers'); $this->assertArrayHasKey('action', $collection->all()); $this->assertArrayHasKey('put', $collection->all()); From 39c70bb429d9e7815b39b1e8967da4826f4fa119 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Tue, 4 Nov 2025 10:31:36 +0100 Subject: [PATCH 24/91] [Twig] Ensure WrappedTemplatedEmail::getReturnPath() returns a string --- .../Twig/Mime/WrappedTemplatedEmail.php | 2 +- .../Tests/Mime/WrappedTemplatedEmailTest.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php b/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php index 1feedc20370bb..b74769346c56b 100644 --- a/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php +++ b/src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php @@ -92,7 +92,7 @@ public function setReturnPath(string $address): static public function getReturnPath(): string { - return $this->message->getReturnPath(); + return $this->message->getReturnPath()?->toString() ?? ''; } /** diff --git a/src/Symfony/Bridge/Twig/Tests/Mime/WrappedTemplatedEmailTest.php b/src/Symfony/Bridge/Twig/Tests/Mime/WrappedTemplatedEmailTest.php index 428ebc93dc4ab..74a9acd6e2b83 100644 --- a/src/Symfony/Bridge/Twig/Tests/Mime/WrappedTemplatedEmailTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Mime/WrappedTemplatedEmailTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bridge\Twig\Mime\BodyRenderer; use Symfony\Bridge\Twig\Mime\TemplatedEmail; +use Symfony\Bridge\Twig\Mime\WrappedTemplatedEmail; use Twig\Environment; use Twig\Loader\FilesystemLoader; @@ -99,4 +100,22 @@ private function buildEmail(string $template): TemplatedEmail return $email; } + + public function testGetReturnPathWhenNull() + { + $twig = $this->createMock(Environment::class); + $message = new TemplatedEmail(); + $email = new WrappedTemplatedEmail($twig, $message); + + $this->assertSame('', $email->getReturnPath()); + } + + public function testGetReturnPathWhenSet() + { + $twig = $this->createMock(Environment::class); + $message = (new TemplatedEmail())->returnPath('test@example.com'); + $email = new WrappedTemplatedEmail($twig, $message); + + $this->assertSame('test@example.com', $email->getReturnPath()); + } } From b760e9019d0379af47dc0760966927ea5cd25d27 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Wed, 5 Nov 2025 05:18:57 +0100 Subject: [PATCH 25/91] [Cache] Remove unset call on undefined variable in PhpArrayAdapter --- src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php index 39678032624f6..cb446ba73f535 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php @@ -324,7 +324,7 @@ public function warmUp(array $values): array file_put_contents($tmpFile, $dump); @chmod($tmpFile, 0o666 & ~umask()); - unset($serialized, $value, $dump); + unset($value, $dump); @rename($tmpFile, $this->file); unset(self::$valuesCache[$this->file]); From 1b5947dd133f1e23fdc435b8690283451d4053bc Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Sat, 1 Nov 2025 20:26:01 +0100 Subject: [PATCH 26/91] [Finder] Make method calls explicit in ExcludeDirectoryFilterIterator --- .../Finder/Iterator/ExcludeDirectoryFilterIterator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php b/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php index ebbc76ec7bc46..88be2407602d8 100644 --- a/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php +++ b/src/Symfony/Component/Finder/Iterator/ExcludeDirectoryFilterIterator.php @@ -72,12 +72,12 @@ public function __construct(\Iterator $iterator, array $directories) */ public function accept(): bool { - if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) { + if ($this->isRecursive && isset($this->excludedDirs[$this->current()->getFilename()]) && $this->current()->isDir()) { return false; } if ($this->excludedPattern) { - $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath(); + $path = $this->current()->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath(); $path = str_replace('\\', '/', $path); return !preg_match($this->excludedPattern, $path); From 51dc311f0c0e006780f40477ed5c90374039a901 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 5 Nov 2025 08:22:59 +0100 Subject: [PATCH 27/91] [DependencyInjection] Include return type in AppReference shape --- .../Compiler/PhpConfigReferenceDumpPass.php | 23 +++---- .../Tests/Fixtures/reference.php | 63 ++++++++++--------- .../Loader/Configurator/AppReference.php | 25 ++++---- 3 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PhpConfigReferenceDumpPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PhpConfigReferenceDumpPass.php index 4ebdf603e47c8..d7bc0ad2ceb49 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PhpConfigReferenceDumpPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PhpConfigReferenceDumpPass.php @@ -60,11 +60,11 @@ public static function config(array $config): array private const WHEN_ENV_APP_TEMPLATE = <<<'PHPDOC' - * "when@{ENV}"?: array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig,{SHAPE} - * }, + * "when@{ENV}"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig,{SHAPE} + * }, PHPDOC; private const ROUTES_TYPES_TEMPLATE = <<<'PHPDOC' @@ -121,23 +121,24 @@ public function process(ContainerBuilder $container): void $r = new \ReflectionClass(AppReference::class); - if (false === $i = strpos($phpdoc = $r->getDocComment(), "\n */")) { + if (false === $i = strpos($phpdoc = $r->getDocComment(), "\n * @psalm-type ConfigType = ")) { throw new \LogicException(\sprintf('Cannot insert config shape in "%s".', AppReference::class)); } $appTypes = substr_replace($phpdoc, $appTypes, $i, 0); - if (false === $i = strpos($phpdoc = $r->getMethod('config')->getDocComment(), "\n * ...getShapeForExtensions($anyEnvExtensions, $container), $i, 0); - $i += \strlen($appParam) - \strlen($phpdoc); + $appTypes = substr_replace($phpdoc, $this->getShapeForExtensions($anyEnvExtensions, $container), $i, 0); + $i += \strlen($appTypes) - \strlen($phpdoc); foreach ($extensionsPerEnv as $env => $extensions) { - $appParam = substr_replace($appParam, strtr(self::WHEN_ENV_APP_TEMPLATE, [ + $appTypes = substr_replace($appTypes, strtr(self::WHEN_ENV_APP_TEMPLATE, [ '{ENV}' => $env, '{SHAPE}' => $this->getShapeForExtensions($extensions, $container, ' '), ]), $i, 0); } + $appParam = $r->getMethod('config')->getDocComment(); $r = new \ReflectionClass(RoutesReference::class); @@ -191,7 +192,7 @@ private function getShapeForExtensions(array $extensions, ContainerBuilder $cont foreach ($extensions as $extension) { if ($this->getConfiguration($extension, $container)) { $type = $this->camelCase($extension->getAlias()).'Config'; - $shape .= \sprintf("\n * %s%s?: %s,", $indent, $extension->getAlias(), $type); + $shape .= \sprintf("\n * %s%s?: %s,", $indent, $extension->getAlias(), $type); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php index b806b93406b4d..f7f0de56e04a2 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/reference.php @@ -130,40 +130,43 @@ * count?: int, * }, * } + * @psalm-type ConfigType = array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * test?: TestConfig, + * "when@dev"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * test?: TestConfig, + * }, + * "when@prod"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * test?: TestConfig, + * }, + * "when@test"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * test?: TestConfig, + * }, + * ..., + * }> + * } */ final class App extends AppReference { /** - * @param array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig, - * test?: TestConfig, - * "when@dev"?: array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig, - * test?: TestConfig, - * }, - * "when@prod"?: array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig, - * test?: TestConfig, - * }, - * "when@test"?: array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig, - * test?: TestConfig, - * }, - * ..., - * }> - * } $config + * @param ConfigType $config + * + * @psalm-return ConfigType */ public static function config(array $config): array { diff --git a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AppReference.php b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AppReference.php index c6f5b1db67c45..4dd51a8363829 100644 --- a/src/Symfony/Component/DependencyInjection/Loader/Configurator/AppReference.php +++ b/src/Symfony/Component/DependencyInjection/Loader/Configurator/AppReference.php @@ -134,21 +134,24 @@ * ... * } * @psalm-type ExtensionType = array + * @psalm-type ConfigType = array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * ..., + * }> + * } */ class AppReference { /** - * @param array{ - * imports?: ImportsConfig, - * parameters?: ParametersConfig, - * services?: ServicesConfig, - * ..., - * }> - * } $config + * @param ConfigType $config + * + * @psalm-return ConfigType */ public static function config(array $config): array { From 6c9cc877dc4c621e285078ab9b31aefc6ef87166 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Mon, 3 Nov 2025 01:27:25 +0100 Subject: [PATCH 28/91] [Filesystem] Unify logic for isAbsolute() in Path --- .../Component/Filesystem/Filesystem.php | 10 +---- src/Symfony/Component/Filesystem/Path.php | 40 +++++-------------- .../Component/Filesystem/Tests/PathTest.php | 29 +++++++++++--- 3 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index bfe3878030b36..d08d99ddab881 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -576,17 +576,11 @@ public function mirror(string $originDir, string $targetDir, ?\Traversable $iter } /** - * Returns whether the file path is an absolute path. + * Returns whether the given path is absolute. */ public function isAbsolutePath(string $file): bool { - return '' !== $file && (strspn($file, '/\\', 0, 1) - || (\strlen($file) > 3 && ctype_alpha($file[0]) - && ':' === $file[1] - && strspn($file, '/\\', 2, 1) - ) - || null !== parse_url($file, \PHP_URL_SCHEME) - ); + return Path::isAbsolute($file); } /** diff --git a/src/Symfony/Component/Filesystem/Path.php b/src/Symfony/Component/Filesystem/Path.php index 2f2e879032899..ecf313580365d 100644 --- a/src/Symfony/Component/Filesystem/Path.php +++ b/src/Symfony/Component/Filesystem/Path.php @@ -358,38 +358,18 @@ public static function changeExtension(string $path, string $extension): string return substr($path, 0, -\strlen($actualExtension)).$extension; } + /** + * Returns whether the given path is absolute. + */ public static function isAbsolute(string $path): bool { - if ('' === $path) { - return false; - } - - // Strip scheme - if (false !== ($schemeSeparatorPosition = strpos($path, '://')) && 1 !== $schemeSeparatorPosition) { - $path = substr($path, $schemeSeparatorPosition + 3); - } - - $firstCharacter = $path[0]; - - // UNIX root "/" or "\" (Windows style) - if ('/' === $firstCharacter || '\\' === $firstCharacter) { - return true; - } - - // Windows root - if (\strlen($path) > 1 && ctype_alpha($firstCharacter) && ':' === $path[1]) { - // Special case: "C:" - if (2 === \strlen($path)) { - return true; - } - - // Normal case: "C:/ or "C:\" - if ('/' === $path[2] || '\\' === $path[2]) { - return true; - } - } - - return false; + return '' !== $path && (strspn($path, '/\\', 0, 1) + || (\strlen($path) > 3 && ctype_alpha($path[0]) + && ':' === $path[1] + && strspn($path, '/\\', 2, 1) + ) + || null !== parse_url($path, \PHP_URL_SCHEME) + ); } public static function isRelative(string $path): bool diff --git a/src/Symfony/Component/Filesystem/Tests/PathTest.php b/src/Symfony/Component/Filesystem/Tests/PathTest.php index 285d55f0d3345..f04a0376a3727 100644 --- a/src/Symfony/Component/Filesystem/Tests/PathTest.php +++ b/src/Symfony/Component/Filesystem/Tests/PathTest.php @@ -364,31 +364,50 @@ public function testChangeExtension(string $path, string $extension, string $pat public static function provideIsAbsolutePathTests(): \Generator { + // UNIX-style absolute paths yield ['/css/style.css', true]; yield ['/', true]; yield ['css/style.css', false]; yield ['', false]; + // UNIX-style absolute paths with backslashes yield ['\\css\\style.css', true]; yield ['\\', true]; yield ['css\\style.css', false]; + // Windows-style absolute paths yield ['C:/css/style.css', true]; yield ['D:/', true]; yield ['C:///windows', true]; yield ['C://test', true]; + // Windows-style absolute paths with backslashes yield ['E:\\css\\style.css', true]; yield ['F:\\', true]; + // Windows special case (drive only) + yield ['C:', true]; + + // URLs and stream wrappers are considered absolute yield ['phar:///css/style.css', true]; yield ['phar:///', true]; + yield ['http://example.com', true]; + yield ['ftp://user@server/path', true]; + yield ['vfs://root/file.txt', true]; + + // "C:" without a slash is treated as a scheme by parse_url() + yield ['C:css/style.css', true]; + + // Relative paths + yield ['/var/lib', true]; + yield ['c:\\\\var\\lib', true]; // c:\\var\lib + yield ['\\var\\lib', true]; + yield ['var/lib', false]; + yield ['../var/lib', false]; + yield ['', false]; - // Windows special case - yield ['C:', true]; - - // Not considered absolute - yield ['C:css/style.css', false]; + // Empty path + yield ['', false]; } /** From 83f086d0b578d3d668395945a0f280eeb2713cfd Mon Sep 17 00:00:00 2001 From: soyuka Date: Fri, 31 Oct 2025 17:17:47 +0100 Subject: [PATCH 29/91] [ObjectMapper] lazy loading --- .../Component/ObjectMapper/ObjectMapper.php | 47 +++++++++++------ .../Fixtures/DefaultLazy/OrderSource.php | 12 +++++ .../Fixtures/DefaultLazy/OrderTarget.php | 9 ++++ .../Tests/Fixtures/DefaultLazy/UserSource.php | 11 ++++ .../Tests/Fixtures/DefaultLazy/UserTarget.php | 8 +++ .../ObjectMapper/Tests/ObjectMapperTest.php | 52 ++++++++++++++----- 6 files changed, 110 insertions(+), 29 deletions(-) create mode 100644 src/Symfony/Component/ObjectMapper/Tests/Fixtures/DefaultLazy/OrderSource.php create mode 100644 src/Symfony/Component/ObjectMapper/Tests/Fixtures/DefaultLazy/OrderTarget.php create mode 100644 src/Symfony/Component/ObjectMapper/Tests/Fixtures/DefaultLazy/UserSource.php create mode 100644 src/Symfony/Component/ObjectMapper/Tests/Fixtures/DefaultLazy/UserTarget.php diff --git a/src/Symfony/Component/ObjectMapper/ObjectMapper.php b/src/Symfony/Component/ObjectMapper/ObjectMapper.php index f4f5c6d20efd6..bb14c9e7191b1 100644 --- a/src/Symfony/Component/ObjectMapper/ObjectMapper.php +++ b/src/Symfony/Component/ObjectMapper/ObjectMapper.php @@ -32,7 +32,7 @@ final class ObjectMapper implements ObjectMapperInterface, ObjectMapperAwareInte /** * Tracks recursive references. */ - private ?\SplObjectStorage $objectMap = null; + private ?\WeakMap $objectMap = null; public function __construct( private readonly ObjectMapperMetadataFactoryInterface $metadataFactory = new ReflectionObjectMapperMetadataFactory(), @@ -45,12 +45,20 @@ public function __construct( public function map(object $source, object|string|null $target = null): object { - $objectMapInitialized = false; - if (null === $this->objectMap) { - $this->objectMap = new \SplObjectStorage(); - $objectMapInitialized = true; + if ($this->objectMap) { + return $this->doMap($source, $target, $this->objectMap, false); } + $this->objectMap = new \WeakMap(); + try { + return $this->doMap($source, $target, $this->objectMap, true); + } finally { + $this->objectMap = null; + } + } + + private function doMap(object $source, object|string|null $target, \WeakMap $objectMap, bool $rootCall): object + { $metadata = $this->metadataFactory->create($source); $map = $this->getMapTarget($metadata, null, $source, null); $target ??= $map?->target; @@ -89,7 +97,7 @@ public function map(object $source, object|string|null $target = null): object throw new MappingException(\sprintf('Expected the mapped object to be an instance of "%s" but got "%s".', $targetRefl->getName(), get_debug_type($mappedTarget))); } - $this->objectMap[$source] = $mappedTarget; + $objectMap[$source] = $mappedTarget; $ctorArguments = []; $targetConstructor = $targetRefl->getConstructor(); foreach ($targetConstructor?->getParameters() ?? [] as $parameter) { @@ -146,7 +154,7 @@ public function map(object $source, object|string|null $target = null): object continue; } - $value = $this->getSourceValue($source, $mappedTarget, $value, $this->objectMap, $mapping); + $value = $this->getSourceValue($source, $mappedTarget, $value, $objectMap, $mapping); $this->storeValue($targetPropertyName, $mapToProperties, $ctorArguments, $value); } @@ -156,12 +164,12 @@ public function map(object $source, object|string|null $target = null): object continue; } - $value = $this->getSourceValue($source, $mappedTarget, $this->getRawValue($source, $propertyName), $this->objectMap); + $value = $this->getSourceValue($source, $mappedTarget, $this->getRawValue($source, $propertyName), $objectMap); $this->storeValue($propertyName, $mapToProperties, $ctorArguments, $value); } } - if (!$mappingToObject && !$map?->transform && $targetConstructor) { + if ((!$mappingToObject || !$rootCall) && !$map?->transform && $targetConstructor) { try { $mappedTarget->__construct(...$ctorArguments); } catch (\ReflectionException $e) { @@ -181,10 +189,6 @@ public function map(object $source, object|string|null $target = null): object $this->propertyAccessor ? $this->propertyAccessor->setValue($mappedTarget, $property, $value) : ($mappedTarget->{$property} = $value); } - if ($objectMapInitialized) { - $this->objectMap = null; - } - return $mappedTarget; } @@ -218,7 +222,7 @@ private function getRawValue(object $source, string $propertyName): mixed return $source->{$propertyName}; } - private function getSourceValue(object $source, object $target, mixed $value, \SplObjectStorage $objectMap, ?Mapping $mapping = null): mixed + private function getSourceValue(object $source, object $target, mixed $value, \WeakMap $objectMap, ?Mapping $mapping = null): mixed { if ($mapping?->transform) { $value = $this->applyTransforms($mapping, $value, $source, $target); @@ -236,8 +240,21 @@ private function getSourceValue(object $source, object $target, mixed $value, \S $value = $target; } elseif ($objectMap->offsetExists($value)) { $value = $objectMap[$value]; + } elseif (\PHP_VERSION_ID < 80400) { + return ($this->objectMapper ?? $this)->map($value, $mapTo->target); } else { - $value = ($this->objectMapper ?? $this)->map($value, $mapTo->target); + $refl = new \ReflectionClass($mapTo->target); + $mapper = $this->objectMapper ?? $this; + + return $refl->newLazyGhost(function ($target) use ($mapper, $value, $objectMap) { + $previousMap = $this->objectMap; + $this->objectMap = $objectMap; + try { + $objectMap[$value] = $mapper->map($value, $target); + } finally { + $this->objectMap = $previousMap; + } + }); } } diff --git a/src/Symfony/Component/ObjectMapper/Tests/Fixtures/DefaultLazy/OrderSource.php b/src/Symfony/Component/ObjectMapper/Tests/Fixtures/DefaultLazy/OrderSource.php new file mode 100644 index 0000000000000..da921c5a41627 --- /dev/null +++ b/src/Symfony/Component/ObjectMapper/Tests/Fixtures/DefaultLazy/OrderSource.php @@ -0,0 +1,12 @@ +assertEquals($expect, $mapper->map(...$args)); + $mapped = $mapper->map(...$args); + + if (\PHP_VERSION_ID >= 80400 && isset($mapped->relation) && $mapped->relation instanceof D ) { + $mapped->relation->baz; + } + + $this->assertEquals($expect, $mapped); } /** @@ -457,31 +467,27 @@ public function testDecorateObjectMapper() { $mapper = new ObjectMapper(); $myMapper = new class($mapper) implements ObjectMapperInterface { - private ?\SplObjectStorage $embededMap = null; - - public function __construct(private readonly ObjectMapperInterface $mapper) + public function __construct(private ObjectMapperInterface $mapper) { - $this->embededMap = new \SplObjectStorage(); + $this->mapper = $mapper->withObjectMapper($this); } public function map(object $source, object|string|null $target = null): object { - if (isset($this->embededMap[$source])) { - $target = $this->embededMap[$source]; - } - $mapped = $this->mapper->map($source, $target); - $this->embededMap[$source] = $mapped; + + if ($source instanceof C) { + $mapped->baz = 'got decorated'; + } return $mapped; } }; - $mapper = $mapper->withObjectMapper($myMapper); - $d = new D(baz: 'foo', bat: 'bar'); $c = new C(foo: 'foo', bar: 'bar'); $myNewD = $myMapper->map($c); + $this->assertSame('got decorated', $myNewD->baz); $a = new A(); $a->foo = 'test'; @@ -491,8 +497,8 @@ public function map(object $source, object|string|null $target = null): object $a->relation = $c; $a->relationNotMapped = $d; - $b = $mapper->map($a); - $this->assertSame($myNewD, $b->relation); + $b = $myMapper->map($a); + $this->assertSame('got decorated', $b->relation->baz); } #[DataProvider('validPartialInputProvider')] @@ -559,4 +565,22 @@ public function testTransformCollection() $this->assertEquals([new TransformCollectionD('a'), new TransformCollectionD('b')], $transformed->foo); } + + #[RequiresPhp('>=8.4')] + public function testEmbedsAreLazyLoadedByDefault() + { + $mapper = new ObjectMapper(); + $source = new OrderSource(); + $source->id = 123; + $source->user = new UserSource(); + $source->user->name = 'Test User'; + $target = $mapper->map($source, OrderTarget::class); + $this->assertInstanceOf(OrderTarget::class, $target); + $this->assertSame(123, $target->id); + $this->assertInstanceOf(UserTarget::class, $target->user); + $refl = new \ReflectionClass(UserTarget::class); + $this->assertTrue($refl->isUninitializedLazyObject($target->user)); + $this->assertSame('Test User', $target->user->name); + $this->assertFalse($refl->isUninitializedLazyObject($target->user)); + } } From 0314a1b40cc57241a5a1fa51610cb0f3b5d9bb55 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 5 Nov 2025 15:38:32 +0100 Subject: [PATCH 30/91] - --- src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php b/src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php index 620163b246295..c2d754fdd93d9 100644 --- a/src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php +++ b/src/Symfony/Component/ObjectMapper/Tests/ObjectMapperTest.php @@ -89,7 +89,7 @@ public function testMap($expect, $args, array $deps = []) $mapper = new ObjectMapper(...$deps); $mapped = $mapper->map(...$args); - if (\PHP_VERSION_ID >= 80400 && isset($mapped->relation) && $mapped->relation instanceof D ) { + if (isset($mapped->relation) && $mapped->relation instanceof D ) { $mapped->relation->baz; } @@ -564,7 +564,6 @@ public function testTransformCollection() $this->assertEquals([new TransformCollectionD('a'), new TransformCollectionD('b')], $transformed->foo); } - #[RequiresPhp('>=8.4')] public function testEmbedsAreLazyLoadedByDefault() { $mapper = new ObjectMapper(); From 0cdd2223b8d5fa0930c7f6a49f7ebda8950a4ded Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 5 Nov 2025 15:40:01 +0100 Subject: [PATCH 31/91] [ObjectMapper] Remove dead code --- src/Symfony/Component/ObjectMapper/ObjectMapper.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Symfony/Component/ObjectMapper/ObjectMapper.php b/src/Symfony/Component/ObjectMapper/ObjectMapper.php index 3f134f809bcb6..b302f62b8d763 100644 --- a/src/Symfony/Component/ObjectMapper/ObjectMapper.php +++ b/src/Symfony/Component/ObjectMapper/ObjectMapper.php @@ -240,8 +240,6 @@ private function getSourceValue(object $source, object $target, mixed $value, \W $value = $target; } elseif ($objectMap->offsetExists($value)) { $value = $objectMap[$value]; - } elseif (\PHP_VERSION_ID < 80400) { - return ($this->objectMapper ?? $this)->map($value, $mapTo->target); } else { $refl = new \ReflectionClass($mapTo->target); $mapper = $this->objectMapper ?? $this; From 7f3e591f1cf4898ae13310b09de7c146be56568c Mon Sep 17 00:00:00 2001 From: Villermen Date: Wed, 5 Nov 2025 18:00:56 +0100 Subject: [PATCH 32/91] Keep body size limit for AMP redirects --- src/Symfony/Component/HttpClient/Response/AmpResponse.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/HttpClient/Response/AmpResponse.php b/src/Symfony/Component/HttpClient/Response/AmpResponse.php index 0fb5bc0b5a408..b1a42ac6e0497 100644 --- a/src/Symfony/Component/HttpClient/Response/AmpResponse.php +++ b/src/Symfony/Component/HttpClient/Response/AmpResponse.php @@ -329,6 +329,7 @@ private static function followRedirects(Request $originRequest, AmpClientState $ $request->setTcpConnectTimeout($originRequest->getTcpConnectTimeout()); $request->setTlsHandshakeTimeout($originRequest->getTlsHandshakeTimeout()); $request->setTransferTimeout($originRequest->getTransferTimeout()); + $request->setBodySizeLimit($originRequest->getBodySizeLimit()); if (303 === $status || \in_array($status, [301, 302], true) && 'POST' === $response->getRequest()->getMethod()) { // Do like curl and browsers: turn POST to GET on 301, 302 and 303 From 990443275d910d6316a3d6406a63931d00c90201 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 5 Nov 2025 18:39:22 +0100 Subject: [PATCH 33/91] [HttpClient] Fix redirects config with amphp --- src/Symfony/Component/HttpClient/Response/AmpResponse.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/Response/AmpResponse.php b/src/Symfony/Component/HttpClient/Response/AmpResponse.php index b1a42ac6e0497..44c4de7a6ac64 100644 --- a/src/Symfony/Component/HttpClient/Response/AmpResponse.php +++ b/src/Symfony/Component/HttpClient/Response/AmpResponse.php @@ -329,7 +329,10 @@ private static function followRedirects(Request $originRequest, AmpClientState $ $request->setTcpConnectTimeout($originRequest->getTcpConnectTimeout()); $request->setTlsHandshakeTimeout($originRequest->getTlsHandshakeTimeout()); $request->setTransferTimeout($originRequest->getTransferTimeout()); - $request->setBodySizeLimit($originRequest->getBodySizeLimit()); + $request->setBodySizeLimit(0); + if (method_exists($request, 'setInactivityTimeout')) { + $request->setInactivityTimeout(0); + } if (303 === $status || \in_array($status, [301, 302], true) && 'POST' === $response->getRequest()->getMethod()) { // Do like curl and browsers: turn POST to GET on 301, 302 and 303 From a92de5c665ee59d31e1693b81eec6036fb2c258a Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Wed, 5 Nov 2025 19:15:03 +0100 Subject: [PATCH 34/91] [VarExporter] Fixes phpdoc syntax in LazyObjectRegistry --- .../Component/VarExporter/Internal/LazyObjectRegistry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/VarExporter/Internal/LazyObjectRegistry.php b/src/Symfony/Component/VarExporter/Internal/LazyObjectRegistry.php index 2d635e9b01ec6..75b182a1a5fa2 100644 --- a/src/Symfony/Component/VarExporter/Internal/LazyObjectRegistry.php +++ b/src/Symfony/Component/VarExporter/Internal/LazyObjectRegistry.php @@ -41,7 +41,7 @@ class LazyObjectRegistry public static array $classAccessors = []; /** - * @var array + * @var array */ public static array $parentGet = []; From b158a9874818d19bbb9a4b7506e766612a95857f Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Wed, 5 Nov 2025 20:07:09 +0100 Subject: [PATCH 35/91] [Clock] Removes redundant timezone check --- src/Symfony/Component/Clock/MockClock.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Symfony/Component/Clock/MockClock.php b/src/Symfony/Component/Clock/MockClock.php index 71500375638af..6e4e7a05a6712 100644 --- a/src/Symfony/Component/Clock/MockClock.php +++ b/src/Symfony/Component/Clock/MockClock.php @@ -70,12 +70,6 @@ public function withTimeZone(\DateTimeZone|string $timezone): static { if (\is_string($timezone)) { $timezone = new \DateTimeZone($timezone); - } elseif (\is_string($timezone)) { - try { - $timezone = new \DateTimeZone($timezone); - } catch (\Exception $e) { - throw new \DateInvalidTimeZoneException($e->getMessage(), $e->getCode(), $e); - } } $clone = clone $this; From 752933fe55d1d2f1941cda0ccaf022c233474419 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Wed, 5 Nov 2025 23:54:08 +0100 Subject: [PATCH 36/91] [Serializer] Fix BackedEnumNormalizer behavior with partial denormalization --- .../Normalizer/BackedEnumNormalizer.php | 20 ++++---- .../Normalizer/BackedEnumNormalizerTest.php | 26 ++++++++++ .../Serializer/Tests/SerializerTest.php | 48 +++++++++++++++++++ 3 files changed, 83 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/BackedEnumNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/BackedEnumNormalizer.php index 70f39d810a460..006de5390b523 100644 --- a/src/Symfony/Component/Serializer/Normalizer/BackedEnumNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/BackedEnumNormalizer.php @@ -57,30 +57,28 @@ public function denormalize(mixed $data, string $type, ?string $format = null, a throw new InvalidArgumentException('The data must belong to a backed enumeration.'); } - if ($context[self::ALLOW_INVALID_VALUES] ?? false) { - if (null === $data || (!\is_int($data) && !\is_string($data))) { - return null; - } + $allowInvalidValues = $context[self::ALLOW_INVALID_VALUES] ?? false; - try { - return $type::tryFrom($data); - } catch (\TypeError) { + if (null === $data || (!\is_int($data) && !\is_string($data))) { + if ($allowInvalidValues && !isset($context['not_normalizable_value_exceptions'])) { return null; } - } - if (!\is_int($data) && !\is_string($data)) { throw NotNormalizableValueException::createForUnexpectedDataType('The data is neither an integer nor a string, you should pass an integer or a string that can be parsed as an enumeration case of type '.$type.'.', $data, [Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_STRING], $context['deserialization_path'] ?? null, true); } try { return $type::from($data); - } catch (\ValueError $e) { + } catch (\ValueError|\TypeError $e) { if (isset($context['has_constructor'])) { throw new InvalidArgumentException('The data must belong to a backed enumeration of type '.$type, 0, $e); } - throw NotNormalizableValueException::createForUnexpectedDataType('The data must belong to a backed enumeration of type '.$type, $data, [$type], $context['deserialization_path'] ?? null, true, 0, $e); + if ($allowInvalidValues && !isset($context['not_normalizable_value_exceptions'])) { + return null; + } + + throw NotNormalizableValueException::createForUnexpectedDataType('The data must belong to a backed enumeration of type '.$type, $data, [Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_STRING], $context['deserialization_path'] ?? null, true, 0, $e); } } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/BackedEnumNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/BackedEnumNormalizerTest.php index 46963fe70b7bf..2f76f735ed25d 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/BackedEnumNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/BackedEnumNormalizerTest.php @@ -126,4 +126,30 @@ public function testItUsesTryFromIfContextIsPassed() $this->assertSame(StringBackedEnumDummy::GET, $this->normalizer->denormalize('GET', StringBackedEnumDummy::class, null, [BackedEnumNormalizer::ALLOW_INVALID_VALUES => true])); } + + public function testDenormalizeNullWithAllowInvalidAndCollectErrorsThrows() + { + $this->expectException(NotNormalizableValueException::class); + $this->expectExceptionMessage('The data is neither an integer nor a string'); + + $context = [ + BackedEnumNormalizer::ALLOW_INVALID_VALUES => true, + 'not_normalizable_value_exceptions' => [], // Indicate that we want to collect errors + ]; + + $this->normalizer->denormalize(null, StringBackedEnumDummy::class, null, $context); + } + + public function testDenormalizeInvalidValueWithAllowInvalidAndCollectErrorsThrows() + { + $this->expectException(NotNormalizableValueException::class); + $this->expectExceptionMessage('The data must belong to a backed enumeration of type'); + + $context = [ + BackedEnumNormalizer::ALLOW_INVALID_VALUES => true, + 'not_normalizable_value_exceptions' => [], + ]; + + $this->normalizer->denormalize('invalid-value', StringBackedEnumDummy::class, null, $context); + } } diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index b1b56e5c07aff..952ffc6cb9676 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -15,6 +15,7 @@ use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; +use Symfony\Component\PropertyInfo\Extractor\SerializerExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; use Symfony\Component\Serializer\Encoder\CsvEncoder; use Symfony\Component\Serializer\Encoder\JsonEncoder; @@ -1794,6 +1795,38 @@ public function testDeserializeObjectWithAsymmetricPropertyVisibility() $this->assertSame('one', $object->item); $this->assertSame('final', $object->type); // Value set in the constructor; must not be changed during deserialization } + + public function testPartialDenormalizationWithInvalidEnumAndAllowInvalid() + { + $factory = new ClassMetadataFactory(new AttributeLoader()); + $extractor = new PropertyInfoExtractor( + [new SerializerExtractor($factory)], + [new ReflectionExtractor()] + ); + $serializer = new Serializer( + [ + new ArrayDenormalizer(), + new BackedEnumNormalizer(), + new ObjectNormalizer($factory, null, null, $extractor), + ], + ); + + $context = [ + 'collect_denormalization_errors' => true, + 'allow_invalid_values' => true, + ]; + + try { + $serializer->denormalize(['id' => 123, 'status' => null], SerializerTestRequestDto::class, null, $context); + $this->fail('PartialDenormalizationException was not thrown.'); + } catch (PartialDenormalizationException $exception) { + $this->assertCount(1, $exception->getErrors()); + $error = $exception->getErrors()[0]; + + $this->assertSame('status', $error->getPath()); + $this->assertSame(['int', 'string'], $error->getExpectedTypes()); + } + } } class Model @@ -1969,3 +2002,18 @@ interface NormalizerAwareNormalizer extends NormalizerInterface, NormalizerAware interface DenormalizerAwareDenormalizer extends DenormalizerInterface, DenormalizerAwareInterface { } + +enum SerializerTestBackedEnum: string +{ + case PENDING = 'pending'; + case ACTIVE = 'active'; +} + +class SerializerTestRequestDto +{ + public function __construct( + public int $id, + public SerializerTestBackedEnum $status, + ) { + } +} From 8302e523862015cdcd4bdd53e2df5d3f43edea40 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 28 Sep 2025 17:28:02 +0200 Subject: [PATCH 37/91] [HttpFoundation] Fix parsing hosts and schemes in URLs --- .../Component/HttpFoundation/Request.php | 34 +++++++- .../HttpFoundation/Tests/RequestTest.php | 79 ++++++++++++++++--- .../EventListener/RouterListenerTest.php | 3 +- 3 files changed, 99 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index ab8f5b081f61b..5710507e628ee 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -351,10 +351,21 @@ public static function create(string $uri, string $method = 'GET', array $parame $server['PATH_INFO'] = ''; $server['REQUEST_METHOD'] = strtoupper($method); + if (($i = strcspn($uri, ':/?#')) && ':' === ($uri[$i] ?? null) && (strspn($uri, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+-.') !== $i || strcspn($uri, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'))) { + throw new BadRequestException('Invalid URI: Scheme is malformed.'); + } if (false === $components = parse_url(\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#')) { throw new BadRequestException('Invalid URI.'); } + $part = ($components['user'] ?? '').':'.($components['pass'] ?? ''); + + if (':' !== $part && \strlen($part) !== strcspn($part, '[]')) { + throw new BadRequestException('Invalid URI: Userinfo is malformed.'); + } + if (($part = $components['host'] ?? '') && !self::isHostValid($part)) { + throw new BadRequestException('Invalid URI: Host is malformed.'); + } if (false !== ($i = strpos($uri, '\\')) && $i < strcspn($uri, '?#')) { throw new BadRequestException('Invalid URI: A URI cannot contain a backslash.'); } @@ -1151,10 +1162,8 @@ public function getHost(): string // host is lowercase as per RFC 952/2181 $host = strtolower(preg_replace('/:\d+$/', '', trim($host))); - // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user) - // check that it does not contain forbidden characters (see RFC 952 and RFC 2181) - // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names - if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) { + // the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user) + if ($host && !self::isHostValid($host)) { if (!$this->isHostValid) { return ''; } @@ -2135,4 +2144,21 @@ private function isIisRewrite(): bool return $this->isIisRewrite; } + + /** + * See https://url.spec.whatwg.org/. + */ + private static function isHostValid(string $host): bool + { + if ('[' === $host[0]) { + return ']' === $host[-1] && filter_var(substr($host, 1, -1), \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6); + } + + if (preg_match('/\.[0-9]++\.?$/D', $host)) { + return null !== filter_var($host, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4 | \FILTER_NULL_ON_FAILURE); + } + + // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names + return '' === preg_replace('/[-a-zA-Z0-9_]++\.?/', '', $host); + } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 3ab13d1f22c4f..429a2862d9faa 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -2237,11 +2237,9 @@ public function testFactory() Request::setFactory(null); } - /** - * @dataProvider getLongHostNames - */ - public function testVeryLongHosts($host) + public function testVeryLongHosts() { + $host = 'a'.str_repeat('.a', 40000); $start = microtime(true); $request = Request::create('/'); @@ -2284,14 +2282,6 @@ public static function getHostValidities() ]; } - public static function getLongHostNames() - { - return [ - ['a'.str_repeat('.a', 40000)], - [str_repeat(':', 101)], - ]; - } - /** * @dataProvider methodIdempotentProvider */ @@ -2667,6 +2657,71 @@ public function testReservedFlags() $this->assertNotSame(0b10000000, $value, \sprintf('The constant "%s" should not use the reserved value "0b10000000".', $constant)); } } + + /** + * @dataProvider provideMalformedUrls + */ + public function testMalformedUrls(string $url, string $expectedException) + { + $this->expectException(BadRequestException::class); + $this->expectExceptionMessage($expectedException); + + Request::create($url); + } + + public static function provideMalformedUrls(): array + { + return [ + ['http://normal.com[@vulndetector.com/', 'Invalid URI: Userinfo is malformed.'], + ['http://[normal.com@vulndetector.com/', 'Invalid URI: Userinfo is malformed.'], + ['http://normal.com@[vulndetector.com/', 'Invalid URI: Host is malformed.'], + ['http://[[normal.com@][vulndetector.com/', 'Invalid URI: Userinfo is malformed.'], + ['http://[vulndetector.com]', 'Invalid URI: Host is malformed.'], + ['http://[0:0::vulndetector.com]:80', 'Invalid URI: Host is malformed.'], + ['http://[2001:db8::vulndetector.com]', 'Invalid URI: Host is malformed.'], + ['http://[malicious.com]', 'Invalid URI: Host is malformed.'], + ['http://[evil.org]', 'Invalid URI: Host is malformed.'], + ['http://[internal.server]', 'Invalid URI: Host is malformed.'], + ['http://[192.168.1.1]', 'Invalid URI: Host is malformed.'], + ['http://192.abc.1.1', 'Invalid URI: Host is malformed.'], + ['http://[localhost]', 'Invalid URI: Host is malformed.'], + ["\x80https://example.com", 'Invalid URI: Scheme is malformed.'], + ['>https://example.com', 'Invalid URI: Scheme is malformed.'], + ["http\x0b://example.com", 'Invalid URI: Scheme is malformed.'], + ["https\x80://example.com", 'Invalid URI: Scheme is malformed.'], + ['http>://example.com', 'Invalid URI: Scheme is malformed.'], + ['0http://example.com', 'Invalid URI: Scheme is malformed.'], + ]; + } + + /** + * @dataProvider provideLegitimateUrls + */ + public function testLegitimateUrls(string $url) + { + $request = Request::create($url); + + $this->assertInstanceOf(Request::class, $request); + } + + public static function provideLegitimateUrls(): array + { + return [ + ['http://example.com'], + ['https://example.com'], + ['http://example.com:8080'], + ['https://example.com:8443'], + ['http://user:pass@example.com'], + ['http://user:pass@example.com:8080'], + ['http://user:pass@example.com/path'], + ['http://[2001:db8::1]'], + ['http://[2001:db8::1]:8080'], + ['http://[2001:db8::1]/path'], + ['http://[::1]'], + ['http://example.com/path'], + [':path'], + ]; + } } class RequestContentProxy extends Request diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php index 8c270a8e6e13e..ffceed9876af8 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php @@ -205,7 +205,8 @@ public function testRequestWithBadHost() { $this->expectException(BadRequestHttpException::class); $kernel = $this->createMock(HttpKernelInterface::class); - $request = Request::create('http://bad host %22/'); + $request = Request::create('/'); + $request->headers->set('host', 'bad host %22'); $event = new RequestEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST); $requestMatcher = $this->createMock(RequestMatcherInterface::class); From 8c52edd197ea484254f5f2205131ede37d790628 Mon Sep 17 00:00:00 2001 From: Wolfgang Klinger Date: Sun, 26 Oct 2025 18:51:55 +0100 Subject: [PATCH 38/91] [Messenger] Fix commands writing to STDERR instead of STDOUT Messenger commands were using getErrorOutput() for all output, causing structured data (tables, messages) to go to STDERR instead of STDOUT. Fixes #60822 --- .../Command/AbstractFailedMessagesCommand.php | 10 +- .../Command/ConsumeMessagesCommand.php | 12 +- .../Command/FailedMessagesRemoveCommand.php | 26 ++-- .../Command/FailedMessagesRetryCommand.php | 38 +++--- .../Command/FailedMessagesShowCommand.php | 26 ++-- .../Messenger/Command/StatsCommand.php | 8 +- .../Messenger/Command/StopWorkersCommand.php | 3 +- .../Command/ConsumeMessagesCommandTest.php | 70 +++++++++++ .../FailedMessagesRemoveCommandTest.php | 69 +++++++++++ .../FailedMessagesRetryCommandTest.php | 112 ++++++++++++++++++ .../Command/FailedMessagesShowCommandTest.php | 56 +++++++++ .../Tests/Command/StatsCommandTest.php | 29 +++++ .../Tests/Command/StopWorkersCommandTest.php | 20 ++++ 13 files changed, 418 insertions(+), 61 deletions(-) diff --git a/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php b/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php index f20de25f44a22..815554859c7f9 100644 --- a/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/AbstractFailedMessagesCommand.php @@ -72,8 +72,10 @@ protected function getMessageId(Envelope $envelope): mixed return $stamp?->getId(); } - protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io): void + protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io, ?SymfonyStyle $errorIo = null): void { + $errorIo ??= $io->getErrorStyle(); + $io->title('Failed Message Details'); /** @var SentToFailureTransportStamp|null $sentToFailureTransportStamp */ @@ -94,7 +96,7 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io): v } if (null === $sentToFailureTransportStamp) { - $io->warning('Message does not appear to have been sent to this transport after failing'); + $errorIo->warning('Message does not appear to have been sent to this transport after failing'); } else { $failedAt = ''; $errorMessage = ''; @@ -133,7 +135,7 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io): v if ($io->isVeryVerbose()) { $io->title('Message:'); if (null !== $lastMessageDecodingFailedStamp) { - $io->error('The message could not be decoded. See below an APPROXIMATIVE representation of the class.'); + $errorIo->error('The message could not be decoded. See below an APPROXIMATIVE representation of the class.'); } $dump = new Dumper($io, null, $this->createCloner()); $io->writeln($dump($envelope->getMessage())); @@ -142,7 +144,7 @@ protected function displaySingleMessage(Envelope $envelope, SymfonyStyle $io): v $io->writeln(null === $flattenException ? '(no data)' : $dump($flattenException)); } else { if (null !== $lastMessageDecodingFailedStamp) { - $io->error('The message could not be decoded.'); + $errorIo->error('The message could not be decoded.'); } $io->writeln(' Re-run command with -vv to see more message & error details.'); } diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index 28c8441976c12..7c121a0c04926 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -23,7 +23,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\SymfonyStyle; @@ -133,7 +132,7 @@ protected function configure(): void */ protected function interact(InputInterface $input, OutputInterface $output) { - $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); + $io = new SymfonyStyle($input, $output); if ($this->receiverNames && !$input->getArgument('receivers')) { if (1 === \count($this->receiverNames)) { @@ -215,19 +214,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int $stopsWhen[] = 'received a stop signal via the messenger:stop-workers command'; - $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); $io->success(\sprintf('Consuming messages from transport%s "%s".', \count($receivers) > 1 ? 's' : '', implode(', ', $receiverNames))); if ($stopsWhen) { $last = array_pop($stopsWhen); $stopsWhen = ($stopsWhen ? implode(', ', $stopsWhen).' or ' : '').$last; - $io->comment("The worker will automatically exit once it has {$stopsWhen}."); + $errorIo->comment("The worker will automatically exit once it has {$stopsWhen}."); } - $io->comment('Quit the worker with CONTROL-C.'); + $errorIo->comment('Quit the worker with CONTROL-C.'); if (OutputInterface::VERBOSITY_VERBOSE > $output->getVerbosity()) { - $io->comment('Re-run the command with a -vv option to see logs about consumed messages.'); + $errorIo->comment('Re-run the command with a -vv option to see logs about consumed messages.'); } $bus = $input->getOption('bus') ? $this->routableBus->getMessageBus($input->getOption('bus')) : $this->routableBus; diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php index 648060a6773aa..27d8f99b74b25 100644 --- a/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php +++ b/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php @@ -16,7 +16,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; @@ -55,7 +54,8 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); $failureTransportName = $input->getOption('transport'); if (self::DEFAULT_TRANSPORT_OPTION === $failureTransportName) { @@ -82,15 +82,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int } if ($shouldDeleteAllMessages) { - $this->removeAllMessages($receiver, $io, $shouldForce, $shouldDisplayMessages); + $this->removeAllMessages($receiver, $io, $errorIo, $shouldForce, $shouldDisplayMessages); } else { - $this->removeMessagesById($ids, $receiver, $io, $shouldForce, $shouldDisplayMessages); + $this->removeMessagesById($ids, $receiver, $io, $errorIo, $shouldForce, $shouldDisplayMessages); } return 0; } - private function removeMessagesById(array $ids, ListableReceiverInterface $receiver, SymfonyStyle $io, bool $shouldForce, bool $shouldDisplayMessages): void + private function removeMessagesById(array $ids, ListableReceiverInterface $receiver, SymfonyStyle $io, SymfonyStyle $errorIo, bool $shouldForce, bool $shouldDisplayMessages): void { foreach ($ids as $id) { $this->phpSerializer?->acceptPhpIncompleteClass(); @@ -101,25 +101,25 @@ private function removeMessagesById(array $ids, ListableReceiverInterface $recei } if (null === $envelope) { - $io->error(\sprintf('The message with id "%s" was not found.', $id)); + $errorIo->error(\sprintf('The message with id "%s" was not found.', $id)); continue; } if ($shouldDisplayMessages) { - $this->displaySingleMessage($envelope, $io); + $this->displaySingleMessage($envelope, $io, $errorIo); } - if ($shouldForce || $io->confirm('Do you want to permanently remove this message?', false)) { + if ($shouldForce || $errorIo->confirm('Do you want to permanently remove this message?', false)) { $receiver->reject($envelope); $io->success(\sprintf('Message with id %s removed.', $id)); } else { - $io->note(\sprintf('Message with id %s not removed.', $id)); + $errorIo->note(\sprintf('Message with id %s not removed.', $id)); } } } - private function removeAllMessages(ListableReceiverInterface $receiver, SymfonyStyle $io, bool $shouldForce, bool $shouldDisplayMessages): void + private function removeAllMessages(ListableReceiverInterface $receiver, SymfonyStyle $io, SymfonyStyle $errorIo, bool $shouldForce, bool $shouldDisplayMessages): void { if (!$shouldForce) { if ($receiver instanceof MessageCountAwareInterface) { @@ -128,7 +128,7 @@ private function removeAllMessages(ListableReceiverInterface $receiver, SymfonyS $question = 'Do you want to permanently remove all failed messages?'; } - if (!$io->confirm($question, false)) { + if (!$errorIo->confirm($question, false)) { return; } } @@ -136,13 +136,13 @@ private function removeAllMessages(ListableReceiverInterface $receiver, SymfonyS $count = 0; foreach ($receiver->all() as $envelope) { if ($shouldDisplayMessages) { - $this->displaySingleMessage($envelope, $io); + $this->displaySingleMessage($envelope, $io, $errorIo); } $receiver->reject($envelope); ++$count; } - $io->note(\sprintf('%d messages were removed.', $count)); + $errorIo->note(\sprintf('%d messages were removed.', $count)); } } diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php index 59a9d25345f9f..1fa82cb0c17b2 100644 --- a/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php +++ b/src/Symfony/Component/Messenger/Command/FailedMessagesRetryCommand.php @@ -18,7 +18,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -91,18 +90,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $this->eventDispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(1)); - $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); - $io->comment('Quit this command with CONTROL-C.'); + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); + $errorIo->comment('Quit this command with CONTROL-C.'); if (!$output->isVeryVerbose()) { - $io->comment('Re-run the command with a -vv option to see logs about consumed messages.'); + $errorIo->comment('Re-run the command with a -vv option to see logs about consumed messages.'); } $failureTransportName = $input->getOption('transport'); if (self::DEFAULT_TRANSPORT_OPTION === $failureTransportName) { - $this->printWarningAvailableFailureTransports($io, $this->getGlobalFailureReceiverName()); + $this->printWarningAvailableFailureTransports($errorIo, $this->getGlobalFailureReceiverName()); } if ('' === $failureTransportName || null === $failureTransportName) { - $failureTransportName = $this->interactiveChooseFailureTransport($io); + $failureTransportName = $this->interactiveChooseFailureTransport($errorIo); } $failureTransportName = self::DEFAULT_TRANSPORT_OPTION === $failureTransportName ? $this->getGlobalFailureReceiverName() : $failureTransportName; @@ -118,12 +118,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int throw new RuntimeException('Message id must be passed when in non-interactive mode.'); } - $this->runInteractive($failureTransportName, $io, $shouldForce); + $this->runInteractive($failureTransportName, $io, $errorIo, $shouldForce); return 0; } - $this->retrySpecificIds($failureTransportName, $ids, $io, $shouldForce); + $this->retrySpecificIds($failureTransportName, $ids, $io, $errorIo, $shouldForce); if (!$this->shouldStop) { $io->success('All done!'); @@ -151,7 +151,7 @@ public function handleSignal(int $signal, int|false $previousExitCode = 0): int| return $this->forceExit ? 0 : false; } - private function runInteractive(string $failureTransportName, SymfonyStyle $io, bool $shouldForce): void + private function runInteractive(string $failureTransportName, SymfonyStyle $io, SymfonyStyle $errorIo, bool $shouldForce): void { $receiver = $this->failureTransports->get($failureTransportName); $count = 0; @@ -178,11 +178,11 @@ private function runInteractive(string $failureTransportName, SymfonyStyle $io, break; } - $this->retrySpecificEnvelopes($envelopes, $failureTransportName, $io, $shouldForce); + $this->retrySpecificEnvelopes($envelopes, $failureTransportName, $io, $errorIo, $shouldForce); } } else { // get() and ask messages one-by-one - $count = $this->runWorker($failureTransportName, $receiver, $io, $shouldForce); + $count = $this->runWorker($failureTransportName, $receiver, $io, $errorIo, $shouldForce); } // avoid success message if nothing was processed @@ -191,14 +191,14 @@ private function runInteractive(string $failureTransportName, SymfonyStyle $io, } } - private function runWorker(string $failureTransportName, ReceiverInterface $receiver, SymfonyStyle $io, bool $shouldForce): int + private function runWorker(string $failureTransportName, ReceiverInterface $receiver, SymfonyStyle $io, SymfonyStyle $errorIo, bool $shouldForce): int { $count = 0; - $listener = function (WorkerMessageReceivedEvent $messageReceivedEvent) use ($io, $receiver, $shouldForce, &$count) { + $listener = function (WorkerMessageReceivedEvent $messageReceivedEvent) use ($io, $errorIo, $receiver, $shouldForce, &$count) { ++$count; $envelope = $messageReceivedEvent->getEnvelope(); - $this->displaySingleMessage($envelope, $io); + $this->displaySingleMessage($envelope, $io, $errorIo); if ($envelope->last(MessageDecodingFailedStamp::class)) { throw new \RuntimeException(\sprintf('The message with id "%s" could not decoded, it can only be shown or removed.', $this->getMessageId($envelope) ?? '?')); @@ -206,7 +206,7 @@ private function runWorker(string $failureTransportName, ReceiverInterface $rece $this->forceExit = true; try { - $shouldHandle = $shouldForce || 'retry' === $io->choice('Please select an action', ['retry', 'delete'], 'retry'); + $shouldHandle = $shouldForce || 'retry' === $errorIo->choice('Please select an action', ['retry', 'delete'], 'retry'); } finally { $this->forceExit = false; } @@ -237,7 +237,7 @@ private function runWorker(string $failureTransportName, ReceiverInterface $rece return $count; } - private function retrySpecificIds(string $failureTransportName, array $ids, SymfonyStyle $io, bool $shouldForce): void + private function retrySpecificIds(string $failureTransportName, array $ids, SymfonyStyle $io, SymfonyStyle $errorIo, bool $shouldForce): void { $receiver = $this->getReceiver($failureTransportName); @@ -257,7 +257,7 @@ private function retrySpecificIds(string $failureTransportName, array $ids, Symf } $singleReceiver = new SingleMessageReceiver($receiver, $envelope); - $this->runWorker($failureTransportName, $singleReceiver, $io, $shouldForce); + $this->runWorker($failureTransportName, $singleReceiver, $io, $errorIo, $shouldForce); if ($this->shouldStop) { break; @@ -265,13 +265,13 @@ private function retrySpecificIds(string $failureTransportName, array $ids, Symf } } - private function retrySpecificEnvelopes(array $envelopes, string $failureTransportName, SymfonyStyle $io, bool $shouldForce): void + private function retrySpecificEnvelopes(array $envelopes, string $failureTransportName, SymfonyStyle $io, SymfonyStyle $errorIo, bool $shouldForce): void { $receiver = $this->getReceiver($failureTransportName); foreach ($envelopes as $envelope) { $singleReceiver = new SingleMessageReceiver($receiver, $envelope); - $this->runWorker($failureTransportName, $singleReceiver, $io, $shouldForce); + $this->runWorker($failureTransportName, $singleReceiver, $io, $errorIo, $shouldForce); if ($this->shouldStop) { break; diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php index 927e6705a0e94..957a803917add 100644 --- a/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php +++ b/src/Symfony/Component/Messenger/Command/FailedMessagesShowCommand.php @@ -16,7 +16,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Messenger\Stamp\ErrorDetailsStamp; @@ -54,14 +53,15 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); $failureTransportName = $input->getOption('transport'); if (self::DEFAULT_TRANSPORT_OPTION === $failureTransportName) { - $this->printWarningAvailableFailureTransports($io, $this->getGlobalFailureReceiverName()); + $this->printWarningAvailableFailureTransports($errorIo, $this->getGlobalFailureReceiverName()); } if ('' === $failureTransportName || null === $failureTransportName) { - $failureTransportName = $this->interactiveChooseFailureTransport($io); + $failureTransportName = $this->interactiveChooseFailureTransport($errorIo); } $failureTransportName = self::DEFAULT_TRANSPORT_OPTION === $failureTransportName ? $this->getGlobalFailureReceiverName() : $failureTransportName; @@ -76,15 +76,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int if ($input->getOption('stats')) { $this->listMessagesPerClass($failureTransportName, $io, $input->getOption('max')); } elseif (null === $id = $input->getArgument('id')) { - $this->listMessages($failureTransportName, $io, $input->getOption('max'), $input->getOption('class-filter')); + $this->listMessages($failureTransportName, $io, $errorIo, $input->getOption('max'), $input->getOption('class-filter')); } else { - $this->showMessage($failureTransportName, $id, $io); + $this->showMessage($failureTransportName, $id, $io, $errorIo); } return 0; } - private function listMessages(?string $failedTransportName, SymfonyStyle $io, int $max, ?string $classFilter = null): void + private function listMessages(?string $failedTransportName, SymfonyStyle $io, SymfonyStyle $errorIo, int $max, ?string $classFilter = null): void { /** @var ListableReceiverInterface $receiver */ $receiver = $this->getReceiver($failedTransportName); @@ -93,7 +93,7 @@ private function listMessages(?string $failedTransportName, SymfonyStyle $io, in $rows = []; if ($classFilter) { - $io->comment(\sprintf('Displaying only \'%s\' messages', $classFilter)); + $errorIo->comment(\sprintf('Displaying only \'%s\' messages', $classFilter)); } $this->phpSerializer?->acceptPhpIncompleteClass(); @@ -132,12 +132,12 @@ private function listMessages(?string $failedTransportName, SymfonyStyle $io, in $io->table(['Id', 'Class', 'Failed at', 'Error'], $rows); if ($rowsCount === $max) { - $io->comment(\sprintf('Showing first %d messages.', $max)); + $errorIo->comment(\sprintf('Showing first %d messages.', $max)); } elseif ($classFilter) { - $io->comment(\sprintf('Showing %d message(s).', $rowsCount)); + $errorIo->comment(\sprintf('Showing %d message(s).', $rowsCount)); } - $io->comment(\sprintf('Run messenger:failed:show {id} --transport=%s -vv to see message details.', $failedTransportName)); + $errorIo->comment(\sprintf('Run messenger:failed:show {id} --transport=%s -vv to see message details.', $failedTransportName)); } private function listMessagesPerClass(?string $failedTransportName, SymfonyStyle $io, int $max): void @@ -172,7 +172,7 @@ private function listMessagesPerClass(?string $failedTransportName, SymfonyStyle $io->table(['Class', 'Count'], $countPerClass); } - private function showMessage(?string $failedTransportName, string $id, SymfonyStyle $io): void + private function showMessage(?string $failedTransportName, string $id, SymfonyStyle $io, SymfonyStyle $errorIo): void { /** @var ListableReceiverInterface $receiver */ $receiver = $this->getReceiver($failedTransportName); @@ -186,7 +186,7 @@ private function showMessage(?string $failedTransportName, string $id, SymfonySt throw new RuntimeException(\sprintf('The message "%s" was not found.', $id)); } - $this->displaySingleMessage($envelope, $io); + $this->displaySingleMessage($envelope, $io, $errorIo); $io->writeln([ '', diff --git a/src/Symfony/Component/Messenger/Command/StatsCommand.php b/src/Symfony/Component/Messenger/Command/StatsCommand.php index 107ef5bbe8e65..5dc50f0263daa 100644 --- a/src/Symfony/Component/Messenger/Command/StatsCommand.php +++ b/src/Symfony/Component/Messenger/Command/StatsCommand.php @@ -16,7 +16,6 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface; @@ -60,7 +59,8 @@ protected function configure() protected function execute(InputInterface $input, OutputInterface $output): int { - $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); $transportNames = $this->transportNames; if ($input->getArgument('transport_names')) { @@ -71,7 +71,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $uncountableTransports = []; foreach ($transportNames as $transportName) { if (!$this->transportLocator->has($transportName)) { - $io->warning(\sprintf('The "%s" transport does not exist.', $transportName)); + $errorIo->warning(\sprintf('The "%s" transport does not exist.', $transportName)); continue; } @@ -87,7 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $io->table(['Transport', 'Count'], $outputTable); if ($uncountableTransports) { - $io->note(\sprintf('Unable to get message count for the following transports: "%s".', implode('", "', $uncountableTransports))); + $errorIo->note(\sprintf('Unable to get message count for the following transports: "%s".', implode('", "', $uncountableTransports))); } return 0; diff --git a/src/Symfony/Component/Messenger/Command/StopWorkersCommand.php b/src/Symfony/Component/Messenger/Command/StopWorkersCommand.php index e7f7fe79de985..002ee0494ac75 100644 --- a/src/Symfony/Component/Messenger/Command/StopWorkersCommand.php +++ b/src/Symfony/Component/Messenger/Command/StopWorkersCommand.php @@ -15,7 +15,6 @@ use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Messenger\EventListener\StopWorkerOnRestartSignalListener; @@ -54,7 +53,7 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); + $io = new SymfonyStyle($input, $output); $cacheItem = $this->restartSignalCachePool->getItem(StopWorkerOnRestartSignalListener::RESTART_REQUESTED_TIMESTAMP_KEY); $cacheItem->set(microtime(true)); diff --git a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php index d9739b4eab9db..d979a19861600 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php @@ -280,4 +280,74 @@ public static function provideCompletionSuggestions() yield 'receiver (no repeat)' => [['async', ''], ['async_high', 'failed']]; yield 'option --bus' => [['--bus', ''], ['messenger.bus.default']]; } + + public function testSuccessMessageGoesToStdout() + { + $envelope = new Envelope(new \stdClass(), [new BusNameStamp('dummy-bus')]); + + $receiver = $this->createMock(ReceiverInterface::class); + $receiver->expects($this->once())->method('get')->willReturn([$envelope]); + + $receiverLocator = $this->createMock(ContainerInterface::class); + $receiverLocator->expects($this->once())->method('has')->with('dummy-receiver')->willReturn(true); + $receiverLocator->expects($this->once())->method('get')->with('dummy-receiver')->willReturn($receiver); + + $bus = $this->createMock(MessageBusInterface::class); + $bus->expects($this->once())->method('dispatch'); + + $busLocator = $this->createMock(ContainerInterface::class); + $busLocator->expects($this->once())->method('has')->with('dummy-bus')->willReturn(true); + $busLocator->expects($this->once())->method('get')->with('dummy-bus')->willReturn($bus); + + $command = new ConsumeMessagesCommand(new RoutableMessageBus($busLocator), $receiverLocator, new EventDispatcher()); + + $application = new Application(); + $application->add($command); + $tester = new CommandTester($application->get('messenger:consume')); + $tester->execute([ + 'receivers' => ['dummy-receiver'], + '--limit' => 1, + ], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('Consuming messages from transport', $stdout); + $this->assertStringNotContainsString('Consuming messages from transport', $stderr); + } + + public function testCommentsGoToStderr() + { + $envelope = new Envelope(new \stdClass(), [new BusNameStamp('dummy-bus')]); + + $receiver = $this->createMock(ReceiverInterface::class); + $receiver->expects($this->once())->method('get')->willReturn([$envelope]); + + $receiverLocator = $this->createMock(ContainerInterface::class); + $receiverLocator->expects($this->once())->method('has')->with('dummy-receiver')->willReturn(true); + $receiverLocator->expects($this->once())->method('get')->with('dummy-receiver')->willReturn($receiver); + + $bus = $this->createMock(MessageBusInterface::class); + $bus->expects($this->once())->method('dispatch'); + + $busLocator = $this->createMock(ContainerInterface::class); + $busLocator->expects($this->once())->method('has')->with('dummy-bus')->willReturn(true); + $busLocator->expects($this->once())->method('get')->with('dummy-bus')->willReturn($bus); + + $command = new ConsumeMessagesCommand(new RoutableMessageBus($busLocator), $receiverLocator, new EventDispatcher()); + + $application = new Application(); + $application->add($command); + $tester = new CommandTester($application->get('messenger:consume')); + $tester->execute([ + 'receivers' => ['dummy-receiver'], + '--limit' => 1, + ], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringNotContainsString('Quit the worker with CONTROL-C', $stdout); + $this->assertStringContainsString('Quit the worker with CONTROL-C', $stderr); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRemoveCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRemoveCommandTest.php index bb8365d351637..68a4721a2160c 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRemoveCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRemoveCommandTest.php @@ -351,4 +351,73 @@ public function testRemoveAllMessages() $this->assertStringContainsString('Failed Message Details', $tester->getDisplay()); $this->assertStringContainsString('4 messages were removed.', $tester->getDisplay()); } + + public function testSuccessMessageGoesToStdout() + { + $globalFailureReceiverName = 'failure_receiver'; + $receiver = $this->createMock(ListableReceiverInterface::class); + + $envelope = new Envelope(new \stdClass(), [new TransportMessageIdStamp('some_id')]); + $receiver->method('find')->with('some_id')->willReturn($envelope); + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->method('has')->willReturn(true); + $serviceLocator->method('get')->willReturn($receiver); + + $command = new FailedMessagesRemoveCommand($globalFailureReceiverName, $serviceLocator); + $tester = new CommandTester($command); + $tester->execute(['id' => ['some_id'], '--force' => true], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('Message with id some_id removed', $stdout); + $this->assertStringNotContainsString('Message with id some_id removed', $stderr); + } + + public function testErrorMessageGoesToStderr() + { + $globalFailureReceiverName = 'failure_receiver'; + $receiver = $this->createMock(ListableReceiverInterface::class); + + $receiver->method('find')->with('not_found')->willReturn(null); + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->method('has')->willReturn(true); + $serviceLocator->method('get')->willReturn($receiver); + + $command = new FailedMessagesRemoveCommand($globalFailureReceiverName, $serviceLocator); + $tester = new CommandTester($command); + $tester->execute(['id' => ['not_found']], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringNotContainsString('[ERROR]', $stdout); + $this->assertStringContainsString('The message with id "not_found" was not found', $stderr); + } + + public function testNoteMessageGoesToStderr() + { + $globalFailureReceiverName = 'failure_receiver'; + $receiver = $this->createMock(ListableReceiverInterface::class); + + $envelope = new Envelope(new \stdClass(), [new TransportMessageIdStamp('some_id')]); + $receiver->method('find')->with('some_id')->willReturn($envelope); + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->method('has')->willReturn(true); + $serviceLocator->method('get')->willReturn($receiver); + + $command = new FailedMessagesRemoveCommand($globalFailureReceiverName, $serviceLocator); + $tester = new CommandTester($command); + $tester->setInputs(['no']); + $tester->execute(['id' => ['some_id']], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringNotContainsString('[NOTE]', $stdout); + $this->assertStringContainsString('Message with id some_id not removed', $stderr); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php index 4b72ff464965f..dc034b299944c 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRetryCommandTest.php @@ -21,6 +21,7 @@ use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; +use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface; class FailedMessagesRetryCommandTest extends TestCase { @@ -223,4 +224,115 @@ public function testCompleteIdWithSpecifiedTransport() $this->assertSame(['2ab50dfa1fbf', '78c2da843723'], $suggestions); } + + public function testSuccessMessageGoesToStdout() + { + $envelope = new Envelope(new \stdClass(), [new TransportMessageIdStamp('some_id')]); + $receiver = $this->createMock(ListableReceiverInterface::class); + $receiver->method('find')->with('some_id')->willReturn($envelope); + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->method('has')->willReturn(true); + $serviceLocator->method('get')->willReturn($receiver); + + $command = new FailedMessagesRetryCommand( + 'failure_receiver', + $serviceLocator, + $this->createMock(MessageBusInterface::class), + new EventDispatcher() + ); + + $tester = new CommandTester($command); + $tester->setInputs(['retry']); + $tester->execute(['id' => ['some_id']], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('All done!', $stdout); + $this->assertStringNotContainsString('All done!', $stderr); + } + + public function testCommentsGoToStderr() + { + $envelope = new Envelope(new \stdClass(), [new TransportMessageIdStamp('some_id')]); + $receiver = $this->createMock(ListableReceiverInterface::class); + $receiver->method('find')->with('some_id')->willReturn($envelope); + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->method('has')->willReturn(true); + $serviceLocator->method('get')->willReturn($receiver); + + $command = new FailedMessagesRetryCommand( + 'failure_receiver', + $serviceLocator, + $this->createMock(MessageBusInterface::class), + new EventDispatcher() + ); + + $tester = new CommandTester($command); + $tester->setInputs(['retry']); + $tester->execute(['id' => ['some_id']], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('Quit this command with CONTROL-C', $stderr); + $this->assertStringNotContainsString('Quit this command with CONTROL-C', $stdout); + } + + public function testPendingMessageCountGoesToStdout() + { + $receiver = new class implements ListableReceiverInterface, MessageCountAwareInterface { + public function get(): iterable + { + return []; + } + + public function ack(Envelope $envelope): void + { + } + + public function reject(Envelope $envelope): void + { + } + + public function find(mixed $id): ?Envelope + { + return null; + } + + public function all(?int $limit = null): iterable + { + return []; + } + + public function getMessageCount(): int + { + return 5; + } + }; + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->method('has')->willReturn(true); + $serviceLocator->method('get')->willReturn($receiver); + + $command = new FailedMessagesRetryCommand( + 'failure_receiver', + $serviceLocator, + $this->createMock(MessageBusInterface::class), + new EventDispatcher() + ); + + $tester = new CommandTester($command); + $tester->execute(['--force' => true], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('There are', $stdout); + $this->assertStringContainsString('5', $stdout); + $this->assertStringContainsString('messages pending', $stdout); + $this->assertStringNotContainsString('messages pending', $stderr); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php index c67f85efd30e7..e9ff674e72ed1 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php @@ -23,6 +23,7 @@ use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; use Symfony\Component\Messenger\Stamp\TransportMessageIdStamp; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; +use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface; use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; /** @@ -466,4 +467,59 @@ public function testCompleteIdWithSpecifiedTransport() $this->assertSame(['2ab50dfa1fbf', '78c2da843723'], $suggestions); } + + public function testTableOutputGoesToStdout() + { + $envelope = new Envelope(new \stdClass(), [ + new TransportMessageIdStamp('2ab50dfa1fbf'), + new SentToFailureTransportStamp('async'), + new RedeliveryStamp(0), + ]); + + $receiver = $this->createMock(ListableReceiverInterface::class); + $receiver->expects($this->once())->method('all')->with(50)->willReturn([$envelope]); + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->method('has')->willReturn(true); + $serviceLocator->method('get')->willReturn($receiver); + + $command = new FailedMessagesShowCommand('failure_receiver', $serviceLocator); + $tester = new CommandTester($command); + $tester->execute([], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('2ab50dfa1fbf', $stdout); + $this->assertStringContainsString('messenger:failed:show', $stderr); + $this->assertStringNotContainsString('2ab50dfa1fbf', $stderr); + } + + public function testPendingMessageCountGoesToStdout() + { + $receiver = new class implements ListableReceiverInterface, MessageCountAwareInterface { + public function get(): iterable { return []; } + public function ack(Envelope $envelope): void {} + public function reject(Envelope $envelope): void {} + public function find(mixed $id): ?Envelope { return null; } + public function all(int $limit = null): iterable { return []; } + public function getMessageCount(): int { return 3; } + }; + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->method('has')->willReturn(true); + $serviceLocator->method('get')->willReturn($receiver); + + $command = new FailedMessagesShowCommand('failure_receiver', $serviceLocator); + $tester = new CommandTester($command); + $tester->execute(['--max' => 5], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('There are', $stdout); + $this->assertStringContainsString('3', $stdout); + $this->assertStringContainsString('messages pending', $stdout); + $this->assertStringNotContainsString('messages pending', $stderr); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Command/StatsCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/StatsCommandTest.php index 9eb8268930609..421293d50177d 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/StatsCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/StatsCommandTest.php @@ -116,4 +116,33 @@ public function testWithNotExistingTransport() $this->assertStringNotContainsString('another_message_countable', $display); $this->assertStringNotContainsString('! [NOTE] Unable to get message count for the following transports: "simple".', $display); } + + public function testTableOutputGoesToStdout() + { + $tester = new CommandTester($this->command); + $tester->execute([], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('Transport', $stdout); + $this->assertStringContainsString('message_countable', $stdout); + + $this->assertStringContainsString('[WARNING]', $stderr); + $this->assertStringContainsString('[NOTE]', $stderr); + $this->assertStringNotContainsString('Transport', $stderr); + } + + public function testWarningsGoToStderrWithSpecificTransport() + { + $tester = new CommandTester($this->command); + $tester->execute(['transport_names' => ['message_countable']], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('message_countable', $stdout); + $this->assertStringNotContainsString('[WARNING]', $stderr); + $this->assertStringNotContainsString('Transport', $stderr); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Command/StopWorkersCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/StopWorkersCommandTest.php index fd5ddae244b70..87c0d00b203c7 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/StopWorkersCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/StopWorkersCommandTest.php @@ -32,4 +32,24 @@ public function testItSetsCacheItem() $tester = new CommandTester($command); $tester->execute([]); } + + public function testSuccessMessageGoesToStdout() + { + $cachePool = $this->createMock(CacheItemPoolInterface::class); + $cacheItem = $this->createMock(CacheItemInterface::class); + $cacheItem->method('set'); + $cachePool->method('getItem')->willReturn($cacheItem); + $cachePool->method('save'); + + $command = new StopWorkersCommand($cachePool); + + $tester = new CommandTester($command); + $tester->execute([], ['capture_stderr_separately' => true]); + + $stdout = $tester->getDisplay(); + $stderr = $tester->getErrorOutput(); + + $this->assertStringContainsString('Signal successfully sent', $stdout); + $this->assertStringNotContainsString('Signal successfully sent', $stderr); + } } From 3cec296a2c0ae94d87eaa2233169bf4773d8cfda Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 6 Nov 2025 12:12:43 +0100 Subject: [PATCH 39/91] CS fix --- .../Command/FailedMessagesShowCommandTest.php | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php index e9ff674e72ed1..41c2a39bf3ae1 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesShowCommandTest.php @@ -498,12 +498,33 @@ public function testTableOutputGoesToStdout() public function testPendingMessageCountGoesToStdout() { $receiver = new class implements ListableReceiverInterface, MessageCountAwareInterface { - public function get(): iterable { return []; } - public function ack(Envelope $envelope): void {} - public function reject(Envelope $envelope): void {} - public function find(mixed $id): ?Envelope { return null; } - public function all(int $limit = null): iterable { return []; } - public function getMessageCount(): int { return 3; } + public function get(): iterable + { + return []; + } + + public function ack(Envelope $envelope): void + { + } + + public function reject(Envelope $envelope): void + { + } + + public function find(mixed $id): ?Envelope + { + return null; + } + + public function all(?int $limit = null): iterable + { + return []; + } + + public function getMessageCount(): int + { + return 3; + } }; $serviceLocator = $this->createMock(ServiceLocator::class); From 0e10ab7b71b11d295ec940b201771e3b75d44a6a Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 6 Nov 2025 12:17:34 +0100 Subject: [PATCH 40/91] Fix merge --- .../Messenger/Command/StatsCommand.php | 4 ++-- .../Command/ConsumeMessagesCommandTest.php | 20 ++++++++----------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/Messenger/Command/StatsCommand.php b/src/Symfony/Component/Messenger/Command/StatsCommand.php index c493c6ca6dd44..2469d845257e2 100644 --- a/src/Symfony/Component/Messenger/Command/StatsCommand.php +++ b/src/Symfony/Component/Messenger/Command/StatsCommand.php @@ -99,14 +99,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int } match ($format) { - 'txt' => $this->outputText($io, $outputTable, $uncountableTransports), + 'txt' => $this->outputText($io, $errorIo, $outputTable, $uncountableTransports), 'json' => $this->outputJson($io, $outputTable, $uncountableTransports), }; return 0; } - private function outputText(SymfonyStyle $io, array $outputTable, array $uncountableTransports): void + private function outputText(SymfonyStyle $io, SymfonyStyle $errorIo, array $outputTable, array $uncountableTransports): void { $io->table(['Transport', 'Count'], $outputTable); diff --git a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php index c7f2012d4f523..95106da1c131f 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php @@ -314,16 +314,14 @@ public function testSuccessMessageGoesToStdout() $receiver = $this->createMock(ReceiverInterface::class); $receiver->expects($this->once())->method('get')->willReturn([$envelope]); - $receiverLocator = $this->createMock(ContainerInterface::class); - $receiverLocator->expects($this->once())->method('has')->with('dummy-receiver')->willReturn(true); - $receiverLocator->expects($this->once())->method('get')->with('dummy-receiver')->willReturn($receiver); + $receiverLocator = new Container(); + $receiverLocator->set('dummy-receiver', $receiver); $bus = $this->createMock(MessageBusInterface::class); $bus->expects($this->once())->method('dispatch'); - $busLocator = $this->createMock(ContainerInterface::class); - $busLocator->expects($this->once())->method('has')->with('dummy-bus')->willReturn(true); - $busLocator->expects($this->once())->method('get')->with('dummy-bus')->willReturn($bus); + $busLocator = new Container(); + $busLocator->set('dummy-bus', $bus); $command = new ConsumeMessagesCommand(new RoutableMessageBus($busLocator), $receiverLocator, new EventDispatcher()); @@ -349,16 +347,14 @@ public function testCommentsGoToStderr() $receiver = $this->createMock(ReceiverInterface::class); $receiver->expects($this->once())->method('get')->willReturn([$envelope]); - $receiverLocator = $this->createMock(ContainerInterface::class); - $receiverLocator->expects($this->once())->method('has')->with('dummy-receiver')->willReturn(true); - $receiverLocator->expects($this->once())->method('get')->with('dummy-receiver')->willReturn($receiver); + $receiverLocator = new Container(); + $receiverLocator->set('dummy-receiver', $receiver); $bus = $this->createMock(MessageBusInterface::class); $bus->expects($this->once())->method('dispatch'); - $busLocator = $this->createMock(ContainerInterface::class); - $busLocator->expects($this->once())->method('has')->with('dummy-bus')->willReturn(true); - $busLocator->expects($this->once())->method('get')->with('dummy-bus')->willReturn($bus); + $busLocator = new Container(); + $busLocator->set('dummy-bus', $bus); $command = new ConsumeMessagesCommand(new RoutableMessageBus($busLocator), $receiverLocator, new EventDispatcher()); From d69f7c2118e00bcbfbdb8fde48a7fb2196af7f1a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 6 Nov 2025 12:19:34 +0100 Subject: [PATCH 41/91] re-allow ext-redis 6.1 --- composer.json | 2 +- src/Symfony/Component/Cache/CHANGELOG.md | 2 +- .../Cache/Traits/Redis62ProxyTrait.php | 52 +++++++++++++++++++ .../Cache/Traits/RedisCluster62ProxyTrait.php | 47 +++++++++++++++++ .../Cache/Traits/RedisClusterProxy.php | 21 +------- .../Component/Cache/Traits/RedisProxy.php | 26 +--------- .../Component/Cache/Traits/RedisTrait.php | 4 +- src/Symfony/Component/Cache/composer.json | 2 +- .../Messenger/Bridge/Redis/CHANGELOG.md | 2 +- .../Bridge/Redis/Transport/Connection.php | 9 +++- .../Messenger/Bridge/Redis/composer.json | 2 +- 11 files changed, 115 insertions(+), 54 deletions(-) create mode 100644 src/Symfony/Component/Cache/Traits/Redis62ProxyTrait.php create mode 100644 src/Symfony/Component/Cache/Traits/RedisCluster62ProxyTrait.php diff --git a/composer.json b/composer.json index 41ab14fab3b5a..b8ae634a14d1b 100644 --- a/composer.json +++ b/composer.json @@ -170,7 +170,7 @@ }, "conflict": { "ext-psr": "<1.1|>=2", - "ext-redis": "<6.2", + "ext-redis": "<6.1", "ext-relay": "<0.12.1", "amphp/amp": "<2.5", "async-aws/core": "<1.5", diff --git a/src/Symfony/Component/Cache/CHANGELOG.md b/src/Symfony/Component/Cache/CHANGELOG.md index fb3016acbdc11..8b0c2b7e7faa5 100644 --- a/src/Symfony/Component/Cache/CHANGELOG.md +++ b/src/Symfony/Component/Cache/CHANGELOG.md @@ -4,7 +4,7 @@ CHANGELOG 7.4 --- - * Bump ext-redis to 6.2 and ext-relay to 0.12 minimum + * Bump ext-redis to 6.1 and ext-relay to 0.12 minimum 7.3 --- diff --git a/src/Symfony/Component/Cache/Traits/Redis62ProxyTrait.php b/src/Symfony/Component/Cache/Traits/Redis62ProxyTrait.php new file mode 100644 index 0000000000000..d29466cbb92c5 --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/Redis62ProxyTrait.php @@ -0,0 +1,52 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +if (version_compare(phpversion('redis'), '6.2.0', '>=')) { + /** + * @internal + */ + trait Redis62ProxyTrait + { + public function expiremember($key, $field, $ttl, $unit = null): \Redis|false|int + { + return $this->initializeLazyObject()->expiremember(...\func_get_args()); + } + + public function expirememberat($key, $field, $timestamp): \Redis|false|int + { + return $this->initializeLazyObject()->expirememberat(...\func_get_args()); + } + + public function getWithMeta($key): \Redis|array|false + { + return $this->initializeLazyObject()->getWithMeta(...\func_get_args()); + } + + public function serverName(): false|string + { + return $this->initializeLazyObject()->serverName(...\func_get_args()); + } + + public function serverVersion(): false|string + { + return $this->initializeLazyObject()->serverVersion(...\func_get_args()); + } + } +} else { + /** + * @internal + */ + trait Redis62ProxyTrait + { + } +} diff --git a/src/Symfony/Component/Cache/Traits/RedisCluster62ProxyTrait.php b/src/Symfony/Component/Cache/Traits/RedisCluster62ProxyTrait.php new file mode 100644 index 0000000000000..e96dee46d2907 --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/RedisCluster62ProxyTrait.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\Component\Cache\Traits; + +if (version_compare(phpversion('redis'), '6.2.0', '>=')) { + /** + * @internal + */ + trait RedisCluster62ProxyTrait + { + public function expiremember($key, $field, $ttl, $unit = null): \Redis|false|int + { + return $this->initializeLazyObject()->expiremember(...\func_get_args()); + } + + public function expirememberat($key, $field, $timestamp): \Redis|false|int + { + return $this->initializeLazyObject()->expirememberat(...\func_get_args()); + } + + public function getdel($key): mixed + { + return $this->initializeLazyObject()->getdel(...\func_get_args()); + } + + public function getWithMeta($key): \RedisCluster|array|false + { + return $this->initializeLazyObject()->getWithMeta(...\func_get_args()); + } + } +} else { + /** + * @internal + */ + trait RedisCluster62ProxyTrait + { + } +} diff --git a/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php b/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php index 2cde053d1e1b3..6bedcfaf7f731 100644 --- a/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php +++ b/src/Symfony/Component/Cache/Traits/RedisClusterProxy.php @@ -24,6 +24,7 @@ class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); */ class RedisClusterProxy extends \RedisCluster implements ResetInterface, LazyObjectInterface { + use RedisCluster62ProxyTrait; use RedisProxyTrait { resetLazyObject as reset; } @@ -273,16 +274,6 @@ public function expireat($key, $timestamp, $mode = null): \RedisCluster|bool return $this->initializeLazyObject()->expireat(...\func_get_args()); } - public function expiremember($key, $field, $ttl, $unit = null): \Redis|false|int - { - return $this->initializeLazyObject()->expiremember(...\func_get_args()); - } - - public function expirememberat($key, $field, $timestamp): \Redis|false|int - { - return $this->initializeLazyObject()->expirememberat(...\func_get_args()); - } - public function expiretime($key): \RedisCluster|false|int { return $this->initializeLazyObject()->expiretime(...\func_get_args()); @@ -353,21 +344,11 @@ public function get($key): mixed return $this->initializeLazyObject()->get(...\func_get_args()); } - public function getWithMeta($key): \RedisCluster|array|false - { - return $this->initializeLazyObject()->getWithMeta(...\func_get_args()); - } - public function getbit($key, $value): \RedisCluster|false|int { return $this->initializeLazyObject()->getbit(...\func_get_args()); } - public function getdel($key): mixed - { - return $this->initializeLazyObject()->getdel(...\func_get_args()); - } - public function getex($key, $options = []): \RedisCluster|false|string { return $this->initializeLazyObject()->getex(...\func_get_args()); diff --git a/src/Symfony/Component/Cache/Traits/RedisProxy.php b/src/Symfony/Component/Cache/Traits/RedisProxy.php index 6c75c9ad646cc..7e99b606f7889 100644 --- a/src/Symfony/Component/Cache/Traits/RedisProxy.php +++ b/src/Symfony/Component/Cache/Traits/RedisProxy.php @@ -24,6 +24,7 @@ class_exists(\Symfony\Component\VarExporter\Internal\LazyObjectState::class); */ class RedisProxy extends \Redis implements ResetInterface, LazyObjectInterface { + use Redis62ProxyTrait; use RedisProxyTrait { resetLazyObject as reset; } @@ -273,16 +274,6 @@ public function expireAt($key, $timestamp, $mode = null): \Redis|bool return $this->initializeLazyObject()->expireAt(...\func_get_args()); } - public function expiremember($key, $field, $ttl, $unit = null): \Redis|false|int - { - return $this->initializeLazyObject()->expiremember(...\func_get_args()); - } - - public function expirememberat($key, $field, $timestamp): \Redis|false|int - { - return $this->initializeLazyObject()->expirememberat(...\func_get_args()); - } - public function expiretime($key): \Redis|false|int { return $this->initializeLazyObject()->expiretime(...\func_get_args()); @@ -448,11 +439,6 @@ public function getTransferredBytes(): array return $this->initializeLazyObject()->getTransferredBytes(...\func_get_args()); } - public function getWithMeta($key): \Redis|array|false - { - return $this->initializeLazyObject()->getWithMeta(...\func_get_args()); - } - public function getset($key, $value): \Redis|false|string { return $this->initializeLazyObject()->getset(...\func_get_args()); @@ -913,16 +899,6 @@ public function select($db): \Redis|bool return $this->initializeLazyObject()->select(...\func_get_args()); } - public function serverName(): false|string - { - return $this->initializeLazyObject()->serverName(...\func_get_args()); - } - - public function serverVersion(): false|string - { - return $this->initializeLazyObject()->serverVersion(...\func_get_args()); - } - public function set($key, $value, $options = null): \Redis|bool|string { return $this->initializeLazyObject()->set(...\func_get_args()); diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index 918336a700696..97233ed0c5b95 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -193,7 +193,7 @@ public static function createConnection(#[\SensitiveParameter] string $dsn, arra } if (isset($params['sentinel']) && !class_exists(\Predis\Client::class) && !class_exists(\RedisSentinel::class) && !class_exists(Sentinel::class)) { - throw new CacheException('Redis Sentinel support requires one of: "predis/predis", "ext-redis >= 5.2", "ext-relay".'); + throw new CacheException('Redis Sentinel support requires one of: "predis/predis", "ext-redis >= 6.1", "ext-relay".'); } foreach (['lazy', 'persistent', 'cluster'] as $option) { @@ -224,7 +224,7 @@ public static function createConnection(#[\SensitiveParameter] string $dsn, arra }; if (isset($params['sentinel']) && !is_a($class, \Predis\Client::class, true) && !class_exists(\RedisSentinel::class) && !class_exists(Sentinel::class)) { - throw new CacheException(\sprintf('Cannot use Redis Sentinel: class "%s" does not extend "Predis\Client" and neither ext-redis >= 5.2 nor ext-relay have been found.', $class)); + throw new CacheException(\sprintf('Cannot use Redis Sentinel: class "%s" does not extend "Predis\Client" and neither ext-redis >= 6.1 nor ext-relay have been found.', $class)); } $isRedisExt = is_a($class, \Redis::class, true); diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json index e5834e9fbd29f..c8280d55549a6 100644 --- a/src/Symfony/Component/Cache/composer.json +++ b/src/Symfony/Component/Cache/composer.json @@ -43,7 +43,7 @@ "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "conflict": { - "ext-redis": "<6.2", + "ext-redis": "<6.1", "ext-relay": "<0.12.1", "doctrine/dbal": "<3.6", "symfony/dependency-injection": "<6.4", diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/CHANGELOG.md b/src/Symfony/Component/Messenger/Bridge/Redis/CHANGELOG.md index fe19b620056af..fb2f1eeeb811e 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/Bridge/Redis/CHANGELOG.md @@ -4,7 +4,7 @@ CHANGELOG 7.4 --- - * Bump ext-redis to 6.2 and ext-relay to 0.12 minimum + * Bump ext-redis to 6.1 and ext-relay to 0.12 minimum 7.3 --- diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php index 3e4d3231c644e..5b18bd2c9f346 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php +++ b/src/Symfony/Component/Messenger/Bridge/Redis/Transport/Connection.php @@ -79,7 +79,7 @@ public function __construct(array $options, \Redis|Relay|\RedisCluster|null $red $sentinelMaster = $options['sentinel'] ?? $options['redis_sentinel'] ?? $options['sentinel_master'] ?? null; if (null !== $sentinelMaster && !class_exists(\RedisSentinel::class) && !class_exists(Sentinel::class)) { - throw new InvalidArgumentException('Redis Sentinel support requires ext-redis>=5.2, or ext-relay.'); + throw new InvalidArgumentException('Redis Sentinel support requires ext-redis>=6.1, or ext-relay.'); } if (null !== $sentinelMaster && $redis instanceof \RedisCluster) { @@ -697,6 +697,7 @@ public function getMessageCount(): int } // Iterate through the stream. See https://redis.io/commands/xrange/#iterating-a-stream. + $useExclusiveRangeInterval = version_compare(phpversion('redis'), '6.2.0', '>='); $total = 0; while (true) { if (!$range = $redis->xRange($this->stream, $lastDeliveredId, '+', 100)) { @@ -705,7 +706,11 @@ public function getMessageCount(): int $total += \count($range); - $lastDeliveredId = preg_replace_callback('#\d+$#', static fn (array $matches) => (int) $matches[0] + 1, array_key_last($range)); + if ($useExclusiveRangeInterval) { + $lastDeliveredId = preg_replace_callback('#\d+$#', static fn (array $matches) => (int) $matches[0] + 1, array_key_last($range)); + } else { + $lastDeliveredId = '('.array_key_last($range); + } } } diff --git a/src/Symfony/Component/Messenger/Bridge/Redis/composer.json b/src/Symfony/Component/Messenger/Bridge/Redis/composer.json index a1d17a472b1cf..aee4d1722b27f 100644 --- a/src/Symfony/Component/Messenger/Bridge/Redis/composer.json +++ b/src/Symfony/Component/Messenger/Bridge/Redis/composer.json @@ -26,7 +26,7 @@ "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "conflict": { - "ext-redis": "<6.2", + "ext-redis": "<6.1", "ext-relay": "<0.12" }, "autoload": { From 676a3f28fcc866f4be07953eb607bd4d191a7b35 Mon Sep 17 00:00:00 2001 From: Alexandr Samuilov Date: Thu, 6 Nov 2025 22:26:06 +0200 Subject: [PATCH 42/91] [Validator] Reviewed and corrected Belarussian translations for the Validator component in the validators.be.xlf file --- .../Resources/translations/validators.be.xlf | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf index d7060b5e0bdc5..13c6d43a23026 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf @@ -20,7 +20,7 @@ The value you selected is not a valid choice. - Абранае вамі значэнне не сапраўднае. + Выбранае вамі значэнне не з’яўляецца сапраўдным. You must select at least {{ limit }} choice.|You must select at least {{ limit }} choices. @@ -32,7 +32,7 @@ One or more of the given values is invalid. - Адзін або некалькі пазначаных значэнняў з'яўляецца несапраўдным. + Адно або некалькі з зададзеных значэнняў не з’яўляюцца сапраўднымі. This field was not expected. @@ -76,15 +76,15 @@ This value is too long. It should have {{ limit }} character or less.|This value is too long. It should have {{ limit }} characters or less. - Значэнне занадта доўгае. Яно павінна мець {{ limit }} сімвал або менш.|Значэнне занадта доўгае. Яно павінна мець {{ limit }} сімвалаў або менш. + Значэнне занадта доўгае. Яно павінна мець не больш за {{ limit }} сімвал.|Значэнне занадта доўгае. Яно павінна мець не больш за {{ limit }} сімвалаў. This value should be {{ limit }} or more. - Значэнне павінна быць {{ limit }} або больш. + Значэнне павінна быць не менш за {{ limit }}. This value is too short. It should have {{ limit }} character or more.|This value is too short. It should have {{ limit }} characters or more. - Значэнне занадта кароткае. Яно павінна мець прынамсі {{ limit }} сімвал.|Значэнне занадта кароткае. Яно павінна мець прынамсі {{ limit }} сімвалаў. + Значэнне занадта кароткае. Яно павінна мець не менш за {{ limit }} сімвал.|Значэнне занадта кароткае. Яно павінна мець не менш за {{ limit }} сімвалаў. This value should not be blank. @@ -100,7 +100,7 @@ This value is not valid. - Значэнне з'яўляецца не сапраўдным. + Значэнне не з'яўляецца сапраўдным. This value is not a valid time. @@ -112,7 +112,7 @@ The two values should be equal. - Абодва значэнні павінны быць аднолькавымі. + Абодва значэнні павінны супадаць. The file is too large. Allowed maximum size is {{ limit }} {{ suffix }}. @@ -140,7 +140,7 @@ This value is not a valid language. - Значэнне не з'яўляецца сапраўдным мовай. + Значэнне не з'яўляецца сапраўднай мовай. This value is not a valid locale. @@ -168,11 +168,11 @@ The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px. - Гэты выява занадта вялікая ({{ width }}px). Дазваляецца максімальная вышыня {{ max_width }}px. + Гэтая выява занадта высокая ({{ height }}px). Дазваляецца максімальная вышыня — {{ max_height }}px. The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px. - Гэта выява занадта маленькая ({{ width }}px). Дазваляецца мінімальная вышыня {{ min_width }}px. + Гэтая выява занадта нізкая ({{ height }}px). Дазваляецца мінімальная вышыня — {{ min_height }}px. This value should be the user's current password. @@ -204,11 +204,11 @@ This collection should contain {{ limit }} element or more.|This collection should contain {{ limit }} elements or more. - Калекцыя павінна змяшчаць прынамсі {{ limit }} элемент.|Калекцыя павінна змяшчаць прынамсі {{ limit }} элементаў. + Калекцыя павінна змяшчаць не менш за {{ limit }} элемент.|Калекцыя павінна змяшчаць не менш за {{ limit }} элементаў. This collection should contain {{ limit }} element or less.|This collection should contain {{ limit }} elements or less. - Калекцыя павінна змяшчаць {{ limit }} або менш элемент.|Калекцыя павінна змяшчаць {{ limit }} або менш элементаў. + Калекцыя павінна змяшчаць не больш за {{ limit }} элемент.|Калекцыя павінна змяшчаць не больш за {{ limit }} элементаў. This collection should contain exactly {{ limit }} element.|This collection should contain exactly {{ limit }} elements. @@ -236,7 +236,7 @@ This value is neither a valid ISBN-10 nor a valid ISBN-13. - Гэта значэнне не з'яўляецца сапраўдным ISBN-10 або ISBN-13. + Гэта значэнне не з'яўляецца ні сапраўдным ISBN-10, ні сапраўдным ISBN-13. This value is not a valid ISSN. @@ -252,11 +252,11 @@ This value should be greater than {{ compared_value }}. - Значэнне павінна быць больш чым {{ compared_value }}. + Значэнне павінна быць больш за {{ compared_value }}. This value should be greater than or equal to {{ compared_value }}. - Значэнне павінна быць больш чым або раўняцца {{ compared_value }}. + Значэнне павінна быць больш за або роўнае {{ compared_value }}. This value should be identical to {{ compared_value_type }} {{ compared_value }}. @@ -264,11 +264,11 @@ This value should be less than {{ compared_value }}. - Значэнне павінна быць менш чым {{ compared_value }}. + Значэнне павінна быць менш за {{ compared_value }}. This value should be less than or equal to {{ compared_value }}. - Значэнне павінна быць менш чым або раўняцца {{ compared_value }}. + Значэнне павінна быць менш за або роўнае {{ compared_value }}. This value should not be equal to {{ compared_value }}. @@ -280,11 +280,11 @@ The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. - Суадносіны бакоў выявы з'яўляецца занадта вялікім ({{ ratio }}). Дазваляецца максімальныя суадносіны {{max_ratio}} . + Суадносіны бакоў выявы занадта вялікія ({{ ratio }}). Дазваляецца максімальныя суадносіны {{max_ratio}} . The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. - Суадносіны бакоў выявы з'яўляецца занадта маленькімі ({{ ratio }}). Дазваляецца мінімальныя суадносіны {{ min_ratio }}. + Суадносіны бакоў выявы занадта малыя ({{ ratio }}). Дазваляецца мінімальныя суадносіны {{ min_ratio }}. The image is square ({{ width }}x{{ height }}px). Square images are not allowed. @@ -304,7 +304,7 @@ The host could not be resolved. - Не магчыма знайсці імя хоста. + Не магчыма вызначыць імя хоста. This value does not match the expected {{ charset }} charset. @@ -344,7 +344,7 @@ This value should be either positive or zero. - Значэнне павінна быць дадатным ці нуль. + Значэнне павінна быць або дадатным, або роўным нулю. This value should be negative. @@ -352,7 +352,7 @@ This value should be either negative or zero. - Значэнне павінна быць адмоўным ці нуль. + Значэнне павінна быць або адмоўным, або роўным нулю. This value is not a valid timezone. @@ -368,11 +368,11 @@ This value is not a valid hostname. - Значэнне не з'яўляецца карэктным імем хаста. + Значэнне не з'яўляецца сапраўднай/карэктнай назвай хоста. The number of elements in this collection should be a multiple of {{ compared_value }}. - Колькасць элементаў у гэтай калекцыі павінна быць кратным {{compared_value}}. + Колькасць элементаў у гэтай калекцыі павінна быць кратнай {{compared_value}}. This value should satisfy at least one of the following constraints: @@ -388,7 +388,7 @@ This value should be a valid expression. - Значэнне не з'яўляецца сапраўдным выразам. + Значэнне павінна быць сапраўдным выразам. This value is not a valid CSS color. @@ -404,7 +404,7 @@ The filename is too long. It should have {{ filename_max_length }} character or less.|The filename is too long. It should have {{ filename_max_length }} characters or less. - Назва файла занадта доўгая. Ён павінен мець {{ filename_max_length }} сімвал або менш.|Назва файла занадта доўгая. Ён павінен мець {{ filename_max_length }} сімвалы або менш.|Назва файла занадта доўгая. Ён павінен мець {{ filename_max_length }} сімвалаў або менш. + Назва файла занадта доўгая. Яна павинна мець {{ filename_max_length }} сімвал або менш.|Назва файла занадта доўгая. Яна павінна мець {{ filename_max_length }} сімвалы або менш.|Назва файла занадта доўгая. Яна павінна мець {{ filename_max_length }} сімвалаў або менш. The password strength is too low. Please use a stronger password. @@ -420,7 +420,7 @@ Mixing numbers from different scripts is not allowed. - Змешванне лікаў з розных алфавітаў не дапускаецца. + Змешванне лічбаў з розных алфавітаў не дапускаецца. Using hidden overlay characters is not allowed. @@ -440,7 +440,7 @@ This URL is missing a top-level domain. - Гэтаму URL бракуе дамен верхняга ўзроўню. + У гэтым URL няма дамена верхняга ўзроўню. This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words. @@ -472,83 +472,83 @@ This file is not a valid video. - Гэты файл не з'яўляецца сапраўдным відэа. + Гэты файл не з'яўляецца сапраўдным відэа. The size of the video could not be detected. - Не ўдалося вызначыць памер відэа. + Не ўдалося вызначыць памер відэа. The video width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px. - Шырыня відэа занадта вялікая ({{ width }}px). Дапушчальная максімальная шырыня — {{ max_width }}px. + Шырыня відэа занадта вялікая ({{ width }}px). Дапушчальная максімальная шырыня — {{ max_width }}px. The video width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px. - Шырыня відэа занадта малая ({{ width }}px). Мінімальная чаканая шырыня — {{ min_width }}px. + Шырыня відэа занадта малая ({{ width }}px). Мінімальная чаканая шырыня — {{ min_width }}px. The video height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px. - Вышыня відэа занадта вялікая ({{ height }}px). Дазволеная максімальная вышыня — {{ max_height }}px. + Вышыня відэа занадта вялікая ({{ height }}px). Дазволеная максімальная вышыня — {{ max_height }}px. The video height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px. - Вышыня відэа занадта малая ({{ height }}px). Чаканая мінімальная вышыня — {{ min_height }}px. + Вышыня відэа занадта малая ({{ height }}px). Чаканая мінімальная вышыня — {{ min_height }}px. The video has too few pixels ({{ pixels }} pixels). Minimum amount expected is {{ min_pixels }} pixels. - Відэа мае занадта мала пікселяў ({{ pixels }}). Мінімальная колькасць чакаецца {{ min_pixels }}. + Відэа мае занадта мала пікселяў ({{ pixels }}). Мінімальная колькасць чакаецца {{ min_pixels }}. The video has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels. - Відэа мае занадта шмат пікселяў ({{ pixels }}). Максімальна дапушчальная колькасць — {{ max_pixels }}. + Відэа мае занадта шмат пікселяў ({{ pixels }}). Максімальна дапушчальная колькасць — {{ max_pixels }}. The video ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. - Суадносіны відэа занадта вялікія ({{ ratio }}). Дапушчальна максімальнае суадносіны — {{ max_ratio }}. + Суадносіны відэа занадта вялікія ({{ ratio }}). Дапушчальнае максімальнае суадносіна — {{ max_ratio }}. The video ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. - Суадносіны відэа занадта малыя ({{ ratio }}). Мінімальнае чаканае суадносіны — {{ min_ratio }}. + Суадносіны відэа занадта малыя ({{ ratio }}). Мінімальнае чаканае суадносіна — {{ min_ratio }}. The video is square ({{ width }}x{{ height }}px). Square videos are not allowed. - Відэа квадратнае ({{ width }}x{{ height }}px). Квадратныя відэа не дазволены. + Відэа квадратнае ({{ width }}x{{ height }}px). Квадратныя відэа не дазваляюцца. The video is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented videos are not allowed. - Відэа ў ландшафтнай арыентацыі ({{ width }}x{{ height }} пікс.). Ландшафтныя відэа не дазваляюцца. + Відэа ў ландшафтнай арыентацыі ({{ width }}x{{ height }} px). Ландшафтныя відэа не дазваляюцца. The video is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented videos are not allowed. - Відэа ў партрэтнай арыентацыі ({{ width }}x{{ height }}px). Відэа ў партрэтнай арыентацыі не дазваляюцца. + Відэа ў партрэтнай арыентацыі ({{ width }}x{{ height }}px). Відэа ў партрэтнай арыентацыі не дазваляюцца. The video file is corrupted. - Відэафайл пашкоджаны. + Відэафайл пашкоджаны. The video contains multiple streams. Only one stream is allowed. - Відэа змяшчае некалькі патокаў. Дазволены толькі адзін паток. + Відэа змяшчае некалькі патокаў. Дазволены толькі адзін паток. Unsupported video codec "{{ codec }}". - Непадтрымліваемы відэакодэк «{{ codec }}». + Непадтрымліваемы відэакодэк «{{ codec }}». Unsupported video container "{{ container }}". - Непадтрымліваемы кантэйнер відэа "{{ container }}". + Непадтрымліваемы кантэйнер відэа "{{ container }}". The image file is corrupted. - Файл выявы пашкоджаны. + Файл выявы пашкоджаны. The image has too few pixels ({{ pixels }} pixels). Minimum amount expected is {{ min_pixels }} pixels. - У выяве занадта мала пікселяў ({{ pixels }}). Чакаемы мінімум — {{ min_pixels }}. + У выяве занадта мала пікселяў ({{ pixels }}). Мінімальная колькасць — {{ min_pixels }}. The image has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels. - Малюнак мае занадта шмат пікселяў ({{ pixels }}). Чаканая максімальная колькасць {{ max_pixels }}. + Малюнак мае занадта шмат пікселяў ({{ pixels }}). Максімальная дапушчальная колькасць {{ max_pixels }}. This filename does not match the expected charset. From 3d30da5c2fc0fc30f6a7a7970854d72c6fca946a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 6 Nov 2025 21:52:08 +0100 Subject: [PATCH 43/91] Update CHANGELOG for 6.4.28 --- CHANGELOG-6.4.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG-6.4.md b/CHANGELOG-6.4.md index c59232c251108..08f66befc59fc 100644 --- a/CHANGELOG-6.4.md +++ b/CHANGELOG-6.4.md @@ -7,6 +7,20 @@ in 6.4 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.4.0...v6.4.1 +* 6.4.28 (2025-11-06) + + * bug #62324 [HttpFoundation] Fix parsing hosts and schemes in URLs (nicolas-grekas) + * bug #62171 [Messenger] Fix commands writing to `STDERR` instead of `STDOUT` (wazum) + * bug #62315 Keep body size limit for AMP redirects (villermen) + * bug #62091 [BrowserKit] The BrowserKit history with parameter separator without slash. (biozshock) + * bug #62290 [Routing] Fix matching the "0" URL (cs278) + * bug #62285 [HttpClient] Reject 3xx pushed responses (nicolas-grekas) + * bug #62267 [Config] Use the empty string instead of null as an array offset (santysisi) + * bug #62246 [HttpFoundation] Allow Request::setFormat() to override predefined formats (longwave) + * bug #62242 [MonologBridge] Accept HttpExceptionInterface in HttpCodeActivationStrategy (GromNaN) + * bug #62222 [Cache] fix ext-redis 6.2.0 compatibility (xabbuh) + * bug #62201 [HtmlSanitizer] Remove `srcdoc` from allowed attributes (Spomky) + * 6.4.27 (2025-10-28) * bug #62145 [Mailer] Fix parsing message ids in SMTP responses (hacfi) From 2c3c2cff64e142b4fea246c55f307fb2c4e2488d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 6 Nov 2025 21:52:24 +0100 Subject: [PATCH 44/91] Update CONTRIBUTORS for 6.4.28 --- CONTRIBUTORS.md | 7175 ++++++++++++++++++++++++----------------------- 1 file changed, 3589 insertions(+), 3586 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 27aed59237b4d..200f2c332bf01 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -10,8 +10,8 @@ The Symfony Connect username in parenthesis allows to get more information - Alexander M. Turek (derrabus) - Oskar Stark (oskarstark) - Robin Chalas (chalas_r) - - Bernhard Schussek (bschussek) - Alexandre Daubois (alexandre-daubois) + - Bernhard Schussek (bschussek) - Matthias Schmidt - Tobias Schultze (tobion) - Grégoire Pineau (lyrixx) @@ -21,8 +21,8 @@ The Symfony Connect username in parenthesis allows to get more information - Wouter de Jong (wouterj) - Yonel Ceruto (yonelceruto) - Jordi Boggiano (seldaek) - - Maxime Steinhausser (ogizanagi) - HypeMC (hypemc) + - Maxime Steinhausser (ogizanagi) - Kévin Dunglas (dunglas) - Javier Eguiluz (javier.eguiluz) - Victor Berchet (victor) @@ -41,3961 +41,3964 @@ The Symfony Connect username in parenthesis allows to get more information - Romain Neutron - Kevin Bond (kbond) - Joseph Bielawski (stloyd) - - Abdellatif Ait boudad (aitboudad) - Drak (drak) + - Abdellatif Ait boudad (aitboudad) - Lukas Kahwe Smith (lsmith) - Hamza Amrouche (simperfit) - Martin Hasoň (hason) - - Jeremy Mikola (jmikola) - matlec + - Santiago San Martin (santysisi) + - Jeremy Mikola (jmikola) - Mathieu Santostefano (welcomattic) - Jean-François Simon (jfsimon) - Benjamin Eberlei (beberlei) - Igor Wiedler - Jan Schädlich (jschaedl) - - Santiago San Martin (santysisi) - Mathieu Lechat (mat_the_cat) - Vincent Langlet (deviling) - Simon André (simonandre) - - Matthias Pigulla (mpdude) - - Gabriel Ostrolucký (gadelat) - Antonio J. García Lagar (ajgarlag) - Kévin THERAGE (kevin_therage) - - Jonathan Wage (jwage) + - Matthias Pigulla (mpdude) + - Gabriel Ostrolucký (gadelat) - corradogrimoldi + - Jonathan Wage (jwage) + - Valtteri R (valtzu) - Valentin Udaltsov (vudaltsov) - Grégoire Paris (greg0ire) - - Valtteri R (valtzu) - Alexandre Salomé (alexandresalome) - William DURAND + - Pierre du Plessis (pierredup) - Dany Maillard (maidmaid) - - Francis Besset (francisbesset) - - Titouan Galopin (tgalopin) + - Alexander Schranz (alexander-schranz) - stealth35 ‏ (stealth35) - - Pierre du Plessis (pierredup) - Diego Saint Esteben (dosten) + - Gábor Egyed (1ed) + - Titouan Galopin (tgalopin) + - Francis Besset (francisbesset) - Alexander Mols (asm89) - - Alexander Schranz (alexander-schranz) - Eriksen Costa - - Gábor Egyed (1ed) - Tomasz Kowalczyk (thunderer) - David Maicher (dmaicher) - Bulat Shakirzyanov (avalanche123) + - Gary PEGEOT (gary-p) - Iltar van der Berg - Miha Vrhovnik (mvrhov) - - Gary PEGEOT (gary-p) - - Allison Guilhem (a_guilhem) - Saša Stamenković (umpirsky) - - Ruud Kamphuis (ruudk) + - Allison Guilhem (a_guilhem) - Mathieu Piot (mpiot) + - Ruud Kamphuis (ruudk) + - Pierre-Emmanuel CAPEL - Vasilij Duško (staff) - Tomas Norkūnas (norkunas) - - Konstantin Kudryashov (everzet) - Sarah Khalil (saro0h) + - Konstantin Kudryashov (everzet) - Laurent VOULLEMIER (lvo) - - Bilal Amarni (bamarni) - Guilhem N (guilhemn) + - Bilal Amarni (bamarni) - Eriksen Costa - Vladimir Reznichenko (kalessil) - Florin Patan (florinpatan) - - Henrik Bjørnskov (henrikbjorn) - Peter Rehm (rpet) + - Henrik Bjørnskov (henrikbjorn) - David Buchmann (dbu) - Hubert Lenoir (hubert_lenoir) - Andrej Hudec (pulzarraider) - Jáchym Toušek (enumag) + - Christian Raue - Eric Clemmons (ericclemmons) - Arnout Boks (aboks) - - Christian Raue - - Michel Weimerskirch (mweimerskirch) - - Pierre-Emmanuel CAPEL + - Antoine Makdessi (amakdessi) - Douglas Greenshields (shieldo) - - Denis (yethee) - - Alex Pott - Issei Murasawa (issei_m) - - Antoine Makdessi (amakdessi) + - Michel Weimerskirch (mweimerskirch) + - Alex Pott + - Denis (yethee) - Baldini - Frank A. Fiebig (fafiebig) - Fran Moreno (franmomu) - - Dariusz Górecki (canni) - Charles Sarrazin (csarrazi) - Henrik Westphal (snc) - soyuka + - Dariusz Górecki (canni) - Ener-Getick - Massimiliano Arione (garak) - - Phil E. Taylor (philetaylor) - - Graham Campbell (graham) - Tugdual Saunier (tucksaun) + - Graham Campbell (graham) + - Phil E. Taylor (philetaylor) - Joel Wurtz (brouznouf) - - Lee McDermott - Brandon Turner - Luis Cordova (cordoval) - - Bart van den Burg (burgov) + - Lee McDermott - Toni Uebernickel (havvg) - Vasilij Dusko | CREATION - - Daniel Holmes (dholmes) - - Julien Falque (julienfalque) - - Jordan Alliot (jalliot) + - Bart van den Burg (burgov) - Konstantin Myakshin (koc) - - Yanick Witschi (toflar) - - Théo FIDRY + - Jordan Alliot (jalliot) + - Julien Falque (julienfalque) + - Daniel Holmes (dholmes) - John Wards (johnwards) + - Théo FIDRY + - Yanick Witschi (toflar) - Antoine Hérault (herzult) - Konstantin.Myakshin - - Arnaud Le Blanc (arnaud-lb) - - Rokas Mikalkėnas (rokasm) - - Maxime STEINHAUSSER - Jeroen Spee (jeroens) - - Sebastiaan Stok (sstok) - Tac Tacelosky (tacman1123) - - Jacob Dreesen (jdreesen) - - Brice BERNARD (brikou) + - Rokas Mikalkėnas (rokasm) + - Sebastiaan Stok (sstok) + - Arnaud Le Blanc (arnaud-lb) + - Maxime STEINHAUSSER - gnito-org - - Peter Kokot (peterkokot) - Tim Nagel (merk) + - Jacob Dreesen (jdreesen) + - Florent Morselli (spomky_) + - Peter Kokot (peterkokot) - Jérôme Vasseur (jvasseur) + - Brice BERNARD (brikou) - Chris Wilkinson (thewilkybarkid) - - Nicolas Philippe (nikophil) + - Vladimir Tsykun (vtsykun) - Lars Strojny (lstrojny) - marc.weistroff + - Nicolas Philippe (nikophil) - Michal Piotrowski - - Vladimir Tsykun (vtsykun) - - Florent Morselli (spomky_) - - Javier Spagnoletti (phansys) - Adrien Brault (adrienbrault) + - David Prévot (taffit) + - Javier Spagnoletti (phansys) - Włodzimierz Gajda (gajdaw) - - Teoh Han Hui (teohhanhui) - Colin Frei - Przemysław Bogusz (przemyslaw-bogusz) - Florian Voutzinos (florianv) + - Younes ENNAJI (yokho) + - Teoh Han Hui (teohhanhui) - Paráda József (paradajozsef) - - Gordon Franke (gimler) - - Dāvis Zālītis (k0d3r1s) - - Fabien Pennequin (fabienpennequin) - - Gregor Harlan (gharlan) - - Maximilian Beckers (maxbeckers) - Alexander Schwenn (xelaris) + - Maximilian Beckers (maxbeckers) + - Fabien Pennequin (fabienpennequin) + - Dāvis Zālītis (k0d3r1s) + - Gordon Franke (gimler) - Saif Eddin Gmati (azjezz) - Baptiste Clavié (talus) + - Gregor Harlan (gharlan) - Maxime Helias (maxhelias) - - François-Xavier de Guillebon (de-gui_f) - - Alexis Lefebvre - - Malte Schlüter (maltemaltesich) - - jeremyFreeAgent (jeremyfreeagent) - - Vasilij Dusko - - Christopher Hertel (chertel) + - Bob van de Vijver (bobvandevijver) - Daniel Wehner (dawehner) - Hugo Alliaume (kocal) + - François-Xavier de Guillebon (de-gui_f) + - Malte Schlüter (maltemaltesich) - Michael Babker (mbabker) - - Bob van de Vijver (bobvandevijver) + - Christopher Hertel (chertel) + - Vasilij Dusko - Joshua Thijssen - - Niels Keurentjes (curry684) + - Alexis Lefebvre + - jeremyFreeAgent (jeremyfreeagent) - Jérôme Parmentier (lctrs) - - Andreas Schempp (aschempp) - - Smaine Milianni (ismail1432) - - OGAWA Katsuhiro (fivestar) - - Stefano Sala (stefano.sala) - - Robert Schönthal (digitalkaoz) + - Ion Bazan (ionbazan) - Gabriel Caruso + - Stefano Sala (stefano.sala) - Richard van Laak (rvanlaak) - Eric GELOEN (gelo) - - Jonathan Scheiber (jmsche) + - Smaine Milianni (ismail1432) - Jhonny Lidfors (jhonne) - - Ion Bazan (ionbazan) + - Robert Schönthal (digitalkaoz) + - OGAWA Katsuhiro (fivestar) + - Jonathan Scheiber (jmsche) + - Niels Keurentjes (curry684) + - Andreas Schempp (aschempp) - Sebastian Hörl (blogsh) - - Tigran Azatyan (tigranazatyan) - - Juti Noppornpitak (shiroyuki) - - Arnaud Kleinpeter (nanocom) - - Hidenori Goto (hidenorigoto) - - Thomas Landauer (thomas-landauer) - Daniel Gomes (danielcsgomes) - - Florent Mata (fmata) - - Anthony MARTIN + - Thomas Landauer (thomas-landauer) + - Hidenori Goto (hidenorigoto) + - Arnaud Kleinpeter (nanocom) + - Tigran Azatyan (tigranazatyan) - Guilherme Blanco (guilhermeblanco) - - David Prévot (taffit) - - Alessandro Chitolina (alekitto) - - Roman Martinuk (a2a4) - - Pablo Godel (pgodel) - - jwdeitch + - Anthony MARTIN + - Florent Mata (fmata) + - Juti Noppornpitak (shiroyuki) - Michael Käfer (michael_kaefer) - - Farhad Safarov (safarov) - - Jan Rosier (rosier) - Fritz Michael Gschwantner (fritzmg) + - jwdeitch + - Jan Rosier (rosier) - Rafael Dohms (rdohms) + - Roman Martinuk (a2a4) + - Pablo Godel (pgodel) + - Farhad Safarov (safarov) + - Alessandro Chitolina (alekitto) - Albert Casademont (acasademont) - - Philipp Wahala (hifi) - - Romain Monteil (ker0x) - Vyacheslav Pavlov - - Tomas Votruba (tomas_votruba) - Jérémy Derussé - - Richard Shank (iampersistent) - - Stiven Llupa (sllupa) + - Philipp Wahala (hifi) + - Andréia Bohner (andreia) + - Tomas Votruba (tomas_votruba) + - Romain Monteil (ker0x) - Matthieu Napoli (mnapoli) - Dawid Nowak + - Ahmed TAILOULOUTE (ahmedtai) + - Richard Shank (iampersistent) + - George Mponos (gmponos) + - Roland Franssen - Tom Van Looy (tvlooy) + - Sokolov Evgeniy (ewgraf) - Ben Davies (bendavies) - - Arman Hosseini (arman) - - Andréia Bohner (andreia) - Simon Berger - - Sokolov Evgeniy (ewgraf) - - George Mponos (gmponos) - - Ahmed TAILOULOUTE (ahmedtai) - - Roland Franssen - - Jannik Zschiesche + - Stiven Llupa (sllupa) + - Arman Hosseini (arman) + - Sergey (upyx) + - Antonio Pauletich (x-coder264) + - Oleg Voronkovich - GDIBass - - Alessandro Lai (jean85) - - Fabien Bourigault (fbourigault) - - Artur Kotyrba - - Wouter J + - Vincent Touzet (vincenttouzet) + - Jannik Zschiesche + - Tyson Andre + - Alex Hofbauer (alexhofbauer) + - Marco Pivetta (ocramius) + - Daniel Burger + - Olivier Dolbeau (odolbeau) + - Matthieu Ouellette-Vachon (maoueh) + - Guillaume (guill) - Amal Raghav (kertz) + - Fabien Bourigault (fbourigault) + - Michał Pipa (michal.pipa) + - Samuel NELA (snela) + - YaFou + - wkania - Indra Gunawan (indragunawan) + - Artur Kotyrba - Jonathan Ingram - - Alex Hofbauer (alexhofbauer) + - Alessandro Lai (jean85) - 77web - - Sergey (upyx) - - Olivier Dolbeau (odolbeau) - - wkania - - Marco Pivetta (ocramius) - - Matthieu Ouellette-Vachon (maoueh) - - Antonio Pauletich (x-coder264) - - Daniel Burger - - Gocha Ossinkine (ossinkine) - - Vincent Touzet (vincenttouzet) - Jesse Rushlow (geeshoe) - - Oleg Voronkovich + - Wouter J + - Gocha Ossinkine (ossinkine) - Clemens Tolboom - - YaFou - - Michał Pipa (michal.pipa) - Rouven Weßling (realityking) - - Guillaume (guill) - - Tyson Andre - - Samuel NELA (snela) - - Arnaud PETITPAS (apetitpa) - - Mario A. Alvarez Garcia (nomack84) - - Marek Štípek (maryo) - Quynh Xuan Nguyen (seriquynh) - - Justin Hileman (bobthecow) - - Mikael Pajunen - - Marko Kaznovac (kaznovac) - - Dorian Villet (gnutix) + - Mario A. Alvarez Garcia (nomack84) + - Richard Miller + - Larry Garfield (crell) - Clément JOBEILI (dator) - - Michael Voříšek - - Nate Wiebe (natewiebe13) - - Daniel Espendiller + - Thomas Rabaix (rande) + - Anthony GRASSIOT (antograssiot) + - Dorian Villet (gnutix) + - Arnaud PETITPAS (apetitpa) + - Aleksandar Jakovljevic (ajakov) + - Dmitrii Chekaliuk (lazyhammer) - Colin O'Dell (colinodell) - - D (denderello) - - Andreas Möller (localheinz) - - Alan Poulain (alanpoulain) + - Daniel Espendiller - Vincent AUBERT (vincent) + - Christian Scheb + - Chi-teck + - Michael Voříšek + - Marko Kaznovac (kaznovac) - Asis Pattisahusiwa - - Martin Hujer (martinhujer) - - Warnar Boekkooi (boekkooi) + - Victor Bocharsky (bocharsky_bw) + - D (denderello) - Sébastien Alfaiate (seb33300) - - Larry Garfield (crell) - - Aleksandar Jakovljevic (ajakov) - - zairig imad (zairigimad) - - Baptiste Leduc (korbeil) - - Thomas Rabaix (rande) - - Christian Scheb + - Quentin Devos + - Nate Wiebe (natewiebe13) + - Justin Hileman (bobthecow) + - Martin Hujer (martinhujer) + - Warnar Boekkooi (boekkooi) - James Halsall (jaitsu) + - Mikael Pajunen + - Andreas Möller (localheinz) + - zairig imad (zairigimad) - DQNEO - - Dmitrii Chekaliuk (lazyhammer) + - Baptiste Leduc (korbeil) + - Marek Štípek (maryo) + - Alan Poulain (alanpoulain) - Sergey Linnik (linniksa) - - Anthony GRASSIOT (antograssiot) - - Victor Bocharsky (bocharsky_bw) - - Chi-teck - - Richard Miller - - Quentin Devos - - Stepan Anchugov (kix) + - Timo Bakx (timobakx) + - Karoly Gossler (connorhu) + - Christian Schmidt - Noel Guilbert (noel) - - Stadly - Martin Schuhfuß (usefulthink) - - Giorgio Premi + - Andre Rømcke (andrerom) + - bronze1man + - Markus Fasselt (digilist) + - Pierre Minnieur (pminnieur) - Loick Piera (pyrech) - - Filippo Tessarotto (slamdunk) - - Christian Schmidt + - Leo Feyer - Patrick Landolt (scube) - - Andreas Hucks (meandmymonkey) - - Denis Brumann (dbrumann) - - Andre Rømcke (andrerom) + - Guilliam Xavier + - Stepan Anchugov (kix) + - Bram Leeda (bram123) + - Giorgio Premi - Mathieu Lemoine (lemoinem) - - Remon van de Kamp - - Timo Bakx (timobakx) + - apetitpa + - mcfedr (mcfedr) - Bastien Jaillot (bastnic) - Ruben Gonzalez (rubenrua) - - Bram Leeda (bram123) - - Markus Fasselt (digilist) - - Leo Feyer + - Denis Brumann (dbrumann) + - Filippo Tessarotto (slamdunk) + - Stadly + - Andreas Hucks (meandmymonkey) + - Remon van de Kamp + - Nikolay Labinskiy (e-moe) - sun (sun) - - Pierre Minnieur (pminnieur) - - Guilliam Xavier - - bronze1man - - Karoly Gossler (connorhu) - Benjamin Dulau (dbenjamin) - - Nikolay Labinskiy (e-moe) - - mcfedr (mcfedr) - - apetitpa - - Anderson Müller - - Sven Paulus (subsven) - - Maciej Malarz (malarzm) - - Michele Orselli (orso) - - Evert Harmeling (evertharmeling) - - Oleg Andreyev (oleg.andreyev) - - Mantis Development - - Leszek Prabucki (l3l0) + - Peter Kruithof (pkruithof) - fd6130 (fdtvui) - - Hugo Monteiro (monteiro) + - Wojciech Kania + - Timothée Barray (tyx) + - John Kary (johnkary) - Tristan Darricau (tristandsensio) - - Jérémie Augustin (jaugustin) - - jeff - - Maxime Veber (nek-) - Michael Lee (zerustech) - - Marcin Sikoń (marphi) - - Thomas Lallement (raziel057) - - Priyadi Iman Nurcahyo (priyadi) - - Marcel Beerta (mazen) - - Jeroen Noten (jeroennoten) + - Dmitrii Poddubnyi (karser) + - Florian Lonqueu-Brochard (florianlb) + - Leszek Prabucki (l3l0) + - François Zaninotto (fzaninotto) + - Evert Harmeling (evertharmeling) - Jonathan H. Wage - - Julien Brochet - - Valentine Boineau (valentineboineau) - - Pascal Montoya + - Michele Orselli (orso) - Võ Xuân Tiến (tienvx) - - Florian Lonqueu-Brochard (florianlb) - Joe Bennett (kralos) - - Peter Kruithof (pkruithof) - - Rui Marinho (ruimarinho) + - jeff + - Pascal Montoya + - Jan Sorgalla (jsor) + - Marcel Beerta (mazen) + - Jérémie Augustin (jaugustin) - henrikbjorn + - Maxime Veber (nek-) + - Priyadi Iman Nurcahyo (priyadi) + - Oleg Andreyev (oleg.andreyev) + - François Pluchino (francoispluchino) + - Mantis Development + - Maciej Malarz (malarzm) - Edi Modrić (emodric) + - roman joly (eltharin) + - Julien Brochet + - Anderson Müller + - Marcin Sikoń (marphi) + - Jeroen Noten (jeroennoten) + - Thomas Lallement (raziel057) - Dustin Whittle (dustinwhittle) + - Sven Paulus (subsven) - Yassine Guedidi (yguedidi) - - Dmitrii Poddubnyi (karser) + - Valentine Boineau (valentineboineau) + - Rui Marinho (ruimarinho) + - Hugo Monteiro (monteiro) - Arjen van der Meijden - - Jan Sorgalla (jsor) - - François Zaninotto (fzaninotto) - - John Kary (johnkary) - - François Pluchino (francoispluchino) - - Wojciech Kania - - Timothée Barray (tyx) - - roman joly (eltharin) - - Sylvain Fabre (sylfabre) - - Chris Smith (cs278) - - Lynn van der Berg (kjarli) - - Bob den Otter (bopp) - Christian Schmidt - - Sullivan SENECHAL (soullivaneuh) - - Pierre Ambroise (dotordu) - - GordonsLondon + - Marc Weistroff (futurecat) + - Thomas Schulz (king2500) - Benoît Burnichon (bburnichon) - - BoShurik - - Joseph Rouff (rouffj) - - Matthieu Lempereur (mryamous) - - Michel Roca (mroca) - - Daniel Tschinder - - Anton Chernikov (anton_ch1989) - - Pierre-Yves Lebecq (pylebecq) - - Daniel Gorgan - - Jordan Samouh (jordansamouh) - - javaDeveloperKid - - Xavier Perez - - Iker Ibarguren (ikerib) - - Benjamin Leveque (benji07) - - Adrian Rudnik (kreischweide) - - maxime.steinhausser - - David Badura (davidbadura) + - Elnur Abdurrakhimov (elnur) + - Chekote - Alif Rachmawadi - - Uwe Jäger (uwej711) - - jdhoek - - Hidde Wieringa (hiddewie) - - Philipp Cordes (corphi) - - dFayet - - Rob Frawley 2nd (robfrawley) - - a.dmitryuk + - Lynn van der Berg (kjarli) - Renan (renanbr) - - Roman Ring (inori) + - Eugene Leonovich (rybakit) + - jdhoek - Félix Labrecque (woodspire) + - a.dmitryuk + - Anton Chernikov (anton_ch1989) + - dFayet + - David Badura (davidbadura) + - Xavier Perez + - siganushka (siganushka) - Fabien S (bafs) + - Arjen Brouwer (arjenjb) + - Michaël Perrin (michael.perrin) + - Xavier Montaña Carreras (xmontana) + - Alexander Kotynia (olden) + - Sylvain Fabre (sylfabre) + - Daniel Gorgan + - Dariusz Ruminski + - Patrick McDougle (patrick-mcdougle) + - Pierre-Yves Lebecq (pylebecq) + - Ray + - maxime.steinhausser + - Manuel Reinhard (sprain) - Zan Baldwin (zanbaldwin) + - Romaric Drigon (romaricdrigon) - Emanuele Panzeri (thepanz) + - Daniel Tschinder + - Sullivan SENECHAL (soullivaneuh) + - Roman Ring (inori) + - BoShurik + - Benjamin Leveque (benji07) + - Jordan Samouh (jordansamouh) + - Adrian Rudnik (kreischweide) + - Bob den Otter (bopp) - Michał (bambucha15) - - Tim Goudriaan (codedmonkey) - - Aurélien Pillevesse (aurelienpillevesse) - - Patrick McDougle (patrick-mcdougle) - - Marc Weistroff (futurecat) - - Kyle - - Manuel Reinhard (sprain) - - Thomas Schulz (king2500) + - Chris Smith (cs278) - Adam Prager (padam87) - - Alexander Kotynia (olden) - - Nikita Konstantinov (unkind) - - Dariusz Ruminski - - Chekote - - Romaric Drigon (romaricdrigon) - - Eugene Leonovich (rybakit) - - Francois Zaninotto - Arnt Gulbrandsen - Thomas Adam - - Xavier Montaña Carreras (xmontana) - - Arjen Brouwer (arjenjb) - - Michaël Perrin (michael.perrin) + - Matthieu Lempereur (mryamous) + - Francois Zaninotto + - Pierre Ambroise (dotordu) + - Philipp Cordes (corphi) + - Aurélien Pillevesse (aurelienpillevesse) + - Iker Ibarguren (ikerib) + - Tim Goudriaan (codedmonkey) + - Joseph Rouff (rouffj) + - Rob Frawley 2nd (robfrawley) + - Kyle + - javaDeveloperKid + - GordonsLondon + - Hidde Wieringa (hiddewie) + - Michel Roca (mroca) + - Nikita Konstantinov (unkind) - Danny Berger (dpb587) - - Elnur Abdurrakhimov (elnur) - - Ray - Jurica Vlahoviček (vjurica) - - Karoly Negyesi (chx) + - Uwe Jäger (uwej711) + - Manuel de Ruiter (manuel) + - Raphaël Geffroy (raphael-geffroy) + - Joppe De Cuyper (joppedc) + - Gustavo Piltcher - Thomas Bisignani (toma) - - SiD (plbsid) + - schlndh + - realmfoo - Sergey Belyshkin (sbelyshkin) - - Damien Alexandre (damienalexandre) - - Thomas Tourlourat (armetiz) - Wodor Wodorski + - Christopher Davis (chrisguitarguy) - Dominique Bongiraud + - Yoann RENARD (yrenard) + - Gabor Toth (tgabi333) - Francesc Rosàs (frosas) - - Dane Powell - - Gustavo Piltcher + - Simon Podlipsky (simpod) + - Robert Kiss (kepten) + - Alex Rock (pierstoval) + - Beau Simensen (simensen) + - Serkan Yildiz (srknyldz) + - Jan Decavele (jandc) + - Atsuhiro KUBO (iteman) + - Vilius Grigaliūnas + - Sébastien Lavoie (lavoiesl) - Wouter Van Hecke - - Pavel Kirpitsov (pavel-kirpichyov) - - Joppe De Cuyper (joppedc) - - Link1515 - - Christian Schmidt - - Andrey Lebedev (alebedev) - - Samaël Villette (samadu61) - - Philippe SEGATORI (tigitz) - - Florian Klein (docteurklein) - - Moshe Weitzman (weitzman) - - Bertrand Zuchuat (garfield-fr) - - Marc Morera (mmoreram) - - Gabor Toth (tgabi333) - - Michael Holm (hollo) - - Raphaël Geffroy (raphael-geffroy) - - Herberto Graca - - Marco Petersen (ocrampete16) + - Kim Hemsø Rasmussen (kimhemsoe) - Matthieu Auger (matthieuauger) - - Ivan Kurnosov - - Emanuele Gaspari (inmarelibero) - - Andrew Moore (finewolf) - - Alexandre Quercia (alquerci) - - Tiago Ribeiro (fixe) - - Andrii Bodnar - - Jordane VASPARD (elementaire) - - Mathieu Rochette (mathroc) + - Josip Kruslin (jkruslin) + - Sebastien Morel (plopix) + - Thomas Tourlourat (armetiz) - Jerzy Zawadzki (jzawadzki) - - Atsuhiro KUBO (iteman) - - Frank de Jonge - - Bohan Yang (brentybh) + - Dane Powell - Craig Duncan (duncan3dc) - - Dalibor Karlović - - Jan Decavele (jandc) - - Michael Hirschler (mvhirsch) - - Loïc Frémont (loic425) - - Alex Rock (pierstoval) - - Josip Kruslin (jkruslin) - - renanbr - - Kim Hemsø Rasmussen (kimhemsoe) + - Marcos Sánchez + - SiD (plbsid) + - Samaël Villette (samadu61) - Cédric Anne - - Blanchon Vincent (blanchonvincent) - - Vilius Grigaliūnas - - Sebastien Morel (plopix) + - Benjamin Morel + - Emanuele Gaspari (inmarelibero) + - Warxcell (warxcell) + - Pavel Batanov (scaytrase) + - Philippe SEGATORI (tigitz) + - Magnus Nordlander (magnusnordlander) + - Manuel Kießling (manuelkiessling) + - Karoly Negyesi (chx) + - Frank de Jonge + - Thibault Gattolliat (crovitche) + - Pascal Luna (skalpa) + - Soner Sayakci - Aurelijus Valeiša (aurelijus) - - Christopher Davis (chrisguitarguy) + - Bohan Yang (brentybh) + - Marc Morera (mmoreram) + - Blanchon Vincent (blanchonvincent) - Baptiste Lafontaine (magnetik) - - Yoann RENARD (yrenard) - - Sébastien Lavoie (lavoiesl) - - Marcos Sánchez - - Raul Fraile (raulfraile) + - renanbr + - Jack Worman (jworman) + - Stepan Tanasiychuk (stfalcon) + - Michael Holm (hollo) + - Maximilian Reichel (phramz) - Grégoire Passault (gregwar) - - Lee Rowlands - - schlndh + - Mathieu Rochette (mathroc) + - Andrii Bodnar - Ismael Ambrosi (iambrosi) - - Andrey Esaulov (andremaha) + - Tiago Ribeiro (fixe) + - Dalibor Karlović + - jaugustin + - Pavel Kirpitsov (pavel-kirpichyov) + - Bertrand Zuchuat (garfield-fr) - Emmanuel BORGES - - Manuel de Ruiter (manuel) - - Simon Podlipsky (simpod) - - Ben Hakim - - Robert Kiss (kepten) - - Maximilian Reichel (phramz) - - Serkan Yildiz (srknyldz) - - Pavel Batanov (scaytrase) - - realmfoo - janschoenherr - - Warxcell (warxcell) - - Stepan Tanasiychuk (stfalcon) - - Alexey Kopytko (sanmai) - - jaugustin + - Alexandre Quercia (alquerci) + - Ben Hakim + - Raul Fraile (raulfraile) - rudy onfroy (ronfroy) - - Pascal Luna (skalpa) - - Beau Simensen (simensen) - - Soner Sayakci - - Jack Worman (jworman) - - Magnus Nordlander (magnusnordlander) - - Manuel Kießling (manuelkiessling) - - Benjamin Morel - - Greg Thornton (xdissent) - - BrokenSourceCode - - Tarmo Leppänen (tarlepp) - - Ivan Mezinov - - siganushka (siganushka) - - Eduardo Gulias (egulias) - - Vyacheslav Salakhutdinov (megazoll) - - Daniel STANCU - - Pierre Rineau - - ivan - - Zmey - - Thibaut Cheymol (tcheymol) - - Dmytro Borysovskyi (dmytr0) - - hossein zolfi (ocean) - - Gwendolen Lynch - - Ben Scott (bpscott) - - Arthur de Moulins (4rthem) - - Ivan Sarastov (isarastov) - - Thomas Perez (scullwm) + - Jordane VASPARD (elementaire) + - Lee Rowlands + - Herberto Graca + - Link1515 + - Alexey Kopytko (sanmai) + - Marco Petersen (ocrampete16) + - Loïc Frémont (loic425) + - Andrey Esaulov (andremaha) + - Andrew Moore (finewolf) + - Michael Hirschler (mvhirsch) + - Ivan Kurnosov + - Christian Schmidt + - Moshe Weitzman (weitzman) + - Andrey Lebedev (alebedev) + - Damien Alexandre (damienalexandre) + - Florian Klein (docteurklein) + - Thomas Royer (cydonia7) - Islam Israfilov (islam93) + - Ivan Sarastov (isarastov) - SUMIDA, Ippei (ippey_s) - - Robbert Klarenbeek (robbertkl) + - vladimir.reznichenko + - Grzegorz (Greg) Zdanowski (kiler129) + - Nicolas LEFEVRE (nicoweb) + - James Gilliland (neclimdul) + - Oscar Cubo Medina (ocubom) + - Haralan Dobrev (hkdobrev) + - BrokenSourceCode + - Kevin van Sonsbeek (kevin_van_sonsbeek) + - Grenier Kévin (mcsky_biig) + - Dave Long + - giulio de donato (liuggio) + - Vyacheslav Salakhutdinov (megazoll) + - Albert Jessurum (ajessu) + - hubert lecorche (hlecorche) + - Marc Biorklund (mbiork) + - Matthias Althaus (althaus) + - Jakub Kucharovic (jkucharovic) + - Yannick Ihmels (ihmels) + - Ilija Tovilo (ilijatovilo) - FORT Pierre-Louis (plfort) + - Thomas Perez (scullwm) + - Tobias Naumann (tna) + - Valmonzo - Vitalii Ekert (comrade42) - - Urinbayev Shakhobiddin (shokhaa) - - Vincent Chalamon - - Yannick Ihmels (ihmels) - - Asmir Mustafic (goetas) - - Martin Herndl (herndlm) - - Hamza Makraz (makraz) + - ivan + - Greg Thornton (xdissent) + - Johann Pardanaud + - Maksym Slesarenko (maksym_slesarenko) - Shein Alexey - - Gonzalo Vilaseca (gonzalovilaseca) - - Roumen Damianoff - - Vitaliy Zakharov (zakharovvi) - - giulio de donato (liuggio) - - Vadim Kharitonov (vadim) - - Valmonzo - - vladimir.reznichenko - - Harm van Tilborg (hvt) - - Jan Böhmer - - Tobias Naumann (tna) - - Issam Raouf (iraouf) - - Jakub Kucharovic (jkucharovic) - - smoench - - Marc Biorklund (mbiork) - - mondrake (mondrake) - - Pablo Lozano (arkadis) - - Mateusz Sip (mateusz_sip) - - Ricard Clau (ricardclau) - - Francesco Levorato - - Kieran Brahney - - Hendrik Luup (hluup) - - Thibault Gattolliat (crovitche) - - Oscar Cubo Medina (ocubom) - - Sanpi (sanpi) + - Nathanael Noblet (gnat) - Raffaele Carelle - - Daniel Beyer + - Kirill chEbba Chebunin + - Laszlo Korte + - ShinDarth + - Francesco Levorato - C (dagardner) - - Philippe Segatori - - Alex Bowers - - Tobias Sjösten (tobiassjosten) - - Dimitri Gritsajuk (ottaviano) - Lorenz Schori - - Felix Labrecque - - Daniel Tschinder - - Grenier Kévin (mcsky_biig) - Rhodri Pugh (rodnaph) - - Diego Agulló (aeoris) + - Tobias Sjösten (tobiassjosten) + - Robbert Klarenbeek (robbertkl) + - Vitaliy Zakharov (zakharovvi) + - hossein zolfi (ocean) + - Eduardo Gulias (egulias) + - Mateusz Sip (mateusz_sip) + - Ahmed Raafat + - Alex (aik099) + - Pavel Volokitin (pvolok) + - Ricard Clau (ricardclau) + - Philippe Segatori + - Pablo Lozano (arkadis) + - Pierre Rineau + - Ben Scott (bpscott) - Clara van Miert - - Haralan Dobrev (hkdobrev) - - kor3k kor3k (kor3k) - - Alessandro Desantis - - Kevin van Sonsbeek (kevin_van_sonsbeek) - - Philipp Kräutli (pkraeutli) - - Matthew Lewinski (lewinski) - Stéphane PY (steph_py) - - Pol Dellaiera (drupol) - - Nathanael Noblet (gnat) - - Gasan Guseynov (gassan) - - Inal DJAFAR (inalgnu) - - Erin Millard - - hubert lecorche (hlecorche) - - Fabien Villepinte - - flack (flack) + - Daniel Beyer - Endre Fejes - - Julien Galenski (ruian) - - Grzegorz (Greg) Zdanowski (kiler129) - - Anthon Pang (robocoder) - - Laszlo Korte - - Maksym Slesarenko (maksym_slesarenko) - - Pavel Volokitin (pvolok) - - Hassan Amouhzi - - Costin Bereveanu (schniper) - - Michele Locati - - Ahmed Raafat - - Terje Bråten - - ShinDarth - - Kirill chEbba Chebunin - - Clément Gautier (clementgautier) - - Yaroslav Kiliba - - Nicolas LEFEVRE (nicoweb) - - Ilija Tovilo (ilijatovilo) - Eric Masoero (eric-masoero) - - Joe Lencioni - - Maximilian Ruta (deltachaos) - - Jeremy Livingston (jeremylivingston) - - Artem (artemgenvald) - - Gyula Sallai (salla) - - Thomas Royer (cydonia7) - - James Gilliland (neclimdul) - - Cătălin Dan (dancatalin) - - Alain Hippolyte (aloneh) - - Albert Jessurum (ajessu) - - Christophe L. (christophelau) + - Erin Millard + - Jan Böhmer + - Kieran Brahney + - Harm van Tilborg (hvt) + - Xavier HAUSHERR + - Terje Bråten + - Thibaut Cheymol (tcheymol) + - Lukáš Holeczy (holicz) - Kristen Gilden - - Kai + - Tarmo Leppänen (tarlepp) + - Vadim Kharitonov (vadim) + - Philipp Kräutli (pkraeutli) + - Joe Lencioni + - Gyula Sallai (salla) + - Daniel STANCU + - Felix Labrecque + - Arthur de Moulins (4rthem) + - Vincent Chalamon + - Martin Herndl (herndlm) + - Costin Bereveanu (schniper) - Marek Kalnik (marekkalnik) - - Johann Pardanaud - - Thierry T (lepiaf) + - Gasan Guseynov (gassan) + - Daniel Tschinder + - Diego Agulló (aeoris) + - Hendrik Luup (hluup) + - Gwendolen Lynch + - smoench + - Ivan Mezinov + - Michele Locati + - Dimitri Gritsajuk (ottaviano) + - Gonzalo Vilaseca (gonzalovilaseca) + - Roumen Damianoff + - Urinbayev Shakhobiddin (shokhaa) + - Julien Galenski (ruian) + - Maximilian Ruta (deltachaos) + - Alain Hippolyte (aloneh) + - Yaroslav Kiliba + - Clément Gautier (clementgautier) + - Sanpi (sanpi) - Marc Morales Valldepérez (kuert) - - Max Baldanza - - Xavier HAUSHERR - - Matthias Althaus (althaus) - - Lukáš Holeczy (holicz) - Karel Souffriau - - Alex (aik099) - - Vladyslav Loboda + - Hamza Makraz (makraz) + - Matthew Lewinski (lewinski) + - Fabien Villepinte + - Alessandro Desantis + - kor3k kor3k (kor3k) + - flack (flack) - Kev - - Ricardo Oliveira (ricardolotr) - - Mark Challoner (markchalloner) - - Neil Peyssard (nepey) - - Rodrigo Borrego Bernabé (rodrigobb) - - Markus Lanthaler (lanthaler) - - Marek Zajac - - Valentin Jonovs - - simon chrzanowski (simonch) - - Benjamin (yzalis) - - Andy Palmer (andyexeter) - - Patrick Reimers (preimers) - - Dave Hulbert (dave1010) - - Thomas Durand - - Zbigniew Malcherczyk (ferror) - - Samuele Lilli (doncallisto) - - Martin Kirilov (wucdbm) - - Pavel Campr (pcampr) - - Thomas Talbot (ioni) - - Vitaliy Tverdokhlib (vitaliytv) - - Sebastian Bergmann + - Zmey + - Dmytro Borysovskyi (dmytr0) + - Asmir Mustafic (goetas) + - Vladyslav Loboda + - Alex Bowers + - Cătălin Dan (dancatalin) + - Artem (artemgenvald) + - Anthon Pang (robocoder) + - Pol Dellaiera (drupol) + - mondrake (mondrake) + - Inal DJAFAR (inalgnu) + - Hassan Amouhzi + - Christophe L. (christophelau) + - Jeremy Livingston (jeremylivingston) + - Issam Raouf (iraouf) + - Max Baldanza + - Kai + - Thierry T (lepiaf) + - Gigino Chianese (sajito) + - kylekatarnls (kylekatarnls) + - Nicolas Dewez (nicolas_dewez) + - Erkhembayar Gantulga (erheme318) + - Steven RENAUX (steven_renaux) + - Matthew Smeets + - Anton Bakai + - Fabrice Bernhard (fabriceb) + - Ben Ramsey (ramsey) - Zach Badgett (zachbadgett) - - Toni Rudolf (toooni) - - Antonin CLAUZIER (0x346e3730) - - Hans Mackowiak - - Aurélien Fredouelle - - Gunnstein Lye (glye) - - Sergey Melesh (sergex) - - Cédric Lombardot (cedriclombardot) - - Jérôme Tanghe (deuchnord) - - Chris Tanaskoski (devristo) - - Ned Schwartz + - Martin Morávek (keeo) + - Bahman Mehrdad (bahman) - Boris Vujicic (boris.vujicic) - - Judicaël RUFFIEUX (axanagor) - - Jeroen Thora (bolle) + - Roman Anasal - Grummfy (grummfy) - - Artem Stepin (astepin) - - Desjardins Jérôme (jewome62) - - sasezaki - - R. Achmad Dadang Nur Hidayanto (dadangnh) - - Jérémy DECOOL (jdecool) - - Niklas Fiekas - - Jaik Dean (jaikdean) - - nathanpage - - Steffen Roßkamp - - Dries Vints - - AKeeman (akeeman) - - Mohammad Emran Hasan (phpfour) - - Florent Destremau (florentdestremau) - - Vicent Soria Durá (vicentgodella) - - Wu (wu-agriconomie) - - Arturs Vonda - - Erkhembayar Gantulga (erheme318) - - Pavel Popov (metaer) - - Arnaud De Abreu (arnaud-deabreu) - - Philipp Rieber (bicpi) - - Andrew M-Y (andr) - - Soufian EZ ZANTAR (soezz) - - Andrew Udvare (audvare) - - Bilge - - Daniel Tiringer - - Ioan Negulescu - - Robert-Jan de Dreu - - Roy Van Ginneken (rvanginneken) - Denis Kulichkin (onexhovia) - - Brian King - Anatoly Pashin (b1rdex) - - boombatower - - Markus Bachmann (baachi) - - Sam Fleming (sam_fleming) - - Restless-ET - - Jiří Bok - - DerManoMann - - Mokhtar Tlili (sf-djuba) - - Greg ORIOL - - Jérôme Macias (jeromemacias) - - Gildas Quéméner (gquemener) - - Geoffrey Tran (geoff) - - Xavier HAUSHERR - - Alexandru Furculita (afurculita) - - Matheo Daninos (mathdns) - - Eduardo Oliveira (entering) - - Luc Vieillescazes (iamluc) - - Benoit Galati (benoitgalati) - - Lenard Palko - - mmokhi - - quentin neyrat (qneyrat) - - Brayden Williams (redstar504) - - Ahmed Ghanem (ahmedghanem00) - - Markus Staab - - Damien Fernandes - - Sergio Santoro - - Benjamin Georgeault (wedgesama) - - Maelan LE BORGNE - - Giso Stallenberg (gisostallenberg) - - Maxim Dovydenok (dovydenok-maxim) - - Yoshio HANAWA - - PHAS Developer - - Scott Arciszewski - - Roman Anasal - - BASAK Semih (itsemih) - - Petrisor Ciprian Daniel - - Anthony Ferrara - - Martin Morávek (keeo) - - ReenExe - - wicliff wolda (wickedone) - - Jonas Flodén (flojon) - - Laurent Masforné (heisenberg) - - Alex Bakhturin - - Tom Klingenberg - - Berny Cantos (xphere81) - - Benjamin Zaslavsky (tiriel) - - Ryan - - Jan Schumann - - Jannik Zschiesche - - Kevin Saliou (kbsali) - - Nate (frickenate) - - W0rma + - Yi-Jyun Pan + - Jerzy Lekowski (jlekowski) + - Vlad Gregurco (vgregurco) - Benjamin Cremer (bcremer) - - vagrant - - Steven RENAUX (steven_renaux) - - Pablo Díez (pablodip) + - Adam Szaraniec + - NickSdot + - Klaus Silveira (klaussilveira) + - Nate (frickenate) - Romain Gautier (mykiwi) - - Matthew Grasmick - - Alexander Deruwe (aderuwe) - - M. (mbontemps) - - Peter Bowyer (pbowyer) + - Jakub Škvára (jskvara) + - Marcin Michalski (marcinmichalski) + - Andy Palmer (andyexeter) + - mamazu + - Marcos Rezende (rezende79) + - François Dume (franek) + - Loïc Faugeron + - Asier Illarramendi (doup) + - Jonathan Johnson (jrjohnson) + - Dmitriy Mamontov (mamontovdmitriy) - Adam Harvey - - Martijn Cuppens - - Piotr Kugla (piku235) - - William Arslett (warslett) - - PatNowak - - Krzysztof Piasecki (krzysztek) - - Remi Collet - - Mathias STRASSER (roukmoute) - - Andrii Dembitskyi - - Webnet team (webnet) - - Erik Trapman - - Erik Saunier (snickers) - - Angelov Dejan (angelov) - - Ivan Rey (ivanrey) - - Quentin Dequippe (qdequippe) - - Marcin Szepczynski (czepol) - - mcben + - Benjamin Georgeault (wedgesama) + - Jannik Zschiesche - Gawain Lynch (gawain) - - Fabrice Bernhard (fabriceb) - - geoffrey - - Ben Ramsey (ramsey) - - Lctrs - - Rob Bast - - Korvin Szanto - - Ziumin - - Andrey Astakhov (aast) + - Luc Vieillescazes (iamluc) + - Erik Trapman + - Shakhobiddin - Marcin Chyłek (songoq) - - Miroslav Šustek (sustmi) - - Andrii Dembitskyi - - Maarten de Boer (mdeboer) - - Barry vd. Heuvel (barryvdh) - - Nadim AL ABDOU (nadim) - - Chris Sedlmayr (catchamonkey) - - Stefan Kruppa - - Steven Surowiec (steves) - - Matthieu Bontemps - - Dmitriy Mamontov (mamontovdmitriy) - - Forfarle (forfarle) - - ilyes kooli (skafandri) - - BENOIT POLASZEK (bpolaszek) + - Jacek Jędrzejewski (jacek.jedrzejewski) + - Bhavinkumar Nakrani (bhavin4u) + - Christoph Mewes (xrstf) + - Markus Bachmann (baachi) + - Roberto Espinoza (respinoza) + - AnneKir + - Sergey Panteleev + - Fabian Lange (codingfabian) + - Dries Vints + - Jérémy M (th3mouk) + - Krzysztof Piasecki (krzysztek) + - PatNowak + - simon chrzanowski (simonch) + - Kamil Kokot (pamil) + - Joachim Løvgaard (loevgaard) - Jan van Thoor (janvt) - - lancergr - - Johnny Robeson (johnny) - - Belhassen Bouchoucha (crownbackend) - - Alireza Mirsepassi (alirezamirsepassi) - - Nicolas Dewez (nicolas_dewez) - - Tobias Bönner - - Claude Khedhiri (ck-developer) + - Dominik Zogg + - Emanuele Iannone + - Mark Challoner (markchalloner) + - Berny Cantos (xphere81) - nikos.sotiropoulos - - Dirk Pahl (dirkaholic) - - Michel Salib (michelsalib) - - Matthew Smeets - - Koen Reiniers (koenre) + - BASAK Semih (itsemih) + - Disquedur - Marvin Feldmann (breyndotechse) - - Norbert Orzechowicz (norzechowicz) - - Sergey Panteleev - - Ben Roberts (benr77) - - Christoph Mewes (xrstf) - - Shawn Iwinski - - Jeanmonod David (jeanmonod) - - Yi-Jyun Pan - - Tri Pham (phamuyentri) - - Matthias Krauser (mkrauser) + - Matthijs van den Bos (matthijs) + - Johan Vlaar (johjohan) + - Raulnet + - Arkadius Stefanski (arkadius) + - Florent Destremau (florentdestremau) + - Samuele Lilli (doncallisto) - Ariel Ferrandini (aferrandini) - - Gijs van Lammeren - - François Dume (franek) - - Jerzy Lekowski (jlekowski) + - Petr Duda (petrduda) + - Oleksii Zhurbytskyi + - Dennis Fridrich (dfridrich) + - Tom Klingenberg + - Dave Hulbert (dave1010) + - lancergr + - Miroslav Šustek (sustmi) + - Sergey Melesh (sergex) + - Axel Guckelsberger (guite) + - Maximilian Zumbansen + - Trent Steel (trsteel88) + - Gunnstein Lye (glye) + - Erik Saunier (snickers) + - Andrii Dembitskyi + - Shawn Iwinski + - Scott Arciszewski + - Chad Sikorra (chadsikorra) - Sander Toonen (xatoo) - - Gábor Fási - - Antonio Jose Cerezo (ajcerezo) - - Tavo Nieves J (tavoniievez) - - Arnaud POINTET (oipnet) - - Loïc Faugeron - - Shakhobiddin - - Nicolas Rigaud - - Emanuele Iannone - - Raulnet - - Ivan Nikolaev (destillat) - - Faizan Akram Dar (faizanakram) + - Martijn Cuppens + - Chris Sedlmayr (catchamonkey) + - mmokhi - Christian Gripp (core23) - - Dariusz Ruminski - - Vlad Gregurco (vgregurco) - - AnneKir - - NickSdot - - kylekatarnls (kylekatarnls) - - Adam Szaraniec - - Oleksiy (alexndlm) - - Quentin Schuler (sukei) - - Joshua Nye - - Vadim Borodavko (javer) - - Jérémy REYNAUD (babeuloula) - - Nathan Dench (ndenc2) - - De Cock Xavier (xdecock) - - M. Vondano - - Denis Gorbachev (starfall) - - Florian Rey (nervo) - - Tobias Weichart + - Chris Tanaskoski (devristo) + - Eduardo Oliveira (entering) + - Miro Michalicka + - Thomas Durand + - Patrick Reimers (preimers) + - Alireza Mirsepassi (alirezamirsepassi) + - Jeanmonod David (jeanmonod) + - Rodrigo Borrego Bernabé (rodrigobb) + - Ivan Rey (ivanrey) + - Webnet team (webnet) + - Dawid Pakuła (zulusx) + - Greg Anderson + - Sergio Santoro + - Jérôme Macias (jeromemacias) + - Dirk Pahl (dirkaholic) + - Lescot Edouard (idetox) + - Pablo Díez (pablodip) + - Ahmed Ghanem (ahmedghanem00) + - Antonin CLAUZIER (0x346e3730) + - Quentin Dequippe (qdequippe) + - Kevin McBride + - Arnaud De Abreu (arnaud-deabreu) + - Hans Mackowiak + - Ricardo Oliveira (ricardolotr) + - Mohammad Emran Hasan (phpfour) + - Ryan + - Ivan Nikolaev (destillat) + - R. Achmad Dadang Nur Hidayanto (dadangnh) + - Andrii Dembitskyi - Markus S. (staabm) - - Jan Nedbal - - Anton Bakai - - Maxime Pinot (maximepinot) - - Kurt Thiemann - - Marcos Rezende (rezende79) - - John Bafford (jbafford) + - Guilherme Ferreira + - Valentin Jonovs - Krasimir Bosilkov (kbosilkov) - Ioan Ovidiu Enache (ionutenache) - - Stefan Gehrig (sgehrig) - - Greg Anderson - - Jakub Škvára (jskvara) - - Bhavinkumar Nakrani (bhavin4u) - - Jacek Jędrzejewski (jacek.jedrzejewski) - - Jan Ole Behrens (deegital) - - battye - - tim - - Matthijs van den Bos (matthijs) - - Bernd Stellwag - - Andreas Hennings - - Guilherme Ferreira - - Jonas Elfering - - Kevin McBride - - Klaus Silveira (klaussilveira) - - Johan Vlaar (johjohan) - - Jérémy M (th3mouk) - - Roberto Espinoza (respinoza) - - Asier Illarramendi (doup) - - Fabian Lange (codingfabian) - - Disquedur - - Leevi Graham (leevigraham) - - Kamil Kokot (pamil) + - Steven Surowiec (steves) - Michael Moravec - - Dominik Zogg - - Axel Guckelsberger (guite) - - Jonathan Johnson (jrjohnson) - - Chad Sikorra (chadsikorra) - - Petr Duda (petrduda) - - Gigino Chianese (sajito) - - Lescot Edouard (idetox) - - Miro Michalicka - - Arkadius Stefanski (arkadius) - - Maximilian Zumbansen - - Florian Merle (florian-merle) - - lenar - - Dawid Pakuła (zulusx) - - Jérôme Vieilledent (lolautruche) + - Barry vd. Heuvel (barryvdh) + - Tri Pham (phamuyentri) + - Jeroen Thora (bolle) + - AKeeman (akeeman) + - Vicent Soria Durá (vicentgodella) + - Martin Kirilov (wucdbm) + - Steffen Roßkamp + - Tobias Weichart + - W0rma + - Alexander Deruwe (aderuwe) + - Matthew Grasmick + - Niklas Fiekas + - Angelov Dejan (angelov) + - Dariusz Ruminski + - Markus Lanthaler (lanthaler) + - Nathan Dench (ndenc2) + - ilyes kooli (skafandri) + - Matthias Krauser (mkrauser) + - Korvin Szanto + - Leevi Graham (leevigraham) + - Tobias Bönner + - Petrisor Ciprian Daniel + - Quentin Schuler (sukei) + - Joshua Nye + - quentin neyrat (qneyrat) + - Pavel Campr (pcampr) + - Rob Bast + - Jan Ole Behrens (deegital) - Nils Adermann (naderman) - - mamazu - - Oleksii Zhurbytskyi + - Gijs van Lammeren + - Sebastian Bergmann + - Artem Stepin (astepin) + - Maelan LE BORGNE + - sasezaki + - Remi Collet + - Maxim Dovydenok (dovydenok-maxim) + - Vitaliy Tverdokhlib (vitaliytv) + - Jérémy REYNAUD (babeuloula) + - battye + - Geoffrey Tran (geoff) + - wicliff wolda (wickedone) + - vagrant + - Neil Peyssard (nepey) + - Michel Salib (michelsalib) + - Yoshio HANAWA + - Peter Bowyer (pbowyer) + - Jaik Dean (jaikdean) + - Kurt Thiemann + - Antonio Jose Cerezo (ajcerezo) - Eugene Wissner - - Joachim Løvgaard (loevgaard) - - Filip Procházka (fprochazka) - - Trent Steel (trsteel88) - - Evan S Kaufman (evanskaufman) - - Dennis Fridrich (dfridrich) - - Bahman Mehrdad (bahman) - - Marcin Michalski (marcinmichalski) - - Mickaël Andrieu (mickaelandrieu) - - Vladimir Valikayev - - Natsuki Ikeguchi - - shubhalgupta - - Simo Heinonen (simoheinonen) - - Adán Lobato (adanlobato) - - Koen Kuipers (koku) - - Kagan Balga (kagan-balga) - - Adrian Günter (adrianguenter) - - Maxim Tugaev (tugmaks) - - Cyril Quintin (cyqui) - - Michael Devery (mickadoo) - - Cameron Porter - - aaa2000 (aaa2000) - - Geert De Deckere - - Max Rath (drak3) - - Dhananjay Goratela - - Pierre-Yves Landuré (biapy) - - Jon Gotlin (jongotlin) - - Johnson Page (jwpage) - - Oleksandr Barabolia (oleksandrbarabolia) - - David Romaní - - Rafał Wrzeszcz (rafalwrzeszcz) - - Sofiane HADDAG (sofhad) - - Nikita Nefedov (nikita2206) - - Loïc Chardonnet + - Daniel Tiringer + - Jérémy DECOOL (jdecool) + - Pavel Popov (metaer) + - Nadim AL ABDOU (nadim) + - Gildas Quéméner (gquemener) + - Mokhtar Tlili (sf-djuba) + - boombatower + - Arturs Vonda + - Ben Roberts (benr77) + - Greg ORIOL + - nathanpage + - Claude Khedhiri (ck-developer) + - Matheo Daninos (mathdns) + - Jiří Bok + - Soufian EZ ZANTAR (soezz) + - Faizan Akram Dar (faizanakram) + - Anthony Ferrara + - Zbigniew Malcherczyk (ferror) + - Sam Fleming (sam_fleming) + - Benjamin Zaslavsky (tiriel) + - Philipp Rieber (bicpi) + - Restless-ET + - Desjardins Jérôme (jewome62) + - Robert-Jan de Dreu + - Alex Bakhturin + - Xavier HAUSHERR + - Benoit Galati (benoitgalati) + - Jérôme Tanghe (deuchnord) + - Marek Zajac + - Lenard Palko + - geoffrey + - Brayden Williams (redstar504) + - Laurent Masforné (heisenberg) + - Marcin Szepczynski (czepol) + - De Cock Xavier (xdecock) + - BENOIT POLASZEK (bpolaszek) + - Ioan Negulescu - Jonas Elfering - - wanxiangchwng - - Anton A. Sumin - - Dennis Langen (nijusan) - - Toni Peric (tperic) - - Barney Hanlon - - Tarjei Huse (tarjei) - - Philippe Segatori - - Aleksandr Volochnev (exelenz) - - Emil Masiakowski - - Alexandre Parent - - Matt Robinson (inanimatt) - - Daniel González (daniel.gonzalez) - - Vincent Composieux (eko) - - Andreas Lutro (anlutro) - - Javier López (loalf) + - Vadim Borodavko (javer) + - Andreas Hennings + - Nicolas Rigaud + - Bernd Stellwag + - Alexandru Furculita (afurculita) + - Roy Van Ginneken (rvanginneken) + - Florian Merle (florian-merle) + - DerManoMann + - Koen Reiniers (koenre) + - Kevin Saliou (kbsali) + - Markus Staab + - Jonas Flodén (flojon) + - Arnaud POINTET (oipnet) + - PHAS Developer + - Lctrs + - mcben + - Aurélien Fredouelle + - Bilge + - Brian King + - ReenExe + - Andrew Udvare (audvare) + - Wu (wu-agriconomie) + - Stefan Kruppa + - Maarten de Boer (mdeboer) + - Gábor Fási + - Benjamin (yzalis) + - lenar + - Jérôme Vieilledent (lolautruche) + - Jan Nedbal + - Andrew M-Y (andr) + - Andrey Astakhov (aast) + - Oleksiy (alexndlm) + - Toni Rudolf (toooni) + - Filip Procházka (fprochazka) + - Judicaël RUFFIEUX (axanagor) + - Piotr Kugla (piku235) + - Denis Gorbachev (starfall) + - Ziumin + - William Arslett (warslett) + - Forfarle (forfarle) + - Damien Fernandes + - Jan Schumann + - Norbert Orzechowicz (norzechowicz) + - Johnny Robeson (johnny) + - John Bafford (jbafford) + - Florian Rey (nervo) + - M. Vondano + - M. (mbontemps) + - Giso Stallenberg (gisostallenberg) + - Mathias STRASSER (roukmoute) + - Ned Schwartz + - Stefan Gehrig (sgehrig) + - Cédric Lombardot (cedriclombardot) + - Tavo Nieves J (tavoniievez) + - Thomas Talbot (ioni) + - Belhassen Bouchoucha (crownbackend) + - Matthieu Bontemps + - Maxime Pinot (maximepinot) + - Evan S Kaufman (evanskaufman) + - tim + - Andrew Neil Forster (krciga22) + - Kieran Cross (kilosierracharlie) + - Ettore Del Negro (ettoredn) + - Florian Weber + - Renan Rodrigo + - Nicolas Dousson + - d-mitrofanov-v - Zouaoui Montassar - - Richard Bradley - - Kyle Evans (kevans91) - - Joost van Driel (j92) - - Roger Guasch (rogerguasch) + - Luis Tacón (lutacon) + - Benjamin Toussaint (bto) - Rafael Kraut - - Masterklavi + - Yuriy Vilks (igrizzli) + - David ALLIX + - SpacePossum + - Nikolai Mikhailov (sonnymilton) + - Álvaro Sánchez Taboada - katario - - StefanoTarditi - - Jake (jakesoft) + - Philippe Segatori + - Xavier Leune (xleune) + - Pierre-Yves Landuré (biapy) + - Marcos Gómez Vilches (markitosgv) + - Jan Prieser + - Pierre Hennequart + - Andre Hoong + - Simon Leblanc (leblanc_simon) + - Thijs-jan Veldhuizen (tjveldhuizen) + - David Romaní + - Mark Schmale (masch) + - Ivan + - izzyp + - Ondrej Machulda (ondram) + - Geordie + - Benjamin Schoch (bschoch) + - Simon Heimberg (simon_heimberg) + - James Hudson (mrthehud) + - DT Inier (gam6itko) - Aurimas Niekis (aurimasniekis) - - Christopher Hall (mythmakr) - - Konstantin Grachev (grachevko) - - den - - Pierre Vanliefland (pvanliefland) - - Andrew Berry - - Ramunas Pabreza (doobas) - - Jiri Barous - - Felds Liscia (felds) - - Douglas Hammond (wizhippo) - - Robert Fischer (sandoba) - - fago - - Adrien Jourdier (eclairia) - - Renan Rodrigo - - Tim Düsterhus - - Álvaro Sánchez Taboada + - Hossein Bukhamsin + - Ivan Menshykov + - Xavier Lacot (xavier) + - Alex Bacart + - Gina Peter Banyard + - Egor Taranov + - Tomasz Ignatiuk + - Tarjei Huse (tarjei) + - Evan Shaw + - Jörn Lang - mohamed - Daniel Cestari - Jacques MOATI (jmoati) - - Xavier Briand (xavierbriand) - - skmedix (skmedix) - - Loïc Ovigne (oviglo) - - Denis Zunke (donalberto) - - zenmate - - Sébastien JEAN (sebastien76) - - Luis Tacón (lutacon) - - Cyril Vermandé (cyve) + - Adrien Roches (neirda24) + - Kevin SCHNEKENBURGER + - rtek + - Michael Roterman (wtfzdotnet) + - Don Pinkster + - Alex Kalineskou + - _sir_kane (waly) + - Lorenzo Millucci (lmillucci) + - Gabrielle Langer + - Tristan Roussel + - Paweł Niedzielski (steveb) + - Julien DIDIER (juliendidier) + - Trevor North + - Travis Carden (traviscarden) + - Sem Schidler (xvilo) + - Tinjo Schöni + - Denis Charrier (brucewouaigne) + - Stéphane Escandell (sescandell) + - Rimas Kudelis + - Chris Boden (cboden) + - Ryan Hendrickson + - Ahmed Ashraf (ahmedash95) + - Christin Gruber (christingruber) + - Vitaliy Ryaboy (vitaliy) + - Maks 3w (maks3w) + - Arjan Keeman + - Jakub Vrána (vrana) + - Guillaume Verstraete + - Gladhon + - Tales Santos (tsantos84) + - umpirski + - Dmitrii Tarasov (dtarasov) + - fzerorubigd + - Volodymyr Panivko + - Alaattin Kahramanlar (alaattin) + - aegypius + - DUPUCH (bdupuch) + - Matt Johnson (gdibass) + - Andrei C. (moldman) + - Daniel González (daniel.gonzalez) + - Jon Gotlin (jongotlin) + - Edvin Hultberg + - Zhuravlev Alexander (scif) + - Benoît Merlet (trompette) + - Tomasz Kusy + - Marko H. Tamminen (gzumba) - Petar Obradović + - Sergey Kolodyazhnyy (skolodyazhnyy) + - James Michael DuPont + - Johnson Page (jwpage) + - Jeroen van den Enden (endroid) + - Cornel Cruceru (amne) + - Arturas Smorgun (asarturas) + - Robin van der Vleuten (robinvdvleuten) + - Davide Borsatto (davide.borsatto) + - Kagan Balga (kagan-balga) - Luca Saba (lucasaba) - - Sergey Zolotov (enleur) - - Tamas Szijarto - - d-mitrofanov-v - - Adrien Lucas (adrienlucas) - - Jean Pasdeloup - - Guilherme Augusto Henschel - - Alexander Li (aweelex) - - Martijn Evers - - Ricky Su (ricky) - - Glodzienski - - Maxime COLIN (maximecolin) - - Piotr Stankowski - - Abhoryo - - Don Pinkster - - Fabian Vogler (fabian) - - Jesper Skytte (greew) - - Harry Walter (haswalt) - - Olivier Maisonneuve - - Thibault Buathier (gwemox) - - Jérôme Tamarelle (jtamarelle-prismamedia) - - noniagriconomie - - Jan Walther (janwalther) - - Rodrigo Méndez (rodmen) - - Ettore Del Negro (ettoredn) - - Christian Sciberras (uuf6429) - - yclian - - Derek ROTH - - Simon Mönch - - Matthew Davis (mdavis1982) - - Thijs-jan Veldhuizen (tjveldhuizen) - - Josiah (josiah) - - stlrnz - - Xavier Leune (xleune) - - Mickaël Isaert (misaert) - - Thomas Nunninger - - Jonathan (jlslew) - - Pavol Tuka - - Daniël Brekelmans (dbrekelmans) - - Nicole Cordes (ichhabrecht) - - Ondrej Machulda (ondram) - - DUPUCH (bdupuch) - - Guillaume Aveline - - Aurimas Niekis (gcds) - - Rootie - - Oleg Zinchenko (cystbear) - - Massimiliano Braglia (massimilianobraglia) - - Evan Shaw - - Marek Pietrzak (mheki) - - Andrey Sevastianov - - Asier Etxebeste - - Pierre Hennequart - - Reedy - - Jörn Lang - - abdul malik ikhsan (samsonasik) - - Arno Geurts - - Arturas Smorgun (asarturas) - - Tiago Brito (blackmx) - - Johnny Peck (johnnypeck) - - Thiago Cordeiro (thiagocordeiro) - - Alexander Kim - - Daisuke Ohata - - Fred Cox - - Geordie - - Shaharia Azam - - Pierrick VIGNAND (pierrick) - - Marc Laporte - - Loïc Beurlet - - Korvin Szanto - - Jordan Deitch - - Vitaliy Ryaboy (vitaliy) - - Gennadi Janzen - - Andrew Hilobok (hilobok) - - Patrick Allaert - - Ilia (aliance) - - Philipp Kolesnikov - - Sebastian Marek (proofek) - - Philipp Keck - - Vladimir Varlamov (iamvar) - - Lukas Mencl - - Oriol Viñals - - Marcos Gómez Vilches (markitosgv) - - Vitaliy Zhuk (zhukv) - - Tomasz Kusy - - Christophe Villeger (seragan) - - Maksim Muruev + - Alex Xandra Albert Sim + - COMBROUSE Dimitri + - Paulo Ribeiro (paulo) + - Adrian Nguyen (vuphuong87) - Yosmany Garcia (yosmanyga) - - David Molineus - - Åsmund Garfors - - Khoo Yong Jun - - Kevin (oxfouzer) - - Sascha Grossenbacher (berdir) - - Christian Stoller (naitsirch) - - Roland Franssen :) - - Lars Vierbergen (vierbergenlars) - - Jeroen Fiege (fieg) - - Gintautas Miselis (naktibalda) - - Ivan Grigoriev (greedyivan) + - Dmitry Parnas (parnas) + - Mickaël Andrieu (mickaelandrieu) + - Benjamin Lebon - Mardari Dorel (dorumd) - - louismariegaborit - - Artur Eshenbrener - - Jason Tan (jt2k) - - Baptiste Meyer (meyerbaptiste) - - Oliver Hader - - Arnaud Frézet - - Eric COURTIAL - - Jelle Raaijmakers (gmta) - - Gerard van Helden (drm) - - rtek - - Kevin SCHNEKENBURGER - - Richard Quadling + - Jonathan (jlslew) - Alexander Grimalovsky (flying) - - Stanislau Kviatkouski (7-zete-7) - - Noémi Salaün (noemi-salaun) - - Martin Parsiegla (spea) - - Daniel Alejandro Castro Arellano (lexcast) - - Michał Jusięga - - Marc Abramowitz - - Brunet Laurent (lbrunet) - - frost-nzcr4 - - Mark Sonnabaum - - Simon (kosssi) + - Gerard van Helden (drm) + - louismariegaborit + - Malte Blättermann + - Francisco Alvarez (sormes) + - Rustam Bakeev (nommyde) + - Tamás Nagy (t-bond) + - Morten Wulff (wulff) - Ilya Levin (ilyachase) - Amr Ezzat (amrezzat) - - Stéphane Delprat - - datibbaw - - MrMicky - - Kuba Werłos (kuba) - - fedor.f - - Benjamin Laugueux - - michaelwilliams - - vitaliytv - - Jose Gonzalez - - Mark Schmale (masch) - - Julien Fredon - - Michel Hunziker - - Xavier Lacot (xavier) - - Simon Watiau (simonwatiau) - - izzyp - - Pedro Miguel Maymone de Resende (pedroresende) + - David Marín Carreño (davefx) + - Matteo Beccati (matteobeccati) + - Philipp Keck + - Vladimir Varlamov (iamvar) + - Christophe Villeger (seragan) + - ampaze + - Stefan Warman (warmans) + - Sherin Bloemendaal + - alexandre.lassauge + - Jon Dufresne + - Gálik Pál + - Jordan Deitch + - origaminal + - Benjamin Grandfond (benjamin) + - Jean-Baptiste GOMOND (mjbgo) - Mantas Var (mvar) - - Jibé Barth (jibbarth) - - Terje Bråten - - Dustin Dobervich (dustin10) - - Jesper Noordsij - Chris Jones (magikid) - - AndrolGenhald - - Damien Fa - - Ворожцов Максим (myks92) - - Gábor Tóth - - Raphaëll Roussel - - Jawira Portugal (jawira) - - James Hudson (mrthehud) - - Matthieu Bontemps - - Julien Turby - - Benoît Merlet (trompette) - - Cristoforo Cervino (cristoforocervino) - - Andrew Neil Forster (krciga22) - - Stephan Vierkant (svierkant) - - “Filip - - zenas1210 - - Julien Maulny - - hugovms - - Maximilian Bösing - - Ivo Bathke (ivoba) - - Kieran - - wuchen90 - - Eric Abouaf (neyric) - - Patrick Dawkins (pjcdawkins) - - Carlos Pereira De Amorim (epitre) - - Tristan Roussel - - Ulumuddin Cahyadi Yunus (joenoez) - - David ALLIX - - Sinan Eldem (sineld) - - Steve Grunwell - - Gabrielle Langer - - avorobiev - - fzerorubigd - - Casper Valdemar Poulsen - - Jan Prieser - - Randy Geraads - - Jan Pintr - - Gerben Oolbekkink - - Kieran Cross (kilosierracharlie) - - Matthieu Calie (matth--) - - Tinjo Schöni - - Franck RANAIVO-HARISOA (franckranaivo) - - Mikhail Yurasov (mym) - - Thomas Trautner (thomastr) - - Oriol Viñals - - Alexander Dmitryuk (coden1) - - Robin van der Vleuten (robinvdvleuten) - - Sebastian Blum - - origaminal - - Wouter van der Loop (toppy-hennie) - - Jason Woods - - Vincent CHALAMON - - Alexandre Dupuy (satchette) - - ToshY - - Pierre-Emmanuel Tanguy (petanguy) - - Carl Casbolt (carlcasbolt) - - Marcel Hernandez + - VJ + - Laurent Bassin (lbassin) + - Marek Pietrzak (mheki) + - Nykopol (nykopol) + - Carlos Buenosvinos (carlosbuenosvinos) + - Andrii Popov (andrii-popov) + - Reedy + - Xavier Briand (xavierbriand) + - grizlik + - Noah Heck (myesain) - Balazs Csaba - - 243083df - - Sylvain BEISSIER (sylvain-beissier) - - Geoffrey Brier (geoffrey-brier) - - Benjamin Schoch (bschoch) - - Gina Peter Banyard - - Antoine Corcy - - Aydin Hassan - - Alex Bogomazov (alebo) - - Jeremiasz Major - - Tony Tran - - Jakub Podhorsky (podhy) - - flip111 - - Sascha Dens (saschadens) - - Sebastian Grodzicki (sgrodzicki) - - Richard Henkenjohann (richardhj) - - Kien Nguyen - - Gustavo Falco (gfalco) - - Sem Schidler (xvilo) - - Simon Heimberg (simon_heimberg) - - Gert de Pagter - - Alex Bacart - - Travis Carden (traviscarden) - - Rostyslav Kinash - - Niklas Keller + - Simon DELICATA + - Antal Áron (antalaron) - Andreas Braun - - Andrew Codispoti - - Ke WANG (yktd26) - - Jordi Sala Morales (jsala) - - Alaattin Kahramanlar (alaattin) - - Takashi Kanemoto (ttskch) - - grizlik - - Henry Snoek (snoek09) - - VJ - - Florian Pfitzer (marmelatze) - - develop - - Paul Kamer (pkamer) - - Dave Marshall (davedevelopment) - - Adrian Nguyen (vuphuong87) - - Dennis Hotson - - Malte Blättermann - - Sergey Yastrebov - - Baldur Rensch (brensch) - - Franco Traversaro (belinde) - scyzoryck - - mweimerskirch - - Marko Petrovic - - Laurent Bassin (lbassin) - - Sebastian Paczkowski (sebpacz) - - Shin Ohno (ganchiku) - - Thibaut THOUEMENT (thibaut_thouement) - - Rimas Kudelis - - Benoît Bourgeois (bierdok) - - Michael Lutz - - Michiel Boeckaert (milio) - - Chris Boden (cboden) - - phpner - - Yi-Jyun Pan - - Alex Kalineskou - - Cosmin Sandu - - Ana Raro + - mfettig - julien57 - - Paweł Niedzielski (steveb) - - Reyo Stallenberg (reyostallenberg) - - Degory Valentine - - Martins Sipenko - - radar3301 - - Matt Janssen - - ampaze - - Arjan Keeman - - Rodrigo Aguilera - - Richard van den Brand (ricbra) - - Baptiste CONTRERAS - - Jesper Noordsij - - Egor Taranov - - Paweł Wacławczyk (pwc) - - Vladimir Luchaninov (luchaninov) - - Tomasz Ignatiuk - - Pascal Helfenstein - - Grégoire Penverne (gpenverne) - - Ben - - Xav` (xavismeh) - - Trevor North - - Ivan Kurnosov - - Jayson Xu (superjavason) - - Jean-Baptiste GOMOND (mjbgo) - - vladimir.panivko + - Gert Wijnalda (cinamo) + - Mátyás Somfai (smatyas) - kick-the-bucket - - Andreas Leathley (iquito) - - Nicolas de Marqué (nicola) - - Grinbergs Reinis (shima5) - - Evan Villemez - - Mike Meier (mykon) + - Bozhidar Hristov + - Geert De Deckere + - Andreas Lutro (anlutro) + - Dhananjay Goratela + - Nicole Cordes (ichhabrecht) + - Oliver Hoff + - Gennady Telegin + - benjaminmal + - Harry Walter (haswalt) + - Thibault Buathier (gwemox) + - Nikita Nefedov (nikita2206) + - Korvin Szanto + - Lukas Mencl + - fedor.f + - Jose Gonzalez + - Jiri Barous + - Konstantin Grachev (grachevko) + - Åsmund Garfors + - Asier Etxebeste + - Christian Sciberras (uuf6429) + - Tristan Maindron (tmaindron) + - Renan Gonçalves (renan_saddam) + - Emil Einarsson + - Jake (jakesoft) + - Vincent Composieux (eko) + - Massimiliano Braglia (massimilianobraglia) + - Jeroen Fiege (fieg) + - Grégoire Penverne (gpenverne) + - Jason Tan (jt2k) + - Randy Geraads + - Andrew Hilobok (hilobok) + - wanxiangchwng + - Barney Hanlon + - Dennis Hotson + - Geoffrey Brier (geoffrey-brier) + - Gert de Pagter + - Kay Wei + - Ruben Gonzalez (rubenruateltek) + - vitaliytv + - Antoine Corcy + - Sergey Zolotov (enleur) + - Martijn Evers - Robin Lehrmann - - Hossein Bukhamsin - - Ryan Hendrickson - - James Johnston - - Volodymyr Panivko - - Francisco Alvarez (sormes) + - Joschi Kuphal + - Maksim Muruev + - Ilia (aliance) + - Marcel Hernandez + - Matthieu Calie (matth--) + - den + - Alexandre parent + - ornicar + - phpner + - Jay Klehr + - ouardisoft + - Maxim Tugaev (tugmaks) + - StefanoTarditi + - Thomas Nunninger + - Adam + - Philipp Scheit (pscheit) + - Ondrej Exner + - RJ Garcia - Pedro Casado (pdr33n) + - James Hemery + - Florian Hermann (fhermann) + - Cosmin Sandu + - Benjamin Zikarsky (bzikarsky) + - Cyril Vermandé (cyve) + - Guilherme Augusto Henschel - Maximilian.Beckers - - Benjamin Toussaint (bto) - - umpirski - - Ivan - - Bálint Szekeres - - Marco Lipparini (liarco) - - Noah Heck (myesain) - - Florent Viel (luxifer) - - Fabien Salles (blacked) - - Toon Verwerft (veewee) - - Chris Heng (gigablah) - - Brajk19 - - Arun Philip - - Simeon Kolev (simeon_kolev9) - - SpacePossum - - Cyril Pascal (paxal) - - Andre Hoong + - Reyo Stallenberg (reyostallenberg) - Carlos Quintana - - Denis Charrier (brucewouaigne) - - Wolfgang Klinger (wolfgangklingerplan2net) - - Andrii Popov (andrii-popov) - - Calin Mihai Pristavu - - johan Vlaar - - Benedikt Lenzen (demigodcode) - - Pavlo Pelekh (pelekh) - - stoccc - - Youssef Benhssaien (moghreb) - - Douglas Reith (douglas_reith) - - Michael Roterman (wtfzdotnet) - - Gálik Pál - - Simon Leblanc (leblanc_simon) - - Davide Borsatto (davide.borsatto) - - James Michael DuPont - - mwsaz - - Stéphane Escandell (sescandell) - - Roy Klutman (royklutman) - - andrey1s - - Delf Tonder (leberknecht) - - Brad Jones - - Evan C - marie - - Adrien Wilmet (adrienfr) - - Mathias Brodala (mbrodala) - - Jay Klehr - - Zhuravlev Alexander (scif) - - Tony Malzhacker - - Maks 3w (maks3w) - - Dmitrii Tarasov (dtarasov) - - Mátyás Somfai (smatyas) - - James Hemery - - Andrei C. (moldman) - - COMBROUSE Dimitri - - Adrien Roches (neirda24) - - Tales Santos (tsantos84) - - alexandre.lassauge - - Rustam Bakeev (nommyde) + - Christophe V. (cvergne) + - Tony Tran + - Guillaume Aveline + - Ivan Grigoriev (greedyivan) + - Minna N + - Johann Saunier (prophet777) + - Jakub Kulhan (jakubkulhan) + - Thomas P - Kristijan Kanalaš (kristijan_kanalas_infostud) - - Francis Turmel (fturmel) - - Ian Irlen - Venu - - Julien DIDIER (juliendidier) - - Sergey Kolodyazhnyy (skolodyazhnyy) - - Quentin de Longraye (quentinus95) - Shahriar56 - - Wesley Lancel - - Tamás Nagy (t-bond) - - Jan Kramer - - jochenvdv - - Morgan Auchede - - Thomas P - - Claudio Zizza - - Christian Grasso (chris54721) - - Minna N + - Shin Ohno (ganchiku) + - frost-nzcr4 + - Toni Peric (tperic) + - Cristoforo Cervino (cristoforocervino) + - Eric Abouaf (neyric) + - develop + - hugovms + - “Filip + - Josiah (josiah) + - Paul Oms - EStyles (insidestyles) - - Bastien DURAND (deamon) - - Dennis Væversted (srnzitcom) - - Christophe V. (cvergne) - - Reinier Kip + - Jawira Portugal (jawira) + - mweimerskirch + - Carlos Pereira De Amorim (epitre) + - Jan Walther (janwalther) + - Abhoryo + - Steve Grunwell + - Richard van den Brand (ricbra) + - Florian Wolfsjaeger (flowolf) + - ywisax + - acoulton + - Xesxen + - Marko Petrovic + - Jan Kramer + - Patrick Allaert + - Joost van Driel (j92) + - Mickaël Buliard (mbuliard) + - Jonatan Männchen + - Johannes Klauss (cloppy) + - Mikhail Yurasov (mym) + - Wolfgang Klinger (wolfgangklingerplan2net) + - Krzysztof Łabuś (crozin) + - bogdan - Alex (garrett) - - Paulo Ribeiro (paulo) - - Ahmed Ashraf (ahmedash95) - - Mohamed Gamal - - Malte Müns - - Dmitry Parnas (parnas) - - Joschi Kuphal - - Israel J. Carberry - - Cornel Cruceru (amne) - - Dragos Protung (dragosprotung) - - Jakub Kulhan (jakubkulhan) - - Sherin Bloemendaal - - Johann Saunier (prophet777) - - Ondrej Exner - - DemigodCode - - Antal Áron (antalaron) - - Vladyslav Petrovych - - Daniel Badura - - Gennady Telegin - - Jakub Vrána (vrana) - - Kristof Van Cauwenbergh (kristofvc) - - Bastien THOMAS - - David Marín Carreño (davefx) - - ywisax - - mfettig - - Nahuel Cuesta (ncuesta) - - Alexander Miehe - - Seb Koelen - - acoulton - - Stefan Warman (warmans) - - Jon Dufresne - - Jaroslav Kuba - - Alex Xandra Albert Sim - - Lorenzo Millucci (lmillucci) - - Simon Schick (simonsimcity) - - Gladhon - - bogdan - - Adam - - Florian Hermann (fhermann) - - Philipp Scheit (pscheit) - - Ivan Menshykov - - Andreas Erhard (andaris) - - Wybren Koelmans (wybren_koelmans) - - Guillaume Verstraete - - Johannes Klauss (cloppy) - - arai - - Vincent Simonin - - Christin Gruber (christingruber) - - Dmytro Boiko (eagle) - - Ruben Gonzalez (rubenruateltek) - - Ruben Jacobs (rubenj) - - Roberto Nygaard - - Jonatan Männchen - - Hany el-Kerdany - - Simon Terrien (sterrien) - - Julie Hourcade (juliehde) - - Neil Ferreira - - _sir_kane (waly) - - Thorry84 - - Nico Haase - - ouardisoft - - Nykopol (nykopol) - - Mario Ramundo (rammar) - - Krzysztof Łabuś (crozin) - - Christian Soronellas (theunic) - - Matthieu Mota (matthieumota) - - Carlos Buenosvinos (carlosbuenosvinos) - - Sébastien Santoro (dereckson) - - Robert Gruendler (pulse00) - - Yuriy Vilks (igrizzli) - - Benjamin Grandfond (benjamin) - - Simon DELICATA + - Sebastian Krebs + - Roger Guasch (rogerguasch) + - Fractal Zombie + - michaelwilliams + - aaa2000 (aaa2000) + - Koen Kuipers (koku) + - Gustavo Falco (gfalco) - Julien Tattevin (jutattevin) - - Quentin Dreyer (qkdreyer) - - Edvin Hultberg - - Alexandre parent - - RJ Garcia - - Maksim Kotlyar (makasim) + - Jeremiasz Major + - Sascha Grossenbacher (berdir) + - Shaharia Azam + - Jordi Sala Morales (jsala) + - Besnik Br + - Tiago Brito (blackmx) + - Loïc Beurlet + - Felds Liscia (felds) + - Alexander Miehe + - Dennis Væversted (srnzitcom) + - Evan C + - zenas1210 + - Elan Ruusamäe (glen) + - Gregor Nathanael Meyer (spackmat) + - Maxime Douailin + - Simon (kosssi) + - Sebastian Grodzicki (sgrodzicki) + - Nahuel Cuesta (ncuesta) + - Kristof Van Cauwenbergh (kristofvc) + - Youssef Benhssaien (moghreb) + - Gábor Tóth + - Niklas Keller - Andrew Tchircoff (andrewtch) - - Matt Johnson (gdibass) - - Michael Piecko (michael.piecko) - - alexpozzi - - Morten Wulff (wulff) - - Hidde Boomsma (hboomsma) - - aegypius - - Mior Muhammad Zaki (crynobone) - - sl_toto (sl_toto) - - Dominik Ulrich - - Bill Hance (billhance) - - Miquel Rodríguez Telep (mrtorrent) - - Stephan Vock (glaubinix) - - Ian Jenkins (jenkoian) - - Matteo Beccati (matteobeccati) - - Stefano Degenkamp (steef) - - David Fuhr - - Achilles Kaloeridis (achilles) - - Nicolas Dousson - - Thomas Ploch - - Sébastien Despont (bouillou) - - Jeroen van den Enden (endroid) - - Sofien Naas - - benjaminmal - - Gert Wijnalda (cinamo) - - Mihai Stancu - - Paul Oms + - Vladyslav Petrovych + - Loïc Chardonnet + - Gintautas Miselis (naktibalda) + - Baptiste Meyer (meyerbaptiste) + - Brad Jones + - Mark Sonnabaum + - Simon Watiau (simonwatiau) + - Raphaëll Roussel + - Damien Fa + - Franco Traversaro (belinde) - John Bohn (jbohn) - - Marko H. Tamminen (gzumba) - - Tristan Maindron (tmaindron) - - Renan Gonçalves (renan_saddam) + - Behnoush Norouzali (behnoush) + - Thiago Cordeiro (thiagocordeiro) + - Johnny Peck (johnnypeck) + - Pedro Miguel Maymone de Resende (pedroresende) + - abdul malik ikhsan (samsonasik) + - jochenvdv + - arai + - 243083df + - wuchen90 + - Anton A. Sumin + - Stéphan Kochen + - flip111 + - Baldur Rensch (brensch) - Romanavr + - Stefano Degenkamp (steef) + - Michael Devery (mickadoo) + - Stephan Vierkant (svierkant) + - Marc Abramowitz + - Artur Eshenbrener + - Maxime COLIN (maximecolin) + - David Molineus + - Michiel Boeckaert (milio) + - Rodrigo Aguilera + - Pierre Vanliefland (pvanliefland) + - Wouter van der Loop (toppy-hennie) + - Patrick Dawkins (pjcdawkins) + - Alexandre Parent + - Sébastien JEAN (sebastien76) + - zenmate + - radar3301 + - Adrien Jourdier (eclairia) + - Pierrick VIGNAND (pierrick) + - Denis Zunke (donalberto) + - Ворожцов Максим (myks92) + - Florian Pfitzer (marmelatze) + - Piotr Stankowski + - Noémi Salaün (noemi-salaun) + - Carson Full (carsonfull) + - Terje Bråten - Martin (meckhardt) - - Behnoush Norouzali (behnoush) - - Emil Einarsson - - Mickaël Buliard (mbuliard) - - Bozhidar Hristov - - Kay Wei - Reen Lokum - - Krystian Marcisz (simivar) - - Thibault Richard (t-richard) - - Thomas Cochard (tcochard) - - Oliver Hoff - - Besnik Br + - Simon Mönch + - Daniel Badura + - Sinan Eldem (sineld) + - Mathias Brodala (mbrodala) + - vladimir.panivko + - Dragos Protung (dragosprotung) + - Loïc Ovigne (oviglo) + - Miquel Rodríguez Telep (mrtorrent) + - shubhalgupta + - Javier López (loalf) + - Mihai Stancu + - Stephan Vock (glaubinix) + - Nguyen Xuan Quynh + - Cyril Quintin (cyqui) + - Toon Verwerft (veewee) + - James Johnston + - Aleksandr Volochnev (exelenz) + - Daniel Alejandro Castro Arellano (lexcast) + - NanoSector - Sander De la Marche (sanderdlm) - - Carson Full (carsonfull) - - Elan Ruusamäe (glen) - - Roy de Vos Burchart - Ana Raro - - Florian Wolfsjaeger (flowolf) - - Fractal Zombie - - Gregor Nathanael Meyer (spackmat) - - DT Inier (gam6itko) - - Manuel Alejandro Paz Cetina - - Maxime Douailin - - buffcode - - ornicar - - Xesxen - - Sebastian Krebs - - Benjamin Zikarsky (bzikarsky) - - NanoSector - - Benjamin Lebon + - Glodzienski + - Robert Gruendler (pulse00) + - Oriol Viñals + - Alexander Dmitryuk (coden1) + - ToshY + - Brajk19 - Fabien LUCAS (flucas2) - - Stéphan Kochen - - Mouad ZIANI (mouadziani) - - Nguyen Xuan Quynh - - Nikolai Mikhailov (sonnymilton) - - Andre Johnson - - Amine Yakoubi - - Alessandro Loffredo - - Andrew Brown - - Misha Klomp (mishaklomp) - - Nathan DIdier (icz) - - none (nelexa) - - zors1 - - Adam Katz - - David Christmann - - Mauro Foti (skler) - - abunch - - Eugene Babushkin (warl) - - Gerrit Drost - - Nicolas Valverde - - Jordan Hoff - - Julia - - Mehdi Mabrouk (mehdidev) - - Viktor Novikov (nowiko) - - Neagu Cristian-Doru (cristian-neagu) - - Jakub Vrána - - Gustavo Adrian - - Victor Truhanovich (victor_truhanovich) - - Christian Flach (cmfcmf) - - gndk - - Franck Ranaivo-Harisoa - - Oncle Tom - - Peter Gribanov - - Guillaume Loulier (guikingone) - - Wotre - - Berat Doğan - - Ahmad Al-Naib - - Shaun Simmons - - Igor Kokhlov (verdet) - - Ulugbek Miniyarov - - Kuzia - - Szymon Kamiński (szk) - - Aurélien MARTIN - - Mark Topper - - Matthias Neid - - Radek Wionczek (rwionczek) - - Volker Killesreiter (ol0lll) - - Ilya Chekalsky - - Sergiy Sokolenko - - Florent SEVESTRE (aniki-taicho) - - SnakePin - - BenjaminBeck - - Armando - - Mikkel Paulson - - A. Pauly - - Marcin Szepczynski (szepczynski) - - Bogdan Scordaliu - - Mas Iting - - Markus Tacker - - Ross Motley (rossmotley) - - Markus Reinhold - - Rémi Leclerc - - Roy-Orbison - - Alexey Popkov - - Shrey Puranik - - Patrik Patie Gmitter - - Joe - - Botond Dani (picur) - - Marvin Butkereit - - Thomas Bibaut - - smokeybear87 - - Freek Van der Herten (freekmurze) - - popnikos - - Noel Light-Hilary - - Youpie - - Sergey Stavichenko (sergey_stavichenko) - - Julius (sakalys) - - Attila Bukor (r1pp3rj4ck) - - steveYeah - - dasmfm - - Stelian Mocanita (stelian) - - Franz Liedke (franzliedke) - - Ivan Nemets - - Yasmany Cubela Medina (bitgandtter) - - George Giannoulopoulos - - Eduard Morcinek - - Tadcka - - Adam - - Gustavo Adrian - - Tomáš Votruba - - Jared Farrish - - amcastror - - Taras Girnyk - - Frank Neff (fneff) - - Nacho Martin (nacmartin) - - Ángel Guzmán Maeso (shakaran) - - Edwin Hageman - - René Kerner - - Maciej Schmidt - - Sander Marechal - - arduanov - - Yannick Warnier (ywarnier) - - cay89 - - Malte Schlüter - - Maksym Pustynnikov (pustynnikov) - - Shyim - - Craig Menning (cmenning) - - Malcolm Fell (emarref) - - Renato Mendes Figueiredo - - Alessio Baglio (ioalessio) - - Neophy7e - - NothingWeAre - - Tomáš Korec (tomkorec) - - Steffen Persch (n3o77) - - Christoph Krapp - - Kevin Verschaeve (keversc) - - TheMhv - - Georg Ringer (georgringer) - - Peter Potrowl - - RevZer0 (rav) - - Evgeniy Koval - - Jonathan Vollebregt - - Omar Yepez (oyepez003) - - Marko Vušak - - Tomasz Szymczyk (karion) - - Jaymin G - - Brooks Boyd - - ChS - - Łukasz Giza (destroyer) - - Manatsawin Hanmongkolchai - - Luca Genuzio (genuzio) - - Helmut Januschka - - Cosmin-Romeo TANASE - - Ayke Halder - - Michael - - michael.kubovic - - Balázs Benyó (duplabe) - - Joris Garonian (grifx) - - Ondřej Frei - - Anthony Ferrara - - Volodymyr Kupriienko (greeflas) - - Thomas Jarrand - - Konrad Mohrfeldt - - Vlad Gapanovich (gapik) - - Wojciech Skorodecki - - Stefan Moonen - - PaoRuby - - GagnarTest (gagnartest) - - Nikos Charalampidis - - Gerhard Seidel (gseidel) - - Andras Ratz - - Ismo Vuorinen - - David Legatt (dlegatt) - - Norbert Schultheisz - - Matthias Meyer - - zolikonta - - Justin Rainbow (jrainbow) - - Manuele Menozzi - - Tobias Genberg (lorceroth) - - Xavier REN - - changmin.keum - - Eno Mullaraj (emullaraj) - - Felix Marezki - - Kai Eichinger - - Vladislav Krupenkin (ideea) - - Andrew Coulton - - Jakub Kisielewski - - Konrad - - Andrew Marcinkevičius (ifdattic) - - Ronny (big-r) - - Loïc Vernet (coil) - - Hugo Posnic - - Ph3nol - - Ernesto Domato - - Raul Garcia Canet (juagarc4) - - Bart Reunes (metalarend) - - Ferenczi Krisztian (fchris82) - - zcodes - - Adam Bramley - - Eduardo Conceição - - Ariful Alam - - Matt Lehner - - rogamoore - - Amirreza Shafaat (amirrezashafaat) - - David Grüner (vworldat) - - ffd000 - - Abdelilah Jabri - - Tobias Weinert (tweini) - - Sven Scholz - - Jannik Zschiesche - - Evgeniy Tetenchuk - - Matt Ketmo (mattketmo) - - Sander Hagen - - Volker (skydiablo) - - Chihiro Adachi (chihiro-adachi) - - Giorgio Premi - - mlazovla - - Zander Baldwin - - DaikiOnodera - - d.huethorst - - Ismail Faizi (kanafghan) - - Farid Jalilov - - Marco Pfeiffer - - Arnaud CHASSEUX - - Aleksandr Dankovtsev - - Lesnykh Ilia - - Harald Tollefsen - - tarlepp - - Matthias Derer - - Per Sandström (per) - - Patryk Kozłowski - - Paul Ferrett - - Shane Preece (shane) - - Sorin Pop (sorinpop) - - Dilek Erkut - - Matthias Bilger - - vlakoff - - Rudy Onfroy - - Luciano Mammino (loige) - - Angel Fernando Quiroz Campos (angelfqc) - - Mike Gladysch - - Alexandru Năstase - - Dmitry (staratel) - - Sven Nolting - - Peter Dietrich (xosofox) - - David Lumaye (tux1124) - - Dave Heineman (dheineman) - - Jeffrey Moelands (jeffreymoelands) - - NIRAV MUKUNDBHAI PATEL (niravpatel919) - - Holger Lösken - - Danil - - rchoquet - - Dan Finnie - - Evgeny Efimov (edefimov) - - Lukas Naumann - - Serge (nfx) - - Artem Oliinyk (artemoliynyk) - - Daniel Iwaniec - - Robert Queck - - Emmanuel Vella (emmanuel.vella) - - “teerasak” - - Karim Miladi - - Gary Houbre (thegarious) - - Prasetyo Wicaksono (jowy) - - Stewart Malik - - Safonov Nikita (ns3777k) - - Ener-Getick - - Vasily Khayrulin (sirian) - - Marco - - Floran Brutel (notFloran) (floran) - - Gijs Kunze - - Jorge Vahldick (jvahldick) - - Pontus Mårdnäs - - Jules Matsounga (hyoa) - - CarolienBEER - - Jochen Bayer (jocl) - - Florent Olivaud - - nietonfir - - Clément Bertillon (skigun) - - jwaguet - - Anton Kroshilin - - Daniel Kolvik (dkvk) - - Ilya Bulakh - - Jay Severson - - znerol - - Kevin Mian Kraiker - - Charly Terrier (charlypoppins) - - Vadim Tyukov (vatson) - - Alexander Janssen (tnajanssen) - - czachor - - Eric Krona - - sam-bee - - Guillaume BRETOU (guiguiboy) - - Steve Hyde - - Makdessi Alex - - misterx - - Ian Phillips - - david perez (davidpv) - - Toby Griffiths (tog) - - Emirald Mateli - - aim8604 - - vdauchy - - Richard Heine - - Joan Cruz - - Fabio Panaccione - - koyolgecen - - Sergey Novikov (s12v) - - Aurelijus Rožėnas - - Michael Tibben - - Peter Smeets (darkspartan) - - Gil Hadad - - Andy Raines - - Pavel Golovin (pgolovin) - - Sebastian Utz - - Benoit Leveque - - linh - - Mathias Geat - - AbdElKader Bouadjadja - - Walther Lalk - - Imangazaliev Muhammad (imangazaliev) - - Florian Guimier - - Oleg Mifle - - Bernard van der Esch (adeptofvoltron) - - Raul Rodriguez (raul782) - - phuc vo (phucwan) - - Andrei Igna - - Florian Caron (shalalalala) - - Lin Lu - - Vedran Mihočinec (v-m-i) - - Artyom Protaskin - - Tomanhez - - Bram Van der Sype (brammm) - - Dan Ordille (dordille) - - Almog Baku (almogbaku) - - Tristan Pouliquen - - Alexandre Beaujour - - Michal Gebauer - - Peter Bex - - cybernet (cybernet2u) - - Gunther Konig - - Andrey Helldar - - Grégoire Rabasse - - Adamo Crespi (aerendir) - - Emmanuel Dreyfus - - gr1ev0us - - Rafał - - Radoslaw Kowalewski - - Alex Vo (votanlean) - - hjkl - - Julien Sanchez (sumbobyboys) - - Jonathan Gough - - Eric Caron - - Marvin Petker - - ondrowan - - Jeremy Benoist - - Vašek Purchart (vasek-purchart) - - Greg Korba - - Erwan Nader (ernadoo) - - Evgeny Z (meze) - - Vincent AMSTOUTZ (vincent_amstz) - - mindaugasvcs - - Ken Stanley - - ZiYao54 - - Pawel Smolinski - - Gennadi Janzen - - Christian Rishøj - - Romain Geissler - - Flavian Sierk - - Vladislav Nikolayev (luxemate) - - Sergii Dolgushev (serhey) - - gitlost - - gauss - - Arnaud Buathier (arnapou) - - Adrien Samson (adriensamson) - - Mantas Urnieža - - Mathieu Dewet (mdewet) - - Michael Hüneburg - - Jeroen De Dauw (jeroendedauw) - - Dale.Nash - - Jacek Wilczyński (jacekwilczynski) - - Ondřej Mirtes (mirtes) - - Frédéric Bouchery (fbouchery) - - Roman Tymoshyk (tymoshyk) - - Dmitrii Fedorenko (dmifedorenko) - - Ernest Hymel - - Mamikon Arakelyan (mamikon) - - Théo DELCEY - - Ash014 - - Marcin Nowak - - Tomas Liubinas - - Christian Schiffler - - PLAZANET Pierre (pedrotroller) - - Zlatoslav Desyatnikov - - gedrox - - Toro Hill - - Foxprodev - - Dmitry Korotovsky - - Conrad Kleinespel (conradk) - - mmokhi - - Michał Strzelecki - - Tammy D - - Nicolas Macherey - - Marc Jauvin - - mohammadreza honarkhah - - Alexandru Bucur - - ConneXNL - - Benhssaein Youssef - - Yuriy Potemkin - - Myke79 - - inspiran - - Dave Long - - bill moll - - Hadrien Cren (hcren) - - Adam Wójs (awojs) - - Nilmar Sanchez Muguercia - - Aleksandar Dimitrov (netbull) - - Ettore Del Negro - - Athorcis - - Maria Grazia Patteri - - Nicolas Martin (cocorambo) - - Benjamin Ellis - - Gerry Vandermaesen (gerryvdm) - - Guillaume Sainthillier (guillaume-sainthillier) - - Neil Katin - - Bernat Llibre Martín (bernatllibre) - - Marek Víger (freezy) - - Tom Hart - - Jan Christoph Beyer - - taiiiraaa - - Charles-Henri Bruyand - - 🦅KoNekoD - - Arrakis (arrakis) - - parinz1234 - - Matthew J Mucklo - - Markus Staab - - Gilles Doge (gido) - - Phil Davis - - Boris Medvedev - - Gerard Berengue Llobera (bere) - - Saem Ghani - - Maciej Paprocki (maciekpaprocki) - - Jeroen Bouwmans - - Viacheslav Sychov - - Siragusa (asiragusa) - - Fabien D. (fabd) - - Nico Hiort af Ornäs - - ShiraNai7 - - RichardGuilland - - Penny Leach - - tamcy - - Pavinthan - - Oksana Kozlova (oksanakozlova) - - Jorge Martin (jorgemartind) - - omerida - - Jelle Kapitein - - Josef Cech - - drublic - - Fleuv - - Thomason, James - - Benoit Garret - - SenTisso - - Nicolas Roudaire - - jack.shpartko - - Thomas - - Mikko Pesari - - AlbinoDrought - - HellFirePvP - - Alex Demchenko - - Patrick Berenschot - - Yannick - - Matěj Humpál - - HADJEDJ Vincent (hadjedjvincent) - - David Windell - - soyuka - - Jordi Rejas - - Vitali Tsyrkin - - Abderrahim (phydev) - - Bhujagendra Ishaya - - Daniel González Zaballos (dem3trio) - - Wouter Diesveld - - wivaku - - Tournoud (damientournoud) - - Jenne van der Meer - - Luke Towers - - Karl Shea - - SuRiKmAn - - Grégory Pelletier (ip512) - - Péter Buri (burci) - - Anatol Belski - - Jordan de Laune (jdelaune) - - LubenZA - - Guido Donnari - - Faton (notaf) - - Michael van Tricht - - Jānis Lukss - - Jeroen van den Nieuwenhuisen - - Alexandre Segura - - Ahmad Mayahi (ahmadmayahi) - - Matthew (mattvick) - - František Bereň - - michal - - Romain Pierre - - Valentin - - Benjamin Rosenberger - - Vladimir Khramtsov (chrome) - - Martin Eckhardt - - Dmitrii Baranov - - Damon Jones (damon__jones) - - Bikal Basnet - - excelwebzone - - Peter Culka - - creiner - - Martins Eglitis - - Aharon Perkel - - Tijs Verkoyen - - Anthony Tenneriello - - Rosio (ben-rosio) - - Bailey Parker - - Dan Harper - - JK Groupe - - Mark de Haan (markdehaan) - - Mark Ogilvie - - Ulrik Nielsen (mrbase) - - Karlos Presumido (oneko) - - Artem Lopata - - inwebo veritas (inwebo) - - Nerijus Arlauskas (nercury) - - Klaus Purer - - Sébastien Lévêque (legenyes) - - tatankat - - Sébastien Armand (khepin) - - Bruno Nogueira Nascimento Wowk - - Kaipi Yann - - muchafm - - Philipp Strube - - Tim Porter - - Barthold Bos - - Yewhen Khoptynskyi (khoptynskyi) - - Aarón Nieves Fernández - - Yevhen Sidelnyk - - omniError - - goohib - - Eric Grimois - - Bálint Szekeres - - Lukas Kaltenbach - - Dylan - - Muriel (metalmumu) - - Marcos Labad - - Andriy Prokopenko (sleepyboy) - - Rene de Lima Barbosa (renedelima) - - Yura Uvarov (zim32) - - Stas Soroka (stasyan) - - Javan Eskander - - Sebastian Drewer-Gutland (sdg) - - Oleg Sedinkin (akeylimepie) - - matze - - goabonga - - Jontsa - - George Dietrich - - Boris Grishenko (arczinosek) - - Krzysztof Pyrkosz - - Sebastian Ionescu - - Markus Klein - - Baptiste Leduc (bleduc) - - Felipy Amorim (felipyamorim) - - Adam Monsen (meonkeys) - - Wim Molenberghs (wimm) - - Duncan de Boer (farmer-duck) - - Alain Flaus (halundra) - - Cedric Kastner (nurtext) - - Nicolás Alonso - - bokonet - - Bartłomiej Zając - - Dalibor Karlović - - Alexey Buyanow (alexbuyanow) - - Valentin - - Thomas Hanke - - Andreas Allacher - - iamvar - - Frederik Schmitt - - Ahmed Abdulrahman - - V1nicius00 - - Vladimir Sazhin - - Bruno Baguette - - Michael Gwynne - - VAN DER PUTTE Guillaume (guillaume_vdp) - - j.schmitt - - Htun Htun Htet (ryanhhh91) - - kaiwa - - Dan Brown - - Arend-Jan Tetteroo - - LHommet Nicolas (nicolaslh) - - Aleksei Lebedev - - Harold Iedema - - Farhad Hedayatifard - - Mahmoud Mostafa (mahmoud) - - Ismail Asci (ismailasci) - - JuntaTom (juntatom) - - Sami Mussbach - - Jovan Perovic (jperovic) - - Christian Kolb - - Tom Panier (neemzy) - - Max Grigorian (maxakawizard) - - jprivet-dev - - Kai Eichinger - - Antanas Arvasevicius - - jack.thomas (jackthomasatl) - - Timon van der Vorm - - Niels Robin-Aubertin - - Pavel Barton - - Yevgen Kovalienia - - Rémi Blaise - - florian-michael-mast - - Helmut Hummel (helhum) - - Benjamin Paap (benjaminpaap) - - Ronny López (ronnylt) - - Jorge Maiden (jorgemaiden) - - Richard van Velzen - - Emilien Escalle - - Serhii Polishchuk (spolischook) - - Valentin VALCIU - - Claude Dioudonnat - - Per Modin - - Ladislav Tánczos - - Christopher Georg (sky-chris) - - Nouhail AL FIDI (alfidi) - - Evgeny (disparity) - - László GÖRÖG - - Peter Ward - - David Wolter (davewww) - - Asrorbek Sultanov - - sez-open - - robmro27 - - sabruss - - Vincent LEFORT (vlefort) - - Arkadiusz Rzadkowolski (flies) - - SOEDJEDE Felix (fsoedjede) - - peter - - Javier - - Karolis - - maxime.perrimond - - Evgeny Anisiforov - - patrickmaynard - - Arkadiusz Kondas (itcraftsmanpl) - - Eviljeks - - Kenjy Thiébault (kthiebault) - - Marc Lemay (flug) - - xdavidwu - - Martin Komischke - - Rafał Treffler - - Daan van Renterghem - - damaya - - Nico Müller (nicomllr) - - Joseph Deray - - alex - - Kacper Gunia (cakper) - - j4nr6n (j4nr6n) - - Bruno MATEU - - Oleg Golovakhin (doc_tr) - - wusuopu - - Timothée BARRAY - - Stuart Fyfe - - Krzysztof Menżyk (krymen) - - dsech - - Anna Filina (afilina) - - Ivo - - Matthew Foster (mfoster) - - Sam Malone - - Jan Vernarsky - - Klaas Cuvelier (kcuvelier) - - klemens - - Nicolas Attard (nicolasattard) - - Victor Prudhomme - - Ema Panz - - Antonio Peric-Mazar (antonioperic) - - bahram - - Wim Godden (wimg) - - Adrien Moiruad - - craigmarvelley - - Caligone - - Yurii K - - ryunosuke - - Tom Houdmont - - robin.de.croock - - Dmitriy Derepko - - nuncanada - - GurvanVgx - - Ksaveras Šakys (xawiers) - - Maxim Lovchikov - - Xavier HAUSHERR - - Andrew Zhilin (zhil) - - Denis Kop - - fbuchlak - - Olaf Klischat - - Sobhan Sharifi (50bhan) - - Matt Wells - - Adriaan Zonnenberg - - Thomas BERTRAND (sevrahk) - - MARYNICH Mikhail (mmarynich-ext) - - Arnau González - - dakur - - Fred Cox - - Bart Brouwer (bartbrouwer) - - sualko - - Pavol Tuka - - Viktor Bajraktar (njutn95) - - Alexey Popkov - - Roeland Jago Douma - - Robert-Jan de Dreu - - Rich Sage - - Christian Neff (secondtruth) - - Houssem - - eminjk - - Michael Genereux - - Daniel Richter (richtermeister) - - Damien Vauchel (damien_vauchel) - - Gabriel Solomon (gabrielsolomon) - - Yoann Chocteau (kezaweb) - - Víctor Mateo (victormateo) - - Adam Kiss - - WaiSkats - - Yannick - - Miloš Milutinović - - ergiegonzaga - - Thomas Counsell - - Cédric Girard - - Christian Morgan - - Julien Pauli - - Saif Eddin G - - Jan Vernarsky - - g123456789l - - Dr. Gianluigi "Zane" Zanettini - - AUDUL - - Albert Bakker (babbert) - - ReScO - - Paul Mitchum (paul-m) - - Antoine Bellion (abellion) - - Ahmed Abdou - - Kevin Frantz - - sauliusnord - - Aleksejs Kovalovs (aleksejs1) - - Wissame MEKHILEF - - Martijn Evers - - Pedro Magalhães (pmmaga) - - RENAUDIN Xavier (xorrox) - - Christoph Kappestein - - Frederic Godfrin - - Hein Zaw Htet™ - - Steve Preston - - Nathanaël Martel (nathanaelmartel) - - Amaury Leroux de Lens (amo__) - - Foxprodev - - mlievertz - - Paulius Jarmalavičius (pjarmalavicius) - - wiseguy1394 - - Alexander Zogheb - - Shude - - Alexandre Melard - - Ikhsan Agustian - - Sebastian Göttschkes (sgoettschkes) - - Paul Matthews - - Robert Campbell - - rtek - - Emmanuelpcg - - Soner Sayakci - - Jozef Môstka (mostkaj) - - Pierre Rineau - - Johannes - - Nicolas Appriou - - Mihai Stancu - - Yohann Tilotti - - Oscar Esteve (oesteve) - - Wim Hendrikx - - Juga Paazmaya - - Juan M Martínez - - sal-car - - Andrew Clark (tqt_andrew_clark) - - Pieter - - Grégoire Hébert (gregoirehebert) - - d-ph - - Marie Minasyan (marie.minassyan) - - Sylvain METAYER - - David Ronchaud - - Martynas Narbutas - - Marco Jantke - - Adiel Cristo (arcristo) - - Thomas Rothe - - Dmytro Dzubenko - - Joas Schilling - - BilgeXA - - jfcixmedia - - alanzarli - - BiaDd - - Peter Schultz - - Benjamin Bender - - Eddie Abou-Jaoude (eddiejaoude) - - helmi - - mlpo (mlpo) - - Nicolas Jourdan (nicolasjc) - - Bastien Clément (bastienclement) - - Patrizio Bekerle - - Atthaphon Urairat - - Sagrario Meneses - - Muharrem Demirci (mdemirci) - - Bert Hekman - - Silas Joisten (silasjoisten) - - Florian Morello - - Richard Trebichavský - - Adam Prickett - - Lauris Binde (laurisb) - - vladyslavstartsev - - Jan Eichhorn (exeu) - - Stephan Wentz (temp) - - nyro (nyro) - - Marcus - - Victor Macko (victor_m) - - Mateusz Lerczak - - Colin Michoudet - - Jonas Hünig - - Dcp (decap94) - - shdev - - Denis Klementjev (dklementjev) - - Ioana Hazsda (ioana-hazsda) - - Clément LEFEBVRE (nemoneph) - - Chris de Kok - - Matej Žilák (teo_sk) - - Janusz Mocek - - Brieuc Thomas - - root - - Jiri Falis - - Nick Chiu - - Kevin EMO - - Wing - - Charles Sanquer (csanquer) - - moldcraft - - Christoph Vincent Schaefer (cvschaefer) - - Pierre LEJEUNE (darkanakin41) - - Bert Ramakers - - Andriy - - Pavel Starosek (octisher) - - Alexey Berezuev - - Bastien Picharles - - Claus Due (namelesscoder) - - Sebastian Schwarz - - Simon Ackermann - - Simon Paarlberg (blamh) - - Haritz Iturbe (hizai) - - alefranz - - Ryan Rogers - - Guillaume Smolders (guillaumesmo) - - dlorek - - David Brooks - - Steve Marvell - - Thiago Melo - - vltrof - - Mihai Nica (redecs) - - Alberto Pirovano (geezmo) - - Bárbara Luz - - Olivier Scherler (oscherler) - - Linnaea Von Lavia - - Sergei Shitikov - - Alexander McCullagh (mccullagh) - - Adrien Gallou (agallou) - - Şəhriyar İmanov (shehriyari) - - Arend Hummeling - - Marin Nicolae - - Bjorn Twachtmann (dotbjorn) - - insekticid - - alexpods - - Roromix - - David Zuelke - - Simon Sargeant - - Constantine Shtompel - - Nikola Svitlica (thecelavi) - - Francois Martin - - avi123 - - Franz Wilding (killerpoke) - - Artem (digi) - - Andrea Sprega (asprega) - - Florent Cailhol - - Wahyu Kristianto (kristories) - - Eric Hertwig - - Taylan Kasap - - Sezil - - kernig - - Chris - - Dennis Jaschinski (d.jaschinski) - - Markkus Millend - - Aurimas Rimkus (patrikas) - - simbera - - Vincent Chalnot - - Thomas Decaux - - heccjj - - Sander van der Vlugt (stranding) - - Mihail Krasilnikov (krasilnikovm) - - Martin Auswöger - - Maxime PINEAU - - Ron Gähler (t-ronx) - - Tomas Kmieliauskas - - Jiri Korenek - - Simon Mönch - - Martin Schophaus (m_schophaus_adcada) - - Tomasz (timitao) - - 蝦米 - - Kris Kelly - - Jens Schulze - - Ondřej Führer - - enomotodev - - tpetry - - djama - - Bart Wach - - AnrDaemon - - gechetspr - - Kévin - - Mark van den Berg - - Victor - - Ramon Ornelas (ramonornela) - - Mateusz Żyła (plotkabytes) - - Ismail Özgün Turan (dadeather) - - Uladzimir Tsykun - - Clément - - Jonas Claes - - Artiom - - Skorney - - Lane Shukhov - - Matt Fields - - Lajos Veres (vlajos) - - toxxxa - - Nsbx - - Amine Matmati - - Ari Pringle (apringle) - - chillbram - - Orestis - - Flohw - - Jérémy CROMBEZ (jeremy) - - Romain - - Marco Pfeiffer - - Laurent Negre (raulnet) - - Brian Corrigan - - Zayan Goripov - - André Matthies - - ttomor - - Gavin (gavin-markup) - - Max Baldanza - - Nicolae Serban - - Ali Sol - - Carlos Fernandes (carferdas) - - dantleech - - Aleksey Prilipko - - Thomas Baccelli (atomiix) - - Mati Kochen (mtk) - - Pitchaya Boonsarngsuk - - dmitrijivanenko - - Orban Florin (forban) - - Szymon Tarasiński - - Danish Translation Contributor - - benkarrer-lab - - Yurguis Garcia (yurguis) - - William Pinaud (docfx) - - Marcin Twardowski - - M.Mahdi Mahmoodian - - Peter van Dommelen - - Romeo - - JustDylan23 - - czachor - - ged15 - - Jean-François Morin-Abdullah - - Elias Häußler - - Sait KURT - - Simon Jamain - - qzylalala - - abluchet - - cgonzalez - - Antonio Torres - - Àlex Fiestas - - Luis Pabon (luispabon) - - Taras Hinyk - - Christian Seel - - Damian (baum) - - Muhammad Elhwawshy - - Peter Mead (petermead) - - Attila Szeremi - - Matijn (matijn-made) - - Marcus Jaschen - - Ovidiu Gabriel Gireada (ovidiugireada) - - Walter Dal Mut (wdalmut) - - Your Name - - Vincent QUATREVIEUX - - Ahmed Soliman (ahmedsoliman) - - Anton Dyshkant - - gazi04 - - Olivier Laviale (olvlvl) - - Dmytro Liashko - - Kevin Auivinet - - Michel Krenz - - alekLexis - - Patrick Kuijvenhoven - - Benjamin Pick - - dangkhoagms (dangkhoagms) - - Léon Gersen - - Nico Hiort af Ornäs - - victor-prdh - - Julien BERNARD - - Piergiuseppe Longo - - Filippos Karailanidis - - anna-lena.waltinger - - Patricia Wagner - - Patricia - - Stan Jansen (stanjan) - - David Barratt - - Kai Dederichs - - Anne-Sophie Bachelard - - Wang Jingyu - - Konstantin S. M. Möllers (ksmmoellers) - - Elias Teutschländer - - Anton Babenko (antonbabenko) - - Philippe Pichet - - GK-302 - - Aryel Tupinamba (dfkimera) - - Antoine (antoinela_adveris) - - Jiri Velek - - Haikiri - - Johannes Goslar - - Bernhard Rusch - - Tero Alén (tero) - - Florian Bogey - - Junaid Farooq (junaidfarooq) - - Léo VINCENT - - Benoit Mallo - - DerManoMann - - Andrii Serdiuk (andreyserdjuk) - - Robin Duval (robin-duval) - - Jan Dobrovodský (j-dobr) - - Vladislav Iurciuc - - Samuel Gordalina (gordalina) - - Thomas Baumgartner (shoplifter) - - brian978 - - Billy Scheufler - - andreabreu98 - - Josef Hlavatý - - andrey-tech - - Sebastian Busch (sebu) - - Adoni Pavlakis (adoni) - - Nicolas Le Goff (nlegoff) - - Ruud Arentsen - - michalmarcinkowski - - Daniel Basten (axhm3a) - - Tristan Kretzer - - Ivan Pepelko (pepelko) - - Darryl Hein (xmmedia) - - Guillaume Royer - - chesteroni (chesteroni) - - Matteo Galli - - Houziaux mike - - georaldc - - Alessandro Tagliapietra (alex88) - - Evgeny Ruban - - Marc J. Schmidt (marcjs) - - Flo Gleixner (redflo) - - PierreRebeilleau - - Jean-Guilhem Rouel (jean-gui) - - Michał Dąbrowski (defrag) - - Thomas Dutrion (theocrite) - - Alexandre Jardin (alexandre.jardin) - - Vacheslav Silyutin - - Sébastien COURJEAN - - Sam Anthony - - Frank Jogeleit - - Matt Daum (daum) - - Eric Schildkamp - - Mohamed Karnichi (amiral) - - Jeroen de Boer - - Krzysztof Przybyszewski (kprzybyszewski) - - Julius Kiekbusch - - Dario Guarracino + - David Fuhr + - Matthieu Bontemps + - Pascal Helfenstein + - Sergey Yastrebov + - Delf Tonder (leberknecht) + - Israel J. Carberry + - Fabien Salles (blacked) + - Grinbergs Reinis (shima5) + - avorobiev + - Ke WANG (yktd26) + - Pierre-Emmanuel Tanguy (petanguy) + - Reinier Kip + - Thorry84 + - Thomas Cochard (tcochard) + - Malte Müns + - Christopher Hall (mythmakr) + - Alex Bogomazov (alebo) + - Dennis Langen (nijusan) + - Gerben Oolbekkink + - sl_toto (sl_toto) + - Jesper Noordsij + - Baptiste CONTRERAS + - Sebastian Marek (proofek) + - Matthew Davis (mdavis1982) + - Jason Woods + - Mike Meier (mykon) + - Kevin (oxfouzer) + - Claudio Zizza + - Tamas Szijarto + - Vladimir Valikayev + - Ian Jenkins (jenkoian) + - Sofien Naas + - Stéphane Delprat + - stlrnz + - Ramunas Pabreza (doobas) + - Robert Fischer (sandoba) + - Thomas Ploch + - Douglas Hammond (wizhippo) + - Bastien DURAND (deamon) + - Michał Jusięga + - AndrolGenhald + - Natsuki Ikeguchi + - Ben + - Xav` (xavismeh) + - Vladimir Luchaninov (luchaninov) + - Paweł Wacławczyk (pwc) + - Degory Valentine + - Henry Snoek (snoek09) + - Takashi Kanemoto (ttskch) + - Brunet Laurent (lbrunet) + - Matthieu Mota (matthieumota) + - Sébastien Santoro (dereckson) + - Cameron Porter + - Sascha Dens (saschadens) + - Andrew Codispoti + - Julien Fredon + - Michel Hunziker + - Richard Henkenjohann (richardhj) + - Kien Nguyen + - Ulumuddin Cahyadi Yunus (joenoez) + - Benjamin Laugueux + - Dustin Dobervich (dustin10) + - Rostyslav Kinash + - Vincent CHALAMON + - Alexandre Dupuy (satchette) + - Nico Haase + - Ricky Su (ricky) + - Fabian Vogler (fabian) + - Sébastien Despont (bouillou) + - Quentin Dreyer (qkdreyer) + - Franck RANAIVO-HARISOA (franckranaivo) + - buffcode + - Achilles Kaloeridis (achilles) + - Ruben Jacobs (rubenj) + - Martins Sipenko + - noniagriconomie + - Jesper Noordsij + - Bálint Szekeres + - stoccc + - Pavlo Pelekh (pelekh) + - skmedix (skmedix) + - Cyril Pascal (paxal) + - Kuba Werłos (kuba) + - Sergii Dolgushev (serhey) + - Jayson Xu (superjavason) + - Casper Valdemar Poulsen + - Jesper Skytte (greew) + - Rootie + - Francis Turmel (fturmel) + - Pavol Tuka + - Philipp Kolesnikov + - Marco Lipparini (liarco) + - Olivier Maisonneuve + - Mouad ZIANI (mouadziani) + - Sofiane HADDAG (sofhad) + - Evan Villemez + - Mario Ramundo (rammar) + - Lars Vierbergen (vierbergenlars) + - Simeon Kolev (simeon_kolev9) + - Julien Maulny + - Bastien THOMAS + - Simon Terrien (sterrien) + - Marc Laporte + - Douglas Reith (douglas_reith) + - Thibault Richard (t-richard) + - Matt Janssen + - Emil Masiakowski + - Simo Heinonen (simoheinonen) + - Thomas Trautner (thomastr) + - Yi-Jyun Pan + - Maksim Kotlyar (makasim) + - Michael Piecko (michael.piecko) + - alexpozzi + - Benedikt Lenzen (demigodcode) + - Calin Mihai Pristavu + - Sebastian Blum + - Arno Geurts + - Andrey Sevastianov + - Wesley Lancel + - Adán Lobato (adanlobato) + - Krystian Marcisz (simivar) + - mwsaz + - Morgan Auchede + - Ian Irlen + - Andreas Erhard (andaris) + - Simon Schick (simonsimcity) + - Quentin de Longraye (quentinus95) + - Tony Malzhacker + - Vincent Simonin + - Dmytro Boiko (eagle) + - Kyle Evans (kevans91) + - Seb Koelen + - Arun Philip + - Aurimas Niekis (gcds) + - Carl Casbolt (carlcasbolt) + - Rafał Wrzeszcz (rafalwrzeszcz) + - DemigodCode + - Roberto Nygaard + - Rodrigo Méndez (rodmen) + - Paul Kamer (pkamer) + - Oleksandr Barabolia (oleksandrbarabolia) + - Jaroslav Kuba + - Dave Marshall (davedevelopment) + - Mohamed Gamal + - Jonas Elfering + - Michael Lutz + - Jibé Barth (jibbarth) + - Sylvain BEISSIER (sylvain-beissier) + - Ana Raro + - Hany el-Kerdany + - Christian Stoller (naitsirch) + - Alexander Li (aweelex) + - Bill Hance (billhance) + - Adrien Lucas (adrienlucas) + - Jean Pasdeloup + - Roy de Vos Burchart + - Manuel Alejandro Paz Cetina + - Julien Turby + - yclian + - Derek ROTH + - Adrian Günter (adrianguenter) + - Max Rath (drak3) + - Christian Soronellas (theunic) + - Mickaël Isaert (misaert) + - fago + - Tim Düsterhus + - Oriol Viñals + - Daisuke Ohata + - Jan Pintr + - Oleg Zinchenko (cystbear) + - Fred Cox + - Gennadi Janzen + - Vitaliy Zhuk (zhukv) + - Oliver Hader + - Arnaud Frézet + - Eric COURTIAL + - Ivo Bathke (ivoba) + - Sebastian Paczkowski (sebpacz) + - Thibaut THOUEMENT (thibaut_thouement) + - Andreas Leathley (iquito) + - Maximilian Bösing + - Stanislau Kviatkouski (7-zete-7) + - Kieran + - Mior Muhammad Zaki (crynobone) + - Dominik Ulrich + - datibbaw + - Jelle Raaijmakers (gmta) + - Daniël Brekelmans (dbrekelmans) + - Jérôme Tamarelle (jtamarelle-prismamedia) + - Martin Parsiegla (spea) + - Richard Bradley + - Benoît Bourgeois (bierdok) + - Chris Heng (gigablah) + - Florent Viel (luxifer) + - Roy Klutman (royklutman) + - Adrien Wilmet (adrienfr) + - Andrew Berry + - Wybren Koelmans (wybren_koelmans) + - Julie Hourcade (juliehde) + - Neil Ferreira + - Ivan Kurnosov + - Christian Grasso (chris54721) + - Matt Robinson (inanimatt) + - Richard Quadling + - Nicolas de Marqué (nicola) + - Alexander Kim + - Khoo Yong Jun + - MrMicky + - Aydin Hassan + - Jakub Podhorsky (podhy) + - johan Vlaar + - andrey1s + - Hidde Boomsma (hboomsma) + - Masterklavi + - Vallel Blanco + - Starfox64 - Christophe Meneses (c77men) - - Kirill Saksin - - Wouter Sioen (wouter_sioen) - - Vincent - - Nicolas Schwartz (nicoschwartz) - - AlberT - - Matthew Burns - - Abdouarrahmane FOUAD (fabdouarrahmane) - - Erfan Bahramali - - joris de wit (jdewit) - - Jaap van Otterdijk (jaapio) - - Gusakov Nikita (hell0w0rd) - - Curtis (ccorliss) - - andreyserdjuk - - Marcel Siegert - - rvoisin - - Cayetano Soriano Gallego (neoshadybeat) - - Matteo Giachino (matteosister) - - Benny Born - - César Suárez (csuarez) - - Philipp Fritsche - - Steve Müller - - Camille Dejoye (cdejoye) - - David Négrier (moufmouf) - - Sortex - - Malte Wunsch (maltewunsch) - - Jörn Lang - - Francisco Facioni (fran6co) - - Samael tomas - - Kirill Nesmeyanov (serafim) - - benatespina (benatespina) - - Marcos Quesada (marcos_quesada) - - Alexandre Fiocre (demos77) - - Sander Goossens (sandergo90) + - George Giannoulopoulos + - Peter Breuls + - natechicago + - Wouter de Wild + - Dennis Tobar + - Knallcharge + - efeen + - Brian Freytag + - Paweł Tomulik + - Volker Killesreiter (ol0lll) + - Jeroen de Boer + - Nathan Sepulveda + - Alexandru Patranescu + - Jeremiah VALERIE + - Eduard Morcinek - cthulhu + - Pontus Mårdnäs - ivan - Eduardo García Sanz (coma) - Maxime Corteel (mcorteel) - - Joel Marcey + - Jules Matsounga (hyoa) + - Pascal Woerde (pascalwoerde) + - Eric Schildkamp + - Karim Cassam Chenaï (ka) + - Piotr Antosik (antek88) + - Andreas Frömer + - Matt Daum (daum) + - Erwin Dirks + - tarlepp + - Harald Tollefsen + - Benjamin Franzke + - Danijel Obradović + - Erika Heidi Reinaldo (erikaheidi) + - Mathieu Morlon (glutamatt) + - Thomas Decaux + - Lesnykh Ilia + - Tadcka + - Aleksandr Dankovtsev + - Adam + - Gustavo Adrian + - aetxebeste + - Vincent Godé + - Nicolas Séverin + - Frederik Schwan + - ProgMiner + - GuillaumeVerdon + - Mike Milano (mmilano) + - Bouke Haarsma + - cmfcmf + - Mauricio Lopez (diaspar) + - eRIZ + - Julius Beckmann (h4cc) + - Kirill Lazarev + - Dan Kadera + - Valentin + - Baptiste Leduc (bleduc) + - Martijn Boers (plebian) + - Felipy Amorim (felipyamorim) + - Valérian Lepeule (vlepeule) + - orlovv + - Adam Monsen (meonkeys) + - Ahmed Shamim Hassan (me_shaon) + - Nicolas ASSING (nicolasassing) + - Maciej Zgadzaj + - Helmut Januschka + - Gilles Gauthier + - Wim Molenberghs (wimm) + - Guile (guile) + - neodevcode + - Robert Meijers + - guangwu + - Oliver Eglseder + - Thibaut Salanon + - heccjj + - Thomas Hanke + - EXT - THERAGE Kevin + - thib92 + - RFreij + - Will Rowe + - amcastror + - Taras Girnyk + - Frank Neff (fneff) + - Nacho Martin (nacmartin) + - Ángel Guzmán Maeso (shakaran) + - Edwin Hageman + - G.R.Dalenoort + - Julien Manganne (juuuuuu) + - Michal Forbak + - Joachim Krempel (jkrempel) + - George Bateman + - Andreas Allacher + - René Kerner + - Maciej Schmidt + - Sander Marechal + - David Joos (djoos) + - “teerasak” + - Malte Schlüter + - Dan Blows + - Maksym Pustynnikov (pustynnikov) + - Maxime PINEAU + - Boullé William (williamboulle) + - psampaz (psampaz) + - Jakub Caban (lustmored) + - Soha Jin - Joshua Behrens (joshuabehrens) + - Ionut Cioflan + - Vivien - Raphaëll Roussel - Marien Fressinaud + - Benjamin Dos Santos + - Pchol + - Daniel Rotter (danrot) + - Gerben Wijnja + - Karim Miladi + - ElisDN + - Ryan Rud + - Malcolm Fell (emarref) + - Mikkel Paulson + - Evgeny Anisiforov + - Ashura + - allison guilhem + - Dominic Tubach + - evgkord + - Ramon Cuñat + - Julian Krzefski + - Giuseppe Arcuti + - Tomas Kmieliauskas + - Paul Andrieux + - szymek + - Ville Mattila + - Christian Eikermann + - Raphael Hardt + - raplider + - Peter Potrowl + - Evert Jan Hakvoort + - mieszko4 + - Adrian + - mshavliuk - Osayawe Ogbemudia Terry (terdia) - Javier Espinosa (javespi) - - Oleh Korneliuk + - Stan Jansen (stanjan) + - Stano Turza + - patrickmaynard + - Matt Farmer + - Dennis Smink (dsmink) + - Dariusz Ruminski + - Joel Marcey + - Emre Akinci (emre) + - phc + - Loenix + - Arkadiusz Kondas (itcraftsmanpl) + - Julien Bianchi (jubianchi) + - Michel Bardelmeijer + - Marc Bennewitz + - Eviljeks + - Remi Collet + - Benjamin RICHARD + - Arkalo2 + - Christian + - ging-dev + - Jorrit Schippers (jorrit) + - Guillaume Lajarige (molkobain) + - Ikko Ashimine + - withbest + - Ali Sol + - Derek Bonner + - upchuk + - Cantepie + - Bojan + - Wojciech Sznapka + - djordy + - Rémi Faivre (rfv) + - dantleech - Lucas Bäuerle + - Ivo Valchev + - Sorin Gitlan (forapathy) + - Damien Fayet (rainst0rm) + - Carlos Tasada + - Koray Zorluoglu + - Andreas Forsblom (aforsblo) + - boite + - dbrekelmans + - Dawid Sajdak + - Dan Wilga + - Thomas Chmielowiec (chmielot) + - Nicolae Serban + - Reece Fowell (reecefowell) + - Alex Silcock - Guillaume LECERF - Jelle Bekker (jbekker) - Pierre-Chanel Gauthier (kmecnin) + - Alexandre Segura + - Talha Zekeriya Durmuş + - Mbechezi Nawo + - Sami Mussbach + - xaav + - Roman Igoshin (masterro) + - VojtaB + - Jovan Perovic (jperovic) + - Michael Olšavský + - Ulrik McArdle + - JakeFr + - Oncle Tom + - 2manypeople + - Christian Kolb + - Fabian Haase + - Sébastien COURJEAN + - Saem Ghani + - Tadas Gliaubicas (tadcka) + - Mehrdad + - Ash014 + - Jiri Korenek + - Janusz Jabłoński (yanoosh) + - mousezheng + - Gavin Staniforth + - Artfaith + - Rénald Casagraude (rcasagraude) + - povilas + - Vitalii + - Pathpat + - Renato Mendes Figueiredo + - William Thomson (gauss) + - Alessio Baglio (ioalessio) + - Neophy7e + - NothingWeAre + - Michael Dowling (mtdowling) + - VolCh + - Gert de Pagter + - Agustin Gomes + - Tomáš Korec (tomkorec) + - Stefan Graupner (efrane) + - Frank Schulze (xit) + - Daniel Mecke (daniel_mecke) + - Ilia Sergunin (maranqz) + - Cyril HERRERA + - Mikhail Prosalov (mprosalov) + - The Whole Life to Learn + - Andreas Kleemann (andesk) + - Martin Mandl (m2mtech) + - sensio + - Dmitrii Lozhkin + - Benedict Massolle (bemas) + - Pierre Schmitz + - Oussama Elgoumri + - spdionis + - Steffen Persch (n3o77) + - Christoph Krapp + - Rubén Calvo (rubencm) + - mark burdett + - Tatsuya Tsuruoka + - Steffen Keuper + - Cristobal Dabed + - Greg Szczotka (greg606) + - Max Summe + - Karel Syrový + - Ostrzyciel + - Frankie Wittevrongel + - Kamil Madejski (kmadejski) + - Thomas Citharel (tcit) + - fduch (fduch) + - Pawel Szczepanek (pauluz) + - Andre Eckardt (korve) + - Christoph König (chriskoenig) + - J Bruni + - Simone Fumagalli (hpatoio) + - Tomáš Polívka (draczris) + - Andreas Hasenack + - Alejandro Diaz Torres + - Kamil Musial + - DSeemiller + - kurozumi (kurozumi) + - Normunds + - Eduard Bulava (nonanerz) + - Sergii Dolgushev (sergii-swds) + - Jean Ragouin + - Simon Asika + - Valmont Pehaut-Pietri (valmonzo) + - Clement Herreman (clemherreman) + - Elias Teutschländer + - Ivan Tse + - Peter Gribanov + - Hoffmann András + - A. Pauly + - Marcin Szepczynski (szepczynski) + - alireza + - Marc Lemay (flug) + - xdavidwu + - Nikita Sklyarov + - Marc J. Schmidt (marcjs) + - Sander Coolen (scoolen) + - Chris Jones (leek) + - Markus Thielen + - Yorkie Chadwick (yorkie76) + - Samael tomas + - Kasper Hansen + - Beno!t POLASZEK + - Martin Komischke + - Henne Van Och (hennevo) + - Jeroen de Graaf + - Yannick Bensacq (cibou) + - Aaron Piotrowski (trowski) + - Iwan van Staveren (istaveren) + - Brian Freytag + - Icode4Food (icode4food) + - Brandon Antonio Lorenzo + - Felix Eymonot (hyanda) + - Asil Barkin Elik (asilelik) + - Matthias Neid + - fabios + - Edwin + - Gregório Bonfante Borba (bonfante) + - Joe Springe + - Amin Hosseini (aminh) + - otsch + - Jason Schilling (chapterjason) + - Marcus Stöhr (dafish) + - RevZer0 (rav) + - Norman Soetbeer + - Andreas Allacher + - Adam Elsodaney (archfizz) + - Evgeniy Koval + - Albin Kerouaton + - Mikkel Paulson + - Jonathan Vollebregt + - Jelte Steijaert (jelte) + - Camille Baronnet + - Swen van Zanten + - Peter Gribanov + - Walter Doekes + - Quentin Moreau (sheitak) + - Dominic Luidold + - Nico Müller (nicomllr) + - MGDSoft + - Joseph Deray + - Florent Blaison (orkin) + - Michaël VEROUX + - Charly Goblet (_mocodo) + - Jan Vernieuwe (vernija) + - Erik van Wingerden + - Michael J + - Denis Golubovskiy (bukashk0zzz) + - Arman + - Dmitriy Tkachenko (neka) + - Lucas Matte + - Arnaud + - Danil Khaliullin (bifidokk) + - Ilya Biryukov (ibiryukov) + - George Yiannoulopoulos + - Chris Shennan (chrisshennan) + - Konstantinos Alexiou + - arduanov + - Peter Jaap Blaakmeer + - Nathan PAGE (nathix) + - Raphael Davaillaud + - Fleuv + - Arnaud CHASSEUX + - Marco Pfeiffer + - Vic D'Elfant (vicdelfant) + - Quentin Favrie + - David Vancl + - Ricardo de Vries (ricardodevries) + - Martin Schophaus (m_schophaus_adcada) + - Adel ELHAIBA (eadel) + - Ronny López (ronnylt) + - ssilatel + - Jorge Maiden (jorgemaiden) + - uncaught + - Alexandre Pavy + - Seyedramin Banihashemi (ramin) + - Schvoy Norbert (schvoy) + - Pierre-Olivier Vares (povares) + - andersmateusz + - Laurent Moreau + - James Cowgill + - Omar Yepez (oyepez003) + - Marko Vušak + - Matthieu Prat + - twifty + - Vladislav Vlastovskiy (vlastv) + - David Stone + - Danilo Silva + - Hugo Fonseca (fonsecas72) + - Taras Hinyk + - Joseph FRANCLIN + - Jens Hatlak + - Tyler Stroud + - Sema + - Damián Nohales (eagleoneraptor) + - Tomasz Szymczyk (karion) + - Jaymin G + - Gunnar Lium (gunnarlium) + - gr8b + - Brooks Boyd + - ChS + - Łukasz Giza (destroyer) + - Manatsawin Hanmongkolchai + - Nicolas Badey (nico-b) + - Richard van Velzen + - Vasily Khayrulin (sirian) + - Maxime P + - Hans N. Hjort + - akimsko - Pascal Hofmann - Adrien Chinour - Vladimir Chernyshev (volch) - Jérémy (libertjeremy) - Valentin Nazarov + - Robert Meijers + - Tomas Javaisis + - Mark Beech (jaybizzle) + - Ganesh Chandrasekaran (gxc4795) + - Floran Brutel (notFloran) (floran) - Ruben Kruiswijk - - Christian Jul Jensen - - Gabriel Birke - - BRAMILLE Sébastien (oktapodia) - - Carsten Nielsen (phreaknerd) - - Jon Cave - - Bruno BOUTAREL - - Diego Aguiar (mollokhan) - - Arthur Woimbée - - Ahmad El-Bardan - - Tristan Bessoussa (sf_tristanb) - - Juan Mrad - - n-aleha - - Fabrice Locher - - Andrey Ryaguzov - - Max Voloshin (maxvoloshin) - - Tom Kaminski - - karstennilsen - - Ilia Lazarev (ilzrv) - - Dan Ionut Dumitriu (danionut90) - - Marcel Pociot (mpociot) - - Simon Bouland (bouland) - - Thibaut Arnoud (thibautarnoud) - - ProgMiner - - Bouke Haarsma - - Radosław Benkel - - Gyula Szucs - - jannick-holm - - Damian Sromek - - lerminou - - Ashura - - Stéphane Seng (stephaneseng) - - Andrey Chernykh - - Matthias Larisch - - Giuseppe Campanelli - - Wojciech Błoszyk (wbloszyk) - - Michael Zangerle - - Denis Golubovskiy (bukashk0zzz) - - Benjamin Dos Santos - - Adam Elsodaney (archfizz) - - Andreas Allacher - - Joe Springe - - Gregório Bonfante Borba (bonfante) - - Edwin - - Frankie Wittevrongel - - Karel Syrový - - Frank Schulze (xit) - - Stefan Graupner (efrane) - - Agustin Gomes - - Gert de Pagter - - Nguyen Tuan Minh (tuanminhgp) - - Marcin Kruk - - jonmldr - - Derek Bonner - - Christian - - phc - - Emre Akinci (emre) - - Jeroen de Graaf - - Ivan Nemets - - Ibrahim Bougaoua - - Pierre Tondereau - - Kevin Decherf - - pf - - Kevin Herrera (kherge) - - Yiorgos Kalligeros + - Brian Graham (incognito) + - Vincent Vermeulen + - Alexander Kurilo (kamazee) - Mickael Perraud - - Artyum Petrov - - Ken Marfilla (marfillaster) - - Andrew (drew) - - Diego Sapriza - - Markus Ramšak - - Mostafa - - Lucas Bustamante - - Julien Moulin (lizjulien) - - Maksym Romanowski (maxromanovsky) - - Rudolf Ratusiński - - Constantine Shtompel - - BrokenSourceCode - - Frédéric G. Marand (fgm) - - Jacek Kobus (jackks) - - Clemens Krack - - Jeremiah VALERIE - - Karim Cassam Chenaï (ka) - - PatrickRedStar - - Saem Ghani - - DerStoffel - - Adel ELHAIBA (eadel) - - ssilatel - - uncaught + - timesince + - tamar peled + - László GÖRÖG + - Arkadiusz Rzadkowolski (flies) + - Sean Templeton + - karolsojko + - SOEDJEDE Felix (fsoedjede) + - Petr Jaroš (petajaros) + - Pavel Witassek + - markusu49 + - RTUnreal + - Sjoerd Adema + - Phillip Look (plook) + - Dawid Nowak + - Johannes + - Robert-Jan de Dreu + - Andrei O + - Ramazan APAYDIN (rapaydin) + - Billie Thompson + - Stefan Oderbolz + - Alexander Bauer (abauer) + - Tom Newby (tomnewbyau) + - Rich Sage + - Ayke Halder + - Ondřej Frei + - sebastian + - DidierLmn + - Success Go + - czachor + - Michael Steininger + - Kris Kelly + - Stuart Fyfe + - Timothy Anido (xanido) + - František Maša + - Vincent Bouzeran + - Michael Zangerle + - Wojciech Błoszyk (wbloszyk) + - Farid Jalilov + - d.huethorst + - EdgarPE + - Jordi Llonch (jordillonch) + - Krzysztof Menżyk (krymen) + - Nardberjean + - Alexander Pasichnik (alex_brizzz) + - Andrea Giannantonio + - Stefano Cappellini (stefano_cappellini) + - Raphaël Droz + - Valouleloup + - Dmitry Simushev + - Verlhac Gaëtan (viviengaetan) + - pdragun + - Kurt Thiemann + - Sven Fabricius + - Pierrick Charron + - Danish Translation Contributor + - klemens + - Kris Buist + - Benny Born + - Thomas Chmielowiec - James Sansbury - Pierre Dudoret - Arend Hummeling - - Giorgio Premi - - ivelin vasilev - - Abdul.Mohsen B. A. A + - casdal + - DerStoffel + - Kévin + - Steven Dubois + - Vlad Dumitrache + - Anton Sukhachev (mrsuh) + - Kasperki + - Cédric Lahouste (rapotor) + - Jose Manuel Gonzalez (jgonzalez) + - Giuseppe Campanelli + - Edvinas Klovas + - Gustavo Adrian + - Mark van den Berg + - Jorge Vahldick (jvahldick) + - Hallison Boaventura (hallisonboaventura) + - Thomas Bibb + - oscartv + - Anne-Julia Seitz + - Matt Brunt + - Marcel Siegert + - Maximilian Berghoff (electricmaxxx) + - Christian Jul Jensen + - Gabriel Birke + - Robert Gurau + - BRAMILLE Sébastien (oktapodia) + - Carsten Nielsen (phreaknerd) + - Matěj Humpál + - nietonfir + - Jan Pintr + - j0k (j0k) + - Camille Dejoye (cdejoye) + - Xesau + - David Soms + - Illia Antypenko (aivus) + - Alessandra Lai + - Maxime Aknin (3m1x4m) + - Luis Ramón López López (lrlopez) + - Rares Vlaseanu (raresvla) + - Volodymyr Kupriienko (greeflas) + - Patrick Kaufmann + - curlycarla2004 + - Thomas Dubuffet (thomasdubuffet) + - boulei_n + - Rafał Toboła + - David Stone + - Łukasz Chruściel (lchrusciel) + - youssef saoubou + - wusuopu + - Aaron Stephens (astephens) + - Alexandre Jardin (alexandre.jardin) + - takashiraki + - Joseph Maarek + - Thomas Dutrion (theocrite) + - Cas + - MusikAnimal + - Troy Crawford + - Michał Dąbrowski (defrag) + - Timothée BARRAY + - Jean-Guilhem Rouel (jean-gui) + - Zayan Goripov + - Julien Boudry + - dsech + - Stefano A. (stefano93) + - Iliya Miroslavov Iliev (i.miroslavov) + - BrokenSourceCode + - Rodrigo Díez Villamuera (rodrigodiez) + - Dalibor Karlović + - Diego Sapriza + - Kirk Madera + - Evgeny Ruban + - Gemorroj (gemorroj) + - Boris Betzholz + - Robert Kopera + - Rutger Hertogh + - Mikko Ala-Fossi + - Ala Eddine Khefifi (nayzo) + - Sven Nolting + - Giovanni Albero (johntree) + - Dominik Hajduk (dominikalp) + - Bohdan Pliachenko + - david-binda + - Sebastian Landwehr (dword123) + - Alessandro Loffredo + - Zacharias Luiten + - Yoann MOROCUTTI + - Moza Bogdan (bogdan_moza) + - Pierre Foresi (pforesi) + - Marcello Mönkemeyer (marcello-moenkemeyer) + - Mark Pedron (markpedron) + - Alexandre GESLIN + - dened + - Kousuke Ebihara (co3k) + - tourze + - pawel-lewtak + - Egor Gorbachev + - Jeremy Benoist + - Andrew Brown + - Johannes Müller (johmue) + - Andrea Civita + - Ciaran McNulty (ciaranmcnulty) + - Dominik Piekarski (dompie) + - Xavier Amado (xamado) + - Michael Simonson (mikes) + - Simon Müller (boscho) + - jwaguet + - Michael Bessolov + - Signor Pedro + - Philipp Hoffmann (philipphoffmann) + - Marco Wansinck (mwansinck) + - Mark Topper + - alifanau + - Claudiu Cristea + - Aurélien MARTIN + - Sepehr Lajevardi + - Mark Spink + - Anton Kroshilin + - Jakub Vrána + - Rowan Manning + - Nicolas Sauveur (baishu) + - Schuyler Jager (sjager) + - Michael Genereux + - Plamen Mishev (pmishev) + - remieuronews + - Grayson Koonce + - Sander Hagen + - Irmantas Šiupšinskas (irmantas) + - Gabriel Solomon (gabrielsolomon) + - Richard Quadling + - James Michael DuPont + - Konstantin S. M. Möllers (ksmmoellers) + - Ian Littman (iansltx) + - Matt Ketmo (mattketmo) + - Bogdan + - Víctor Mateo (victormateo) + - Jean-Christophe Cuvelier [Artack] + - Kévin Gonella + - Rainrider + - Evgeniy Tetenchuk + - Sylvain Lorinet + - Konrad Mohrfeldt + - nuryagdy mustapayev (nueron) + - Eddy + - Menno Holtkamp + - samuel laulhau (lalop) + - Chris Tickner + - Stephen Lewis (tehanomalousone) + - Gilbertsoft + - Marion Hurteau (marionleherisson) + - Flo Gleixner (redflo) + - Jessica F Martinez + - Adam Kiss + - Sam Ward + - Peter Trebaticky + - Pete Mitchell (peterjmit) + - Vlad Gapanovich (gapik) + - Wojciech Skorodecki + - Hryhorii Hrebiniuk + - Stefan Moonen + - Vladimir Melnik + - Courcier Marvin (helyakin) + - Žan V. Dragan + - Nikita Starshinov (biji) + - roog + - AlbinoDrought + - Lyubomir Grozdanov (lubo13) + - es + - Gerd Christian Kunze (derdu) + - Daniel Kay (danielkay-cp) + - Julien Menth (cfjulien) + - Ha Phan (haphan) + - Sergey Fedotov + - Alexis + - Jérémy Jourdin (jjk801) + - Anna Filina (afilina) + - Ismail Faizi (kanafghan) + - Ivo + - Rini Misini + - Nicolas A. Bérard-Nault + - Tugba Celebioglu + - Juanmi Rodriguez Cerón + - llupa + - Szymon Tarasiński + - GagnarTest (gagnartest) + - Nikos Charalampidis + - Klaas Cuvelier (kcuvelier) + - dantleech + - Clemens Krack + - Luis Ramirez (luisdeimos) + - jdcook + - Adrien Moiruad - Romain Dorgueil + - Alex Teterin (errogaht) + - stefan.r - Benoit Lévêque (benoit_leveque) - Matt Drollette (mdrollette) - Alexander Menk + - Jonathan Poston + - Mara Blaga + - Jared Farrish - Stefanos Psarras (stefanos) + - Pavel Stejskal (spajxo) + - craigmarvelley + - Rodolfo Ruiz + - Nicolas Lemoine + - AnotherSymfonyUser (arderyp) + - Denis Kop + - Alessandro Tagliapietra (alex88) + - Victor + - developer-av + - Tobias Feijten (tobias93) + - Oliver Hoff + - Valery Maslov (coderberg) + - Stephanie Trumtel (einahp) + - Bradley Zeggelaar + - Jon Cave + - Bruno BOUTAREL + - Eric J. Duran + - Antonio Mansilla + - Gerard + - Yann LUCAS (drixs6o9) + - Georg Ringer (georgringer) + - Viet Pham + - Peter Potrowl + - Frank Dekker + - Juan Miguel Besada Vidal (soutlink) + - Adrian Philipp + - Andre Johnson + - Amine Yakoubi + - Michał Marcin Brzuchalski (brzuchal) + - Orban Florin (forban) + - Diego Aguiar (mollokhan) + - Willem Verspyck + - Dušan Kasan (dudo1904) + - Andrey Chernykh + - Stéphane Seng (stephaneseng) + - Ashura + - Zuruuh + - TheMhv + - Jérémie Broutier + - hainey + - Radosław Kowalewski + - Lance Chen + - Juan Gonzalez Montes (juanwilde) + - GurvanVgx + - MGatner + - lerminou + - db306 + - Damian Sromek + - DaikiOnodera + - Misha Klomp (mishaklomp) + - Masao Maeda (brtriver) + - Nathan DIdier (icz) + - Juraj Surman + - Antonio Angelino + - Jesper Noordsij + - georaldc + - Gerhard Seidel (gseidel) + - gondo (gondo) + - Houziaux mike + - chesteroni (chesteroni) + - none (nelexa) + - zors1 + - Adam Katz + - David Christmann + - Alexis Lefebvre + - André Matthies + - Julien JANVIER (jjanvier) + - Hossein Hosni + - detinkin + - caalholm + - Tim Jabs (rubinum) + - tadas - Phobetor - Vladimir Mantulo (mantulo) + - Marcin Chwedziak + - Ilya Bulakh - izenin - valmonzo - - Hugo Sales - - Christopher Parotat - - Sergio - - Andreas Streichardt - - Alexis MARQUIS - - Shiro - - Ahmed EBEN HASSINE (famas23) - - Anthony Moutte - - everyx - - Dennis Fehr - - Randel Palu - - Rick Prent - - skalpa - - Sergey Fokin (tyraelqp) - - Fabian Steiner (fabstei) - - sarah-eit - - Jan Marek (janmarek) - - Guillaume Aveline - - Dominik Schwind (dominikschwind) - - sdkawata - - Cedrick Oka + - Nicolas Bastien (nicolas_bastien) + - BenjaminBeck + - Oxan van Leeuwen + - Romain Jacquart (romainjacquart) + - Geoffrey Pécro (gpekz) + - Juan Traverso + - Benjamin BOUDIER + - Paul L McNeely (mcneely) + - Derek Stephen McLean + - Simon Neidhold + - adam-mospan + - Jan Emrich + - Antanas Arvasevicius + - seho-nl + - Martijn Croonen + - Guillaume Gammelin + - Andoni Larzabal (andonilarz) + - WoutervanderLoop.nl + - Jochen Mandl + - dmitrijivanenko + - tante kinast (tante) + - Emmanuelpcg + - Mauro Foti (skler) + - abunch + - Eugene Babushkin (warl) + - Jesper Søndergaard Pedersen (zerrvox) + - Arthur Woimbée + - Ahmad El-Bardan + - Adrian Olek (adrianolek) + - Juan Luis (juanlugb) + - ddebree - mantulo - - stefan.r - - Alex Teterin (errogaht) - - Daniel Kay (danielkay-cp) - - roog - - Nikita Starshinov (biji) + - afaricamp + - AbdelatifAitBara + - Cedrick Oka + - darnel + - sdkawata + - Dominik Schwind (dominikschwind) + - Jon Green (jontjs) + - Kevin Meijer + - Marc Torres + - Guillaume Aveline + - Jan Marek (janmarek) + - Anthony Moutte + - Ulugbek Miniyarov + - Thierry Marianne + - Simon / Yami + - Pablo Monterde Perez (plebs) + - Antoine Leblanc + - riadh26 + - Tim Lieberman + - Lenar Lõhmus + - Alexis MARQUIS + - wesleyh + - Maxwell Vandervelde + - Valentin Barbu (jimie) + - adhamiamirhossein + - Ben Johnson - Povilas S. (povilas) - - Michael Bessolov - - Greg Szczotka (greg606) - - Steffen Keuper - - Tatsuya Tsuruoka - - Alexander Menk - - Yury (daffox) - - Thomas Ploch - - Leonid Terentyev - - MrNicodemuz - - jamogon - - Daniel Tschinder + - wiseguy1394 + - Jeremiah VALERIE + - Radek Wionczek (rwionczek) + - Florent SEVESTRE (aniki-taicho) - Hans Höchtl (hhoechtl) - Even André Fiskvik - - Ivo Valchev - - Ninos - - Marek Binkowski - - AntoineDly - - Cyrille Bourgois (cyrilleb) - - Paul Seiffert (seiffert) - - Sören Bernstein - - Roma (memphys) - - Roberto Guido - - Albert Ganiev (helios-ag) - - Thomas Ferney (thomasf) - - Arne Groskurth - - Urban Suppiger - - Vincent Bouzeran - - Max Beutel - - Peter Breuls - - natechicago - - Wouter de Wild + - Abdul.Mohsen B. A. A + - Andrew Carter (andrewcarteruk) + - Vladislav (simpson) + - Walter Dal Mut (wdalmut) + - Mathieu Ledru (matyo91) + - Gaylord Poillon (gaylord_p) + - David Legatt (dlegatt) + - Juga Paazmaya + - Manuele Menozzi + - Tobias Genberg (lorceroth) + - Xavier REN + - Ovidiu Gabriel Gireada (ovidiugireada) + - Rémi Leclerc + - Sergey Stavichenko (sergey_stavichenko) + - Julius (sakalys) - wallach-game - Stefan Kleff (stefanxl) - Vaidas Lažauskas - LoginovIlya - Sajad Torkamani - - sensio - - Vincent Godé - - cmfcmf - - Mauricio Lopez (diaspar) - - Julius Beckmann (h4cc) - - Thibaut Salanon - - Gerben Wijnja - - Wickex - - ElisDN - - Roman Igoshin (masterro) - - Michael Olšavský - - Eduard Bulava (nonanerz) - - Sergii Dolgushev (sergii-swds) - - Lucas Matte - - Arnaud - - Ilya Biryukov (ibiryukov) - - Andrea Giannantonio - - Stefano Cappellini (stefano_cappellini) - - mikocevar - - Wojciech Gorczyca - - Łukasz Chruściel (lchrusciel) - - Oliver Hoff - - Valery Maslov (coderberg) - - Bart Baaten - - Stephanie Trumtel (einahp) - - Bradley Zeggelaar - - Antonio Angelino - - Jesper Noordsij - - darnel - - Temuri Takalandze (abgeo) - - Benjamin Schultz (bschultz) - - Thierry Marianne - - Alex Plekhanov - - Simon / Yami + - Tomáš Votruba + - changmin.keum + - Till Klampaeckel (till) + - Tema Yud + - Diego Campoy + - Trevor N. Suarez (rican7) + - Mei Gwilym (meigwilym) + - Carl Julian Sauter + - Andrew Coulton + - Yannick Vanhaeren (yvh) + - tirnanog06 + - Angel Koilov (po_taka) + - Bertalan Attila + - Luís Cobucci (lcobucci) + - Waqas Ahmed + - sarah-eit + - Will Donohoe + - Nathanael d. Noblet + - Juan M Martínez + - Shyim + - Alex Niedre + - AmsTaFF (amstaff) + - Nicholas Ruunu (nicholasruunu) - Abudarham Yuval - - Jan Hort - - Martynas Sudintas (martiis) - - Jakub Simon - - Andreas - - Rafael Villa Verde - - Cedric BERTOLINI (alsciende) - - Maxim Semkin - - Stephen - - Felicitus - - Ondřej Frei - - Mo Di (modi) - - Bizley - - Moritz Borgmann (mborgmann) - - Shaun Simmons - - Pierre-Henry Soria 🌴 (pierrehenry) - - Tamás Szigeti + - tsilefy + - Mateusz Żyła (plotkabytes) + - Kevin Jansen + - Ferran Vidal + - Martin Mayer (martin) + - Дмитрий Пацура + - Yurguis Garcia (yurguis) + - qsz + - Ismail Özgün Turan (dadeather) + - Jörg Rühl + - Szymon Kamiński (szk) + - Luca Genuzio (genuzio) + - Cosmin-Romeo TANASE + - Wojciech Gorczyca + - Kuzia + - Ian Carroll + - Nico Hiort af Ornäs + - Thomas Jarrand + - Andras Ratz + - David de Boer (ddeboer) + - Philipp Kretzschmar + - Dionysis Arvanitis + - Steve Frécinaux + - Gil Hadad + - Tobias Rautenkranz + - Siebe Vanden Eynden + - vladyslavstartsev + - pritasil + - Giorgio Premi + - Christian Weiske + - Ismo Vuorinen + - Mario Blažek (marioblazek) + - Norbert Schultheisz + - Matthias Meyer + - Matthias Bilger + - Zakaria AMMOURA (zakariaamm) + - Billie Thompson + - Denys Voronin (hurricane) + - Pitchaya Boonsarngsuk + - Jakub Kisielewski + - Jay Severson + - Simone Di Maulo (toretto460) + - Choong Wei Tjeng (choonge) + - Aaron Scherer (aequasi) - Daniel Londero (dlondero) - Javier Alfonso Bellota de Frutos - - Tim Strehle - - Enrico Schultz - - Javier Ledezma - - Matthew Covey - - parhs - - kaywalker + - Owen Gray (otis) + - jannick-holm + - Gyula Szucs + - Konrad + - Uladzimir Tsykun + - Kim Laï Trinh + - Dmitry Danilson + - Léon Gersen + - Radosław Benkel + - Sandro Hopf (senaria) - Jeffrey Cafferata (jcidnl) - Adrien Foulon - - Raphaël Davaillaud - - Pavel Prischepa - - Pierre Geyer (ptheg) - - max - - Stanislav Gamaiunov (happyproff) - - Bermon Clément (chou666) - - hamza - - Nicolas Eeckeloo (neeckeloo) - - John VanDeWeghe - - PabloKowalczyk - - Simone Ruggieri - - Rares Sebastian Moldovan (raresmldvn) - - Harry Wiseman - - Carsten Eilers (fnc) - - martkop26 - - Tobias Feijten (tobias93) - - Courcier Marvin (helyakin) - - Hryhorii Hrebiniuk - - Renan Taranto (renan-taranto) - - mboultoureau - - Nil Borodulia - - Soha Jin - - Dan Blows - - David Joos (djoos) - - Rik van der Heijden + - Franck Ranaivo-Harisoa + - Thibaut Arnoud (thibautarnoud) + - Abdiel Carrazana (abdielcs) + - thecaliskan + - Wouter Ras + - adnen chouibi - Ibon Conesa (ibonkonesa) - Michal Trojanowski - - Michael Telgmann - - Pieter Jordaan - - Patrick Luca Fazzi (ap3ir0n) + - paullallier + - Nicholas Byfleet (nickbyfleet) + - Marcin Nowak + - Willem Mouwen + - Mario Young + - Christopher Parotat + - Tristan Bessoussa (sf_tristanb) + - Juan Mrad + - Taylor Otwell + - Jan Hort + - Martynas Sudintas (martiis) + - Jakub Simon + - Rafael Villa Verde + - Cedric BERTOLINI (alsciende) + - Maxim Semkin + - bch36 + - Eric Stern - Tobias Stöckler - Andrejs Leonovs - Ruud Seberechts - satalaondrej + - Richard Hodgson + - Frank Jogeleit + - Stephen + - Felicitus + - Ondřej Frei + - Holger Lösken + - Arash Tabrizian (ghost098) + - Michael Dawart (mdawart) + - Fabian Steiner (fabstei) + - Xavier HAUSHERR + - Andrew Zhilin (zhil) + - Clément + - e-ivanov + - Konstantin Chigakov + - Sergey Fokin (tyraelqp) + - Andrew Marcinkevičius (ifdattic) + - Ronny (big-r) - scourgen hung (scourgen) - Alexis BOYER + - Loïc Vernet (coil) + - skalpa + - Hugo Posnic + - Babichev Maxim (rez1dent3) + - Keri Henare (kerihenare) + - Philip Frank + - Staormin + - Jonas Claes + - Damien Harper (damien.harper) + - Rick Prent + - marbul + - Matheus Gontijo + - Albert Prat + - Darryl Hein (xmmedia) - Mohammad Ali Sarbanha (sarbanha) + - Ivan Pepelko (pepelko) + - Tristan Kretzer + - Daniel Basten (axhm3a) - Grzegorz Łukaszewicz (newicz) - - Bruno Rodrigues de Araujo (brunosinister) - - Ben Gamra Housseine (hbgamra) - - rewrit3 - - Paweł Tomulik - - Nathan Sepulveda - - Alexandru Patranescu - - Piotr Antosik (antek88) - - Andreas Frömer - - Matt Farmer - - Dennis Smink (dsmink) - - Julien Bianchi (jubianchi) - - Dariusz Czech - - Michel Bardelmeijer - - Marc Bennewitz - - Sorin Gitlan (forapathy) - - The Whole Life to Learn - - Dmitrii Lozhkin - - Pierre Schmitz - - Nikita Sklyarov - - Amin Hosseini (aminh) - - otsch - - Jelte Steijaert (jelte) - - Swen van Zanten - - Victoria Quirante Ruiz (victoria) - - Rémy LESCALLIER - - Marcel Berteler - - Sergey Yuferev - - wesign (inscrutable01) - - Vladislav Rastrusny (fractalizer) - - wetternest - - Frank Naegler - - nerdgod - - Dmitri Petmanson - - julien_tempo1 (julien_tempo1) - - Jérôme Nadaud (jnadaud) - - Schvoy Norbert (schvoy) - - Pierre-Olivier Vares (povares) - - Konstantin Bogomolov - - Patrick Daley (padrig) - - ghazy ben ahmed - - stloyd - - Nasim - - alireza - - Hoffmann András - - Kamil Musial - - Andreas Hasenack - - J Bruni - - Andre Eckardt (korve) - - andersmateusz - - Laurent Moreau - - James Cowgill - - Max Summe - - Adrian - - mieszko4 - - psampaz (psampaz) - - Zeeshan Rashid (zeeshan) - - Piotr Zajac - - Koalabaerchen - - Keith Maika - - Linas Ramanauskas - - Yohan Giarelli (frequence-web) - - Juan Ases García (ases) - - fruty - - Dan (dantleech) - - Mark Beech (jaybizzle) - - Ganesh Chandrasekaran (gxc4795) - - Hubert Moreau (hmoreau) - - Dominik Ritter (dritter) - - ddebree - - Tim van Densen - - Ruben Jansen - - Alexander Onatskiy - - Roger Webb - - Drew Butler - - Serhii Smirnov - - Igor Timoshenko (igor.timoshenko) - - Georgi Georgiev - - Pierre Sv (rrr63) - - rhel-eo - - Bogdan Rancichi (devck) - - Elliot Anderson (elliot) - - Martin Pärtel - - André Laugks - - Klaas Naaijkens - - Gabriel Moreira - - neghmurken - - Stano Turza - - GuillaumeVerdon - - Dustin Wilson - - Alexander Kurilo (kamazee) - - Brian Graham (incognito) + - Moritz Kraft (userfriendly) + - Pedro Magalhães (pmmaga) + - Warwick + - Mo Di (modi) + - dangkhoagms (dangkhoagms) + - Ismail Turan + - michalmarcinkowski + - Ph3nol + - Raul Garcia Canet (juagarc4) + - shreypuranik + - gr1ev0us + - Jure (zamzung) + - Ruud Arentsen + - Nicolas Appriou + - Rafał + - Nicolas Le Goff (nlegoff) + - n-aleha + - Adoni Pavlakis (adoni) + - Fabrice Locher + - Sebastian Busch (sebu) + - Andrey Ryaguzov + - Adam Bramley + - Markus Baumer + - kshida + - Moritz Borgmann (mborgmann) + - Shaun Simmons + - Guillaume Royer + - Pierre-Henry Soria 🌴 (pierrehenry) + - Tamás Szigeti + - Samuel Vogel (samuelvogel) + - Philip Dahlstrøm + - Andrii Serdiuk (andreyserdjuk) + - Sergio + - Kristen Gilden + - Sébastien Lévêque (legenyes) + - Jean-Baptiste Delhommeau + - Enrico + - Mati Kochen (mtk) + - Alan Chen + - Laurent Clouet + - ureimers - Norman Soetbeer - - Michal Forbak - - Joachim Krempel (jkrempel) - - George Bateman - - upchuk - - Cantepie - - Gavin Staniforth - - Artfaith - - Rénald Casagraude (rcasagraude) - - Damián Nohales (eagleoneraptor) - - Hans N. Hjort - - Timothy Anido (xanido) - - Cyrille Jouineau (tuxosaurus) - - Pierrick Charron - - Florian Cellier - - Kamil Szalewski (szal1k) - - Qingshan Luo - - Kirill Roskolii - - Nikita Popov (nikic) - - Kris Buist - - Dmitry Simushev - - Hallison Boaventura (hallisonboaventura) - - Matt Brunt - - Maximilian Berghoff (electricmaxxx) - - Rodrigo Díez Villamuera (rodrigodiez) - - Dalibor Karlović - - Zacharias Luiten - - Steven Dubois - - Citia (citia) - - Thibaut Chieux - - Jelizaveta Lemeševa (broken_core) - - Wojciech Zimoń - - Signor Pedro - - Philipp Hoffmann (philipphoffmann) - - Marco Wansinck (mwansinck) - - alangvazq - - alifanau - - Claudiu Cristea - - Gilbertsoft - - Sam Ward - - Peter Trebaticky - - Pete Mitchell (peterjmit) - - Gerd Christian Kunze (derdu) - - Julien Menth (cfjulien) - - Ha Phan (haphan) - - Sergey Fedotov - - Anton Zagorskii - - Benjamin BOUDIER - - Paul L McNeely (mcneely) - - Derek Stephen McLean - - Simon Neidhold - - Andrew Carter (andrewcarteruk) - - Bertalan Attila - - Luís Cobucci (lcobucci) - - Pierre-Louis LAUNAY - - Sergei Gorjunov + - Sam Anthony + - shreyadenny - Máximo Cuadros (mcuadros) - - Thanos Polymeneas (thanos) - - Vladimir Pakhomchik - - Kevin Weber - - kwiateusz - - Markus + - andrey-tech + - DerManoMann + - Josef Hlavatý + - Eduardo Conceição + - Laurens Laman + - Ariful Alam + - Sam Williams + - andreabreu98 + - Sjors Ottjes + - Denis Yuzhanin + - Michael Pohlers (mick_the_big) + - Florian Heller + - Thomas Boileau (tboileau) + - Stefan Hüsges (tronsha) + - Gerrit Addiks + - Artiom + - Enrico Schultz + - Javier Ledezma + - tinect (tinect) + - Andrii Boiko + - Pierre Geyer (ptheg) + - max + - Louis-Proffit - Kérian MONTES-MORIN (kerianmm) - - Cédric Lahouste (rapotor) - - Nicolas Tallefourtané (nicolab) + - jc - Jakub Chábek - - Lesueurs Frédéric (fredlesueurs) - - Jeremy Bush - - Lebnik - - Kevin Dew - - Gleb Sidora + - Abdelilah Jabri + - Valentin - Christian Wahler (christian) - - fh-github@fholzhauer.de - - Yuri Karaban - - Adam Klvač - - Oleksii Svitiashchuk + - Matthew Donadio + - j.schmitt + - Jairo Pastor - Andrzej - - Nicolas A. Bérard-Nault - - nuryagdy mustapayev (nueron) - - Richard Hodgson - - Dennis Tobar - - Eric Stern - - Taylor Otwell - - adnen chouibi - - Wouter Ras - - thecaliskan - - Choong Wei Tjeng (choonge) - - Denys Voronin (hurricane) - - thib92 - - Valentin Barbu (jimie) + - Harry Wiseman + - Serhiy Lunak (slunak) + - martkop26 + - Lars Moelleken + - Tim Ward + - Liverbool (liverbool) + - Dustin Wilson + - Nicolas Bondoux (nsbx) + - Htun Htun Htet (ryanhhh91) - Adrien Peyre (adpeyre) - - Jan Pintr - - Robert Gurau - - sebastian - - Fabian Kropfhamer (fabiank) - - Ivan Tse - - Sergio Santoro - - Stefan Oderbolz - - Vincent Vermeulen - - Tomas Javaisis - - akimsko - - Joseph FRANCLIN - - Ricardo de Vries (ricardodevries) - - Vic D'Elfant (vicdelfant) - - Julien Manganne (juuuuuu) - - G.R.Dalenoort - - Will Rowe - - EXT - THERAGE Kevin - - Oliver Eglseder - - guangwu - - orlovv - - Dan Kadera - - eRIZ - - Carlos Ortega Huetos - - klyk50 - - Monet Emilien - - Gabi Udrescu - - Juliano Petronetto - - Pablo Ogando Ferreira - - Mike Francis - - Nick Stemerdink - - Axel Venet - - Abderrahman DAIF (death_maker) - - Robin Kanters (anddarerobin) - - Tiago Garcia (tiagojsag) - - Andrea Ruggiero (pupax) - - Joeri Verdeyen (jverdeyen) - - Halil Hakan Karabay (hhkrby) - - Jakub Sacha - - neFAST - - Andras Debreczeni + - Sam Malone + - Alexander Cheprasov + - Nathaniel Catchpole + - Alan Bondarchuk + - Renan Taranto (renan-taranto) + - mboultoureau + - Nguyen Tuan Minh (tuanminhgp) + - Marcin Kruk + - jonmldr + - Michael Schneider + - Skorney + - ncou + - joris + - Zan Baldwin (zanderbaldwin) + - Michael Squires + - Ignacio Alveal + - Ivan Nemets + - Ibrahim Bougaoua + - Antoine Beyet + - Pierre Tondereau + - Quique Porta (quiqueporta) + - Ser5 + - André Filipe Gonçalves Neves (seven) + - Stephen Clouse + - Lane Shukhov + - Craig Menning (cmenning) + - dinitrol + - Matthieu + - ddegentesh + - Roland Franssen :) + - Johan Wilfer (johanwilfer) + - Oriol Mangas Abellan (oriolman) + - Ramon Kleiss (akathos) + - Marc Duboc (icemad) + - Igor Kokhlov (verdet) + - Shaun Simmons + - pf + - znerol + - Ahmad Al-Naib + - Pablo Maria Martelletti (pmartelletti) + - Markus Staab + - Bailey Parker + - Xavier RENAUDIN + - David Soria Parra + - abulford + - Filippos Karailanidis - Laurent Bachelier (laurentb) - - Robert Worgul - - tsufeki - - Mehdi Achour (machour) - - Marek Šimeček (mssimi) + - Bizley + - Alexandre Tranchant (alexandre_t) + - Michael Orlitzky + - downace + - Olivier Scherler (oscherler) + - Ahmed HANNACHI (tiecoders) + - Jos Elstgeest + - Zachary Tong (polyfractal) + - InbarAbraham - Brad Treloar - - David Szkiba - - Peter Simoncic - - Rachid Hammaoui (makmaoui) - - Vladimir Sadicov (xtech) - - m.chwedziak - - pkowalczyk - - voodooism - - John Espiritu (johnillo) - - neodevcode - - Paul Andrieux - - szymek - - gstapinato - - fduch (fduch) - - Pawel Szczepanek (pauluz) - - fabios - - Quentin Moreau (sheitak) - - MGDSoft - - Florent Blaison (orkin) - - Charly Goblet (_mocodo) - - Dominik Hajduk (dominikalp) - - fabi + - Frédéric G. Marand (fgm) + - Peter Orosz (ill_logical) + - Jimmy Leger (redpanda) + - Przemysław Piechota (kibao) + - Shamimul Alam + - Jannik Zschiesche + - PatrickRedStar + - Nil Borodulia + - Linnaea Von Lavia + - gstapinato + - timaschew + - Raphael de Almeida (raphaeldealmeida) + - Piergiuseppe Longo + - tamirvs + - Jean-Baptiste Nahan + - Kai Dederichs + - Sven Scholz + - Tobias Weinert (tweini) + - Ramon Ornelas (ramonornela) + - Carsten Eilers (fnc) + - Gleb Sidora - martijn - - Petr Jaroš (petajaros) - - Pavel Witassek - - Sjoerd Adema - - Phillip Look (plook) - - Dawid Nowak - - Johannes - - Billie Thompson - - Agata - - Troy McCabe + - Nicolas Tallefourtané (nicolab) + - Kevin Dew + - Lebnik + - Viktor Bajraktar (njutn95) + - fh-github@fholzhauer.de + - Yuri Karaban + - Adam Klvač + - Oleksii Svitiashchuk + - Kajetan Kołtuniak (kajtii) + - Karolis Daužickas (kdauzickas) + - Geoff + - Anne-Sophie Bachelard + - Joel Lusavuvu (enigma97) + - Fabian Kropfhamer (fabiank) + - Sergio Santoro + - ivelin vasilev - Jeremy David (jeremy.david) - - Jose Manuel Gonzalez (jgonzalez) - - Edvinas Klovas - - llupa - - Luis Ramirez (luisdeimos) - - jdcook - - Nicolas Lemoine - - AnotherSymfonyUser (arderyp) + - Ali Tavafi + - Alexey Popkov + - fabi + - Ole Rößner (basster) + - Roeland Jago Douma + - Berat Doğan + - Thomas Bibaut + - smokeybear87 + - Freek Van der Herten (freekmurze) + - victor-prdh + - AUDUL - Mathieu TUDISCO (mathieutu) - - Laurens Laman - - tsilefy - - Andrea Civita - - Ciaran McNulty (ciaranmcnulty) - - Dominik Piekarski (dompie) - - MGatner + - Wotre + - Albert Bakker (babbert) + - Dariusz Czech + - Yannick Warnier (ywarnier) + - cay89 + - Tito Miguel Costa (titomiguelcosta) + - Ross Motley (rossmotley) + - Markus Reinhold + - Helmer Aaviksoo + - Albion Bame (abame) + - mikocevar + - Chris Maiden (matason) + - John Edmerson Pizarra + - Rikijs Murgs + - Guillaume Loulier (guikingone) + - Johan de Ruijter + - Amaury Leroux de Lens (amo__) + - Jeremy Bush + - Guillermo Gisinger (t3chn0r) + - Julien ARBEY + - Rein Baarsma (solidwebcode) + - Foxprodev + - Nick Chiu + - Bjorn Twachtmann (dotbjorn) + - Safonov Nikita (ns3777k) + - Mahmoud Mostafa (mahmoud) + - jprivet-dev + - Kevin EMO + - Anton (bonio) + - divinity76 + - Jonathan Hedstrom + - Nicolas Pion + - Wing + - Charles Sanquer (csanquer) + - mlievertz + - moldcraft + - Bert Ramakers + - Franz Wilding (killerpoke) + - Chris + - Billy Scheufler + - Dennis Jaschinski (d.jaschinski) + - Markkus Millend + - brian978 + - Matt Fields + - Peter Ward + - Thomas Baumgartner (shoplifter) + - tatankat + - Igor Tarasov (polosatus) + - gndk + - Emirald Mateli + - Oleg Krasavin (okwinza) + - Ryan Rogers + - Lajos Veres (vlajos) + - Yevhen Sidelnyk + - omniError + - aim8604 + - Dylan + - Muriel (metalmumu) + - Marcos Labad + - Christian Flach (cmfcmf) + - Andriy Prokopenko (sleepyboy) + - Wang Jingyu + - jean pasqualini (darkilliant) + - George Dietrich + - Boris Grishenko (arczinosek) + - Krzysztof Pyrkosz + - Duncan de Boer (farmer-duck) + - Alain Flaus (halundra) + - Nicolás Alonso + - Bartłomiej Zając + - Alexey Buyanow (alexbuyanow) + - Saif Eddin G + - Nicolas + - Ismail Asci (ismailasci) + - Andrea Quintino (dirk39) + - Brian Debuire + - Attila Bukor (r1pp3rj4ck) + - steveYeah + - dasmfm + - Citia (citia) + - Thibaut Chieux + - JuntaTom (juntatom) + - Max Grigorian (maxakawizard) - Antanas Arvasevicius - - seho-nl - - Guillaume Gammelin - - Michael Simonson (mikes) - - Simon Müller (boscho) - - Andoni Larzabal (andonilarz) - - WoutervanderLoop.nl - - Raito Akehanareru (raito) - - Dmytro Pigin (dotty) - - Daniele Orru' (danydev) - - Flinsch - - Jon Green (jontjs) - - Kevin Meijer - - Marc Torres - - Sven Fabricius - - Arseny Razin - - Pierre Gasté (pierre_g) - - Alberto Aldegheri - - Marin Bînzari (spartakusmd) - - Vincent Chalamon - - Norman Soetbeer - - Alan Chen - - Kristen Gilden - - Philip Dahlstrøm - - Samuel Vogel (samuelvogel) - - kshida - - Arash Tabrizian (ghost098) - - Jörg Rühl - - Nicholas Ruunu (nicholasruunu) - - Alex Niedre - - adhamiamirhossein - - Geoffrey Pécro (gpekz) - - Pavel Stejskal (spajxo) - - Ben Johnson - - Jeremiah VALERIE - - Jan Vernieuwe (vernija) - - Michaël VEROUX - - Dominic Luidold - - Walter Doekes - - Peter Gribanov - - Camille Baronnet - - Mikkel Paulson - - Albin Kerouaton - - Kamil Piwowarski (cyklista) - - Ralf Kühnel (ralfkuehnel) - - Drew Butler - - Paweł Stasicki - - Jérôme Dumas - - Nicolas Fabre (nfabre) - - EdgarPE - - Kevin Nadin (kevinjhappy) - - Dan Patrick (mdpatrick) - - Christiaan Wiesenekker - - Andrea Giuliano (shark) - - Fraller Balázs (fracsi) - - Michael Schneider - - Will Donohoe - - Nathanael d. Noblet - - Kirill Lazarev - - Valentin - - Robert Meijers - - RFreij - - Eric J. Duran - - Antonio Mansilla - - Tischoi - - Ikko Ashimine - - withbest - - Alexandre Segura - - Talha Zekeriya Durmuş - - Mbechezi Nawo - - Bernd Matzner (bmatzner) - - Rubén Calvo (rubencm) - - Simon Asika - - Valmont Pehaut-Pietri (valmonzo) - - Clement Herreman (clemherreman) - - Martin Mayer (martin) - - Tito Costa - - Дмитрий Пацура - - Marcus Stöhr (dafish) - - qsz - - paullallier - - Gerard - - Julien JANVIER (jjanvier) - - Hossein Hosni - - detinkin - - caalholm - - Jochen Mandl - - tante kinast (tante) + - Dario Savella + - Vacheslav Silyutin + - Simon Bouland (bouland) + - Marcel Pociot (mpociot) + - Alexandre Melard + - Kenjy Thiébault (kthiebault) + - ZiYao54 + - Pavel Barton + - Yevgen Kovalienia + - Rémi Blaise + - florian-michael-mast + - Helmut Hummel (helhum) + - Dan Ionut Dumitriu (danionut90) + - Julien BERNARD + - Ahto Türkson + - Dominik Pesch (dombn) + - toxxxa + - Pawel Smolinski + - Ilia Lazarev (ilzrv) + - Benjamin Paap (benjaminpaap) + - Gavin (gavin-markup) + - Lesueurs Frédéric (fredlesueurs) + - Jelizaveta Lemeševa (broken_core) + - alangvazq + - Adiel Cristo (arcristo) + - stloyd + - Rafał Treffler + - damaya + - ghazy ben ahmed + - Patrick Daley (padrig) + - Kacper Gunia (cakper) + - Konstantin Bogomolov + - Yoann Chocteau (kezaweb) + - jersoe + - Caligone + - Anatol Belski + - robin.de.croock + - David Lumaye (tux1124) + - Andrey Helldar + - Grégoire Rabasse + - Adamo Crespi (aerendir) + - David Lima + - goabonga + - Nsbx + - Daniel Kolvik (dkvk) + - Nicolas Attard (nicolasattard) + - Victor Prudhomme + - Ema Panz + - Thomas Rothe + - Alex Carol (picard89) + - Buster Neece - pizzaminded - - Vyacheslav Slinko - - Nicolas Badey (nico-b) - - Julien ARBEY - - Maxime P - - tamar peled - - Patrick Kaufmann - - Thomas Dubuffet (thomasdubuffet) - - boulei_n - - Helmer Aaviksoo - - Moza Bogdan (bogdan_moza) - - Pierre Foresi (pforesi) - - Mark Pedron (markpedron) - - dened - - Kousuke Ebihara (co3k) - - tourze - - pawel-lewtak - - Ian Carroll - - Vladimir Melnik - - Lyubomir Grozdanov (lubo13) - - es - - Till Klampaeckel (till) - - Diego Campoy - - Trevor N. Suarez (rican7) - - Mei Gwilym (meigwilym) - - Babichev Maxim (rez1dent3) - - Keri Henare (kerihenare) - - Philip Frank - - Staormin - - Damien Harper (damien.harper) - - Ismail Turan - - Jody Mickey (jwmickey) - - pthompson - - Bert ter Heide (bertterheide) - - Tom Maguire - - Fabien - - Adria Lopez (adlpz) - - Sandro Hopf (senaria) - - Beth Binkovitz + - Antonio Peric-Mazar (antonioperic) + - adenkejawen + - bahram + - Adriaan Zonnenberg + - sam-bee + - Benjamin Laugueux + - Guillaume BRETOU (guiguiboy) + - Makdessi Alex + - Aleksejs Kovalovs (aleksejs1) + - Wissame MEKHILEF + - Christian Stocker + - Vladimir Vasilev (bobahvas) + - Jan Vernarsky + - Kevin Verschaeve (keversc) + - Victor Macko (victor_m) + - Tomaz Ahlin + - Atthaphon Urairat + - Sagrario Meneses + - Muharrem Demirci (mdemirci) + - William Pinaud (docfx) + - Mateusz Lerczak + - Dr. Gianluigi "Zane" Zanettini + - Rares Sebastian Moldovan (raresmldvn) + - Marin Bînzari (spartakusmd) + - Vincent Chalamon + - Dave Heineman (dheineman) + - Ksaveras Šakys (xawiers) + - maxime.perrimond + - Matteo Galli + - Dalibor Karlović + - Oleg Mifle + - phuc vo (phucwan) + - cgonzalez + - David Zuelke + - Simon Sargeant + - Lin Lu + - Vedran Mihočinec (v-m-i) + - Kélian Bousquet (kells) + - Constantine Shtompel + - Bram Van der Sype (brammm) + - Nikola Svitlica (thecelavi) + - mindaugasvcs + - insekticid + - Paulius Jarmalavičius (pjarmalavicius) + - david perez (davidpv) + - Derek Lambert (dlambert) + - ffd000 + - Benjamin Ellis + - Jeffrey Moelands (jeffreymoelands) + - bill moll + - Nerijus Arlauskas (nercury) + - Amine Matmati + - Ari Pringle (apringle) + - Toby Griffiths (tog) + - David Grüner (vworldat) + - Robert Campbell + - Benoit Leveque + - Randel Palu + - Bárbara Luz + - Romain Pierre + - Sobhan Sharifi (50bhan) + - Matt Wells + - Dennis Fehr + - everyx + - Vladislav Iurciuc + - dakur + - Fred Cox + - Bart Brouwer (bartbrouwer) + - linh + - Jonathan Gough + - Eric Caron + - Stewart Malik + - Stephan Wentz (temp) + - Artyom Protaskin + - Tomanhez + - Zander Baldwin + - sualko + - Pavol Tuka + - Daniel Perez Pinazo (pitiflautico) + - Peter Dietrich (xosofox) + - Pierre Rebeilleau (pierrereb) + - dropfen + - Christian Neff (secondtruth) + - PLAZANET Pierre (pedrotroller) + - AbdElKader Bouadjadja + - julien.galenski + - misterx + - Thomas Counsell + - Cédric Girard + - Gunther Konig + - Julien Pauli + - Christian Morgan + - ergiegonzaga + - Miloš Milutinović + - Pierre Grimaud (pgrimaud) + - Soner Sayakci + - Walther Lalk + - Emilien Escalle + - Jozef Môstka (mostkaj) + - karstennilsen + - Pierre Rineau + - Walid BOUGHDIRI (walidboughdiri) + - Johannes + - Marvin Petker + - ondrowan + - Jeremy Benoist + - ollie harridge (ollietb) + - ReScO + - Dan Ordille (dordille) + - Almog Baku (almogbaku) + - Alexandre Beaujour + - Pablo Schläpfer + - rkerner + - Jens Schulze + - Konstantin Scheumann + - Ariel J. Birnbaum + - Ian Phillips + - Ondřej Führer + - Ahmed Abdou + - Kevin Frantz + - sauliusnord + - M.Mahdi Mahmoodian + - RENAUDIN Xavier (xorrox) + - Christoph Kappestein + - Thanh Trần + - Frederic Godfrin + - alsar + - Artem (nexim) + - Justin Rainbow (jrainbow) + - Tom Corrigan (tomcorrigan) + - Marin Nicolae + - Nicolas Appriou + - Markus + - Benoit Mallo + - Andrei Igna + - Simone Ruggieri + - Michal Gebauer + - Peter Bex + - Florian Caron (shalalalala) + - Ikhsan Agustian + - Sebastian Göttschkes (sgoettschkes) + - Paul Matthews + - Jan Dobrovodský (j-dobr) + - Léo VINCENT + - Robin Duval (robin-duval) + - Junaid Farooq (junaidfarooq) + - Mihai Stancu + - Benjamin Pick + - vdauchy + - Penny Leach + - Florian Bogey + - koyolgecen + - Patrick Kuijvenhoven + - Yohann Tilotti + - Oscar Esteve (oesteve) + - Wim Hendrikx + - sal-car + - Asrorbek Sultanov + - Gennadi Janzen + - Tero Alén (tero) + - PabloKowalczyk + - Radoslaw Kowalewski + - alekLexis + - John VanDeWeghe + - Grégoire Hébert (gregoirehebert) + - d-ph + - Marie Minasyan (marie.minassyan) + - Sylvain METAYER + - Richard Heine + - Joan Cruz + - Aurelijus Rožėnas + - Nicolas Eeckeloo (neeckeloo) + - Jacek Wilczyński (jacekwilczynski) + - Ondřej Mirtes (mirtes) + - Dale.Nash + - Benoit Garret + - Fabio Panaccione + - Romain Geissler + - chillbram + - David Ronchaud + - Martynas Narbutas + - Marco Jantke + - Dmytro Dzubenko + - Joas Schilling + - BilgeXA + - jfcixmedia + - alanzarli + - Eric Krona + - Tom Kaminski + - hjkl + - Peter Schultz + - Max Voloshin (maxvoloshin) + - Attila Szeremi + - Eddie Abou-Jaoude (eddiejaoude) + - Frédéric Bouchery (fbouchery) + - Christian Schiffler + - Cedric Kastner (nurtext) + - Marco + - Bastien Clément (bastienclement) + - Pavel.Batanov + - Lars Ambrosius Wallenborn (larsborn) + - Ahmed EBEN HASSINE (famas23) + - helmi + - mlpo (mlpo) + - Nicolas Jourdan (nicolasjc) + - Flavian Sierk + - jack.shpartko + - Andy Raines + - Victor Truhanovich (victor_truhanovich) + - Benjamin Long + - nyro (nyro) + - Marcus + - Colin Michoudet + - Daniil Gentili + - Evgeny Z (meze) + - Jonas Hünig + - Richard Čepas + - Orestis + - Dcp (decap94) + - shdev + - Paul Le Corre + - Muhammed Akbulut + - Luis Pabon (luispabon) + - Peter Mead (petermead) + - Haritz + - kaiwa + - Pavel Golovin (pgolovin) + - Flohw + - alex + - Claus Due (namelesscoder) + - Sebastian Schwarz + - Simon Ackermann + - Simon Paarlberg (blamh) + - Haritz Iturbe (hizai) + - alefranz + - Alexander McCullagh (mccullagh) + - Adrien Gallou (agallou) + - Myke79 + - inspiran - k-sahara - - tamirvs - - Bruno Ziegler (sfcoder) - - Przemysław Piechota (kibao) - - Peter Orosz (ill_logical) - - ncou - - Alexander Cheprasov - - Liverbool (liverbool) - - Stephen Lewis (tehanomalousone) - - remieuronews - - Alexandre GESLIN - - Marcello Mönkemeyer (marcello-moenkemeyer) - - Kirk Madera - - Stefano A. (stefano93) - - David Stone - - curlycarla2004 - - David Soms - - George Yiannoulopoulos - - Michael Dowling (mtdowling) - - William Thomson (gauss) - - Vitalii - - Fabian Haase - - Ulrik McArdle - - VojtaB - - Alex Silcock - - boite - - Abdiel Carrazana (abdielcs) - - Christian López Espínola (penyaskito) - - Emilie Lorenzo - - Samy D (dinduks) - - Ryan Rud - - Daniel Rotter (danrot) - - Vivien - - Ionut Cioflan - - Anton (bonio) - - divinity76 - - Oleg Krasavin (okwinza) - - joris - - Zan Baldwin (zanderbaldwin) - - Piet Steinhart - - elattariyassine - - Filipe Guerra - - Yendric - - Emre YILMAZ - - Elías (eliasfernandez) - - Storkeus - - devel - - Stephen Clouse - - rkerner - - Mickael GOETZ - - Roman Orlov - - zorn - - Chris McGehee + - Beth Binkovitz + - Bermon Clément (chou666) + - Şəhriyar İmanov (shehriyari) + - Roromix + - Stanislav Gamaiunov (happyproff) + - Muhammad Elhwawshy + - Nasim + - Ken Stanley + - MARYNICH Mikhail (mmarynich-ext) + - Jules Lamur + - Vyacheslav Slinko + - Joris Garonian (grifx) + - Bert Hekman + - Arnau González + - Silas Joisten (silasjoisten) + - Florian Morello + - Richard Trebichavský + - alexpods + - Tomasz (timitao) + - Daniel Richter (richtermeister) + - Alberto Aldegheri + - Alex Vo (votanlean) + - Adrien Samson (adriensamson) + - Mantas Urnieža + - Mathieu Dewet (mdewet) + - Pierre Gasté (pierre_g) - Nowfel2501 - - Alejandro Diaz Torres - - Lars Ambrosius Wallenborn (larsborn) - - Thanh Trần - - Bart Ruysseveldt - - Ariel J. Birnbaum - - Nicolas Pion - - Jonathan Hedstrom - - Johan de Ruijter - - Johan Wilfer (johanwilfer) - - ddegentesh - - dinitrol - - André Filipe Gonçalves Neves (seven) - - Quique Porta (quiqueporta) - - Antoine Beyet - - Irmantas Šiupšinskas (irmantas) - - Giovanni Albero (johntree) - - Ala Eddine Khefifi (nayzo) - - Florian Heller - - Sjors Ottjes - - Andrei O - - karolsojko - - timesince - - Iwan van Staveren (istaveren) - - Kasper Hansen - - Chris Jones (leek) - - Tadas Gliaubicas (tadcka) - - Olexandr Kalaidzhy - - Cláudio Cesar - - Thomas Beaujean - - Anamarija Papić (anamarijapapic) - - Javier Núñez Berrocoso (javiernuber) - - Oussama Elgoumri - - Sébastien Decrême (sebdec) - - Milos Colakovic (project2481) - - Ross Tuck - - Pascal Woerde (pascalwoerde) - - Mathieu Morlon (glutamatt) - - Frederik Schwan - - Jakub Caban (lustmored) - - Pchol - - v.shevelev - - Matthieu - - Ashura - - allison guilhem - - Dominic Tubach - - Ramon Cuñat - - Julian Krzefski - - Evert Jan Hakvoort - - Yorkie Chadwick (yorkie76) - - Beno!t POLASZEK - - Henne Van Och (hennevo) - - Jason Schilling (chapterjason) - - Matthieu Prat - - twifty - - Vladislav Vlastovskiy (vlastv) - - David Stone - - Danilo Silva - - DidierLmn - - Success Go - - Michael Steininger - - František Maša - - Jordi Llonch (jordillonch) - - Nardberjean - - Alexander Pasichnik (alex_brizzz) - - Verlhac Gaëtan (viviengaetan) - - Iliya Miroslavov Iliev (i.miroslavov) - - Mark Spink - - Richard Quadling - - James Michael DuPont - - Eddy - - Menno Holtkamp - - samuel laulhau (lalop) - - Alexis - - Jérémy Jourdin (jjk801) - - Arek Bochinski - - Juan Luis (juanlugb) - - Antoine Leblanc - - riadh26 - - Tim Lieberman - - Lenar Lõhmus - - Alexis MARQUIS - - wesleyh - - AmsTaFF (amstaff) - - Kim Laï Trinh - - Dmitry Danilson - - Willem Mouwen - - Mario Young - - Joao Paulo V Martins (jpjoao) - - tinect (tinect) - - Andrii Boiko - - Johannes + - Zlatoslav Desyatnikov + - Chris McGehee + - Damian (baum) + - Mathias Geat + - Anthony Ferrara + - Artem Oliinyk (artemoliynyk) + - Daniel Iwaniec + - Robert Queck + - Igor Plantaš + - Kevin Vergauwen (innocenzo) + - Thomas Baccelli (atomiix) + - Alexander Zogheb + - Gerry Vandermaesen (gerryvdm) + - Guillaume Sainthillier (guillaume-sainthillier) + - Gerard Berengue Llobera (bere) + - Aaron Somi + - SenTisso + - Nicolas Roudaire + - Rafał Muszyński (rafmus90) + - Guillem Fondin (guillemfondin) + - HADJEDJ Vincent (hadjedjvincent) + - David Windell + - soyuka + - Jordi Rejas + - Jérémy CROMBEZ (jeremy) + - Carlos Fernandes (carferdas) + - Tomas Liubinas + - gedrox + - Toro Hill + - Evrard Boulou + - Vitali Tsyrkin + - Péter Buri (burci) + - zorn + - Samy D (dinduks) + - Jordan de Laune (jdelaune) + - Danil + - Karlos Presumido (oneko) + - inwebo veritas (inwebo) + - Adam Prickett + - Stas Soroka (stasyan) + - Bruno MATEU + - Imangazaliev Muhammad (imangazaliev) + - Emilie Lorenzo + - Denis Klementjev (dklementjev) + - Ioana Hazsda (ioana-hazsda) + - Christoph Vincent Schaefer (cvschaefer) + - Ferenczi Krisztian (fchris82) + - Kai Eichinger + - Daan van Renterghem + - Dmitriy Derepko + - gechetspr + - David Wolter (davewww) + - Vincent AMSTOUTZ (vincent_amstz) + - Eric Grimois + - sez-open + - Bernhard Rusch + - robmro27 + - Emmanuel Dreyfus + - Foxprodev + - mmokhi + - sabruss + - Olatunbosun Egberinde + - Hadrien Cren (hcren) + - Adam Wójs (awojs) + - rtek + - Volker (skydiablo) + - Mephistofeles + - Jérôme Nadaud (jnadaud) + - Christian Seel + - Bernard van der Esch (adeptofvoltron) + - julien_tempo1 (julien_tempo1) + - Wim Godden (wimg) + - Tom Panier (neemzy) + - Dmitri Petmanson + - Chris de Kok + - Vladimir Sazhin - Bram Tweedegolf (bram_tweedegolf) - - Zoran Makrevski (zmakrevski) - - Daniil Gentili - - Benjamin Long - - Artem (nexim) - - alsar - - ollie harridge (ollietb) - - Walid BOUGHDIRI (walidboughdiri) - - Rein Baarsma (solidwebcode) - - Thomas Boileau (tboileau) - - Matheus Gontijo - - marbul - - Serhiy Lunak (slunak) - - Lars Moelleken - - Dušan Kasan (dudo1904) - - Willem Verspyck - - Michał Marcin Brzuchalski (brzuchal) - - Nicholas Byfleet (nickbyfleet) - - Adrian Philipp - - oscartv - - Mike Milano (mmilano) - - Nicolas Séverin - - Ser5 - - Abdouni Karim (abdounikarim) - - Lin Clark - - Bojan - - Andreas Heigl (heiglandreas) - - Wojciech Sznapka - - John Nickell (jrnickell) - - Philippe Degeeter (pdegeeter) - - Ryan Linnit - - Darius Leskauskas (darles) - - temperatur - - Vincent MOULENE (vints24) - - Sébastien HOUZE - - Daniele Cesarini (ijanki) - - Jakub Janata (janatjak) - - tuqqu - - abulford - - Johan - - demeritcowboy - - Abdelhakim ABOULHAJ - - Morimoto Ryosuke - - Blackfelix - - Rafael Tovar - - MaPePeR - - Ahmed HANNACHI (tiecoders) - - Jos Elstgeest - - Miłosz Guglas (miloszowi) - - Jason Desrosiers - - Maciej Zgadzaj - - bch36 - - Gilles Gauthier - - Guile (guile) - - ging-dev - - Jorrit Schippers (jorrit) - - Ivo Valchev - - Yannick Bensacq (cibou) - - Aaron Piotrowski (trowski) - - Brian Freytag - - Icode4Food (icode4food) - - Brandon Antonio Lorenzo - - Felix Eymonot (hyanda) - - Asil Barkin Elik (asilelik) - - Quentin Favrie - - David Vancl - - Nicolas Sauveur (baishu) - - Schuyler Jager (sjager) - - Plamen Mishev (pmishev) - - Nicolas - - Ali Tavafi - - Grayson Koonce - - Rodolfo Ruiz - - Zuruuh - - Jérémie Broutier - - hainey - - Radosław Kowalewski - - Lance Chen - - Juan Gonzalez Montes (juanwilde) - - efeen - - Daniel Bannert - - Alexis Lefebvre - - tadas - - JG (jege) - - Sylvain Just - - Matt Emerson + - Kevin Mian Kraiker + - Charly Terrier (charlypoppins) + - Bruno Baguette + - Clément LEFEBVRE (nemoneph) + - Bart Ruysseveldt + - Romeo + - Maxim Lovchikov + - Matej Žilák (teo_sk) + - Pavel Prischepa + - Janusz Mocek + - Nilmar Sanchez Muguercia + - Marcus Jaschen + - g123456789l + - Raphaël Davaillaud + - Pierre LEJEUNE (darkanakin41) + - nerdgod + - Frank Naegler + - Ener-Getick + - Alexey Berezuev + - Brieuc Thomas + - Vladislav Rastrusny (fractalizer) + - Johannes Goslar + - Aurimas Rimkus (patrikas) + - simbera + - enomotodev + - tpetry + - AnrDaemon + - Romain + - Haikiri + - Marco Pfeiffer + - Bastien Picharles + - SuRiKmAn + - Jiri Velek + - Raul Rodriguez (raul782) + - Nico Hiort af Ornäs + - Antoine (antoinela_adveris) + - Guillaume Smolders (guillaumesmo) + - dlorek + - Bálint Szekeres + - Rene de Lima Barbosa (renedelima) + - Yura Uvarov (zim32) + - Aryel Tupinamba (dfkimera) + - Samuel Gordalina (gordalina) + - PaoRuby + - Klaus Purer + - drublic + - goohib + - David Brooks + - Arseny Razin + - Steve Marvell + - Ron Gähler (t-ronx) + - Thiago Melo + - vltrof + - Mihai Nica (redecs) + - Alberto Pirovano (geezmo) + - Chihiro Adachi (chihiro-adachi) + - Neagu Cristian-Doru (cristian-neagu) + - ttomor + - René Landgrebe + - Gordienko Vladislav + - GK-302 + - Philippe Pichet + - Arend Hummeling + - djama + - Francois Martin + - avi123 + - Eric Hertwig + - Taylan Kasap + - Nicolas Martin (cocorambo) + - Anton Babenko (antonbabenko) + - Christian López Espínola (penyaskito) + - MightyBranch + - Bernat Llibre Martín (bernatllibre) + - Lorenzo Adinolfi (loru88) + - Sezil + - Aleksey Prilipko + - Àlex Fiestas + - Sander van der Vlugt (stranding) + - Antonio Torres + - Mihail Krasilnikov (krasilnikovm) + - Yuriy Potemkin + - hamza + - Martin Auswöger + - Max Baldanza + - WaiSkats + - Yannick + - Goran (gog) + - Steve Hyde + - Adria Lopez (adlpz) + - Maciej Paprocki (maciekpaprocki) + - Jeroen Bouwmans + - Marek Víger (freezy) + - Fabien + - Saem Ghani + - Tom Maguire + - Viacheslav Sychov + - cybernet (cybernet2u) + - Tristan Pouliquen + - Philipp Strube + - Tim Porter + - Your Name + - Laurent Negre (raulnet) + - Julien Sanchez (sumbobyboys) + - Vašek Purchart (vasek-purchart) + - Greg Korba + - pthompson + - Erwan Nader (ernadoo) + - Jody Mickey (jwmickey) + - tamcy + - Jan Vernarsky + - CDR + - nuncanada + - Troy McCabe + - Vladislav Nikolayev (luxemate) + - gitlost + - gauss + - Arnaud Buathier (arnapou) + - Jeroen De Dauw (jeroendedauw) + - Siragusa (asiragusa) + - Fabien D. (fabd) + - Agata + - ShiraNai7 + - RichardGuilland + - Wojciech Zimoń + - abluchet + - Christian Rishøj + - Nikita Popov (nikic) + - Josef Cech + - Roman Tymoshyk (tymoshyk) + - Dmitrii Fedorenko (dmifedorenko) + - Gijs Kunze + - Ernest Hymel + - Mamikon Arakelyan (mamikon) + - Théo DELCEY + - Clément Bertillon (skigun) + - Kirill Roskolii + - NIRAV MUKUNDBHAI PATEL (niravpatel919) + - Qingshan Luo + - Kovacs Nicolas + - Alex Olmos (alexolmos) + - Camille Islasse + - Aleksandar Dimitrov (netbull) + - Ettore Del Negro + - Jorge Martin (jorgemartind) + - omerida + - Jelle Kapitein + - wesign (inscrutable01) + - HellFirePvP + - Alex Demchenko + - Patrick Berenschot + - Yannick + - Oleg Golovakhin (doc_tr) + - Wouter Diesveld + - Sergey Yuferev + - zolikonta + - Athorcis + - Marcel Berteler + - Maria Grazia Patteri + - qzylalala + - Neil Katin + - gazi04 + - Thomason, James + - Rémy LESCALLIER + - fbuchlak + - Javan Eskander + - Sebastian Drewer-Gutland (sdg) + - Dilek Erkut + - vlakoff + - Rudy Onfroy + - Victoria Quirante Ruiz (victoria) + - Luciano Mammino (loige) + - Angel Fernando Quiroz Campos (angelfqc) + - Johannes + - Joao Paulo V Martins (jpjoao) + - wivaku + - Olaf Klischat + - Brian Corrigan + - Eno Mullaraj (emullaraj) + - Felix Marezki + - Kai Eichinger + - Sergey Novikov (s12v) + - Abderrahim (phydev) + - Bhujagendra Ishaya + - Victor Garcia + - vlechemin + - Tom Hart + - Jan Christoph Beyer + - taiiiraaa + - Amirreza Shafaat (amirrezashafaat) + - Charles-Henri Bruyand + - 🦅KoNekoD + - Arrakis (arrakis) + - parinz1234 + - Matthew J Mucklo + - Anton Dyshkant + - Markus Staab + - Gilles Doge (gido) + - Peter van Dommelen + - Phil Davis + - Boris Medvedev + - rogamoore + - Oleg Sedinkin (akeylimepie) + - Jontsa + - Matt Lehner + - Ahmed Soliman (ahmedsoliman) + - Daniel González Zaballos (dem3trio) + - Jenne van der Meer + - Stefan Kruppa + - Luke Towers + - Emmanuel Vella (emmanuel.vella) + - Hein Zaw Htet™ - Thorsten Hallwas - - Gordienko Vladislav - - René Landgrebe - - jc - - Dario Savella - - jean pasqualini (darkilliant) - - Ole Rößner (basster) - - Joel Lusavuvu (enigma97) - - Kajetan Kołtuniak (kajtii) - - Zachary Tong (polyfractal) - - Pablo Maria Martelletti (pmartelletti) - - Markus Baumer - - markusu49 - - Adrian Olek (adrianolek) - - Martijn Croonen - - Jan Emrich - - adam-mospan - - Juan Traverso - - Mara Blaga - - Xavier Amado (xamado) - - Reda DAOUDI - - Gordienko Vladislav - - Stefan Koopmanschap - - lol768 - - Jeremy Benoist - - casdal - - mark burdett - - Benedict Massolle (bemas) - - mousezheng - - Janusz Jabłoński (yanoosh) - - Brian Freytag - - Valérian Galliat - - luffy1727 - - error56 - - Oleksii Bulba - - Nei Rauni Santos (nrauni) - - Brandon Kelly (brandonkelly) - - Sébastien HOUZÉ - - Oriol Mangas Abellan (oriolman) - - fmarchalemisys - - David Otton - - Luis Galeas - - Piers Warmers + - Thomas + - Mikko Pesari + - Vincent QUATREVIEUX + - Matt Emerson + - Oksana Kozlova (oksanakozlova) + - bokonet + - Gary Houbre (thegarious) + - Benjamin Rosenberger + - Tournoud (damientournoud) + - Sylvain Just + - JG (jege) + - iamvar + - Viktor Novikov (nowiko) + - Frederik Schmitt + - Ahmed Abdulrahman + - V1nicius00 + - Patrizio Bekerle + - Mehdi Mabrouk (mehdidev) + - rchoquet + - Dan Finnie + - LubenZA + - Guido Donnari + - Vladimir Khramtsov (chrome) + - Karl Shea + - Grégory Pelletier (ip512) + - Matthew (mattvick) + - František Bereň + - michal + - Valentin + - Ludek Stepan + - Kevin Auivinet + - Mark de Haan (markdehaan) + - Matijn (matijn-made) + - Mark Ogilvie + - Ulrik Nielsen (mrbase) + - Michel Krenz + - jack.thomas (jackthomasatl) + - Serhii Polishchuk (spolischook) + - Valentin VALCIU + - Claude Dioudonnat + - Ahmad Mayahi (ahmadmayahi) + - Bikal Basnet + - excelwebzone + - Peter Culka + - Tammy D + - Aleksei Lebedev + - peter + - ChrisC + - Martin Eckhardt + - Dmitrii Baranov + - Artem (digi) + - Damon Jones (damon__jones) + - Dan Harper + - JK Groupe + - Flinsch + - Javier + - Daniele Orru' (danydev) + - Karolis + - Vadim Tyukov (vatson) + - Dmytro Pigin (dotty) + - Raito Akehanareru (raito) + - Alexander Janssen (tnajanssen) + - Matthew Foster (mfoster) + - wetternest + - Julius Kiekbusch + - Marcin Twardowski + - Krzysztof Przybyszewski (kprzybyszewski) + - Vincent LEFORT (vlefort) + - matze + - Dmitry (staratel) + - Mohamed Karnichi (amiral) + - Pavinthan + - PierreRebeilleau + - Faton (notaf) + - Michael van Tricht + - Jānis Lukss + - Jeroen van den Nieuwenhuisen + - Alexandre Segura + - Artem Lopata + - Vladislav Krupenkin (ideea) + - Bruno Ziegler (sfcoder) + - Michael Gwynne + - Sebastian Utz + - Timon van der Vorm + - Niels Robin-Aubertin + - David Barratt + - Per Modin + - Patricia + - Ladislav Tánczos + - Christopher Georg (sky-chris) + - Nouhail AL FIDI (alfidi) + - Evgeny (disparity) + - j4nr6n (j4nr6n) + - CarolienBEER + - Jochen Bayer (jocl) + - Florent Olivaud + - VAN DER PUTTE Guillaume (guillaume_vdp) + - benkarrer-lab + - Jan Eichhorn (exeu) + - Simon Jamain + - Patricia Wagner + - anna-lena.waltinger + - Yurii K + - ryunosuke + - Tom Houdmont + - Sait KURT + - Martijn Evers + - Yoann MOROCUTTI + - Dario Guarracino + - Shiro + - Alexis MARQUIS + - Andreas Streichardt + - Bart Wach + - Ruslan Zavacky (ruslanzavacky) + - Houssem + - eminjk + - Benjamin Schultz (bschultz) + - Steve Preston + - Nathanaël Martel (nathanaelmartel) + - Elias Häußler + - Temuri Takalandze (abgeo) + - Thibault G + - kaywalker + - Jean-François Morin-Abdullah + - ged15 + - creiner + - Matthew Covey + - Martins Eglitis + - Florian Guimier + - Steeve Titeca (stiteca) + - Dan Brown + - czachor + - Pieter + - tomasz-kusy + - Michael Tibben + - root + - Peter Smeets (darkspartan) + - Gautier Deuette + - Arend-Jan Tetteroo + - Jiri Falis + - LHommet Nicolas (nicolaslh) + - Alexandru Năstase + - Aharon Perkel + - fruty + - Juan Ases García (ases) + - Tijs Verkoyen + - Jason Stephens + - kwiateusz + - Gonzalo Míguez + - Philipp + - Ben Miller + - Giorgio Premi + - mlazovla + - Andreas Heigl (heiglandreas) + - John Nickell (jrnickell) + - Michał Strzelecki + - Nicolas Macherey + - Daniel Strøm + - Giuseppe Petraroli (gpetraroli) + - Goran Juric + - Mathieu MARCHOIS (mmar) + - Jonny Schmid (schmidjon) + - Alan ZARLI + - Peter Bouwdewijn + - Kevin Weber + - Silvio Ginter + - Marc Jauvin + - mohammadreza honarkhah - Pedro Silva - Tony Vermeiren (tony) + - Zoran Makrevski (zmakrevski) + - Cyrille Bourgois (cyrilleb) + - AntoineDly + - Marek Binkowski - Malaney J. Hill - - Jairo Pastor + - Ninos + - Ivo Valchev - Ilya Vertakov + - Axel Venet + - Nick Stemerdink + - Alexandru Bucur + - ConneXNL + - Benhssaein Youssef + - Dan (dantleech) + - Hubert Moreau (hmoreau) + - Anamarija Papić (anamarijapapic) + - Mike Francis + - Paul Seiffert (seiffert) + - Reda DAOUDI + - Vladimir Pakhomchik + - Pablo Ogando Ferreira + - Sören Bernstein + - Roma (memphys) - Maks Rafalko (bornfree) - Andy Stanberry - Simon Frost - Cas van Dongen - emilienbouard (neime) - - Ionel Scutelnicu (ionelscutelnicu) + - Juliano Petronetto + - Gordienko Vladislav + - Stefan Koopmanschap + - Philippe Degeeter (pdegeeter) + - Ryan Linnit + - Thanos Polymeneas (thanos) + - Bart Baaten + - Maerlyn + - Gabi Udrescu + - Luis Galeas + - lol768 + - Tito Costa + - fmarchalemisys - cilefen (cilefen) - - Geoffrey Monte (numerogeek) - - Arrilot - - Tito Miguel Costa (titomiguelcosta) + - Yann (yann_eugone) + - Michal Čihař + - Iain Cambridge + - Ionel Scutelnicu (ionelscutelnicu) + - David Otton + - joris de wit (jdewit) + - Sébastien HOUZÉ + - Jaap van Otterdijk (jaapio) + - Gusakov Nikita (hell0w0rd) + - Curtis (ccorliss) + - andreyserdjuk + - Michael Hüneburg + - Brandon Kelly (brandonkelly) + - Ivan Yivoff + - pborreli + - Nei Rauni Santos (nrauni) + - Łukasz Makuch + - Abderrahman DAIF (death_maker) + - Robin Kanters (anddarerobin) + - Tiago Garcia (tiagojsag) + - Andrea Ruggiero (pupax) + - Ivan Tse + - Dominik Ritter (dritter) + - Tim van Densen + - Chris Tiearney + - Oz (import) + - Dmitry Korotovsky + - Ernesto Domato + - Conrad Kleinespel (conradk) + - Oleksii Bulba + - karl.rixon + - BiaDd + - patrick-mcdougle + - Laurent G. (laurentg) + - Maarten Nusteling (nusje2000) + - Benjamin Bender + - Darius Leskauskas (darles) + - Kevin Decherf + - error56 + - zcodes + - Ruben Jansen + - Thomas Beaujean + - Andriy + - Piers Warmers + - Alexander Onatskiy + - Roger Webb + - rvoisin + - Cayetano Soriano Gallego (neoshadybeat) + - Matteo Giachino (matteosister) - Jitendra Adhikari (adhocore) + - Cláudio Cesar + - Anthony Tenneriello + - Armando + - Monet Emilien + - Kevin Herrera (kherge) + - Yiorgos Kalligeros + - klyk50 + - Rosio (ben-rosio) + - Peter Thompson (petert82) + - Arek Bochinski - maxperei - johnstevenson + - Patrick Luca Fazzi (ap3ir0n) - ju1ius - - chispita - - Peter van Dommelen - - Kévin Gomez (kevin) - - David Gorges (davidgorges) - - Alex Nostadt - - André Laugks + - Roberto Guido + - Albert Ganiev (helios-ag) + - César Suárez (csuarez) + - Daniel Kozák + - George Sparrow + - Flavien Knuchel (knuch) + - Thomas BERTRAND (sevrahk) + - Harold Iedema + - Michael Lively (mlivelyjr) + - Andreas + - temperatur + - Vincent MOULENE (vints24) + - Sébastien HOUZE + - Farhad Hedayatifard + - Philipp Fritsche + - Prasetyo Wicaksono (jowy) + - Steve Müller + - David Négrier (moufmouf) + - Sortex + - Matthias Larisch + - Malte Wunsch (maltewunsch) + - Jörn Lang + - danilovict2 + - Drew Butler + - botbotbot + - Sergei Shitikov + - Pavel Starosek (octisher) + - Geoffrey Monte (numerogeek) + - Damien Vauchel (damien_vauchel) + - Arrilot - Viktoriia Zolotova - - Knallcharge - - aetxebeste - - Alfonso Fernández García - - Boullé William (williamboulle) - - Mehrdad - - Kevin Jansen - - Ferran Vidal - - DSeemiller - - kurozumi (kurozumi) - - Normunds - - Valouleloup - - Thomas Bibb - - Maerlyn + - Bruno Rodrigues de Araujo (brunosinister) + - Ben Gamra Housseine (hbgamra) + - rewrit3 + - Paul Mitchum (paul-m) + - Bert ter Heide (bertterheide) + - Antoine Bellion (abellion) + - Dmytro Liashko - Pablo Borowicz - Zdeněk Drahoš - Luis Muñoz - - Anne-Julia Seitz - - Illia Antypenko (aivus) - - Alessandra Lai - - Maxime Aknin (3m1x4m) - - Rares Vlaseanu (raresvla) - - Albion Bame (abame) - - Yoann MOROCUTTI - - Egor Gorbachev - - Ian Littman (iansltx) - - Bogdan - - Jean-Christophe Cuvelier [Artack] - - Kévin Gonella - - Rainrider - - Sylvain Lorinet - - Maxime AILLOUD (mailloud) - - mwos - - Asrorbek (asrorbek) - - developer-av - - Mario Blažek (marioblazek) - - gondo (gondo) - - Oxan van Leeuwen - - Romain Jacquart (romainjacquart) - - afaricamp - - AbdelatifAitBara - - Thibault G - - David de Boer (ddeboer) - - Philipp Kretzschmar - - Dionysis Arvanitis - - Steve Frécinaux - - Tobias Rautenkranz - - Siebe Vanden Eynden - - Jules Lamur - - povilas - - Pathpat - - Owen Gray (otis) - - Cyril HERRERA - - Michael Dawart (mdawart) - - Simone Di Maulo (toretto460) - - Aaron Scherer (aequasi) - - Albert Prat - - Stefan Hüsges (tronsha) - - Konstantin Scheumann - - Pablo Schläpfer - - Chris Maiden (matason) - - Daniel Perez Pinazo (pitiflautico) - - Derek Lambert (dlambert) - - Kélian Bousquet (kells) - - John Edmerson Pizarra - - Rikijs Murgs - - Raphael Hardt - - Kurt Thiemann - - pdragun - - Raphaël Droz - - VolCh - - Nathaniel Catchpole - - Žan V. Dragan - - Alan Bondarchuk - - Danil Khaliullin (bifidokk) - - Markus Staab - - Xavier RENAUDIN - - David Soria Parra + - Oliver Klee + - Bogdan Scordaliu + - Shude + - Mas Iting + - Markus Tacker + - Hugo Sales + - Olexandr Kalaidzhy + - Roy-Orbison + - Alexey Popkov + - Anton Zagorskii + - Shrey Puranik + - Patrik Patie Gmitter + - Nicolas Fabre (nfabre) + - Maxime AILLOUD (mailloud) + - mwos + - Yohan Giarelli (frequence-web) + - Asrorbek (asrorbek) + - Andrew Clark (tqt_andrew_clark) + - Andrea Sprega (asprega) + - Daniele Cesarini (ijanki) + - Lauris Binde (laurisb) + - Jakub Janata (janatjak) + - tuqqu + - Simon Mönch + - Dennis Haarbrink + - Linas Ramanauskas + - Lance McNearney + - Bernd Matzner (bmatzner) + - agaktr + - Pieter Jordaan + - Serhii Smirnov + - Michael Telgmann + - Igor Timoshenko (igor.timoshenko) + - Georgi Georgiev + - Pierre Sv (rrr63) + - ibasaw + - Rik van der Heijden + - Tobias Speicher + - Lin Clark + - Julia + - Joeri Verdeyen (jverdeyen) + - Halil Hakan Karabay (hhkrby) + - Keith Maika + - Jakub Sacha + - neFAST + - Andras Debreczeni + - Florent Cailhol + - Olivier Laviale (olvlvl) + - v.shevelev + - Artyum Petrov + - RAHUL K JHA + - Evgeny Efimov (edefimov) + - Ken Marfilla (marfillaster) + - Andrew (drew) + - Abdouni Karim (abdounikarim) + - Kevin Nadin (kevinjhappy) + - Alex Plekhanov + - Dan Patrick (mdpatrick) + - Thomas Ferney (thomasf) + - Arne Groskurth + - Lukas Naumann + - Urban Suppiger + - Roman Orlov + - Erfan Bahramali + - Abdouarrahmane FOUAD (fabdouarrahmane) + - Matthew Burns - Muhammad Aakash - Roman Tyshyk - - Paul LE CORRE - - dima-gr - - Abdulkadir N. A. - - Lance McNearney - - Oliver Klee - - botbotbot - - danilovict2 - - Michael Lively (mlivelyjr) - - George Sparrow - - Daniel Kozák - - Guillermo Gisinger (t3chn0r) - - Yann (yann_eugone) - - Michal Čihař - - Iain Cambridge - - Ivan Yivoff - - pborreli - - Ramon Kleiss (akathos) - - Marc Duboc (icemad) + - Serge (nfx) + - Wahyu Kristianto (kristories) + - AlberT + - Nicolas Schwartz (nicoschwartz) - Andrew Tch - ADmad - Yurun - azine - - Moritz Kraft (userfriendly) - - Warwick + - Jordan Hoff + - 蝦米 + - Nicolas Valverde + - Robert Worgul + - chispita + - Daniel Tschinder + - jamogon + - Peter van Dommelen + - MrNicodemuz + - Shane Preece (shane) + - Christiaan Wiesenekker + - Joe + - Botond Dani (picur) + - Pierre-Louis LAUNAY + - Marvin Butkereit + - tsufeki + - Sorin Pop (sorinpop) + - Leonid Terentyev + - Gerrit Drost - Mert Simsek (mrtsmsk0) - - Rutger Hertogh - - Clément R. (clemrwan) - - Michal Kurzeja (mkurzeja) - - Aurélien Fontaine - - Ben Oman - - Dmitry Hordinky - - Erwin Dirks - - Valérian Lepeule (vlepeule) - - Jonathan Poston + - Thomas Ploch + - Yury (daffox) - Jm Aribau (jmaribau) - Peter Zwosta - - Ahmed Shamim Hassan (me_shaon) - - Nicolas ASSING (nicolasassing) - - raplider - - Peter Potrowl - - Damien Fayet (rainst0rm) - - Koray Zorluoglu - - Andreas Forsblom (aforsblo) - - dbrekelmans - - Denis Yuzhanin - - Michael Pohlers (mick_the_big) - - Dawid Sajdak - - 2manypeople - - Ruslan Zavacky (ruslanzavacky) - - Cristobal Dabed + - Tischoi + - Sébastien Armand (khepin) - Claas Augner - - Igor Tarasov (polosatus) - - Andrea Quintino (dirk39) - - Jean-Baptiste Nahan - - Christoph König (chriskoenig) - - Simone Fumagalli (hpatoio) - - Tomáš Polívka (draczris) - - Brian Debuire - - Arman - - Chris Shennan (chrisshennan) - - Konstantinos Alexiou - - Peter Jaap Blaakmeer - - Nathan PAGE (nathix) - - Yoann MOROCUTTI - - Johannes Müller (johmue) - - db306 - - Alexandre Tranchant (alexandre_t) - - Michael Orlitzky - - downace - - Camille Islasse - - Alex Olmos (alexolmos) - - Kovacs Nicolas - - Luis Ramón López López (lrlopez) - - CDR - - MightyBranch - - Goran (gog) - - Lorenzo Adinolfi (loru88) - - Raphael Davaillaud - - Seyedramin Banihashemi (ramin) - - Stefan Kruppa - - Nicolas Bastien (nicolas_bastien) - - Mephistofeles - - Olatunbosun Egberinde - - Pavel.Batanov - - Chansig - - adenkejawen - - David Lima - - jersoe - - ChrisC - - Ludek Stepan - - Zakaria AMMOURA (zakariaamm) - - Christian Weiske - - pritasil - - Aaron Stephens (astephens) - - youssef saoubou - - Julien Boudry - - Thomas Chmielowiec - - Ramazan APAYDIN (rapaydin) - - Dariusz Ruminski - - Martin Mandl (m2mtech) - - spdionis - - Jesper Søndergaard Pedersen (zerrvox) - - mshavliuk - - Michael Nelson - - Elías Fernández - - RAHUL K JHA - - Gunnar Lium (gunnarlium) - - Rowan Manning - - gr8b - - Daniel Strøm - - Giuseppe Petraroli (gpetraroli) - - Goran Juric - - Mathieu MARCHOIS (mmar) - - Jonny Schmid (schmidjon) - - Alan ZARLI - - Peter Bouwdewijn - - Silvio Ginter - - Chris Tiearney - - Oz (import) - - Thomas Citharel (tcit) - - Jean Ragouin - - karl.rixon - - patrick-mcdougle - - ibasaw - - Tobias Speicher - - Sepehr Lajevardi - - Sean Templeton - - Alexander Bauer (abauer) - - Tom Newby (tomnewbyau) + - Mickael GOETZ + - Paul Ferrett + - parhs - Tayfun Aydin - Jack Wright - - MatTheCat - - Yann Rabiller (einenlum) - - Daniel Bartoníček - - Jake Bishop (yakobeyak) - - andreybolonin1989@gmail.com - - Vallel Blanco - - Starfox64 - - djordy - - Rémi Faivre (rfv) - - Daniel Mecke (daniel_mecke) - - Ilia Sergunin (maranqz) - - Ahto Türkson - - Dominik Pesch (dombn) - - Jean-Baptiste Delhommeau - - Enrico - - Sander Coolen (scoolen) - - Markus Thielen - - Hugo Fonseca (fonsecas72) - - Jens Hatlak - - Tyler Stroud - - Sema - - Vladislav (simpson) - - Mathieu Ledru (matyo91) - - Gaylord Poillon (gaylord_p) - - Yannick Vanhaeren (yvh) - - tirnanog06 - - Angel Koilov (po_taka) - - e-ivanov - - Konstantin Chigakov + - rhel-eo + - Francisco Facioni (fran6co) + - Sergei Gorjunov + - Patryk Kozłowski + - Markus Ramšak + - Mostafa + - Lucas Bustamante + - Julien Moulin (lizjulien) + - Maksym Romanowski (maxromanovsky) + - Per Sandström (per) + - Matthias Derer + - Daniel Tiringer + - Rudolf Ratusiński + - Constantine Shtompel + - Johan + - demeritcowboy + - Tim Strehle + - Bruno Nogueira Nascimento Wowk + - Chansig + - Maxime THIRY + - Mike Gladysch + - Michael + - michael.kubovic + - Clément R. (clemrwan) + - Mehdi Achour (machour) + - Marek Šimeček (mssimi) + - Kaipi Yann + - Kirill Nesmeyanov (serafim) + - Cesar Scur (cesarscur) + - benatespina (benatespina) + - Marcos Quesada (marcos_quesada) + - Alexandre Fiocre (demos77) + - popnikos + - Noel Light-Hilary + - Youpie + - Oleh Korneliuk - Mykola Zyk - - shreypuranik - - Jure (zamzung) - - Nicolas Appriou - - Laurent Clouet - - ureimers - - Vlad Dumitrache - - Anton Sukhachev (mrsuh) - - Kasperki - - shreyadenny - - Gautier Deuette - - tomasz-kusy - - Steeve Titeca (stiteca) - - vlechemin - - Victor Garcia - - Evrard Boulou - - Guillem Fondin (guillemfondin) - - Rafał Muszyński (rafmus90) - - Aaron Somi - - Kevin Vergauwen (innocenzo) - - Igor Plantaš - - Haritz - - Muhammed Akbulut - - Paul Le Corre - - Richard Čepas - - Tom Corrigan (tomcorrigan) - - Pierre Grimaud (pgrimaud) - - julien.galenski - - dropfen - - Pierre Rebeilleau (pierrereb) - - Sam Williams + - Vincent + - Abdelhakim ABOULHAJ + - Morimoto Ryosuke + - Wouter Sioen (wouter_sioen) + - Blackfelix + - Rafael Tovar + - MaPePeR + - Kirill Saksin + - Michal Kurzeja (mkurzeja) + - Elías Fernández + - Aurélien Fontaine + - David Courtey (david-crty) + - Ben Oman + - Dmitry Hordinky + - Alex Vasilchenko + - Stelian Mocanita (stelian) + - Michael Nelson + - Jérôme Dumas + - Javier Núñez Berrocoso (javiernuber) + - Balázs Benyó (duplabe) - Patrick Carlo-Hickman - carlos-ea - Pierre Tachoire - - Tomaz Ahlin - - Vladimir Vasilev (bobahvas) - - Christian Stocker - - Benjamin Laugueux - - Matthew Donadio - - Louis-Proffit - - Gerrit Addiks - - Tim Ward - - JakeFr - - Carlos Tasada - - Billie Thompson - - Waqas Ahmed - - Carl Julian Sauter - - Tema Yud - - Karolis Daužickas (kdauzickas) - - Geoff - - Maxwell Vandervelde - - Pablo Monterde Perez (plebs) - - Marcin Chwedziak - - Tim Jabs (rubinum) - - Nicolas Bondoux (nsbx) - - Juraj Surman - - Masao Maeda (brtriver) - - Viet Pham - - Yann LUCAS (drixs6o9) - - Jessica F Martinez - - Marion Hurteau (marionleherisson) - - Dmitriy Tkachenko (neka) - - Andreas Kleemann (andesk) - - Mikhail Prosalov (mprosalov) - - Christian Eikermann - - Giuseppe Arcuti - - xaav - - evgkord - - Michael Squires - - Ignacio Alveal - - Alex Vasilchenko - - David Courtey (david-crty) - - Thijs Reijgersberg - - Justin Reherman (jreherman) - - Robert Korulczyk - - Benjamin Franzke - - Danijel Obradović - - Erika Heidi Reinaldo (erikaheidi) - - RTUnreal - - Martijn Boers (plebian) - - j0k (j0k) - - Cesar Scur (cesarscur) - - Maxime THIRY - - Daniel Tiringer - - Peter Thompson (petert82) - - Robert Meijers - - Jason Stephens - - Gonzalo Míguez - - Philipp - - Ben Miller - - Rafał Toboła - - Łukasz Makuch - - Ivan Tse - - Laurent G. (laurentg) - - Maarten Nusteling (nusje2000) - - Loenix - - Flavien Knuchel (knuch) - - dantleech - - Guillaume Lajarige (molkobain) - - Dennis Haarbrink - - Alex Carol (picard89) - - Buster Neece - - Xesau - - agaktr - - InbarAbraham + - Bart Reunes (metalarend) + - Paweł Stasicki + - muchafm + - Drew Butler - François Poguet - Götz Gottwald + - Ralf Kühnel (ralfkuehnel) - Kubicki Kamil (kubik) - - Jimmy Leger (redpanda) - - Shamimul Alam + - Kamil Szalewski (szal1k) + - David Szkiba + - Florian Cellier + - Peter Simoncic + - Rachid Hammaoui (makmaoui) + - Sébastien Decrême (sebdec) + - Bogdan Rancichi (devck) + - Elliot Anderson (elliot) + - Martin Pärtel + - André Laugks + - Klaas Naaijkens + - Kévin Gomez (kevin) + - David Gorges (davidgorges) + - Kamil Piwowarski (cyklista) + - Barthold Bos + - kernig + - Vladimir Sadicov (xtech) + - m.chwedziak + - SnakePin + - Thijs Reijgersberg + - Alex Nostadt + - André Laugks + - Sergiy Sokolenko + - Milos Colakovic (project2481) + - Ross Tuck + - Ilya Chekalsky + - Yewhen Khoptynskyi (khoptynskyi) + - Sander Goossens (sandergo90) + - devel + - Aarón Nieves Fernández + - Cyrille Jouineau (tuxosaurus) + - JustDylan23 + - Miłosz Guglas (miloszowi) + - Jason Desrosiers + - Storkeus + - Abdulkadir N. A. + - dima-gr + - Justin Reherman (jreherman) + - Robert Korulczyk + - Gabriel Moreira + - Vincent Chalnot + - Paul LE CORRE + - Max Beutel + - Alexander Menk + - luffy1727 + - Lukas Kaltenbach + - Wickex + - Andrea Giuliano (shark) + - Alfonso Fernández García + - Fraller Balázs (fracsi) + - Sebastian Ionescu + - Valérian Galliat - Artem Kolesnikov (tyomo4ka) - - Dan Wilga - - Thomas Chmielowiec (chmielot) - - Raphael de Almeida (raphaeldealmeida) - Michael Hudson-Doyle - Alan Scott + - Markus Klein + - Koalabaerchen - baron (bastien) - - timaschew - - Ville Mattila - - Remi Collet - - Benjamin RICHARD - - takashiraki - - Joseph Maarek - - Arkalo2 - - Ostrzyciel - - Kamil Madejski (kmadejski) - - Erik van Wingerden - - Michael J - - Alexandre Pavy - - Mikko Ala-Fossi - - Reece Fowell (reecefowell) - - Cas - - MusikAnimal - - Troy Crawford - - Gemorroj (gemorroj) - - Boris Betzholz - - Robert Kopera - - Bohdan Pliachenko - - david-binda - - Sebastian Landwehr (dword123) - - Chris Tickner - - Rini Misini - - Tugba Celebioglu - - Juanmi Rodriguez Cerón - - Juan Miguel Besada Vidal (soutlink) + - pkowalczyk + - voodooism + - Piotr Zajac + - John Espiritu (johnillo) + - Jacek Kobus (jackks) + - Zeeshan Rashid (zeeshan) + - Franz Liedke (franzliedke) + - Ivan Nemets + - Elías (eliasfernandez) + - Yasmany Cubela Medina (bitgandtter) + - Emre YILMAZ + - neghmurken + - Carlos Ortega Huetos + - MatTheCat + - Yann Rabiller (einenlum) + - Yendric + - Daniel Bartoníček + - Filipe Guerra + - Jake Bishop (yakobeyak) + - elattariyassine + - Daniel Bannert + - Piet Steinhart + - andreybolonin1989@gmail.com From 383b43034af073eecd695fecc49f9f4f5834ea4e Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 6 Nov 2025 21:52:25 +0100 Subject: [PATCH 45/91] Update VERSION for 6.4.28 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index b2d9608470cc5..be7bf938b96c2 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.4.28-DEV'; + public const VERSION = '6.4.28'; public const VERSION_ID = 60428; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 28; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2026'; public const END_OF_LIFE = '11/2027'; From 105ca41d34a077851df8f4c02d4d80a82ba82bd3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 6 Nov 2025 21:57:28 +0100 Subject: [PATCH 46/91] Bump Symfony version to 6.4.29 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index be7bf938b96c2..470bed395cab9 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.4.28'; - public const VERSION_ID = 60428; + public const VERSION = '6.4.29-DEV'; + public const VERSION_ID = 60429; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 28; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 29; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '11/2026'; public const END_OF_LIFE = '11/2027'; From 84b47471509460a5b7d59b4d6dd4ff63fad7ed6c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 6 Nov 2025 21:58:07 +0100 Subject: [PATCH 47/91] Update CHANGELOG for 7.3.6 --- CHANGELOG-7.3.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG-7.3.md b/CHANGELOG-7.3.md index 595e09640b5b0..dbf07a56d5001 100644 --- a/CHANGELOG-7.3.md +++ b/CHANGELOG-7.3.md @@ -7,6 +7,25 @@ in 7.3 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/v7.3.0...v7.3.1 +* 7.3.6 (2025-11-06) + + * bug #62324 [HttpFoundation] Fix parsing hosts and schemes in URLs (nicolas-grekas) + * bug #62171 [Messenger] Fix commands writing to `STDERR` instead of `STDOUT` (wazum) + * bug #62315 Keep body size limit for AMP redirects (villermen) + * bug #62237 [Form] Fix EnumType choice_label logic for grouped choices (yoeunes) + * bug #62283 [Filesystem] Unify logic for isAbsolute() in Path (yoeunes) + * bug #62091 [BrowserKit] The BrowserKit history with parameter separator without slash. (biozshock) + * bug #62297 [Twig] Ensure WrappedTemplatedEmail::getReturnPath() returns a string (yoeunes) + * bug #62294 [Console] Add missing VERBOSITY_SILENT case in CommandDataCollector (yoeunes) + * bug #62290 [Routing] Fix matching the "0" URL (cs278) + * bug #62285 [HttpClient] Reject 3xx pushed responses (nicolas-grekas) + * bug #62267 [Config] Use the empty string instead of null as an array offset (santysisi) + * bug #62246 [HttpFoundation] Allow Request::setFormat() to override predefined formats (longwave) + * bug #62242 [MonologBridge] Accept HttpExceptionInterface in HttpCodeActivationStrategy (GromNaN) + * bug #62222 [Cache] fix ext-redis 6.2.0 compatibility (xabbuh) + * bug #62197 [Validator] Fix call to undefined getParser() in YamlValidator (yoeunes) + * bug #62201 [HtmlSanitizer] Remove `srcdoc` from allowed attributes (Spomky) + * 7.3.5 (2025-10-28) * bug #62153 [HttpFoundation] Fix issue where ServerEvent with "0" data is not sent (santysisi) From 9a9bc41a5d17e1efa1d44a057963235527c5800a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 6 Nov 2025 21:58:12 +0100 Subject: [PATCH 48/91] Update VERSION for 7.3.6 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 64a1fc149a72c..43c239d43fc43 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '7.3.6-DEV'; + public const VERSION = '7.3.6'; public const VERSION_ID = 70306; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 3; public const RELEASE_VERSION = 6; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '01/2026'; public const END_OF_LIFE = '01/2026'; From a2b613eb97db8485f605bcf901fbb1dd66377d03 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 6 Nov 2025 22:09:28 +0100 Subject: [PATCH 49/91] Bump Symfony version to 7.3.7 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 43c239d43fc43..397c31f109cb3 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '7.3.6'; - public const VERSION_ID = 70306; + public const VERSION = '7.3.7-DEV'; + public const VERSION_ID = 70307; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 3; - public const RELEASE_VERSION = 6; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 7; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '01/2026'; public const END_OF_LIFE = '01/2026'; From 27fa6c58882b693663dcd820fe7337fec5a3dc52 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 30 Oct 2025 14:14:20 +0100 Subject: [PATCH 50/91] compatibility with ext-redis 6.3 --- .../Cache/Traits/Redis63ProxyTrait.php | 162 ++++++++++++++++++ .../Component/Cache/Traits/Redis6Proxy.php | 1 + .../Cache/Traits/RedisCluster63ProxyTrait.php | 162 ++++++++++++++++++ .../Cache/Traits/RedisCluster6Proxy.php | 1 + 4 files changed, 326 insertions(+) create mode 100644 src/Symfony/Component/Cache/Traits/Redis63ProxyTrait.php create mode 100644 src/Symfony/Component/Cache/Traits/RedisCluster63ProxyTrait.php diff --git a/src/Symfony/Component/Cache/Traits/Redis63ProxyTrait.php b/src/Symfony/Component/Cache/Traits/Redis63ProxyTrait.php new file mode 100644 index 0000000000000..6f6b370173aa4 --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/Redis63ProxyTrait.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +if (version_compare(phpversion('redis'), '6.3.0', '>=')) { + /** + * @internal + */ + trait Redis63ProxyTrait + { + public function delifeq($key, $value): \Redis|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->delifeq(...\func_get_args()); + } + + public function hexpire($key, $ttl, $fields, $mode = null): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hexpire(...\func_get_args()); + } + + public function hexpireat($key, $time, $fields, $mode = null): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hexpireat(...\func_get_args()); + } + + public function hexpiretime($key, $fields): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hexpiretime(...\func_get_args()); + } + + public function hgetdel($key, $fields): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hgetdel(...\func_get_args()); + } + + public function hgetex($key, $fields, $expiry = null): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hgetex(...\func_get_args()); + } + + public function hGetWithMeta($key, $member): mixed + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hGetWithMeta(...\func_get_args()); + } + + public function hpersist($key, $fields): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpersist(...\func_get_args()); + } + + public function hpexpire($key, $ttl, $fields, $mode = null): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpexpire(...\func_get_args()); + } + + public function hpexpireat($key, $mstime, $fields, $mode = null): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpexpireat(...\func_get_args()); + } + + public function hpexpiretime($key, $fields): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpexpiretime(...\func_get_args()); + } + + public function hpttl($key, $fields): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpttl(...\func_get_args()); + } + + public function hsetex($key, $fields, $expiry = null): \Redis|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hsetex(...\func_get_args()); + } + + public function httl($key, $fields): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->httl(...\func_get_args()); + } + + public function vadd($key, $values, $element, $options = null): \Redis|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vadd(...\func_get_args()); + } + + public function vcard($key): \Redis|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vcard(...\func_get_args()); + } + + public function vdim($key): \Redis|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vdim(...\func_get_args()); + } + + public function vemb($key, $member, $raw = false): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vemb(...\func_get_args()); + } + + public function vgetattr($key, $member, $decode = true): \Redis|array|string|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vgetattr(...\func_get_args()); + } + + public function vinfo($key): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vinfo(...\func_get_args()); + } + + public function vismember($key, $member): \Redis|bool + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vismember(...\func_get_args()); + } + + public function vlinks($key, $member, $withscores = false): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vlinks(...\func_get_args()); + } + + public function vrandmember($key, $count = 0): \Redis|array|string|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vrandmember(...\func_get_args()); + } + + public function vrange($key, $min, $max, $count = -1): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vrange(...\func_get_args()); + } + + public function vrem($key, $member): \Redis|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vrem(...\func_get_args()); + } + + public function vsetattr($key, $member, $attributes): \Redis|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vsetattr(...\func_get_args()); + } + + public function vsim($key, $member, $options = null): \Redis|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vsim(...\func_get_args()); + } + } +} else { + /** + * @internal + */ + trait Redis63ProxyTrait + { + } +} diff --git a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php index 1cf207b16a6fb..e321fee6244ed 100644 --- a/src/Symfony/Component/Cache/Traits/Redis6Proxy.php +++ b/src/Symfony/Component/Cache/Traits/Redis6Proxy.php @@ -27,6 +27,7 @@ class Redis6Proxy extends \Redis implements ResetInterface, LazyObjectInterface { use Redis61ProxyTrait; use Redis62ProxyTrait; + use Redis63ProxyTrait; use LazyProxyTrait { resetLazyObject as reset; } diff --git a/src/Symfony/Component/Cache/Traits/RedisCluster63ProxyTrait.php b/src/Symfony/Component/Cache/Traits/RedisCluster63ProxyTrait.php new file mode 100644 index 0000000000000..374f96214b8cd --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/RedisCluster63ProxyTrait.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +if (version_compare(phpversion('redis'), '6.3.0', '>=')) { + /** + * @internal + */ + trait RedisCluster63ProxyTrait + { + public function delifeq($key, $value): \RedisCluster|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->delifeq(...\func_get_args()); + } + + public function hexpire($key, $ttl, $fields, $mode = null): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hexpire(...\func_get_args()); + } + + public function hexpireat($key, $time, $fields, $mode = null): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hexpireat(...\func_get_args()); + } + + public function hexpiretime($key, $fields): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hexpiretime(...\func_get_args()); + } + + public function hgetdel($key, $fields): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hgetdel(...\func_get_args()); + } + + public function hgetex($key, $fields, $expiry = null): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hgetex(...\func_get_args()); + } + + public function hgetWithMeta($key, $member): mixed + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hgetWithMeta(...\func_get_args()); + } + + public function hpersist($key, $fields): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpersist(...\func_get_args()); + } + + public function hpexpire($key, $ttl, $fields, $mode = null): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpexpire(...\func_get_args()); + } + + public function hpexpireat($key, $mstime, $fields, $mode = null): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpexpireat(...\func_get_args()); + } + + public function hpexpiretime($key, $fields): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpexpiretime(...\func_get_args()); + } + + public function hpttl($key, $fields): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hpttl(...\func_get_args()); + } + + public function hsetex($key, $fields, $expiry = null): \RedisCluster|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->hsetex(...\func_get_args()); + } + + public function httl($key, $fields): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->httl(...\func_get_args()); + } + + public function vadd($key, $values, $element, $options = null): \RedisCluster|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vadd(...\func_get_args()); + } + + public function vcard($key): \RedisCluster|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vcard(...\func_get_args()); + } + + public function vdim($key): \RedisCluster|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vdim(...\func_get_args()); + } + + public function vemb($key, $member, $raw = false): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vemb(...\func_get_args()); + } + + public function vgetattr($key, $member, $decode = true): \RedisCluster|array|string|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vgetattr(...\func_get_args()); + } + + public function vinfo($key): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vinfo(...\func_get_args()); + } + + public function vismember($key, $member): \RedisCluster|bool + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vismember(...\func_get_args()); + } + + public function vlinks($key, $member, $withscores = false): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vlinks(...\func_get_args()); + } + + public function vrandmember($key, $count = 0): \RedisCluster|array|string|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vrandmember(...\func_get_args()); + } + + public function vrange($key, $min, $max, $count = -1): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vrange(...\func_get_args()); + } + + public function vrem($key, $member): \RedisCluster|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vrem(...\func_get_args()); + } + + public function vsetattr($key, $member, $attributes): \RedisCluster|int|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vsetattr(...\func_get_args()); + } + + public function vsim($key, $member, $options = null): \RedisCluster|array|false + { + return ($this->lazyObjectState->realInstance ??= ($this->lazyObjectState->initializer)())->vsim(...\func_get_args()); + } + } +} else { + /** + * @internal + */ + trait RedisCluster63ProxyTrait + { + } +} diff --git a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php index 3fa80ca8ca88b..1ea025dd0fa11 100644 --- a/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php +++ b/src/Symfony/Component/Cache/Traits/RedisCluster6Proxy.php @@ -27,6 +27,7 @@ class RedisCluster6Proxy extends \RedisCluster implements ResetInterface, LazyOb { use RedisCluster61ProxyTrait; use RedisCluster62ProxyTrait; + use RedisCluster63ProxyTrait; use LazyProxyTrait { resetLazyObject as reset; } From 705f1ed37a3f6e69ea8eb3301d122be976e52ee3 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Wed, 5 Nov 2025 18:55:28 +0100 Subject: [PATCH 51/91] [PHPDoc] Fix various PHPDoc syntax errors --- .../Component/TypeInfo/TypeContext/TypeContextFactory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/TypeInfo/TypeContext/TypeContextFactory.php b/src/Symfony/Component/TypeInfo/TypeContext/TypeContextFactory.php index ec3d260ff621a..5b4c7682539fd 100644 --- a/src/Symfony/Component/TypeInfo/TypeContext/TypeContextFactory.php +++ b/src/Symfony/Component/TypeInfo/TypeContext/TypeContextFactory.php @@ -43,12 +43,12 @@ final class TypeContextFactory private static array $reflectionClassCache = []; /** - * @var array + * @var array> */ private array $intermediateTypeContextCache = []; /** - * @var array + * @var array> */ private array $typeContextCache = []; From e31b19232fbed646b06a8b728f085e0ec3bfe598 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 6 Nov 2025 16:02:08 +0100 Subject: [PATCH 52/91] [DependencyInjection] Fix merging explicit tags and #[AsTaggeditem] --- .../Attribute/AsTaggedItem.php | 4 +- .../Compiler/PriorityTaggedServiceTrait.php | 83 +++++++++---------- .../PriorityTaggedServiceTraitTest.php | 66 ++++++++++----- 3 files changed, 87 insertions(+), 66 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php b/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php index de751213acad5..3b94c60979db1 100644 --- a/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php +++ b/src/Symfony/Component/DependencyInjection/Attribute/AsTaggedItem.php @@ -20,8 +20,8 @@ class AsTaggedItem { /** - * @param string|null $index The property or method to use to index the item in the iterator/locator - * @param int|null $priority The priority of the item; the higher the number, the earlier the tagged service will be located in the iterator/locator + * @param string|null $index The index at which the service will be found when consuming tagged iterators/locators + * @param int|null $priority The priority of the service in iterators/locators; the higher the number, the earlier it will */ public function __construct( public ?string $index = null, diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php index 8c6b5b582770d..99a4556e5e403 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PriorityTaggedServiceTrait.php @@ -65,47 +65,57 @@ private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagNam $class = $definition->getClass(); $class = $container->getParameterBag()->resolveValue($class) ?: null; $reflector = null !== $class ? $container->getReflectionClass($class) : null; - $checkTaggedItem = !$definition->hasTag($definition->isAutoconfigured() ? 'container.ignore_attributes' : $tagName); + $loadFromDefaultMethods = $reflector && null !== $defaultPriorityMethod; + $phpAttributes = $definition->isAutoconfigured() && !$definition->hasTag('container.ignore_attributes') ? $reflector?->getAttributes(AsTaggedItem::class) : []; + + foreach ($phpAttributes ??= [] as $i => $attribute) { + $attribute = $attribute->newInstance(); + $phpAttributes[$i] = [ + 'priority' => $attribute->priority, + $indexAttribute ?? '' => $attribute->index, + ]; + if (null === $defaultPriority) { + $defaultPriority = $attribute->priority ?? 0; + $defaultIndex = $attribute->index; + } + } + if (1 >= \count($phpAttributes)) { + $phpAttributes = []; + } + + for ($i = 0; $i < \count($attributes); ++$i) { + if (!($attribute = $attributes[$i]) && $phpAttributes) { + array_splice($attributes, $i--, 1, $phpAttributes); + continue; + } - foreach ($attributes as $attribute) { $index = $priority = null; if (isset($attribute['priority'])) { $priority = $attribute['priority']; - } elseif (null === $defaultPriority && $defaultPriorityMethod && $reflector) { - $defaultPriority = PriorityTaggedServiceUtil::getDefault($serviceId, $reflector, $defaultPriorityMethod, $tagName, 'priority', $checkTaggedItem); + } elseif ($loadFromDefaultMethods) { + $defaultPriority = PriorityTaggedServiceUtil::getDefault($serviceId, $reflector, $defaultPriorityMethod, $tagName, 'priority') ?? $defaultPriority; + $defaultIndex = PriorityTaggedServiceUtil::getDefault($serviceId, $reflector, $defaultIndexMethod ?? 'getDefaultName', $tagName, $indexAttribute) ?? $defaultIndex; + $loadFromDefaultMethods = false; } $priority ??= $defaultPriority ??= 0; if (null === $indexAttribute && !$defaultIndexMethod && !$needsIndexes) { - $services[] = [$priority, ++$i, null, $serviceId, null]; + $services[] = [$priority, $i, null, $serviceId, null]; continue 2; } if (null !== $indexAttribute && isset($attribute[$indexAttribute])) { $index = $parameterBag->resolveValue($attribute[$indexAttribute]); } - if (null === $index && null === $defaultIndex && $defaultPriorityMethod && $reflector) { - $defaultIndex = PriorityTaggedServiceUtil::getDefault($serviceId, $reflector, $defaultIndexMethod ?? 'getDefaultName', $tagName, $indexAttribute, $checkTaggedItem); + if (null === $index && $loadFromDefaultMethods) { + $defaultPriority = PriorityTaggedServiceUtil::getDefault($serviceId, $reflector, $defaultPriorityMethod, $tagName, 'priority') ?? $defaultPriority; + $defaultIndex = PriorityTaggedServiceUtil::getDefault($serviceId, $reflector, $defaultIndexMethod ?? 'getDefaultName', $tagName, $indexAttribute) ?? $defaultIndex; + $loadFromDefaultMethods = false; } $index ??= $defaultIndex ??= $definition->getTag('container.decorator')[0]['id'] ?? $serviceId; - $services[] = [$priority, ++$i, $index, $serviceId, $class]; - } - - if ($reflector) { - $attributes = $reflector->getAttributes(AsTaggedItem::class); - $attributeCount = \count($attributes); - - foreach ($attributes as $attribute) { - $instance = $attribute->newInstance(); - - if (!$instance->index && 1 < $attributeCount) { - throw new InvalidArgumentException(\sprintf('Attribute "%s" on class "%s" cannot have an empty index when repeated.', AsTaggedItem::class, $class)); - } - - $services[] = [$instance->priority ?? 0, ++$i, $instance->index ?? $serviceId, $serviceId, $class]; - } + $services[] = [$priority, $i, $index, $serviceId, $class]; } } @@ -113,13 +123,11 @@ private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagNam $refs = []; foreach ($services as [, , $index, $serviceId, $class]) { - if (!$class) { - $reference = new Reference($serviceId); - } elseif ($index === $serviceId) { - $reference = new TypedReference($serviceId, $class); - } else { - $reference = new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $index); - } + $reference = match (true) { + !$class => new Reference($serviceId), + $index === $serviceId => new TypedReference($serviceId, $class), + default => new TypedReference($serviceId, $class, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $index), + }; if (null === $index) { $refs[] = $reference; @@ -137,25 +145,16 @@ private function findAndSortTaggedServices(string|TaggedIteratorArgument $tagNam */ class PriorityTaggedServiceUtil { - public static function getDefault(string $serviceId, \ReflectionClass $r, string $defaultMethod, string $tagName, ?string $indexAttribute, bool $checkTaggedItem): string|int|null + public static function getDefault(string $serviceId, \ReflectionClass $r, string $defaultMethod, string $tagName, ?string $indexAttribute): string|int|null { - $class = $r->getName(); - - if (!$checkTaggedItem && !$r->hasMethod($defaultMethod)) { - return null; - } - - if ($checkTaggedItem && !$r->hasMethod($defaultMethod)) { - foreach ($r->getAttributes(AsTaggedItem::class) as $attribute) { - return 'priority' === $indexAttribute ? $attribute->newInstance()->priority : $attribute->newInstance()->index; - } - + if (!$r->hasMethod($defaultMethod)) { return null; } if ($r->isInterface()) { return null; } + $class = $r->name; if (null !== $indexAttribute) { $service = $class !== $serviceId ? \sprintf('service "%s"', $serviceId) : 'on the corresponding service'; diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php index 3f767257def91..aa67cf96cf236 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/PriorityTaggedServiceTraitTest.php @@ -233,6 +233,7 @@ public function testTaggedItemAttributes() 'hello' => new TypedReference('service2', HelloNamedService::class), 'multi_hello_1' => new TypedReference('service6', MultiTagHelloNamedService::class), 'service1' => new TypedReference('service1', FooTagClass::class), + 'multi_hello_0' => new TypedReference('service6', MultiTagHelloNamedService::class), ]; $services = $priorityTaggedServiceTraitImplementation->test($tag, $container); @@ -240,22 +241,6 @@ public function testTaggedItemAttributes() $this->assertEquals($expected, $priorityTaggedServiceTraitImplementation->test($tag, $container)); } - public function testTaggedItemAttributesRepeatedWithoutNameThrows() - { - $container = new ContainerBuilder(); - $container->register('service1', MultiNoNameTagHelloNamedService::class) - ->setAutoconfigured(true) - ->addTag('my_custom_tag'); - - (new ResolveInstanceofConditionalsPass())->process($container); - $tag = new TaggedIteratorArgument('my_custom_tag', 'foo', 'getFooBar', exclude: ['service4', 'service5']); - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Attribute "Symfony\Component\DependencyInjection\Attribute\AsTaggedItem" on class "Symfony\Component\DependencyInjection\Tests\Compiler\MultiNoNameTagHelloNamedService" cannot have an empty index when repeated.'); - - (new PriorityTaggedServiceTraitImplementation())->test($tag, $container); - } - public function testResolveIndexedTags() { $container = new ContainerBuilder(); @@ -283,6 +268,48 @@ public function testResolveIndexedTags() $this->assertSame(array_keys($expected), array_keys($services)); $this->assertEquals($expected, $priorityTaggedServiceTraitImplementation->test($tag, $container)); } + + public function testAttributesAreMergedWithTags() + { + $container = new ContainerBuilder(); + $definition = $container->register('service_attr_first', MultiTagHelloNamedService::class); + $definition->setAutoconfigured(true); + $definition->addTag('my_custom_tag', ['foo' => 'z']); + $definition->addTag('my_custom_tag', []); + + (new ResolveInstanceofConditionalsPass())->process($container); + + $priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation(); + + $tag = new TaggedIteratorArgument('my_custom_tag', 'foo', 'getFooBar'); + $services = $priorityTaggedServiceTraitImplementation->test($tag, $container); + + $expected = [ + 'multi_hello_2' => new TypedReference('service_attr_first', MultiTagHelloNamedService::class), + 'multi_hello_1' => new TypedReference('service_attr_first', MultiTagHelloNamedService::class), + 'z' => new TypedReference('service_attr_first', MultiTagHelloNamedService::class), + 'multi_hello_0' => new TypedReference('service_attr_first', MultiTagHelloNamedService::class), + ]; + $this->assertSame(array_keys($expected), array_keys($services)); + $this->assertEquals($expected, $services); + } + + public function testAttributesAreFallbacks() + { + $container = new ContainerBuilder(); + $definition = $container->register('service_attr_first', MultiTagHelloNamedService::class); + $definition->setAutoconfigured(true); + $definition->addTag('my_custom_tag', ['foo' => 'z']); + + (new ResolveInstanceofConditionalsPass())->process($container); + + $priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation(); + + $tag = new TaggedIteratorArgument('my_custom_tag', 'foo', 'getFooBar'); + $services = $priorityTaggedServiceTraitImplementation->test($tag, $container); + + $this->assertEquals(['z' => new TypedReference('service_attr_first', MultiTagHelloNamedService::class)], $services); + } } class PriorityTaggedServiceTraitImplementation @@ -305,18 +332,13 @@ class HelloNamedService2 { } +#[AsTaggedItem(index: 'multi_hello_0', priority: 0)] #[AsTaggedItem(index: 'multi_hello_1', priority: 1)] #[AsTaggedItem(index: 'multi_hello_2', priority: 2)] class MultiTagHelloNamedService { } -#[AsTaggedItem(priority: 1)] -#[AsTaggedItem(priority: 2)] -class MultiNoNameTagHelloNamedService -{ -} - interface HelloInterface { public static function getFooBar(): string; From 2179ff67b857a592417b6fa899adad9d6e85d26c Mon Sep 17 00:00:00 2001 From: "Jorge P. Hernandez Lalcebo" Date: Thu, 6 Nov 2025 17:28:56 -0500 Subject: [PATCH 53/91] Postal mailer transport message ID retrieval --- .../Bridge/Postal/Tests/Transport/PostalApiTransportTest.php | 2 +- .../Mailer/Bridge/Postal/Transport/PostalApiTransport.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Postal/Tests/Transport/PostalApiTransportTest.php b/src/Symfony/Component/Mailer/Bridge/Postal/Tests/Transport/PostalApiTransportTest.php index b064380ad086a..1938ffde6c69e 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postal/Tests/Transport/PostalApiTransportTest.php +++ b/src/Symfony/Component/Mailer/Bridge/Postal/Tests/Transport/PostalApiTransportTest.php @@ -64,7 +64,7 @@ public function testSend() $this->assertSame(base64_encode('some attachment'), $body['attachments'][0]['data']); $this->assertSame('foo@bar.fr', $body['reply_to']); - return new JsonMockResponse(['message_id' => 'foobar'], [ + return new JsonMockResponse(['data' => ['message_id' => 'foobar']], [ 'http_code' => 200, ]); }); diff --git a/src/Symfony/Component/Mailer/Bridge/Postal/Transport/PostalApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Postal/Transport/PostalApiTransport.php index 3593f2e78c91b..028d135bd5132 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postal/Transport/PostalApiTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Postal/Transport/PostalApiTransport.php @@ -64,7 +64,7 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e throw new HttpTransportException('Unable to send an email: '.$result['message'].\sprintf(' (code %d).', $statusCode), $response); } - $sentMessage->setMessageId($result['message_id']); + $sentMessage->setMessageId($result['data']['message_id']); return $response; } From 0d1aba8b05f4783a08a97b09569ed751dca7786f Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Sun, 9 Nov 2025 10:35:58 +0100 Subject: [PATCH 54/91] [ExpressionLanguage] Compile numbers with var_export in Compiler::repr for thread-safety --- .../Component/ExpressionLanguage/Compiler.php | 10 +------ .../Tests/Node/ConstantNodeTest.php | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/ExpressionLanguage/Compiler.php b/src/Symfony/Component/ExpressionLanguage/Compiler.php index 419eef8d14a0f..b810d72aaee70 100644 --- a/src/Symfony/Component/ExpressionLanguage/Compiler.php +++ b/src/Symfony/Component/ExpressionLanguage/Compiler.php @@ -114,15 +114,7 @@ public function string(string $value): static public function repr(mixed $value): static { if (\is_int($value) || \is_float($value)) { - if (false !== $locale = setlocale(\LC_NUMERIC, 0)) { - setlocale(\LC_NUMERIC, 'C'); - } - - $this->raw($value); - - if (false !== $locale) { - setlocale(\LC_NUMERIC, $locale); - } + $this->raw(var_export($value, true)); } elseif (null === $value) { $this->raw('null'); } elseif (\is_bool($value)) { diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/Node/ConstantNodeTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/Node/ConstantNodeTest.php index f1b10041d0e44..36c8084eafeb0 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/Node/ConstantNodeTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/Node/ConstantNodeTest.php @@ -31,13 +31,40 @@ public static function getEvaluateData(): array public static function getCompileData(): array { return [ + // Booleans ['false', new ConstantNode(false)], ['true', new ConstantNode(true)], + + // Null ['null', new ConstantNode(null)], + + // Integers ['3', new ConstantNode(3)], + ['-10', new ConstantNode(-10)], + ['0', new ConstantNode(0)], + + // Floats ['3.3', new ConstantNode(3.3)], + ['42.0', new ConstantNode(42.0)], + ['-1.23', new ConstantNode(-1.23)], + ['0.1', new ConstantNode(0.1)], + ['1.0', new ConstantNode(1.0)], + ['1.0E-6', new ConstantNode(1.0e-6)], + ['1.23456789E+20', new ConstantNode(1.23456789e+20)], + ['3.3', new ConstantNode(3.2999999999999998)], + ['0.30000000000000004', new ConstantNode(0.1 + 0.2)], + ['INF', new ConstantNode(\INF)], + ['-INF', new ConstantNode(-\INF)], + ['NAN', new ConstantNode(\NAN)], + + // Strings ['"foo"', new ConstantNode('foo')], + ['""', new ConstantNode('')], + ['"a\\"b"', new ConstantNode('a"b')], + + // Arrays ['[0 => 1, "b" => "a"]', new ConstantNode([1, 'b' => 'a'])], + ['[]', new ConstantNode([])], ]; } From b8a983714f587c3edfa7bf6205c648512a5ea6c3 Mon Sep 17 00:00:00 2001 From: dogedede <167682813+dogedede@users.noreply.github.com> Date: Sun, 9 Nov 2025 20:05:08 +0800 Subject: [PATCH 55/91] Remove review state for Belarusian translations (entries 141 and 142) --- .../Validator/Resources/translations/validators.be.xlf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf index 13c6d43a23026..448a2e9e4f030 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.be.xlf @@ -548,11 +548,11 @@ The image has too many pixels ({{ pixels }} pixels). Maximum amount expected is {{ max_pixels }} pixels. - Малюнак мае занадта шмат пікселяў ({{ pixels }}). Максімальная дапушчальная колькасць {{ max_pixels }}. + Малюнак мае занадта шмат пікселяў ({{ pixels }}). Максімальная дапушчальная колькасць {{ max_pixels }}. This filename does not match the expected charset. - Гэта назва файла не адпавядае чаканаму набору знакаў. + Гэта назва файла не адпавядае чаканаму набору знакаў. From 5ffa260fa434c0b2b665d552f3a5a5a9474b51a5 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Sat, 8 Nov 2025 20:47:53 +0100 Subject: [PATCH 56/91] [OptionsResolver] Ensure remove() also unsets deprecation status --- .../OptionsResolver/OptionsResolver.php | 2 +- .../Tests/OptionsResolverTest.php | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index da7553cfb20c7..9b9fe43cce410 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -803,7 +803,7 @@ public function remove(string|array $optionNames): static foreach ((array) $optionNames as $option) { unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option]); - unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option], $this->info[$option]); + unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option], $this->info[$option], $this->deprecated[$option]); } return $this; diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index a9f3aaf30562a..de4a4c2629052 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -2566,4 +2566,33 @@ public function testPrototypeDefinition() $this->assertSame($expectedOptions, $actualOptions); } + + public function testRemoveAlsoRemovesDeprecation() + { + $this->resolver->setDefined('foo'); + $this->resolver->setDeprecated('foo', 'vendor/package', '1.0'); + $this->assertTrue($this->resolver->isDeprecated('foo')); + + $this->resolver->remove('foo'); + $this->assertFalse($this->resolver->isDeprecated('foo')); + + $this->resolver->setDefault('foo', 'bar'); + $this->assertFalse($this->resolver->isDeprecated('foo')); + + $count = 0; + set_error_handler(static function (int $type) use (&$count) { + if (\E_USER_DEPRECATED === $type) { + ++$count; + } + + return false; + }); + + try { + $this->resolver->resolve(['foo' => 'value']); + $this->assertSame(0, $count); + } finally { + restore_error_handler(); + } + } } From 87ec08d43aa0b26eaba1aa54283c94d81549b0d1 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Sat, 8 Nov 2025 17:48:27 +0100 Subject: [PATCH 57/91] [Clock] Align MockClock::sleep() behavior with NativeClock for negative values --- src/Symfony/Component/Clock/MockClock.php | 4 ++++ src/Symfony/Component/Clock/Tests/MockClockTest.php | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Symfony/Component/Clock/MockClock.php b/src/Symfony/Component/Clock/MockClock.php index 9ba2726bf3453..64424aba85cdb 100644 --- a/src/Symfony/Component/Clock/MockClock.php +++ b/src/Symfony/Component/Clock/MockClock.php @@ -54,6 +54,10 @@ public function now(): DatePoint public function sleep(float|int $seconds): void { + if (0 >= $seconds) { + return; + } + $now = (float) $this->now->format('Uu') + $seconds * 1e6; $now = substr_replace(\sprintf('@%07.0F', $now), '.', -6, 0); $timezone = $this->now->getTimezone(); diff --git a/src/Symfony/Component/Clock/Tests/MockClockTest.php b/src/Symfony/Component/Clock/Tests/MockClockTest.php index f54c27e78dd25..d74de22798424 100644 --- a/src/Symfony/Component/Clock/Tests/MockClockTest.php +++ b/src/Symfony/Component/Clock/Tests/MockClockTest.php @@ -117,4 +117,14 @@ public function testWithTimeZone() $this->assertNotSame($clock, $utcClock); $this->assertSame('UTC', $utcClock->now()->getTimezone()->getName()); } + + public function testSleepWithNegativeValueDoesNothing() + { + $initialTime = new \DateTimeImmutable('2000-01-01 12:00:00 UTC'); + + $clock = new MockClock($initialTime); + $clock->sleep(-10.5); + + $this->assertEquals($initialTime, $clock->now()); + } } From f2cf86250344f692cbdce55f88a0977d2a5c20e5 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Wed, 12 Nov 2025 00:27:57 +0100 Subject: [PATCH 58/91] [Yaml] Fix parsing of unquoted multiline scalars with comments or blank lines --- src/Symfony/Component/Yaml/Parser.php | 9 +++ .../Component/Yaml/Tests/ParserTest.php | 67 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 40702ed639e7a..ebfd27d432404 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -762,6 +762,11 @@ private function parseValue(string $value, int $flags, string $context): mixed $lines = []; while ($this->moveToNextLine()) { + if ($this->isCurrentLineBlank()) { + $lines[] = ''; + continue; + } + // unquoted strings end before the first unindented line if (0 === $this->getCurrentLineIndentation()) { $this->moveToPreviousLine(); @@ -769,6 +774,10 @@ private function parseValue(string $value, int $flags, string $context): mixed break; } + if ($this->isCurrentLineComment()) { + continue; + } + $lines[] = trim($this->currentLine); } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index ebf032795fcec..b523612a6ed93 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1751,6 +1751,73 @@ public function testParseMultiLineUnquotedString() $this->assertSame(['foo' => 'bar baz foobar foo', 'bar' => 'baz'], $this->parser->parse($yaml)); } + /** + * @dataProvider getUnquotedMultilineScalarIgnoresCommentsData + */ + public function testUnquotedMultilineScalarIgnoresComments(string $yaml, array $expected) + { + $this->assertSame($expected, $this->parser->parse($yaml)); + } + + public static function getUnquotedMultilineScalarIgnoresCommentsData() + { + yield 'comments interspersed' => [ + << 'unquoted next line final line', + 'another_key' => 'works', + ], + ]; + + yield 'only comments' => [ + << 'unquoted', + 'another_key' => 'works', + ], + ]; + + yield 'blank lines and comments' => [ + << "unquoted next line\nfinal line", + 'another_key' => 'works', + ], + ]; + + yield 'comment at end' => [ + << 'unquoted next line', + 'another_key' => 'works', + ], + ]; + } + /** * @dataProvider unquotedStringWithTrailingComment */ From d96afe2d501e15435bb5c4c82f916a141b5c352f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 12 Nov 2025 10:33:48 +0100 Subject: [PATCH 59/91] run test using a read-only directory on Windows too --- .../PhpConfigReferenceDumpPassTest.php | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PhpConfigReferenceDumpPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PhpConfigReferenceDumpPassTest.php index c38e15f494936..e63ed13a379c1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PhpConfigReferenceDumpPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/PhpConfigReferenceDumpPassTest.php @@ -26,17 +26,31 @@ class PhpConfigReferenceDumpPassTest extends TestCase { + private string $readOnlyDir; private string $tempDir; protected function setUp(): void { $this->tempDir = sys_get_temp_dir().'/sf_test_config_reference'; mkdir($this->tempDir, 0o777, true); + + // Create a read-only directory to simulate write errors + $this->readOnlyDir = $this->tempDir.'/readonly'; + mkdir($this->readOnlyDir, 0o444, true); + + // Make the directory read-only on Windows + if ('\\' === \DIRECTORY_SEPARATOR) { + exec('attrib +r '.escapeshellarg($this->readOnlyDir)); + } } protected function tearDown(): void { if (is_dir($this->tempDir)) { + if ('\\' === \DIRECTORY_SEPARATOR) { + exec('attrib -r '.escapeshellarg($this->readOnlyDir)); + } + $fs = new Filesystem(); $fs->remove($this->tempDir); } @@ -64,23 +78,15 @@ public function testProcessWithConfigDir() public function testProcessIgnoresFileWriteErrors() { - if ('\\' === \DIRECTORY_SEPARATOR) { - self::markTestSkipped('Cannot reliably make directory read-only on Windows.'); - } - - // Create a read-only directory to simulate write errors - $readOnlyDir = $this->tempDir.'/readonly'; - mkdir($readOnlyDir, 0o444, true); - $container = new ContainerBuilder(); $container->setParameter('.container.known_envs', ['dev', 'prod', 'test']); - $pass = new PhpConfigReferenceDumpPass($readOnlyDir.'/reference.php', [ + $pass = new PhpConfigReferenceDumpPass($this->readOnlyDir.'/reference.php', [ TestBundle::class => ['all' => true], ]); $pass->process($container); - $this->assertFileDoesNotExist($readOnlyDir.'/reference.php'); + $this->assertFileDoesNotExist($this->readOnlyDir.'/reference.php'); $this->assertEmpty($container->getResources()); } From c2dfe2f228470965fdb61682be6d76beae021cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 12 Nov 2025 11:57:56 +0100 Subject: [PATCH 60/91] [FrameworkBundle] Update deprecation message for collect_serializer_data --- .../FrameworkBundle/DependencyInjection/FrameworkExtension.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 347c4762a5c4c..d6898db49d59b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1053,7 +1053,7 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $ } if (false === $config['collect_serializer_data']) { - trigger_deprecation('symfony/framework-bundle', '7.3', 'Setting the "framework.profiler.collect_serializer_data" config option to "false" is deprecated.'); + trigger_deprecation('symfony/framework-bundle', '7.3', 'Not setting the "framework.profiler.collect_serializer_data" config option to "true" is deprecated.'); } if ($this->isInitializedConfigEnabled('serializer') && $config['collect_serializer_data']) { From 741f633945fd504e4e4328371ff65cd14432e8fa Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 12 Nov 2025 12:22:55 +0100 Subject: [PATCH 61/91] Update CHANGELOG for 6.4.29 --- CHANGELOG-6.4.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG-6.4.md b/CHANGELOG-6.4.md index 08f66befc59fc..ba92750786213 100644 --- a/CHANGELOG-6.4.md +++ b/CHANGELOG-6.4.md @@ -7,6 +7,10 @@ in 6.4 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.4.0...v6.4.1 +* 6.4.29 (2025-11-12) + + * security #cve-2025-64500 [HttpFoundation] Fix parsing pathinfo with no leading slash (nicolas-grekas) + * 6.4.28 (2025-11-06) * bug #62324 [HttpFoundation] Fix parsing hosts and schemes in URLs (nicolas-grekas) From bf1f849447efb2bd872be46654ab810bcfe6b8cf Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 12 Nov 2025 12:22:59 +0100 Subject: [PATCH 62/91] Update VERSION for 6.4.29 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 470bed395cab9..cf4de3085f057 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.4.29-DEV'; + public const VERSION = '6.4.29'; public const VERSION_ID = 60429; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 29; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2026'; public const END_OF_LIFE = '11/2027'; From 9951c6de28c769dc575573663483e494671c02f5 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 12 Nov 2025 12:32:01 +0100 Subject: [PATCH 63/91] Bump Symfony version to 6.4.30 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index cf4de3085f057..3a9c63380557e 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -76,12 +76,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.4.29'; - public const VERSION_ID = 60429; + public const VERSION = '6.4.30-DEV'; + public const VERSION_ID = 60430; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 29; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 30; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '11/2026'; public const END_OF_LIFE = '11/2027'; From e12524e8285ea58e4d7a56f3f2a3a2d48574ecfe Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 12 Nov 2025 12:38:35 +0100 Subject: [PATCH 64/91] Update CHANGELOG for 7.3.7 --- CHANGELOG-7.3.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG-7.3.md b/CHANGELOG-7.3.md index dbf07a56d5001..5cd69bd01a04b 100644 --- a/CHANGELOG-7.3.md +++ b/CHANGELOG-7.3.md @@ -7,6 +7,11 @@ in 7.3 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/v7.3.0...v7.3.1 +* 7.3.7 (2025-11-12) + + * security #cve-2025-64500 [HttpFoundation] Fix parsing pathinfo with no leading slash (nicolas-grekas) + * bug #62333 Postal mailer transport message ID retrieval (lalcebo) + * 7.3.6 (2025-11-06) * bug #62324 [HttpFoundation] Fix parsing hosts and schemes in URLs (nicolas-grekas) From 2be828b43f10bb0e61230e7e0b3f52eb8d162d78 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 12 Nov 2025 12:38:40 +0100 Subject: [PATCH 65/91] Update VERSION for 7.3.7 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 397c31f109cb3..0a7055f838ce1 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '7.3.7-DEV'; + public const VERSION = '7.3.7'; public const VERSION_ID = 70307; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 3; public const RELEASE_VERSION = 7; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '01/2026'; public const END_OF_LIFE = '01/2026'; From 8ea1b3a46be12c936af56109981afd456d4d0895 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 12 Nov 2025 12:49:44 +0100 Subject: [PATCH 66/91] Bump Symfony version to 7.3.8 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 0a7055f838ce1..1301ec047ce75 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -73,12 +73,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '7.3.7'; - public const VERSION_ID = 70307; + public const VERSION = '7.3.8-DEV'; + public const VERSION_ID = 70308; public const MAJOR_VERSION = 7; public const MINOR_VERSION = 3; - public const RELEASE_VERSION = 7; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 8; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '01/2026'; public const END_OF_LIFE = '01/2026'; From 7d9c8109abcf03e7e66a49600bc48f912d6e162b Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Sat, 8 Nov 2025 16:30:25 +0100 Subject: [PATCH 67/91] [OptionsResolver] Fix missing prototype key in nested error paths --- .../OptionsResolver/OptionsResolver.php | 5 +++ .../Tests/OptionsResolverTest.php | 42 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 9b9fe43cce410..3a232a3802c73 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -964,6 +964,11 @@ public function offsetGet(mixed $option, bool $triggerDeprecation = true): mixed $resolver = new self(); $resolver->prototype = false; $resolver->parentsOptions = $this->parentsOptions; + + if ($this->prototype && null !== $this->prototypeIndex && null !== ($parentOptionKey = array_key_last($resolver->parentsOptions))) { + $resolver->parentsOptions[$parentOptionKey] .= \sprintf('[%s]', $this->prototypeIndex); + } + $resolver->parentsOptions[] = $option; foreach ($this->nested[$option] as $closure) { $closure($resolver, $this); diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index de4a4c2629052..c2c5bc45c5d6c 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -2595,4 +2595,46 @@ public function testRemoveAlsoRemovesDeprecation() restore_error_handler(); } } + + public function testNestedPrototypeErrorPathHasFullContext() + { + $resolver = new OptionsResolver(); + + $resolver->setDefault('connections', static function (OptionsResolver $connResolver) { + $connResolver->setPrototype(true); + $connResolver->setRequired(['host', 'database']); + $connResolver->setDefault('user', 'root'); + + $connResolver->setDefault('replicas', static function (OptionsResolver $replicaResolver) { + $replicaResolver->setPrototype(true); + $replicaResolver->setRequired(['host']); + $replicaResolver->setDefault('user', 'read_only'); + }); + }); + + $this->expectException(MissingOptionsException::class); + $this->expectExceptionMessage('The required option "connections[main_db][replicas][1][host]" is missing.'); + + $options = [ + 'connections' => [ + 'main_db' => [ + 'host' => 'localhost', + 'database' => 'app_db', + 'replicas' => [ + ['host' => 'replica-01.local', 'user' => 'read_only'], + ['user' => 'other_user'], // Index 1 -> "host" is missing here + ], + ], + 'audit_db' => [ + 'host' => 'audit.local', + 'database' => 'audit_db', + 'replicas' => [ + ['host' => 'audit-replica.local'], + ], + ], + ], + ]; + + $resolver->resolve($options); + } } From 45e43139b2dfa9d00701fc775fe4b64aeac7cfbf Mon Sep 17 00:00:00 2001 From: Link1515 Date: Wed, 8 Oct 2025 15:52:33 +0800 Subject: [PATCH 68/91] [Serializer] fix Inherited properties normalization --- .../Normalizer/ObjectNormalizer.php | 15 ++++- .../Tests/Normalizer/ObjectNormalizerTest.php | 64 ++++++++++++++++++- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php index 0793d7da1a753..aef1e963dc7df 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -107,7 +107,7 @@ protected function extractAttributes(object $object, ?string $format = null, arr 'i' => str_starts_with($name, 'is') && isset($name[$i = 2]), default => false, } && !ctype_lower($name[$i])) { - if ($reflClass->hasProperty($name)) { + if ($this->hasProperty($reflMethod, $name)) { $attributeName = $name; } else { $attributeName = substr($name, $i); @@ -139,6 +139,19 @@ protected function extractAttributes(object $object, ?string $format = null, arr return array_keys($attributes); } + private function hasProperty(\ReflectionMethod $method, string $propName): bool + { + $class = $method->getDeclaringClass(); + + do { + if ($class->hasProperty($propName)) { + return true; + } + } while ($class = $class->getParentClass()); + + return false; + } + protected function getAttributeValue(object $object, string $attribute, ?string $format = null, array $context = []): mixed { $mapping = $this->classDiscriminatorResolver?->getMappingForMappedObject($object); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 8ef6ead38f8f7..a07f1decc261d 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -1017,7 +1017,7 @@ public function testNormalizeObjectWithPropertyAndAccessorMethodsWithSameName() $this->assertSame('getFoo', $denormalized->getFoo()); // On the initial object the value was 'foo', but the normalizer prefers the accessor method 'getFoo' - // Thus on the denoramilzed object the value is 'getFoo' + // Thus on the denormalized object the value is 'getFoo' $this->assertSame('foo', $object->foo); $this->assertSame('getFoo', $denormalized->foo); @@ -1026,6 +1026,56 @@ public function testNormalizeObjectWithPropertyAndAccessorMethodsWithSameName() $this->assertSame('isFoo', $denormalized->isFoo()); } + public function testNormalizeChildExtendsObjectWithPropertyAndAccessorSameName() + { + // This test follows the same logic used in testNormalizeObjectWithPropertyAndAccessorMethodsWithSameName() + $normalizer = $this->getNormalizerForAccessors(); + + $object = new ChildExtendsObjectWithPropertyAndAccessorSameName( + 'foo', + 'getFoo', + 'canFoo', + 'hasFoo', + 'isFoo' + ); + $normalized = $normalizer->normalize($object); + + $this->assertSame([ + 'getFoo' => 'getFoo', + 'canFoo' => 'canFoo', + 'hasFoo' => 'hasFoo', + 'isFoo' => 'isFoo', + // The getFoo accessor method is used for foo, thus it's also 'getFoo' instead of 'foo' + 'foo' => 'getFoo', + ], $normalized); + + $denormalized = $this->normalizer->denormalize($normalized, ChildExtendsObjectWithPropertyAndAccessorSameName::class); + + $this->assertSame('getFoo', $denormalized->getFoo()); + + // On the initial object the value was 'foo', but the normalizer prefers the accessor method 'getFoo' + // Thus on the denormalized object the value is 'getFoo' + $this->assertSame('foo', $object->foo); + $this->assertSame('getFoo', $denormalized->foo); + + $this->assertSame('hasFoo', $denormalized->hasFoo()); + $this->assertSame('canFoo', $denormalized->canFoo()); + $this->assertSame('isFoo', $denormalized->isFoo()); + } + + public function testNormalizeChildWithPropertySameAsParentMethod() + { + $normalizer = $this->getNormalizerForAccessors(); + + $object = new ChildWithPropertySameAsParentMethod('foo'); + $normalized = $normalizer->normalize($object); + + $this->assertSame([ + 'foo' => 'foo', + ], + $normalized); + } + /** * Priority of accessor methods is defined by the PropertyReadInfoExtractorInterface passed to the PropertyAccessor * component. By default ReflectionExtractor::$defaultAccessorPrefixes are used. @@ -1501,6 +1551,18 @@ public function isFoo() } } +class ChildExtendsObjectWithPropertyAndAccessorSameName extends ObjectWithPropertyAndAccessorSameName +{ +} + +class ChildWithPropertySameAsParentMethod extends ObjectWithPropertyAndAllAccessorMethods +{ + private $canFoo; + private $getFoo; + private $hasFoo; + private $isFoo; +} + class ObjectWithPropertyHasserAndIsser { public function __construct( From 321054332da5138867664196e7f6116ac20e0d4f Mon Sep 17 00:00:00 2001 From: matlec Date: Fri, 17 Oct 2025 11:08:59 +0200 Subject: [PATCH 69/91] =?UTF-8?q?[Security]=20Fix=20`HttpUtils::createRequ?= =?UTF-8?q?est()`=20when=20the=20context=E2=80=99s=20base=20URL=20isn?= =?UTF-8?q?=E2=80=99t=20empty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Component/Security/Http/HttpUtils.php | 18 ++++++++++++--- .../Security/Http/Tests/HttpUtilsTest.php | 23 ++++++++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Security/Http/HttpUtils.php b/src/Symfony/Component/Security/Http/HttpUtils.php index af0c732fd03d6..8cdc8806b6828 100644 --- a/src/Symfony/Component/Security/Http/HttpUtils.php +++ b/src/Symfony/Component/Security/Http/HttpUtils.php @@ -73,9 +73,21 @@ public function createRequest(Request $request, string $path): Request if ($trustedProxies = Request::getTrustedProxies()) { Request::setTrustedProxies([], Request::getTrustedHeaderSet()); } - $newRequest = Request::create($this->generateUri($request, $path), 'get', [], $request->cookies->all(), [], $request->server->all()); - if ($trustedProxies) { - Request::setTrustedProxies($trustedProxies, Request::getTrustedHeaderSet()); + + $context = $this->urlGenerator?->getContext(); + if ($baseUrl = $context?->getBaseUrl()) { + $context->setBaseUrl(''); + } + + try { + $newRequest = Request::create($this->generateUri($request, $path), 'get', [], $request->cookies->all(), [], $request->server->all()); + } finally { + if ($trustedProxies) { + Request::setTrustedProxies($trustedProxies, Request::getTrustedHeaderSet()); + } + if ($baseUrl) { + $context->setBaseUrl($baseUrl); + } } static $setSession; diff --git a/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php b/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php index c042b02c9ad5f..17cf30722751a 100644 --- a/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php +++ b/src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php @@ -16,10 +16,13 @@ use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Security\Http\HttpUtils; use Symfony\Component\Security\Http\SecurityRequestAttributes; @@ -233,7 +236,7 @@ public static function provideSecurityRequestAttributes() ]; } - public function testCreateRequestHandlesTrustedHeaders() + public function testCreateRequestFromPathHandlesTrustedHeaders() { Request::setTrustedProxies(['127.0.0.1'], Request::HEADER_X_FORWARDED_PREFIX); @@ -243,6 +246,24 @@ public function testCreateRequestHandlesTrustedHeaders() ); } + public function testCreateRequestFromRouteHandlesTrustedHeaders() + { + Request::setTrustedProxies(['127.0.0.1'], Request::HEADER_X_FORWARDED_PREFIX); + + $request = Request::create('/', server: ['HTTP_X_FORWARDED_PREFIX' => '/foo']); + + $urlGenerator = new UrlGenerator( + $routeCollection = new RouteCollection(), + (new RequestContext())->fromRequest($request), + ); + $routeCollection->add('root', new Route('/')); + + $this->assertSame( + 'http://localhost/foo/', + (new HttpUtils($urlGenerator))->createRequest($request, 'root')->getUri(), + ); + } + public function testCheckRequestPath() { $utils = new HttpUtils($this->getUrlGenerator()); From e195833c45c4e5d066983384c68b2a8213bd98bd Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 6 Oct 2024 21:33:45 +0200 Subject: [PATCH 70/91] ObjectNormalizer: allow null and scalar --- .../Normalizer/AbstractObjectNormalizer.php | 11 ++- .../Tests/Fixtures/ScalarNormalizer.php | 42 ++++++++++++ .../Tests/Fixtures/StdClassNormalizer.php | 34 ++++++++++ .../AbstractObjectNormalizerTest.php | 8 +-- .../Normalizer/GetSetMethodNormalizerTest.php | 68 ++++++++++++++----- .../Tests/Normalizer/ObjectNormalizerTest.php | 41 ++++++++--- 6 files changed, 167 insertions(+), 37 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/ScalarNormalizer.php create mode 100644 src/Symfony/Component/Serializer/Tests/Fixtures/StdClassNormalizer.php diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 7c64fc74c13ac..8a6581641abff 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -210,12 +210,11 @@ public function normalize(mixed $object, ?string $format = null, array $context foreach ($stack as $attribute => $attributeValue) { $attributeContext = $this->getAttributeNormalizationContext($object, $attribute, $context); - if (null === $attributeValue || \is_scalar($attributeValue)) { - $data = $this->updateData($data, $attribute, $attributeValue, $class, $format, $attributeContext, $attributesMetadata, $classMetadata); - continue; - } - if (!$this->serializer instanceof NormalizerInterface) { + if (null === $attributeValue || \is_scalar($attributeValue)) { + $data = $this->updateData($data, $attribute, $attributeValue, $class, $format, $attributeContext, $attributesMetadata, $classMetadata); + continue; + } throw new LogicException(\sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer.', $attribute)); } @@ -465,7 +464,7 @@ private function validateAndDenormalize(array $types, string $currentClass, stri // This try-catch should cover all NotNormalizableValueException (and all return branches after the first // exception) so we could try denormalizing all types of an union type. If the target type is not an union - // type, we will just re-throw the catched exception. + // type, we will just re-throw the caught exception. // In the case of no denormalization succeeds with an union type, it will fall back to the default exception // with the acceptable types list. try { diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/ScalarNormalizer.php b/src/Symfony/Component/Serializer/Tests/Fixtures/ScalarNormalizer.php new file mode 100644 index 0000000000000..c4e114ec66723 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/ScalarNormalizer.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 Symfony\Component\Serializer\Tests\Fixtures; + +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +class ScalarNormalizer implements NormalizerInterface +{ + public function normalize(mixed $object, ?string $format = null, array $context = []): string + { + $data = $object; + + if (!\is_string($data)) { + $data = (string) $object; + } + + return strtoupper($data); + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + return \is_scalar($data); + } + + public function getSupportedTypes(?string $format): array + { + return [ + 'native-boolean' => true, + 'native-integer' => true, + 'native-string' => true, + ]; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Fixtures/StdClassNormalizer.php b/src/Symfony/Component/Serializer/Tests/Fixtures/StdClassNormalizer.php new file mode 100644 index 0000000000000..c231c53c5e0d6 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Fixtures/StdClassNormalizer.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\Component\Serializer\Tests\Fixtures; + +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; + +class StdClassNormalizer implements NormalizerInterface +{ + public function normalize(mixed $object, ?string $format = null, array $context = []): string + { + return 'string_object'; + } + + public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool + { + return $data instanceof \stdClass; + } + + public function getSupportedTypes(?string $format): array + { + return [ + \stdClass::class => true, + ]; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php index d86b65c5a40da..441f15752b492 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/AbstractObjectNormalizerTest.php @@ -960,7 +960,7 @@ protected function createChildContext(array $parentContext, string $attribute, ? $this->assertSame($firstChildContextCacheKey, $secondChildContextCacheKey); } - public function testChildContextKeepsOriginalContextCacheKey() + public function testChildContextChangesContextCacheKey() { $foobar = new Dummy(); $foobar->foo = new EmptyDummy(); @@ -968,7 +968,7 @@ public function testChildContextKeepsOriginalContextCacheKey() $foobar->baz = 'baz'; $normalizer = new class extends AbstractObjectNormalizerDummy { - public $childContextCacheKey; + public array $childContextCacheKeys = []; protected function extractAttributes(object $object, ?string $format = null, array $context = []): array { @@ -983,7 +983,7 @@ protected function getAttributeValue(object $object, string $attribute, ?string protected function createChildContext(array $parentContext, string $attribute, ?string $format): array { $childContext = parent::createChildContext($parentContext, $attribute, $format); - $this->childContextCacheKey = $childContext['cache_key']; + $this->childContextCacheKeys[$attribute] = $childContext['cache_key']; return $childContext; } @@ -992,7 +992,7 @@ protected function createChildContext(array $parentContext, string $attribute, ? $serializer = new Serializer([$normalizer]); $serializer->normalize($foobar, null, ['cache_key' => 'hardcoded', 'iri' => '/dummy/1']); - $this->assertSame('hardcoded-foo', $normalizer->childContextCacheKey); + $this->assertSame(['foo' => 'hardcoded-foo', 'bar' => 'hardcoded-bar', 'baz' => 'hardcoded-baz'], $normalizer->childContextCacheKeys); } public function testChildContextCacheKeyStaysFalseWhenOriginalCacheKeyIsFalse() diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php index 4ba602f107711..a3bdad8f82a17 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Serializer\Tests\Normalizer; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; @@ -34,7 +33,9 @@ use Symfony\Component\Serializer\Tests\Fixtures\Attributes\ClassWithIgnoreAttribute; use Symfony\Component\Serializer\Tests\Fixtures\Attributes\GroupDummy; use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy; +use Symfony\Component\Serializer\Tests\Fixtures\ScalarNormalizer; use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder; +use Symfony\Component\Serializer\Tests\Fixtures\StdClassNormalizer; use Symfony\Component\Serializer\Tests\Normalizer\Features\CacheableObjectAttributesTestTrait; use Symfony\Component\Serializer\Tests\Normalizer\Features\CallbacksTestTrait; use Symfony\Component\Serializer\Tests\Normalizer\Features\CircularReferenceTestTrait; @@ -61,7 +62,7 @@ class GetSetMethodNormalizerTest extends TestCase use TypeEnforcementTestTrait; private GetSetMethodNormalizer $normalizer; - private SerializerInterface&NormalizerInterface&MockObject $serializer; + private SerializerInterface&NormalizerInterface $serializer; protected function setUp(): void { @@ -70,8 +71,8 @@ protected function setUp(): void private function createNormalizer(array $defaultContext = []): void { - $this->serializer = $this->createMock(SerializerNormalizer::class); $this->normalizer = new GetSetMethodNormalizer(null, null, null, null, null, $defaultContext); + $this->serializer = new Serializer([$this->normalizer, new StdClassNormalizer()]); $this->normalizer->setSerializer($this->serializer); } @@ -91,13 +92,6 @@ public function testNormalize() $obj->setCamelCase('camelcase'); $obj->setObject($object); - $this->serializer - ->expects($this->once()) - ->method('normalize') - ->with($object, 'any') - ->willReturn('string_object') - ; - $this->assertEquals( [ 'foo' => 'foo', @@ -111,6 +105,29 @@ public function testNormalize() ); } + public function testNormalizeWithoutSerializer() + { + $obj = new GetSetDummy(); + $obj->setFoo('foo'); + $obj->setBar('bar'); + $obj->setBaz(true); + $obj->setCamelCase('camelcase'); + + $this->normalizer = new GetSetMethodNormalizer(); + + $this->assertEquals( + [ + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => true, + 'fooBar' => 'foobar', + 'camelCase' => 'camelcase', + 'object' => null, + ], + $this->normalizer->normalize($obj, 'any') + ); + } + public function testDenormalize() { $obj = $this->normalizer->denormalize( @@ -377,8 +394,7 @@ public function testUnableToNormalizeObjectAttribute() { $this->expectException(LogicException::class); $this->expectExceptionMessage('Cannot normalize attribute "object" because the injected serializer is not a normalizer'); - $serializer = $this->createMock(SerializerInterface::class); - $this->normalizer->setSerializer($serializer); + $this->normalizer->setSerializer($this->createMock(SerializerInterface::class)); $obj = new GetSetDummy(); $object = new \stdClass(); @@ -523,6 +539,30 @@ public function testNormalizeWithMethodNamesSimilarToAccessors() $this->assertSame(['class' => 'class', 123 => 123], $normalizer->normalize(new GetSetWithAccessorishMethod())); } + public function testNormalizeWithScalarValueNormalizer() + { + $normalizer = new GetSetMethodNormalizer(); + $normalizer->setSerializer(new Serializer([$normalizer, new ScalarNormalizer()])); + + $obj = new GetSetDummy(); + $obj->setFoo('foo'); + $obj->setBar(10); + $obj->setBaz(true); + $obj->setCamelCase('camelcase'); + + $this->assertSame( + [ + 'foo' => 'FOO', + 'bar' => '10', + 'baz' => '1', + 'fooBar' => 'FOO10', + 'camelCase' => 'CAMELCASE', + 'object' => null, + ], + $normalizer->normalize($obj, 'any') + ); + } + public function testDenormalizeWithDiscriminator() { $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); @@ -701,10 +741,6 @@ public function otherMethod() } } -abstract class SerializerNormalizer implements SerializerInterface, NormalizerInterface -{ -} - class GetConstructorOptionalArgsDummy { protected $foo; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 6e5ae015d5510..4b49fe3a64069 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Serializer\Tests\Normalizer; use PHPStan\PhpDocParser\Parser\PhpDocParser; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Component\PropertyAccess\PropertyAccessorBuilder; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; @@ -49,6 +48,7 @@ use Symfony\Component\Serializer\Tests\Fixtures\Php74DummyPrivate; use Symfony\Component\Serializer\Tests\Fixtures\Php80Dummy; use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder; +use Symfony\Component\Serializer\Tests\Fixtures\StdClassNormalizer; use Symfony\Component\Serializer\Tests\Normalizer\Features\AttributesTestTrait; use Symfony\Component\Serializer\Tests\Normalizer\Features\CacheableObjectAttributesTestTrait; use Symfony\Component\Serializer\Tests\Normalizer\Features\CallbacksTestTrait; @@ -86,7 +86,7 @@ class ObjectNormalizerTest extends TestCase use TypeEnforcementTestTrait; private ObjectNormalizer $normalizer; - private SerializerInterface&NormalizerInterface&MockObject $serializer; + private SerializerInterface&NormalizerInterface $serializer; protected function setUp(): void { @@ -95,8 +95,8 @@ protected function setUp(): void private function createNormalizer(array $defaultContext = [], ?ClassMetadataFactoryInterface $classMetadataFactory = null): void { - $this->serializer = $this->createMock(ObjectSerializerNormalizer::class); $this->normalizer = new ObjectNormalizer($classMetadataFactory, null, null, null, null, null, $defaultContext); + $this->serializer = new Serializer([new StdClassNormalizer(), $this->normalizer]); $this->normalizer->setSerializer($this->serializer); } @@ -111,13 +111,6 @@ public function testNormalize() $obj->setObject($object); $obj->setGo(true); - $this->serializer - ->expects($this->once()) - ->method('normalize') - ->with($object, 'any') - ->willReturn('string_object') - ; - $this->assertEquals( [ 'foo' => 'foo', @@ -132,6 +125,32 @@ public function testNormalize() ); } + public function testNormalizeWithoutSerializer() + { + $obj = new ObjectDummy(); + $obj->setFoo('foo'); + $obj->bar = 'bar'; + $obj->setBaz(true); + $obj->setCamelCase('camelcase'); + $obj->setObject(null); + $obj->setGo(true); + + $this->normalizer = new ObjectNormalizer(); + + $this->assertEquals( + [ + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => true, + 'fooBar' => 'foobar', + 'camelCase' => 'camelcase', + 'object' => null, + 'go' => true, + ], + $this->normalizer->normalize($obj, 'any') + ); + } + public function testNormalizeObjectWithUninitializedProperties() { $obj = new Php74Dummy(); @@ -962,7 +981,7 @@ public function testObjectNormalizerWithAttributeLoaderAndObjectHasStaticPropert protected function getNormalizerForAccessors($accessorPrefixes = null): ObjectNormalizer { - $accessorPrefixes = $accessorPrefixes ?? ReflectionExtractor::$defaultAccessorPrefixes; + $accessorPrefixes ??= ReflectionExtractor::$defaultAccessorPrefixes; $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); $propertyAccessorBuilder = (new PropertyAccessorBuilder()) ->setReadInfoExtractor( From 1a877f4e6ab7bb0c692f1eebdd863d4694d05f65 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Fri, 7 Nov 2025 04:58:05 +0100 Subject: [PATCH 71/91] [PropertyInfo] Fix ReflectionExtractor handling of underscore-only property names --- .../Extractor/ReflectionExtractor.php | 4 + .../Extractor/ReflectionExtractorTest.php | 74 ++++++++++++++----- .../Tests/Fixtures/UnderscoreDummy.php | 31 ++++++++ 3 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 src/Symfony/Component/PropertyInfo/Tests/Fixtures/UnderscoreDummy.php diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 6311c55fd33b9..379737de507c9 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -757,6 +757,10 @@ private function isMethodAccessible(\ReflectionClass $class, string $methodName, */ private function camelize(string $string): string { + if ('' === ltrim($string, '_')) { + return $string; + } + return str_replace(' ', '', ucwords(str_replace('_', ' ', $string))); } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index a1acfc5708949..fddb81422a051 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -28,6 +28,7 @@ use Symfony\Component\PropertyInfo\Tests\Fixtures\Php7ParentDummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\Php81Dummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\SnakeCaseDummy; +use Symfony\Component\PropertyInfo\Tests\Fixtures\UnderscoreDummy; use Symfony\Component\PropertyInfo\Tests\Fixtures\VirtualProperties; use Symfony\Component\PropertyInfo\Type; @@ -361,31 +362,31 @@ public static function defaultValueProvider() /** * @dataProvider getReadableProperties */ - public function testIsReadable($property, $expected) + public function testIsReadable(string $class, string $property, bool $expected) { - $this->assertSame( - $expected, - $this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property, []) - ); + $this->assertSame($expected, $this->extractor->isReadable($class, $property, [])); } public static function getReadableProperties() { return [ - ['bar', false], - ['baz', false], - ['parent', true], - ['a', true], - ['b', false], - ['c', true], - ['d', true], - ['e', false], - ['f', false], - ['Id', true], - ['id', true], - ['Guid', true], - ['guid', false], - ['element', false], + [Dummy::class, 'bar', false], + [Dummy::class, 'baz', false], + [Dummy::class, 'parent', true], + [Dummy::class, 'a', true], + [Dummy::class, 'b', false], + [Dummy::class, 'c', true], + [Dummy::class, 'd', true], + [Dummy::class, 'e', false], + [Dummy::class, 'f', false], + [Dummy::class, 'Id', true], + [Dummy::class, 'id', true], + [Dummy::class, 'Guid', true], + [Dummy::class, 'guid', false], + [Dummy::class, 'element', false], + [UnderscoreDummy::class, '_', true], + [UnderscoreDummy::class, '__', true], + [UnderscoreDummy::class, '___', false], ]; } @@ -560,6 +561,10 @@ public static function readAccessorProvider(): array [Dummy::class, 'foo', true, PropertyReadInfo::TYPE_PROPERTY, 'foo', PropertyReadInfo::VISIBILITY_PUBLIC, false], [Php71Dummy::class, 'foo', true, PropertyReadInfo::TYPE_METHOD, 'getFoo', PropertyReadInfo::VISIBILITY_PUBLIC, false], [Php71Dummy::class, 'buz', true, PropertyReadInfo::TYPE_METHOD, 'getBuz', PropertyReadInfo::VISIBILITY_PUBLIC, false], + [UnderscoreDummy::class, '_', true, PropertyReadInfo::TYPE_METHOD, 'get_', PropertyReadInfo::VISIBILITY_PUBLIC, false], + [UnderscoreDummy::class, '__', true, PropertyReadInfo::TYPE_METHOD, 'get__', PropertyReadInfo::VISIBILITY_PUBLIC, false], + [UnderscoreDummy::class, 'foo_bar', false, null, null, null, null], + [UnderscoreDummy::class, '_foo_', false, null, null, null, null], ]; } @@ -792,4 +797,35 @@ public static function provideVirtualPropertiesMutator(): iterable yield ['virtualSetHookOnly', PropertyReadInfo::VISIBILITY_PUBLIC, PropertyWriteInfo::VISIBILITY_PUBLIC]; yield ['virtualHook', PropertyReadInfo::VISIBILITY_PUBLIC, PropertyWriteInfo::VISIBILITY_PUBLIC]; } + + /** + * @dataProvider camelizeProvider + */ + public function testCamelize(string $input, string $expected) + { + $reflection = new \ReflectionClass($this->extractor); + $method = $reflection->getMethod('camelize'); + + if (\PHP_VERSION_ID < 80500) { + $method->setAccessible(true); + } + + $this->assertSame($expected, $method->invoke($this->extractor, $input)); + } + + public static function camelizeProvider(): iterable + { + yield 'single underscore' => ['_', '_']; + yield 'double underscore' => ['__', '__']; + yield 'triple underscore' => ['___', '___']; + + yield 'snake case' => ['foo_bar', 'FooBar']; + yield 'double snake case' => ['foo__bar', 'FooBar']; + yield 'leading underscore' => ['_foo', 'Foo']; + yield 'trailing underscore' => ['foo_', 'Foo']; + yield 'leading and trailing' => ['_foo_bar_', 'FooBar']; + yield 'empty string' => ['', '']; + yield 'no underscore' => ['fooBar', 'FooBar']; + yield 'pascal case' => ['FooBar', 'FooBar']; + } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/UnderscoreDummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/UnderscoreDummy.php new file mode 100644 index 0000000000000..7cb2d2c740a06 --- /dev/null +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/UnderscoreDummy.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyInfo\Tests\Fixtures; + +/** + * Fixture class for testing underscore-only properties. + */ +class UnderscoreDummy +{ + private float $_; + private float $__; + + public function get_(): float + { + return $this->_; + } + + public function get__(): float + { + return $this->__; + } +} From 17eec0c53101985c4790d05bfa7d494c444016ff Mon Sep 17 00:00:00 2001 From: Jeroen <1517978+Jeroeny@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:11:34 +0100 Subject: [PATCH 72/91] Fix Warning: curl_multi_select(): timeout must be positive --- .../Component/HttpClient/Response/TransportResponseTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/Response/TransportResponseTrait.php b/src/Symfony/Component/HttpClient/Response/TransportResponseTrait.php index 4cc110fbcf475..6f8289305274e 100644 --- a/src/Symfony/Component/HttpClient/Response/TransportResponseTrait.php +++ b/src/Symfony/Component/HttpClient/Response/TransportResponseTrait.php @@ -299,7 +299,7 @@ public static function stream(iterable $responses, ?float $timeout = null): \Gen continue; } - if (-1 === self::select($multi, min($timeoutMin, $timeoutMax - $elapsedTimeout))) { + if (-1 === self::select($multi, min($timeoutMin, max(0, $timeoutMax - $elapsedTimeout)))) { usleep((int) min(500, 1E6 * $timeoutMin)); } From bc951612d2d3e32e2f992e76e4f6ea49b12f2e92 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Wed, 12 Nov 2025 16:19:31 +0100 Subject: [PATCH 73/91] [Mime] Remove unused variable in Email::prepareParts --- src/Symfony/Component/Mime/Email.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Mime/Email.php b/src/Symfony/Component/Mime/Email.php index 8183c7899efa6..c5a65641b6702 100644 --- a/src/Symfony/Component/Mime/Email.php +++ b/src/Symfony/Component/Mime/Email.php @@ -507,7 +507,7 @@ private function prepareParts(): ?array } if ($name !== $part->getContentId()) { - $html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html, $count); + $html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html); } $relatedParts[$name] = $part; $part->setName($part->getContentId())->asInline(); From 6128512ec5903dd700a59f6fcae6a102490a4ccd Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 12 Nov 2025 16:55:31 +0100 Subject: [PATCH 74/91] fix merge --- .../Tests/OptionsResolverTest.php | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index ffd4787135020..984a9b54491cd 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -2862,48 +2862,4 @@ public function testRemoveAlsoRemovesDeprecation() restore_error_handler(); } } - - #[IgnoreDeprecations] - #[Group('legacy')] - public function testNestedPrototypeErrorPathHasFullContext() - { - $resolver = new OptionsResolver(); - - $resolver->setDefault('connections', static function (OptionsResolver $connResolver) { - $connResolver->setPrototype(true); - $connResolver->setRequired(['host', 'database']); - $connResolver->setDefault('user', 'root'); - - $connResolver->setDefault('replicas', static function (OptionsResolver $replicaResolver) { - $replicaResolver->setPrototype(true); - $replicaResolver->setRequired(['host']); - $replicaResolver->setDefault('user', 'read_only'); - }); - }); - - $this->expectException(MissingOptionsException::class); - $this->expectExceptionMessage('The required option "connections[main_db][replicas][1][host]" is missing.'); - - $options = [ - 'connections' => [ - 'main_db' => [ - 'host' => 'localhost', - 'database' => 'app_db', - 'replicas' => [ - ['host' => 'replica-01.local', 'user' => 'read_only'], - ['user' => 'other_user'], // Index 1 -> "host" is missing here - ], - ], - 'audit_db' => [ - 'host' => 'audit.local', - 'database' => 'audit_db', - 'replicas' => [ - ['host' => 'audit-replica.local'], - ], - ], - ], - ]; - - $resolver->resolve($options); - } } From f7c898fe0c41757ecd00b912e181e8e28c9bccfe Mon Sep 17 00:00:00 2001 From: eltharin Date: Thu, 6 Nov 2025 11:44:46 +0100 Subject: [PATCH 75/91] [Routing] Fix default value not taken if usigng name:entity.attribute --- .../Component/Routing/Loader/AttributeClassLoader.php | 6 +++--- .../AttributeFixtures/DefaultValueController.php | 11 +++++++++++ .../Routing/Tests/Loader/AttributeClassLoaderTest.php | 6 +++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php b/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php index b73e94ea09819..f2bc668d09cb1 100644 --- a/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php +++ b/src/Symfony/Component/Routing/Loader/AttributeClassLoader.php @@ -219,11 +219,11 @@ protected function addRoute(RouteCollection $collection, object $attr, array $gl continue; } foreach ($paths as $locale => $path) { - if (preg_match(\sprintf('/\{%s(?:<.*?>)?\}/', preg_quote($param->name)), $path)) { + if (preg_match(\sprintf('/\{(?|([^\}:<]++):%s(?:\.[^\}<]++)?|(%1$s))(?:<.*?>)?\}/', preg_quote($param->name)), $path, $matches)) { if (\is_scalar($defaultValue = $param->getDefaultValue()) || null === $defaultValue) { - $defaults[$param->name] = $defaultValue; + $defaults[$matches[1]] = $defaultValue; } elseif ($defaultValue instanceof \BackedEnum) { - $defaults[$param->name] = $defaultValue->value; + $defaults[$matches[1]] = $defaultValue->value; } break; } diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DefaultValueController.php b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DefaultValueController.php index dc5d0c4e52ee3..5dcf5181c0e01 100644 --- a/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DefaultValueController.php +++ b/src/Symfony/Component/Routing/Tests/Fixtures/AttributeFixtures/DefaultValueController.php @@ -3,6 +3,7 @@ namespace Symfony\Component\Routing\Tests\Fixtures\AttributeFixtures; use Symfony\Component\Routing\Attribute\Route; +use Symfony\Component\Routing\Tests\Fixtures\AttributedClasses\BarClass; use Symfony\Component\Routing\Tests\Fixtures\Enum\TestIntBackedEnum; use Symfony\Component\Routing\Tests\Fixtures\Enum\TestStringBackedEnum; @@ -30,4 +31,14 @@ public function stringEnumAction(TestStringBackedEnum $default = TestStringBacke public function intEnumAction(TestIntBackedEnum $default = TestIntBackedEnum::Diamonds) { } + + #[Route(path: '/defaultMappedParam/{libelle:bar}', name: 'defaultMappedParam_default')] + public function defaultMappedParam(?BarClass $bar = null) + { + } + + #[Route(path: '/defaultAdvancedMappedParam/{barLibelle:bar.libelle}', name: 'defaultAdvancedMappedParam_default')] + public function defaultAdvancedMappedParam(?BarClass $bar = null) + { + } } diff --git a/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php b/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php index 50a10a16cac2f..022e0c9f83ea5 100644 --- a/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php +++ b/src/Symfony/Component/Routing/Tests/Loader/AttributeClassLoaderTest.php @@ -171,12 +171,16 @@ public function testLocalizedPathRoutesWithExplicitPathPropety() public function testDefaultValuesForMethods() { $routes = $this->loader->load(DefaultValueController::class); - $this->assertCount(5, $routes); + $this->assertCount(7, $routes); $this->assertEquals('/{default}/path', $routes->get('action')->getPath()); $this->assertEquals('value', $routes->get('action')->getDefault('default')); $this->assertEquals('Symfony', $routes->get('hello_with_default')->getDefault('name')); $this->assertEquals('World', $routes->get('hello_without_default')->getDefault('name')); $this->assertEquals('diamonds', $routes->get('string_enum_action')->getDefault('default')); + $this->assertArrayHasKey('libelle', $routes->get('defaultMappedParam_default')->getDefaults()); + $this->assertNull($routes->get('defaultMappedParam_default')->getDefault('libelle')); + $this->assertArrayHasKey('barLibelle', $routes->get('defaultAdvancedMappedParam_default')->getDefaults()); + $this->assertNull($routes->get('defaultAdvancedMappedParam_default')->getDefault('barLibelle')); $this->assertEquals(20, $routes->get('int_enum_action')->getDefault('default')); } From cd32dac14745fb4b67e7afb0c6db235e8af5a48e Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Mon, 20 Oct 2025 16:37:41 +0200 Subject: [PATCH 76/91] [JsonStreamer] Rebuild cache on class update --- .../Resources/config/json_streamer.php | 3 + .../Tests/Functional/JsonStreamerTest.php | 10 +- .../CacheWarmer/StreamerCacheWarmer.php | 6 +- .../JsonStreamer/JsonStreamReader.php | 4 +- .../JsonStreamer/JsonStreamWriter.php | 4 +- .../Read/StreamReaderGenerator.php | 47 +++---- .../Component/JsonStreamer/StreamerDumper.php | 106 ++++++++++++++++ .../JsonStreamer/Tests/StreamerDumperTest.php | 117 ++++++++++++++++++ .../Write/StreamWriterGenerator.php | 52 +++----- .../Component/JsonStreamer/composer.json | 1 + 10 files changed, 278 insertions(+), 72 deletions(-) create mode 100644 src/Symfony/Component/JsonStreamer/StreamerDumper.php create mode 100644 src/Symfony/Component/JsonStreamer/Tests/StreamerDumperTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_streamer.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_streamer.php index 79fb25833e066..4535c259d25e1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_streamer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_streamer.php @@ -32,6 +32,7 @@ tagged_locator('json_streamer.value_transformer'), service('json_streamer.write.property_metadata_loader'), param('.json_streamer.stream_writers_dir'), + service('config_cache_factory')->ignoreOnInvalid(), ]) ->set('json_streamer.stream_reader', JsonStreamReader::class) ->args([ @@ -39,6 +40,7 @@ service('json_streamer.read.property_metadata_loader'), param('.json_streamer.stream_readers_dir'), param('.json_streamer.lazy_ghosts_dir'), + service('config_cache_factory')->ignoreOnInvalid(), ]) ->alias(JsonStreamWriter::class, 'json_streamer.stream_writer') ->alias(JsonStreamReader::class, 'json_streamer.stream_reader') @@ -106,6 +108,7 @@ param('.json_streamer.stream_writers_dir'), param('.json_streamer.stream_readers_dir'), service('logger')->ignoreOnInvalid(), + service('config_cache_factory')->ignoreOnInvalid(), ]) ->tag('kernel.cache_warmer') diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonStreamerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonStreamerTest.php index 9816015b4484e..510c4913e9090 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonStreamerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/JsonStreamerTest.php @@ -13,6 +13,7 @@ use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonStreamer\Dto\Dummy; use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\JsonStreamer\StreamerDumper; use Symfony\Component\JsonStreamer\StreamReaderInterface; use Symfony\Component\JsonStreamer\StreamWriterInterface; use Symfony\Component\TypeInfo\Type; @@ -62,6 +63,13 @@ public function testWarmupStreamableClasses() static::getContainer()->get('json_streamer.cache_warmer.streamer.alias')->warmUp(static::getContainer()->getParameter('kernel.cache_dir')); $this->assertFileExists($streamWritersDir); - $this->assertCount(2, glob($streamWritersDir.'/*')); + + if (!class_exists(StreamerDumper::class)) { + $this->assertCount(2, glob($streamWritersDir.'/*')); + } else { + $this->assertCount(2, glob($streamWritersDir.'/*.php')); + $this->assertCount(2, glob($streamWritersDir.'/*.php.meta')); + $this->assertCount(2, glob($streamWritersDir.'/*.php.meta.json')); + } } } diff --git a/src/Symfony/Component/JsonStreamer/CacheWarmer/StreamerCacheWarmer.php b/src/Symfony/Component/JsonStreamer/CacheWarmer/StreamerCacheWarmer.php index 232b592fbbd3f..b359a22ec38cd 100644 --- a/src/Symfony/Component/JsonStreamer/CacheWarmer/StreamerCacheWarmer.php +++ b/src/Symfony/Component/JsonStreamer/CacheWarmer/StreamerCacheWarmer.php @@ -13,6 +13,7 @@ use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; +use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; use Symfony\Component\JsonStreamer\Exception\ExceptionInterface; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; @@ -42,9 +43,10 @@ public function __construct( string $streamWritersDir, string $streamReadersDir, private LoggerInterface $logger = new NullLogger(), + ?ConfigCacheFactoryInterface $configCacheFactory = null, ) { - $this->streamWriterGenerator = new StreamWriterGenerator($streamWriterPropertyMetadataLoader, $streamWritersDir); - $this->streamReaderGenerator = new StreamReaderGenerator($streamReaderPropertyMetadataLoader, $streamReadersDir); + $this->streamWriterGenerator = new StreamWriterGenerator($streamWriterPropertyMetadataLoader, $streamWritersDir, $configCacheFactory); + $this->streamReaderGenerator = new StreamReaderGenerator($streamReaderPropertyMetadataLoader, $streamReadersDir, $configCacheFactory); } public function warmUp(string $cacheDir, ?string $buildDir = null): array diff --git a/src/Symfony/Component/JsonStreamer/JsonStreamReader.php b/src/Symfony/Component/JsonStreamer/JsonStreamReader.php index e813f4a8a5408..28e9b74c01066 100644 --- a/src/Symfony/Component/JsonStreamer/JsonStreamReader.php +++ b/src/Symfony/Component/JsonStreamer/JsonStreamReader.php @@ -13,6 +13,7 @@ use PHPStan\PhpDocParser\Parser\PhpDocParser; use Psr\Container\ContainerInterface; +use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\JsonStreamer\Mapping\GenericTypePropertyMetadataLoader; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoader; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; @@ -46,8 +47,9 @@ public function __construct( PropertyMetadataLoaderInterface $propertyMetadataLoader, string $streamReadersDir, ?string $lazyGhostsDir = null, + ?ConfigCacheFactoryInterface $configCacheFactory = null, ) { - $this->streamReaderGenerator = new StreamReaderGenerator($propertyMetadataLoader, $streamReadersDir); + $this->streamReaderGenerator = new StreamReaderGenerator($propertyMetadataLoader, $streamReadersDir, $configCacheFactory); $this->instantiator = new Instantiator(); $this->lazyInstantiator = new LazyInstantiator($lazyGhostsDir); } diff --git a/src/Symfony/Component/JsonStreamer/JsonStreamWriter.php b/src/Symfony/Component/JsonStreamer/JsonStreamWriter.php index bbe31af9de57a..ea5529642bfb4 100644 --- a/src/Symfony/Component/JsonStreamer/JsonStreamWriter.php +++ b/src/Symfony/Component/JsonStreamer/JsonStreamWriter.php @@ -13,6 +13,7 @@ use PHPStan\PhpDocParser\Parser\PhpDocParser; use Psr\Container\ContainerInterface; +use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\JsonStreamer\Mapping\GenericTypePropertyMetadataLoader; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoader; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; @@ -41,8 +42,9 @@ public function __construct( private ContainerInterface $valueTransformers, PropertyMetadataLoaderInterface $propertyMetadataLoader, string $streamWritersDir, + ?ConfigCacheFactoryInterface $configCacheFactory = null, ) { - $this->streamWriterGenerator = new StreamWriterGenerator($propertyMetadataLoader, $streamWritersDir); + $this->streamWriterGenerator = new StreamWriterGenerator($propertyMetadataLoader, $streamWritersDir, $configCacheFactory); } public function write(mixed $data, Type $type, array $options = []): \Traversable&\Stringable diff --git a/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php b/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php index 18720297b16c6..87149ac8be4fc 100644 --- a/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php +++ b/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php @@ -14,8 +14,7 @@ use PhpParser\PhpVersion; use PhpParser\PrettyPrinter; use PhpParser\PrettyPrinter\Standard; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; use Symfony\Component\JsonStreamer\DataModel\FunctionDataAccessor; use Symfony\Component\JsonStreamer\DataModel\Read\BackedEnumNode; @@ -29,6 +28,7 @@ use Symfony\Component\JsonStreamer\Exception\RuntimeException; use Symfony\Component\JsonStreamer\Exception\UnsupportedException; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonStreamer\StreamerDumper; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\BackedEnumType; use Symfony\Component\TypeInfo\Type\BuiltinType; @@ -47,14 +47,16 @@ */ final class StreamReaderGenerator { + private StreamerDumper $dumper; private ?PhpAstBuilder $phpAstBuilder = null; private ?PrettyPrinter $phpPrinter = null; - private ?Filesystem $fs = null; public function __construct( private PropertyMetadataLoaderInterface $propertyMetadataLoader, private string $streamReadersDir, + ?ConfigCacheFactoryInterface $cacheFactory = null, ) { + $this->dumper = new StreamerDumper($propertyMetadataLoader, $streamReadersDir, $cacheFactory); } /** @@ -64,46 +66,27 @@ public function __construct( */ public function generate(Type $type, bool $decodeFromStream, array $options = []): string { - $path = $this->getPath($type, $decodeFromStream); - if (is_file($path)) { - return $path; - } - - $this->phpAstBuilder ??= new PhpAstBuilder(); - $this->phpPrinter ??= new Standard(['phpVersion' => PhpVersion::fromComponents(8, 2)]); - $this->fs ??= new Filesystem(); + $path = \sprintf('%s%s%s.json%s.php', $this->streamReadersDir, \DIRECTORY_SEPARATOR, hash('xxh128', (string) $type), $decodeFromStream ? '.stream' : ''); + $generateContent = function () use ($type, $decodeFromStream, $options): string { + $this->phpAstBuilder ??= new PhpAstBuilder(); + $this->phpPrinter ??= new Standard(['phpVersion' => PhpVersion::fromComponents(8, 2)]); - $dataModel = $this->createDataModel($type, $options); - $nodes = $this->phpAstBuilder->build($dataModel, $decodeFromStream, $options); - $content = $this->phpPrinter->prettyPrintFile($nodes)."\n"; - - if (!$this->fs->exists($this->streamReadersDir)) { - $this->fs->mkdir($this->streamReadersDir); - } + $dataModel = $this->createDataModel($type, $options); + $nodes = $this->phpAstBuilder->build($dataModel, $decodeFromStream, $options); - $tmpFile = $this->fs->tempnam(\dirname($path), basename($path)); + return $this->phpPrinter->prettyPrintFile($nodes)."\n"; + }; - try { - $this->fs->dumpFile($tmpFile, $content); - $this->fs->rename($tmpFile, $path); - $this->fs->chmod($path, 0666 & ~umask()); - } catch (IOException $e) { - throw new RuntimeException(\sprintf('Failed to write "%s" stream reader file.', $path), previous: $e); - } + $this->dumper->dump($type, $path, $generateContent); return $path; } - private function getPath(Type $type, bool $decodeFromStream): string - { - return \sprintf('%s%s%s.json%s.php', $this->streamReadersDir, \DIRECTORY_SEPARATOR, hash('xxh128', (string) $type), $decodeFromStream ? '.stream' : ''); - } - /** * @param array $options * @param array $context */ - public function createDataModel(Type $type, array $options = [], array $context = []): DataModelNodeInterface + private function createDataModel(Type $type, array $options = [], array $context = []): DataModelNodeInterface { $context['original_type'] ??= $type; diff --git a/src/Symfony/Component/JsonStreamer/StreamerDumper.php b/src/Symfony/Component/JsonStreamer/StreamerDumper.php new file mode 100644 index 0000000000000..317739ff7b9c3 --- /dev/null +++ b/src/Symfony/Component/JsonStreamer/StreamerDumper.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonStreamer; + +use Symfony\Component\Config\ConfigCacheFactoryInterface; +use Symfony\Component\Config\ConfigCacheInterface; +use Symfony\Component\Config\Resource\ReflectionClassResource; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\Type\GenericType; +use Symfony\Component\TypeInfo\Type\ObjectType; + +/** + * @author Mathias Arlaud + * + * @internal + */ +final class StreamerDumper +{ + private ?Filesystem $fs = null; + + public function __construct( + private PropertyMetadataLoaderInterface $propertyMetadataLoader, + private string $cacheDir, + private ?ConfigCacheFactoryInterface $cacheFactory = null, + ) { + } + + /** + * Dumps the generated content to the given path, optionally using config cache. + * + * @param callable(): string $generateContent + */ + public function dump(Type $type, string $path, callable $generateContent): void + { + if ($this->cacheFactory) { + $this->cacheFactory->cache( + $path, + function (ConfigCacheInterface $cache) use ($generateContent, $type) { + $resourceClasses = $this->getResourceClassNames($type); + $cache->write( + $generateContent(), + array_map(fn (string $c) => new ReflectionClassResource(new \ReflectionClass($c)), $resourceClasses), + ); + }, + ); + + return; + } + + $this->fs ??= new Filesystem(); + + if (!$this->fs->exists($this->cacheDir)) { + $this->fs->mkdir($this->cacheDir); + } + + if (!$this->fs->exists($path)) { + $this->fs->dumpFile($path, $generateContent()); + } + } + + /** + * Retrieves resources class names required for caching based on the provided type. + * + * @param list $classNames + * @param array $context + * + * @return list + */ + private function getResourceClassNames(Type $type, array $classNames = [], array $context = []): array + { + $context['original_type'] ??= $type; + + foreach ($type->traverse() as $t) { + if ($t instanceof ObjectType) { + if (\in_array($t->getClassName(), $classNames, true)) { + return $classNames; + } + + $classNames[] = $t->getClassName(); + + foreach ($this->propertyMetadataLoader->load($t->getClassName(), [], $context) as $property) { + $classNames = [...$classNames, ...$this->getResourceClassNames($property->getType(), $classNames)]; + } + } + + if ($t instanceof GenericType) { + foreach ($t->getVariableTypes() as $variableType) { + $classNames = [...$classNames, ...$this->getResourceClassNames($variableType, $classNames)]; + } + } + } + + return array_values(array_unique($classNames)); + } +} diff --git a/src/Symfony/Component/JsonStreamer/Tests/StreamerDumperTest.php b/src/Symfony/Component/JsonStreamer/Tests/StreamerDumperTest.php new file mode 100644 index 0000000000000..ea4f86a64179c --- /dev/null +++ b/src/Symfony/Component/JsonStreamer/Tests/StreamerDumperTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonStreamer\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\ConfigCacheFactory; +use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoader; +use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonStreamer\StreamerDumper; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Enum\DummyBackedEnum; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\ClassicDummy; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithArray; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithNameAttributes; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithOtherDummies; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\SelfReferencingDummy; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; + +class StreamerDumperTest extends TestCase +{ + private string $cacheDir; + + protected function setUp(): void + { + parent::setUp(); + + $this->cacheDir = \sprintf('%s/symfony_json_streamer_test/any', sys_get_temp_dir()); + + if (is_dir($this->cacheDir)) { + array_map('unlink', glob($this->cacheDir.'/*')); + rmdir($this->cacheDir); + } + } + + public function testDumpWithConfigCache() + { + $path = $this->cacheDir.'/streamer.php'; + + $dumper = new StreamerDumper($this->createMock(PropertyMetadataLoaderInterface::class), $this->cacheDir, new ConfigCacheFactory(true)); + $dumper->dump(Type::int(), $path, fn () => 'CONTENT'); + + $this->assertFileExists($path); + $this->assertFileExists($path.'.meta'); + $this->assertFileExists($path.'.meta.json'); + + $this->assertStringEqualsFile($path, 'CONTENT'); + } + + public function testDumpWithoutConfigCache() + { + $path = $this->cacheDir.'/streamer.php'; + + $dumper = new StreamerDumper($this->createMock(PropertyMetadataLoaderInterface::class), $this->cacheDir); + $dumper->dump(Type::int(), $path, fn () => 'CONTENT'); + + $this->assertFileExists($path); + $this->assertStringEqualsFile($path, 'CONTENT'); + } + + /** + * @dataProvider getCacheResourcesDataProvider + * + * @param list $expectedClassNames + */ + public function testGetCacheResources(Type $type, array $expectedClassNames) + { + $path = $this->cacheDir.'/streamer.php'; + + $dumper = new StreamerDumper(new PropertyMetadataLoader(TypeResolver::create()), $this->cacheDir, new ConfigCacheFactory(true)); + $dumper->dump($type, $path, fn () => 'CONTENT'); + + $resources = json_decode(file_get_contents($path.'.meta.json'), true)['resources']; + $classNames = array_column($resources, 'className'); + + $this->assertSame($expectedClassNames, $classNames); + } + + /** + * @return iterable}> + */ + public static function getCacheResourcesDataProvider(): iterable + { + yield 'scalar' => [Type::int(), []]; + yield 'enum' => [Type::enum(DummyBackedEnum::class), [DummyBackedEnum::class]]; + yield 'object' => [Type::object(ClassicDummy::class), [ClassicDummy::class]]; + yield 'collection of objects' => [ + Type::list(Type::object(ClassicDummy::class)), + [ClassicDummy::class], + ]; + yield 'generic with objects' => [ + Type::generic(Type::object(ClassicDummy::class), Type::object(DummyWithArray::class)), + [DummyWithArray::class, ClassicDummy::class], + ]; + yield 'union with objects' => [ + Type::union(Type::int(), Type::object(ClassicDummy::class), Type::object(DummyWithArray::class)), + [ClassicDummy::class, DummyWithArray::class], + ]; + yield 'intersection with objects' => [ + Type::intersection(Type::object(ClassicDummy::class), Type::object(DummyWithArray::class)), + [ClassicDummy::class, DummyWithArray::class], + ]; + yield 'object with object properties' => [ + Type::object(DummyWithOtherDummies::class), + [DummyWithOtherDummies::class, DummyWithNameAttributes::class, ClassicDummy::class], + ]; + yield 'object with self reference' => [Type::object(SelfReferencingDummy::class), [SelfReferencingDummy::class]]; + } +} diff --git a/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php b/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php index f7ac60f21deee..2a04a95b72495 100644 --- a/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php +++ b/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php @@ -14,8 +14,7 @@ use PhpParser\PhpVersion; use PhpParser\PrettyPrinter; use PhpParser\PrettyPrinter\Standard; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\JsonStreamer\DataModel\DataAccessorInterface; use Symfony\Component\JsonStreamer\DataModel\FunctionDataAccessor; use Symfony\Component\JsonStreamer\DataModel\PropertyDataAccessor; @@ -30,6 +29,7 @@ use Symfony\Component\JsonStreamer\Exception\RuntimeException; use Symfony\Component\JsonStreamer\Exception\UnsupportedException; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonStreamer\StreamerDumper; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\BackedEnumType; use Symfony\Component\TypeInfo\Type\BuiltinType; @@ -48,15 +48,17 @@ */ final class StreamWriterGenerator { + private StreamerDumper $dumper; private ?PhpAstBuilder $phpAstBuilder = null; private ?PhpOptimizer $phpOptimizer = null; private ?PrettyPrinter $phpPrinter = null; - private ?Filesystem $fs = null; public function __construct( private PropertyMetadataLoaderInterface $propertyMetadataLoader, private string $streamWritersDir, + ?ConfigCacheFactoryInterface $cacheFactory = null, ) { + $this->dumper = new StreamerDumper($propertyMetadataLoader, $streamWritersDir, $cacheFactory); } /** @@ -66,45 +68,24 @@ public function __construct( */ public function generate(Type $type, array $options = []): string { - $path = $this->getPath($type); - if (is_file($path)) { - return $path; - } - - $this->phpAstBuilder ??= new PhpAstBuilder(); - $this->phpOptimizer ??= new PhpOptimizer(); - $this->phpPrinter ??= new Standard(['phpVersion' => PhpVersion::fromComponents(8, 2)]); - $this->fs ??= new Filesystem(); - - $dataModel = $this->createDataModel($type, new VariableDataAccessor('data'), $options, ['depth' => 0]); - - $nodes = $this->phpAstBuilder->build($dataModel, $options); - $nodes = $this->phpOptimizer->optimize($nodes); - - $content = $this->phpPrinter->prettyPrintFile($nodes)."\n"; + $path = \sprintf('%s%s%s.json.php', $this->streamWritersDir, \DIRECTORY_SEPARATOR, hash('xxh128', (string) $type)); + $generateContent = function () use ($type, $options): string { + $this->phpAstBuilder ??= new PhpAstBuilder(); + $this->phpOptimizer ??= new PhpOptimizer(); + $this->phpPrinter ??= new Standard(['phpVersion' => PhpVersion::fromComponents(8, 2)]); - if (!$this->fs->exists($this->streamWritersDir)) { - $this->fs->mkdir($this->streamWritersDir); - } + $dataModel = $this->createDataModel($type, new VariableDataAccessor('data'), $options); + $nodes = $this->phpAstBuilder->build($dataModel, $options); + $nodes = $this->phpOptimizer->optimize($nodes); - $tmpFile = $this->fs->tempnam(\dirname($path), basename($path)); + return $this->phpPrinter->prettyPrintFile($nodes)."\n"; + }; - try { - $this->fs->dumpFile($tmpFile, $content); - $this->fs->rename($tmpFile, $path); - $this->fs->chmod($path, 0666 & ~umask()); - } catch (IOException $e) { - throw new RuntimeException(\sprintf('Failed to write "%s" stream writer file.', $path), previous: $e); - } + $this->dumper->dump($type, $path, $generateContent); return $path; } - private function getPath(Type $type): string - { - return \sprintf('%s%s%s.json.php', $this->streamWritersDir, \DIRECTORY_SEPARATOR, hash('xxh128', (string) $type)); - } - /** * @param array $options * @param array $context @@ -112,6 +93,7 @@ private function getPath(Type $type): string private function createDataModel(Type $type, DataAccessorInterface $accessor, array $options = [], array $context = []): DataModelNodeInterface { $context['original_type'] ??= $type; + $context['depth'] ??= 0; if ($type instanceof UnionType) { return new CompositeNode($accessor, array_map(fn (Type $t): DataModelNodeInterface => $this->createDataModel($t, $accessor, $options, $context), $type->getTypes())); diff --git a/src/Symfony/Component/JsonStreamer/composer.json b/src/Symfony/Component/JsonStreamer/composer.json index 2682f67a883fa..2f48bc0ea2754 100644 --- a/src/Symfony/Component/JsonStreamer/composer.json +++ b/src/Symfony/Component/JsonStreamer/composer.json @@ -26,6 +26,7 @@ }, "require-dev": { "phpstan/phpdoc-parser": "^1.0", + "symfony/config": "^7.2", "symfony/dependency-injection": "^7.2", "symfony/http-kernel": "^7.2" }, From caeb1a00d0168132c4d577e657032b180bafb284 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Mon, 3 Nov 2025 05:50:50 +0100 Subject: [PATCH 77/91] [HttpFoundation] Fix AcceptHeader overwrites items with different parameters --- .../Component/HttpFoundation/AcceptHeader.php | 184 +++++++++++- .../HttpFoundation/Tests/AcceptHeaderTest.php | 49 +++- .../HttpFoundation/Tests/RequestTest.php | 261 ++++++++++++++++++ 3 files changed, 479 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/AcceptHeader.php b/src/Symfony/Component/HttpFoundation/AcceptHeader.php index 853c000e00f12..58a0b43dd5b9c 100644 --- a/src/Symfony/Component/HttpFoundation/AcceptHeader.php +++ b/src/Symfony/Component/HttpFoundation/AcceptHeader.php @@ -25,7 +25,7 @@ class_exists(AcceptHeaderItem::class); class AcceptHeader { /** - * @var AcceptHeaderItem[] + * @var array */ private array $items = []; @@ -73,7 +73,9 @@ public function __toString(): string */ public function has(string $value): bool { - return isset($this->items[$value]); + $canonicalKey = $this->getCanonicalKey(AcceptHeaderItem::fromString($value)); + + return isset($this->items[$canonicalKey]); } /** @@ -81,7 +83,25 @@ public function has(string $value): bool */ public function get(string $value): ?AcceptHeaderItem { - return $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? null; + $queryItem = AcceptHeaderItem::fromString($value.';q=1'); + $canonicalKey = $this->getCanonicalKey($queryItem); + + if (isset($this->items[$canonicalKey])) { + return $this->items[$canonicalKey]; + } + + // Collect and filter matching candidates + if (!$candidates = array_filter($this->items, fn (AcceptHeaderItem $item) => $this->matches($item, $queryItem))) { + return null; + } + + usort($candidates, fn ($a, $b) => + $this->getSpecificity($b, $queryItem) <=> $this->getSpecificity($a, $queryItem) // Descending specificity + ?: $b->getQuality() <=> $a->getQuality() // Descending quality + ?: $a->getIndex() <=> $b->getIndex() // Ascending index (stability) + ); + + return reset($candidates); } /** @@ -91,7 +111,7 @@ public function get(string $value): ?AcceptHeaderItem */ public function add(AcceptHeaderItem $item): static { - $this->items[$item->getValue()] = $item; + $this->items[$this->getCanonicalKey($item)] = $item; $this->sorted = false; return $this; @@ -114,7 +134,7 @@ public function all(): array */ public function filter(string $pattern): self { - return new self(array_filter($this->items, fn (AcceptHeaderItem $item) => preg_match($pattern, $item->getValue()))); + return new self(array_filter($this->items, static fn ($item) => preg_match($pattern, $item->getValue()))); } /** @@ -133,18 +153,154 @@ public function first(): ?AcceptHeaderItem private function sort(): void { if (!$this->sorted) { - uasort($this->items, function (AcceptHeaderItem $a, AcceptHeaderItem $b) { - $qA = $a->getQuality(); - $qB = $b->getQuality(); + uasort($this->items, static fn ($a, $b) => $b->getQuality() <=> $a->getQuality() ?: $a->getIndex() <=> $b->getIndex()); + + $this->sorted = true; + } + } - if ($qA === $qB) { - return $a->getIndex() > $b->getIndex() ? 1 : -1; - } + /** + * Generates the canonical key for storing/retrieving an item. + */ + private function getCanonicalKey(AcceptHeaderItem $item): string + { + $parts = []; - return $qA > $qB ? -1 : 1; - }); + // Normalize and sort attributes for consistent key generation + $attributes = $this->getMediaParams($item); + ksort($attributes); - $this->sorted = true; + foreach ($attributes as $name => $value) { + if (null === $value) { + $parts[] = $name; // Flag parameter (e.g., "flowed") + continue; + } + + // Quote values containing spaces, commas, semicolons, or equals per RFC 9110 + // This handles cases like 'format="value with space"' or similar. + $quotedValue = \is_string($value) && preg_match('/[\s;,=]/', $value) ? '"'.addcslashes($value, '"\\').'"' : $value; + + $parts[] = $name.'='.$quotedValue; } + + return $item->getValue().($parts ? ';'.implode(';', $parts) : ''); + } + + /** + * Checks if a given header item (range) matches a queried item (value). + * + * @param AcceptHeaderItem $rangeItem The item from the Accept header (e.g., text/*;format=flowed) + * @param AcceptHeaderItem $queryItem The item being queried (e.g., text/plain;format=flowed;charset=utf-8) + */ + private function matches(AcceptHeaderItem $rangeItem, AcceptHeaderItem $queryItem): bool + { + $rangeValue = strtolower($rangeItem->getValue()); + $queryValue = strtolower($queryItem->getValue()); + + // Handle universal wildcard ranges + if ('*' === $rangeValue || '*/*' === $rangeValue) { + return $this->rangeParametersMatch($rangeItem, $queryItem); + } + + // Queries for '*' only match wildcard ranges (handled above) + if ('*' === $queryValue) { + return false; + } + + // Ensure media vs. non-media consistency + $isQueryMedia = str_contains($queryValue, '/'); + $isRangeMedia = str_contains($rangeValue, '/'); + + if ($isQueryMedia !== $isRangeMedia) { + return false; + } + + // Non-media: exact match only (wildcards handled above) + if (!$isQueryMedia) { + return $rangeValue === $queryValue && $this->rangeParametersMatch($rangeItem, $queryItem); + } + + // Media type: type/subtype with wildcards + [$queryType, $querySubtype] = explode('/', $queryValue, 2); + [$rangeType, $rangeSubtype] = explode('/', $rangeValue, 2) + [1 => '*']; + + if ('*' !== $rangeType && $rangeType !== $queryType) { + return false; + } + + if ('*' !== $rangeSubtype && $rangeSubtype !== $querySubtype) { + return false; + } + + // Parameters must match + return $this->rangeParametersMatch($rangeItem, $queryItem); + } + + /** + * Checks if the parameters of a range item are satisfied by the query item. + * + * Parameters are case-insensitive; range params must be a subset of query params. + */ + private function rangeParametersMatch(AcceptHeaderItem $rangeItem, AcceptHeaderItem $queryItem): bool + { + $queryAttributes = $this->getMediaParams($queryItem); + $rangeAttributes = $this->getMediaParams($rangeItem); + + foreach ($rangeAttributes as $name => $rangeValue) { + if (!\array_key_exists($name, $queryAttributes)) { + return false; // Missing required param + } + + $queryValue = $queryAttributes[$name]; + + if (null === $rangeValue) { + return null === $queryValue; // Both flags or neither + } + + if (null === $queryValue || strtolower($queryValue) !== strtolower($rangeValue)) { + return false; + } + } + + return true; + } + + /** + * Calculates a specificity score for sorting: media precision + param count. + */ + private function getSpecificity(AcceptHeaderItem $item, AcceptHeaderItem $queryItem): int + { + $rangeValue = strtolower($item->getValue()); + $queryValue = strtolower($queryItem->getValue()); + + $paramCount = \count($this->getMediaParams($item)); + + $isQueryMedia = str_contains($queryValue, '/'); + $isRangeMedia = str_contains($rangeValue, '/'); + + if (!$isQueryMedia && !$isRangeMedia) { + return ('*' !== $rangeValue ? 2000 : 1000) + $paramCount; + } + + [$rangeType, $rangeSubtype] = explode('/', $rangeValue, 2) + [1 => '*']; + + $specificity = match (true) { + '*' !== $rangeSubtype => 3000, // Exact subtype (text/plain) + '*' !== $rangeType => 2000, // Type wildcard (text/*) + default => 1000, // Full wildcard (*/* or *) + }; + + return $specificity + $paramCount; + } + + /** + * Returns normalized attributes: keys lowercased, excluding 'q'. + */ + private function getMediaParams(AcceptHeaderItem $item): array + { + $attributes = array_change_key_case($item->getAttributes(), \CASE_LOWER); + unset($attributes['q']); + + return $attributes; } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php b/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php index e972d714e068a..d64b9c29c73cc 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/AcceptHeaderTest.php @@ -100,6 +100,8 @@ public static function provideSortingData() 'quality has priority' => ['*;q=0.3,ISO-8859-1,utf-8;q=0.7', ['ISO-8859-1', 'utf-8', '*']], 'order matters when q is equal' => ['*;q=0.3,ISO-8859-1;q=0.7,utf-8;q=0.7', ['ISO-8859-1', 'utf-8', '*']], 'order matters when q is equal2' => ['*;q=0.3,utf-8;q=0.7,ISO-8859-1;q=0.7', ['utf-8', 'ISO-8859-1', '*']], + 'additional attributes like "format" should be handled according RFC 9110' => ['text/*;q=0.3, text/plain;q=0.7, text/plain;format=flowed, text/plain;format=fixed;q=0.4, */*;q=0.5', ['text/plain;format=flowed', 'text/plain', '*/*', 'text/plain;format=fixed', 'text/*']], + 'additional attributes like "format" should be handled according obsoleted RFC 7231 as well' => ['text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5', ['text/html;level=1', 'text/html', '*/*', 'text/html;level=2', 'text/*']], ]; } @@ -109,7 +111,7 @@ public static function provideSortingData() public function testDefaultValue($acceptHeader, $value, $expectedQuality) { $header = AcceptHeader::fromString($acceptHeader); - $this->assertSame($expectedQuality, $header->get($value)->getQuality()); + $this->assertSame($expectedQuality, $header->get($value)?->getQuality()); } public static function provideDefaultValueData() @@ -128,5 +130,50 @@ public static function provideDefaultValueData() yield ['*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', '*', 0.3]; yield ['*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', 'utf-8', 0.7]; yield ['*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', 'SHIFT_JIS', 0.3]; + yield 'additional attributes like "format" should be handled according RFC 9110' => ['text/*;q=0.3, text/plain;q=0.7, text/plain;format=flowed, text/plain;format=fixed;q=0.4, */*;q=0.5', 'text/plain;format=flowed', 1.0]; + yield 'additional attributes like "format" should be handled according obsoleted RFC 7231 as well' => ['text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5', 'text/html;level=1', 1.0]; + + // Edge cases for case-insensitivity + yield 'case-insensitive param names' => ['text/plain;format=flowed;q=0.8, text/plain;Format=fixed', 'text/plain;format=fixed', 1.0]; + yield 'case-insensitive charset' => ['text/plain;Charset=utf-8', 'text/plain;charset=utf-8', 1.0]; + + // Quoted values and specials + yield 'quoted value with space' => ['text/plain;param="value with space"', 'text/plain;param="value with space"', 1.0]; + yield 'quoted value with backslash' => ['text/plain;param="value\\with\\backslash"', 'text/plain;param="value\\with\\backslash"', 1.0]; + yield 'mismatched quoted' => ['text/plain;param="value with space"', 'text/plain;param=value with space', 1.0]; + + // Flag params or empty + yield 'flag param' => ['text/plain;flowed;q=0.9', 'text/plain;flowed', 0.9]; + yield 'empty param value' => ['text/plain;param=', 'text/plain;param=""', 1.0]; + yield 'missing required flag' => ['text/plain;flowed', 'text/plain', null]; + + // Extra params in query + yield 'extra param in query' => ['text/plain;format=flowed', 'text/plain;format=flowed;charset=utf-8', 1.0]; + yield 'missing required param in query' => ['text/plain;format=flowed', 'text/plain;charset=utf-8', null]; + yield 'wildcard with param' => ['text/*;format=flowed', 'text/plain;format=flowed', 1.0]; + yield 'wildcard missing param' => ['text/*;format=flowed', 'text/plain', null]; + + // Wildcards and specificity + yield 'specificity priority' => ['*/*;q=0.1, text/*;format=flowed;q=0.5, text/plain;q=0.8', 'text/plain;format=flowed', 0.8]; + yield 'wildcard with param match' => ['*/*;param=test', 'text/plain;param=test', 1.0]; + yield 'wildcard with param no match' => ['*/*;param=test', 'text/plain', null]; + + // Non-media types + yield 'charset wildcard' => ['utf-8;q=0.9, *;q=0.5', 'iso-8859-1', 0.5]; + yield 'language star' => ['*;q=0.5', 'en-US', 0.5]; + yield 'non-media */*' => ['*/*;q=0.5', 'utf-8', 0.5]; + + // Ties and duplicates + yield 'duplicate params tie on index' => ['text/plain;format=flowed;q=0.8, text/plain;format=flowed;q=0.8', 'text/plain;format=flowed', 0.8]; + yield 'param count tie' => ['text/plain;q=0.5, text/plain;format=flowed;q=0.5', 'text/plain;format=flowed;extra=foo', 0.5]; + + // Invalid/malformed + yield 'non-media invalid' => ['text', 'text', 1.0]; + yield 'invalid subtype' => ['text/', 'text/plain', null]; + yield 'empty header' => ['', 'text/plain', null]; + + // Mixed case types + yield 'mixed case type' => ['Text/Plain;Format=flowed', 'text/plain;format=flowed', 1.0]; + yield 'mixed case charset' => ['UTF-8;q=0.9', 'utf-8', 0.9]; } } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 848fa8708e781..b5a1998ce75bc 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -2732,6 +2732,267 @@ public static function provideLegitimateUrls(): array [':path'], ]; } + + /** + * @dataProvider provideAcceptableContentTypesRfc9110 + */ + public function testGetAcceptableContentTypesRfc9110(string $acceptHeader, array $expectedContentTypes) + { + $request = new Request(); + $request->headers->set('Accept', $acceptHeader); + $this->assertSame($expectedContentTypes, $request->getAcceptableContentTypes()); + } + + public static function provideAcceptableContentTypesRfc9110(): iterable + { + // Basic sorting by quality + yield 'quality sorting' => [ + 'text/html;q=0.9, application/json;q=0.8, text/plain;q=1.0', + ['text/plain', 'text/html', 'application/json'], + ]; + + // Parameters with RFC9110 canonical key generation (parameters normalized to lowercase) + yield 'parameters with canonical keys' => [ + 'text/plain, text/plain;format=flowed', + ['text/plain', 'text/plain;format=flowed'], + ]; + + yield 'parameters with quality and order' => [ + 'text/*;q=0.3, text/plain;q=0.7, text/plain;format=flowed, text/plain;format=fixed;q=0.4, */*;q=0.5', + ['text/plain;format=flowed', 'text/plain', '*/*', 'text/plain;format=fixed', 'text/*'], + ]; + + yield 'multiple parameters with order' => [ + 'text/html;level=1, text/html;level=2;q=0.4, text/html;q=0.7, text/*;q=0.3', + ['text/html;level=1', 'text/html', 'text/html;level=2', 'text/*'], + ]; + + // Case insensitivity for parameters - parameter names normalized to lowercase in canonical keys + yield 'case-insensitive param names normalized' => [ + 'text/plain;format=flowed;q=0.8, text/plain;Format=fixed', + ['text/plain;format=fixed', 'text/plain;format=flowed'], + ]; + + yield 'case-insensitive charset normalized' => [ + 'text/plain;Charset=utf-8, text/plain;charset=iso-8859-1;q=0.8', + ['text/plain;charset=utf-8', 'text/plain;charset=iso-8859-1'], + ]; + + // Quoted values with spaces + yield 'quoted value with space' => [ + 'text/plain;param="value with space"', + ['text/plain;param="value with space"'], + ]; + + yield 'quoted value with special chars' => [ + 'text/plain;param="value;with=special,chars"', + ['text/plain;param="value;with=special,chars"'], + ]; + + // Wildcards with parameters + yield 'wildcard type with parameter' => [ + 'text/*;format=flowed, text/plain', + ['text/*;format=flowed', 'text/plain'], + ]; + + yield 'wildcard all with parameter' => [ + '*/*;q=0.5, text/html;q=0.9', + ['text/html', '*/*'], + ]; + + // Stability - original order when quality is equal + yield 'order preserved for equal quality' => [ + 'application/json, application/xml, text/html', + ['application/json', 'application/xml', 'text/html'], + ]; + + yield 'order preserved for equal quality with params' => [ + 'text/plain;format=flowed;q=0.8, text/plain;format=fixed;q=0.8', + ['text/plain;format=flowed', 'text/plain;format=fixed'], + ]; + + // Complex scenarios + yield 'complex with wildcards and params' => [ + 'text/html, application/xhtml+xml, application/xml;q=0.9, text/*;q=0.8, */*;q=0.7', + ['text/html', 'application/xhtml+xml', 'application/xml', 'text/*', '*/*'], + ]; + + yield 'charset parameter order' => [ + 'text/plain, text/plain;charset=utf-8', + ['text/plain', 'text/plain;charset=utf-8'], + ]; + + yield 'multiple params on same type' => [ + 'text/plain;charset=utf-8;format=flowed, text/plain;charset=utf-8, text/plain', + ['text/plain;charset=utf-8;format=flowed', 'text/plain;charset=utf-8', 'text/plain'], + ]; + + // Edge cases + yield 'single wildcard' => [ + '*/*', + ['*/*'], + ]; + + yield 'type wildcard only' => [ + 'text/*', + ['text/*'], + ]; + + yield 'mixed case media types' => [ + 'Text/HTML, Application/JSON', + ['Text/HTML', 'Application/JSON'], + ]; + } + + /** + * @dataProvider providePreferredFormatRfc9110 + */ + public function testGetPreferredFormatRfc9110(string $acceptHeader, ?string $expectedFormat, ?string $default = 'html') + { + $request = new Request(); + $request->headers->set('Accept', $acceptHeader); + $this->assertSame($expectedFormat, $request->getPreferredFormat($default)); + } + + public static function providePreferredFormatRfc9110(): iterable + { + // Basic format detection with parameters + yield 'json with charset parameter' => [ + 'application/json;charset=utf-8', + 'json', + 'html', + ]; + + yield 'xml with version parameter' => [ + 'application/xml;version=1.0', + 'xml', + 'html', + ]; + + // Quality-based preference + yield 'json preferred over xml by quality' => [ + 'application/json;q=0.9, application/xml;q=0.8', + 'json', + 'html', + ]; + + yield 'xml preferred over json by quality' => [ + 'application/xml;q=0.9, application/json;q=0.8', + 'xml', + 'html', + ]; + + // Specificity affects format selection + yield 'more specific parameter wins with equal quality' => [ + 'application/json, application/json;charset=utf-8', + 'json', + 'html', + ]; + + yield 'text/html with level parameter' => [ + 'text/html;level=1, application/json', + 'html', + null, + ]; + + // Wildcards + yield 'wildcard type matches first known format' => [ + 'text/*', + 'html', + 'html', + ]; + + yield 'wildcard all matches default' => [ + '*/*', + 'html', + 'html', + ]; + + yield 'wildcard with quality lower than specific' => [ + 'application/json;q=0.9, */*;q=0.5', + 'json', + 'html', + ]; + + // Multiple content types with RFC9110 parameter handling + yield 'complex accept with parameters' => [ + 'text/html;q=0.9, application/xhtml+xml, application/xml;q=0.8, text/*;q=0.7', + 'html', + 'html', + ]; + + yield 'json with multiple parameters' => [ + 'application/json;charset=utf-8;profile=strict, text/html', + 'json', + 'html', + ]; + + // Case sensitivity - media type case is preserved, must use lowercase for proper format matching + yield 'lowercase content type for format matching' => [ + 'application/json', + 'json', + 'html', + ]; + + yield 'lowercase with parameters for format matching' => [ + 'application/json;charset=utf-8', + 'json', + 'html', + ]; + + // Quoted parameter values + yield 'quoted parameter value' => [ + 'application/json;profile="http://example.com/schema"', + 'json', + 'html', + ]; + + // Order preservation with equal quality and specificity + yield 'first match wins with equal priority' => [ + 'application/json;q=0.9, application/xml;q=0.9', + 'json', + 'html', + ]; + + // No match scenarios + yield 'unknown content type returns default' => [ + 'application/vnd.custom+unknown', + 'html', + 'html', + ]; + + yield 'unknown with null default' => [ + 'application/vnd.custom+unknown', + null, + null, + ]; + + // Empty or malformed + yield 'empty accept header' => [ + '', + 'html', + 'html', + ]; + + // Real-world examples + yield 'browser-like accept header' => [ + 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'html', + 'html', + ]; + + yield 'api client prefer json' => [ + 'application/json, text/plain, */*', + 'json', + 'html', + ]; + + yield 'rss/atom feeds' => [ + 'application/atom+xml;q=0.9, application/rss+xml;q=0.8', + 'atom', + 'html', + ]; + } } class RequestContentProxy extends Request From 293294133508e35bce396bd435985f75bf766926 Mon Sep 17 00:00:00 2001 From: wuchen90 Date: Fri, 10 Oct 2025 12:03:55 +0200 Subject: [PATCH 78/91] [HttpKernel] Fix StreamedResponse with chunks support in HttpKernelBrowser --- .../Component/HttpKernel/HttpKernelBrowser.php | 14 ++++++++++---- .../HttpKernel/Tests/HttpKernelBrowserTest.php | 13 +++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php b/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php index db6d0e08a9462..470678562b219 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php +++ b/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php @@ -180,10 +180,16 @@ protected function filterFiles(array $files): array */ protected function filterResponse(object $response): DomResponse { - // this is needed to support StreamedResponse - ob_start(); - $response->sendContent(); - $content = ob_get_clean(); + $content = ''; + ob_start(static function ($chunk) use (&$content) { + $content .= $chunk; + }); + + try { + $response->sendContent(); + } finally { + ob_end_clean(); + } return new DomResponse($content, $response->getStatusCode(), $response->headers->all()); } diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpKernelBrowserTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpKernelBrowserTest.php index 51fcbfac23dc9..3729c53904982 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpKernelBrowserTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpKernelBrowserTest.php @@ -89,6 +89,19 @@ public function testFilterResponseSupportsStreamedResponses() $this->assertEquals('foo', $domResponse->getContent()); } + public function testFilterResponseSupportsStreamedResponsesWithChunks() + { + $client = new HttpKernelBrowser(new TestHttpKernel()); + + $r = new \ReflectionObject($client); + $m = $r->getMethod('filterResponse'); + + $response = new StreamedResponse(new \ArrayIterator(['foo'])); + + $domResponse = $m->invoke($client, $response); + $this->assertEquals('foo', $domResponse->getContent()); + } + public function testUploadedFile() { $source = tempnam(sys_get_temp_dir(), 'source'); From 3dcc31b8679b20a724f500cedcdc1e8ae9a1b0e6 Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Mon, 3 Nov 2025 23:23:22 +0100 Subject: [PATCH 79/91] [DependencyInjection] Fix lazy proxy creation for interfaces aliased to final classes --- .../Compiler/AutowirePass.php | 30 +++++++++-- .../Tests/Compiler/AutowirePassTest.php | 41 ++++++++++++++ .../Fixtures/includes/autowiring_classes.php | 53 +++++++++++++++++++ 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 89369b3c32daf..8927cdc99011a 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -335,9 +335,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a $value = $this->doProcessValue($value); } elseif ($lazy = $attribute->lazy) { $value ??= $getValue(); - if ($this->container->has($value->getType())) { - $type = $this->container->findDefinition($value->getType())->getClass(); - } + $type = $this->resolveProxyType($type, $value->getType()); $definition = (new Definition($type)) ->setFactory('current') ->setArguments([[$value]]) @@ -758,4 +756,30 @@ private function getCombinedAlias(string $type, ?string $name = null): ?string return $alias; } + + /** + * Resolves the class name that should be proxied for a lazy service. + * + * @param string $originalType The original parameter type-hint (e.g., the interface) + * @param string $serviceId The service ID the type-hint resolved to (e.g., the alias) + */ + private function resolveProxyType(string $originalType, string $serviceId): string + { + if (!$this->container->has($serviceId)) { + return $originalType; + } + + $resolvedType = $this->container->findDefinition($serviceId)->getClass(); + $resolvedType = $this->container->getParameterBag()->resolveValue($resolvedType); + + if (!$resolvedType || !class_exists($resolvedType, false) && !interface_exists($resolvedType, false)) { + return $originalType; + } + + if (\PHP_VERSION_ID < 80400 && $this->container->getReflectionClass($resolvedType, false)->isFinal()) { + return $originalType; + } + + return $resolvedType; + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index d6bbbc70ffc09..b78e6616f18fa 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -1411,4 +1411,45 @@ public function testAutowireAttributeWithEnvVar() $this->assertSame('%env(bool:ENABLED)%', $container->resolveEnvPlaceholders($definition->getArguments()[0])); $this->assertSame('%env(default::OPTIONAL)%', $container->resolveEnvPlaceholders($definition->getArguments()[1])); } + + public function testLazyProxyForInterfaceWithFinalImplementation() + { + $container = new ContainerBuilder(); + $container->register('final_impl', FinalLazyProxyImplementation::class); + $container->setAlias(LazyProxyTestInterface::class, 'final_impl'); + + $container->register(LazyProxyInterfaceConsumer::class) + ->setAutoconfigured(true) + ->setAutowired(true) + ->setPublic(true); + + $container->compile(); + + $service = $container->get(LazyProxyInterfaceConsumer::class); + $this->assertInstanceOf(LazyProxyInterfaceConsumer::class, $service); + + // Trigger lazy load + $dep = $service->getDep()->getSelf(); + $this->assertInstanceOf(FinalLazyProxyImplementation::class, $dep); + } + + public function testLazyProxyWithClassInheritance() + { + $container = new ContainerBuilder(); + $container->register(BaseLazyProxyClass::class, ExtendedLazyProxyClass::class); + + $container->register(LazyProxyInheritanceConsumer::class) + ->setAutoconfigured(true) + ->setAutowired(true) + ->setPublic(true); + + $container->compile(); + + $service = $container->get(LazyProxyInheritanceConsumer::class); + $this->assertInstanceOf(LazyProxyInheritanceConsumer::class, $service); + + // Trigger lazy load + $dep = $service->getDependency()->getSelf(); + $this->assertInstanceOf(ExtendedLazyProxyClass::class, $dep); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php index 9e07d0283e396..73d641f5466f2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes.php @@ -524,3 +524,56 @@ public static function staticCreateFooWithParam(mixed $someParam): MyInlineServi return new MyInlineService($someParam); } } + +interface LazyProxyTestInterface +{ + public function getSelf(): self; +} + +final class FinalLazyProxyImplementation implements LazyProxyTestInterface +{ + public function getSelf(): self + { + return $this; + } +} + +class BaseLazyProxyClass +{ + public function getSelf(): self + { + return $this; + } +} + +class ExtendedLazyProxyClass extends BaseLazyProxyClass +{ + public function getSelf(): self + { + return $this; + } +} + +class LazyProxyInterfaceConsumer +{ + public function __construct(#[Autowire(lazy: true)] private readonly LazyProxyTestInterface $dep) + { + } + + public function getDep(): LazyProxyTestInterface + { + return $this->dep; + } +} + +class LazyProxyInheritanceConsumer +{ + public function __construct(#[Autowire(lazy: true)] private readonly BaseLazyProxyClass $dep) + { + } + + public function getDependency(): BaseLazyProxyClass + { + return $this->dep; + } +} From a79695c0fcdca7053674e6410893894205e4ad70 Mon Sep 17 00:00:00 2001 From: Santiago San Martin Date: Sat, 8 Nov 2025 18:43:26 -0300 Subject: [PATCH 80/91] [Translation][Lokalise] fix "Project too big for sync export" --- .../Bridge/Lokalise/LokaliseProvider.php | 122 +++++++++++++++++- .../Tests/Fixtures/Symfony-locale.zip | Bin 0 -> 1214 bytes .../Lokalise/Tests/LokaliseProviderTest.php | 94 ++++++++++++++ 3 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/Translation/Bridge/Lokalise/Tests/Fixtures/Symfony-locale.zip diff --git a/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php b/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php index 8e45a2540d150..44598c8ccea75 100644 --- a/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php +++ b/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php @@ -13,6 +13,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\Translation\Exception\ProviderException; +use Symfony\Component\Translation\Exception\RuntimeException; use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Component\Translation\MessageCatalogueInterface; use Symfony\Component\Translation\Provider\ProviderInterface; @@ -31,6 +32,9 @@ final class LokaliseProvider implements ProviderInterface { private const LOKALISE_GET_KEYS_LIMIT = 5000; + private const PROJECT_TOO_BIG_STATUS_CODE = 413; + private const FAILED_PROCESS_STATUS = ['cancelled', 'failed']; + private const SUCESS_PROCESS_STATUS = 'finished'; private HttpClientInterface $client; private LoaderInterface $loader; @@ -165,7 +169,14 @@ private function exportFiles(array $locales, array $domains): array } if (200 !== $response->getStatusCode()) { - throw new ProviderException(\sprintf('Unable to export translations from Lokalise: "%s".', $response->getContent(false)), $response); + if (self::PROJECT_TOO_BIG_STATUS_CODE !== ($responseContent['error']['code'] ?? null)) { + throw new ProviderException(\sprintf('Unable to export translations from Lokalise: "%s".', $response->getContent(false)), $response); + } + if (!\extension_loaded('zip')) { + throw new ProviderException(\sprintf('Unable to export translations from Lokalise: "%s". Make sure that the "zip" extension is enabled.', $response->getContent(false)), $response); + } + + return $this->exportFilesAsync($locales, $domains); } // Lokalise returns languages with "-" separator, we need to reformat them to "_" separator. @@ -176,6 +187,115 @@ private function exportFiles(array $locales, array $domains): array return array_combine($reformattedLanguages, $responseContent['files']); } + /** + * @see https://developers.lokalise.com/reference/download-files-async + */ + private function exportFilesAsync(array $locales, array $domains): array + { + $response = $this->client->request('POST', 'files/async-download', [ + 'json' => [ + 'format' => 'symfony_xliff', + 'original_filenames' => true, + 'filter_langs' => array_values($locales), + 'filter_filenames' => array_map($this->getLokaliseFilenameFromDomain(...), $domains), + 'export_empty_as' => 'skip', + 'replace_breaks' => false, + ], + ]); + + if (200 !== $response->getStatusCode()) { + throw new ProviderException(\sprintf('Unable to export translations from Lokalise: "%s".', $response->getContent(false)), $response); + } + + $processId = $response->toArray()['process_id']; + while (true) { + $response = $this->client->request('GET', \sprintf('processes/%s', $processId)); + $process = $response->toArray()['process']; + if (\in_array($process['status'], self::FAILED_PROCESS_STATUS, true)) { + throw new ProviderException(\sprintf('Unable to export translations from Lokalise: "%s".', $response->getContent(false)), $response); + } + if (self::SUCESS_PROCESS_STATUS === $process['status']) { + $downloadUrl = $process['details']['download_url']; + break; + } + usleep(500000); + } + + $response = $this->client->request('GET', $downloadUrl, ['buffer' => false]); + if (200 !== $response->getStatusCode()) { + throw new ProviderException(\sprintf('Unable to download translations file from Lokalise: "%s".', $response->getContent(false)), $response); + } + $zipFile = tempnam(sys_get_temp_dir(), 'lokalise'); + $extractPath = $zipFile.'.dir'; + try { + if (!$h = @fopen($zipFile, 'w')) { + throw new RuntimeException(error_get_last()['message'] ?? 'Failed to create temporary file.'); + } + foreach ($this->client->stream($response) as $chunk) { + fwrite($h, $chunk->getContent()); + } + fclose($h); + + $zip = new \ZipArchive(); + if (!$zip->open($zipFile)) { + throw new RuntimeException('Failed to open zipped translations from Lokalise.'); + } + + try { + if (!$zip->extractTo($extractPath)) { + throw new RuntimeException('Failed to unzip translations from Lokalize.'); + } + } finally { + $zip->close(); + } + + return $this->getZipContents($extractPath); + } finally { + if (is_resource($h)) { + fclose($h); + } + @unlink($zipFile); + $this->removeDir($extractPath); + } + } + + private function getZipContents(string $dir): array + { + $contents = []; + foreach (scandir($dir) as $lang) { + if (\in_array($lang, ['.', '..'], true)) { + continue; + } + $path = $dir.'/'.$lang; + // Lokalise returns languages with "-" separator, we need to reformat them to "_" separator. + $lang = str_replace('-', '_', $lang); + foreach (scandir($path) as $name) { + if (!\in_array($name, ['.', '..'], true)) { + $contents[$lang][$name]['content'] = file_get_contents($path.'/'.$name); + } + } + } + + return $contents; + } + + private function removeDir(string $dir): void + { + if (!is_dir($dir)) { + return; + } + $it = new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS); + $files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST); + foreach ($files as $file) { + if ($file->isDir()) { + rmdir($file->getPathname()); + } else { + unlink($file->getPathname()); + } + } + rmdir($dir); + } + private function createKeys(array $keys, string $domain): array { $keysToCreate = []; diff --git a/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/Fixtures/Symfony-locale.zip b/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/Fixtures/Symfony-locale.zip new file mode 100644 index 0000000000000000000000000000000000000000..2b40317dc6e1997e37a32a69327f6864704aff7d GIT binary patch literal 1214 zcmWIWW@Zs#00E_G8PQ+{lwbzZsd@SVURW#{#G>!%kEKU0bREB&r+;t1 z-2R!yCA&7vTl9_XNA0x8<~2)vEEiog_%ry%vPXRNndzKf&9drBBlL372g!n0qmlGw65>4Vv($WJ}Wml}|8GmjU+4?L3!6 zFFJOLMss-2|Mhj7sbQFbb@%MA@r!#N1nK|Scx0dV=i?8)JFI_U@|x}bgTwYrjm0P2 zc-B?^Fqt6qT2s^TereU~)7>k!%R?d&6tP%ixfm^$o8EI;bOK{p3Kq-7*kajs{cQ!2 zxs&4^Z{|+AzSQZ4q*kcxrIl;1tX=funB&oy?(XF0_r;r+P7m=973ZE`Z6V)arQ4ir z&XID_{7;0u1#|v}&HM6ye9xc1ivQ1*cUK?1d3s3bdhp@p4^*=@sqB?&NHH-wpW2=# zb-=xHO?*Y->DHXX>ka#YIx6SA{#o0*P+Y(LaNwUGMgOwa8N4`E)pbkyL4RZz@1{vc zPkY>s+x*b4-WI9W^XkmH0+#uEjic`fc`Hoae=4CtOHuS_g6|sfR~5VOuk0>8U~uDJ z)my1^k;@nNeHWCnv&yY)QGK>*l3Q+=34`N~rD`p~1_73De|vtt ziq+oxRl7kw@4U}7Zp&{P{N%g8ckc7f5B)Y?Iq6G|R9$|#bo%!T@BT9ccr!BDGvm&~ zpjZcizm6ahoTDKcaOG)`G7wnOs0bvn<#6Jyd ['code' => 413, 'message' => 'test']], + ['http_code' => 406], + ); + }; + $secondResponse = function (): ResponseInterface { + return new JsonMockResponse( + ['process_id' => 123], + ); + }; + $thirdResponse = function (): ResponseInterface { + return new JsonMockResponse( + ['process' => ['status' => 'finished', 'details' => ['download_url' => 'https://api.lokalise.com/Symfony-locale.zip']]], + ); + }; + $fourResponse = function (string $method, string $url, array $options = []) use ($zipLocation): ResponseInterface { + $this->assertSame('GET', $method); + $this->assertSame('https://api.lokalise.com/Symfony-locale.zip', $url); + $this->assertFalse($options['buffer']); + + return new MockResponse(file_get_contents($zipLocation)); + }; + + $provider = self::createProvider((new MockHttpClient([$firstResponse, $secondResponse, $thirdResponse, $fourResponse]))->withOptions([ + 'base_uri' => 'https://api.lokalise.com/api2/projects/PROJECT_ID/', + 'headers' => ['X-Api-Token' => 'API_KEY'], + ]), new XliffFileLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.lokalise.com'); + $translatorBag = $provider->read(['foo'], ['baz']); + + // We don't want to assert equality of metadata here, due to the ArrayLoader usage. + /** @var MessageCatalogue $catalogue */ + foreach ($translatorBag->getCatalogues() as $catalogue) { + $catalogue->deleteMetadata('', ''); + } + + $arrayLoader = new ArrayLoader(); + $expectedTranslatorBag = new TranslatorBag(); + $expectedTranslatorBag->addCatalogue($arrayLoader->load( + [ + 'how are you' => 'How are you?', + 'welcome_header' => 'Hello world!', + ], + 'en', + 'no_filename' + )); + $expectedTranslatorBag->addCatalogue($arrayLoader->load( + [ + 'how are you' => 'Como estas?', + 'welcome_header' => 'Hola mundo!', + ], + 'es', + 'no_filename' + )); + $this->assertEquals($expectedTranslatorBag->getCatalogues(), $translatorBag->getCatalogues()); + } + + /** + * @requires extension zip + */ + public function testReadWithExportAsyncFailedProcess() + { + $firstResponse = function (): ResponseInterface { + return new JsonMockResponse( + ['error' => ['code' => 413, 'message' => 'test']], + ['http_code' => 406], + ); + }; + $secondResponse = function (): ResponseInterface { + return new JsonMockResponse( + ['process_id' => 123], + ); + }; + $thirdResponse = function (): ResponseInterface { + return new JsonMockResponse( + ['process' => ['status' => 'failed']], + ); + }; + + $provider = self::createProvider((new MockHttpClient([$firstResponse, $secondResponse, $thirdResponse]))->withOptions([ + 'base_uri' => 'https://api.lokalise.com/api2/projects/PROJECT_ID/', + 'headers' => ['X-Api-Token' => 'API_KEY'], + ]), new XliffFileLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.lokalise.com'); + + $this->expectException(ProviderException::class); + $provider->read(['foo'], ['baz']); + } + public function testDeleteProcess() { $getKeysIdsForMessagesDomainResponse = function (string $method, string $url, array $options = []): ResponseInterface { From bfe4671353581a643cb68f826d885879760385ce Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 12 Nov 2025 19:51:23 +0100 Subject: [PATCH 81/91] fix merge --- .../Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php | 7 +++++-- src/Symfony/Component/HttpKernel/HttpKernelBrowser.php | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php b/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php index d3b54679d9bc0..ec3ff9ad20cd1 100644 --- a/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php +++ b/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php @@ -167,8 +167,11 @@ public function createResponse(Response $symfonyResponse): ResponseInterface return ''; }, 1); - $symfonyResponse->sendContent(); - ob_end_clean(); + try { + $symfonyResponse->sendContent(); + } finally { + ob_end_clean(); + } } else { $stream->write($symfonyResponse->getContent()); } diff --git a/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php b/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php index 470678562b219..f29655945ea0d 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php +++ b/src/Symfony/Component/HttpKernel/HttpKernelBrowser.php @@ -183,6 +183,8 @@ protected function filterResponse(object $response): DomResponse $content = ''; ob_start(static function ($chunk) use (&$content) { $content .= $chunk; + + return ''; }); try { From 24fde468ae09856da04bf7de4de3eff8add94985 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 12 Nov 2025 20:00:12 +0100 Subject: [PATCH 82/91] CS fix --- src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php b/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php index ec3ff9ad20cd1..a226469dcaeef 100644 --- a/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php +++ b/src/Symfony/Bridge/PsrHttpMessage/Factory/PsrHttpFactory.php @@ -167,7 +167,7 @@ public function createResponse(Response $symfonyResponse): ResponseInterface return ''; }, 1); - try { + try { $symfonyResponse->sendContent(); } finally { ob_end_clean(); From ef9e803a997dcf4444d9f6f34c4625d0df95ddb4 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 13 Nov 2025 08:44:33 +0100 Subject: [PATCH 83/91] ReflectionMethod::setAccessible() is no-op since PHP 8.1 --- .../PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php index fddb81422a051..2e73a6aedf51e 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php @@ -806,10 +806,6 @@ public function testCamelize(string $input, string $expected) $reflection = new \ReflectionClass($this->extractor); $method = $reflection->getMethod('camelize'); - if (\PHP_VERSION_ID < 80500) { - $method->setAccessible(true); - } - $this->assertSame($expected, $method->invoke($this->extractor, $input)); } From 2d3779afc95e52d1f12df398b21d0cf94c4792a6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 13 Nov 2025 09:11:23 +0100 Subject: [PATCH 84/91] [HttpFoundation] Fix RequestTest insulation --- .../Component/HttpFoundation/AcceptHeader.php | 26 +++++++++---------- .../invalid_cookie_name.php | 2 +- .../HttpFoundation/Tests/RequestTest.php | 2 ++ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/AcceptHeader.php b/src/Symfony/Component/HttpFoundation/AcceptHeader.php index 58a0b43dd5b9c..e735c75647bf5 100644 --- a/src/Symfony/Component/HttpFoundation/AcceptHeader.php +++ b/src/Symfony/Component/HttpFoundation/AcceptHeader.php @@ -46,18 +46,15 @@ public function __construct(array $items) */ public static function fromString(?string $headerValue): self { - $parts = HeaderUtils::split($headerValue ?? '', ',;='); + $items = []; + foreach (HeaderUtils::split($headerValue ?? '', ',;=') as $i => $parts) { + $part = array_shift($parts); + $item = new AcceptHeaderItem($part[0], HeaderUtils::combine($parts)); - return new self(array_map(function ($subParts) { - static $index = 0; - $part = array_shift($subParts); - $attributes = HeaderUtils::combine($subParts); - - $item = new AcceptHeaderItem($part[0], $attributes); - $item->setIndex($index++); + $items[] = $item->setIndex($i); + } - return $item; - }, $parts)); + return new self($items); } /** @@ -95,10 +92,11 @@ public function get(string $value): ?AcceptHeaderItem return null; } - usort($candidates, fn ($a, $b) => - $this->getSpecificity($b, $queryItem) <=> $this->getSpecificity($a, $queryItem) // Descending specificity - ?: $b->getQuality() <=> $a->getQuality() // Descending quality - ?: $a->getIndex() <=> $b->getIndex() // Ascending index (stability) + usort( + $candidates, + fn ($a, $b) => $this->getSpecificity($b, $queryItem) <=> $this->getSpecificity($a, $queryItem) // Descending specificity + ?: $b->getQuality() <=> $a->getQuality() // Descending quality + ?: $a->getIndex() <=> $b->getIndex() // Ascending index (stability) ); return reset($candidates); diff --git a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php index 3acf86039d93d..15fab1492fd1f 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Fixtures/response-functional/invalid_cookie_name.php @@ -6,6 +6,6 @@ try { $r->headers->setCookie(new Cookie('Hello + world', 'hodor', 0, null, null, null, false, true)); -} catch (\InvalidArgumentException $e) { +} catch (InvalidArgumentException $e) { echo $e->getMessage(); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index b5a1998ce75bc..632e61159eddd 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -31,6 +31,8 @@ protected function tearDown(): void { Request::setTrustedProxies([], -1); Request::setTrustedHosts([]); + Request::setFactory(null); + \Closure::bind(static fn () => self::$formats = null, null, Request::class)(); } public function testInitialize() From 6731fff15f26e48ac1456b5ba63552a1d61fbf3e Mon Sep 17 00:00:00 2001 From: Younes ENNAJI Date: Wed, 12 Nov 2025 18:33:48 +0100 Subject: [PATCH 85/91] [Console] Fix signal handlers not being cleared after command termination --- src/Symfony/Component/Console/Application.php | 16 +- .../Console/SignalRegistry/SignalRegistry.php | 61 +++++- .../Console/Tests/ApplicationTest.php | 177 +++++++++++++++++- .../SignalRegistry/SignalRegistryTest.php | 63 +++++++ 4 files changed, 310 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Console/Application.php b/src/Symfony/Component/Console/Application.php index dd562f0eb4569..8a5364f4a0edc 100644 --- a/src/Symfony/Component/Console/Application.php +++ b/src/Symfony/Component/Console/Application.php @@ -1012,12 +1012,16 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI } } + $registeredSignals = false; $commandSignals = $command instanceof SignalableCommandInterface ? $command->getSubscribedSignals() : []; if ($commandSignals || $this->dispatcher && $this->signalsToDispatchEvent) { if (!$this->signalRegistry) { throw new RuntimeException('Unable to subscribe to signal events. Make sure that the "pcntl" extension is installed and that "pcntl_*" functions are not disabled by your php.ini\'s "disable_functions" directive.'); } + $registeredSignals = true; + $this->getSignalRegistry()->pushCurrentHandlers(); + if ($this->dispatcher) { // We register application signals, so that we can dispatch the event foreach ($this->signalsToDispatchEvent as $signal) { @@ -1067,7 +1071,13 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI } if (null === $this->dispatcher) { - return $command->run($input, $output); + try { + return $command->run($input, $output); + } finally { + if ($registeredSignals) { + $this->getSignalRegistry()->popPreviousHandlers(); + } + } } // bind before the console.command event, so the listeners have access to input options/arguments @@ -1097,6 +1107,10 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI if (0 === $exitCode = $event->getExitCode()) { $e = null; } + } finally { + if ($registeredSignals) { + $this->getSignalRegistry()->popPreviousHandlers(); + } } $event = new ConsoleTerminateEvent($command, $input, $output, $exitCode); diff --git a/src/Symfony/Component/Console/SignalRegistry/SignalRegistry.php b/src/Symfony/Component/Console/SignalRegistry/SignalRegistry.php index ef2e5f04e16d6..ac8851b061eec 100644 --- a/src/Symfony/Component/Console/SignalRegistry/SignalRegistry.php +++ b/src/Symfony/Component/Console/SignalRegistry/SignalRegistry.php @@ -13,8 +13,21 @@ final class SignalRegistry { + /** + * @var array> + */ private array $signalHandlers = []; + /** + * @var array>> + */ + private array $stack = []; + + /** + * @var array + */ + private array $originalHandlers = []; + public function __construct() { if (\function_exists('pcntl_async_signals')) { @@ -24,17 +37,21 @@ public function __construct() public function register(int $signal, callable $signalHandler): void { - if (!isset($this->signalHandlers[$signal])) { - $previousCallback = pcntl_signal_get_handler($signal); + $previous = pcntl_signal_get_handler($signal); + + if (!isset($this->originalHandlers[$signal])) { + $this->originalHandlers[$signal] = $previous; + } - if (\is_callable($previousCallback)) { - $this->signalHandlers[$signal][] = $previousCallback; + if (!isset($this->signalHandlers[$signal])) { + if (\is_callable($previous) && [$this, 'handle'] !== $previous) { + $this->signalHandlers[$signal][] = $previous; } } $this->signalHandlers[$signal][] = $signalHandler; - pcntl_signal($signal, $this->handle(...)); + pcntl_signal($signal, [$this, 'handle']); } public static function isSupported(): bool @@ -54,4 +71,38 @@ public function handle(int $signal): void $signalHandler($signal, $hasNext); } } + + /** + * Pushes the current active handlers onto the stack and clears the active list. + * + * This prepares the registry for a new set of handlers within a specific scope. + * + * @internal + */ + public function pushCurrentHandlers(): void + { + $this->stack[] = $this->signalHandlers; + $this->signalHandlers = []; + } + + /** + * Restores the previous handlers from the stack, making them active. + * + * This also restores the original OS-level signal handler if no + * more handlers are registered for a signal that was just popped. + * + * @internal + */ + public function popPreviousHandlers(): void + { + $popped = $this->signalHandlers; + $this->signalHandlers = array_pop($this->stack) ?? []; + + // Restore OS handler if no more Symfony handlers for this signal + foreach ($popped as $signal => $handlers) { + if (!($this->signalHandlers[$signal] ?? false) && isset($this->originalHandlers[$signal])) { + pcntl_signal($signal, $this->originalHandlers[$signal]); + } + } + } } diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 9d57161d33b85..639527f2d28dd 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -824,7 +824,7 @@ public function testSetCatchErrors(bool $catchExceptions) try { $tester->run(['command' => 'boom']); - $this->fail('The exception is not catched.'); + $this->fail('The exception is not caught.'); } catch (\Throwable $e) { $this->assertInstanceOf(\Error::class, $e); $this->assertSame('This is an error.', $e->getMessage()); @@ -2259,6 +2259,181 @@ private function runRestoresSttyTest(array $params, int $expectedExitCode, bool } } + /** + * @requires extension pcntl + */ + public function testSignalHandlersAreCleanedUpAfterCommandRuns() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new SignableCommand(false)); + + $signalRegistry = $application->getSignalRegistry(); + $tester = new ApplicationTester($application); + + $this->assertCount(0, $this->getHandlersForSignal($signalRegistry, \SIGUSR1), 'Registry should be empty initially.'); + + $tester->run(['command' => 'signal']); + $this->assertCount(0, $this->getHandlersForSignal($signalRegistry, \SIGUSR1), 'Registry should be empty after first run.'); + + $tester->run(['command' => 'signal']); + $this->assertCount(0, $this->getHandlersForSignal($signalRegistry, \SIGUSR1), 'Registry should still be empty after second run.'); + } + + /** + * @requires extension pcntl + */ + public function testSignalHandlersCleanupOnException() + { + $command = new class('signal:exception') extends Command implements SignalableCommandInterface { + public function getSubscribedSignals(): array + { + return [\SIGUSR1]; + } + + public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false + { + return false; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + throw new \RuntimeException('Test exception'); + } + }; + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(true); + $application->add($command); + + $signalRegistry = $application->getSignalRegistry(); + $tester = new ApplicationTester($application); + + $this->assertCount(0, $this->getHandlersForSignal($signalRegistry, \SIGUSR1), 'Pre-condition: Registry must be empty.'); + + $tester->run(['command' => 'signal:exception']); + $this->assertCount(0, $this->getHandlersForSignal($signalRegistry, \SIGUSR1), 'Signal handlers must be cleaned up even on exception.'); + } + + /** + * @requires extension pcntl + */ + public function testNestedCommandsIsolateSignalHandlers() + { + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + + $signalRegistry = $application->getSignalRegistry(); + $self = $this; + + $innerCommand = new class('signal:inner') extends Command implements SignalableCommandInterface { + public $signalRegistry; + public $self; + + public function getSubscribedSignals(): array + { + return [\SIGUSR1]; + } + + public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false + { + return false; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $handlers = $this->self->getHandlersForSignal($this->signalRegistry, \SIGUSR1); + $this->self->assertCount(1, $handlers, 'Inner command should only see its own handler.'); + $output->write('Inner execute.'); + + return 0; + } + }; + + $outerCommand = new class('signal:outer') extends Command implements SignalableCommandInterface { + public $signalRegistry; + public $self; + + public function getSubscribedSignals(): array + { + return [\SIGUSR1]; + } + + public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false + { + return false; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $handlersBefore = $this->self->getHandlersForSignal($this->signalRegistry, \SIGUSR1); + $this->self->assertCount(1, $handlersBefore, 'Outer command must have its handler registered.'); + + $output->write('Outer pre-run.'); + + $this->getApplication()->find('signal:inner')->run(new ArrayInput([]), $output); + + $output->write('Outer post-run.'); + + $handlersAfter = $this->self->getHandlersForSignal($this->signalRegistry, \SIGUSR1); + $this->self->assertCount(1, $handlersAfter, 'Outer command\'s handler must be restored.'); + $this->self->assertSame($handlersBefore, $handlersAfter, 'Handler stack must be identical after pop.'); + + return 0; + } + }; + + $innerCommand->self = $self; + $innerCommand->signalRegistry = $signalRegistry; + $outerCommand->self = $self; + $outerCommand->signalRegistry = $signalRegistry; + + $application->add($innerCommand); + $application->add($outerCommand); + + $tester = new ApplicationTester($application); + + $this->assertCount(0, $this->getHandlersForSignal($signalRegistry, \SIGUSR1), 'Pre-condition: Registry must be empty.'); + $tester->run(['command' => 'signal:outer']); + $this->assertStringContainsString('Outer pre-run.Inner execute.Outer post-run.', $tester->getDisplay()); + + $this->assertCount(0, $this->getHandlersForSignal($signalRegistry, \SIGUSR1), 'Registry must be empty after all commands are finished.'); + } + + /** + * @requires extension pcntl + */ + public function testOriginalHandlerRestoredAfterPop() + { + $this->assertSame(\SIG_DFL, pcntl_signal_get_handler(\SIGUSR1), 'Pre-condition: Original handler for SIGUSR1 must be SIG_DFL.'); + + $application = new Application(); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add(new SignableCommand(false)); + + $tester = new ApplicationTester($application); + $tester->run(['command' => 'signal']); + + $this->assertSame(\SIG_DFL, pcntl_signal_get_handler(\SIGUSR1), 'OS-level handler for SIGUSR1 must be restored to SIG_DFL.'); + + $tester->run(['command' => 'signal']); + $this->assertSame(\SIG_DFL, pcntl_signal_get_handler(\SIGUSR1), 'OS-level handler must remain SIG_DFL after a second run.'); + } + + /** + * Reads the private "signalHandlers" property of the SignalRegistry for assertions. + */ + public function getHandlersForSignal(SignalRegistry $registry, int $signal): array + { + $handlers = (\Closure::bind(fn () => $this->signalHandlers, $registry, SignalRegistry::class))(); + + return $handlers[$signal] ?? []; + } + private function createSignalableApplication(Command $command, ?EventDispatcherInterface $dispatcher): Application { $application = new Application(); diff --git a/src/Symfony/Component/Console/Tests/SignalRegistry/SignalRegistryTest.php b/src/Symfony/Component/Console/Tests/SignalRegistry/SignalRegistryTest.php index f997f7c1d8cee..77b7e28e72623 100644 --- a/src/Symfony/Component/Console/Tests/SignalRegistry/SignalRegistryTest.php +++ b/src/Symfony/Component/Console/Tests/SignalRegistry/SignalRegistryTest.php @@ -129,4 +129,67 @@ public function testTwoCallbacksForASignalPreviousCallbackFromAnotherRegistry() $this->assertTrue($isHandled1); $this->assertTrue($isHandled2); } + + public function testPushPopIsolatesHandlers() + { + $registry = new SignalRegistry(); + + $signal = \SIGUSR1; + + $handler1 = static function () {}; + $handler2 = static function () {}; + + $registry->pushCurrentHandlers(); + $registry->register($signal, $handler1); + + $this->assertCount(1, $this->getHandlersForSignal($registry, $signal)); + + $registry->pushCurrentHandlers(); + $registry->register($signal, $handler2); + + $this->assertCount(1, $this->getHandlersForSignal($registry, $signal)); + $this->assertSame([$handler2], $this->getHandlersForSignal($registry, $signal)); + + $registry->popPreviousHandlers(); + + $this->assertCount(1, $this->getHandlersForSignal($registry, $signal)); + $this->assertSame([$handler1], $this->getHandlersForSignal($registry, $signal)); + + $registry->popPreviousHandlers(); + + $this->assertCount(0, $this->getHandlersForSignal($registry, $signal)); + } + + public function testRestoreOriginalOnEmptyAfterPop() + { + if (!\extension_loaded('pcntl')) { + $this->markTestSkipped('PCNTL extension required'); + } + + $registry = new SignalRegistry(); + + $signal = \SIGUSR2; + + $original = pcntl_signal_get_handler($signal); + + $handler = static function () {}; + + $registry->pushCurrentHandlers(); + $registry->register($signal, $handler); + + $this->assertNotEquals($original, pcntl_signal_get_handler($signal)); + + $registry->popPreviousHandlers(); + + $this->assertEquals($original, pcntl_signal_get_handler($signal)); + } + + private function getHandlersForSignal(SignalRegistry $registry, int $signal): array + { + $ref = new \ReflectionClass($registry); + $prop = $ref->getProperty('signalHandlers'); + $handlers = $prop->getValue($registry); + + return $handlers[$signal] ?? []; + } } From c22d2d8f9a9d4ebb29b8a25796bba8da2341117b Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Thu, 13 Nov 2025 09:29:54 +0100 Subject: [PATCH 86/91] [JsonStreamer] Finish #62063 upmerge --- .../Read/StreamReaderGenerator.php | 41 ++---- .../Component/JsonStreamer/StreamerDumper.php | 106 ++++++++++++++++ .../JsonStreamer/Tests/StreamerDumperTest.php | 117 ++++++++++++++++++ .../Write/StreamWriterGenerator.php | 42 ++----- 4 files changed, 246 insertions(+), 60 deletions(-) create mode 100644 src/Symfony/Component/JsonStreamer/StreamerDumper.php create mode 100644 src/Symfony/Component/JsonStreamer/Tests/StreamerDumperTest.php diff --git a/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php b/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php index a6aee49162bc9..e6c5fdbe60d1a 100644 --- a/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php +++ b/src/Symfony/Component/JsonStreamer/Read/StreamReaderGenerator.php @@ -11,8 +11,7 @@ namespace Symfony\Component\JsonStreamer\Read; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\JsonStreamer\DataModel\Read\BackedEnumNode; use Symfony\Component\JsonStreamer\DataModel\Read\CollectionNode; use Symfony\Component\JsonStreamer\DataModel\Read\CompositeNode; @@ -22,6 +21,7 @@ use Symfony\Component\JsonStreamer\Exception\RuntimeException; use Symfony\Component\JsonStreamer\Exception\UnsupportedException; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonStreamer\StreamerDumper; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\BackedEnumType; use Symfony\Component\TypeInfo\Type\BuiltinType; @@ -40,13 +40,15 @@ */ final class StreamReaderGenerator { + private StreamerDumper $dumper; private ?PhpGenerator $phpGenerator = null; - private ?Filesystem $fs = null; public function __construct( private PropertyMetadataLoaderInterface $propertyMetadataLoader, private string $streamReadersDir, + ?ConfigCacheFactoryInterface $cacheFactory = null, ) { + $this->dumper = new StreamerDumper($propertyMetadataLoader, $streamReadersDir, $cacheFactory); } /** @@ -56,39 +58,18 @@ public function __construct( */ public function generate(Type $type, bool $decodeFromStream, array $options = []): string { - $path = $this->getPath($type, $decodeFromStream); - if (is_file($path)) { - return $path; - } - - $this->phpGenerator ??= new PhpGenerator(); - $this->fs ??= new Filesystem(); + $path = \sprintf('%s%s%s.json%s.php', $this->streamReadersDir, \DIRECTORY_SEPARATOR, hash('xxh128', (string) $type), $decodeFromStream ? '.stream' : ''); + $generateContent = function () use ($type, $decodeFromStream, $options): string { + $this->phpGenerator ??= new PhpGenerator(); - $dataModel = $this->createDataModel($type, $options); - $php = $this->phpGenerator->generate($dataModel, $decodeFromStream, $options); + return $this->phpGenerator->generate($this->createDataModel($type, $options), $decodeFromStream, $options); + }; - if (!$this->fs->exists($this->streamReadersDir)) { - $this->fs->mkdir($this->streamReadersDir); - } - - $tmpFile = $this->fs->tempnam(\dirname($path), basename($path)); - - try { - $this->fs->dumpFile($tmpFile, $php); - $this->fs->rename($tmpFile, $path); - $this->fs->chmod($path, 0o666 & ~umask()); - } catch (IOException $e) { - throw new RuntimeException(\sprintf('Failed to write "%s" stream reader file.', $path), previous: $e); - } + $this->dumper->dump($type, $path, $generateContent); return $path; } - private function getPath(Type $type, bool $decodeFromStream): string - { - return \sprintf('%s%s%s.json%s.php', $this->streamReadersDir, \DIRECTORY_SEPARATOR, hash('xxh128', (string) $type), $decodeFromStream ? '.stream' : ''); - } - /** * @param array $options * @param array $context diff --git a/src/Symfony/Component/JsonStreamer/StreamerDumper.php b/src/Symfony/Component/JsonStreamer/StreamerDumper.php new file mode 100644 index 0000000000000..317739ff7b9c3 --- /dev/null +++ b/src/Symfony/Component/JsonStreamer/StreamerDumper.php @@ -0,0 +1,106 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonStreamer; + +use Symfony\Component\Config\ConfigCacheFactoryInterface; +use Symfony\Component\Config\ConfigCacheInterface; +use Symfony\Component\Config\Resource\ReflectionClassResource; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\Type\GenericType; +use Symfony\Component\TypeInfo\Type\ObjectType; + +/** + * @author Mathias Arlaud + * + * @internal + */ +final class StreamerDumper +{ + private ?Filesystem $fs = null; + + public function __construct( + private PropertyMetadataLoaderInterface $propertyMetadataLoader, + private string $cacheDir, + private ?ConfigCacheFactoryInterface $cacheFactory = null, + ) { + } + + /** + * Dumps the generated content to the given path, optionally using config cache. + * + * @param callable(): string $generateContent + */ + public function dump(Type $type, string $path, callable $generateContent): void + { + if ($this->cacheFactory) { + $this->cacheFactory->cache( + $path, + function (ConfigCacheInterface $cache) use ($generateContent, $type) { + $resourceClasses = $this->getResourceClassNames($type); + $cache->write( + $generateContent(), + array_map(fn (string $c) => new ReflectionClassResource(new \ReflectionClass($c)), $resourceClasses), + ); + }, + ); + + return; + } + + $this->fs ??= new Filesystem(); + + if (!$this->fs->exists($this->cacheDir)) { + $this->fs->mkdir($this->cacheDir); + } + + if (!$this->fs->exists($path)) { + $this->fs->dumpFile($path, $generateContent()); + } + } + + /** + * Retrieves resources class names required for caching based on the provided type. + * + * @param list $classNames + * @param array $context + * + * @return list + */ + private function getResourceClassNames(Type $type, array $classNames = [], array $context = []): array + { + $context['original_type'] ??= $type; + + foreach ($type->traverse() as $t) { + if ($t instanceof ObjectType) { + if (\in_array($t->getClassName(), $classNames, true)) { + return $classNames; + } + + $classNames[] = $t->getClassName(); + + foreach ($this->propertyMetadataLoader->load($t->getClassName(), [], $context) as $property) { + $classNames = [...$classNames, ...$this->getResourceClassNames($property->getType(), $classNames)]; + } + } + + if ($t instanceof GenericType) { + foreach ($t->getVariableTypes() as $variableType) { + $classNames = [...$classNames, ...$this->getResourceClassNames($variableType, $classNames)]; + } + } + } + + return array_values(array_unique($classNames)); + } +} diff --git a/src/Symfony/Component/JsonStreamer/Tests/StreamerDumperTest.php b/src/Symfony/Component/JsonStreamer/Tests/StreamerDumperTest.php new file mode 100644 index 0000000000000..8b7c0ca24c641 --- /dev/null +++ b/src/Symfony/Component/JsonStreamer/Tests/StreamerDumperTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonStreamer\Tests; + +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\ConfigCacheFactory; +use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoader; +use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonStreamer\StreamerDumper; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Enum\DummyBackedEnum; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\ClassicDummy; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithArray; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithNameAttributes; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithOtherDummies; +use Symfony\Component\JsonStreamer\Tests\Fixtures\Model\SelfReferencingDummy; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; + +class StreamerDumperTest extends TestCase +{ + private string $cacheDir; + + protected function setUp(): void + { + parent::setUp(); + + $this->cacheDir = \sprintf('%s/symfony_json_streamer_test/any', sys_get_temp_dir()); + + if (is_dir($this->cacheDir)) { + array_map('unlink', glob($this->cacheDir.'/*')); + rmdir($this->cacheDir); + } + } + + public function testDumpWithConfigCache() + { + $path = $this->cacheDir.'/streamer.php'; + + $dumper = new StreamerDumper($this->createMock(PropertyMetadataLoaderInterface::class), $this->cacheDir, new ConfigCacheFactory(true)); + $dumper->dump(Type::int(), $path, fn () => 'CONTENT'); + + $this->assertFileExists($path); + $this->assertFileExists($path.'.meta'); + $this->assertFileExists($path.'.meta.json'); + + $this->assertStringEqualsFile($path, 'CONTENT'); + } + + public function testDumpWithoutConfigCache() + { + $path = $this->cacheDir.'/streamer.php'; + + $dumper = new StreamerDumper($this->createMock(PropertyMetadataLoaderInterface::class), $this->cacheDir); + $dumper->dump(Type::int(), $path, fn () => 'CONTENT'); + + $this->assertFileExists($path); + $this->assertStringEqualsFile($path, 'CONTENT'); + } + + /** + * @param list $expectedClassNames + */ + #[DataProvider('getCacheResourcesDataProvider')] + public function testGetCacheResources(Type $type, array $expectedClassNames) + { + $path = $this->cacheDir.'/streamer.php'; + + $dumper = new StreamerDumper(new PropertyMetadataLoader(TypeResolver::create()), $this->cacheDir, new ConfigCacheFactory(true)); + $dumper->dump($type, $path, fn () => 'CONTENT'); + + $resources = json_decode(file_get_contents($path.'.meta.json'), true)['resources']; + $classNames = array_column($resources, 'className'); + + $this->assertSame($expectedClassNames, $classNames); + } + + /** + * @return iterable}> + */ + public static function getCacheResourcesDataProvider(): iterable + { + yield 'scalar' => [Type::int(), []]; + yield 'enum' => [Type::enum(DummyBackedEnum::class), [DummyBackedEnum::class]]; + yield 'object' => [Type::object(ClassicDummy::class), [ClassicDummy::class]]; + yield 'collection of objects' => [ + Type::list(Type::object(ClassicDummy::class)), + [ClassicDummy::class], + ]; + yield 'generic with objects' => [ + Type::generic(Type::object(ClassicDummy::class), Type::object(DummyWithArray::class)), + [DummyWithArray::class, ClassicDummy::class], + ]; + yield 'union with objects' => [ + Type::union(Type::int(), Type::object(ClassicDummy::class), Type::object(DummyWithArray::class)), + [ClassicDummy::class, DummyWithArray::class], + ]; + yield 'intersection with objects' => [ + Type::intersection(Type::object(ClassicDummy::class), Type::object(DummyWithArray::class)), + [ClassicDummy::class, DummyWithArray::class], + ]; + yield 'object with object properties' => [ + Type::object(DummyWithOtherDummies::class), + [DummyWithOtherDummies::class, DummyWithNameAttributes::class, ClassicDummy::class], + ]; + yield 'object with self reference' => [Type::object(SelfReferencingDummy::class), [SelfReferencingDummy::class]]; + } +} diff --git a/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php b/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php index 1739a2b58bd60..777f2d2d5506d 100644 --- a/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php +++ b/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php @@ -11,8 +11,7 @@ namespace Symfony\Component\JsonStreamer\Write; -use Symfony\Component\Filesystem\Exception\IOException; -use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\JsonStreamer\DataModel\Write\BackedEnumNode; use Symfony\Component\JsonStreamer\DataModel\Write\CollectionNode; use Symfony\Component\JsonStreamer\DataModel\Write\CompositeNode; @@ -22,6 +21,7 @@ use Symfony\Component\JsonStreamer\Exception\RuntimeException; use Symfony\Component\JsonStreamer\Exception\UnsupportedException; use Symfony\Component\JsonStreamer\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonStreamer\StreamerDumper; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\BackedEnumType; use Symfony\Component\TypeInfo\Type\BuiltinType; @@ -40,13 +40,15 @@ */ final class StreamWriterGenerator { + private StreamerDumper $dumper; private ?PhpGenerator $phpGenerator = null; - private ?Filesystem $fs = null; public function __construct( private PropertyMetadataLoaderInterface $propertyMetadataLoader, private string $streamWritersDir, + ?ConfigCacheFactoryInterface $cacheFactory = null, ) { + $this->dumper = new StreamerDumper($propertyMetadataLoader, $streamWritersDir, $cacheFactory); } /** @@ -56,39 +58,18 @@ public function __construct( */ public function generate(Type $type, array $options = []): string { - $path = $this->getPath($type); - if (is_file($path)) { - return $path; - } - - $this->phpGenerator ??= new PhpGenerator(); - $this->fs ??= new Filesystem(); + $path = \sprintf('%s%s%s.json.php', $this->streamWritersDir, \DIRECTORY_SEPARATOR, hash('xxh128', (string) $type)); + $generateContent = function () use ($type, $options): string { + $this->phpGenerator ??= new PhpGenerator(); - $dataModel = $this->createDataModel($type, '$data', $options, ['depth' => 0]); - $php = $this->phpGenerator->generate($dataModel, $options); + return $this->phpGenerator->generate($this->createDataModel($type, '$data', $options), $options); + }; - if (!$this->fs->exists($this->streamWritersDir)) { - $this->fs->mkdir($this->streamWritersDir); - } - - $tmpFile = $this->fs->tempnam(\dirname($path), basename($path)); - - try { - $this->fs->dumpFile($tmpFile, $php); - $this->fs->rename($tmpFile, $path); - $this->fs->chmod($path, 0o666 & ~umask()); - } catch (IOException $e) { - throw new RuntimeException(\sprintf('Failed to write "%s" stream writer file.', $path), previous: $e); - } + $this->dumper->dump($type, $path, $generateContent); return $path; } - private function getPath(Type $type): string - { - return \sprintf('%s%s%s.json.php', $this->streamWritersDir, \DIRECTORY_SEPARATOR, hash('xxh128', (string) $type)); - } - /** * @param array $options * @param array $context @@ -96,6 +77,7 @@ private function getPath(Type $type): string private function createDataModel(Type $type, string $accessor, array $options = [], array $context = []): DataModelNodeInterface { $context['original_type'] ??= $type; + $context['depth'] ??= 0; if ($type instanceof UnionType) { return new CompositeNode($accessor, array_map(fn (Type $t): DataModelNodeInterface => $this->createDataModel($t, $accessor, $options, $context), $type->getTypes())); From a9b53e9236c92fa84fd8b4b03034d16b08b51cb1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 13 Nov 2025 09:49:24 +0100 Subject: [PATCH 87/91] [HttpFoundation] Fix tests --- src/Symfony/Component/HttpFoundation/Tests/RequestTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php index 5286ac92d0ed3..707a2708b9a1e 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php @@ -588,11 +588,11 @@ public static function getFormatToMimeTypeMapProvider() ['form', ['application/x-www-form-urlencoded', 'multipart/form-data']], ['rss', ['application/rss+xml']], ['soap', ['application/soap+xml']], - ['html', ['application/xhtml+xml']], + ['html', ['text/html', 'application/xhtml+xml']], ['problem', ['application/problem+json']], ['hal', ['application/hal+json', 'application/hal+xml']], ['jsonapi', ['application/vnd.api+json']], - ['yaml', ['application/x-yaml', 'text/yaml']], + ['yaml', ['text/yaml', 'application/x-yaml']], ['wbxml', ['application/vnd.wap.wbxml']], ]; } From e727725c7ef02c6fc47cc54a40e77631f175f6e3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 13 Nov 2025 09:53:59 +0100 Subject: [PATCH 88/91] fix merge --- .../Component/Console/Tests/ApplicationTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index f7a19575d1857..898e9b62257ca 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -2319,7 +2319,7 @@ public function testSignalHandlersAreCleanedUpAfterCommandRuns() $application = new Application(); $application->setAutoExit(false); $application->setCatchExceptions(false); - $application->add(new SignableCommand(false)); + $application->addCommand(new SignableCommand(false)); $signalRegistry = $application->getSignalRegistry(); $tester = new ApplicationTester($application); @@ -2372,7 +2372,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $application = new Application(); $application->setAutoExit(false); $application->setCatchExceptions(true); - $application->add($command); + $application->addCommand($command); $signalRegistry = $application->getSignalRegistry(); $tester = new ApplicationTester($application); @@ -2554,8 +2554,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $outerCommand->self = $self; $outerCommand->signalRegistry = $signalRegistry; - $application->add($innerCommand); - $application->add($outerCommand); + $application->addCommand($innerCommand); + $application->addCommand($outerCommand); $tester = new ApplicationTester($application); @@ -2591,7 +2591,7 @@ public function testOriginalHandlerRestoredAfterPop() $application = new Application(); $application->setAutoExit(false); $application->setCatchExceptions(false); - $application->add(new SignableCommand(false)); + $application->addCommand(new SignableCommand(false)); $tester = new ApplicationTester($application); $tester->run(['command' => 'signal']); From 215caa3b8f41820bf951cd4a1e40a3230eefec42 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 13 Nov 2025 10:28:44 +0100 Subject: [PATCH 89/91] [FrameworkBundle] Fix wiring JsonStreamReader --- .../FrameworkBundle/Resources/config/json_streamer.php | 3 ++- src/Symfony/Component/JsonStreamer/JsonStreamReader.php | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_streamer.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_streamer.php index 4535c259d25e1..5d359ceb85ed8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_streamer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_streamer.php @@ -21,6 +21,7 @@ use Symfony\Component\JsonStreamer\Mapping\Read\DateTimeTypePropertyMetadataLoader as ReadDateTimeTypePropertyMetadataLoader; use Symfony\Component\JsonStreamer\Mapping\Write\AttributePropertyMetadataLoader as WriteAttributePropertyMetadataLoader; use Symfony\Component\JsonStreamer\Mapping\Write\DateTimeTypePropertyMetadataLoader as WriteDateTimeTypePropertyMetadataLoader; +use Symfony\Component\JsonStreamer\StreamerDumper; use Symfony\Component\JsonStreamer\ValueTransformer\DateTimeToStringValueTransformer; use Symfony\Component\JsonStreamer\ValueTransformer\StringToDateTimeValueTransformer; @@ -39,8 +40,8 @@ tagged_locator('json_streamer.value_transformer'), service('json_streamer.read.property_metadata_loader'), param('.json_streamer.stream_readers_dir'), + class_exists(StreamerDumper::class) ? service('config_cache_factory')->ignoreOnInvalid() : param('.json_streamer.lazy_ghosts_dir'), param('.json_streamer.lazy_ghosts_dir'), - service('config_cache_factory')->ignoreOnInvalid(), ]) ->alias(JsonStreamWriter::class, 'json_streamer.stream_writer') ->alias(JsonStreamReader::class, 'json_streamer.stream_reader') diff --git a/src/Symfony/Component/JsonStreamer/JsonStreamReader.php b/src/Symfony/Component/JsonStreamer/JsonStreamReader.php index 28e9b74c01066..15e72d6fd43c6 100644 --- a/src/Symfony/Component/JsonStreamer/JsonStreamReader.php +++ b/src/Symfony/Component/JsonStreamer/JsonStreamReader.php @@ -46,9 +46,13 @@ public function __construct( private ContainerInterface $valueTransformers, PropertyMetadataLoaderInterface $propertyMetadataLoader, string $streamReadersDir, + ConfigCacheFactoryInterface|string|null $configCacheFactory = null, ?string $lazyGhostsDir = null, - ?ConfigCacheFactoryInterface $configCacheFactory = null, ) { + if (\is_string($configCacheFactory)) { + $lazyGhostsDir = $configCacheFactory; + $configCacheFactory = null; + } $this->streamReaderGenerator = new StreamReaderGenerator($propertyMetadataLoader, $streamReadersDir, $configCacheFactory); $this->instantiator = new Instantiator(); $this->lazyInstantiator = new LazyInstantiator($lazyGhostsDir); From d83172dcaf77618ca2768ece022c387cfe3a548d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 13 Nov 2025 13:51:14 +0100 Subject: [PATCH 90/91] Update CHANGELOG for 8.0.0-RC1 --- CHANGELOG-8.0.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/CHANGELOG-8.0.md b/CHANGELOG-8.0.md index 24205126a23f8..2b17bf205b416 100644 --- a/CHANGELOG-8.0.md +++ b/CHANGELOG-8.0.md @@ -7,6 +7,44 @@ in 8.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/v8.0.0...v8.0.1 +* 8.0.0-RC1 (2025-11-13) + + * bug #62335 [Console] Fix signal handlers not being cleared after command termination (yoeunes) + * bug #62348 [Translation][Lokalise] fix "Project too big for sync export" (santysisi) + * bug #62304 [DependencyInjection] Fix lazy proxy creation for interfaces aliased to final classes (yoeunes) + * bug #62036 [HttpKernel] Fix StreamedResponse with chunks support in HttpKernelBrowser (wuchen90) + * bug #62063 [JsonStreamer] Rebuild cache on class update (mtarld) + * bug #62287 [HttpFoundation] Fix AcceptHeader overwrites items with different parameters (yoeunes) + * bug #62325 [Routing] Fix default value not taken if usigng name:entity.attribute (eltharin) + * bug #62329 [DependencyInjection] Fix merging explicit tags and #[AsTaggeditem] (nicolas-grekas) + * bug #62356 [HttpClient] Fix `Warning: curl_multi_select(): timeout must be positive` (Jeroeny) + * bug #62334 [PropertyInfo] Fix `ReflectionExtractor` handling of underscore-only property names (yoeunes) + * bug #58473 [Serializer] Fix `AbstractObjectNormalizer` to allow scalar values to be normalized (Hanmac, xabbuh) + * bug #62093 [Security] Fix `HttpUtils::createRequest()` when the context’s base URL isn’t empty (MatTheCat) + * bug #62007 [Serializer] fix inherited properties normalization (Link1515) + * bug #62286 [Cache] compatibility with ext-redis 6.3 (xabbuh) + * bug #62321 [Serializer] Fix BackedEnumNormalizer behavior with partial denormalization (yoeunes) + * bug #62344 [OptionsResolver] Fix missing prototype key in nested error paths (yoeunes) + * bug #62346 [Clock] Align MockClock::sleep() behavior with NativeClock for negative values (yoeunes) + * bug #62347 [OptionsResolver] Ensure remove() also unsets deprecation status (yoeunes) + * bug #62359 [Yaml] Fix parsing of unquoted multiline scalars with comments or blank lines (yoeunes) + * bug #62350 [ExpressionLanguage] Compile numbers with var_export in Compiler::repr for thread-safety (yoeunes) + * security #cve-2025-64500 [HttpFoundation] Fix parsing pathinfo with no leading slash (nicolas-grekas) + * bug #62333 Postal mailer transport message ID retrieval (lalcebo) + * feature #62326 [Cache][Messenger] re-allow ext-redis 6.1 (xabbuh) + * bug #62324 [HttpFoundation] Fix parsing hosts and schemes in URLs (nicolas-grekas) + * bug #62171 [Messenger] Fix commands writing to `STDERR` instead of `STDOUT` (wazum) + * bug #62315 Keep body size limit for AMP redirects (villermen) + * bug #62214 [ObjectMapper] lazy loading (soyuka) + * bug #62237 [Form] Fix EnumType choice_label logic for grouped choices (yoeunes) + * bug #62283 [Filesystem] Unify logic for isAbsolute() in Path (yoeunes) + * feature #62302 [Routing] Simplify importing routes defined on controller services (nicolas-grekas) + * bug #62091 [BrowserKit] The BrowserKit history with parameter separator without slash. (biozshock) + * bug #62297 [Twig] Ensure WrappedTemplatedEmail::getReturnPath() returns a string (yoeunes) + * bug #62294 [Console] Add missing VERBOSITY_SILENT case in CommandDataCollector (yoeunes) + * bug #62290 [Routing] Fix matching the "0" URL (cs278) + * bug #62285 [HttpClient] Reject 3xx pushed responses (nicolas-grekas) + * 8.0.0-BETA2 (2025-11-02) * feature #62270 [Lock][DynamoDB] Allow symfony/lock 8.0 (DavidPrevot) From 18a87a48a2f2672b1b025be9aa854264f33476be Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 13 Nov 2025 13:51:17 +0100 Subject: [PATCH 91/91] Update VERSION for 8.0.0-RC1 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 765f2c731f1d2..bcb34aac67d43 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -71,12 +71,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '8.0.0-DEV'; + public const VERSION = '8.0.0-RC1'; public const VERSION_ID = 80000; public const MAJOR_VERSION = 8; public const MINOR_VERSION = 0; public const RELEASE_VERSION = 0; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = 'RC1'; public const END_OF_MAINTENANCE = '07/2026'; public const END_OF_LIFE = '07/2026';