Completed
Pull Request — master (#9826)
by Mike
10:01
created

WC_Countries::get_continents()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 6
rs 9.4286
cc 2
eloc 4
nc 2
nop 0
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 18 and the first side effect is on line 4.

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
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit; // Exit if accessed directly
5
}
6
7
/**
8
 * WooCommerce countries
9
 *
10
 * The WooCommerce countries class stores country/state data.
11
 *
12
 * @class       WC_Countries
13
 * @version     2.3.0
14
 * @package     WooCommerce/Classes
15
 * @category    Class
16
 * @author      WooThemes
17
 */
18
class WC_Countries {
19
20
	/** @var array Array of locales */
21
	public $locale;
22
23
	/** @var array Array of address formats for locales */
24
	public $address_formats;
25
26
	/**
27
	 * Auto-load in-accessible properties on demand.
28
	 * @param  mixed $key
29
	 * @return mixed
30
	 */
31
	public function __get( $key ) {
32
		if ( 'countries' == $key ) {
33
			return $this->get_countries();
34
		} elseif ( 'states' == $key ) {
35
			return $this->get_states();
36
		}
37
	}
38
39
	/**
40
	 * Get all countries.
41
	 * @return array
42
	 */
43
	public function get_countries() {
44
		if ( empty( $this->countries ) ) {
45
			$this->countries = apply_filters( 'woocommerce_countries', include( WC()->plugin_path() . '/i18n/countries.php' ) );
46
			if ( apply_filters( 'woocommerce_sort_countries', true ) ) {
47
				asort( $this->countries );
48
			}
49
		}
50
		return $this->countries;
51
	}
52
53
	/**
54
	 * Get all continents.
55
	 * @return array
56
	 */
57
	public function get_continents() {
58
		if ( empty( $this->continents ) ) {
59
			$this->continents = apply_filters( 'woocommerce_continents', include( WC()->plugin_path() . '/i18n/continents.php' ) );
60
		}
61
		return $this->continents;
62
	}
63
64
	/**
65
	 * Load the states.
66
	 */
67
	public function load_country_states() {
68
		global $states;
69
70
		// States set to array() are blank i.e. the country has no use for the state field.
71
		$states = array(
72
			'AF' => array(),
73
			'AT' => array(),
74
			'AX' => array(),
75
			'BE' => array(),
76
			'BI' => array(),
77
			'CZ' => array(),
78
			'DE' => array(),
79
			'DK' => array(),
80
			'EE' => array(),
81
			'FI' => array(),
82
			'FR' => array(),
83
			'IS' => array(),
84
			'IL' => array(),
85
			'KR' => array(),
86
			'NL' => array(),
87
			'NO' => array(),
88
			'PL' => array(),
89
			'PT' => array(),
90
			'SG' => array(),
91
			'SK' => array(),
92
			'SI' => array(),
93
			'LK' => array(),
94
			'SE' => array(),
95
			'VN' => array(),
96
		);
97
98
		// Load only the state files the shop owner wants/needs.
99
		$allowed = array_merge( $this->get_allowed_countries(), $this->get_shipping_countries() );
100
101
		if ( $allowed ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $allowed of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
102
			foreach ( $allowed as $code => $country ) {
103
				if ( ! isset( $states[ $code ] ) && file_exists( WC()->plugin_path() . '/i18n/states/' . $code . '.php' ) ) {
104
					include( WC()->plugin_path() . '/i18n/states/' . $code . '.php' );
105
				}
106
			}
107
		}
108
109
		$this->states = apply_filters( 'woocommerce_states', $states );
110
	}
111
112
	/**
113
	 * Get the states for a country.
114
	 * @param  string $cc country code
115
	 * @return array of states
116
	 */
117
	public function get_states( $cc = null ) {
118
		if ( empty( $this->states ) ) {
119
			$this->load_country_states();
120
		}
121
122
		if ( ! is_null( $cc ) ) {
123
			return isset( $this->states[ $cc ] ) ? $this->states[ $cc ] : false;
124
		} else {
125
			return $this->states;
126
		}
127
	}
128
129
	/**
130
	 * Get the base country for the store.
131
	 * @return string
132
	 */
133
	public function get_base_country() {
134
		$default = wc_get_base_location();
135
		return apply_filters( 'woocommerce_countries_base_country', $default['country'] );
136
	}
137
138
	/**
139
	 * Get the base state for the store.
140
	 * @return string
141
	 */
142
	public function get_base_state() {
143
		$default = wc_get_base_location();
144
		return apply_filters( 'woocommerce_countries_base_state', $default['state'] );
145
	}
146
147
	/**
148
	 * Get the base city for the store.
149
	 * @return string
150
	 */
151
	public function get_base_city() {
152
		return apply_filters( 'woocommerce_countries_base_city', '' );
153
	}
154
155
	/**
156
	 * Get the base postcode for the store.
157
	 * @return string
158
	 */
159
	public function get_base_postcode() {
160
		return apply_filters( 'woocommerce_countries_base_postcode', '' );
161
	}
162
163
	/**
164
	 * Get the allowed countries for the store.
165
	 * @return array
166
	 */
167 View Code Duplication
	public function get_allowed_countries() {
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...
168
		if ( get_option( 'woocommerce_allowed_countries' ) !== 'specific' ) {
169
			return $this->countries;
170
		}
171
172
		$countries = array();
173
174
		$raw_countries = get_option( 'woocommerce_specific_allowed_countries' );
175
176
		foreach ( $raw_countries as $country ) {
177
			$countries[ $country ] = $this->countries[ $country ];
178
		}
179
180
		return apply_filters( 'woocommerce_countries_allowed_countries', $countries );
181
	}
182
183
	/**
184
	 * Get the countries you ship to.
185
	 * @return array
186
	 */
187
	public function get_shipping_countries() {
188
		if ( get_option( 'woocommerce_ship_to_countries' ) == '' ) {
189
			return $this->get_allowed_countries();
190
		}
191
192
		if ( get_option( 'woocommerce_ship_to_countries' ) !== 'specific' ) {
193
			return $this->countries;
194
		}
195
196
		$countries = array();
197
198
		$raw_countries = get_option( 'woocommerce_specific_ship_to_countries' );
199
200
		foreach ( $raw_countries as $country ) {
201
			$countries[ $country ] = $this->countries[ $country ];
202
		}
203
204
		return apply_filters( 'woocommerce_countries_shipping_countries', $countries );
205
	}
206
207
	/**
208
	 * get_allowed_country_states function.
209
	 * @return array
210
	 */
211 View Code Duplication
	public function get_allowed_country_states() {
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...
212
		if ( get_option( 'woocommerce_allowed_countries' ) !== 'specific' ) {
213
			return $this->states;
214
		}
215
216
		$states = array();
217
218
		$raw_countries = get_option( 'woocommerce_specific_allowed_countries' );
219
220
		foreach ( $raw_countries as $country ) {
221
			if ( isset( $this->states[ $country ] ) ) {
222
				$states[ $country ] = $this->states[ $country ];
223
			}
224
		}
225
226
		return apply_filters( 'woocommerce_countries_allowed_country_states', $states );
227
	}
228
229
	/**
230
	 * get_shipping_country_states function.
231
	 * @return array
232
	 */
233
	public function get_shipping_country_states() {
234
		if ( get_option( 'woocommerce_ship_to_countries' ) == '' ) {
235
			return $this->get_allowed_country_states();
236
		}
237
238
		if ( get_option( 'woocommerce_ship_to_countries' ) !== 'specific' ) {
239
			return $this->states;
240
		}
241
242
		$states = array();
243
244
		$raw_countries = get_option( 'woocommerce_specific_ship_to_countries' );
245
246
		foreach ( $raw_countries as $country ) {
247
			if ( ! empty( $this->states[ $country ] ) ) {
248
				$states[ $country ] = $this->states[ $country ];
249
			}
250
		}
251
252
		return apply_filters( 'woocommerce_countries_shipping_country_states', $states );
253
	}
254
255
	/**
256
	 * Gets an array of countries in the EU.
257
	 *
258
	 * MC (monaco) and IM (isle of man, part of UK) also use VAT.
259
	 *
260
	 * @param  $type Type of countries to retrieve. Blank for EU member countries. eu_vat for EU VAT countries.
261
	 * @return string[]
262
	 */
263
	public function get_european_union_countries( $type = '' ) {
264
		$countries = array( 'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HU', 'HR', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK' );
265
266
		if ( 'eu_vat' === $type ) {
267
			$countries[] = 'MC';
268
			$countries[] = 'IM';
269
		}
270
271
		return $countries;
272
	}
273
274
	/**
275
	 * Gets the correct string for shipping - either 'to the' or 'to'
276
	 * @return string
277
	 */
278
	public function shipping_to_prefix() {
279
		$countries  = array( 'GB', 'US', 'AE', 'CZ', 'DO', 'NL', 'PH', 'USAF' );
280
		$return     = in_array( WC()->customer->get_shipping_country(), $countries ) ? __( 'to the', 'woocommerce' ) : __( 'to', 'woocommerce' );
281
282
		return apply_filters( 'woocommerce_countries_shipping_to_prefix', $return, WC()->customer->get_shipping_country() );
283
	}
284
285
	/**
286
	 * Prefix certain countries with 'the'
287
	 * @return string
288
	 */
289
	public function estimated_for_prefix() {
290
		$countries  = array( 'GB', 'US', 'AE', 'CZ', 'DO', 'NL', 'PH', 'USAF' );
291
		$return     = in_array( $this->get_base_country(), $countries ) ? __( 'the', 'woocommerce' ) . ' ' : '';
292
293
		return apply_filters( 'woocommerce_countries_estimated_for_prefix', $return, $this->get_base_country() );
294
	}
295
296
	/**
297
	 * Correctly name tax in some countries VAT on the frontend.
298
	 * @return string
299
	 */
300
	public function tax_or_vat() {
301
		$return = in_array( $this->get_base_country(), $this->get_european_union_countries( 'eu_vat' ) ) ? __( 'VAT', 'woocommerce' ) : __( 'Tax', 'woocommerce' );
302
303
		return apply_filters( 'woocommerce_countries_tax_or_vat', $return );
304
	}
305
306
	/**
307
	 * Include the Inc Tax label.
308
	 * @return string
309
	 */
310
	public function inc_tax_or_vat() {
311
		$return = in_array( $this->get_base_country(), $this->get_european_union_countries( 'eu_vat' ) ) ? __( '(incl. VAT)', 'woocommerce' ) : __( '(incl. tax)', 'woocommerce' );
312
313
		return apply_filters( 'woocommerce_countries_inc_tax_or_vat', $return );
314
	}
315
316
	/**
317
	 * Include the Ex Tax label.
318
	 * @return string
319
	 */
320
	public function ex_tax_or_vat() {
321
		$return = in_array( $this->get_base_country(), $this->get_european_union_countries( 'eu_vat' ) ) ? __( '(ex. VAT)', 'woocommerce' ) : __( '(ex. tax)', 'woocommerce' );
322
323
		return apply_filters( 'woocommerce_countries_ex_tax_or_vat', $return );
324
	}
325
326
	/**
327
	 * Outputs the list of countries and states for use in dropdown boxes.
328
	 * @param string $selected_country (default: '')
329
	 * @param string $selected_state (default: '')
330
	 * @param bool   $escape (default: false)
331
	 */
332
	public function country_dropdown_options( $selected_country = '', $selected_state = '', $escape = false ) {
333
		if ( $this->countries ) foreach ( $this->countries as $key => $value ) :
334
			if ( $states = $this->get_states( $key ) ) :
335
				echo '<optgroup label="' . esc_attr( $value ) . '">';
336
					foreach ( $states as $state_key => $state_value ) :
337
						echo '<option value="' . esc_attr( $key ) . ':' . $state_key . '"';
338
339
						if ( $selected_country == $key && $selected_state == $state_key ) {
340
							echo ' selected="selected"';
341
						}
342
343
						echo '>' . $value . ' &mdash; ' . ( $escape ? esc_js( $state_value ) : $state_value ) . '</option>';
344
					endforeach;
345
				echo '</optgroup>';
346
			else :
347
				echo '<option';
348
				if ( $selected_country == $key && $selected_state == '*' ) {
349
					echo ' selected="selected"';
350
				}
351
				echo ' value="' . esc_attr( $key ) . '">' . ( $escape ? esc_js( $value ) : $value ) . '</option>';
352
			endif;
353
		endforeach;
354
	}
355
356
	/**
357
	 * Get country address formats.
358
	 * @return array
359
	 */
360
	public function get_address_formats() {
361
		if ( ! $this->address_formats ) :
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->address_formats of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
362
363
			// Common formats
364
			$postcode_before_city = "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}";
365
366
			// Define address formats
367
			$this->address_formats = apply_filters( 'woocommerce_localisation_address_formats', array(
368
				'default' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode}\n{country}",
369
				'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
370
				'AT' => $postcode_before_city,
371
				'BE' => $postcode_before_city,
372
				'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
373
				'CH' => $postcode_before_city,
374
				'CL' => "{company}\n{name}\n{address_1}\n{address_2}\n{state}\n{postcode} {city}\n{country}",
375
				'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",
376
				'CZ' => $postcode_before_city,
377
				'DE' => $postcode_before_city,
378
				'EE' => $postcode_before_city,
379
				'FI' => $postcode_before_city,
380
				'DK' => $postcode_before_city,
381
				'FR' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city_upper}\n{country}",
382
				'HK' => "{company}\n{first_name} {last_name_upper}\n{address_1}\n{address_2}\n{city_upper}\n{state_upper}\n{country}",
383
				'HU' => "{name}\n{company}\n{city}\n{address_1}\n{address_2}\n{postcode}\n{country}",
384
				'IN' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} - {postcode}\n{state}, {country}",
385
				'IS' => $postcode_before_city,
386
				'IT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode}\n{city}\n{state_upper}\n{country}",
387
				'JP' => "{postcode}\n{state}{city}{address_1}\n{address_2}\n{company}\n{last_name} {first_name}\n{country}",
388
				'TW' => "{company}\n{last_name} {first_name}\n{address_1}\n{address_2}\n{state}, {city} {postcode}\n{country}",
389
				'LI' => $postcode_before_city,
390
				'NL' => $postcode_before_city,
391
				'NZ' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {postcode}\n{country}",
392
				'NO' => $postcode_before_city,
393
				'PL' => $postcode_before_city,
394
				'SK' => $postcode_before_city,
395
				'SI' => $postcode_before_city,
396
				'ES' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city}\n{state}\n{country}",
397
				'SE' => $postcode_before_city,
398
				'TR' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city} {state}\n{country}",
399
				'US' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}, {state_code} {postcode}\n{country}",
400
				'VN' => "{name}\n{company}\n{address_1}\n{city}\n{country}",
401
			));
402
		endif;
403
404
		return $this->address_formats;
405
	}
406
407
	/**
408
	 * Get country address format.
409
	 * @param  array  $args (default: array())
410
	 * @return string address
411
	 */
412
	public function get_formatted_address( $args = array() ) {
413
		$default_args = array(
414
			'first_name' => '',
415
			'last_name'  => '',
416
			'company'    => '',
417
			'address_1'  => '',
418
			'address_2'  => '',
419
			'city'       => '',
420
			'state'      => '',
421
			'postcode'   => '',
422
			'country'    => ''
423
		);
424
425
		$args = array_map( 'trim', wp_parse_args( $args, $default_args ) );
426
427
		extract( $args );
428
429
		// Get all formats
430
		$formats = $this->get_address_formats();
431
432
		// Get format for the address' country
433
		$format = ( $country && isset( $formats[ $country ] ) ) ? $formats[ $country ] : $formats['default'];
434
435
		// Handle full country name
436
		$full_country = ( isset( $this->countries[ $country ] ) ) ? $this->countries[ $country ] : $country;
437
438
		// Country is not needed if the same as base
439
		if ( $country == $this->get_base_country() && ! apply_filters( 'woocommerce_formatted_address_force_country_display', false ) ) {
440
			$format = str_replace( '{country}', '', $format );
441
		}
442
443
		// Handle full state name
444
		$full_state = ( $country && $state && isset( $this->states[ $country ][ $state ] ) ) ? $this->states[ $country ][ $state ] : $state;
445
446
		// Substitute address parts into the string
447
		$replace = array_map( 'esc_html', apply_filters( 'woocommerce_formatted_address_replacements', array(
448
			'{first_name}'       => $first_name,
449
			'{last_name}'        => $last_name,
450
			'{name}'             => $first_name . ' ' . $last_name,
451
			'{company}'          => $company,
452
			'{address_1}'        => $address_1,
453
			'{address_2}'        => $address_2,
454
			'{city}'             => $city,
455
			'{state}'            => $full_state,
456
			'{postcode}'         => $postcode,
457
			'{country}'          => $full_country,
458
			'{first_name_upper}' => strtoupper( $first_name ),
459
			'{last_name_upper}'  => strtoupper( $last_name ),
460
			'{name_upper}'       => strtoupper( $first_name . ' ' . $last_name ),
461
			'{company_upper}'    => strtoupper( $company ),
462
			'{address_1_upper}'  => strtoupper( $address_1 ),
463
			'{address_2_upper}'  => strtoupper( $address_2 ),
464
			'{city_upper}'       => strtoupper( $city ),
465
			'{state_upper}'      => strtoupper( $full_state ),
466
			'{state_code}'       => strtoupper( $state ),
467
			'{postcode_upper}'   => strtoupper( $postcode ),
468
			'{country_upper}'    => strtoupper( $full_country ),
469
		), $args ) );
470
471
		$formatted_address = str_replace( array_keys( $replace ), $replace, $format );
472
473
		// Clean up white space
474
		$formatted_address = preg_replace( '/  +/', ' ', trim( $formatted_address ) );
475
		$formatted_address = preg_replace( '/\n\n+/', "\n", $formatted_address );
476
477
		// Break newlines apart and remove empty lines/trim commas and white space
478
		$formatted_address = array_filter( array_map( array( $this, 'trim_formatted_address_line' ), explode( "\n", $formatted_address ) ) );
479
480
		// Add html breaks
481
		$formatted_address = implode( '<br/>', $formatted_address );
482
483
		// We're done!
484
		return $formatted_address;
485
	}
486
487
	/**
488
	 * Trim white space and commas off a line.
489
	 * @param  string
490
	 * @return string
491
	 */
492
	private function trim_formatted_address_line( $line ) {
493
		return trim( $line, ", " );
494
	}
495
496
	/**
497
	 * Returns the fields we show by default. This can be filtered later on.
498
	 * @return array
499
	 */
500
	public function get_default_address_fields() {
501
		$fields = array(
502
			'first_name' => array(
503
				'label'    => __( 'First Name', 'woocommerce' ),
504
				'required' => true,
505
				'class'    => array( 'form-row-first' ),
506
			),
507
			'last_name' => array(
508
				'label'    => __( 'Last Name', 'woocommerce' ),
509
				'required' => true,
510
				'class'    => array( 'form-row-last' ),
511
				'clear'    => true
512
			),
513
			'company' => array(
514
				'label' => __( 'Company Name', 'woocommerce' ),
515
				'class' => array( 'form-row-wide' ),
516
			),
517
			'country' => array(
518
				'type'     => 'country',
519
				'label'    => __( 'Country', 'woocommerce' ),
520
				'required' => true,
521
				'class'    => array( 'form-row-wide', 'address-field', 'update_totals_on_change' ),
522
			),
523
			'address_1' => array(
524
				'label'       => __( 'Address', 'woocommerce' ),
525
				'placeholder' => _x( 'Street address', 'placeholder', 'woocommerce' ),
526
				'required'    => true,
527
				'class'       => array( 'form-row-wide', 'address-field' )
528
			),
529
			'address_2' => array(
530
				'placeholder' => _x( 'Apartment, suite, unit etc. (optional)', 'placeholder', 'woocommerce' ),
531
				'class'       => array( 'form-row-wide', 'address-field' ),
532
				'required'    => false
533
			),
534
			'city' => array(
535
				'label'       => __( 'Town / City', 'woocommerce' ),
536
				'placeholder' => __( 'Town / City', 'woocommerce' ),
537
				'required'    => true,
538
				'class'       => array( 'form-row-wide', 'address-field' )
539
			),
540
			'state' => array(
541
				'type'        => 'state',
542
				'label'       => __( 'State / County', 'woocommerce' ),
543
				'required'    => true,
544
				'class'       => array( 'form-row-first', 'address-field' ),
545
				'validate'    => array( 'state' )
546
			),
547
			'postcode' => array(
548
				'label'       => __( 'Postcode / ZIP', 'woocommerce' ),
549
				'placeholder' => __( 'Postcode / ZIP', 'woocommerce' ),
550
				'required'    => true,
551
				'class'       => array( 'form-row-last', 'address-field' ),
552
				'clear'       => true,
553
				'validate'    => array( 'postcode' )
554
			),
555
		);
556
557
		return apply_filters( 'woocommerce_default_address_fields', $fields );
558
	}
559
560
	/**
561
	 * Get JS selectors for fields which are shown/hidden depending on the locale.
562
	 * @return array
563
	 */
564
	public function get_country_locale_field_selectors() {
565
		$locale_fields = array (
566
			'address_1' => '#billing_address_1_field, #shipping_address_1_field',
567
			'address_2' => '#billing_address_2_field, #shipping_address_2_field',
568
			'state'     => '#billing_state_field, #shipping_state_field, #calc_shipping_state_field',
569
			'postcode'  => '#billing_postcode_field, #shipping_postcode_field, #calc_shipping_postcode_field',
570
			'city'      => '#billing_city_field, #shipping_city_field, #calc_shipping_city_field',
571
		);
572
		return apply_filters( 'woocommerce_country_locale_field_selectors', $locale_fields );
573
	}
574
575
	/**
576
	 * Get country locale settings.
577
	 * @return array
578
	 * @todo  [2.4] Check select2 4.0.0 compatibility with `placeholder` attribute and uncomment relevant lines. https://github.com/woothemes/woocommerce/issues/7729
579
	 */
580
	public function get_country_locale() {
581
		if ( ! $this->locale ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->locale of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
582
583
			// Locale information used by the checkout
584
			$this->locale = apply_filters( 'woocommerce_get_country_locale', array(
585
				'AE' => array(
586
					'postcode' => array(
587
						'required' => false,
588
						'hidden'   => true
589
					),
590
				),
591
				'AF' => array(
592
					'state' => array(
593
						'required' => false,
594
					),
595
				),
596
				'AT' => array(
597
					'postcode_before_city' => true,
598
					'state' => array(
599
						'required' => false
600
					)
601
				),
602
				'AU' => array(
603
					'city'      => array(
604
						'label'       => __( 'Suburb', 'woocommerce' ),
605
						'placeholder' => __( 'Suburb', 'woocommerce' ),
606
					),
607
					'postcode'  => array(
608
						'label'       => __( 'Postcode', 'woocommerce' ),
609
						'placeholder' => __( 'Postcode', 'woocommerce' ),
610
					),
611
					'state'     => array(
612
						'label'       => __( 'State', 'woocommerce' ),
613
						//'placeholder' => __( 'State', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
614
					)
615
				),
616
				'AX' => array(
617
					'postcode_before_city' => true,
618
					'state' => array(
619
						'required' => false,
620
					),
621
				),
622
				'BD' => array(
623
					'postcode' => array(
624
						'required' => false
625
					),
626
					'state' => array(
627
						'label'       => __( 'District', 'woocommerce' ),
628
						//'placeholder' => __( 'District', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
629
					)
630
				),
631
				'BE' => array(
632
					'postcode_before_city' => true,
633
					'state' => array(
634
						'required'    => false,
635
						'label'       => __( 'Province', 'woocommerce' ),
636
						//'placeholder' => __( 'Province', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
637
					),
638
				),
639
				'BI' => array(
640
					'state' => array(
641
						'required' => false,
642
					),
643
				),
644
				'BO' => array(
645
					'postcode' => array(
646
						'required' => false,
647
						'hidden'   => true
648
					),
649
				),
650
				'BS' => array(
651
					'postcode' => array(
652
						'required' => false,
653
						'hidden'   => true
654
					),
655
				),
656
				'CA' => array(
657
					'state' => array(
658
						'label'       => __( 'Province', 'woocommerce' ),
659
						//'placeholder' => __( 'Province', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
660
					)
661
				),
662
				'CH' => array(
663
					'postcode_before_city' => true,
664
					'state' => array(
665
						'label'       => __( 'Canton', 'woocommerce' ),
666
						//'placeholder' => __( 'Canton', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
667
						'required'    => false
668
					)
669
				),
670
				'CL' => array(
671
					'city'      => array(
672
						'required' 	=> true,
673
					),
674
					'postcode'  => array(
675
						'required' => false
676
					),
677
					'state'     => array(
678
						'label'       => __( 'Region', 'woocommerce' ),
679
						//'placeholder' => __( 'Region', 'woocommerce' )
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
680
					)
681
				),
682
				'CN' => array(
683
					'state' => array(
684
						'label'       => __( 'Province', 'woocommerce' ),
685
						//'placeholder' => __( 'Province', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
686
					)
687
				),
688
				'CO' => array(
689
					'postcode' => array(
690
						'required' => false
691
					)
692
				),
693
				'CZ' => array(
694
					'state' => array(
695
						'required' => false
696
					)
697
				),
698
				'DE' => array(
699
					'postcode_before_city' => true,
700
					'state' => array(
701
						'required' => false
702
					)
703
				),
704
				'DK' => array(
705
					'postcode_before_city' => true,
706
					'state' => array(
707
						'required' => false
708
					)
709
				),
710
				'EE' => array(
711
					'postcode_before_city' => true,
712
					'state' => array(
713
						'required' => false
714
					)
715
				),
716
				'FI' => array(
717
					'postcode_before_city' => true,
718
					'state' => array(
719
						'required' => false
720
					)
721
				),
722
				'FR' => array(
723
					'postcode_before_city' => true,
724
					'state' => array(
725
						'required' => false
726
					)
727
				),
728
				'HK' => array(
729
					'postcode' => array(
730
						'required' => false
731
					),
732
					'city'  => array(
733
						'label'       => __( 'Town / District', 'woocommerce' ),
734
						'placeholder' => __( 'Town / District', 'woocommerce' )
735
					),
736
					'state' => array(
737
						'label'       => __( 'Region', 'woocommerce' ),
738
						//'placeholder' => __( 'Region', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
739
					)
740
				),
741
				'HU' => array(
742
					'state' => array(
743
						'label'       => __( 'County', 'woocommerce' ),
744
						//'placeholder' => __( 'County', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
745
					)
746
				),
747
				'ID' => array(
748
					'state' => array(
749
						'label'       => __( 'Province', 'woocommerce' ),
750
						//'placeholder' => __( 'Province', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
751
					)
752
				),
753
				'IS' => array(
754
					'postcode_before_city' => true,
755
					'state' => array(
756
						'required' => false
757
					)
758
				),
759
				'IL' => array(
760
					'postcode_before_city' => true,
761
					'state' => array(
762
						'required' => false
763
					)
764
				),
765
				'IT' => array(
766
					'postcode_before_city' => true,
767
					'state' => array(
768
						'required'    => true,
769
						'label'       => __( 'Province', 'woocommerce' ),
770
						//'placeholder' => __( 'Province', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
771
					)
772
				),
773
				'JP' => array(
774
					'state' => array(
775
						'label' => __( 'Prefecture', 'woocommerce' )
776
					)
777
				),
778
				'KR' => array(
779
					'state' => array(
780
						'required' => false
781
					)
782
				),
783
				'NL' => array(
784
					'postcode_before_city' => true,
785
					'state' => array(
786
						'required'    => false,
787
						'label'       => __( 'Province', 'woocommerce' ),
788
						//'placeholder' => __( 'Province', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
789
					)
790
				),
791
				'NZ' => array(
792
					'state' => array(
793
						'required' => false
794
					)
795
				),
796
				'NO' => array(
797
					'postcode_before_city' => true,
798
					'state' => array(
799
						'required' => false
800
					)
801
				),
802
				'NP' => array(
803
					'state' => array(
804
						'label'       => __( 'State / Zone', 'woocommerce' ),
805
						//'placeholder' => __( 'State / Zone', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
806
					),
807
					'postcode' => array(
808
						'required' => false
809
					)
810
				),
811
				'PL' => array(
812
					'postcode_before_city' => true,
813
					'state' => array(
814
						'required' => false
815
					)
816
				),
817
				'PT' => array(
818
					'state' => array(
819
						'required' => false
820
					)
821
				),
822
				'RO' => array(
823
					'state' => array(
824
						'required' => false
825
					)
826
				),
827
				'SG' => array(
828
					'state' => array(
829
						'required' => false
830
					)
831
				),
832
				'SK' => array(
833
					'postcode_before_city' => true,
834
					'state' => array(
835
						'required' => false
836
					)
837
				),
838
				'SI' => array(
839
					'postcode_before_city' => true,
840
					'state' => array(
841
						'required' => false
842
					)
843
				),
844
				'ES' => array(
845
					'postcode_before_city' => true,
846
					'state' => array(
847
						'label'       => __( 'Province', 'woocommerce' ),
848
						//'placeholder' => __( 'Province', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
849
					)
850
				),
851
				'LI' => array(
852
					'postcode_before_city' => true,
853
					'state' => array(
854
						'label'       => __( 'Municipality', 'woocommerce' ),
855
						//'placeholder' => __( 'Municipality', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
856
						'required'    => false
857
					)
858
				),
859
				'LK' => array(
860
					'state' => array(
861
						'required' => false
862
					)
863
				),
864
				'SE' => array(
865
					'postcode_before_city' => true,
866
					'state' => array(
867
						'required' => false
868
					)
869
				),
870
				'TR' => array(
871
					'postcode_before_city' => true,
872
					'state' => array(
873
						'label'       => __( 'Province', 'woocommerce' ),
874
						//'placeholder' => __( 'Province', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
875
					)
876
				),
877
				'US' => array(
878
					'postcode'  => array(
879
						'label'       => __( 'ZIP', 'woocommerce' ),
880
						'placeholder' => __( 'ZIP', 'woocommerce' ),
881
					),
882
					'state'     => array(
883
						'label'       => __( 'State', 'woocommerce' ),
884
						//'placeholder' => __( 'State', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
885
					)
886
				),
887
				'GB' => array(
888
					'postcode'  => array(
889
						'label'       => __( 'Postcode', 'woocommerce' ),
890
						'placeholder' => __( 'Postcode', 'woocommerce' ),
891
					),
892
					'state'     => array(
893
						'label'       => __( 'County', 'woocommerce' ),
894
						//'placeholder' => __( 'County', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
895
						'required'    => false
896
					)
897
				),
898
				'VN' => array(
899
					'postcode_before_city' => true,
900
					'state' => array(
901
						'required' => false
902
					),
903
					'postcode' => array(
904
						'required' => false,
905
						'hidden'   => false
906
					),
907
					'address_2' => array(
908
						'required' => false,
909
						'hidden'   => true
910
					)
911
				),
912
				'WS' => array(
913
					'postcode' => array(
914
						'required' => false,
915
						'hidden'   => true
916
					),
917
				),
918
				'ZA' => array(
919
					'state' => array(
920
						'label'       => __( 'Province', 'woocommerce' ),
921
						//'placeholder' => __( 'Province', 'woocommerce' ),
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
922
					)
923
				),
924
				'ZW' => array(
925
					'postcode' => array(
926
						'required' => false,
927
						'hidden'   => true
928
					),
929
				),
930
			));
931
932
			$this->locale = array_intersect_key( $this->locale, array_merge( $this->get_allowed_countries(), $this->get_shipping_countries() ) );
933
934
			// Default Locale Can be filtered to override fields in get_address_fields().
935
			// Countries with no specific locale will use default.
936
			$this->locale['default'] = apply_filters('woocommerce_get_country_locale_default', $this->get_default_address_fields() );
937
938
			// Filter default AND shop base locales to allow overides via a single function. These will be used when changing countries on the checkout
939
			if ( ! isset( $this->locale[ $this->get_base_country() ] ) ) {
940
				$this->locale[ $this->get_base_country() ] = $this->locale['default'];
941
			}
942
943
			$this->locale['default']                   = apply_filters( 'woocommerce_get_country_locale_base', $this->locale['default'] );
944
			$this->locale[ $this->get_base_country() ] = apply_filters( 'woocommerce_get_country_locale_base', $this->locale[ $this->get_base_country() ] );
945
		}
946
947
		return $this->locale;
948
	}
949
950
	/**
951
	 * Apply locale and get address fields.
952
	 * @param  mixed  $country
953
	 * @param  string $type (default: 'billing_')
954
	 * @return array
955
	 */
956
	public function get_address_fields( $country = '', $type = 'billing_' ) {
957
		if ( ! $country ) {
958
			$country = $this->get_base_country();
959
		}
960
961
		$fields = $this->get_default_address_fields();
962
		$locale = $this->get_country_locale();
963
964
		if ( isset( $locale[ $country ] ) ) {
965
			$fields = wc_array_overlay( $fields, $locale[ $country ] );
966
		}
967
968
		// Prepend field keys
969
		$address_fields = array();
970
971
		foreach ( $fields as $key => $value ) {
972
			$keys = array_keys( $fields );
973
			$address_fields[ $type . $key ] = $value;
974
975
			// Add email and phone after company or last
976
			if ( $type == 'billing_' && ( 'company' === $key || ( ! array_key_exists( 'company', $fields ) && $key === end( $keys ) ) ) ) {
977
				$address_fields['billing_email'] = array(
978
					'label'		=> __( 'Email Address', 'woocommerce' ),
979
					'required'	=> true,
980
					'type'		=> 'email',
981
					'class'		=> array( 'form-row-first' ),
982
					'validate'	=> array( 'email' ),
983
				);
984
				$address_fields['billing_phone'] = array(
985
					'label'    	=> __( 'Phone', 'woocommerce' ),
986
					'required' 	=> true,
987
					'type'		=> 'tel',
988
					'class'    	=> array( 'form-row-last' ),
989
					'clear'    	=> true,
990
					'validate' 	=> array( 'phone' ),
991
				);
992
			}
993
		}
994
995
		$address_fields = apply_filters( 'woocommerce_' . $type . 'fields', $address_fields, $country );
996
997
		return $address_fields;
998
	}
999
}
1000