Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.67% covered (success)
91.67%
11 / 12
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
StatsCache
91.67% covered (success)
91.67%
11 / 12
80.00% covered (warning)
80.00%
4 / 5
8.04
0.00% covered (danger)
0.00%
0 / 1
 get
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 set
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getAllMetrics
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clear
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 cacheKey
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7declare( strict_types=1 );
8
9namespace Wikimedia\Stats;
10
11use TypeError;
12use Wikimedia\Stats\Metrics\MetricInterface;
13use Wikimedia\Stats\Metrics\NullMetric;
14
15/**
16 * Singleton cache for Metric instances.
17 *
18 * For reuse and collision avoidance.  Serves as the data source for Metric Emitters.
19 *
20 * @author Cole White
21 * @since 1.41
22 */
23class StatsCache {
24
25    public const DELIMITER = '.';
26
27    /** @var MetricInterface[] */
28    private array $cache = [];
29
30    /**
31     * Get a metric object from cache or null.
32     *
33     * @param string $component
34     * @param string $name
35     * @param string $expectedClass
36     * @return MetricInterface|null
37     * @throws TypeError If cached value is for a different metric type.
38     */
39    public function get( string $component, string $name, string $expectedClass ) {
40        $key = self::cacheKey( $component, $name );
41        $metric = $this->cache[$key] ?? null;
42        if ( $metric !== null && get_class( $metric ) !== $expectedClass ) {
43            throw new TypeError( "Encountered metric name collision: $key defined as "
44                . get_class( $metric ) . " but $expectedClass was requested" );
45        }
46        return $metric;
47    }
48
49    /**
50     * Add a metric object to the cache.
51     *
52     * @param string $component
53     * @param string $name
54     * @param MetricInterface|NullMetric $metric
55     */
56    public function set( string $component, string $name, $metric ): void {
57        $this->cache[self::cacheKey( $component, $name )] = $metric;
58    }
59
60    /**
61     * Get all metrics from cache.
62     *
63     * @return MetricInterface[]
64     */
65    public function getAllMetrics(): array {
66        return $this->cache;
67    }
68
69    /**
70     * Clears the cache.
71     */
72    public function clear(): void {
73        $this->cache = [];
74    }
75
76    /**
77     * Get the metric formatted name.
78     *
79     * Takes the provided name and constructs a more specific name by combining
80     * component and name.
81     */
82    public static function cacheKey( string $component, string $name ): string {
83        // mitigate collision of empty-component metric with a component metric
84        if ( $component !== '' ) {
85            return implode( self::DELIMITER, [ $component, $name ] );
86        }
87        return $name;
88    }
89}