Skip to content

Commit 6b637a7

Browse files
committed
initial cleanup of memcached cache adapter for upstream
squashed commits: - fabbot.io diff applied to fix style issues - removal of phpunit 5.x features to support legacy PHP versions, changes to texting server array for HHVM fix - fabbot.io patch for style fix - removed unnessissary abstract test class (copied from redis tests during initial creation of the tests for this code
1 parent 60b4dd0 commit 6b637a7

File tree

2 files changed

+415
-0
lines changed

2 files changed

+415
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
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+
const DEFAULT_HOST = '127.0.0.1';
22+
const DEFAULT_PORT = 11211;
23+
const DEFAULT_WEIGHT = -1;
24+
25+
/**
26+
* @var \Memcached
27+
*/
28+
private $memcached;
29+
30+
/**
31+
* @param \Memcached $memcachedClient
32+
* @param string $namespace
33+
* @param int $defaultLifetime
34+
*/
35+
public function __construct(\Memcached $memcachedClient, $namespace = '', $defaultLifetime = 0)
36+
{
37+
parent::__construct($namespace, $defaultLifetime);
38+
39+
$this->memcached = $memcachedClient;
40+
}
41+
42+
/**
43+
* @param string|null $persistentId
44+
*
45+
* @return \Memcached
46+
*/
47+
public static function createClient($persistentId = null)
48+
{
49+
return new \Memcached($persistentId);
50+
}
51+
52+
/**
53+
* @param array $servers
54+
* @param array $options
55+
*
56+
* @return MemcachedAdapter
57+
*/
58+
public static function createAdapter(array $servers = array(), array $options = array())
59+
{
60+
$adapter = new static(static::createClient());
61+
$adapter->setupConnection($servers, $options);
62+
63+
return $adapter;
64+
}
65+
66+
/**
67+
* @param array $servers
68+
* @param array $options
69+
*
70+
* @return \Memcached
71+
*/
72+
public function setupConnection(array $servers = array(), array $options = array())
73+
{
74+
foreach ($servers as $server) {
75+
$this->addServer($server);
76+
}
77+
78+
foreach ($options as $opt => $val) {
79+
$this->setOption($opt, $val);
80+
}
81+
82+
return $this->memcached;
83+
}
84+
85+
/**
86+
* @param mixed[] $server
87+
*/
88+
public function addServer(array $server)
89+
{
90+
list($host, $port, $weight) = $this->filterServerValues(
91+
isset($server[0]) ? $server[0] : self::DEFAULT_HOST,
92+
isset($server[1]) ? $server[1] : self::DEFAULT_PORT,
93+
isset($server[2]) ? $server[2] : self::DEFAULT_WEIGHT
94+
);
95+
96+
if (!$this->isServerAlreadyEnabled($host, $port)) {
97+
$this->memcached->addServer($host, $port, $weight);
98+
}
99+
}
100+
101+
/**
102+
* @param mixed $opt
103+
* @param mixed $val
104+
*
105+
* @return bool
106+
*/
107+
public function setOption($opt, $val)
108+
{
109+
list($opt, $val) = $this->filterOptionValues($opt, $val);
110+
111+
return $this->memcached->setOption($opt, $val)
112+
&& $this->memcached->getResultCode() === \Memcached::RES_SUCCESS;
113+
}
114+
115+
/**
116+
* @param mixed $opt
117+
* @param mixed $val
118+
*
119+
* @return mixed[]
120+
*/
121+
private function filterOptionValues($opt, $val)
122+
{
123+
list($opt, $val) = array(
124+
$opt = $this->tryResolvingConstant($opt),
125+
$val = $this->tryResolvingConstant($val),
126+
);
127+
128+
if (false === filter_var($opt, FILTER_VALIDATE_INT)) {
129+
throw new InvalidArgumentException(sprintf('Option type invalid "%s": must resolve as int', $opt));
130+
}
131+
132+
if (false === filter_var($val, FILTER_VALIDATE_INT) && false === filter_var($val, FILTER_VALIDATE_BOOLEAN)) {
133+
throw new InvalidArgumentException(sprintf('Option value invalid "%s": must resolve as int or bool', $val));
134+
}
135+
136+
return array($opt, $val);
137+
}
138+
139+
/**
140+
* @param mixed $val
141+
*
142+
* @return mixed
143+
*/
144+
private function tryResolvingConstant($val)
145+
{
146+
return defined($constant = '\Memcached::'.$val) ? constant($constant) : $val;
147+
}
148+
149+
/**
150+
* {@inheritdoc}
151+
*/
152+
protected function doFetch(array $ids)
153+
{
154+
foreach ($this->memcached->getMulti($ids) as $id => $val) {
155+
try {
156+
yield $id => parent::unserialize($val);
157+
} catch (\Exception $e) {
158+
continue;
159+
}
160+
}
161+
}
162+
163+
/**
164+
* {@inheritdoc}
165+
*/
166+
protected function doHave($id)
167+
{
168+
return $this->memcached->get($id) !== false
169+
&& $this->memcached->getResultCode() !== \Memcached::RES_NOTFOUND;
170+
}
171+
172+
/**
173+
* {@inheritdoc}
174+
*/
175+
protected function doClear($namespace)
176+
{
177+
return $this->memcached->flush()
178+
&& $this->memcached->getResultCode() === \Memcached::RES_SUCCESS;
179+
}
180+
181+
/**
182+
* {@inheritdoc}
183+
*/
184+
protected function doDelete(array $ids)
185+
{
186+
$deletions = array_filter((array) $this->memcached->deleteMulti($ids), function ($ret) {
187+
return $ret === true || $ret === \Memcached::RES_NOTFOUND;
188+
});
189+
190+
return count($deletions) === count($ids);
191+
}
192+
193+
/**
194+
* {@inheritdoc}
195+
*/
196+
protected function doSave(array $values, $lifetime)
197+
{
198+
$serialized = $exceptions = array();
199+
200+
foreach ($values as $id => $val) {
201+
try {
202+
$serialized[$id] = serialize($val);
203+
} catch (\Exception $e) {
204+
$exceptions[] = $id;
205+
}
206+
}
207+
208+
$this->memcached->setMulti($serialized, $lifetime);
209+
210+
return $exceptions;
211+
}
212+
213+
/**
214+
* @param string $host
215+
* @param int $port
216+
* @param int $weight
217+
*
218+
* @return mixed[]
219+
*/
220+
private function filterServerValues($host, $port, $weight)
221+
{
222+
if (false === filter_var($host, FILTER_VALIDATE_IP) && false === filter_var(gethostbyname($host), FILTER_VALIDATE_IP)) {
223+
throw new InvalidArgumentException(sprintf('Server host option invalid "%s": must be a valid IP or resolvable hostname', $host));
224+
}
225+
226+
if (false === filter_var($port, FILTER_VALIDATE_INT)) {
227+
throw new InvalidArgumentException(sprintf('Server port option invalid "%s": must be an integer', $port));
228+
}
229+
230+
if (false === filter_var($weight, FILTER_VALIDATE_INT)) {
231+
throw new InvalidArgumentException(sprintf('Server weight option invalid "%s": must be an integer', $weight));
232+
}
233+
234+
return array($host, $port, $weight);
235+
}
236+
237+
/**
238+
* @param string $host
239+
* @param int $port
240+
*
241+
* @return bool
242+
*/
243+
private function isServerAlreadyEnabled($host, $port)
244+
{
245+
$matches = array_filter($this->memcached->getServerList(), function ($server) use ($host, $port) {
246+
return $host === array_shift($server)
247+
&& $port === array_shift($server);
248+
});
249+
250+
return count($matches) > 0;
251+
}
252+
}

0 commit comments

Comments
 (0)