Passed
Push — master ( 994f84...0e30e3 )
by MusikAnimal
06:32
created

TopEdits::__construct()   A

Complexity

Conditions 6
Paths 24

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 24
nop 8
dl 0
loc 24
ccs 13
cts 13
cp 1
crap 6
rs 9.2222
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * This file contains only the TopEdits class.
4
 */
5
6
declare(strict_types = 1);
7
8
namespace AppBundle\Model;
9
10
use AppBundle\Helper\AutomatedEditsHelper;
11
12
/**
13
 * TopEdits returns the top-edited pages by a user.
14
 */
15
class TopEdits extends Model
16
{
17
    /** @var string[]|Edit[] Top edits, either to a page or across namespaces. */
18
    protected $topEdits = [];
19
20
    /** @var int Number of bytes added across all top edits. */
21
    protected $totalAdded = 0;
22
23
    /** @var int Number of bytes removed across all top edits. */
24
    protected $totalRemoved = 0;
25
26
    /** @var int Number of top edits marked as minor. */
27
    protected $totalMinor = 0;
28
29
    /** @var int Number of automated top edits. */
30
    protected $totalAutomated = 0;
31
32
    /** @var int Number of reverted top edits. */
33
    protected $totalReverted = 0;
34
35
    private const DEFAULT_LIMIT_SINGLE_NAMESPACE = 1000;
36
    private const DEFAULT_LIMIT_ALL_NAMESPACES = 20;
37
38
    /**
39
     * TopEdits constructor.
40
     * @param Project $project
41
     * @param User $user
42
     * @param Page $page
43
     * @param string|int $namespace Namespace ID or 'all'.
44
     * @param int|false $start Start date in a format accepted by strtotime()
45
     * @param int|false $end End date in a format accepted by strtotime()
46
     * @param int $limit Number of rows to fetch. This defaults to DEFAULT_LIMIT_SINGLE_NAMESPACE if $this->namespace
47
     *   is a single namespace (int), and DEFAULT_LIMIT_ALL_NAMESPACES if $this->namespace is 'all'.
48
     * @param int $offset Number of pages past the initial dataset. Used for pagination.
49
     */
50 4
    public function __construct(
51
        Project $project,
52
        User $user,
53
        ?Page $page = null,
54
        $namespace = 0,
55
        $start = false,
56
        $end = false,
57
        $limit = null,
58
        $offset = 0
59
    ) {
60 4
        $this->project = $project;
61 4
        $this->user = $user;
62 4
        $this->page = $page;
63 4
        $this->namespace = 'all' === $namespace ? 'all' : (int)$namespace;
64 4
        $this->start = false === $start ? '' : date('Y-m-d', $start);
65 4
        $this->end = false === $end ? '' : date('Y-m-d', $end);
66 4
        $this->offset = (int)$offset;
67
68 4
        if (null !== $limit) {
69 3
            $this->limit = (int)$limit;
70
        } else {
71 2
            $this->limit = 'all' === $this->namespace
72 1
                ? self::DEFAULT_LIMIT_ALL_NAMESPACES
73 2
                : self::DEFAULT_LIMIT_SINGLE_NAMESPACE;
74
        }
75 4
    }
76
77
    /**
78
     * Get total number of bytes added.
79
     * @return int
80
     */
81 1
    public function getTotalAdded(): int
82
    {
83 1
        return $this->totalAdded;
84
    }
85
86
    /**
87
     * Get total number of bytes removed.
88
     * @return int
89
     */
90 1
    public function getTotalRemoved(): int
91
    {
92 1
        return $this->totalRemoved;
93
    }
94
95
    /**
96
     * Get total number of edits marked as minor.
97
     * @return int
98
     */
99 1
    public function getTotalMinor(): int
100
    {
101 1
        return $this->totalMinor;
102
    }
103
104
    /**
105
     * Get total number of automated edits.
106
     * @return int
107
     */
108 1
    public function getTotalAutomated(): int
109
    {
110 1
        return $this->totalAutomated;
111
    }
112
113
    /**
114
     * Get total number of edits that were reverted.
115
     * @return int
116
     */
117 1
    public function getTotalReverted(): int
118
    {
119 1
        return $this->totalReverted;
120
    }
121
122
    /**
123
     * Get the top edits data.
124
     * @return array|Edit[]
125
     */
126 3
    public function getTopEdits(): array
127
    {
128 3
        return $this->topEdits;
129
    }
130
131
    /**
132
     * Get the total number of top edits.
133
     * @return int
134
     */
135 1
    public function getNumTopEdits(): int
136
    {
137 1
        return count($this->topEdits);
138
    }
139
140
    /**
141
     * Get the average time between edits (in days).
142
     * @return float
143
     */
144 1
    public function getAtbe(): float
145
    {
146 1
        $firstDateTime = $this->topEdits[0]->getTimestamp();
147 1
        $lastDateTime = end($this->topEdits)->getTimestamp();
148 1
        $secs = $firstDateTime->getTimestamp() - $lastDateTime->getTimestamp();
149 1
        $days = $secs / (60 * 60 * 24);
150 1
        return $days / count($this->topEdits);
151
    }
152
153
    /**
154
     * Set the Page on the TopEdits instance.
155
     * @param Page $page
156
     */
157 1
    public function setPage(Page $page): void
158
    {
159 1
        $this->page = $page;
160 1
    }
161
162
    /**
163
     * Fetch and store all the data we need to show the TopEdits view.
164
     * This is the public method that should be called before using
165
     * the getter methods.
166
     * @param bool $format Whether to format the results, including stats for
167
     *     number of reverts, etc. This is set to false for the API endpoint.
168
     */
169 3
    public function prepareData(bool $format = true): void
170
    {
171 3
        if (isset($this->page)) {
172 1
            $this->topEdits = $this->getTopEditsPage($format);
173
        } else {
174 2
            $this->topEdits = $this->getTopEditsNamespace($format);
175
        }
176 3
    }
177
178
    /**
179
     * Get the top edits by a user in the given namespace, or 'all' namespaces.
180
     * @param bool $format Whether to format the results, including stats for
181
     *     number of reverts, etc. This is set to false for the API endpoint.
182
     * @return string[] Results keyed by namespace.
183
     */
184 2
    private function getTopEditsNamespace(bool $format): array
185
    {
186 2
        if ('all' === $this->namespace) {
187 1
            $pages = $this->getRepository()->getTopEditsAllNamespaces(
0 ignored issues
show
Bug introduced by
The method getTopEditsAllNamespaces() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\TopEditsRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

187
            $pages = $this->getRepository()->/** @scrutinizer ignore-call */ getTopEditsAllNamespaces(
Loading history...
188 1
                $this->project,
189 1
                $this->user,
190 1
                $this->start,
191 1
                $this->end,
192 1
                $this->limit
193
            );
194
        } else {
195 1
            $pages = $this->getRepository()->getTopEditsNamespace(
0 ignored issues
show
Bug introduced by
The method getTopEditsNamespace() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\TopEditsRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

195
            $pages = $this->getRepository()->/** @scrutinizer ignore-call */ getTopEditsNamespace(
Loading history...
196 1
                $this->project,
197 1
                $this->user,
198 1
                $this->namespace,
199 1
                $this->start,
200 1
                $this->end,
201 1
                $this->limit,
202 1
                $this->offset * $this->limit
203
            );
204
        }
205
206 2
        if ($format) {
207 2
            return $this->formatTopPagesNamespace($pages);
208
        } else {
209
            return $pages;
210
        }
211
    }
212
213
    /**
214
     * Get the total number of pages edited in the namespace.
215
     * @return int|null
216
     */
217
    public function getNumPagesNamespace(): ?int
218
    {
219
        if ('all' === $this->namespace) {
220
            return null;
221
        }
222
223
        return (int)$this->getRepository()->countEditsNamespace(
0 ignored issues
show
Bug introduced by
The method countEditsNamespace() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\TopEditsRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

223
        return (int)$this->getRepository()->/** @scrutinizer ignore-call */ countEditsNamespace(
Loading history...
224
            $this->project,
225
            $this->user,
226
            $this->namespace,
227
            $this->start,
228
            $this->end
229
        );
230
    }
231
232
    /**
233
     * Get the top edits to the given page.
234
     * @param bool $format Whether to format the results, including stats for
235
     *     number of reverts, etc. This is set to false for the API endpoint.
236
     * @return Edit[]
237
     */
238 1
    private function getTopEditsPage(bool $format = true): array
239
    {
240 1
        $revs = $this->getRepository()->getTopEditsPage(
0 ignored issues
show
Bug introduced by
The method getTopEditsPage() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\TopEditsRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

240
        $revs = $this->getRepository()->/** @scrutinizer ignore-call */ getTopEditsPage(
Loading history...
241 1
            $this->page,
242 1
            $this->user,
243 1
            $this->start,
244 1
            $this->end
245
        );
246
247 1
        if ($format) {
248 1
            return $this->formatTopEditsPage($revs);
249
        } else {
250
            return $revs;
251
        }
252
    }
253
254
    /**
255
     * Format the results for top edits to a single page. This method also computes
256
     * totals for added/removed text, automated and reverted edits.
257
     * @param array[] $revs As returned by TopEditsRepository::getTopEditsPage.
258
     * @return Edit[]
259
     */
260 1
    private function formatTopEditsPage(array $revs): array
261
    {
262 1
        $edits = [];
263
264
        /** @var AutomatedEditsHelper $aeh */
265 1
        $aeh = $this->getRepository()
266 1
            ->getContainer()
0 ignored issues
show
Bug introduced by
The method getContainer() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\TopEditsRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

266
            ->/** @scrutinizer ignore-call */ getContainer()
Loading history...
267 1
            ->get('app.automated_edits_helper');
268
269 1
        foreach ($revs as $revision) {
270
            // Check if the edit was reverted based on the edit summary of the following edit.
271
            // If so, update $revision so that when an Edit is instantiated, it will have the 'reverted' option set.
272 1
            if ($aeh->isRevert($revision['parent_comment'], $this->project)) {
273 1
                $revision['reverted'] = 1;
274
            }
275
276 1
            $edit = $this->getEditAndIncrementCounts($revision);
277
278 1
            $edits[] = $edit;
279
        }
280
281 1
        return $edits;
282
    }
283
284
    /**
285
     * Create an Edit instance for the given revision, and increment running totals.
286
     * This is used by self::formatTopEditsPage().
287
     * @param string[] $revision Revision row as retrieved from the database.
288
     * @return Edit
289
     */
290 1
    private function getEditAndIncrementCounts(array $revision): Edit
291
    {
292 1
        $edit = new Edit($this->page, $revision);
293
294 1
        if ($edit->isAutomated($this->getRepository()->getContainer())) {
295 1
            $this->totalAutomated++;
296
        }
297
298 1
        if ($edit->isMinor()) {
299 1
            $this->totalMinor++;
300
        }
301
302 1
        if ($edit->isReverted()) {
303 1
            $this->totalReverted++;
304
        } else {
305
            // Length changes don't count if they were reverted.
306 1
            if ($revision['length_change'] > 0) {
307 1
                $this->totalAdded += $revision['length_change'];
308
            } else {
309 1
                $this->totalRemoved += $revision['length_change'];
310
            }
311
        }
312
313 1
        return $edit;
314
    }
315
316
    /**
317
     * Format the results to be keyed by namespace.
318
     * @param array $pages As returned by TopEditsRepository::getTopEditsNamespace()
319
     *   or TopEditsRepository::getTopEditsAllNamespaces().
320
     * @return array Same results but keyed by namespace.
321
     */
322 2
    private function formatTopPagesNamespace(array $pages): array
323
    {
324
        /** @var string[] $topEditedPages The top edited pages, keyed by namespace ID. */
325 2
        $topEditedPages = [];
326
327 2
        foreach ($pages as $page) {
328 2
            $nsId = (int)$page['page_namespace'];
329
330
            // FIXME: needs refactoring, done in PagesController::getPagepileResult() and AppExtension::titleWithNs().
331 2
            if (0 === $nsId) {
332 1
                $page['page_title_ns'] = $page['page_title'];
333
            } else {
334 2
                $page['page_title_ns'] = (
335 2
                    $this->project->getNamespaces()[$page['page_namespace']] ?? ''
336 2
                ).':'.$page['page_title'];
337
            }
338
339 2
            if (isset($topEditedPages[$nsId])) {
340 2
                $topEditedPages[$nsId][] = $page;
341
            } else {
342 2
                $topEditedPages[$nsId] = [$page];
343
            }
344
        }
345
346 2
        return $topEditedPages;
347
    }
348
}
349