Completed
Pull Request — master (#15)
by Helpful
02:08
created
code/HybridSessionStore.php 3 patches
Doc Comments   +8 added lines, -5 removed lines patch added patch discarded remove patch
@@ -56,8 +56,8 @@  discard block
 block discarded – undo
56 56
     private $saltedKey;
57 57
 
58 58
     /**
59
-     * @param $key a per-site secret string which is used as the base encryption key.
60
-     * @param $salt a per-session random string which is used as a salt to generate a per-session key
59
+     * @param string $key a per-site secret string which is used as the base encryption key.
60
+     * @param string $salt a per-session random string which is used as a salt to generate a per-session key
61 61
      *
62 62
      * The base encryption key needs to stay secret. If an attacker ever gets it, they can read their session,
63 63
      * and even modify & re-sign it.
@@ -82,7 +82,7 @@  discard block
 block discarded – undo
82 82
     /**
83 83
      * Encrypt and then sign some cleartext
84 84
      *
85
-     * @param $cleartext - The cleartext to encrypt and sign
85
+     * @param string $cleartext - The cleartext to encrypt and sign
86 86
      * @return string - The encrypted-and-signed message as base64 ASCII.
87 87
      */
88 88
     public function encrypt($cleartext)
@@ -105,8 +105,8 @@  discard block
 block discarded – undo
105 105
     /**
106 106
      * Check the signature on an encrypted-and-signed message, and if valid decrypt the content
107 107
      *
108
-     * @param $data - The encrypted-and-signed message as base64 ASCII
109
-     * @return bool|string - The decrypted cleartext or false if signature failed
108
+     * @param string $data - The encrypted-and-signed message as base64 ASCII
109
+     * @return string|false - The decrypted cleartext or false if signature failed
110 110
      */
111 111
     public function decrypt($data)
112 112
     {
@@ -467,6 +467,9 @@  discard block
 block discarded – undo
467 467
         $this->setKey($this->getKey());
468 468
     }
469 469
 
470
+    /**
471
+     * @param string $key
472
+     */
470 473
     public function setKey($key)
471 474
     {
472 475
         parent::setKey($key);
Please login to merge, or discard this patch.
Indentation   +515 added lines, -515 removed lines patch added patch discarded remove patch
@@ -8,37 +8,37 @@  discard block
 block discarded – undo
8 8
  * registers it (including registering session_write_close as a shutdown function)
9 9
  */
10 10
 if (!interface_exists('SessionHandlerInterface')) {
11
-    interface SessionHandlerInterface
12
-    {
13
-        /* Methods */
14
-        public function close();
15
-        public function destroy($session_id);
16
-        public function gc($maxlifetime);
17
-        public function open($save_path, $name);
18
-        public function read($session_id);
19
-        public function write($session_id, $session_data);
20
-    }
11
+	interface SessionHandlerInterface
12
+	{
13
+		/* Methods */
14
+		public function close();
15
+		public function destroy($session_id);
16
+		public function gc($maxlifetime);
17
+		public function open($save_path, $name);
18
+		public function read($session_id);
19
+		public function write($session_id, $session_data);
20
+	}
21 21
 }
22 22
 
23 23
 if (version_compare(PHP_VERSION, '5.4.0', '<')) {
24
-    function register_sessionhandler($handler)
25
-    {
26
-        session_set_save_handler(
27
-            array($handler, 'open'),
28
-            array($handler, 'close'),
29
-            array($handler, 'read'),
30
-            array($handler, 'write'),
31
-            array($handler, 'destroy'),
32
-            array($handler, 'gc')
33
-        );
34
-
35
-        register_shutdown_function('session_write_close');
36
-    }
24
+	function register_sessionhandler($handler)
25
+	{
26
+		session_set_save_handler(
27
+			array($handler, 'open'),
28
+			array($handler, 'close'),
29
+			array($handler, 'read'),
30
+			array($handler, 'write'),
31
+			array($handler, 'destroy'),
32
+			array($handler, 'gc')
33
+		);
34
+
35
+		register_shutdown_function('session_write_close');
36
+	}
37 37
 } else {
38
-    function register_sessionhandler($handler)
39
-    {
40
-        session_set_save_handler($handler, true);
41
-    }
38
+	function register_sessionhandler($handler)
39
+	{
40
+		session_set_save_handler($handler, true);
41
+	}
42 42
 }
43 43
 
44 44
 /**
@@ -48,143 +48,143 @@  discard block
 block discarded – undo
48 48
  */
49 49
 class HybridSessionStore_Crypto
50 50
 {
51
-    private $key;
52
-    private $ivSize;
53
-    private $keySize;
54
-
55
-    public $salt;
56
-    private $saltedKey;
57
-
58
-    /**
59
-     * @param $key a per-site secret string which is used as the base encryption key.
60
-     * @param $salt a per-session random string which is used as a salt to generate a per-session key
61
-     *
62
-     * The base encryption key needs to stay secret. If an attacker ever gets it, they can read their session,
63
-     * and even modify & re-sign it.
64
-     *
65
-     * The salt is a random per-session string that is used with the base encryption key to create a per-session key.
66
-     * This (amongst other things) makes sure an attacker can't use a known-plaintext attack to guess the key.
67
-     *
68
-     * Normally we could create a salt on encryption, send it to the client as part of the session (it doesn't
69
-     * need to remain secret), then use the returned salt to decrypt. But we already have the Session ID which makes
70
-     * a great salt, so no need to generate & handle another one.
71
-     */
72
-    public function __construct($key, $salt)
73
-    {
74
-        $this->ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
75
-        $this->keySize = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
76
-
77
-        $this->key = $key;
78
-        $this->salt = $salt;
79
-        $this->saltedKey = hash_pbkdf2('sha256', $this->key, $this->salt, 1000, $this->keySize, true);
80
-    }
81
-
82
-    /**
83
-     * Encrypt and then sign some cleartext
84
-     *
85
-     * @param $cleartext - The cleartext to encrypt and sign
86
-     * @return string - The encrypted-and-signed message as base64 ASCII.
87
-     */
88
-    public function encrypt($cleartext)
89
-    {
90
-        $iv = mcrypt_create_iv($this->ivSize, MCRYPT_DEV_URANDOM);
91
-
92
-        $enc = mcrypt_encrypt(
93
-            MCRYPT_RIJNDAEL_256,
94
-            $this->saltedKey,
95
-            $cleartext,
96
-            MCRYPT_MODE_CBC,
97
-            $iv
98
-        );
99
-
100
-        $hash = hash_hmac('sha256', $enc, $this->saltedKey);
101
-
102
-        return base64_encode($iv.$hash.$enc);
103
-    }
104
-
105
-    /**
106
-     * Check the signature on an encrypted-and-signed message, and if valid decrypt the content
107
-     *
108
-     * @param $data - The encrypted-and-signed message as base64 ASCII
109
-     * @return bool|string - The decrypted cleartext or false if signature failed
110
-     */
111
-    public function decrypt($data)
112
-    {
113
-        $data = base64_decode($data);
114
-
115
-        $iv   = substr($data, 0, $this->ivSize);
116
-        $hash = substr($data, $this->ivSize, 64);
117
-        $enc  = substr($data, $this->ivSize + 64);
118
-
119
-        $cleartext = rtrim(mcrypt_decrypt(
120
-            MCRYPT_RIJNDAEL_256,
121
-            $this->saltedKey,
122
-            $enc,
123
-            MCRYPT_MODE_CBC,
124
-            $iv
125
-        ), "\x00");
126
-
127
-        // Needs to be after decrypt so it always runs, to avoid timing attack
128
-        $gen_hash = hash_hmac('sha256', $enc, $this->saltedKey);
129
-
130
-        if ($gen_hash == $hash) {
131
-            return $cleartext;
132
-        }
133
-        return false;
134
-    }
51
+	private $key;
52
+	private $ivSize;
53
+	private $keySize;
54
+
55
+	public $salt;
56
+	private $saltedKey;
57
+
58
+	/**
59
+	 * @param $key a per-site secret string which is used as the base encryption key.
60
+	 * @param $salt a per-session random string which is used as a salt to generate a per-session key
61
+	 *
62
+	 * The base encryption key needs to stay secret. If an attacker ever gets it, they can read their session,
63
+	 * and even modify & re-sign it.
64
+	 *
65
+	 * The salt is a random per-session string that is used with the base encryption key to create a per-session key.
66
+	 * This (amongst other things) makes sure an attacker can't use a known-plaintext attack to guess the key.
67
+	 *
68
+	 * Normally we could create a salt on encryption, send it to the client as part of the session (it doesn't
69
+	 * need to remain secret), then use the returned salt to decrypt. But we already have the Session ID which makes
70
+	 * a great salt, so no need to generate & handle another one.
71
+	 */
72
+	public function __construct($key, $salt)
73
+	{
74
+		$this->ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
75
+		$this->keySize = mcrypt_get_key_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
76
+
77
+		$this->key = $key;
78
+		$this->salt = $salt;
79
+		$this->saltedKey = hash_pbkdf2('sha256', $this->key, $this->salt, 1000, $this->keySize, true);
80
+	}
81
+
82
+	/**
83
+	 * Encrypt and then sign some cleartext
84
+	 *
85
+	 * @param $cleartext - The cleartext to encrypt and sign
86
+	 * @return string - The encrypted-and-signed message as base64 ASCII.
87
+	 */
88
+	public function encrypt($cleartext)
89
+	{
90
+		$iv = mcrypt_create_iv($this->ivSize, MCRYPT_DEV_URANDOM);
91
+
92
+		$enc = mcrypt_encrypt(
93
+			MCRYPT_RIJNDAEL_256,
94
+			$this->saltedKey,
95
+			$cleartext,
96
+			MCRYPT_MODE_CBC,
97
+			$iv
98
+		);
99
+
100
+		$hash = hash_hmac('sha256', $enc, $this->saltedKey);
101
+
102
+		return base64_encode($iv.$hash.$enc);
103
+	}
104
+
105
+	/**
106
+	 * Check the signature on an encrypted-and-signed message, and if valid decrypt the content
107
+	 *
108
+	 * @param $data - The encrypted-and-signed message as base64 ASCII
109
+	 * @return bool|string - The decrypted cleartext or false if signature failed
110
+	 */
111
+	public function decrypt($data)
112
+	{
113
+		$data = base64_decode($data);
114
+
115
+		$iv   = substr($data, 0, $this->ivSize);
116
+		$hash = substr($data, $this->ivSize, 64);
117
+		$enc  = substr($data, $this->ivSize + 64);
118
+
119
+		$cleartext = rtrim(mcrypt_decrypt(
120
+			MCRYPT_RIJNDAEL_256,
121
+			$this->saltedKey,
122
+			$enc,
123
+			MCRYPT_MODE_CBC,
124
+			$iv
125
+		), "\x00");
126
+
127
+		// Needs to be after decrypt so it always runs, to avoid timing attack
128
+		$gen_hash = hash_hmac('sha256', $enc, $this->saltedKey);
129
+
130
+		if ($gen_hash == $hash) {
131
+			return $cleartext;
132
+		}
133
+		return false;
134
+	}
135 135
 }
136 136
 
137 137
 abstract class HybridSessionStore_Base implements SessionHandlerInterface
138 138
 {
139
-    /**
140
-     * Session secret key
141
-     *
142
-     * @var string
143
-     */
144
-    protected $key = null;
145
-
146
-    /**
147
-     * Assign a new session secret key
148
-     *
149
-     * @param string $key
150
-     */
151
-    public function setKey($key)
152
-    {
153
-        $this->key = $key;
154
-    }
155
-
156
-    /**
157
-     * Get the session secret key
158
-     *
159
-     * @return string
160
-     */
161
-    protected function getKey()
162
-    {
163
-        return $this->key;
164
-    }
165
-
166
-    /**
167
-     * Get lifetime in number of seconds
168
-     *
169
-     * @return int
170
-     */
171
-    protected function getLifetime()
172
-    {
173
-        $params = session_get_cookie_params();
174
-        $cookieLifetime = (int)$params['lifetime'];
175
-        $gcLifetime = (int)ini_get('session.gc_maxlifetime');
176
-        return $cookieLifetime ? min($cookieLifetime, $gcLifetime) : $gcLifetime;
177
-    }
178
-
179
-    /**
180
-     * Gets the current unix timestamp
181
-     *
182
-     * @return int
183
-     */
184
-    protected function getNow()
185
-    {
186
-        return (int)SS_Datetime::now()->Format('U');
187
-    }
139
+	/**
140
+	 * Session secret key
141
+	 *
142
+	 * @var string
143
+	 */
144
+	protected $key = null;
145
+
146
+	/**
147
+	 * Assign a new session secret key
148
+	 *
149
+	 * @param string $key
150
+	 */
151
+	public function setKey($key)
152
+	{
153
+		$this->key = $key;
154
+	}
155
+
156
+	/**
157
+	 * Get the session secret key
158
+	 *
159
+	 * @return string
160
+	 */
161
+	protected function getKey()
162
+	{
163
+		return $this->key;
164
+	}
165
+
166
+	/**
167
+	 * Get lifetime in number of seconds
168
+	 *
169
+	 * @return int
170
+	 */
171
+	protected function getLifetime()
172
+	{
173
+		$params = session_get_cookie_params();
174
+		$cookieLifetime = (int)$params['lifetime'];
175
+		$gcLifetime = (int)ini_get('session.gc_maxlifetime');
176
+		return $cookieLifetime ? min($cookieLifetime, $gcLifetime) : $gcLifetime;
177
+	}
178
+
179
+	/**
180
+	 * Gets the current unix timestamp
181
+	 *
182
+	 * @return int
183
+	 */
184
+	protected function getNow()
185
+	{
186
+		return (int)SS_Datetime::now()->Format('U');
187
+	}
188 188
 }
189 189
 
190 190
 /**
@@ -203,375 +203,375 @@  discard block
 block discarded – undo
203 203
  */
204 204
 class HybridSessionStore_Cookie extends HybridSessionStore_Base
205 205
 {
206
-    /**
207
-     * Maximum length of a cookie value in characters
208
-     *
209
-     * @var int
210
-     * @config
211
-     */
212
-    private static $max_length = 1024;
213
-
214
-    /**
215
-     * Encryption service
216
-     *
217
-     * @var HybridSessionStore_Crypto
218
-     */
219
-    protected $crypto;
220
-
221
-    /**
222
-     * Name of cookie
223
-     *
224
-     * @var string
225
-     */
226
-    protected $cookie;
227
-
228
-    /**
229
-     * Known unmodified value of this cookie. If the cookie backend has been read into the application,
230
-     * then the backend is unable to verify the modification state of this value internally within the
231
-     * system, so this will be left null unless written back.
232
-     *
233
-     * If the content exceeds max_length then the backend can also not maintain this cookie, also
234
-     * setting this variable to null.
235
-     *
236
-     * @var string
237
-     */
238
-    protected $currentCookieData;
239
-
240
-    public function open($save_path, $name)
241
-    {
242
-        $this->cookie = $name.'_2';
243
-        // Read the incoming value, then clear the cookie - we might not be able
244
-        // to do so later if write() is called after headers are sent
245
-        // This is intended to force a failover to the database store if the
246
-        // modified session cannot be emitted.
247
-        $this->currentCookieData = Cookie::get($this->cookie);
248
-        if ($this->currentCookieData) {
249
-            Cookie::set($this->cookie, '');
250
-        }
251
-    }
252
-
253
-    public function close()
254
-    {
255
-    }
256
-
257
-    /**
258
-     * Get the cryptography store for the specified session
259
-     *
260
-     * @param string $session_id
261
-     * @return HybridSessionStore_Crypto
262
-     */
263
-    protected function getCrypto($session_id)
264
-    {
265
-        $key = $this->getKey();
266
-        if (!$key) {
267
-            return null;
268
-        }
269
-        if (!$this->crypto || $this->crypto->salt != $session_id) {
270
-            $this->crypto = new HybridSessionStore_Crypto($key, $session_id);
271
-        }
272
-        return $this->crypto;
273
-    }
274
-
275
-    public function read($session_id)
276
-    {
277
-        // Check ability to safely decrypt content
278
-        if (!$this->currentCookieData
279
-            || !($crypto = $this->getCrypto($session_id))
280
-        ) {
281
-            return;
282
-        }
283
-
284
-        // Decrypt and invalidate old data
285
-        $cookieData = $crypto->decrypt($this->currentCookieData);
286
-        $this->currentCookieData = null;
287
-
288
-        // Verify expiration
289
-        if ($cookieData) {
290
-            $expiry = (int)substr($cookieData, 0, 10);
291
-            $data = substr($cookieData, 10);
292
-
293
-            if ($expiry > $this->getNow()) {
294
-                return $data;
295
-            }
296
-        }
297
-    }
298
-
299
-    /**
300
-     * Determine if the session could be verifably written to cookie storage
301
-     *
302
-     * @return bool
303
-     */
304
-    protected function canWrite()
305
-    {
306
-        return !headers_sent();
307
-    }
308
-
309
-    public function write($session_id, $session_data)
310
-    {
311
-        // Check ability to safely encrypt and write content
312
-        if (!$this->canWrite()
313
-            || (strlen($session_data) > Config::inst()->get(__CLASS__, 'max_length'))
314
-            || !($crypto = $this->getCrypto($session_id))
315
-        ) {
316
-            return false;
317
-        }
318
-
319
-        // Prepare content for write
320
-        $params = session_get_cookie_params();
321
-        // Total max lifetime, stored internally
322
-        $lifetime = $this->getLifetime();
323
-        $expiry = $this->getNow() + $lifetime;
324
-
325
-        // Restore the known good cookie value
326
-        $this->currentCookieData = $this->crypto->encrypt(
327
-            sprintf('%010u', $expiry) . $session_data
328
-        );
329
-
330
-        // Respect auto-expire on browser close for the session cookie (in case the cookie lifetime is zero)
331
-        $cookieLifetime = min((int)$params['lifetime'], $lifetime);
332
-        Cookie::set(
333
-            $this->cookie,
334
-            $this->currentCookieData,
335
-            $cookieLifetime / 86400,
336
-            $params['path'],
337
-            $params['domain'],
338
-            $params['secure'],
339
-            $params['httponly']
340
-        );
341
-
342
-        return true;
343
-    }
344
-
345
-    public function destroy($session_id)
346
-    {
347
-        $this->currentCookieData = null;
348
-        Cookie::force_expiry($this->cookie);
349
-    }
350
-
351
-    public function gc($maxlifetime)
352
-    {
353
-        // NOP
354
-    }
206
+	/**
207
+	 * Maximum length of a cookie value in characters
208
+	 *
209
+	 * @var int
210
+	 * @config
211
+	 */
212
+	private static $max_length = 1024;
213
+
214
+	/**
215
+	 * Encryption service
216
+	 *
217
+	 * @var HybridSessionStore_Crypto
218
+	 */
219
+	protected $crypto;
220
+
221
+	/**
222
+	 * Name of cookie
223
+	 *
224
+	 * @var string
225
+	 */
226
+	protected $cookie;
227
+
228
+	/**
229
+	 * Known unmodified value of this cookie. If the cookie backend has been read into the application,
230
+	 * then the backend is unable to verify the modification state of this value internally within the
231
+	 * system, so this will be left null unless written back.
232
+	 *
233
+	 * If the content exceeds max_length then the backend can also not maintain this cookie, also
234
+	 * setting this variable to null.
235
+	 *
236
+	 * @var string
237
+	 */
238
+	protected $currentCookieData;
239
+
240
+	public function open($save_path, $name)
241
+	{
242
+		$this->cookie = $name.'_2';
243
+		// Read the incoming value, then clear the cookie - we might not be able
244
+		// to do so later if write() is called after headers are sent
245
+		// This is intended to force a failover to the database store if the
246
+		// modified session cannot be emitted.
247
+		$this->currentCookieData = Cookie::get($this->cookie);
248
+		if ($this->currentCookieData) {
249
+			Cookie::set($this->cookie, '');
250
+		}
251
+	}
252
+
253
+	public function close()
254
+	{
255
+	}
256
+
257
+	/**
258
+	 * Get the cryptography store for the specified session
259
+	 *
260
+	 * @param string $session_id
261
+	 * @return HybridSessionStore_Crypto
262
+	 */
263
+	protected function getCrypto($session_id)
264
+	{
265
+		$key = $this->getKey();
266
+		if (!$key) {
267
+			return null;
268
+		}
269
+		if (!$this->crypto || $this->crypto->salt != $session_id) {
270
+			$this->crypto = new HybridSessionStore_Crypto($key, $session_id);
271
+		}
272
+		return $this->crypto;
273
+	}
274
+
275
+	public function read($session_id)
276
+	{
277
+		// Check ability to safely decrypt content
278
+		if (!$this->currentCookieData
279
+			|| !($crypto = $this->getCrypto($session_id))
280
+		) {
281
+			return;
282
+		}
283
+
284
+		// Decrypt and invalidate old data
285
+		$cookieData = $crypto->decrypt($this->currentCookieData);
286
+		$this->currentCookieData = null;
287
+
288
+		// Verify expiration
289
+		if ($cookieData) {
290
+			$expiry = (int)substr($cookieData, 0, 10);
291
+			$data = substr($cookieData, 10);
292
+
293
+			if ($expiry > $this->getNow()) {
294
+				return $data;
295
+			}
296
+		}
297
+	}
298
+
299
+	/**
300
+	 * Determine if the session could be verifably written to cookie storage
301
+	 *
302
+	 * @return bool
303
+	 */
304
+	protected function canWrite()
305
+	{
306
+		return !headers_sent();
307
+	}
308
+
309
+	public function write($session_id, $session_data)
310
+	{
311
+		// Check ability to safely encrypt and write content
312
+		if (!$this->canWrite()
313
+			|| (strlen($session_data) > Config::inst()->get(__CLASS__, 'max_length'))
314
+			|| !($crypto = $this->getCrypto($session_id))
315
+		) {
316
+			return false;
317
+		}
318
+
319
+		// Prepare content for write
320
+		$params = session_get_cookie_params();
321
+		// Total max lifetime, stored internally
322
+		$lifetime = $this->getLifetime();
323
+		$expiry = $this->getNow() + $lifetime;
324
+
325
+		// Restore the known good cookie value
326
+		$this->currentCookieData = $this->crypto->encrypt(
327
+			sprintf('%010u', $expiry) . $session_data
328
+		);
329
+
330
+		// Respect auto-expire on browser close for the session cookie (in case the cookie lifetime is zero)
331
+		$cookieLifetime = min((int)$params['lifetime'], $lifetime);
332
+		Cookie::set(
333
+			$this->cookie,
334
+			$this->currentCookieData,
335
+			$cookieLifetime / 86400,
336
+			$params['path'],
337
+			$params['domain'],
338
+			$params['secure'],
339
+			$params['httponly']
340
+		);
341
+
342
+		return true;
343
+	}
344
+
345
+	public function destroy($session_id)
346
+	{
347
+		$this->currentCookieData = null;
348
+		Cookie::force_expiry($this->cookie);
349
+	}
350
+
351
+	public function gc($maxlifetime)
352
+	{
353
+		// NOP
354
+	}
355 355
 }
356 356
 
357 357
 class HybridSessionStore_Database extends HybridSessionStore_Base
358 358
 {
359
-    /**
360
-     * Determine if the DB is ready to use.
361
-     *
362
-     * @return bool
363
-     * @throws Exception
364
-     */
365
-    protected function isDatabaseReady()
366
-    {
367
-        // Such as during setup of testsession prior to DB connection.
368
-        if (!DB::isActive()) {
369
-            return false;
370
-        }
371
-
372
-        // If we have a DB of the wrong type then complain
373
-        if (!(DB::getConn() instanceof MySQLDatabase)) {
374
-            throw new Exception('HybridSessionStore currently only works with MySQL databases');
375
-        }
376
-
377
-        // Prevent freakout during dev/build
378
-        return ClassInfo::hasTable('HybridSessionDataObject');
379
-    }
380
-
381
-    public function open($save_path, $name)
382
-    {
383
-    }
384
-
385
-    public function close()
386
-    {
387
-    }
388
-
389
-    public function read($session_id)
390
-    {
391
-        if (!$this->isDatabaseReady()) {
392
-            return null;
393
-        }
394
-
395
-        $result = DB::query(sprintf(
396
-            'SELECT "Data" FROM "HybridSessionDataObject"
359
+	/**
360
+	 * Determine if the DB is ready to use.
361
+	 *
362
+	 * @return bool
363
+	 * @throws Exception
364
+	 */
365
+	protected function isDatabaseReady()
366
+	{
367
+		// Such as during setup of testsession prior to DB connection.
368
+		if (!DB::isActive()) {
369
+			return false;
370
+		}
371
+
372
+		// If we have a DB of the wrong type then complain
373
+		if (!(DB::getConn() instanceof MySQLDatabase)) {
374
+			throw new Exception('HybridSessionStore currently only works with MySQL databases');
375
+		}
376
+
377
+		// Prevent freakout during dev/build
378
+		return ClassInfo::hasTable('HybridSessionDataObject');
379
+	}
380
+
381
+	public function open($save_path, $name)
382
+	{
383
+	}
384
+
385
+	public function close()
386
+	{
387
+	}
388
+
389
+	public function read($session_id)
390
+	{
391
+		if (!$this->isDatabaseReady()) {
392
+			return null;
393
+		}
394
+
395
+		$result = DB::query(sprintf(
396
+			'SELECT "Data" FROM "HybridSessionDataObject"
397 397
 			WHERE "SessionID" = \'%s\' AND "Expiry" >= %u',
398
-            Convert::raw2sql($session_id),
399
-            $this->getNow()
400
-        ));
401
-
402
-        if ($result && $result->numRecords()) {
403
-            $data = $result->first();
404
-            return $data['Data'];
405
-        }
406
-    }
407
-
408
-    public function write($session_id, $session_data)
409
-    {
410
-        if (!$this->isDatabaseReady()) {
411
-            return false;
412
-        }
413
-
414
-        $expiry = $this->getNow() + $this->getLifetime();
415
-        DB::query($str = sprintf(
416
-            'INSERT INTO "HybridSessionDataObject" ("SessionID", "Expiry", "Data")
398
+			Convert::raw2sql($session_id),
399
+			$this->getNow()
400
+		));
401
+
402
+		if ($result && $result->numRecords()) {
403
+			$data = $result->first();
404
+			return $data['Data'];
405
+		}
406
+	}
407
+
408
+	public function write($session_id, $session_data)
409
+	{
410
+		if (!$this->isDatabaseReady()) {
411
+			return false;
412
+		}
413
+
414
+		$expiry = $this->getNow() + $this->getLifetime();
415
+		DB::query($str = sprintf(
416
+			'INSERT INTO "HybridSessionDataObject" ("SessionID", "Expiry", "Data")
417 417
 			VALUES (\'%1$s\', %2$u, \'%3$s\')
418 418
 			ON DUPLICATE KEY UPDATE "Expiry" = %2$u, "Data" = \'%3$s\'',
419
-            Convert::raw2sql($session_id),
420
-            $expiry,
421
-            Convert::raw2sql($session_data)
422
-        ));
423
-
424
-        return true;
425
-    }
426
-
427
-    public function destroy($session_id)
428
-    {
429
-        // NOP
430
-    }
431
-
432
-    public function gc($maxlifetime)
433
-    {
434
-        if (!$this->isDatabaseReady()) {
435
-            return;
436
-        }
437
-        DB::query(sprintf(
438
-            'DELETE FROM "HybridSessionDataObject" WHERE "Expiry" < %u',
439
-            $this->getNow()
440
-        ));
441
-    }
419
+			Convert::raw2sql($session_id),
420
+			$expiry,
421
+			Convert::raw2sql($session_data)
422
+		));
423
+
424
+		return true;
425
+	}
426
+
427
+	public function destroy($session_id)
428
+	{
429
+		// NOP
430
+	}
431
+
432
+	public function gc($maxlifetime)
433
+	{
434
+		if (!$this->isDatabaseReady()) {
435
+			return;
436
+		}
437
+		DB::query(sprintf(
438
+			'DELETE FROM "HybridSessionDataObject" WHERE "Expiry" < %u',
439
+			$this->getNow()
440
+		));
441
+	}
442 442
 }
443 443
 
444 444
 
445 445
 class HybridSessionStore extends HybridSessionStore_Base
446 446
 {
447
-    /**
448
-     * List of session handlers
449
-     *
450
-     * @var array[HybridSessionStore_Base]
451
-     */
452
-    protected $handlers = array();
453
-
454
-    /**
455
-     * True if this session store has been initialised
456
-     *
457
-     * @var bool
458
-     */
459
-    protected static $enabled = false;
460
-
461
-    /**
462
-     * @param array[HybridSessionStore_Base]
463
-     */
464
-    public function setHandlers($handlers)
465
-    {
466
-        $this->handlers = $handlers;
467
-        $this->setKey($this->getKey());
468
-    }
469
-
470
-    public function setKey($key)
471
-    {
472
-        parent::setKey($key);
473
-        foreach ($this->handlers as $handler) {
474
-            $handler->setKey($key);
475
-        }
476
-    }
477
-
478
-    /**
479
-     * @return array[SessionHandlerInterface]
480
-     */
481
-    public function getHandlers()
482
-    {
483
-        return $this->handlers;
484
-    }
485
-
486
-    public function open($save_path, $name)
487
-    {
488
-        foreach ($this->handlers as $handler) {
489
-            $handler->open($save_path, $name);
490
-        }
491
-
492
-        return true;
493
-    }
494
-
495
-    public function close()
496
-    {
497
-        foreach ($this->handlers as $handler) {
498
-            $handler->close();
499
-        }
500
-
501
-        return true;
502
-    }
503
-
504
-    public function read($session_id)
505
-    {
506
-        foreach ($this->handlers as $handler) {
507
-            if ($data = $handler->read($session_id)) {
508
-                return $data;
509
-            }
510
-        }
511
-
512
-        return '';
513
-    }
514
-
515
-    public function write($session_id, $session_data)
516
-    {
517
-        foreach ($this->handlers as $handler) {
518
-            if ($handler->write($session_id, $session_data)) {
519
-                return;
520
-            }
521
-        }
522
-    }
523
-
524
-    public function destroy($session_id)
525
-    {
526
-        foreach ($this->handlers as $handler) {
527
-            $handler->destroy($session_id);
528
-        }
529
-    }
530
-
531
-    public function gc($maxlifetime)
532
-    {
533
-        foreach ($this->handlers as $handler) {
534
-            $handler->gc($maxlifetime);
535
-        }
536
-    }
537
-
538
-    /**
539
-     * Register the session handler as the default
540
-     *
541
-     * @param string $key Desired session key
542
-     */
543
-    public static function init($key = null)
544
-    {
545
-        $instance = Injector::inst()->get(__CLASS__);
546
-        if (empty($key)) {
547
-            user_error(
548
-                'HybridSessionStore::init() was not given a $key. Disabling cookie-based storage',
549
-                E_USER_WARNING
550
-            );
551
-        } else {
552
-            $instance->setKey($key);
553
-        }
554
-        register_sessionhandler($instance);
555
-        self::$enabled = true;
556
-    }
557
-
558
-    public static function is_enabled()
559
-    {
560
-        return self::$enabled;
561
-    }
447
+	/**
448
+	 * List of session handlers
449
+	 *
450
+	 * @var array[HybridSessionStore_Base]
451
+	 */
452
+	protected $handlers = array();
453
+
454
+	/**
455
+	 * True if this session store has been initialised
456
+	 *
457
+	 * @var bool
458
+	 */
459
+	protected static $enabled = false;
460
+
461
+	/**
462
+	 * @param array[HybridSessionStore_Base]
463
+	 */
464
+	public function setHandlers($handlers)
465
+	{
466
+		$this->handlers = $handlers;
467
+		$this->setKey($this->getKey());
468
+	}
469
+
470
+	public function setKey($key)
471
+	{
472
+		parent::setKey($key);
473
+		foreach ($this->handlers as $handler) {
474
+			$handler->setKey($key);
475
+		}
476
+	}
477
+
478
+	/**
479
+	 * @return array[SessionHandlerInterface]
480
+	 */
481
+	public function getHandlers()
482
+	{
483
+		return $this->handlers;
484
+	}
485
+
486
+	public function open($save_path, $name)
487
+	{
488
+		foreach ($this->handlers as $handler) {
489
+			$handler->open($save_path, $name);
490
+		}
491
+
492
+		return true;
493
+	}
494
+
495
+	public function close()
496
+	{
497
+		foreach ($this->handlers as $handler) {
498
+			$handler->close();
499
+		}
500
+
501
+		return true;
502
+	}
503
+
504
+	public function read($session_id)
505
+	{
506
+		foreach ($this->handlers as $handler) {
507
+			if ($data = $handler->read($session_id)) {
508
+				return $data;
509
+			}
510
+		}
511
+
512
+		return '';
513
+	}
514
+
515
+	public function write($session_id, $session_data)
516
+	{
517
+		foreach ($this->handlers as $handler) {
518
+			if ($handler->write($session_id, $session_data)) {
519
+				return;
520
+			}
521
+		}
522
+	}
523
+
524
+	public function destroy($session_id)
525
+	{
526
+		foreach ($this->handlers as $handler) {
527
+			$handler->destroy($session_id);
528
+		}
529
+	}
530
+
531
+	public function gc($maxlifetime)
532
+	{
533
+		foreach ($this->handlers as $handler) {
534
+			$handler->gc($maxlifetime);
535
+		}
536
+	}
537
+
538
+	/**
539
+	 * Register the session handler as the default
540
+	 *
541
+	 * @param string $key Desired session key
542
+	 */
543
+	public static function init($key = null)
544
+	{
545
+		$instance = Injector::inst()->get(__CLASS__);
546
+		if (empty($key)) {
547
+			user_error(
548
+				'HybridSessionStore::init() was not given a $key. Disabling cookie-based storage',
549
+				E_USER_WARNING
550
+			);
551
+		} else {
552
+			$instance->setKey($key);
553
+		}
554
+		register_sessionhandler($instance);
555
+		self::$enabled = true;
556
+	}
557
+
558
+	public static function is_enabled()
559
+	{
560
+		return self::$enabled;
561
+	}
562 562
 }
563 563
 
564 564
 class HybridSessionStore_RequestFilter implements RequestFilter
565 565
 {
566
-    public function preRequest(SS_HTTPRequest $request, Session $session, DataModel $model)
567
-    {
568
-        // NOP
569
-    }
570
-
571
-    public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model)
572
-    {
573
-        if (HybridSessionStore::is_enabled()) {
574
-            session_write_close();
575
-        }
576
-    }
566
+	public function preRequest(SS_HTTPRequest $request, Session $session, DataModel $model)
567
+	{
568
+		// NOP
569
+	}
570
+
571
+	public function postRequest(SS_HTTPRequest $request, SS_HTTPResponse $response, DataModel $model)
572
+	{
573
+		if (HybridSessionStore::is_enabled()) {
574
+			session_write_close();
575
+		}
576
+	}
577 577
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -99,7 +99,7 @@  discard block
 block discarded – undo
99 99
 
100 100
         $hash = hash_hmac('sha256', $enc, $this->saltedKey);
101 101
 
102
-        return base64_encode($iv.$hash.$enc);
102
+        return base64_encode($iv . $hash . $enc);
103 103
     }
104 104
 
105 105
     /**
@@ -171,8 +171,8 @@  discard block
 block discarded – undo
171 171
     protected function getLifetime()
172 172
     {
173 173
         $params = session_get_cookie_params();
174
-        $cookieLifetime = (int)$params['lifetime'];
175
-        $gcLifetime = (int)ini_get('session.gc_maxlifetime');
174
+        $cookieLifetime = (int) $params['lifetime'];
175
+        $gcLifetime = (int) ini_get('session.gc_maxlifetime');
176 176
         return $cookieLifetime ? min($cookieLifetime, $gcLifetime) : $gcLifetime;
177 177
     }
178 178
 
@@ -183,7 +183,7 @@  discard block
 block discarded – undo
183 183
      */
184 184
     protected function getNow()
185 185
     {
186
-        return (int)SS_Datetime::now()->Format('U');
186
+        return (int) SS_Datetime::now()->Format('U');
187 187
     }
188 188
 }
189 189
 
@@ -239,7 +239,7 @@  discard block
 block discarded – undo
239 239
 
240 240
     public function open($save_path, $name)
241 241
     {
242
-        $this->cookie = $name.'_2';
242
+        $this->cookie = $name . '_2';
243 243
         // Read the incoming value, then clear the cookie - we might not be able
244 244
         // to do so later if write() is called after headers are sent
245 245
         // This is intended to force a failover to the database store if the
@@ -287,7 +287,7 @@  discard block
 block discarded – undo
287 287
 
288 288
         // Verify expiration
289 289
         if ($cookieData) {
290
-            $expiry = (int)substr($cookieData, 0, 10);
290
+            $expiry = (int) substr($cookieData, 0, 10);
291 291
             $data = substr($cookieData, 10);
292 292
 
293 293
             if ($expiry > $this->getNow()) {
@@ -328,7 +328,7 @@  discard block
 block discarded – undo
328 328
         );
329 329
 
330 330
         // Respect auto-expire on browser close for the session cookie (in case the cookie lifetime is zero)
331
-        $cookieLifetime = min((int)$params['lifetime'], $lifetime);
331
+        $cookieLifetime = min((int) $params['lifetime'], $lifetime);
332 332
         Cookie::set(
333 333
             $this->cookie,
334 334
             $this->currentCookieData,
Please login to merge, or discard this patch.
code/HybridSessionDataObject.php 1 patch
Indentation   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -2,14 +2,14 @@
 block discarded – undo
2 2
 
3 3
 class HybridSessionDataObject extends DataObject
4 4
 {
5
-    private static $db = array(
6
-        'SessionID' => 'Varchar(64)',
7
-        'Expiry' => 'Int',
8
-        'Data' => 'Text'
9
-    );
5
+	private static $db = array(
6
+		'SessionID' => 'Varchar(64)',
7
+		'Expiry' => 'Int',
8
+		'Data' => 'Text'
9
+	);
10 10
 
11
-    private static $indexes = array(
12
-        'SessionID' => array('type' => 'unique', 'value' => '"SessionID"'),
13
-        'Expiry' => true
14
-    );
11
+	private static $indexes = array(
12
+		'SessionID' => array('type' => 'unique', 'value' => '"SessionID"'),
13
+		'Expiry' => true
14
+	);
15 15
 }
Please login to merge, or discard this patch.