Completed
Push — master ( dc7aae...2784d5 )
by MusikAnimal
02:59
created

User::countEdits()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 4
1
<?php
2
/**
3
 * This file contains only the User class.
4
 */
5
6
namespace Xtools;
7
8
use Exception;
9
use DateTime;
10
11
/**
12
 * A User is a wiki user who has the same username across all projects in an XTools installation.
13
 */
14
class User extends Model
15
{
16
17
    /** @var int The user's ID. */
18
    protected $id;
19
20
    /** @var string The user's username. */
21
    protected $username;
22
23
    /** @var DateTime|bool Expiry of the current block of the user */
24
    protected $blockExpiry;
25
26
    /**
27
     * Create a new User given a username.
28
     * @param string $username
29
     */
30
    public function __construct($username)
31
    {
32
        $this->username = ucfirst(str_replace('_', ' ', trim($username)));
33
    }
34
35
    /**
36
     * Get the username.
37
     * @return string
38
     */
39
    public function getUsername()
40
    {
41
        return $this->username;
42
    }
43
44
    /**
45
     * Get a md5 hash of the username to be used as a cache key.
46
     * This ensures the cache key does not contain reserved characters.
47
     * You could also use the ID, but that may require an unnecessary DB query.
48
     * @return string
49
     */
50
    public function getCacheKey()
51
    {
52
        return md5($this->username);
53
    }
54
55
    /**
56
     * Get the user's ID on the given project.
57
     * @param Project $project
58
     * @return int
59
     */
60
    public function getId(Project $project)
61
    {
62
        return $this->getRepository()->getId($project->getDatabaseName(), $this->getUsername());
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 getId() does only exist in the following sub-classes of Xtools\Repository: Xtools\UserRepository. 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...
63
    }
64
65
    /**
66
     * Get the user's registration date on the given project.
67
     * @param Project $project
68
     * @return DateTime|false False if no registration date was found.
69
     */
70
    public function getRegistrationDate(Project $project)
71
    {
72
        $registrationDate = $this->getRepository()->getRegistrationDate(
73
            $project->getDatabaseName(),
74
            $this->getUsername()
75
        );
76
77
        return DateTime::createFromFormat('YmdHis', $registrationDate);
78
    }
79
80
    /**
81
     * Get a list of this user's groups on the given project.
82
     * @param Project $project The project.
83
     * @return string[]
84
     */
85
    public function getGroups(Project $project)
86
    {
87
        $groupsData = $this->getRepository()->getGroups($project, $this->getUsername());
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 getGroups() does only exist in the following sub-classes of Xtools\Repository: Xtools\UserRepository. 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...
88
        $groups = preg_grep('/\*/', $groupsData, PREG_GREP_INVERT);
89
        sort($groups);
90
        return $groups;
91
    }
92
93
    /**
94
     * Get a list of this user's groups on all projects.
95
     * @param Project $project A project to query; if not provided, the default will be used.
96
     */
97
    public function getGlobalGroups(Project $project = null)
98
    {
99
        return $this->getRepository()->getGlobalGroups($this->getUsername(), $project);
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Xtools\Repository as the method getGlobalGroups() does only exist in the following sub-classes of Xtools\Repository: Xtools\UserRepository. 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...
100
    }
101
102
    /**
103
     * Does this user exist on the given project.
104
     * @param Project $project
105
     * @return bool
106
     */
107
    public function existsOnProject(Project $project)
108
    {
109
        $id = $this->getId($project);
110
        return $id > 0;
111
    }
112
113
    /**
114
     * Is this user an Administrator on the given project?
115
     * @param Project $project The project.
116
     * @return bool
117
     */
118
    public function isAdmin(Project $project)
119
    {
120
        return (false !== array_search('sysop', $this->getGroups($project)));
121
    }
122
123
    /**
124
     * Is this user an anonymous user (IP)?
125
     * @return bool
126
     */
127
    public function isAnon()
128
    {
129
        return filter_var($this->username, FILTER_VALIDATE_IP);
130
    }
131
132
    /**
133
     * Get the expiry of the current block on the user
134
     * @param Project $project The project.
135
     * @return DateTime|bool Expiry as DateTime, true if indefinite,
136
     *                       or false if they are not blocked.
137
     */
138
    public function getBlockExpiry(Project $project)
139
    {
140
        if (isset($this->blockExpiry)) {
141
            return $this->blockExpiry;
142
        }
143
144
        $expiry = $this->getRepository()->getBlockExpiry(
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 getBlockExpiry() does only exist in the following sub-classes of Xtools\Repository: Xtools\UserRepository. 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...
145
            $project->getDatabaseName(),
146
            $this->getId($project)
147
        );
148
149
        if ($expiry === 'infinity') {
150
            $this->blockExpiry = true;
151
        } elseif ($expiry === false) {
152
            $this->blockExpiry = false;
153
        } else {
154
            $this->blockExpiry = new DateTime($expiry);
155
        }
156
157
        return $this->blockExpiry;
158
    }
159
160
    /**
161
     * Is this user currently blocked on the given project?
162
     * @param Project $project The project.
163
     * @return bool
164
     */
165
    public function isBlocked(Project $project)
166
    {
167
        return $this->getBlockExpiry($project) !== false;
168
    }
169
170
    /**
171
     * Get edit count within given timeframe and namespace
172
     * @param Project $project
173
     * @param int|string [$namespace] Namespace ID or 'all' for all namespaces
174
     * @param string [$start] Start date in a format accepted by strtotime()
175
     * @param string [$end] End date in a format accepted by strtotime()
176
     */
177
    public function countEdits(Project $project, $namespace = 'all', $start = '', $end = '')
178
    {
179
        return $this->getRepository()->countEdits($project, $this, $namespace, $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 countEdits() does only exist in the following sub-classes of Xtools\Repository: Xtools\UserRepository. 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...
180
    }
181
182
    /**
183
     * Get the number of edits this user made using semi-automated tools.
184
     * @param Project $project
185
     * @param string|int [$namespace] Namespace ID or 'all'
186
     * @param string [$start] Start date in a format accepted by strtotime()
187
     * @param string [$end] End date in a format accepted by strtotime()
188
     * @return int Result of query, see below.
189
     */
190
    public function countAutomatedEdits(Project $project, $namespace = 'all', $start = '', $end = '')
191
    {
192
        return (int) $this->getRepository()->countAutomatedEdits($project, $this, $namespace, $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 countAutomatedEdits() does only exist in the following sub-classes of Xtools\Repository: Xtools\UserRepository. 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...
193
    }
194
195
    /**
196
     * Get non-automated contributions for this user.
197
     * @param Project $project
198
     * @param string|int [$namespace] Namespace ID or 'all'
199
     * @param string [$start] Start date in a format accepted by strtotime()
200
     * @param string [$end] End date in a format accepted by strtotime()
201
     * @param int [$offset] Used for pagination, offset results by N edits
202
     * @return array[] Result of query, with columns (string) 'page_title',
203
     *   (int) 'page_namespace', (int) 'rev_id', (DateTime) 'timestamp',
204
     *   (bool) 'minor', (int) 'length', (int) 'length_change', (string) 'comment'
205
     */
206
    public function getNonAutomatedEdits(
207
        Project $project,
208
        $namespace = 'all',
209
        $start = '',
210
        $end = '',
211
        $offset = 0
212
    ) {
213
        $revs = $this->getRepository()->getNonAutomatedEdits(
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 getNonAutomatedEdits() does only exist in the following sub-classes of Xtools\Repository: Xtools\UserRepository. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
214
            $project,
215
            $this,
216
            $namespace,
217
            $start,
218
            $end,
219
            $offset
220
        );
221
222
        $namespaces = $project->getNamespaces();
223
224
        return array_map(function ($rev) use ($namespaces) {
225
            $pageTitle = $rev['page_title'];
226
227
            if ($rev['page_namespace'] !== '0') {
228
                $pageTitle = $namespaces[$rev['page_namespace']] . ":$pageTitle";
229
            }
230
231
            return [
232
                'page_title' => $pageTitle,
233
                'page_namespace' => (int) $rev['page_namespace'],
234
                'rev_id' => (int) $rev['rev_id'],
235
                'timestamp' => DateTime::createFromFormat('YmdHis', $rev['timestamp']),
236
                'minor' => (bool) $rev['minor'],
237
                'length' => (int) $rev['length'],
238
                'length_change' => (int) $rev['length_change'],
239
                'comment' => $rev['comment'],
240
            ];
241
        }, $revs);
242
    }
243
244
    /**
245
     * Get non-automated contributions for the given user.
246
     * @param Project $project
247
     * @param string|int [$namespace] Namespace ID or 'all'
248
     * @param string [$start] Start date in a format accepted by strtotime()
249
     * @param string [$end] End date in a format accepted by strtotime()
250
     * @return string[] Each tool that they used along with the count and link:
251
     *                  [
252
     *                      'Twinkle' => [
253
     *                          'count' => 50,
254
     *                          'link' => 'Wikipedia:Twinkle',
255
     *                      ],
256
     *                  ]
257
     */
258
    public function getAutomatedCounts(Project $project, $namespace = 'all', $start = '', $end = '')
259
    {
260
        return $this->getRepository()->getAutomatedCounts($project, $this, $namespace, $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 getAutomatedCounts() does only exist in the following sub-classes of Xtools\Repository: Xtools\UserRepository. 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...
261
    }
262
263
    /**
264
     * Is this user the same as the current XTools user?
265
     * @return bool
266
     */
267
    public function isCurrentlyLoggedIn()
268
    {
269
        try {
270
            $ident = $this->getRepository()->getXtoolsUserInfo();
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 getXtoolsUserInfo() does only exist in the following sub-classes of Xtools\Repository: Xtools\UserRepository. 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...
271
        } catch (Exception $exception) {
272
            return false;
273
        }
274
        return isset($ident->username) && $ident->username === $this->getUsername();
275
    }
276
}
277