wiki.techinc.nl/includes/libs/Stats/StatsUtils.php
Cole White b3d20f8873 Stats: simplify metrics configuration, enforce builder pattern
BREAKING INTERFACE CHANGE

 * getMetric() (and all subtypes) now only require a $name.
 * Labels are added dynamically and order enforced by BaseMetric labelKeys.
 * Metric component is now defined in the service instance.
 * Remove InvalidLabelsException.
 * Make addLabelKey() private.
 * Removed unused functions.
 * NullMetric to return $this - support builder pattern.
 * Update tests.

Bug: T240685
Change-Id: Id8cb62ec74907cb54dc39b6a228a230eed0c957d
2023-03-02 23:09:41 +00:00

167 lines
4.6 KiB
PHP

<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
* @file
*/
declare( strict_types=1 );
namespace Wikimedia\Stats;
use InvalidArgumentException;
use Wikimedia\Stats\Exceptions\InvalidConfigurationException;
/**
*
* StatsUtils Implementation
*
* Functionality common to all metric types.
*
* @author Cole White
* @since 1.38
*/
class StatsUtils {
/** @var string */
public const RE_VALID_NAME_AND_LABEL_NAME = "/^[a-zA-Z_][a-zA-Z0-9_]*$/";
/** @var float */
public const DEFAULT_SAMPLE_RATE = 1.0;
/**
* Validates the new sample rate. Throws InvalidArgumentException if provided an invalid rate.
*
* @param float $newSampleRate
* @throws InvalidArgumentException
*/
public static function validateNewSampleRate( float $newSampleRate ): void {
if ( $newSampleRate < 0.0 || $newSampleRate > 1.0 ) {
throw new InvalidArgumentException( "Sample rate can only be between 0.0 and 1.0. Got: " . $newSampleRate );
}
}
/**
* Returns a subset of samples based on configured sample rate.
*
* @param float $sampleRate
* @param array $samples
* @return array
*/
public static function getFilteredSamples( float $sampleRate, array $samples ): array {
if ( $sampleRate === 1.0 ) {
return $samples;
}
$output = [];
$randMax = mt_getrandmax();
foreach ( $samples as $sample ) {
if ( mt_rand() / $randMax < $sampleRate ) {
$output[] = $sample;
}
}
return $output;
}
/**
* Determines if provided string is a valid name.
*
* @param string $name
* @return void
* @throws InvalidArgumentException
* @throws InvalidConfigurationException
*/
public static function validateMetricName( string $name ) {
if ( $name === "" ) {
throw new InvalidArgumentException( "Stats: Metric name cannot be empty." );
}
if ( !preg_match( self::RE_VALID_NAME_AND_LABEL_NAME, $name ) ) {
throw new InvalidConfigurationException( "Invalid metric name: '" . $name . "'" );
}
}
/**
* Determines if provided string is a valid label key.
*
* @param string $key
* @return void
* @throws InvalidArgumentException
* @throws InvalidConfigurationException
*/
public static function validateLabelKey( string $key ) {
if ( $key === "" ) {
throw new InvalidArgumentException( "Stats: Label key cannot be empty." );
}
if ( !preg_match( self::RE_VALID_NAME_AND_LABEL_NAME, $key ) ) {
throw new InvalidConfigurationException( "Invalid label key: '" . $key . "'" );
}
}
public static function validateLabelValue( string $value ) {
if ( $value === "" ) {
throw new InvalidArgumentException( "Stats: Label value cannot be empty." );
}
}
/**
* Merges two associative arrays of labels. Prioritizes leftmost labels.
*
* @param array $leftLabels
* @param array $rightLabels
* @return array
*/
public static function mergeLabels( array $leftLabels, array $rightLabels ): array {
$output = [];
foreach ( $leftLabels as $key => $value ) {
$output[$key] = $value;
}
foreach ( $rightLabels as $key => $value ) {
if ( array_key_exists( $key, $output ) ) {
continue;
}
$output[$key] = $value;
}
return $output;
}
/**
* Normalize an array of strings.
*
* @param string[] $entities
* @return string[]
*/
public static function normalizeArray( array $entities ): array {
$normalizedEntities = [];
foreach ( $entities as $entity ) {
$normalizedEntities[] = self::normalizeString( $entity );
}
return $normalizedEntities;
}
/**
* Normalize strings to a metrics-compatible format.
*
* Replace any other non-alphanumeric characters with underscores.
* Eliminate repeated underscores.
* Trim leading or trailing underscores.
*
* @param string $entity
* @return string
*/
public static function normalizeString( string $entity ): string {
$entity = preg_replace( "/[^a-z0-9]/i", "_", $entity );
$entity = preg_replace( "/_+/", "_", $entity );
return trim( $entity, "_" );
}
}