Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.73% |
78 / 79 |
|
66.67% |
2 / 3 |
CRAP | |
0.00% |
0 / 1 |
MultiListBuilder | |
98.73% |
78 / 79 |
|
66.67% |
2 / 3 |
17 | |
0.00% |
0 / 1 |
buildWeightedTagsFromLegacyParameters | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
buildWeightedTags | |
97.06% |
33 / 34 |
|
0.00% |
0 / 1 |
5 | |||
buildTagWeightsFromLegacyParameters | |
100.00% |
43 / 43 |
|
100.00% |
1 / 1 |
11 |
1 | <?php |
2 | |
3 | namespace CirrusSearch\Extra\MultiList; |
4 | |
5 | use Wikimedia\Assert\Assert; |
6 | |
7 | /** |
8 | * Utility class for encoding weighted tags. |
9 | * |
10 | * @see https://wikitech.wikimedia.org/wiki/Search/WeightedTags |
11 | */ |
12 | class MultiListBuilder { |
13 | |
14 | private const WEIGHTED_TAG_DEFAULT_NAME = 'exists'; |
15 | |
16 | /** |
17 | * @param string $tagPrefix A prefix shared by all `$tagNames` |
18 | * @param string|string[]|null $tagNames Optional tag name or list of tag names. |
19 | * Each tag will be set for each target ID. Omit for tags which are fully defined by their prefix. |
20 | * @param int|int[]|null $tagWeights Optional tag weight(s). |
21 | * * If `$tagNames` is null, an integer. |
22 | * * Otherwise, a `[ tagName => weight ]` map. This may only use a subset of `$tagNames` as keys. |
23 | * However, only valid keys (which exist in `$tagNames`) will result in returned tags. |
24 | * |
25 | * A single weight ranges between 1-1000. |
26 | * |
27 | * @return MultiListWeightedTag[] |
28 | * @deprecated use {@link buildWeightedTags} instead use {@link buildTagWeightsFromLegacyParameters} to migrate |
29 | */ |
30 | public static function buildWeightedTagsFromLegacyParameters( |
31 | string $tagPrefix, |
32 | $tagNames = null, |
33 | $tagWeights = null |
34 | ): array { |
35 | $tagWeights = self::buildTagWeightsFromLegacyParameters( $tagNames, $tagWeights ); |
36 | |
37 | return self::buildWeightedTags( $tagPrefix, $tagWeights ); |
38 | } |
39 | |
40 | /** |
41 | * @param string $tagPrefix A prefix shared by all `$tagNames` |
42 | * @param null|null[]|int[] $tagWeightsByName Optional tag weights. A map of optional weights, keyed by tag name. |
43 | * Omit for tags which are fully defined by their prefix. |
44 | * A single weight ranges between 1-1000. |
45 | * |
46 | * @return MultiListWeightedTag[] |
47 | */ |
48 | public static function buildWeightedTags( |
49 | string $tagPrefix, |
50 | ?array $tagWeightsByName = null |
51 | ): array { |
52 | Assert::precondition( |
53 | strpos( $tagPrefix, MultiListItem::DELIMITER ) === false, |
54 | "invalid tag prefix $tagPrefix: must not contain " . MultiListItem::DELIMITER |
55 | ); |
56 | |
57 | Assert::parameterType( |
58 | [ |
59 | 'array', |
60 | 'null' |
61 | ], |
62 | $tagWeightsByName, |
63 | '$tagWeightsByName' |
64 | ); |
65 | |
66 | if ( $tagWeightsByName === null ) { |
67 | $tagWeightsByName = [ self::WEIGHTED_TAG_DEFAULT_NAME => null ]; |
68 | } |
69 | |
70 | foreach ( $tagWeightsByName as $tagName => $tagWeight ) { |
71 | Assert::precondition( |
72 | strpos( $tagName, MultiListWeightedTag::WEIGHT_DELIMITER ) === false, |
73 | "invalid tag name $tagName: must not contain " . MultiListWeightedTag::WEIGHT_DELIMITER |
74 | ); |
75 | if ( $tagWeight !== null ) { |
76 | Assert::precondition( |
77 | is_int( $tagWeight ), |
78 | "weights must be integers but $tagWeight is " . get_debug_type( $tagWeight ) |
79 | ); |
80 | Assert::precondition( |
81 | $tagWeight >= 1 && $tagWeight <= 1000, |
82 | "weights must be between 1 and 1000 (found: $tagWeight)" |
83 | ); |
84 | } |
85 | } |
86 | |
87 | return array_map( |
88 | static fn ( $tagName ) => new MultiListWeightedTag( |
89 | $tagPrefix, $tagName, $tagWeightsByName[$tagName] |
90 | ), |
91 | array_keys( $tagWeightsByName ) |
92 | ); |
93 | } |
94 | |
95 | /** |
96 | * Merges `$tagNames` and `$tagWeights` into a single map that can be passed to {@link buildWeightedTags}. |
97 | * |
98 | * @param null|string|string[] $tagNames Optional tag name or list of tag names. |
99 | * Each tag will be set for each target ID. Omit for tags which are fully defined by their prefix. |
100 | * @param null|int|int[] $tagWeights Optional tag weight(s). |
101 | * * If `$tagNames` is null, an integer. |
102 | * * Otherwise, a `[ tagName => weight ]` map. This may only use a subset of `$tagNames` as keys. |
103 | * * However, only valid keys (which exist in `$tagNames`) will result in returned tags. |
104 | * |
105 | * A single weight ranges between 1-1000. |
106 | * |
107 | * @return null[]|int[] A map of tag weights keyed by tag name |
108 | * @see buildWeightedTags |
109 | * @see buildWeightedTagsFromLegacyParameters |
110 | */ |
111 | public static function buildTagWeightsFromLegacyParameters( $tagNames = null, $tagWeights = null ) { |
112 | Assert::parameterType( |
113 | [ |
114 | 'string', |
115 | 'array', |
116 | 'null' |
117 | ], |
118 | $tagNames, |
119 | '$tagNames' |
120 | ); |
121 | if ( $tagNames === null ) { |
122 | $tagNames = [ self::WEIGHTED_TAG_DEFAULT_NAME ]; |
123 | if ( $tagWeights !== null ) { |
124 | Assert::parameterType( 'integer', $tagWeights, '$tagWeights' ); |
125 | $tagWeights = [ self::WEIGHTED_TAG_DEFAULT_NAME => $tagWeights ]; |
126 | } else { |
127 | $tagWeights = [ self::WEIGHTED_TAG_DEFAULT_NAME => null ]; |
128 | } |
129 | } elseif ( is_string( $tagNames ) ) { |
130 | if ( $tagWeights === null ) { |
131 | $tagWeights = [ $tagNames => null ]; |
132 | } |
133 | |
134 | $tagNames = [ $tagNames ]; |
135 | } elseif ( is_array( $tagNames ) ) { |
136 | Assert::parameterElementType( 'string', $tagNames, '$tagNames' ); |
137 | if ( $tagWeights === null ) { |
138 | $tagWeights = array_fill_keys( $tagNames, null ); |
139 | } |
140 | } |
141 | |
142 | if ( $tagWeights ) { |
143 | foreach ( $tagWeights as $tagName => $tagWeight ) { |
144 | Assert::precondition( |
145 | strpos( $tagName, MultiListWeightedTag::WEIGHT_DELIMITER ) === false, |
146 | "invalid tag name $tagName: must not contain " . MultiListWeightedTag::WEIGHT_DELIMITER |
147 | ); |
148 | Assert::precondition( |
149 | in_array( $tagName, $tagNames, true ), |
150 | "tag name $tagName used in \$tagWeights but not found in \$tagNames" |
151 | ); |
152 | |
153 | if ( $tagWeight !== null ) { |
154 | Assert::precondition( |
155 | is_int( $tagWeight ), |
156 | "weights must be integers but $tagWeight is " . get_debug_type( $tagWeight ) |
157 | ); |
158 | Assert::precondition( |
159 | $tagWeight >= 1 && $tagWeight <= 1000, |
160 | "weights must be between 1 and 1000 (found: $tagWeight)" |
161 | ); |
162 | } |
163 | } |
164 | } |
165 | |
166 | return $tagWeights; |
167 | } |
168 | |
169 | } |