diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 00a686580d01f..30408a440624e 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,10 +1,10 @@ | Q | A | ------------- | --- -| Branch? | 6.2 for features / 4.4, 5.4, 6.0 or 6.1 for bug fixes +| Branch? | 6.3 for features / 4.4, 5.4, 6.0, 6.1, or 6.2 for bug fixes | Bug fix? | yes/no | New feature? | yes/no | Deprecations? | yes/no -| Tickets | Fix #... +| Tickets | Fix #... | License | MIT | Doc PR | symfony/symfony-docs#... ------------------------]'). + "\nProcessing \"foobar\"...". + $this->generateOutput("[----->----------------------]\nProcessing \"foobar\"..."), + stream_get_contents($output->getStream()) + ); + } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php index ac3ca8b29bab5..e31e5f78a2c12 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php @@ -42,7 +42,7 @@ public function process(ContainerBuilder $container) $tagsToKeep = $container->hasParameter('container.behavior_describing_tags') ? $container->getParameter('container.behavior_describing_tags') - : ['container.do_not_inline', 'container.service_locator', 'container.service_subscriber']; + : ['container.do_not_inline', 'container.service_locator', 'container.service_subscriber', 'container.service_subscriber.locator']; foreach ($definitions as [$id, $definition]) { $decoratedService = $definition->getDecoratedService(); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php index a6f5187782f74..e46ac5dbb4146 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php @@ -129,7 +129,7 @@ private function processDefinition(ContainerBuilder $container, string $id, Defi foreach ($instanceofTags[$i] as $k => $v) { if (null === $definition->getDecoratedService() || \in_array($k, $tagsToKeep, true)) { foreach ($v as $v) { - if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) { + if ($definition->hasTag($k) && (!$v || \in_array($v, $definition->getTag($k)))) { continue; } $definition->addTag($k, $v); diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php index 7387a8a934f4d..67103a78522d3 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ServiceLocatorTagPass.php @@ -40,6 +40,10 @@ protected function processValue(mixed $value, bool $isRoot = false): mixed return self::register($this->container, $value->getValues()); } + if ($value instanceof Definition) { + $value->setBindings(parent::processValue($value->getBindings())); + } + if (!$value instanceof Definition || !$value->hasTag('container.service_locator')) { return parent::processValue($value, $isRoot); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php index 5f7bd8cfbe7d2..7f548492b1fea 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php @@ -248,7 +248,7 @@ public function testProcessLeavesServiceSubscriberTagOnOriginalDefinition() $container = new ContainerBuilder(); $container ->register('foo') - ->setTags(['container.service_subscriber' => [], 'bar' => ['attr' => 'baz']]) + ->setTags(['container.service_subscriber' => [], 'container.service_subscriber.locator' => [], 'bar' => ['attr' => 'baz']]) ; $container ->register('baz') @@ -258,7 +258,7 @@ public function testProcessLeavesServiceSubscriberTagOnOriginalDefinition() $this->process($container); - $this->assertEquals(['container.service_subscriber' => []], $container->getDefinition('baz.inner')->getTags()); + $this->assertEquals(['container.service_subscriber' => [], 'container.service_subscriber.locator' => []], $container->getDefinition('baz.inner')->getTags()); $this->assertEquals(['bar' => ['attr' => 'baz'], 'foobar' => ['attr' => 'bar']], $container->getDefinition('baz')->getTags()); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php index bf164ebe5b104..72f875db6903d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; @@ -152,7 +153,7 @@ public function testProcessInlinesWhenThereAreMultipleReferencesButFromTheSameDe $this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.'); } - public function testCanDecorateServiceSubscriber() + public function testCanDecorateServiceSubscriberUsingBinding() { $container = new ContainerBuilder(); $container->register(ServiceSubscriberStub::class) @@ -160,11 +161,33 @@ public function testCanDecorateServiceSubscriber() ->setPublic(true); $container->register(DecoratedServiceSubscriber::class) + ->setProperty('inner', new Reference(DecoratedServiceSubscriber::class.'.inner')) ->setDecoratedService(ServiceSubscriberStub::class); $container->compile(); $this->assertInstanceOf(DecoratedServiceSubscriber::class, $container->get(ServiceSubscriberStub::class)); + $this->assertInstanceOf(ServiceSubscriberStub::class, $container->get(ServiceSubscriberStub::class)->inner); + $this->assertInstanceOf(ServiceLocator::class, $container->get(ServiceSubscriberStub::class)->inner->container); + } + + public function testCanDecorateServiceSubscriberReplacingArgument() + { + $container = new ContainerBuilder(); + $container->register(ServiceSubscriberStub::class) + ->setArguments([new Reference(ContainerInterface::class)]) + ->addTag('container.service_subscriber') + ->setPublic(true); + + $container->register(DecoratedServiceSubscriber::class) + ->setProperty('inner', new Reference(DecoratedServiceSubscriber::class.'.inner')) + ->setDecoratedService(ServiceSubscriberStub::class); + + $container->compile(); + + $this->assertInstanceOf(DecoratedServiceSubscriber::class, $container->get(ServiceSubscriberStub::class)); + $this->assertInstanceOf(ServiceSubscriberStub::class, $container->get(ServiceSubscriberStub::class)->inner); + $this->assertInstanceOf(ServiceLocator::class, $container->get(ServiceSubscriberStub::class)->inner->container); } public function testCanDecorateServiceLocator() @@ -826,6 +849,7 @@ static function (ChildDefinition $definition, CustomAutoconfiguration $attribute $definition->addTag('app.custom_tag', get_object_vars($attribute) + ['class' => $reflector->getName()]); } ); + $container->registerForAutoconfiguration(TaggedService1::class)->addTag('app.custom_tag'); $container->register('one', TaggedService1::class) ->setPublic(true) @@ -1043,6 +1067,13 @@ public function testTaggedIteratorAndLocatorWithExclude() class ServiceSubscriberStub implements ServiceSubscriberInterface { + public $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + public static function getSubscribedServices(): array { return []; @@ -1051,6 +1082,7 @@ public static function getSubscribedServices(): array class DecoratedServiceSubscriber { + public $inner; } class DecoratedServiceLocator implements ServiceProviderInterface diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php index 644876e81f20a..4d248306a0b7d 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ServiceLocatorTagPassTest.php @@ -214,6 +214,18 @@ public function testDefinitionOrderIsTheSame() static::assertSame(['service-2', 'service-1'], array_keys($factories)); } + + public function testBindingsAreProcessed() + { + $container = new ContainerBuilder(); + + $definition = $container->register('foo') + ->setBindings(['foo' => new ServiceLocatorArgument()]); + + (new ServiceLocatorTagPass())->process($container); + + $this->assertInstanceOf(Reference::class, $definition->getBindings()['foo']->getValues()[0]); + } } class Locator diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 05193a2f0c2e8..e88b5a5bdfca1 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -27,7 +27,7 @@ "symfony/service-contracts": "^1.1|^2|^3" }, "require-dev": { - "doctrine/collections": "~1.0", + "doctrine/collections": "^1.0|^2.0", "symfony/validator": "^5.4|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", diff --git a/src/Symfony/Component/HttpClient/AmpHttpClient.php b/src/Symfony/Component/HttpClient/AmpHttpClient.php index 168bf133a04aa..c20ae173ea2e6 100644 --- a/src/Symfony/Component/HttpClient/AmpHttpClient.php +++ b/src/Symfony/Component/HttpClient/AmpHttpClient.php @@ -17,6 +17,7 @@ use Amp\Http\Client\PooledHttpClient; use Amp\Http\Client\Request; use Amp\Http\Tunnel\Http1TunnelConnector; +use Amp\Promise; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Symfony\Component\HttpClient\Exception\TransportException; @@ -29,7 +30,11 @@ use Symfony\Contracts\Service\ResetInterface; if (!interface_exists(DelegateHttpClient::class)) { - throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the "amphp/http-client" package is not installed. Try running "composer require amphp/http-client".'); + throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the "amphp/http-client" package is not installed. Try running "composer require amphp/http-client:^4.2.1".'); +} + +if (!interface_exists(Promise::class)) { + throw new \LogicException('You cannot use "Symfony\Component\HttpClient\AmpHttpClient" as the installed "amphp/http-client" is not compatible with this version of "symfony/http-client". Try downgrading "amphp/http-client" to "^4.2.1".'); } /** diff --git a/src/Symfony/Component/HttpClient/HttpClient.php b/src/Symfony/Component/HttpClient/HttpClient.php index 4d138746e0cdb..0e7d9b4405e33 100644 --- a/src/Symfony/Component/HttpClient/HttpClient.php +++ b/src/Symfony/Component/HttpClient/HttpClient.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpClient; use Amp\Http\Client\Connection\ConnectionLimitingPool; +use Amp\Promise; use Symfony\Contracts\HttpClient\HttpClientInterface; /** @@ -30,7 +31,7 @@ final class HttpClient */ public static function create(array $defaultOptions = [], int $maxHostConnections = 6, int $maxPendingPushes = 50): HttpClientInterface { - if ($amp = class_exists(ConnectionLimitingPool::class)) { + if ($amp = class_exists(ConnectionLimitingPool::class) && interface_exists(Promise::class)) { if (!\extension_loaded('curl')) { return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes); } @@ -61,7 +62,7 @@ public static function create(array $defaultOptions = [], int $maxHostConnection return new AmpHttpClient($defaultOptions, null, $maxHostConnections, $maxPendingPushes); } - @trigger_error((\extension_loaded('curl') ? 'Upgrade' : 'Install').' the curl extension or run "composer require amphp/http-client" to perform async HTTP operations, including full HTTP/2 support', \E_USER_NOTICE); + @trigger_error((\extension_loaded('curl') ? 'Upgrade' : 'Install').' the curl extension or run "composer require amphp/http-client:^4.2.1" to perform async HTTP operations, including full HTTP/2 support', \E_USER_NOTICE); return new NativeHttpClient($defaultOptions, $maxHostConnections); } diff --git a/src/Symfony/Component/HttpClient/HttplugClient.php b/src/Symfony/Component/HttpClient/HttplugClient.php index 8b552c9533b6d..f832333ed7e39 100644 --- a/src/Symfony/Component/HttpClient/HttplugClient.php +++ b/src/Symfony/Component/HttpClient/HttplugClient.php @@ -254,12 +254,17 @@ private function sendPsr7Request(RequestInterface $request, bool $buffer = null) $body->seek(0); } - return $this->client->request($request->getMethod(), (string) $request->getUri(), [ + $options = [ 'headers' => $request->getHeaders(), 'body' => $body->getContents(), - 'http_version' => '1.0' === $request->getProtocolVersion() ? '1.0' : null, 'buffer' => $buffer, - ]); + ]; + + if ('1.0' === $request->getProtocolVersion()) { + $options['http_version'] = '1.0'; + } + + return $this->client->request($request->getMethod(), (string) $request->getUri(), $options); } catch (\InvalidArgumentException $e) { throw new RequestException($e->getMessage(), $request, $e); } catch (TransportExceptionInterface $e) { diff --git a/src/Symfony/Component/HttpClient/Psr18Client.php b/src/Symfony/Component/HttpClient/Psr18Client.php index e1595f9f4a067..5d6a15171fa3f 100644 --- a/src/Symfony/Component/HttpClient/Psr18Client.php +++ b/src/Symfony/Component/HttpClient/Psr18Client.php @@ -91,11 +91,16 @@ public function sendRequest(RequestInterface $request): ResponseInterface $body->seek(0); } - $response = $this->client->request($request->getMethod(), (string) $request->getUri(), [ + $options = [ 'headers' => $request->getHeaders(), 'body' => $body->getContents(), - 'http_version' => '1.0' === $request->getProtocolVersion() ? '1.0' : null, - ]); + ]; + + if ('1.0' === $request->getProtocolVersion()) { + $options['http_version'] = '1.0'; + } + + $response = $this->client->request($request->getMethod(), (string) $request->getUri(), $options); $psrResponse = $this->responseFactory->createResponse($response->getStatusCode()); diff --git a/src/Symfony/Component/HttpFoundation/IpUtils.php b/src/Symfony/Component/HttpFoundation/IpUtils.php index 174347115a3eb..49f433b76cc30 100644 --- a/src/Symfony/Component/HttpFoundation/IpUtils.php +++ b/src/Symfony/Component/HttpFoundation/IpUtils.php @@ -113,6 +113,15 @@ public static function checkIp6(string $requestIp, string $ip): bool throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".'); } + // Check to see if we were given a IP4 $requestIp or $ip by mistake + if (str_contains($requestIp, '.') || str_contains($ip, '.')) { + return self::$checkedIps[$cacheKey] = false; + } + + if (!filter_var($requestIp, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { + return self::$checkedIps[$cacheKey] = false; + } + if (str_contains($ip, '/')) { [$address, $netmask] = explode('/', $ip, 2); diff --git a/src/Symfony/Component/HttpFoundation/Test/Constraint/ResponseCookieValueSame.php b/src/Symfony/Component/HttpFoundation/Test/Constraint/ResponseCookieValueSame.php index db1e733dce18f..f62a4cffb9148 100644 --- a/src/Symfony/Component/HttpFoundation/Test/Constraint/ResponseCookieValueSame.php +++ b/src/Symfony/Component/HttpFoundation/Test/Constraint/ResponseCookieValueSame.php @@ -59,7 +59,7 @@ protected function matches($response): bool return false; } - return $this->value === $cookie->getValue(); + return $this->value === (string) $cookie->getValue(); } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php index 5476c3d3018d5..9db54719a65b5 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php @@ -74,6 +74,10 @@ public function getIpv6Data() [false, '}__test|O:21:"JDatabaseDriverMysqli":3:{s:2', '::1'], [false, '2a01:198:603:0:396e:4789:8e99:890f', 'unknown'], [false, '', '::1'], + [false, '127.0.0.1', '::1'], + [false, '0.0.0.0/8', '::1'], + [false, '::1', '127.0.0.1'], + [false, '::1', '0.0.0.0/8'], ]; } diff --git a/src/Symfony/Component/HttpFoundation/Tests/Test/Constraint/ResponseCookieValueSameTest.php b/src/Symfony/Component/HttpFoundation/Tests/Test/Constraint/ResponseCookieValueSameTest.php index fc195309a4b29..1b68b20bddf59 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Test/Constraint/ResponseCookieValueSameTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Test/Constraint/ResponseCookieValueSameTest.php @@ -41,4 +41,12 @@ public function testConstraint() $this->fail(); } + + public function testCookieWithNullValueIsComparedAsEmptyString() + { + $response = new Response(); + $response->headers->setCookie(Cookie::create('foo', null, 0, '/path')); + + $this->assertTrue((new ResponseCookieValueSame('foo', '', '/path'))->evaluate($response, '', true)); + } } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php index d8953f564b440..e9a7494a4c761 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/DateTimeValueResolver.php @@ -60,8 +60,6 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable $format = $attribute->format; } - $date = false; - if (null !== $format) { $date = $class::createFromFormat($format, $value); @@ -73,7 +71,7 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable $value = '@'.$value; } try { - $date = new $class($value); + $date = new $class($value ?? 'now'); } catch (\Exception) { $date = false; } diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php index 22a4d2ae74498..ae2dbbb477dc7 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/NotTaggedControllerValueResolver.php @@ -69,8 +69,9 @@ public function resolve(Request $request, ArgumentMetadata $argument): iterable } if (!$this->container->has($controller)) { - $i = strrpos($controller, ':'); - $controller = substr($controller, 0, $i).strtolower(substr($controller, $i)); + $controller = (false !== $i = strrpos($controller, ':')) + ? substr($controller, 0, $i).strtolower(substr($controller, $i)) + : $controller.'::__invoke'; } $what = sprintf('argument $%s of "%s()"', $argument->getName(), $controller); diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index 2e861f32cf970..c0a97c650b717 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -25,6 +25,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\TypedReference; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; /** @@ -154,7 +155,7 @@ public function process(ContainerBuilder $container) $invalidBehavior = ContainerInterface::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE; } - if (Request::class === $type || SessionInterface::class === $type) { + if (Request::class === $type || SessionInterface::class === $type || Response::class === $type) { continue; } diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 78193d31c6ec8..f279be75340c0 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -78,11 +78,11 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static array $freshCache = []; - public const VERSION = '6.1.7'; - public const VERSION_ID = 60107; + public const VERSION = '6.1.8'; + public const VERSION_ID = 60108; public const MAJOR_VERSION = 6; public const MINOR_VERSION = 1; - public const RELEASE_VERSION = 7; + public const RELEASE_VERSION = 8; public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '01/2023'; diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php index e1c3d662c6ece..b438c527a32da 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/DateTimeValueResolverTest.php @@ -113,6 +113,26 @@ public function testNullableWithEmptyAttribute() $this->assertNull($results[0]); } + /** + * @dataProvider getTimeZones + */ + public function testNow(string $timezone) + { + date_default_timezone_set($timezone); + $resolver = new DateTimeValueResolver(); + + $argument = new ArgumentMetadata('dummy', \DateTime::class, false, false, null, false); + $request = self::requestWithAttributes(['dummy' => null]); + + /** @var \Generator $results */ + $results = $resolver->resolve($request, $argument); + $results = iterator_to_array($results); + + $this->assertCount(1, $results); + $this->assertEquals('0', $results[0]->diff(new \DateTimeImmutable())->format('%s')); + $this->assertSame($timezone, $results[0]->getTimezone()->getName(), 'Default timezone'); + } + public function testPreviouslyConvertedAttribute() { $resolver = new DateTimeValueResolver(); diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php index 3cf2f0f18562e..4577b5a6d2384 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/NotTaggedControllerValueResolverTest.php @@ -98,6 +98,17 @@ public function testControllerNameIsAnArray() $resolver->resolve($request, $argument); } + public function testInvokableController() + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Could not resolve argument $dummy of "App\Controller\Mine::__invoke()", maybe you forgot to register the controller as a service or missed tagging it with the "controller.service_arguments"?'); + $resolver = new NotTaggedControllerValueResolver(new ServiceLocator([])); + $argument = new ArgumentMetadata('dummy', \stdClass::class, false, false, null); + $request = $this->requestWithAttributes(['_controller' => 'App\Controller\Mine']); + $this->assertTrue($resolver->supports($request, $argument)); + $resolver->resolve($request, $argument); + } + private function requestWithAttributes(array $attributes) { $request = Request::create('/'); diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php index 85e057439cfe6..302f9e35dd70d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -24,6 +24,7 @@ use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass; use Symfony\Component\HttpKernel\Tests\Fixtures\Suit; @@ -443,6 +444,20 @@ public function testBindWithTarget() $this->assertEquals($expected, $locator->getArgument(0)); } + public function testResponseArgumentIsIgnored() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service', 'stdClass')->addArgument([]); + + $container->register('foo', WithResponseArgument::class) + ->addTag('controller.service_arguments'); + + (new RegisterControllerArgumentLocatorsPass())->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + $this->assertEmpty(array_keys($locator), 'Response typed argument is ignored'); + } + public function testAutowireAttribute() { if (!class_exists(Autowire::class)) { @@ -558,6 +573,13 @@ public function fooAction( } } +class WithResponseArgument +{ + public function fooAction(Response $response, ?Response $nullableResponse) + { + } +} + class WithAutowireAttribute { public function fooAction( diff --git a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php index e007fd46148c8..ecfb21994946a 100644 --- a/src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php +++ b/src/Symfony/Component/Mailer/Transport/Smtp/Stream/AbstractStream.php @@ -75,7 +75,7 @@ public function readLine(): string } $line = fgets($this->out); - if ('' === $line) { + if ('' === $line || false === $line) { $metas = stream_get_meta_data($this->out); if ($metas['timed_out']) { throw new TransportException(sprintf('Connection to "%s" timed out.', $this->getReadConnectionDescription())); diff --git a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php index 2c3556fbd3d30..5eee8270fbcb2 100644 --- a/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Bridge/Doctrine/Tests/Transport/DoctrineIntegrationTest.php @@ -28,13 +28,10 @@ class DoctrineIntegrationTest extends TestCase private $driverConnection; /** @var Connection */ private $connection; - /** @var string */ - private $sqliteFile; protected function setUp(): void { - $this->sqliteFile = sys_get_temp_dir().'/symfony.messenger.sqlite'; - $dsn = getenv('MESSENGER_DOCTRINE_DSN') ?: 'sqlite:///'.$this->sqliteFile; + $dsn = getenv('MESSENGER_DOCTRINE_DSN') ?: 'sqlite://:memory:'; $this->driverConnection = DriverManager::getConnection(['url' => $dsn]); $this->connection = new Connection([], $this->driverConnection); } @@ -42,9 +39,6 @@ protected function setUp(): void protected function tearDown(): void { $this->driverConnection->close(); - if (file_exists($this->sqliteFile)) { - @unlink($this->sqliteFile); - } } public function testConnectionSendAndGet() diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index a97fcc1874c97..6d51d4d3ac932 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -17,6 +17,7 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; +use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -131,7 +132,7 @@ protected function interact(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output); - if ($this->receiverNames && 0 === \count($input->getArgument('receivers'))) { + if ($this->receiverNames && !$input->getArgument('receivers')) { $io->block('Which transports/receivers do you want to consume?', null, 'fg=white;bg=blue', ' ', true); $io->writeln('Choose which receivers you want to consume messages from in order of priority.'); @@ -145,7 +146,7 @@ protected function interact(InputInterface $input, OutputInterface $output) $input->setArgument('receivers', $io->askQuestion($question)); } - if (0 === \count($input->getArgument('receivers'))) { + if (!$input->getArgument('receivers')) { throw new RuntimeException('Please pass at least one receiver.'); } } @@ -174,7 +175,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $stopsWhen = []; - if ($limit = $input->getOption('limit')) { + if (null !== $limit = $input->getOption('limit')) { + if (!is_numeric($limit) || 0 >= $limit) { + throw new InvalidOptionException(sprintf('Option "limit" must be a positive integer, "%s" passed.', $limit)); + } + $stopsWhen[] = "processed {$limit} messages"; $this->eventDispatcher->addSubscriber(new StopWorkerOnMessageLimitListener($limit, $this->logger)); } @@ -189,7 +194,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->eventDispatcher->addSubscriber(new StopWorkerOnMemoryLimitListener($this->convertToBytes($memoryLimit), $this->logger)); } - if (null !== ($timeLimit = $input->getOption('time-limit'))) { + if (null !== $timeLimit = $input->getOption('time-limit')) { + if (!is_numeric($timeLimit) || 0 >= $timeLimit) { + throw new InvalidOptionException(sprintf('Option "time-limit" must be a positive integer, "%s" passed.', $timeLimit)); + } + $stopsWhen[] = "been running for {$timeLimit}s"; $this->eventDispatcher->addSubscriber(new StopWorkerOnTimeLimitListener($timeLimit, $this->logger)); } @@ -197,7 +206,7 @@ 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->success(sprintf('Consuming messages from transport%s "%s".', \count($receivers) > 0 ? 's' : '', implode(', ', $receiverNames))); + $io->success(sprintf('Consuming messages from transport%s "%s".', \count($receivers) > 1 ? 's' : '', implode(', ', $receiverNames))); if ($stopsWhen) { $last = array_pop($stopsWhen); @@ -252,11 +261,11 @@ private function convertToBytes(string $memoryLimit): int switch (substr(rtrim($memoryLimit, 'b'), -1)) { case 't': $max *= 1024; - // no break + // no break case 'g': $max *= 1024; - // no break + // no break case 'm': $max *= 1024; - // no break + // no break case 'k': $max *= 1024; } diff --git a/src/Symfony/Component/Messenger/EventListener/StopWorkerOnTimeLimitListener.php b/src/Symfony/Component/Messenger/EventListener/StopWorkerOnTimeLimitListener.php index d384f4af49f74..47ac5c411c969 100644 --- a/src/Symfony/Component/Messenger/EventListener/StopWorkerOnTimeLimitListener.php +++ b/src/Symfony/Component/Messenger/EventListener/StopWorkerOnTimeLimitListener.php @@ -15,6 +15,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Messenger\Event\WorkerRunningEvent; use Symfony\Component\Messenger\Event\WorkerStartedEvent; +use Symfony\Component\Messenger\Exception\InvalidArgumentException; /** * @author Simon Delicata @@ -30,6 +31,10 @@ public function __construct(int $timeLimitInSeconds, LoggerInterface $logger = n { $this->timeLimitInSeconds = $timeLimitInSeconds; $this->logger = $logger; + + if ($timeLimitInSeconds <= 0) { + throw new InvalidArgumentException('Time limit must be greater than zero.'); + } } public function onWorkerStarted(): void diff --git a/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php b/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php index 537c2845826de..c6f7773e33352 100644 --- a/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/HandleMessageMiddleware.php @@ -57,8 +57,10 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope ]; $exceptions = []; + $alreadyHandled = false; foreach ($this->handlersLocator->getHandlers($envelope) as $handlerDescriptor) { if ($this->messageHasAlreadyBeenHandled($envelope, $handlerDescriptor)) { + $alreadyHandled = true; continue; } @@ -116,7 +118,7 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope } } - if (null === $handler) { + if (null === $handler && !$alreadyHandled) { if (!$this->allowNoHandlers) { throw new NoHandlerForMessageException(sprintf('No handler for message "%s".', $context['class'])); } diff --git a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php index 1bcfeb04a987a..91a191de31498 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Exception\InvalidOptionException; use Symfony\Component\Console\Tester\CommandCompletionTester; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -68,7 +69,7 @@ public function testBasicRun() ]); $tester->assertCommandIsSuccessful(); - $this->assertStringContainsString('[OK] Consuming messages from transports "dummy-receiver"', $tester->getDisplay()); + $this->assertStringContainsString('[OK] Consuming messages from transport "dummy-receiver"', $tester->getDisplay()); } public function testRunWithBusOption() @@ -101,7 +102,7 @@ public function testRunWithBusOption() ]); $tester->assertCommandIsSuccessful(); - $this->assertStringContainsString('[OK] Consuming messages from transports "dummy-receiver"', $tester->getDisplay()); + $this->assertStringContainsString('[OK] Consuming messages from transport "dummy-receiver"', $tester->getDisplay()); } public function provideRunWithResetServicesOption(): iterable @@ -146,7 +147,71 @@ public function testRunWithResetServicesOption(bool $shouldReset) $this->assertEquals($shouldReset, $receiver->hasBeenReset(), '$receiver->reset() should have been called'); $tester->assertCommandIsSuccessful(); - $this->assertStringContainsString('[OK] Consuming messages from transports "dummy-receiver"', $tester->getDisplay()); + $this->assertStringContainsString('[OK] Consuming messages from transport "dummy-receiver"', $tester->getDisplay()); + } + + /** + * @dataProvider getInvalidOptions + */ + public function testRunWithInvalidOption(string $option, string $value, string $expectedMessage) + { + $receiverLocator = $this->createMock(ContainerInterface::class); + $receiverLocator->expects($this->once())->method('has')->with('dummy-receiver')->willReturn(true); + + $busLocator = $this->createMock(ContainerInterface::class); + + $command = new ConsumeMessagesCommand(new RoutableMessageBus($busLocator), $receiverLocator, new EventDispatcher()); + + $application = new Application(); + $application->add($command); + $tester = new CommandTester($application->get('messenger:consume')); + + $this->expectException(InvalidOptionException::class); + $this->expectExceptionMessage($expectedMessage); + $tester->execute([ + 'receivers' => ['dummy-receiver'], + $option => $value, + ]); + } + + public function getInvalidOptions() + { + yield 'Zero message limit' => ['--limit', '0', 'Option "limit" must be a positive integer, "0" passed.']; + yield 'Non-numeric message limit' => ['--limit', 'whatever', 'Option "limit" must be a positive integer, "whatever" passed.']; + + yield 'Zero second time limit' => ['--time-limit', '0', 'Option "time-limit" must be a positive integer, "0" passed.']; + yield 'Non-numeric time limit' => ['--time-limit', 'whatever', 'Option "time-limit" must be a positive integer, "whatever" passed.']; + } + + public function testRunWithTimeLimit() + { + $envelope = new Envelope(new \stdClass(), [new BusNameStamp('dummy-bus')]); + + $receiver = $this->createMock(ReceiverInterface::class); + $receiver->method('get')->willReturn([$envelope]); + + $receiverLocator = $this->createMock(ContainerInterface::class); + $receiverLocator->method('has')->with('dummy-receiver')->willReturn(true); + $receiverLocator->method('get')->with('dummy-receiver')->willReturn($receiver); + + $bus = $this->createMock(MessageBusInterface::class); + + $busLocator = $this->createMock(ContainerInterface::class); + $busLocator->method('has')->with('dummy-bus')->willReturn(true); + $busLocator->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'], + '--time-limit' => 1, + ]); + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertStringContainsString('[OK] Consuming messages from transport "dummy-receiver"', $tester->getDisplay()); } /** diff --git a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php index 503800e1cd6e1..34b84596c67d7 100644 --- a/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php +++ b/src/Symfony/Component/Messenger/Tests/Middleware/HandleMessageMiddlewareTest.php @@ -129,6 +129,24 @@ public function testThrowsNoHandlerException() $middleware->handle(new Envelope(new DummyMessage('Hey')), new StackMiddleware()); } + public function testMessageAlreadyHandled() + { + $handler = $this->createPartialMock(HandleMessageMiddlewareTestCallable::class, ['__invoke']); + + $middleware = new HandleMessageMiddleware(new HandlersLocator([ + DummyMessage::class => [$handler], + ])); + + $envelope = new Envelope(new DummyMessage('Hey')); + + $envelope = $middleware->handle($envelope, $this->getStackMock()); + $handledStamp = $envelope->all(HandledStamp::class); + + $envelope = $middleware->handle($envelope, $this->getStackMock()); + + $this->assertSame($envelope->all(HandledStamp::class), $handledStamp); + } + public function testAllowNoHandlers() { $middleware = new HandleMessageMiddleware(new HandlersLocator([]), true); diff --git a/src/Symfony/Component/Mime/Tests/Crypto/SMimeEncryptorTest.php b/src/Symfony/Component/Mime/Tests/Crypto/SMimeEncrypterTest.php similarity index 98% rename from src/Symfony/Component/Mime/Tests/Crypto/SMimeEncryptorTest.php rename to src/Symfony/Component/Mime/Tests/Crypto/SMimeEncrypterTest.php index 92df05e391c7e..75f3f3c4eb866 100644 --- a/src/Symfony/Component/Mime/Tests/Crypto/SMimeEncryptorTest.php +++ b/src/Symfony/Component/Mime/Tests/Crypto/SMimeEncrypterTest.php @@ -19,7 +19,7 @@ /** * @requires extension openssl */ -class SMimeEncryptorTest extends SMimeTestCase +class SMimeEncrypterTest extends SMimeTestCase { public function testEncryptMessage() { diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php index f922cb985098d..6c0212fa40095 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php @@ -85,7 +85,7 @@ protected function doSend(MessageInterface $message): SentMessage 'apikey' => $this->apiKey, 'message' => $message->getSubject(), 'from' => $this->from, - 'test' => $this->testMode ? 0 : 1, + 'test' => $this->testMode ? 1 : 0, 'to' => $message->getPhone(), ], ]); diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php index 28e96cc491877..76cb97a39cc76 100644 --- a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php @@ -11,12 +11,14 @@ namespace Symfony\Component\Notifier\Bridge\SmsBiuras\Tests; +use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\Notifier\Bridge\SmsBiuras\SmsBiurasTransport; use Symfony\Component\Notifier\Message\ChatMessage; use Symfony\Component\Notifier\Message\MessageInterface; use Symfony\Component\Notifier\Message\SmsMessage; use Symfony\Component\Notifier\Test\TransportTestCase; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; final class SmsBiurasTransportTest extends TransportTestCase { @@ -40,4 +42,48 @@ public function unsupportedMessagesProvider(): iterable yield [new ChatMessage('Hello!')]; yield [$this->createMock(MessageInterface::class)]; } + + /** + * @dataProvider provideTestMode() + */ + public function testTestMode(int $expected, bool $testMode) + { + $message = new SmsMessage('+37012345678', 'Hello World!'); + + $response = $this->createMock(ResponseInterface::class); + $response->expects($this->atLeast(1)) + ->method('getStatusCode') + ->willReturn(200); + $response->expects($this->atLeast(1)) + ->method('getContent') + ->willReturn('OK: 519545'); + + $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ($response, $message, $expected): ResponseInterface { + $this->assertSame('GET', $method); + $this->assertSame(sprintf( + 'https://savitarna.smsbiuras.lt/api?uid=uid&apikey=api_key&message=%s&from=from&test=%s&to=%s', + rawurlencode($message->getSubject()), + $expected, + rawurlencode($message->getPhone()) + ), $url); + $this->assertSame($expected, $options['query']['test']); + + $this->assertSame(200, $response->getStatusCode()); + $this->assertSame('OK: 519545', $response->getContent()); + + return $response; + }); + + $transport = new SmsBiurasTransport('uid', 'api_key', 'from', $testMode, $client); + + $sentMessage = $transport->send($message); + + $this->assertSame('519545', $sentMessage->getMessageId()); + } + + public static function provideTestMode(): iterable + { + yield [1, true]; + yield [0, false]; + } } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php index 00dea793e7169..8d956a1103fc0 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Fixtures/Dummy.php @@ -84,7 +84,7 @@ class Dummy extends ParentDummy public $h; /** - * @var ?string|int + * @var string|int|null */ public $i; diff --git a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php index a9c7c1b478f45..6cf083bb4ee86 100644 --- a/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php +++ b/src/Symfony/Component/PropertyInfo/Util/PhpDocTypeHelper.php @@ -12,6 +12,7 @@ namespace Symfony\Component\PropertyInfo\Util; use phpDocumentor\Reflection\PseudoType; +use phpDocumentor\Reflection\PseudoTypes\ConstExpression; use phpDocumentor\Reflection\PseudoTypes\List_; use phpDocumentor\Reflection\Type as DocType; use phpDocumentor\Reflection\Types\Array_; @@ -42,6 +43,11 @@ final class PhpDocTypeHelper */ public function getTypes(DocType $varType): array { + if ($varType instanceof ConstExpression) { + // It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment + return []; + } + $types = []; $nullable = false; @@ -67,6 +73,11 @@ public function getTypes(DocType $varType): array for ($typeIndex = 0; $varType->has($typeIndex); ++$typeIndex) { $type = $varType->get($typeIndex); + if ($type instanceof ConstExpression) { + // It's safer to fall back to other extractors here, as resolving const types correctly is not easy at the moment + return []; + } + // If null is present, all types are nullable if ($type instanceof Null_) { $nullable = true; diff --git a/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php b/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php index c28b42b289c68..acd63c12083b7 100644 --- a/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php +++ b/src/Symfony/Component/Translation/Bridge/Lokalise/LokaliseProvider.php @@ -151,6 +151,7 @@ private function exportFiles(array $locales, array $domains): array 'filter_langs' => array_values($locales), 'filter_filenames' => array_map($this->getLokaliseFilenameFromDomain(...), $domains), 'export_empty_as' => 'skip', + 'replace_breaks' => false, ], ]); diff --git a/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/LokaliseProviderTest.php b/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/LokaliseProviderTest.php index 5df996e94327b..0c3b7d511aa43 100644 --- a/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/LokaliseProviderTest.php +++ b/src/Symfony/Component/Translation/Bridge/Lokalise/Tests/LokaliseProviderTest.php @@ -562,6 +562,7 @@ public function testReadForOneLocaleAndOneDomain(string $locale, string $domain, 'filter_langs' => [$locale], 'filter_filenames' => [$domain.'.xliff'], 'export_empty_as' => 'skip', + 'replace_breaks' => false, ]); $this->assertSame('POST', $method); diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 1a862b7739585..b085b28f041d0 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -77,7 +77,7 @@ public static function parse(string $value = null, int $flags = 0, array &$refer ++$i; break; default: - $result = self::parseScalar($value, $flags, null, $i, null === $tag, $references); + $result = self::parseScalar($value, $flags, null, $i, true, $references); } // some comments are allowed at the end @@ -639,7 +639,6 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer } return octdec($value); - // Optimize for returning strings. case \in_array($scalar[0], ['+', '-', '.'], true) || is_numeric($scalar[0]): if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar)) { $scalar = str_replace('_', '', $scalar); diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index 5ac9b81661e65..eedd3367d4e77 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -473,8 +473,6 @@ public function testDumpingTaggedValueSequenceWithInlinedTagValues() YAML; $this->assertSame($expected, $yaml); - // @todo Fix the parser, preserve numbers. - $data[2] = new TaggedValue('number', '5'); $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS)); } @@ -503,8 +501,6 @@ public function testDumpingTaggedValueMapRespectsInlineLevel() YAML; $this->assertSame($expected, $yaml); - // @todo Fix the parser, preserve numbers. - $data['count'] = new TaggedValue('number', '5'); $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS)); } @@ -558,9 +554,6 @@ public function testDumpingNotInlinedNullTaggedValue() YAML; $this->assertSame($expected, $this->dumper->dump($data, 2)); - - // @todo Fix the parser, don't stringify null. - $data['foo'] = new TaggedValue('bar', 'null'); $this->assertSameData($data, $this->parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS | Yaml::PARSE_CONSTANT)); } @@ -677,7 +670,7 @@ public function testDumpMultiLineStringAsScalarBlock() nested_inlined_multi_line_string: { inlined_multi_line: "foo\nbar\r\nempty line:\n\nbaz" } YAML -); + ); $this->assertSame($expected, $yml); $this->assertSame($data, $this->parser->parse($yml)); } diff --git a/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml b/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml index b5f41a2b1eccc..2acc4998e207e 100644 --- a/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml +++ b/src/Symfony/Component/Yaml/Tests/Fixtures/YtsSpecificationExamples.yml @@ -913,7 +913,7 @@ yaml: | no int: ! 12 string: !!str 12 php: | - [ 'integer' => 12, 'no int' => '12', 'string' => '12' ] + [ 'integer' => 12, 'no int' => 12, 'string' => '12' ] --- test: Private types todo: true diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 1749e2d49a307..75a07755386ff 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -741,6 +741,24 @@ public function testTagWithEmptyValueInMapping() $this->assertSame('', $value['foo']->getValue()); } + public function testTagWithQuotedInteger() + { + $value = Inline::parse('!number "5"', Yaml::PARSE_CUSTOM_TAGS); + + $this->assertInstanceOf(TaggedValue::class, $value); + $this->assertSame('number', $value->getTag()); + $this->assertSame('5', $value->getValue()); + } + + public function testTagWithUnquotedInteger() + { + $value = Inline::parse('!number 5', Yaml::PARSE_CUSTOM_TAGS); + + $this->assertInstanceOf(TaggedValue::class, $value); + $this->assertSame('number', $value->getTag()); + $this->assertSame(5, $value->getValue()); + } + public function testUnfinishedInlineMap() { $this->expectException(ParseException::class); diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 228c2f2ee9c69..0941075b10b57 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -54,8 +54,7 @@ public function testTaggedValueTopLevelNumber() { $yml = '!number 5'; $data = $this->parser->parse($yml, Yaml::PARSE_CUSTOM_TAGS); - // @todo Preserve the number, don't turn into string. - $expected = new TaggedValue('number', '5'); + $expected = new TaggedValue('number', 5); $this->assertSameData($expected, $data); } @@ -63,9 +62,8 @@ public function testTaggedValueTopLevelNull() { $yml = '!tag null'; $data = $this->parser->parse($yml, Yaml::PARSE_CUSTOM_TAGS); - // @todo Preserve literal null, don't turn into string. - $expected = new TaggedValue('tag', 'null'); - $this->assertSameData($expected, $data); + + $this->assertSameData(new TaggedValue('tag', null), $data); } public function testTaggedValueTopLevelString() @@ -1555,8 +1553,6 @@ public function testParseDateAsMappingValue() } /** - * @param $lineNumber - * @param $yaml * @dataProvider parserThrowsExceptionWithCorrectLineNumberProvider */ public function testParserThrowsExceptionWithCorrectLineNumber($lineNumber, $yaml) @@ -2310,7 +2306,7 @@ public function taggedValuesProvider() public function testNonSpecificTagSupport() { - $this->assertSame('12', $this->parser->parse('! 12')); + $this->assertSame(12, $this->parser->parse('! 12')); } public function testCustomTagsDisabled() pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy