Test Failed
Pull Request — main (#406)
by MusikAnimal
12:27 queued 07:43
created

GlobalContribs::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 6
dl 0
loc 14
ccs 6
cts 6
cp 1
crap 2
rs 10
1
<?php
2
declare(strict_types = 1);
3
4
namespace AppBundle\Model;
5
6
use AppBundle\Repository\GlobalContribsRepository;
7
8
/**
9
 * A GlobalContribs provides a list of a user's edits to all projects.
10
 */
11
class GlobalContribs extends Model
12
{
13
    /** @var int Number of results per page. */
14
    public const PAGE_SIZE = 50;
15
16
    /** @var int[] Keys are project DB names. */
17
    protected $globalEditCounts;
18
19
    /** @var array Most recent revisions across all projects. */
20
    protected $globalEdits;
21
22
    /**
23
     * GlobalContribs constructor.
24
     * @param User $user
25
     * @param string|int|null $namespace Namespace ID or 'all'.
26
     * @param false|int $start As Unix timestamp.
27
     * @param false|int $end As Unix timestamp.
28
     * @param false|int $offset As Unix timestamp.
29
     * @param false|int $limit Number of results to return.
30 2
     */
31
    public function __construct(
32 2
        User $user,
33 2
        $namespace = 'all',
34 2
        $start = false,
35 2
        $end = false,
36 2
        $offset = false,
37 2
        $limit = self::PAGE_SIZE
38
    ) {
39
        $this->user = $user;
40
        $this->namespace = '' == $namespace ? 0 : $namespace;
41
        $this->start = $start;
42
        $this->end = $end;
43
        $this->offset = $offset;
44
        $this->limit = $limit;
0 ignored issues
show
Documentation Bug introduced by
It seems like $limit can also be of type false. However, the property $limit is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
45
    }
46
47
    /**
48
     * Get the total edit counts for the top n projects of this user.
49
     * @param int $numProjects
50
     * @return mixed[] Each element has 'total' and 'project' keys.
51
     */
52
    public function globalEditCountsTopN(int $numProjects = 10): array
53
    {
54 1
        // Get counts.
55
        $editCounts = $this->globalEditCounts(true);
56
        // Truncate, and return.
57 1
        return array_slice($editCounts, 0, $numProjects);
58
    }
59 1
60
    /**
61
     * Get the total number of edits excluding the top n.
62
     * @param int $numProjects
63
     * @return int
64
     */
65
    public function globalEditCountWithoutTopN(int $numProjects = 10): int
66
    {
67 1
        $editCounts = $this->globalEditCounts(true);
68
        $bottomM = array_slice($editCounts, $numProjects);
69 1
        $total = 0;
70 1
        foreach ($bottomM as $editCount) {
71 1
            $total += $editCount['total'];
72 1
        }
73 1
        return $total;
74
    }
75 1
76
    /**
77
     * Get the grand total of all edits on all projects.
78
     * @return int
79
     */
80
    public function globalEditCount(): int
81
    {
82 1
        $total = 0;
83
        foreach ($this->globalEditCounts() as $editCount) {
84 1
            $total += $editCount['total'];
85 1
        }
86 1
        return $total;
87
    }
88 1
89
    /**
90
     * Get the total revision counts for all projects for this user.
91
     * @param bool $sorted Whether to sort the list by total, or not.
92
     * @return mixed[] Each element has 'total' and 'project' keys.
93
     */
94
    public function globalEditCounts(bool $sorted = false): array
95
    {
96 1
        if (empty($this->globalEditCounts)) {
97
            $this->globalEditCounts = $this->getRepository()
98 1
                ->globalEditCounts($this->user);
0 ignored issues
show
Bug introduced by
The method globalEditCounts() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\GlobalContribsRepository. ( Ignorable by Annotation )

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

98
                ->/** @scrutinizer ignore-call */ globalEditCounts($this->user);
Loading history...
99 1
        }
100 1
101
        if ($sorted) {
102
            // Sort.
103 1
            uasort($this->globalEditCounts, function ($a, $b) {
104
                return $b['total'] - $a['total'];
105
            });
106 1
        }
107 1
108
        return $this->globalEditCounts;
109
    }
110 1
111
    public function numProjectsWithEdits(): int
112
    {
113
        return count($this->getRepository()->getProjectsWithEdits($this->user));
0 ignored issues
show
Bug introduced by
The method getProjectsWithEdits() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\GlobalContribsRepository. ( Ignorable by Annotation )

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

113
        return count($this->getRepository()->/** @scrutinizer ignore-call */ getProjectsWithEdits($this->user));
Loading history...
114
    }
115
116
    /**
117
     * Get the most recent revisions across all projects.
118
     * @return Edit[]
119
     */
120
    public function globalEdits(): array
121
    {
122 1
        if (is_array($this->globalEdits)) {
0 ignored issues
show
introduced by
The condition is_array($this->globalEdits) is always true.
Loading history...
123
            return $this->globalEdits;
124 1
        }
125
126
        // Get projects with edits.
127
        $projects = $this->getRepository()->getProjectsWithEdits($this->user);
128
        if (0 === count($projects)) {
129 1
            return [];
130 1
        }
131
132
        // Get all revisions for those projects.
133
        /** @var GlobalContribsRepository $globalContribsRepo */
134
        $globalContribsRepo = $this->getRepository();
135
        $globalRevisionsData = $globalContribsRepo->getRevisions(
136 1
            array_keys($projects),
137 1
            $this->user,
138 1
            $this->namespace,
139 1
            $this->start,
140 1
            $this->end,
141 1
            $this->limit + 1,
142 1
            $this->offset
143 1
        );
144 1
        $globalEdits = [];
145
146 1
        foreach ($globalRevisionsData as $revision) {
147
            /** @var Project $project */
148 1
            $project = $projects[$revision['dbName']];
149
150 1
            // Can happen if the project is given from CentralAuth API but the database is not being replicated.
151
            if (null === $project) {
152
                continue;
153 1
            }
154
155
            $edit = $this->getEditFromRevision($project, $revision);
156
            $globalEdits[$edit->getTimestamp()->getTimestamp().'-'.$edit->getId()] = $edit;
157 1
        }
158 1
159
        // Sort and prune, before adding more.
160
        krsort($globalEdits);
161
        $this->globalEdits = array_slice($globalEdits, 0, $this->limit);
162 1
163 1
        return $this->globalEdits;
164
    }
165 1
166
    private function getEditFromRevision(Project $project, array $revision): Edit
167
    {
168 1
        $nsName = '';
169
        if ($revision['page_namespace']) {
170 1
            $nsName = $project->getNamespaces()[$revision['page_namespace']];
171 1
        }
172 1
173
        $page = $project->getRepository()
174
            ->getPage($project, ltrim($nsName.':'.$revision['page_title'], ':'));
0 ignored issues
show
Bug introduced by
The method getPage() does not exist on AppBundle\Repository\Repository. It seems like you code against a sub-type of AppBundle\Repository\Repository such as AppBundle\Repository\ProjectRepository. ( Ignorable by Annotation )

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

174
            ->/** @scrutinizer ignore-call */ getPage($project, ltrim($nsName.':'.$revision['page_title'], ':'));
Loading history...
175 1
        return new Edit($page, $revision);
176 1
    }
177
}
178