RedisRoom::add()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 3
eloc 4
c 1
b 0
f 1
nc 4
nop 2
dl 0
loc 8
rs 10
1
<?php
2
3
namespace SwooleTW\Http\Websocket\Rooms;
4
5
use Illuminate\Support\Arr;
6
use Predis\Client as RedisClient;
7
use Predis\Pipeline\Pipeline;
8
9
/**
10
 * Class RedisRoom
11
 */
12
class RedisRoom implements RoomContract
13
{
14
    /**
15
     * @var \Predis\Client
16
     */
17
    protected $redis;
18
19
    /**
20
     * @var array
21
     */
22
    protected $config;
23
24
    /**
25
     * @var string
26
     */
27
    protected $prefix = 'swoole:';
28
29
    /**
30
     * RedisRoom constructor.
31
     *
32
     * @param array $config
33
     */
34
    public function __construct(array $config)
35
    {
36
        $this->config = $config;
37
    }
38
39
    /**
40
     * @param \Predis\Client|null $redis
41
     *
42
     * @return \SwooleTW\Http\Websocket\Rooms\RoomContract
43
     */
44
    public function prepare(RedisClient $redis = null): RoomContract
45
    {
46
        $this->setRedis($redis);
47
        $this->setPrefix();
48
        $this->cleanRooms();
49
50
        return $this;
51
    }
52
53
    /**
54
     * Set redis client.
55
     *
56
     * @param \Predis\Client|null $redis
57
     */
58
    public function setRedis(?RedisClient $redis = null)
59
    {
60
        if (! $redis) {
61
            $server = Arr::get($this->config, 'server', []);
62
            $options = Arr::get($this->config, 'options', []);
63
64
            // forbid setting prefix from options
65
            if (Arr::has($options, 'prefix')) {
66
                $options = Arr::except($options, 'prefix');
67
            }
68
69
            $redis = new RedisClient($server, $options);
70
        }
71
72
        $this->redis = $redis;
73
    }
74
75
    /**
76
     * Set key prefix from config.
77
     */
78
    protected function setPrefix()
79
    {
80
        if ($prefix = Arr::get($this->config, 'prefix')) {
81
            $this->prefix = $prefix;
82
        }
83
    }
84
85
    /**
86
     * Get redis client.
87
     */
88
    public function getRedis()
89
    {
90
        return $this->redis;
91
    }
92
93
    /**
94
     * Add multiple socket fds to a room.
95
     *
96
     * @param int fd
0 ignored issues
show
Bug introduced by
The type SwooleTW\Http\Websocket\Rooms\fd was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
97
     * @param array|string rooms
0 ignored issues
show
Bug introduced by
The type SwooleTW\Http\Websocket\Rooms\rooms was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
98
     */
99
    public function add(int $fd, $rooms)
100
    {
101
        $rooms = is_array($rooms) ? $rooms : [$rooms];
102
103
        $this->addValue($fd, $rooms, RoomContract::DESCRIPTORS_KEY);
104
105
        foreach ($rooms as $room) {
106
            $this->addValue($room, [$fd], RoomContract::ROOMS_KEY);
107
        }
108
    }
109
110
    /**
111
     * Delete multiple socket fds from a room.
112
     *
113
     * @param int fd
114
     * @param array|string rooms
115
     */
116
    public function delete(int $fd, $rooms)
117
    {
118
        $rooms = is_array($rooms) ? $rooms : [$rooms];
119
        $rooms = count($rooms) ? $rooms : $this->getRooms($fd);
120
121
        $this->removeValue($fd, $rooms, RoomContract::DESCRIPTORS_KEY);
122
123
        foreach ($rooms as $room) {
124
            $this->removeValue($room, [$fd], RoomContract::ROOMS_KEY);
125
        }
126
    }
127
128
    /**
129
     * Add value to redis.
130
     *
131
     * @param $key
132
     * @param array $values
133
     * @param string $table
134
     *
135
     * @return $this
136
     */
137
    public function addValue($key, array $values, string $table)
138
    {
139
        $this->checkTable($table);
140
        $redisKey = $this->getKey($key, $table);
141
142
        $this->redis->pipeline(function (Pipeline $pipe) use ($redisKey, $values) {
143
            foreach ($values as $value) {
144
                $pipe->sadd($redisKey, $value);
145
            }
146
        });
147
148
        return $this;
149
    }
150
151
    /**
152
     * Remove value from redis.
153
     *
154
     * @param $key
155
     * @param array $values
156
     * @param string $table
157
     *
158
     * @return $this
159
     */
160
    public function removeValue($key, array $values, string $table)
161
    {
162
        $this->checkTable($table);
163
        $redisKey = $this->getKey($key, $table);
164
165
        $this->redis->pipeline(function (Pipeline $pipe) use ($redisKey, $values) {
166
            foreach ($values as $value) {
167
                $pipe->srem($redisKey, $value);
168
            }
169
        });
170
171
        return $this;
172
    }
173
174
    /**
175
     * Get all sockets by a room key.
176
     *
177
     * @param string room
178
     *
179
     * @return array
180
     */
181
    public function getClients(string $room)
182
    {
183
        return $this->getValue($room, RoomContract::ROOMS_KEY) ?? [];
184
    }
185
186
    /**
187
     * Get all rooms by a fd.
188
     *
189
     * @param int fd
190
     *
191
     * @return array
192
     */
193
    public function getRooms(int $fd)
194
    {
195
        return $this->getValue($fd, RoomContract::DESCRIPTORS_KEY) ?? [];
196
    }
197
198
    /**
199
     * Check table for rooms and descriptors.
200
     *
201
     * @param string $table
202
     */
203
    protected function checkTable(string $table)
204
    {
205
        if (! in_array($table, [RoomContract::ROOMS_KEY, RoomContract::DESCRIPTORS_KEY])) {
206
            throw new \InvalidArgumentException("Invalid table name: `{$table}`.");
207
        }
208
    }
209
210
    /**
211
     * Get value.
212
     *
213
     * @param string $key
214
     * @param string $table
215
     *
216
     * @return array
217
     */
218
    public function getValue(string $key, string $table)
219
    {
220
        $this->checkTable($table);
221
222
        $result = $this->redis->smembers($this->getKey($key, $table));
223
224
        // Try to fix occasional non-array returned result
225
        return is_array($result) ? $result : [];
226
    }
227
228
    /**
229
     * Get key.
230
     *
231
     * @param string $key
232
     * @param string $table
233
     *
234
     * @return string
235
     */
236
    public function getKey(string $key, string $table)
237
    {
238
        return "{$this->prefix}{$table}:{$key}";
239
    }
240
241
    /**
242
     * Clean all rooms.
243
     */
244
    protected function cleanRooms(): void
245
    {
246
        if (count($keys = $this->redis->keys("{$this->prefix}*"))) {
247
            $this->redis->del($keys);
248
        }
249
    }
250
}
251