11
11
use Clue \Redis \Protocol \Factory as ProtocolFactory ;
12
12
use UnderflowException ;
13
13
use RuntimeException ;
14
+ use InvalidArgumentException ;
14
15
use React \Promise \Deferred ;
15
16
use Clue \Redis \Protocol \Model \ErrorReply ;
16
17
use Clue \Redis \Protocol \Model \ModelInterface ;
18
+ use Clue \Redis \Protocol \Model \MultiBulkReply ;
17
19
use Clue \Redis \Protocol \Model \StatusReply ;
18
20
19
21
class StreamingClient extends EventEmitter implements Client
@@ -25,6 +27,8 @@ class StreamingClient extends EventEmitter implements Client
25
27
private $ ending = false ;
26
28
private $ closed = false ;
27
29
30
+ private $ subscribed = 0 ;
31
+ private $ psubscribed = 0 ;
28
32
private $ monitoring = false ;
29
33
30
34
public function __construct (Stream $ stream , ParserInterface $ parser = null , SerializerInterface $ serializer = null )
@@ -74,18 +78,43 @@ public function __call($name, $args)
74
78
{
75
79
$ request = new Deferred ();
76
80
81
+ $ name = strtolower ($ name );
82
+
83
+ // special (p)(un)subscribe commands only accept a single parameter and have custom response logic applied
84
+ static $ pubsubs = array ('subscribe ' , 'unsubscribe ' , 'psubscribe ' , 'punsubscribe ' );
85
+
77
86
if ($ this ->ending ) {
78
87
$ request ->reject (new RuntimeException ('Connection closed ' ));
88
+ } elseif (count ($ args ) !== 1 && in_array ($ name , $ pubsubs )) {
89
+ $ request ->reject (new InvalidArgumentException ('PubSub commands limited to single argument ' ));
79
90
} else {
80
91
$ this ->stream ->write ($ this ->serializer ->getRequestMessage ($ name , $ args ));
81
92
$ this ->requests []= $ request ;
82
93
}
83
94
84
- if (strtolower ( $ name) === 'monitor ' ) {
95
+ if ($ name === 'monitor ' ) {
85
96
$ monitoring =& $ this ->monitoring ;
86
97
$ request ->then (function () use (&$ monitoring ) {
87
98
$ monitoring = true ;
88
99
});
100
+ } elseif (in_array ($ name , $ pubsubs )) {
101
+ $ that = $ this ;
102
+ $ subscribed =& $ this ->subscribed ;
103
+ $ psubscribed =& $ this ->psubscribed ;
104
+
105
+ $ request ->then (function ($ array ) use ($ that , &$ subscribed , &$ psubscribed ) {
106
+ $ first = array_shift ($ array );
107
+
108
+ // (p)(un)subscribe messages are to be forwarded
109
+ $ that ->emit ($ first , $ array );
110
+
111
+ // remember number of (p)subscribe topics
112
+ if ($ first === 'subscribe ' || $ first === 'unsubscribe ' ) {
113
+ $ subscribed = $ array [1 ];
114
+ } else {
115
+ $ psubscribed = $ array [1 ];
116
+ }
117
+ });
89
118
}
90
119
91
120
return $ request ->promise ();
@@ -100,6 +129,17 @@ public function handleMessage(ModelInterface $message)
100
129
return ;
101
130
}
102
131
132
+ if (($ this ->subscribed !== 0 || $ this ->psubscribed !== 0 ) && $ message instanceof MultiBulkReply) {
133
+ $ array = $ message ->getValueNative ();
134
+ $ first = array_shift ($ array );
135
+
136
+ // pub/sub messages are to be forwarded and should not be processed as request responses
137
+ if (in_array ($ first , array ('message ' , 'pmessage ' ))) {
138
+ $ this ->emit ($ first , $ array );
139
+ return ;
140
+ }
141
+ }
142
+
103
143
if (!$ this ->requests ) {
104
144
throw new UnderflowException ('Unexpected reply received, no matching request found ' );
105
145
}
0 commit comments