| Total Complexity | 45 |
| Total Lines | 274 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 1 |
Complex classes like RuntimeDataset often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use RuntimeDataset, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 17 | final class RuntimeDataset implements \ArrayAccess, \Countable, \SeekableIterator |
||
| 18 | { |
||
| 19 | public const DEFAULT = 'en'; |
||
| 20 | |||
| 21 | /** @var ConfigurationInterface */ |
||
| 22 | private $config; |
||
| 23 | |||
| 24 | /** @var ?Dataset */ |
||
| 25 | private $dataset; |
||
| 26 | |||
| 27 | /** @var ?string */ |
||
| 28 | private $locale; |
||
| 29 | |||
| 30 | /** @var ?bool */ |
||
| 31 | private $native; |
||
| 32 | |||
| 33 | /** @var ?string[] */ |
||
| 34 | private $presets; |
||
| 35 | |||
| 36 | public function __construct(ConfigurationInterface $configuration, ?Dataset $dataset = null) |
||
| 37 | { |
||
| 38 | $this->config = $configuration; |
||
| 39 | $this->dataset = $dataset; |
||
| 40 | } |
||
| 41 | |||
| 42 | /** |
||
| 43 | * @param string[] $indices |
||
| 44 | * |
||
| 45 | * @return false|string |
||
| 46 | */ |
||
| 47 | public static function archive(Dataset $dataset, array $indices = EmojiParser::INDICES) |
||
| 48 | { |
||
| 49 | foreach ($indices as $index) { |
||
| 50 | $dataset->indexBy($index); |
||
| 51 | } |
||
| 52 | |||
| 53 | $serialize = \serialize($dataset); |
||
| 54 | |||
| 55 | return \gzencode($serialize, 9); |
||
| 56 | } |
||
| 57 | |||
| 58 | public static function unarchive(string $filename): Dataset |
||
| 59 | { |
||
| 60 | if (! \file_exists($filename)) { |
||
| 61 | throw new FileNotFoundException($filename); |
||
| 62 | } |
||
| 63 | |||
| 64 | if ( |
||
| 65 | ! ($contents = \file_get_contents($filename)) || |
||
| 66 | ! ($decoded = \gzdecode($contents)) |
||
| 67 | ) { |
||
| 68 | throw new UnarchiveException($filename); |
||
| 69 | } |
||
| 70 | |||
| 71 | try { |
||
| 72 | /** @var ?Dataset $dataset */ |
||
| 73 | $dataset = \unserialize((string) $decoded); |
||
| 74 | } catch (\Throwable $throwable) { |
||
| 75 | throw new MalformedArchiveException($filename, $throwable); |
||
| 76 | } |
||
| 77 | |||
| 78 | if (! $dataset instanceof Dataset) { |
||
| 79 | throw new MalformedArchiveException($filename); |
||
| 80 | } |
||
| 81 | |||
| 82 | return $dataset; |
||
| 83 | } |
||
| 84 | |||
| 85 | public static function normalizeLocale(string $locale): string |
||
| 86 | { |
||
| 87 | // Immediately return if locale is an exact match. |
||
| 88 | if (\in_array($locale, EmojibaseDatasetInterface::SUPPORTED_LOCALES, true)) { |
||
| 89 | return $locale; |
||
| 90 | } |
||
| 91 | |||
| 92 | // Immediately return if this local has already been normalized. |
||
| 93 | /** @var string[] $normalized */ |
||
| 94 | static $normalized = []; |
||
| 95 | if (isset($normalized[$locale])) { |
||
| 96 | return $normalized[$locale]; |
||
| 97 | } |
||
| 98 | |||
| 99 | $original = $locale; |
||
| 100 | $normalized[$original] = ''; |
||
| 101 | |||
| 102 | // Otherwise, see if it just needs some TLC. |
||
| 103 | $locale = \strtolower($locale); |
||
| 104 | $locale = \preg_replace('/[^a-z]/', '-', $locale) ?? $locale; |
||
| 105 | foreach ([$locale, \current(\explode('-', $locale, 2))] as $locale) { |
||
| 106 | if (\in_array($locale, EmojibaseDatasetInterface::SUPPORTED_LOCALES, true)) { |
||
| 107 | $normalized[$original] = $locale; |
||
| 108 | break; |
||
| 109 | } |
||
| 110 | } |
||
| 111 | |||
| 112 | return $normalized[$original]; |
||
| 113 | } |
||
| 114 | |||
| 115 | /** |
||
| 116 | * @param string|string[] $value |
||
| 117 | * |
||
| 118 | * @return string[] |
||
| 119 | */ |
||
| 120 | public static function normalizePreset($value): array |
||
| 121 | { |
||
| 122 | // Presets. |
||
| 123 | $presets = []; |
||
| 124 | foreach ((array) $value as $preset) { |
||
| 125 | if (isset(EmojibaseShortcodeInterface::PRESET_ALIASES[$preset])) { |
||
| 126 | $presets[] = EmojibaseShortcodeInterface::PRESET_ALIASES[$preset]; |
||
| 127 | } elseif (isset(EmojibaseShortcodeInterface::PRESETS[$preset])) { |
||
| 128 | $presets[] = EmojibaseShortcodeInterface::PRESETS[$preset]; |
||
| 129 | } else { |
||
| 130 | throw InvalidConfigurationException::forConfigOption('preset', $preset, \sprintf( |
||
| 131 | 'Accepted values are: %s.', |
||
| 132 | \implode(', ', \array_map( |
||
| 133 | static function ($s) { |
||
| 134 | return \sprintf('"%s"', $s); |
||
| 135 | }, |
||
| 136 | EmojibaseShortcodeInterface::SUPPORTED_PRESETS |
||
| 137 | )) |
||
| 138 | )); |
||
| 139 | } |
||
| 140 | } |
||
| 141 | |||
| 142 | return \array_values(\array_unique($presets)); |
||
| 143 | } |
||
| 144 | |||
| 145 | public function count(): int |
||
| 146 | { |
||
| 147 | return $this->getDataset()->count(); |
||
| 148 | } |
||
| 149 | |||
| 150 | public function current(): Emoji |
||
| 151 | { |
||
| 152 | return $this->getDataset()->current(); |
||
| 153 | } |
||
| 154 | |||
| 155 | /** |
||
| 156 | * @param callable(Emoji):bool $callback |
||
| 157 | */ |
||
| 158 | public function filter(callable $callback): RuntimeDataset |
||
| 161 | } |
||
| 162 | |||
| 163 | public function getDataset(): Dataset |
||
| 164 | { |
||
| 165 | if ($this->dataset === null) { |
||
| 166 | $locale = $this->getLocale(); |
||
| 167 | $presets = $this->getPresets(); |
||
| 168 | $throwables = []; |
||
| 169 | $presets = \array_filter($presets); |
||
| 170 | $remaining = $presets; |
||
| 171 | while (\count($remaining) > 0) { |
||
| 172 | $preset = \array_shift($remaining); |
||
| 173 | try { |
||
| 174 | $this->dataset = self::unarchive(\sprintf('%s/%s/%s.gz', Dataset::DIRECTORY, $locale, $preset)); |
||
| 175 | break; |
||
| 176 | } catch (\Throwable $throwable) { |
||
| 177 | $throwables[$preset] = $throwable; |
||
| 178 | } |
||
| 179 | } |
||
| 180 | |||
| 181 | if ($this->dataset === null) { |
||
| 182 | throw new LocalePresetException($locale, $throwables); |
||
| 183 | } |
||
| 184 | } |
||
| 185 | |||
| 186 | return $this->dataset; |
||
| 187 | } |
||
| 188 | |||
| 189 | public function getLocale(): string |
||
| 190 | { |
||
| 191 | if ($this->locale === null) { |
||
| 192 | $this->locale = (string) ($this->config->get('local') ?? self::DEFAULT); |
||
| 193 | } |
||
| 194 | |||
| 195 | return $this->locale; |
||
| 196 | } |
||
| 197 | |||
| 198 | /** |
||
| 199 | * @return string[] |
||
| 200 | */ |
||
| 201 | public function getPresets(): array |
||
| 216 | } |
||
| 217 | |||
| 218 | public function indexBy(string $index = 'hexcode'): RuntimeDataset |
||
| 219 | { |
||
| 220 | return new self($this->config, $this->getDataset()->indexBy($index)); |
||
| 221 | } |
||
| 222 | |||
| 223 | public function isNative(): bool |
||
| 224 | { |
||
| 225 | if ($this->native === null) { |
||
| 226 | $locale = $this->getLocale(); |
||
| 227 | $default = \in_array($locale, EmojibaseDatasetInterface::NON_LATIN_LOCALES, true); |
||
| 228 | |||
| 229 | /** @var ?bool $native */ |
||
| 230 | $native = $this->config->get('native'); |
||
| 231 | |||
| 232 | $this->native = $native === null |
||
| 233 | ? $default |
||
| 234 | : $native && $default; |
||
| 235 | } |
||
| 236 | |||
| 237 | return $this->native; |
||
| 238 | } |
||
| 239 | |||
| 240 | public function key(): string |
||
| 241 | { |
||
| 242 | return (string) $this->getDataset()->key(); |
||
| 243 | } |
||
| 244 | |||
| 245 | public function next(): void |
||
| 246 | { |
||
| 247 | $this->getDataset()->next(); |
||
| 248 | } |
||
| 249 | |||
| 250 | /** @param string $offset */ |
||
| 251 | public function offsetExists($offset): bool // phpcs:ignore |
||
| 252 | { |
||
| 253 | return $this->getDataset()->offsetExists($offset); |
||
| 254 | } |
||
| 255 | |||
| 256 | /** @param string $offset */ |
||
| 257 | public function offsetGet($offset): ?Emoji // phpcs:ignore |
||
| 258 | { |
||
| 259 | return $this->getDataset()->offsetGet($offset); |
||
| 260 | } |
||
| 261 | |||
| 262 | /** |
||
| 263 | * @param string $offset |
||
| 264 | * @param mixed $value |
||
| 265 | */ |
||
| 266 | public function offsetSet($offset, $value): void // phpcs:ignore |
||
| 269 | } |
||
| 270 | |||
| 271 | /** @param string $offset */ |
||
| 272 | public function offsetUnset($offset): void // phpcs:ignore |
||
| 273 | { |
||
| 274 | $this->getDataset()->offsetUnset($offset); |
||
| 275 | } |
||
| 276 | |||
| 277 | public function rewind(): void |
||
| 280 | } |
||
| 281 | |||
| 282 | /** @param int $position */ |
||
| 283 | public function seek($position): void // phpcs:ignore |
||
| 284 | { |
||
| 286 | } |
||
| 287 | |||
| 288 | public function valid(): bool |
||
| 291 | } |
||
| 292 | } |
||
| 293 |