Completed
Push — master ( ac3fb6...2d8d7f )
by
unknown
02:49
created

User   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 17
lcom 1
cbo 2
dl 0
loc 155
rs 10
c 3
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getUsername() 0 4 1
A getCacheKey() 0 4 1
A getId() 0 4 1
A getGroups() 0 7 1
A getGlobalGroups() 0 4 1
A existsOnProject() 0 5 1
A isAdmin() 0 4 1
A isAnon() 0 4 1
A getBlockExpiry() 0 21 4
A isBlocked() 0 4 1
A isCurrentlyLoggedIn() 0 9 3
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 string 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(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 a list of this user's groups on the given project.
67
     * @param Project $project The project.
68
     * @return string[]
69
     */
70
    public function getGroups(Project $project)
71
    {
72
        $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...
73
        $groups = preg_grep('/\*/', $groupsData, PREG_GREP_INVERT);
74
        sort($groups);
75
        return $groups;
76
    }
77
78
    /**
79
     * Get a list of this user's groups on all projects.
80
     * @param Project $project A project to query; if not provided, the default will be used.
81
     */
82
    public function getGlobalGroups(Project $project = null)
83
    {
84
        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...
85
    }
86
87
    /**
88
     * Does this user exist on the given project.
89
     * @param Project $project
90
     * @return bool
91
     */
92
    public function existsOnProject(Project $project)
93
    {
94
        $id = $this->getId($project);
95
        return $id > 0;
96
    }
97
98
    /**
99
     * Is this user an Administrator on the given project?
100
     * @param Project $project The project.
101
     * @return bool
102
     */
103
    public function isAdmin(Project $project)
104
    {
105
        return (false !== array_search('sysop', $this->getGroups($project)));
106
    }
107
108
    /**
109
     * Is this user an anonymous user (IP)?
110
     * @return bool
111
     */
112
    public function isAnon()
113
    {
114
        return filter_var($this->username, FILTER_VALIDATE_IP);
115
    }
116
117
    /**
118
     * Get the expiry of the current block on the user
119
     * @param Project $project The project.
120
     * @return DateTime|bool Expiry as DateTime, true if indefinite,
121
     *                       or false if they are not blocked.
122
     */
123
    public function getBlockExpiry(Project $project)
124
    {
125
        if (isset($this->blockExpiry)) {
126
            return $this->blockExpiry;
127
        }
128
129
        $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...
130
            $project->getDatabaseName(),
131
            $this->getId($project)
132
        );
133
134
        if ($expiry === 'infinity') {
135
            $this->blockExpiry = true;
0 ignored issues
show
Documentation Bug introduced by
The property $blockExpiry was declared of type string, but true is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
136
        } elseif ($expiry === false) {
137
            $this->blockExpiry = false;
0 ignored issues
show
Documentation Bug introduced by
The property $blockExpiry was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
138
        } else {
139
            $this->blockExpiry = new DateTime($expiry);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \DateTime($expiry) of type object<DateTime> is incompatible with the declared type string of property $blockExpiry.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
140
        }
141
142
        return $this->blockExpiry;
143
    }
144
145
    /**
146
     * Is this user currently blocked on the given project?
147
     * @param Project $project The project.
148
     * @return bool
149
     */
150
    public function isBlocked(Project $project)
151
    {
152
        return $this->getBlockExpiry($project) !== false;
153
    }
154
155
    /**
156
     * Is this user the same as the current XTools user?
157
     * @return bool
158
     */
159
    public function isCurrentlyLoggedIn()
160
    {
161
        try {
162
            $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...
163
        } catch (Exception $exception) {
164
            return false;
165
        }
166
        return isset($ident->username) && $ident->username === $this->getUsername();
167
    }
168
}
169