Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
62.86% |
22 / 35 |
|
37.50% |
3 / 8 |
CRAP | |
0.00% |
0 / 1 |
StatsUtils | |
62.86% |
22 / 35 |
|
37.50% |
3 / 8 |
46.80 | |
0.00% |
0 / 1 |
validateNewSampleRate | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
3 | |||
getFilteredSamples | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
4.03 | |||
validateMetricName | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
3.14 | |||
validateLabelKey | |
50.00% |
2 / 4 |
|
0.00% |
0 / 1 |
4.12 | |||
validateLabelValue | |
50.00% |
1 / 2 |
|
0.00% |
0 / 1 |
2.50 | |||
mergeLabels | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
normalizeArray | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
normalizeString | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * This program is free software; you can redistribute it and/or modify |
4 | * it under the terms of the GNU General Public License as published by |
5 | * the Free Software Foundation; either version 2 of the License, or |
6 | * (at your option) any later version. |
7 | * |
8 | * This program is distributed in the hope that it will be useful, |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | * GNU General Public License for more details. |
12 | * |
13 | * You should have received a copy of the GNU General Public License along |
14 | * with this program; if not, write to the Free Software Foundation, Inc., |
15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 | * http://www.gnu.org/copyleft/gpl.html |
17 | * @file |
18 | */ |
19 | |
20 | declare( strict_types=1 ); |
21 | |
22 | namespace Wikimedia\Stats; |
23 | |
24 | use InvalidArgumentException; |
25 | use Wikimedia\Stats\Exceptions\InvalidConfigurationException; |
26 | |
27 | /** |
28 | * |
29 | * StatsUtils Implementation |
30 | * |
31 | * Functionality common to all metric types. |
32 | * |
33 | * @author Cole White |
34 | * @since 1.38 |
35 | */ |
36 | class StatsUtils { |
37 | |
38 | /** @var string */ |
39 | public const RE_VALID_NAME_AND_LABEL_NAME = "/^[a-zA-Z_][a-zA-Z0-9_]*$/"; |
40 | |
41 | /** @var float */ |
42 | public const DEFAULT_SAMPLE_RATE = 1.0; |
43 | |
44 | /** |
45 | * Validates the new sample rate. Throws InvalidArgumentException if provided an invalid rate. |
46 | * |
47 | * @param float $newSampleRate |
48 | * @throws InvalidArgumentException |
49 | */ |
50 | public static function validateNewSampleRate( float $newSampleRate ): void { |
51 | if ( $newSampleRate < 0.0 || $newSampleRate > 1.0 ) { |
52 | throw new InvalidArgumentException( "Sample rate can only be between 0.0 and 1.0. Got: " . $newSampleRate ); |
53 | } |
54 | } |
55 | |
56 | /** |
57 | * Returns a subset of samples based on configured sample rate. |
58 | * |
59 | * @param float $sampleRate |
60 | * @param array $samples |
61 | * @return array |
62 | */ |
63 | public static function getFilteredSamples( float $sampleRate, array $samples ): array { |
64 | if ( $sampleRate === 1.0 ) { |
65 | return $samples; |
66 | } |
67 | $output = []; |
68 | $randMax = mt_getrandmax(); |
69 | foreach ( $samples as $sample ) { |
70 | if ( mt_rand() / $randMax < $sampleRate ) { |
71 | $output[] = $sample; |
72 | } |
73 | } |
74 | return $output; |
75 | } |
76 | |
77 | /** |
78 | * Determines if provided string is a valid name. |
79 | * |
80 | * @param string $name |
81 | * @return void |
82 | * @throws InvalidArgumentException |
83 | * @throws InvalidConfigurationException |
84 | */ |
85 | public static function validateMetricName( string $name ) { |
86 | if ( $name === "" ) { |
87 | throw new InvalidArgumentException( "Stats: Metric name cannot be empty." ); |
88 | } |
89 | if ( !preg_match( self::RE_VALID_NAME_AND_LABEL_NAME, $name ) ) { |
90 | throw new InvalidConfigurationException( "Invalid metric name: '" . $name . "'" ); |
91 | } |
92 | } |
93 | |
94 | /** |
95 | * Determines if provided string is a valid label key. |
96 | * |
97 | * @param string $key |
98 | * @return void |
99 | * @throws InvalidArgumentException |
100 | * @throws InvalidConfigurationException |
101 | */ |
102 | public static function validateLabelKey( string $key ) { |
103 | if ( $key === "" ) { |
104 | throw new InvalidArgumentException( "Stats: Label key cannot be empty." ); |
105 | } |
106 | if ( !preg_match( self::RE_VALID_NAME_AND_LABEL_NAME, $key ) ) { |
107 | throw new InvalidConfigurationException( "Invalid label key: '" . $key . "'" ); |
108 | } |
109 | } |
110 | |
111 | public static function validateLabelValue( string $value ) { |
112 | if ( $value === "" ) { |
113 | throw new InvalidArgumentException( "Stats: Label value cannot be empty." ); |
114 | } |
115 | } |
116 | |
117 | /** |
118 | * Merges two associative arrays of labels. Prioritizes leftmost labels. |
119 | * |
120 | * @param array $leftLabels |
121 | * @param array $rightLabels |
122 | * @return array |
123 | */ |
124 | public static function mergeLabels( array $leftLabels, array $rightLabels ): array { |
125 | $output = []; |
126 | foreach ( $leftLabels as $key => $value ) { |
127 | $output[$key] = $value; |
128 | } |
129 | foreach ( $rightLabels as $key => $value ) { |
130 | if ( array_key_exists( $key, $output ) ) { |
131 | continue; |
132 | } |
133 | $output[$key] = $value; |
134 | } |
135 | return $output; |
136 | } |
137 | |
138 | /** |
139 | * Normalize an array of strings. |
140 | * |
141 | * @param string[] $entities |
142 | * @return string[] |
143 | */ |
144 | public static function normalizeArray( array $entities ): array { |
145 | $normalizedEntities = []; |
146 | foreach ( $entities as $entity ) { |
147 | $normalizedEntities[] = self::normalizeString( $entity ); |
148 | } |
149 | return $normalizedEntities; |
150 | } |
151 | |
152 | /** |
153 | * Normalize strings to a metrics-compatible format. |
154 | * |
155 | * Replace any other non-alphanumeric characters with underscores. |
156 | * Eliminate repeated underscores. |
157 | * Trim leading or trailing underscores. |
158 | * |
159 | * @param string $entity |
160 | * @return string |
161 | */ |
162 | public static function normalizeString( string $entity ): string { |
163 | $entity = preg_replace( "/[^a-z0-9]/i", "_", $entity ); |
164 | $entity = preg_replace( "/_+/", "_", $entity ); |
165 | return trim( $entity, "_" ); |
166 | } |
167 | } |