Passed
Push — master ( 59b574...7291fc )
by De
01:53
created

AbstractDatabase   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 220
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 23
c 1
b 0
f 0
dl 0
loc 220
rs 10

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A next() 0 3 1
A key() 0 3 1
C loadDatabase() 0 68 9
A current() 0 3 1
A count() 0 3 1
A getLocalMessagesPath() 0 3 1
A getIndexDefinition() 0 3 1
A getISONumber() 0 7 1
A find() 0 9 2
A valid() 0 3 1
A rewind() 0 3 1
A toArray() 0 3 1
A getDatabaseFilePath() 0 3 1
1
<?php
2
3
namespace Sokil\IsoCodes;
4
5
/**
6
 * Abstract collection of ISO entries
7
 */
8
abstract class AbstractDatabase implements \Iterator, \Countable
9
{
10
    /**
11
     * Path to ISO databases
12
     */
13
    const DATABASE_PATH = '/../databases';
14
15
    /**
16
     * Path to gettext localised messages
17
     */
18
    const MESSAGES_PATH = '/../messages';
19
20
    /**
21
     * @var array[]
22
     */
23
    private $clusterIndex = [];
24
25
    /**
26
     * Index to search by entry field's values
27
     *
28
     * @var array
29
     */
30
    private $index = [];
31
32
    public function __construct()
33
    {
34
        $this->loadDatabase($this->getDatabaseFilePath());
35
    }
36
37
    /**
38
     * ISO Standard Number
39
     *
40
     * @return string
41
     *
42
     * @throws \Exception
43
     */
44
    public static function getISONumber()
45
    {
46
        throw new \Exception(
47
            sprintf(
48
                'Method "%s" must be inmpemented in class %s',
49
                __METHOD__,
50
                get_class()
51
            )
52
        );
53
    }
54
55
    /**
56
     * @param array $entry
57
     *
58
     * @return object
59
     */
60
    abstract protected function arrayToEntry(array $entry);
61
62
    /**
63
     * List of entry fields to be indexed and searched.
64
     * May be override in child classes to search by indexed fields.
65
     *
66
     * @return array
67
     */
68
    protected function getIndexDefinition()
69
    {
70
        return [];
71
    }
72
73
    /**
74
     * @return string
75
     */
76
    private function getDatabaseFilePath()
77
    {
78
        return __DIR__ . self::DATABASE_PATH . '/iso_' . $this->getISONumber() . '.json';
79
    }
80
81
    /**
82
     * @return string
83
     */
84
    private function getLocalMessagesPath()
85
    {
86
        return __DIR__ . self::MESSAGES_PATH;
87
    }
88
89
    /**
90
     * @param string $databaseFile
91
     */
92
    private function loadDatabase($databaseFile)
93
    {
94
        // add gettext domain
95
        bindtextdomain(
96
            $this->getISONumber(),
97
            $this->getLocalMessagesPath()
98
        );
99
100
        bind_textdomain_codeset(
101
            $this->getISONumber(),
102
            'UTF-8'
103
        );
104
105
        // load database from json file
106
        $json = json_decode(
107
            file_get_contents($databaseFile),
108
            true
109
        );
110
111
        // build index from database
112
        $entryList = $json[$this->getISONumber()];
113
114
        // index database
115
        $indexedFields = $this->getIndexDefinition();
116
117
        if (empty($indexedFields)) {
118
            $this->clusterIndex = $entryList;
119
        } else {
120
            // init all defined indexes
121
            foreach ($entryList as &$entry) {
122
                foreach ($indexedFields as $indexName => $indexDefinition) {
123
                    if (is_array($indexDefinition)) {
124
                        $reference = &$this->index[$indexName];
125
                        // compound index
126
                        foreach ($indexDefinition as $indexDefinitionPart) {
127
                            // limited length of field
128
                            if (is_array($indexDefinitionPart)) {
129
                                $indexDefinitionPartValue = substr(
130
                                    $entry[$indexDefinitionPart[0]],
131
                                    0,
132
                                    $indexDefinitionPart[1]
133
                                );
134
                            } else {
135
                                $indexDefinitionPartValue = $entry[$indexDefinitionPart];
136
                            }
137
                            if (!isset($reference[$indexDefinitionPartValue])) {
138
                                $reference[$indexDefinitionPartValue] = [];
139
                            }
140
                            $reference = &$reference[$indexDefinitionPartValue];
141
                        }
142
143
                        $reference = $this->arrayToEntry($entry);
144
                    } else {
145
                        // single index
146
                        $indexName = $indexDefinition;
147
                        // skip empty field
148
                        if (empty($entry[$indexDefinition])) {
149
                            continue;
150
                        }
151
                        // add to index
152
                        $this->index[$indexName][$entry[$indexDefinition]] = $this->arrayToEntry($entry);
153
                    }
154
                }
155
            }
156
157
            // set cluster index as first index
158
            $clusterIndexName = key($this->index);
159
            $this->clusterIndex = &$this->index[$clusterIndexName];
160
        }
161
    }
162
163
    /**
164
     * @param string $indexedFieldName
165
     * @param string|int $fieldValue
166
     *
167
     * @return object|null
168
     */
169
    protected function find($indexedFieldName, $fieldValue)
170
    {
171
        if (!isset($this->index[$indexedFieldName][$fieldValue])) {
172
            throw new \InvalidArgumentException(sprintf('Unknown field %s', $indexedFieldName));
173
        }
174
175
        $result = $this->index[$indexedFieldName][$fieldValue];
176
177
        return $result;
178
    }
179
180
    /**
181
     * @return object[]
182
     */
183
    public function toArray()
184
    {
185
        return iterator_to_array($this);
186
    }
187
188
    /**
189
     * @return object
190
     */
191
    public function current()
192
    {
193
        return current($this->clusterIndex);
194
    }
195
196
    /**
197
     * @return int|null
198
     */
199
    public function key()
200
    {
201
        return key($this->clusterIndex);
202
    }
203
204
    public function next()
205
    {
206
        next($this->clusterIndex);
207
    }
208
    
209
    public function rewind()
210
    {
211
        reset($this->clusterIndex);
212
    }
213
214
    /**
215
     * @return bool
216
     */
217
    public function valid()
218
    {
219
        return $this->key() !== null;
220
    }
221
222
    /**
223
     * @return int
224
     */
225
    public function count()
226
    {
227
        return count($this->clusterIndex);
228
    }
229
}
230