8000 Merge branch '7.2' into 7.3 · symfony/symfony@3cb2479 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3cb2479

Browse files
Merge branch '7.2' into 7.3
* 7.2: [WebProfilerBundle] Fix tests [Cache] Tests for Redis Replication with cache [BrowserKit] Fix submitting forms with empty file fields [Notifier] [BlueSky] Change the value returned as the message ID [WebProfilerBundle] Fix interception for non conventional redirects [DependencyInjection] Do not preload functions [DependencyInjection] Fix cloned lazy services not sharing their dependencies when dumped with PhpDumper [Form][FrameworkBundle] Use auto-configuration to make the default CSRF token id apply only to the app; not to bundles [HttpClient] Fix activity tracking leading to negative timeout errors [Serializer] Handle default context in named Serializer [Security] Return null instead of empty username to fix deprecation notice [DependencyInjection] Fix env default processor with scalar node
2 parents 09ba274 + 82a12a4 commit 3cb2479

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+596
-98
lines changed

.github/workflows/integration-tests.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,17 @@ jobs:
8181
REDIS_MASTER_HOST: redis
8282
REDIS_MASTER_SET: redis_sentinel
8383
REDIS_SENTINEL_QUORUM: 1
84+
redis-primary:
85+
image: redis:latest
86+
hostname: redis-primary
87+
ports:
88+
- 16381:6379
89+
90+
redis-replica:
91+
image: redis:latest
92+
ports:
93+
- 16382:6379
94+
command: redis-server --slaveof redis-primary 6379
8495
memcached:
8596
image: memcached:1.6.5
8697
ports:
@@ -239,6 +250,7 @@ jobs:
239250
REDIS_CLUSTER_HOSTS: 'localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005'
240251
REDIS_SENTINEL_HOSTS: 'unreachable-host:26379 localhost:26379 localhost:26379'
241252
REDIS_SENTINEL_SERVICE: redis_sentinel
253+
REDIS_REPLICATION_HOSTS: 'localhost:16381 localhost:16382'
242254
MESSENGER_REDIS_DSN: redis://127.0.0.1:7006/messages
243255
MESSENGER_AMQP_DSN: amqp://localhost/%2f/messages
244256
MESSENGER_SQS_DSN: "sqs://localhost:4566/messages?sslmode=disable&poll_timeout=0.01"

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ public function load(array $configs, ContainerBuilder $container): void
644644
$container->registerForAutoconfiguration(DataCollectorInterface::class)
645645
->addTag('data_collector');
646646
$container->registerForAutoconfiguration(FormTypeInterface::class)
647-
->addTag('form.type');
647+
->addTag('form.type', ['csrf_token_id' => '%.form.type_extension.csrf.token_id%']);
648648
$container->registerForAutoconfiguration(FormTypeGuesserInterface::class)
649649
->addTag('form.type_guesser');
650650
$container->registerForAutoconfiguration(FormTypeExtensionInterface::class)
@@ -815,9 +815,7 @@ private function registerFormConfiguration(array $config, ContainerBuilder $cont
815815
$container->setParameter('form.type_extension.csrf.enabled', true);
816816
$container->setParameter('form.type_extension.csrf.field_name', $config['form']['csrf_protection']['field_name']);
817817
$container->setParameter('form.type_extension.csrf.field_attr', $config['form']['csrf_protection']['field_attr']);
818-
819-
$container->getDefinition('form.type_extension.csrf')
820-
->replaceArgument(7, $config['form']['csrf_protection']['token_id']);
818+
$container->setParameter('.form.type_extension.csrf.token_id', $config['form']['csrf_protection']['token_id']);
821819
} else {
822820
$container->setParameter('form.type_extension.csrf.enabled', false);
823821
}

src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
param('validator.translation_domain'),
2525
service('form.server_params'),
2626
param('form.type_extension.csrf.field_attr'),
27-
abstract_arg('framework.form.csrf_protection.token_id'),
27+
param('.form.type_extension.csrf.token_id'),
2828
])
2929
->tag('form.type_extension')
3030
;

src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public function onKernelResponse(ResponseEvent $event): void
104104
return;
105105
}
106106

107-
if ($response->headers->has('X-Debug-Token') && $response->isRedirect() && $this->interceptRedirects && 'html' === $request->getRequestFormat()) {
107+
if ($response->headers->has('X-Debug-Token') && $response->isRedirect() && $this->interceptRedirects && 'html' === $request->getRequestFormat() && $response->headers->has('Location')) {
108108
if ($request->hasSession() && ($session = $request->getSession())->isStarted() && $session->getFlashBag() instanceof AutoExpireFlashBag) {
109109
// keep current flashes for one more request if using AutoExpireFlashBag
110110
$session->getFlashBag()->setAll($session->getFlashBag()->peekAll());

src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public static function getInjectToolbarTests()
6363
public function testHtmlRedirectionIsIntercepted($statusCode)
6464
{
6565
$response = new Response('Some content', $statusCode);
66+
$response->headers->set('Location', 'https://example.com/');
6667
$response->headers->set('X-Debug-Token', 'xxxxxxxx');
6768
$event = new ResponseEvent($this->createMock(Kernel::class), new Request(), HttpKernelInterface::MAIN_REQUEST, $response);
6869

@@ -76,6 +77,7 @@ public function testHtmlRedirectionIsIntercepted($statusCode)
7677
public function testNonHtmlRedirectionIsNotIntercepted()
7778
{
7879
$response = new Response('Some content', '301');
80+
$response->headers->set('Location', 'https://example.com/');
7981
$response->headers->set('X-Debug-Token', 'xxxxxxxx');
8082
$event = new ResponseEvent($this->createMock(Kernel::class), new Request([], [], ['_format' => 'json']), HttpKernelInterface::MAIN_REQUEST, $response);
8183

@@ -139,6 +141,7 @@ public function testToolbarIsNotInjectedOnContentDispositionAttachment()
139141
public function testToolbarIsNotInjectedOnRedirection($statusCode)
140142
{
141143
$response = new Response('<html><head></head><body></body></html>', $statusCode);
144+
$response->headers->set('Location', 'https://example.com/');
142145
$response->headers->set('X-Debug-Token', 'xxxxxxxx');
143146
$event = new ResponseEvent($this->createMock(Kernel::class), new Request(), HttpKernelInterface::MAIN_REQUEST, $response);
144147

src/Symfony/Component/BrowserKit/HttpBrowser.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,15 @@ private function getUploadedFiles(array $files): array
145145
}
146146
if (!isset($file['tmp_name'])) {
147147
$uploadedFiles[$name] = $this->getUploadedFiles($file);
148+
continue;
148149
}
149-
if (isset($file['tmp_name'])) {
150-
$uploadedFiles[$name] = DataPart::fromPath($file['tmp_name'], $file['name']);
150+
151+
if ('' === $file['tmp_name']) {
152+
$uploadedFiles[$name] = new DataPart('', '');
153+
continue;
151154
}
155+
156+
$uploadedFiles[$name] = DataPart::fromPath($file['tmp_name'], $file['name']);
152157
}
153158

154159
return $uploadedFiles;

src/Symfony/Component/BrowserKit/Tests/HttpBrowserTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use Symfony\Component\BrowserKit\CookieJar;
1515
use Symfony\Component\BrowserKit\History;
1616
use Symfony\Component\BrowserKit\HttpBrowser;
17+
use Symfony\Component\HttpClient\MockHttpClient;
18+
use Symfony\Component\HttpClient\Response\MockResponse;
1719
use Symfony\Contracts\HttpClient\HttpClientInterface;
1820
use Symfony\Contracts\HttpClient\ResponseInterface;
1921

@@ -208,6 +210,37 @@ public static function forwardSlashesRequestPathProvider()
208210
];
209211
}
210212

213+
public function testEmptyUpload()
214+
{
215+
$client = new MockHttpClient(function ($method, $url, $options) {
216+
$this->assertSame('POST', $method);
217+
$this->assertSame('http://localhost/', $url);
218+
$this->assertStringStartsWith('Content-Type: multipart/form-data; boundary=', $options['normalized_headers']['content-type'][0]);
219+
220+
$body = '';
221+
while ('' !== $data = $options['body'](1024)) {
222+
$body .= $data;
223+
}
224+
225+
$expected = <<<EOTXT
226+
--%s\r
227+
Content-Type: application/octet-stream\r
228+
Content-Transfer-Encoding: 8bit\r
229+
Content-Disposition: form-data; name="file"; filename=""\r
230+
\r
231+
\r
232+
--%s--\r
233+
234+
EOTXT;
235+
$this->assertStringMatchesFormat($expected, $body);
236+
237+
return new MockResponse();
238+
});
239+
240+
$browser = new HttpBrowser($client);
241+
$browser->request('POST', '/', [], ['file' => ['tmp_name' => '', 'name' => 'file']]);
242+
}
243+
211244
private function uploadFile(string $data): string
212245
{
213246
$path = tempnam(sys_get_temp_dir(), 'http');
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Tests\Adapter;
13+
14+
use Symfony\Component\Cache\Adapter\RedisAdapter;
15+
16+
/**
17+
* @group integration
18+
*/
19+
class PredisRedisReplicationAdapterTest extends AbstractRedisAdapterTestCase
20+
{
21+
public static function setUpBeforeClass(): void
22+
{
23+
if (!$hosts = getenv('REDIS_REPLICATION_HOSTS')) {
24+
self::markTestSkipped('REDIS_REPLICATION_HOSTS env var is not defined.');
25+
}
26+
27+
self::$redis = RedisAdapter::createConnection('redis:?host['.str_replace(' ', ']&host[', $hosts).'][alias]=master', ['class' => \Predis\Client::class, 'prefix' => 'prefix_']);
28+
}
29+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Tests\Adapter;
13+
14+
/**
15+
* @group integration
16+
*/
17+
class PredisReplicationAdapterTest extends AbstractRedisAdapterTestCase
18+
{
19+
public static function setUpBeforeClass(): void
20+
{
21+
parent::setUpBeforeClass();
22+
self::$redis = new \Predis\Client(array_combine(['host', 'port'], explode(':', getenv('REDIS_HOST')) + [1 => 6379]), ['prefix' => 'prefix_']);
23+
}
24+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Tests\Adapter;
13+
14+
use Psr\Cache\CacheItemPoolInterface;
15+
use Symfony\Component\Cache\Adapter\RedisTagAwareAdapter;
16+
17+
/**
18+
* @group integration
19+
*/
20+
class PredisTagAwareReplicationAdapterTest extends PredisReplicationAdapterTest
21+
{
22+
use TagAwareTestTrait;
23+
24+
protected function setUp(): void
25+
{
26+
parent::setUp();
27+
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
28+
}
29+
30+
public function createCachePool(int $defaultLifetime = 0, ?string $testMethod = null): CacheItemPoolInterface
31+
{
32+
$this->assertInstanceOf(\Predis\Client::class, self::$redis);
33+
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
34+
35+
return $adapter;
36+
}
37+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Tests\Adapter;
13+
14+
use Psr\Cache\CacheItemPoolInterface;
15+
use Symfony\Component\Cache\Adapter\AbstractAdapter;
16+
use Symfony\Component\Cache\Adapter\RedisAdapter;
17+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
18+
use Symfony\Component\Cache\Traits\RedisClusterProxy;
19+
20+
/**
21+
* @group integration
22+
*/
23+
class RedisReplicationAdapterTest extends AbstractRedisAdapterTestCase
24+
{
25+
public static function setUpBeforeClass(): void
26+
{
27+
if (!$hosts = getenv('REDIS_REPLICATION_HOSTS')) {
28+
self::markTestSkipped('REDIS_REPLICATION_HOSTS env var is not defined.');
29+
}
30+
31+
self::$redis = AbstractAdapter::createConnection('redis:?host['.str_replace(' ', ']&host[', $hosts).'][alias]=master', ['lazy' => true]);
32+
self::$redis->setOption(\Redis::OPT_PREFIX, 'prefix_');
33+
}
34+
35+
public function createCachePool(int $defaultLifetime = 0, ?string $testMethod = null): CacheItemPoolInterface
36+
{
37+
if ('testClearWithPrefix' === $testMethod && \defined('Redis::SCAN_PREFIX')) {
38+
self::$redis->setOption(\Redis::OPT_SCAN, \Redis::SCAN_PREFIX);
39+
}
40+
41+
$this->assertInstanceOf(RedisClusterProxy::class, self::$redis);
42+
$adapter = new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
43+
44+
return $adapter;
45+
}
46+
47+
/**
48+
* @dataProvider provideFailedCreateConnection
49+
*/
50+
public function testFailedCreateConnection(string $dsn)
51+
{
52+
$this->expectException(InvalidArgumentException::class);
53+
$this->expectExceptionMessage('Redis connection ');
54+
RedisAdapter::createConnection($dsn);
55+
}
56+
57+
public static function provideFailedCreateConnection(): array
58+
{
59+
return [
60+
['redis://localhost:1234'],
61+
['redis://foo@localhost?role=master'],
62+
['redis://localhost/123?role=master'],
63+
];
64+
}
65+
}

src/Symfony/Component/Cache/Traits/RedisTrait.php

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Predis\Connection\Aggregate\ReplicationInterface;
1818
use Predis\Connection\Cluster\ClusterInterface as Predis2ClusterInterface;
1919
use Predis\Connection\Cluster\RedisCluster as Predis2RedisCluster;
20+
use Predis\Connection\Replication\ReplicationInterface as Predis2ReplicationInterface;
2021
use Predis\Response\ErrorInterface;
2122
use Predis\Response\Status;
2223
use Relay\Relay;
@@ -479,9 +480,16 @@ protected function doClear(string $namespace): bool
479480
$cleared = true;
480481
$hosts = $this->getHosts();
481482
$host = reset($hosts);
482-
if ($host instanceof \Predis\Client && $host->getConnection() instanceof ReplicationInterface) {
483-
// Predis supports info command only on the master in replication environments
484-
$hosts = [$host->getClientFor('master')];
483+
if ($host instanceof \Predis\Client) {
484+
$connection = $host->getConnection();
485+
486+
if ($connection instanceof ReplicationInterface) {
487+
$hosts = [$host->getClientFor('master')];
488+
} elseif ($connection instanceof Predis2ReplicationInterface) {
489+
$connection->switchToMaster();
490+
491+
$hosts = [$host];
492+
}
485493
}
486494

487495
foreach ($hosts as $host) {

src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ private function isInlineableDefinition(string $id, Definition $definition): boo
223223
return false;
224224
}
225225

226-
return $this->container->getDefinition($srcId)->isShared();
226+
$srcDefinition = $this->container->getDefinition($srcId);
227+
228+
return $srcDefinition->isShared() && !$srcDefinition->isLazy();
227229
}
228230
}

0 commit comments

Comments
 (0)
0