1<?php
2
3namespace CommerceGuys\Intl\Country;
4
5use CommerceGuys\Intl\LocaleResolverTrait;
6use CommerceGuys\Intl\Exception\UnknownCountryException;
7
8/**
9 * Manages countries based on JSON definitions.
10 */
11class CountryRepository implements CountryRepositoryInterface
12{
13    use LocaleResolverTrait;
14
15    /**
16     * Base country definitions.
17     *
18     * Contains data common to all locales, such as the country numeric,
19     * three-letter, currency codes.
20     *
21     * @var array
22     */
23    protected $baseDefinitions = [];
24
25    /**
26     * Per-locale country definitions.
27     *
28     * @var array
29     */
30    protected $definitions = [];
31
32    /**
33     * Creates a CountryRepository instance.
34     *
35     * @param string $definitionPath The path to the country definitions.
36     *                               Defaults to 'resources/country'.
37     */
38    public function __construct($definitionPath = null)
39    {
40        $this->definitionPath = $definitionPath ? $definitionPath : __DIR__ . '/../../resources/country/';
41    }
42
43    /**
44     * {@inheritdoc}
45     */
46    public function get($countryCode, $locale = null, $fallbackLocale = null)
47    {
48        $locale = $this->resolveLocale($locale, $fallbackLocale);
49        $definitions = $this->loadDefinitions($locale);
50        if (!isset($definitions[$countryCode])) {
51            throw new UnknownCountryException($countryCode);
52        }
53
54        return $this->createCountryFromDefinition($countryCode, $definitions[$countryCode], $locale);
55    }
56
57    /**
58     * {@inheritdoc}
59     */
60    public function getAll($locale = null, $fallbackLocale = null)
61    {
62        $locale = $this->resolveLocale($locale, $fallbackLocale);
63        $definitions = $this->loadDefinitions($locale);
64        $countries = [];
65        foreach ($definitions as $countryCode => $definition) {
66            $countries[$countryCode] = $this->createCountryFromDefinition($countryCode, $definition, $locale);
67        }
68
69        return $countries;
70    }
71
72    /**
73     * {@inheritdoc}
74     */
75    public function getList($locale = null, $fallbackLocale = null)
76    {
77        $locale = $this->resolveLocale($locale, $fallbackLocale);
78        $definitions = $this->loadDefinitions($locale);
79        $list = [];
80        foreach ($definitions as $countryCode => $definition) {
81            $list[$countryCode] = $definition['name'];
82        }
83
84        return $list;
85    }
86
87    /**
88     * Loads the country definitions for the provided locale.
89     *
90     * @param string $locale The desired locale.
91     *
92     * @return array
93     */
94    protected function loadDefinitions($locale)
95    {
96        if (!isset($this->definitions[$locale])) {
97            $filename = $this->definitionPath . $locale . '.json';
98            $this->definitions[$locale] = json_decode(file_get_contents($filename), true);
99
100            // Make sure the base definitions have been loaded.
101            if (empty($this->baseDefinitions)) {
102                $this->baseDefinitions = json_decode(file_get_contents($this->definitionPath . 'base.json'), true);
103            }
104            // Merge-in base definitions.
105            foreach ($this->definitions[$locale] as $countryCode => $definition) {
106                $this->definitions[$locale][$countryCode] += $this->baseDefinitions[$countryCode];
107            }
108        }
109
110        return $this->definitions[$locale];
111    }
112
113    /**
114     * Creates a country object from the provided definition.
115     *
116     * @param string $countryCode The country code.
117     * @param array  $definition  The country definition.
118     * @param string $locale      The locale of the country definition.
119     *
120     * @return Country
121     */
122    protected function createCountryFromDefinition($countryCode, array $definition, $locale)
123    {
124        $country = new Country();
125        $setValues = \Closure::bind(function ($countryCode, $definition, $locale) {
126            $this->countryCode = $countryCode;
127            $this->name = $definition['name'];
128            $this->locale = $locale;
129            if (isset($definition['three_letter_code'])) {
130                $this->threeLetterCode = $definition['three_letter_code'];
131            }
132            if (isset($definition['numeric_code'])) {
133                $this->numericCode = $definition['numeric_code'];
134            }
135            if (isset($definition['currency_code'])) {
136                $this->currencyCode = $definition['currency_code'];
137            }
138        }, $country, '\CommerceGuys\Intl\Country\Country');
139        $setValues($countryCode, $definition, $locale);
140
141        return $country;
142    }
143}
144