Completed
Push — master ( 5e33bb...9a10b7 )
by Sam
03:35
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
use Symfony\Component\VarDumper\VarDumper;
7
8
/**
9
 * An EditCounter provides statistics about a user's edits on a project.
10
 */
11
class EditCounter extends Model
12
{
13
    
14
    /** @var Project */
15
    protected $project;
16
    
17
    /** @var User */
18
    protected $user;
19
20
    /** @var int[] */
21
    protected $revisionCounts;
22
23
    /** @var string[] */
24
    protected $revisionDates;
25
26
    /** @var int[] */
27
    protected $pageCounts;
28
    
29
    /** @var int[] */
30
    protected $logCounts;
31
32
    public function __construct(Project $project, User $user)
33
    {
34
        $this->project = $project;
35
        $this->user = $user;
36
    }
37
38
    /**
39
     * Get revision count data.
40
     * @return int[]
41
     */
42
    protected function getRevisionCounts()
43
    {
44
        if (! is_array($this->revisionCounts)) {
45
            $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...
46
                ->getRevisionCounts($this->project, $this->user);
47
        }
48
        return $this->revisionCounts;
49
    }
50
51
    /**
52
     * Get revision dates.
53
     * @return int[]
54
     */
55
    protected function getRevisionDates()
56
    {
57
        if (! is_array($this->revisionDates)) {
58
            $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...
59
                ->getRevisionDates($this->project, $this->user);
60
        }
61
        return $this->revisionDates;
62
    }
63
64
    /**
65
     * Get page count data.
66
     * @return int[]
67
     */
68
    protected function getPageCounts()
69
    {
70
        if (! is_array($this->pageCounts)) {
71
            $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...
72
                ->getPageCounts($this->project, $this->user);
73
        }
74
        return $this->pageCounts;
75
    }
76
77
    /**
78
     * Get revision dates.
79
     * @return int[]
80
     */
81
    protected function getLogCounts()
82
    {
83
        if (! is_array($this->logCounts)) {
84
            $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...
85
                ->getLogCounts($this->project, $this->user);
86
        }
87
        return $this->logCounts;
88
    }
89
90
    public function countLiveRevisions()
91
    {
92
        $revCounts = $this->getRevisionCounts();
93
        return isset($revCounts['live']) ? $revCounts['live'] : 0;
94
    }
95
96
    /**
97
     * Get the total number of revisions that have been deleted.
98
     * @return int
99
     */
100
    public function countDeletedRevisions()
101
    {
102
        $revCounts = $this->getRevisionCounts();
103
        return isset($revCounts['deleted']) ? $revCounts['deleted'] : 0;
104
    }
105
106
    /**
107
     * Get the total edit count (live + deleted).
108
     * @return int
109
     */
110
    public function countAllRevisions()
111
    {
112
        return $this->countLiveRevisions() + $this->countDeletedRevisions();
113
    }
114
115
    /**
116
     * Get the total number of revisions with comments.
117
     * @return int
118
     */
119
    public function countRevisionsWithComments()
120
    {
121
        $revCounts = $this->getRevisionCounts();
122
        return isset($revCounts['with_comments']) ? $revCounts['with_comments'] : 0;
123
    }
124
125
    /**
126
     * Get the total number of revisions without comments.
127
     * @return int
128
     */
129
    public function countRevisionsWithoutComments()
130
    {
131
        return $this->countAllRevisions() - $this->countRevisionsWithComments();
132
    }
133
134
    /**
135
     * Get the total number of revisions marked as 'minor' by the user.
136
     * @return int
137
     */
138
    public function countMinorRevisions()
139
    {
140
        $revCounts = $this->getRevisionCounts();
141
        return isset($revCounts['minor']) ? $revCounts['minor'] : 0;
142
    }
143
144
    /**
145
     * Get the total number of revisions under 20 bytes.
146
     */
147
    public function countSmallRevisions()
148
    {
149
        $revCounts = $this->getRevisionCounts();
150
        return isset($revCounts['small']) ? $revCounts['small'] : 0;
151
    }
152
153
    /**
154
     * Get the total number of revisions over 1000 bytes.
155
     */
156
    public function countLargeRevisions()
157
    {
158
        $revCounts = $this->getRevisionCounts();
159
        return isset($revCounts['large']) ? $revCounts['large'] : 0;
160
    }
161
162
    /**
163
     * Get the average revision size for the user.
164
     * @return float Size in bytes.
165
     */
166
    public function averageRevisionSize()
167
    {
168
        $revisionCounts = $this->getRevisionCounts();
169
        return round($revisionCounts['average_size'], 3);
170
    }
171
172
    /**
173
     * Get the total number of non-deleted pages edited by the user.
174
     * @return int
175
     */
176
    public function countLivePagesEdited()
177
    {
178
        $pageCounts = $this->getPageCounts();
179
        return isset($pageCounts['edited-live']) ? $pageCounts['edited-live'] : 0;
180
    }
181
182
    /**
183
     * Get the total number of deleted pages ever edited by the user.
184
     * @return int
185
     */
186
    public function countDeletedPagesEdited()
187
    {
188
        $pageCounts = $this->getPageCounts();
189
        return isset($pageCounts['edited-deleted']) ? $pageCounts['edited-deleted'] : 0;
190
    }
191
192
    /**
193
     * Get the total number of pages ever edited by this user (both live and deleted).
194
     * @return int
195
     */
196
    public function countAllPagesEdited()
197
    {
198
        return $this->countLivePagesEdited() + $this->countDeletedPagesEdited();
199
    }
200
201
    /**
202
     * Get the total number of semi-automated edits.
203
     * @return int
204
     */
205
    public function countAutomatedEdits()
206
    {
207
    }
208
209
    /**
210
     * Get the total number of pages (both still live and those that have been deleted) created
211
     * by the user.
212
     * @return int
213
     */
214
    public function countPagesCreated()
215
    {
216
        return $this->countCreatedPagesLive() + $this->countPagesCreatedDeleted();
217
    }
218
219
    /**
220
     * Get the total number of pages created by the user, that have not been deleted.
221
     * @return int
222
     */
223
    public function countCreatedPagesLive()
224
    {
225
        $pageCounts = $this->getPageCounts();
226
        return isset($pageCounts['created-live']) ? (int)$pageCounts['created-live'] : 0;
227
    }
228
229
    /**
230
     * Get the total number of pages created by the user, that have since been deleted.
231
     * @return int
232
     */
233
    public function countPagesCreatedDeleted()
234
    {
235
        $pageCounts = $this->getPageCounts();
236
        return isset($pageCounts['created-deleted']) ? (int)$pageCounts['created-deleted'] : 0;
237
    }
238
239
    /**
240
     * Get the total number of pages that have been deleted by the user.
241
     * @return int
242
     */
243
    public function countPagesDeleted()
244
    {
245
        $logCounts = $this->getLogCounts();
246
        return isset($logCounts['delete-delete']) ? (int)$logCounts['delete-delete'] : 0;
247
    }
248
249
    /**
250
     * Get the total number of pages moved by the user.
251
     * @return int
252
     */
253
    public function countPagesMoved()
254
    {
255
        $logCounts = $this->getLogCounts();
256
        return isset($logCounts['move-move']) ? (int)$logCounts['move-move'] : 0;
257
    }
258
259
    /**
260
     * Get the average number of edits per page (including deleted revisions and pages).
261
     * @return float
262
     */
263
    public function averageRevisionsPerPage()
264
    {
265
        if ($this->countAllPagesEdited() == 0) {
266
            return 0;
267
        }
268
        return round($this->countAllRevisions() / $this->countAllPagesEdited(), 3);
269
    }
270
271
    /**
272
     * Average number of edits made per day.
273
     * @return float
274
     */
275
    public function averageRevisionsPerDay()
276
    {
277
        if ($this->getDays() == 0) {
278
            return 0;
279
        }
280
        return round($this->countAllRevisions() / $this->getDays(), 3);
281
    }
282
    
283
    /**
284
     * Get the total number of edits made by the user with semi-automating tools.
285
     * @TODO
286
     */
287
    public function countAutomatedRevisions()
288
    {
289
        return 0;
290
    }
291
    
292
    /**
293
     * Get the count of (non-deleted) edits made in the given timeframe to now.
294
     * @param string $time One of 'day', 'week', 'month', or 'year'.
295
     * @return int The total number of live edits.
296
     */
297
    public function countRevisionsInLast($time)
298
    {
299
        $revCounts = $this->getRevisionCounts();
300
        return isset($revCounts[$time]) ? $revCounts[$time] : 0;
301
    }
302
303
    /**
304
     * Get the date and time of the user's first edit.
305
     */
306
    public function datetimeFirstRevision()
307
    {
308
        $first = $this->getRevisionDates()['first'];
309
        return new DateTime($first);
310
    }
311
312
    /**
313
     * Get the date and time of the user's first edit.
314
     * @return DateTime
315
     */
316
    public function datetimeLastRevision()
317
    {
318
        $last = $this->getRevisionDates()['last'];
319
        return new DateTime($last);
320
    }
321
322
    /**
323
     * Get the number of days between the first and last edits.
324
     * If there's only one edit, this is counted as one day.
325
     * @return int
326
     */
327
    public function getDays()
328
    {
329
        $days = $this->datetimeLastRevision()->diff($this->datetimeFirstRevision())->days;
330
        return $days > 0 ? $days : 1;
331
    }
332
333
    public function countFilesUploaded()
334
    {
335
        $logCounts = $this->getLogCounts();
336
        return $logCounts['upload-upload'] ?: 0;
337
    }
338
339
    public function countFilesUploadedCommons()
340
    {
341
        $logCounts = $this->getLogCounts();
342
        return $logCounts['files_uploaded_commons'] ?: 0;
343
    }
344
345
    /**
346
     * Get the total number of revisions the user has sent thanks for.
347
     * @return int
348
     */
349
    public function thanks()
350
    {
351
        $logCounts = $this->getLogCounts();
352
        return $logCounts['thanks-thank'] ?: 0;
353
    }
354
355
    /**
356
     * Get the total number of approvals
357
     * @return int
358
     */
359
    public function approvals()
360
    {
361
        $logCounts = $this->getLogCounts();
362
        $total = $logCounts['review-approve'] +
363
        (!empty($logCounts['review-approve-a']) ? $logCounts['review-approve-a'] : 0) +
364
        (!empty($logCounts['review-approve-i']) ? $logCounts['review-approve-i'] : 0) +
365
        (!empty($logCounts['review-approve-ia']) ? $logCounts['review-approve-ia'] : 0);
366
        return $total;
367
    }
368
369
    /**
370
     * @return int
371
     */
372
    public function patrols()
373
    {
374
        $logCounts = $this->getLogCounts();
375
        return $logCounts['patrol-patrol'] ?: 0;
376
    }
377
378
    /**
379
     * Get the total edit counts for the top n projects of this user.
380
     * @param User $user
381
     * @param Project $project
382
     * @param int $numProjects
383
     * @return mixed[] Each element has 'total' and 'project' keys.
384
     */
385
    public function topProjectsEditCounts(User $user, Project $project, $numProjects = 10)
386
    {
387
        // Get counts.
388
        $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...
389
        // Sort.
390
        uasort($editCounts, function ($a, $b) {
391
            return $b['total'] - $a['total'];
392
        });
393
        // 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...
394
        return array_slice($editCounts, 0, $numProjects);
395
    }
396
397
    /**
398
     * Get the given user's total edit counts per namespace.
399
     */
400
    public function namespaceTotals()
401
    {
402
        $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...
403
        arsort($counts);
404
        return $counts;
405
    }
406
407
    /**
408
     * Get a summary of the times of day and the days of the week that the user has edited.
409
     */
410
    public function timeCard()
411
    {
412
        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...
413
    }
414
415
    /**
416
     *
417
     */
418
    public function yearCounts()
419
    {
420
        $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...
421
        $out = [
422
            'years' => [],
423
            'namespaces' => [],
424
            'totals' => [],
425
        ];
426
        foreach ($totals as $total) {
427
            $out['years'][$total['year']] = $total['year'];
428
            $out['namespaces'][$total['page_namespace']] = $total['page_namespace'];
429
            if (!isset($out['totals'][$total['page_namespace']])) {
430
                $out['totals'][$total['page_namespace']] = [];
431
            }
432
            $out['totals'][$total['page_namespace']][$total['year']] = $total['count'];
433
        }
434
435
        return $out;
436
    }
437
}
438