8000 Merge branch '6.4' into 7.1 · symfony/symfony@ffd60b6 · GitHub
[go: up one dir, main page]

Skip to content
8000

Commit ffd60b6

Browse files
committed
Merge branch '6.4' into 7.1
* 6.4: [AssetMapper] Fix `JavaScriptImportPathCompiler` regex for non-latin characters Definition::$class may not be class-string require Cache component versions compatible with Redis 6.1 [Twitter][Notifier] Fix post INIT upload [Messenger][RateLimiter] fix additional message handled when using a rate limiter [Serializer] fixed object normalizer for a class with `cancel` method Fix bucket size reduce when previously created with bigger size
2 parents e713ac2 + 8dabfd7 commit ffd60b6

File tree

17 files changed

+263
-34
lines changed

17 files changed

+263
-34
lines changed

src/Symfony/Component/AssetMapper/Compiler/JavaScriptImportPathCompiler.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ final class JavaScriptImportPathCompiler implements AssetCompilerInterface
5050
)
5151
\s*[\'"`](\.\/[^\'"`\n]++|(\.\.\/)*+[^\'"`\n]++)[\'"`]\s*[;\)]
5252
?
53-
/mx';
53+
/mxu';
5454

5555
public function __construct(
5656
private readonly ImportMapConfigReader $importMapConfigReader,

src/Symfony/Component/AssetMapper/Tests/Compiler/JavaScriptImportPathCompilerTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,22 @@ public static function provideCompileTests(): iterable
172172
'expectedJavaScriptImports' => ['/assets/other.js' => ['lazy' => false, 'asset' => 'other.js', 'add' => true]],
173173
];
174174

175+
yield 'static_named_import_with_unicode_character' => [
176+
'input' => 'import { ɵmyFunction } from "./other.js";',
177+
'expectedJavaScriptImports' => ['/assets/other.js' => ['lazy' => false, 'asset' => 'other.js', 'add' => true]],
178+
];
179+
180+
yield 'static_multiple_named_imports' => [
181+
'input' => <<<EOF
182+
import {
183+
myFunction,
184+
helperFunction
185+
} from "./other.js";
186+
EOF
187+
,
188+
'expectedJavaScriptImports' => ['/assets/other.js' => ['lazy' => false, 'asset' => 'other.js', 'add' => true]],
189+
];
190+
175191
yield 'static_import_everything' => [
176192
'input' => 'import * as myModule from "./other.js";',
177193
'expectedJavaScriptImports' => ['/assets/other.js' => ['lazy' => false, 'asset' => 'other.js', 'add' => true]],

src/Symfony/Component/DependencyInjection/Definition.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,6 @@ public function setClass(?string $class): static
180180

181181
/**
182182
* Gets the service class.
183-
*
184-
* @return class-string|null
185183
*/
186184
public function getClass(): ?string
187185
{

src/Symfony/Component/HttpFoundation/composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"require-dev": {
2424
"doctrine/dbal": "^3.6|^4",
2525
"predis/predis": "^1.1|^2.0",
26-
"symfony/cache": "^6.4|^7.0",
26+
"symfony/cache": "^6.4.12|^7.1.5",
2727
"symfony/dependency-injection": "^6.4|^7.0",
2828
"symfony/http-kernel": "^6.4|^7.0",
2929
"symfony/mime": "^6.4|^7.0",
@@ -32,7 +32,7 @@
3232
},
3333
"conflict": {
3434
"doctrine/dbal": "<3.6",
35-
"symfony/cache": "<6.4"
35+
"symfony/cache": "<6.4.12|>=7.0,<7.1.5"
3636
},
3737
"autoload": {
3838
"psr-4": { "Symfony\\Component\\HttpFoundation\\": "" },

src/Symfony/Component/Messenger/Tests/WorkerTest.php

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Psr\EventDispatcher\EventDispatcherInterface;
1616
use Psr\Log\LoggerInterface;
17+
use Symfony\Bridge\PhpUnit\ClockMock;
1718
use Symfony\Component\Clock\MockClock;
1819
use Symfony\Component\EventDispatcher\EventDispatcher;
1920
use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter;
@@ -47,8 +48,8 @@
4748
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
4849
use Symfony\Component\Messenger\Worker;
4950
use Symfony\Component\RateLimiter\RateLimiterFactory;
51+
use Symfony\Component\RateLimiter\Reservation;
5052
use Symfony\Component\RateLimiter\Storage\InMemoryStorage;
51-
use Symfony\Contracts\Service\ResetInterface;
5253

5354
/**
5455
* @group time-sensitive
@@ -73,7 +74,7 @@ public function testWorkerDispatchTheReceivedMessage()
7374
return $envelopes[] = $envelope;
7475
});
7576

76-
$dispatcher = new class() implements EventDispatcherInterface {
77+
$dispatcher = new class implements EventDispatcherInterface {
7778
private StopWorkerOnMessageLimitListener $listener;
7879

7980
public function __construct()
@@ -403,7 +404,7 @@ public function testWorkerLimitQueuesUnsupported()
403404

404405
$worker = new Worker(['transport1' => $receiver1, 'transport2' => $receiver2], $bus, clock: new MockClock());
405406
$this->expectException(RuntimeException::class);
406-
$this->expectExceptionMessage(sprintf('Receiver for "transport2" does not implement "%s".', QueueReceiverInterface::class));
407+
$this->expectExceptionMessage(\sprintf('Receiver for "transport2" does not implement "%s".', QueueReceiverInterface::class));
407408
$worker->run(['queues' => ['foo']]);
408409
}
409410

@@ -418,7 +419,7 @@ public function testWorkerMessageReceivedEventMutability()
418419
$eventDispatcher = new EventDispatcher();
419420
$eventDispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(1));
420421

421-
$stamp = new class() implements StampInterface {
422+
$stamp = new class implements StampInterface {
422423
};
423424
$listener = function (WorkerMessageReceivedEvent $event) use ($stamp) {
424425
$event->addStamps($stamp);
@@ -438,21 +439,21 @@ public function testWorkerRateLimitMessages()
438439
$envelope = [
439440
new Envelope(new DummyMessage('message1')),
440441
new Envelope(new DummyMessage('message2')),
442+
new Envelope(new DummyMessage('message3')),
443+
new Envelope(new DummyMessage('message4')),
441444
];
442445
$receiver = new DummyReceiver([$envelope]);
443446

444447
$bus = $this->createMock(MessageBusInterface::class);
445448
$bus->method('dispatch')->willReturnArgument(0);
446449

447450
$eventDispatcher = new EventDispatcher();
448-
$eventDispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(2));
451+
$eventDispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(4));
449452

450453
$rateLimitCount = 0;
451-
$listener = function (WorkerRateLimitedEvent $event) use (&$rateLimitCount) {
454+
$eventDispatcher->addListener(WorkerRateLimitedEvent::class, static function () use (&$rateLimitCount) {
452455
++$rateLimitCount;
453-
$event->getLimiter()->reset(); // Reset limiter to continue test
454-
};
455-
$eventDispatcher->addListener(WorkerRateLimitedEvent::class, $listener);
456+
});
456457

457458
$rateLimitFactory = new RateLimiterFactory([
458459
'id' => 'bus',
@@ -461,11 +462,14 @@ public function testWorkerRateLimitMessages()
461462
'interval' => '1 minute',
462463
], new InMemoryStorage());
463464

465+
ClockMock::register(Reservation::class);
466+
ClockMock::register(InMemoryStorage::class);
467+
464468
$worker = new Worker(['bus' => $receiver], $bus, $eventDispatcher, null, ['bus' => $rateLimitFactory], new MockClock());
465469
$worker->run();
466470

467-
$this->assertCount(2, $receiver->getAcknowledgedEnvelopes());
468-
$this->assertEquals(1, $rateLimitCount);
471+
$this->assertSame(4, $receiver->getAcknowledgeCount());
472+
$this->assertSame(3, $rateLimitCount);
469473
}
470474

471475
public function testWorkerShouldLogOnStop()

src/Symfony/Component/Messenger/Worker.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function run(array $options = []): void
8787
// if queue names are specified, all receivers must implement the QueueReceiverInterface
8888
foreach ($this->receivers as $transportName => $receiver) {
8989
if (!$receiver instanceof QueueReceiverInterface) {
90-
throw new RuntimeException(sprintf('Receiver for "%s" does not implement "%s".', $transportName, QueueReceiverInterface::class));
90+
throw new RuntimeException(\sprintf('Receiver for "%s" does not implement "%s".', $transportName, QueueReceiverInterface::class));
9191
}
9292
}
9393
}
@@ -244,6 +244,7 @@ private function rateLimit(string $transportName): void
244244

245245
$this->eventDispatcher?->dispatch(new WorkerRateLimitedEvent($rateLimiter, $transportName));
246246
$rateLimiter->reserve()->wait();
247+
$rateLimiter->consume();
247248
}
248249

249250
private function flush(bool $force): bool

src/Symfony/Component/Notifier/Bridge/Twitter/Tests/TwitterTransportTest.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ public function testTweetImage()
6666
$transport = $this->createTransport(new MockHttpClient((function () {
6767
yield function (string $method, string $url, array $options) {
6868
$this->assertSame('POST', $method);
69-
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=185&media_type=image/gif&media_category=tweet_image', $url);
69+
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json', $url);
70+
$this->assertArrayHasKey('body', $options);
71+
$this->assertSame($options['body'], 'command=INIT&total_bytes=185&media_type=image%2Fgif&media_category=tweet_image');
7072
$this->assertArrayHasKey('authorization', $options['normalized_headers']);
7173

7274
return new MockResponse('{"media_id_string":"gif123"}');
@@ -127,15 +129,19 @@ public function testTweetVideo()
127129
$transport = $this->createTransport(new MockHttpClient((function () {
128130
yield function (string $method, string $url, array $options) {
129131
$this->assertSame('POST', $method);
130-
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=185&media_type=image/gif&media_category=tweet_video', $url);
132+
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json', $url);
133+
$this->assertArrayHasKey('body', $options);
134+
$this->assertSame($options['body'], 'command=INIT&total_bytes=185&media_type=image%2Fgif&media_category=tweet_video');
131135
$this->assertArrayHasKey('authorization', $options['normalized_headers']);
132136

133137
return new MockResponse('{"media_id_string":"gif123"}');
134138
};
135139

136140
yield function (string $method, string $url, array $options) {
137141
$this->assertSame('POST', $method);
138-
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=185&media_type=image/gif&media_category=subtitles', $url);
142+
$this->assertSame('https://upload.twitter.com/1.1/media/upload.json', $url);
143+
$this->assertArrayHasKey('body', $options);
144+
$this->assertSame($options['body'], 'command=INIT&total_bytes=185&media_type=image%2Fgif&media_category=subtitles');
139145
$this->assertArrayHasKey('authorization', $options['normalized_headers']);
140146

141147
return new MockResponse('{"media_id_string":"sub234"}');

src/Symfony/Component/Notifier/Bridge/Twitter/TwitterTransport.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,32 +156,32 @@ private function uploadMedia(array $media): array
156156
'category' => $category,
157157
'owners' => $extraOwners,
158158
]) {
159-
$query = [
159+
$body = [
160160
'command' => 'INIT',
161161
'total_bytes' => $file->getSize(),
162162
'media_type' => $file->getContentType(),
163163
];
164164

165165
if ($category) {
166-
$query['media_category'] = $category;
166+
$body['media_category'] = $category;
167167
}
168168

169169
if ($extraOwners) {
170-
$query['additional_owners'] = implode(',', $extraOwners);
170+
$body['additional_owners'] = implode(',', $extraOwners);
171171
}
172172

173173
$pool[++$i] = $this->request('POST', '/1.1/media/upload.json', [
174-
'query' => $query,
174+
'body' => $body,
175175
'user_data' => [$i, null, 0, fopen($file->getPath(), 'r'), $alt, $subtitles],
176176
]);
177177

178178
if ($subtitles) {
179-
$query['total_bytes'] = $subtitles->getSize();
180-
$query['media_type'] = $subtitles->getContentType();
181-
$query['media_category'] = 'subtitles';
179+
$body['total_bytes'] = $subtitles->getSize();
180+
$body['media_type'] = $subtitles->getContentType();
181+
$body['media_category'] = 'subtitles';
182182

183183
$pool[++$i] = $this->request('POST', '/1.1/media/upload.json', [
184-
'query' => $query,
184+
'body' => $body,
185185
'user_data' => [$i, null, 0, fopen($subtitles->getPath(), 'r'), null, $subtitles],
186186
]);
187187
}

src/Symfony/Component/RateLimiter/Policy/TokenBucketLimiter.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ public function reserve(int $tokens = 1, ?float $maxTime = null): Reservation
6767
$now = microtime(true);
6868
$availableTokens = $bucket->getAvailableTokens($now);
6969

70+
if ($availableTokens > $this->maxBurst) {
71+
$availableTokens = $this->maxBurst;
72+
}
73+
7074
if ($availableTokens >= $tokens) {
7175
// tokens are now available, update bucket
7276
$bucket->setTokens($availableTokens - $tokens);

src/Symfony/Component/RateLimiter/Tests/Policy/TokenBucketLimiterTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ public function testReserveMoreTokensThanBucketSize()
5757
$limiter->reserve(15);
5858
}
5959

60+
public function testReduceBucketSizeWhenAlreadyExistInStorageWithBiggerBucketSize()
61+
{
62+
$limiter = $this->createLimiter(100);
63+
64+
$limiter->consume();
65+
66+
$limiter2 = $this->createLimiter(1);
67+
$limiter2->consume();
68+
69+
$this->assertFalse($limiter2->consume()->isAccepted());
70+
}
71+
6072
public function testReserveMaxWaitingTime()
6173
{
6274
$limiter = $this->createLimiter(10, Rate::perMinute());

src/Symfony/Component/Serializer/Mapping/Loader/AttributeLoader.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata): bool
124124
}
125125

126126
$accessorOrMutator = preg_match('/^(get|is|has|set)(.+)$/i', $method->name, $matches);
127-
if ($accessorOrMutator) {
127+
if ($accessorOrMutator && !ctype_lower($matches[2][0])) {
128128
$attributeName = lcfirst($matches[2]);
129129

130130
if (isset($attributesMetadata[$attributeName])) {

src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ private function isGetMethod(\ReflectionMethod $method): bool
8787
return !$method->isStatic()
8888
&& !($method->getAttributes(Ignore::class) || $method->getAttributes(LegacyIgnore::class))
8989
&& !$method->getNumberOfRequiredParameters()
90-
&& ((2 < ($methodLength = \strlen($method->name)) && str_starts_with($method->name, 'is'))
91-
|| (3 < $methodLength && (str_starts_with($method->name, 'has') || str_starts_with($method->name, 'get')))
90+
&& ((2 < ($methodLength = \strlen($method->name)) && str_starts_with($method->name, 'is') && !ctype_lower($method->name[2]))
91+
|| (3 < $methodLength && (str_starts_with($method->name, 'has') || str_starts_with($method->name, 'get')) && !ctype_lower($method->name[3]))
9292
);
9393
}
9494

@@ -100,7 +100,9 @@ private function isSetMethod(\ReflectionMethod $method): bool
100100
return !$method->isStatic()
101101
&& !$method->getAttributes(Ignore::class)
102102
&& 0 < $method->getNumberOfParameters()
103-
&& str_starts_with($method->name, 'set');
103+
&& str_starts_with($method->name, 'set')
104+
&& !ctype_lower($method->name[3])
105+
;
104106
}
105107

106108
protected function extractAttributes(object $object, ?string $format = null, array $context = []): array

src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ protected function extractAttributes(object $object, ?string $format = null, arr
8888
$name = $reflMethod->name;
8989
$attributeName = null;
9090

91-
if (3 < \strlen($name) && match ($name[0]) {
91+
// ctype_lower check to find out if method looks like accessor but actually is not, e.g. hash, cancel
92+
if (3 < \strlen($name) && !ctype_lower($name[3]) && match ($name[0]) {
9293
'g' => str_starts_with($name, 'get'),
9394
'h' => str_starts_with($name, 'has'),
9495
'c' => str_starts_with($name, 'can'),
@@ -100,7 +101,7 @@ protected function extractAttributes(object $object, ?string $format = null, arr
100101
if (!$reflClass->hasProperty($attributeName)) {
101102
$attributeName = lcfirst($attributeName);
102103
}
103-
} elseif ('is' !== $name && str_starts_with($name, 'is')) {
104+
} elseif ('is' !== $name && str_starts_with($name, 'is') && !ctype_lower($name[2])) {
104105
// issers
105106
$attributeName = substr($name, 2);
106107

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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\Serializer\Tests\Fixtures\Attributes;
13+
14+
class AccessorishGetters
15+
{
16+
public function hash(): void
17+
{
18+
}
19+
20+
public function cancel()
21+
{
22+
}
23+
24+
public function getField1()
25+
{
26+
}
27+
28+
public function isField2()
29+
{
30+
}
31+
32+
public function hasField3()
33+
{
34+
}
35+
36+
public function setField4()
37+
{
38+
}
39+
}

src/Symfony/Component/Serializer/Tests/Mapping/Loader/AttributeLoaderTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\AbstractDummyFirstChild;
2424
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\AbstractDummySecondChild;
2525
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\AbstractDummyThirdChild;
26+
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\AccessorishGetters;
2627
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\BadAttributeDummy;
2728
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\BadMethodContextDummy;
2829
use Symfony\Component\Serializer\Tests\Fixtures\Attributes\ContextDummyParent;
@@ -229,6 +230,22 @@ public function testLoadWithInvalidAttribute()
229230
$this->loader->loadClassMetadata($classMetadata);
230231
}
231232

233+
public function testIgnoresAccessorishGetters()
234+
{
235+
$classMetadata = new ClassMetadata(AccessorishGetters::class);
236+
$this->loader->loadClassMetadata($classMetadata);
237+
238+
$attributesMetadata = $classMetadata->getAttributesMetadata();
239+
240+
self::assertCount(4, $classMetadata->getAttributesMetadata());
241+
242+
self::assertArrayHasKey('field1', $attributesMetadata);
243+
self::assertArrayHasKey('field2', $attributesMetadata);
244+
self::assertArrayHasKey('field3', $attributesMetadata);
245+
self::assertArrayHasKey('field4', $attributesMetadata);
246+
self::assertArrayNotHasKey('h', $attributesMetadata);
247+
}
248+
232249
protected function getLoaderForContextMapping(): AttributeLoader
233250
{
234251
return $this->loader;

0 commit comments

Comments
 (0)
0