diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
index 5fc271c0512d0..e285ebf8a13cb 100644
--- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
+++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
@@ -5,6 +5,7 @@ CHANGELOG
-----
* Allowed configuring taggable cache pools via a new `framework.cache.pools.tags` option (bool|service-id)
+ * Allowed configuring PDO-based cache pools via a new `cache.adapter.pdo` abstract service
* Deprecated auto-injection of the container in AbstractController instances, register them as service subscribers instead
* Deprecated processing of services tagged `security.expression_language_provider` in favor of a new `AddExpressionLanguageProvidersPass` in SecurityBundle.
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
index 98e0e0e4b8444..131d7dc66c610 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
@@ -866,6 +866,7 @@ private function addCacheSection(ArrayNodeDefinition $rootNode)
->scalarNode('default_psr6_provider')->end()
->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end()
->scalarNode('default_memcached_provider')->defaultValue('memcached://localhost')->end()
+ ->scalarNode('default_pdo_provider')->defaultValue('doctrine.dbal.default_connection')->end()
->arrayNode('pools')
->useAttributeAsKey('name')
->prototype('array')
diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
index b5f4b4d9c366c..1ab8e9c4d8587 100644
--- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
@@ -1547,7 +1547,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con
// Inline any env vars referenced in the parameter
$container->setParameter('cache.prefix.seed', $container->resolveEnvPlaceholders($container->getParameter('cache.prefix.seed'), true));
}
- foreach (array('doctrine', 'psr6', 'redis', 'memcached') as $name) {
+ foreach (array('doctrine', 'psr6', 'redis', 'memcached', 'pdo') as $name) {
if (isset($config[$name = 'default_'.$name.'_provider'])) {
$container->setAlias('cache.'.$name, new Alias(Compiler\CachePoolPass::getServiceProvider($container, $config[$name]), false));
}
diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml
index d31be8db3fc90..a4d082e4e4ce1 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml
+++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/cache.xml
@@ -109,6 +109,19 @@
+
+
+
+
+
+ 0
+
+
+
+
+
+
+
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
index 58f7e564c97f3..c148e87162c86 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php
@@ -266,6 +266,7 @@ protected static function getBundleDefaultConfig()
'directory' => '%kernel.cache_dir%/pools',
'default_redis_provider' => 'redis://localhost',
'default_memcached_provider' => 'memcached://localhost',
+ 'default_pdo_provider' => 'doctrine.dbal.default_connection',
),
'workflows' => array(
'enabled' => false,
diff --git a/src/Symfony/Component/Cache/CHANGELOG.md b/src/Symfony/Component/Cache/CHANGELOG.md
index 98cf028026a61..13884de905333 100644
--- a/src/Symfony/Component/Cache/CHANGELOG.md
+++ b/src/Symfony/Component/Cache/CHANGELOG.md
@@ -7,6 +7,7 @@ CHANGELOG
* added `CacheInterface`, which provides stampede protection via probabilistic early expiration and should become the preferred way to use a cache
* added sub-second expiry accuracy for backends that support it
* added support for phpredis 4 `compression` and `tcp_keepalive` options
+ * added automatic table creation when using Doctrine DBAL with PDO-based backends
* throw `LogicException` when `CacheItem::tag()` is called on an item coming from a non tag-aware pool
* deprecated `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead
* deprecated the `AbstractAdapter::createSystemCache()` method
diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php
index 1e8c6155bdd35..f585c35524c4f 100644
--- a/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php
+++ b/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php
@@ -33,7 +33,6 @@ public static function setupBeforeClass()
self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
$pool = new PdoAdapter(DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'path' => self::$dbFile)));
- $pool->createTable();
}
public static function tearDownAfterClass()
diff --git a/src/Symfony/Component/Cache/Traits/PdoTrait.php b/src/Symfony/Component/Cache/Traits/PdoTrait.php
index a88099ecb183e..19a94c9acda75 100644
--- a/src/Symfony/Component/Cache/Traits/PdoTrait.php
+++ b/src/Symfony/Component/Cache/Traits/PdoTrait.php
@@ -14,6 +14,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\DBALException;
+use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Schema\Schema;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
@@ -150,7 +151,11 @@ public function prune()
$deleteSql .= " AND $this->idCol LIKE :namespace";
}
- $delete = $this->getConnection()->prepare($deleteSql);
+ try {
+ $delete = $this->getConnection()->prepare($deleteSql);
+ } catch (TableNotFoundException $e) {
+ return true;
+ }
$delete->bindValue(':time', time(), \PDO::PARAM_INT);
if ('' !== $this->namespace) {
@@ -229,7 +234,10 @@ protected function doClear($namespace)
$sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'";
}
- $conn->exec($sql);
+ try {
+ $conn->exec($sql);
+ } catch (TableNotFoundException $e) {
+ }
return true;
}
@@ -241,8 +249,11 @@ protected function doDelete(array $ids)
{
$sql = str_pad('', (count($ids) << 1) - 1, '?,');
$sql = "DELETE FROM $this->table WHERE $this->idCol IN ($sql)";
- $stmt = $this->getConnection()->prepare($sql);
- $stmt->execute(array_values($ids));
+ try {
+ $stmt = $this->getConnection()->prepare($sql);
+ $stmt->execute(array_values($ids));
+ } catch (TableNotFoundException $e) {
+ }
return true;
}
@@ -302,7 +313,14 @@ protected function doSave(array $values, $lifetime)
$now = time();
$lifetime = $lifetime ?: null;
- $stmt = $conn->prepare($sql);
+ try {
+ $stmt = $conn->prepare($sql);
+ } catch (TableNotFoundException $e) {
+ if (!$conn->isTransactionActive() || \in_array($this->driver, array('pgsql', 'sqlite', 'sqlsrv'), true)) {
+ $this->createTable();
+ }
+ $stmt = $conn->prepare($sql);
+ }
if ('sqlsrv' === $driver || 'oci' === $driver) {
$stmt->bindParam(1, $id);
diff --git a/src/Symfony/Component/Cache/composer.json b/src/Symfony/Component/Cache/composer.json
index e76644186efae..b043aeb58b47d 100644
--- a/src/Symfony/Component/Cache/composer.json
+++ b/src/Symfony/Component/Cache/composer.json
@@ -28,11 +28,12 @@
"require-dev": {
"cache/integration-tests": "dev-master",
"doctrine/cache": "~1.6",
- "doctrine/dbal": "~2.4",
+ "doctrine/dbal": "~2.5",
"predis/predis": "~1.0",
"symfony/var-dumper": "^4.1.1"
},
"conflict": {
+ "doctrine/dbal": "<2.5",
"symfony/var-dumper": "<3.4"
},
"autoload": {