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 3947b38

Browse filesBrowse files
committed
memcached cache adapter configurable with dsn/options
1 parent 51bc35c commit 3947b38
Copy full SHA for 3947b38

File tree

2 files changed

+405
-0
lines changed
Filter options

2 files changed

+405
-0
lines changed
+201Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
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;
13+
14+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
15+
16+
/**
17+
* @author Rob Frawley 2nd <rmf@src.run>
18+
*/
19+
class MemcachedAdapter extends AbstractAdapter
20+
{
21+
private static $defaultClientServer = array(
22+
'host' => '127.0.0.1',
23+
'port' => 11211,
24+
'weight' => 100,
25+
);
26+
27+
private $client;
28+
29+
public static function isSupported()
30+
{
31+
return extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
32+
}
33+
34+
public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0)
35+
{
36+
parent::__construct($namespace, $defaultLifetime);
37+
$this->client = $client;
38+
}
39+
40+
/**
41+
* Factory method to create adapter setup with configured \Memcache client instance.
42+
*
43+
* Valid DSN values include the following:
44+
* - memcached://localhost : Specifies only the host (defaults used for port and weight)
45+
* - memcached://example.com:1234 : Specifies host and port (defaults weight)
46+
* - memcached://example.com:1234?weight=50 : Specifies host, port, and weight (no defaults used)
47+
*
48+
* Options are expected to be passed as an associative array with indexes of the option type with corresponding
49+
* values as the option assignment. Valid options include any client constants, as described in the PHP manual:
50+
* - http://php.net/manual/en/memcached.constants.php
51+
*
52+
* @param string|null $dsn
53+
* @param array $opts
54+
* @param string|null $persistentId
55+
*
56+
* @return MemcachedAdapter
57+
*/
58+
public static function create($dsn = null, array $opts = array(), $persistentId = null)
59+
{
60+
$adapter = new static(new \Memcached($persistentId));
61+
$adapter->setup($dsn ? array($dsn) : array(), $opts);
62+
63+
return $adapter;
64+
}
65+
66+
/**
67+
* @param string[] $dsns
68+
* @param mixed[] $opts
69+
*
70+
* @return $this
71+
*/
72+
public function setup(array $dsns = array(), array $opts = array())
73+
{
74+
foreach ($opts as $opt => $val) {
75+
$this->setOption($opt, $val);
76+
}
77+
foreach ($dsns as $dsn) {
78+
$this->addServer($dsn);
79+
}
80+
81+
return $this;
82+
}
83+
84+
/**
85+
* {@inheritdoc}
86+
*/
87+
protected function doSave(array $values, $lifetime)
88+
{
89+
return $this->client->setMulti($values, $lifetime)
90+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS;
91+
}
92+
93+
/**
94+
* {@inheritdoc}
95+
*/
96+
protected function doFetch(array $ids)
97+
{
98+
return $this->client->getMulti($ids);
99+
}
100+
101+
/**
102+
* {@inheritdoc}
103+
*/
104+
protected function doHave($id)
105+
{
106+
return $this->client->get($id) !== false
107+
|| $this->client->getResultCode() === \Memcached::RES_SUCCESS;
108+
}
109+
/**
110+
* {@inheritdoc}
111+
*/
112+
protected function doDelete(array $ids)
113+
{
114+
$toDelete = count($ids);
115+
foreach ((array) $this->client->deleteMulti($ids) as $result) {
116+
if (true === $result || \Memcached::RES_NOTFOUND === $result) {
117+
--$toDelete;
118+
}
119+
}
120+
121+
return 0 === $toDelete;
122+
}
123+
124+
/**
125+
* {@inheritdoc}
126+
*/
127+
protected function doClear($namespace)
128+
{
129+
if (!isset($namespace[0]) || false === $ids = $this->getIdsByPrefix($namespace)) {
130+
return $this->client->flush();
131+
}
132+
133+
$isCleared = true;
134+
do {
135+
$isCleared = $this->doDelete($ids) && $isCleared;
136+
} while ($ids = $this->getIdsByPrefix($namespace));
137+
138+
return $isCleared;
139+
}
140+
141+
private function getIdsByPrefix($namespace)
142+
{
143+
if (false === $ids = $this->client->getAllKeys()) {
144+
return false;
145+
}
146+
147+
return array_filter((array) $ids, function ($id) use ($namespace) {
148+
return 0 === strpos($id, $namespace);
149+
});
150+
}
151+
152+
private function setOption($opt, $val)
153+
{
154+
return $this->client->setOption(
155+
$this->resolveOption($opt),
156+
$this->resolveOption($val)
157+
);
158+
}
159+
160+
private function resolveOption($val)
161+
{
162+
return defined($constant = '\Memcached::'.strtoupper($val)) ? constant($constant) : $val;
163+
}
164+
165+
private function addServer($dsn)
166+
{
167+
list($host, $port, $weight) = $this->parseServerDSN($dsn);
168+
169+
return $this->isServerInClientPool($host, $port)
170+
|| ($this->client->addServer($host, $port, $weight)
171+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS);
172+
}
173+
174+
private function parseServerDSN($dsn)
175+
{
176+
if (false === ($srv = parse_url($dsn)) || $srv['scheme'] !== 'memcached' || count($srv) > 4) {
177+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s (expected "memcached://[<string>[:<int>]][?weight=<int>]")', $dsn));
178+
}
179+
180+
unset($srv['scheme']);
181+
$srv += self::$defaultClientServer;
182+
183+
if (isset($srv['query']) && 1 === preg_match('{weight=([^&]{1,})}', $srv['query'], $weight)) {
184+
unset($srv['query']);
185+
$srv['weight'] = (int) $weight[1];
186+
}
187+
188+
return array_values($srv);
189+
}
190+
191+
private function isServerInClientPool($host, $port)
192+
{
193+
foreach ($this->client->getServerList() as $srv) {
194+
if ($host === $srv['host'] && $port === $srv['port']) {
195+
return true;
196+
}
197+
}
198+
199+
return false;
200+
}
201+
}

0 commit comments

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