Issues (81)

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.

src/Traits/Criteriable.php (4 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
declare(strict_types=1);
4
5
namespace Rinvex\Repository\Traits;
6
7
use Closure;
8
use Illuminate\Support\Arr;
9
use Rinvex\Repository\Contracts\CriterionContract;
10
use Rinvex\Repository\Contracts\RepositoryContract;
11
use Rinvex\Repository\Exceptions\CriterionException;
12
use Rinvex\Repository\Exceptions\RepositoryException;
13
14
trait Criteriable
15
{
16
    /**
17
     * List of repository criteria.
18
     *
19
     * @var array
20
     */
21
    protected $criteria = [];
22
23
    /**
24
     * List of default repository criteria.
25
     *
26
     * @var array
27
     */
28
    protected $defaultCriteria = [];
29
30
    /**
31
     * Skip criteria flag.
32
     * If setted to true criteria will not be apply to the query.
33
     *
34
     * @var bool
35
     */
36
    protected $skipCriteria = false;
37
38
    /**
39
     * Skip default criteria flag.
40
     * If setted to true default criteria will not be added to the criteria list.
41
     *
42
     * @var bool
43
     */
44
    protected $skipDefaultCriteria = false;
45
46
    /**
47
     * Return name for the criterion.
48
     * If as criterion in parameter passed string we assume that is criterion class name.
49
     *
50
     * @param CriterionContract|Closure|string $criteria
51
     *
52
     * @return string
53
     */
54
    public function getCriterionName($criteria): string
55
    {
56
        if ($criteria instanceof Closure) {
57
            return spl_object_hash($criteria);
58
        }
59
60
        return is_object($criteria) ? get_class($criteria) : $criteria;
61
    }
62
63
    /**
64
     * Try to instantiate given criterion class name with this arguments.
65
     *
66
     * @param $class
67
     * @param $arguments
68
     *
69
     * @throws CriterionException
70
     *
71
     * @return mixed
0 ignored issues
show
Consider making the return type a bit more specific; maybe use object.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
72
     */
73
    protected function instantiateCriterion($class, $arguments)
74
    {
75
        $reflection = new \ReflectionClass($class);
76
77
        if (! $reflection->implementsInterface(CriterionContract::class)) {
78
            throw CriterionException::classNotImplementContract($class);
79
        }
80
81
        // If arguments is an associative array we can assume their order and parameter existence
82
        if (Arr::isAssoc($arguments)) {
83
            $parameters = array_column($reflection->getConstructor()->getParameters(), 'name');
84
85
            $arguments = array_filter(array_map(function ($parameter) use ($arguments) {
86
                return $arguments[$parameter] ?? null;
87
            }, $parameters));
88
        }
89
90
        return $reflection->newInstanceArgs($arguments);
91
    }
92
93
    /**
94
     * Return class and arguments from passed array criterion.
95
     * Extracting class and arguments from array.
96
     *
97
     * @param array $criterion
98
     *
99
     * @throws CriterionException
100
     *
101
     * @return array
102
     */
103
    protected function extractCriterionClassAndArgs(array $criterion): array
104
    {
105
        if (count($criterion) > 2 || empty($criterion)) {
106
            throw CriterionException::wrongArraySignature($criterion);
107
        }
108
109
        // If an array is assoc we assume that the key is a class and value is an arguments
110
        if (Arr::isAssoc($criterion)) {
111
            $criterion = [array_keys($criterion)[0], array_values($criterion)[0]];
112
        } elseif (count($criterion) === 1) {
113
            // If an array is not assoc but count is one, we can assume there is a class without arguments.
114
            // Like when a string passed as criterion
115
            array_push($criterion, []);
116
        }
117
118
        return $criterion;
119
    }
120
121
    /**
122
     * Add criterion to the specific list.
123
     * low-level implementation of adding criterion to the list.
124
     *
125
     * @param Closure|CriterionContract|array|string $criterion
126
     * @param string                                 $list
127
     *
128
     * @throws CriterionException
129
     * @throws RepositoryException
130
     *
131
     * @return $this
132
     */
133
    protected function addCriterion($criterion, $list)
134
    {
135
        if (! property_exists($this, $list)) {
136
            throw RepositoryException::listNotFound($list, $this);
137
        }
138
139
        if (! $criterion instanceof Closure &&
140
            ! $criterion instanceof CriterionContract &&
141
            ! is_string($criterion) &&
142
            ! is_array($criterion)
143
        ) {
144
            throw CriterionException::wrongCriterionType($criterion);
145
        }
146
147
        //If criterion is a string we will assume it is a class name without arguments
148
        //and we need to normalize signature for instantiation try
149
        if (is_string($criterion)) {
150
            $criterion = [$criterion, []];
151
        }
152
153
        //If the criterion is an array we will assume it is an array of class name with arguments
154
        //and try to instantiate this
155
        if (is_array($criterion)) {
156
            $criterion = call_user_func_array([$this, 'instantiateCriterion'], $this->extractCriterionClassAndArgs($criterion));
157
        }
158
159
        $this->{$list}[$this->getCriterionName($criterion)] = $criterion;
160
161
        return $this;
162
    }
163
164
    /**
165
     * Add criteria to the specific list
166
     * low-level implementation of adding criteria to the list.
167
     *
168
     * @param array $criteria
169
     * @param $list
170
     */
171
    protected function addCriteria(array $criteria, $list)
172
    {
173
        array_walk($criteria, function ($value, $key) use ($list) {
174
            $criterion = is_string($key) ? [$key, $value] : $value;
175
            $this->addCriterion($criterion, $list);
176
        });
177
    }
178
179
    /**
180
     * Push criterion to the criteria list.
181
     *
182
     * @param CriterionContract|Closure|array|string $criterion
183
     *
184
     * @return $this
185
     */
186
    public function pushCriterion($criterion)
187
    {
188
        $this->addCriterion($criterion, 'criteria');
189
190
        return $this;
191
    }
192
193
    /**
194
     * Remove provided criterion from criteria list.
195
     *
196
     * @param CriterionContract|Closure|string $criterion
197
     *
198
     * @return $this
199
     */
200
    public function removeCriterion($criterion)
201
    {
202
        unset($this->criteria[$this->getCriterionName($criterion)]);
203
204
        return $this;
205
    }
206
207
    /**
208
     * Remove provided criteria from criteria list.
209
     *
210
     * @param array $criteria
211
     *
212
     * @return RepositoryContract
0 ignored issues
show
Should the return type not be Criteriable?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
213
     */
214
    public function removeCriteria(array $criteria)
215
    {
216
        array_walk($criteria, function ($criterion) {
217
            $this->removeCriterion($criterion);
218
        });
219
220
        return $this;
221
    }
222
223
    /**
224
     * Push array of criteria to the criteria list.
225
     *
226
     * @param array $criteria
227
     *
228
     * @return $this
229
     */
230
    public function pushCriteria(array $criteria)
231
    {
232
        $this->addCriteria($criteria, 'criteria');
233
234
        return $this;
235
    }
236
237
    /**
238
     * Flush criteria list.
239
     * We can flush criteria only when they is not skipped.
240
     *
241
     * @return $this
242
     */
243
    public function flushCriteria()
244
    {
245
        if (! $this->skipCriteria) {
246
            $this->criteria = [];
247
        }
248
249
        return $this;
250
    }
251
252
    /**
253
     * Set default criteria list.
254
     *
255
     * @param array $criteria
256
     *
257
     * @return $this
258
     */
259
    public function setDefaultCriteria(array $criteria)
260
    {
261
        $this->addCriteria($criteria, 'defaultCriteria');
262
263
        return $this;
264
    }
265
266
    /**
267
     * Return default criteria list.
268
     *
269
     * @return array
270
     */
271
    public function getDefaultCriteria(): array
272
    {
273
        return $this->defaultCriteria;
274
    }
275
276
    /**
277
     * Return current list of criteria.
278
     *
279
     * @return array
280
     */
281
    public function getCriteria(): array
282
    {
283
        if ($this->skipCriteria) {
284
            return [];
285
        }
286
287
        return $this->skipDefaultCriteria ? $this->criteria : array_merge($this->getDefaultCriteria(), $this->criteria);
288
    }
289
290
    /**
291
     * Set skipCriteria flag.
292
     *
293
     * @param bool|true $flag
294
     *
295
     * @return $this
296
     */
297
    public function skipCriteria($flag = true)
298
    {
299
        $this->skipCriteria = $flag;
0 ignored issues
show
Documentation Bug introduced by
It seems like $flag can also be of type object<Rinvex\Repository\Traits\true>. However, the property $skipCriteria is declared as type boolean. 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...
300
301
        return $this;
302
    }
303
304
    /**
305
     * Set skipDefaultCriteria flag.
306
     *
307
     * @param bool|true $flag
308
     *
309
     * @return $this
310
     */
311
    public function skipDefaultCriteria($flag = true)
312
    {
313
        $this->skipDefaultCriteria = $flag;
0 ignored issues
show
Documentation Bug introduced by
It seems like $flag can also be of type object<Rinvex\Repository\Traits\true>. However, the property $skipDefaultCriteria is declared as type boolean. 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...
314
315
        return $this;
316
    }
317
318
    /**
319
     * Check if a given criterion name now in the criteria list.
320
     *
321
     * @param CriterionContract|Closure|string $criterion
322
     *
323
     * @return bool
324
     */
325
    public function hasCriterion($criterion): bool
326
    {
327
        return isset($this->getCriteria()[$this->getCriterionName($criterion)]);
328
    }
329
330
    /**
331
     * Return criterion object or closure from criteria list by name.
332
     *
333
     * @param $criterion
334
     *
335
     * @return CriterionContract|Closure|null
336
     */
337
    public function getCriterion($criterion)
338
    {
339
        if ($this->hasCriterion($criterion)) {
340
            return $this->getCriteria()[$this->getCriterionName($criterion)];
341
        }
342
    }
343
344
    /**
345
     * Apply criteria list to the given query.
346
     *
347
     * @param $query
348
     * @param $repository
349
     *
350
     * @return mixed
351
     */
352
    public function applyCriteria($query, $repository)
353
    {
354
        foreach ($this->getCriteria() as $criterion) {
355
            if ($criterion instanceof CriterionContract) {
356
                $query = $criterion->apply($query, $repository);
357
            } elseif ($criterion instanceof Closure) {
358
                $query = $criterion($query, $repository);
359
            }
360
        }
361
362
        return $query;
363
    }
364
}
365