Skip to content

Commit b02cf9e

Browse files
committed
Factory accepts Connector from Socket and deprecate legacy SocketClient
1 parent a22406c commit b02cf9e

File tree

8 files changed

+174
-15
lines changed

8 files changed

+174
-15
lines changed

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,29 @@ $factory = new Factory($loop);
8181
```
8282

8383
If you need custom DNS, proxy or TLS settings, you can explicitly pass a
84-
custom instance of the [`ConnectorInterface`](https://github.com/reactphp/socket-client#connectorinterface):
84+
custom instance of the [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface):
8585

8686
```php
87+
$connector = new \React\Socket\Connector($loop, array(
88+
'dns' => '127.0.0.1',
89+
'tcp' => array(
90+
'bindto' => '192.168.10.1:0'
91+
),
92+
'tls' => array(
93+
'verify_peer' => false,
94+
'verify_peer_name' => false
95+
)
96+
));
97+
8798
$factory = new Factory($loop, $connector);
8899
```
89100

101+
> Legacy notice: As of `v1.2.0`, the optional connector should implement the new
102+
`React\Socket\ConnectorInterface`. For BC reasons it also accepts the
103+
legacy `React\SocketClient\ConnectorInterface`.
104+
This legacy API will be removed in a future `v2.0.0` version, so it's highly
105+
recommended to upgrade to the above API.
106+
90107
#### createClient()
91108

92109
The `createClient($redisUri = null)` method can be used to create a new [`Client`](#client).

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
],
1313
"require": {
1414
"php": ">=5.3",
15+
"react/event-loop": "0.3.*|0.4.*",
1516
"react/promise": "^2.0 || ^1.1",
17+
"react/socket": "^0.7",
1618
"react/socket-client": "^0.7",
17-
"react/event-loop": "0.3.*|0.4.*",
1819
"clue/redis-protocol": "0.3.*",
1920
"evenement/evenement": "~1.0|~2.0"
2021
},

src/ConnectionUpcaster.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
namespace Clue\React\Redis;
4+
5+
use Evenement\EventEmitter;
6+
use React\Socket\ConnectionInterface;
7+
use React\Stream\DuplexStreamInterface;
8+
use React\Stream\WritableStreamInterface;
9+
use React\Stream\Util;
10+
11+
/**
12+
* Adapter to upcast a legacy SocketClient-Connector result to a new Socket-ConnectionInterface
13+
*
14+
* @internal
15+
*/
16+
class ConnectionUpcaster extends EventEmitter implements ConnectionInterface
17+
{
18+
private $stream;
19+
20+
public function __construct(DuplexStreamInterface $stream)
21+
{
22+
$this->stream = $stream;
23+
24+
Util::forwardEvents($stream, $this, array('data', 'end', 'close', 'error', 'drain'));
25+
$this->stream->on('close', array($this, 'close'));
26+
}
27+
28+
public function isReadable()
29+
{
30+
return $this->stream->isReadable();
31+
}
32+
33+
public function isWritable()
34+
{
35+
return $this->isWritable();
36+
}
37+
38+
public function pause()
39+
{
40+
$this->stream->pause();
41+
}
42+
43+
public function resume()
44+
{
45+
$this->stream->resume();
46+
}
47+
48+
public function pipe(WritableStreamInterface $dest, array $options = array())
49+
{
50+
$this->stream->pipe($dest, $options);
51+
}
52+
53+
public function write($data)
54+
{
55+
return $this->stream->write($data);
56+
}
57+
58+
public function end($data = null)
59+
{
60+
return $this->stream->end($data);
61+
}
62+
63+
public function close()
64+
{
65+
$this->stream->close();
66+
$this->removeAllListeners();
67+
}
68+
69+
public function getRemoteAddress()
70+
{
71+
return null;
72+
}
73+
74+
public function getLocalAddress()
75+
{
76+
return null;
77+
}
78+
}

src/ConnectorUpcaster.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Clue\React\Redis;
4+
5+
use React\Socket\ConnectorInterface;
6+
use React\SocketClient\ConnectorInterface as LegacyConnectorInterface;
7+
use React\Stream\DuplexStreamInterface;
8+
9+
/**
10+
* Adapter to upcast a legacy SocketClient:v0.7/v0.6 Connector to a new Socket:v0.8 Connector
11+
*
12+
* @internal
13+
*/
14+
class ConnectorUpcaster implements ConnectorInterface
15+
{
16+
private $legacy;
17+
18+
public function __construct(LegacyConnectorInterface $connector)
19+
{
20+
$this->legacy = $connector;
21+
}
22+
23+
public function connect($uri)
24+
{
25+
return $this->legacy->connect($uri)->then(function (DuplexStreamInterface $stream) {
26+
return new ConnectionUpcaster($stream);
27+
});
28+
}
29+
}

src/Factory.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,35 @@
22

33
namespace Clue\React\Redis;
44

5-
use React\SocketClient\ConnectorInterface;
6-
use React\Stream\Stream;
75
use Clue\React\Redis\StreamingClient;
86
use Clue\Redis\Protocol\Factory as ProtocolFactory;
9-
use React\SocketClient\Connector;
10-
use InvalidArgumentException;
117
use React\EventLoop\LoopInterface;
128
use React\Promise;
9+
use React\Socket\ConnectionInterface;
10+
use React\Socket\Connector;
11+
use React\Socket\ConnectorInterface;
12+
use InvalidArgumentException;
1313

1414
class Factory
1515
{
1616
private $connector;
1717
private $protocol;
1818

19-
public function __construct(LoopInterface $loop, ConnectorInterface $connector = null, ProtocolFactory $protocol = null)
19+
/**
20+
* @param LoopInterface $loop
21+
* @param ConnectorInterface|\React\SocketClient\ConnectorInterface|null $connector
22+
* [optional] Connector to use. Should be `null` in order to use default
23+
* Connector. Passing a `\React\SocketClient\ConnectorInterface` is
24+
* deprecated and only supported for BC reasons and will be removed in
25+
* future versions.
26+
* @param ProtocolFactory|null $protocol
27+
*/
28+
public function __construct(LoopInterface $loop, $connector = null, ProtocolFactory $protocol = null)
2029
{
2130
if ($connector === null) {
2231
$connector = new Connector($loop);
32+
} elseif (!$connector instanceof ConnectorInterface) {
33+
$connector = new ConnectorUpcaster($connector);
2334
}
2435

2536
if ($protocol === null) {
@@ -46,7 +57,7 @@ public function createClient($target = null)
4657

4758
$protocol = $this->protocol;
4859

49-
$promise = $this->connector->connect($parts['host'] . ':' . $parts['port'])->then(function (Stream $stream) use ($protocol) {
60+
$promise = $this->connector->connect($parts['host'] . ':' . $parts['port'])->then(function (ConnectionInterface $stream) use ($protocol) {
5061
return new StreamingClient($stream, $protocol->createResponseParser(), $protocol->createSerializer());
5162
});
5263

src/StreamingClient.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
namespace Clue\React\Redis;
44

55
use Evenement\EventEmitter;
6-
use React\Stream\Stream;
76
use Clue\Redis\Protocol\Parser\ParserInterface;
87
use Clue\Redis\Protocol\Parser\ParserException;
9-
use Clue\Redis\Protocol\Model\ErrorReplyException;
108
use Clue\Redis\Protocol\Serializer\SerializerInterface;
119
use Clue\Redis\Protocol\Factory as ProtocolFactory;
1210
use UnderflowException;
@@ -17,6 +15,7 @@
1715
use Clue\Redis\Protocol\Model\ModelInterface;
1816
use Clue\Redis\Protocol\Model\MultiBulkReply;
1917
use Clue\Redis\Protocol\Model\StatusReply;
18+
use React\Stream\DuplexStreamInterface;
2019

2120
/**
2221
* @internal
@@ -34,7 +33,7 @@ class StreamingClient extends EventEmitter implements Client
3433
private $psubscribed = 0;
3534
private $monitoring = false;
3635

37-
public function __construct(Stream $stream, ParserInterface $parser = null, SerializerInterface $serializer = null)
36+
public function __construct(DuplexStreamInterface $stream, ParserInterface $parser = null, SerializerInterface $serializer = null)
3837
{
3938
if ($parser === null || $serializer === null) {
4039
$factory = new ProtocolFactory();

tests/FactoryTest.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class FactoryTest extends TestCase
1212
public function setUp()
1313
{
1414
$this->loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();
15-
$this->connector = $this->getMockBuilder('React\SocketClient\ConnectorInterface')->getMock();
15+
$this->connector = $this->getMockBuilder('React\Socket\ConnectorInterface')->getMock();
1616
$this->factory = new Factory($this->loop, $this->connector);
1717
}
1818

@@ -39,9 +39,18 @@ public function testWillConnectToLocalIpWhenTargetIsLocalhost()
3939
$this->factory->createClient('tcp://localhost:1337');
4040
}
4141

42+
public function testWillUpcastLegacyConnectorAndConnect()
43+
{
44+
$this->connector = $this->getMockBuilder('React\SocketClient\ConnectorInterface')->getMock();
45+
$this->factory = new Factory($this->loop, $this->connector);
46+
47+
$this->connector->expects($this->once())->method('connect')->with('example.com:1337')->willReturn(Promise\reject(new \RuntimeException()));
48+
$this->factory->createClient('tcp://example.com:1337');
49+
}
50+
4251
public function testWillResolveIfConnectorResolves()
4352
{
44-
$stream = $this->getMockBuilder('React\Stream\Stream')->disableOriginalConstructor()->getMock();
53+
$stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
4554
$stream->expects($this->never())->method('write');
4655

4756
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
@@ -52,7 +61,7 @@ public function testWillResolveIfConnectorResolves()
5261

5362
public function testWillWriteSelectCommandIfTargetContainsPath()
5463
{
55-
$stream = $this->getMockBuilder('React\Stream\Stream')->disableOriginalConstructor()->getMock();
64+
$stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
5665
$stream->expects($this->once())->method('write')->with("*2\r\n$6\r\nselect\r\n$4\r\ndemo\r\n");
5766

5867
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));
@@ -61,7 +70,7 @@ public function testWillWriteSelectCommandIfTargetContainsPath()
6170

6271
public function testWillWriteAuthCommandIfTargetContainsUserInfo()
6372
{
64-
$stream = $this->getMockBuilder('React\Stream\Stream')->disableOriginalConstructor()->getMock();
73+
$stream = $this->getMockBuilder('React\Socket\ConnectionInterface')->getMock();
6574
$stream->expects($this->once())->method('write')->with("*2\r\n$4\r\nauth\r\n$11\r\nhello:world\r\n");
6675

6776
$this->connector->expects($this->once())->method('connect')->willReturn(Promise\resolve($stream));

tests/FunctionalTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Clue\React\Redis\StreamingClient;
66
use React\Promise\Deferred;
77
use Clue\React\Block;
8+
use React\SocketClient\Connector;
89

910
class FunctionalTest extends TestCase
1011
{
@@ -39,6 +40,20 @@ public function testPing()
3940
return $client;
4041
}
4142

43+
public function testPingClientWithLegacyConnector()
44+
{
45+
$this->client->close();
46+
$this->factory = new Factory($this->loop, new Connector($this->loop));
47+
$this->client = $this->createClient(getenv('REDIS_URI'));
48+
49+
$promise = $this->client->ping();
50+
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
51+
$promise->then($this->expectCallableOnce('PONG'));
52+
53+
$this->client->end();
54+
$this->waitFor($this->client);
55+
}
56+
4257
public function testMgetIsNotInterpretedAsSubMessage()
4358
{
4459
$client = $this->client;

0 commit comments

Comments
 (0)