Skip to content

Commit 2d6913c

Browse files
committed
feature symfony#60023 [Lock] Add LockKeyNormalizer (valtzu)
This PR was merged into the 7.4 branch. Discussion ---------- [Lock] Add `LockKeyNormalizer` | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | no | New feature? | yes | Deprecations? | no | License | MIT [The Lock documentation](https://symfony.com/doc/7.3/components/lock.html#serializing-locks) mentions that the lock keys are serializable – however, it seems that's the case only if you're using native php serializer. I'm proposing to add `LockKeyNormalizer` to make the example in the docs work the same when using Symfony Serializer too. Commits ------- d773b6c [Lock] Add `LockKeyNormalizer`
2 parents 46098d2 + d773b6c commit 2d6913c

File tree

7 files changed

+121
-1
lines changed

7 files changed

+121
-1
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
use Symfony\Component\Lock\LockFactory;
120120
use Symfony\Component\Lock\LockInterface;
121121
use Symfony\Component\Lock\PersistingStoreInterface;
122+
use Symfony\Component\Lock\Serializer\LockKeyNormalizer;
122123
use Symfony\Component\Lock\Store\StoreFactory;
123124
use Symfony\Component\Mailer\Bridge as MailerBridge;
124125
use Symfony\Component\Mailer\Command\MailerTestCommand;
@@ -2277,6 +2278,11 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont
22772278
{
22782279
$loader->load('lock.php');
22792280

2281+
// BC layer Lock < 7.4
2282+
if (!interface_exists(DenormalizerInterface::class) || !class_exists(LockKeyNormalizer::class)) {
2283+
$container->removeDefinition('serializer.normalizer.lock_key');
2284+
}
2285+
22802286
foreach ($config['resources'] as $resourceName => $resourceStores) {
22812287
if (0 === \count($resourceStores)) {
22822288
continue;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

1414
use Symfony\Component\Lock\LockFactory;
15+
use Symfony\Component\Lock\Serializer\LockKeyNormalizer;
1516
use Symfony\Component\Lock\Store\CombinedStore;
1617
use Symfony\Component\Lock\Strategy\ConsensusStrategy;
1718

@@ -26,5 +27,8 @@
2627
->args([abstract_arg('Store')])
2728
->call('setLogger', [service('logger')->ignoreOnInvalid()])
2829
->tag('monolog.logger', ['channel' => 'lock'])
30+
31+
->set('serializer.normalizer.lock_key', LockKeyNormalizer::class)
32+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -880])
2933
;
3034
};

src/Symfony/Component/Lock/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.4
5+
---
6+
7+
* Add `LockKeyNormalizer`
8+
49
7.3
510
---
611

src/Symfony/Component/Lock/Key.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,13 @@ public function isExpired(): bool
8989
return null !== $this->expiringTime && $this->expiringTime <= microtime(true);
9090
}
9191

92+
public function __unserialize(array $data): void
93+
{
94+
$this->resource = $data['resource'];
95+
$this->expiringTime = $data['expiringTime'];
96+
$this->state = $data['state'];
97+
}
98+
9299
public function __serialize(): array
93100
{
94101
if (!$this->serializable) {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Lock\Serializer;
13+
14+
use Symfony\Component\Lock\Key;
15+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
16+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
17+
18+
/**
19+
* Normalize {@see Key} instance to transfer an acquired lock between processes.
20+
*
21+
* @author Valtteri R <[email protected]>
22+
*/
23+
final class LockKeyNormalizer implements NormalizerInterface, DenormalizerInterface
24+
{
25+
public function getSupportedTypes(?string $format): array
26+
{
27+
return [Key::class => true];
28+
}
29+
30+
/**
31+
* @param Key $data
32+
*/
33+
public function normalize(mixed $data, ?string $format = null, array $context = []): array
34+
{
35+
return $data->__serialize();
36+
}
37+
38+
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
39+
{
40+
return $data instanceof Key;
41+
}
42+
43+
public function denormalize(mixed $data, string $type, ?string $format = null, array $context = []): Key
44+
{
45+
$key = (new \ReflectionClass(Key::class))->newInstanceWithoutConstructor();
46+
$key->__unserialize($data);
47+
48+
return $key;
49+
}
50+
51+
public function supportsDenormalization(mixed $data, string $type, ?string $format = null, array $context = []): bool
52+
{
53+
return Key::class === $type;
54+
}
55+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Lock\Tests\Serializer;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Lock\Exception\UnserializableKeyException;
16+
use Symfony\Component\Lock\Key;
17+
use Symfony\Component\Lock\Serializer\LockKeyNormalizer;
18+
19+
class LockKeyNormalizerTest extends TestCase
20+
{
21+
public function testNormalizeAndDenormalize()
22+
{
23+
$key = new Key(__METHOD__);
24+
$key->reduceLifetime(1);
25+
$key->setState('foo', 'bar');
26+
$normalizer = new LockKeyNormalizer();
27+
28+
$copy = $normalizer->denormalize($normalizer->normalize($key), Key::class);
29+
$this->assertSame($key->getState('foo'), $copy->getState('foo'));
30+
$this->assertEqualsWithDelta($key->getRemainingLifetime(), $copy->getRemainingLifetime(), 0.001);
31+
}
32+
33+
public function testNormalizingUnserializableLockThrows()
34+
{
35+
$key = new Key(__METHOD__);
36+
$key->markUnserializable();
37+
$normalizer = new LockKeyNormalizer();
38+
39+
$this->expectException(UnserializableKeyException::class);
40+
$normalizer->normalize($key);
41+
}
42+
}

src/Symfony/Component/Lock/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
},
2222
"require-dev": {
2323
"doctrine/dbal": "^3.6|^4",
24-
"predis/predis": "^1.1|^2.0"
24+
"predis/predis": "^1.1|^2.0",
25+
"symfony/serializer": "^6.4|^7.0"
2526
},
2627
"conflict": {
2728
"doctrine/dbal": "<3.6",

0 commit comments

Comments
 (0)