This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
1 | <?php |
||
2 | |||
3 | declare(strict_types = 1); |
||
4 | |||
5 | namespace App\Model; |
||
6 | |||
7 | use App\Repository\EditSummaryRepository; |
||
8 | use DateTime; |
||
9 | |||
10 | /** |
||
11 | * An EditSummary provides statistics about a user's edit summary usage over time. |
||
12 | */ |
||
13 | class EditSummary extends Model |
||
14 | { |
||
15 | /** @var int Number of edits from present to consider as 'recent'. */ |
||
16 | protected int $numEditsRecent; |
||
17 | |||
18 | /** |
||
19 | * Counts of summaries, raw edits, and per-month breakdown. |
||
20 | * Keys are underscored because this also is served in the API. |
||
21 | * @var array |
||
22 | */ |
||
23 | protected array $data = [ |
||
24 | 'recent_edits_minor' => 0, |
||
25 | 'recent_edits_major' => 0, |
||
26 | 'total_edits_minor' => 0, |
||
27 | 'total_edits_major' => 0, |
||
28 | 'total_edits' => 0, |
||
29 | 'recent_summaries_minor' => 0, |
||
30 | 'recent_summaries_major' => 0, |
||
31 | 'total_summaries_minor' => 0, |
||
32 | 'total_summaries_major' => 0, |
||
33 | 'total_summaries' => 0, |
||
34 | 'month_counts' => [], |
||
35 | ]; |
||
36 | |||
37 | /** |
||
38 | * EditSummary constructor. |
||
39 | * |
||
40 | * @param EditSummaryRepository $repository |
||
41 | * @param Project $project The project we're working with. |
||
42 | * @param User $user The user to process. |
||
43 | * @param int|string $namespace Namespace ID or 'all' for all namespaces. |
||
44 | * @param int|false $start Start date as Unix timestamp. |
||
45 | * @param int|false $end End date as Unix timestamp. |
||
46 | * @param int $numEditsRecent Number of edits from present to consider as 'recent'. |
||
47 | */ |
||
48 | public function __construct( |
||
49 | EditSummaryRepository $repository, |
||
50 | Project $project, |
||
51 | User $user, |
||
52 | $namespace, |
||
53 | $start = false, |
||
54 | $end = false, |
||
55 | int $numEditsRecent = 150 |
||
56 | ) { |
||
57 | $this->repository = $repository; |
||
58 | $this->project = $project; |
||
59 | $this->user = $user; |
||
60 | $this->namespace = $namespace; |
||
61 | $this->start = $start; |
||
62 | $this->end = $end; |
||
63 | $this->numEditsRecent = $numEditsRecent; |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * Get the total number of edits. |
||
68 | * @return int |
||
69 | */ |
||
70 | public function getTotalEdits(): int |
||
71 | { |
||
72 | return $this->data['total_edits']; |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * Get the total number of minor edits. |
||
77 | * @return int |
||
78 | */ |
||
79 | public function getTotalEditsMinor(): int |
||
80 | { |
||
81 | return $this->data['total_edits_minor']; |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * Get the total number of major (non-minor) edits. |
||
86 | * @return int |
||
87 | */ |
||
88 | public function getTotalEditsMajor(): int |
||
89 | { |
||
90 | return $this->data['total_edits_major']; |
||
91 | } |
||
92 | |||
93 | /** |
||
94 | * Get the total number of recent minor edits. |
||
95 | * @return int |
||
96 | */ |
||
97 | public function getRecentEditsMinor(): int |
||
98 | { |
||
99 | return $this->data['recent_edits_minor']; |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * Get the total number of recent major (non-minor) edits. |
||
104 | * @return int |
||
105 | */ |
||
106 | public function getRecentEditsMajor(): int |
||
107 | { |
||
108 | return $this->data['recent_edits_major']; |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * Get the total number of edits with summaries. |
||
113 | * @return int |
||
114 | */ |
||
115 | public function getTotalSummaries(): int |
||
116 | { |
||
117 | return $this->data['total_summaries']; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Get the total number of minor edits with summaries. |
||
122 | * @return int |
||
123 | */ |
||
124 | public function getTotalSummariesMinor(): int |
||
125 | { |
||
126 | return $this->data['total_summaries_minor']; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Get the total number of major (non-minor) edits with summaries. |
||
131 | * @return int |
||
132 | */ |
||
133 | public function getTotalSummariesMajor(): int |
||
134 | { |
||
135 | return $this->data['total_summaries_major']; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * Get the total number of recent minor edits with with summaries. |
||
140 | * @return int |
||
141 | */ |
||
142 | public function getRecentSummariesMinor(): int |
||
143 | { |
||
144 | return $this->data['recent_summaries_minor']; |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * Get the total number of recent major (non-minor) edits with with summaries. |
||
149 | * @return int |
||
150 | */ |
||
151 | public function getRecentSummariesMajor(): int |
||
152 | { |
||
153 | return $this->data['recent_summaries_major']; |
||
154 | } |
||
155 | |||
156 | /** |
||
157 | * Get the month counts. |
||
158 | * @return array Months as 'YYYY-MM' as the keys, |
||
159 | * with key 'total' and 'summaries' as the values. |
||
160 | */ |
||
161 | public function getMonthCounts(): array |
||
162 | { |
||
163 | return $this->data['month_counts']; |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * Get the whole blob of counts. |
||
168 | * @return array Counts of summaries, raw edits, and per-month breakdown. |
||
169 | * @codeCoverageIgnore |
||
170 | */ |
||
171 | public function getData(): array |
||
172 | { |
||
173 | return $this->data; |
||
174 | } |
||
175 | |||
176 | /** |
||
177 | * Fetch the data from the database, process, and put in memory. |
||
178 | * @codeCoverageIgnore |
||
179 | */ |
||
180 | public function prepareData(): array |
||
181 | { |
||
182 | // Do our database work in the Repository, passing in reference |
||
183 | // to $this->processRow so we can do post-processing here. |
||
184 | $ret = $this->repository->prepareData( |
||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
185 | [$this, 'processRow'], |
||
186 | $this->project, |
||
187 | $this->user, |
||
188 | $this->namespace, |
||
189 | $this->start, |
||
190 | $this->end |
||
191 | ); |
||
192 | |||
193 | // We want to keep all the default zero values if there are no contributions. |
||
194 | if (count($ret) > 0) { |
||
195 | $this->data = $ret; |
||
196 | } |
||
197 | |||
198 | return $ret; |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Process a single row from the database, updating class properties with counts. |
||
203 | * @param string[] $row As retrieved from the revision table. |
||
204 | * @return string[] |
||
205 | */ |
||
206 | public function processRow(array $row): array |
||
207 | { |
||
208 | // Extract the date out of the date field |
||
209 | $timestamp = DateTime::createFromFormat('YmdHis', $row['rev_timestamp']); |
||
210 | |||
211 | $monthKey = $timestamp->format('Y-m'); |
||
212 | |||
213 | // Grand total for number of edits |
||
214 | $this->data['total_edits']++; |
||
215 | |||
216 | // Update total edit count for this month. |
||
217 | $this->updateMonthCounts($monthKey, 'total'); |
||
218 | |||
219 | // Total edit summaries |
||
220 | if ($this->hasSummary($row)) { |
||
221 | $this->data['total_summaries']++; |
||
222 | |||
223 | // Update summary count for this month. |
||
224 | $this->updateMonthCounts($monthKey, 'summaries'); |
||
225 | } |
||
226 | |||
227 | if ($this->isMinor($row)) { |
||
228 | $this->updateMajorMinorCounts($row, 'minor'); |
||
229 | } else { |
||
230 | $this->updateMajorMinorCounts($row, 'major'); |
||
231 | } |
||
232 | |||
233 | return $this->data; |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * Given the row in `revision`, update minor counts. |
||
238 | * @param string[] $row As retrieved from the revision table. |
||
239 | * @param string $type Either 'minor' or 'major'. |
||
240 | * @codeCoverageIgnore |
||
241 | */ |
||
242 | private function updateMajorMinorCounts(array $row, string $type): void |
||
243 | { |
||
244 | $this->data['total_edits_'.$type]++; |
||
245 | |||
246 | $hasSummary = $this->hasSummary($row); |
||
247 | $isRecent = $this->data['recent_edits_'.$type] < $this->numEditsRecent; |
||
248 | |||
249 | if ($hasSummary) { |
||
250 | $this->data['total_summaries_'.$type]++; |
||
251 | } |
||
252 | |||
253 | // Update recent edits counts. |
||
254 | if ($isRecent) { |
||
255 | $this->data['recent_edits_'.$type]++; |
||
256 | |||
257 | if ($hasSummary) { |
||
258 | $this->data['recent_summaries_'.$type]++; |
||
259 | } |
||
260 | } |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Was the given row in `revision` marked as a minor edit? |
||
265 | * @param string[] $row As retrieved from the revision table. |
||
266 | * @return boolean |
||
267 | */ |
||
268 | private function isMinor(array $row): bool |
||
269 | { |
||
270 | return 1 === (int)$row['rev_minor_edit']; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Taking into account automated edit summaries, does the given |
||
275 | * row in `revision` have a user-supplied edit summary? |
||
276 | * @param string[] $row As retrieved from the revision table. |
||
277 | * @return boolean |
||
278 | */ |
||
279 | private function hasSummary(array $row): bool |
||
280 | { |
||
281 | $summary = preg_replace("/^\/\* (.*?) \*\/\s*/", '', $row['comment'] ?: ''); |
||
282 | return '' !== $summary; |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * Check and see if the month is set for given $monthKey and $type. |
||
287 | * If it is, increment it, otherwise set it to 1. |
||
288 | * @param string $monthKey In the form 'YYYY-MM'. |
||
289 | * @param string $type Either 'total' or 'summaries'. |
||
290 | * @codeCoverageIgnore |
||
291 | */ |
||
292 | private function updateMonthCounts(string $monthKey, string $type): void |
||
293 | { |
||
294 | if (isset($this->data['month_counts'][$monthKey][$type])) { |
||
295 | $this->data['month_counts'][$monthKey][$type]++; |
||
296 | } else { |
||
297 | $this->data['month_counts'][$monthKey][$type] = 1; |
||
298 | } |
||
299 | } |
||
300 | } |
||
301 |