Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 3ba7a48

Browse filesBrowse files
committed
Merge branch '7.1' into 7.2
* 7.1: [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] Revert default groups [Serializer] fixed object normalizer for a class with `cancel` method Fix bucket size reduce when previously created with bigger size
2 parents 4e682e4 + ffd60b6 commit 3ba7a48
Copy full SHA for 3ba7a48

27 files changed

+268
-144
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/AssetMapper/Compiler/JavaScriptImportPathCompiler.php
+1-1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/AssetMapper/Tests/Compiler/JavaScriptImportPathCompilerTest.php
+16Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Definition.php
-2Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpFoundation/composer.json
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"require-dev": {
2525
"doctrine/dbal": "^3.6|^4",
2626
"predis/predis": "^1.1|^2.0",
27-
"symfony/cache": "^6.4|^7.0",
27+
"symfony/cache": "^6.4.12|^7.1.5",
2828
"symfony/dependency-injection": "^6.4|^7.0",
2929
"symfony/http-kernel": "^6.4|^7.0",
3030
"symfony/mime": "^6.4|^7.0",
@@ -33,7 +33,7 @@
3333
},
3434
"conflict": {
3535
"doctrine/dbal": "<3.6",
36-
"symfony/cache": "<6.4"
36+
"symfony/cache": "<6.4.12|>=7.0,<7.1.5"
3737
},
3838
"autoload": {
3939
"psr-4": { "Symfony\\Component\\HttpFoundation\\": "" },

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Messenger/Tests/WorkerTest.php
+11-7Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface;
5050
use Symfony\Component\Messenger\Worker;
5151
use Symfony\Component\RateLimiter\RateLimiterFactory;
52+
use Symfony\Component\RateLimiter\Reservation;
5253
use Symfony\Component\RateLimiter\Storage\InMemoryStorage;
5354

5455
/**
@@ -439,21 +440,21 @@ public function testWorkerRateLimitMessages()
439440
$envelope = [
440441
new Envelope(new DummyMessage('message1')),
441442
new Envelope(new DummyMessage('message2')),
443+
new Envelope(new DummyMessage('message3')),
444+
new Envelope(new DummyMessage('message4')),
442445
];
443446
$receiver = new DummyReceiver([$envelope]);
444447

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

448451
$eventDispatcher = new EventDispatcher();
449-
$eventDispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(2));
452+
$eventDispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(4));
450453

451454
$rateLimitCount = 0;
452-
$listener = function (WorkerRateLimitedEvent $event) use (&$rateLimitCount) {
455+
$eventDispatcher->addListener(WorkerRateLimitedEvent::class, static function () use (&$rateLimitCount) {
453456
++$rateLimitCount;
454-
$event->getLimiter()->reset(); // Reset limiter to continue test
455-
};
456-
$eventDispatcher->addListener(WorkerRateLimitedEvent::class, $listener);
457+
});
457458

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

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

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

472476
public function testWorkerShouldLogOnStop()

‎src/Symfony/Component/Messenger/Worker.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Messenger/Worker.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ private function rateLimit(string $transportName): void
257257

258258
$this->eventDispatcher?->dispatch(new WorkerRateLimitedEvent($rateLimiter, $transportName));
259259
$rateLimiter->reserve()->wait();
260+
$rateLimiter->consume();
260261
}
261262

262263
private function flush(bool $force): bool

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Notifier/Bridge/Twitter/Tests/TwitterTransportTest.php
+9-3Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Notifier/Bridge/Twitter/TwitterTransport.php
+8-8Lines 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/PropertyInfo/Extractor/SerializerExtractor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/Extractor/SerializerExtractor.php
+1-5Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,11 @@ public function getProperties(string $class, array $context = []): ?array
3838
return null;
3939
}
4040

41-
$groups = $context['serializer_groups'] ?? [];
42-
$groupsHasBeenDefined = [] !== $groups;
43-
$groups = array_merge($groups, ['Default', (false !== $nsSep = strrpos($class, '\\')) ? substr($class, $nsSep + 1) : $class]);
44-
4541
$properties = [];
4642
$serializerClassMetadata = $this->classMetadataFactory->getMetadataFor($class);
4743

4844
foreach ($serializerClassMetadata->getAttributesMetadata() as $serializerAttributeMetadata) {
49-
if (!$serializerAttributeMetadata->isIgnored() && (!$groupsHasBeenDefined || array_intersect(array_merge($serializerAttributeMetadata->getGroups(), ['*']), $groups))) {
45+
if (!$serializerAttributeMetadata->isIgnored() && (null === $context['serializer_groups'] || \in_array('*', $context['serializer_groups'], true) || array_intersect($serializerAttributeMetadata->getGroups(), $context['serializer_groups']))) {
5046
$properties[] = $serializerAttributeMetadata->getName();
5147
}
5248
}

‎src/Symfony/Component/PropertyInfo/Tests/Extractor/SerializerExtractorTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/Tests/Extractor/SerializerExtractorTest.php
-2Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ public function testGetProperties()
4343
public function testGetPropertiesWithIgnoredProperties()
4444
{
4545
$this->assertSame(['visibleProperty'], $this->extractor->getProperties(IgnorePropertyDummy::class, ['serializer_groups' => ['a']]));
46-
$this->assertSame(['visibleProperty'], $this->extractor->getProperties(IgnorePropertyDummy::class, ['serializer_groups' => ['Default']]));
47-
$this->assertSame(['visibleProperty'], $this->extractor->getProperties(IgnorePropertyDummy::class, ['serializer_groups' => ['IgnorePropertyDummy']]));
4846
}
4947

5048
public function testGetPropertiesWithAnyGroup()

‎src/Symfony/Component/PropertyInfo/Tests/Fixtures/IgnorePropertyDummy.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/Tests/Fixtures/IgnorePropertyDummy.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
*/
2020
class IgnorePropertyDummy
2121
{
22-
#[Groups(['a', 'Default', 'IgnorePropertyDummy'])]
22+
#[Groups(['a'])]
2323
public $visibleProperty;
2424

2525
#[Groups(['a']), Ignore]

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/RateLimiter/Policy/TokenBucketLimiter.php
+4Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/RateLimiter/Tests/Policy/TokenBucketLimiterTest.php
+12Lines 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/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/CHANGELOG.md
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ CHANGELOG
1919

2020
* Add arguments `$class`, `$format` and `$context` to `NameConverterInterface::normalize()` and `NameConverterInterface::denormalize()`
2121
* Add `DateTimeNormalizer::CAST_KEY` context option
22-
* Add `Default` and "class name" default groups
2322
* Add `AbstractNormalizer::FILTER_BOOL` context option
2423
* Add `CamelCaseToSnakeCaseNameConverter::REQUIRE_SNAKE_CASE_PROPERTIES` context option
2524
* Deprecate `AbstractNormalizerContextBuilder::withDefaultContructorArguments(?array $defaultContructorArguments)`, use `withDefaultConstructorArguments(?array $defaultConstructorArguments)` instead (note the missing `s` character in Contructor word in deprecated method)

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Mapping/Loader/AttributeLoader.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata): bool
115115
}
116116

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

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

‎src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/NameConverter/MetadataAwareNameConverter.php
+2-5Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,13 @@ private function getCacheValueForAttributesMetadata(string $class, array $contex
128128
}
129129

130130
$metadataGroups = $metadata->getGroups();
131-
132131
$contextGroups = (array) ($context[AbstractNormalizer::GROUPS] ?? []);
133-
$contextGroupsHasBeenDefined = [] !== $contextGroups;
134-
$contextGroups = array_merge($contextGroups, ['Default', (false !== $nsSep = strrpos($class, '\\')) ? substr($class, $nsSep + 1) : $class]);
135132

136-
if ($contextGroupsHasBeenDefined && !$metadataGroups) {
133+
if ($contextGroups && !$metadataGroups) {
137134
continue;
138135
}
139136

140-
if ($metadataGroups && !array_intersect(array_merge($metadataGroups, ['*']), $contextGroups)) {
137+
if ($metadataGroups && !array_intersect($metadataGroups, $contextGroups) && !\in_array('*', $contextGroups, true)) {
141138
continue;
142139
}
143140

‎src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php
+3-8Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,32 +222,27 @@ protected function getAllowedAttributes(string|object $classOrObject, array $con
222222
return false;
223223
}
224224

225-
$classMetadata = $this->classMetadataFactory->getMetadataFor($classOrObject);
226-
$class = $classMetadata->getName();
227-
228225
$groups = $this->getGroups($context);
229-
$groupsHasBeenDefined = [] !== $groups;
230-
$groups = array_merge($groups, ['Default', (false !== $nsSep = strrpos($class, '\\')) ? substr($class, $nsSep + 1) : $class]);
231226

232227
$allowedAttributes = [];
233228
$ignoreUsed = false;
234229

235-
foreach ($classMetadata->getAttributesMetadata() as $attributeMetadata) {
230+
foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) {
236231
if ($ignore = $attributeMetadata->isIgnored()) {
237232
$ignoreUsed = true;
238233
}
239234

240235
// If you update this check, update accordingly the one in Symfony\Component\PropertyInfo\Extractor\SerializerExtractor::getProperties()
241236
if (
242237
!$ignore
243-
&& (!$groupsHasBeenDefined || array_intersect(array_merge($attributeMetadata->getGroups(), ['*']), $groups))
238+
&& ([] === $groups || \in_array('*', $groups, true) || array_intersect($attributeMetadata->getGroups(), $groups))
244239
&& $this->isAllowedAttribute($classOrObject, $name = $attributeMetadata->getName(), null, $context)
245240
) {
246241
$allowedAttributes[] = $attributesAsString ? $name : $attributeMetadata;
247242
}
248243
}
249244

250-
if (!$ignoreUsed && !$groupsHasBeenDefined && $allowExtraAttributes) {
245+
if (!$ignoreUsed && [] === $groups && $allowExtraAttributes) {
251246
// Backward Compatibility with the code using this method written before the introduction of @Ignore
252247
return false;
253248
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php
+5-3Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php
+3-2Lines 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

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.