Completed
Branch FET-10486-add-timestamp-checki... (611b15)
by
unknown
136:24 queued 121:17
created

EE_Session::garbage_collection()   C

Complexity

Conditions 10
Paths 8

Size

Total Lines 60
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 35
nc 8
nop 0
dl 0
loc 60
rs 6.5333
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A EE_Session::wp_loaded() 0 5 3
A EE_Session::reset_instance() 0 4 1
A EE_Session::configure_garbage_collection_filters() 0 23 3

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 use EventEspresso\core\exceptions\InvalidSessionDataException;
2
use EventEspresso\core\services\cache\CacheStorageInterface;
3
4
if (!defined( 'EVENT_ESPRESSO_VERSION')) {exit('No direct script access allowed');}
5
/**
6
 *
7
 * EE_Session class
8
 *
9
 * @package    Event Espresso
10
 * @subpackage includes/classes
11
 * @author     Brent Christensen
12
 */
13
 class EE_Session {
14
15
	 const session_id_prefix = 'ee_ssn_';
16
17
	 const hash_check_prefix = 'ee_shc_';
18
19
	 /**
20
	  * instance of the EE_Session object
21
	  * @var EE_Session
22
	  */
23
	 private static $_instance;
24
25
     /**
26
      * @var CacheStorageInterface $cache_storage
27
      */
28
     protected $cache_storage;
29
30
     /**
31
      * EE_Encryption object
32
      *
33
      * @var EE_Encryption
34
      */
35
     protected $encryption;
36
37
     /**
38
	  * the session id
39
	  * @var string
40
	  */
41
	 private $_sid;
42
43
	 /**
44
	  * session id salt
45
	  * @var string
46
	  */
47
	 private $_sid_salt;
48
49
	 /**
50
	  * session data
51
	  * @var array
52
	  */
53
	 private $_session_data = array();
54
55
	 /**
56
	  * how long an EE session lasts
57
	  * default session lifespan of 2 hours (for not so instant IPNs)
58
	  * @var int
59
	  */
60
	 private $_lifespan;
61
62
	 /**
63
	  * session expiration time as Unix timestamp in GMT
64
	  * @var int
65
	  */
66
	 private $_expiration;
67
68
    /**
69
     * whether or not session has expired at some point
70
     *
71
     * @var boolean
72
     */
73
    private $_expired = false;
74
75
	 /**
76
	  * current time as Unix timestamp in GMT
77
	  * @var int
78
	  */
79
	 private $_time;
80
81
	 /**
82
	  * whether to encrypt session data
83
	  * @var bool
84
	  */
85
	 private $_use_encryption = false;
86
87
	 /**
88
	  * well... according to the server...
89
	  * @var null
90
	  */
91
	 private $_user_agent;
92
93
	 /**
94
	  * do you really trust the server ?
95
	  * @var null
96
	  */
97
	 private $_ip_address;
98
99
	 /**
100
	  * current WP user_id
101
	  * @var null
102
	  */
103
	 private $_wp_user_id;
104
105
	 /**
106
	  * array for defining default session vars
107
	  * @var array
108
	  */
109
	 private $_default_session_vars = array (
110
        'id'            => null,
111
        'user_id'       => null,
112
        'ip_address'    => null,
113
        'user_agent'    => null,
114
        'init_access'   => null,
115
        'last_access'   => null,
116
        'expiration'    => null,
117
        'pages_visited' => array(),
118
	);
119
120
121
122
	 /**
123
	  * @singleton method used to instantiate class object
124
      * @param CacheStorageInterface $cache_storage
125
      * @param \EE_Encryption        $encryption
126
	  * @return EE_Session
127
	  * @throws InvalidSessionDataException
128
	  * @throws \EE_Error
129
	  */
130
	public static function instance(
131
        CacheStorageInterface $cache_storage = null,
132
        EE_Encryption $encryption = null
133
    ) {
134
		// check if class object is instantiated
135
		// session loading is turned ON by default, but prior to the init hook, can be turned back OFF via:
136
		// add_filter( 'FHEE_load_EE_Session', '__return_false' );
137
		if ( ! self::$_instance instanceof EE_Session && apply_filters( 'FHEE_load_EE_Session', true ) ) {
138
			self::$_instance = new self($cache_storage, $encryption );
0 ignored issues
show
Bug introduced by
It seems like $cache_storage defined by parameter $cache_storage on line 131 can be null; however, EE_Session::__construct() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
139
		}
140
		return self::$_instance;
141
	}
142
143
144
145
	 /**
146
      * protected constructor to prevent direct creation
147
      *
148
      * @param CacheStorageInterface $cache_storage
149
      * @param \EE_Encryption $encryption
150
	  * @throws \EE_Error
151
	  * @throws \EventEspresso\core\exceptions\InvalidSessionDataException
152
	  */
153
	 protected function __construct(CacheStorageInterface $cache_storage, EE_Encryption $encryption = null ) {
154
155
		// session loading is turned ON by default, but prior to the init hook, can be turned back OFF via: add_filter( 'FHEE_load_EE_Session', '__return_false' );
156
		if ( ! apply_filters( 'FHEE_load_EE_Session', true ) ) {
157
			return;
158
		}
159
		do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
160
		if ( ! defined( 'ESPRESSO_SESSION' ) ) {
161
			define( 'ESPRESSO_SESSION', true );
162
		}
163
		// default session lifespan in seconds
164
		$this->_lifespan = apply_filters(
165
			'FHEE__EE_Session__construct___lifespan',
166
			60 * MINUTE_IN_SECONDS
167
		) + 1;
168
		/*
169
		 * do something like the following to adjust the session lifespan:
170
		 * 		public static function session_lifespan() {
171
		 * 			return 15 * MINUTE_IN_SECONDS;
172
		 * 		}
173
		 */
174
		// retrieve session options from db
175
		$session_settings = (array) get_option( 'ee_session_settings', array() );
176
		if ( ! empty( $session_settings )) {
177
			// cycle though existing session options
178
			foreach ( $session_settings as $var_name => $session_setting ) {
179
				// set values for class properties
180
				$var_name = '_' . $var_name;
181
				$this->{$var_name} = $session_setting;
182
			}
183
		}
184
         $this->cache_storage = $cache_storage;
185
         // are we using encryption?
186
         $this->_use_encryption = $encryption instanceof EE_Encryption && EE_Registry::instance()->CFG->admin->encode_session_data();
187
         // \EEH_Debug_Tools::printr($this->_use_encryption, '$this->_use_encryption', __FILE__, __LINE__);
188
        // encrypt data via: $this->encryption->encrypt();
189
        $this->encryption = $encryption;
190
		// filter hook allows outside functions/classes/plugins to change default empty cart
191
		$extra_default_session_vars = apply_filters( 'FHEE__EE_Session__construct__extra_default_session_vars', array() );
192
		array_merge( $this->_default_session_vars, $extra_default_session_vars );
193
		// apply default session vars
194
		$this->_set_defaults();
195
         add_action('AHEE__EE_System__initialize', array($this, 'open_session'));
196
         // check request for 'clear_session' param
197
		add_action( 'AHEE__EE_Request_Handler__construct__complete', array( $this, 'wp_loaded' ));
198
		// once everything is all said and done,
199
		add_action( 'shutdown', array( $this, 'update' ), 100 );
200
         $this->configure_garbage_collection_filters();
201
	}
202
203
204
205
     /**
206
      * @return void
207
      * @throws \EventEspresso\core\exceptions\InvalidSessionDataException
208
      * @throws \EE_Error
209
      */
210
	 public function open_session() {
211
         // check for existing session and retrieve it from db
212
         if ( ! $this->_espresso_session()) {
213
             // or just start a new one
214
             $this->_create_espresso_session();
215
         }
216
     }
217
218
219
220
    /**
221
     * @return bool
222
     */
223
    public function expired()
224
    {
225
        return $this->_expired;
226
    }
227
228
229
230
    /**
231
     * @return void
232
     */
233
    public function reset_expired()
234
    {
235
        $this->_expired = false;
236
    }
237
238
239
	 /**
240
	  * @return int
241
	  */
242
	 public function expiration() {
243
		 return $this->_expiration;
244
	 }
245
246
247
248
    /**
249
     * @return int
250
     */
251
    public function extension()
252
    {
253
        return apply_filters('FHEE__EE_Session__extend_expiration__seconds_added', (10 * MINUTE_IN_SECONDS));
254
    }
255
256
257
258
    /**
259
     * @param int $time number of seconds to add to session expiration
260
     */
261
    public function extend_expiration($time = 0)
262
    {
263
        $time = $time ? $time : $this->extension();
264
        $this->_expiration += absint($time);
265
    }
266
267
268
269
270
	 /**
271
	  * @return int
272
	  */
273
	 public function lifespan() {
274
		 return $this->_lifespan;
275
	 }
276
277
278
279
	/**
280
	 * This just sets some defaults for the _session data property
281
	 *
282
	 * @access private
283
	 * @return void
284
	 */
285
	private function _set_defaults() {
286
		// set some defaults
287
		foreach ( $this->_default_session_vars as $key => $default_var ) {
288
			if ( is_array( $default_var )) {
289
				$this->_session_data[ $key ] = array();
290
			} else {
291
				$this->_session_data[ $key ] = '';
292
			}
293
		}
294
	}
295
296
297
298
	/**
299
	 * @retrieve session data
300
	 * @access	public
301
	 * @return	string
302
	 */
303
	public function id() {
304
		return $this->_sid;
305
	}
306
307
308
309
    /**
310
     * @param \EE_Cart $cart
311
     * @return bool
312
     */
313
    public function set_cart(EE_Cart $cart)
314
    {
315
        $this->_session_data['cart'] = $cart;
316
        return true;
317
    }
318
319
320
321
	 /**
322
	  * reset_cart
323
	  */
324
	 public function reset_cart() {
325
        do_action('AHEE__EE_Session__reset_cart__before_reset', $this);
326
		 $this->_session_data['cart'] = NULL;
327
	 }
328
329
330
331
	 /**
332
	  * @return \EE_Cart
333
	  */
334
	 public function cart() {
335
        return isset($this->_session_data['cart']) && $this->_session_data['cart'] instanceof EE_Cart
336
            ? $this->_session_data['cart']
337
            : null;
338
	 }
339
340
341
342
	 /**
343
	  * @param \EE_Checkout $checkout
344
	  * @return bool
345
	  */
346
	 public function set_checkout( EE_Checkout $checkout ) {
347
		 $this->_session_data['checkout'] = $checkout;
348
		 return TRUE;
349
	 }
350
351
352
353
	 /**
354
	  * reset_checkout
355
	  */
356
	 public function reset_checkout() {
357
        do_action('AHEE__EE_Session__reset_checkout__before_reset', $this);
358
		 $this->_session_data['checkout'] = NULL;
359
	 }
360
361
362
363
	 /**
364
	  * @return \EE_Checkout
365
	  */
366
	 public function checkout() {
367
        return isset($this->_session_data['checkout']) && $this->_session_data['checkout'] instanceof EE_Checkout
368
            ? $this->_session_data['checkout']
369
            : null;
370
	 }
371
372
373
374
	 /**
375
	  * @param \EE_Transaction $transaction
376
	  * @return bool
377
	  * @throws \EE_Error
378
	  */
379
	 public function set_transaction( EE_Transaction $transaction ) {
380
		 // first remove the session from the transaction before we save the transaction in the session
381
		 $transaction->set_txn_session_data( NULL );
382
		 $this->_session_data['transaction'] = $transaction;
383
		 return TRUE;
384
	 }
385
386
387
388
	 /**
389
	  * reset_transaction
390
	  */
391
	 public function reset_transaction() {
392
        do_action('AHEE__EE_Session__reset_transaction__before_reset', $this);
393
		 $this->_session_data['transaction'] = NULL;
394
	 }
395
396
397
398
	 /**
399
	  * @return \EE_Transaction
400
	  */
401
	 public function transaction() {
402
        return isset($this->_session_data['transaction'])
403
               && $this->_session_data['transaction'] instanceof EE_Transaction
404
           ? $this->_session_data['transaction']
405
           : null;
406
	 }
407
408
409
410
	 /**
411
	  * retrieve session data
412
	  * @access    public
413
	  * @param null $key
414
	  * @param bool $reset_cache
415
	  * @return    array
416
	  */
417
	public function get_session_data( $key = NULL, $reset_cache = FALSE ) {
418
		if ( $reset_cache ) {
419
			$this->reset_cart();
420
			$this->reset_checkout();
421
			$this->reset_transaction();
422
		}
423
		 if ( ! empty( $key ))  {
424
			return  isset( $this->_session_data[ $key ] ) ? $this->_session_data[ $key ] : NULL;
425
		}  else  {
426
			return $this->_session_data;
427
		}
428
	}
429
430
431
432
	 /**
433
	  * set session data
434
	  * @access 	public
435
	  * @param 	array $data
436
	  * @return 	TRUE on success, FALSE on fail
437
	  */
438
	public function set_session_data( $data ) {
439
440
		// nothing ??? bad data ??? go home!
441 View Code Duplication
		if ( empty( $data ) || ! is_array( $data )) {
442
			EE_Error::add_error( __( 'No session data or invalid session data was provided.', 'event_espresso' ), __FILE__, __FUNCTION__, __LINE__ );
443
			return FALSE;
444
		}
445
446
		foreach ( $data as $key =>$value ) {
447
			if ( isset( $this->_default_session_vars[ $key ] )) {
448
				EE_Error::add_error( sprintf( __( 'Sorry! %s is a default session datum and can not be reset.', 'event_espresso' ), $key ), __FILE__, __FUNCTION__, __LINE__ );
449
				return FALSE;
450
			} else {
451
				$this->_session_data[ $key ] = $value;
452
			}
453
		}
454
455
		return TRUE;
456
457
	}
458
459
460
461
	 /**
462
	  * @initiate session
463
	  * @access   private
464
	  * @return TRUE on success, FALSE on fail
465
	  * @throws \EventEspresso\core\exceptions\InvalidSessionDataException
466
	  * @throws \EE_Error
467
	  */
468
	private function _espresso_session() {
469
		do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
470
		// check that session has started
471
		if ( session_id() === '' ) {
472
			//starts a new session if one doesn't already exist, or re-initiates an existing one
473
			session_start();
474
		}
475
		// get our modified session ID
476
		$this->_sid = $this->_generate_session_id();
477
		// and the visitors IP
478
		$this->_ip_address = $this->_visitor_ip();
479
		// set the "user agent"
480
		$this->_user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? esc_attr( $_SERVER['HTTP_USER_AGENT'] ) : FALSE;
481
		// now let's retrieve what's in the db
482
        $session_data = $this->_retrieve_session_data();
483
        if (! empty($session_data)) {
484
            // get the current time in UTC
485
			$this->_time = isset( $this->_time ) ? $this->_time : time();
486
			// and reset the session expiration
487
			$this->_expiration = isset( $session_data['expiration'] )
488
				? $session_data['expiration']
489
				: $this->_time + $this->_lifespan;
490
		} else {
491
            // set initial site access time and the session expiration
492
			$this->_set_init_access_and_expiration();
493
			// set referer
494
			$this->_session_data[ 'pages_visited' ][ $this->_session_data['init_access'] ] = isset( $_SERVER['HTTP_REFERER'] )
495
				? esc_attr( $_SERVER['HTTP_REFERER'] )
496
				: '';
497
			// no previous session = go back and create one (on top of the data above)
498
			return FALSE;
499
		}
500
        // now the user agent
501
		if ( $session_data['user_agent'] !== $this->_user_agent ) {
502
			return FALSE;
503
		}
504
		// wait a minute... how old are you?
505
		if ( $this->_time > $this->_expiration ) {
506
			// yer too old fer me!
507
            $this->_expired = true;
508
			// wipe out everything that isn't a default session datum
509
			$this->clear_session( __CLASS__, __FUNCTION__ );
510
		}
511
		// make event espresso session data available to plugin
512
		$this->_session_data = array_merge( $this->_session_data, $session_data );
513
		return TRUE;
514
515
	}
516
517
518
519
     /**
520
      * _get_session_data
521
      * Retrieves the session data, and attempts to correct any encoding issues that can occur due to improperly setup databases
522
      *
523
      * @return array
524
      * @throws \EventEspresso\core\exceptions\InvalidSessionDataException
525
      */
526
     protected function _retrieve_session_data()
527
     {
528
         $ssn_key = EE_Session::session_id_prefix . $this->_sid;
529
         try {
530
             // we're using WP's Transient API to store session data using the PHP session ID as the option name
531
             $session_data = $this->cache_storage->get($ssn_key, false);
532
	         if (empty($session_data)) {
533
		         return array();
534
             }
535
             if (apply_filters('FHEE__EE_Session___perform_session_id_hash_check', WP_DEBUG)) {
536
                 $hash_check = $this->cache_storage->get(
537
                     EE_Session::hash_check_prefix . $this->_sid,
538
                     false
539
                 );
540 View Code Duplication
                 if ($hash_check && $hash_check !== md5($session_data)) {
541
	                 EE_Error::add_error(
542
                         sprintf(
543
                             __(
544
                                 'The stored data for session %1$s failed to pass a hash check and therefore appears to be invalid.',
545
                                 'event_espresso'
546
                             ),
547
                             EE_Session::session_id_prefix . $this->_sid
548
                         ),
549
                         __FILE__, __FUNCTION__, __LINE__
550
                     );
551
                 }
552
             }
553
         } catch (Exception $e) {
554
             // let's just eat that error for now and attempt to correct any corrupted data
555
             global $wpdb;
556
             $row = $wpdb->get_row(
557
                 $wpdb->prepare(
558
                     "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1",
559
                     '_transient_' . $ssn_key
560
                 )
561
             );
562
             $session_data = is_object($row) ? $row->option_value : null;
563
             if ($session_data) {
564
                 $session_data = preg_replace_callback(
565
                     '!s:(d+):"(.*?)";!',
566 View Code Duplication
                     function ($match) {
567
                         return $match[1] === strlen($match[2])
568
                             ? $match[0]
569
                             : 's:' . strlen($match[2]) . ':"' . $match[2] . '";';
570
                     },
571
                     $session_data
572
                 );
573
             }
574
	         $session_data = maybe_unserialize($session_data);
575
         }
576
	     // in case the data is encoded... try to decode it
577
         $session_data = $this->encryption instanceof EE_Encryption
578
             ? $this->encryption->base64_string_decode($session_data)
579
             : $session_data;
580
581
         if ( ! is_array($session_data)) {
582
             try {
583
	             $session_data = maybe_unserialize($session_data);
584
             } catch (Exception $e) {
585
                 $msg = esc_html__(
586
                     'An error occurred while attempting to unserialize the session data.',
587
                     'event_espresso'
588
                 );
589
                 $msg .= WP_DEBUG
590
                     ? '<br><pre>' . print_r($session_data, true) . '</pre><br>' . $this->find_serialize_error($session_data)
591
                     : '';
592
                 throw new InvalidSessionDataException($msg, 0, $e);
593
             }
594
         }
595
         // just a check to make sure the session array is indeed an array
596
         if ( ! is_array($session_data)) {
597
             // no?!?! then something is wrong
598
             $msg = esc_html__(
599
                 'The session data is missing, invalid, or corrupted.',
600
                 'event_espresso'
601
             );
602
             $msg .= WP_DEBUG
603
                 ? '<br><pre>' . print_r($session_data, true) . '</pre><br>' . $this->find_serialize_error($session_data)
604
                 : '';
605
	         throw new InvalidSessionDataException($msg);
606
         }
607 View Code Duplication
	     if ( isset($session_data['transaction'] ) && absint($session_data['transaction'] ) !== 0 ) {
608
             $session_data['transaction'] = EEM_Transaction::instance()->get_one_by_ID(
609
                 $session_data['transaction']
610
	         );
611
	     }
612
         return $session_data;
613
     }
614
615
616
617
	 /**
618
	  * _generate_session_id
619
	  * Retrieves the PHP session id either directly from the PHP session,
620
	  * or from the $_REQUEST array if it was passed in from an AJAX request.
621
	  * The session id is then salted and hashed (mmm sounds tasty)
622
	  * so that it can be safely used as a $_REQUEST param
623
	  *
624
	  * @return string
625
	  */
626
	protected function _generate_session_id() {
627
		// check if the SID was passed explicitly, otherwise get from session, then add salt and hash it to reduce length
628
		if ( isset( $_REQUEST[ 'EESID' ] ) ) {
629
			$session_id = sanitize_text_field( $_REQUEST[ 'EESID' ] );
630
		} else {
631
			$session_id = md5( session_id() . get_current_blog_id() . $this->_get_sid_salt() );
632
		}
633
		return apply_filters( 'FHEE__EE_Session___generate_session_id__session_id', $session_id );
634
	}
635
636
637
638
	 /**
639
	  * _get_sid_salt
640
	  *
641
	  * @return string
642
	  */
643
	protected function _get_sid_salt() {
644
		// was session id salt already saved to db ?
645
		if ( empty( $this->_sid_salt ) ) {
646
			// no?  then maybe use WP defined constant
647
			if ( defined( 'AUTH_SALT' ) ) {
648
				$this->_sid_salt = AUTH_SALT;
649
			}
650
			// if salt doesn't exist or is too short
651
			if ( strlen( $this->_sid_salt ) < 32 ) {
652
				// create a new one
653
				$this->_sid_salt = wp_generate_password( 64 );
654
			}
655
			// and save it as a permanent session setting
656
			$session_settings = get_option( 'ee_session_settings' );
657
			$session_settings[ 'sid_salt' ] = $this->_sid_salt;
658
			update_option( 'ee_session_settings', $session_settings );
659
		}
660
		return $this->_sid_salt;
661
	}
662
663
664
665
	 /**
666
	  * _set_init_access_and_expiration
667
	  * @return void
668
	  */
669
	protected function _set_init_access_and_expiration() {
670
		$this->_time = time();
671
		$this->_expiration = $this->_time + $this->_lifespan;
672
		// set initial site access time
673
		$this->_session_data['init_access'] = $this->_time;
674
		// and the session expiration
675
		$this->_session_data['expiration'] = $this->_expiration;
676
	}
677
678
679
680
     /**
681
      * @update session data  prior to saving to the db
682
      * @access public
683
      * @param bool $new_session
684
      * @return TRUE on success, FALSE on fail
685
      * @throws \EE_Error
686
      */
687
	public function update( $new_session = FALSE ) {
688
		$this->_session_data = isset( $this->_session_data )
689
			&& is_array( $this->_session_data )
690
			&& isset( $this->_session_data['id'])
691
			? $this->_session_data
692
			: array();
693
		if ( empty( $this->_session_data )) {
694
			$this->_set_defaults();
695
		}
696
		$session_data = array();
697
		foreach ( $this->_session_data as $key => $value ) {
698
699
			switch( $key ) {
700
701
				case 'id' :
702
					// session ID
703
					$session_data['id'] = $this->_sid;
704
				break;
705
706
				case 'ip_address' :
707
					// visitor ip address
708
					$session_data['ip_address'] = $this->_visitor_ip();
709
				break;
710
711
				case 'user_agent' :
712
					// visitor user_agent
713
					$session_data['user_agent'] = $this->_user_agent;
714
				break;
715
716
				case 'init_access' :
717
					$session_data['init_access'] = absint( $value );
718
				break;
719
720
				case 'last_access' :
721
					// current access time
722
					$session_data['last_access'] = $this->_time;
723
				break;
724
725
				case 'expiration' :
726
					// when the session expires
727
					$session_data['expiration'] = ! empty( $this->_expiration )
728
						? $this->_expiration
729
						: $session_data['init_access'] + $this->_lifespan;
730
				break;
731
732
				case 'user_id' :
733
					// current user if logged in
734
					$session_data['user_id'] = $this->_wp_user_id();
735
				break;
736
737
				case 'pages_visited' :
738
					$page_visit = $this->_get_page_visit();
739
					if ( $page_visit ) {
740
						// set pages visited where the first will be the http referrer
741
						$this->_session_data[ 'pages_visited' ][ $this->_time ] = $page_visit;
742
						// we'll only save the last 10 page visits.
743
						$session_data[ 'pages_visited' ] = array_slice( $this->_session_data['pages_visited'], -10 );
744
					}
745
				break;
746
747
				default :
748
					// carry any other data over
749
					$session_data[$key] = $this->_session_data[$key];
750
751
			}
752
753
		}
754
755
		$this->_session_data = $session_data;
756
		// creating a new session does not require saving to the db just yet
757
		if ( ! $new_session ) {
758
			// ready? let's save
759
			if ( $this->_save_session_to_db() ) {
760
				return TRUE;
761
			} else {
762
				return FALSE;
763
			}
764
		}
765
		// meh, why not?
766
		return TRUE;
767
768
	}
769
770
771
772
     /**
773
      * @create session data array
774
      * @access public
775
      * @return bool
776
      * @throws \EE_Error
777
      */
778
	private function _create_espresso_session( ) {
779
		do_action( 'AHEE_log', __CLASS__, __FUNCTION__, '' );
780
		// use the update function for now with $new_session arg set to TRUE
781
		return  $this->update( TRUE ) ? TRUE : FALSE;
782
	}
783
784
785
786
	 /**
787
	  * _save_session_to_db
788
	  *
789
	  * @access public
790
	  * @return string
791
	  * @throws \EE_Error
792
	  */
793
	private function _save_session_to_db() {
794
		if (
795
			// if the current request is NOT one of the following
796
			! (
797
                // an an AJAX request from the frontend
798
                EE_Registry::instance()->REQ->front_ajax
799
                || (
800
                    // OR an admin request that is NOT AJAX
801
					! ( defined( 'DOING_AJAX' ) && DOING_AJAX )
802
                    && is_admin()
803
				)
804
                || (
805
                    // OR an espresso page
806
                    EE_Registry::instance()->REQ instanceof EE_Request_Handler
807
                    && EE_Registry::instance()->REQ->is_espresso_page()
808
                )
809
            )
810
		) {
811
			return false;
812
		}
813
		$transaction = $this->transaction();
814
		if ( $transaction instanceof EE_Transaction ) {
815
			if ( ! $transaction->ID() ) {
816
				$transaction->save();
817
			}
818
			$this->_session_data['transaction'] = $transaction->ID();
819
		}
820
        // then serialize all of our session data
821
		$session_data = serialize($this->_session_data);
822
		// do we need to also encode it to avoid corrupted data when saved to the db?
823
		$session_data = $this->_use_encryption
824
            ? $this->encryption->base64_string_encode( $session_data )
825
            : $session_data;
826
		// maybe save hash check
827
		if ( apply_filters( 'FHEE__EE_Session___perform_session_id_hash_check', WP_DEBUG ) ) {
828
            $this->cache_storage->add(
829
                EE_Session::hash_check_prefix . $this->_sid,
830
                md5($session_data),
831
                $this->_lifespan
832
            );
833
        }
834
        // we're using the Transient API for storing session data,
835
        return $this->cache_storage->add(
836
            EE_Session::session_id_prefix . $this->_sid,
837
            $session_data,
838
            $this->_lifespan
839
        );
840
    }
841
842
843
844
845
846
	/**
847
	 * _visitor_ip
848
	 *	attempt to get IP address of current visitor from server
849
	 * plz see: http://stackoverflow.com/a/2031935/1475279
850
	 *
851
	 *	@access public
852
	 *	@return string
853
	 */
854 View Code Duplication
	private function _visitor_ip() {
855
		$visitor_ip = '0.0.0.0';
856
		$server_keys = array(
857
			'HTTP_CLIENT_IP',
858
			'HTTP_X_FORWARDED_FOR',
859
			'HTTP_X_FORWARDED',
860
			'HTTP_X_CLUSTER_CLIENT_IP',
861
			'HTTP_FORWARDED_FOR',
862
			'HTTP_FORWARDED',
863
			'REMOTE_ADDR'
864
		);
865
		foreach ( $server_keys as $key ){
866
			if ( isset( $_SERVER[ $key ] )) {
867
				foreach ( array_map( 'trim', explode( ',', $_SERVER[ $key ] )) as $ip ) {
868
					if ( $ip === '127.0.0.1' || filter_var( $ip, FILTER_VALIDATE_IP ) !== FALSE ) {
869
						$visitor_ip = $ip;
870
					}
871
				}
872
			}
873
		}
874
		return $visitor_ip;
875
	}
876
877
878
879
880
881
	/**
882
	 *			@get the full page request the visitor is accessing
883
	 *		  	@access public
884
	 *			@return string
885
	 */
886
	public function _get_page_visit() {
887
		$page_visit = home_url('/') . 'wp-admin/admin-ajax.php';
888
		// check for request url
889
		if ( isset( $_SERVER['REQUEST_URI'] )) {
890
			$http_host = '';
891
			$page_id = '?';
892
			$e_reg = '';
893
			$request_uri = esc_url( $_SERVER['REQUEST_URI'] );
894
			$ru_bits = explode( '?', $request_uri );
895
			$request_uri = $ru_bits[0];
896
			// check for and grab host as well
897
			if ( isset( $_SERVER['HTTP_HOST'] )) {
898
				$http_host = esc_url( $_SERVER['HTTP_HOST'] );
899
			}
900
			// check for page_id in SERVER REQUEST
901
			if ( isset( $_REQUEST['page_id'] )) {
902
				// rebuild $e_reg without any of the extra parameters
903
				$page_id = '?page_id=' . esc_attr( $_REQUEST['page_id'] ) . '&amp;';
904
			}
905
			// check for $e_reg in SERVER REQUEST
906
			if ( isset( $_REQUEST['ee'] )) {
907
				// rebuild $e_reg without any of the extra parameters
908
				$e_reg = 'ee=' . esc_attr( $_REQUEST['ee'] );
909
			}
910
			$page_visit = rtrim( $http_host . $request_uri . $page_id . $e_reg, '?' );
911
		}
912
		return $page_visit !== home_url( '/wp-admin/admin-ajax.php' ) ? $page_visit : '';
913
914
	}
915
916
917
918
919
920
	/**
921
	 * 	@the current wp user id
922
	 * 	@access public
923
	 * 	@return int
924
	 */
925
	public function _wp_user_id() {
926
		// if I need to explain the following lines of code, then you shouldn't be looking at this!
927
		$this->_wp_user_id = get_current_user_id();
928
		return $this->_wp_user_id;
929
	}
930
931
932
933
     /**
934
      * Clear EE_Session data
935
      *
936
      * @access public
937
      * @param string $class
938
      * @param string $function
939
      * @return void
940
      * @throws \EE_Error
941
      */
942
	public function clear_session( $class = '', $function = '' ) {
943
		//echo '<h3 style="color:#999;line-height:.9em;"><span style="color:#2EA2CC">' . __CLASS__ . '</span>::<span style="color:#E76700">' . __FUNCTION__ . '( ' . $class . '::' . $function . '() )</span><br/><span style="font-size:9px;font-weight:normal;">' . __FILE__ . '</span>    <b style="font-size:10px;">  ' . __LINE__ . ' </b></h3>';
944
        do_action( 'AHEE_log', __FILE__, __FUNCTION__, 'session cleared by : ' . $class . '::' .  $function . '()' );
945
		$this->reset_cart();
946
		$this->reset_checkout();
947
		$this->reset_transaction();
948
		// wipe out everything that isn't a default session datum
949
		$this->reset_data( array_keys( $this->_session_data ));
950
		// reset initial site access time and the session expiration
951
		$this->_set_init_access_and_expiration();
952
		$this->_save_session_to_db();
953
	}
954
955
956
957
	 /**
958
	  * @resets all non-default session vars
959
	  * @access public
960
	  * @param array $data_to_reset
961
	  * @param bool  $show_all_notices
962
	  * @return TRUE on success, FALSE on fail
963
	  */
964
	public function reset_data( $data_to_reset = array(), $show_all_notices = FALSE ) {
965
		// if $data_to_reset is not in an array, then put it in one
966
		if ( ! is_array( $data_to_reset ) ) {
967
			$data_to_reset = array ( $data_to_reset );
968
		}
969
		// nothing ??? go home!
970
		if ( empty( $data_to_reset )) {
971
			EE_Error::add_error( __( 'No session data could be reset, because no session var name was provided.', 'event_espresso' ), __FILE__, __FUNCTION__, __LINE__ );
972
			return FALSE;
973
		}
974
		$return_value = TRUE;
975
		// since $data_to_reset is an array, cycle through the values
976
		foreach ( $data_to_reset as $reset ) {
977
978
			// first check to make sure it is a valid session var
979
			if ( isset( $this->_session_data[ $reset ] )) {
980
				// then check to make sure it is not a default var
981
				if ( ! array_key_exists( $reset, $this->_default_session_vars )) {
982
					// remove session var
983
					unset( $this->_session_data[ $reset ] );
984
					if ( $show_all_notices ) {
985
						EE_Error::add_success( sprintf( __( 'The session variable %s was removed.', 'event_espresso' ), $reset ), __FILE__, __FUNCTION__, __LINE__ );
986
					}
987
					$return_value = !isset($return_value) ? TRUE : $return_value;
988
989 View Code Duplication
				} else {
990
					// yeeeeeeeeerrrrrrrrrrr OUT !!!!
991
					if ( $show_all_notices ) {
992
						EE_Error::add_error( sprintf( __( 'Sorry! %s is a default session datum and can not be reset.', 'event_espresso' ), $reset ), __FILE__, __FUNCTION__, __LINE__ );
993
					}
994
					$return_value = FALSE;
995
				}
996
997 View Code Duplication
			} else if ( $show_all_notices ) {
998
				// oops! that session var does not exist!
999
				EE_Error::add_error( sprintf( __( 'The session item provided, %s, is invalid or does not exist.', 'event_espresso' ), $reset ), __FILE__, __FUNCTION__, __LINE__ );
1000
				$return_value = FALSE;
1001
			}
1002
1003
		} // end of foreach
1004
1005
		return $return_value;
1006
1007
	}
1008
1009
1010
1011
     /**
1012
      *   wp_loaded
1013
      *
1014
      * @access public
1015
      * @throws \EE_Error
1016
      */
1017
	public function wp_loaded() {
1018
		if ( isset(  EE_Registry::instance()->REQ ) && EE_Registry::instance()->REQ->is_set( 'clear_session' )) {
1019
			$this->clear_session( __CLASS__, __FUNCTION__ );
1020
		}
1021
	}
1022
1023
1024
1025
     /**
1026
      * Used to reset the entire object (for tests).
1027
      *
1028
      * @since 4.3.0
1029
      * @throws \EE_Error
1030
      */
1031
	public function reset_instance() {
1032
		$this->clear_session();
1033
		self::$_instance = NULL;
1034
	}
1035
1036
1037
1038
     public function configure_garbage_collection_filters()
1039
     {
1040
         // run old filter we had for controlling session cleanup
1041
         $expired_session_transient_delete_query_limit = absint(
1042
             apply_filters(
1043
                 'FHEE__EE_Session__garbage_collection___expired_session_transient_delete_query_limit',
1044
                 50
1045
             )
1046
         );
1047
         // is there a value? or one that is different than the default 50 records?
1048
         if ($expired_session_transient_delete_query_limit === 0) {
1049
             // hook into TransientCacheStorage in case Session cleanup was turned off
1050
             add_filter('FHEE__TransientCacheStorage__transient_cleanup_schedule', '__return_zero');
1051
         } else if ($expired_session_transient_delete_query_limit !== 50) {
1052
             // or use that for the new transient cleanup query limit
1053
             add_filter(
1054
                 'FHEE__TransientCacheStorage__clearExpiredTransients__limit',
1055
                 function () use ($expired_session_transient_delete_query_limit) {
1056
                     return $expired_session_transient_delete_query_limit;
1057
                 }
1058
             );
1059
         }
1060
     }
1061
1062
1063
1064
	 /**
1065
	  * @see http://stackoverflow.com/questions/10152904/unserialize-function-unserialize-error-at-offset/21389439#10152996
1066
	  * @param $data1
1067
	  * @return string
1068
	  */
1069
	 private function find_serialize_error( $data1 ) {
1070
		$error = '<pre>';
1071
		 $data2 = preg_replace_callback(
1072
			 '!s:(\d+):"(.*?)";!',
1073 View Code Duplication
			 function ( $match ) {
1074
				 return ( $match[1] === strlen( $match[2] ) )
1075
					 ? $match[0]
1076
					 : 's:'
1077
					   . strlen( $match[2] )
1078
					   . ':"'
1079
					   . $match[2]
1080
					   . '";';
1081
			 },
1082
			 $data1
1083
		 );
1084
		$max = ( strlen( $data1 ) > strlen( $data2 ) ) ? strlen( $data1 ) : strlen( $data2 );
1085
		$error .= $data1 . PHP_EOL;
1086
		$error .= $data2 . PHP_EOL;
1087
		for ( $i = 0; $i < $max; $i++ ) {
1088
			if ( @$data1[ $i ] !== @$data2[ $i ] ) {
1089
				$error .= 'Difference ' . @$data1[ $i ] . ' != ' . @$data2[ $i ] . PHP_EOL;
1090
				$error .= "\t-> ORD number " . ord( @$data1[ $i ] ) . ' != ' . ord( @$data2[ $i ] ) . PHP_EOL;
1091
				$error .= "\t-> Line Number = $i" . PHP_EOL;
1092
				$start = ( $i - 20 );
1093
				$start = ( $start < 0 ) ? 0 : $start;
1094
				$length = 40;
1095
				$point = $max - $i;
1096
				if ( $point < 20 ) {
1097
					$rlength = 1;
1098
					$rpoint = -$point;
1099
				} else {
1100
					$rpoint = $length - 20;
1101
					$rlength = 1;
1102
				}
1103
				$error .= "\t-> Section Data1  = ";
1104
				$error .= substr_replace(
1105
					substr( $data1, $start, $length ),
1106
					"<b style=\"color:green\">{$data1[ $i ]}</b>",
1107
					$rpoint,
1108
					$rlength
1109
				);
1110
				$error .= PHP_EOL;
1111
				$error .= "\t-> Section Data2  = ";
1112
				$error .= substr_replace(
1113
					substr( $data2, $start, $length ),
1114
					"<b style=\"color:red\">{$data2[ $i ]}</b>",
1115
					$rpoint,
1116
					$rlength
1117
				);
1118
				$error .= PHP_EOL;
1119
			}
1120
		}
1121
		$error .= '</pre>';
1122
		return $error;
1123
	}
1124
1125
 }
1126
/* End of file EE_Session.class.php */
1127
/* Location: /includes/classes/EE_Session.class.php */
1128