Completed
Push — master ( 311c54...9cca27 )
by Mike
15:59
created

WC_Gateway_COD   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 220
Duplicated Lines 1.36 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 3
loc 220
rs 9
wmc 35
lcom 1
cbo 6

6 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 24 2
A init_form_fields() 0 57 2
F is_available() 0 74 23
A process_payment() 0 18 2
A thankyou_page() 0 5 2
A email_instructions() 3 5 4

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit; // Exit if accessed directly
5
}
6
7
/**
8
 * Cash on Delivery Gateway.
9
 *
10
 * Provides a Cash on Delivery Payment Gateway.
11
 *
12
 * @class 		WC_Gateway_COD
13
 * @extends		WC_Payment_Gateway
14
 * @version		2.1.0
15
 * @package		WooCommerce/Classes/Payment
16
 * @author 		WooThemes
17
 */
18
class WC_Gateway_COD extends WC_Payment_Gateway {
19
20
	/**
21
	 * Constructor for the gateway.
22
	 */
23
	public function __construct() {
24
		$this->id                 = 'cod';
25
		$this->icon               = apply_filters( 'woocommerce_cod_icon', '' );
26
		$this->method_title       = __( 'Cash on Delivery', 'woocommerce' );
27
		$this->method_description = __( 'Have your customers pay with cash (or by other means) upon delivery.', 'woocommerce' );
28
		$this->has_fields         = false;
29
30
		// Load the settings
31
		$this->init_form_fields();
32
		$this->init_settings();
33
34
		// Get settings
35
		$this->title              = $this->get_option( 'title' );
36
		$this->description        = $this->get_option( 'description' );
37
		$this->instructions       = $this->get_option( 'instructions', $this->description );
38
		$this->enable_for_methods = $this->get_option( 'enable_for_methods', array() );
39
		$this->enable_for_virtual = $this->get_option( 'enable_for_virtual', 'yes' ) === 'yes' ? true : false;
40
41
		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
42
		add_action( 'woocommerce_thankyou_cod', array( $this, 'thankyou_page' ) );
43
44
		// Customer Emails
45
		add_action( 'woocommerce_email_before_order_table', array( $this, 'email_instructions' ), 10, 3 );
46
	}
47
48
	/**
49
	 * Initialise Gateway Settings Form Fields.
50
	 */
51
	public function init_form_fields() {
52
		$shipping_methods = array();
53
54
		foreach ( WC()->shipping()->load_shipping_methods() as $method ) {
0 ignored issues
show
Bug introduced by
The expression WC()->shipping()->load_shipping_methods() of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
55
			$shipping_methods[ $method->id ] = $method->get_method_title();
56
		}
57
58
		$this->form_fields = array(
59
			'enabled' => array(
60
				'title'       => __( 'Enable COD', 'woocommerce' ),
61
				'label'       => __( 'Enable Cash on Delivery', 'woocommerce' ),
62
				'type'        => 'checkbox',
63
				'description' => '',
64
				'default'     => 'no',
65
			),
66
			'title' => array(
67
				'title'       => __( 'Title', 'woocommerce' ),
68
				'type'        => 'text',
69
				'description' => __( 'Payment method description that the customer will see on your checkout.', 'woocommerce' ),
70
				'default'     => __( 'Cash on Delivery', 'woocommerce' ),
71
				'desc_tip'    => true,
72
			),
73
			'description' => array(
74
				'title'       => __( 'Description', 'woocommerce' ),
75
				'type'        => 'textarea',
76
				'description' => __( 'Payment method description that the customer will see on your website.', 'woocommerce' ),
77
				'default'     => __( 'Pay with cash upon delivery.', 'woocommerce' ),
78
				'desc_tip'    => true,
79
			),
80
			'instructions' => array(
81
				'title'       => __( 'Instructions', 'woocommerce' ),
82
				'type'        => 'textarea',
83
				'description' => __( 'Instructions that will be added to the thank you page.', 'woocommerce' ),
84
				'default'     => __( 'Pay with cash upon delivery.', 'woocommerce' ),
85
				'desc_tip'    => true,
86
			),
87
			'enable_for_methods' => array(
88
				'title'             => __( 'Enable for shipping methods', 'woocommerce' ),
89
				'type'              => 'multiselect',
90
				'class'             => 'wc-enhanced-select',
91
				'css'               => 'width: 450px;',
92
				'default'           => '',
93
				'description'       => __( 'If COD is only available for certain methods, set it up here. Leave blank to enable for all methods.', 'woocommerce' ),
94
				'options'           => $shipping_methods,
95
				'desc_tip'          => true,
96
				'custom_attributes' => array(
97
					'data-placeholder' => __( 'Select shipping methods', 'woocommerce' ),
98
				),
99
			),
100
			'enable_for_virtual' => array(
101
				'title'             => __( 'Accept for virtual orders', 'woocommerce' ),
102
				'label'             => __( 'Accept COD if the order is virtual', 'woocommerce' ),
103
				'type'              => 'checkbox',
104
				'default'           => 'yes',
105
			),
106
	   );
107
	}
108
109
	/**
110
	 * Check If The Gateway Is Available For Use.
111
	 *
112
	 * @return bool
113
	 */
114
	public function is_available() {
115
		$order          = null;
116
		$needs_shipping = false;
117
118
		// Test if shipping is needed first
119
		if ( WC()->cart && WC()->cart->needs_shipping() ) {
120
			$needs_shipping = true;
121
		} elseif ( is_page( wc_get_page_id( 'checkout' ) ) && 0 < get_query_var( 'order-pay' ) ) {
122
			$order_id = absint( get_query_var( 'order-pay' ) );
123
			$order    = wc_get_order( $order_id );
124
125
			// Test if order needs shipping.
126
			if ( 0 < sizeof( $order->get_items() ) ) {
127
				foreach ( $order->get_items() as $item ) {
128
					$_product = $item->get_product();
129
					if ( $_product && $_product->needs_shipping() ) {
130
						$needs_shipping = true;
131
						break;
132
					}
133
				}
134
			}
135
		}
136
137
		$needs_shipping = apply_filters( 'woocommerce_cart_needs_shipping', $needs_shipping );
138
139
		// Virtual order, with virtual disabled
140
		if ( ! $this->enable_for_virtual && ! $needs_shipping ) {
141
			return false;
142
		}
143
144
		// Check methods
145
		if ( ! empty( $this->enable_for_methods ) && $needs_shipping ) {
146
147
			// Only apply if all packages are being shipped via chosen methods, or order is virtual
148
			$chosen_shipping_methods_session = WC()->session->get( 'chosen_shipping_methods' );
149
150
			if ( isset( $chosen_shipping_methods_session ) ) {
151
				$chosen_shipping_methods = array_unique( $chosen_shipping_methods_session );
152
			} else {
153
				$chosen_shipping_methods = array();
154
			}
155
156
			$check_method = false;
157
158
			if ( is_object( $order ) ) {
159
				if ( $order->shipping_method ) {
160
					$check_method = $order->shipping_method;
161
				}
162
			} elseif ( empty( $chosen_shipping_methods ) || sizeof( $chosen_shipping_methods ) > 1 ) {
163
				$check_method = false;
164
			} elseif ( sizeof( $chosen_shipping_methods ) == 1 ) {
165
				$check_method = $chosen_shipping_methods[0];
166
			}
167
168
			if ( ! $check_method ) {
169
				return false;
170
			}
171
172
			$found = false;
173
174
			foreach ( $this->enable_for_methods as $method_id ) {
175
				if ( strpos( $check_method, $method_id ) === 0 ) {
176
					$found = true;
177
					break;
178
				}
179
			}
180
181
			if ( ! $found ) {
182
				return false;
183
			}
184
		}
185
186
		return parent::is_available();
187
	}
188
189
190
	/**
191
	 * Process the payment and return the result.
192
	 *
193
	 * @param int $order_id
194
	 * @return array
195
	 */
196
	public function process_payment( $order_id ) {
197
		$order = wc_get_order( $order_id );
198
199
		// Mark as processing or on-hold (payment won't be taken until delivery)
200
		$order->update_status( apply_filters( 'woocommerce_cod_process_payment_order_status', $order->has_downloadable_item() ? 'on-hold' : 'processing', $order ), __( 'Payment to be made upon delivery.', 'woocommerce' ) );
201
202
		// Reduce stock levels
203
		wc_reduce_stock_levels( $order_id );
204
205
		// Remove cart
206
		WC()->cart->empty_cart();
207
208
		// Return thankyou redirect
209
		return array(
210
			'result' 	=> 'success',
211
			'redirect'	=> $this->get_return_url( $order ),
0 ignored issues
show
Documentation introduced by
$order is of type false|object, but the function expects a object<WC_Order>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
212
		);
213
	}
214
215
	/**
216
	 * Output for the order received page.
217
	 */
218
	public function thankyou_page() {
219
		if ( $this->instructions ) {
220
			echo wpautop( wptexturize( $this->instructions ) );
221
		}
222
	}
223
224
	/**
225
	 * Add content to the WC emails.
226
	 *
227
	 * @access public
228
	 * @param WC_Order $order
229
	 * @param bool $sent_to_admin
230
	 * @param bool $plain_text
231
	 */
232
	public function email_instructions( $order, $sent_to_admin, $plain_text = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $plain_text 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...
233 View Code Duplication
		if ( $this->instructions && ! $sent_to_admin && 'cod' === $order->get_payment_method() ) {
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...
234
			echo wpautop( wptexturize( $this->instructions ) ) . PHP_EOL;
235
		}
236
	}
237
}
238