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 1fb3338

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 - move to allow dsn for memcached client config - fabbot.io style fixes - cleanup of docblocks to bring in line with symfony standards
1 parent 60b4dd0 commit 1fb3338
Copy full SHA for 1fb3338

File tree

2 files changed

+407
-0
lines changed
Filter options

2 files changed

+407
-0
lines changed
+240Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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 $clientDefaults = array(
22+
'host' => '127.0.0.1',
23+
'port' => 11211,
24+
'path' => -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 $defaultLifetime
35+
*/
36+
public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0)
37+
{
38+
parent::__construct($namespace, $defaultLifetime);
39+
40+
$this->client = $client;
41+
}
42+
43+
/**
44+
* Static factory method to create an adapter instance for Memcached.
45+
*
46+
* Example valid DSN values:
47+
* - memcached://localhost Specify only the host (defaults used otherwise)
48+
* - memcached://example.com:1234 Specify the port
49+
* - memcached://example.com:1234/@50 Specify the server weight using the format `@weight` in DSN path
50+
*
51+
* To determine the available options, reference the predefined constant options in the official manual.
52+
* Expected is an associative array of options types with their corresponding value.
53+
* - http://php.net/manual/en/memcached.constants.php
54+
*
55+
* @param string|null $dsn
56+
* @param array $opts
57+
* @param string|null $persistentId
58+
*
59+
* @return MemcachedAdapter
60+
*/
61+
public static function create($dsn = null, array $opts = array(), $persistentId = null)
62+
{
63+
if (!extension_loaded('memcached')) {
64+
throw new InvalidArgumentException('Failed to create Memcache client due to missing "memcached" extension.');
65+
}
66+
67+
$adapter = new static(new \Memcached($persistentId));
68+
$adapter->setup($dsn ? array($dsn) : array(), $opts);
69+
70+
return $adapter;
71+
}
72+
73+
/**
74+
* Provide ability to reconfigure adapter after construction. See {@see create()} for acceptable DSN formats.
75+
*
76+
* @param string[] $dsns
77+
* @param mixed[] $opts
78+
*
79+
* @return bool
80+
*/
81+
public function setup(array $dsns = array(), array $opts = array())
82+
{
83+
$ret = true;
84+
85+
foreach ($opts as $opt => $val) {
86+
$ret = $this->setOption($opt, $val) && $ret;
87+
}
88+
foreach ($dsns as $dsn) {
89+
$ret = $this->addServer($dsn) && $ret;
90+
}
91+
92+
return $ret;
93+
}
94+
95+
/**
96+
* Returns the \Memcached client instance.
97+
*
98+
* @return \Memcached
99+
*/
100+
public function getClient()
101+
{
102+
return $this->client;
103+
}
104+
105+
/**
106+
* {@inheritdoc}
107+
*/
108+
protected function doSave(array $values, $lifetime)
109+
{
110+
return $this->client->setMulti($values, $lifetime) !== false
111+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS;
112+
}
113+
114+
/**
115+
* {@inheritdoc}
116+
*/
117+
protected function doFetch(array $ids)
118+
{
119+
foreach ($this->client->getMulti($ids) as $id => $val) {
120+
yield $id => $val;
121+
}
122+
}
123+
124+
/**
125+
* {@inheritdoc}
126+
*/
127+
protected function doHave($id)
128+
{
129+
return $this->client->get($id) !== false
130+
|| $this->client->getResultCode() !== \Memcached::RES_NOTFOUND;
131+
}
132+
133+
/**
134+
* {@inheritdoc}
135+
*/
136+
protected function doClear($namespace)
137+
{
138+
if (isset($namespace[0]) && false !== $ids = $this->client->getAllKeys()) {
139+
return $this->doDelete(array_filter($ids, function ($id) use ($namespace) {
140+
return 0 === strpos($id, $namespace);
141+
}));
142+
}
143+
144+
return $this->client->flush()
145+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS;
146+
}
147+
148+
/**
149+
* {@inheritdoc}
150+
*/
151+
protected function doDelete(array $ids)
152+
{
153+
$toDelete = count($ids);
154+
155+
foreach ((array) $this->client->deleteMulti($ids) as $result) {
156+
if (true === $result || \Memcached::RES_NOTFOUND === $result) {
157+
--$toDelete;
158+
}
159+
}
160+
161+
return 0 === $toDelete;
162+
}
163+
164+
private function addServer($dsn)
165+
{
166+
list(, $host, $port, $weight) = $this->parseServerDSN($dsn);
167+
168+
if ($this->isServerAlreadyEnabled($host, $port)) {
169+
return true;
170+
}
171+
172+
return $this->client->addServer($host, $port, $weight)
173+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS;
174+
}
175+
176+
private function parseServerDSN($dsn)
177+
{
178+
if (false === $server = parse_url($dsn)) {
179+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn));
180+
}
181+
182+
if (isset($server['path']) && 1 === preg_match('{^/@([0-9]+)}', $server['path'], $path)) {
183+
$server['path'] = isset($path[1]) ? (int) $path[1] : false;
184+
}
185+
186+
$server += $this->clientDefaults;
187+
$this->filterServerValues($server['host'], $server['path']);
188+
189+
return array_values($server);
190+
}
191+
192+
private function filterServerValues($host, $weight)
193+
{
194+
if (false === filter_var($host, FILTER_VALIDATE_IP) &&
195+
false === filter_var(gethostbyname($host), FILTER_VALIDATE_IP)) {
196+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN host: %s', $host));
197+
}
198+
199+
if (false === filter_var($weight, FILTER_VALIDATE_INT)) {
200+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN weight: %s', $weight));
201+
}
202+
}
203+
204+
private function isServerAlreadyEnabled($host, $port)
205+
{
206+
$matches = array_filter($this->client->getServerList(), function ($server) use ($host, $port) {
207+
return $host === array_shift($server)
208+
&& $port === array_shift($server);
209+
});
210+
211+
return count($matches) > 0;
212+
}
213+
214+
private function setOption($opt, $val)
215+
{
216+
list($opt, $val) = $this->filterOptionValues($opt, $val);
217+
218+
return $this->client->setOption($opt, $val)
219+
&& $this->client->getResultCode() === \Memcached::RES_SUCCESS;
220+
}
221+
222+
private function filterOptionValues($opt, $val)
223+
{
224+
if (false === filter_var($opt = $this->tryClientConstantResolution($opt), FILTER_VALIDATE_INT)) {
225+
throw new InvalidArgumentException(sprintf('Option type invalid "%s": must resolve as int', $opt));
226+
}
227+
228+
if (false === filter_var($val = $this->tryClientConstantResolution($val), FILTER_VALIDATE_INT) &&
229+
false === filter_var($val, FILTER_VALIDATE_BOOLEAN)) {
230+
throw new InvalidArgumentException(sprintf('Option value invalid "%s": must resolve as int or bool', $val));
231+
}
232+
233+
return array($opt, $val);
234+
}
235+
236+
private function tryClientConstantResolution($val)
237+
{
238+
return defined($constant = '\Memcached::'.strtoupper($val)) ? constant($constant) : $val;
239+
}
240+
}

0 commit comments

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