1<?php
2
3namespace CommerceGuys\Intl\Currency;
4
5use CommerceGuys\Intl\LocaleResolverTrait;
6use CommerceGuys\Intl\Exception\UnknownCurrencyException;
7
8/**
9 * Manages currencies based on JSON definitions.
10 */
11class CurrencyRepository implements CurrencyRepositoryInterface
12{
13    use LocaleResolverTrait;
14
15    /**
16     * Base currency definitions.
17     *
18     * Contains data common to all locales, such as the currency numeric
19     * code, number of fraction digits.
20     *
21     * @var array
22     */
23    protected $baseDefinitions = [];
24
25    /**
26     * Per-locale currency definitions.
27     *
28     * @var array
29     */
30    protected $definitions = [];
31
32    /**
33     * Creates a CurrencyRepository instance.
34     *
35     * @param string $definitionPath The path to the currency definitions.
36     *                               Defaults to 'resources/currency'.
37     */
38    public function __construct($definitionPath = null)
39    {
40        $this->definitionPath = $definitionPath ? $definitionPath : __DIR__ . '/../../resources/currency/';
41    }
42
43    /**
44     * {@inheritdoc}
45     */
46    public function get($currencyCode, $locale = null, $fallbackLocale = null)
47    {
48        $locale = $this->resolveLocale($locale, $fallbackLocale);
49        $definitions = $this->loadDefinitions($locale);
50        if (!isset($definitions[$currencyCode])) {
51            throw new UnknownCurrencyException($currencyCode);
52        }
53
54        return $this->createCurrencyFromDefinition($currencyCode, $definitions[$currencyCode], $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        $currencies = [];
65        foreach ($definitions as $currencyCode => $definition) {
66            $currencies[$currencyCode] = $this->createCurrencyFromDefinition($currencyCode, $definition, $locale);
67        }
68
69        return $currencies;
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 $currencyCode => $definition) {
81            $list[$currencyCode] = $definition['name'];
82        }
83
84        return $list;
85    }
86
87    /**
88     * Loads the currency 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 $currencyCode => $definition) {
106                $this->definitions[$locale][$currencyCode] += $this->baseDefinitions[$currencyCode];
107            }
108        }
109
110        return $this->definitions[$locale];
111    }
112
113    /**
114     * Creates a currency object from the provided definition.
115     *
116     * @param string $currencyCode The currency code.
117     * @param array  $definition   The currency definition.
118     * @param string $locale       The locale of the currency definition.
119     *
120     * @return Currency
121     */
122    protected function createCurrencyFromDefinition($currencyCode, array $definition, $locale)
123    {
124        if (!isset($definition['symbol'])) {
125            $definition['symbol'] = $currencyCode;
126        }
127        if (!isset($definition['fraction_digits'])) {
128            $definition['fraction_digits'] = 2;
129        }
130
131        $currency = new Currency();
132        $setValues = \Closure::bind(function ($currencyCode, $definition, $locale) {
133            $this->currencyCode = $currencyCode;
134            $this->name = $definition['name'];
135            $this->numericCode = $definition['numeric_code'];
136            $this->symbol = $definition['symbol'];
137            $this->fractionDigits = $definition['fraction_digits'];
138            $this->locale = $locale;
139        }, $currency, '\CommerceGuys\Intl\Currency\Currency');
140        $setValues($currencyCode, $definition, $locale);
141
142        return $currency;
143    }
144}
145