Skip to content

Commit bb03b99

Browse files
Merge branch '5.4' into 6.3
* 5.4: [Cache][Lock] Fix PDO store not creating table + add tests Closes symfony#51936-Added Missing translations for Czech (cs) in validators.cs.xlf file Added missing translations in turkish and updated validators.tr.xlf [Serializer] Fix denormalizing date intervals having both weeks and days [Validator] updated Turkish translation [Serializer] Fix denormalize constructor arguments Add some more non-countable English nouns Add hint that changing input arguments has no effect [DomCrawler] Revert "bug symfony#52579 UriResolver support path with colons" [VarExporter] Fix handling mangled property names returned by __sleep() Update Github template for 7.1
2 parents 7ebd082 + 48be4b3 commit bb03b99

File tree

27 files changed

+349
-81
lines changed

27 files changed

+349
-81
lines changed

.github/PULL_REQUEST_TEMPLATE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
| Q | A
22
| ------------- | ---
3-
| Branch? | 6.4 for features / 5.4 or 6.3 for bug fixes <!-- see below -->
3+
| Branch? | 7.1 for features / 5.4, 6.3, 6.4, or 7.0 for bug fixes <!-- see below -->
44
| Bug fix? | yes/no
55
| New feature? | yes/no <!-- please update src/**/CHANGELOG.md files -->
66
| Deprecations? | yes/no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->

src/Symfony/Component/Cache/Adapter/DoctrineDbalAdapter.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,8 @@ private function getServerVersion(): string
388388
return $this->serverVersion;
389389
}
390390

391-
$conn = $this->conn->getWrappedConnection();
391+
// The condition should be removed once support for DBAL <3.3 is dropped
392+
$conn = method_exists($this->conn, 'getNativeConnection') ? $this->conn->getNativeConnection() : $this->conn->getWrappedConnection();
392393
if ($conn instanceof ServerInfoAwareConnection) {
393394
return $this->serverVersion = $conn->getServerVersion();
394395
}

src/Symfony/Component/Cache/Adapter/PdoAdapter.php

+19-2
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ protected function doSave(array $values, int $lifetime): array|bool
286286
try {
287287
$stmt = $conn->prepare($sql);
288288
} catch (\PDOException) {
289-
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
289+
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
290290
$this->createTable();
291291
}
292292
$stmt = $conn->prepare($sql);
@@ -321,7 +321,7 @@ protected function doSave(array $values, int $lifetime): array|bool
321321
try {
322322
$stmt->execute();
323323
} catch (\PDOException) {
324-
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
324+
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
325325
$this->createTable();
326326
}
327327
$stmt->execute();
@@ -369,4 +369,21 @@ private function getServerVersion(): string
369369
{
370370
return $this->serverVersion ??= $this->conn->getAttribute(\PDO::ATTR_SERVER_VERSION);
371371
}
372+
373+
private function isTableMissing(\PDOException $exception): bool
374+
{
375+
$driver = $this->driver;
376+
$code = $exception->getCode();
377+
378+
switch (true) {
379+
case 'pgsql' === $driver && '42P01' === $code:
380+
case 'sqlite' === $driver && str_contains($exception->getMessage(), 'no such table:'):
381+
case 'oci' === $driver && 942 === $code:
382+
case 'sqlsrv' === $driver && 208 === $code:
383+
case 'mysql' === $driver && 1146 === $code:
384+
return true;
385+
default:
386+
return false;
387+
}
388+
}
372389
}

src/Symfony/Component/Cache/Tests/Adapter/DoctrineDbalAdapterTest.php

+31-12
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
use Doctrine\DBAL\DriverManager;
1919
use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory;
2020
use Doctrine\DBAL\Schema\Schema;
21-
use PHPUnit\Framework\SkippedTestSuiteError;
2221
use Psr\Cache\CacheItemPoolInterface;
2322
use Symfony\Component\Cache\Adapter\DoctrineDbalAdapter;
2423
use Symfony\Component\Cache\Tests\Fixtures\DriverWrapper;
2524

2625
/**
26+
* @requires extension pdo_sqlite
27+
*
2728
* @group time-sensitive
2829
*/
2930
class DoctrineDbalAdapterTest extends AdapterTestCase
@@ -32,10 +33,6 @@ class DoctrineDbalAdapterTest extends AdapterTestCase
3233

3334
public static function setUpBeforeClass(): void
3435
{
35-
if (!\extension_loaded('pdo_sqlite')) {
36-
throw new SkippedTestSuiteError('Extension pdo_sqlite required.');
37-
}
38-
3936
self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
4037
}
4138

@@ -107,13 +104,12 @@ public function testConfigureSchemaTableExists()
107104
}
108105

109106
/**
110-
* @dataProvider provideDsn
107+
* @dataProvider provideDsnWithSQLite
111108
*/
112-
public function testDsn(string $dsn, string $file = null)
109+
public function testDsnWithSQLite(string $dsn, string $file = null)
113110
{
114111
try {
115112
$pool = new DoctrineDbalAdapter($dsn);
116-
$pool->createTable();
117113

118114
$item = $pool->getItem('key');
119115
$item->set('value');
@@ -125,12 +121,35 @@ public function testDsn(string $dsn, string $file = null)
125121
}
126122
}
127123

128-
public static function provideDsn()
124+
public static function provideDsnWithSQLite()
129125
{
130126
$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
131-
yield ['sqlite://localhost/'.$dbFile.'1', $dbFile.'1'];
132-
yield ['sqlite3:///'.$dbFile.'3', $dbFile.'3'];
133-
yield ['sqlite://localhost/:memory:'];
127+
yield 'SQLite file' => ['sqlite://localhost/'.$dbFile.'1', $dbFile.'1'];
128+
yield 'SQLite3 file' => ['sqlite3:///'.$dbFile.'3', $dbFile.'3'];
129+
yield 'SQLite in memory' => ['sqlite://localhost/:memory:'];
130+
}
131+
132+
/**
133+
* @requires extension pdo_pgsql
134+
*
135+
* @group integration
136+
*/
137+
public function testDsnWithPostgreSQL()
138+
{
139+
if (!$host = getenv('POSTGRES_HOST')) {
140+
$this->markTestSkipped('Missing POSTGRES_HOST env variable');
141+
}
142+
143+
try {
144+
$pool = new DoctrineDbalAdapter('pgsql://postgres:password@'.$host);
145+
146+
$item = $pool->getItem('key');
147+
$item->set('value');
148+
$this->assertTrue($pool->save($item));
149+
} finally {
150+
$pdo = new \PDO('pgsql:host='.$host.';user=postgres;password=password');
151+
$pdo->exec('DROP TABLE IF EXISTS cache_items');
152+
}
134153
}
135154

136155
protected function isPruned(DoctrineDbalAdapter $cache, string $name): bool

src/Symfony/Component/Cache/Tests/Adapter/PdoAdapterTest.php

+32-11
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111

1212
namespace Symfony\Component\Cache\Tests\Adapter;
1313

14-
use PHPUnit\Framework\SkippedTestSuiteError;
1514
use Psr\Cache\CacheItemPoolInterface;
1615
use Symfony\Component\Cache\Adapter\PdoAdapter;
1716

1817
/**
18+
* @requires extension pdo_sqlite
19+
*
1920
* @group time-sensitive
2021
*/
2122
class PdoAdapterTest extends AdapterTestCase
@@ -24,10 +25,6 @@ class PdoAdapterTest extends AdapterTestCase
2425

2526
public static function setUpBeforeClass(): void
2627
{
27-
if (!\extension_loaded('pdo_sqlite')) {
28-
throw new SkippedTestSuiteError('Extension pdo_sqlite required.');
29-
}
30-
3128
self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
3229

3330
$pool = new PdoAdapter('sqlite:'.self::$dbFile);
@@ -69,13 +66,12 @@ public function testCleanupExpiredItems()
6966
}
7067

7168
/**
72-
* @dataProvider provideDsn
69+
* @dataProvider provideDsnSQLite
7370
*/
74-
public function testDsn(string $dsn, string $file = null)
71+
public function testDsnWithSQLite(string $dsn, string $file = null)
7572
{
7673
try {
7774
$pool = new PdoAdapter($dsn);
78-
$pool->createTable();
7975

8076
$item = $pool->getItem('key');
8177
$item->set('value');
@@ -87,11 +83,36 @@ public function testDsn(string $dsn, string $file = null)
8783
}
8884
}
8985

90-
public static function provideDsn()
86+
public static function provideDsnSQLite()
9187
{
9288
$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
93-
yield ['sqlite:'.$dbFile.'2', $dbFile.'2'];
94-
yield ['sqlite::memory:'];
89+
yield 'SQLite file' => ['sqlite:'.$dbFile.'2', $dbFile.'2'];
90+
yield 'SQLite in memory' => ['sqlite::memory:'];
91+
}
92+
93+
/**
94+
* @requires extension pdo_pgsql
95+
*
96+
* @group integration
97+
*/
98+
public function testDsnWithPostgreSQL()
99+
{
100+
if (!$host = getenv('POSTGRES_HOST')) {
101+
$this->markTestSkipped('Missing POSTGRES_HOST env variable');
102+
}
103+
104+
$dsn = 'pgsql:host='.$host.';user=postgres;password=password';
105+
106+
try {
107+
$pool = new PdoAdapter($dsn);
108+
109+
$item = $pool->getItem('key');
110+
$item->set('value');
111+
$this->assertTrue($pool->save($item));
112+
} finally {
113+
$pdo = new \PDO($dsn);
114+
$pdo->exec('DROP TABLE IF EXISTS cache_items');
115+
}
95116
}
96117

97118
protected function isPruned(PdoAdapter $cache, string $name): bool

src/Symfony/Component/Console/Event/ConsoleCommandEvent.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@
1212
namespace Symfony\Component\Console\Event;
1313

1414
/**
15-
* Allows to do things before the command is executed, like skipping the command or changing the input.
15+
* Allows to do things before the command is executed, like skipping the command or executing code before the command is
16+
* going to be executed.
17+
*
18+
* Changing the input arguments will have no effect.
1619
*
1720
* @author Fabien Potencier <fabien@symfony.com>
1821
*/

src/Symfony/Component/DomCrawler/Tests/UriResolverTest.php

+1-3
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,7 @@ public static function provideResolverTests()
8585
['foo', 'http://localhost?bar=1', 'http://localhost/foo'],
8686
['foo', 'http://localhost#bar', 'http://localhost/foo'],
8787

88-
['foo:1', 'http://localhost', 'http://localhost/foo:1'],
89-
['/bar:1', 'http://localhost', 'http://localhost/bar:1'],
90-
['foo/bar:1', 'http://localhost', 'http://localhost/foo/bar:1'],
88+
['http://', 'http://localhost', 'http://'],
9189
];
9290
}
9391
}

src/Symfony/Component/DomCrawler/UriResolver.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public static function resolve(string $uri, ?string $baseUri): string
3333
$uri = trim($uri);
3434

3535
// absolute URL?
36-
if (\is_string(parse_url($uri, \PHP_URL_SCHEME))) {
36+
if (null !== parse_url($uri, \PHP_URL_SCHEME)) {
3737
return $uri;
3838
}
3939

src/Symfony/Component/HttpFoundation/Session/Storage/Handler/SessionHandlerFactory.php

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public static function createHandler(object|string $connection, array $options =
7878
}
7979

8080
$connection = DriverManager::getConnection($params, $config);
81+
// The condition should be removed once support for DBAL <3.3 is dropped
8182
$connection = method_exists($connection, 'getNativeConnection') ? $connection->getNativeConnection() : $connection->getWrappedConnection();
8283
// no break;
8384

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

+30-3
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public function save(Key $key)
9595
try {
9696
$stmt = $conn->prepare($sql);
9797
} catch (\PDOException) {
98-
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
98+
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
9999
$this->createTable();
100100
}
101101
$stmt = $conn->prepare($sql);
@@ -107,8 +107,18 @@ public function save(Key $key)
107107
try {
108108
$stmt->execute();
109109
} catch (\PDOException) {
110-
// the lock is already acquired. It could be us. Let's try to put off.
111-
$this->putOffExpiration($key, $this->initialTtl);
110+
if ($this->isTableMissing($e) && (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true))) {
111+
$this->createTable();
112+
113+
try {
114+
$stmt->execute();
115+
} catch (\PDOException $e) {
116+
$this->putOffExpiration($key, $this->initialTtl);
117+
}
118+
} else {
119+
// the lock is already acquired. It could be us. Let's try to put off.
120+
$this->putOffExpiration($key, $this->initialTtl);
121+
}
112122
}
113123

114124
$this->randomlyPrune();
@@ -238,4 +248,21 @@ private function getCurrentTimestampStatement(): string
238248
default => (string) time(),
239249
};
240250
}
251+
252+
private function isTableMissing(\PDOException $exception): bool
253+
{
254+
$driver = $this->getDriver();
255+
$code = $exception->getCode();
256+
257+
switch (true) {
258+
case 'pgsql' === $driver && '42P01' === $code:
259+
case 'sqlite' === $driver && str_contains($exception->getMessage(), 'no such table:'):
260+
case 'oci' === $driver && 942 === $code:
261+
case 'sqlsrv' === $driver && 208 === $code:
262+
case 'mysql' === $driver && 1146 === $code:
263+
return true;
264+
default:
265+
return false;
266+
}
267+
}
241268
}

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

+30-6
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ public function testAbortAfterExpiration()
7474
}
7575

7676
/**
77-
* @dataProvider provideDsn
77+
* @dataProvider provideDsnWithSQLite
7878
*/
79-
public function testDsn(string $dsn, string $file = null)
79+
public function testDsnWithSQLite(string $dsn, string $file = null)
8080
{
8181
$key = new Key(uniqid(__METHOD__, true));
8282

@@ -92,12 +92,36 @@ public function testDsn(string $dsn, string $file = null)
9292
}
9393
}
9494

95-
public static function provideDsn()
95+
public static function provideDsnWithSQLite()
9696
{
9797
$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
98-
yield ['sqlite://localhost/'.$dbFile.'1', $dbFile.'1'];
99-
yield ['sqlite3:///'.$dbFile.'3', $dbFile.'3'];
100-
yield ['sqlite://localhost/:memory:'];
98+
yield 'SQLite file' => ['sqlite://localhost/'.$dbFile.'1', $dbFile.'1'];
99+
yield 'SQLite3 file' => ['sqlite3:///'.$dbFile.'3', $dbFile.'3'];
100+
yield 'SQLite in memory' => ['sqlite://localhost/:memory:'];
101+
}
102+
103+
/**
104+
* @requires extension pdo_pgsql
105+
*
106+
* @group integration
107+
*/
108+
public function testDsnWithPostgreSQL()
109+
{
110+
if (!$host = getenv('POSTGRES_HOST')) {
111+
$this->markTestSkipped('Missing POSTGRES_HOST env variable');
112+
}
113+
114+
$key = new Key(uniqid(__METHOD__, true));
115+
116+
try {
117+
$store = new DoctrineDbalStore('pgsql://postgres:password@'.$host);
118+
119+
$store->save($key);
120+
$this->assertTrue($store->exists($key));
121+
} finally {
122+
$pdo = new \PDO('pgsql:host='.$host.';user=postgres;password=password');
123+
$pdo->exec('DROP TABLE IF EXISTS lock_keys');
124+
}
101125
}
102126

103127
/**

0 commit comments

Comments
 (0)