Territory::is_containing()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace ICanBoogie\CLDR\Supplemental\Territory;
4
5
use DateTimeImmutable;
6
use DateTimeInterface;
7
use ICanBoogie\Accessor\AccessorTrait;
8
use ICanBoogie\CLDR\Core\Locale;
9
use ICanBoogie\CLDR\Core\LocaleId;
10
use ICanBoogie\CLDR\Core\Localizable;
11
use ICanBoogie\CLDR\Numbers\Currency;
12
use ICanBoogie\CLDR\Repository;
13
use Throwable;
14
15
use function ICanBoogie\trim_prefix;
16
use function in_array;
17
18
/**
19
 * A territory.
20
 *
21
 * @property-read array<string, mixed> $containment The `territoryContainment` data.
22
 * @property-read array<string, mixed> $info The `territoryInfo` data.
23
 * @property-read RegionCurrencies $currencies The currencies available in the territory.
24
 * @property-read Currency|null $currency The current currency.
25
 * @property-read string $first_day The code of the first day of the week for the territory.
26
 * @property-read string $weekend_start The code of the first day of the weekend.
27
 * @property-read string $weekend_end The code of the last day of the weekend.
28
 * @property-read string|bool $language The ISO code of the official language, or `false` if it
29
 * cannot be determined.
30
 * @property-read string $name_as_* The name of the territory in the specified language.
31
 * @property-read int $population The population of the territory.
32
 *
33
 * @link https://www.unicode.org/reports/tr35/tr35-numbers.html#Supplemental_Currency_Data
34
 *
35
 * @implements Localizable<Territory, TerritoryLocalized>
36
 */
37
final class Territory implements Localizable
38
{
39
    /**
40
     * @uses get_containment
41
     * @uses get_currencies
42
     * @uses get_currency
43
     * @uses get_info
44
     * @uses get_language
45
     * @uses get_first_day
46
     * @uses get_weekend_start
47
     * @uses get_weekend_end
48
     * @uses get_population
49
     */
50
    use AccessorTrait;
51
52
    /**
53
     * @phpstan-ignore-next-line
54
     */
55
    private array $containment;
56
57
    /**
58
     * @return array<string, mixed>
59
     */
60
    private function get_containment(): array
61
    {
62
        return $this->containment ??= $this->retrieve_from_supplemental('territoryContainment');
63
    }
64
65
    private RegionCurrencies $currencies;
66
67
    private function get_currencies(): RegionCurrencies
68
    {
69
        return $this->currencies ??= RegionCurrencies::from(
70
            $this->repository->supplemental['currencyData']['region'][$this->code->value]
71
        );
72
    }
73
74
    private ?Currency $currency;
75
76
    /**
77
     * @throws Throwable
78
     */
79
    private function get_currency(): ?Currency
0 ignored issues
show
Unused Code introduced by
The method get_currency() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
80
    {
81
        return $this->currency ??= $this->currency_at();
82
    }
83
84
    /**
85
     * @phpstan-ignore-next-line
86
     */
87
    private array $info;
88
89
    /**
90
     * @return array<string, mixed>
91
     */
92
    private function get_info(): array
93
    {
94
        return $this->info ??= $this->retrieve_from_supplemental('territoryInfo');
95
    }
96
97
    private string|false $language;
98
99
    /**
100
     * Return the ISO code of the official language of the territory.
101
     *
102
     * @return string|false
103
     *     The ISO code of the official language, or `false` if it cannot be determined.
104
     */
105
    private function get_language(): string|false
0 ignored issues
show
Unused Code introduced by
The method get_language() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
106
    {
107
        $make = function () {
108
            $info = $this->get_info();
109
110
            foreach ($info['languagePopulation'] as $language => $lp) {
111
                if (
112
                    empty($lp['_officialStatus'])
113
                    || ($lp['_officialStatus'] != "official" && $lp['_officialStatus'] != "de_facto_official")
114
                ) {
115
                    continue;
116
                }
117
118
                return $language;
119
            }
120
121
            return false;
122
        };
123
124
        return $this->language ??= $make();
0 ignored issues
show
Bug Best Practice introduced by
The expression return AssignCoalesceNode could return the type boolean which is incompatible with the type-hinted return false|string. Consider adding an additional type-check to rule them out.
Loading history...
125
    }
126
127
    private function get_first_day(): string
0 ignored issues
show
Unused Code introduced by
The method get_first_day() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
128
    {
129
        return $this->resolve_week_data('firstDay');
130
    }
131
132
    private function get_weekend_start(): string
0 ignored issues
show
Unused Code introduced by
The method get_weekend_start() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
133
    {
134
        return $this->resolve_week_data('weekendStart');
135
    }
136
137
    private function get_weekend_end(): string
0 ignored issues
show
Unused Code introduced by
The method get_weekend_end() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
138
    {
139
        return $this->resolve_week_data('weekendEnd');
140
    }
141
142
    private function get_population(): int
0 ignored issues
show
Unused Code introduced by
The method get_population() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
143
    {
144
        $info = $this->get_info();
145
146
        return (int)$info['_population'];
147
    }
148
149
    public function __construct(
150
        public readonly Repository $repository,
151
        public readonly TerritoryCode $code,
152
    ) {
153
    }
154
155
    public function __toString(): string
156
    {
157
        return $this->code->value;
158
    }
159
160
    /**
161
     * @param string $property
162
     *
163
     * @return mixed
164
     */
165
    public function __get($property)
166
    {
167
        if (str_starts_with($property, 'name_as_')) {
168
            $locale_id = trim_prefix($property, 'name_as_');
169
            $locale_id = strtr($locale_id, '_', '-');
170
171
            return $this->name_as($locale_id);
172
        }
173
174
        return $this->accessor_get($property);
175
    }
176
177
    /**
178
     * @return array<string, mixed>
179
     */
180
    private function retrieve_from_supplemental(string $section): array
181
    {
182
        return $this->repository->supplemental[$section][$this->code->value];
183
    }
184
185
    /**
186
     * Return the currency used in the territory at a point in time.
187
     *
188
     * @param DateTimeInterface|string|null $date
189
     *
190
     * @throws Throwable
191
     */
192
    public function currency_at(DateTimeInterface|string|null $date = null): ?Currency
193
    {
194
        $date = $this->ensure_is_datetime($date)->format('Y-m-d');
195
196
        $code = $this->get_currencies()->at($date)?->code;
197
198
        if (!$code) {
199
            return null;
200
        }
201
202
        return Currency::of($code);
203
    }
204
205
    /**
206
     * Whether the territory contains the specified territory.
207
     */
208
    public function is_containing(string $code): bool
209
    {
210
        $containment = $this->get_containment();
211
212
        return in_array($code, $containment['_contains']);
213
    }
214
215
    /**
216
     * Return the name of the territory localized according to the specified locale code.
217
     */
218
    public function name_as(string|LocaleId $locale_id): string
219
    {
220
        return $this->localized($locale_id)->name;
221
    }
222
223
    public function localized(Locale|LocaleId|string $locale): TerritoryLocalized
224
    {
225
        if (!$locale instanceof Locale) {
226
            $locale = $this->repository->locale_for($locale);
227
        }
228
229
        return new TerritoryLocalized($this, $locale);
230
    }
231
232
    private function resolve_week_data(string $which): string
233
    {
234
        $code = $this->code;
235
        $data = $this->repository->supplemental['weekData'][$which];
236
237
        return $data[$code->value] ?? $data['001'];
238
    }
239
240
    private function ensure_is_datetime(DateTimeInterface|string|null $datetime): DateTimeInterface
241
    {
242
        if ($datetime === null) {
243
            return new DateTimeImmutable();
244
        }
245
246
        if ($datetime instanceof DateTimeInterface) {
247
            return $datetime;
248
        }
249
250
        return new DateTimeImmutable($datetime);
251
    }
252
}
253