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 2336e57

Browse filesBrowse 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 - changes per @nicolas-grekas and @stof - fabbot.io fixes - cleanup docblocks
1 parent 60b4dd0 commit 2336e57
Copy full SHA for 2336e57

File tree

2 files changed

+428
-0
lines changed
Filter options

2 files changed

+428
-0
lines changed
+264Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
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 $connectionDefaults = array(
22+
'host' => '127.0.0.1',
23+
'port' => 11211,
24+
'weight' => -1,
25+
);
26+
27+
private $client;
28+
29+
/**
30+
* Construct adapter by passing a \Memcached instance and an optional namespace and default cache entry ttl.
31+
*
32+
* @param \Memcached $client
33+
* @param string $namespace
34+
* @param int $lifetime
35+
*/
36+
public function __construct(\Memcached $client, $namespace = '', $lifetime = 0)
37+
{
38+
parent::__construct($namespace, $lifetime);
39+
40+
$this->client = $client;
41+
}
42+
43+
/**
44+
* Static factory method to create an adapter instance for Memcached.
45+
*
46+
* @param array $servers
47+
* @param array $options
48+
* @param string|null $persistentId
49+
*
50+
* @return MemcachedAdapter
51+
*/
52+
public static function createAdapter(array $servers = array(), array $options = array(), $persistentId = null)
53+
{
54+
if (!extension_loaded('memcached')) {
55+
throw new \RuntimeException('Cannot create Memcached instance for cache adapter due to missing "memcached" ext.');
56+
}
57+
58+
$adapter = new static(new \Memcached($persistentId));
59+
$adapter->setupAdapter($servers, $options);
60+
61+
return $adapter;
62+
}
63+
64+
/**
65+
* Provide ability to reconfigure adapter after construction.
66+
*
67+
* @param array $servers
68+
* @param array $options
69+
*
70+
* @return bool
71+
*/
72+
public function setupAdapter(array $servers = array(), array $options = array())
73+
{
74+
$success = true;
75+
foreach ($servers as $server) {
76+
$success = $this->addServer($server) & $success;
77+
}
78+
foreach ($options as $opt => $val) {
79+
$success = $this->setOption($opt, $val) & $success;
80+
}
81+
82+
return $success;
83+
}
84+
85+
/**
86+
* Getter that returns an instance of the Memcached client object.
87+
*
88+
* @return \Memcached
89+
*/
90+
public function getClient()
91+
{
92+
return $this->client;
93+
}
94+
95+
/**
96+
* {@inheritdoc}
97+
*/
98+
protected function doSave(array $values, $lifetime)
99+
{
100+
return $this->client->setMulti($values, $lifetime) !== false
101+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS;
102+
}
103+
104+
/**
105+
* {@inheritdoc}
106+
*/
107+
protected function doFetch(array $ids)
108+
{
109+
foreach ($this->client->getMulti($ids) as $id => $val) {
110+
yield $id => $val;
111+
}
112+
}
113+
114+
/**
115+
* {@inheritdoc}
116+
*/
117+
protected function doHave($id)
118+
{
119+
return $this->client->get($id) !== false
120+
|| $this->client->getResultCode() !== \Memcached::RES_NOTFOUND;
121+
}
122+
123+
/**
124+
* {@inheritdoc}
125+
*/
126+
protected function doClear($namespace)
127+
{
128+
if (false !== $ids = $this->client->getAllKeys()) {
129+
return $this->doDelete(array_filter($ids, function ($id) use ($namespace) {
130+
return false !== strpos($id, $namespace);
131+
}));
132+
}
133+
134+
return $this->client->flush()
135+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS;
136+
}
137+
138+
/**
139+
* {@inheritdoc}
140+
*/
141+
protected function doDelete(array $ids)
142+
{
143+
$toDelete = count($ids);
144+
145+
foreach ((array) $this->client->deleteMulti($ids) as $result) {
146+
if (true === $result || \Memcached::RES_NOTFOUND === $result) {
147+
--$toDelete;
148+
}
149+
}
150+
151+
return 0 === $toDelete;
152+
}
153+
154+
/**
155+
* @param mixed[] $server
156+
*
157+
* @return bool
158+
*/
159+
private function addServer(array $server)
160+
{
161+
list($host, $port, $weight) = $this->filterServerValues(
162+
isset($server[0]) ? $server[0] : $this->connectionDefaults['host'],
163+
isset($server[1]) ? $server[1] : $this->connectionDefaults['port'],
164+
isset($server[2]) ? $server[2] : $this->connectionDefaults['weight']
165+
);
166+
167+
if ($this->isServerAlreadyEnabled($host, $port)) {
168+
return true;
169+
}
170+
171+
return $this->client->addServer($host, $port, $weight)
172+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS;
173+
}
174+
175+
/**
176+
* @param string $host
177+
* @param int $port
178+
* @param int $weight
179+
*
180+
* @return mixed[]
181+
*/
182+
private function filterServerValues($host, $port, $weight)
183+
{
184+
if (false === filter_var($host, FILTER_VALIDATE_IP) &&
185+
false === filter_var(gethostbyname($host), FILTER_VALIDATE_IP)) {
186+
throw new InvalidArgumentException(sprintf('Server host option invalid "%s": must be a valid IP or resolvable hostname', $host));
187+
}
188+
189+
if (false === filter_var($port, FILTER_VALIDATE_INT)) {
190+
throw new InvalidArgumentException(sprintf('Server port option invalid "%s": must be an integer', $port));
191+
}
192+
193+
if (false === filter_var($weight, FILTER_VALIDATE_INT)) {
194+
throw new InvalidArgumentException(sprintf('Server weight option invalid "%s": must be an integer', $weight));
195+
}
196+
197+
return array($host, $port, $weight);
198+
}
199+
200+
/**
201+
* Check if the passed server (host and port) is already registered with the \Memcached client. This is especially
202+
* important due to how the extension works in shared environments like Apache, where no performing this check will
203+
* result in the same server being added to the client (on a persistent connection), resulting in 100 of the same
204+
* server definition applied to the client after 100 page requests...etc.
205+
*
206+
* @param string $host
207+
* @param int $port
208+
*
209+
* @return bool
210+
*/
211+
private function isServerAlreadyEnabled($host, $port)
212+
{
213+
$matches = array_filter($this->client->getServerList(), function ($server) use ($host, $port) {
214+
return $host === array_shift($server)
215+
&& $port === array_shift($server);
216+
});
217+
218+
return count($matches) > 0;
219+
}
220+
221+
/**
222+
* @param mixed $opt
223+
* @param mixed $val
224+
*
225+
* @return bool
226+
*/
227+
private function setOption($opt, $val)
228+
{
229+
list($opt, $val) = $this->filterOptionValues($opt, $val);
230+
231+
return $this->client->setOption($opt, $val)
232+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS;
233+
}
234+
235+
/**
236+
* @param mixed $opt
237+
* @param mixed $val
238+
*
239+
* @return mixed[]
240+
*/
241+
private function filterOptionValues($opt, $val)
242+
{
243+
if (false === filter_var($opt = $this->tryClientConstantResolution($opt), FILTER_VALIDATE_INT)) {
244+
throw new InvalidArgumentException(sprintf('Option type invalid "%s": must resolve as int', $opt));
245+
}
246+
247+
if (false === filter_var($val = $this->tryClientConstantResolution($val), FILTER_VALIDATE_INT) &&
248+
false === filter_var($val, FILTER_VALIDATE_BOOLEAN)) {
249+
throw new InvalidArgumentException(sprintf('Option value invalid "%s": must resolve as int or bool', $val));
250+
}
251+
252+
return array($opt, $val);
253+
}
254+
255+
/**
256+
* @param mixed $val
257+
*
258+
* @return mixed
259+
*/
260+
private function tryClientConstantResolution($val)
261+
{
262+
return defined($constant = '\Memcached::'.strtoupper($val)) ? constant($constant) : $val;
263+
}
264+
}

0 commit comments

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