Skip to content

Commit 36d8af3

Browse files
Merge branch '4.4'
* 4.4: Re-allow to use "tagged" in service definitions [HttpFoundation] Allow to not pass a parameter to Request::isMethodSafe() Add missing lock connection string in FrameworkExtension [DomCrawler] normalizeWhitespace should be true by default [DoctrineBridge] Auto-validation must work if no regex are passed Allows URL DSN in Lock and Cache
2 parents f1d1a9b + a522801 commit 36d8af3

File tree

8 files changed

+123
-7
lines changed

8 files changed

+123
-7
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ CHANGELOG
1414
* added InvalidTtlException
1515
* deprecated `StoreInterface` in favor of `BlockingStoreInterface` and `PersistingStoreInterface`
1616
* `Factory` is deprecated, use `LockFactory` instead
17-
17+
* `StoreFactory::createStore` allows PDO and Zookeeper DSN.
18+
* deprecated services `lock.store.flock`, `lock.store.semaphore`, `lock.store.memcached.abstract` and `lock.store.redis.abstract`,
19+
use `StoreFactory::createStore` instead.
20+
1821
4.2.0
1922
-----
2023

Store/PdoStore.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\DBAL\Connection;
1515
use Doctrine\DBAL\DBALException;
16+
use Doctrine\DBAL\DriverManager;
1617
use Doctrine\DBAL\Schema\Schema;
1718
use Symfony\Component\Lock\Exception\InvalidArgumentException;
1819
use Symfony\Component\Lock\Exception\InvalidTtlException;
@@ -219,8 +220,15 @@ private function getUniqueToken(Key $key): string
219220
private function getConnection(): object
220221
{
221222
if (null === $this->conn) {
222-
$this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions);
223-
$this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
223+
if (strpos($this->dsn, '://')) {
224+
if (!class_exists(DriverManager::class)) {
225+
throw new InvalidArgumentException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $this->dsn));
226+
}
227+
$this->conn = DriverManager::getConnection(['url' => $this->dsn]);
228+
} else {
229+
$this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions);
230+
$this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
231+
}
224232
}
225233

226234
return $this->conn;

Store/StoreFactory.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,24 @@ public static function createStore($connection)
5858
return new FlockStore(substr($connection, 8));
5959
case 'semaphore' === $connection:
6060
return new SemaphoreStore();
61-
case class_exists(AbstractAdapter::class) && preg_match('#^[a-z]++://#', $connection):
62-
return static::createStore(AbstractAdapter::createConnection($connection));
61+
case 0 === strpos($connection, 'redis://') && class_exists(AbstractAdapter::class):
62+
case 0 === strpos($connection, 'rediss://') && class_exists(AbstractAdapter::class):
63+
return new RedisStore(AbstractAdapter::createConnection($connection, ['lazy' => true]));
64+
case 0 === strpos($connection, 'memcached://') && class_exists(AbstractAdapter::class):
65+
return new MemcachedStore(AbstractAdapter::createConnection($connection, ['lazy' => true]));
66+
case 0 === strpos($connection, 'sqlite:'):
67+
case 0 === strpos($connection, 'mysql:'):
68+
case 0 === strpos($connection, 'pgsql:'):
69+
case 0 === strpos($connection, 'oci:'):
70+
case 0 === strpos($connection, 'sqlsrv:'):
71+
case 0 === strpos($connection, 'sqlite3://'):
72+
case 0 === strpos($connection, 'mysql2://'):
73+
case 0 === strpos($connection, 'postgres://'):
74+
case 0 === strpos($connection, 'postgresql://'):
75+
case 0 === strpos($connection, 'mssql://'):
76+
return new PdoStore($connection);
77+
case 0 === strpos($connection, 'zookeeper://'):
78+
return new ZookeeperStore(ZookeeperStore::createConnection($connection));
6379
default:
6480
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
6581
}

Store/ZookeeperStore.php

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

1212
namespace Symfony\Component\Lock\Store;
1313

14+
use Symfony\Component\Lock\Exception\InvalidArgumentException;
1415
use Symfony\Component\Lock\Exception\LockAcquiringException;
1516
use Symfony\Component\Lock\Exception\LockConflictedException;
1617
use Symfony\Component\Lock\Exception\LockReleasingException;
@@ -33,6 +34,24 @@ public function __construct(\Zookeeper $zookeeper)
3334
$this->zookeeper = $zookeeper;
3435
}
3536

37+
public static function createConnection(string $dsn): \Zookeeper
38+
{
39+
if (0 !== strpos($dsn, 'zookeeper:')) {
40+
throw new InvalidArgumentException(sprintf('Unsupported DSN: %s.', $dsn));
41+
}
42+
43+
if (false === $params = parse_url($dsn)) {
44+
throw new InvalidArgumentException(sprintf('Invalid Zookeeper DSN: %s.', $dsn));
45+
}
46+
47+
$host = $params['host'] ?? '';
48+
if (isset($params['port'])) {
49+
$host .= ':'.$params['port'];
50+
}
51+
52+
return new \Zookeeper($host);
53+
}
54+
3655
/**
3756
* {@inheritdoc}
3857
*/

Tests/Store/PdoStoreTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,34 @@ public function testInvalidTtlConstruct()
7373

7474
return new PdoStore('sqlite:'.self::$dbFile, [], 0.1, 0.1);
7575
}
76+
77+
/**
78+
* @dataProvider provideDsn
79+
*/
80+
public function testDsn(string $dsn, string $file = null)
81+
{
82+
$key = new Key(uniqid(__METHOD__, true));
83+
84+
try {
85+
$store = new PdoStore($dsn);
86+
$store->createTable();
87+
88+
$store->save($key);
89+
$this->assertTrue($store->exists($key));
90+
} finally {
91+
if (null !== $file) {
92+
@unlink($file);
93+
}
94+
}
95+
}
96+
97+
public function provideDsn()
98+
{
99+
$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
100+
yield ['sqlite://localhost/'.$dbFile, ''.$dbFile];
101+
yield ['sqlite:'.$dbFile, ''.$dbFile];
102+
yield ['sqlite3:///'.$dbFile, ''.$dbFile];
103+
yield ['sqlite://localhost/:memory:'];
104+
yield ['sqlite::memory:'];
105+
}
76106
}

Tests/Store/StoreFactoryTest.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Component\Cache\Traits\RedisProxy;
1717
use Symfony\Component\Lock\Store\FlockStore;
1818
use Symfony\Component\Lock\Store\MemcachedStore;
19+
use Symfony\Component\Lock\Store\PdoStore;
1920
use Symfony\Component\Lock\Store\RedisStore;
2021
use Symfony\Component\Lock\Store\SemaphoreStore;
2122
use Symfony\Component\Lock\Store\StoreFactory;
@@ -50,13 +51,34 @@ public function validConnections()
5051
}
5152
if (class_exists(\Zookeeper::class)) {
5253
yield [$this->createMock(\Zookeeper::class), ZookeeperStore::class];
54+
yield ['zookeeper://localhost:2181', ZookeeperStore::class];
5355
}
5456
if (\extension_loaded('sysvsem')) {
5557
yield ['semaphore', SemaphoreStore::class];
5658
}
5759
if (class_exists(\Memcached::class) && class_exists(AbstractAdapter::class)) {
5860
yield ['memcached://server.com', MemcachedStore::class];
5961
}
62+
if (class_exists(\Redis::class) && class_exists(AbstractAdapter::class)) {
63+
yield ['redis://localhost', RedisStore::class];
64+
}
65+
if (class_exists(\PDO::class)) {
66+
yield ['sqlite:/tmp/sqlite.db', PdoStore::class];
67+
yield ['sqlite::memory:', PdoStore::class];
68+
yield ['mysql:host=localhost;dbname=test;', PdoStore::class];
69+
yield ['pgsql:host=localhost;dbname=test;', PdoStore::class];
70+
yield ['oci:host=localhost;dbname=test;', PdoStore::class];
71+
yield ['sqlsrv:server=localhost;Database=test', PdoStore::class];
72+
yield ['mysql://server.com/test', PdoStore::class];
73+
yield ['mysql2://server.com/test', PdoStore::class];
74+
yield ['pgsql://server.com/test', PdoStore::class];
75+
yield ['postgres://server.com/test', PdoStore::class];
76+
yield ['postgresql://server.com/test', PdoStore::class];
77+
yield ['sqlite:///tmp/test', PdoStore::class];
78+
yield ['sqlite3:///tmp/test', PdoStore::class];
79+
yield ['oci:///server.com/test', PdoStore::class];
80+
yield ['mssql:///server.com/test', PdoStore::class];
81+
}
6082

6183
yield ['flock', FlockStore::class];
6284
yield ['flock://'.sys_get_temp_dir(), FlockStore::class];

Tests/Store/ZookeeperStoreTest.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,26 @@ public function getStore(): PersistingStoreInterface
3030
{
3131
$zookeeper_server = getenv('ZOOKEEPER_HOST').':2181';
3232

33-
$zookeeper = new \Zookeeper(implode(',', [$zookeeper_server]));
33+
$zookeeper = new \Zookeeper($zookeeper_server);
3434

3535
return StoreFactory::createStore($zookeeper);
3636
}
3737

38+
/**
39+
* @dataProvider provideValidConnectionString
40+
*/
41+
public function testCreateConnection(string $connectionString)
42+
{
43+
$this->assertInstanceOf(\Zookeeper::class, ZookeeperStore::createConnection($connectionString));
44+
}
45+
46+
public function provideValidConnectionString(): iterable
47+
{
48+
yield 'single host' => ['zookeeper://localhost:2181'];
49+
yield 'single multiple host' => ['zookeeper://localhost:2181,localhost:2181'];
50+
yield 'with extra attributes' => ['zookeeper://localhost:2181/path?option=value'];
51+
}
52+
3853
public function testSaveSucceedsWhenPathContainsMoreThanOneNode()
3954
{
4055
$store = $this->getStore();

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,13 @@
2020
"psr/log": "~1.0"
2121
},
2222
"require-dev": {
23-
"doctrine/dbal": "~2.4",
23+
"doctrine/dbal": "~2.5",
2424
"mongodb/mongodb": "~1.1",
2525
"predis/predis": "~1.0"
2626
},
27+
"conflict": {
28+
"doctrine/dbal": "<2.5"
29+
},
2730
"autoload": {
2831
"psr-4": { "Symfony\\Component\\Lock\\": "" },
2932
"exclude-from-classmap": [

0 commit comments

Comments
 (0)