Completed
Push — master ( fb62b9...43af20 )
by Mike
08:19
created

WC_Shipping_Legacy_Flat_Rate::evaluate_cost()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 37
Code Lines 19

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 37
rs 8.8571
cc 2
eloc 19
nc 2
nop 2
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-settings&tab=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, 2 );
32
	}
33
34
	/**
35
	 * Process and redirect if disabled.
36
	 */
37
	public function process_admin_options() {
38
		parent::process_admin_options();
39
40
		if ( 'no' === $this->settings[ 'enabled' ] ) {
41
			wp_redirect( admin_url( 'admin.php?page=wc-settings&tab=shipping&section=options' ) );
42
			exit;
43
		}
44
	}
45
46
	/**
47
	 * Return the name of the option in the WP DB.
48
	 * @since 2.6.0
49
	 * @return string
50
	 */
51
	public function get_option_key() {
52
		return $this->plugin_id . 'flat_rate' . '_settings';
53
	}
54
55
	/**
56
	 * init function.
57
	 */
58
	public function init() {
59
		// Load the settings.
60
		$this->init_form_fields();
61
		$this->init_settings();
62
63
		// Define user set variables
64
		$this->title        = $this->get_option( 'title' );
65
		$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...
66
		$this->countries    = $this->get_option( 'countries' );
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_option('countries') of type * is incompatible with the declared type array of property $countries.

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...
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...
67
		$this->tax_status   = $this->get_option( 'tax_status' );
68
		$this->cost         = $this->get_option( 'cost' );
69
		$this->type         = $this->get_option( 'type', 'class' );
70
		$this->options      = $this->get_option( 'options', false ); // @deprecated in 2.4.0
71
	}
72
73
	/**
74
	 * Initialise Settings Form Fields.
75
	 */
76
	public function init_form_fields() {
77
		$this->form_fields = include( 'includes/settings-flat-rate.php' );
78
	}
79
80
	/**
81
	 * Evaluate a cost from a sum/string.
82
	 * @param  string $sum
83
	 * @param  array  $args
84
	 * @return string
85
	 */
86
	protected function evaluate_cost( $sum, $args = array() ) {
87
		include_once( 'includes/class-wc-eval-math.php' );
88
89
		$locale   = localeconv();
90
		$decimals = array( wc_get_price_decimal_separator(), $locale['decimal_point'], $locale['mon_decimal_point'] );
91
92
		$this->fee_cost = $args['cost'];
93
94
		// Expand shortcodes
95
		add_shortcode( 'fee', array( $this, 'fee' ) );
96
97
		$sum = do_shortcode( str_replace(
98
			array(
99
				'[qty]',
100
				'[cost]'
101
			),
102
			array(
103
				$args['qty'],
104
				$args['cost']
105
			),
106
			$sum
107
		) );
108
109
		remove_shortcode( 'fee', array( $this, 'fee' ) );
110
111
		// Remove whitespace from string
112
		$sum = preg_replace( '/\s+/', '', $sum );
113
114
		// Remove locale from string
115
		$sum = str_replace( $decimals, '.', $sum );
116
117
		// Trim invalid start/end characters
118
		$sum = rtrim( ltrim( $sum, "\t\n\r\0\x0B+*/" ), "\t\n\r\0\x0B+-*/" );
119
120
		// Do the math
121
		return $sum ? WC_Eval_Math::evaluate( $sum ) : 0;
122
	}
123
124
	/**
125
	 * Work out fee (shortcode).
126
	 * @param  array $atts
127
	 * @return string
128
	 */
129 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...
130
		$atts = shortcode_atts( array(
131
			'percent' => '',
132
			'min_fee' => ''
133
		), $atts );
134
135
		$calculated_fee = 0;
136
137
		if ( $atts['percent'] ) {
138
			$calculated_fee = $this->fee_cost * ( floatval( $atts['percent'] ) / 100 );
139
		}
140
141
		if ( $atts['min_fee'] && $calculated_fee < $atts['min_fee'] ) {
142
			$calculated_fee = $atts['min_fee'];
143
		}
144
145
		return $calculated_fee;
146
	}
147
148
	/**
149
	 * calculate_shipping function.
150
	 *
151
	 * @param array $package (default: array())
152
	 */
153
	public function calculate_shipping( $package = array() ) {
154
		$rate = array(
155
			'id'      => $this->id,
156
			'label'   => $this->title,
157
			'cost'    => 0,
158
			'package' => $package,
159
		);
160
161
		// Calculate the costs
162
		$has_costs = false; // True when a cost is set. False if all costs are blank strings.
163
		$cost      = $this->get_option( 'cost' );
164
165 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...
166
			$has_costs    = true;
167
			$rate['cost'] = $this->evaluate_cost( $cost, array(
168
				'qty'  => $this->get_package_item_qty( $package ),
169
				'cost' => $package['contents_cost']
170
			) );
171
		}
172
173
		// Add shipping class costs
174
		$found_shipping_classes = $this->find_shipping_classes( $package );
175
		$highest_class_cost     = 0;
176
177 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...
178
			// Also handles BW compatibility when slugs were used instead of ids
179
			$shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' );
180
			$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', '' );
181
182
			if ( $class_cost_string === '' ) {
183
				continue;
184
			}
185
186
			$has_costs  = true;
187
			$class_cost = $this->evaluate_cost( $class_cost_string, array(
188
				'qty'  => array_sum( wp_list_pluck( $products, 'quantity' ) ),
189
				'cost' => array_sum( wp_list_pluck( $products, 'line_total' ) )
190
			) );
191
192
			if ( $this->type === 'class' ) {
193
				$rate['cost'] += $class_cost;
194
			} else {
195
				$highest_class_cost = $class_cost > $highest_class_cost ? $class_cost : $highest_class_cost;
196
			}
197
		}
198
199
		if ( $this->type === 'order' && $highest_class_cost ) {
200
			$rate['cost'] += $highest_class_cost;
201
		}
202
203
		$rate['package'] = $package;
204
205
		// Add the rate
206
		if ( $has_costs ) {
207
			$this->add_rate( $rate );
208
		}
209
210
		/**
211
		 * Developers can add additional flat rates based on this one via this action since @version 2.4.
212
		 *
213
		 * Previously there were (overly complex) options to add additional rates however this was not user.
214
		 * friendly and goes against what Flat Rate Shipping was originally intended for.
215
		 *
216
		 * This example shows how you can add an extra rate based on this flat rate via custom function:
217
		 *
218
		 * 		add_action( 'woocommerce_flat_rate_shipping_add_rate', 'add_another_custom_flat_rate', 10, 2 );
219
		 *
220
		 * 		function add_another_custom_flat_rate( $method, $rate ) {
221
		 * 			$new_rate          = $rate;
222
		 * 			$new_rate['id']    .= ':' . 'custom_rate_name'; // Append a custom ID.
223
		 * 			$new_rate['label'] = 'Rushed Shipping'; // Rename to 'Rushed Shipping'.
224
		 * 			$new_rate['cost']  += 2; // Add $2 to the cost.
225
		 *
226
		 * 			// Add it to WC.
227
		 * 			$method->add_rate( $new_rate );
228
		 * 		}.
229
		 */
230
		do_action( 'woocommerce_flat_rate_shipping_add_rate', $this, $rate );
231
	}
232
233
	/**
234
	 * Get items in package.
235
	 * @param  array $package
236
	 * @return int
237
	 */
238 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...
239
		$total_quantity = 0;
240
		foreach ( $package['contents'] as $item_id => $values ) {
241
			if ( $values['quantity'] > 0 && $values['data']->needs_shipping() ) {
242
				$total_quantity += $values['quantity'];
243
			}
244
		}
245
		return $total_quantity;
246
	}
247
248
	/**
249
	 * Finds and returns shipping classes and the products with said class.
250
	 * @param mixed $package
251
	 * @return array
252
	 */
253 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...
254
		$found_shipping_classes = array();
255
256
		foreach ( $package['contents'] as $item_id => $values ) {
257
			if ( $values['data']->needs_shipping() ) {
258
				$found_class = $values['data']->get_shipping_class();
259
260
				if ( ! isset( $found_shipping_classes[ $found_class ] ) ) {
261
					$found_shipping_classes[ $found_class ] = array();
262
				}
263
264
				$found_shipping_classes[ $found_class ][ $item_id ] = $values;
265
			}
266
		}
267
268
		return $found_shipping_classes;
269
	}
270
271
	/**
272
	 * Adds extra calculated flat rates.
273
	 *
274
	 * @deprecated 2.4.0
275
	 *
276
	 * Additonal rates defined like this:
277
	 * 	Option Name | Additional Cost [+- Percents%] | Per Cost Type (order, class, or item).
278
	 */
279
	public function calculate_extra_shipping( $method, $rate ) {
280
		if ( $this->options ) {
281
			$options = array_filter( (array) explode( "\n", $this->options ) );
282
283
			foreach ( $options as $option ) {
284
				$this_option = array_map( 'trim', explode( WC_DELIMITER, $option ) );
285
				if ( sizeof( $this_option ) !== 3 ) {
286
					continue;
287
				}
288
				$extra_rate          = $rate;
289
				$extra_rate['id']    = $this->id . ':' . urldecode( sanitize_title( $this_option[0] ) );
290
				$extra_rate['label'] = $this_option[0];
291
				$extra_cost          = $this->get_extra_cost( $this_option[1], $this_option[2], $rate['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...
292
				if ( is_array( $extra_rate['cost'] ) ) {
293
					$extra_rate['cost']['order'] = $extra_rate['cost']['order'] + $extra_cost;
294
				} else {
295
					$extra_rate['cost'] += $extra_cost;
296
				}
297
298
				$this->add_rate( $extra_rate );
299
			}
300
		}
301
	}
302
303
	/**
304
	 * Calculate the percentage adjustment for each shipping rate.
305
	 *
306
	 * @deprecated 2.4.0
307
	 * @param  float  $cost
308
	 * @param  float  $percent_adjustment
309
	 * @param  string $percent_operator
310
	 * @param  float  $base_price
311
	 * @return float
312
	 */
313
	public function calc_percentage_adjustment( $cost, $percent_adjustment, $percent_operator, $base_price ) {
314
		if ( '+' == $percent_operator ) {
315
			$cost += $percent_adjustment * $base_price;
316
		} else {
317
			$cost -= $percent_adjustment * $base_price;
318
		}
319
		return $cost;
320
	}
321
322
	/**
323
	 * Get extra cost.
324
	 *
325
	 * @deprecated 2.4.0
326
	 * @param  string $cost_string
327
	 * @param  string $type
328
	 * @param  array $package
329
	 * @return float
330
	 */
331
	public function get_extra_cost( $cost_string, $type, $package ) {
332
		$cost         = $cost_string;
333
		$cost_percent = false;
334
		$pattern      =
335
			'/' .           // start regex
336
			'(\d+\.?\d*)' . // capture digits, optionally capture a `.` and more digits
337
			'\s*' .         // match whitespace
338
			'(\+|-)' .      // capture the operand
339
			'\s*'.          // match whitespace
340
			'(\d+\.?\d*)'.  // capture digits, optionally capture a `.` and more digits
341
			'\%/';          // match the percent sign & end regex
342
		if ( preg_match( $pattern, $cost_string, $this_cost_matches ) ) {
343
			$cost_operator = $this_cost_matches[2];
344
			$cost_percent  = $this_cost_matches[3] / 100;
345
			$cost          = $this_cost_matches[1];
346
		}
347
		switch ( $type ) {
348
			case 'class' :
349
				$cost = $cost * sizeof( $this->find_shipping_classes( $package ) );
350
			break;
351
			case 'item' :
352
				$cost = $cost * $this->get_package_item_qty( $package );
353
			break;
354
		}
355
		if ( $cost_percent ) {
356
			switch ( $type ) {
357
				case 'class' :
358
					$shipping_classes = $this->find_shipping_classes( $package );
359
					foreach ( $shipping_classes as $shipping_class => $items ){
360
						foreach ( $items as $item_id => $values ) {
361
							$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...
362
						}
363
					}
364
				break;
365
				case 'item' :
366
					foreach ( $package['contents'] as $item_id => $values ) {
367
						if ( $values['data']->needs_shipping() ) {
368
							$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...
369
						}
370
					}
371
				break;
372
				case  'order' :
373
					$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...
374
				break;
375
			}
376
		}
377
		return $cost;
378
	}
379
}
380