Skip to content

Commit 279342f

Browse files
committed
New config format
1 parent 7ef3f75 commit 279342f

File tree

5 files changed

+149
-81
lines changed

5 files changed

+149
-81
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

+22-28
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
1919
use Symfony\Component\Config\Definition\ConfigurationInterface;
2020
use Symfony\Component\Form\Form;
21+
use Symfony\Component\Lock\Lock;
2122
use Symfony\Component\Lock\Store\SemaphoreStore;
2223
use Symfony\Component\Serializer\Serializer;
2324
use Symfony\Component\Translation\Translator;
@@ -815,36 +816,29 @@ private function addLockSection(ArrayNodeDefinition $rootNode)
815816
->children()
816817
->arrayNode('lock')
817818
->info('Lock configuration')
818-
->canBeEnabled()
819+
->{!class_exists(FullStack::class) && class_exists(Lock::class) ? 'canBeDisabled' : 'canBeEnabled'}()
820+
->beforeNormalization()
821+
->ifString()->then(function ($v) { return array('enabled' => true, 'resources' => $v); })
822+
->end()
823+
->beforeNormalization()
824+
->ifTrue(function($v) { return is_array($v) && !isset($v['resources']); })
825+
->then(function ($v) { $e = $v['enabled']; unset($v['enabled']); return array('enabled' => $e, 'resources' => $v); })
826+
->end()
827+
->addDefaultsIfNotSet()
819828
->children()
820-
->arrayNode('flock')
821-
->canBeDisabled()
822-
->end()
823-
->arrayNode('semaphore')
824-
->{class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'canBeDisabled' : 'canBeEnabled'}()
825-
->end()
826-
->arrayNode('memcached')
827-
->canBeEnabled()
828-
->children()
829-
->arrayNode('hosts')
830-
->beforeNormalization()
831-
->ifTrue(function ($v) { return !is_array($v) && null !== $v; })
832-
->then(function ($v) { return is_bool($v) ? array() : preg_split('/\s*,\s*/', $v); })
833-
->end()
834-
->prototype('scalar')->end()
835-
->end()
829+
->arrayNode('resources')
830+
->requiresAtLeastOneElement()
831+
->defaultValue(array('default' => array(class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphore' : 'flock')))
832+
->beforeNormalization()
833+
->ifString()->then(function ($v) { return array('default' => $v); })
836834
->end()
837-
->end()
838-
->arrayNode('redis')
839-
->canBeEnabled()
840-
->children()
841-
->arrayNode('hosts')
842-
->beforeNormalization()
843-
->ifTrue(function ($v) { return !is_array($v) && null !== $v; })
844-
->then(function ($v) { return is_bool($v) ? array() : preg_split('/\s*,\s*/', $v); })
845-
->end()
846-
->prototype('scalar')->end()
847-
->end()
835+
->beforeNormalization()
836+
->ifTrue(function($v) { return is_array($v) && array_keys($v) === range(0, count($v) - 1); })
837+
->then(function ($v) { return array('default' => $v); })
838+
->end()
839+
->prototype('array')
840+
->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end()
841+
->prototype('scalar')->end()
848842
->end()
849843
->end()
850844
->end()

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

+64-52
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Bridge\Monolog\Processor\DebugProcessor;
1616
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
1717
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
18+
use Symfony\Component\Cache\Adapter\AbstractAdapter;
1819
use Symfony\Component\Cache\Adapter\AdapterInterface;
1920
use Symfony\Component\Cache\Adapter\ArrayAdapter;
2021
use Symfony\Component\Config\FileLocator;
@@ -28,6 +29,7 @@
2829
use Symfony\Component\DependencyInjection\ContainerBuilder;
2930
use Symfony\Component\DependencyInjection\ContainerInterface;
3031
use Symfony\Component\DependencyInjection\Definition;
32+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
3133
use Symfony\Component\DependencyInjection\Exception\LogicException;
3234
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
3335
use Symfony\Component\DependencyInjection\Reference;
@@ -41,6 +43,10 @@
4143
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
4244
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
4345
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
46+
use Symfony\Component\Lock\Lock;
47+
use Symfony\Component\Lock\Store\MemcachedStore;
48+
use Symfony\Component\Lock\Store\RedisStore;
49+
use Symfony\Component\Lock\Store\StoreFactory;
4450
use Symfony\Component\PropertyAccess\PropertyAccessor;
4551
use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface;
4652
use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface;
@@ -1410,69 +1416,75 @@ private function registerLockConfiguration(array $config, ContainerBuilder $cont
14101416

14111417
$container->getDefinition('lock.store.flock')->replaceArgument(0, sys_get_temp_dir());
14121418

1413-
// configure connectable stores
1414-
foreach (array('redis', 'memcached') as $store) {
1415-
if ($this->isConfigEnabled($container, $config[$store]) && count($config[$store]['hosts']) > 0) {
1416-
/** @var Reference[] $hostsDefinitions */
1417-
$hostsDefinitions = array();
1418-
foreach ($config[$store]['hosts'] as $host) {
1419-
$definition = new ChildDefinition('lock.store.'.$store.'.abstract');
1420-
1421-
// generate a service connection for the host
1422-
$container->resolveEnvPlaceholders($host, null, $usedEnvs);
1423-
if ($usedEnvs || preg_match('#^[a-z]++://#', $host)) {
1424-
$dsn = $host;
1419+
foreach ($config['resources'] as $resourceName => $resourceStores) {
1420+
if (0 === count($resourceStores)) {
1421+
continue;
1422+
}
14251423

1426-
if (!$container->hasDefinition($host = 'lock.connection.'.$store.'.'.md5($dsn))) {
1424+
// Generate stores
1425+
$storeDefinitions = array();
1426+
foreach ($resourceStores as $storeDsn) {
1427+
$storeDsn = $container->resolveEnvPlaceholders($storeDsn, null, $usedEnvs);
1428+
switch (true) {
1429+
case 'flock' === $storeDsn:
1430+
$storeDefinition = new Reference('lock.store.flock');
1431+
break;
1432+
case 'semaphore' === $storeDsn:
1433+
$storeDefinition = new Reference('lock.store.semaphore');
1434+
break;
1435+
case $usedEnvs || preg_match('#^[a-z]++://#', $storeDsn):
1436+
if (!$container->hasDefinition($connectionDefinitionId = md5($storeDsn))) {
14271437
$connectionDefinition = new Definition(\stdClass::class);
1428-
$connectionDefinition->setPublic(false);
1429-
$connectionDefinition->setFactory(array($container->getDefinition($definition->getParent())->getClass(), 'createConnection'));
1430-
$connectionDefinition->setArguments(array($dsn));
1431-
$container->setDefinition($host, $connectionDefinition);
1438+
$connectionDefinition ->setPublic(false);
1439+
$connectionDefinition ->setFactory(array(StoreFactory::class, 'createConnection'));
1440+
$connectionDefinition ->setArguments(array($storeDsn));
1441+
$container->setDefinition($connectionDefinitionId, $connectionDefinition );
14321442
}
1433-
}
14341443

1435-
$definition->replaceArgument(0, new Reference($host));
1436-
$container->setDefinition($name = 'lock.store.'.$store.'.'.md5($host), $definition);
1444+
$storeDefinition = new Definition(\stdClass::class);
1445+
$storeDefinition->setFactory(array(StoreFactory::class, 'createStore'));
1446+
$storeDefinition->setArguments(array(new Reference($connectionDefinitionId)));
14371447

1438-
$hostsDefinitions[] = new Reference($name);
1439-
}
1448+
$container->setDefinition($storeDefinitionId = 'lock.'.$resourceName.'.store.'.md5($storeDsn), $storeDefinition);
1449+
1450+
$storeDefinition = new Reference($storeDefinitionId);
1451+
break;
1452+
case $usedEnvs:
14401453

1441-
if (count($hostsDefinitions) > 1) {
1442-
$definition = new ChildDefinition('lock.store.combined.abstract');
1443-
$definition->replaceArgument(0, $hostsDefinitions);
1444-
$container->setDefinition('lock.store.'.$store, $definition);
1445-
} else {
1446-
$container->setAlias('lock.store.'.$store, new Alias((string) $hostsDefinitions[0]));
1454+
break;
1455+
default:
1456+
throw new InvalidArgumentException(sprintf('Lock store DSN "%s" is not valid in resource "%s"', $storeDsn, $resourceName));
14471457
}
1448-
}
1449-
}
14501458

1451-
// wrap non blocking store with retry mechanism
1452-
foreach (array('redis', 'memcached') as $store) {
1453-
if ($container->has($name = 'lock.store.'.$store)) {
1454-
$container->register($name.'.retry', 'Symfony\\Component\\Lock\\Store\\RetryTillSaveStore')
1455-
->setDecoratedService($name)
1456-
->addArgument(new Reference($name.'.retry.inner'))
1457-
->setPublic(false)
1458-
;
1459+
$storeDefinitions[] = $storeDefinition;
14591460
}
1460-
}
14611461

1462-
// generate factory for activated stores
1463-
$hasAlias = false;
1464-
// Order of stores matters: First enabled will be used in the default "lock.factory"
1465-
foreach (array('redis', 'memcached', 'semaphore', 'flock') as $store) {
1466-
if ($this->isConfigEnabled($container, $config[$store]) && $container->has('lock.store.'.$store)) {
1467-
$definition = new ChildDefinition('lock.factory.abstract');
1468-
$definition->replaceArgument(0, new Reference('lock.store.'.$store));
1469-
$definition->setPublic(true);
1470-
$container->setDefinition('lock.factory.'.$store, $definition);
1462+
// Wrap array of stores with CombinedStore
1463+
if (count($storeDefinitions) > 1) {
1464+
$combinedDefinition = new ChildDefinition('lock.store.combined.abstract');
1465+
$combinedDefinition->replaceArgument(0, $storeDefinitions);
1466+
$container->setDefinition('lock.'.$resourceName.'.store', $combinedDefinition);
1467+
} else {
1468+
$container->setAlias('lock.'.$resourceName.'.store', new Alias((string) $storeDefinitions[0]));
1469+
}
14711470

1472-
if (!$hasAlias) {
1473-
$container->setAlias('lock.factory', new Alias('lock.factory.'.$store));
1474-
$hasAlias = true;
1475-
}
1471+
// Generate factories for each resource
1472+
$factoryDefinition = new ChildDefinition('lock.factory.abstract');
1473+
$factoryDefinition->replaceArgument(0, new Reference('lock.'.$resourceName.'.store'));
1474+
$factoryDefinition->setPublic(true);
1475+
$container->setDefinition('lock.'.$resourceName.'.factory', $factoryDefinition);
1476+
1477+
// Generate services for lock instances
1478+
$lockDefinition = new Definition(Lock::class);
1479+
$lockDefinition->setFactory(array(new Reference('lock.'.$resourceName.'.factory'), 'createLock'));
1480+
$lockDefinition->setArguments(array($resourceName));
1481+
$container->setDefinition('lock.'.$resourceName, $lockDefinition);
1482+
1483+
// provide alias for default resource
1484+
if ('default' === $resourceName) {
1485+
$container->setAlias('lock.store', new Alias('lock.'.$resourceName.'.store'));
1486+
$container->setAlias('lock.factory', new Alias('lock.'.$resourceName.'.factory'));
1487+
$container->setAlias('lock', new Alias('lock.'.$resourceName));
14761488
}
14771489
}
14781490
}

src/Symfony/Bundle/FrameworkBundle/Resources/config/lock.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
<argument type="service" id="lock.strategy.majority" /> <!-- Strategy -->
2626
</service>
2727

28-
<service id="lock.strategy.majority" class="Symfony\Component\Lock\Strategy\MajorityStrategy" public="false" />
28+
<service id="lock.strategy.majority" class="Symfony\Component\Lock\Strategy\ConsensusStrategy" public="false" />
2929

3030
<service id="lock.factory.abstract" class="Symfony\Component\Lock\Factory" abstract="true" public="false">
3131
<tag name="monolog.logger" channel="lock" />

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

+9
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Bundle\FullStack;
1717
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1818
use Symfony\Component\Config\Definition\Processor;
19+
use Symfony\Component\Lock\Store\SemaphoreStore;
1920

2021
class ConfigurationTest extends TestCase
2122
{
@@ -241,6 +242,14 @@ protected static function getBundleDefaultConfig()
241242
'web_link' => array(
242243
'enabled' => !class_exists(FullStack::class),
243244
),
245+
'lock' => array(
246+
'enabled' => !class_exists(FullStack::class),
247+
'resources' => array(
248+
'default' => array(
249+
class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphore' : 'flock'
250+
)
251+
)
252+
)
244253
);
245254
}
246255
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Store;
13+
14+
use Psr\Log\LoggerAwareInterface;
15+
use Psr\Log\LoggerAwareTrait;
16+
use Psr\Log\NullLogger;
17+
use Symfony\Component\Lock\Exception\InvalidArgumentException;
18+
19+
/**
20+
* StoreFactory create stores and connections
21+
*
22+
* @author Jérémy Derussé <jeremy@derusse.com>
23+
*/
24+
class StoreFactory
25+
{
26+
public static function createConnection($dsn, array $options = [])
27+
{
28+
if (!is_string($dsn)) {
29+
throw new InvalidArgumentException(sprintf('The %s() method expect argument #1 to be string, %s given.', __METHOD__, gettype($dsn)));
30+
}
31+
if (0 === strpos($dsn, 'redis://')) {
32+
return RedisStore::createConnection($dsn, $options);
33+
}
34+
if (0 === strpos($dsn, 'memcached://')) {
35+
return MemcachedStore::createConnection($dsn, $options);
36+
}
37+
38+
throw new InvalidArgumentException(sprintf('Unsupported DSN: %s.', $dsn));
39+
}
40+
41+
public static function createStore($connection)
42+
{
43+
if ($connection instanceof \Redis || $connection instanceof \RedisArray || $connection instanceof \RedisCluster || $connection instanceof \Predis\Client) {
44+
return new RedisStore($connection);
45+
}
46+
47+
if ($connection instanceof \Memcached) {
48+
return new MemcachedStore($connection);
49+
}
50+
51+
throw new InvalidArgumentException(sprintf('Unsupported Connection: %s.', get_class($connection)));
52+
}
53+
}

0 commit comments

Comments
 (0)