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 a4d62d3

Browse filesBrowse files
committed
Connection facotry
1 parent 11dbd24 commit a4d62d3
Copy full SHA for a4d62d3

11 files changed

+799
-2
lines changed

‎src/Symfony/Component/Dsn/Configuration/DsnFunction.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Dsn/Configuration/DsnFunction.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ public function getName(): string
5353
return $this->name;
5454
}
5555

56+
/**
57+
* @return array<DsnFunction|Dsn>
58+
*/
5659
public function getArguments(): array
5760
{
5861
return $this->arguments;
+60Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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+
declare(strict_types=1);
13+
14+
namespace Symfony\Component\Dsn;
15+
16+
/**
17+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
18+
*/
19+
class ConnectionFactory implements ConnectionFactoryInterface
20+
{
21+
/**
22+
* @var array<string> of classes implementing ConnectionFactoryInterface
23+
*/
24+
private static $factories = [];
25+
26+
public static function addFactory(string $factory, $prepend = false): void
27+
{
28+
if (!is_a($factory, ConnectionFactoryInterface::class, true)) {
29+
throw new \LogicException(sprintf('Argument to "%s::addFactory()" must be a class string to a class implementing "%s".', self::class, ConnectionFactoryInterface::class));
30+
}
31+
32+
if ($prepend) {
33+
array_unshift(self::$factories, $factory);
34+
} else {
35+
self::$factories[] = $factory;
36+
}
37+
}
38+
39+
public static function create(string $dsn): object
40+
{
41+
foreach (self::$factories as $factory) {
42+
if ($factory::supports($dsn)) {
43+
return $factory::create($dsn);
44+
}
45+
}
46+
47+
//throw new exception
48+
}
49+
50+
public static function supports(string $dsn): bool
51+
{
52+
foreach (self::$factories as $factory) {
53+
if ($factory::supports($dsn)) {
54+
return true;
55+
}
56+
}
57+
58+
return false;
59+
}
60+
}
+24Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
declare(strict_types=1);
13+
14+
namespace Symfony\Component\Dsn;
15+
16+
/**
17+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
18+
*/
19+
interface ConnectionFactoryInterface
20+
{
21+
public static function create(string $dsn): object;
22+
23+
public static function supports(string $dsn): bool;
24+
}
+51Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
declare(strict_types=1);
13+
14+
namespace Symfony\Component\Dsn;
15+
16+
class ConnectionRegistry
17+
{
18+
/**
19+
* @var array [dsn => Connection]
20+
*/
21+
private $connections = [];
22+
23+
/**
24+
* @var ConnectionFactoryInterface
25+
*/
26+
private $factory;
27+
28+
public function __construct(ConnectionFactoryInterface $factory)
29+
{
30+
$this->factory = $factory;
31+
}
32+
33+
public function addConnection(string $dsn, object $connection)
34+
{
35+
$this->connections[$dsn] = $connection;
36+
}
37+
38+
public function has(string $dsn): bool
39+
{
40+
return isset($this->connections[$dsn]);
41+
}
42+
43+
public function getConnection(string $dsn): object
44+
{
45+
if ($this->has($dsn)) {
46+
return $this->connections[$dsn];
47+
}
48+
49+
return $this->connections[$dsn] = $this->factory::create($dsn);
50+
}
51+
}
+23Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
declare(strict_types=1);
13+
14+
namespace Symfony\Component\Dsn\Exception;
15+
16+
/**
17+
* Base ExceptionInterface for the Dsn Component.
18+
*
19+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
20+
*/
21+
interface ExceptionInterface
22+
{
23+
}
+23Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
declare(strict_types=1);
13+
14+
namespace Symfony\Component\Dsn\Exception;
15+
16+
/**
17+
* Base InvalidArgumentException for the Dsn component.
18+
*
19+
* @author Jérémy Derussé <jeremy@derusse.com>
20+
*/
21+
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
22+
{
23+
}

‎src/Symfony/Component/Dsn/Exception/InvalidDsnException.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Dsn/Exception/InvalidDsnException.php
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818
*
1919
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
2020
*/
21-
class InvalidDsnException extends \InvalidArgumentException
21+
class InvalidDsnException extends InvalidArgumentException
2222
{
2323
private $dsn;
2424

2525
public function __construct(string $dsn, string $message)
2626
{
2727
$this->dsn = $dsn;
28-
parent::__construct(sprintf('%s. (%s)', $message, $dsn));
28+
parent::__construct(sprintf('%s (%s)', $message, $dsn));
2929
}
3030

3131
public function getDsn(): string
+176Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
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+
declare(strict_types=1);
13+
14+
namespace Symfony\Component\Dsn\Factory;
15+
16+
use Symfony\Component\Dsn\Configuration\Dsn;
17+
use Symfony\Component\Dsn\Configuration\Path;
18+
use Symfony\Component\Dsn\Configuration\Url;
19+
use Symfony\Component\Dsn\ConnectionFactoryInterface;
20+
use Symfony\Component\Dsn\DsnParser;
21+
use Symfony\Component\Dsn\Exception\FunctionNotSupportedException;
22+
use Symfony\Component\Dsn\Exception\InvalidArgumentException;
23+
24+
/**
25+
* @author Nicolas Grekas <p@tchwork.com>
26+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
27+
* @author Jérémy Derussé <jeremy@derusse.com>
28+
*/
29+
class MemcachedFactory implements ConnectionFactoryInterface
30+
{
31+
private static $defaultClientOptions = [
32+
'class' => null,
33+
'persistent_id' => null,
34+
'username' => null,
35+
'password' => null,
36+
'serializer' => 'php',
37+
];
38+
39+
/**
40+
* Example DSN strings.
41+
*
42+
* - memcached://localhost:11222?retry_timeout=10
43+
* - memcached(memcached://127.0.0.1)?persistent_id=foobar
44+
* - memcached(memcached://127.0.0.1 memcached://127.0.0.2?retry_timeout=10)?persistent_id=foobar
45+
*/
46+
public static function create(string $dsnString): object
47+
{
48+
$rootDsn = DsnParser::parseFunc($dsnString);
49+
if ('dsn' !== $rootDsn->getName() && 'memcached' !== $rootDsn->getName()) {
50+
throw new FunctionNotSupportedException($dsnString, $rootDsn->getName());
51+
}
52+
53+
set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); });
54+
55+
try {
56+
$options = $rootDsn->getParameters() + static::$defaultClientOptions;
57+
58+
$class = null === $options['class'] ? \Memcached::class : $options['class'];
59+
unset($options['class']);
60+
if (is_a($class, \Memcached::class, true)) {
61+
$client = new $class($options['persistent_id']);
62+
} elseif (class_exists($class, false)) {
63+
throw new InvalidArgumentException(sprintf('"%s" is not a subclass of "Memcached".', $class));
64+
} else {
65+
throw new InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
66+
}
67+
68+
$username = $options['username'];
69+
$password = $options['password'];
70+
71+
$servers = [];
72+
foreach ($rootDsn->getArguments() as $dsn) {
73+
if (!$dsn instanceof Dsn) {
74+
throw new InvalidArgumentException('Only one DSN function is allowed.');
75+
}
76+
77+
if ('memcached' !== $dsn->getScheme()) {
78+
throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: "%s" does not start with "memcached://".', $dsn));
79+
}
80+
81+
$username = $dsn->getUser() ?? $username;
82+
$password = $dsn->getPassword() ?? $password;
83+
$path = $dsn->getPath();
84+
$params['weight'] = 0;
85+
86+
if (null !== $path && preg_match('#/(\d+)$#', $path, $m)) {
87+
$params['weight'] = $m[1];
88+
$path = substr($path, 0, -\strlen($m[0]));
89+
}
90+
91+
if ($dsn instanceof Url) {
92+
$servers[] = [$dsn->getHost(), $dsn->getPort() ?? 11211, $params['weight']];
93+
} elseif ($dsn instanceof Path) {
94+
$params['host'] = $path;
95+
$servers[] = [$path, null, $params['weight']];
96+
} else {
97+
foreach ($dsn->getParameter('hosts', []) as $host => $weight) {
98+
if (false === $port = strrpos($host, ':')) {
99+
$hosts[$host] = [$host, 11211, (int) $weight];
100+
} else {
101+
$hosts[$host] = [substr($host, 0, $port), (int) substr($host, 1 + $port), (int) $weight];
102+
}
103+
}
104+
$servers = array_merge($servers, array_values($hosts));
105+
}
106+
107+
$params += $dsn->getParameters();
108+
$options = $dsn->getParameters() + $options;
109+
}
110+
111+
// set client's options
112+
unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']);
113+
$options = array_change_key_case($options, CASE_UPPER);
114+
$client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
115+
$client->setOption(\Memcached::OPT_NO_BLOCK, true);
116+
$client->setOption(\Memcached::OPT_TCP_NODELAY, true);
117+
if (!\array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !\array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) {
118+
$client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
119+
}
120+
foreach ($options as $name => $value) {
121+
if (\is_int($name)) {
122+
continue;
123+
}
124+
if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) {
125+
$value = \constant('Memcached::'.$name.'_'.strtoupper($value));
126+
}
127+
$opt = \constant('Memcached::OPT_'.$name);
128+
129+
unset($options[$name]);
130+
$options[$opt] = $value;
131+
}
132+
$client->setOptions($options);
133+
134+
// set client's servers, taking care of persistent connections
135+
if (!$client->isPristine()) {
136+
$oldServers = [];
137+
foreach ($client->getServerList() as $server) {
138+
$oldServers[] = [$server['host'], $server['port']];
139+
}
140+
141+
$newServers = [];
142+
foreach ($servers as $server) {
143+
if (1 < \count($server)) {
144+
$server = array_values($server);
145+
unset($server[2]);
146+
$server[1] = (int) $server[1];
147+
}
148+
$newServers[] = $server;
149+
}
150+
151+
if ($oldServers !== $newServers) {
152+
$client->resetServerList();
153+
$client->addServers($servers);
154+
}
155+
} else {
156+
$client->addServers($servers);
157+
}
158+
159+
if (null !== $username || null !== $password) {
160+
if (!method_exists($client, 'setSaslAuthData')) {
161+
trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.');
162+
}
163+
$client->setSaslAuthData($username, $password);
164+
}
165+
166+
return $client;
167+
} finally {
168+
restore_error_handler();
169+
}
170+
}
171+
172+
public static function supports(string $dsn): bool
173+
{
174+
return 0 !== strpos($dsn, 'memcached:');
175+
}
176+
}

0 commit comments

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