Skip to content

Commit 4724d79

Browse files
committed
Merge pull request #24 from aequasi/master
Allowing use of DSN for redis and mongo factories
2 parents 667cf8d + ff1c11a commit 4724d79

File tree

6 files changed

+668
-25
lines changed

6 files changed

+668
-25
lines changed

src/DSN.php

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
<?php
2+
3+
/*
4+
* This file is part of php-cache\adapter-bundle package.
5+
*
6+
* (c) 2015-2015 Aaron Scherer <[email protected]>, Tobias Nyholm <[email protected]>
7+
*
8+
* This source file is subject to the MIT license that is bundled
9+
* with this source code in the file LICENSE.
10+
*/
11+
12+
namespace Cache\AdapterBundle;
13+
14+
/**
15+
* @author Aaron Scherer <[email protected]>
16+
*
17+
* @see https://github.com/snc/SncRedisBundle/blob/master/DependencyInjection/Configuration/RedisDsn.php
18+
*/
19+
class DSN
20+
{
21+
private static $PORTS = [
22+
'redis' => 6379,
23+
'mongodb' => 27017,
24+
'tcp' => 6379,
25+
];
26+
27+
/**
28+
* @type string
29+
*/
30+
protected $dsn;
31+
32+
/**
33+
* @type string
34+
*/
35+
protected $protocol;
36+
37+
/**
38+
* @type array
39+
*/
40+
protected $authentication;
41+
42+
/**
43+
* @type array
44+
*/
45+
protected $hosts;
46+
47+
/**
48+
* @type int
49+
*/
50+
protected $database;
51+
52+
/**
53+
* @type array
54+
*/
55+
protected $parameters = [];
56+
57+
/**
58+
* Constructor.
59+
*
60+
* @param string $dsn
61+
*/
62+
public function __construct($dsn)
63+
{
64+
$this->dsn = $dsn;
65+
$this->parseDsn($dsn);
66+
}
67+
68+
/**
69+
* @return string
70+
*/
71+
public function getDsn()
72+
{
73+
return $this->dsn;
74+
}
75+
76+
/**
77+
* @return string
78+
*/
79+
public function getProtocol()
80+
{
81+
return $this->protocol;
82+
}
83+
84+
/**
85+
* @return int|null
86+
*/
87+
public function getDatabase()
88+
{
89+
return $this->database;
90+
}
91+
92+
/**
93+
* @return array
94+
*/
95+
public function getHosts()
96+
{
97+
return $this->hosts;
98+
}
99+
100+
/**
101+
* @return null|string
102+
*/
103+
public function getFirstHost()
104+
{
105+
return $this->hosts[0]['host'];
106+
}
107+
108+
/**
109+
* @return null|int
110+
*/
111+
public function getFirstPort()
112+
{
113+
return $this->hosts[0]['port'];
114+
}
115+
116+
/**
117+
* @return array
118+
*/
119+
public function getAuthentication()
120+
{
121+
return $this->authentication;
122+
}
123+
124+
public function getUsername()
125+
{
126+
return $this->authentication['username'];
127+
}
128+
129+
public function getPassword()
130+
{
131+
return $this->authentication['password'];
132+
}
133+
134+
/**
135+
* @return array
136+
*/
137+
public function getParameters()
138+
{
139+
return $this->parameters;
140+
}
141+
142+
/**
143+
* @return bool
144+
*/
145+
public function isValid()
146+
{
147+
if (null === $this->getProtocol()) {
148+
return false;
149+
}
150+
151+
if (!in_array($this->getProtocol(), ['redis', 'mongodb', 'tcp'])) {
152+
return false;
153+
}
154+
155+
if (empty($this->getHosts())) {
156+
return false;
157+
}
158+
159+
return true;
160+
}
161+
162+
private function parseProtocol($dsn)
163+
{
164+
$regex = '/^(\w+):\/\//i';
165+
166+
preg_match($regex, $dsn, $matches);
167+
168+
if (isset($matches[1])) {
169+
$protocol = $matches[1];
170+
if (!in_array($protocol, ['redis', 'mongodb', 'tcp'])) {
171+
return false;
172+
}
173+
174+
$this->protocol = $protocol;
175+
}
176+
}
177+
178+
/**
179+
* @param string $dsn
180+
*/
181+
private function parseDsn($dsn)
182+
{
183+
$this->parseProtocol($dsn);
184+
if ($this->getProtocol() === null) {
185+
return;
186+
}
187+
188+
// Remove the protocol
189+
$dsn = str_replace($this->protocol.'://', '', $dsn);
190+
191+
// Parse and remove auth if they exist
192+
if (false !== $pos = strrpos($dsn, '@')) {
193+
$temp = explode(':', str_replace('\@', '@', substr($dsn, 0, $pos)));
194+
$dsn = substr($dsn, $pos + 1);
195+
196+
$auth = [];
197+
if (count($temp) === 2) {
198+
$auth['username'] = $temp[0];
199+
$auth['password'] = $temp[1];
200+
} else {
201+
$auth['password'] = $temp[0];
202+
}
203+
204+
$this->authentication = $auth;
205+
}
206+
207+
if (strpos($dsn, '?') !== false) {
208+
if (strpos($dsn, '/') === false) {
209+
$dsn = str_replace('?', '/?', $dsn);
210+
}
211+
}
212+
213+
$temp = explode('/', $dsn);
214+
$this->parseHosts($temp[0]);
215+
216+
if (isset($temp[1])) {
217+
$params = $temp[1];
218+
$temp = explode('?', $params);
219+
$this->database = empty($temp[0]) ? null : $temp[0];
220+
if (isset($temp[1])) {
221+
$this->parseParameters($temp[1]);
222+
}
223+
}
224+
}
225+
226+
private function parseHosts($hostString)
227+
{
228+
preg_match_all('/(?P<host>[\w-._]+)(?::(?P<port>\d+))?/mi', $hostString, $matches);
229+
230+
$hosts = [];
231+
foreach ($matches['host'] as $index => $match) {
232+
$port = !empty($matches['port'][$index])
233+
? (int) $matches['port'][$index]
234+
: self::$PORTS[$this->protocol];
235+
$hosts[] = ['host' => $match, 'port' => $port];
236+
}
237+
238+
$this->hosts = $hosts;
239+
}
240+
241+
/**
242+
* @param string $params
243+
*
244+
* @return string
245+
*/
246+
protected function parseParameters($params)
247+
{
248+
$parameters = explode('&', $params);
249+
250+
foreach ($parameters as $parameter) {
251+
$kv = explode('=', $parameter, 2);
252+
$this->parameters[$kv[0]] = isset($kv[1]) ? $kv[1] : null;
253+
}
254+
255+
return '';
256+
}
257+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
/*
4+
* This file is part of php-cache\adapter-bundle package.
5+
*
6+
* (c) 2015-2015 Aaron Scherer <[email protected]>, Tobias Nyholm <[email protected]>
7+
*
8+
* This source file is subject to the MIT license that is bundled
9+
* with this source code in the file LICENSE.
10+
*/
11+
12+
namespace Cache\AdapterBundle\Factory;
13+
14+
use Cache\AdapterBundle\DSN;
15+
use Symfony\Component\OptionsResolver\OptionsResolver;
16+
17+
/**
18+
* @author Aaron Scherer <[email protected]>
19+
*/
20+
abstract class AbstractDsnAdapterFactory extends AbstractAdapterFactory
21+
{
22+
/**
23+
* @type DSN
24+
*/
25+
private $DSN;
26+
27+
/**
28+
* @return DSN
29+
*/
30+
protected function getDsn()
31+
{
32+
return $this->DSN;
33+
}
34+
35+
/**
36+
* {@inheritdoc}
37+
*/
38+
protected static function configureOptionResolver(OptionsResolver $resolver)
39+
{
40+
$resolver->setDefaults(['dsn' => '']);
41+
$resolver->setAllowedTypes('dsn', ['string']);
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public static function validate(array $options, $adapterName)
48+
{
49+
parent::validate($options, $adapterName);
50+
51+
if (empty($options['dsn'])) {
52+
return;
53+
}
54+
55+
$dsn = new DSN($options['dsn']);
56+
if (!$dsn->isValid()) {
57+
throw new \InvalidArgumentException('Invalid DSN: '.$options['dsn']);
58+
}
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
public function createAdapter(array $options = [])
65+
{
66+
if (!empty($options['dsn'])) {
67+
$dsn = new DSN($options['dsn']);
68+
if (!$dsn->isValid()) {
69+
throw new \InvalidArgumentException('Invalid DSN: '.$options['dsn']);
70+
}
71+
72+
$this->DSN = $dsn;
73+
}
74+
75+
return parent::createAdapter($options);
76+
}
77+
}

src/Factory/MongoDBFactory.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,26 @@
1717

1818
/**
1919
* @author Tobias Nyholm <[email protected]>
20+
* @author Aaron Scherer <[email protected]>
2021
*/
21-
class MongoDBFactory extends AbstractAdapterFactory
22+
class MongoDBFactory extends AbstractDsnAdapterFactory
2223
{
2324
protected static $dependencies = [
24-
['requiredClass' => 'Cache\Adapter\MongoDB\MongoDBCachePool', 'packageName' => 'cache/mongodb-adapter'],
25+
['requiredClass' => 'Cache\Adapter\MongoDB\MongoDBCachePool', 'packageName' => 'cache/mongodb-adapter'],
2526
];
2627

2728
/**
2829
* {@inheritdoc}
2930
*/
3031
public function getAdapter(array $config)
3132
{
32-
$manager = new Manager(sprintf('mongodb://%s:%s', $config['host'], $config['port']));
33+
$dsn = static::getDsn();
34+
if (empty($dsn)) {
35+
$manager = new Manager(sprintf('mongodb://%s:%s', $config['host'], $config['port']));
36+
} else {
37+
$manager = new Manager($dsn->getDsn());
38+
}
39+
3340
$collection = MongoDBCachePool::createCollection($manager, $config['namespace']);
3441

3542
return new MongoDBCachePool($collection);
@@ -40,11 +47,15 @@ public function getAdapter(array $config)
4047
*/
4148
protected static function configureOptionResolver(OptionsResolver $resolver)
4249
{
43-
$resolver->setDefaults([
44-
'host' => '127.0.0.1',
45-
'port' => 11211,
46-
'namespace' => 'cache',
47-
]);
50+
parent::configureOptionResolver($resolver);
51+
52+
$resolver->setDefaults(
53+
[
54+
'host' => '127.0.0.1',
55+
'port' => 11211,
56+
'namespace' => 'cache',
57+
]
58+
);
4859

4960
$resolver->setAllowedTypes('host', ['string']);
5061
$resolver->setAllowedTypes('port', ['string', 'int']);

0 commit comments

Comments
 (0)