Test Setup Failed
Pull Request — main (#426)
by MusikAnimal
17:10 queued 11:44
created

Pages::__construct()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 2
nop 8
dl 0
loc 18
rs 10
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 Pages class.
4
 */
5
6
declare(strict_types = 1);
7
8
namespace App\Model;
9
10
use DateTime;
11
12
/**
13
 * A Pages provides statistics about the pages created by a given User.
14
 */
15
class Pages extends Model
16
{
17
    private const RESULTS_LIMIT_SINGLE_NAMESPACE = 1000;
18
    private const RESULTS_LIMIT_ALL_NAMESPACES = 50;
19
20
    /** @var string One of 'noredirects', 'onlyredirects' or 'all' for both. */
21
    protected $redirects;
22
23
    /** @var string One of 'live', 'deleted' or 'all' for both. */
24
    protected $deleted;
25
26
    /** @var mixed[] The list of pages including various statistics, keyed by namespace. */
27
    protected $pages;
28
29
    /** @var mixed[] Number of redirects/pages that were created/deleted, broken down by namespace. */
30
    protected $countsByNamespace;
31
32
    /**
33
     * Pages constructor.
34
     * @param Project $project
35
     * @param User $user
36
     * @param string|int $namespace Namespace ID or 'all'.
37
     * @param string $redirects One of 'noredirects', 'onlyredirects' or 'all' for both.
38
     * @param string $deleted One of 'live', 'deleted' or 'all' for both.
39
     * @param int|false $start Start date as Unix timestamp.
40
     * @param int|false $end End date as Unix timestamp.
41
     * @param int|false $offset Unix timestamp. Used for pagination.
42
     */
43
    public function __construct(
44
        Project $project,
45
        User $user,
46
        $namespace = 0,
47
        $redirects = 'noredirects',
48
        $deleted = 'all',
49
        $start = false,
50
        $end = false,
51
        $offset = false
52
    ) {
53
        $this->project = $project;
54
        $this->user = $user;
55
        $this->namespace = 'all' === $namespace ? 'all' : (int)$namespace;
56
        $this->start = $start;
57
        $this->end = $end;
58
        $this->redirects = $redirects ?: 'noredirects';
59
        $this->deleted = $deleted ?: 'all';
60
        $this->offset = $offset;
61
    }
62
63
    /**
64
     * The redirects option associated with this Pages instance.
65
     * @return string
66
     */
67
    public function getRedirects(): string
68
    {
69
        return $this->redirects;
70
    }
71
72
    /**
73
     * The deleted pages option associated with this Page instance.
74
     * @return string
75
     */
76
    public function getDeleted(): string
77
    {
78
        return $this->deleted;
79
    }
80
81
    /**
82
     * Fetch and prepare the pages created by the user.
83
     * @param bool $all Whether to get *all* results. This should only be used for
84
     *     export options. HTTP and JSON should paginate.
85
     * @return array
86
     * @codeCoverageIgnore
87
     */
88
    public function prepareData(bool $all = false): array
89
    {
90
        $this->pages = [];
91
92
        foreach ($this->getNamespaces() as $ns) {
93
            $data = $this->fetchPagesCreated($ns, $all);
94
            $this->pages[$ns] = count($data) > 0
95
                ? $this->formatPages($data)[$ns]
96
                : [];
97
        }
98
99
        return $this->pages;
100
    }
101
102
    /**
103
     * The public function to get the list of all pages created by the user,
104
     * up to self::resultsPerPage(), across all namespaces.
105
     * @param bool $all Whether to get *all* results. This should only be used for
106
     *     export options. HTTP and JSON should paginate.
107
     * @return array
108
     */
109
    public function getResults(bool $all = false): array
110
    {
111
        if (null === $this->pages) {
112
            $this->prepareData($all);
113
        }
114
        return $this->pages;
115
    }
116
117
    /**
118
     * Return a ISO 8601 timestamp of the last result. This is used for pagination purposes.
119
     * @return string|null
120
     */
121
    public function getLastTimestamp(): ?string
122
    {
123
        if ($this->isMultiNamespace()) {
124
            // No pagination in multi-namespace view.
125
            return null;
126
        }
127
128
        $numResults = count($this->getResults()[$this->getNamespace()]);
129
        $timestamp = new DateTime($this->getResults()[$this->getNamespace()][$numResults - 1]['rev_timestamp']);
130
        return $timestamp->format('Y-m-d\TH:i:s');
131
    }
132
133
    /**
134
     * Get the total number of pages the user has created.
135
     * @return int
136
     */
137
    public function getNumPages(): int
138
    {
139
        $total = 0;
140
        foreach (array_values($this->getCounts()) as $values) {
141
            $total += $values['count'];
142
        }
143
        return $total;
144
    }
145
146
    /**
147
     * Get the total number of pages we're showing data for.
148
     * @return int
149
     */
150
    public function getNumResults(): int
151
    {
152
        $total = 0;
153
        foreach (array_values($this->getResults()) as $pages) {
154
            $total += count($pages);
155
        }
156
        return $total;
157
    }
158
159
    /**
160
     * Get the total number of pages that are currently deleted.
161
     * @return int
162
     */
163
    public function getNumDeleted(): int
164
    {
165
        $total = 0;
166
        foreach (array_values($this->getCounts()) as $values) {
167
            $total += $values['deleted'];
168
        }
169
        return $total;
170
    }
171
172
    /**
173
     * Get the total number of pages that are currently redirects.
174
     * @return int
175
     */
176
    public function getNumRedirects(): int
177
    {
178
        $total = 0;
179
        foreach (array_values($this->getCounts()) as $values) {
180
            $total += $values['redirects'];
181
        }
182
        return $total;
183
    }
184
185
    /**
186
     * Get the namespaces in which this user has created pages.
187
     * @return int[] The IDs.
188
     */
189
    public function getNamespaces(): array
190
    {
191
        return array_keys($this->getCounts());
192
    }
193
194
    /**
195
     * Number of namespaces being reported.
196
     * @return int
197
     */
198
    public function getNumNamespaces(): int
199
    {
200
        return count(array_keys($this->getCounts()));
201
    }
202
203
    /**
204
     * Are there more than one namespace in the results?
205
     * @return bool
206
     */
207
    public function isMultiNamespace(): bool
208
    {
209
        return $this->getNumNamespaces() > 1 || ('all' === $this->getNamespace() && 1 === $this->getNumNamespaces());
210
    }
211
212
    /**
213
     * Get the sum of all page sizes, across all specified namespaces.
214
     * @return int
215
     */
216
    public function getTotalPageSize(): int
217
    {
218
        return array_sum(array_column($this->getCounts(), 'total_length'));
219
    }
220
221
    /**
222
     * Get average size across all pages.
223
     * @return float
224
     */
225
    public function averagePageSize(): float
226
    {
227
        return $this->getTotalPageSize() / $this->getNumPages();
228
    }
229
230
    /**
231
     * Number of redirects/pages that were created/deleted, broken down by namespace.
232
     * @return array Namespace IDs as the keys, with values 'count', 'deleted' and 'redirects'.
233
     */
234
    public function getCounts(): array
235
    {
236
        if (null !== $this->countsByNamespace) {
237
            return $this->countsByNamespace;
238
        }
239
240
        $counts = [];
241
242
        foreach ($this->countPagesCreated() as $row) {
243
            $ns = (int)$row['namespace'];
244
            $count = (int)$row['count'];
245
            $totalLength = (int)$row['total_length'];
246
            $counts[$ns] = [
247
                'count' => $count,
248
                'total_length' => $totalLength,
249
                'avg_length' => $count > 0 ? $totalLength / $count : 0,
250
            ];
251
            if ('live' !== $this->deleted) {
252
                $counts[$ns]['deleted'] = (int)$row['deleted'];
253
            }
254
            if ('noredirects' !== $this->redirects) {
255
                $counts[$ns]['redirects'] = (int)$row['redirects'];
256
            }
257
        }
258
259
        $this->countsByNamespace = $counts;
260
        return $this->countsByNamespace;
261
    }
262
263
    /**
264
     * Get the number of pages the user created by assessment.
265
     * @return array Keys are the assessment class, values are the counts.
266
     */
267
    public function getAssessmentCounts(): array
268
    {
269
        if ($this->getNumPages() > $this->resultsPerPage()) {
270
            $counts = $this->getRepository()->getAssessmentCounts(
0 ignored issues
show
Bug introduced by
The method getAssessmentCounts() does not exist on App\Repository\Repository. It seems like you code against a sub-type of App\Repository\Repository such as App\Repository\PagesRepository. ( Ignorable by Annotation )

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

270
            $counts = $this->getRepository()->/** @scrutinizer ignore-call */ getAssessmentCounts(
Loading history...
271
                $this->project,
272
                $this->user,
273
                $this->namespace,
274
                $this->redirects
275
            );
276
        } else {
277
            $counts = [];
278
            foreach ($this->pages as $nsPages) {
279
                foreach ($nsPages as $page) {
280
                    if (!isset($counts[$page['pa_class']])) {
281
                        $counts[$page['pa_class']] = 1;
282
                    } else {
283
                        $counts[$page['pa_class']]++;
284
                    }
285
                }
286
            }
287
        }
288
289
        arsort($counts);
290
291
        return $counts;
292
    }
293
294
    /**
295
     * Number of results to show, depending on the namespace.
296
     * @param bool $all Whether to get *all* results. This should only be used for
297
     *     export options. HTTP and JSON should paginate.
298
     * @return int|false
299
     */
300
    public function resultsPerPage(bool $all = false)
301
    {
302
        if (true === $all) {
303
            return false;
304
        }
305
        if ('all' === $this->namespace) {
306
            return self::RESULTS_LIMIT_ALL_NAMESPACES;
307
        }
308
        return self::RESULTS_LIMIT_SINGLE_NAMESPACE;
309
    }
310
311
    /**
312
     * Run the query to get pages created by the user with options.
313
     * This is ran independently for each namespace if $this->namespace is 'all'.
314
     * @param int $namespace Namespace ID.
315
     * @param bool $all Whether to get *all* results. This should only be used for
316
     *     export options. HTTP and JSON should paginate.
317
     * @return array
318
     */
319
    private function fetchPagesCreated(int $namespace, bool $all = false): array
320
    {
321
        return $this->getRepository()->getPagesCreated(
0 ignored issues
show
Bug introduced by
The method getPagesCreated() does not exist on App\Repository\Repository. It seems like you code against a sub-type of App\Repository\Repository such as App\Repository\PagesRepository. ( Ignorable by Annotation )

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

321
        return $this->getRepository()->/** @scrutinizer ignore-call */ getPagesCreated(
Loading history...
322
            $this->project,
323
            $this->user,
324
            $namespace,
325
            $this->redirects,
326
            $this->deleted,
327
            $this->start,
328
            $this->end,
329
            $this->resultsPerPage($all),
330
            $this->offset
331
        );
332
    }
333
334
    /**
335
     * Run the query to get the number of pages created by the user with given options.
336
     * @return array
337
     */
338
    private function countPagesCreated(): array
339
    {
340
        return $this->getRepository()->countPagesCreated(
0 ignored issues
show
Bug introduced by
The method countPagesCreated() does not exist on App\Repository\Repository. It seems like you code against a sub-type of App\Repository\Repository such as App\Repository\PagesRepository. ( Ignorable by Annotation )

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

340
        return $this->getRepository()->/** @scrutinizer ignore-call */ countPagesCreated(
Loading history...
341
            $this->project,
342
            $this->user,
343
            $this->namespace,
344
            $this->redirects,
345
            $this->deleted,
346
            $this->start,
347
            $this->end
348
        );
349
    }
350
351
    /**
352
     * Format the data, adding humanized timestamps, page titles, assessment badges,
353
     * and sorting by namespace and then timestamp.
354
     * @param array $pages As returned by self::fetchPagesCreated()
355
     * @return array
356
     */
357
    private function formatPages(array $pages): array
358
    {
359
        $results = [];
360
361
        foreach ($pages as $row) {
362
            $datetime = DateTime::createFromFormat('YmdHis', $row['rev_timestamp']);
363
            $datetimeHuman = $datetime->format('Y-m-d H:i');
364
365
            $pageData = array_merge($row, [
366
                'raw_time' => $row['rev_timestamp'],
367
                'human_time' => $datetimeHuman,
368
                'page_title' => str_replace('_', ' ', $row['page_title']),
369
            ]);
370
371
            if ($this->project->hasPageAssessments()) {
372
                $pageData['badge'] = $this->project
373
                    ->getPageAssessments()
374
                    ->getBadgeURL($pageData['pa_class']);
375
                $pageData['badge_file'] = $this->project
376
                    ->getPageAssessments()
377
                    ->getBadgeURL($pageData['pa_class'], true);
378
            }
379
380
            $results[$row['namespace']][] = $pageData;
381
        }
382
383
        return $results;
384
    }
385
}
386