GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( 7d455c...f6fa23 )
by Lonnie
07:56
created

CI_Session_redis_driver::write()   D

Complexity

Conditions 9
Paths 12

Size

Total Lines 39
Code Lines 19

Duplication

Lines 39
Ratio 100 %
Metric Value
dl 39
loc 39
rs 4.9091
cc 9
eloc 19
nc 12
nop 2
1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
9
 * Copyright (c) 2014 - 2015, British Columbia Institute of Technology
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/)
32
 * @copyright	Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	http://codeigniter.com
35
 * @since	Version 3.0.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
40
/**
41
 * CodeIgniter Session Redis Driver
42
 *
43
 * @package	CodeIgniter
44
 * @subpackage	Libraries
45
 * @category	Sessions
46
 * @author	Andrey Andreev
47
 * @link	http://codeigniter.com/user_guide/libraries/sessions.html
48
 */
49
class CI_Session_redis_driver extends CI_Session_driver implements SessionHandlerInterface {
50
51
	/**
52
	 * phpRedis instance
53
	 *
54
	 * @var	resource
55
	 */
56
	protected $_redis;
57
58
	/**
59
	 * Key prefix
60
	 *
61
	 * @var	string
62
	 */
63
	protected $_key_prefix = 'ci_session:';
64
65
	/**
66
	 * Lock key
67
	 *
68
	 * @var	string
69
	 */
70
	protected $_lock_key;
71
72
	// ------------------------------------------------------------------------
73
74
	/**
75
	 * Class constructor
76
	 *
77
	 * @param	array	$params	Configuration parameters
78
	 * @return	void
79
	 */
80
	public function __construct(&$params)
81
	{
82
		parent::__construct($params);
83
84
		if (empty($this->_config['save_path']))
85
		{
86
			log_message('error', 'Session: No Redis save path configured.');
87
		}
88
		elseif (preg_match('#(?:tcp://)?([^:?]+)(?:\:(\d+))?(\?.+)?#', $this->_config['save_path'], $matches))
89
		{
90
			isset($matches[3]) OR $matches[3] = ''; // Just to avoid undefined index notices below
91
			$this->_config['save_path'] = array(
92
				'host' => $matches[1],
93
				'port' => empty($matches[2]) ? NULL : $matches[2],
94
				'password' => preg_match('#auth=([^\s&]+)#', $matches[3], $match) ? $match[1] : NULL,
95
				'database' => preg_match('#database=(\d+)#', $matches[3], $match) ? (int) $match[1] : NULL,
96
				'timeout' => preg_match('#timeout=(\d+\.\d+)#', $matches[3], $match) ? (float) $match[1] : NULL
97
			);
98
99
			preg_match('#prefix=([^\s&]+)#', $matches[3], $match) && $this->_key_prefix = $match[1];
100
		}
101
		else
102
		{
103
			log_message('error', 'Session: Invalid Redis save path format: '.$this->_config['save_path']);
104
		}
105
106 View Code Duplication
		if ($this->_config['match_ip'] === TRUE)
107
		{
108
			$this->_key_prefix .= $_SERVER['REMOTE_ADDR'].':';
109
		}
110
	}
111
112
	// ------------------------------------------------------------------------
113
114
	/**
115
	 * Open
116
	 *
117
	 * Sanitizes save_path and initializes connection.
118
	 *
119
	 * @param	string	$save_path	Server path
120
	 * @param	string	$name		Session cookie name, unused
121
	 * @return	bool
122
	 */
123
	public function open($save_path, $name)
124
	{
125
		if (empty($this->_config['save_path']))
126
		{
127
			return $this->_failure;
128
		}
129
130
		$redis = new Redis();
131
		if ( ! $redis->connect($this->_config['save_path']['host'], $this->_config['save_path']['port'], $this->_config['save_path']['timeout']))
132
		{
133
			log_message('error', 'Session: Unable to connect to Redis with the configured settings.');
134
		}
135
		elseif (isset($this->_config['save_path']['password']) && ! $redis->auth($this->_config['save_path']['password']))
136
		{
137
			log_message('error', 'Session: Unable to authenticate to Redis instance.');
138
		}
139
		elseif (isset($this->_config['save_path']['database']) && ! $redis->select($this->_config['save_path']['database']))
140
		{
141
			log_message('error', 'Session: Unable to select Redis database with index '.$this->_config['save_path']['database']);
142
		}
143
		else
144
		{
145
			$this->_redis = $redis;
146
			return $this->_success;
147
		}
148
149
		return $this->_failure;
150
	}
151
152
	// ------------------------------------------------------------------------
153
154
	/**
155
	 * Read
156
	 *
157
	 * Reads session data and acquires a lock
158
	 *
159
	 * @param	string	$session_id	Session ID
160
	 * @return	string	Serialized session data
161
	 */
162 View Code Duplication
	public function read($session_id)
163
	{
164
		if (isset($this->_redis) && $this->_get_lock($session_id))
165
		{
166
			// Needed by write() to detect session_regenerate_id() calls
167
			$this->_session_id = $session_id;
168
169
			$session_data = (string) $this->_redis->get($this->_key_prefix.$session_id);
170
			$this->_fingerprint = md5($session_data);
171
			return $session_data;
172
		}
173
174
		return $this->_failure;
175
	}
176
177
	// ------------------------------------------------------------------------
178
179
	/**
180
	 * Write
181
	 *
182
	 * Writes (create / update) session data
183
	 *
184
	 * @param	string	$session_id	Session ID
185
	 * @param	string	$session_data	Serialized session data
186
	 * @return	bool
187
	 */
188 View Code Duplication
	public function write($session_id, $session_data)
189
	{
190
		if ( ! isset($this->_redis))
191
		{
192
			return $this->_failure;
193
		}
194
		// Was the ID regenerated?
195
		elseif ($session_id !== $this->_session_id)
196
		{
197
			if ( ! $this->_release_lock() OR ! $this->_get_lock($session_id))
198
			{
199
				return $this->_failure;
200
			}
201
202
			$this->_fingerprint = md5('');
203
			$this->_session_id = $session_id;
204
		}
205
206
		if (isset($this->_lock_key))
207
		{
208
			$this->_redis->setTimeout($this->_lock_key, 300);
209
			if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
210
			{
211
				if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
212
				{
213
					$this->_fingerprint = $fingerprint;
214
					return $this->_success;
215
				}
216
217
				return $this->_failure;
218
			}
219
220
			return ($this->_redis->setTimeout($this->_key_prefix.$session_id, $this->_config['expiration']))
221
				? $this->_success
222
				: $this->_failure;
223
		}
224
225
		return $this->_failure;
226
	}
227
228
	// ------------------------------------------------------------------------
229
230
	/**
231
	 * Close
232
	 *
233
	 * Releases locks and closes connection.
234
	 *
235
	 * @return	bool
236
	 */
237
	public function close()
238
	{
239
		if (isset($this->_redis))
240
		{
241
			try {
242
				if ($this->_redis->ping() === '+PONG')
243
				{
244
					isset($this->_lock_key) && $this->_redis->delete($this->_lock_key);
245
					if ($this->_redis->close() === $this->_failure)
0 ignored issues
show
Bug introduced by
The method close cannot be called on $this->_redis (of type resource).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
246
					{
247
						return $this->_failure;
248
					}
249
				}
250
			}
251
			catch (RedisException $e)
252
			{
253
				log_message('error', 'Session: Got RedisException on close(): '.$e->getMessage());
254
			}
255
256
			$this->_redis = NULL;
257
			return $this->_success;
258
		}
259
260
		return $this->_success;
261
	}
262
263
	// ------------------------------------------------------------------------
264
265
	/**
266
	 * Destroy
267
	 *
268
	 * Destroys the current session.
269
	 *
270
	 * @param	string	$session_id	Session ID
271
	 * @return	bool
272
	 */
273
	public function destroy($session_id)
274
	{
275
		if (isset($this->_redis, $this->_lock_key))
276
		{
277
			if (($result = $this->_redis->delete($this->_key_prefix.$session_id)) !== 1)
278
			{
279
				log_message('debug', 'Session: Redis::delete() expected to return 1, got '.var_export($result, TRUE).' instead.');
280
			}
281
282
			$this->_cookie_destroy();
283
			return $this->_success;
284
		}
285
286
		return $this->_failure;
287
	}
288
289
	// ------------------------------------------------------------------------
290
291
	/**
292
	 * Garbage Collector
293
	 *
294
	 * Deletes expired sessions
295
	 *
296
	 * @param	int 	$maxlifetime	Maximum lifetime of sessions
297
	 * @return	bool
298
	 */
299
	public function gc($maxlifetime)
300
	{
301
		// Not necessary, Redis takes care of that.
302
		return $this->_success;
303
	}
304
305
	// ------------------------------------------------------------------------
306
307
	/**
308
	 * Get lock
309
	 *
310
	 * Acquires an (emulated) lock.
311
	 *
312
	 * @param	string	$session_id	Session ID
313
	 * @return	bool
314
	 */
315
	protected function _get_lock($session_id)
316
	{
317
		if (isset($this->_lock_key))
318
		{
319
			return $this->_redis->setTimeout($this->_lock_key, 300);
320
		}
321
322
		// 30 attempts to obtain a lock, in case another request already has it
323
		$lock_key = $this->_key_prefix.$session_id.':lock';
324
		$attempt = 0;
325 View Code Duplication
		do
326
		{
327
			if (($ttl = $this->_redis->ttl($lock_key)) > 0)
328
			{
329
				sleep(1);
330
				continue;
331
			}
332
333
			if ( ! $this->_redis->setex($lock_key, 300, time()))
334
			{
335
				log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
336
				return FALSE;
337
			}
338
339
			$this->_lock_key = $lock_key;
340
			break;
341
		}
342
		while (++$attempt < 30);
343
344
		if ($attempt === 30)
345
		{
346
			log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.');
347
			return FALSE;
348
		}
349
		elseif ($ttl === -1)
350
		{
351
			log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.');
352
		}
353
354
		$this->_lock = TRUE;
355
		return TRUE;
356
	}
357
358
	// ------------------------------------------------------------------------
359
360
	/**
361
	 * Release lock
362
	 *
363
	 * Releases a previously acquired lock
364
	 *
365
	 * @return	bool
366
	 */
367 View Code Duplication
	protected function _release_lock()
368
	{
369
		if (isset($this->_redis, $this->_lock_key) && $this->_lock)
370
		{
371
			if ( ! $this->_redis->delete($this->_lock_key))
372
			{
373
				log_message('error', 'Session: Error while trying to free lock for '.$this->_lock_key);
374
				return FALSE;
375
			}
376
377
			$this->_lock_key = NULL;
378
			$this->_lock = FALSE;
379
		}
380
381
		return TRUE;
382
	}
383
384
}
385