Skip to content

Commit 3b524f6

Browse files
committed
feature symfony#38395 [lock] Prevent user serializing the key when store does not support it. (jderusse)
This PR was merged into the 5.2-dev branch. Discussion ---------- [lock] Prevent user serializing the key when store does not support it. | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | / | License | MIT | Doc PR | / Some store relies on connection with the running process. ie. kernel relaease flock/semaphore, or zookeeper neeeds a connection to the database. When the users tries to serialize the key to send it to another process, they are not aware that they lose the lock. This PR throws an exception in that situation. Commits ------- 1ec0630 Prevent user serializing the key
2 parents c6a747d + 1ec0630 commit 3b524f6

File tree

10 files changed

+131
-0
lines changed

10 files changed

+131
-0
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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\Exception;
13+
14+
/**
15+
* UnserializableKeyException is thrown when the key contains state that can no
16+
* be serialized and the user try to serialize it.
17+
* ie. Connection with a database, flock, semaphore, ...
18+
*
19+
* @author Jérémy Derussé <[email protected]>
20+
*/
21+
class UnserializableKeyException extends \RuntimeException implements ExceptionInterface
22+
{
23+
}

src/Symfony/Component/Lock/Key.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Lock;
1313

14+
use Symfony\Component\Lock\Exception\UnserializableKeyException;
15+
1416
/**
1517
* Key is a container for the state of the locks in stores.
1618
*
@@ -21,6 +23,7 @@ final class Key
2123
private $resource;
2224
private $expiringTime;
2325
private $state = [];
26+
private $serializable = true;
2427

2528
public function __construct(string $resource)
2629
{
@@ -52,6 +55,11 @@ public function getState(string $stateKey)
5255
return $this->state[$stateKey];
5356
}
5457

58+
public function markUnserializable(): void
59+
{
60+
$this->serializable = false;
61+
}
62+
5563
public function resetLifetime()
5664
{
5765
$this->expiringTime = null;
@@ -83,4 +91,13 @@ public function isExpired(): bool
8391
{
8492
return null !== $this->expiringTime && $this->expiringTime <= microtime(true);
8593
}
94+
95+
public function __sleep(): array
96+
{
97+
if (!$this->serializable) {
98+
throw new UnserializableKeyException('The key can not be serialized.');
99+
}
100+
101+
return ['resource', 'expiringTime', 'state'];
102+
}
86103
}

src/Symfony/Component/Lock/Store/FlockStore.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ private function lock(Key $key, bool $read, bool $blocking)
125125
}
126126

127127
$key->setState(__CLASS__, [$read, $handle]);
128+
$key->markUnserializable();
128129
}
129130

130131
/**

src/Symfony/Component/Lock/Store/SemaphoreStore.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ private function lock(Key $key, bool $blocking)
7676
}
7777

7878
$key->setState(__CLASS__, $resource);
79+
$key->markUnserializable();
7980
}
8081

8182
/**

src/Symfony/Component/Lock/Store/ZookeeperStore.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public function save(Key $key)
6565
$token = $this->getUniqueToken($key);
6666

6767
$this->createNewLock($resource, $token);
68+
$key->markUnserializable();
6869

6970
$this->checkNotExpired($key);
7071
}
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;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Lock\Exception\UnserializableKeyException;
16+
use Symfony\Component\Lock\Key;
17+
18+
/**
19+
* @author Jérémy Derussé <[email protected]>
20+
*/
21+
class KeyTest extends TestCase
22+
{
23+
public function testSerialize()
24+
{
25+
$key = new Key(__METHOD__);
26+
$key->reduceLifetime(1);
27+
$key->setState('foo', 'bar');
28+
29+
$copy = unserialize(serialize($key));
30+
$this->assertSame($key->getState('foo'), $copy->getState('foo'));
31+
$this->assertEqualsWithDelta($key->getRemainingLifetime(), $copy->getRemainingLifetime(), 0.001);
32+
}
33+
34+
public function testUnserialize()
35+
{
36+
$key = new Key(__METHOD__);
37+
$key->markUnserializable();
38+
39+
$this->expectException(UnserializableKeyException::class);
40+
serialize($key);
41+
}
42+
}

src/Symfony/Component/Lock/Tests/Store/FlockStoreTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class FlockStoreTest extends AbstractStoreTest
2222
{
2323
use BlockingStoreTestTrait;
2424
use SharedLockStoreTestTrait;
25+
use UnserializableTestTrait;
2526

2627
/**
2728
* {@inheritdoc}

src/Symfony/Component/Lock/Tests/Store/SemaphoreStoreTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
class SemaphoreStoreTest extends AbstractStoreTest
2424
{
2525
use BlockingStoreTestTrait;
26+
use UnserializableTestTrait;
2627

2728
/**
2829
* {@inheritdoc}
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\Store;
13+
14+
use Symfony\Component\Lock\Exception\UnserializableKeyException;
15+
use Symfony\Component\Lock\Key;
16+
use Symfony\Component\Lock\PersistingStoreInterface;
17+
18+
/**
19+
* @author Jérémy Derussé <[email protected]>
20+
*/
21+
trait UnserializableTestTrait
22+
{
23+
/**
24+
* @see AbstractStoreTest::getStore()
25+
*
26+
* @return PersistingStoreInterface
27+
*/
28+
abstract protected function getStore();
29+
30+
public function testUnserializableKey()
31+
{
32+
$store = $this->getStore();
33+
34+
$key = new Key(uniqid(__METHOD__, true));
35+
36+
$store->save($key);
37+
$this->assertTrue($store->exists($key));
38+
39+
$this->expectException(UnserializableKeyException::class);
40+
serialize($key);
41+
}
42+
}

src/Symfony/Component/Lock/Tests/Store/ZookeeperStoreTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
*/
2424
class ZookeeperStoreTest extends AbstractStoreTest
2525
{
26+
use UnserializableTestTrait;
27+
2628
/**
2729
* @return ZookeeperStore
2830
*/

0 commit comments

Comments
 (0)