Completed
Push — master ( 77d109...dc7aae )
by Sam
05:53 queued 03:04
created

Page::countLinksAndRedirects()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * This file contains only the Page class.
4
 */
5
6
namespace Xtools;
7
8
/**
9
 * A Page is a single wiki page in one project.
10
 */
11
class Page extends Model
12
{
13
14
    /** @var Project The project that this page belongs to. */
15
    protected $project;
16
17
    /** @var string The page name as provided at instantiation. */
18
    protected $unnormalizedPageName;
19
20
    /** @var string[] Metadata about this page. */
21
    protected $pageInfo;
22
23
    /** @var string[] Revision history of this page */
24
    protected $revisions;
25
26
    /**
27
     * Page constructor.
28
     * @param Project $project
29
     * @param string $pageName
30
     */
31
    public function __construct(Project $project, $pageName)
32
    {
33
        $this->project = $project;
34
        $this->unnormalizedPageName = $pageName;
35
    }
36
37
    /**
38
     * Get basic information about this page from the repository.
39
     * @return \string[]
40
     */
41
    protected function getPageInfo()
42
    {
43
        if (empty($this->pageInfo)) {
44
            $this->pageInfo = $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 getPageInfo() does only exist in the following sub-classes of Xtools\Repository: 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...
45
                    ->getPageInfo($this->project, $this->unnormalizedPageName);
46
        }
47
        return $this->pageInfo;
48
    }
49
50
    /**
51
     * Get the page's title.
52
     * @return string
53
     */
54
    public function getTitle()
55
    {
56
        $info = $this->getPageInfo();
57
        return isset($info['title']) ? $info['title'] : $this->unnormalizedPageName;
58
    }
59
60
    /**
61
     * Get this page's database ID.
62
     * @return int
63
     */
64
    public function getId()
65
    {
66
        $info = $this->getPageInfo();
67
        return isset($info['pageid']) ? $info['pageid'] : null;
68
    }
69
70
    /**
71
     * Get this page's length in bytes.
72
     * @return int
73
     */
74
    public function getLength()
75
    {
76
        $info = $this->getPageInfo();
77
        return isset($info['length']) ? $info['length'] : null;
78
    }
79
80
    /**
81
     * Get HTML for the stylized display of the title.
82
     * The text will be the same as Page::getTitle().
83
     * @return string
84
     */
85
    public function getDisplayTitle()
86
    {
87
        $info = $this->getPageInfo();
88
        if (isset($info['displaytitle'])) {
89
            return $info['displaytitle'];
90
        }
91
        return $this->getTitle();
92
    }
93
94
    /**
95
     * Get the full URL of this page.
96
     * @return string
97
     */
98
    public function getUrl()
99
    {
100
        $info = $this->getPageInfo();
101
        return isset($info['fullurl']) ? $info['fullurl'] : null;
102
    }
103
104
    /**
105
     * Get the numerical ID of the namespace of this page.
106
     * @return int
107
     */
108
    public function getNamespace()
109
    {
110
        $info = $this->getPageInfo();
111
        return isset($info['ns']) ? $info['ns'] : null;
112
    }
113
114
    /**
115
     * Get the number of page watchers.
116
     * @return int
117
     */
118
    public function getWatchers()
119
    {
120
        $info = $this->getPageInfo();
121
        return isset($info['watchers']) ? $info['watchers'] : null;
122
    }
123
124
    /**
125
     * Whether or not this page exists.
126
     * @return bool
127
     */
128
    public function exists()
129
    {
130
        $info = $this->getPageInfo();
131
        return !isset($info['missing']) && !isset($info['invalid']);
132
    }
133
134
    /**
135
     * Get the Project to which this page belongs.
136
     * @return Project
137
     */
138
    public function getProject()
139
    {
140
        return $this->project;
141
    }
142
143
    /**
144
     * Get the language code for this page.
145
     * If not set, the language code for the project is returned.
146
     * @return string
147
     */
148
    public function getLang()
149
    {
150
        $info = $this->getPageInfo();
151
        if (isset($info['pagelanguage'])) {
152
            return $info['pagelanguage'];
153
        } else {
154
            return $this->getProject()->getLang();
155
        }
156
    }
157
158
    /**
159
     * Get the Wikidata ID of this page.
160
     * @return string
161
     */
162
    public function getWikidataId()
163
    {
164
        $info = $this->getPageInfo();
165
        if (isset($info['pageprops']['wikibase_item'])) {
166
            return $info['pageprops']['wikibase_item'];
167
        } else {
168
            return null;
169
        }
170
    }
171
172
    /**
173
     * Get the number of revisions the page has.
174
     * @param User $user Optionally limit to those of this user.
175
     * @return int
176
     */
177
    public function getNumRevisions(User $user = null)
178
    {
179
        // Return the count of revisions if already present
180
        if (!empty($this->revisions)) {
181
            return count($this->revisions);
182
        }
183
184
        // Otherwise do a COUNT in the event fetching
185
        // all revisions is not desired
186
        return (int) $this->getRepository()->getNumRevisions($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 getNumRevisions() does only exist in the following sub-classes of Xtools\Repository: 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...
187
    }
188
189
    /**
190
     * Get all edits made to this page.
191
     * @param User|null $user Specify to get only revisions by the given user.
192
     * @return array
193
     */
194
    public function getRevisions(User $user = null)
195
    {
196
        if ($this->revisions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->revisions of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
197
            return $this->revisions;
198
        }
199
200
        $this->revisions = $this->getRepository()->getRevisions($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 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...
201
202
        return $this->revisions;
203
    }
204
205
    /**
206
     * Get the statement for a single revision,
207
     * so that you can iterate row by row.
208
     * @param User|null $user Specify to get only revisions by the given user.
209
     * @return Doctrine\DBAL\Driver\PDOStatement
210
     */
211
    public function getRevisionsStmt(User $user = null)
212
    {
213
        return $this->getRepository()->getRevisionsStmt($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 getRevisionsStmt() does only exist in the following sub-classes of Xtools\Repository: 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...
214
    }
215
216
    /**
217
     * Get various basic info used in the API, including the
218
     *   number of revisions, unique authors, initial author
219
     *   and edit count of the initial author.
220
     * This is combined into one query for better performance.
221
     * Caching is intentionally disabled, because using the gadget,
222
     *   this will get hit for a different page constantly, where
223
     *   the likelihood of cache benefiting us is slim.
224
     * @return string[]
225
     */
226
    public function getBasicEditingInfo()
227
    {
228
        return $this->getRepository()->getBasicEditingInfo($this);
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 getBasicEditingInfo() does only exist in the following sub-classes of Xtools\Repository: 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...
229
    }
230
231
    /**
232
     * Get assessments of this page
233
     * @return string[]|false `false` if unsupported, or array in the format of:
234
     *         [
235
     *             'assessment' => 'C', // overall assessment
236
     *             'wikiprojects' => [
237
     *                 'Biography' => [
238
     *                     'assessment' => 'C',
239
     *                     'badge' => 'url',
240
     *                 ],
241
     *                 ...
242
     *             ],
243
     *             'wikiproject_prefix' => 'Wikipedia:WikiProject_',
244
     *         ]
245
     */
246
    public function getAssessments()
247
    {
248
        if (!$this->project->hasPageAssessments() || $this->getNamespace() !== 0) {
249
            return false;
250
        }
251
252
        $projectDomain = $this->project->getDomain();
253
        $config = $this->project->getRepository()->getAssessmentsConfig($projectDomain);
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 getAssessmentsConfig() does only exist in the following sub-classes of Xtools\Repository: Xtools\ProjectRepository. 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...
254
        $data = $this->getRepository()->getAssessments($this->project, [$this->getId()]);
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 getAssessments() does only exist in the following sub-classes of Xtools\Repository: 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...
255
256
        // Set the default decorations for the overall quality assessment
257
        // This will be replaced with the first valid class defined for any WikiProject
258
        $overallQuality = $config['class']['Unknown'];
259
        $overallQuality['value'] = '???';
260
261
        $decoratedAssessments = [];
262
263
        foreach ($data as $assessment) {
264
            $classValue = $assessment['class'];
265
266
            // Use ??? as the presented value when the class is unknown or is not defined in the config
267
            if ($classValue === 'Unknown' || $classValue === '' || !isset($config['class'][$classValue])) {
268
                $classAttrs = $config['class']['Unknown'];
269
                $assessment['class']['value'] = '???';
270
                $assessment['class']['category'] = $classAttrs['category'];
271
                $assessment['class']['color'] = $classAttrs['color'];
272
                $assessment['class']['badge'] = "https://upload.wikimedia.org/wikipedia/commons/"
273
                    . $classAttrs['badge'];
274
            } else {
275
                $classAttrs = $config['class'][$classValue];
276
                $assessment['class'] = [
277
                    'value' => $classValue,
278
                    'color' => $classAttrs['color'],
279
                    'category' => $classAttrs['category'],
280
                ];
281
282
                // add full URL to badge icon
283
                if ($classAttrs['badge'] !== '') {
284
                    $assessment['class']['badge'] = $this->project->getAssessmentBadgeURL($classValue);
285
                }
286
            }
287
288
            if ($overallQuality['value'] === '???') {
289
                $overallQuality = $assessment['class'];
290
                $overallQuality['category'] = $classAttrs['category'];
291
            }
292
293
            $importanceValue = $assessment['importance'];
294
            $importanceUnknown = $importanceValue === 'Unknown' || $importanceValue === '';
295
296
            if ($importanceUnknown || !isset($config['importance'][$importanceValue])) {
297
                $importanceAttrs = $config['importance']['Unknown'];
298
                $assessment['importance'] = $importanceAttrs;
299
                $assessment['importance']['value'] = '???';
300
                $assessment['importance']['category'] = $importanceAttrs['category'];
301
            } else {
302
                $importanceAttrs = $config['importance'][$importanceValue];
303
                $assessment['importance'] = [
304
                    'value' => $importanceValue,
305
                    'color' => $importanceAttrs['color'],
306
                    'weight' => $importanceAttrs['weight'], // numerical weight for sorting purposes
307
                    'category' => $importanceAttrs['category'],
308
                ];
309
            }
310
311
            $decoratedAssessments[$assessment['wikiproject']] = $assessment;
312
        }
313
314
        return [
315
            'assessment' => $overallQuality,
316
            'wikiprojects' => $decoratedAssessments,
317
            'wikiproject_prefix' => $config['wikiproject_prefix']
318
        ];
319
    }
320
321
    /**
322
     * Get CheckWiki errors for this page
323
     * @return string[] See getErrors() for format
324
     */
325
    public function getCheckWikiErrors()
326
    {
327
        return $this->getRepository()->getCheckWikiErrors($this);
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 getCheckWikiErrors() does only exist in the following sub-classes of Xtools\Repository: 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...
328
    }
329
330
    /**
331
     * Get Wikidata errors for this page
332
     * @return string[] See getErrors() for format
333
     */
334
    public function getWikidataErrors()
335
    {
336
        $errors = [];
337
338
        if (empty($this->getWikidataId())) {
339
            return [];
340
        }
341
342
        $wikidataInfo = $this->getRepository()->getWikidataInfo($this);
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 getWikidataInfo() does only exist in the following sub-classes of Xtools\Repository: 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...
343
344
        $terms = array_map(function ($entry) {
345
            return $entry['term'];
346
        }, $wikidataInfo);
347
348
        $lang = $this->getLang();
349
350 View Code Duplication
        if (!in_array('label', $terms)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
351
            $errors[] = [
352
                'prio' => 2,
353
                'name' => 'Wikidata',
354
                'notice' => "Label for language <em>$lang</em> is missing", // FIXME: i18n
355
                'explanation' => "See: <a target='_blank' " .
356
                    "href='//www.wikidata.org/wiki/Help:Label'>Help:Label</a>",
357
            ];
358
        }
359
360 View Code Duplication
        if (!in_array('description', $terms)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
361
            $errors[] = [
362
                'prio' => 3,
363
                'name' => 'Wikidata',
364
                'notice' => "Description for language <em>$lang</em> is missing", // FIXME: i18n
365
                'explanation' => "See: <a target='_blank' " .
366
                    "href='//www.wikidata.org/wiki/Help:Description'>Help:Description</a>",
367
            ];
368
        }
369
370
        return $errors;
371
    }
372
373
    /**
374
     * Get Wikidata and CheckWiki errors, if present
375
     * @return string[] List of errors in the format:
376
     *    [[
377
     *         'prio' => int,
378
     *         'name' => string,
379
     *         'notice' => string (HTML),
380
     *         'explanation' => string (HTML)
381
     *     ], ... ]
382
     */
383
    public function getErrors()
384
    {
385
        // Includes label and description
386
        $wikidataErrors = $this->getWikidataErrors();
387
388
        $checkWikiErrors = $this->getCheckWikiErrors();
389
390
        return array_merge($wikidataErrors, $checkWikiErrors);
391
    }
392
393
    /**
394
     * Get all wikidata items for the page, not just languages of sister projects
395
     * @return int Number of records.
396
     */
397
    public function getWikidataItems()
398
    {
399
        return $this->getRepository()->getWikidataItems($this);
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 getWikidataItems() does only exist in the following sub-classes of Xtools\Repository: 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...
400
    }
401
402
    /**
403
     * Count wikidata items for the page, not just languages of sister projects
404
     * @return int Number of records.
405
     */
406
    public function countWikidataItems()
407
    {
408
        return $this->getRepository()->countWikidataItems($this);
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 countWikidataItems() does only exist in the following sub-classes of Xtools\Repository: 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...
409
    }
410
411
    /**
412
     * Get number of in and outgoing links and redirects to this page.
413
     * @return string[] Counts with the keys 'links_ext_count', 'links_out_count',
414
     *                  'links_in_count' and 'redirects_count'
415
     */
416
    public function countLinksAndRedirects()
417
    {
418
        return $this->getRepository()->countLinksAndRedirects($this);
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 countLinksAndRedirects() does only exist in the following sub-classes of Xtools\Repository: 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...
419
    }
420
421
    /**
422
     * Get page views for the given page and timeframe.
423
     * @param string|DateTime $start In the format YYYYMMDD
424
     * @param string|DateTime $end In the format YYYYMMDD
425
     * @return string[]
426
     */
427
    public function getPageviews($start, $end)
428
    {
429
        return $this->getRepository()->getPageviews($this, $start, $end);
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 getPageviews() does only exist in the following sub-classes of Xtools\Repository: 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...
430
    }
431
432
    /**
433
     * Get the sum of pageviews over the last N days
434
     * @param int [$days] Default 30
435
     * @return int Number of pageviews
436
     */
437
    public function getLastPageviews($days = 30)
438
    {
439
        $start = date('Ymd', strtotime("-$days days"));
440
        $end = date('Ymd');
441
442
        $pageviews = $this->getPageviews($start, $end);
443
444
        return array_sum(array_map(function ($item) {
445
            return (int) $item['views'];
446
        }, $pageviews['items']));
447
    }
448
}
449