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 c55532c

Browse filesBrowse files
committed
support memcached dsn using new memcacheclient helper
1 parent 7bc268e commit c55532c
Copy full SHA for c55532c

File tree

4 files changed

+438
-11
lines changed
Filter options

4 files changed

+438
-11
lines changed
+211Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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\Cache\Adapter\Client;
13+
14+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
15+
16+
/**
17+
* @author Rob Frawley 2nd <rmf@src.run>
18+
*
19+
* @internal
20+
*/
21+
final class MemcachedClient
22+
{
23+
private static $serverDefaults = array(
24+
'host' => 'localhost',
25+
'port' => 11211,
26+
'weight' => 100,
27+
);
28+
29+
private static $optionDefaults = array(
30+
'compression' => true,
31+
'binary_protocol' => true,
32+
'libketama_compatible' => true,
33+
);
34+
35+
private $client;
36+
private $errorLevel;
37+
38+
public function __construct(array $servers = array(), array $options = array())
39+
{
40+
$this->client = new \Memcached(isset($options['persistent_id']) ? $options['persistent_id'] : null);
41+
$this->setOptions($options);
42+
$this->setServers($servers);
43+
}
44+
45+
/**
46+
* @return \Memcached
47+
*/
48+
public static function create($servers = array(), array $options = array())
49+
{
50+
return (new static(is_array($servers) ? $servers : array($servers), $options))->getClient();
51+
}
52+
53+
public static function isSupported()
54+
{
55+
return extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
56+
}
57+
58+
public function getClient()
59+
{
60+
return $this->client;
61+
}
62+
63+
private function setOptions(array $options)
64+
{
65+
unset($options['persistent_id']);
66+
$options += static::$optionDefaults;
67+
68+
foreach (array_reverse($options) as $named => $value) {
69+
$this->addOption($named, $value);
70+
}
71+
}
72+
73+
private function addOption($named, $value)
74+
{
75+
$this->silenceErrorInitialize();
76+
$result = $this->client->setOption($this->resolveOptionNamed($named), $this->resolveOptionValue($named, $value));
77+
$this->silenceErrorRestoreAct(!$result, 'Invalid option: %s=%s', array(var_export($named, true), var_export($value, true)));
78+
}
79+
80+
private function resolveOptionNamed($named)
81+
{
82+
if (!defined($constant = sprintf('\Memcached::OPT_%s', strtoupper($named)))) {
83+
throw new InvalidArgumentException(sprintf('Invalid option named: %s', $named));
84+
}
85+
86+
return constant($constant);
87+
}
88+
89+
private function resolveOptionValue($named, $value)
90+
{
91+
$typed = preg_replace('{_.*$}', '', $named);
92+
93+
if (defined($constant = sprintf('\Memcached::%s_%s', strtoupper($typed), strtoupper($value)))
94+
|| defined($constant = sprintf('\Memcached::%s', strtoupper($value)))) {
95+
return constant($constant);
96+
}
97+
98+
return $value;
99+
}
100+
101+
private function setServers(array $dsns)
102+
{
103+
foreach ($dsns as $i => $dsn) {
104+
$this->addServer($i, $dsn);
105+
}
106+
}
107+
108+
private function addServer($i, $dsn)
109+
{
110+
if (false === $server = $this->resolveServer($dsn)) {
111+
throw new InvalidArgumentException(sprintf('Invalid server %d DSN: %s', $i, $dsn));
112+
}
113+
114+
if ($this->hasServer($server['host'], $server['port'])) {
115+
return;
116+
}
117+
118+
if (isset($server['user']) && isset($server['port'])) {
119+
$this->setServerAuthentication($server['user'], $server['pass']);
120+
}
121+
122+
$this->client->addServer($server['host'], $server['port'], $server['weight']);
123+
}
124+
125+
private function hasServer($host, $port)
126+
{
127+
foreach ($this->client->getServerList() as $server) {
128+
if ($server['host'] === $host && $server['port'] === $port) {
129+
return true;
130+
}
131+
}
132+
133+
return false;
134+
}
135+
136+
private function setServerAuthentication($user, $pass)
137+
{
138+
$this->silenceErrorInitialize();
139+
$result = $this->client->setSaslAuthData($user, $pass);
140+
$this->silenceErrorRestoreAct(!$result, 'Could not set SASL authentication:');
141+
}
142+
143+
private function resolveServer($dsn)
144+
{
145+
if (0 !== strpos($dsn, 'memcached')) {
146+
return false;
147+
}
148+
149+
if (false !== $server = $this->resolveServerAsHost($dsn)) {
150+
return $server;
151+
}
152+
153+
return $this->resolveServerAsSock($dsn);
154+
}
155+
156+
private function resolveServerAsHost($dsn)
157+
{
158+
if (false === $server = parse_url($dsn)) {
159+
return false;
160+
}
161+
162+
return $this->resolveServerCommon($server);
163+
}
164+
165+
private function resolveServerAsSock($dsn)
166+
{
167+
if (1 !== preg_match('{memcached:\/\/(?:(?<user>.+?):(?<pass>.+?)@)?(?<host>\/[^?]+)(?:\?)?(?<query>.+)?}', $dsn, $server)) {
168+
return false;
169+
}
170+
171+
if (0 === strpos(strrev($server['host']), '/')) {
172+
$server['host'] = substr($server['host'], 0, -1);
173+
}
174+
175+
return $this->resolveServerCommon(array_filter($server, function ($v, $i) {
176+
return !is_int($i) && !empty($v);
177+
}, ARRAY_FILTER_USE_BOTH));
178+
}
179+
180+
private function resolveServerCommon($server)
181+
{
182+
parse_str(isset($server['query']) ? $server['query'] : '', $query);
183+
184+
$server += array_filter($query, function ($index) {
185+
return in_array($index, array('weight'));
186+
}, ARRAY_FILTER_USE_KEY);
187+
188+
$server += static::$serverDefaults;
189+
190+
return array_filter($server, function ($index) {
191+
return in_array($index, array('host', 'port', 'weight', 'user', 'pass'));
192+
}, ARRAY_FILTER_USE_KEY);
193+
}
194+
195+
private function silenceErrorInitialize()
196+
{
197+
$this->errorLevel = error_reporting(~E_ALL);
198+
}
199+
200+
private function silenceErrorRestoreAct($throwError, $format, array $replacements = array())
201+
{
202+
error_reporting($this->errorLevel);
203+
204+
if ($throwError) {
205+
$errorRet = error_get_last();
206+
$errorMsg = isset($errorRet['message']) ? $errorRet['message'] : $this->client->getResultMessage();
207+
208+
throw new InvalidArgumentException(vsprintf($format.' (%s)', array_merge($replacements, array($errorMsg))));
209+
}
210+
}
211+
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php
+19-2Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Cache\Adapter;
1313

14+
use Symfony\Component\Cache\Adapter\Client\MemcachedClient;
15+
1416
/**
1517
* @author Rob Frawley 2nd <rmf@src.run>
1618
*/
@@ -24,9 +26,15 @@ public function __construct(\Memcached $client, $namespace = '', $defaultLifetim
2426
$this->client = $client;
2527
}
2628

27-
public static function isSupported()
29+
/**
30+
* @param string[] $servers
31+
* @param mixed[] $options
32+
*
33+
* @return \Memcached
34+
*/
35+
public static function createConnection($servers = array(), array $options = array())
2836
{
29-
return extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
37+
return MemcachedClient::create($servers, $options);
3038
}
3139

3240
/**
@@ -75,4 +83,13 @@ protected function doClear($namespace)
7583
{
7684
return $this->client->flush();
7785
}
86+
87+
public function __destruct()
88+
{
89+
parent::__destruct();
90+
91+
if (!$this->client->isPersistent() && method_exists($this->client, 'flushBuffers')) {
92+
$this->client->flushBuffers();
93+
}
94+
}
7895
}

0 commit comments

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