Completed
Push — 1.x ( d54394...964a06 )
by Cy
01:54
created

RedisSentinelDatabase   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 8
lcom 1
cbo 4
dl 0
loc 139
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A subscribe() 0 9 1
A psubscribe() 0 4 1
A createSingleClients() 0 20 3
A createSingleClient() 0 22 1
A setSentinelConnectionOptions() 0 10 2
1
<?php
2
3
namespace Monospice\LaravelRedisSentinel;
4
5
use Closure;
6
use Illuminate\Redis\Database as RedisDatabase;
7
use Illuminate\Support\Arr;
8
use Monospice\LaravelRedisSentinel\PredisConnection;
9
use Monospice\SpicyIdentifiers\DynamicMethod;
10
11
/**
12
 * Enables Laravel's Redis database driver to accept configuration options for
13
 * Redis Sentinel connections independently.
14
 *
15
 * By default, Laravel's Redis service permits a single set of configuration
16
 * options for all of the Redis connections passed to the Predis client. This
17
 * prevents us from declaring separate parameters for individual Redis services
18
 * managed by Sentinel. For example, we may wish to connect to a separate Redis
19
 * Sentinel replication group, or use separate Redis databases for caching,
20
 * queues, and sessions. This wrapper class enables us to declare parameters
21
 * for each connection in the "redis-sentinel" block of the database
22
 * configuration which it will use to configure individual clients.
23
 *
24
 * @category Package
25
 * @package  Monospice\LaravelRedisSentinel
26
 * @author   Cy Rossignol <[email protected]>
27
 * @license  See LICENSE file
28
 * @link     https://github.com/monospice/laravel-redis-sentinel-drivers
29
 */
30
class RedisSentinelDatabase extends RedisDatabase
31
{
32
    /**
33
     * Configuration options specific to Sentinel connection operation
34
     *
35
     * We cannot pass these options as an array to the Predis client.
36
     * Instead, we'll set them on the connection directly using methods
37
     * provided by the SentinelReplication class of the Predis package.
38
     *
39
     * @var array
40
     */
41
    protected $sentinelConnectionOptionKeys = [
42
        'sentinel_timeout',
43
        'retry_wait',
44
        'retry_limit',
45
        'update_sentinels',
46
    ];
47
48
    /**
49
     * Subscribe to a set of given channels for messages.
50
     *
51
     * @param array|string $channels   The names of the channels to subscribe to
52
     * @param Closure      $callback   Executed for each message. Receives the
53
     * message string in the first argument and the message channel as the
54
     * second argument. Return FALSE to unsubscribe.
55
     * @param string|null  $connection Name of the connection to subscribe with.
56
     * @param string       $method     The subscription command ("subscribe" or
57
     * "psubscribe").
58
     *
59
     * @return void
60
     */
61
    public function subscribe(
62
        $channels,
63
        Closure $callback,
64
        $connection = null,
65
        $method = 'subscribe'
66
    ) {
67
        $this->connection($connection)
68
            ->createSubscription($channels, $callback, $method);
69
    }
70
71
    /**
72
     * Subscribe to a set of given channels with wildcards.
73
     *
74
     * @param array|string $channels   The names of the channels to subscribe to
75
     * @param Closure      $callback   Executed for each message. Receives the
76
     * message string in the first argument and the message channel as the
77
     * second argument. Return FALSE to unsubscribe.
78
     * @param string|null  $connection Name of the connection to subscribe with.
79
     *
80
     * @return void
81
     */
82
    public function psubscribe($channels, Closure $callback, $connection = null)
83
    {
84
        $this->subscribe($channels, $callback, $connection, __FUNCTION__);
85
    }
86
87
    /**
88
     * Create an array of single connection clients.
89
     *
90
     * @param array $servers The set of options for each Sentinel connection
91
     * @param array $options The global options shared by all Sentinel clients
92
     *
93
     * @return array Each element contains a Predis client that represents a
94
     * connection defined in the 'redis-sentinel' block in config/database.php
95
     */
96
    protected function createSingleClients(array $servers, array $options = [])
97
    {
98
        $clients = [];
99
100
        // Laravel < 5.1 doesn't extract the global options from the connection
101
        // configuration for us
102
        if (array_key_exists('options', $servers)) {
103
            $options = (array) Arr::pull($servers, 'options');
104
        }
105
106
        // Automatically set "replication" to "sentinel". This is the Sentinel
107
        // driver, after all.
108
        $options['replication'] = 'sentinel';
109
110
        foreach ($servers as $key => $server) {
111
            $clients[$key] = $this->createSingleClient($server, $options);
112
        }
113
114
        return $clients;
115
    }
116
117
    /**
118
     * Create a Predis client instance for a Redis Sentinel connection
119
     *
120
     * @param array $server  The configuration options for the connection
121
     * @param array $options The global options shared by all Sentinel clients
122
     *
123
     * @return Client The Predis Client instance
0 ignored issues
show
Documentation introduced by
Should the return type not be PredisConnection?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
124
     */
125
    protected function createSingleClient(array $server, array $options)
126
    {
127
        // Merge the global options shared by all Sentinel connections with
128
        // connection-specific options
129
        $clientOpts = (array) Arr::pull($server, 'options');
130
        $clientOpts = array_merge($options, $clientOpts);
131
132
        $sentinelKeys = array_flip($this->sentinelConnectionOptionKeys);
133
134
        // Extract the array of Sentinel connection options from the rest of
135
        // the client options
136
        $sentinelOpts = array_intersect_key($clientOpts, $sentinelKeys);
137
138
        // Filter the Sentinel connection options elements from the client
139
        // options array
140
        $clientOpts = array_diff_key($clientOpts, $sentinelKeys);
141
142
        $client = new PredisConnection($server, $clientOpts);
143
        $this->setSentinelConnectionOptions($client, $sentinelOpts);
144
145
        return $client;
146
    }
147
148
    /**
149
     * Sets the Sentinel-specific connection options on a Predis Client
150
     * connection
151
     *
152
     * @param Client $client       The Predis Client to set options for
0 ignored issues
show
Documentation introduced by
Should the type for parameter $client not be PredisConnection?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
153
     * @param array  $sentinelOpts The options supported by Predis for
154
     * Sentinel-specific connections
155
     *
156
     * @return void
157
     */
158
    protected function setSentinelConnectionOptions(
159
        PredisConnection $client,
160
        array $sentinelOpts
161
    ) {
162
        foreach ($sentinelOpts as $option => $value) {
163
            DynamicMethod::parseFromUnderscore($option)
164
                ->prepend('set')
165
                ->callOn($client, [ $value ]);
166
        }
167
    }
168
}
169