Test Failed
Push — docs ( 541d16...38ea2c )
by Mark
35:24
created

RuntimeDataset   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 232
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 79
dl 0
loc 232
rs 9.28
c 0
b 0
f 0
wmc 39

20 Methods

Rating   Name   Duplication   Size   Complexity  
A isNative() 0 15 4
A offsetGet() 0 3 1
A archive() 0 9 2
A __construct() 0 4 1
A valid() 0 3 1
A offsetExists() 0 3 1
A getDataset() 0 29 6
A current() 0 6 1
A indexBy() 0 3 1
A getLocale() 0 7 2
A seek() 0 3 1
A key() 0 3 1
A offsetUnset() 0 3 1
A offsetSet() 0 3 1
A filter() 0 3 1
A count() 0 3 1
A getPresets() 0 23 5
A unarchive() 0 27 6
A next() 0 3 1
A rewind() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace League\Emoji\Dataset;
6
7
use League\Configuration\ConfigurationInterface;
8
use League\Emoji\Emojibase\EmojibaseDatasetInterface;
9
use League\Emoji\Emojibase\EmojibaseShortcodeInterface;
10
use League\Emoji\Exception\FileNotFoundException;
11
use League\Emoji\Exception\LocalePresetException;
12
use League\Emoji\Exception\MalformedArchiveException;
13
use League\Emoji\Exception\UnarchiveException;
14
use League\Emoji\Parser\EmojiParser;
15
16
final class RuntimeDataset implements \ArrayAccess, \Countable, \SeekableIterator
17
{
18
    public const DEFAULT = 'en';
19
20
    /** @var ConfigurationInterface */
21
    private $config;
22
23
    /** @var ?Dataset */
24
    private $dataset;
25
26
    /** @var ?string */
27
    private $locale;
28
29
    /** @var ?bool */
30
    private $native;
31
32
    /** @var ?string[] */
33
    private $presets;
34
35
    public function __construct(ConfigurationInterface $configuration, ?Dataset $dataset = null)
36
    {
37
        $this->config  = $configuration;
38
        $this->dataset = $dataset;
39
    }
40
41
    /**
42
     * @param string[] $indices
43
     *
44
     * @return false|string
45
     */
46
    public static function archive(Dataset $dataset, array $indices = EmojiParser::INDICES)
47
    {
48
        foreach ($indices as $index) {
49
            $dataset->indexBy($index);
50
        }
51
52
        $serialize = \serialize($dataset);
53
54
        return \gzencode($serialize, 9);
55
    }
56
57
    public static function unarchive(string $filename): Dataset
58
    {
59
        if (! \file_exists($filename)) {
60
            throw new FileNotFoundException($filename);
61
        }
62
63
        if (
64
            ! ($contents = \file_get_contents($filename)) ||
65
            ! ($decoded = \gzdecode($contents))
66
        ) {
67
            throw new UnarchiveException($filename);
68
        }
69
70
        try {
71
            /** @var ?Dataset $dataset */
72
            $dataset = \unserialize((string) $decoded, [
73
                'allowed_classes' => [Dataset::class, Emoji::class],
74
            ]);
75
        } catch (\Throwable $throwable) {
76
            throw new MalformedArchiveException($filename, $throwable);
77
        }
78
79
        if (! $dataset instanceof Dataset) {
80
            throw new MalformedArchiveException($filename);
81
        }
82
83
        return $dataset;
84
    }
85
86
    public function count(): int
87
    {
88
        return $this->getDataset()->count();
89
    }
90
91
    public function current(): ?Emoji
92
    {
93
        /** @var ?Emoji $current */
94
        $current = $this->getDataset()->current();
95
96
        return $current;
97
    }
98
99
    /**
100
     * @param callable(Emoji):bool $callback
101
     */
102
    public function filter(callable $callback): RuntimeDataset
103
    {
104
        return new self($this->config, $this->getDataset()->filter($callback));
105
    }
106
107
    public function getDataset(): Dataset
108
    {
109
        if ($this->dataset === null) {
110
            /** @var \Throwable[] $throwables */
111
            $throwables = [];
112
            $locale     = $this->getLocale();
113
            $presets    = $this->getPresets();
114
115
            $remaining = $presets;
116
            while (\count($remaining) > 0) {
117
                $preset = \array_shift($remaining);
118
                try {
119
                    $this->dataset = self::unarchive(\sprintf('%s/%s/%s.gz', Dataset::DIRECTORY, $locale, $preset));
120
                    break;
121
                } catch (\Throwable $throwable) {
122
                    $throwables[$preset] = $throwable;
123
                }
124
            }
125
126
            if ($this->dataset === null) {
127
                if ($this->config->data()->has('locale')) {
128
                    $locale = (string) $this->config->data()->get('locale');
129
                }
130
131
                throw new LocalePresetException($locale, $throwables);
132
            }
133
        }
134
135
        return $this->dataset;
136
    }
137
138
    public function getLocale(): string
139
    {
140
        if ($this->locale === null) {
141
            $this->locale = (string) ($this->config->get('locale') ?? self::DEFAULT);
142
        }
143
144
        return $this->locale;
145
    }
146
147
    /**
148
     * @return string[]
149
     */
150
    public function getPresets(): array
151
    {
152
        if ($this->presets === null) {
153
            /** @var string[] $presets */
154
            $presets = (array) $this->config->get('preset');
155
156
            // Prepend the native preset if local is requires it and enabled.
157
            if ($this->isNative()) {
158
                \array_unshift($presets, EmojibaseShortcodeInterface::PRESET_CLDR_NATIVE);
159
            } else {
160
                /** @var int|false $key */
161
                $key = \array_search(EmojibaseShortcodeInterface::PRESET_CLDR_NATIVE, $presets, true);
162
163
                // Only remove the CLDR native preset if it's not the only one provided.
164
                if ($key !== false && \count($presets) !== 1) {
165
                    \array_splice($presets, $key, 1);
166
                }
167
            }
168
169
            $this->presets = \array_filter(\array_values(\array_unique(\array_filter($presets))));
170
        }
171
172
        return $this->presets;
173
    }
174
175
    public function indexBy(string $index = 'hexcode'): RuntimeDataset
176
    {
177
        return new self($this->config, $this->getDataset()->indexBy($index));
178
    }
179
180
    public function isNative(): bool
181
    {
182
        if ($this->native === null) {
183
            $locale  = $this->getLocale();
184
            $default = \in_array($locale, EmojibaseDatasetInterface::NON_LATIN_LOCALES, true);
185
186
            /** @var ?bool $native */
187
            $native = $this->config->get('native');
188
189
            $this->native = $native === null
190
                ? $default
191
                : $native && $default;
192
        }
193
194
        return $this->native;
195
    }
196
197
    public function key(): string
198
    {
199
        return (string) $this->getDataset()->key();
200
    }
201
202
    public function next(): void
203
    {
204
        $this->getDataset()->next();
205
    }
206
207
    /** @param string $offset */
208
    public function offsetExists($offset): bool // phpcs:ignore
209
    {
210
        return $this->getDataset()->offsetExists($offset);
211
    }
212
213
    /** @param string $offset */
214
    public function offsetGet($offset): ?Emoji // phpcs:ignore
215
    {
216
        return $this->getDataset()->offsetGet($offset);
217
    }
218
219
    /**
220
     * @param string $offset
221
     * @param mixed  $value
222
     */
223
    public function offsetSet($offset, $value): void // phpcs:ignore
224
    {
225
        throw new \BadMethodCallException('Unable to modify immutable object.');
226
    }
227
228
    /** @param string $offset */
229
    public function offsetUnset($offset): void // phpcs:ignore
230
    {
231
        throw new \BadMethodCallException('Unable to modify immutable object.');
232
    }
233
234
    public function rewind(): void
235
    {
236
        $this->getDataset()->rewind();
237
    }
238
239
    /** @param int $position */
240
    public function seek($position): void // phpcs:ignore
241
    {
242
        $this->getDataset()->seek($position);
243
    }
244
245
    public function valid(): bool
246
    {
247
        return $this->getDataset()->valid();
248
    }
249
}
250