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