Completed
Pull Request — master (#20)
by
unknown
03:15
created

Resque_Redis::__construct()   B

Complexity

Conditions 10
Paths 144

Size

Total Lines 37
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 10.3936

Importance

Changes 5
Bugs 0 Features 1
Metric Value
cc 10
eloc 20
c 5
b 0
f 1
nc 144
nop 3
dl 0
loc 37
ccs 16
cts 19
cp 0.8421
crap 10.3936
rs 7.2999

How to fix   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
 * Wrap Credis to add namespace support and various helper methods.
4
 *
5
 * @package		Resque/Redis
6
 * @author		Chris Boulton <[email protected]>
7
 * @license		http://www.opensource.org/licenses/mit-license.php
8
 */
9
class Resque_Redis
10
{
11
	/**
12
	 * Redis namespace
13
	 * @var string
14
	 */
15
	private static $defaultNamespace = 'resque:';
16
17
	/**
18
	 * A default host to connect to
19
	 */
20
	const DEFAULT_HOST = 'localhost';
21
22
	/**
23
	 * The default Redis port
24
	 */
25
	const DEFAULT_PORT = 6379;
26
27
	/**
28
	 * The default Redis Database number
29
	 */
30
	const DEFAULT_DATABASE = 0;
31
32
	/**
33
	 * @var array List of all commands in Redis that supply a key as their
34
	 *	first argument. Used to prefix keys with the Resque namespace.
35
	 */
36
	private $keyCommands = array(
37
		'exists',
38
		'del',
39
		'type',
40
		'keys',
41
		'expire',
42
		'ttl',
43
		'move',
44
		'set',
45
		'setex',
46
		'get',
47
		'getset',
48
		'setnx',
49
		'incr',
50
		'incrby',
51
		'decr',
52
		'decrby',
53
		'rpush',
54
		'lpush',
55
		'llen',
56
		'lrange',
57
		'ltrim',
58
		'lindex',
59
		'lset',
60
		'lrem',
61
		'lpop',
62
		'blpop',
63
		'rpop',
64
		'sadd',
65
		'srem',
66
		'spop',
67
		'scard',
68
		'sismember',
69
		'smembers',
70
		'srandmember',
71
		'zadd',
72
		'zrem',
73
		'zrange',
74
		'zrevrange',
75
		'zrangebyscore',
76
		'zcard',
77
		'zscore',
78
		'zremrangebyscore',
79
		'sort',
80
		'rename',
81
		'rpoplpush'
82
	);
83
	// sinterstore
84
	// sunion
85
	// sunionstore
86
	// sdiff
87
	// sdiffstore
88
	// sinter
89
	// smove
90
	// mget
91
	// msetnx
92
	// mset
93
	// renamenx
94
95
	private $scanCommands = array(
96
		'scan',
97
		'hscan',
98
		'sscan',
99
		'zscan',
100
	);
101
102
	/**
103
	 * Set Redis namespace (prefix) default: resque
104
	 * @param string $namespace
105
	 */
106 2
	public static function prefix($namespace)
107
	{
108 2
	    if (substr($namespace, -1) !== ':' && $namespace != '') {
109 2
	        $namespace .= ':';
110
	    }
111 2
	    self::$defaultNamespace = $namespace;
112 2
	}
113
114
	/**
115
	 * @param string|array $server A DSN or array
116
	 * @param int $database A database number to select. However, if we find a valid database number in the DSN the
117
	 *                      DSN-supplied value will be used instead and this parameter is ignored.
118
	 * @param object $client Optional Credis_Cluster or Credis_Client instance instantiated by you
119
	 */
120 68
    public function __construct($server, $database = null, $client = null)
121
	{
122
		try {
123 68
			if (is_array($server)) {
124
				$this->driver = new Credis_Cluster($server);
0 ignored issues
show
Bug Best Practice introduced by
The property driver does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Deprecated Code introduced by
The class Credis_Cluster has been deprecated. ( Ignorable by Annotation )

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

124
				$this->driver = /** @scrutinizer ignore-deprecated */ new Credis_Cluster($server);
Loading history...
125
			}
126 68
			else if (is_object($client)) {
127 2
				$this->driver = $client;
128
			}
129
			else {
130 67
				list($host, $port, $dsnDatabase, $user, $password, $options) = self::parseDsn($server);
131
				// $user is not used, only $password
132
133
				// Look for known Credis_Client options
134 67
				$timeout = isset($options['timeout']) ? intval($options['timeout']) : null;
135 67
				$persistent = isset($options['persistent']) ? $options['persistent'] : '';
136 67
				$maxRetries = isset($options['max_connect_retries']) ? $options['max_connect_retries'] : 0;
137
138 67
				$this->driver = new Credis_Client($host, $port, $timeout, $persistent);
139 67
				$this->driver->setMaxConnectRetries($maxRetries);
140 67
				if ($password){
141
					$this->driver->auth($password);
142
				}
143
144
				// If we have found a database in our DSN, use it instead of the `$database`
145
				// value passed into the constructor.
146 67
				if ($dsnDatabase !== false) {
147
					$database = $dsnDatabase;
148
				}
149
			}
150
151 68
			if ($database !== null) {
152 68
				$this->driver->select($database);
153
			}
154
		}
155 2
		catch(CredisException $e) {
156 2
			throw new Resque_RedisException('Error communicating with Redis: ' . $e->getMessage(), 0, $e);
157
		}
158 67
	}
159
160
	/**
161
	 * Parse a DSN string, which can have one of the following formats:
162
	 *
163
	 * - host:port
164
	 * - redis://user:pass@host:port/db?option1=val1&option2=val2
165
	 * - tcp://user:pass@host:port/db?option1=val1&option2=val2
166
	 * - unix:///path/to/redis.sock
167
	 *
168
	 * Note: the 'user' part of the DSN is not used.
169
	 *
170
	 * @param string $dsn A DSN string
171
	 * @return array An array of DSN compotnents, with 'false' values for any unknown components. e.g.
172
	 *               [host, port, db, user, pass, options]
173
	 */
174 88
	public static function parseDsn($dsn)
175
	{
176 88
		if ($dsn == '') {
177
			// Use a sensible default for an empty DNS string
178 1
			$dsn = 'redis://' . self::DEFAULT_HOST;
179
		}
180 88
		if(substr($dsn, 0, 7) === 'unix://') {
181
			return array(
182
				$dsn,
183
				null,
184
				false,
185
				null,
186
				null,
187
				null,
188
			);
189
		}
190 88
		$parts = parse_url($dsn);
191
192
		// Check the URI scheme
193 88
		$validSchemes = array('redis', 'tcp');
194 88
		if (isset($parts['scheme']) && ! in_array($parts['scheme'], $validSchemes)) {
195 3
			throw new \InvalidArgumentException("Invalid DSN. Supported schemes are " . implode(', ', $validSchemes));
196
		}
197
198
		// Allow simple 'hostname' format, which `parse_url` treats as a path, not host.
199 85
		if ( ! isset($parts['host']) && isset($parts['path'])) {
200 1
			$parts['host'] = $parts['path'];
201 1
			unset($parts['path']);
202
		}
203
204
		// Extract the port number as an integer
205 85
		$port = isset($parts['port']) ? intval($parts['port']) : self::DEFAULT_PORT;
206
207
		// Get the database from the 'path' part of the URI
208 85
		$database = false;
209 85
		if (isset($parts['path'])) {
210
			// Strip non-digit chars from path
211 7
			$database = intval(preg_replace('/[^0-9]/', '', $parts['path']));
212
		}
213
214
		// Extract any 'user' and 'pass' values
215 85
		$user = isset($parts['user']) ? $parts['user'] : false;
216 85
		$pass = isset($parts['pass']) ? $parts['pass'] : false;
217
218
		// Convert the query string into an associative array
219 85
		$options = array();
220 85
		if (isset($parts['query'])) {
221
			// Parse the query string into an array
222 6
			parse_str($parts['query'], $options);
223
		}
224
225
		return array(
226 85
			$parts['host'],
227 85
			$port,
228 85
			$database,
229 85
			$user,
230 85
			$pass,
231 85
			$options,
232
		);
233
	}
234
235
	/**
236
	 * Magic method to handle all function requests and prefix key based
237
	 * operations with the {self::$defaultNamespace} key prefix.
238
	 *
239
	 * @param string $name The name of the method called.
240
	 * @param array $args Array of supplied arguments to the method.
241
	 * @return mixed Return value from Resident::call() based on the command.
242
	 */
243 73
	public function __call($name, $args)
244
	{
245 73
		if (in_array($name, $this->keyCommands)) {
246 73
			if (is_array($args[0])) {
247 1
				foreach ($args[0] AS $i => $v) {
248 1
					$args[0][$i] = self::$defaultNamespace . $v;
249
				}
250
			}
251
			else {
252 73
				$args[0] = self::$defaultNamespace . $args[0];
253
			}
254
		} elseif (in_array($name, $this->scanCommands)) {
255
			// fix scan cursor to be a reference.
256
			$args[0] = &$args[0];
257
		}
258
		try {
259 73
			return $this->driver->__call($name, $args);
260
		}
261
		catch (CredisException $e) {
262
			throw new Resque_RedisException('Error communicating with Redis: ' . $e->getMessage(), 0, $e);
263
		}
264
	}
265
266 13
	public static function getPrefix()
267
	{
268 13
	    return self::$defaultNamespace;
269
	}
270
271
	public static function removePrefix($string)
272
	{
273
	    $prefix=self::getPrefix();
274
275
	    if (substr($string, 0, strlen($prefix)) == $prefix) {
276
	        $string = substr($string, strlen($prefix), strlen($string) );
277
	    }
278
	    return $string;
279
	}
280
}
281