Completed
Push — master ( c0f44e...28fb72 )
by Harrison
01:40
created

PhpredisAdapter::setNamespace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Linio\Component\Cache\Adapter;
6
7
use Linio\Component\Cache\Exception\InvalidConfigurationException;
8
use Linio\Component\Cache\Exception\KeyNotFoundException;
9
use Redis;
10
11
class PhpredisAdapter extends AbstractAdapter implements AdapterInterface
12
{
13
    /**
14
     * @var Redis
15
     */
16
    protected $client;
17
18
    /**
19
     * @var int
20
     */
21
    protected $ttl;
22
23
    /**
24
     * @var array
25
     */
26
    protected $config;
27
28
    public function __construct(array $config = [], bool $lazy = true)
29
    {
30
        if (!extension_loaded('redis')) {
31
            throw new InvalidConfigurationException('PhpRedisAdapter requires "phpredis" extension. See https://github.com/phpredis/phpredis.');
32
        }
33
34
        $this->config = $config;
35
36
        if (!$lazy) {
37
            $this->getClient();
38
        }
39
    }
40
41
    protected function getClient(): Redis
42
    {
43
        if (!$this->client instanceof Redis) {
44
            $this->createClient($this->config);
45
        }
46
47
        return $this->client;
48
    }
49
50 View Code Duplication
    public function get(string $key)
51
    {
52
        $result = $this->getClient()->get($key);
53
54
        if ($result === false && !$this->getClient()->exists($key)) {
55
            throw new KeyNotFoundException();
56
        }
57
58
        return $result;
59
    }
60
61 View Code Duplication
    public function getMulti(array $keys): array
62
    {
63
        $result = $this->getClient()->mGet($keys);
64
        $values = [];
65
66
        foreach ($keys as $index => $key) {
67
            if ($result[$index]) {
68
                $values[$key] = $result[$index];
69
            }
70
        }
71
72
        return $values;
73
    }
74
75
    public function set(string $key, $value): bool
76
    {
77 View Code Duplication
        if ($this->ttl === null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
78
            $result = $this->getClient()->set($key, $value);
79
        } else {
80
            $result = $this->getClient()->setex($key, $this->ttl, $value);
81
        }
82
83
        return (bool) $result;
84
    }
85
86
    public function setMulti(array $data): bool
87
    {
88
        if ($this->ttl === null) {
89
            $result = $this->getClient()->mset($data);
90
        } else {
91
            $result = true;
92
            foreach ($data as $key => $value) {
93
                $result = $result && $this->client->setex($key, $this->ttl, $value);
94
            }
95
        }
96
97
        return (bool) $result;
98
    }
99
100
    public function contains(string $key): bool
101
    {
102
        return $this->getClient()->exists($key);
103
    }
104
105
    public function delete(string $key): bool
106
    {
107
        $this->client->delete($key);
108
109
        return true;
110
    }
111
112
    public function deleteMulti(array $keys): bool
113
    {
114
        $this->client->delete($keys);
115
116
        return true;
117
    }
118
119
    public function flush(): bool
120
    {
121
        return (bool) $this->getClient()->flushDB();
122
    }
123
124
    public function setClient(Redis $client)
125
    {
126
        $this->client = $client;
127
    }
128
129
    protected function createClient(array $config)
130
    {
131
        $params = $this->getConnectionParameters($config);
132
        $this->client = new Redis();
133
134
        if ($params['connection_persistent']) {
135
            $connectionId = 1;
136
137
            if ($params['pool_size'] > 1) {
138
                $connectionId = mt_rand(1, $params['pool_size']);
139
            }
140
141
            $persistentId = sprintf('%s-%s-%s', $params['port'], $params['database'], $connectionId);
142
            $this->client->pconnect($params['host'], $params['port'], $params['timeout'] ?? 0, $persistentId, $params['retry_interval'] ?? 0);
143
        } else {
144
            $this->client->connect($params['host'], $params['port'], $params['timeout'] ?? 0, '', $params['retry_interval'] ?? 0);
145
        }
146
147
        if ($params['password']) {
148
            if (!$this->client->auth($params['password'])) {
149
                throw new InvalidConfigurationException(sprintf('Invalid password for phpredis adapter: %s:%s', $params['host'], $params['port']));
150
            }
151
        }
152
153
        if ($params['database']) {
154
            $this->client->select($params['database']);
155
        }
156
157
        if ($params['serializer']) {
158
            switch ($params['serializer']) {
159
                case 'none':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
160
                    $this->client->setOption(Redis::OPT_SERIALIZER, (string) Redis::SERIALIZER_NONE);
161
                    break;
162
                case 'php':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
163
                    $this->client->setOption(Redis::OPT_SERIALIZER, (string) Redis::SERIALIZER_PHP);
164
                    break;
165
                case 'igbinary':
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
166
                    if (!extension_loaded('igbinary')) {
167
                        throw new InvalidConfigurationException('Serializer igbinary requires "igbinary" extension. See https://pecl.php.net/package/igbinary');
168
                    }
169
170
                    if (!defined('Redis::SERIALIZER_IGBINARY')) {
171
                        throw new InvalidConfigurationException('Serializer igbinary requires run extension compilation using configure with --enable-redis-igbinary');
172
                    }
173
174
                    $this->client->setOption(Redis::OPT_SERIALIZER, (string) Redis::SERIALIZER_IGBINARY);
175
                    break;
176
            }
177
        }
178
179
        $this->client->setOption(Redis::OPT_SCAN, (string) Redis::SCAN_NORETRY);
180
181
        if (isset($config['ttl'])) {
182
            $this->ttl = $config['ttl'];
183
        }
184
185
        if (isset($config['cache_not_found_keys'])) {
186
            $this->cacheNotFoundKeys = (bool) $config['cache_not_found_keys'];
187
        }
188
189
        if (isset($params['read_timeout'])) {
190
            $this->client->setOption(Redis::OPT_READ_TIMEOUT, (string) $params['read_timeout']);
191
        }
192
    }
193
194
    protected function getConnectionParameters(array $config): array
195
    {
196
        $connectionParameters = [];
197
        $connectionParameters['host'] = $config['host'] ?? '127.0.0.1';
198
        $connectionParameters['port'] = $config['port'] ?? 6379;
199
        $connectionParameters['password'] = $config['password'] ?? null;
200
        $connectionParameters['database'] = $config['database'] ?? 0;
201
        $connectionParameters['timeout'] = $config['timeout'] ?? null;
202
        $connectionParameters['read_timeout'] = $config['read_timeout'] ?? null;
203
        $connectionParameters['retry_interval'] = $config['retry_interval'] ?? null;
204
        $connectionParameters['serializer'] = $config['serializer'] ?? null;
205
        $connectionParameters['connection_persistent'] = $config['connection_persistent'] ?? false;
206
        $connectionParameters['pool_size'] = $config['pool_size'] ?? 1;
207
208
        return $connectionParameters;
209
    }
210
211
    public function setNamespace(string $namespace)
212
    {
213
        $this->getClient()->setOption(Redis::OPT_PREFIX, $namespace . ':');
214
        parent::setNamespace($namespace);
215
    }
216
217
    public function setTtl(int $ttl)
218
    {
219
        $this->ttl = $ttl;
220
    }
221
}
222