Completed
Push — master ( b61081...bf01ff )
by Pavel
04:03
created

User   F

Complexity

Total Complexity 88

Size/Duplication

Total Lines 633
Duplicated Lines 6.64 %

Coupling/Cohesion

Components 4
Dependencies 5

Test Coverage

Coverage 82.66%

Importance

Changes 0
Metric Value
wmc 88
lcom 4
cbo 5
dl 42
loc 633
c 0
b 0
f 0
rs 1.4746
ccs 205
cts 248
cp 0.8266

36 Methods

Rating   Name   Duplication   Size   Complexity  
A tableName() 0 4 1
A find() 0 5 1
A findIdentity() 0 4 1
A findByToken() 0 4 2
A findIdentityByAccessToken() 0 7 2
A confirm() 0 20 3
A isConfirmed() 0 4 1
A isTokenExpired() 0 4 1
A isConfirmTokenExpired() 0 4 1
A isRestoreTokenExpired() 0 4 1
B rules() 0 39 4
A attributeLabels() 0 19 1
A getProfile() 0 4 1
A beforeSave() 0 10 4
A afterSave() 0 11 2
A transactions() 0 6 1
A getId() 0 4 1
A validateAuthKey() 0 5 3
A getAuthKey() 0 4 1
A isActive() 0 4 1
B create() 3 30 6
C register() 3 30 7
A generatePassword() 0 4 1
A generateToken() 0 8 1
A restore() 0 19 4
A isBlocked() 0 4 1
B block() 18 18 5
A unblock() 18 18 4
B changePassword() 0 18 5
A newPassword() 0 20 4
A isRestore() 0 4 1
B getStatusText() 0 23 6
A statusesList() 0 9 1
A getGenderText() 0 17 4
A gendersList() 0 7 1
A resend() 0 9 4

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like User often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use User, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * User model for the module yii2-activeuser
4
 *
5
 * @link https://github.com/inblank/yii2-activeuser
6
 * @copyright Copyright (c) 2016 Pavel Aleksandrov <[email protected]>
7
 * @license http://opensource.org/licenses/MIT
8
 */
9
namespace inblank\activeuser\models;
10
11
use inblank\activeuser\traits\CommonTrait;
12
use yii;
13
use yii\db\ActiveRecord;
14
use yii\web\IdentityInterface;
15
16
/**
17
 * This is the model class for table "{{%activeuser_users}}"
18
 *
19
 * Table fields:
20
 * @property integer $id user identifier
21
 * @property integer $status user status. STATUS_ACTIVE, STATUS_BLOCKED, STATUS_CONFIRM, STATUS_RESTORE
22
 * @property string $email user email used as login
23
 * @property string $pass_hash hash of user password
24
 * @property string $name real user name
25
 * @property int $gender user gender. User::MALE, User::FEMALE. If empty, not set
26
 * @property string $birth user birth date
27
 * @property string $avatar user avatar filename
28
 * @property string $access_token user access token for use site API
29
 * @property string $auth_key user key for access by `remember me`
30
 * @property string $token token for registration confirm or restore password
31
 * @property int $token_created_at the time when the token was created
32
 * @property string $registered_at user registration date
33
 *
34
 * By getters:
35
 * @property $statusText user status as text
36
 * @property $genderText user gender as text
37
 *
38
 * Relations:
39
 * @property Profile $profile user profile data
40
 */
41
class User extends ActiveRecord implements yii\web\IdentityInterface
42
{
43
    use CommonTrait;
44
45
    /** Male gender */
46
    const MALE = 1;
47
    /** Female gender */
48
    const FEMALE = 2;
49
50
    /** Active user status */
51
    const STATUS_ACTIVE = 1;
52
    /** Blocked user status */
53
    const STATUS_BLOCKED = 2;
54
    /** User await email confirmation status */
55
    const STATUS_CONFIRM = 3;
56
    /** User await access restore status */
57
    const STATUS_RESTORE = 4;
58
59
    // TODO where store password between data enter and confirmation email? Password must be send with congratulation email
60
    /** @var string password field on registration on restore */
61
    public $password;
62
63
    /**
64
     * @inheritdoc
65
     */
66 15
    public static function tableName()
67
    {
68 15
        return '{{%activeuser_users}}';
69
    }
70
71
    /**
72
     * @inheritdoc
73
     * @return UserQuery the active query used by this AR class.
74
     */
75 14
    public static function find()
76
    {
77 14
        $queryClass = self::di('UserQuery');
78 14
        return new $queryClass(get_called_class());
79
    }
80
81
    /**
82
     * Finds an identity by the given ID.
83
     * @param string|integer $id the ID to be looked for
84
     * @return IdentityInterface the identity object that matches the given ID.
85
     * Null should be returned if such an identity cannot be found
86
     * or the identity is not in an active state (disabled, deleted, etc.)
87
     */
88 2
    public static function findIdentity($id)
89
    {
90 2
        return static::findOne($id);
91
    }
92
93
    /**
94
     * Find user by token
95
     * @param string $token for search
96
     * @return null|static
97
     */
98 3
    public static function findByToken($token)
99
    {
100 3
        return empty($token) ? null : static::findOne(['token' => $token]);
101
    }
102
103
    /**
104
     * Finds an identity by the given token.
105
     * @param mixed $token the token to be looked for
106
     * @param mixed $type the type of the token. The value of this parameter depends on the implementation.
107
     * For example, [[\yii\filters\auth\HttpBearerAuth]] will set this parameter to be `yii\filters\auth\HttpBearerAuth`.
108
     * @return IdentityInterface the identity object that matches the given token.
109
     * Null should be returned if such an identity cannot be found
110
     * or the identity is not in an active state (disabled, deleted, etc.)
111
     */
112 1
    public static function findIdentityByAccessToken($token, $type = null)
113
    {
114 1
        if (empty($token)) {
115 1
            return null;
116
        }
117 1
        return static::findOne(['access_token' => $token, 'status' => self::STATUS_ACTIVE]);
118
    }
119
120
    /**
121
     * Confirm user registration
122
     * @return bool return false if cannot confirm.
123
     * If in errors list has key `error`, that user already confirmed
124
     * If in errors list has key `token`, that confirm token was expired
125
     */
126 3
    public function confirm()
127
    {
128 3
        if ($this->isConfirmed()) {
129 1
            $this->addError('error', Yii::t('activeuser_general', 'User already confirmed'));
130 1
            return false;
131
        }
132 3
        if ($this->isConfirmTokenExpired()) {
133 1
            $this->addError('token', Yii::t('activeuser_general', 'You token was expired'));
134 1
            return false;
135
        }
136 3
        $this->updateAttributes([
137 3
            'token' => null,
138 3
            'token_created_at' => 0,
139 3
            'status' => self::STATUS_ACTIVE,
140
        ]);
141 3
        $this->module->sendMessage('register', [
142 3
            'user' => $this,
143
        ]);
144 3
        return true;
145
    }
146
147
    /**
148
     * Check that user was confirmed
149
     * @return bool
150
     */
151 6
    public function isConfirmed()
152
    {
153 6
        return $this->status !== self::STATUS_CONFIRM;
154
    }
155
156
    /**
157
     * Checks that the token was expired
158
     * @param int $timeToExpire time
159
     * @return bool
160
     */
161 3
    public function isTokenExpired($timeToExpire)
162
    {
163 3
        return $this->token_created_at + $timeToExpire < time();
164
    }
165
166
    /**
167
     * Checks that the confirmation token was expired
168
     * @return bool
169
     */
170 3
    public function isConfirmTokenExpired()
171
    {
172 3
        return $this->isTokenExpired($this->getModule()->confirmationTime);
173
    }
174
175
    /**
176
     * Checks that the restore token was expired
177
     * @return bool
178
     */
179 1
    public function isRestoreTokenExpired()
180
    {
181 1
        return $this->isTokenExpired($this->getModule()->restoreTime);
182
    }
183
184
    /**
185
     * @inheritdoc
186
     */
187 10
    public function rules()
188
    {
189
        // todo check password length
190
        return [
191 10
            [['email', 'password'], 'required'],
192
            ['email', 'unique'],
193
            ['email', 'string', 'max' => 200],
194
            ['email', 'email'],
195
            ['name', 'string', 'max' => 200],
196
            ['name', function () {
197 10
                if (in_array('name', $this->module->registrationFields) && empty($this->name)) {
198 2
                    $this->addError('name', Yii::t('activeuser_general', 'Name cannot be blank.'));
199
                }
200 10
            }, 'skipOnEmpty' => false],
201 10
            ['status', 'in', 'range' => [
202 10
                self::STATUS_ACTIVE,
203 10
                self::STATUS_BLOCKED,
204 10
                self::STATUS_CONFIRM,
205 10
                self::STATUS_RESTORE,
206
            ]],
207
            ['status', 'default', 'value' => function () {
208 9
                return $this->module->enableConfirmation ? self::STATUS_CONFIRM : self::STATUS_ACTIVE;
209 10
            }],
210 10
            ['gender', 'in', 'range' => [
211 10
                0,
212 10
                self::MALE,
213 10
                self::FEMALE,
214
            ]],
215
            ['birth', 'date', 'format' => 'php:Y-m-d', 'skipOnEmpty' => true,],
216
            ['token_created_at', 'integer'],
217
            ['registered_at', 'date', 'format' => 'php:Y-m-d H:i:s'],
218 10
            ['registered_at', 'default', 'value' => function () {
219 10
                return date('Y-m-d H:i:s');
220 10
            }],
221
            // rules below for prevent nullable value of attributes
222
            [['name', 'avatar'], 'default', 'value' => ''],
223
            [['gender', 'token_created_at'], 'default', 'value' => 0],
224
        ];
225
    }
226
227
    /**
228
     * @inheritdoc
229
     */
230 1
    public function attributeLabels()
231
    {
232
        return [
233 1
            'id' => 'ID',
234 1
            'status' => Yii::t('activeuser_general', 'Status'),
235 1
            'email' => Yii::t('activeuser_general', 'Email'),
236 1
            'password' => Yii::t('activeuser_general', 'Password'),
237 1
            'pass_hash' => Yii::t('activeuser_general', 'Password hash'),
238 1
            'name' => Yii::t('activeuser_general', 'Name'),
239 1
            'gender' => Yii::t('activeuser_general', 'Gender'),
240 1
            'birth' => Yii::t('activeuser_general', 'Birth'),
241 1
            'avatar' => Yii::t('activeuser_general', 'Avatar'),
242 1
            'access_token' => Yii::t('activeuser_general', 'Access token'),
243 1
            'auth_key' => Yii::t('activeuser_general', 'Auth key'),
244 1
            'token' => Yii::t('activeuser_general', 'Token'),
245 1
            'token_created_at' => Yii::t('activeuser_general', 'Token created'),
246 1
            'registered_at' => Yii::t('activeuser_general', 'Registered'),
247
        ];
248
    }
249
250
    /**
251
     * Get user profile
252
     * @return yii\db\ActiveQuery
253
     */
254 1
    public function getProfile()
255
    {
256 1
        return $this->hasOne(self::di('Profile'), ['user_id' => 'id']);
257
    }
258
259
    /**
260
     * @inheritdoc
261
     */
262 10
    public function beforeSave($insert)
263
    {
264 10
        if (!parent::beforeSave($insert)) {
265 1
            return false;
266
        }
267 9
        if ($this->getIsNewRecord() && !empty($this->password)) {
268 9
            $this->pass_hash = Yii::$app->getSecurity()->generatePasswordHash($this->password);
269
        }
270 9
        return true;
271
    }
272
273
    /**
274
     * @inheritdoc
275
     */
276 9
    public function afterSave($insert, $changedAttributes)
277
    {
278 9
        if ($insert) {
279
            // add profile for new user
280 9
            Yii::createObject([
281 9
                'class' => self::di('Profile'),
282 9
                'user_id' => $this->id,
283 9
            ])->save();
284
        }
285 9
        parent::afterSave($insert, $changedAttributes);
286 9
    }
287
288
    /**
289
     * @inheritdoc
290
     */
291 10
    public function transactions()
292
    {
293
        return [
294 10
            self::SCENARIO_DEFAULT => self::OP_INSERT,
295
        ];
296
    }
297
298
    /**
299
     * Returns an ID that can uniquely identify a user identity.
300
     * @return string|integer an ID that uniquely identifies a user identity.
301
     */
302 2
    public function getId()
303
    {
304 2
        return $this->id;
305
    }
306
307
    /**
308
     * Validates the given auth key.
309
     *
310
     * This is required if [[User::enableAutoLogin]] is enabled.
311
     * @param string $authKey the given auth key
312
     * @return boolean whether the given auth key is valid.
313
     * @see getAuthKey()
314
     */
315 1
    public function validateAuthKey($authKey)
316
    {
317 1
        $userAuthKey = $this->getAuthKey();
318 1
        return $this->status === self::STATUS_ACTIVE && !empty($userAuthKey) && $userAuthKey === $authKey;
319
    }
320
321
    /**
322
     * Returns a key that can be used to check the validity of a given identity ID.
323
     *
324
     * The key should be unique for each individual user, and should be persistent
325
     * so that it can be used to check the validity of the user identity.
326
     *
327
     * The space of such keys should be big enough to defeat potential identity attacks.
328
     *
329
     * This is required if [[User::enableAutoLogin]] is enabled.
330
     * @return string a key that is used to check the validity of a given identity ID.
331
     * @see validateAuthKey()
332
     */
333 1
    public function getAuthKey()
334
    {
335 1
        return $this->auth_key;
336
    }
337
338
    /**
339
     * Check active user
340
     * @return bool
341
     */
342 2
    public function isActive()
343
    {
344 2
        return $this->status === self::STATUS_ACTIVE;
345
    }
346
347
    /**
348
     * User creation
349
     * For create the user you always must set attributes `email`, `password` and `name`
350
     * @param bool $sendEmail whether to send email about registration
351
     * @return bool
352
     */
353 1
    public function create($sendEmail = false)
354
    {
355 1
        $oldRegisterFields = $this->module->registrationFields;
356 1
        $this->module->registrationFields = ['password', 'name'];
357 1 View Code Duplication
        if ($this->getIsNewRecord() == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
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...
358 1
            throw new \RuntimeException('Calling "' . __CLASS__ . '::' . __METHOD__ . '" on existing user');
359
        }
360 1
        $generatedPassword = false;
361 1
        if (empty($this->password)) {
362
            // password autogenerate
363 1
            $generatedPassword = true;
364 1
            $this->password = $this->generatePassword();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->generatePassword() can also be of type array. However, the property $password is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
365
        }
366 1
        $this->status = self::STATUS_ACTIVE;
367 1
        $isCreated = $this->save();
368 1
        $this->module->registrationFields = $oldRegisterFields;
369 1
        if (!$isCreated) {
370 1
            if ($generatedPassword) {
371
                $this->password = null;
372
            }
373 1
            return false;
374
        }
375 1
        if ($sendEmail) {
376
            // send email with registration congratulation
377 1
            $this->module->sendMessage('register', [
378 1
                'user' => $this,
379
            ]);
380
        }
381 1
        return true;
382
    }
383
384
    /**
385
     * User registration
386
     * @return bool
387
     */
388 8
    public function register()
389
    {
390 8 View Code Duplication
        if ($this->getIsNewRecord() == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
391 1
            throw new \RuntimeException('Calling "' . __CLASS__ . '::' . __METHOD__ . '" on existing user');
392
        }
393 8
        if (!$this->module->enableRegistration) {
394 1
            $this->addError('registration', Yii::t('activeuser_general', 'Registration is not available'));
395 1
            return false;
396
        }
397 8
        if (!in_array('password', $this->module->registrationFields)) {
398
            // password autogenerate
399 6
            $this->password = $this->generatePassword();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->generatePassword() can also be of type array. However, the property $password is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
400
        }
401 8
        if (!$this->save()) {
402 1
            return false;
403
        }
404 8
        if ($this->module->enableConfirmation) {
405 8
            $this->generateToken();
406
            // send email with confirm link
407 8
            $this->module->sendMessage('confirm', [
408 8
                'user' => $this,
409
            ]);
410 1
        } elseif ($this->module->enableRegistrationEmail) {
411
            // send email with registration congratulation
412 1
            $this->module->sendMessage('register', [
413 1
                'user' => $this,
414
            ]);
415
        }
416 8
        return true;
417
    }
418
419
    /**
420
     * Password generator
421
     * @return mixed
422
     */
423 7
    public function generatePassword()
424
    {
425 7
        return (new \PWGen())->generate();
426
    }
427
428
    /**
429
     * Generate special hash
430
     * @return string
431
     */
432 8
    public function generateToken()
433
    {
434 8
        $this->updateAttributes([
435 8
            'token' => Yii::$app->security->generateRandomString(40),
436 8
            'token_created_at' => time(),
437
        ]);
438 8
        return $this->token;
439
    }
440
441
    /**
442
     * Start password restore procedure
443
     * @return bool
444
     */
445 1
    public function restore()
446
    {
447 1
        if (!$this->module->enablePasswordRestore) {
448 1
            $this->addError('error', Yii::t('activeuser_general', 'Password restore by email is disabled'));
449 1
            return false;
450
        }
451 1
        if ($this->isBlocked() || !$this->isConfirmed()) {
452 1
            $this->addError('error', Yii::t('activeuser_general', 'You cannot start restore procedure'));
453 1
            return false;
454
        }
455 1
        $this->generateToken();
456 1
        $this->updateAttributes([
457 1
            'status' => self::STATUS_RESTORE,
458
        ]);
459 1
        $this->module->sendMessage('restore', [
460 1
            'user' => $this,
461
        ]);
462 1
        return true;
463
    }
464
465
    /**
466
     * Check blocked user
467
     * @return bool
468
     */
469 4
    public function isBlocked()
470
    {
471 4
        return $this->status === self::STATUS_BLOCKED;
472
    }
473
474
    /**
475
     * Block user
476
     * @param bool $sendMail whether to send confirmation email about blocking.
477
     * If null, use global setting Module::$enableBlockingEmail
478
     * @return bool return false if user already blocked or not confirmed
479
     */
480 1 View Code Duplication
    public function block($sendMail = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
481
    {
482 1
        if ($this->isBlocked() || !$this->isConfirmed()) {
483 1
            return false;
484
        }
485 1
        if ($sendMail === null) {
486 1
            $sendMail = $this->module->enableBlockingEmail;
487
        }
488 1
        $this->updateAttributes([
489 1
            'status' => self::STATUS_BLOCKED
490
        ]);
491 1
        if ($sendMail) {
492 1
            $this->module->sendMessage('block', [
493 1
                'user' => $this,
494
            ]);
495
        }
496 1
        return true;
497
    }
498
499
    /**
500
     * Unblock user
501
     * @param bool $sendMail whether to send confirmation email about unblocking.
502
     * If null, use global setting Module::$enableUnblockingEmail
503
     * @return bool return false if user not blocked
504
     */
505 1 View Code Duplication
    public function unblock($sendMail = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
506
    {
507 1
        if (!$this->isBlocked()) {
508 1
            return false;
509
        }
510 1
        if ($sendMail === null) {
511 1
            $sendMail = $this->module->enableBlockingEmail;
512
        }
513 1
        $this->updateAttributes([
514 1
            'status' => self::STATUS_ACTIVE
515
        ]);
516 1
        if ($sendMail) {
517 1
            $this->module->sendMessage('unblock', [
518 1
                'user' => $this,
519
            ]);
520
        }
521 1
        return true;
522
    }
523
524
    /**
525
     * Change the user password
526
     * @return bool
527
     */
528 1
    public function changePassword()
529
    {
530 1
        if (!$this->isRestore()) {
531 1
            $this->addError('error', Yii::t('activeuser_general', 'User not request restore procedure'));
532 1
            return false;
533
        }
534 1
        if ($this->isRestoreTokenExpired()) {
535 1
            $this->addError('token', Yii::t('activeuser_general', 'You token was expired'));
536 1
            return false;
537
        }
538 1
        if (empty($this->password) && !$this->module->generatePassOnRestore) {
539 1
            $this->addError('password', Yii::t('activeuser_general', 'Password cannot be blank'));
540 1
            return false;
541
        }
542 1
        $this->status = self::STATUS_ACTIVE;
543 1
        $this->newPassword();
544 1
        return true;
545
    }
546
547
    /**
548
     * Set new password
549
     * @param bool $sendEmail whether to send email about password change
550
     * @throws yii\base\Exception
551
     * @throws yii\base\InvalidConfigException
552
     */
553 1
    public function newPassword($sendEmail = null)
554
    {
555 1
        if (empty($this->password)) {
556 1
            $this->password = $this->generatePassword();
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->generatePassword() can also be of type array. However, the property $password is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
557
        }
558 1
        $this->updateAttributes([
559 1
            'pass_hash' => Yii::$app->getSecurity()->generatePasswordHash($this->password),
560
            'token' => null,
561 1
            'token_created_at' => 0,
562 1
            'status' => $this->status,
563
        ]);
564 1
        if ($sendEmail === null) {
565 1
            $sendEmail = $this->getModule()->enableNewPasswordEmail;
566
        }
567 1
        if ($sendEmail) {
568 1
            $this->module->sendMessage('passchanged', [
569 1
                'user' => $this,
570
            ]);
571
        }
572 1
    }
573
574
    /**
575
     * Check that user request restore
576
     * @return bool
577
     */
578 2
    public function isRestore()
579
    {
580 2
        return $this->status === self::STATUS_RESTORE;
581
    }
582
583
    /**
584
     * Get user status as text
585
     * @param string $status status value. If not set, get from model
586
     * @return null|string
587
     */
588
    public function getStatusText($status = null)
589
    {
590
        if ($status === null) {
591
            $status = $this->status;
592
        }
593
        switch ($status) {
594
            case self::STATUS_ACTIVE:
595
                $statusText = 'Active';
596
                break;
597
            case self::STATUS_BLOCKED:
598
                $statusText = 'Blocked';
599
                break;
600
            case self::STATUS_CONFIRM:
601
                $statusText = 'Confirm';
602
                break;
603
            case self::STATUS_RESTORE:
604
                $statusText = 'Restore';
605
                break;
606
            default:
607
                return null;
608
        }
609
        return Yii::t('activeuser_general', $statusText);
610
    }
611
612
    /**
613
     * Get status list as array
614
     * @return array
615
     */
616
    public function statusesList()
617
    {
618
        return [
619
            self::STATUS_ACTIVE => $this->getStatusText(self::STATUS_ACTIVE),
620
            self::STATUS_BLOCKED => $this->getStatusText(self::STATUS_BLOCKED),
621
            self::STATUS_CONFIRM => $this->getStatusText(self::STATUS_CONFIRM),
622
            self::STATUS_RESTORE => $this->getStatusText(self::STATUS_RESTORE),
623
        ];
624
    }
625
626
    /**
627
     * Get user gender as text
628
     * @param int $gender gender value. If not set, get from model
629
     * @return null|string
630
     */
631
    public function getGenderText($gender = null)
632
    {
633
        if ($gender === null) {
634
            $gender = $this->gender;
635
        }
636
        switch ($gender) {
637
            case self::MALE:
638
                $genderText = 'Men';
639
                break;
640
            case self::FEMALE:
641
                $genderText = 'Women';
642
                break;
643
            default:
644
                return null;
645
        }
646
        return Yii::t('activeuser_general', $genderText);
647
    }
648
649
    /**
650
     * Get gender list as array
651
     * @return array
652
     */
653
    public function gendersList()
654
    {
655
        return [
656
            self::MALE => $this->getGenderText(self::MALE),
657
            self::FEMALE => $this->getGenderText(self::FEMALE),
658
        ];
659
    }
660
661
    /**
662
     * Resend confirmation message
663
     */
664
    public function resend()
665
    {
666
        if ($this->getModule()->enableConfirmation && !$this->getIsNewRecord() && $this->status === self::STATUS_CONFIRM) {
667
            $this->generateToken();
668
            $this->getModule()->sendMessage('confirm', [
669
                'user' => $this,
670
            ]);
671
        }
672
    }
673
}
674