Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9083e84

Browse files
[Cache] Add DSN, createClient & better error reporting to MemcachedAdapter
1 parent 61a67ec commit 9083e84

File tree

4 files changed

+183
-15
lines changed

4 files changed

+183
-15
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@ public static function getServiceProvider(ContainerBuilder $container, $name)
108108
{
109109
$container->resolveEnvPlaceholders($name, null, $usedEnvs);
110110

111-
if (0 === strpos($name, 'redis://') || $usedEnvs) {
111+
if ($usedEnvs || preg_match('#^[a-z]++://#', $name)) {
112112
$dsn = $name;
113113

114114
if (!$container->hasDefinition($name = md5($dsn))) {
115-
$definition = new Definition(\Redis::class);
115+
$definition = new Definition('_');
116116
$definition->setPublic(false);
117-
$definition->setFactory(array(RedisAdapter::class, 'createConnection'));
117+
$definition->setFactory(array(AbstractAdapter::class, 'createConnection'));
118118
$definition->setArguments(array($dsn));
119119
$container->setDefinition($name, $definition);
120120
}

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
],
1818
"require": {
1919
"php": ">=5.5.9",
20-
"symfony/cache": "~3.2",
20+
"symfony/cache": "~3.3",
2121
"symfony/class-loader": "~3.2",
2222
"symfony/dependency-injection": "~3.3",
2323
"symfony/config": "~2.8|~3.0",

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,21 @@ public static function createSystemCache($namespace, $defaultLifetime, $version,
115115
return new ChainAdapter(array($apcu, $fs));
116116
}
117117

118+
public static function createConnection($dsn, array $options = array())
119+
{
120+
if (!is_string($dsn)) {
121+
throw new InvalidArgumentException(sprintf('The %s() method expect argument #1 to be string, %s given.', __METHOD__, gettype($dsn)));
122+
}
123+
if (0 === strpos($dsn, 'redis://')) {
124+
return RedisAdapter::createConnection($dsn, $options);
125+
}
126+
if (0 === strpos($dsn, 'memcached://')) {
127+
return MemcachedAdapter::createConnection($dsn, $options);
128+
}
129+
130+
throw new InvalidArgumentException(sprintf('Unsupported DSN: %s.', $dsn));
131+
}
132+
118133
/**
119134
* Fetches several cache items.
120135
*

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

Lines changed: 164 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,68 +11,221 @@
1111

1212
namespace Symfony\Component\Cache\Adapter;
1313

14+
use Symfony\Component\Cache\Exception\CacheException;
15+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
16+
1417
/**
1518
* @author Rob Frawley 2nd <[email protected]>
19+
* @author Nicolas Grekas <[email protected]>
1620
*/
1721
class MemcachedAdapter extends AbstractAdapter
1822
{
23+
private static $defaultClientOptions = array(
24+
'persistent_id' => null,
25+
'username' => null,
26+
'password' => null,
27+
'binary_protocol' => true,
28+
'libketama_compatible' => true,
29+
);
30+
31+
protected $maxIdLength = 250;
32+
1933
private $client;
2034

35+
public static function isSupported()
36+
{
37+
return extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
38+
}
39+
2140
public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0)
2241
{
42+
if (!static::isSupported()) {
43+
throw new CacheException('Memcached >= 2.2.0 is required');
44+
}
45+
$opt = $client->getOption(\Memcached::OPT_SERIALIZER);
46+
if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) {
47+
throw new CacheException('MemcachedAdapter: serializer must be PHP or IGBINARY.');
48+
}
49+
if (!$client->getOption(\Memcached::OPT_BINARY_PROTOCOL)) {
50+
throw new CacheException('MemcachedAdapter: OPT_BINARY_PROTOCOL must be used.');
51+
}
52+
if ($client->getOption(\Memcached::OPT_NO_BLOCK)) {
53+
throw new CacheException('MemcachedAdapter: OPT_NO_BLOCK must be disabled.');
54+
}
55+
$this->maxIdLength -= strlen($client->getOption(\Memcached::OPT_PREFIX_KEY));
56+
2357
parent::__construct($namespace, $defaultLifetime);
2458
$this->client = $client;
2559
}
2660

27-
public static function isSupported()
61+
/**
62+
* @return \Memcached
63+
*
64+
* @throws \ErrorEception When invalid options or servers are provided.
65+
*/
66+
public static function createConnection($servers, array $options = array())
2867
{
29-
return extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
68+
if (is_string($servers)) {
69+
$servers = array($servers);
70+
} elseif (!is_array($servers)) {
71+
throw new InvalidArgumentException(sprintf('MemcachedAdapter::createClient() expects array or string as first argument, %s given.', get_type($servers)));
72+
}
73+
set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); });
74+
try {
75+
$options += static::$defaultClientOptions;
76+
$client = new \Memcached($options['persistent_id']);
77+
$username = $options['username'];
78+
$password = $options['password'];
79+
unset($options['persistent_id'], $options['username'], $options['password']);
80+
81+
// set client's options
82+
foreach ($options as $name => $value) {
83+
if (is_int($name)) {
84+
continue;
85+
}
86+
$ucName = strtoupper($name);
87+
$opt = constant('Memcached::OPT_'.$ucName);
88+
89+
if ('HASH' === $ucName || 'SERIALIZER' === $ucName || 'DISTRIBUTION' === $ucName) {
90+
$value = constant('Memcached::'.$ucName.'_'.strtoupper($value));
91+
}
92+
93+
unset($options[$name]);
94+
$options[$opt] = $value;
95+
}
96+
$client->setOptions($options);
97+
98+
// parse any DSN in $servers
99+
foreach ($servers as $i => $dsn) {
100+
if (is_array($dsn)) {
101+
continue;
102+
}
103+
if (0 !== strpos($dsn, 'memcached://')) {
104+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s does not start with "memcached://"', $dsn));
105+
}
106+
$params = preg_replace_callback('#^memcached://(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) {
107+
if (!empty($m[1])) {
108+
list($username, $password) = explode(':', $m[1], 2) + array(1 => null);
109+
}
110+
111+
return 'file://';
112+
}, $dsn);
113+
if (false === $params = parse_url($params)) {
114+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
115+
}
116+
if (!isset($params['host']) && !isset($params['path'])) {
117+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
118+
}
119+
if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) {
120+
$params['weight'] = $m[1];
121+
$params['path'] = substr($params['path'], 0, -strlen($m[0]));
122+
}
123+
$params += array(
124+
'host' => isset($params['host']) ? $params['host'] : $params['path'],
125+
'port' => isset($params['host']) ? 11211 : null,
126+
'weight' => 0,
127+
);
128+
if (isset($params['query'])) {
129+
parse_str($params['query'], $query);
130+
$params += $query;
131+
}
132+
133+
$servers[$i] = array($params['host'], $params['port'], $params['weight']);
134+
}
135+
136+
// set client's servers, taking care of persistent connections
137+
if (!$client->isPristine()) {
138+
$oldServers = array();
139+
foreach ($client->getServerList() as $server) {
140+
$oldServers[] = array($server['host'], $server['port']);
141+
}
142+
143+
$newServers = array();
144+
foreach ($servers as $server) {
145+
if (1 < count($server)) {
146+
$server = array_values($server);
147+
unset($server[2]);
148+
$server[1] = (int) $server[1];
149+
}
150+
$newServers[] = $server;
151+
}
152+
153+
if ($oldServers !== $newServers) {
154+
// before resetting, ensure $servers is valid
155+
$client->addServers($servers);
156+
$client->resetServerList();
157+
}
158+
}
159+
$client->addServers($servers);
160+
161+
if (null !== $username || null !== $password) {
162+
if (!method_exists($client, 'setSaslAuthData')) {
163+
trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.');
164+
}
165+
$client->setSaslAuthData($username, $password);
166+
}
167+
168+
return $client;
169+
} finally {
170+
restore_error_handler();
171+
}
30172
}
31173

32174
/**
33175
* {@inheritdoc}
34176
*/
35177
protected function doSave(array $values, $lifetime)
36178
{
37-
return $this->client->setMulti($values, $lifetime) && $this->client->getResultCode() === \Memcached::RES_SUCCESS;
179+
return $this->checkResultCode($this->client->setMulti($values, $lifetime));
38180
}
39181

40182
/**
41183
* {@inheritdoc}
42184
*/
43185
protected function doFetch(array $ids)
44186
{
45-
return $this->client->getMulti($ids);
187+
return $this->checkResultCode($this->client->getMulti($ids));
46188
}
47189

48190
/**
49191
* {@inheritdoc}
50192
*/
51193
protected function doHave($id)
52194
{
53-
return $this->client->get($id) !== false || $this->client->getResultCode() === \Memcached::RES_SUCCESS;
195+
return false !== $this->client->get($id) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode());
54196
}
55197

56198
/**
57199
* {@inheritdoc}
58200
*/
59201
protected function doDelete(array $ids)
60202
{
61-
$toDelete = count($ids);
62-
foreach ($this->client->deleteMulti($ids) as $result) {
63-
if (\Memcached::RES_SUCCESS === $result || \Memcached::RES_NOTFOUND === $result) {
64-
--$toDelete;
203+
$ok = true;
204+
foreach ($this->checkResultCode($this->client->deleteMulti($ids)) as $result) {
205+
if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) {
206+
$ok = false;
65207
}
66208
}
67209

68-
return 0 === $toDelete;
210+
return $ok;
69211
}
70212

71213
/**
72214
* {@inheritdoc}
73215
*/
74216
protected function doClear($namespace)
75217
{
76-
return $this->client->flush();
218+
return $this->checkResultCode($this->client->flush());
219+
}
220+
221+
private function checkResultCode($result)
222+
{
223+
$code = $this->client->getResultCode();
224+
225+
if (\Memcached::RES_SUCCESS === $code || \Memcached::RES_NOTFOUND === $code) {
226+
return $result;
227+
}
228+
229+
throw new CacheException(sprintf('MemcachedAdapter client error: %s.', strtolower($this->client->getResultMessage())));
77230
}
78231
}

0 commit comments

Comments
 (0)