Passed
Push — 1.0.0-dev ( 066288...93958a )
by nguereza
09:45
created

DBSessionHandler::setDependencyInstanceFromParamOrCreate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 4
dl 0
loc 7
rs 10
1
<?php 
2
	defined('ROOT_PATH') || exit('Access denied');
3
	/**
4
	 * TNH Framework
5
	 *
6
	 * A simple PHP framework using HMVC architecture
7
	 *
8
	 * This content is released under the GNU GPL License (GPL)
9
	 *
10
	 * Copyright (C) 2017 Tony NGUEREZA
11
	 *
12
	 * This program is free software; you can redistribute it and/or
13
	 * modify it under the terms of the GNU General Public License
14
	 * as published by the Free Software Foundation; either version 3
15
	 * of the License, or (at your option) any later version.
16
	 *
17
	 * This program is distributed in the hope that it will be useful,
18
	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
	 * GNU General Public License for more details.
21
	 *
22
	 * You should have received a copy of the GNU General Public License
23
	 * along with this program; if not, write to the Free Software
24
	 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25
	*/
26
	
27
	/**
28
	 * check if the interface "SessionHandlerInterface" exists (normally in PHP 5.4 this already exists)
29
	 */
30
	if ( !interface_exists('SessionHandlerInterface')){
31
		show_error('"SessionHandlerInterface" interface does not exists or is disabled can not use it to handler database session.');
32
	}
33
34
	class DBSessionHandler implements SessionHandlerInterface{
35
		
36
		/**
37
		 * The encryption method to use to encrypt session data in database
38
		 * @const string
39
		 */
40
		const DB_SESSION_HASH_METHOD = 'AES-256-CBC';
41
		
42
		/**
43
		 * Super global instance
44
		 * @var object
45
		 */
46
		protected $OBJ = null;
47
48
		/**
49
		 * Session secret to use 
50
		 * @var string
51
		 */
52
		private $sessionSecret = null;
53
54
		/**
55
		 * The initialisation vector to use for openssl
56
		 * @var string
57
		 */
58
		private $iv = null;
59
60
		/**
61
		 * The model instance to use
62
		 * @var object
63
		 */
64
		private $modelInstance = null;
65
66
		/**
67
		 * The columns of the table to use to store session data
68
		 * @var array
69
		 */
70
		private $sessionTableColumns = array();
71
72
		/**
73
		 * The instance of the Log 
74
		 * @var Log
75
		 */
76
		private $logger;
77
78
		/**
79
         * Instance of the Loader class
80
         * @var Loader
81
         */
82
        protected $loader = null;
83
84
        /**
85
         * Create new instance of Database session handler
86
         * @param object $modelInstance the model instance
87
         */
88
		public function __construct(DBSessionHandlerModel $modelInstance = null){
89
			//Set Log instance to use
90
	        $this->setLoggerFromParamOrCreate(null);
91
			
92
	    	//Set Loader instance to use
93
	        $this->setDependencyInstanceFromParamOrCreate('loader', null, 'Loader', 'classes');
94
	       
95
		    $this->OBJ = & get_instance();
96
		    
97
			if (is_object($modelInstance)){
98
				$this->setModelInstance($modelInstance);
99
			}
100
		}
101
102
		/**
103
		 * Set the session secret used to encrypt the data in database 
104
		 * @param string $secret the base64 string secret
105
		 */
106
		public function setSessionSecret($secret){
107
			$this->sessionSecret = $secret;
108
			return $this;
109
		}
110
111
		/**
112
		 * Return the session secret
113
		 * @return string 
114
		 */
115
		public function getSessionSecret(){
116
			return $this->sessionSecret;
117
		}
118
119
120
		/**
121
		 * Set the initializer vector for openssl 
122
		 * @param string $key the session secret used in base64 format
123
		 */
124
		public function setInitializerVector($key){
125
			$ivLength = openssl_cipher_iv_length(self::DB_SESSION_HASH_METHOD);
126
			$key = base64_decode($key);
127
			$this->iv = substr(hash('sha256', $key), 0, $ivLength);
128
			return $this;
129
		}
130
131
		/**
132
		 * Return the initializer vector
133
		 * @return string 
134
		 */
135
		public function getInitializerVector(){
136
			return $this->iv;
137
		}
138
139
		/**
140
		 * Open the database session handler, here nothing to do just return true
141
		 * @param  string $savePath    the session save path
142
		 * @param  string $sessionName the session name
143
		 * @return boolean 
144
		 */
145
		public function open($savePath, $sessionName){
146
			$this->logger->debug('Opening database session handler for [' . $sessionName . ']');
147
			//try to check if session secret is set before
148
			$secret = $this->getSessionSecret();
149
			if (empty($secret)){
150
				$secret = get_config('session_secret', null);
151
				$this->setSessionSecret($secret);
152
			}
153
			$this->logger->info('Session secret: ' . $secret);
154
155
			if (! is_object($this->modelInstance)){
156
				$this->setModelInstanceFromConfig();
157
			}
158
			$this->setInitializerVector($secret);
159
160
			//set session tables columns
161
			$this->sessionTableColumns = $this->modelInstance->getSessionTableColumns();
162
163
			if (empty($this->sessionTableColumns)){
164
				show_error('The session handler is "database" but the table columns not set');
165
			}
166
			$this->logger->info('Database session, the model columns are listed below: ' . stringfy_vars($this->sessionTableColumns));
167
			
168
			//delete the expired session
169
			$timeActivity = get_config('session_inactivity_time', 100);
170
			$this->gc($timeActivity);
171
			
172
			return true;
173
		}
174
175
		/**
176
		 * Close the session
177
		 * @return boolean
178
		 */
179
		public function close(){
180
			$this->logger->debug('Closing database session handler');
181
			return true;
182
		}
183
184
		/**
185
		 * Get the session value for the given session id
186
		 * @param  string $sid the session id to use
187
		 * @return string      the session data in serialiaze format
188
		 */
189
		public function read($sid){
190
			$this->logger->debug('Reading database session data for SID: ' . $sid);
191
			$instance = $this->getModelInstance();
192
			$columns = $this->sessionTableColumns;
193
			$this->loader->functions('user_agent'); 
194
			$this->loader->library('Browser'); 
195
			
196
			$ip = get_ip();
197
			$host = @gethostbyaddr($ip) or null;
198
			$browser = $this->OBJ->browser->getPlatform().', '.$this->OBJ->browser->getBrowser().' '.$this->OBJ->browser->getVersion();
199
			
200
			$data = $instance->get_by(array($columns['sid'] => $sid, $columns['shost'] => $host, $columns['sbrowser'] => $browser));
201
			if ($data && isset($data->{$columns['sdata']})){
202
				//checking inactivity 
203
				$timeInactivity = time() - get_config('session_inactivity_time', 100);
204
				if ($data->{$columns['stime']} < $timeInactivity){
205
					$this->logger->info('Database session data for SID: ' . $sid . ' already expired, destroy it');
206
					$this->destroy($sid);
207
					return null;
208
				}
209
				return $this->decode($data->{$columns['sdata']});
210
			}
211
			$this->logger->info('Database session data for SID: ' . $sid . ' is not valid return false, may be the session ID is wrong');
212
			return null;
213
		}
214
215
		/**
216
		 * Save the session data
217
		 * @param  string $sid  the session ID
218
		 * @param  mixed $data the session data to save in serialize format
219
		 * @return boolean 
220
		 */
221
		public function write($sid, $data){
222
			$this->logger->debug('Saving database session data for SID: ' . $sid . ', data: ' . stringfy_vars($data));
223
			$instance = $this->getModelInstance();
224
			$columns = $this->sessionTableColumns;
225
226
			$this->loader->functions('user_agent'); 
227
			$this->loader->library('Browser'); 
228
229
			$ip = get_ip();
230
			$keyValue = $instance->getKeyValue();
231
			$host = @gethostbyaddr($ip) or null;
232
			$browser = $this->OBJ->browser->getPlatform().', '.$this->OBJ->browser->getBrowser().' '.$this->OBJ->browser->getVersion();
233
			$data = $this->encode($data);
234
			$params = array(
235
							$columns['sid'] => $sid,
236
							$columns['sdata'] => $data,
237
							$columns['stime'] => time(),
238
							$columns['shost'] => $host,
239
							$columns['sbrowser'] => $browser,
240
							$columns['sip'] => $ip,
241
							$columns['skey'] => $keyValue
242
						);
243
			$this->logger->info('Database session data to save are listed below :' . stringfy_vars($params));
244
			$exists = $instance->get($sid);
245
			if ($exists){
246
				$this->logger->info('Session data for SID: ' . $sid . ' already exists, just update it');
247
				//update
248
				unset($params[$columns['sid']]);
249
				return $instance->update($sid, $params);
250
			}
251
			$this->logger->info('Session data for SID: ' . $sid . ' not yet exists, insert it now');
252
			return $instance->insert($params);
253
			return true;
0 ignored issues
show
Unused Code introduced by
return true is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
254
		}
255
256
257
		/**
258
		 * Destroy the session data for the given session id
259
		 * @param  string $sid the session id value
260
		 * @return boolean
261
		 */
262
		public function destroy($sid){
263
			$this->logger->debug('Destroy of session data for SID: ' . $sid);
264
			$instance = $this->modelInstance;
265
			$instance->delete($sid);
266
			return true;
267
		}
268
269
		/**
270
		 * Clean the expire session data to save espace
271
		 * @param  integer $maxLifetime the max lifetime
272
		 * @return boolean
273
		 */
274
		public function gc($maxLifetime){
275
			$instance = $this->modelInstance;
276
			$time = time() - $maxLifetime;
277
			$this->logger->debug('Garbage collector of expired session. maxLifetime [' . $maxLifetime . '] sec, expired time [' . $time . ']');
278
			$instance->deleteByTime($time);
279
			return true;
280
		}
281
282
		/**
283
		 * Encode the session data using the openssl
284
		 * @param  mixed $data the session data to encode
285
		 * @return mixed the encoded session data
286
		 */
287
		public function encode($data){
288
			$key = base64_decode($this->sessionSecret);
289
			$dataEncrypted = openssl_encrypt($data , self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
290
			$output = base64_encode($dataEncrypted);
291
			return $output;
292
		}
293
294
295
		/**
296
		 * Decode the session data using the openssl
297
		 * @param  mixed $data the data to decode
298
		 * @return mixed       the decoded data
299
		 */
300
		public function decode($data){
301
			$key = base64_decode($this->sessionSecret);
302
			$data = base64_decode($data);
303
			$data = openssl_decrypt($data, self::DB_SESSION_HASH_METHOD, $key, OPENSSL_RAW_DATA, $this->getInitializerVector());
304
			return $data;
305
		}
306
307
		
308
		/**
309
         * Return the loader instance
310
         * @return object Loader the loader instance
311
         */
312
        public function getLoader(){
313
            return $this->loader;
314
        }
315
316
        /**
317
         * set the loader instance for future use
318
         * @param object Loader $loader the loader object
319
         */
320
         public function setLoader($loader){
321
            $this->loader = $loader;
322
            return $this;
323
        }
324
325
        /**
326
         * Return the model instance
327
         * @return object DBSessionHandlerModel the model instance
328
         */
329
        public function getModelInstance(){
330
            return $this->modelInstance;
331
        }
332
333
        /**
334
         * set the model instance for future use
335
         * @param DBSessionHandlerModel $modelInstance the model object
336
         */
337
         public function setModelInstance(DBSessionHandlerModel $modelInstance){
338
            $this->modelInstance = $modelInstance;
339
            return $this;
340
        }
341
342
        /**
343
	     * Return the Log instance
344
	     * @return Log
345
	     */
346
	    public function getLogger(){
347
	      return $this->logger;
348
	    }
349
350
	    /**
351
	     * Set the log instance
352
	     * @param Log $logger the log object
353
	     */
354
	    public function setLogger(Log $logger){
355
	      $this->logger = $logger;
356
	      return $this;
357
	    }
358
359
	    /**
360
	     * Set the dependencies instance using argument or create new instance if is null
361
	     * @param string $name this class property name.
362
	     * @param object $instance the instance. If is not null will use it
363
	     * otherwise will create new instance.
364
	     * @param string $loadClassName the name of class to load using class_loader function.
365
	     * @param string $loadClassPath the path of class to load using class_loader function.
366
	     *
367
	     * @return object this current instance
368
	     */
369
	    protected function setDependencyInstanceFromParamOrCreate($name, $instance = null, $loadClassName = null, $loadClassePath = 'classes'){
370
	      if ($instance !== null){
371
	        $this->{$name} = $instance;
372
	        return $this;
373
	      }
374
	      $this->{$name} =& class_loader($loadClassName, $loadClassePath);
375
	      return $this;
376
	    }
377
	    
378
		   /**
379
	     * Set the Log instance using argument or create new instance
380
	     * @param object $logger the Log instance if not null
381
	     *
382
	     * @return object this current instance
383
	     */
384
	    protected function setLoggerFromParamOrCreate(Log $logger = null){
385
	      $this->setDependencyInstanceFromParamOrCreate('logger', $logger, 'Log', 'classes');
386
	      if ($logger === null){
387
	        $this->logger->setLogger('Library::DBSessionHandler');
388
	      }
389
	      return $this;
390
	    }
391
392
	    /**
393
	     * Set the model instance using the configuration for session
394
	     */
395
	    protected function setModelInstanceFromConfig(){
396
	    	$modelName = get_config('session_save_path');
397
			$this->logger->info('The database session model: ' . $modelName);
398
			$this->loader->model($modelName, 'dbsessionhandlerinstance'); 
399
			//@codeCoverageIgnoreStart
400
            if (isset($this->OBJ->dbsessionhandlerinstance) 
401
            	&& ! ($this->OBJ->dbsessionhandlerinstance instanceof DBSessionHandlerModel)
402
            ) {
403
				show_error('To use database session handler, your class model "' . get_class($this->OBJ->dbsessionhandlerinstance) . '" need extends "DBSessionHandlerModel"');
404
			}  
405
			//@codeCoverageIgnoreEnd
406
			
407
			//set model instance
408
			$this->modelInstance = $this->OBJ->dbsessionhandlerinstance;
409
	    }
410
	}
411