Completed
Pull Request — master (#9826)
by Mike
15:37
created

WC_Shipping_Legacy_Flat_Rate::calculate_shipping()   C

Complexity

Conditions 11
Paths 136

Size

Total Lines 76
Code Lines 32

Duplication

Lines 28
Ratio 36.84 %
Metric Value
dl 28
loc 76
rs 5.1145
cc 11
eloc 32
nc 136
nop 1

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
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 16 and the first side effect is on line 3.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Flat Rate Shipping Method.
8
 *
9
 * This class is here for backwards commpatility for methods existing before zones existed.
10
 *
11
 * @deprecated  2.6.0
12
 * @version		2.4.0
13
 * @package		WooCommerce/Classes/Shipping
14
 * @author 		WooThemes
15
 */
16
class WC_Shipping_Legacy_Flat_Rate extends WC_Shipping_Method {
17
18
	/** @var string cost passed to [fee] shortcode */
19
	protected $fee_cost = '';
20
21
	/**
22
	 * Constructor.
23
	 */
24
	public function __construct() {
25
		$this->id                 = 'legacy_flat_rate';
26
		$this->method_title       = __( 'Flat Rate (Legacy)', 'woocommerce' );
27
		$this->method_description = sprintf( __( '<strong>This method is deprecated in 2.6.0 and will be removed in future versions - we recommend disabling it and instead setting up a new rate within your <a href="%s">Shipping Zones</a>.</strong>', 'woocommerce' ), admin_url( 'admin.php?page=wc-shipping' ) );
28
		$this->init();
29
30
		add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
31
		add_action( 'woocommerce_flat_rate_shipping_add_rate', array( $this, 'calculate_extra_shipping' ), 10, 3 );
32
	}
33
34
	/**
35
	 * Return the name of the option in the WP DB.
36
	 * @since 2.6.0
37
	 * @return string
38
	 */
39
	public function get_option_key() {
40
		return $this->plugin_id . 'flat_rate' . '_settings';
41
	}
42
43
	/**
44
	 * init function.
45
	 */
46
	public function init() {
47
		// Load the settings.
48
		$this->init_form_fields();
49
		$this->init_settings();
50
51
		// Define user set variables
52
		$this->title        = $this->get_option( 'title' );
53
		$this->availability = $this->get_option( 'availability' );
0 ignored issues
show
Deprecated Code introduced by
The property WC_Shipping_Method::$availability has been deprecated with message: 2.6.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
54
		$this->countries    = $this->get_option( 'countries' );
0 ignored issues
show
Deprecated Code introduced by
The property WC_Shipping_Method::$countries has been deprecated with message: 2.6.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
55
		$this->tax_status   = $this->get_option( 'tax_status' );
56
		$this->cost         = $this->get_option( 'cost' );
57
		$this->type         = $this->get_option( 'type', 'class' );
58
		$this->options      = $this->get_option( 'options', false ); // @deprecated in 2.4.0
59
	}
60
61
	/**
62
	 * Initialise Settings Form Fields.
63
	 */
64
	public function init_form_fields() {
65
		$this->form_fields = include( 'includes/settings-flat-rate.php' );
66
	}
67
68
	/**
69
	 * Evaluate a cost from a sum/string.
70
	 * @param  string $sum
71
	 * @param  array  $args
72
	 * @return string
73
	 */
74 View Code Duplication
	protected function evaluate_cost( $sum, $args = array() ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
75
		include_once( 'includes/class-wc-eval-math.php' );
76
77
		$locale   = localeconv();
78
		$decimals = array( wc_get_price_decimal_separator(), $locale['decimal_point'], $locale['mon_decimal_point'] );
79
80
		$this->fee_cost = $args['cost'];
81
82
		// Expand shortcodes
83
		add_shortcode( 'fee', array( $this, 'fee' ) );
84
85
		$sum = do_shortcode( str_replace(
86
			array(
87
				'[qty]',
88
				'[cost]'
89
			),
90
			array(
91
				$args['qty'],
92
				$args['cost']
93
			),
94
			$sum
95
		) );
96
97
		remove_shortcode( 'fee', array( $this, 'fee' ) );
98
99
		// Remove whitespace from string
100
		$sum = preg_replace( '/\s+/', '', $sum );
101
102
		// Remove locale from string
103
		$sum = str_replace( $decimals, '.', $sum );
104
105
		// Trim invalid start/end characters
106
		$sum = rtrim( ltrim( $sum, "\t\n\r\0\x0B+*/" ), "\t\n\r\0\x0B+-*/" );
107
108
		// Do the math
109
		return $sum ? WC_Eval_Math::evaluate( $sum ) : 0;
110
	}
111
112
	/**
113
	 * Work out fee (shortcode).
114
	 * @param  array $atts
115
	 * @return string
116
	 */
117 View Code Duplication
	public function fee( $atts ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
118
		$atts = shortcode_atts( array(
119
			'percent' => '',
120
			'min_fee' => ''
121
		), $atts );
122
123
		$calculated_fee = 0;
124
125
		if ( $atts['percent'] ) {
126
			$calculated_fee = $this->fee_cost * ( floatval( $atts['percent'] ) / 100 );
127
		}
128
129
		if ( $atts['min_fee'] && $calculated_fee < $atts['min_fee'] ) {
130
			$calculated_fee = $atts['min_fee'];
131
		}
132
133
		return $calculated_fee;
134
	}
135
136
	/**
137
	 * calculate_shipping function.
138
	 *
139
	 * @param array $package (default: array())
140
	 */
141
	public function calculate_shipping( $package = array() ) {
142
		$rate = array(
143
			'id'    => $this->id,
144
			'label' => $this->title,
145
			'cost'  => 0,
146
		);
147
148
		// Calculate the costs
149
		$has_costs = false; // True when a cost is set. False if all costs are blank strings.
150
		$cost      = $this->get_option( 'cost' );
151
152 View Code Duplication
		if ( $cost !== '' ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
			$has_costs    = true;
154
			$rate['cost'] = $this->evaluate_cost( $cost, array(
155
				'qty'  => $this->get_package_item_qty( $package ),
156
				'cost' => $package['contents_cost']
157
			) );
158
		}
159
160
		// Add shipping class costs
161
		$found_shipping_classes = $this->find_shipping_classes( $package );
162
		$highest_class_cost     = 0;
163
164 View Code Duplication
		foreach ( $found_shipping_classes as $shipping_class => $products ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
165
			// Also handles BW compatibility when slugs were used instead of ids
166
			$shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' );
167
			$class_cost_string   = $shipping_class_term && $shipping_class_term->term_id ? $this->get_option( 'class_cost_' . $shipping_class_term->term_id, $this->get_option( 'class_cost_' . $shipping_class, '' ) ) : $this->get_option( 'no_class_cost', '' );
168
169
			if ( $class_cost_string === '' ) {
170
				continue;
171
			}
172
173
			$has_costs  = true;
174
			$class_cost = $this->evaluate_cost( $class_cost_string, array(
175
				'qty'  => array_sum( wp_list_pluck( $products, 'quantity' ) ),
176
				'cost' => array_sum( wp_list_pluck( $products, 'line_total' ) )
177
			) );
178
179
			if ( $this->type === 'class' ) {
180
				$rate['cost'] += $class_cost;
181
			} else {
182
				$highest_class_cost = $class_cost > $highest_class_cost ? $class_cost : $highest_class_cost;
183
			}
184
		}
185
186
		if ( $this->type === 'order' && $highest_class_cost ) {
187
			$rate['cost'] += $highest_class_cost;
188
		}
189
190
		// Add the rate
191
		if ( $has_costs ) {
192
			$this->add_rate( $rate );
193
		}
194
195
		/**
196
		 * Developers can add additional flat rates based on this one via this action since @version 2.4.
197
		 *
198
		 * Previously there were (overly complex) options to add additional rates however this was not user.
199
		 * friendly and goes against what Flat Rate Shipping was originally intended for.
200
		 *
201
		 * This example shows how you can add an extra rate based on this flat rate via custom function:
202
		 *
203
		 * 		add_action( 'woocommerce_flat_rate_shipping_add_rate', 'add_another_custom_flat_rate', 10, 2 );
204
		 *
205
		 * 		function add_another_custom_flat_rate( $method, $rate ) {
206
		 * 			$new_rate          = $rate;
207
		 * 			$new_rate['id']    .= ':' . 'custom_rate_name'; // Append a custom ID.
208
		 * 			$new_rate['label'] = 'Rushed Shipping'; // Rename to 'Rushed Shipping'.
209
		 * 			$new_rate['cost']  += 2; // Add $2 to the cost.
210
		 *
211
		 * 			// Add it to WC.
212
		 * 			$method->add_rate( $new_rate );
213
		 * 		}.
214
		 */
215
		do_action( 'woocommerce_flat_rate_shipping_add_rate', $this, $rate, $package );
216
	}
217
218
	/**
219
	 * Get items in package.
220
	 * @param  array $package
221
	 * @return int
222
	 */
223 View Code Duplication
	public function get_package_item_qty( $package ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
224
		$total_quantity = 0;
225
		foreach ( $package['contents'] as $item_id => $values ) {
226
			if ( $values['quantity'] > 0 && $values['data']->needs_shipping() ) {
227
				$total_quantity += $values['quantity'];
228
			}
229
		}
230
		return $total_quantity;
231
	}
232
233
	/**
234
	 * Finds and returns shipping classes and the products with said class.
235
	 * @param mixed $package
236
	 * @return array
237
	 */
238 View Code Duplication
	public function find_shipping_classes( $package ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239
		$found_shipping_classes = array();
240
241
		foreach ( $package['contents'] as $item_id => $values ) {
242
			if ( $values['data']->needs_shipping() ) {
243
				$found_class = $values['data']->get_shipping_class();
244
245
				if ( ! isset( $found_shipping_classes[ $found_class ] ) ) {
246
					$found_shipping_classes[ $found_class ] = array();
247
				}
248
249
				$found_shipping_classes[ $found_class ][ $item_id ] = $values;
250
			}
251
		}
252
253
		return $found_shipping_classes;
254
	}
255
256
	/**
257
	 * Adds extra calculated flat rates.
258
	 *
259
	 * @deprecated 2.4.0
260
	 *
261
	 * Additonal rates defined like this:
262
	 * 	Option Name | Additional Cost [+- Percents%] | Per Cost Type (order, class, or item).
263
	 */
264
	public function calculate_extra_shipping( $method, $rate, $package ) {
265
		if ( $this->options ) {
266
			$options = array_filter( (array) explode( "\n", $this->options ) );
267
268
			foreach ( $options as $option ) {
269
				$this_option = array_map( 'trim', explode( WC_DELIMITER, $option ) );
270
				if ( sizeof( $this_option ) !== 3 ) {
271
					continue;
272
				}
273
				$extra_rate          = $rate;
274
				$extra_rate['id']    = $this->id . ':' . urldecode( sanitize_title( $this_option[0] ) );
275
				$extra_rate['label'] = $this_option[0];
276
				$extra_cost          = $this->get_extra_cost( $this_option[1], $this_option[2], $package );
0 ignored issues
show
Deprecated Code introduced by
The method WC_Shipping_Legacy_Flat_Rate::get_extra_cost() has been deprecated with message: 2.4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
277
				if ( is_array( $extra_rate['cost'] ) ) {
278
					$extra_rate['cost']['order'] = $extra_rate['cost']['order'] + $extra_cost;
279
				} else {
280
					$extra_rate['cost'] += $extra_cost;
281
				}
282
				$this->add_rate( $extra_rate );
283
			}
284
		}
285
	}
286
287
	/**
288
	 * Calculate the percentage adjustment for each shipping rate.
289
	 *
290
	 * @deprecated 2.4.0
291
	 * @param  float  $cost
292
	 * @param  float  $percent_adjustment
293
	 * @param  string $percent_operator
294
	 * @param  float  $base_price
295
	 * @return float
296
	 */
297
	public function calc_percentage_adjustment( $cost, $percent_adjustment, $percent_operator, $base_price ) {
298
		if ( '+' == $percent_operator ) {
299
			$cost += $percent_adjustment * $base_price;
300
		} else {
301
			$cost -= $percent_adjustment * $base_price;
302
		}
303
		return $cost;
304
	}
305
306
	/**
307
	 * Get extra cost.
308
	 *
309
	 * @deprecated 2.4.0
310
	 * @param  string $cost_string
311
	 * @param  string $type
312
	 * @param  array $package
313
	 * @return float
314
	 */
315
	public function get_extra_cost( $cost_string, $type, $package ) {
316
		$cost         = $cost_string;
317
		$cost_percent = false;
318
		$pattern      =
319
			'/' .           // start regex
320
			'(\d+\.?\d*)' . // capture digits, optionally capture a `.` and more digits
321
			'\s*' .         // match whitespace
322
			'(\+|-)' .      // capture the operand
323
			'\s*'.          // match whitespace
324
			'(\d+\.?\d*)'.  // capture digits, optionally capture a `.` and more digits
325
			'\%/';          // match the percent sign & end regex
326
		if ( preg_match( $pattern, $cost_string, $this_cost_matches ) ) {
327
			$cost_operator = $this_cost_matches[2];
328
			$cost_percent  = $this_cost_matches[3] / 100;
329
			$cost          = $this_cost_matches[1];
330
		}
331
		switch ( $type ) {
332
			case 'class' :
333
				$cost = $cost * sizeof( $this->find_shipping_classes( $package ) );
334
			break;
335
			case 'item' :
336
				$cost = $cost * $this->get_package_item_qty( $package );
337
			break;
338
		}
339
		if ( $cost_percent ) {
340
			switch ( $type ) {
341
				case 'class' :
342
					$shipping_classes = $this->find_shipping_classes( $package );
343
					foreach ( $shipping_classes as $shipping_class => $items ){
344
						foreach ( $items as $item_id => $values ) {
345
							$cost = $this->calc_percentage_adjustment( $cost, $cost_percent, $cost_operator, $values['line_total'] );
0 ignored issues
show
Deprecated Code introduced by
The method WC_Shipping_Legacy_Flat_...percentage_adjustment() has been deprecated with message: 2.4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
346
						}
347
					}
348
				break;
349
				case 'item' :
350
					foreach ( $package['contents'] as $item_id => $values ) {
351
						if ( $values['data']->needs_shipping() ) {
352
							$cost = $this->calc_percentage_adjustment( $cost, $cost_percent, $cost_operator, $values['line_total'] );
0 ignored issues
show
Deprecated Code introduced by
The method WC_Shipping_Legacy_Flat_...percentage_adjustment() has been deprecated with message: 2.4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
353
						}
354
					}
355
				break;
356
				case  'order' :
357
					$cost = $this->calc_percentage_adjustment( $cost, $cost_percent, $cost_operator, $package['contents_cost'] );
0 ignored issues
show
Deprecated Code introduced by
The method WC_Shipping_Legacy_Flat_...percentage_adjustment() has been deprecated with message: 2.4.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
358
				break;
359
			}
360
		}
361
		return $cost;
362
	}
363
}
364