Completed
Push — master ( 5c8e7a...62162d )
by MusikAnimal
22s
created

Page::getWikitext()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 2
eloc 7
nc 2
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 the page's title without the namespace.
62
     * @return string
63
     */
64
    public function getTitleWithoutNamespace()
65
    {
66
        $info = $this->getPageInfo();
67
        $title = isset($info['title']) ? $info['title'] : $this->unnormalizedPageName;
68
        $nsName = $this->getNamespaceName();
69
        return str_replace($nsName . ':', '', $title);
70
    }
71
72
    /**
73
     * Get this page's database ID.
74
     * @return int
75
     */
76
    public function getId()
77
    {
78
        $info = $this->getPageInfo();
79
        return isset($info['pageid']) ? $info['pageid'] : null;
80
    }
81
82
    /**
83
     * Get this page's length in bytes.
84
     * @return int
85
     */
86
    public function getLength()
87
    {
88
        $info = $this->getPageInfo();
89
        return isset($info['length']) ? $info['length'] : null;
90
    }
91
92
    /**
93
     * Get HTML for the stylized display of the title.
94
     * The text will be the same as Page::getTitle().
95
     * @return string
96
     */
97
    public function getDisplayTitle()
98
    {
99
        $info = $this->getPageInfo();
100
        if (isset($info['displaytitle'])) {
101
            return $info['displaytitle'];
102
        }
103
        return $this->getTitle();
104
    }
105
106
    /**
107
     * Get the full URL of this page.
108
     * @return string
109
     */
110
    public function getUrl()
111
    {
112
        $info = $this->getPageInfo();
113
        return isset($info['fullurl']) ? $info['fullurl'] : null;
114
    }
115
116
    /**
117
     * Get the numerical ID of the namespace of this page.
118
     * @return int
119
     */
120
    public function getNamespace()
121
    {
122
        $info = $this->getPageInfo();
123
        return isset($info['ns']) ? $info['ns'] : null;
124
    }
125
126
    /**
127
     * Get the name of the namespace of this page.
128
     * @return string
129
     */
130
    public function getNamespaceName()
131
    {
132
        $info = $this->getPageInfo();
133
        return isset($info['ns'])
134
            ? $this->getProject()->getNamespaces()[$info['ns']]
135
            : null;
136
    }
137
138
    /**
139
     * Get the number of page watchers.
140
     * @return int
141
     */
142
    public function getWatchers()
143
    {
144
        $info = $this->getPageInfo();
145
        return isset($info['watchers']) ? $info['watchers'] : null;
146
    }
147
148
    /**
149
     * Whether or not this page exists.
150
     * @return bool
151
     */
152
    public function exists()
153
    {
154
        $info = $this->getPageInfo();
155
        return !isset($info['missing']) && !isset($info['invalid']);
156
    }
157
158
    /**
159
     * Get the Project to which this page belongs.
160
     * @return Project
161
     */
162
    public function getProject()
163
    {
164
        return $this->project;
165
    }
166
167
    /**
168
     * Get the language code for this page.
169
     * If not set, the language code for the project is returned.
170
     * @return string
171
     */
172
    public function getLang()
173
    {
174
        $info = $this->getPageInfo();
175
        if (isset($info['pagelanguage'])) {
176
            return $info['pagelanguage'];
177
        } else {
178
            return $this->getProject()->getLang();
179
        }
180
    }
181
182
    /**
183
     * Get the Wikidata ID of this page.
184
     * @return string
185
     */
186
    public function getWikidataId()
187
    {
188
        $info = $this->getPageInfo();
189
        if (isset($info['pageprops']['wikibase_item'])) {
190
            return $info['pageprops']['wikibase_item'];
191
        } else {
192
            return null;
193
        }
194
    }
195
196
    /**
197
     * Get the number of revisions the page has.
198
     * @param User $user Optionally limit to those of this user.
199
     * @return int
200
     */
201
    public function getNumRevisions(User $user = null)
202
    {
203
        // Return the count of revisions if already present
204
        if (!empty($this->revisions)) {
205
            return count($this->revisions);
206
        }
207
208
        // Otherwise do a COUNT in the event fetching
209
        // all revisions is not desired
210
        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...
211
    }
212
213
    /**
214
     * Get all edits made to this page.
215
     * @param User|null $user Specify to get only revisions by the given user.
216
     * @return array
217
     */
218
    public function getRevisions(User $user = null)
219
    {
220
        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...
221
            return $this->revisions;
222
        }
223
224
        $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...
225
226
        return $this->revisions;
227
    }
228
229
    /**
230
     * Get the full page wikitext.
231
     * @return string|null Null if nothing was found.
232
     */
233
    public function getWikitext()
234
    {
235
        $content = $this->getRepository()->getPagesWikitext(
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 getPagesWikitext() 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...
236
            $this->getProject(),
237
            [ $this->getTitle() ]
238
        );
239
240
        return isset($content[$this->getTitle()])
241
            ? $content[$this->getTitle()]
242
            : null;
243
    }
244
245
    /**
246
     * Get the statement for a single revision,
247
     * so that you can iterate row by row.
248
     * @param User|null $user Specify to get only revisions by the given user.
249
     * @return Doctrine\DBAL\Driver\PDOStatement
250
     */
251
    public function getRevisionsStmt(User $user = null)
252
    {
253
        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...
254
    }
255
256
    /**
257
     * Get various basic info used in the API, including the
258
     *   number of revisions, unique authors, initial author
259
     *   and edit count of the initial author.
260
     * This is combined into one query for better performance.
261
     * Caching is intentionally disabled, because using the gadget,
262
     *   this will get hit for a different page constantly, where
263
     *   the likelihood of cache benefiting us is slim.
264
     * @return string[]
265
     */
266
    public function getBasicEditingInfo()
267
    {
268
        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...
269
    }
270
271
    /**
272
     * Get assessments of this page
273
     * @return string[]|false `false` if unsupported, or array in the format of:
274
     *         [
275
     *             'assessment' => 'C', // overall assessment
276
     *             'wikiprojects' => [
277
     *                 'Biography' => [
278
     *                     'assessment' => 'C',
279
     *                     'badge' => 'url',
280
     *                 ],
281
     *                 ...
282
     *             ],
283
     *             'wikiproject_prefix' => 'Wikipedia:WikiProject_',
284
     *         ]
285
     */
286
    public function getAssessments()
287
    {
288
        if (!$this->project->hasPageAssessments() || $this->getNamespace() !== 0) {
289
            return false;
290
        }
291
292
        $projectDomain = $this->project->getDomain();
293
        $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...
294
        $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...
295
296
        // Set the default decorations for the overall quality assessment
297
        // This will be replaced with the first valid class defined for any WikiProject
298
        $overallQuality = $config['class']['Unknown'];
299
        $overallQuality['value'] = '???';
300
301
        $decoratedAssessments = [];
302
303
        foreach ($data as $assessment) {
304
            $classValue = $assessment['class'];
305
306
            // Use ??? as the presented value when the class is unknown or is not defined in the config
307
            if ($classValue === 'Unknown' || $classValue === '' || !isset($config['class'][$classValue])) {
308
                $classAttrs = $config['class']['Unknown'];
309
                $assessment['class']['value'] = '???';
310
                $assessment['class']['category'] = $classAttrs['category'];
311
                $assessment['class']['color'] = $classAttrs['color'];
312
                $assessment['class']['badge'] = "https://upload.wikimedia.org/wikipedia/commons/"
313
                    . $classAttrs['badge'];
314
            } else {
315
                $classAttrs = $config['class'][$classValue];
316
                $assessment['class'] = [
317
                    'value' => $classValue,
318
                    'color' => $classAttrs['color'],
319
                    'category' => $classAttrs['category'],
320
                ];
321
322
                // add full URL to badge icon
323
                if ($classAttrs['badge'] !== '') {
324
                    $assessment['class']['badge'] = $this->project->getAssessmentBadgeURL($classValue);
325
                }
326
            }
327
328
            if ($overallQuality['value'] === '???') {
329
                $overallQuality = $assessment['class'];
330
                $overallQuality['category'] = $classAttrs['category'];
331
            }
332
333
            $importanceValue = $assessment['importance'];
334
            $importanceUnknown = $importanceValue === 'Unknown' || $importanceValue === '';
335
336
            if ($importanceUnknown || !isset($config['importance'][$importanceValue])) {
337
                $importanceAttrs = $config['importance']['Unknown'];
338
                $assessment['importance'] = $importanceAttrs;
339
                $assessment['importance']['value'] = '???';
340
                $assessment['importance']['category'] = $importanceAttrs['category'];
341
            } else {
342
                $importanceAttrs = $config['importance'][$importanceValue];
343
                $assessment['importance'] = [
344
                    'value' => $importanceValue,
345
                    'color' => $importanceAttrs['color'],
346
                    'weight' => $importanceAttrs['weight'], // numerical weight for sorting purposes
347
                    'category' => $importanceAttrs['category'],
348
                ];
349
            }
350
351
            $decoratedAssessments[$assessment['wikiproject']] = $assessment;
352
        }
353
354
        return [
355
            'assessment' => $overallQuality,
356
            'wikiprojects' => $decoratedAssessments,
357
            'wikiproject_prefix' => $config['wikiproject_prefix']
358
        ];
359
    }
360
361
    /**
362
     * Get CheckWiki errors for this page
363
     * @return string[] See getErrors() for format
364
     */
365
    public function getCheckWikiErrors()
366
    {
367
        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...
368
    }
369
370
    /**
371
     * Get Wikidata errors for this page
372
     * @return string[] See getErrors() for format
373
     */
374
    public function getWikidataErrors()
375
    {
376
        $errors = [];
377
378
        if (empty($this->getWikidataId())) {
379
            return [];
380
        }
381
382
        $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...
383
384
        $terms = array_map(function ($entry) {
385
            return $entry['term'];
386
        }, $wikidataInfo);
387
388
        $lang = $this->getLang();
389
390 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...
391
            $errors[] = [
392
                'prio' => 2,
393
                'name' => 'Wikidata',
394
                'notice' => "Label for language <em>$lang</em> is missing", // FIXME: i18n
395
                'explanation' => "See: <a target='_blank' " .
396
                    "href='//www.wikidata.org/wiki/Help:Label'>Help:Label</a>",
397
            ];
398
        }
399
400 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...
401
            $errors[] = [
402
                'prio' => 3,
403
                'name' => 'Wikidata',
404
                'notice' => "Description for language <em>$lang</em> is missing", // FIXME: i18n
405
                'explanation' => "See: <a target='_blank' " .
406
                    "href='//www.wikidata.org/wiki/Help:Description'>Help:Description</a>",
407
            ];
408
        }
409
410
        return $errors;
411
    }
412
413
    /**
414
     * Get Wikidata and CheckWiki errors, if present
415
     * @return string[] List of errors in the format:
416
     *    [[
417
     *         'prio' => int,
418
     *         'name' => string,
419
     *         'notice' => string (HTML),
420
     *         'explanation' => string (HTML)
421
     *     ], ... ]
422
     */
423
    public function getErrors()
424
    {
425
        // Includes label and description
426
        $wikidataErrors = $this->getWikidataErrors();
427
428
        $checkWikiErrors = $this->getCheckWikiErrors();
429
430
        return array_merge($wikidataErrors, $checkWikiErrors);
431
    }
432
433
    /**
434
     * Get all wikidata items for the page, not just languages of sister projects
435
     * @return int Number of records.
436
     */
437
    public function getWikidataItems()
438
    {
439
        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...
440
    }
441
442
    /**
443
     * Count wikidata items for the page, not just languages of sister projects
444
     * @return int Number of records.
445
     */
446
    public function countWikidataItems()
447
    {
448
        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...
449
    }
450
451
    /**
452
     * Get number of in and outgoing links and redirects to this page.
453
     * @return string[] Counts with the keys 'links_ext_count', 'links_out_count',
454
     *                  'links_in_count' and 'redirects_count'
455
     */
456
    public function countLinksAndRedirects()
457
    {
458
        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...
459
    }
460
461
    /**
462
     * Get the sum of pageviews for the given page and timeframe.
463
     * @param string|DateTime $start In the format YYYYMMDD
464
     * @param string|DateTime $end In the format YYYYMMDD
465
     * @return string[]
466
     */
467
    public function getPageviews($start, $end)
468
    {
469
        try {
470
            $pageviews = $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...
471
        } catch (\GuzzleHttp\Exception\ClientException $e) {
472
            // 404 means zero pageviews
473
            return 0;
474
        }
475
476
        return array_sum(array_map(function ($item) {
477
            return (int) $item['views'];
478
        }, $pageviews['items']));
479
    }
480
481
    /**
482
     * Get the sum of pageviews over the last N days
483
     * @param int [$days] Default 30
484
     * @return int Number of pageviews
485
     */
486
    public function getLastPageviews($days = 30)
487
    {
488
        $start = date('Ymd', strtotime("-$days days"));
489
        $end = date('Ymd');
490
        return $this->getPageviews($start, $end);
491
    }
492
}
493