Passed
Push — master ( dd4668...f14b8a )
by John
23:32 queued 04:11
created

RedisFactory::create()   F

Complexity

Conditions 20
Paths 1252

Size

Total Lines 87
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 20
eloc 52
c 2
b 0
f 0
nc 1252
nop 0
dl 0
loc 87
rs 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Alejandro Varela <[email protected]>
6
 * @author Christoph Wurst <[email protected]>
7
 * @author Jörn Friedrich Dreyer <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program. If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
namespace OC;
28
29
class RedisFactory {
30
	public const REDIS_MINIMAL_VERSION = '2.2.5';
31
	public const REDIS_EXTRA_PARAMETERS_MINIMAL_VERSION = '5.3.0';
32
33
	/** @var  \Redis|\RedisCluster */
34
	private $instance;
35
36
	/** @var  SystemConfig */
37
	private $config;
38
39
	/**
40
	 * RedisFactory constructor.
41
	 *
42
	 * @param SystemConfig $config
43
	 */
44
	public function __construct(SystemConfig $config) {
45
		$this->config = $config;
46
	}
47
48
	private function create() {
49
		$isCluster = in_array('redis.cluster', $this->config->getKeys());
50
		$config = $isCluster
51
			? $this->config->getValue('redis.cluster', [])
52
			: $this->config->getValue('redis', []);
53
54
		if (empty($config)) {
55
			throw new \Exception('Redis config is empty');
56
		}
57
58
		if ($isCluster && !class_exists('RedisCluster')) {
59
			throw new \Exception('Redis Cluster support is not available');
60
		}
61
62
		if (isset($config['timeout'])) {
63
			$timeout = $config['timeout'];
64
		} else {
65
			$timeout = 0.0;
66
		}
67
68
		if (isset($config['read_timeout'])) {
69
			$readTimeout = $config['read_timeout'];
70
		} else {
71
			$readTimeout = 0.0;
72
		}
73
74
		$auth = null;
75
		if (isset($config['password']) && $config['password'] !== '') {
76
			if (isset($config['user']) && $config['user'] !== '') {
77
				$auth = [$config['user'], $config['password']];
78
			} else {
79
				$auth = $config['password'];
80
			}
81
		}
82
83
		// # TLS support
84
		// # https://github.com/phpredis/phpredis/issues/1600
85
		$connectionParameters = $this->getSslContext($config);
86
87
		// cluster config
88
		if ($isCluster) {
89
			// Support for older phpredis versions not supporting connectionParameters
90
			if ($connectionParameters !== null) {
91
				$this->instance = new \RedisCluster(null, $config['seeds'], $timeout, $readTimeout, false, $auth, $connectionParameters);
0 ignored issues
show
Unused Code introduced by
The call to RedisCluster::__construct() has too many arguments starting with $connectionParameters. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

91
				$this->instance = /** @scrutinizer ignore-call */ new \RedisCluster(null, $config['seeds'], $timeout, $readTimeout, false, $auth, $connectionParameters);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
92
			} else {
93
				$this->instance = new \RedisCluster(null, $config['seeds'], $timeout, $readTimeout, false, $auth);
94
			}
95
96
			if (isset($config['failover_mode'])) {
97
				$this->instance->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, $config['failover_mode']);
98
			}
99
		} else {
100
			$this->instance = new \Redis();
101
102
			if (isset($config['host'])) {
103
				$host = $config['host'];
104
			} else {
105
				$host = '127.0.0.1';
106
			}
107
108
			if (isset($config['port'])) {
109
				$port = $config['port'];
110
			} elseif ($host[0] !== '/') {
111
				$port = 6379;
112
			} else {
113
				$port = null;
114
			}
115
116
			// Support for older phpredis versions not supporting connectionParameters
117
			if ($connectionParameters !== null) {
118
				// Non-clustered redis requires connection parameters to be wrapped inside `stream`
119
				$connectionParameters = [
120
					'stream' => $this->getSslContext($config)
121
				];
122
				$this->instance->connect($host, $port, $timeout, null, 0, $readTimeout, $connectionParameters);
0 ignored issues
show
Unused Code introduced by
The call to Redis::connect() has too many arguments starting with $connectionParameters. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

122
				$this->instance->/** @scrutinizer ignore-call */ 
123
                     connect($host, $port, $timeout, null, 0, $readTimeout, $connectionParameters);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
123
			} else {
124
				$this->instance->connect($host, $port, $timeout, null, 0, $readTimeout);
125
			}
126
127
128
			// Auth if configured
129
			if ($auth !== null) {
130
				$this->instance->auth($auth);
131
			}
132
133
			if (isset($config['dbindex'])) {
134
				$this->instance->select($config['dbindex']);
135
			}
136
		}
137
	}
138
139
	/**
140
	 * Get the ssl context config
141
	 *
142
	 * @param Array $config the current config
143
	 * @return Array|null
144
	 * @throws \UnexpectedValueException
145
	 */
146
	private function getSslContext($config) {
147
		if (isset($config['ssl_context'])) {
148
			if (!$this->isConnectionParametersSupported()) {
149
				throw new \UnexpectedValueException(\sprintf(
150
					'php-redis extension must be version %s or higher to support ssl context',
151
					self::REDIS_EXTRA_PARAMETERS_MINIMAL_VERSION
152
				));
153
			}
154
			return $config['ssl_context'];
155
		}
156
		return null;
157
	}
158
159
	public function getInstance() {
160
		if (!$this->isAvailable()) {
161
			throw new \Exception('Redis support is not available');
162
		}
163
		if (!$this->instance instanceof \Redis) {
164
			$this->create();
165
		}
166
167
		return $this->instance;
168
	}
169
170
	public function isAvailable() {
171
		return extension_loaded('redis')
172
		&& version_compare(phpversion('redis'), '2.2.5', '>=');
173
	}
174
175
	/**
176
	 * Php redis does support configurable extra parameters since version 5.3.0, see: https://github.com/phpredis/phpredis#connect-open.
177
	 * We need to check if the current version supports extra connection parameters, otherwise the connect method will throw an exception
178
	 *
179
	 * @return boolean
180
	 */
181
	private function isConnectionParametersSupported(): bool {
182
		return \extension_loaded('redis') &&
183
			\version_compare(\phpversion('redis'), self::REDIS_EXTRA_PARAMETERS_MINIMAL_VERSION, '>=');
184
	}
185
}
186