Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
1 / 1
ClientHintsLookupResults
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
4 / 4
12
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getGroupedClientHintsDataForReferenceIds
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
5
 getClientHintsDataForReferenceId
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
5
 getRawData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace MediaWiki\CheckUser\ClientHints;
4
5use InvalidArgumentException;
6use MediaWiki\CheckUser\Services\UserAgentClientHintsManager;
7
8/**
9 * Value object for the result of UserAgentClientHintsLookup::getClientHintsByReferenceIds
10 * which contains reference IDs to ClientHintsData objects.
11 *
12 * This is used, instead of a two-dimensional list, to enforce that
13 * the map IDs are valid. This class stores the data as a two-dimensional
14 * list.
15 */
16class ClientHintsLookupResults {
17    /** @var int[][] */
18    private array $referenceIdsToClientHintsDataIndex;
19
20    /** @var ClientHintsData[] */
21    private array $clientHintsDataObjects;
22
23    /**
24     * @param int[][] $referenceIdsToClientHintsDataIndex A map of reference type and reference ID values
25     *   to integer keys in $clientHintsDataObjects array.
26     * @param ClientHintsData[] $clientHintsDataObjects An array of ClientHintsData objects where the keys are
27     *   integers that are the second-dimension value in the first parameter.
28     */
29    public function __construct( array $referenceIdsToClientHintsDataIndex, array $clientHintsDataObjects ) {
30        $this->referenceIdsToClientHintsDataIndex = $referenceIdsToClientHintsDataIndex;
31        $this->clientHintsDataObjects = $clientHintsDataObjects;
32    }
33
34    /**
35     * Get unique ClientHintsData objects and the number of times they were used for a
36     * array of reference IDs.
37     *
38     * @param ClientHintsReferenceIds|null $referenceIds The reference IDs to get the objects for.
39     *   Null for all reference IDs.
40     * @return array[] An array of two arrays. The first array has keys corresponding to a key in the second array and
41     *   the value is the number of rows the ClientHintsData object is associated with. The second array has values
42     *   of unique ClientHintsData objects.
43     */
44    public function getGroupedClientHintsDataForReferenceIds( ?ClientHintsReferenceIds $referenceIds ): array {
45        // Store the keys to the ClientHintsData objects in $this->clientHintsDataObjects
46        // as values in the array $clientHintsDataObjectKeys that are associated with
47        // the reference IDs provided in the first parameter to this method.
48        $clientHintsDataObjectKeys = [];
49        foreach ( $this->referenceIdsToClientHintsDataIndex as $referenceType => $referenceIdsForReferenceType ) {
50            if ( $referenceIds ) {
51                // If filtering for specific reference IDs, filter so that only the reference IDs that
52                // were requested are used for the grouping and counting operations later in this method.
53                $clientHintsDataObjectKeys = array_merge( $clientHintsDataObjectKeys, array_values( array_intersect_key(
54                    $referenceIdsForReferenceType,
55                    array_flip( $referenceIds->getReferenceIds( $referenceType ) ),
56                ) ) );
57            } else {
58                // If the $referenceIds parameter was null, then this means apply no filtering to the reference
59                // IDs that are used.
60                $clientHintsDataObjectKeys = array_merge(
61                    $clientHintsDataObjectKeys,
62                    array_values( $referenceIdsForReferenceType )
63                );
64            }
65        }
66
67        // Count the number of occurrences for each integer in the $clientHintsDataObjectKeys
68        // array. This counts how many occurrences of a given unique ClientHintsData object
69        // are present for the reference IDs in $referenceIds.
70        $groupedClientHintsIds = array_count_values( $clientHintsDataObjectKeys );
71
72        // Create an array of ClientHintsData objects where the keys
73        // are the key for this object in $this->clientHintsDataObjects.
74        // This array will contain the subset of $this->clientHintsDataObjects
75        // where each key is present in $groupedClientHintsIds.
76        $clientHintsDataObjects = [];
77        foreach ( array_keys( $groupedClientHintsIds ) as $clientHintsId ) {
78            if ( array_key_exists( $clientHintsId, $this->clientHintsDataObjects ) ) {
79                // Add the ClientHintsData object into the return array.
80                $clientHintsDataObjects[$clientHintsId] = $this->clientHintsDataObjects[$clientHintsId];
81            } else {
82                // If, for some reason, there is no ClientHintsData object in
83                // $this->clientHintsDataObjects, then just silently ignore
84                // and remove the group from the return list.
85                unset( $groupedClientHintsIds[$clientHintsId] );
86            }
87        }
88        return [ $groupedClientHintsIds, $clientHintsDataObjects ];
89    }
90
91    /**
92     * Get the ClientHintsData object for a given reference ID and reference type.
93     *
94     * @param int $referenceId The reference ID
95     * @param int $referenceType The reference type (one of the UserAgentClientHintsManager::IDENTIFIER_* integer
96     *   constants).
97     * @return ClientHintsData|null
98     */
99    public function getClientHintsDataForReferenceId( int $referenceId, int $referenceType ): ?ClientHintsData {
100        // Validate that the $referenceType given is a valid reference type. If not, then
101        // return an exception to indicate a problem in the code.
102        if ( !array_key_exists( $referenceType, UserAgentClientHintsManager::IDENTIFIER_TO_TABLE_NAME_MAP ) ) {
103            throw new InvalidArgumentException( "Unrecognised reference type '$referenceType'" );
104        }
105        // Check that the reference IDs to ClientHintsData object index ID map has an
106        // entry for this reference ID and reference type. Otherwise, return null.
107        // If it does then also check that the index in the ClientHintsData objects array
108        // exists. Otherwise, return null.
109        if (
110            !array_key_exists( $referenceType, $this->referenceIdsToClientHintsDataIndex ) ||
111            !array_key_exists( $referenceId, $this->referenceIdsToClientHintsDataIndex[$referenceType] ) ||
112            !array_key_exists(
113                $this->referenceIdsToClientHintsDataIndex[$referenceType][$referenceId],
114                $this->clientHintsDataObjects
115            )
116        ) {
117            return null;
118        }
119        // The reference ID matches a ClientHintsData object, so return it.
120        return $this->clientHintsDataObjects[$this->referenceIdsToClientHintsDataIndex[$referenceType][$referenceId]];
121    }
122
123    /**
124     * Allows UserAgentClientHintsFormatter to get the raw data
125     * for UserAgentClientHintsFormatter::batchFormatClientHintsData.
126     *
127     * @internal For use by UserAgentClientHintsFormatter only.
128     * @return array[]
129     */
130    public function getRawData(): array {
131        return [ $this->referenceIdsToClientHintsDataIndex, $this->clientHintsDataObjects ];
132    }
133}