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 73d5ef7

Browse filesBrowse files
committed
cache adapters for memcached and memcache extensions
1 parent d6e8937 commit 73d5ef7
Copy full SHA for 73d5ef7

File tree

6 files changed

+845
-0
lines changed
Filter options

6 files changed

+845
-0
lines changed
+146Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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 MemcacheAdapter extends AbstractAdapter
20+
{
21+
use MemcacheAdapterTrait;
22+
23+
/**
24+
* Construct adapter by passing a \Memcache instance and an optional namespace and default cache entry ttl.
25+
*
26+
* @param \Memcache $client
27+
* @param string|null $namespace
28+
* @param int $defaultLifetime
29+
*/
30+
public function __construct(\Memcache $client, $namespace = '', $defaultLifetime = 0)
31+
{
32+
parent::__construct($namespace, $defaultLifetime);
33+
$this->client = $client;
34+
}
35+
36+
/**
37+
* Factory creation method that provides an instance of this adapter with a\Memcache client instantiated and setup.
38+
*
39+
* Valid DSN values include the following:
40+
* - memcache://localhost : Specifies only the host (defaults used for port and weight)
41+
* - memcache://example.com:1234 : Specifies host and port (defaults weight)
42+
* - memcache://example.com:1234?weight=50 : Specifies host, port, and weight (no defaults used)
43+
*
44+
* @param string|null $dsn
45+
*
46+
* @return MemcacheAdapter
47+
*/
48+
public static function create($dsn = null)
49+
{
50+
if (!extension_loaded('memcache') || !version_compare(phpversion('memcache'), '3.0.8', '>')) {
51+
throw new InvalidArgumentException('Failed to create memcache client due to missing "memcache" extension or version <3.0.9.');
52+
}
53+
54+
$adapter = new static(new \Memcache());
55+
$adapter->setup($dsn ? array($dsn) : array());
56+
57+
return $adapter;
58+
}
59+
60+
/**
61+
* {@inheritdoc}
62+
*/
63+
protected function doSave(array $values, $lifetime)
64+
{
65+
$result = true;
66+
67+
foreach ($values as $id => $val) {
68+
$result = $this->client->set($id, $val, null, $lifetime) && $result;
69+
}
70+
71+
return $result;
72+
}
73+
74+
/**
75+
* {@inheritdoc}
76+
*/
77+
protected function doFetch(array $ids)
78+
{
79+
foreach ($this->client->get($ids) as $id => $val) {
80+
yield $id => $val;
81+
}
82+
}
83+
84+
/**
85+
* {@inheritdoc}
86+
*/
87+
protected function doHave($id)
88+
{
89+
return $this->client->get($id) !== false;
90+
}
91+
92+
/**
93+
* {@inheritdoc}
94+
*/
95+
protected function doDelete(array $ids)
96+
{
97+
$remaining = array_filter($ids, function ($id) {
98+
return false !== $this->client->get($id) && false === $this->client->delete($id);
99+
});
100+
101+
return 0 === count($remaining);
102+
}
103+
104+
private function getIdsByPrefix($namespace)
105+
{
106+
$ids = array();
107+
foreach ($this->client->getExtendedStats('slabs') as $slabGroup) {
108+
foreach ($slabGroup as $slabId => $slabMetadata) {
109+
if (!is_array($slabMetadata)) {
110+
continue;
111+
}
112+
foreach ($this->client->getExtendedStats('cachedump', (int) $slabId, 1000) as $slabIds) {
113+
if (is_array($slabIds)) {
114+
$ids = array_merge($ids, array_keys($slabIds));
115+
}
116+
}
117+
}
118+
}
119+
120+
return array_filter((array) $ids, function ($id) use ($namespace) {
121+
return 0 === strpos($id, $namespace);
122+
});
123+
}
124+
125+
private function addServer($dsn)
126+
{
127+
list($host, $port, $weight) = $this->dsnExtract($dsn);
128+
129+
return $this->isServerInClientPool($host, $port)
130+
|| $this->client->addServer($host, $port, false, $weight);
131+
}
132+
133+
private function setOption($opt, $val)
134+
{
135+
return true;
136+
}
137+
138+
private function isServerInClientPool($host, $port)
139+
{
140+
$restore = error_reporting(~E_ALL);
141+
$srvStat = $this->client->getServerStatus($host, $port);
142+
error_reporting($restore);
143+
144+
return 1 === $srvStat;
145+
}
146+
}
+114Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
* @internal
20+
*/
21+
trait MemcacheAdapterTrait
22+
{
23+
private static $defaultClientServerValues = array(
24+
'host' => '127.0.0.1',
25+
'port' => 11211,
26+
'weight' => 100,
27+
);
28+
29+
/**
30+
* @var \Memcache|\Memcached
31+
*/
32+
private $client;
33+
34+
/**
35+
* Provide ability to reconfigure adapter after construction. See {@see create()} for acceptable DSN formats.
36+
*
37+
* @param string[] $dsns
38+
* @param mixed[] $opts
39+
*
40+
* @return bool
41+
*/
42+
public function setup(array $dsns = array(), array $opts = array())
43+
{
44+
$return = true;
45+
46+
foreach ($opts as $opt => $val) {
47+
$return = $this->setOption($opt, $val) && $return;
48+
}
49+
foreach ($dsns as $dsn) {
50+
$return = $this->addServer($dsn) && $return;
51+
}
52+
53+
return $return;
54+
}
55+
56+
/**
57+
* Returns the Memcache client instance.
58+
*
59+
* @return \Memcache|\Memcached
60+
*/
61+
public function getClient()
62+
{
63+
return $this->client;
64+
}
65+
66+
/**
67+
* {@inheritdoc}
68+
*/
69+
protected function doClear($namespace)
70+
{
71+
if (!isset($namespace[0]) || false === $ids = $this->getIdsByPrefix($namespace)) {
72+
return $this->client->flush();
73+
}
74+
75+
$return = true;
76+
77+
do {
78+
$return = $this->doDelete($ids) && $return;
79+
} while ($ids = $this->getIdsByPrefix($namespace));
80+
81+
return $return;
82+
}
83+
84+
private function dsnExtract($dsn)
85+
{
86+
$scheme = false !== strpos(static::class, 'Memcached') ? 'memcached' : 'memcache';
87+
88+
if (false === ($srv = parse_url($dsn)) || $srv['scheme'] !== $scheme || count($srv) > 4) {
89+
throw new InvalidArgumentException(sprintf('Invalid %s DSN: %s (expects "%s://example.com[:1234][?weight=<int>]")', $scheme, $dsn, $scheme));
90+
}
91+
92+
if (isset($srv['query']) && 1 === preg_match('{weight=([^&]{1,})}', $srv['query'], $weight)) {
93+
$srv['weight'] = (int) $weight[1];
94+
}
95+
96+
return $this->dsnSanitize($srv, $scheme);
97+
}
98+
99+
private function dsnSanitize(array $srv, $scheme)
100+
{
101+
$srv += self::$defaultClientServerValues;
102+
103+
if (false === ($host = filter_var($srv['host'], FILTER_VALIDATE_IP)) ||
104+
false === ($host = filter_var($srv['host'], FILTER_SANITIZE_URL))) {
105+
throw new InvalidArgumentException(sprintf('Invalid %s DSN host: %s (expects resolvable IP or hostname)', $scheme, $srv['host']));
106+
}
107+
108+
if (false === ($weight = filter_var($srv['weight'], FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 100))))) {
109+
throw new InvalidArgumentException(sprintf('Invalid %s DSN weight: %s (expects int >=1 and <= 100)', $scheme, $srv['weight']));
110+
}
111+
112+
return array($host, $srv['port'], $weight);
113+
}
114+
}

0 commit comments

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