diff --git a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php
index 9ac53b87f6..c01382348b 100644
--- a/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php
+++ b/src/Symfony/Bridge/Doctrine/Security/RememberMe/DoctrineTokenProvider.php
@@ -81,7 +81,11 @@ class DoctrineTokenProvider implements TokenProviderInterface
$sql = 'DELETE FROM rememberme_token WHERE series=:series';
$paramValues = ['series' => $series];
$paramTypes = ['series' => \PDO::PARAM_STR];
- $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
+ if (method_exists($this->conn, 'executeStatement')) {
+ $this->conn->executeStatement($sql, $paramValues, $paramTypes);
+ } else {
+ $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
+ }
}
/**
@@ -101,7 +105,11 @@ class DoctrineTokenProvider implements TokenProviderInterface
'lastUsed' => self::$useDeprecatedConstants ? Type::DATETIME : Types::DATETIME_MUTABLE,
'series' => \PDO::PARAM_STR,
];
- $updated = $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
+ if (method_exists($this->conn, 'executeStatement')) {
+ $updated = $this->conn->executeStatement($sql, $paramValues, $paramTypes);
+ } else {
+ $updated = $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
+ }
if ($updated < 1) {
throw new TokenNotFoundException('No token found.');
}
@@ -129,6 +137,10 @@ class DoctrineTokenProvider implements TokenProviderInterface
'value' => \PDO::PARAM_STR,
'lastUsed' => self::$useDeprecatedConstants ? Type::DATETIME : Types::DATETIME_MUTABLE,
];
- $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
+ if (method_exists($this->conn, 'executeStatement')) {
+ $this->conn->executeStatement($sql, $paramValues, $paramTypes);
+ } else {
+ $this->conn->executeUpdate($sql, $paramValues, $paramTypes);
+ }
}
}
diff --git a/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php b/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php
index df696ff7ff..75faf9012a 100644
--- a/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php
+++ b/src/Symfony/Bridge/Doctrine/Tests/Security/RememberMe/DoctrineTokenProviderTest.php
@@ -72,7 +72,7 @@ class DoctrineTokenProviderTest extends TestCase
'driver' => 'pdo_sqlite',
'url' => 'sqlite:///:memory:',
]);
- $connection->executeUpdate(<<< 'SQL'
+ $connection->{method_exists($connection, 'executeStatement') ? 'executeStatement' : 'executeUpdate'}(<<< 'SQL'
CREATE TABLE rememberme_token (
series char(88) UNIQUE PRIMARY KEY NOT NULL,
value char(88) NOT NULL,
diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
index e0df94c349..077430994b 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
@@ -43,12 +43,11 @@ abstract class AbstractAdapter implements AdapterInterface, CacheInterface, Logg
throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace));
}
$this->createCacheItem = \Closure::bind(
- static function ($key, $value, $isHit) use ($defaultLifetime) {
+ static function ($key, $value, $isHit) {
$item = new CacheItem();
$item->key = $key;
$item->value = $v = $value;
$item->isHit = $isHit;
- $item->defaultLifetime = $defaultLifetime;
// Detect wrapped values that encode for their expiry and creation duration
// For compactness, these values are packed in the key of an array using
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
@@ -66,7 +65,7 @@ abstract class AbstractAdapter implements AdapterInterface, CacheInterface, Logg
);
$getId = \Closure::fromCallable([$this, 'getId']);
$this->mergeByLifetime = \Closure::bind(
- static function ($deferred, $namespace, &$expiredIds) use ($getId) {
+ static function ($deferred, $namespace, &$expiredIds) use ($getId, $defaultLifetime) {
$byLifetime = [];
$now = microtime(true);
$expiredIds = [];
@@ -74,7 +73,9 @@ abstract class AbstractAdapter implements AdapterInterface, CacheInterface, Logg
foreach ($deferred as $key => $item) {
$key = (string) $key;
if (null === $item->expiry) {
- $ttl = 0 < $item->defaultLifetime ? $item->defaultLifetime : 0;
+ $ttl = 0 < $defaultLifetime ? $defaultLifetime : 0;
+ } elseif (0 === $item->expiry) {
+ $ttl = 0;
} elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) {
$expiredIds[] = $getId($key);
continue;
diff --git a/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php
index 1a73d974c9..d5c6aae572 100644
--- a/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/AbstractTagAwareAdapter.php
@@ -44,10 +44,9 @@ abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagA
throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace));
}
$this->createCacheItem = \Closure::bind(
- static function ($key, $value, $isHit) use ($defaultLifetime) {
+ static function ($key, $value, $isHit) {
$item = new CacheItem();
$item->key = $key;
- $item->defaultLifetime = $defaultLifetime;
$item->isTaggable = true;
// If structure does not match what we expect return item as is (no value and not a hit)
if (!\is_array($value) || !\array_key_exists('value', $value)) {
@@ -72,7 +71,7 @@ abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagA
$getId = \Closure::fromCallable([$this, 'getId']);
$tagPrefix = self::TAGS_PREFIX;
$this->mergeByLifetime = \Closure::bind(
- static function ($deferred, &$expiredIds) use ($getId, $tagPrefix) {
+ static function ($deferred, &$expiredIds) use ($getId, $tagPrefix, $defaultLifetime) {
$byLifetime = [];
$now = microtime(true);
$expiredIds = [];
@@ -80,7 +79,9 @@ abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagA
foreach ($deferred as $key => $item) {
$key = (string) $key;
if (null === $item->expiry) {
- $ttl = 0 < $item->defaultLifetime ? $item->defaultLifetime : 0;
+ $ttl = 0 < $defaultLifetime ? $defaultLifetime : 0;
+ } elseif (0 === $item->expiry) {
+ $ttl = 0;
} elseif (0 >= $ttl = (int) (0.1 + $item->expiry - $now)) {
$expiredIds[] = $getId($key);
continue;
diff --git a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
index 05920d01a9..37d89a695b 100644
--- a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
@@ -29,20 +29,21 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
private $values = [];
private $expiries = [];
private $createCacheItem;
+ private $defaultLifetime;
/**
* @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
*/
public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true)
{
+ $this->defaultLifetime = $defaultLifetime;
$this->storeSerialized = $storeSerialized;
$this->createCacheItem = \Closure::bind(
- static function ($key, $value, $isHit) use ($defaultLifetime) {
+ static function ($key, $value, $isHit) {
$item = new CacheItem();
$item->key = $key;
$item->value = $value;
$item->isHit = $isHit;
- $item->defaultLifetime = $defaultLifetime;
return $item;
},
@@ -172,8 +173,8 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) {
return false;
}
- if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) {
- $expiry = microtime(true) + $item["\0*\0defaultLifetime"];
+ if (null === $expiry && 0 < $this->defaultLifetime) {
+ $expiry = microtime(true) + $this->defaultLifetime;
}
$this->values[$key] = $value;
diff --git a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
index 19589ccc40..5deb8d9583 100644
--- a/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ChainAdapter.php
@@ -70,15 +70,11 @@ class ChainAdapter implements AdapterInterface, CacheInterface, PruneableInterfa
unset($sourceMetadata[CacheItem::METADATA_TAGS]);
$item->value = $sourceItem->value;
- $item->expiry = $sourceMetadata[CacheItem::METADATA_EXPIRY] ?? $sourceItem->expiry;
$item->isHit = $sourceItem->isHit;
$item->metadata = $item->newMetadata = $sourceItem->metadata = $sourceMetadata;
- if (0 < $sourceItem->defaultLifetime && $sourceItem->defaultLifetime < $defaultLifetime) {
- $defaultLifetime = $sourceItem->defaultLifetime;
- }
- if (0 < $defaultLifetime && ($item->defaultLifetime <= 0 || $defaultLifetime < $item->defaultLifetime)) {
- $item->defaultLifetime = $defaultLifetime;
+ if (0 < $defaultLifetime) {
+ $item->expiresAfter($defaultLifetime);
}
return $item;
diff --git a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php
index 6524144962..b86b46bbb9 100644
--- a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/PdoAdapter.php
@@ -137,7 +137,11 @@ class PdoAdapter extends AbstractAdapter implements PruneableInterface
$table->setPrimaryKey([$this->idCol]);
foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
- $conn->exec($sql);
+ if (method_exists($conn, 'executeStatement')) {
+ $conn->executeStatement($sql);
+ } else {
+ $conn->exec($sql);
+ }
}
return;
@@ -168,7 +172,11 @@ class PdoAdapter extends AbstractAdapter implements PruneableInterface
throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
}
- $conn->exec($sql);
+ if (method_exists($conn, 'executeStatement')) {
+ $conn->executeStatement($sql);
+ } else {
+ $conn->exec($sql);
+ }
}
/**
@@ -280,7 +288,11 @@ class PdoAdapter extends AbstractAdapter implements PruneableInterface
}
try {
- $conn->exec($sql);
+ if (method_exists($conn, 'executeStatement')) {
+ $conn->executeStatement($sql);
+ } else {
+ $conn->exec($sql);
+ }
} catch (TableNotFoundException $e) {
} catch (\PDOException $e) {
}
diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
index bd96fa87b9..82a2deaf08 100644
--- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
@@ -33,6 +33,7 @@ class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterfa
private $createCacheItem;
private $setInnerItem;
private $poolHash;
+ private $defaultLifetime;
public function __construct(CacheItemPoolInterface $pool, string $namespace = '', int $defaultLifetime = 0)
{
@@ -40,8 +41,9 @@ class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterfa
$this->poolHash = $poolHash = spl_object_hash($pool);
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace);
$this->namespaceLen = \strlen($namespace);
+ $this->defaultLifetime = $defaultLifetime;
$this->createCacheItem = \Closure::bind(
- static function ($key, $innerItem) use ($defaultLifetime, $poolHash) {
+ static function ($key, $innerItem) use ($poolHash) {
$item = new CacheItem();
$item->key = $key;
@@ -52,7 +54,6 @@ class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterfa
$item->value = $v = $innerItem->get();
$item->isHit = $innerItem->isHit();
$item->innerItem = $innerItem;
- $item->defaultLifetime = $defaultLifetime;
$item->poolHash = $poolHash;
// Detect wrapped values that encode for their expiry and creation duration
@@ -223,8 +224,8 @@ class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterfa
return false;
}
$item = (array) $item;
- if (null === $item["\0*\0expiry"] && 0 < $item["\0*\0defaultLifetime"]) {
- $item["\0*\0expiry"] = microtime(true) + $item["\0*\0defaultLifetime"];
+ if (null === $item["\0*\0expiry"] && 0 < $this->defaultLifetime) {
+ $item["\0*\0expiry"] = microtime(true) + $this->defaultLifetime;
}
if ($item["\0*\0poolHash"] === $this->poolHash && $item["\0*\0innerItem"]) {
diff --git a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php
index 793df433f9..10375f7c07 100644
--- a/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php
+++ b/src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php
@@ -49,7 +49,6 @@ class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterfac
$item = new CacheItem();
$item->key = $key;
$item->value = $value;
- $item->defaultLifetime = $protoItem->defaultLifetime;
$item->expiry = $protoItem->expiry;
$item->poolHash = $protoItem->poolHash;
@@ -94,8 +93,7 @@ class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterfac
$this->invalidateTags = \Closure::bind(
static function (AdapterInterface $tagsAdapter, array $tags) {
foreach ($tags as $v) {
- $v->defaultLifetime = 0;
- $v->expiry = null;
+ $v->expiry = 0;
$tagsAdapter->saveDeferred($v);
}
diff --git a/src/Symfony/Component/Cache/CacheItem.php b/src/Symfony/Component/Cache/CacheItem.php
index d147a95b07..42d6a9247f 100644
--- a/src/Symfony/Component/Cache/CacheItem.php
+++ b/src/Symfony/Component/Cache/CacheItem.php
@@ -27,7 +27,6 @@ final class CacheItem implements ItemInterface
protected $value;
protected $isHit = false;
protected $expiry;
- protected $defaultLifetime;
protected $metadata = [];
protected $newMetadata = [];
protected $innerItem;
@@ -78,7 +77,7 @@ final class CacheItem implements ItemInterface
public function expiresAt($expiration): self
{
if (null === $expiration) {
- $this->expiry = $this->defaultLifetime > 0 ? microtime(true) + $this->defaultLifetime : null;
+ $this->expiry = null;
} elseif ($expiration instanceof \DateTimeInterface) {
$this->expiry = (float) $expiration->format('U.u');
} else {
@@ -96,7 +95,7 @@ final class CacheItem implements ItemInterface
public function expiresAfter($time): self
{
if (null === $time) {
- $this->expiry = $this->defaultLifetime > 0 ? microtime(true) + $this->defaultLifetime : null;
+ $this->expiry = null;
} elseif ($time instanceof \DateInterval) {
$this->expiry = microtime(true) + \DateTime::createFromFormat('U', 0)->add($time)->format('U.u');
} elseif (\is_int($time)) {
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php
index 99b38834f8..3e723f1b6a 100644
--- a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php
+++ b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php
@@ -31,7 +31,7 @@ class ChainAdapterTest extends AdapterTestCase
return new ChainAdapter([new FilesystemAdapter('a', $defaultLifetime), new FilesystemAdapter('b', $defaultLifetime)], $defaultLifetime);
}
- return new ChainAdapter([new ArrayAdapter($defaultLifetime), new ExternalAdapter(), new FilesystemAdapter('', $defaultLifetime)], $defaultLifetime);
+ return new ChainAdapter([new ArrayAdapter($defaultLifetime), new ExternalAdapter($defaultLifetime), new FilesystemAdapter('', $defaultLifetime)], $defaultLifetime);
}
public function testEmptyAdaptersException()
@@ -69,6 +69,124 @@ class ChainAdapterTest extends AdapterTestCase
$this->assertFalse($cache->prune());
}
+ public function testMultipleCachesExpirationWhenCommonTtlIsNotSet()
+ {
+ if (isset($this->skippedTests[__FUNCTION__])) {
+ $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
+ }
+
+ $adapter1 = new ArrayAdapter(4);
+ $adapter2 = new ArrayAdapter(2);
+
+ $cache = new ChainAdapter([$adapter1, $adapter2]);
+
+ $cache->save($cache->getItem('key')->set('value'));
+
+ $item = $adapter1->getItem('key');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value', $item->get());
+
+ $item = $adapter2->getItem('key');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value', $item->get());
+
+ sleep(2);
+
+ $item = $adapter1->getItem('key');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value', $item->get());
+
+ $item = $adapter2->getItem('key');
+ $this->assertFalse($item->isHit());
+
+ sleep(2);
+
+ $item = $adapter1->getItem('key');
+ $this->assertFalse($item->isHit());
+
+ $adapter2->save($adapter2->getItem('key1')->set('value1'));
+
+ $item = $cache->getItem('key1');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value1', $item->get());
+
+ sleep(2);
+
+ $item = $adapter1->getItem('key1');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value1', $item->get());
+
+ $item = $adapter2->getItem('key1');
+ $this->assertFalse($item->isHit());
+
+ sleep(2);
+
+ $item = $adapter1->getItem('key1');
+ $this->assertFalse($item->isHit());
+ }
+
+ public function testMultipleCachesExpirationWhenCommonTtlIsSet()
+ {
+ if (isset($this->skippedTests[__FUNCTION__])) {
+ $this->markTestSkipped($this->skippedTests[__FUNCTION__]);
+ }
+
+ $adapter1 = new ArrayAdapter(4);
+ $adapter2 = new ArrayAdapter(2);
+
+ $cache = new ChainAdapter([$adapter1, $adapter2], 6);
+
+ $cache->save($cache->getItem('key')->set('value'));
+
+ $item = $adapter1->getItem('key');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value', $item->get());
+
+ $item = $adapter2->getItem('key');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value', $item->get());
+
+ sleep(2);
+
+ $item = $adapter1->getItem('key');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value', $item->get());
+
+ $item = $adapter2->getItem('key');
+ $this->assertFalse($item->isHit());
+
+ sleep(2);
+
+ $item = $adapter1->getItem('key');
+ $this->assertFalse($item->isHit());
+
+ $adapter2->save($adapter2->getItem('key1')->set('value1'));
+
+ $item = $cache->getItem('key1');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value1', $item->get());
+
+ sleep(2);
+
+ $item = $adapter1->getItem('key1');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value1', $item->get());
+
+ $item = $adapter2->getItem('key1');
+ $this->assertFalse($item->isHit());
+
+ sleep(2);
+
+ $item = $adapter1->getItem('key1');
+ $this->assertTrue($item->isHit());
+ $this->assertEquals('value1', $item->get());
+
+ sleep(2);
+
+ $item = $adapter1->getItem('key1');
+ $this->assertFalse($item->isHit());
+ }
+
private function getPruneableMock(): AdapterInterface
{
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
index 4be8814977..dce846f10e 100644
--- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
+++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php
@@ -368,7 +368,7 @@ class ChoiceType extends AbstractType
'value' => $choiceView->value,
'label' => $choiceView->label,
'attr' => $choiceView->attr,
- 'translation_domain' => $options['translation_domain'],
+ 'translation_domain' => $options['choice_translation_domain'],
'block_name' => 'entry',
];
diff --git a/src/Symfony/Component/Form/Resources/translations/validators.en.xlf b/src/Symfony/Component/Form/Resources/translations/validators.en.xlf
index 89814258d1..97ed83fd47 100644
--- a/src/Symfony/Component/Form/Resources/translations/validators.en.xlf
+++ b/src/Symfony/Component/Form/Resources/translations/validators.en.xlf
@@ -18,6 +18,102 @@