Skip to content

Commit 51b8200

Browse files
committed
initial cleanup of memcached cache adapter for upstream
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
1 parent 60b4dd0 commit 51b8200

File tree

3 files changed

+436
-0
lines changed

3 files changed

+436
-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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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\Tests\Adapter;
13+
14+
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
15+
16+
abstract class AbstractMemcachedAdapterTest extends AdapterTestCase
17+
{
18+
protected $skippedTests = array(
19+
//'testExpiration' => 'Testing expiration slows down the test suite',
20+
//'testHasItemReturnsFalseWhenDeferredItemIsExpired' => 'Testing expiration slows down the test suite',
21+
//'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
22+
);
23+
24+
/**
25+
* @var \Memcached
26+
*/
27+
protected static $memcached;
28+
29+
public function createCachePool($defaultLifetime = 0)
30+
{
31+
return new MemcachedAdapter(self::$memcached, str_replace('\\', '.', __CLASS__), $defaultLifetime);
32+
}
33+
34+
public static function setupBeforeClass()
35+
{
36+
if (!extension_loaded('memcached')) {
37+
self::markTestSkipped('Extension memcached required.');
38+
}
39+
}
40+
41+
public static function tearDownAfterClass()
42+
{
43+
self::$memcached->flush();
44+
self::$memcached = null;
45+
}
46+
}

0 commit comments

Comments
 (0)