Issues (187)

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/MultipleBlameableTrait.php (12 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.name/
9
 * @copyright Copyright (c) 2016 vistart
10
 * @license https://vistart.name/license/
11
 */
12
13
namespace vistart\Models\traits;
14
15
use vistart\helpers\Number;
16
use vistart\Models\events\MultipleBlameableEvent;
17
use vistart\Models\models\BaseUserModel;
18
use yii\base\ModelEvent;
19
use yii\base\InvalidCallException;
20
use yii\base\InvalidConfigException;
21
use yii\base\InvalidParamException;
22
use yii\web\JsonParser;
23
24
/**
25
 * 一个模型的某个属性可能对应多个责任者,该 trait 用于处理此种情况。此种情况违反
26
 * 了关系型数据库第一范式,因此此 trait 只适用于责任者属性修改不频繁的场景,在开
27
 * 发时必须严格测试数据一致性,并同时考量性能。
28
 * Basic Principles:
29
 * <ol>
30
 * <li>when adding blame, it will check whether each of blames including to be
31
 * added is valid.
32
 * </li>
33
 * <li>when removing blame, as well as counting, getting or setting list of them,
34
 * it will also check whether each of blames is valid.
35
 * </li>
36
 * <li>By default, once blame was deleted, the guid of it is not removed from
37
 * list of blames immediately. It will check blame if valid when adding, removing,
38
 * counting, getting and setting it. You can define a blame model and attach it
39
 * events triggered when inserting, updating and deleting a blame, then disable
40
 * checking the validity of blames.
41
 * </li>
42
 * </ol>
43
 * Notice:
44
 * <ol>
45
 * <li>You must specify two properties: $multiBlamesClass and $multiBlamesAttribute.
46
 * <ul>
47
 * <li>$multiBlamesClass specify the class name of blame.</li>
48
 * <li>$multiBlamesAttribute specify the field name of blames.</li>
49
 * </ul>
50
 * </li>
51
 * <li>You should rename the following methods to be needed optionally.</li>
52
 * </ol>
53
 * @property-read array $multiBlamesAttributeRules
54
 * @property string[] $blameGuids
55
 * @property-read array $allBlames
56
 * @property-read array $nonBlameds
57
 * @property-read integer $blamesCount
58
 * @version 2.0
59
 * @author vistart <[email protected]>
60
 */
61
trait MultipleBlameableTrait
62
{
63
64
    /**
65
     * @var string class name of multiple blameable class.
66
     */
67
    public $multiBlamesClass = '';
68
69
    /**
70
     * @var string name of multiple blameable attribute.
71
     */
72
    public $multiBlamesAttribute = 'blames';
73
74
    /**
75
     * @var integer the limit of blames. it should be greater than or equal 1, and
76
     * less than or equal 10.
77
     */
78
    public $blamesLimit = 10;
79
80
    /**
81
     * @var boolean determines whether blames list has been changed.
82
     */
83
    public $blamesChanged = false;
84
85
    /**
86
     * @var string event name.
87
     */
88
    public static $eventMultipleBlamesChanged = 'multipleBlamesChanged';
89
90
    /**
91
     * Get the rules associated with multiple blameable attribute.
92
     * @return array rules.
93
     */
94 10
    public function getMultipleBlameableAttributeRules()
95
    {
96 10
        return is_string($this->multiBlamesAttribute) ? [
97 10
            [[$this->multiBlamesAttribute], 'required'],
98 10
            [[$this->multiBlamesAttribute], 'string', 'max' => $this->blamesLimit * 39 + 1],
99 10
            [[$this->multiBlamesAttribute], 'default', 'value' => '[]'],
100 10
            ] : [];
101
    }
102
103
    /**
104
     * Add specified blame.
105
     * @param {$this->multiBlamesClass}|string $blame
0 ignored issues
show
The doc-type {$this->multiBlamesClass}|string could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
106
     * @return false|array
107
     * @throws InvalidParamException
108
     * @throws InvalidCallException
109
     */
110 2
    public function addBlame($blame)
111
    {
112 2
        if (!is_string($this->multiBlamesAttribute)) {
113
            return false;
114
        }
115 2
        $blameGuid = '';
116 2
        if (is_string($blame)) {
117 1
            $blameGuid = $blame;
118 1
        }
119 2
        if ($blame instanceof $this->multiBlamesClass) {
120 1
            $blameGuid = $blame->guid;
121 1
        }
122 2
        $blameGuids = $this->getBlameGuids(true);
123 2
        if (array_search($blameGuid, $blameGuids)) {
124
            throw new InvalidParamException('the blame has existed.');
125
        }
126 2
        if ($this->getBlamesCount() >= $this->blamesLimit) {
127
            throw new InvalidCallException("the limit($this->blamesLimit) of blames has been reached.");
128
        }
129 2
        $blameGuids[] = $blameGuid;
130 2
        $this->setBlameGuids($blameGuids);
131 2
        return $this->getBlameGuids();
132
    }
133
134
    /**
135
     * Create blame.
136
     * @param BaseUserModel $user who will own this blame.
137
     * @param array $config blame class configuration array.
138
     * @return {$this->multiBlamesClass}
0 ignored issues
show
The doc-type {$this->multiBlamesClass} could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
139
     */
140 1
    public static function createBlame($user, $config = [])
141
    {
142 1
        if (!($user instanceof BaseUserModel)) {
143
            $message = 'the type of user instance must be the extended class of BaseUserModel.';
144
            throw new InvalidParamException($message);
145
        }
146 1
        $mbClass = static::buildNoInitModel();
147 1
        $mbi = $mbClass->multiBlamesClass;
148 1
        return $user->create($mbi::className(), $config);
149
    }
150
151
    /**
152
     * Add specified blame, or create it before adding if doesn't exist.
153
     * But you should save the blame instance before adding, or the operation
154
     * will fail.
155
     * @param {$this->multiBlamesClass}|string|array $blame 
0 ignored issues
show
The doc-type {$this->multiBlamesClass}|string|array could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
156
     * It will be regarded as blame's guid if it is a string. And assign the
157
     * reference parameter $blame the instance if it existed, or create one if not
158
     * found.
159
     * If it is {$this->multiBlamesClass} instance and existed, then will add it, or
160
     * false will be given if it is not found in database. So if you want to add
161
     * blame instance, you should save it before adding.
162
     * If it is a array, it will be regarded as configuration array of blame.
163
     * Notice! This parameter passed by reference, so it must be a variable!
164
     * @param BaseUserModel $user whose blame.
165
     * If null, it will take this blameable model's user.
166
     * @return false|array false if blame created failed or not enable this feature.
167
     * blames guid array if created and added successfully.
168
     * @throws InvalidConfigException
169
     * @throws InvalidParamException
170
     * @see addBlame()
171
     */
172 1
    public function addOrCreateBlame(&$blame = null, $user = null)
173
    {
174 1
        if (!is_string($this->multiBlamesClass)) {
175
            throw new InvalidConfigException('$multiBlamesClass must be specified if you want to use multiple blameable features.');
176
        }
177 1
        if (is_array($blame)) {
178 1
            if ($user == null) {
179 1
                $user = $this->user;
0 ignored issues
show
The property user 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...
180 1
            }
181 1
            $blame = static::getOrCreateBlame($blame, $user);
182 1
            if (!$blame->save()) {
183
                return false;
184
            }
185 1
            return $this->addBlame($blame->guid);
186
        }
187
        $blameGuid = '';
188
        if (is_string($blame)) {
189
            $blameGuid = $blame;
190
        }
191
        if ($blame instanceof $this->multiBlamesClass) {
192
            $blameGuid = $blame->guid;
193
        }
194
        if (($mbi = static::getBlame($blameGuid)) !== null) {
195
            return $this->addBlame($mbi);
196
        }
197
        return false;
198
    }
199
200
    /**
201
     * Remove specified blame.
202
     * @param {$this->multiBlamesClass} $blame
0 ignored issues
show
The doc-type {$this->multiBlamesClass} could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
203
     * @return false|array all guids in json format.
204
     */
205 1
    public function removeBlame($blame)
206
    {
207 1
        if (!is_string($this->multiBlamesAttribute)) {
208
            return false;
209
        }
210 1
        $blameGuid = '';
211 1
        if (is_string($blame)) {
212 1
            $blameGuid = $blame;
213 1
        }
214 1
        if ($blame instanceof $this->multiBlamesClass) {
215 1
            $blameGuid = $blame->guid;
216 1
        }
217 1
        $blameGuids = $this->getBlameGuids(true);
218 1
        if (($key = array_search($blameGuid, $blameGuids)) !== false) {
219 1
            unset($blameGuids[$key]);
220 1
            $this->setBlameGuids($blameGuids);
221 1
        }
222 1
        return $this->getBlameGuids();
223
    }
224
225
    /**
226
     * Remove all blames.
227
     */
228 10
    public function removeAllBlames()
229
    {
230 10
        $this->setBlameGuids();
231 10
    }
232
233
    /**
234
     * Count the blames.
235
     * @return integer
236
     */
237 2
    public function getBlamesCount()
238
    {
239 2
        return count($this->getBlameGuids(true));
240
    }
241
242
    /**
243
     * Get the guid array of blames. it may check all guids if valid before return.
244
     * @param boolean $checkValid determines whether checking the blame is valid.
245
     * @return array all guids in json format.
246
     */
247 2
    public function getBlameGuids($checkValid = false)
248
    {
249 2
        $multiBlamesAttribute = $this->multiBlamesAttribute;
250 2
        if ($multiBlamesAttribute === false) {
251
            return [];
252
        }
253 2
        $jsonParser = new JsonParser();
254 2
        $guids = $jsonParser->parse($this->$multiBlamesAttribute, true);
0 ignored issues
show
true is of type boolean, 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...
255 2
        if ($checkValid) {
256 2
            $guids = $this->unsetInvalidBlames($guids);
257 2
        }
258 2
        return $guids;
259
    }
260
261
    /**
262
     * Event triggered when blames list changed.
263
     * @param MultipleBlameableEvent $event
264
     */
265 10
    public function onBlamesChanged($event)
266
    {
267 10
        $sender = $event->sender;
268 10
        $sender->blamesChanged = $event->blamesChanged;
269 10
    }
270
271
    /**
272
     * Remove invalid blame guid from provided guid array.
273
     * @param array $guids guid array of blames.
274
     * @return array guid array of blames unset invalid.
275
     */
276 10
    protected function unsetInvalidBlames($guids)
277
    {
278 10
        $checkedGuids = Number::unsetInvalidUuids($guids);
279 10
        $multiBlamesClass = $this->multiBlamesClass;
280 10
        foreach ($checkedGuids as $key => $guid) {
281 2
            $blame = $multiBlamesClass::findOne($guid);
282 2
            if (!$blame) {
283 1
                unset($checkedGuids[$key]);
284 1
            }
285 10
        }
286 10
        $diff = array_diff($guids, $checkedGuids);
287 10
        $eventName = static::$eventMultipleBlamesChanged;
288 10
        $this->trigger($eventName, new MultipleBlameableEvent(['blamesChanged' => !empty($diff)]));
0 ignored issues
show
It seems like trigger() 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...
289 10
        return $checkedGuids;
290
    }
291
292
    /**
293
     * Set the guid array of blames, it may check all guids if valid.
294
     * @param array $guids guid array of blames.
295
     * @param boolean $checkValid determines whether checking the blame is valid.
296
     * @return false|array all guids.
297
     */
298 10
    public function setBlameGuids($guids = [], $checkValid = true)
299
    {
300 10
        if (!is_array($guids) || $this->multiBlamesAttribute === false) {
301
            return null;
302
        }
303 10
        if ($checkValid) {
304 10
            $guids = $this->unsetInvalidBlames($guids);
305 10
        }
306 10
        $multiBlamesAttribute = $this->multiBlamesAttribute;
307 10
        $this->$multiBlamesAttribute = json_encode(array_values($guids));
308 10
        return $guids;
309
    }
310
311
    /**
312
     * Get blame.
313
     * @param string $blameGuid
314
     * @return {$this->multiBlamesClass}
0 ignored issues
show
The doc-type {$this->multiBlamesClass} could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
315
     */
316 2
    public static function getBlame($blameGuid)
317
    {
318 2
        $self = static::buildNoInitModel();
319 2
        if (empty($self->multiBlamesClass) || !is_string($self->multiBlamesClass) || $self->multiBlamesAttribute === false) {
320
            return null;
321
        }
322 2
        $mbClass = $self->multiBlamesClass;
323 2
        return $mbClass::findOne($blameGuid);
324
    }
325
326
    /**
327
     * Get or create blame.
328
     * @param string|array $blameGuid
329
     * @param BaseUserModel $user
330
     * @return {$this->multiBlamesClass}|null
0 ignored issues
show
The doc-type {$this->multiBlamesClass}|null could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
331
     */
332 1
    public static function getOrCreateBlame($blameGuid, $user = null)
333
    {
334 1
        if (is_string($blameGuid)) {
335
            $blameGuid = static::getBlame($blameGuid);
336
            if ($blameGuid !== null) {
337
                return $blameGuid;
338
            }
339
        }
340 1
        if (is_array($blameGuid)) {
341 1
            return static::createBlame($user, $blameGuid);
0 ignored issues
show
It seems like $user defined by parameter $user on line 332 can be null; however, vistart\Models\traits\Mu...bleTrait::createBlame() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
342
        }
343
        return null;
344
    }
345
346
    /**
347
     * Get all ones to be blamed by `$blame`.
348
     * @param {$this->multiBlamesClass} $blame
0 ignored issues
show
The doc-type {$this->multiBlamesClass} could not be parsed: Unknown type name "{$this-" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
349
     * @return array
350
     */
351 1
    public function getBlameds($blame)
352
    {
353 1
        $blameds = static::getBlame($blame->guid);
354 1
        if (empty($blameds)) {
355 1
            return null;
356
        }
357 1
        $createdByAttribute = $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...
358 1
        return static::find()->where([$createdByAttribute => $this->$createdByAttribute])
359 1
                ->andWhere(['like', $this->multiBlamesAttribute, $blame->guid])->all();
360
    }
361
362
    /**
363
     * Get all the blames of record.
364
     * @return array all blames.
365
     */
366 1
    public function getAllBlames()
367
    {
368 1
        if (empty($this->multiBlamesClass) ||
369 1
            !is_string($this->multiBlamesClass) ||
370 1
            $this->multiBlamesAttribute === false) {
371
            return null;
372
        }
373 1
        $multiBlamesClass = $this->multiBlamesClass;
374 1
        $createdByAttribute = $this->createdByAttribute;
375 1
        return $multiBlamesClass::findAll([$createdByAttribute => $this->$createdByAttribute]);
376
    }
377
378
    /**
379
     * Get all records which without any blames.
380
     * @return array all non-blameds.
381
     */
382 1
    public function getNonBlameds()
383
    {
384 1
        $createdByAttribute = $this->createdByAttribute;
385
        $cond = [
386 1
            $createdByAttribute => $this->$createdByAttribute,
387 1
            $this->multiBlamesAttribute => static::getEmptyBlamesJson()
388 1
        ];
389 1
        return static::find()->where($cond)->all();
390
    }
391
392
    /**
393
     * Initialize blames limit.
394
     * @param ModelEvent $event
395
     */
396 10
    public function onInitBlamesLimit($event)
397
    {
398 10
        $sender = $event->sender;
399 10
        if (!is_int($sender->blamesLimit) || $sender->blamesLimit < 1 || $sender->blamesLimit > 64) {
400
            $sender->blamesLimit = 10;
401
        }
402 10
    }
403
404
    /**
405
     * Get the json of empty blames array.
406
     * @return string
407
     */
408 1
    public static function getEmptyBlamesJson()
409
    {
410 1
        return json_encode([]);
411
    }
412
}
413