Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
10 / 10
CRAP
100.00% covered (success)
100.00%
1 / 1
RunnerData
100.00% covered (success)
100.00%
43 / 43
100.00% covered (success)
100.00%
10 / 10
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 record
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
2
 getMatchesMap
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getAllFilters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMatchedFilters
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getProfilingData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTotalRuntime
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTotalConditions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toArray
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 fromArray
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace MediaWiki\Extension\AbuseFilter;
4
5use LogicException;
6use MediaWiki\Extension\AbuseFilter\Parser\RuleCheckerStatus;
7
8/**
9 * Mutable value class storing and accumulating information about filter matches and runtime
10 */
11class RunnerData {
12
13    /**
14     * @var array<string,RuleCheckerStatus>
15     */
16    private $matchedFilters;
17
18    /**
19     * @var array[]
20     * @phan-var array<string,array{time:float,conds:int,result:bool}>
21     */
22    private $profilingData;
23
24    /** @var float */
25    private $totalRuntime;
26
27    /** @var int */
28    private $totalConditions;
29
30    /**
31     * @param RuleCheckerStatus[] $matchedFilters
32     * @param array[] $profilingData
33     * @param float $totalRuntime
34     * @param int $totalConditions
35     */
36    public function __construct(
37        array $matchedFilters = [],
38        array $profilingData = [],
39        float $totalRuntime = 0.0,
40        int $totalConditions = 0
41    ) {
42        $this->matchedFilters = $matchedFilters;
43        $this->profilingData = $profilingData;
44        $this->totalRuntime = $totalRuntime;
45        $this->totalConditions = $totalConditions;
46    }
47
48    /**
49     * Record (memorize) data from a filter run
50     *
51     * @param int $filterID
52     * @param bool $global
53     * @param RuleCheckerStatus $status
54     * @param float $timeTaken
55     */
56    public function record( int $filterID, bool $global, RuleCheckerStatus $status, float $timeTaken ): void {
57        $key = GlobalNameUtils::buildGlobalName( $filterID, $global );
58        if ( array_key_exists( $key, $this->matchedFilters ) ) {
59            throw new LogicException( "Filter '$key' has already been recorded" );
60        }
61        $this->matchedFilters[$key] = $status;
62        $this->profilingData[$key] = [
63            'time' => $timeTaken,
64            'conds' => $status->getCondsUsed(),
65            'result' => $status->getResult()
66        ];
67        $this->totalRuntime += $timeTaken;
68        $this->totalConditions += $status->getCondsUsed();
69    }
70
71    /**
72     * Get information about filter matches in backwards compatible format
73     * @return bool[]
74     * @phan-return array<string,bool>
75     */
76    public function getMatchesMap(): array {
77        return array_map(
78            static function ( $status ) {
79                return $status->getResult();
80            },
81            $this->matchedFilters
82        );
83    }
84
85    /**
86     * @return string[]
87     */
88    public function getAllFilters(): array {
89        return array_keys( $this->matchedFilters );
90    }
91
92    /**
93     * @return string[]
94     */
95    public function getMatchedFilters(): array {
96        return array_keys( array_filter( $this->getMatchesMap() ) );
97    }
98
99    /**
100     * @return array[]
101     */
102    public function getProfilingData(): array {
103        return $this->profilingData;
104    }
105
106    /**
107     * @return float
108     */
109    public function getTotalRuntime(): float {
110        return $this->totalRuntime;
111    }
112
113    /**
114     * @return int
115     */
116    public function getTotalConditions(): int {
117        return $this->totalConditions;
118    }
119
120    /**
121     * Serialize data for edit stash
122     * @return array
123     * @phan-return array{matches:array<string,array>,runtime:float,condCount:int,profiling:array}
124     */
125    public function toArray(): array {
126        return [
127            'matches' => array_map(
128                static function ( $status ) {
129                    return $status->toArray();
130                },
131                $this->matchedFilters
132            ),
133            'profiling' => $this->profilingData,
134            'condCount' => $this->totalConditions,
135            'runtime' => $this->totalRuntime,
136        ];
137    }
138
139    /**
140     * Deserialize data from edit stash
141     * @param array $value
142     * @return self
143     */
144    public static function fromArray( array $value ): self {
145        return new self(
146            array_map( [ RuleCheckerStatus::class, 'fromArray' ], $value['matches'] ),
147            $value['profiling'],
148            $value['runtime'],
149            $value['condCount']
150        );
151    }
152
153}