Issues (236)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

traits/UserRelationTrait.php (36 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 *  _   __ __ _____ _____ ___  ____  _____
5
 * | | / // // ___//_  _//   ||  __||_   _|
6
 * | |/ // /(__  )  / / / /| || |     | |
7
 * |___//_//____/  /_/ /_/ |_||_|     |_|
8
 * @link https://vistart.me/
9
 * @copyright Copyright (c) 2016 - 2017 vistart
10
 * @license https://vistart.me/license/
11
 */
12
13
namespace rhosocial\base\models\traits;
14
15
use rhosocial\base\models\models\BaseUserModel;
16
use rhosocial\base\models\traits\MultipleBlameableTrait as mb;
17
use yii\base\ModelEvent;
18
use yii\base\InvalidConfigException;
19
use yii\base\InvalidValueException;
20
use yii\db\Connection;
21
use yii\db\IntegrityException;
22
23
/**
24
 * Relation features.
25
 * This trait should be used in user relation model which is extended from
26
 * [[BaseBlameableModel]], and is specified `$hostClass` property. And the user
27
 * class should be extended from [[BaseUserModel]], or any other classes used
28
 * [[UserTrait]].
29
 * Notice: Several methods associated with "inserting", "updating" and "removing" may
30
 * involve more DB operations, I strongly recommend those methods to be placed in
31
 * transaction execution, in order to ensure data consistency.
32
 * If you want to use group feature, the class used [[UserRelationGroupTrait]]
33
 * must be used coordinately.
34
 * @property array $groupGuids the guid array of all groups which owned by current relation.
35
 * @property-read array $favoriteRules
36
 * @property boolean $isFavorite
37
 * @property-read static $opposite
38
 * @property-read array $otherGuidRules
39
 * @property string $remark
40
 * @property-read array $remarkRules
41
 * @property-read array $userRelationRules
42
 * @property-read mixed $group
43
 * @property-read array $groupMembers
44
 * @property array $groupGuids
45
 * @property-read array $allGroups
46
 * @property-read array $nonGroupMembers
47
 * @property-read integer $groupsCount
48
 * @property-read array $groupsRules
49
 *
50
 * @method {$this->multiBlamesClass} createGroup(BaseUserModel $user, array $config = [])
51
 * @method array|false addGroup({$this->multiBlamesClass} $blame)
52
 * @method array|false addOrCreateGroup(){$this->multiBlamesClass}|array &$blame = null, BaseUserModel $user = null)
53
 * @method array|false removeGroup({$this->multiBlamesClass}|string $blame)
54
 * @method array|false removeAllGroups()
55
 * @method {$this->multiBlamesClass} getGroup(string $blameGuid)
56
 * @method {$this->multiBlamesClass} getOrCreateGroup(string $blameGuid, BaseUserModel $user = null))
57
 * @method array getGroupMembers({$this->multiBlamesClass} $blame) Get all members that belongs to
58
 * @method array getGroupGuids(bool $checkValid = false)
59
 * @method array|false setGroupGuids(array $guids = [], bool $checkValid = false)
60
 * @method array getOwnGroups() Get all groups that owned this relation.
61
 * @method array setOwnGroups(array $blames)
62
 * @method array isGroupContained({$this->multiBlamesClass} $blame)
63
 * @method array getAllGroups() Get all groups created by whom created this relation.
64
 * @method array getNonGroupMembers(BaseUserModel $user) Get members that do not belong to any group.
65
 * @method integer getGroupsCount() Get the count of groups of this relation.
66
 * @method array getEmptyGroups() Get the groups which does not contain any relations.
67
 * @method array getGroupsRules() Get rules associated with group attribute.
68
 *
69
 * @version 1.0
70
 * @author vistart <[email protected]>
71
 */
72
trait UserRelationTrait
73
{
74
    use mb,
75
        MutualTrait {
76
        mb::addBlame as addGroup;
77
        mb::createBlame as createGroup;
78
        mb::addOrCreateBlame as addOrCreateGroup;
79
        mb::removeBlame as removeGroup;
80
        mb::removeAllBlames as removeAllGroups;
81
        mb::getBlame as getGroup;
82
        mb::getOrCreateBlame as getOrCreateGroup;
83
        mb::getBlameds as getGroupMembers;
84
        mb::getBlameGuids as getGroupGuids;
85
        mb::setBlameGuids as setGroupGuids;
86
        mb::getOwnBlames as getOwnGroups;
87
        mb::setOwnBlames as setOwnGroups;
88
        mb::isBlameOwned as isGroupContained;
89
        mb::getAllBlames as getAllGroups;
90
        mb::getNonBlameds as getNonGroupMembers;
91
        mb::getBlamesCount as getGroupsCount;
92
        mb::getEmptyBlames as getEmptyGroups;
93
        mb::getMultipleBlameableAttributeRules as getGroupsRules;
94
    }
95
96
    /**
97
     * @var string
98
     */
99
    public $remarkAttribute = 'remark';
100
    public static $relationSingle = 0;
101
    public static $relationMutual = 1;
102
    public $relationType = 1;
103
    public static $relationTypes = [
104
        0 => 'Single',
105
        1 => 'Mutual',
106
    ];
107
108
    /**
109
     * @var string the attribute name of which determines the relation type.
110
     */
111
    public $mutualTypeAttribute = 'type';
112
    public static $mutualTypeNormal = 0x00;
113
    public static $mutualTypeSuspend = 0x01;
114
115
    /**
116
     * @var array Mutual types.
117
     */
118
    public static $mutualTypes = [
119
        0x00 => 'Normal',
120
        0x01 => 'Suspend',
121
    ];
122
123
    /**
124
     * @var string the attribute name of which determines the `favorite` field.
125
     */
126
    public $favoriteAttribute = 'favorite';
127
128
    /**
129
     * Permit to build self relation.
130
     * @var boolean
131
     */
132
    public $relationSelf = false;
133
134
    /**
135
     * Get whether this relation is favorite or not.
136
     * @return boolean
137
     */
138 1
    public function getIsFavorite()
139
    {
140 1
        $favoriteAttribute = $this->favoriteAttribute;
141 1
        return (is_string($favoriteAttribute) && !empty($favoriteAttribute)) ?
142 1
        (int) $this->$favoriteAttribute > 0 : null;
143
    }
144
145
    /**
146
     * Set favorite.
147
     * @param boolean $fav
148
     */
149 1
    public function setIsFavorite($fav)
150
    {
151 1
        $favoriteAttribute = $this->favoriteAttribute;
152 1
        return (is_string($favoriteAttribute) && !empty($favoriteAttribute)) ?
153 1
        $this->$favoriteAttribute = ($fav ? 1 : 0) : null;
154
    }
155
156
    /**
157
     * @inheritdoc
158
     */
159 46
    public function rules()
160
    {
161 46
        return array_merge(parent::rules(), $this->getUserRelationRules());
162
    }
163
164
    /**
165
     * Validation rules associated with user relation.
166
     * @return array rules.
167
     */
168 46
    public function getUserRelationRules()
169
    {
170 46
        $rules = [];
171 46
        if ($this->relationType == static::$relationMutual) {
172
            $rules = [
173 12
                [[$this->mutualTypeAttribute], 'in', 'range' => array_keys(static::$mutualTypes)],
174 12
                [[$this->mutualTypeAttribute], 'default', 'value' => static::$mutualTypeNormal],
175
            ];
176
        }
177 46
        return array_merge($rules, $this->getRemarkRules(),
178 46
            $this->getFavoriteRules(),
179 46
            $this->getGroupsRules(),
180 46
        $this->getOtherGuidRules());
181
    }
182
183
    /**
184
     * Get remark.
185
     * @return string remark.
186
     */
187 1
    public function getRemark()
188
    {
189 1
        $remarkAttribute = $this->remarkAttribute;
190 1
        return (is_string($remarkAttribute) && !empty($remarkAttribute)) ? $this->$remarkAttribute : null;
191
    }
192
193
    /**
194
     * Set remark.
195
     * @param string $remark
196
     * @return string remark.
197
     */
198 49
    public function setRemark($remark)
199
    {
200 49
        $remarkAttribute = $this->remarkAttribute;
201 49
        return (is_string($remarkAttribute) && !empty($remarkAttribute)) ? $this->$remarkAttribute = $remark : null;
202
    }
203
204
    /**
205
     * Validation rules associated with remark attribute.
206
     * @return array rules.
207
     */
208 46
    public function getRemarkRules()
209
    {
210 46
        return is_string($this->remarkAttribute) ? [
211 46
            [[$this->remarkAttribute], 'string'],
212 46
            [[$this->remarkAttribute], 'default', 'value' => ''],
213 46
            ] : [];
214
    }
215
216
    /**
217
     * Validation rules associated with favorites attribute.
218
     * @return array rules.
219
     */
220 46
    public function getFavoriteRules()
221
    {
222 46
        return is_string($this->favoriteAttribute) ? [
223 46
            [[$this->favoriteAttribute], 'boolean'],
224 46
            [[$this->favoriteAttribute], 'default', 'value' => 0],
225 46
            ] : [];
226
    }
227
228
    /**
229
     * Validation rules associated with other guid attribute.
230
     * @return array rules.
231
     */
232 46
    public function getOtherGuidRules()
233
    {
234 46
        $rules = array_merge($this->getMutualRules(), [
235 46
            [[$this->otherGuidAttribute,
236 46
                $this->createdByAttribute],
0 ignored issues
show
The property createdByAttribute does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
237 46
                'unique',
238 46
                'targetAttribute' => [$this->otherGuidAttribute, $this->createdByAttribute]],
239
        ]);
240 46
        return $rules;
241
    }
242
243
    /**
244
     * Attach events associated with user relation.
245
     */
246 49
    public function initUserRelationEvents()
247
    {
248 49
        $this->on(static::EVENT_INIT, [$this, 'onInitBlamesLimit']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
249 49
        $this->on(static::$eventNewRecordCreated, [$this, 'onInitGroups']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
250 49
        $this->on(static::$eventNewRecordCreated, [$this, 'onInitRemark']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
251 49
        $this->on(static::$eventMultipleBlamesChanged, [$this, 'onBlamesChanged']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
252 49
        $this->on(static::EVENT_AFTER_INSERT, [$this, 'onInsertRelation']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
253 49
        $this->on(static::EVENT_AFTER_UPDATE, [$this, 'onUpdateRelation']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
254 49
        $this->on(static::EVENT_AFTER_DELETE, [$this, 'onDeleteRelation']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
255 49
    }
256
257
    /**
258
     * Get opposite relation against self.
259
     * @return static
260
     */
261 1
    public function getOpposite()
262
    {
263 1
        if ($this->isNewRecord) {
0 ignored issues
show
The property isNewRecord does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
264 1
            return null;
265
        }
266 1
        return static::find()->opposite($this->initiator, $this->recipient);
267
    }
268
269
    /**
270
     * Check whether the initiator is followed by recipient.
271
     * @param BaseUserModel $initiator
272
     * @param BaseUserModel $recipient
273
     * @return boolean
274
     */
275 6
    public static function isFollowed($initiator, $recipient)
276
    {
277 6
        return static::find()->initiators($recipient)->recipients($initiator)->exists();
278
    }
279
280
    /**
281
     * Check whether the initiator is following recipient.
282
     * @param BaseUserModel $initiator
283
     * @param BaseUserModel $recipient
284
     * @return boolean
285
     */
286 6
    public static function isFollowing($initiator, $recipient)
287
    {
288 6
        return static::find()->initiators($initiator)->recipients($recipient)->exists();
289
    }
290
291
    /**
292
     * Check whether the initiator is following and followed by recipient mutually (Single Relation).
293
     * Or check whether the initiator and recipient are friend whatever the mutual type is normal or suspend.
294
     * @param BaseUserModel $initiator
295
     * @param BaseUserModel $recipient
296
     * @return boolean
297
     */
298 3
    public static function isMutual($initiator, $recipient)
299
    {
300 3
        return static::isFollowed($initiator, $recipient) && static::isFollowing($initiator, $recipient);
301
    }
302
303
    /**
304
     * Check whether the initiator is following and followed by recipient mutually (Single Relation).
305
     * Or check whether the initiator and recipient are friend if the mutual type is normal.
306
     * @param BaseUserModel $initiator
307
     * @param BaseUserModel $recipient
308
     * @return boolean
309
     */
310 6
    public static function isFriend($initiator, $recipient)
311
    {
312 6
        $query = static::find();
313 6
        $model = $query->noInitModel;
314
        /* @var $model static */
315 6
        if ($model->relationType == static::$relationSingle) {
316 2
            return static::isMutual($initiator, $recipient);
317
        }
318 4
        if ($model->relationType == static::$relationMutual) {
319 4
            $relation = static::find()->initiators($initiator)->recipients($recipient)->
320 4
                    andWhere([$model->mutualTypeAttribute => static::$mutualTypeNormal])->exists();
321 4
            $inverse = static::find()->recipients($initiator)->initiators($recipient)->
322 4
                    andWhere([$model->mutualTypeAttribute => static::$mutualTypeNormal])->exists();
323 4
            return $relation && $inverse;
324
        }
325
        return false;
326
    }
327
328
    /**
329
     * Build new or return existed suspend mutual relation, or return null if
330
     * current type is not mutual.
331
     * @see buildRelation()
332
     * @param BaseUserModel|string $user Initiator or its GUID.
333
     * @param BaseUserModel|string $other Recipient or its GUID.
334
     * @return static The relation will be
335
     * given if exists, or return a new relation.
336
     */
337 15
    public static function buildSuspendRelation($user, $other)
338
    {
339 15
        $relation = static::buildRelation($user, $other);
340 15
        if (!$relation || $relation->relationType != static::$relationMutual) {
341 1
            return null;
342
        }
343 14
        $relation->setMutualType(static::$mutualTypeSuspend);
344 14
        return $relation;
345
    }
346
347
    /**
348
     * Build new or return existed normal relation.
349
     * The status of mutual relation will be changed to normal if it is not.
350
     * @see buildRelation()
351
     * @param BaseUserModel|string $user Initiator or its GUID.
352
     * @param BaseUserModel|string $other Recipient or its GUID.
353
     * @return static The relation will be
354
     * given if exists, or return a new relation.
355
     */
356 49
    public static function buildNormalRelation($user, $other)
357
    {
358 49
        $relation = static::buildRelation($user, $other);
359 49
        if (!$relation) {
360 1
            return null;
361
        }
362 49
        if ($relation->relationType == static::$relationMutual) {
363 14
            $relation->setMutualType(static::$mutualTypeNormal);
364
        }
365 49
        return $relation;
366
    }
367
    
368
    /**
369
     * Transform relation from suspend to normal.
370
     * Note: You should ensure the relation model is not new one.
371
     * @param static $relation
0 ignored issues
show
The type UserRelationTrait for parameter $relation is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
372
     * @return boolean
373
     */
374 2
    public static function transformSuspendToNormal($relation)
375
    {
376 2
        if (!($relation instanceof static) || $relation->getIsNewRecord() ||
0 ignored issues
show
It seems like getIsNewRecord() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
377 1
                $relation->relationType != static::$relationMutual) {
378 1
            return false;
379
        }
380 1
        $new = static::buildNormalRelation($relation->initiator, $relation->recipient);
381 1
        return $new->save() && $relation->refresh();
0 ignored issues
show
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
It seems like refresh() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
382
    }
383
    
384
    /**
385
     * Revert relation from normal to suspend.
386
     * Note: You should ensure the relation model is not new one.
387
     * @param static $relation
0 ignored issues
show
The type UserRelationTrait for parameter $relation is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
388
     * @return boolean
389
     */
390 2
    public static function revertNormalToSuspend($relation)
391
    {
392 2
        if (!($relation instanceof static) || $relation->getIsNewRecord() ||
0 ignored issues
show
It seems like getIsNewRecord() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
393 1
                $relation->relationType != static::$relationMutual) {
394 1
            return false;
395
        }
396 1
        $new = static::buildSuspendRelation($relation->initiator, $relation->recipient);
397 1
        return $new->save() && $relation->refresh();
0 ignored issues
show
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
It seems like refresh() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
398
    }
399
400
    /**
401
     * Build new or return existed relation between initiator and recipient.
402
     * If relation between initiator and recipient is not found, new relation will
403
     * be built. If initiator and recipient are the same one and it is not allowed
404
     * to build self relation, null will be given.
405
     * If you want to know whether the relation exists, you can check the return
406
     * value of `getIsNewRecord()` method.
407
     * @param BaseUserModel|string $user Initiator or its GUID.
408
     * @param BaseUserModel|string $other Recipient or its GUID.
409
     * @return static The relation will be
410
     * given if exists, or return a new relation. Or return null if not allowed
411
     * to build self relation,
412
     */
413 49
    protected static function buildRelation($user, $other)
414
    {
415 49
        $relationQuery = static::find()->initiators($user)->recipients($other);
416 49
        $noInit = $relationQuery->noInitModel;
417 49
        $relation = $relationQuery->one();
418 49
        if (!$relation) {
419 49
            $hostClass = $noInit->hostClass;
420 49
            if ($user instanceof BaseUserModel) {
421 49
                $hostClass = $hostClass ? : $user->className();
0 ignored issues
show
$hostClass is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
422 49
                $user = $user->getGUID();
423
            }
424 49
            if ($other instanceof BaseUserModel) {
425 49
                $other = $other->getGUID();
426
            }
427 49
            if (!$noInit->relationSelf && $user == $other) {
428 1
                return null;
429
            }
430 49
            $relation = new static(['host' => $user, 'recipient' => $other]);
0 ignored issues
show
The call to UserRelationTrait::__construct() has too many arguments starting with array('host' => $user, 'recipient' => $other).

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
431
        }
432 49
        return $relation;
433
    }
434
435
    /**
436
     * Build opposite relation throughout the current relation. The opposite
437
     * relation will be given if existed.
438
     * @param static $relation
0 ignored issues
show
The type UserRelationTrait for parameter $relation is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
439
     * @return static
440
     */
441 12
    protected static function buildOppositeRelation($relation)
442
    {
443 12
        if (!$relation) {
444
            return null;
445
        }
446 12
        $opposite = static::buildRelation($relation->recipient, $relation->initiator);
447 12
        if ($relation->relationType == static::$relationSingle) {
448
            $opposite->relationType = static::$relationSingle;
449 12
        } elseif ($relation->relationType == static::$relationMutual) {
450 12
            $opposite->setMutualType($relation->getMutualType());
451
        }
452 12
        return $opposite;
453
    }
454
    
455
    /**
456
     * Get mutual type.
457
     * @return integer
458
     */
459 12
    public function getMutualType()
460
    {
461 12
        $btAttribute = $this->mutualTypeAttribute;
462 12
        if (is_string($btAttribute) && !empty($btAttribute)) {
463 12
            return $this->$btAttribute;
464
        }
465
        return static::$mutualTypeNormal;
466
    }
467
    
468
    /**
469
     * Set mutual type.
470
     * @param integer $type
471
     * @return integer
472
     */
473 14
    protected function setMutualType($type)
474
    {
475 14
        if (!array_key_exists($type, static::$mutualTypes)) {
476
            $type = static::$mutualTypeNormal;
477
        }
478 14
        $btAttribute = $this->mutualTypeAttribute;
479 14
        if (is_string($btAttribute) && !empty($btAttribute)) {
480 14
            return $this->$btAttribute = $type;
481
        }
482
        return static::$mutualTypeNormal;
483
    }
484
    
485
    /**
486
     * Insert relation, the process is placed in a transaction.
487
     * Note: This feature only support relational databases and skip all errors.
488
     * If you don't want to use transaction or database doesn't support it,
489
     * please use `save()` directly.
490
     * @param static $relation
0 ignored issues
show
The type UserRelationTrait for parameter $relation is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
491
     * @param Connection $connection
492
     * @return boolean
493
     * @throws InvalidValueException
494
     * @throws InvalidConfigException
495
     * @throws IntegrityException
496
     */
497 1
    public static function insertRelation($relation, Connection $connection = null)
498
    {
499 1
        if (!($relation instanceof static)) {
500 1
            return false;
501
        }
502 1
        if (!$relation->getIsNewRecord()) {
0 ignored issues
show
It seems like getIsNewRecord() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
503 1
            throw new InvalidValueException('This relation is not new one.');
504
        }
505 1
        if (!$connection && isset(\Yii::$app->db) && \Yii::$app->db instanceof Connection) {
506 1
            $connection = \Yii::$app->db;
507
        }
508 1
        if (!$connection) {
509
            throw new InvalidConfigException('Invalid database connection.');
510
        }
511
        /* @var $db Connection */
512 1
        $transaction = $connection->beginTransaction();
513
        try {
514 1
            if (!$relation->save()) {
0 ignored issues
show
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
515
                throw new IntegrityException('Relation insert failed.');
516
            }
517 1
            $transaction->commit();
518
        } catch (\Exception $ex) {
519
            $transaction->rollBack();
520
            return false;
521
        }
522 1
        return true;
523
    }
524
    
525
    /**
526
     * Remove relation, the process is placed in transaction.
527
     * Note: This feature only support relational databases and skip all errors.
528
     * If you don't want to use transaction or database doesn't support it,
529
     * please use `remove()` directly.
530
     * @param static $relation
0 ignored issues
show
The type UserRelationTrait for parameter $relation is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
531
     * @param Connection $connection
532
     * @return boolean|integer
533
     * @throws InvalidConfigException
534
     */
535 1
    public static function removeRelation($relation, Connection $connection = null)
536
    {
537 1
        if (!($relation instanceof static) || $relation->getIsNewRecord()) {
0 ignored issues
show
It seems like getIsNewRecord() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
538 1
            return false;
539
        }
540
        
541 1
        if (!$connection && isset(\Yii::$app->db) && \Yii::$app->db instanceof Connection) {
542 1
            $connection = \Yii::$app->db;
543
        }
544 1
        if (!$connection) {
545
            throw new InvalidConfigException('Invalid database connection.');
546
        }
547
        /* @var $db Connection */
548 1
        $transaction = $connection->beginTransaction();
549
        try {
550 1
            $result = $relation->remove();
551 1
            $transaction->commit();
552
        } catch (\Exception $ex) {
553
            $transaction->rollBack();
554
            return false;
555
        }
556 1
        return $result;
557
    }
558
559
    /**
560
     * Remove myself.
561
     * @return integer|false The number of relations removed, or false if the remove
562
     * is unsuccessful for some reason. Note that it is possible the number of relations
563
     * removed is 0, even though the remove execution is successful.
564
     */
565 49
    public function remove()
566
    {
567 49
        return $this->delete();
0 ignored issues
show
The method delete() does not exist on rhosocial\base\models\traits\UserRelationTrait. Did you maybe mean onDeleteRelation()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
568
    }
569
570
    /**
571
     * Remove first relation between initiator(s) and recipient(s).
572
     * @param BaseUserModel|string|array $user Initiator or its guid, or array of them.
573
     * @param BaseUserModel|string|array $other Recipient or its guid, or array of them.
574
     * @return integer|false The number of relations removed.
575
     */
576 1
    public static function removeOneRelation($user, $other)
577
    {
578 1
        $model = static::find()->initiators($user)->recipients($other)->one();
579 1
        if ($model instanceof static) {
580 1
            return $model->remove();
581
        }
582
        return false;
583
    }
584
585
    /**
586
     * Remove all relations between initiator(s) and recipient(s).
587
     * @param BaseUserModel|string|array $user Initiator or its guid, or array of them.
588
     * @param BaseUserModel|string|array $other Recipient or its guid, or array of them.
589
     * @return integer The number of relations removed.
590
     */
591 15
    public static function removeAllRelations($user, $other)
592
    {
593 15
        $rni = static::buildNoInitModel();
594 15
        return static::deleteAll([$rni->createdByAttribute => BaseUserModel::compositeGUIDs($user),
595 15
            $rni->otherGuidAttribute => BaseUserModel::compositeGUIDs($other)]);
596
    }
597
598
    /**
599
     * Get first relation between initiator(s) and recipient(s).
600
     * @param BaseUserModel|string|array $user Initiator or its guid, or array of them.
601
     * @param BaseUserModel|string|array $other Recipient or its guid, or array of them.
602
     * @return static
603
     */
604 4
    public static function findOneRelation($user, $other)
605
    {
606 4
        return static::find()->initiators($user)->recipients($other)->one();
607
    }
608
609
    /**
610
     * Get first opposite relation between initiator(s) and recipient(s).
611
     * @param BaseUserModel|string $user Initiator or its guid, or array of them.
612
     * @param BaseUserModel|string $other Recipient or its guid, or array of them.
613
     * @return static
614
     */
615 1
    public static function findOneOppositeRelation($user, $other)
616
    {
617 1
        return static::find()->initiators($other)->recipients($user)->one();
618
    }
619
620
    /**
621
     * Get user's or users' all relations, or by specified groups.
622
     * @param BaseUserModel|string|array $user Initiator or its GUID, or Initiators or their GUIDs.
623
     * @param BaseUserRelationGroupModel|string|array|null $groups UserRelationGroup
624
     * or its guid, or array of them. If you do not want to delimit the groups, please assign null.
625
     * @return array all eligible relations
626
     */
627 1
    public static function findOnesAllRelations($user, $groups = null)
628
    {
629 1
        return static::find()->initiators($user)->groups($groups)->all();
630
    }
631
632
    /**
633
     * Initialize groups attribute.
634
     * @param ModelEvent $event
635
     */
636 49
    public function onInitGroups($event)
637
    {
638 49
        $sender = $event->sender;
639
        /* @var $sender static */
640 49
        $sender->removeAllGroups();
641 49
    }
642
643
    /**
644
     * Initialize remark attribute.
645
     * @param ModelEvent $event
646
     */
647 49
    public function onInitRemark($event)
648
    {
649 49
        $sender = $event->sender;
650
        /* @var $sender static */
651 49
        $sender->setRemark('');
652 49
    }
653
654
    /**
655
     * The event triggered after insert new relation.
656
     * The opposite relation should be inserted without triggering events
657
     * simultaneously after new relation inserted,
658
     * @param ModelEvent $event
659
     * @throws IntegrityException throw if insert failed.
660
     */
661 46
    public function onInsertRelation($event)
662
    {
663 46
        $sender = $event->sender;
664
        /* @var $sender static */
665 46
        if ($sender->relationType == static::$relationMutual) {
666 12
            $opposite = static::buildOppositeRelation($sender);
667 12
            $opposite->off(static::EVENT_AFTER_INSERT, [$opposite, 'onInsertRelation']);
0 ignored issues
show
It seems like off() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
668 12
            if (!$opposite->save()) {
0 ignored issues
show
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
669
                $opposite->recordWarnings();
0 ignored issues
show
It seems like recordWarnings() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
670
                throw new IntegrityException('Reverse relation insert failed.');
671
            }
672 12
            $opposite->on(static::EVENT_AFTER_INSERT, [$opposite, 'onInsertRelation']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
673
        }
674 46
    }
675
676
    /**
677
     * The event triggered after update relation.
678
     * The opposite relation should be updated without triggering events
679
     * simultaneously after existed relation removed.
680
     * @param ModelEvent $event
681
     * @throw IntegrityException throw if update failed.
682
     */
683 11
    public function onUpdateRelation($event)
684
    {
685 11
        $sender = $event->sender;
686
        /* @var $sender static */
687 11
        if ($sender->relationType == static::$relationMutual) {
688 2
            $opposite = static::buildOppositeRelation($sender);
689 2
            $opposite->off(static::EVENT_AFTER_UPDATE, [$opposite, 'onUpdateRelation']);
0 ignored issues
show
It seems like off() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
690 2
            if (!$opposite->save()) {
0 ignored issues
show
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
691
                $opposite->recordWarnings();
0 ignored issues
show
It seems like recordWarnings() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
692
                throw new IntegrityException('Reverse relation update failed.');
693
            }
694 2
            $opposite->on(static::EVENT_AFTER_UPDATE, [$opposite, 'onUpdateRelation']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
695
        }
696 11
    }
697
698
    /**
699
     * The event triggered after delete relation.
700
     * The opposite relation should be deleted without triggering events
701
     * simultaneously after existed relation removed.
702
     * @param ModelEvent $event
703
     */
704 49
    public function onDeleteRelation($event)
705
    {
706 49
        $sender = $event->sender;
707
        /* @var $sender static */
708 49
        if ($sender->relationType == static::$relationMutual) {
709 14
            $sender->off(static::EVENT_AFTER_DELETE, [$sender, 'onDeleteRelation']);
0 ignored issues
show
It seems like off() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
710 14
            static::removeAllRelations($sender->recipient, $sender->initiator);
711 14
            $sender->on(static::EVENT_AFTER_DELETE, [$sender, 'onDeleteRelation']);
0 ignored issues
show
It seems like on() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
712
        }
713 49
    }
714
}
715