This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | if ( ! defined( 'ABSPATH' ) ) { |
||
4 | exit; // Exit if accessed directly |
||
5 | } |
||
6 | |||
7 | /** |
||
8 | * Checkout |
||
9 | * |
||
10 | * The WooCommerce checkout class handles the checkout process, collecting user data and processing the payment. |
||
11 | * |
||
12 | * @class WC_Checkout |
||
13 | * @version 2.1.0 |
||
14 | * @package WooCommerce/Classes |
||
15 | * @category Class |
||
16 | * @author WooThemes |
||
17 | */ |
||
18 | class WC_Checkout { |
||
19 | |||
20 | /** @var array Array of posted form data. */ |
||
21 | public $posted; |
||
22 | |||
23 | /** @var array Array of fields to display on the checkout. */ |
||
24 | public $checkout_fields; |
||
25 | |||
26 | /** @var bool Whether or not the user must create an account to checkout. */ |
||
27 | public $must_create_account; |
||
28 | |||
29 | /** @var bool Whether or not signups are allowed. */ |
||
30 | public $enable_signup; |
||
31 | |||
32 | /** @var object The shipping method being used. */ |
||
33 | private $shipping_method; |
||
0 ignored issues
–
show
|
|||
34 | |||
35 | /** @var WC_Payment_Gateway|string The payment gateway being used. */ |
||
36 | private $payment_method; |
||
37 | |||
38 | /** @var int ID of customer. */ |
||
39 | private $customer_id; |
||
40 | |||
41 | /** @var array Where shipping_methods are stored. */ |
||
42 | public $shipping_methods; |
||
43 | |||
44 | /** |
||
45 | * @var WC_Checkout The single instance of the class |
||
46 | * @since 2.1 |
||
47 | */ |
||
48 | protected static $_instance = null; |
||
49 | |||
50 | /** @var Bool */ |
||
51 | public $enable_guest_checkout; |
||
52 | |||
53 | /** |
||
54 | * Main WC_Checkout Instance. |
||
55 | * |
||
56 | * Ensures only one instance of WC_Checkout is loaded or can be loaded. |
||
57 | * |
||
58 | * @since 2.1 |
||
59 | * @static |
||
60 | * @return WC_Checkout Main instance |
||
61 | */ |
||
62 | public static function instance() { |
||
63 | if ( is_null( self::$_instance ) ) |
||
64 | self::$_instance = new self(); |
||
65 | return self::$_instance; |
||
66 | } |
||
67 | |||
68 | /** |
||
69 | * Cloning is forbidden. |
||
70 | * |
||
71 | * @since 2.1 |
||
72 | */ |
||
73 | public function __clone() { |
||
74 | _doing_it_wrong( __FUNCTION__, __( 'Cheatin’ huh?', 'woocommerce' ), '2.1' ); |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Unserializing instances of this class is forbidden. |
||
79 | * |
||
80 | * @since 2.1 |
||
81 | */ |
||
82 | public function __wakeup() { |
||
83 | _doing_it_wrong( __FUNCTION__, __( 'Cheatin’ huh?', 'woocommerce' ), '2.1' ); |
||
84 | } |
||
85 | |||
86 | /** |
||
87 | * Constructor for the checkout class. Hooks in methods and defines checkout fields. |
||
88 | * |
||
89 | * @access public |
||
90 | */ |
||
91 | public function __construct () { |
||
92 | add_action( 'woocommerce_checkout_billing', array( $this,'checkout_form_billing' ) ); |
||
93 | add_action( 'woocommerce_checkout_shipping', array( $this,'checkout_form_shipping' ) ); |
||
94 | |||
95 | $this->enable_signup = get_option( 'woocommerce_enable_signup_and_login_from_checkout' ) == 'yes' ? true : false; |
||
96 | $this->enable_guest_checkout = get_option( 'woocommerce_enable_guest_checkout' ) == 'yes' ? true : false; |
||
97 | $this->must_create_account = $this->enable_guest_checkout || is_user_logged_in() ? false : true; |
||
98 | |||
99 | // Define all Checkout fields |
||
100 | $this->checkout_fields['billing'] = WC()->countries->get_address_fields( $this->get_value( 'billing_country' ), 'billing_' ); |
||
101 | $this->checkout_fields['shipping'] = WC()->countries->get_address_fields( $this->get_value( 'shipping_country' ), 'shipping_' ); |
||
102 | |||
103 | View Code Duplication | if ( get_option( 'woocommerce_registration_generate_username' ) == 'no' ) { |
|
0 ignored issues
–
show
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. ![]() |
|||
104 | $this->checkout_fields['account']['account_username'] = array( |
||
105 | 'type' => 'text', |
||
106 | 'label' => __( 'Account username', 'woocommerce' ), |
||
107 | 'required' => true, |
||
108 | 'placeholder' => _x( 'Username', 'placeholder', 'woocommerce' ) |
||
109 | ); |
||
110 | } |
||
111 | |||
112 | View Code Duplication | if ( get_option( 'woocommerce_registration_generate_password' ) == 'no' ) { |
|
0 ignored issues
–
show
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. ![]() |
|||
113 | $this->checkout_fields['account']['account_password'] = array( |
||
114 | 'type' => 'password', |
||
115 | 'label' => __( 'Account password', 'woocommerce' ), |
||
116 | 'required' => true, |
||
117 | 'placeholder' => _x( 'Password', 'placeholder', 'woocommerce' ) |
||
118 | ); |
||
119 | } |
||
120 | |||
121 | $this->checkout_fields['order'] = array( |
||
122 | 'order_comments' => array( |
||
123 | 'type' => 'textarea', |
||
124 | 'class' => array('notes'), |
||
125 | 'label' => __( 'Order Notes', 'woocommerce' ), |
||
126 | 'placeholder' => _x('Notes about your order, e.g. special notes for delivery.', 'placeholder', 'woocommerce') |
||
127 | ) |
||
128 | ); |
||
129 | |||
130 | $this->checkout_fields = apply_filters( 'woocommerce_checkout_fields', $this->checkout_fields ); |
||
131 | |||
132 | do_action( 'woocommerce_checkout_init', $this ); |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Checkout process. |
||
137 | */ |
||
138 | public function check_cart_items() { |
||
139 | // When we process the checkout, lets ensure cart items are rechecked to prevent checkout |
||
140 | do_action('woocommerce_check_cart_items'); |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * Output the billing information form. |
||
145 | */ |
||
146 | public function checkout_form_billing() { |
||
147 | wc_get_template( 'checkout/form-billing.php', array( 'checkout' => $this ) ); |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Output the shipping information form. |
||
152 | */ |
||
153 | public function checkout_form_shipping() { |
||
154 | wc_get_template( 'checkout/form-shipping.php', array( 'checkout' => $this ) ); |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * Create an order. Error codes: |
||
159 | * 520 - Cannot insert order into the database. |
||
160 | * 521 - Cannot get order after creation. |
||
161 | * 522 - Cannot update order. |
||
162 | * 525 - Cannot create line item. |
||
163 | * 526 - Cannot create fee item. |
||
164 | * 527 - Cannot create shipping item. |
||
165 | * 528 - Cannot create tax item. |
||
166 | * 529 - Cannot create coupon item. |
||
167 | * @access public |
||
168 | * @throws Exception |
||
169 | * @return int|WP_ERROR |
||
170 | */ |
||
171 | public function create_order() { |
||
172 | global $wpdb; |
||
173 | |||
174 | // Give plugins the opportunity to create an order themselves |
||
175 | if ( $order_id = apply_filters( 'woocommerce_create_order', null, $this ) ) { |
||
176 | return $order_id; |
||
177 | } |
||
178 | |||
179 | try { |
||
180 | // Start transaction if available |
||
181 | wc_transaction_query( 'start' ); |
||
182 | |||
183 | $order_data = array( |
||
184 | 'status' => apply_filters( 'woocommerce_default_order_status', 'pending' ), |
||
185 | 'customer_id' => $this->customer_id, |
||
186 | 'customer_note' => isset( $this->posted['order_comments'] ) ? $this->posted['order_comments'] : '', |
||
187 | 'cart_hash' => md5( json_encode( wc_clean( WC()->cart->get_cart_for_session() ) ) . WC()->cart->total ), |
||
188 | 'created_via' => 'checkout', |
||
189 | ); |
||
190 | |||
191 | // Insert or update the post data |
||
192 | $order_id = absint( WC()->session->order_awaiting_payment ); |
||
193 | |||
194 | /** |
||
195 | * If there is an order pending payment, we can resume it here so |
||
196 | * long as it has not changed. If the order has changed, i.e. |
||
197 | * different items or cost, create a new order. We use a hash to |
||
198 | * detect changes which is based on cart items + order total. |
||
199 | */ |
||
200 | if ( $order_id && $order_data['cart_hash'] === get_post_meta( $order_id, '_cart_hash', true ) && ( $order = wc_get_order( $order_id ) ) && $order->has_status( array( 'pending', 'failed' ) ) ) { |
||
201 | |||
202 | $order_data['order_id'] = $order_id; |
||
203 | $order = wc_update_order( $order_data ); |
||
204 | |||
205 | if ( is_wp_error( $order ) ) { |
||
206 | throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce' ), 522 ) ); |
||
207 | } else { |
||
208 | $order->remove_order_items(); |
||
209 | do_action( 'woocommerce_resume_order', $order_id ); |
||
210 | } |
||
211 | |||
212 | } else { |
||
213 | |||
214 | $order = wc_create_order( $order_data ); |
||
215 | |||
216 | if ( is_wp_error( $order ) ) { |
||
217 | throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce' ), 520 ) ); |
||
218 | } elseif ( false === $order ) { |
||
219 | throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce' ), 521 ) ); |
||
220 | } else { |
||
221 | $order_id = $order->id; |
||
222 | do_action( 'woocommerce_new_order', $order_id ); |
||
223 | } |
||
224 | } |
||
225 | |||
226 | // Store the line items to the new/resumed order |
||
227 | foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) { |
||
228 | $item_id = $order->add_product( |
||
229 | $values['data'], |
||
230 | $values['quantity'], |
||
231 | array( |
||
232 | 'variation' => $values['variation'], |
||
233 | 'totals' => array( |
||
234 | 'subtotal' => $values['line_subtotal'], |
||
235 | 'subtotal_tax' => $values['line_subtotal_tax'], |
||
236 | 'total' => $values['line_total'], |
||
237 | 'tax' => $values['line_tax'], |
||
238 | 'tax_data' => $values['line_tax_data'] // Since 2.2 |
||
239 | ) |
||
240 | ) |
||
241 | ); |
||
242 | |||
243 | if ( ! $item_id ) { |
||
244 | throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce' ), 525 ) ); |
||
245 | } |
||
246 | |||
247 | // Allow plugins to add order item meta |
||
248 | do_action( 'woocommerce_add_order_item_meta', $item_id, $values, $cart_item_key ); |
||
249 | } |
||
250 | |||
251 | // Store fees |
||
252 | foreach ( WC()->cart->get_fees() as $fee_key => $fee ) { |
||
253 | $item_id = $order->add_fee( $fee ); |
||
254 | |||
255 | if ( ! $item_id ) { |
||
256 | throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce' ), 526 ) ); |
||
257 | } |
||
258 | |||
259 | // Allow plugins to add order item meta to fees |
||
260 | do_action( 'woocommerce_add_order_fee_meta', $order_id, $item_id, $fee, $fee_key ); |
||
261 | } |
||
262 | |||
263 | // Store shipping for all packages |
||
264 | foreach ( WC()->shipping->get_packages() as $package_key => $package ) { |
||
265 | if ( isset( $package['rates'][ $this->shipping_methods[ $package_key ] ] ) ) { |
||
266 | $item_id = $order->add_shipping( $package['rates'][ $this->shipping_methods[ $package_key ] ] ); |
||
267 | |||
268 | if ( ! $item_id ) { |
||
269 | throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce' ), 527 ) ); |
||
270 | } |
||
271 | |||
272 | // Allows plugins to add order item meta to shipping |
||
273 | do_action( 'woocommerce_add_shipping_order_item', $order_id, $item_id, $package_key ); |
||
274 | } |
||
275 | } |
||
276 | |||
277 | // Store tax rows |
||
278 | foreach ( array_keys( WC()->cart->taxes + WC()->cart->shipping_taxes ) as $tax_rate_id ) { |
||
279 | if ( $tax_rate_id && ! $order->add_tax( $tax_rate_id, WC()->cart->get_tax_amount( $tax_rate_id ), WC()->cart->get_shipping_tax_amount( $tax_rate_id ) ) && apply_filters( 'woocommerce_cart_remove_taxes_zero_rate_id', 'zero-rated' ) !== $tax_rate_id ) { |
||
280 | throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce' ), 528 ) ); |
||
281 | } |
||
282 | } |
||
283 | |||
284 | // Store coupons |
||
285 | foreach ( WC()->cart->get_coupons() as $code => $coupon ) { |
||
286 | if ( ! $order->add_coupon( $code, WC()->cart->get_coupon_discount_amount( $code ), WC()->cart->get_coupon_discount_tax_amount( $code ) ) ) { |
||
287 | throw new Exception( sprintf( __( 'Error %d: Unable to create order. Please try again.', 'woocommerce' ), 529 ) ); |
||
288 | } |
||
289 | } |
||
290 | |||
291 | // Billing address |
||
292 | $billing_address = array(); |
||
293 | View Code Duplication | if ( $this->checkout_fields['billing'] ) { |
|
0 ignored issues
–
show
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. ![]() |
|||
294 | foreach ( array_keys( $this->checkout_fields['billing'] ) as $field ) { |
||
295 | $field_name = str_replace( 'billing_', '', $field ); |
||
296 | $billing_address[ $field_name ] = $this->get_posted_address_data( $field_name ); |
||
297 | } |
||
298 | } |
||
299 | |||
300 | // Shipping address. |
||
301 | $shipping_address = array(); |
||
302 | View Code Duplication | if ( $this->checkout_fields['shipping'] ) { |
|
0 ignored issues
–
show
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. ![]() |
|||
303 | foreach ( array_keys( $this->checkout_fields['shipping'] ) as $field ) { |
||
304 | $field_name = str_replace( 'shipping_', '', $field ); |
||
305 | $shipping_address[ $field_name ] = $this->get_posted_address_data( $field_name, 'shipping' ); |
||
306 | } |
||
307 | } |
||
308 | |||
309 | $order->set_address( $billing_address, 'billing' ); |
||
310 | $order->set_address( $shipping_address, 'shipping' ); |
||
311 | $order->set_payment_method( $this->payment_method ); |
||
0 ignored issues
–
show
It seems like
$this->payment_method can also be of type string ; however, WC_Abstract_Order::set_payment_method() does only seem to accept object<WC_Payment_Gateway> , maybe add an additional type check?
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check: /**
* @return array|string
*/
function returnsDifferentValues($x) {
if ($x) {
return 'foo';
}
return array();
}
$x = returnsDifferentValues($y);
if (is_array($x)) {
// $x is an array.
}
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue. ![]() |
|||
312 | $order->set_total( WC()->cart->shipping_total, 'shipping' ); |
||
313 | $order->set_total( WC()->cart->get_cart_discount_total(), 'cart_discount' ); |
||
314 | $order->set_total( WC()->cart->get_cart_discount_tax_total(), 'cart_discount_tax' ); |
||
315 | $order->set_total( WC()->cart->tax_total, 'tax' ); |
||
316 | $order->set_total( WC()->cart->shipping_tax_total, 'shipping_tax' ); |
||
317 | $order->set_total( WC()->cart->total ); |
||
318 | |||
319 | // Update user meta |
||
320 | if ( $this->customer_id ) { |
||
321 | if ( apply_filters( 'woocommerce_checkout_update_customer_data', true, $this ) ) { |
||
322 | foreach ( $billing_address as $key => $value ) { |
||
323 | update_user_meta( $this->customer_id, 'billing_' . $key, $value ); |
||
324 | } |
||
325 | if ( WC()->cart->needs_shipping() ) { |
||
326 | foreach ( $shipping_address as $key => $value ) { |
||
327 | update_user_meta( $this->customer_id, 'shipping_' . $key, $value ); |
||
328 | } |
||
329 | } |
||
330 | } |
||
331 | do_action( 'woocommerce_checkout_update_user_meta', $this->customer_id, $this->posted ); |
||
332 | } |
||
333 | |||
334 | // Let plugins add meta |
||
335 | do_action( 'woocommerce_checkout_update_order_meta', $order_id, $this->posted ); |
||
336 | |||
337 | // If we got here, the order was created without problems! |
||
338 | wc_transaction_query( 'commit' ); |
||
339 | |||
340 | } catch ( Exception $e ) { |
||
341 | // There was an error adding order data! |
||
342 | wc_transaction_query( 'rollback' ); |
||
343 | return new WP_Error( 'checkout-error', $e->getMessage() ); |
||
344 | } |
||
345 | |||
346 | return $order_id; |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * Process the checkout after the confirm order button is pressed. |
||
351 | */ |
||
352 | public function process_checkout() { |
||
353 | try { |
||
354 | if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'woocommerce-process_checkout' ) ) { |
||
355 | WC()->session->set( 'refresh_totals', true ); |
||
356 | throw new Exception( __( 'We were unable to process your order, please try again.', 'woocommerce' ) ); |
||
357 | } |
||
358 | |||
359 | if ( ! defined( 'WOOCOMMERCE_CHECKOUT' ) ) { |
||
360 | define( 'WOOCOMMERCE_CHECKOUT', true ); |
||
361 | } |
||
362 | |||
363 | // Prevent timeout |
||
364 | @set_time_limit(0); |
||
365 | |||
366 | do_action( 'woocommerce_before_checkout_process' ); |
||
367 | |||
368 | if ( WC()->cart->is_empty() ) { |
||
369 | throw new Exception( sprintf( __( 'Sorry, your session has expired. <a href="%s" class="wc-backward">Return to shop</a>', 'woocommerce' ), esc_url( wc_get_page_permalink( 'shop' ) ) ) ); |
||
370 | } |
||
371 | |||
372 | do_action( 'woocommerce_checkout_process' ); |
||
373 | |||
374 | // Checkout fields (not defined in checkout_fields) |
||
375 | $this->posted['terms'] = isset( $_POST['terms'] ) ? 1 : 0; |
||
376 | $this->posted['createaccount'] = isset( $_POST['createaccount'] ) && ! empty( $_POST['createaccount'] ) ? 1 : 0; |
||
377 | $this->posted['payment_method'] = isset( $_POST['payment_method'] ) ? stripslashes( $_POST['payment_method'] ) : ''; |
||
378 | $this->posted['shipping_method'] = isset( $_POST['shipping_method'] ) ? $_POST['shipping_method'] : ''; |
||
379 | $this->posted['ship_to_different_address'] = isset( $_POST['ship_to_different_address'] ) ? true : false; |
||
380 | |||
381 | if ( isset( $_POST['shiptobilling'] ) ) { |
||
382 | _deprecated_argument( 'WC_Checkout::process_checkout()', '2.1', 'The "shiptobilling" field is deprecated. The template files are out of date' ); |
||
383 | |||
384 | $this->posted['ship_to_different_address'] = $_POST['shiptobilling'] ? false : true; |
||
385 | } |
||
386 | |||
387 | // Ship to billing only option |
||
388 | if ( wc_ship_to_billing_address_only() ) { |
||
389 | $this->posted['ship_to_different_address'] = false; |
||
390 | } |
||
391 | |||
392 | // Update customer shipping and payment method to posted method |
||
393 | $chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' ); |
||
394 | |||
395 | if ( isset( $this->posted['shipping_method'] ) && is_array( $this->posted['shipping_method'] ) ) { |
||
396 | foreach ( $this->posted['shipping_method'] as $i => $value ) { |
||
397 | $chosen_shipping_methods[ $i ] = wc_clean( $value ); |
||
398 | } |
||
399 | } |
||
400 | |||
401 | WC()->session->set( 'chosen_shipping_methods', $chosen_shipping_methods ); |
||
402 | WC()->session->set( 'chosen_payment_method', $this->posted['payment_method'] ); |
||
403 | |||
404 | // Note if we skip shipping |
||
405 | $skipped_shipping = false; |
||
406 | |||
407 | // Get posted checkout_fields and do validation |
||
408 | foreach ( $this->checkout_fields as $fieldset_key => $fieldset ) { |
||
409 | |||
410 | // Skip shipping if not needed |
||
411 | if ( $fieldset_key == 'shipping' && ( $this->posted['ship_to_different_address'] == false || ! WC()->cart->needs_shipping_address() ) ) { |
||
412 | $skipped_shipping = true; |
||
413 | continue; |
||
414 | } |
||
415 | |||
416 | // Skip account if not needed |
||
417 | if ( $fieldset_key == 'account' && ( is_user_logged_in() || ( $this->must_create_account == false && empty( $this->posted['createaccount'] ) ) ) ) { |
||
0 ignored issues
–
show
|
|||
418 | continue; |
||
419 | } |
||
420 | |||
421 | foreach ( $fieldset as $key => $field ) { |
||
422 | |||
423 | if ( ! isset( $field['type'] ) ) { |
||
424 | $field['type'] = 'text'; |
||
425 | } |
||
426 | |||
427 | // Get Value |
||
428 | switch ( $field['type'] ) { |
||
429 | case "checkbox" : |
||
430 | $this->posted[ $key ] = isset( $_POST[ $key ] ) ? 1 : 0; |
||
431 | break; |
||
432 | case "multiselect" : |
||
433 | $this->posted[ $key ] = isset( $_POST[ $key ] ) ? implode( ', ', array_map( 'wc_clean', $_POST[ $key ] ) ) : ''; |
||
434 | break; |
||
435 | case "textarea" : |
||
436 | $this->posted[ $key ] = isset( $_POST[ $key ] ) ? wp_strip_all_tags( wp_check_invalid_utf8( stripslashes( $_POST[ $key ] ) ) ) : ''; |
||
437 | break; |
||
438 | default : |
||
439 | $this->posted[ $key ] = isset( $_POST[ $key ] ) ? ( is_array( $_POST[ $key ] ) ? array_map( 'wc_clean', $_POST[ $key ] ) : wc_clean( $_POST[ $key ] ) ) : ''; |
||
440 | break; |
||
441 | } |
||
442 | |||
443 | // Hooks to allow modification of value |
||
444 | $this->posted[ $key ] = apply_filters( 'woocommerce_process_checkout_' . sanitize_title( $field['type'] ) . '_field', $this->posted[ $key ] ); |
||
445 | $this->posted[ $key ] = apply_filters( 'woocommerce_process_checkout_field_' . $key, $this->posted[ $key ] ); |
||
446 | |||
447 | // Validation: Required fields |
||
448 | if ( isset( $field['required'] ) && $field['required'] && ( ! isset( $this->posted[ $key ] ) || "" === $this->posted[ $key ] ) ) { |
||
449 | switch ( $fieldset_key ) { |
||
450 | case 'shipping' : |
||
451 | $field_label = sprintf( _x( 'Shipping %s', 'Shipping FIELDNAME', 'woocommerce' ), $field['label'] ); |
||
452 | break; |
||
453 | case 'billing' : |
||
454 | $field_label = sprintf( _x( 'Billing %s', 'Billing FIELDNAME', 'woocommerce' ), $field['label'] ); |
||
455 | break; |
||
456 | default : |
||
457 | $field_label = $field['label']; |
||
458 | break; |
||
459 | } |
||
460 | wc_add_notice( apply_filters( 'woocommerce_checkout_required_field_notice', sprintf( _x( '%s is a required field.', 'FIELDNAME is a required field.', 'woocommerce' ), '<strong>' . $field_label . '</strong>' ), $field_label ), 'error' ); |
||
461 | } |
||
462 | |||
463 | if ( ! empty( $this->posted[ $key ] ) ) { |
||
464 | |||
465 | // Validation rules |
||
466 | if ( ! empty( $field['validate'] ) && is_array( $field['validate'] ) ) { |
||
467 | foreach ( $field['validate'] as $rule ) { |
||
468 | switch ( $rule ) { |
||
469 | case 'postcode' : |
||
470 | $this->posted[ $key ] = strtoupper( str_replace( ' ', '', $this->posted[ $key ] ) ); |
||
471 | |||
472 | if ( ! WC_Validation::is_postcode( $this->posted[ $key ], $_POST[ $fieldset_key . '_country' ] ) ) : |
||
473 | wc_add_notice( __( 'Please enter a valid postcode/ZIP.', 'woocommerce' ), 'error' ); |
||
474 | else : |
||
475 | $this->posted[ $key ] = wc_format_postcode( $this->posted[ $key ], $_POST[ $fieldset_key . '_country' ] ); |
||
476 | endif; |
||
477 | break; |
||
478 | View Code Duplication | case 'phone' : |
|
0 ignored issues
–
show
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. ![]() |
|||
479 | $this->posted[ $key ] = wc_format_phone_number( $this->posted[ $key ] ); |
||
480 | |||
481 | if ( ! WC_Validation::is_phone( $this->posted[ $key ] ) ) |
||
482 | wc_add_notice( '<strong>' . $field['label'] . '</strong> ' . __( 'is not a valid phone number.', 'woocommerce' ), 'error' ); |
||
483 | break; |
||
484 | View Code Duplication | case 'email' : |
|
0 ignored issues
–
show
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. ![]() |
|||
485 | $this->posted[ $key ] = strtolower( $this->posted[ $key ] ); |
||
486 | |||
487 | if ( ! is_email( $this->posted[ $key ] ) ) |
||
488 | wc_add_notice( '<strong>' . $field['label'] . '</strong> ' . __( 'is not a valid email address.', 'woocommerce' ), 'error' ); |
||
489 | break; |
||
490 | case 'state' : |
||
491 | // Get valid states |
||
492 | $valid_states = WC()->countries->get_states( isset( $_POST[ $fieldset_key . '_country' ] ) ? $_POST[ $fieldset_key . '_country' ] : ( 'billing' === $fieldset_key ? WC()->customer->get_country() : WC()->customer->get_shipping_country() ) ); |
||
493 | |||
494 | if ( ! empty( $valid_states ) && is_array( $valid_states ) ) { |
||
495 | $valid_state_values = array_flip( array_map( 'strtolower', $valid_states ) ); |
||
496 | |||
497 | // Convert value to key if set |
||
498 | if ( isset( $valid_state_values[ strtolower( $this->posted[ $key ] ) ] ) ) { |
||
499 | $this->posted[ $key ] = $valid_state_values[ strtolower( $this->posted[ $key ] ) ]; |
||
500 | } |
||
501 | } |
||
502 | |||
503 | // Only validate if the country has specific state options |
||
504 | if ( ! empty( $valid_states ) && is_array( $valid_states ) && sizeof( $valid_states ) > 0 ) { |
||
505 | if ( ! in_array( $this->posted[ $key ], array_keys( $valid_states ) ) ) { |
||
506 | wc_add_notice( '<strong>' . $field['label'] . '</strong> ' . __( 'is not valid. Please enter one of the following:', 'woocommerce' ) . ' ' . implode( ', ', $valid_states ), 'error' ); |
||
507 | } |
||
508 | } |
||
509 | break; |
||
510 | } |
||
511 | } |
||
512 | } |
||
513 | } |
||
514 | } |
||
515 | } |
||
516 | |||
517 | // Update customer location to posted location so we can correctly check available shipping methods |
||
518 | if ( isset( $this->posted['billing_country'] ) ) { |
||
519 | WC()->customer->set_country( $this->posted['billing_country'] ); |
||
520 | } |
||
521 | if ( isset( $this->posted['billing_state'] ) ) { |
||
522 | WC()->customer->set_state( $this->posted['billing_state'] ); |
||
523 | } |
||
524 | if ( isset( $this->posted['billing_postcode'] ) ) { |
||
525 | WC()->customer->set_postcode( $this->posted['billing_postcode'] ); |
||
526 | } |
||
527 | |||
528 | // Shipping Information |
||
529 | if ( ! $skipped_shipping ) { |
||
530 | |||
531 | // Update customer location to posted location so we can correctly check available shipping methods |
||
532 | if ( isset( $this->posted['shipping_country'] ) ) { |
||
533 | WC()->customer->set_shipping_country( $this->posted['shipping_country'] ); |
||
534 | } |
||
535 | if ( isset( $this->posted['shipping_state'] ) ) { |
||
536 | WC()->customer->set_shipping_state( $this->posted['shipping_state'] ); |
||
537 | } |
||
538 | if ( isset( $this->posted['shipping_postcode'] ) ) { |
||
539 | WC()->customer->set_shipping_postcode( $this->posted['shipping_postcode'] ); |
||
540 | } |
||
541 | |||
542 | } else { |
||
543 | |||
544 | // Update customer location to posted location so we can correctly check available shipping methods |
||
545 | if ( isset( $this->posted['billing_country'] ) ) { |
||
546 | WC()->customer->set_shipping_country( $this->posted['billing_country'] ); |
||
547 | } |
||
548 | if ( isset( $this->posted['billing_state'] ) ) { |
||
549 | WC()->customer->set_shipping_state( $this->posted['billing_state'] ); |
||
550 | } |
||
551 | if ( isset( $this->posted['billing_postcode'] ) ) { |
||
552 | WC()->customer->set_shipping_postcode( $this->posted['billing_postcode'] ); |
||
553 | } |
||
554 | |||
555 | } |
||
556 | |||
557 | // Update cart totals now we have customer address |
||
558 | WC()->cart->calculate_totals(); |
||
559 | |||
560 | // Terms |
||
561 | if ( ! isset( $_POST['woocommerce_checkout_update_totals'] ) && empty( $this->posted['terms'] ) && wc_get_page_id( 'terms' ) > 0 && apply_filters( 'woocommerce_checkout_show_terms', true ) ) { |
||
562 | wc_add_notice( __( 'You must accept our Terms & Conditions.', 'woocommerce' ), 'error' ); |
||
563 | } |
||
564 | |||
565 | if ( WC()->cart->needs_shipping() ) { |
||
566 | $shipping_country = WC()->customer->get_shipping_country(); |
||
567 | |||
568 | if ( empty( $shipping_country ) ) { |
||
569 | wc_add_notice( __( 'Please enter an address to continue.', 'woocommerce' ), 'error' ); |
||
570 | } elseif ( ! in_array( WC()->customer->get_shipping_country(), array_keys( WC()->countries->get_shipping_countries() ) ) ) { |
||
571 | wc_add_notice( sprintf( __( 'Unfortunately <strong>we do not ship %s</strong>. Please enter an alternative shipping address.', 'woocommerce' ), WC()->countries->shipping_to_prefix() . ' ' . WC()->customer->get_shipping_country() ), 'error' ); |
||
572 | } |
||
573 | |||
574 | // Validate Shipping Methods |
||
575 | $packages = WC()->shipping->get_packages(); |
||
576 | $this->shipping_methods = WC()->session->get( 'chosen_shipping_methods' ); |
||
0 ignored issues
–
show
It seems like
WC()->session->get('chosen_shipping_methods') can also be of type string . However, the property $shipping_methods is declared as type array . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
577 | |||
578 | foreach ( $packages as $i => $package ) { |
||
579 | if ( ! isset( $package['rates'][ $this->shipping_methods[ $i ] ] ) ) { |
||
580 | wc_add_notice( __( 'No shipping method has been selected. Please double check your address, or contact us if you need any help.', 'woocommerce' ), 'error' ); |
||
581 | $this->shipping_methods[ $i ] = ''; |
||
582 | } |
||
583 | } |
||
584 | } |
||
585 | |||
586 | if ( WC()->cart->needs_payment() ) { |
||
587 | // Payment Method |
||
588 | $available_gateways = WC()->payment_gateways->get_available_payment_gateways(); |
||
589 | |||
590 | if ( ! isset( $available_gateways[ $this->posted['payment_method'] ] ) ) { |
||
591 | $this->payment_method = ''; |
||
592 | wc_add_notice( __( 'Invalid payment method.', 'woocommerce' ), 'error' ); |
||
593 | } else { |
||
594 | $this->payment_method = $available_gateways[ $this->posted['payment_method'] ]; |
||
595 | $this->payment_method->validate_fields(); |
||
596 | } |
||
597 | } else { |
||
598 | $available_gateways = array(); |
||
599 | } |
||
600 | |||
601 | // Action after validation |
||
602 | do_action( 'woocommerce_after_checkout_validation', $this->posted ); |
||
603 | |||
604 | if ( ! isset( $_POST['woocommerce_checkout_update_totals'] ) && wc_notice_count( 'error' ) == 0 ) { |
||
605 | |||
606 | // Customer accounts |
||
607 | $this->customer_id = apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ); |
||
608 | |||
609 | if ( ! is_user_logged_in() && ( $this->must_create_account || ! empty( $this->posted['createaccount'] ) ) ) { |
||
610 | |||
611 | $username = ! empty( $this->posted['account_username'] ) ? $this->posted['account_username'] : ''; |
||
612 | $password = ! empty( $this->posted['account_password'] ) ? $this->posted['account_password'] : ''; |
||
613 | $new_customer = wc_create_new_customer( $this->posted['billing_email'], $username, $password ); |
||
614 | |||
615 | if ( is_wp_error( $new_customer ) ) { |
||
616 | throw new Exception( $new_customer->get_error_message() ); |
||
617 | } |
||
618 | |||
619 | $this->customer_id = $new_customer; |
||
0 ignored issues
–
show
It seems like
$new_customer can also be of type object<WP_Error> . However, the property $customer_id is declared as type integer . Maybe add an additional type check?
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly. For example, imagine you have a variable Either this assignment is in error or a type check should be added for that assignment. class Id
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
}
class Account
{
/** @var Id $id */
public $id;
}
$account_id = false;
if (starsAreRight()) {
$account_id = new Id(42);
}
$account = new Account();
if ($account instanceof Id)
{
$account->id = $account_id;
}
![]() |
|||
620 | |||
621 | wc_set_customer_auth_cookie( $this->customer_id ); |
||
622 | |||
623 | // As we are now logged in, checkout will need to refresh to show logged in data |
||
624 | WC()->session->set( 'reload_checkout', true ); |
||
625 | |||
626 | // Also, recalculate cart totals to reveal any role-based discounts that were unavailable before registering |
||
627 | WC()->cart->calculate_totals(); |
||
628 | |||
629 | // Add customer info from other billing fields |
||
630 | if ( $this->posted['billing_first_name'] && apply_filters( 'woocommerce_checkout_update_customer_data', true, $this ) ) { |
||
631 | $userdata = array( |
||
632 | 'ID' => $this->customer_id, |
||
633 | 'first_name' => $this->posted['billing_first_name'] ? $this->posted['billing_first_name'] : '', |
||
634 | 'last_name' => $this->posted['billing_last_name'] ? $this->posted['billing_last_name'] : '', |
||
635 | 'display_name' => $this->posted['billing_first_name'] ? $this->posted['billing_first_name'] : '' |
||
636 | ); |
||
637 | wp_update_user( apply_filters( 'woocommerce_checkout_customer_userdata', $userdata, $this ) ); |
||
638 | } |
||
639 | } |
||
640 | |||
641 | // Do a final stock check at this point |
||
642 | $this->check_cart_items(); |
||
643 | |||
644 | // Abort if errors are present |
||
645 | if ( wc_notice_count( 'error' ) > 0 ) |
||
646 | throw new Exception(); |
||
647 | |||
648 | $order_id = $this->create_order(); |
||
649 | |||
650 | if ( is_wp_error( $order_id ) ) { |
||
651 | throw new Exception( $order_id->get_error_message() ); |
||
652 | } |
||
653 | |||
654 | do_action( 'woocommerce_checkout_order_processed', $order_id, $this->posted ); |
||
655 | |||
656 | // Process payment |
||
657 | if ( WC()->cart->needs_payment() ) { |
||
658 | |||
659 | // Store Order ID in session so it can be re-used after payment failure |
||
660 | WC()->session->order_awaiting_payment = $order_id; |
||
661 | |||
662 | // Process Payment |
||
663 | $result = $available_gateways[ $this->posted['payment_method'] ]->process_payment( $order_id ); |
||
664 | |||
665 | // Redirect to success/confirmation/payment page |
||
666 | if ( isset( $result['result'] ) && 'success' === $result['result'] ) { |
||
667 | |||
668 | $result = apply_filters( 'woocommerce_payment_successful_result', $result, $order_id ); |
||
669 | |||
670 | if ( is_ajax() ) { |
||
671 | wp_send_json( $result ); |
||
672 | } else { |
||
673 | wp_redirect( $result['redirect'] ); |
||
674 | exit; |
||
675 | } |
||
676 | |||
677 | } |
||
678 | |||
679 | } else { |
||
680 | |||
681 | if ( empty( $order ) ) { |
||
682 | $order = wc_get_order( $order_id ); |
||
683 | } |
||
684 | |||
685 | // No payment was required for order |
||
686 | $order->payment_complete(); |
||
687 | |||
688 | // Empty the Cart |
||
689 | WC()->cart->empty_cart(); |
||
690 | |||
691 | // Get redirect |
||
692 | $return_url = $order->get_checkout_order_received_url(); |
||
693 | |||
694 | // Redirect to success/confirmation/payment page |
||
695 | if ( is_ajax() ) { |
||
696 | wp_send_json( array( |
||
697 | 'result' => 'success', |
||
698 | 'redirect' => apply_filters( 'woocommerce_checkout_no_payment_needed_redirect', $return_url, $order ) |
||
699 | ) ); |
||
700 | } else { |
||
701 | wp_safe_redirect( |
||
702 | apply_filters( 'woocommerce_checkout_no_payment_needed_redirect', $return_url, $order ) |
||
703 | ); |
||
704 | exit; |
||
705 | } |
||
706 | |||
707 | } |
||
708 | |||
709 | } |
||
710 | |||
711 | } catch ( Exception $e ) { |
||
712 | if ( ! empty( $e ) ) { |
||
713 | wc_add_notice( $e->getMessage(), 'error' ); |
||
714 | } |
||
715 | } |
||
716 | |||
717 | // If we reached this point then there were errors |
||
718 | if ( is_ajax() ) { |
||
719 | |||
720 | // only print notices if not reloading the checkout, otherwise they're lost in the page reload |
||
721 | if ( ! isset( WC()->session->reload_checkout ) ) { |
||
722 | ob_start(); |
||
723 | wc_print_notices(); |
||
724 | $messages = ob_get_clean(); |
||
725 | } |
||
726 | |||
727 | $response = array( |
||
728 | 'result' => 'failure', |
||
729 | 'messages' => isset( $messages ) ? $messages : '', |
||
730 | 'refresh' => isset( WC()->session->refresh_totals ) ? 'true' : 'false', |
||
731 | 'reload' => isset( WC()->session->reload_checkout ) ? 'true' : 'false' |
||
732 | ); |
||
733 | |||
734 | unset( WC()->session->refresh_totals, WC()->session->reload_checkout ); |
||
735 | |||
736 | wp_send_json( $response ); |
||
737 | } |
||
738 | } |
||
739 | |||
740 | /** |
||
741 | * Get a posted address field after sanitization and validation. |
||
742 | * @param string $key |
||
743 | * @param string $type billing for shipping |
||
744 | * @return string |
||
745 | */ |
||
746 | public function get_posted_address_data( $key, $type = 'billing' ) { |
||
747 | if ( 'billing' === $type || false === $this->posted['ship_to_different_address'] ) { |
||
748 | $return = isset( $this->posted[ 'billing_' . $key ] ) ? $this->posted[ 'billing_' . $key ] : ''; |
||
749 | } else { |
||
750 | $return = isset( $this->posted[ 'shipping_' . $key ] ) ? $this->posted[ 'shipping_' . $key ] : ''; |
||
751 | } |
||
752 | |||
753 | // Use logged in user's billing email if neccessary |
||
754 | if ( 'email' === $key && empty( $return ) && is_user_logged_in() ) { |
||
755 | $current_user = wp_get_current_user(); |
||
756 | $return = $current_user->user_email; |
||
757 | } |
||
758 | return $return; |
||
759 | } |
||
760 | |||
761 | /** |
||
762 | * Gets the value either from the posted data, or from the users meta data. |
||
763 | * |
||
764 | * @access public |
||
765 | * @param string $input |
||
766 | * @return string|null |
||
767 | */ |
||
768 | public function get_value( $input ) { |
||
769 | if ( ! empty( $_POST[ $input ] ) ) { |
||
770 | |||
771 | return wc_clean( $_POST[ $input ] ); |
||
772 | |||
773 | } else { |
||
774 | |||
775 | $value = apply_filters( 'woocommerce_checkout_get_value', null, $input ); |
||
776 | |||
777 | if ( $value !== null ) { |
||
778 | return $value; |
||
779 | } |
||
780 | |||
781 | // Get the billing_ and shipping_ address fields |
||
782 | if ( isset( $this->checkout_fields['shipping'] ) && isset( $this->checkout_fields['billing'] ) ) { |
||
783 | |||
784 | $address_fields = array_merge( $this->checkout_fields['billing'], $this->checkout_fields['shipping'] ); |
||
785 | |||
786 | if ( is_user_logged_in() && is_array( $address_fields ) && array_key_exists( $input, $address_fields ) ) { |
||
787 | $current_user = wp_get_current_user(); |
||
788 | |||
789 | if ( $meta = get_user_meta( $current_user->ID, $input, true ) ) { |
||
790 | return $meta; |
||
791 | } |
||
792 | |||
793 | if ( $input == 'billing_email' ) { |
||
794 | return $current_user->user_email; |
||
795 | } |
||
796 | } |
||
797 | |||
798 | } |
||
799 | |||
800 | switch ( $input ) { |
||
801 | case 'billing_country' : |
||
802 | return apply_filters( 'default_checkout_country', WC()->customer->get_country() ? WC()->customer->get_country() : '', 'billing' ); |
||
803 | case 'billing_state' : |
||
804 | return apply_filters( 'default_checkout_state', WC()->customer->get_state() ? WC()->customer->get_state() : '', 'billing' ); |
||
805 | case 'billing_postcode' : |
||
806 | return apply_filters( 'default_checkout_postcode', WC()->customer->get_postcode() ? WC()->customer->get_postcode() : '', 'billing' ); |
||
807 | case 'shipping_country' : |
||
808 | return apply_filters( 'default_checkout_country', WC()->customer->get_shipping_country() ? WC()->customer->get_shipping_country() : '', 'shipping' ); |
||
809 | case 'shipping_state' : |
||
810 | return apply_filters( 'default_checkout_state', WC()->customer->get_shipping_state() ? WC()->customer->get_shipping_state() : '', 'shipping' ); |
||
811 | case 'shipping_postcode' : |
||
812 | return apply_filters( 'default_checkout_postcode', WC()->customer->get_shipping_postcode() ? WC()->customer->get_shipping_postcode() : '', 'shipping' ); |
||
813 | default : |
||
814 | return apply_filters( 'default_checkout_' . $input, null, $input ); |
||
815 | } |
||
816 | } |
||
817 | } |
||
818 | } |
||
819 |
This check marks private properties in classes that are never used. Those properties can be removed.