Issues (55)

Security Analysis    not enabled

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/relationships/RelationshipTrait.php (2 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
 * @copyright  Copyright (c) Flipbox Digital Limited
5
 * @license    https://flipboxfactory.com/software/organization/license
6
 * @link       https://www.flipboxfactory.com/software/organization/
7
 */
8
9
namespace flipbox\organizations\relationships;
10
11
use Craft;
12
use craft\base\ElementInterface;
13
use craft\helpers\ArrayHelper;
14
use flipbox\organizations\records\UserAssociation;
15
use Tightenco\Collect\Support\Collection;
16
use yii\base\Exception;
17
use yii\db\ActiveRecord;
18
use yii\db\QueryInterface;
19
20
/**
21
 * @author Flipbox Factory <[email protected]>
22
 * @since 2.0.0
23
 *
24
 * @mixin RelationshipInterface
25
 */
26
trait RelationshipTrait
27
{
28
    use MutatedTrait;
29
30
    /**
31
     * @var Collection|null
32
     */
33
    protected $relations;
34
35
    /**
36
     * @param null $object
37
     * @return int|null
38
     */
39
    abstract protected function findKey($object = null);
40
41
    /**
42
     * Create a new association/relationship record
43
     *
44
     * @param $object
45
     * @return ActiveRecord
46
     */
47
    abstract protected function create($object): ActiveRecord;
48
49
    /**
50
     * Resolve the object that will be related
51
     *
52
     * @param $object
53
     * @return mixed
54
     */
55
    abstract protected function resolveObjectInternal($object);
56
57
    /**
58
     * A collection of existing relationships, indexed by id.  We'll compare these with
59
     * and new relations to determine if we need to add/remove relations
60
     *
61
     * @return Collection
62
     */
63
    abstract protected function existingRelationships(): Collection;
64
65
    /**
66
     * An array of relationships to save and an array of relationships to delete.  This will
67
     * want to be used as such: `list($save, $delete) = $this->delta();`
68
     *
69
     * @return array
70
     */
71
    abstract protected function delta(): array;
72
73
    /**
74
     * @param ActiveRecord|ElementInterface|int|string $object
75
     * @return ActiveRecord
76
     */
77
    public function findOrCreate($object): ActiveRecord
78
    {
79
        if (null === ($association = $this->findOne($object))) {
80
            $association = $this->create($object);
81
        }
82
83
        return $association;
84
    }
85
86
    /**
87
     * @param ActiveRecord|ElementInterface|int|string $object
88
     * @return ActiveRecord
89
     * @throws Exception
90
     */
91
    public function findOrFail($object): ActiveRecord
92
    {
93
        if (null === ($association = $this->findOne($object))) {
94
            throw new Exception("Association could not be found.");
95
        }
96
97
        return $association;
98
    }
99
100
    /**
101
     * @param ActiveRecord|ElementInterface|int|string|null $object
102
     * @return ActiveRecord|null
103
     */
104
    public function findOne($object = null)
105
    {
106
        if (null === ($key = $this->findKey($object))) {
107
            return null;
108
        }
109
110
        return $this->getRelationships()->get($key);
111
    }
112
113
114
    /************************************************************
115
     * COLLECTIONS
116
     ************************************************************/
117
118
    /**
119
     * @inheritDoc
120
     */
121
    public function getRelationships(): Collection
122
    {
123
        if (null === $this->relations) {
124
            $this->relations = $this->existingRelationships();
125
        }
126
127
        return $this->relations;
128
    }
129
130
131
    /************************************************************
132
     * ADD / REMOVE
133
     ************************************************************/
134
135
    /**
136
     * Add one or many object relations (but do not save)
137
     *
138
     * @param $objects
139
     * @param array $attributes
140
     * @return RelationshipInterface
141
     */
142
    public function add($objects, array $attributes = []): RelationshipInterface
143
    {
144
        foreach ($this->objectArray($objects) as $object) {
145
            $this->addOne($object, $attributes);
146
        }
147
148
        return $this;
149
    }
150
151
    /**
152
     * @param $object
153
     * @param array $attributes
154
     * @return RelationshipInterface
155
     */
156
    private function addOne($object, array $attributes = []): RelationshipInterface
157
    {
158
        $isNew = false;
159
160
        // Check if it's already linked
161
        if (null === ($association = $this->findOne($object))) {
162
            $association = $this->create($object);
163
            $isNew = true;
164
        }
165
166
        // Modify?
167
        if (!empty($attributes)) {
168
            Craft::configure(
169
                $association,
170
                $attributes
171
            );
172
173
            $this->mutated = true;
174
175
            if (!$isNew) {
176
                $this->updateCollection($this->relations, $association);
177
            }
178
        }
179
180
        if ($isNew) {
181
            $this->addToRelations($association);
182
        }
183
184
        return $this;
185
    }
186
187
    /**
188
     * @param $objects
189
     * @return RelationshipInterface
190
     */
191
    public function remove($objects): RelationshipInterface
192
    {
193
        foreach ($this->objectArray($objects) as $object) {
194
            if (null !== ($key = $this->findKey($object))) {
195
                $this->removeFromRelations($key);
196
            }
197
        }
198
199
        return $this;
200
    }
201
202
203
    /*******************************************
204
     * SAVE
205
     *******************************************/
206
207
    /**
208
     * @return bool
209
     */
210
    public function save(): bool
211
    {
212
        // No changes?
213
        if (!$this->isMutated()) {
214
            return true;
215
        }
216
217
        $success = true;
218
219
        list($save, $delete) = $this->delta();
220
221
        foreach ($delete as $relationship) {
222
            if (!$relationship->delete()) {
223
                $success = false;
224
            }
225
        }
226
227
        foreach ($save as $relationship) {
228
            if (!$relationship->save()) {
229
                $success = false;
230
            }
231
        }
232
233
        $this->reset();
234
235
        return $success;
236
    }
237
238
239
    /************************************************************
240
     * UTILITIES
241
     ************************************************************/
242
243
    /**
244
     * Reset associations
245
     * @return RelationshipInterface
246
     */
247
    public function reset(): RelationshipInterface
248
    {
249
        $this->relations = null;
250
        $this->mutated = false;
251
        return $this;
252
    }
253
254
    /**
255
     * Reset associations
256
     * @return RelationshipInterface
257
     */
258
    public function clear(): RelationshipInterface
259
    {
260
        $this->newRelations([]);
261
        return $this;
262
    }
263
264
    /**
265
     * @param ActiveRecord|ElementInterface|int|string $object
266
     * @return bool
267
     */
268
    public function exists($object): bool
269
    {
270
        return null !== $this->findKey($object);
271
    }
272
273
274
    /*******************************************
275
     * COLLECTION UTILS
276
     *******************************************/
277
278
    /**
279
     * @param array $associations
280
     * @param bool $mutated
281
     * @return static
282
     */
283
    protected function newRelations(array $associations, bool $mutated = true): self
284
    {
285
        $this->relations = $this->createRelations($associations);
286
        $this->mutated = $mutated;
287
288
        return $this;
289
    }
290
291
    /**
292
     * @param $association
293
     * @return RelationshipTrait
294
     */
295
    protected function addToRelations($association): self
296
    {
297
        $this->insertCollection($this->getRelationships(), $association);
298
        $this->mutated = true;
299
300
        return $this;
301
    }
302
303
    /**
304
     * @param int $key
305
     * @return RelationshipTrait
306
     */
307
    protected function removeFromRelations(int $key): self
308
    {
309
        $this->relations->forget($key);
310
        $this->mutated = true;
311
312
        return $this;
313
    }
314
315
    /**
316
     * @param array $associations
317
     * @return Collection
318
     */
319
    protected function createRelations(array $associations = []): Collection
320
    {
321
        $collection = new Collection();
322
        foreach ($associations as $association) {
323
            $this->insertCollection($collection, $association);
324
        }
325
326
        return $collection;
327
    }
328
329
    /**
330
     * Update a relation that's already association.
331
     *
332
     * @param Collection $collection
333
     * @param $association
334
     */
335
    protected function updateCollection(Collection $collection, $association)
0 ignored issues
show
The parameter $collection is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $association is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
336
    {
337
    }
338
339
    /**
340
     * Insert a relation that's not already associated
341
     *
342
     * @param Collection $collection
343
     * @param $association
344
     */
345
    protected function insertCollection(Collection $collection, $association)
346
    {
347
        $collection->push($association);
348
    }
349
350
351
    /*******************************************
352
     * RESOLVERS
353
     *******************************************/
354
355
    /**
356
     * @inheritDoc
357
     */
358
    protected function resolveObject($object = null)
359
    {
360
        if (null === $object) {
361
            return null;
362
        }
363
364
        if (is_array($object) &&
365
            null !== ($id = ArrayHelper::getValue($object, 'id'))
366
        ) {
367
            $object = ['id' => $id];
368
        }
369
370
        return $this->resolveObjectInternal($object);
371
    }
372
373
    /**
374
     * Ensure we're working with an array of objects, not configs, etc
375
     *
376
     * @param array|QueryInterface|Collection|ElementInterface|UserAssociation $objects
377
     * @return array
378
     */
379
    protected function objectArray($objects): array
380
    {
381
        if ($objects instanceof QueryInterface || $objects instanceof Collection) {
382
            $objects = $objects->all();
383
        }
384
385
        // proper array
386
        if (!is_array($objects) || ArrayHelper::isAssociative($objects)) {
387
            $objects = [$objects];
388
        }
389
390
        return array_filter($objects);
391
    }
392
}
393