Issues (1182)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

includes/class-wc-countries.php (1 issue)

Upgrade to new PHP Analysis Engine

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
 * 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
	 * Get continent code for a country code.
66
	 * @since 2.6.0
67
	 * @param string $cc string
68
	 * @return string
69
	 */
70
	public function get_continent_code_for_country( $cc ) {
71
		$cc                 = trim( strtoupper( $cc ) );
72
		$continents         = $this->get_continents();
73
		$continents_and_ccs = wp_list_pluck( $continents, 'countries' );
74
		foreach ( $continents_and_ccs as $continent_code => $countries ) {
75
			if ( false !== array_search( $cc, $countries ) ) {
76
				return $continent_code;
77
			}
78
		}
79
		return '';
80
	}
81
82
	/**
83
	 * Load the states.
84
	 */
85
	public function load_country_states() {
86
		global $states;
87
88
		// States set to array() are blank i.e. the country has no use for the state field.
89
		$states = array(
90
			'AF' => array(),
91
			'AT' => array(),
92
			'AX' => array(),
93
			'BE' => array(),
94
			'BI' => array(),
95
			'CZ' => array(),
96
			'DE' => array(),
97
			'DK' => array(),
98
			'EE' => array(),
99
			'FI' => array(),
100
			'FR' => array(),
101
			'IS' => array(),
102
			'IL' => array(),
103
			'KR' => array(),
104
			'NL' => array(),
105
			'NO' => array(),
106
			'PL' => array(),
107
			'PT' => array(),
108
			'SG' => array(),
109
			'SK' => array(),
110
			'SI' => array(),
111
			'LK' => array(),
112
			'SE' => array(),
113
			'VN' => array(),
114
		);
115
116
		// Load only the state files the shop owner wants/needs.
117
		$allowed = array_merge( $this->get_allowed_countries(), $this->get_shipping_countries() );
118
119
		if ( $allowed ) {
120
			foreach ( $allowed as $code => $country ) {
121
				if ( ! isset( $states[ $code ] ) && file_exists( WC()->plugin_path() . '/i18n/states/' . $code . '.php' ) ) {
122
					include( WC()->plugin_path() . '/i18n/states/' . $code . '.php' );
123
				}
124
			}
125
		}
126
127
		$this->states = apply_filters( 'woocommerce_states', $states );
128
	}
129
130
	/**
131
	 * Get the states for a country.
132
	 * @param  string $cc country code
133
	 * @return array of states
134
	 */
135
	public function get_states( $cc = null ) {
136
		if ( empty( $this->states ) ) {
137
			$this->load_country_states();
138
		}
139
140
		if ( ! is_null( $cc ) ) {
141
			return isset( $this->states[ $cc ] ) ? $this->states[ $cc ] : false;
142
		} else {
143
			return $this->states;
144
		}
145
	}
146
147
	/**
148
	 * Get the base country for the store.
149
	 * @return string
150
	 */
151
	public function get_base_country() {
152
		$default = wc_get_base_location();
153
		return apply_filters( 'woocommerce_countries_base_country', $default['country'] );
154
	}
155
156
	/**
157
	 * Get the base state for the store.
158
	 * @return string
159
	 */
160
	public function get_base_state() {
161
		$default = wc_get_base_location();
162
		return apply_filters( 'woocommerce_countries_base_state', $default['state'] );
163
	}
164
165
	/**
166
	 * Get the base city for the store.
167
	 * @return string
168
	 */
169
	public function get_base_city() {
170
		return apply_filters( 'woocommerce_countries_base_city', '' );
171
	}
172
173
	/**
174
	 * Get the base postcode for the store.
175
	 * @return string
176
	 */
177
	public function get_base_postcode() {
178
		return apply_filters( 'woocommerce_countries_base_postcode', '' );
179
	}
180
181
	/**
182
	 * Get the allowed countries for the store.
183
	 * @return array
184
	 */
185
	public function get_allowed_countries() {
186
		if ( 'all' === get_option( 'woocommerce_allowed_countries' ) ) {
187
			return $this->countries;
188
		}
189
190
		if( 'all_except' === get_option( 'woocommerce_allowed_countries' ) ) {
191
			$except_countries = get_option( 'woocommerce_all_except_countries', array() );
192
193
			if ( ! $except_countries ) {
194
				return $this->countries;
195
			} else {
196
				$all_except_countries = $this->countries;
197
				foreach( $except_countries as $country ) {
198
					unset( $all_except_countries[ $country ] );
199
				}
200
				return apply_filters( 'woocommerce_countries_allowed_countries', $all_except_countries );
201
			}
202
		}
203
204
		$countries = array();
205
206
		$raw_countries = get_option( 'woocommerce_specific_allowed_countries', array() );
207
208
		if ( $raw_countries ) {
209
			foreach ( $raw_countries as $country ) {
210
				$countries[ $country ] = $this->countries[ $country ];
211
			}
212
		}
213
214
		return apply_filters( 'woocommerce_countries_allowed_countries', $countries );
215
	}
216
217
	/**
218
	 * Get the countries you ship to.
219
	 * @return array
220
	 */
221
	public function get_shipping_countries() {
222
		if ( '' === get_option( 'woocommerce_ship_to_countries' ) ) {
223
			return $this->get_allowed_countries();
224
		}
225
226
		if ( 'all' === get_option( 'woocommerce_ship_to_countries' ) ) {
227
			return $this->countries;
228
		}
229
230
		$countries = array();
231
232
		$raw_countries = get_option( 'woocommerce_specific_ship_to_countries' );
233
234
		if ( $raw_countries ) {
235
			foreach ( $raw_countries as $country ) {
236
				$countries[ $country ] = $this->countries[ $country ];
237
			}
238
		}
239
240
		return apply_filters( 'woocommerce_countries_shipping_countries', $countries );
241
	}
242
243
	/**
244
	 * Get allowed country states.
245
	 * @return array
246
	 */
247
	public function get_allowed_country_states() {
248
		if ( get_option( 'woocommerce_allowed_countries' ) !== 'specific' ) {
249
			return $this->states;
250
		}
251
252
		$states = array();
253
254
		$raw_countries = get_option( 'woocommerce_specific_allowed_countries' );
255
256
		if ( $raw_countries ) {
257
			foreach ( $raw_countries as $country ) {
258
				if ( isset( $this->states[ $country ] ) ) {
259
					$states[ $country ] = $this->states[ $country ];
260
				}
261
			}
262
		}
263
264
		return apply_filters( 'woocommerce_countries_allowed_country_states', $states );
265
	}
266
267
	/**
268
	 * Get shipping country states.
269
	 * @return array
270
	 */
271
	public function get_shipping_country_states() {
272
		if ( get_option( 'woocommerce_ship_to_countries' ) == '' ) {
273
			return $this->get_allowed_country_states();
274
		}
275
276
		if ( get_option( 'woocommerce_ship_to_countries' ) !== 'specific' ) {
277
			return $this->states;
278
		}
279
280
		$states = array();
281
282
		$raw_countries = get_option( 'woocommerce_specific_ship_to_countries' );
283
284
		if ( $raw_countries ) {
285
			foreach ( $raw_countries as $country ) {
286
				if ( ! empty( $this->states[ $country ] ) ) {
287
					$states[ $country ] = $this->states[ $country ];
288
				}
289
			}
290
		}
291
292
		return apply_filters( 'woocommerce_countries_shipping_country_states', $states );
293
	}
294
295
	/**
296
	 * Gets an array of countries in the EU.
297
	 *
298
	 * MC (monaco) and IM (isle of man, part of UK) also use VAT.
299
	 *
300
	 * @param  $type Type of countries to retrieve. Blank for EU member countries. eu_vat for EU VAT countries.
301
	 * @return string[]
302
	 */
303
	public function get_european_union_countries( $type = '' ) {
304
		$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' );
305
306
		if ( 'eu_vat' === $type ) {
307
			$countries[] = 'MC';
308
			$countries[] = 'IM';
309
		}
310
311
		return $countries;
312
	}
313
314
	/**
315
	 * Gets the correct string for shipping - either 'to the' or 'to'
316
	 * @return string
317
	 */
318
	public function shipping_to_prefix( $country_code = '' ) {
319
		$country_code = $country_code ? $country_code : WC()->customer->get_shipping_country();
320
		$countries    = array( 'GB', 'US', 'AE', 'CZ', 'DO', 'NL', 'PH', 'USAF' );
321
		$return       = in_array( $country_code, $countries ) ? __( 'to the', 'woocommerce' ) : __( 'to', 'woocommerce' );
322
323
		return apply_filters( 'woocommerce_countries_shipping_to_prefix', $return, $country_code );
324
	}
325
326
	/**
327
	 * Prefix certain countries with 'the'
328
	 * @return string
329
	 */
330
	public function estimated_for_prefix( $country_code = '' ) {
331
		$country_code = $country_code ? $country_code : $this->get_base_country();
332
		$countries    = array( 'GB', 'US', 'AE', 'CZ', 'DO', 'NL', 'PH', 'USAF' );
333
		$return       = in_array( $country_code, $countries ) ? __( 'the', 'woocommerce' ) . ' ' : '';
334
335
		return apply_filters( 'woocommerce_countries_estimated_for_prefix', $return, $country_code );
336
	}
337
338
	/**
339
	 * Correctly name tax in some countries VAT on the frontend.
340
	 * @return string
341
	 */
342
	public function tax_or_vat() {
343
		$return = in_array( $this->get_base_country(), $this->get_european_union_countries( 'eu_vat' ) ) ? __( 'VAT', 'woocommerce' ) : __( 'Tax', 'woocommerce' );
344
345
		return apply_filters( 'woocommerce_countries_tax_or_vat', $return );
346
	}
347
348
	/**
349
	 * Include the Inc Tax label.
350
	 * @return string
351
	 */
352
	public function inc_tax_or_vat() {
353
		$return = in_array( $this->get_base_country(), $this->get_european_union_countries( 'eu_vat' ) ) ? __( '(incl. VAT)', 'woocommerce' ) : __( '(incl. tax)', 'woocommerce' );
354
355
		return apply_filters( 'woocommerce_countries_inc_tax_or_vat', $return );
356
	}
357
358
	/**
359
	 * Include the Ex Tax label.
360
	 * @return string
361
	 */
362
	public function ex_tax_or_vat() {
363
		$return = in_array( $this->get_base_country(), $this->get_european_union_countries( 'eu_vat' ) ) ? __( '(ex. VAT)', 'woocommerce' ) : __( '(ex. tax)', 'woocommerce' );
364
365
		return apply_filters( 'woocommerce_countries_ex_tax_or_vat', $return );
366
	}
367
368
	/**
369
	 * Outputs the list of countries and states for use in dropdown boxes.
370
	 * @param string $selected_country (default: '')
371
	 * @param string $selected_state (default: '')
372
	 * @param bool $escape (default: false)
373
	 * @param bool   $escape (default: false)
374
	 */
375
	public function country_dropdown_options( $selected_country = '', $selected_state = '', $escape = false ) {
376
		if ( $this->countries ) foreach ( $this->countries as $key => $value ) :
377
			if ( $states = $this->get_states( $key ) ) :
378
				echo '<optgroup label="' . esc_attr( $value ) . '">';
379
					foreach ( $states as $state_key => $state_value ) :
380
						echo '<option value="' . esc_attr( $key ) . ':' . $state_key . '"';
381
382
						if ( $selected_country == $key && $selected_state == $state_key ) {
383
							echo ' selected="selected"';
384
						}
385
386
						echo '>' . $value . ' &mdash; ' . ( $escape ? esc_js( $state_value ) : $state_value ) . '</option>';
387
					endforeach;
388
				echo '</optgroup>';
389
			else :
390
				echo '<option';
391
				if ( $selected_country == $key && $selected_state == '*' ) {
392
					echo ' selected="selected"';
393
				}
394
				echo ' value="' . esc_attr( $key ) . '">' . ( $escape ? esc_js( $value ) : $value ) . '</option>';
395
			endif;
396
		endforeach;
397
	}
398
399
	/**
400
	 * Get country address formats.
401
	 * @return array
402
	 */
403
	public function get_address_formats() {
404
		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...
405
406
			// Common formats
407
			$postcode_before_city = "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}";
408
409
			// Define address formats
410
			$this->address_formats = apply_filters( 'woocommerce_localisation_address_formats', array(
411
				'default' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode}\n{country}",
412
				'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
413
				'AT' => $postcode_before_city,
414
				'BE' => $postcode_before_city,
415
				'CA' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
416
				'CH' => $postcode_before_city,
417
				'CL' => "{company}\n{name}\n{address_1}\n{address_2}\n{state}\n{postcode} {city}\n{country}",
418
				'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",
419
				'CZ' => $postcode_before_city,
420
				'DE' => $postcode_before_city,
421
				'EE' => $postcode_before_city,
422
				'FI' => $postcode_before_city,
423
				'DK' => $postcode_before_city,
424
				'FR' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city_upper}\n{country}",
425
				'HK' => "{company}\n{first_name} {last_name_upper}\n{address_1}\n{address_2}\n{city_upper}\n{state_upper}\n{country}",
426
				'HU' => "{name}\n{company}\n{city}\n{address_1}\n{address_2}\n{postcode}\n{country}",
427
				'IN' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} - {postcode}\n{state}, {country}",
428
				'IS' => $postcode_before_city,
429
				'IT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode}\n{city}\n{state_upper}\n{country}",
430
				'JP' => "{postcode}\n{state}{city}{address_1}\n{address_2}\n{company}\n{last_name} {first_name}\n{country}",
431
				'TW' => "{company}\n{last_name} {first_name}\n{address_1}\n{address_2}\n{state}, {city} {postcode}\n{country}",
432
				'LI' => $postcode_before_city,
433
				'NL' => $postcode_before_city,
434
				'NZ' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {postcode}\n{country}",
435
				'NO' => $postcode_before_city,
436
				'PL' => $postcode_before_city,
437
				'SK' => $postcode_before_city,
438
				'SI' => $postcode_before_city,
439
				'ES' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city}\n{state}\n{country}",
440
				'SE' => $postcode_before_city,
441
				'TR' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city} {state}\n{country}",
442
				'US' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}, {state_code} {postcode}\n{country}",
443
				'VN' => "{name}\n{company}\n{address_1}\n{city}\n{country}",
444
			));
445
		endif;
446
447
		return $this->address_formats;
448
	}
449
450
	/**
451
	 * Get country address format.
452
	 * @param  array  $args (default: array())
453
	 * @return string address
454
	 */
455
	public function get_formatted_address( $args = array() ) {
456
		$default_args = array(
457
			'first_name' => '',
458
			'last_name'  => '',
459
			'company'    => '',
460
			'address_1'  => '',
461
			'address_2'  => '',
462
			'city'       => '',
463
			'state'      => '',
464
			'postcode'   => '',
465
			'country'    => ''
466
		);
467
468
		$args = array_map( 'trim', wp_parse_args( $args, $default_args ) );
469
470
		extract( $args );
471
472
		// Get all formats
473
		$formats = $this->get_address_formats();
474
475
		// Get format for the address' country
476
		$format = ( $country && isset( $formats[ $country ] ) ) ? $formats[ $country ] : $formats['default'];
477
478
		// Handle full country name
479
		$full_country = ( isset( $this->countries[ $country ] ) ) ? $this->countries[ $country ] : $country;
480
481
		// Country is not needed if the same as base
482
		if ( $country == $this->get_base_country() && ! apply_filters( 'woocommerce_formatted_address_force_country_display', false ) ) {
483
			$format = str_replace( '{country}', '', $format );
484
		}
485
486
		// Handle full state name
487
		$full_state = ( $country && $state && isset( $this->states[ $country ][ $state ] ) ) ? $this->states[ $country ][ $state ] : $state;
488
489
		// Substitute address parts into the string
490
		$replace = array_map( 'esc_html', apply_filters( 'woocommerce_formatted_address_replacements', array(
491
			'{first_name}'       => $first_name,
492
			'{last_name}'        => $last_name,
493
			'{name}'             => $first_name . ' ' . $last_name,
494
			'{company}'          => $company,
495
			'{address_1}'        => $address_1,
496
			'{address_2}'        => $address_2,
497
			'{city}'             => $city,
498
			'{state}'            => $full_state,
499
			'{postcode}'         => $postcode,
500
			'{country}'          => $full_country,
501
			'{first_name_upper}' => strtoupper( $first_name ),
502
			'{last_name_upper}'  => strtoupper( $last_name ),
503
			'{name_upper}'       => strtoupper( $first_name . ' ' . $last_name ),
504
			'{company_upper}'    => strtoupper( $company ),
505
			'{address_1_upper}'  => strtoupper( $address_1 ),
506
			'{address_2_upper}'  => strtoupper( $address_2 ),
507
			'{city_upper}'       => strtoupper( $city ),
508
			'{state_upper}'      => strtoupper( $full_state ),
509
			'{state_code}'       => strtoupper( $state ),
510
			'{postcode_upper}'   => strtoupper( $postcode ),
511
			'{country_upper}'    => strtoupper( $full_country ),
512
		), $args ) );
513
514
		$formatted_address = str_replace( array_keys( $replace ), $replace, $format );
515
516
		// Clean up white space
517
		$formatted_address = preg_replace( '/  +/', ' ', trim( $formatted_address ) );
518
		$formatted_address = preg_replace( '/\n\n+/', "\n", $formatted_address );
519
520
		// Break newlines apart and remove empty lines/trim commas and white space
521
		$formatted_address = array_filter( array_map( array( $this, 'trim_formatted_address_line' ), explode( "\n", $formatted_address ) ) );
522
523
		// Add html breaks
524
		$formatted_address = implode( '<br/>', $formatted_address );
525
526
		// We're done!
527
		return $formatted_address;
528
	}
529
530
	/**
531
	 * Trim white space and commas off a line.
532
	 * @param  string $line
533
	 * @return string
534
	 */
535
	private function trim_formatted_address_line( $line ) {
536
		return trim( $line, ", " );
537
	}
538
539
	/**
540
	 * Returns the fields we show by default. This can be filtered later on.
541
	 * @return array
542
	 */
543
	public function get_default_address_fields() {
544
		$fields = array(
545
			'first_name' => array(
546
				'label'        => __( 'First Name', 'woocommerce' ),
547
				'required'     => true,
548
				'class'        => array( 'form-row-first' ),
549
				'autocomplete' => 'given-name',
550
			),
551
			'last_name' => array(
552
				'label'        => __( 'Last Name', 'woocommerce' ),
553
				'required'     => true,
554
				'class'        => array( 'form-row-last' ),
555
				'clear'        => true,
556
				'autocomplete' => 'family-name',
557
			),
558
			'company' => array(
559
				'label'        => __( 'Company Name', 'woocommerce' ),
560
				'class'        => array( 'form-row-wide' ),
561
				'autocomplete' => 'organization',
562
			),
563
			'country' => array(
564
				'type'         => 'country',
565
				'label'        => __( 'Country', 'woocommerce' ),
566
				'required'     => true,
567
				'class'        => array( 'form-row-wide', 'address-field', 'update_totals_on_change' ),
568
				'autocomplete' => 'country',
569
			),
570
			'address_1' => array(
571
				'label'        => __( 'Address', 'woocommerce' ),
572
				'placeholder'  => _x( 'Street address', 'placeholder', 'woocommerce' ),
573
				'required'     => true,
574
				'class'        => array( 'form-row-wide', 'address-field' ),
575
				'autocomplete' => 'address-line1',
576
			),
577
			'address_2' => array(
578
				'placeholder'  => _x( 'Apartment, suite, unit etc. (optional)', 'placeholder', 'woocommerce' ),
579
				'class'        => array( 'form-row-wide', 'address-field' ),
580
				'required'     => false,
581
				'autocomplete' => 'address-line2',
582
			),
583
			'city' => array(
584
				'label'        => __( 'Town / City', 'woocommerce' ),
585
				'required'     => true,
586
				'class'        => array( 'form-row-wide', 'address-field' ),
587
				'autocomplete' => 'address-level2',
588
			),
589
			'state' => array(
590
				'type'         => 'state',
591
				'label'        => __( 'State / County', 'woocommerce' ),
592
				'required'     => true,
593
				'class'        => array( 'form-row-first', 'address-field' ),
594
				'validate'     => array( 'state' ),
595
				'autocomplete' => 'address-level1',
596
			),
597
			'postcode' => array(
598
				'label'        => __( 'Postcode / ZIP', 'woocommerce' ),
599
				'required'     => true,
600
				'class'        => array( 'form-row-last', 'address-field' ),
601
				'clear'        => true,
602
				'validate'     => array( 'postcode' ),
603
				'autocomplete' => 'postal-code',
604
			),
605
		);
606
607
		return apply_filters( 'woocommerce_default_address_fields', $fields );
608
	}
609
610
	/**
611
	 * Get JS selectors for fields which are shown/hidden depending on the locale.
612
	 * @return array
613
	 */
614
	public function get_country_locale_field_selectors() {
615
		$locale_fields = array (
616
			'address_1' => '#billing_address_1_field, #shipping_address_1_field',
617
			'address_2' => '#billing_address_2_field, #shipping_address_2_field',
618
			'state'     => '#billing_state_field, #shipping_state_field, #calc_shipping_state_field',
619
			'postcode'  => '#billing_postcode_field, #shipping_postcode_field, #calc_shipping_postcode_field',
620
			'city'      => '#billing_city_field, #shipping_city_field, #calc_shipping_city_field',
621
		);
622
		return apply_filters( 'woocommerce_country_locale_field_selectors', $locale_fields );
623
	}
624
625
	/**
626
	 * Get country locale settings.
627
	 * @return array
628
	 * @todo  [2.4] Check select2 4.0.0 compatibility with `placeholder` attribute and uncomment relevant lines. https://github.com/woothemes/woocommerce/issues/7729
629
	 */
630
	public function get_country_locale() {
631
		if ( ! $this->locale ) {
632
633
			// Locale information used by the checkout
634
			$this->locale = apply_filters( 'woocommerce_get_country_locale', array(
635
				'AE' => array(
636
					'postcode' => array(
637
						'required' => false,
638
						'hidden'   => true
639
					),
640
				),
641
				'AF' => array(
642
					'state' => array(
643
						'required' => false,
644
					),
645
				),
646
				'AT' => array(
647
					'postcode_before_city' => true,
648
					'state' => array(
649
						'required' => false
650
					)
651
				),
652
				'AU' => array(
653
					'city'      => array(
654
						'label'       => __( 'Suburb', 'woocommerce' ),
655
					),
656
					'postcode'  => array(
657
						'label'       => __( 'Postcode', 'woocommerce' ),
658
					),
659
					'state'     => array(
660
						'label'       => __( 'State', 'woocommerce' ),
661
					)
662
				),
663
				'AX' => array(
664
					'postcode_before_city' => true,
665
					'state' => array(
666
						'required' => false,
667
					),
668
				),
669
				'BD' => array(
670
					'postcode' => array(
671
						'required' => false
672
					),
673
					'state' => array(
674
						'label'       => __( 'District', 'woocommerce' ),
675
					)
676
				),
677
				'BE' => array(
678
					'postcode_before_city' => true,
679
					'state' => array(
680
						'required'    => false,
681
						'label'       => __( 'Province', 'woocommerce' ),
682
					),
683
				),
684
				'BI' => array(
685
					'state' => array(
686
						'required' => false,
687
					),
688
				),
689
				'BO' => array(
690
					'postcode' => array(
691
						'required' => false,
692
						'hidden'   => true
693
					),
694
				),
695
				'BS' => array(
696
					'postcode' => array(
697
						'required' => false,
698
						'hidden'   => true
699
					),
700
				),
701
				'CA' => array(
702
					'state' => array(
703
						'label'       => __( 'Province', 'woocommerce' ),
704
					)
705
				),
706
				'CH' => array(
707
					'postcode_before_city' => true,
708
					'state' => array(
709
						'label'       => __( 'Canton', 'woocommerce' ),
710
						'required'    => false
711
					)
712
				),
713
				'CL' => array(
714
					'city'      => array(
715
						'required' 	=> true,
716
					),
717
					'postcode'  => array(
718
						'required' => false
719
					),
720
					'state'     => array(
721
						'label'       => __( 'Region', 'woocommerce' ),
722
					)
723
				),
724
				'CN' => array(
725
					'state' => array(
726
						'label'       => __( 'Province', 'woocommerce' ),
727
					)
728
				),
729
				'CO' => array(
730
					'postcode' => array(
731
						'required' => false
732
					)
733
				),
734
				'CZ' => array(
735
					'state' => array(
736
						'required' => false
737
					)
738
				),
739
				'DE' => array(
740
					'postcode_before_city' => true,
741
					'state' => array(
742
						'required' => false
743
					)
744
				),
745
				'DK' => array(
746
					'postcode_before_city' => true,
747
					'state' => array(
748
						'required' => false
749
					)
750
				),
751
				'EE' => array(
752
					'postcode_before_city' => true,
753
					'state' => array(
754
						'required' => false
755
					)
756
				),
757
				'FI' => array(
758
					'postcode_before_city' => true,
759
					'state' => array(
760
						'required' => false
761
					)
762
				),
763
				'FR' => array(
764
					'postcode_before_city' => true,
765
					'state' => array(
766
						'required' => false
767
					)
768
				),
769
				'HK' => array(
770
					'postcode' => array(
771
						'required' => false
772
					),
773
					'city'  => array(
774
						'label'       => __( 'Town / District', 'woocommerce' ),
775
					),
776
					'state' => array(
777
						'label'       => __( 'Region', 'woocommerce' ),
778
					)
779
				),
780
				'HU' => array(
781
					'state' => array(
782
						'label'       => __( 'County', 'woocommerce' ),
783
					)
784
				),
785
				'ID' => array(
786
					'state' => array(
787
						'label'       => __( 'Province', 'woocommerce' ),
788
					)
789
				),
790
				'IE' => array(
791
					'postcode' => array(
792
						'required' => false,
793
						'label'    => __( 'Postcode', 'woocommerce' ),
794
					),
795
				),
796
				'IS' => array(
797
					'postcode_before_city' => true,
798
					'state' => array(
799
						'required' => false
800
					)
801
				),
802
				'IL' => array(
803
					'postcode_before_city' => true,
804
					'state' => array(
805
						'required' => false
806
					)
807
				),
808
				'IT' => array(
809
					'postcode_before_city' => true,
810
					'state' => array(
811
						'required'    => true,
812
						'label'       => __( 'Province', 'woocommerce' ),
813
					)
814
				),
815
				'JP' => array(
816
					'state' => array(
817
						'label' => __( 'Prefecture', 'woocommerce' )
818
					)
819
				),
820
				'KR' => array(
821
					'state' => array(
822
						'required' => false
823
					)
824
				),
825
				'NL' => array(
826
					'postcode_before_city' => true,
827
					'state' => array(
828
						'required'    => false,
829
						'label'       => __( 'Province', 'woocommerce' ),
830
					)
831
				),
832
				'NZ' => array(
833
					'postcode' => array(
834
						'label' => __( 'Postcode', 'woocommerce' )
835
					),
836
					'state' => array(
837
						'required' => false,
838
						'label'    => __( 'Region', 'woocommerce' )
839
					)
840
				),
841
				'NO' => array(
842
					'postcode_before_city' => true,
843
					'state' => array(
844
						'required' => false
845
					)
846
				),
847
				'NP' => array(
848
					'state' => array(
849
						'label'       => __( 'State / Zone', 'woocommerce' ),
850
					),
851
					'postcode' => array(
852
						'required' => false
853
					)
854
				),
855
				'PL' => array(
856
					'postcode_before_city' => true,
857
					'state' => array(
858
						'required' => false
859
					)
860
				),
861
				'PT' => array(
862
					'state' => array(
863
						'required' => false
864
					)
865
				),
866
				'RO' => array(
867
					'state' => array(
868
						'required' => false
869
					)
870
				),
871
				'SG' => array(
872
					'state' => array(
873
						'required' => false
874
					)
875
				),
876
				'SK' => array(
877
					'postcode_before_city' => true,
878
					'state' => array(
879
						'required' => false
880
					)
881
				),
882
				'SI' => array(
883
					'postcode_before_city' => true,
884
					'state' => array(
885
						'required' => false
886
					)
887
				),
888
				'ES' => array(
889
					'postcode_before_city' => true,
890
					'state' => array(
891
						'label'       => __( 'Province', 'woocommerce' ),
892
					)
893
				),
894
				'LI' => array(
895
					'postcode_before_city' => true,
896
					'state' => array(
897
						'label'       => __( 'Municipality', 'woocommerce' ),
898
						'required'    => false
899
					)
900
				),
901
				'LK' => array(
902
					'state' => array(
903
						'required' => false
904
					)
905
				),
906
				'SE' => array(
907
					'postcode_before_city' => true,
908
					'state' => array(
909
						'required' => false
910
					)
911
				),
912
				'TR' => array(
913
					'postcode_before_city' => true,
914
					'state' => array(
915
						'label'       => __( 'Province', 'woocommerce' ),
916
					)
917
				),
918
				'US' => array(
919
					'postcode'  => array(
920
						'label'       => __( 'ZIP', 'woocommerce' ),
921
					),
922
					'state'     => array(
923
						'label'       => __( 'State', 'woocommerce' ),
924
					)
925
				),
926
				'GB' => array(
927
					'postcode'  => array(
928
						'label'       => __( 'Postcode', 'woocommerce' ),
929
					),
930
					'state'     => array(
931
						'label'       => __( 'County', 'woocommerce' ),
932
						'required'    => false
933
					)
934
				),
935
				'VN' => array(
936
					'postcode_before_city' => true,
937
					'state' => array(
938
						'required' => false
939
					),
940
					'postcode' => array(
941
						'required' => false,
942
						'hidden'   => false
943
					),
944
					'address_2' => array(
945
						'required' => false,
946
						'hidden'   => true
947
					)
948
				),
949
				'WS' => array(
950
					'postcode' => array(
951
						'required' => false,
952
						'hidden'   => true
953
					),
954
				),
955
				'ZA' => array(
956
					'state' => array(
957
						'label'       => __( 'Province', 'woocommerce' ),
958
					)
959
				),
960
				'ZW' => array(
961
					'postcode' => array(
962
						'required' => false,
963
						'hidden'   => true
964
					),
965
				),
966
			));
967
968
			$this->locale = array_intersect_key( $this->locale, array_merge( $this->get_allowed_countries(), $this->get_shipping_countries() ) );
969
970
			// Default Locale Can be filtered to override fields in get_address_fields().
971
			// Countries with no specific locale will use default.
972
			$this->locale['default'] = apply_filters('woocommerce_get_country_locale_default', $this->get_default_address_fields() );
973
974
			// Filter default AND shop base locales to allow overides via a single function. These will be used when changing countries on the checkout
975
			if ( ! isset( $this->locale[ $this->get_base_country() ] ) ) {
976
				$this->locale[ $this->get_base_country() ] = $this->locale['default'];
977
			}
978
979
			$this->locale['default']                   = apply_filters( 'woocommerce_get_country_locale_base', $this->locale['default'] );
980
			$this->locale[ $this->get_base_country() ] = apply_filters( 'woocommerce_get_country_locale_base', $this->locale[ $this->get_base_country() ] );
981
		}
982
983
		return $this->locale;
984
	}
985
986
	/**
987
	 * Apply locale and get address fields.
988
	 * @param  mixed  $country (default: '')
989
	 * @param  string $type (default: 'billing_')
990
	 * @return array
991
	 */
992
	public function get_address_fields( $country = '', $type = 'billing_' ) {
993
		if ( ! $country ) {
994
			$country = $this->get_base_country();
995
		}
996
997
		$fields = $this->get_default_address_fields();
998
		$locale = $this->get_country_locale();
999
1000
		if ( isset( $locale[ $country ] ) ) {
1001
			$fields = wc_array_overlay( $fields, $locale[ $country ] );
1002
		}
1003
1004
		// Prepend field keys
1005
		$address_fields = array();
1006
1007
		foreach ( $fields as $key => $value ) {
1008
			$keys = array_keys( $fields );
1009
			$address_fields[ $type . $key ] = $value;
1010
1011
			// Add email and phone after company or last
1012
			if ( 'billing_' === $type && ( 'company' === $key || ( ! array_key_exists( 'company', $fields ) && $key === end( $keys ) ) ) ) {
1013
				$address_fields['billing_email'] = array(
1014
					'label'        => __( 'Email Address', 'woocommerce' ),
1015
					'required'     => true,
1016
					'type'         => 'email',
1017
					'class'        => array( 'form-row-first' ),
1018
					'validate'     => array( 'email' ),
1019
					'autocomplete' => 'email',
1020
				);
1021
				$address_fields['billing_phone'] = array(
1022
					'label'        => __( 'Phone', 'woocommerce' ),
1023
					'required'     => true,
1024
					'type'         => 'tel',
1025
					'class'        => array( 'form-row-last' ),
1026
					'clear'        => true,
1027
					'validate'     => array( 'phone' ),
1028
					'autocomplete' => 'tel',
1029
				);
1030
			}
1031
		}
1032
1033
		$address_fields = apply_filters( 'woocommerce_' . $type . 'fields', $address_fields, $country );
1034
1035
		return $address_fields;
1036
	}
1037
}
1038