Completed
Pull Request — master (#97)
by MusikAnimal
02:14
created

Page::getNamespace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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