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 e6baecc

Browse filesBrowse files
committed
support memcached dsn using new client factory interface
1 parent 9f95654 commit e6baecc
Copy full SHA for e6baecc

File tree

6 files changed

+469
-11
lines changed
Filter options

6 files changed

+469
-11
lines changed
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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\Client;
13+
14+
/**
15+
* @author Rob Frawley 2nd <rmf@src.run>
16+
*/
17+
abstract class AbstractClient implements ClientInterface
18+
{
19+
abstract public function __construct(array $servers = array(), array $options = array());
20+
21+
public static function create($servers = array(), array $options = array())
22+
{
23+
return new static(is_array($servers) ? $servers : array($servers), $options);
24+
}
25+
}
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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\Client;
13+
14+
/**
15+
* @author Rob Frawley 2nd <rmf@src.run>
16+
*/
17+
interface ClientInterface
18+
{
19+
public static function isSupported();
20+
public static function create($servers = array(), array $options = array());
21+
}
+184Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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\Client;
13+
14+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
15+
16+
/**
17+
* @author Rob Frawley 2nd <rmf@src.run>
18+
*/
19+
class MemcachedClient extends AbstractClient
20+
{
21+
private static $serverDefaults = array(
22+
'host' => 'localhost',
23+
'port' => 11211,
24+
'weight' => 100,
25+
);
26+
27+
private static $optionDefaults = array(
28+
'compression' => true,
29+
'libketama_compatible' => true,
30+
);
31+
32+
private $client;
33+
34+
public function __construct(array $servers = array(), array $options = array())
35+
{
36+
$this->client = new \Memcached(isset($options['persistent_id']) ? $options['persistent_id'] : null);
37+
$this->setOptions($options);
38+
$this->setServers($servers);
39+
}
40+
41+
public static function isSupported()
42+
{
43+
return extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
44+
}
45+
46+
/**
47+
* @return \Memcached
48+
*/
49+
public function getClient()
50+
{
51+
return $this->client;
52+
}
53+
54+
private function setOptions(array $options)
55+
{
56+
unset($options['persistent_id']);
57+
$options += static::$optionDefaults;
58+
59+
foreach ($options as $named => $value) {
60+
$this->addOption($named, $value);
61+
}
62+
}
63+
64+
private function addOption($named, $value)
65+
{
66+
$toRestore = error_reporting(~E_ALL);
67+
$isSuccess = $this->client->setOption(
68+
$this->resolveOptionNamed($named),
69+
$this->resolveOptionValue($named, $value)
70+
);
71+
error_reporting($toRestore);
72+
73+
if (!$isSuccess) {
74+
$errLast = error_get_last();
75+
$msgLast = isset($errLast['message']) ? $errLast['message'] : $this->client->getResultMessage();
76+
77+
throw new InvalidArgumentException(
78+
sprintf('Invalid option: %s=%s (%s)', var_export($named, true), var_export($value, true), $msgLast)
79+
);
80+
}
81+
}
82+
83+
private function resolveOptionNamed($named)
84+
{
85+
if (!defined($constant = sprintf('\Memcached::OPT_%s', strtoupper($named)))) {
86+
throw new InvalidArgumentException(sprintf('Invalid option named: %s', $named));
87+
}
88+
89+
return constant($constant);
90+
}
91+
92+
private function resolveOptionValue($named, $value)
93+
{
94+
$typed = preg_replace('{_.*$}', '', $named);
95+
96+
if (defined($constant = sprintf('\Memcached::%s_%s', strtoupper($typed), strtoupper($value)))
97+
|| defined($constant = sprintf('\Memcached::%s', strtoupper($value)))) {
98+
return constant($constant);
99+
}
100+
101+
return $value;
102+
}
103+
104+
private function setServers(array $dsns)
105+
{
106+
foreach ($dsns as $i => $dsn) {
107+
$this->addServer($i, $dsn);
108+
}
109+
}
110+
111+
private function addServer($i, $dsn)
112+
{
113+
if (false === $server = $this->resolveServer($dsn)) {
114+
throw new InvalidArgumentException(sprintf('Invalid server %d DSN: %s', $i, $dsn));
115+
}
116+
117+
if ($this->hasServer($server['host'], $server['port'])) {
118+
return;
119+
}
120+
121+
$this->client->addServer($server['host'], $server['port'], $server['weight']);
122+
}
123+
124+
private function hasServer($host, $port)
125+
{
126+
foreach ($this->client->getServerList() as $server) {
127+
if ($server['host'] === $host && $server['port'] === $port) {
128+
return true;
129+
}
130+
}
131+
132+
return false;
133+
}
134+
135+
private function resolveServer($dsn)
136+
{
137+
if (0 !== strpos($dsn, 'memcached')) {
138+
return false;
139+
}
140+
141+
if (false !== $server = $this->resolveServerAsHost($dsn)) {
142+
return $server;
143+
}
144+
145+
return $this->resolveServerAsSock($dsn);
146+
}
147+
148+
private function resolveServerAsHost($dsn)
149+
{
150+
if (false === $server = parse_url($dsn)) {
151+
return false;
152+
}
153+
154+
return $this->resolveServerReturn($server);
155+
}
156+
157+
private function resolveServerAsSock($dsn)
158+
{
159+
if (1 !== preg_match('{memcached:\/\/(?<host>\/[^?]+)(?:\?)?(?<query>.+)?}', $dsn, $matches)) {
160+
return false;
161+
}
162+
163+
return $this->resolveServerReturn(array(
164+
'host' => 0 === strpos(strrev($matches['host']), '/') ? substr($matches['host'], 0, -1) : $matches['host'],
165+
'query' => isset($matches['query']) ? $matches['query'] : null,
166+
));
167+
}
168+
169+
private function resolveServerReturn($server)
170+
{
171+
parse_str(isset($server['query']) ? $server['query'] : '', $query);
172+
173+
$query = array_filter($query, function ($index) {
174+
return in_array($index, array('weight'));
175+
}, ARRAY_FILTER_USE_KEY);
176+
177+
$server += $query;
178+
$server += static::$serverDefaults;
179+
180+
return array_filter($server, function ($index) {
181+
return in_array($index, array('host', 'port', 'weight'));
182+
}, ARRAY_FILTER_USE_KEY);
183+
}
184+
}

‎src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php
+35-2Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,45 @@
1111

1212
namespace Symfony\Component\Cache\Adapter;
1313

14+
use Symfony\Component\Cache\Adapter\Client\MemcachedClient;
15+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
16+
1417
/**
1518
* @author Rob Frawley 2nd <rmf@src.run>
1619
*/
1720
class MemcachedAdapter extends AbstractAdapter
1821
{
1922
private $client;
2023

21-
public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0)
24+
public function __construct($client, $namespace = '', $defaultLifetime = 0)
2225
{
2326
parent::__construct($namespace, $defaultLifetime);
27+
28+
if ($client instanceof MemcachedClient) {
29+
$client = $client->getClient();
30+
}
31+
32+
if (!$client instanceof \Memcached) {
33+
throw new InvalidArgumentException(sprintf('%s() expects parameter 1 to be \Memcached or \%s instance.', __METHOD__, MemcachedClient::class));
34+
}
35+
2436
$this->client = $client;
2537
}
2638

2739
public static function isSupported()
2840
{
29-
return extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
41+
return MemcachedClient::isSupported();
42+
}
43+
44+
/**
45+
* @param string[] $servers
46+
* @param mixed[] $options
47+
*
48+
* @return \Memcached
49+
*/
50+
public static function createConnection($servers = array(), array $options = array())
51+
{
52+
return MemcachedClient::create($servers, $options)->getClient();
3053
}
3154

3255
/**
@@ -75,4 +98,14 @@ protected function doClear($namespace)
7598
{
7699
return $this->client->flush();
77100
}
101+
102+
public function __destruct()
103+
{
104+
if (!$this->client->isPersistent()) {
105+
$this->client->flushBuffers();
106+
$this->client->quit();
107+
}
108+
109+
parent::__destruct();
110+
}
78111
}

0 commit comments

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