Issues (662)

src/Supplemental/Plurals.php (8 issues)

1
<?php
2
3
namespace ICanBoogie\CLDR\Supplemental;
4
5
use ArrayObject;
6
use ICanBoogie\CLDR\Supplemental\Plurals\Rule;
7
use ICanBoogie\CLDR\Supplemental\Plurals\Samples;
8
9
use function array_keys;
10
use function array_shift;
11
use function explode;
12
use function is_null;
13
use function strlen;
14
use function strpos;
15
use function substr;
16
use function trim;
17
18
/**
19
 * Representation of plurals
20
 *
21
 * @extends ArrayObject<string, array<string, string>>
22
 *     Where _key_ is a locale and _value_ a rule.
23
 */
24
final class Plurals extends ArrayObject
25
{
26
    public const COUNT_ZERO = 'zero';
27
    public const COUNT_ONE = 'one';
28
    public const COUNT_TWO = 'two';
29
    public const COUNT_FEW = 'few';
30
    public const COUNT_MANY = 'many';
31
    public const COUNT_OTHER = 'other';
32
33
    /**
34
     * @private
35
     */
36
    public const RULE_COUNT_PREFIX = 'pluralRule-count-';
37
38
    /**
39
     * @var array<string, array<self::COUNT_*, Rule>>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, array<self::COUNT_*, Rule>> at position 6 could not be parsed: Expected '>' at position 6, but found 'self'.
Loading history...
40
     *     Where _key_ is a locale code and _value_ an array where _key_ is a rule count.
41
     */
42
    private array $rules = [];
43
44
    /**
45
     * @var array<string, array<self::COUNT_*, Samples>>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string, array<self::COUNT_*, Samples>> at position 6 could not be parsed: Expected '>' at position 6, but found 'self'.
Loading history...
46
     *     Where _key_ is a locale code and _value_ an array where _key_ is a rule count.
47
     */
48
    private array $samples = [];
49
50
    /**
51
     * @param float|int|numeric-string $number
0 ignored issues
show
Documentation Bug introduced by
The doc comment float|int|numeric-string at position 4 could not be parsed: Unknown type name 'numeric-string' at position 4 in float|int|numeric-string.
Loading history...
52
     *
53
     * @return self::COUNT_*
54
     */
55
    public function rule_for(float|int|string $number, string $language): string
56
    {
57
        $rules = $this->rule_instances_for($language);
58
59
        foreach ($rules as $count => $rule) {
60
            if ($rule->validate($number)) {
61
                return $count;
62
            }
63
        }
64
65
        return self::COUNT_OTHER;
66
    }
67
68
    /**
69
     * @return array<self::COUNT_*>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<self::COUNT_*> at position 2 could not be parsed: Expected '>' at position 2, but found 'self'.
Loading history...
70
     */
71
    public function rules_for(string $locale): array
72
    {
73
        return array_keys($this->rule_instances_for($locale));
74
    }
75
76
    /**
77
     * @return array<self::COUNT_*, Samples>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<self::COUNT_*, Samples> at position 2 could not be parsed: Expected '>' at position 2, but found 'self'.
Loading history...
78
     */
79
    public function samples_for(string $locale): array
80
    {
81
        return $this->samples[$locale] ??= $this->create_samples_for($locale);
82
    }
83
84
    /**
85
     * @return array<self::COUNT_*, Rule>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<self::COUNT_*, Rule> at position 2 could not be parsed: Expected '>' at position 2, but found 'self'.
Loading history...
86
     */
87
    private function rule_instances_for(string $language): array
88
    {
89
        return $this->rules[$language] ??= $this->create_rules_for($language);
90
    }
91
92
    /**
93
     * @return array<self::COUNT_*, Rule>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<self::COUNT_*, Rule> at position 2 could not be parsed: Expected '>' at position 2, but found 'self'.
Loading history...
94
     */
95
    private function create_rules_for(string $language): array
96
    {
97
        $rules = [];
98
        $prefix_length = strlen(self::RULE_COUNT_PREFIX);
99
100
        /** @phpstan-ignore-next-line */
101
        foreach ($this[$language] as $count => $rule_string) {
102
            $count = substr($count, $prefix_length);
103
            $rules[$count] = Rule::from($this->extract_rule($rule_string));
104
        }
105
106
        /** @var array<self::COUNT_*, Rule> */
107
        return $rules;
108
    }
109
110
    private function extract_rule(string $rule_string): string
111
    {
112
        $rule = explode('@', $rule_string, 2);
113
        $rule = array_shift($rule);
114
115
        return trim($rule);
116
    }
117
118
    /**
119
     * @return array<self::COUNT_*, Samples>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<self::COUNT_*, Samples> at position 2 could not be parsed: Expected '>' at position 2, but found 'self'.
Loading history...
120
     */
121
    private function create_samples_for(string $locale): array
122
    {
123
        $samples = [];
124
        $prefix_length = strlen(self::RULE_COUNT_PREFIX);
125
        $rules = $this[$locale];
126
127
        assert(!is_null($rules));
128
129
        foreach ($rules as $count => $rule_string) {
130
            $count = substr($count, $prefix_length);
131
            $samples[$count] = Samples::from($this->extract_samples($rule_string));
132
        }
133
134
        /** @var array<self::COUNT_*, Samples> */
135
        return $samples;
136
    }
137
138
    private function extract_samples(string $rule_string): string
139
    {
140
        $pos = strpos($rule_string, '@');
141
142
        if ($pos === false) {
143
            return "";
144
        }
145
146
        return substr($rule_string, $pos);
147
    }
148
}
149