-
Notifications
You must be signed in to change notification settings - Fork 135
/
AddressFormat.php
341 lines (305 loc) · 10.2 KB
/
AddressFormat.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
<?php
namespace CommerceGuys\Addressing\AddressFormat;
/**
* Provides metadata for storing and presenting a country's addresses.
*/
class AddressFormat
{
protected string $countryCode;
protected ?string $locale = null;
protected string $format;
protected ?string $localFormat = null;
protected array $usedFields = [];
/**
* The used fields, grouped by line.
*/
protected array $groupedFields = [];
protected mixed $requiredFields = [];
/**
* The fields that need to be uppercased.
*/
protected array $uppercaseFields = [];
protected array $defaultValues = [];
protected ?string $administrativeAreaType = null;
protected ?string $localityType = null;
protected ?string $dependentLocalityType = null;
protected ?string $postalCodeType = null;
protected ?string $postalCodePattern = null;
protected ?string $postalCodePrefix = null;
/**
* The subdivision depth.
*/
protected int $subdivisionDepth;
/**
* @throws \ReflectionException
*/
public function __construct(array $definition)
{
// Validate the presence of required properties.
foreach (['country_code', 'format'] as $requiredProperty) {
if (empty($definition[$requiredProperty])) {
throw new \InvalidArgumentException(sprintf('Missing required property %s.', $requiredProperty));
}
}
$this->countryCode = $definition['country_code'];
$this->locale = $definition['locale'] ?? null;
$this->format = $definition['format'];
$this->localFormat = $definition['local_format'] ?? null;
if (isset($definition['required_fields'])) {
AddressField::assertAllExist($definition['required_fields']);
$this->requiredFields = $definition['required_fields'];
}
if (isset($definition['default_values'])) {
AddressField::assertAllExist(array_keys($definition['default_values']));
$this->defaultValues = $definition['default_values'];
}
if (isset($definition['uppercase_fields'])) {
AddressField::assertAllExist($definition['uppercase_fields']);
$this->uppercaseFields = $definition['uppercase_fields'];
}
$this->subdivisionDepth = $definition['subdivision_depth'] ?? 0;
$usedFields = $this->getUsedFields();
if (in_array(AddressField::ADMINISTRATIVE_AREA, $usedFields)) {
if (isset($definition['administrative_area_type'])) {
AdministrativeAreaType::assertExists($definition['administrative_area_type']);
$this->administrativeAreaType = $definition['administrative_area_type'];
}
}
if (in_array(AddressField::LOCALITY, $usedFields)) {
if (isset($definition['locality_type'])) {
LocalityType::assertExists($definition['locality_type']);
$this->localityType = $definition['locality_type'];
}
}
if (in_array(AddressField::DEPENDENT_LOCALITY, $usedFields)) {
if (isset($definition['dependent_locality_type'])) {
DependentLocalityType::assertExists($definition['dependent_locality_type']);
$this->dependentLocalityType = $definition['dependent_locality_type'];
}
}
if (in_array(AddressField::POSTAL_CODE, $usedFields)) {
if (isset($definition['postal_code_type'])) {
PostalCodeType::assertExists($definition['postal_code_type']);
$this->postalCodeType = $definition['postal_code_type'];
}
$this->postalCodePattern = $definition['postal_code_pattern'] ?? null;
$this->postalCodePrefix = $definition['postal_code_prefix'] ?? null;
}
}
/**
* Gets the two-letter country code.
*
* This is a CLDR country code, since CLDR includes additional countries
* for addressing purposes, such as Canary Islands (IC).
*
* @return string The two-letter country code.
*/
public function getCountryCode(): string
{
return $this->countryCode;
}
/**
* Gets the locale.
*
* Only defined if the country has a local format.
*
* @return string|null The locale, if defined.
*/
public function getLocale(): ?string
{
return $this->locale;
}
/**
* Gets the format string.
*
* Defines the layout of an address, and consists of tokens (address fields
* prefixed with a '%') separated by unix newlines (\n).
* Example:
* <code>
* %givenName %familyName
* %organization
* %addressLine1
* %addressLine2
* %addressLine3
* %locality %administrativeArea %postalCode
* </code>
*
* @return string The format string.
*/
public function getFormat(): string
{
return $this->format;
}
/**
* Gets the local format string.
*
* Defined for countries that use a different ordering of fields when the
* address is entered in the native script. For example, China uses a
* major-to-minor format (country first, name last) when the address
* is entered in Chinese.
*
* @return string|null The local format string, if defined.
*/
public function getLocalFormat(): ?string
{
return $this->localFormat;
}
/**
* Gets the list of used fields.
*
* @return array An array of address fields.
* @throws \ReflectionException
*/
public function getUsedFields(): array
{
if (empty($this->usedFields)) {
$this->usedFields = [];
foreach (AddressField::getAll() as $field) {
if (str_contains($this->format, '%' . $field)) {
$this->usedFields[] = $field;
}
}
}
return $this->usedFields;
}
/**
* Gets the list of used subdivision fields.
*
* @throws \ReflectionException
*/
public function getUsedSubdivisionFields(): array
{
$fields = [
AddressField::ADMINISTRATIVE_AREA,
AddressField::LOCALITY,
AddressField::DEPENDENT_LOCALITY,
];
// Remove fields not used by the format, and reset the keys.
$fields = array_intersect($fields, $this->getUsedFields());
return array_values($fields);
}
/**
* Gets the list of required fields.
*
* @return AddressField[]
*/
public function getRequiredFields(): array
{
return $this->requiredFields;
}
/**
* Gets the list of fields that need to be uppercased.
*
* @return AddressField[]
*/
public function getUppercaseFields(): array
{
return $this->uppercaseFields;
}
/**
* Gets the default values.
*
* @return array The default values, keyed by field name.
*/
public function getDefaultValues(): array
{
return $this->defaultValues;
}
/**
* Gets the administrative area type.
*
* Used for presenting the correct label to the end-user.
*
* @return string|null The administrative area type, or null if the
* administrative area field isn't used.
*/
public function getAdministrativeAreaType(): ?string
{
return $this->administrativeAreaType;
}
/**
* Gets the locality type.
*
* Used for presenting the correct label to the end-user.
*
* @return string|null The locality type, or null if the locality field
* isn't used.
*/
public function getLocalityType(): ?string
{
return $this->localityType;
}
/**
* Gets the dependent locality type.
*
* Used for presenting the correct label to the end-user.
*
* @return string|null The dependent locality type, or null if the
* dependent locality field isn't used.
*/
public function getDependentLocalityType(): ?string
{
return $this->dependentLocalityType;
}
/**
* Gets the postal code type.
*
* Used for presenting the correct label to the end-user.
*
* @return string|null The postal code type, or null if the postal code
* field isn't used.
*/
public function getPostalCodeType(): ?string
{
return $this->postalCodeType;
}
/**
* Gets the postal code pattern.
*
* This is a regular expression pattern used to validate postal codes.
* Ignored if a subdivision defines its own full postal code pattern
* (E.g. Hong Kong when specified as a Chinese province).
*
* @return string|null The postal code pattern.
*/
public function getPostalCodePattern(): ?string
{
return $this->postalCodePattern;
}
/**
* Gets the postal code prefix.
*
* The prefix is optional and added to postal codes only when formatting
* an address for international mailing, as recommended by postal services.
*
* @return string|null The postal code prefix.
*/
public function getPostalCodePrefix(): ?string
{
return $this->postalCodePrefix;
}
/**
* Gets the subdivision depth.
*
* Indicates the number of levels of predefined subdivisions.
*
* Note that a country might use a subdivision field without having
* predefined subdivisions for it.
* For example, if the locality field is used by the address format, but
* the subdivision depth is 1, that means that the field element should be
* rendered as a textbox, since there's no known data to put in a dropdown.
*
* It is also possible to have no subdivisions for specific parents, even
* though the country generally has predefined subdivisions at that depth.
*
* @return int The subdivision depth. Possible values:
* 0: no subdivisions have been predefined.
* 1: administrative areas.
* 2: administrative areas, localities.
* 3: administrative areas, localities, dependent localities.
*/
public function getSubdivisionDepth(): int
{
return $this->subdivisionDepth;
}
}