Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8417f2d

Browse filesBrowse files
committed
Include lock component in framework bundle
1 parent db8d87d commit 8417f2d
Copy full SHA for 8417f2d

File tree

5 files changed

+386
-0
lines changed
Filter options

5 files changed

+386
-0
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+46Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\Config\Definition\ConfigurationInterface;
2020
use Symfony\Component\Form\Form;
2121
use Symfony\Component\HttpFoundation\Request;
22+
use Symfony\Component\Lock\Store\SemaphoreStore;
2223
use Symfony\Component\Serializer\Serializer;
2324
use Symfony\Component\Translation\Translator;
2425
use Symfony\Component\Validator\Validation;
@@ -133,6 +134,7 @@ public function getConfigTreeBuilder()
133134
$this->addPropertyInfoSection($rootNode);
134135
$this->addCacheSection($rootNode);
135136
$this->addPhpErrorsSection($rootNode);
137+
$this->addLockSection($rootNode);
136138

137139
return $treeBuilder;
138140
}
@@ -812,4 +814,48 @@ private function addPhpErrorsSection(ArrayNodeDefinition $rootNode)
812814
->end()
813815
;
814816
}
817+
818+
private function addLockSection(ArrayNodeDefinition $rootNode)
819+
{
820+
$rootNode
821+
->children()
822+
->arrayNode('lock')
823+
->info('Lock configuration')
824+
->canBeEnabled()
825+
->children()
826+
->arrayNode('flock')
827+
->canBeDisabled()
828+
->end()
829+
->arrayNode('semaphore')
830+
->{class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'canBeDisabled' : 'canBeEnabled'}()
831+
->end()
832+
->arrayNode('memcached')
833+
->canBeEnabled()
834+
->children()
835+
->arrayNode('hosts')
836+
->beforeNormalization()
837+
->ifTrue(function ($v) { return !is_array($v) && null !== $v; })
838+
->then(function ($v) { return is_bool($v) ? array() : preg_split('/\s*,\s*/', $v); })
839+
->end()
840+
->prototype('scalar')->end()
841+
->end()
842+
->end()
843+
->end()
844+
->arrayNode('redis')
845+
->canBeEnabled()
846+
->children()
847+
->arrayNode('hosts')
848+
->beforeNormalization()
849+
->ifTrue(function ($v) { return !is_array($v) && null !== $v; })
850+
->then(function ($v) { return is_bool($v) ? array() : preg_split('/\s*,\s*/', $v); })
851+
->end()
852+
->prototype('scalar')->end()
853+
->end()
854+
->end()
855+
->end()
856+
->end()
857+
->end()
858+
->end()
859+
;
860+
}
815861
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+84Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ public function load(array $configs, ContainerBuilder $container)
209209
$this->registerPropertyInfoConfiguration($config['property_info'], $container, $loader);
210210
}
211211

212+
if ($this->isConfigEnabled($container, $config['lock'])) {
213+
$this->registerLockConfiguration($config['lock'], $container, $loader);
214+
}
215+
212216
$this->addAnnotatedClassesToCompile(array(
213217
'**Bundle\\Controller\\',
214218
'**Bundle\\Entity\\',
@@ -1311,6 +1315,86 @@ private function registerPropertyInfoConfiguration(array $config, ContainerBuild
13111315
}
13121316
}
13131317

1318+
/**
1319+
* Loads lock configuration.
1320+
*
1321+
* @param array $config
1322+
* @param ContainerBuilder $container
1323+
* @param XmlFileLoader $loader
1324+
*/
1325+
private function registerLockConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
1326+
{
1327+
$loader->load('lock.xml');
1328+
1329+
$container->getDefinition('lock.store.flock')->replaceArgument(0, sys_get_temp_dir());
1330+
1331+
// configure connectable stores
1332+
foreach (array('redis', 'memcached') as $store) {
1333+
if ($this->isConfigEnabled($container, $config[$store]) && count($config[$store]['hosts']) > 0) {
1334+
/** @var Reference[] $hostsDefinitions */
1335+
$hostsDefinitions = array();
1336+
foreach ($config[$store]['hosts'] as $host) {
1337+
$definition = new ChildDefinition('lock.store.'.$store.'.abstract');
1338+
1339+
// generate a service connection for the host
1340+
$container->resolveEnvPlaceholders($host, null, $usedEnvs);
1341+
if ($usedEnvs || preg_match('#^[a-z]++://#', $host)) {
1342+
$dsn = $host;
1343+
1344+
if (!$container->hasDefinition($host = 'lock.connection.'.$store.'.'.md5($dsn))) {
1345+
$connectionDefinition = new Definition(\stdClass::class);
1346+
$connectionDefinition->setPublic(false);
1347+
$connectionDefinition->setFactory(array($container->getDefinition($definition->getParent())->getClass(), 'createConnection'));
1348+
$connectionDefinition->setArguments(array($dsn));
1349+
$container->setDefinition($host, $connectionDefinition);
1350+
}
1351+
}
1352+
1353+
$definition->replaceArgument(0, new Reference($host));
1354+
$container->setDefinition($name = 'lock.store.'.$store.'.'.md5($host), $definition);
1355+
1356+
$hostsDefinitions[] = new Reference($name);
1357+
}
1358+
1359+
if (count($hostsDefinitions) > 1) {
1360+
$definition = new ChildDefinition('lock.store.combined.abstract');
1361+
$definition->replaceArgument(0, $hostsDefinitions);
1362+
$container->setDefinition('lock.store.'.$store, $definition);
1363+
} else {
1364+
$container->setAlias('lock.store.'.$store, new Alias((string) $hostsDefinitions[0]));
1365+
}
1366+
}
1367+
}
1368+
1369+
// wrap non blocking store with retry mechanism
1370+
foreach (array('redis', 'memcached') as $store) {
1371+
if ($container->has($name = 'lock.store.'.$store)) {
1372+
$container->register($name.'.retry', 'Symfony\\Component\\Lock\\Store\\RetryTillSaveStore')
1373+
->setDecoratedService($name)
1374+
->addArgument(new Reference($name.'.retry.inner'))
1375+
->setPublic(false)
1376+
;
1377+
}
1378+
}
1379+
1380+
// generate factory for activated stores
1381+
$hasAlias = false;
1382+
// Order of stores matters: First enabled will be used in the default "lock.factory"
1383+
foreach (array('redis', 'memcached', 'semaphore', 'flock') as $store) {
1384+
if ($this->isConfigEnabled($container, $config[$store]) && $container->has('lock.store.'.$store)) {
1385+
$definition = new ChildDefinition('lock.factory.abstract');
1386+
$definition->replaceArgument(0, new Reference('lock.store.'.$store));
1387+
$definition->setPublic(true);
1388+
$container->setDefinition('lock.factory.'.$store, $definition);
1389+
1390+
if (!$hasAlias) {
1391+
$container->setAlias('lock.factory', new Alias('lock.factory.'.$store));
1392+
$hasAlias = true;
1393+
}
1394+
}
1395+
}
1396+
}
1397+
13141398
private function registerCacheConfiguration(array $config, ContainerBuilder $container)
13151399
{
13161400
$version = substr(str_replace('/', '-', base64_encode(hash('sha256', uniqid(mt_rand(), true), true))), 0, 22);
+39Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
9+
<service id="lock.store.flock" class="Symfony\Component\Lock\Store\FlockStore" public="false">
10+
<argument /> <!-- lock directory -->
11+
</service>
12+
13+
<service id="lock.store.semaphore" class="Symfony\Component\Lock\Store\SemaphoreStore" public="false" />
14+
15+
<service id="lock.store.memcached.abstract" class="Symfony\Component\Lock\Store\MemcachedStore" abstract="true" public="false">
16+
<argument /> <!-- Memcached connection service -->
17+
</service>
18+
19+
<service id="lock.store.redis.abstract" class="Symfony\Component\Lock\Store\RedisStore" abstract="true" public="false">
20+
<argument /> <!-- Redis connection service -->
21+
</service>
22+
23+
<service id="lock.store.combined.abstract" class="Symfony\Component\Lock\Store\CombinedStore" abstract="true" public="false">
24+
<argument /> <!-- List of stores -->
25+
<argument type="service" id="lock.strategy.majority" /> <!-- Strategy -->
26+
</service>
27+
28+
<service id="lock.strategy.majority" class="Symfony\Component\Lock\Strategy\MajorityStrategy" public="false" />
29+
30+
<service id="lock.factory.abstract" class="Symfony\Component\Lock\Factory" abstract="true" public="false">
31+
<tag name="monolog.logger" channel="lock" />
32+
<argument /> <!-- Store -->
33+
<call method="setLogger">
34+
<argument type="service" id="logger" on-invalid="ignore" />
35+
</call>
36+
</service>
37+
38+
</services>
39+
</container>

‎src/Symfony/Component/Lock/Store/MemcachedStore.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Lock/Store/MemcachedStore.php
+127Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
*/
2424
class MemcachedStore implements StoreInterface
2525
{
26+
private static $defaultClientOptions = array(
27+
'persistent_id' => null,
28+
'username' => null,
29+
'password' => null,
30+
);
31+
2632
private $memcached;
2733
private $initialTtl;
2834
/** @var bool */
@@ -51,6 +57,127 @@ public function __construct(\Memcached $memcached, $initialTtl = 300)
5157
$this->initialTtl = $initialTtl;
5258
}
5359

60+
/**
61+
* Creates a Memcached instance.
62+
*
63+
* By default, the binary protocol, block, and libketama compatible options are enabled.
64+
*
65+
* Example DSN:
66+
* - 'memcached://user:pass@localhost?weight=33'
67+
* - array(array('localhost', 11211, 33))
68+
*
69+
* @param string $dsn
70+
* @param array $options See self::$defaultConnectionOptions
71+
*
72+
* @return \Memcached
73+
*
74+
* @throws \ErrorEception When invalid options or dsn are provided
75+
*/
76+
public static function createConnection($dsn, array $options = array())
77+
{
78+
if (0 !== strpos($dsn, 'memcached://')) {
79+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s does not start with "memcached://"', $dsn));
80+
}
81+
if (!static::isSupported()) {
82+
throw new InvalidArgumentException('Memcached extension is required');
83+
}
84+
set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); });
85+
try {
86+
$options += static::$defaultClientOptions;
87+
$client = new \Memcached($options['persistent_id']);
88+
$username = $options['username'];
89+
$password = $options['password'];
90+
unset($options['persistent_id'], $options['username'], $options['password']);
91+
$options = array_change_key_case($options, CASE_UPPER);
92+
93+
// set client's options
94+
$client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
95+
$client->setOption(\Memcached::OPT_NO_BLOCK, false);
96+
if (!array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) {
97+
$client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
98+
}
99+
foreach ($options as $name => $value) {
100+
if (is_int($name)) {
101+
continue;
102+
}
103+
if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) {
104+
$value = constant('Memcached::'.$name.'_'.strtoupper($value));
105+
}
106+
$opt = constant('Memcached::OPT_'.$name);
107+
108+
unset($options[$name]);
109+
$options[$opt] = $value;
110+
}
111+
$client->setOptions($options);
112+
113+
// parse any DSN in $servers
114+
$params = preg_replace_callback('#^memcached://(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) {
115+
if (!empty($m[1])) {
116+
list($username, $password) = explode(':', $m[1], 2) + array(1 => null);
117+
}
118+
119+
return 'file://';
120+
}, $dsn);
121+
if (false === $params = parse_url($params)) {
122+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
123+
}
124+
if (!isset($params['host']) && !isset($params['path'])) {
125+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
126+
}
127+
if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) {
128+
$params['weight'] = $m[1];
129+
$params['path'] = substr($params['path'], 0, -strlen($m[0]));
130+
}
131+
$params += array(
132+
'host' => isset($params['host']) ? $params['host'] : $params['path'],
133+
'port' => isset($params['host']) ? 11211 : null,
134+
'weight' => 0,
135+
);
136+
if (isset($params['query'])) {
137+
parse_str($params['query'], $query);
138+
$params += $query;
139+
}
140+
141+
$servers = array(array($params['host'], $params['port'], $params['weight']));
142+
143+
// set client's servers, taking care of persistent connections
144+
if (!$client->isPristine()) {
145+
$oldServers = array();
146+
foreach ($client->getServerList() as $server) {
147+
$oldServers[] = array($server['host'], $server['port']);
148+
}
149+
150+
$newServers = array();
151+
foreach ($servers as $server) {
152+
if (1 < count($server)) {
153+
$server = array_values($server);
154+
unset($server[2]);
155+
$server[1] = (int) $server[1];
156+
}
157+
$newServers[] = $server;
158+
}
159+
160+
if ($oldServers !== $newServers) {
161+
// before resetting, ensure $servers is valid
162+
$client->addServers($servers);
163+
$client->resetServerList();
164+
}
165+
}
166+
$client->addServers($servers);
167+
168+
if (null !== $username || null !== $password) {
169+
if (!method_exists($client, 'setSaslAuthData')) {
170+
trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.');
171+
}
172+
$client->setSaslAuthData($username, $password);
173+
}
174+
175+
return $client;
176+
} finally {
177+
restore_error_handler();
178+
}
179+
}
180+
54181
/**
55182
* {@inheritdoc}
56183
*/

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.