User   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 208
Duplicated Lines 2.88 %

Coupling/Cohesion

Components 0
Dependencies 4

Importance

Changes 0
Metric Value
dl 6
loc 208
rs 10
c 0
b 0
f 0
wmc 27
lcom 0
cbo 4

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A save() 0 8 2
A passwordEncode() 0 3 1
A login() 0 18 4
A updateLoginTime() 0 6 1
A checkLoggedIn() 0 8 2
B checkAccountAccess() 0 20 6
A logout() 0 6 1
A getAvatarPath() 3 10 4
A updateAvatar() 3 23 5

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace db\access;
4
5
use Asymptix\core\Tools;
6
use Asymptix\db\DBCore;
7
8
/**
9
 * Simple User bean class.
10
 * (You can modify this class according to your database structure)
11
 *
12
 * @category Asymptix PHP Framework
13
 * @author Dmytro Zarezenko <[email protected]>
14
 * @copyright (c) 2009 - 2017, Dmytro Zarezenko
15
 * @license http://opensource.org/licenses/MIT
16
 */
17
class User extends \Asymptix\db\DBTimedObject {
18
    const STATUS_ACTIVATED = 1;
19
    const STATUS_DEACTIVATED = 0;
20
21
    const LOGIN_NOT_ACTIVATED = 0;
22
    const LOGIN_INVALID_USERNAME = -1;
23
    const LOGIN_INVALID_PASSWORD = -2;
24
25
    const GENDER_NONE = 'none';
26
    const GENDER_MALE = 'male';
27
    const GENDER_FEMALE = 'female';
28
29
    const TABLE_NAME = "users";
30
    const ID_FIELD_NAME = "user_id";
31
    protected $fieldsList = array(
32
        'user_id' => 0, // int(10) unsigned NOT NULL AUTO_INCREMENT
33
        'username' => "", // varchar(255) NOT NULL
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
34
        'email' => "", // varchar(255) NOT NULL
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
35
        'password' => "", // varchar(255) NOT NULL
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
36
        'auth_key' => "", // varchar(32) NOT NULL
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
37
        'role' => 0, // int(1) unsigned not null
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
38
39
        'full_name' => "", // VARCHAR(255) NOT NULL
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
40
        'gender' => self::GENDER_NONE, // ENUM( 'male', 'female', 'none', '' ) NOT NULL DEFAULT 'none'
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
41
        'language' => "en", // VARCHAR(2) NOT NULL DEFAULT 'en'
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
42
43
        'last_login_time' => "0000-00-00 00:00:00", // datetime DEFAULT NULL
44
        'create_time' => "0000-00-00 00:00:00", // datetime DEFAULT NULL
45
        'create_user_id' => 0, // int(11) DEFAULT NULL
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
46
        'update_time' => "0000-00-00 00:00:00", // datetime DEFAULT NULL
47
        'update_user_id' => 0, // int(11) DEFAULT NULL
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
48
49
        'activation' => 0, // tinyint(1) NOT NULL DEFAULT '0'
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
50
51
        'signature' => "", // TEXT NOT NULL
52
        'avatar' => "", // varchar(100) not null
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
53
54
        // additional fields according to your database structure
55
    );
56
57
    public function __construct() {
58
        parent::__construct();
59
    }
60
61
    /**
62
     * For user accounts we must verify if login field ID is unique.
63
     */
64
    public function save($debug = false) {
65
        try {
66
            return parent::save($debug);
67
        } catch (DBException $ex) {
0 ignored issues
show
Bug introduced by
The class db\access\DBException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
68
            print($ex->getMessage());
69
            return false; //TODO: maybe verify if duplicate or other error
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type of the parent method Asymptix\db\DBTimedObject::save of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
70
        }
71
    }
72
73
    /**
74
     * Password encoding method.
75
     *
76
     * @param string $password Password
77
     * @return string Encoded password string.
78
     */
79
    public static function passwordEncode($password) {
80
        return md5($password);
81
    }
82
83
    /**
84
     * Login functionality.
85
     *
86
     * @param string $login Username from login form.
87
     * @param string $password Password from login form.
88
     *
89
     * @return mixed User object on success or integer result code if some problems occurred.
90
     */
91
    public static function login($login, $password) {
92
        $user = self::_select(['email' => $login])->limit(1)->go();
93
        if ($user) {
94
            if ($user->isActivated()) {
95
                if ($user->password == self::passwordEncode($password)) {
96
                    $user->updateLoginTime();
97
98
                    return $user;
99
                }
100
101
                return self::LOGIN_INVALID_PASSWORD;
102
            }
103
104
            return self::LOGIN_NOT_ACTIVATED;
105
        }
106
107
        return self::LOGIN_INVALID_USERNAME;
108
    }
109
110
    /**
111
     * Updates login time.
112
     */
113
    public function updateLoginTime() {
114
        $query = "UPDATE " . self::TABLE_NAME . "
115
                     SET last_login_time = NOW()
116
                   WHERE " . self::ID_FIELD_NAME . " = ?";
117
        DBCore::doUpdateQuery($query, "i", array($this->id));
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<db\access\User>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
118
    }
119
120
    /**
121
     * Checks if user is logged in.
122
     *
123
     * @global User $_USER Current user object.
124
     *
125
     * @return boolean
126
     */
127
    public static function checkLoggedIn() {
128
        global $_USER;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
129
130
        if (Tools::isInstanceOf($_USER, new self)) {
0 ignored issues
show
Documentation introduced by
new self() is of type object<db\access\User>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
131
            return true;
132
        }
133
        return false;
134
    }
135
136
    /**
137
     * Checks if account of the current user is equal to the needed account page.
138
     *
139
     * @global User $_USER Current user object.
140
     * @param array $roles Needed roles.
141
     *
142
     * @return boolean
143
     */
144
    public static function checkAccountAccess($roles = array()) {
145
        global $_USER;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
146
147
        if (self::checkLoggedIn()) {
148
            if (empty($roles)) {
149
                return true;
150
            }
151
152
            if (!is_array($roles)) {
153
                $roles = array($roles);
154
            }
155
            foreach ($roles as $role) {
156
                if ($_USER->role == $role) {
157
                    return true;
158
                }
159
            }
160
            return false;
161
        }
162
        return false;
163
    }
164
165
    /**
166
     * Logout functionality.
167
     */
168
    public static function logout() {
169
        global $_USER;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
170
171
        $_USER = null;
172
        session_unset();
173
    }
174
175
    /**
176
     * Returns current users avatar image file path.
177
     *
178
     * @param boolean $icon Return only default icon flag.
179
     *
180
     * @return string Image path.
181
     */
182
    public function getAvatarPath($icon = false) {
183
        $currentAvatarFileName = $this->avatar;
0 ignored issues
show
Documentation introduced by
The property avatar does not exist on object<db\access\User>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
184 View Code Duplication
        if (!empty($currentAvatarFileName) && file_exists(Config::DIR_AVATARS . $currentAvatarFileName)) {
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...
185
            return Config::DIR_AVATARS . $currentAvatarFileName;
186
        }
187
        if ($icon) {
188
            return "img/user_avatar.png";
189
        }
190
        return "img/placehold/100x100.png";
191
    }
192
193
    /**
194
     * Updates users avatar image file path in the DB.
195
     *
196
     * @param string $newAvatarFileName New filename.
197
     *
198
     * @return boolean Success flag.
199
     */
200
    public function updateAvatar($newAvatarFileName) {
201
        $currentAvatarFileName = $this->avatar;
0 ignored issues
show
Documentation introduced by
The property avatar does not exist on object<db\access\User>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
202 View Code Duplication
        if (!empty($currentAvatarFileName) && file_exists(Config::DIR_AVATARS . $currentAvatarFileName)) {
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...
203
            unlink(Config::DIR_AVATARS . $currentAvatarFileName);
204
        }
205
206
        if (file_exists(Config::DIR_AVATARS . $newAvatarFileName)) {
207
            $query = "UPDATE " . self::TABLE_NAME
208
                    . " SET avatar = ?"
209
                    . " WHERE " . self::ID_FIELD_NAME . " = ?";
210
            if (DBCore::doUpdateQuery($query, "si", array(
211
                $newAvatarFileName,
212
                $this->id
0 ignored issues
show
Documentation introduced by
The property id does not exist on object<db\access\User>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
213
            ))) {
214
                $this->avatar = $newAvatarFileName;
0 ignored issues
show
Documentation introduced by
The property avatar does not exist on object<db\access\User>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
215
                return true;
216
            } else {
217
                return false;
218
            }
219
        } else {
220
            return false;
221
        }
222
    }
223
224
}
225