Skip to content

Commit

Permalink
Merge pull request #24 from aequasi/master
Browse files Browse the repository at this point in the history
Allowing use of DSN for redis and mongo factories
  • Loading branch information
cryptiklemur committed Jan 27, 2016
2 parents 667cf8d + ff1c11a commit 4724d79
Show file tree
Hide file tree
Showing 6 changed files with 668 additions and 25 deletions.
257 changes: 257 additions & 0 deletions src/DSN.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
<?php

/*
* This file is part of php-cache\adapter-bundle package.
*
* (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Cache\AdapterBundle;

/**
* @author Aaron Scherer <aequasi@gmail.com>
*
* @see https://github.com/snc/SncRedisBundle/blob/master/DependencyInjection/Configuration/RedisDsn.php
*/
class DSN
{
private static $PORTS = [
'redis' => 6379,
'mongodb' => 27017,
'tcp' => 6379,
];

/**
* @type string
*/
protected $dsn;

/**
* @type string
*/
protected $protocol;

/**
* @type array
*/
protected $authentication;

/**
* @type array
*/
protected $hosts;

/**
* @type int
*/
protected $database;

/**
* @type array
*/
protected $parameters = [];

/**
* Constructor.
*
* @param string $dsn
*/
public function __construct($dsn)
{
$this->dsn = $dsn;
$this->parseDsn($dsn);
}

/**
* @return string
*/
public function getDsn()
{
return $this->dsn;
}

/**
* @return string
*/
public function getProtocol()
{
return $this->protocol;
}

/**
* @return int|null
*/
public function getDatabase()
{
return $this->database;
}

/**
* @return array
*/
public function getHosts()
{
return $this->hosts;
}

/**
* @return null|string
*/
public function getFirstHost()
{
return $this->hosts[0]['host'];
}

/**
* @return null|int
*/
public function getFirstPort()
{
return $this->hosts[0]['port'];
}

/**
* @return array
*/
public function getAuthentication()
{
return $this->authentication;
}

public function getUsername()
{
return $this->authentication['username'];
}

public function getPassword()
{
return $this->authentication['password'];
}

/**
* @return array
*/
public function getParameters()
{
return $this->parameters;
}

/**
* @return bool
*/
public function isValid()
{
if (null === $this->getProtocol()) {
return false;
}

if (!in_array($this->getProtocol(), ['redis', 'mongodb', 'tcp'])) {
return false;
}

if (empty($this->getHosts())) {
return false;
}

return true;
}

private function parseProtocol($dsn)
{
$regex = '/^(\w+):\/\//i';

preg_match($regex, $dsn, $matches);

if (isset($matches[1])) {
$protocol = $matches[1];
if (!in_array($protocol, ['redis', 'mongodb', 'tcp'])) {
return false;
}

$this->protocol = $protocol;
}
}

/**
* @param string $dsn
*/
private function parseDsn($dsn)
{
$this->parseProtocol($dsn);
if ($this->getProtocol() === null) {
return;
}

// Remove the protocol
$dsn = str_replace($this->protocol.'://', '', $dsn);

// Parse and remove auth if they exist
if (false !== $pos = strrpos($dsn, '@')) {
$temp = explode(':', str_replace('\@', '@', substr($dsn, 0, $pos)));
$dsn = substr($dsn, $pos + 1);

$auth = [];
if (count($temp) === 2) {
$auth['username'] = $temp[0];
$auth['password'] = $temp[1];
} else {
$auth['password'] = $temp[0];
}

$this->authentication = $auth;
}

if (strpos($dsn, '?') !== false) {
if (strpos($dsn, '/') === false) {
$dsn = str_replace('?', '/?', $dsn);
}
}

$temp = explode('/', $dsn);
$this->parseHosts($temp[0]);

if (isset($temp[1])) {
$params = $temp[1];
$temp = explode('?', $params);
$this->database = empty($temp[0]) ? null : $temp[0];
if (isset($temp[1])) {
$this->parseParameters($temp[1]);
}
}
}

private function parseHosts($hostString)
{
preg_match_all('/(?P<host>[\w-._]+)(?::(?P<port>\d+))?/mi', $hostString, $matches);

$hosts = [];
foreach ($matches['host'] as $index => $match) {
$port = !empty($matches['port'][$index])
? (int) $matches['port'][$index]
: self::$PORTS[$this->protocol];
$hosts[] = ['host' => $match, 'port' => $port];
}

$this->hosts = $hosts;
}

/**
* @param string $params
*
* @return string
*/
protected function parseParameters($params)
{
$parameters = explode('&', $params);

foreach ($parameters as $parameter) {
$kv = explode('=', $parameter, 2);
$this->parameters[$kv[0]] = isset($kv[1]) ? $kv[1] : null;
}

return '';
}
}
77 changes: 77 additions & 0 deletions src/Factory/AbstractDsnAdapterFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

/*
* This file is part of php-cache\adapter-bundle package.
*
* (c) 2015-2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Cache\AdapterBundle\Factory;

use Cache\AdapterBundle\DSN;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* @author Aaron Scherer <aequasi@gmail.com>
*/
abstract class AbstractDsnAdapterFactory extends AbstractAdapterFactory
{
/**
* @type DSN
*/
private $DSN;

/**
* @return DSN
*/
protected function getDsn()
{
return $this->DSN;
}

/**
* {@inheritdoc}
*/
protected static function configureOptionResolver(OptionsResolver $resolver)
{
$resolver->setDefaults(['dsn' => '']);
$resolver->setAllowedTypes('dsn', ['string']);
}

/**
* {@inheritdoc}
*/
public static function validate(array $options, $adapterName)
{
parent::validate($options, $adapterName);

if (empty($options['dsn'])) {
return;
}

$dsn = new DSN($options['dsn']);
if (!$dsn->isValid()) {
throw new \InvalidArgumentException('Invalid DSN: '.$options['dsn']);
}
}

/**
* {@inheritdoc}
*/
public function createAdapter(array $options = [])
{
if (!empty($options['dsn'])) {
$dsn = new DSN($options['dsn']);
if (!$dsn->isValid()) {
throw new \InvalidArgumentException('Invalid DSN: '.$options['dsn']);
}

$this->DSN = $dsn;
}

return parent::createAdapter($options);
}
}
27 changes: 19 additions & 8 deletions src/Factory/MongoDBFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,26 @@

/**
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
* @author Aaron Scherer <aequasi@gmail.com>
*/
class MongoDBFactory extends AbstractAdapterFactory
class MongoDBFactory extends AbstractDsnAdapterFactory
{
protected static $dependencies = [
['requiredClass' => 'Cache\Adapter\MongoDB\MongoDBCachePool', 'packageName' => 'cache/mongodb-adapter'],
['requiredClass' => 'Cache\Adapter\MongoDB\MongoDBCachePool', 'packageName' => 'cache/mongodb-adapter'],
];

/**
* {@inheritdoc}
*/
public function getAdapter(array $config)
{
$manager = new Manager(sprintf('mongodb://%s:%s', $config['host'], $config['port']));
$dsn = static::getDsn();
if (empty($dsn)) {
$manager = new Manager(sprintf('mongodb://%s:%s', $config['host'], $config['port']));
} else {
$manager = new Manager($dsn->getDsn());
}

$collection = MongoDBCachePool::createCollection($manager, $config['namespace']);

return new MongoDBCachePool($collection);
Expand All @@ -40,11 +47,15 @@ public function getAdapter(array $config)
*/
protected static function configureOptionResolver(OptionsResolver $resolver)
{
$resolver->setDefaults([
'host' => '127.0.0.1',
'port' => 11211,
'namespace' => 'cache',
]);
parent::configureOptionResolver($resolver);

$resolver->setDefaults(
[
'host' => '127.0.0.1',
'port' => 11211,
'namespace' => 'cache',
]
);

$resolver->setAllowedTypes('host', ['string']);
$resolver->setAllowedTypes('port', ['string', 'int']);
Expand Down
Loading

0 comments on commit 4724d79

Please sign in to comment.