Completed
Pull Request — master (#2165)
by Justin
05:43
created

WPSC_Cart   D

Complexity

Total Complexity 214

Size/Duplication

Total Lines 1297
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
dl 0
loc 1297
rs 4.4102
c 0
b 0
f 0
wmc 214
lcom 1
cbo 7

55 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A shopper_location_changing() 0 3 1
A update_location() 0 18 1
A wpsc_refresh_cart_items() 0 9 4
B validate_cart() 0 35 1
A clear_shipping_info() 0 13 1
C shipping_method_selected() 0 26 8
B shipping_quote_selected() 0 21 6
B shipping_info_empty() 0 12 10
C needs_shipping_recalc() 0 52 11
C get_shipping_method() 0 59 14
C get_shipping_option() 0 30 11
B update_shipping() 0 21 5
C get_tax_rate() 0 63 13
C set_item() 0 68 18
A edit_item() 0 18 4
A check_remaining_quantity() 0 15 3
A get_remaining_quantity() 0 3 1
A remove_item() 0 17 2
A empty_cart() 0 15 1
A clear_cache() 0 12 1
A submit_stock_claims() 0 4 1
A cleanup() 0 5 1
B calculate_total_price() 0 25 3
B calculate_subtotal() 0 23 6
A get_items() 0 22 3
A calculate_total_tax() 0 10 2
B calculate_total_weight() 0 16 5
A get_total_shipping_quantity() 0 10 3
A get_item_categories() 0 7 2
A get_item_category_ids() 0 7 2
B calculate_total_shipping() 0 16 5
A has_total_shipping_discount() 0 4 3
B calculate_base_shipping() 0 22 6
A calculate_per_item_shipping() 0 14 4
A uses_shipping() 0 20 4
A process_as_currency() 0 4 1
A save_to_db() 0 7 2
A empty_db() 0 5 1
A next_cart_item() 0 5 1
A the_cart_item() 0 6 2
A have_cart_items() 0 12 4
A rewind_cart_items() 0 6 2
A next_shipping_method() 0 5 1
A the_shipping_method() 0 4 1
A have_shipping_methods() 0 9 4
A rewind_shipping_methods() 0 10 2
B get_shipping_quotes() 0 22 6
A set_default_shipping_quote() 0 7 2
C google_shipping_quotes() 0 38 8
A next_shipping_quote() 0 5 1
A the_shipping_quote() 0 3 1
A have_shipping_quotes() 0 9 4
A rewind_shipping_quotes() 0 7 2
A apply_coupons() 0 15 2

How to fix   Complexity   

Complex Class

Complex classes like WPSC_Cart often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WPSC_Cart, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * WP eCommerce Cart and Cart Item classes
4
 *
5
 * This is the class for the WP eCommerce Cart ,
6
 * The Cart class handles adding, removing and adjusting items in the cart, and totaling up the cost of the items in the cart.
7
 *
8
 *
9
 * @package wp-e-commerce
10
 * @since 3.7
11
 * @subpackage wpsc-cart-classes
12
*/
13
14
/*
15
 * @since 3.8.14
16
 *
17
 * We are going to do a check to see if the cart template API include file has not been included. Pre 3.8.14 the
18
 * template API functions were in the cart.class.php file before the class definition.  In 3.8.14 the functions
19
 * are in a separate file that is included immediately before this file.  In the future we will want to have the option
20
 * of changing the order and classes may be included at a different point in the init sequence.
21
 *
22
 * If we find that a key function we expect to be present does not exist it tells is that this file has been
23
 * improperly included directly in outside code. We will give a doing it wrong message.
24
 *
25
 * So that backwards compatibility is preserved for 3.8.14 we also require_once the cart template API file.
26
 *
27
 */
28
if ( ! function_exists( 'wpsc_cart_need_to_recompute_shipping_quotes' ) ) {
29
	_wpsc_doing_it_wrong( 'cart.class.php', __( 'As of WPeC 3.8.14, a check is made to be sure that wpsc-includes\cart.class.php is not loaded directly by outside code. WPeC internals are likely to be re-organized going forward.  When this happens code that directly includes WPeC internal modules may fail.', 'wp-e-commerce' ), '3.8.14' );
30
}
31
32
require_once( WPSC_FILE_PATH . '/wpsc-includes/cart-template-api.php' );
33
34
/**
35
 * The WPSC Cart class
36
 */
37
class WPSC_Cart {
38
	public $delivery_country;
39
	public $selected_country;
40
	public $delivery_region;
41
	public $selected_region;
42
43
	public $selected_shipping_method = null;
44
	public $selected_shipping_option = null;
45
	public $shipping_option          = null;
46
	public $selected_shipping_amount = null;
47
48
	public $coupon;
49
	public $tax_percentage;
50
	public $unique_id;
51
	public $errors;
52
53
	// caching of frequently used values, these are wiped when the cart is modified and then remade when needed
54
	public $total_tax           = null;
55
	public $base_shipping       = null;
56
	public $total_item_shipping = null;
57
	public $total_shipping      = null;
58
	public $subtotal            = null;
59
	public $total_price         = null;
60
	public $uses_shipping       = null;
61
62
	public $is_incomplete = true;
63
64
	// The cart loop variables
65
	public $cart_items        = array();
66
	public $cart_item         = null;
67
	public $cart_item_count   = 0;
68
	public $current_cart_item = -1;
69
	public $in_the_loop       = false;
70
71
	// The shipping method loop variables
72
	public $shipping_methods        = array();
73
	public $shipping_method         = null;
74
	public $shipping_method_count   = 0;
75
	public $current_shipping_method = -1;
76
	public $in_the_method_loop      = false;
77
78
	// The shipping quote loop variables
79
	public $shipping_quotes        = array();
80
	public $shipping_quote         = null;
81
	public $shipping_quote_count   = 0;
82
	public $current_shipping_quote = -1;
83
	public $in_the_quote_loop      = false;
84
85
	//coupon variable
86
	public $coupons_name   = '';
87
	public $coupons_amount = 0;
88
89
	// Cart signature, a hash indicating uniqueness of cart
90
	public $_signature = '';
91
92
93
    public function __construct() {
94
		$coupon = 'percentage';
95
		$this->update_location();
96
		$this->wpsc_refresh_cart_items();
97
		$this->unique_id = sha1( uniqid( rand(), true ) );
98
99
		add_action( 'wpsc_visitor_location_changing', array( &$this, 'shopper_location_changing' ), 10, 2);
100
		add_filter( 'wpsc_default_shipping_quote', array ($this, 'set_default_shipping_quote' ), 10, 3 );
0 ignored issues
show
introduced by
There must be no space between the Array keyword and the opening parenthesis
Loading history...
101
    }
102
103
    /*
104
     * Action routine to start the processing that has to happen when the customer changes
105
     * location.
106
     *
107
     * @since 3.8.14
108
     * @param array names of checnout items that hav changed since the last time the location for this customer was changed
109
     *
110
     */
111
  	function shopper_location_changing( $what_changed, $visitor_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $what_changed is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $visitor_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
112
  		$this->update_location();
113
  	}
114
115
	/**
116
	 * update_location method, updates the location
117
     * @access public
118
     */
119
120
	public function update_location() {
121
122
		$this->clear_cache();
123
124
		$delivery_country = wpsc_get_customer_meta( 'shippingcountry' );
125
		$billing_country  = wpsc_get_customer_meta( 'billingcountry'  );
126
		$delivery_region  = wpsc_get_customer_meta( 'shippingregion'  );
127
		$billing_region   = wpsc_get_customer_meta( 'billingregion'   );
128
129
		$this->delivery_country = $delivery_country;
130
		$this->selected_country = $billing_country ;
131
		$this->delivery_region  = $delivery_region ;
132
		$this->selected_region  = $billing_region;
133
134
		// adding refresh item
135
136
		$this->wpsc_refresh_cart_items();
137
	}
138
139
	/**
140
    * @description: refresh all items in the cart
141
    *
142
    * @param: void
143
    * @return: null
144
    **/
145
	public function wpsc_refresh_cart_items() {
146
		global $wpsc_cart;
147
148
		if ( is_object( $wpsc_cart ) && is_array( $wpsc_cart->cart_items ) ) {
149
			foreach ( $wpsc_cart->cart_items as $cart_item ) {
150
				$cart_item->refresh_item();
151
			}
152
		}
153
   }
154
155
   /*
156
    * It os time to checkout, or at other points in the workflow and it's time to validate the shopping cart
157
    * call this function.
158
    *
159
    * The function will in turn execute all of the hooks that are built into WPEC, then any hooks added by
160
    * themes and plugins.  This means that validation rules beyond what WPEC has internally can be added as needed.
161
    */
162
   function validate_cart() {
163
164
   		/*
165
   		 * action: wpsc_pre_validate_cart
166
   		 *
167
   		 * Prior to validating the cart we give anyone whoe is interested a chance to do a little setup with this
168
   		 * wpsc_pre_validate_cart.
169
   		 *
170
   		 * This action can be used as a convenient point to change the logic that is esecuted when the 'wpsc_validate_cart'
171
   		 * action is fired.  For example, if you want to do different address checks based on which country is being shipped
172
   		 * to you can call add_action with different function paramters.  Or if you wnated to some extra validation when shipping
173
   		 * address is differnet than billing, perhaps a quick SOAP call to a fraud check service, you can conditionally do an
174
   		 * add action to your function that does the fraud check.
175
   		 *
176
   		 * @param wpsc_cart the cart object
177
   		 * @param current visitor id (use this to get customer meta for the current user
178
   		 */
179
   		do_action( 'wpsc_pre_validate_cart', $this, wpsc_get_current_customer_id() );
180
181
   		/*
182
 		 * action: wpsc_validate_cart
183
   		 *
184
   		 * Validate that the cart contents is valid.  Typically done just prior to checkout.  Most often error conditions
185
   		 * will be recorded to special customer meta values, but other processing can be implemented based on specific needs
186
   		 *
187
   		 * These are the customer/visitor meta values that are typically added to when errors are found:
188
   		 * 			checkout_error_messages
189
   		 * 			gateway_error_messages
190
   		 * 			registration_error_messages
191
   		 *
192
   		 * @param wpsc_cart the cart object
193
   		 * @param current visitor id (use this to get customer meta for the current user
194
   		 */
195
   		do_action( 'wpsc_validate_cart', $this, wpsc_get_current_customer_id() );
196
	}
197
198
	/**
199
	 * Clear all shipping method information for this cart
200
	 *
201
	 * @since 3.8.14
202
	 *
203
	 */
204
	function clear_shipping_info() {
205
		$this->selected_shipping_method = null;
206
		$this->selected_shipping_option = null;
207
		$this->shipping_option          = null;
208
		$this->shipping_method          = null;
209
		$this->shipping_methods         = array();
210
		$this->shipping_quotes          = array();
211
		$this->shipping_quote           = null;
212
		$this->shipping_method_count    = 0;
213
		$this->base_shipping            = null;
214
		$this->total_item_shipping      = null;
215
		$this->total_shipping           = null;
216
	}
217
218
	/**
219
	 * Does the cart have a valid shipping method selected
220
	 *
221
	 * @since 3.8.14.1
222
	 *
223
	 * @return boolean true if a valid shipping method is selected, false otherwise
224
	 */
225
	function shipping_method_selected() {
226
227
		$selected = true;
228
229
		// so the check could be written as one long expression, but thougth it better to make it more
230
		// readily understandable by someone who wants to see what is happening.
231
		// TODO:  All this logic would be unnecessary but for the lack of protected properties and
232
		// the legacy code that may choose to manipulate them directly avoiding class methods
233
234
		// is there a shipping method?
235
		if ( empty( $this->selected_shipping_method ) ) {
236
			$selected = false;
237
		}
238
239
		// first we will check the shipping methods
240
		if ( $selected && ( ! is_array( $this->shipping_methods ) || empty( $this->shipping_methods ) ) ) {
241
			$selected = false;
242
		}
243
244
		// check to be sure the shipping method name is not empty, and is also in the array
245
		if ( $selected && ( empty( $this->selected_shipping_method ) || ! in_array( $this->selected_shipping_method, $this->shipping_methods ) ) ) {
246
			$selected = false;
247
		}
248
249
		return $selected;
250
	}
251
252
	/**
253
	 * Does the cart have a valid shipping quote selected
254
	 *
255
	 * @since 3.8.14.1
256
	 *
257
	 * @return boolean true if a valid shipping method is selected, false otherwise
258
	 */
259
	function shipping_quote_selected() {
260
261
		$selected = true;
262
263
		// so the check could be written as one long expression, but thought it better to make it more
264
		// readily understandable by someone who wants to see what is happening.
265
		// TODO:  All this logic would be unnecessary but for the lack of protected properties and
266
		// the legacy code that may choose to manipulate them directly avoiding class methods
267
268
		// do we have a shipping quotes array
269
		if ( $selected && ( ! is_array( $this->shipping_quotes ) || empty( $this->shipping_quotes ) ) ) {
270
			$selected = false;
271
		}
272
273
		if ( $selected && ! isset( $this->shipping_quotes[$this->selected_shipping_option] )  ) {
274
			$selected = false;
275
		}
276
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
277
278
		return $selected;
279
	}
280
281
282
	/**
283
	 * Is all shipping method information for this cart empty
284
	 *
285
	 * @since 3.8.14
286
	 * @return boolean true if all the shipping fields in the cart are empty
287
	 */
288
	function shipping_info_empty() {
289
		return empty( $this->selected_shipping_method )
290
					&& empty( $this->selected_shipping_option )
291
							&& empty( $this->shipping_method )
292
								&& empty( $this->shipping_methods )
293
									&& empty( $this->shipping_quotes )
294
										&& empty( $this->shipping_quote )
295
											&& empty( $this->shipping_method_count )
296
												&& empty( $this->base_shipping )
297
													&& empty( $this->total_item_shipping )
298
														&& empty( $this->total_shipping );
299
	}
300
301
	/**
302
	 * Is shipping information calculated and ready to use
303
	 *
304
	 * @since 3.8.14
305
	 * @return boolean true if a recalc is necessary
306
	 */
307
	function needs_shipping_recalc() {
308
		global $wpsc_shipping_modules;
309
310
		if ( ! wpsc_is_shipping_enabled() ) {
311
			return false;
312
		}
313
314
		if ( $this->shipping_info_empty() && $this->uses_shipping() ) {
315
			return true;
316
		}
317
318
		$needs_shipping_recalc = false;
319
320
		$what_changed = _wpsc_visitor_location_what_changed();
321
322
		// TODO: this is where we will check the available shipping methods and see if
323
		// the parameters used to create the quotes have changes since the quotes where
324
		// created.  A function of the future shipping component
325
326
		if ( array_key_exists( 'shippingpostcode', $what_changed ) ) {
327
			$custom_shipping = get_option( 'custom_shipping_options' );
328
			foreach ( (array)$custom_shipping as $shipping ) {
329
				if ( isset( $wpsc_shipping_modules[$shipping]->needs_zipcode ) && $wpsc_shipping_modules[$shipping]->needs_zipcode == true ) {
330
					$needs_shipping_recalc = true;
331
					break;
332
				}
333
			}
334
		}
335
336
		// recalculate shipping if country changes
337
		if ( array_key_exists( 'shippingcountry', $what_changed ) ) {
338
			$needs_shipping_recalc = true;
339
		}
340
341
		// recalculate shipping if region changes
342
		if ( array_key_exists( 'shippingregion', $what_changed ) ) {
343
			$needs_shipping_recalc = true;
344
		}
345
346
		// recalculate shipping if state
347
		if ( array_key_exists( 'shippingstate', $what_changed ) ) {
348
			$needs_shipping_recalc = true;
349
		}
350
351
		/*
352
		 * filter: wpsc_needs_shipping_recalc
353
		 *
354
		 * If more processing is needed to determine if shipping really needs to
355
		 * be recalculated it should be hooked on here.
356
		 */
357
		return apply_filters( 'wpsc_needs_shipping_recalc', $needs_shipping_recalc, $this );
358
	}
359
360
	/**
361
	 * get_shipping_rates method, gets the shipping rates
362
     * @access public
363
     */
364
	function get_shipping_method() {
365
		global $wpsc_shipping_modules;
366
367
		// set us up with a shipping method.
368
		$custom_shipping = get_option( 'custom_shipping_options', array() );
369
370
		if ( ! is_array( $custom_shipping ) ) {
371
			$custom_shipping = (array) $custom_shipping;
372
		}
373
374
		$this->shipping_methods      = $custom_shipping;
375
		$this->shipping_method_count = count( $this->shipping_methods );
376
377
		$use_shipping = ! get_option( 'do_not_use_shipping', false );
378
		$ready_to_calculate_shipping = apply_filters( 'wpsc_ready_to_calculate_shipping', true, $this );
379
380
		if ( $use_shipping ) {
381
382
			if ( $this->shipping_method_count > 0 && $ready_to_calculate_shipping ) {
383
				do_action( 'wpsc_before_get_shipping_method', $this );
384
385
				$shipping_quotes = null;
386
387
				foreach ( (array) $custom_shipping as $shipping_module ) {
388
389
					if ( empty( $wpsc_shipping_modules[ $shipping_module ] ) || ! is_callable( array( $wpsc_shipping_modules[ $shipping_module ], 'getQuote' ) ) ) {
390
						continue;
391
					}
392
393
					$raw_quotes = $wpsc_shipping_modules[ $shipping_module ]->getQuote();
394
395
					if ( empty( $raw_quotes ) || ! is_array( $raw_quotes ) ) {
396
						continue;
397
					}
398
399
					if ( is_array( $raw_quotes ) ) {
400
						foreach ( $raw_quotes as $key => $value ) {
401
							$this->shipping_quotes[$wpsc_shipping_modules[ $shipping_module ]->name. ' ' . $key] = $value;
402
						}
403
						$this->shipping_quote_count = count( $this->shipping_quotes );
404
					}
405
				}
406
407
				if ( 1 == count( $this->shipping_methods ) ) {
408
					$this->selected_shipping_method = $this->shipping_methods[0];
409
410
					if ( 1 == count( $this->shipping_quotes ) ) {
411
						reset( $this->shipping_quotes );
412
						$this->selected_shipping_option = key( $this->shipping_quotes );
413
					}
414
				}
415
416
				do_action( 'wpsc_after_get_shipping_method', $this );
417
			}
418
		}
419
420
		$this->rewind_shipping_methods();
421
422
	}
423
424
	/**
425
	 * get_shipping_option method, gets the shipping option from the selected method and associated quotes
426
	 *
427
	 * @access public
428
	 *
429
	 * @return none
430
	 */
431
	function get_shipping_option() {
432
		global $wpdb, $wpsc_shipping_modules;
433
434
		if ( ( count( $this->shipping_quotes ) < 1 ) &&
435
		     isset( $wpsc_shipping_modules[$this->selected_shipping_method] ) &&
436
		     is_callable( array( $wpsc_shipping_modules[$this->selected_shipping_method], 'getQuote' ) ) ) {
437
			$this->shipping_quotes = $wpsc_shipping_modules[$this->selected_shipping_method]->getQuote();
438
		}
439
440
		if ( ! isset( $wpsc_shipping_modules[$this->selected_shipping_method] ) ) {
441
			$this->selected_shipping_option = null;
442
		}
443
444
		if ( count( $this->shipping_quotes ) < 1 ) {
445
			$this->selected_shipping_option = null;
446
		}
447
448
		// if the current shipping option is not valid, go back to no shipping option
449
		if ( ! empty( $this->selected_shipping_option ) && ! isset( $this->shipping_quotes[$this->selected_shipping_option] ) ) {
450
			$this->selected_shipping_option = null;
451
		}
452
453
		// let the world pick a default shipping quote
454
		if (  empty( $this->selected_shipping_option ) && is_array( $this->shipping_quotes ) && ! empty( $this->shipping_quotes ) ) {
455
			$this->selected_shipping_option = apply_filters( 'wpsc_default_shipping_quote', $this->selected_shipping_option, $this->shipping_quotes, $this );
456
		}
457
458
		$this->rewind_shipping_methods();
459
460
	}
461
462
	/**
463
	 * update_shipping method, updates the shipping
464
	 * @access public
465
	 */
466
	function update_shipping( $method, $option ) {
467
		global $wpdb, $wpsc_shipping_modules;
468
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
469
470
		if ( ! empty( $method ) ) {
471
			$this->selected_shipping_method = $method;
472
		}
473
474
		if ( ! empty( $option ) ) {
475
			$this->selected_shipping_option = $option;
476
		}
477
478
		$this->get_shipping_quotes();
479
480
		// reapply coupon in case it's free shipping
481
		if ( $this->coupons_name ) {
482
			$coupon = new wpsc_coupons( $this->coupons_name );
483
			if ( $coupon->is_free_shipping() )
484
				$this->apply_coupons( $coupon->calculate_discount(), $this->coupons_name );
485
		}
486
	}
487
488
	/**
489
	 * get_tax_rate method, gets the tax rate as a percentage, based on the selected country and region
490
	 * * EDIT: Replaced with WPEC Taxes - this function should probably be deprecated
491
	 * Note: to refresh cart items use wpsc_refresh_cart_items
492
	 *
493
	 * @access public
494
	 */
495
	function get_tax_rate() {
496
		$country = new WPSC_Country( get_option( 'base_country' ) );
497
498
		$country_data = WPSC_Countries::get_country( get_option( 'base_country' ), true );
499
		$add_tax = false;
500
501
		if ( $this->selected_country == get_option( 'base_country' ) ) {
502
			// Tax rules for various countries go here, if your countries tax rules
503
			// deviate from this, please supply code to add your region
504
			switch ( $this->selected_country ) {
505
				case 'US' : // USA!
506
					$tax_region = get_option( 'base_region' );
507
					if ( $this->selected_region == get_option( 'base_region' ) && ( get_option( 'lock_tax_to_shipping' ) != '1' ) ) {
508
						// if they in the state, they pay tax
509
						$add_tax = true;
510
					} else if ( $this->delivery_region == get_option( 'base_region' ) ) {
511
512
						// if they live outside the state, but are delivering to within the state, they pay tax also
513
						$add_tax = true;
514
					}
515
					break;
516
517
				case 'CA' : // Canada! apparently in canada, the region that you are in is used for tax purposes
518
					if ( $this->selected_region != null ) {
519
						$tax_region = $this->selected_region;
520
					} else {
521
						$tax_region = get_option( 'base_region' );
522
					}
523
524
					$add_tax = true;
525
					break;
526
527
				default : // Everywhere else!
528
					$tax_region = get_option( 'base_region' );
529
					if ( $country->has_regions() ) {
530
						if ( get_option( 'base_region' ) == $tax_region ) {
531
							$add_tax = true;
532
						}
533
					} else {
534
						$add_tax = true;
535
					}
536
					break;
537
			}
538
		}
539
540
		if ( $add_tax == true ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
541
			if ( $country->has_regions() ) {
542
				$region = $country->get_region( $tax_region );
0 ignored issues
show
Bug introduced by
The variable $tax_region does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
543
				$tax_percentage = $region->get_tax();
0 ignored issues
show
Bug introduced by
The method get_tax cannot be called on $region (of type string|false).

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

Loading history...
544
			} else {
545
				$tax_percentage = $country->get_tax();
546
			}
547
		} else {
548
			// no tax charged = tax equal to 0%
549
			$tax_percentage = 0;
550
		}
551
552
		if ( $this->tax_percentage != $tax_percentage ) {
553
			$this->clear_cache();
554
			$this->tax_percentage = $tax_percentage;
555
			$this->wpsc_refresh_cart_items();
556
		}
557
	}
558
559
	/**
560
	 * Set Item method, requires a product ID and the parameters for the product
561
	 *
562
	 * @access public
563
	 *
564
	 * @param integer the product ID
565
	 * @param array parameters
566
	 * @return boolean true on sucess, false on failure
567
	 */
568
	function set_item( $product_id, $parameters, $updater = false ) {
569
570
		// default action is adding
571
		$add_item        = false;
572
		$edit_item       = false;
573
		$variation_check = true;
574
575
		$parameters = wp_parse_args( $parameters, array(
576
			'quantity'         => 0,
577
			'variation_values' => null
578
		) );
579
580
		if ( wpsc_product_has_variations( $product_id ) && is_null( $parameters['variation_values'] ) ) {
581
			$variation_check = false;
582
		}
583
584
		if ( $variation_check && $parameters['quantity'] > 0 && $this->check_remaining_quantity( $product_id, $parameters['variation_values'], $parameters['quantity'] ) ) {
585
586
			$new_cart_item = new wpsc_cart_item( $product_id, $parameters, $this );
587
588
			do_action( 'wpsc_set_cart_item', $product_id, $parameters, $this, $new_cart_item );
589
590
			$add_item = true;
591
			$edit_item = false;
592
593
			if ( count( $this->cart_items ) > 0 && $new_cart_item->is_donation != 1 ) {
594
595
				// loop through each cart item
596
				foreach ( $this->cart_items as $key => $cart_item ) {
597
598
					// compare product ids and variations.
599
					if ( $cart_item->product_id == $new_cart_item->product_id && $cart_item->product_variations == $new_cart_item->product_variations && $cart_item->custom_message == $new_cart_item->custom_message && $cart_item->custom_file == $new_cart_item->custom_file && $cart_item->item_meta_equal( $new_cart_item ) ) {
600
601
						// if they are the same, increment the count, and break out;
602
						if ( ! $updater ) {
603
							$this->cart_items[$key]->quantity += $new_cart_item->quantity;
604
						} else {
605
							$this->cart_items[$key]->quantity = $new_cart_item->quantity;
606
						}
607
608
						$this->cart_items[$key]->refresh_item();
609
610
						$add_item = false;
611
						$edit_item = true;
612
613
						do_action( 'wpsc_edit_item', $product_id, $parameters, $this );
614
					}
615
				}
616
			}
617
618
			// if we are still adding the item, add it
619
			if ( $add_item ) {
620
				$this->cart_items[] = $new_cart_item;
621
				do_action( 'wpsc_add_item', $product_id, $parameters, $this );
622
			}
623
		}
624
625
		// if some action was performed, return true, otherwise, return false;
626
		$status = false;
627
		if ( $add_item || $edit_item ) {
628
			$status = $new_cart_item;
0 ignored issues
show
Bug introduced by
The variable $new_cart_item does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
629
		}
630
631
		$this->cart_item_count = count( $this->cart_items );
632
		$this->clear_cache();
633
634
		return $status;
635
	}
636
637
	/**
638
	 * Edit Item method
639
	 *
640
	 * @access public
641
	 *
642
	 * @param integer a cart_items key
643
	 * @param array an array of parameters to change
644
	 * @return boolean true on sucess, false on failure
645
	 */
646
	function edit_item( $key, $parameters ) {
647
		if ( isset( $this->cart_items[$key] ) ) {
648
			$product_id = $this->cart_items[$key]->product_id;
649
			$quantity = $parameters ['quantity'] - $this->cart_items[$key]->quantity;
650
			if ( $this->check_remaining_quantity( $product_id, $this->cart_items[$key]->variation_values, $quantity ) == true ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
651
				foreach ( $parameters as $name => $value ) {
652
					$this->cart_items[$key]->$name = $value;
653
				}
654
				$this->cart_items[$key]->refresh_item();
655
				do_action( 'wpsc_edit_item', $product_id, $parameters, $this, $key );
656
				$this->clear_cache();
657
			}
658
659
			return true;
660
		} else {
661
			return false;
662
		}
663
	}
664
665
	/**
666
	 * check remaining quantity method
667
	 * currently only checks remaining stock, in future will do claimed stock and quantity limits
668
	 * will need to return errors, then, rather than true/false, maybe use the wp_error object?
669
	 *
670
	 * @access public
671
	 *
672
	 * @param integer a product ID key
673
	 * @param array variations on the product
674
	 * @return boolean true on sucess, false on failure
675
	 */
676
	function check_remaining_quantity( $product_id, $variations = array(), $quantity = 1 ) {
677
678
		$stock = get_post_meta( $product_id, '_wpsc_stock', true );
679
		$stock = apply_filters( 'wpsc_product_stock', $stock, $product_id, $this );
680
681
		$result = true;
682
683
		if ( is_numeric( $stock ) ) {
684
			$remaining_quantity = wpsc_get_remaining_quantity( $product_id, $variations, $quantity );
685
			if ( $remaining_quantity < $quantity ) {
686
				$result = false;
687
			}
688
		}
689
		return $result;
690
	}
691
692
	/**
693
    * get remaining quantity method
694
    * currently only checks remaining stock, in future will do claimed stock and quantity limits
695
    * will need to return errors, then, rather than true/false, maybe use the wp_error object?
696
    * @access public
697
    *
698
    * @param integer a product ID key
699
    * @param array  variations on the product
700
    *
701
    * @return int Number of product remaining.
702
    */
703
	function get_remaining_quantity( $product_id, $variations = array(), $quantity = 1 ) {
704
		return wpsc_get_remaining_quantity( $product_id, $variations, $quantity );
705
	}
706
707
	/**
708
	 * Remove Item method
709
	 *
710
	 * @access public
711
	 *
712
	 * @param integer a cart_items key
713
	 * @return boolean true on sucess, false on failure
714
	 */
715
	function remove_item( $key ) {
716
		if ( isset( $this->cart_items[$key] ) ) {
717
			$cart_item = & $this->cart_items[$key];
718
			$cart_item->update_item( 0 );
719
			unset( $this->cart_items[$key] );
720
			$this->cart_items = array_values( $this->cart_items );
721
			$this->cart_item_count = count( $this->cart_items );
722
			$this->current_cart_item = - 1;
723
			do_action( 'wpsc_remove_item', $key, $this, $cart_item );
724
725
			$this->clear_cache();
726
			return true;
727
		} else {
728
			$this->clear_cache();
729
			return false;
730
		}
731
	}
732
733
	/**
734
	 * Empty Cart method
735
	 *
736
	 * @access public
737
	 *
738
	 *         No parameters, nothing returned
739
	 */
740
	function empty_cart( $fromwidget = true ) {
0 ignored issues
show
Unused Code introduced by
The parameter $fromwidget is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
741
		$claimed_query = new WPSC_Claimed_Stock( array(	'cart_id' => $this->unique_id ) );
742
		$claimed_query->clear_claimed_stock( 0 );
743
744
		$this->cart_items        = array();
745
		$this->cart_item         = null;
746
		$this->cart_item_count   = 0;
747
		$this->current_cart_item = - 1;
748
		$this->coupons_amount    = 0;
749
		$this->coupons_name      = '';
750
751
		$this->clear_cache();
752
		$this->cleanup();
753
		do_action( 'wpsc_clear_cart', $this );
754
	}
755
756
	/**
757
	 * Clear Cache method, used to clear the cached totals
758
	 *
759
	 * @access public
760
	 *
761
	 *         No parameters, nothing returned
762
	 */
763
	function clear_cache() {
764
		$this->total_tax = null;
765
		$this->base_shipping = null;
766
		$this->total_item_shipping = null;
767
		$this->total_shipping = null;
768
		$this->subtotal = null;
769
		$this->total_price = null;
770
		$this->uses_shipping = null;
771
		$this->shipping_quotes = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $shipping_quotes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
772
		$this->get_shipping_option();
773
		do_action( 'wpsc_after_cart_clear_cache', $this );
774
	}
775
776
	/**
777
	 * submit_stock_claims method, changes the association of the stock claims from the cart unique to the purchase log
778
	 * ID
779
	 *
780
	 * @access public
781
	 *
782
	 *         No parameters, nothing returned
783
	 */
784
	function submit_stock_claims( $purchase_log_id ) {
785
		$claimed_query = new WPSC_Claimed_Stock( array( 'cart_id' => $this->unique_id ) );
786
		$claimed_query->submit_claimed_stock( $purchase_log_id );
787
	}
788
789
	/**
790
	 * cleanup method, cleans up the cart just before final destruction
791
	 *
792
	 * @access public
793
	 *
794
	 *         No parameters, nothing returned
795
	 */
796
	function cleanup() {
797
		wpsc_delete_customer_meta( 'coupon' );
798
		$claimed_query = new WPSC_Claimed_Stock( array( 'cart_id' => $this->unique_id ) );
799
		$claimed_query->clear_claimed_stock( 0 );
800
	}
801
802
	/**
803
	 * Calculate total price method
804
	 *
805
	 * @access public
806
	 *
807
	 * @return float returns the price as a floating point value
808
	 */
809
	function calculate_total_price() {
810
811
		// Calculate individual component that comprise the cart total
812
		$subtotal = $this->calculate_subtotal();
813
		$shipping = $this->calculate_total_shipping();
814
815
		// Get tax only if it is included
816
		$tax = ( ! wpsc_tax_isincluded() ) ? $this->calculate_total_tax() : 0.00;
817
818
		// Get coupon amount, note that no matter what float precision this
819
		// coupon amount is, it's always saved to the database with rounded
820
		// value anyways
821
		$coupons_amount = round( $this->coupons_amount, 2 );
822
823
		// Calculate the total
824
		$total = ( $subtotal > $coupons_amount ) ? ( ( $subtotal - $coupons_amount ) + $shipping + $tax ) : ( $tax + $shipping );
825
826
		// Filter total
827
		$total = apply_filters( 'wpsc_calculate_total_price', $total, $subtotal, $shipping, $tax, $coupons_amount, $this );
828
829
		// Set variable and return
830
		$this->total_price = $total;
831
832
		return $total;
833
	}
834
835
	/**
836
	 * calculate_subtotal method
837
	 *
838
	 * @access public
839
	 *
840
	 * @param boolean for_shipping = exclude items with no shipping,
841
	 * @return float returns the price as a floating point value
842
	 */
843
	function calculate_subtotal( $for_shipping = false ) {
844
		global $wpdb;
845
		if ( $for_shipping == true ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
846
			$total = 0;
847
			foreach ( $this->cart_items as $key => $cart_item ) {
848
				if ( $cart_item->uses_shipping == 1 ) {
849
					$total += $cart_item->total_price;
850
				}
851
			}
852
		} else {
853
			$total = 0;
854
			if ( $this->subtotal == null ) {
855
				foreach ( $this->cart_items as $key => $cart_item ) {
856
					$total += $cart_item->total_price;
857
				}
858
				$this->subtotal = $total;
859
			} else {
860
				$total = $this->subtotal;
861
			}
862
		}
863
864
		return $total;
865
	}
866
867
	/**
868
	 * Return the cart items.
869
	 *
870
	 * Accept an array of arguments:
871
	 *
872
	 * - 'fields': Defaults to 'all', which returns all the fields. Otherwise, specify a field such
873
	 *             as 'quantity' or 'pnp' to get an array of that field only.
874
	 * - 'orderby': Specify a field to sort the cart items. Default to '', which means "unsorted".
875
	 * - 'order'  : Specify the direction of the sort, 'ASC' for ascending, 'DESC' for descending.
876
	 *              Defaults to 'DESC'
877
	 * @since  3.8.9
878
	 * @access public
879
	 * @param  array  $args Array of arguments
880
	 * @return array        Cart items
881
	 */
882
	public function get_items( $args = array() ) {
883
		$defaults = array(
884
				'fields'  => 'all',
885
				'orderby' => '',
886
				'order'   => 'ASC',
887
		);
888
889
		$r = wp_parse_args( $args, $defaults );
890
		extract( $r, EXTR_SKIP );
0 ignored issues
show
introduced by
extract() usage is highly discouraged, due to the complexity and unintended issues it might cause.
Loading history...
891
892
		$results = $this->cart_items;
893
894
		if ( ! empty( $orderby ) ) {
895
			$comparison = new _WPSC_Comparison( $orderby, $order );
896
			usort( $results, array( $comparison, 'compare' ) );
897
		}
898
899
		if ( $fields != 'all' )
900
			$results = wp_list_pluck( $results, $fields );
901
902
		return $results;
903
	}
904
905
	/**
906
	 * calculate total tax method
907
	 *
908
	 * @access public
909
	 * @return float returns the price as a floating point value
910
	 */
911
	function calculate_total_tax() {
912
		$wpec_taxes_controller = new wpec_taxes_controller();
913
		$taxes_total = $wpec_taxes_controller->wpec_taxes_calculate_total();
914
		$this->total_tax = $taxes_total ['total'];
915
916
		if ( isset( $taxes_total['rate'] ) )
917
			$this->tax_percentage = $taxes_total['rate'];
918
919
		return apply_filters( 'wpsc_calculate_total_tax', $this->total_tax, $this );
920
	}
921
922
	/**
923
	 * calculate_total_weight method
924
	 *
925
	 * @access public
926
	 *
927
	 * @param boolean for_shipping = exclude items with no shipping,
928
	 * @return float returns the price as a floating point value
929
	 */
930
	function calculate_total_weight( $for_shipping = false ) {
931
		global $wpdb;
932
		$total = '';
933
		if ( $for_shipping == true ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
934
			foreach ( $this->cart_items as $key => $cart_item ) {
935
				if ( $cart_item->uses_shipping == 1 ) {
936
					$total += $cart_item->weight * $cart_item->quantity;
937
				}
938
			}
939
		} else {
940
			foreach ( $this->cart_items as $key => $cart_item ) {
941
				$total += $cart_item->weight * $cart_item->quantity;
942
			}
943
		}
944
		return $total;
945
	}
946
947
	public function get_total_shipping_quantity() {
948
		$total = 0;
949
950
		foreach ( $this->cart_items as $key => $cart_item ) {
951
			if ( $cart_item->uses_shipping )
952
				$total += $cart_item->quantity;
953
		}
954
955
		return $total;
956
	}
957
958
	/**
959
	 * get category url name method
960
	 *
961
	 * @access public
962
	 *
963
	 * @return float returns the price as a floating point value
0 ignored issues
show
Documentation introduced by
Should the return type not be array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
964
	 */
965
	function get_item_categories() {
966
		$category_list = array();
967
		foreach ( $this->cart_items as $key => $cart_item ) {
968
			$category_list = array_merge( ( array ) $cart_item->category_list, $category_list );
0 ignored issues
show
introduced by
Cast statements must not contain whitespace; expected "(array)" but found "( array )"
Loading history...
969
		}
970
		return $category_list;
971
	}
972
973
	/**
974
	 * get category IDs total price method
975
	 *
976
	 * @access public
977
	 *
978
	 * @return float returns the price as a floating point value
0 ignored issues
show
Documentation introduced by
Should the return type not be array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
979
	 */
980
	function get_item_category_ids() {
981
		$category_list = array();
982
		foreach ( $this->cart_items as $key => $cart_item ) {
983
			$category_list = array_merge( ( array ) $cart_item->category_id_list, $category_list );
0 ignored issues
show
introduced by
Cast statements must not contain whitespace; expected "(array)" but found "( array )"
Loading history...
984
		}
985
		return $category_list;
986
	}
987
988
	/**
989
	 * calculate_total_shipping method, gets the shipping option from the selected method and associated quotes
990
	 *
991
	 * @access public
992
	 * @return float returns the shipping as a floating point value
993
	 */
994
	function calculate_total_shipping() {
995
		$shipping_discount_value = get_option( 'shipping_discount_value' );
996
		$is_free_shipping_enabled = get_option( 'shipping_discount' );
997
		$subtotal = $this->calculate_subtotal();
998
999
		$has_free_shipping = $is_free_shipping_enabled && $shipping_discount_value > 0 && $shipping_discount_value <= $subtotal;
1000
1001
		if ( ! wpsc_uses_shipping() || $has_free_shipping ) {
1002
			$total = 0;
1003
		} else {
1004
			$total = $this->calculate_base_shipping();
1005
			$total += $this->calculate_per_item_shipping();
1006
		}
1007
1008
		return apply_filters( 'wpsc_convert_total_shipping', $total, $this );
1009
	}
1010
1011
	/**
1012
	 * has_total_shipping_discount method, checks whether the carts subtotal is larger or equal to the shipping discount
1013
	 * * value
1014
	 *
1015
	 * @access public
1016
	 * @return float returns true or false depending on whether the cart subtotal is larger or equal to the shipping *
1017
	 *         discount value.
1018
	 */
1019
	function has_total_shipping_discount() {
1020
		$shipping_discount_value = get_option( 'shipping_discount_value' );
1021
		return get_option( 'shipping_discount' ) && $shipping_discount_value > 0 && $shipping_discount_value <= $this->calculate_subtotal();
1022
	}
1023
1024
	/**
1025
	 * calculate_base_shipping method, gets the shipping option from the selected method and associated quotes
1026
	 *
1027
	 * @access public
1028
	 * @return float returns the shipping as a floating point value
1029
	 */
1030
	function calculate_base_shipping() {
1031
		global $wpdb, $wpsc_shipping_modules;
1032
1033
		if ( $this->uses_shipping() ) {
1034
			if (    isset( $wpsc_shipping_modules[ $this->selected_shipping_method ] )
1035
				 && is_callable( array( $wpsc_shipping_modules[ $this->selected_shipping_method ], 'getQuote' ) )
1036
				) {
1037
					$this->shipping_quotes = $wpsc_shipping_modules[ $this->selected_shipping_method ]->getQuote();
1038
			}
1039
1040
			if ( $this->selected_shipping_option == null ) {
1041
				$this->get_shipping_option();
1042
			}
1043
1044
			$total = isset( $this->shipping_quotes[ $this->selected_shipping_option ] ) ? ( float ) $this->shipping_quotes[ $this->selected_shipping_option ] : 0;
0 ignored issues
show
introduced by
Cast statements must not contain whitespace; expected "(float)" but found "( float )"
Loading history...
1045
			$this->base_shipping = $total;
1046
		} else {
1047
			$total = 0;
1048
		}
1049
1050
		return $total;
1051
	}
1052
1053
	/**
1054
	 * calculate_per_item_shipping method, gets the shipping option from the selected method and associated quotesing
1055
	 *
1056
	 * @access public
1057
	 * @return float returns the shipping as a floating point value
1058
	 */
1059
	function calculate_per_item_shipping( $method = null ) {
1060
		global $wpdb, $wpsc_shipping_modules;
1061
		$total = '';
1062
		if ( $method == null ) {
1063
			$method = $this->selected_shipping_method;
1064
		}
1065
		foreach ( ( array ) $this->cart_items as $cart_item ) {
0 ignored issues
show
introduced by
Cast statements must not contain whitespace; expected "(array)" but found "( array )"
Loading history...
1066
			$total += $cart_item->calculate_shipping( $method );
1067
		}
1068
		if ( $method == $this->selected_shipping_method ) {
1069
			$this->total_item_shipping = $total;
1070
		}
1071
		return $total;
1072
	}
1073
1074
	/**
1075
	 * uses shipping method, to determine if shipping is used.
1076
	 *
1077
	 * @access public
1078
	 *         (!(get_option('shipping_discount')== 1) && (get_option('shipping_discount_value') <=
1079
	 *         $wpsc_cart->calculate_subtotal()))
1080
	 * @return float returns the price as a floating point value
1081
	 */
1082
	function uses_shipping() {
1083
		global $wpdb;
1084
1085
		if ( get_option( 'do_not_use_shipping' ) ) {
1086
			return false;
1087
		}
1088
1089
		$uses_shipping = 0;
1090
		if ( ( $this->uses_shipping == null ) ) {
1091
			foreach ( $this->cart_items as $key => $cart_item ) {
1092
				$uses_shipping += ( int ) $cart_item->uses_shipping;
0 ignored issues
show
introduced by
Cast statements must not contain whitespace; expected "(int)" but found "( int )"
Loading history...
1093
			}
1094
		} else {
1095
			$uses_shipping = $this->uses_shipping;
1096
		}
1097
1098
		$this->uses_shipping = $uses_shipping;
1099
1100
		return $uses_shipping;
1101
	}
1102
1103
	/**
1104
	 * process_as_currency method
1105
	 *
1106
	 * @access public
1107
	 *
1108
	 * @param float a price
1109
	 * @return string a price with a currency sign
1110
	 */
1111
	function process_as_currency( $price ) {
1112
		_wpsc_deprecated_function( __FUNCTION__, '3.8', 'wpsc_currency_display' );
1113
		return wpsc_currency_display( $price );
1114
	}
1115
1116
	/**
1117
	 * save_to_db method, saves the cart to the database
1118
	 *
1119
	 * @access public
1120
	 *
1121
	 */
1122
	function save_to_db( $purchase_log_id ) {
1123
		global $wpdb;
1124
1125
		foreach ( $this->cart_items as $key => $cart_item ) {
1126
			$cart_item->save_to_db( $purchase_log_id );
1127
		}
1128
	}
1129
1130
	public function empty_db( $purchase_log_id ) {
1131
		global $wpdb;
1132
		$sql = $wpdb->prepare( 'DELETE FROM ' . WPSC_TABLE_CART_CONTENTS . ' WHERE purchaseid = %d', $purchase_log_id );
1133
		$wpdb->query( $sql );
1134
	}
1135
1136
	/**
1137
	 * cart loop methods
1138
	 */
1139
	function next_cart_item() {
1140
		$this->current_cart_item ++;
1141
		$this->cart_item = $this->cart_items[$this->current_cart_item];
1142
		return $this->cart_item;
1143
	}
1144
1145
	function the_cart_item() {
1146
		$this->in_the_loop = true;
1147
		$this->cart_item = $this->next_cart_item();
1148
		if ( $this->current_cart_item == 0 ) // loop has just started
1149
			do_action( 'wpsc_cart_loop_start' );
1150
	}
1151
1152
	function have_cart_items() {
1153
		if ( $this->current_cart_item + 1 < $this->cart_item_count ) {
1154
			return true;
1155
		} else if ( $this->current_cart_item + 1 == $this->cart_item_count && $this->cart_item_count > 0 ) {
1156
			do_action( 'wpsc_cart_loop_end' );
1157
			// Do some cleaning up after the loop,
1158
			$this->rewind_cart_items();
1159
		}
1160
1161
		$this->in_the_loop = false;
1162
		return false;
1163
	}
1164
1165
	function rewind_cart_items() {
1166
		$this->current_cart_item = - 1;
1167
		if ( $this->cart_item_count > 0 ) {
1168
			$this->cart_item = $this->cart_items[0];
1169
		}
1170
	}
1171
1172
	/**
1173
	 * shipping_methods methods
1174
	 */
1175
	function next_shipping_method() {
1176
		$this->current_shipping_method ++;
1177
		$this->shipping_method = $this->shipping_methods [$this->current_shipping_method];
1178
		return $this->shipping_method;
1179
	}
1180
1181
	function the_shipping_method() {
1182
		$this->shipping_method = $this->next_shipping_method();
1183
		$this->get_shipping_quotes();
1184
	}
1185
1186
	function have_shipping_methods() {
1187
		if ( $this->current_shipping_method + 1 < $this->shipping_method_count ) {
1188
			return true;
1189
		} else if ( $this->current_shipping_method + 1 == $this->shipping_method_count && $this->shipping_method_count > 0 ) {
1190
			// Do some cleaning up after the loop,
1191
			$this->rewind_shipping_methods();
1192
		}
1193
		return false;
1194
	}
1195
1196
	function rewind_shipping_methods() {
1197
1198
		$this->current_shipping_method = - 1;
1199
1200
		$this->shipping_method_count = count( $this->shipping_methods );
1201
1202
		if ( $this->shipping_method_count > 0 ) {
1203
			$this->shipping_method = $this->shipping_methods[0];
1204
		}
1205
	}
1206
1207
	/**
1208
	 * shipping_quotes methods
1209
	 */
1210
	function get_shipping_quotes() {
1211
1212
		global $wpdb, $wpsc_shipping_modules;
1213
		$this->shipping_quotes = array();
1214
		if ( $this->shipping_method == null ) {
1215
			$this->get_shipping_method();
1216
		}
1217
		if ( isset( $wpsc_shipping_modules[$this->shipping_method] ) && is_callable( array( $wpsc_shipping_modules [$this->shipping_method], 'getQuote' ) ) ) {
1218
			$unprocessed_shipping_quotes = $wpsc_shipping_modules[$this->shipping_method]->getQuote();
1219
		}
1220
		$num = 0;
1221
		if ( ! empty( $unprocessed_shipping_quotes ) ) {
1222
			foreach ( ( array ) $unprocessed_shipping_quotes as $shipping_key => $shipping_value ) {
0 ignored issues
show
introduced by
Cast statements must not contain whitespace; expected "(array)" but found "( array )"
Loading history...
1223
				$per_item_shipping = $this->calculate_per_item_shipping( $this->shipping_method );
1224
				$this->shipping_quotes [$num] ['name'] = $shipping_key;
1225
				$this->shipping_quotes [$num] ['value'] = ( float ) $shipping_value + ( float ) $per_item_shipping;
0 ignored issues
show
introduced by
Cast statements must not contain whitespace; expected "(float)" but found "( float )"
Loading history...
1226
				$num ++;
1227
			}
1228
		}
1229
1230
		$this->shipping_quote_count = count( $this->shipping_quotes );
1231
	}
1232
1233
	/**
1234
	 * If there is only one quote, set it as the default.
1235
	 *
1236
	 * @param string     $selected_option  The currently selected rate.
1237
	 * @param array      $shipping_quotes  Array of all available shipping quotes.
1238
	 * @param WPSC_Cart  $wpsc_cart        The WPSC_Cart object.
1239
	 */
1240
	function set_default_shipping_quote( $selected_option, $shipping_quotes, $wpsc_cart ) {
0 ignored issues
show
Unused Code introduced by
The parameter $wpsc_cart is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1241
		if ( count( $shipping_quotes ) == 1 ) {
1242
			reset( $shipping_quotes );
1243
			$selected_option = key( $shipping_quotes );
1244
		}
1245
		return $selected_option;
1246
	}
1247
1248
	function google_shipping_quotes() {
1249
		if ( defined( 'WPEC_LOAD_DEPRECATED' ) && WPEC_LOAD_DEPRECATED ) {
1250
			/*
1251
			 * Couldn't find an easy way to deprecate this function without creating a new class, so it is being
1252
			 * deprecated in place. Google checkout is gone, so this function should not have any purpose going forward
1253
			 * @since 3.8.14
1254
			 */
1255
1256
			global $wpsc_shipping_modules;
1257
			$shipping_quote_count = 0;
1258
			$custom_shipping = get_option( 'custom_shipping_options' );
1259
			$shipping_quotes = null;
1260
			if ( $this->selected_shipping_method != null ) {
1261
				$this->shipping_quotes = $wpsc_shipping_modules [$this->selected_shipping_method]->getQuote();
1262
				// use the selected shipping module
1263
				if ( is_callable( array( $wpsc_shipping_modules [$this->selected_shipping_method], 'getQuote' ) ) ) {
1264
					$this->shipping_quotes = $wpsc_shipping_modules [$this->selected_shipping_method]->getQuote();
1265
				}
1266
			} else {
1267
				// otherwise select the first one with any quotes
1268
				foreach ( ( array ) $custom_shipping as $shipping_module ) {
0 ignored issues
show
introduced by
Cast statements must not contain whitespace; expected "(array)" but found "( array )"
Loading history...
1269
1270
					// if the shipping module does not require a weight, or requires one and the weight is larger than
1271
					// zero
1272
					$this->selected_shipping_method = $shipping_module;
1273
					if ( is_callable( array( $wpsc_shipping_modules [$this->selected_shipping_method], 'getQuote' ) ) ) {
1274
1275
						$this->shipping_quotes = $wpsc_shipping_modules [$this->selected_shipping_method]->getQuote();
1276
					}
1277
1278
					// if we have any shipping quotes, break the loop.
1279
					if ( count( $this->shipping_quotes ) > $shipping_quote_count ) {
1280
						break;
1281
					}
1282
				}
1283
			}
1284
		} // end load deprecated
1285
	}
1286
1287
	function next_shipping_quote() {
1288
		$this->current_shipping_quote ++;
1289
		$this->shipping_quote = $this->shipping_quotes [$this->current_shipping_quote];
1290
		return $this->shipping_quote;
1291
	}
1292
1293
	function the_shipping_quote() {
1294
		$this->shipping_quote = $this->next_shipping_quote();
1295
	}
1296
1297
	function have_shipping_quotes() {
1298
		if ( $this->current_shipping_quote + 1 < $this->shipping_quote_count ) {
1299
			return true;
1300
		} else if ( $this->current_shipping_quote + 1 == $this->shipping_quote_count && $this->shipping_quote_count > 0 ) {
1301
			// Do some cleaning up after the loop,
1302
			$this->rewind_shipping_quotes();
1303
		}
1304
		return false;
1305
	}
1306
1307
	function rewind_shipping_quotes() {
1308
		$this->current_shipping_quote = - 1;
1309
1310
		if ( $this->shipping_quote_count > 0 ) {
1311
			$this->shipping_quote = $this->shipping_quotes[0];
1312
		}
1313
	}
1314
1315
	/**
1316
	 * Applying Coupons
1317
	 */
1318
	function apply_coupons( $coupons_amount = '', $coupon_name = '' ) {
1319
		$this->clear_cache();
1320
		$this->coupons_name = $coupon_name;
1321
		$this->coupons_amount = apply_filters( 'wpsc_coupons_amount', $coupons_amount, $coupon_name, $this );
1322
1323
		$this->calculate_total_price();
1324
1325
		if ( $this->total_price < 0 ) {
1326
1327
			$this->coupons_amount += $this->total_price;
1328
			$this->total_price     = null;
1329
1330
			$this->calculate_total_price();
1331
		}
1332
	}
1333
}
1334
1335
/**
1336
 * A final calculation of shipping method on shipping page, prior to quote display.
1337
 * A regrettable hack, but necessary for 1.0 versions of our shipping interface and theme engine.
1338
 *
1339
 * @link   https://github.com/wp-e-commerce/WP-e-Commerce/issues/1552
1340
 *
1341
 * @since  3.9.0
1342
 * @access private
1343
 *
1344
 * @return void
1345
 */
1346
function _wpsc_calculate_shipping_quotes_before_product_page() {
1347
	global $wpsc_cart;
1348
1349
	if ( $wpsc_cart->uses_shipping() ) {
1350
		$wpsc_cart->get_shipping_method();
1351
		$wpsc_cart->rewind_shipping_methods();
1352
	}
1353
}
1354
1355
add_action( 'wpsc_before_shipping_of_shopping_cart', '_wpsc_calculate_shipping_quotes_before_product_page' , 1 );
1356