Completed
Push — master ( 61d54e...4a5166 )
by Sam
03:19
created

EditCounter::getPageCounts()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
3
namespace Xtools;
4
5
use \DateTime;
6
7
/**
8
 * An EditCounter provides statistics about a user's edits on a project.
9
 */
10
class EditCounter extends Model
11
{
12
    
13
    /** @var Project */
14
    protected $project;
15
    
16
    /** @var User */
17
    protected $user;
18
19
    /** @var int[] */
20
    protected $revisionCounts;
21
22
    /** @var string[] */
23
    protected $revisionDates;
24
25
    /** @var int[] */
26
    protected $pageCounts;
27
    
28
    /** @var int[] */
29
    protected $logCounts;
30
31
    public function __construct(Project $project, User $user)
32
    {
33
        $this->project = $project;
34
        $this->user = $user;
35
    }
36
37
    /**
38
     * Get revision count data.
39
     * @return int[]
40
     */
41
    protected function getRevisionCounts()
42
    {
43
        if (! is_array($this->revisionCounts)) {
44
            $this->revisionCounts = $this->getRepository()
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getRevisionCounts() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
45
                ->getRevisionCounts($this->project, $this->user);
46
        }
47
        return $this->revisionCounts;
48
    }
49
50
    /**
51
     * Get revision dates.
52
     * @return int[]
53
     */
54
    protected function getRevisionDates()
55
    {
56
        if (! is_array($this->revisionDates)) {
57
            $this->revisionDates = $this->getRepository()
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getRevisionDates() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
58
                ->getRevisionDates($this->project, $this->user);
59
        }
60
        return $this->revisionDates;
61
    }
62
63
    /**
64
     * Get page count data.
65
     * @return int[]
66
     */
67
    protected function getPageCounts()
68
    {
69
        if (! is_array($this->pageCounts)) {
70
            $this->pageCounts = $this->getRepository()
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getPageCounts() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
71
                ->getPageCounts($this->project, $this->user);
72
        }
73
        return $this->pageCounts;
74
    }
75
76
    /**
77
     * Get revision dates.
78
     * @return int[]
79
     */
80
    protected function getLogCounts()
81
    {
82
        if (! is_array($this->logCounts)) {
83
            $this->logCounts = $this->getRepository()
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getLogCounts() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
84
                ->getLogCounts($this->project, $this->user);
85
        }
86
        return $this->logCounts;
87
    }
88
89
    public function countLiveRevisions()
90
    {
91
        $revCounts = $this->getRevisionCounts();
92
        return isset($revCounts['live']) ? $revCounts['live'] : 0;
93
    }
94
95
    /**
96
     * Get the total number of revisions that have been deleted.
97
     * @return int
98
     */
99
    public function countDeletedRevisions()
100
    {
101
        $revCounts = $this->getRevisionCounts();
102
        return isset($revCounts['deleted']) ? $revCounts['deleted'] : 0;
103
    }
104
105
    /**
106
     * Get the total edit count (live + deleted).
107
     * @return int
108
     */
109
    public function countAllRevisions()
110
    {
111
        return $this->countLiveRevisions() + $this->countDeletedRevisions();
112
    }
113
114
    /**
115
     * Get the total number of revisions with comments.
116
     * @return int
117
     */
118
    public function countRevisionsWithComments()
119
    {
120
        $revCounts = $this->getRevisionCounts();
121
        return isset($revCounts['with_comments']) ? $revCounts['with_comments'] : 0;
122
    }
123
124
    /**
125
     * Get the total number of revisions without comments.
126
     * @return int
127
     */
128
    public function countRevisionsWithoutComments()
129
    {
130
        return $this->countAllRevisions() - $this->countRevisionsWithComments();
131
    }
132
133
    /**
134
     * Get the total number of revisions marked as 'minor' by the user.
135
     * @return int
136
     */
137
    public function countMinorRevisions()
138
    {
139
        $revCounts = $this->getRevisionCounts();
140
        return isset($revCounts['minor']) ? $revCounts['minor'] : 0;
141
    }
142
143
    /**
144
     * Get the total number of revisions under 20 bytes.
145
     */
146
    public function countSmallRevisions()
147
    {
148
        $revCounts = $this->getRevisionCounts();
149
        return isset($revCounts['small']) ? $revCounts['small'] : 0;
150
    }
151
152
    /**
153
     * Get the total number of revisions over 1000 bytes.
154
     */
155
    public function countLargeRevisions()
156
    {
157
        $revCounts = $this->getRevisionCounts();
158
        return isset($revCounts['large']) ? $revCounts['large'] : 0;
159
    }
160
161
    /**
162
     * Get the average revision size for the user.
163
     * @return float Size in bytes.
164
     */
165
    public function averageRevisionSize()
166
    {
167
        $revisionCounts = $this->getRevisionCounts();
168
        return round($revisionCounts['average_size'], 3);
169
    }
170
171
    /**
172
     * Get the total number of non-deleted pages edited by the user.
173
     * @return int
174
     */
175
    public function countLivePagesEdited()
176
    {
177
        $pageCounts = $this->getPageCounts();
178
        return isset($pageCounts['edited-live']) ? $pageCounts['edited-live'] : 0;
179
    }
180
181
    /**
182
     * Get the total number of deleted pages ever edited by the user.
183
     * @return int
184
     */
185
    public function countDeletedPagesEdited()
186
    {
187
        $pageCounts = $this->getPageCounts();
188
        return isset($pageCounts['edited-deleted']) ? $pageCounts['edited-deleted'] : 0;
189
    }
190
191
    /**
192
     * Get the total number of pages ever edited by this user (both live and deleted).
193
     * @return int
194
     */
195
    public function countAllPagesEdited()
196
    {
197
        return $this->countLivePagesEdited() + $this->countDeletedPagesEdited();
198
    }
199
200
    /**
201
     * Get the total number of semi-automated edits.
202
     * @return int
203
     */
204
    public function countAutomatedEdits()
205
    {
206
    }
207
208
    /**
209
     * Get the total number of pages (both still live and those that have been deleted) created
210
     * by the user.
211
     * @return int
212
     */
213
    public function countPagesCreated()
214
    {
215
        return $this->countCreatedPagesLive() + $this->countPagesCreatedDeleted();
216
    }
217
218
    /**
219
     * Get the total number of pages created by the user, that have not been deleted.
220
     * @return int
221
     */
222
    public function countCreatedPagesLive()
223
    {
224
        $pageCounts = $this->getPageCounts();
225
        return isset($pageCounts['created-live']) ? (int)$pageCounts['created-live'] : 0;
226
    }
227
228
    /**
229
     * Get the total number of pages created by the user, that have since been deleted.
230
     * @return int
231
     */
232
    public function countPagesCreatedDeleted()
233
    {
234
        $pageCounts = $this->getPageCounts();
235
        return isset($pageCounts['created-deleted']) ? (int)$pageCounts['created-deleted'] : 0;
236
    }
237
238
    /**
239
     * Get the total number of pages that have been deleted by the user.
240
     * @return int
241
     */
242
    public function countPagesDeleted()
243
    {
244
        $logCounts = $this->getLogCounts();
245
        return isset($logCounts['delete-delete']) ? (int)$logCounts['delete-delete'] : 0;
246
    }
247
248
    /**
249
     * Get the total number of pages moved by the user.
250
     * @return int
251
     */
252
    public function countPagesMoved()
253
    {
254
        $logCounts = $this->getLogCounts();
255
        return isset($logCounts['move-move']) ? (int)$logCounts['move-move'] : 0;
256
    }
257
258
    /**
259
     * Get the average number of edits per page (including deleted revisions and pages).
260
     * @return float
261
     */
262
    public function averageRevisionsPerPage()
263
    {
264
        if ($this->countAllPagesEdited() == 0) {
265
            return 0;
266
        }
267
        return round($this->countAllRevisions() / $this->countAllPagesEdited(), 3);
268
    }
269
270
    /**
271
     * Average number of edits made per day.
272
     * @return float
273
     */
274
    public function averageRevisionsPerDay()
275
    {
276
        if ($this->getDays() == 0) {
277
            return 0;
278
        }
279
        return round($this->countAllRevisions() / $this->getDays(), 3);
280
    }
281
    
282
    /**
283
     * Get the total number of edits made by the user with semi-automating tools.
284
     * @TODO
285
     */
286
    public function countAutomatedRevisions()
287
    {
288
        return 0;
289
    }
290
    
291
    /**
292
     * Get the count of (non-deleted) edits made in the given timeframe to now.
293
     * @param string $time One of 'day', 'week', 'month', or 'year'.
294
     * @return int The total number of live edits.
295
     */
296
    public function countRevisionsInLast($time)
297
    {
298
        $revCounts = $this->getRevisionCounts();
299
        return isset($revCounts[$time]) ? $revCounts[$time] : 0;
300
    }
301
302
    /**
303
     * Get the date and time of the user's first edit.
304
     */
305
    public function datetimeFirstRevision()
306
    {
307
        $first = $this->getRevisionDates()['first'];
308
        return new DateTime($first);
309
    }
310
311
    /**
312
     * Get the date and time of the user's first edit.
313
     * @return DateTime
314
     */
315
    public function datetimeLastRevision()
316
    {
317
        $last = $this->getRevisionDates()['last'];
318
        return new DateTime($last);
319
    }
320
321
    /**
322
     * Get the number of days between the first and last edits.
323
     * If there's only one edit, this is counted as one day.
324
     * @return int
325
     */
326
    public function getDays()
327
    {
328
        $days = $this->datetimeLastRevision()->diff($this->datetimeFirstRevision())->days;
329
        return $days > 0 ? $days : 1;
330
    }
331
332
    public function countFilesUploaded()
333
    {
334
        $logCounts = $this->getLogCounts();
335
        return $logCounts['upload-upload'] ?: 0;
336
    }
337
338
    public function countFilesUploadedCommons()
339
    {
340
        $logCounts = $this->getLogCounts();
341
        return $logCounts['files_uploaded_commons'] ?: 0;
342
    }
343
344
    /**
345
     * Get the total number of revisions the user has sent thanks for.
346
     * @return int
347
     */
348
    public function thanks()
349
    {
350
        $logCounts = $this->getLogCounts();
351
        return $logCounts['thanks-thank'] ?: 0;
352
    }
353
354
    /**
355
     * Get the total number of approvals
356
     * @return int
357
     */
358
    public function approvals()
359
    {
360
        $logCounts = $this->getLogCounts();
361
        $total = $logCounts['review-approve'] +
362
        (!empty($logCounts['review-approve-a']) ? $logCounts['review-approve-a'] : 0) +
363
        (!empty($logCounts['review-approve-i']) ? $logCounts['review-approve-i'] : 0) +
364
        (!empty($logCounts['review-approve-ia']) ? $logCounts['review-approve-ia'] : 0);
365
        return $total;
366
    }
367
368
    /**
369
     * @return int
370
     */
371
    public function patrols()
372
    {
373
        $logCounts = $this->getLogCounts();
374
        return $logCounts['patrol-patrol'] ?: 0;
375
    }
376
377
    /**
378
     * Get the total edit counts for the top n projects of this user.
379
     * @param User $user
380
     * @param Project $project
381
     * @param int $numProjects
382
     * @return mixed[] Each element has 'total' and 'project' keys.
383
     */
384
    public function topProjectsEditCounts(User $user, Project $project, $numProjects = 10)
385
    {
386
        // Get counts.
387
        $editCounts = $this->getRepository()->getRevisionCountsAllProjects($user, $project);
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getRevisionCountsAllProjects() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
388
        // Sort.
389
        uasort($editCounts, function ($a, $b) {
390
            return $b['total'] - $a['total'];
391
        });
392
        // Truncate, and return.
1 ignored issue
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
393
        return array_slice($editCounts, 0, $numProjects);
394
    }
395
396
    /**
397
     * Get the given user's total edit counts per namespace.
398
     */
399
    public function namespaceTotals()
400
    {
401
        $counts = $this->getRepository()->getNamespaceTotals($this->project, $this->user);
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getNamespaceTotals() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
402
        arsort($counts);
403
        return $counts;
404
    }
405
406
    /**
407
     * Get a summary of the times of day and the days of the week that the user has edited.
408
     */
409
    public function timeCard()
410
    {
411
        return $this->getRepository()->getTimeCard($this->project, $this->user);
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getTimeCard() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
412
    }
413
414
    /**
415
     *
416
     */
417
    public function yearCounts()
418
    {
419
        $totals = $this->getRepository()->getYearCounts($this->project, $this->user);
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getYearCounts() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
420
        $out = [
421
            'years' => [],
422
            'namespaces' => [],
423
            'totals' => [],
424
        ];
425
        foreach ($totals as $total) {
426
            $out['years'][$total['year']] = $total['year'];
427
            $out['namespaces'][$total['page_namespace']] = $total['page_namespace'];
428
            if (!isset($out['totals'][$total['page_namespace']])) {
429
                $out['totals'][$total['page_namespace']] = [];
430
            }
431
            $out['totals'][$total['page_namespace']][$total['year']] = $total['count'];
432
        }
433
434
        return $out;
435
    }
436
437
    /**
438
     *
439
     */
440
    public function monthCounts()
441
    {
442
        $totals = $this->getRepository()->getMonthCounts($this->project, $this->user);
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getMonthCounts() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
443
        $out = [
444
            'years' => [],
445
            'namespaces' => [],
446
            'totals' => [],
447
        ];
448
        $out['max_year'] = 0;
449
        $out['min_year'] = date('Y');
450
        foreach ($totals as $total) {
451
            // Collect all applicable years and namespaces.
452
            $out['max_year'] = max($out['max_year'], $total['year']);
453
            $out['min_year'] = min($out['min_year'], $total['year']);
454
            // Collate the counts by namespace, and then year and month.
455
            $ns = $total['page_namespace'];
456
            if (!isset($out['totals'][$ns])) {
457
                $out['totals'][$ns] = [];
458
            }
459
            $out['totals'][$ns][$total['year'] . $total['month']] = $total['count'];
460
        }
461
        // Fill in the blanks (where no edits were made in a given month for a namespace).
462
        for ($y = $out['min_year']; $y <= $out['max_year']; $y++) {
463
            for ($m = 1; $m <= 12; $m++) {
464
                foreach ($out['totals'] as $nsId => &$total) {
465
                    if (!isset($total[$y . $m])) {
466
                        $total[$y . $m] = 0;
467
                    }
468
                }
469
            }
470
        }
471
        return $out;
472
    }
473
474
    public function latestGlobalRevisions($max = 40)
475
    {
476
        // Re-use the list of active projects from all-project revision counting.
477
        $editCounts = $this->getRepository()->getRevisionCountsAllProjects($this->user, $this->project);
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getRevisionCountsAllProjects() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
478
        // Go through each project that has any edits.
479
        $globalRevisions = [];
480
        $oldest = null;
481
        foreach ($editCounts as $editCount) {
482
            // Don't query revisions if there aren't any.
483
            if ($editCount['total'] == 0) {
484
                continue;
485
            }
486
            // Get this project's revisions.
487
            $revisions = $this->getRepository()
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getRevisions() does only exist in the following sub-classes of Xtools\Repository: Xtools\EditCounterRepository, Xtools\PagesRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
488
                ->getRevisions($editCount['project'], $this->user, $oldest);
489
            foreach ($revisions as &$revision) {
490
                $revision['project'] = $editCount['project'];
491
                $revision['timestamp'] = DateTime::createFromFormat('U', $revision['unix_timestamp']);
492
                // If we've already got enough, only check for those newer than the current oldest.
493
                $enough = (count($globalRevisions) >= $max);
494
                $isOlder = ($oldest === null
495
                            || ($oldest !== null && $revision['unix_timestamp'] < $oldest));
496
                if ($enough && $isOlder) {
497
                    $oldest = $revision['unix_timestamp'];
498
                }
499
                $globalRevisions[$revision['unix_timestamp']] = $revision;
500
            }
501
            // Sort and prune, before adding more.
502
            krsort($globalRevisions);
503
            $globalRevisions = array_slice($globalRevisions, 0, $max);
504
        }
505
        return $globalRevisions;
506
    }
507
}
508