Passed
Push — latest ( 9ae518...6b1f69 )
by Mark
02:40
created

Configuration::definePresetAllowedValues()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 15
ccs 8
cts 8
cp 1
rs 9.9666
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace UnicornFail\Emoji;
6
7
use Dflydev\DotAccessData\Data;
8
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
9
use Symfony\Component\OptionsResolver\Options;
10
use Symfony\Component\OptionsResolver\OptionsResolver;
11
use UnicornFail\Emoji\Emojibase\DatasetInterface;
12
use UnicornFail\Emoji\Emojibase\ShortcodeInterface;
13
use UnicornFail\Emoji\Exception\InvalidConfigurationException;
14
use UnicornFail\Emoji\Util\Normalize;
15
16
class Configuration extends Data implements ConfigurationInterface
17
{
18
    /**
19
     * @param mixed[]|\Traversable $configuration
20
     */
21 699
    public function __construct(?iterable $configuration = null)
22
    {
23 699
        $resolver = new OptionsResolver();
24 699
        $this->configureOptions($resolver);
25
26 699
        $options = $configuration !== null ? (new \ArrayObject($configuration))->getArrayCopy() : [];
0 ignored issues
show
Bug introduced by
$configuration of type iterable is incompatible with the type array|object expected by parameter $input of ArrayObject::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

26
        $options = $configuration !== null ? (new \ArrayObject(/** @scrutinizer ignore-type */ $configuration))->getArrayCopy() : [];
Loading history...
27
28
        try {
29 699
            $data = $resolver->resolve($options);
30 18
        } catch (\Throwable $throwable) {
31 18
            throw new InvalidConfigurationException($throwable->getMessage(), (int) $throwable->getCode(), $throwable->getPrevious());
32
        }
33
34 681
        parent::__construct($data);
35 681
    }
36
37
    /**
38
     * @param mixed[]|\Traversable $configuration
39
     */
40 699
    public static function create(?iterable $configuration = null): ConfigurationInterface
41
    {
42 699
        if ($configuration instanceof ConfigurationInterface) {
43 3
            return $configuration;
44
        }
45
46 696
        return new self($configuration);
47
    }
48
49 699
    public function configureOptions(OptionsResolver $resolver): void
50
    {
51 699
        $this->defineConvertEmoticons($resolver);
52 699
        $this->defineExcludeShortcodes($resolver);
53 699
        $this->defineLocale($resolver);
54 699
        $this->defineNative($resolver);
55 699
        $this->definePresentation($resolver);
56 699
        $this->definePreset($resolver);
57 699
        $this->defineStringableType($resolver);
58 699
    }
59
60 699
    protected function defineConvertEmoticons(OptionsResolver $resolver): void
61
    {
62 699
        $resolver->define('convertEmoticons')
63 699
            ->allowedTypes('bool')
64 699
            ->default(true);
65 699
    }
66
67 699
    protected function defineExcludeShortcodes(OptionsResolver $resolver): void
68
    {
69 699
        $resolver->define('excludeShortcodes')
70 699
            ->allowedTypes('string', 'string[]')
71 699
            ->default([])
72 699
            ->normalize(
73
            /**
74
             * @param mixed $value
75
             *
76
             * @return string[]
77
             */
78
                static function (Options $options, $value): array {
79 699
                    if (! $value) {
80 669
                        return $value;
81
                    }
82
83 33
                    return Normalize::shortcodes($value);
84 699
                }
85
            );
86 699
    }
87
88 699
    protected function defineLocale(OptionsResolver $resolver): void
89
    {
90 699
        $resolver->define('locale')
91 699
            ->allowedTypes('string')
92
            ->allowedValues(static function (string $value): bool {
93 699
                return ! ! Normalize::locale($value);
94 699
            })
95 699
            ->default('en')
96
            ->normalize(static function (Options $options, string $value): string {
97 690
                return Normalize::locale($value);
98 699
            });
99 699
    }
100
101 699
    protected function defineNative(OptionsResolver $resolver): void
102
    {
103 699
        $resolver->define('native')
104 699
            ->allowedTypes('bool')
105
            ->default(static function (Options $options): bool {
106 147
                return \in_array($options['locale'], DatasetInterface::NON_LATIN_LOCALES, true);
107 699
            })
108
            ->normalize(static function (Options $options, bool $value) {
109 690
                return $value && \in_array($options['locale'], DatasetInterface::NON_LATIN_LOCALES, true);
110 699
            });
111 699
    }
112
113 699
    protected function definePresentation(OptionsResolver $resolver): void
114
    {
115 699
        $resolver->define('presentation')
116 699
            ->allowedTypes('int', 'null')
117 699
            ->allowedValues(...DatasetInterface::SUPPORTED_PRESENTATIONS)
118 699
            ->default(DatasetInterface::EMOJI);
119 699
    }
120
121 699
    protected function definePreset(OptionsResolver $resolver): void
122
    {
123 699
        $resolver->define('preset')
124 699
            ->allowedTypes('string', 'string[]')
125 699
            ->allowedValues(\Closure::fromCallable([$this, 'definePresetAllowedValues']))
126 699
            ->default(ShortcodeInterface::DEFAULT_PRESETS)
127 699
            ->normalize(\Closure::fromCallable([$this, 'definePresetNormalize']));
128 699
    }
129
130
    /**
131
     * @param mixed $value
132
     */
133 690
    protected function definePresetAllowedValues($value): bool
134
    {
135 690
        foreach ((array) $value as $v) {
136 690
            if (! \in_array($v, ShortcodeInterface::SUPPORTED_PRESETS, true)) {
137 9
                throw new InvalidOptionsException(\sprintf(
138
                    'The option "preset" with value "%s" is invalid. Accepted values are: %s.',
139 9
                    $v,
140
                    \implode(', ', \array_map(static function ($s) {
141 9
                        return \sprintf('"%s"', $s);
142 9
                    }, ShortcodeInterface::SUPPORTED_PRESETS))
143
                ));
144
            }
145
        }
146
147 681
        return true;
148
    }
149
150
    /**
151
     * @param mixed $value
152
     *
153
     * @return string[]
154
     */
155 681
    protected function definePresetNormalize(Options $options, $value): array
156
    {
157
        // Presets.
158 681
        $presets = [];
159 681
        foreach ((array) $value as $preset) {
160 681
            if (isset(ShortcodeInterface::PRESET_ALIASES[$preset])) {
161 18
                $presets[] = ShortcodeInterface::PRESET_ALIASES[$preset];
162 663
            } elseif (isset(ShortcodeInterface::PRESETS[$preset])) {
163 663
                $presets[] = ShortcodeInterface::PRESETS[$preset];
164
            }
165
        }
166
167
        // Prepend the native preset if local is requires it and enabled.
168 681
        if ($options['native']) {
169 3
            \array_unshift($presets, ShortcodeInterface::PRESET_CLDR_NATIVE);
170
        }
171
172 681
        return \array_values(\array_unique($presets));
173
    }
174
175 699
    protected function defineStringableType(OptionsResolver $resolver): void
176
    {
177 699
        $resolver->define('stringableType')
178 699
            ->allowedTypes('int')
179 699
            ->allowedValues(Lexer::T_EMOTICON, Lexer::T_HTML_ENTITY, Lexer::T_SHORTCODE, Lexer::T_UNICODE)
180 699
            ->default(Lexer::T_UNICODE);
181 699
    }
182
183 72
    public function getIterator(): \ArrayObject
184
    {
185 72
        return new \ArrayObject($this->export());
186
    }
187
}
188