Issues (114)

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/Relationships/HasOneOrMany.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
namespace Analogue\ORM\Relationships;
4
5
use Analogue\ORM\System\Mapper;
6
use Analogue\ORM\EntityCollection;
7
8
abstract class HasOneOrMany extends Relationship
9
{
10
    /**
11
     * The foreign key of the parent model.
12
     *
13
     * @var string
14
     */
15
    protected $foreignKey;
16
17
    /**
18
     * The local key of the parent model.
19
     *
20
     * @var string
21
     */
22
    protected $localKey;
23
24
    /**
25
     * Create a new has many relationship instance.
26
     *
27
     * @param Mapper                 $mapper
28
     * @param \Analogue\ORM\Mappable $parentEntity
29
     * @param string                 $foreignKey
30
     * @param string                 $localKey
31
     */
32
    public function __construct(Mapper $mapper, $parentEntity, $foreignKey, $localKey)
33
    {
34
        $this->localKey = $localKey;
35
        $this->foreignKey = $foreignKey;
36
37
        parent::__construct($mapper, $parentEntity);
38
    }
39
40
    /**
41
     * @param \Analogue\ORM\Entity|EntityCollection $entity
42
     * @return void
43
     */
44
    public function attachTo($entity)
45
    {
46
        if ($entity instanceof EntityCollection) {
47
            $this->attachMany($entity);
48
        }
49
        $this->attachOne($entity);
0 ignored issues
show
It seems like $entity defined by parameter $entity on line 44 can also be of type object<Analogue\ORM\EntityCollection>; however, Analogue\ORM\Relationshi...sOneOrMany::attachOne() does only seem to accept object<Analogue\ORM\Entity>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
50
    }
51
52
    /**
53
     * @param $entityHash
54
     * @return void
55
     */
56
    public function detachFrom($entityHash)
57
    {
58
        if (is_array($entityHash)) {
59
            $this->detachMany($entityHash);
60
            return;
61
        }
62
        $this->detachMany([$entityHash]);
63
    }
64
65
    /**
66
     * @param \Analogue\ORM\Entity $entity
67
     */
68
    public function attachOne($entity)
69
    {
70
        $wrapper = $this->factory->make($entity);
71
72
        // Ok, we need to guess the inverse of the relation from there.
73
        // Let's assume the inverse of the relation method is the name of
74
        // the entity.
75
76
        $wrapper->setEntityAttribute($this->getPlainForeignKey(), $this->getParentKey());
77
    }
78
79
    /**
80
     * @param EntityCollection $entities
81
     */
82
    public function attachMany(EntityCollection $entities)
83
    {
84
        foreach ($entities as $entity) {
85
            $this->attachOne($entity);
86
        }
87
    }
88
89
    /**
90
     * @param $entityHash
91
     */
92
    protected function detachOne($entityHash)
93
    {
94
        $this->detachMany([$entityHash]);
95
    }
96
97
    /**
98
     * Attach ids that are passed as arguments, and detach any other
99
     * @param  mixed $entities
100
     * @throws \InvalidArgumentException
101
     * @return void
102
     */
103
    public function sync(array $entities)
104
    {
105
        $this->detachExcept($entities);
106
    }
107
108
    /**
109
     * @param  $entities
110
     * @throws \InvalidArgumentException
111
     */
112
    protected function detachExcept($entities)
113
    {
114
        $query = $this->query->getQuery()->from($this->relatedMap->getTable());
115
116
        if (count($entities) > 0) {
117
            $keys = $this->getKeys($entities);
118
            $query->whereNotIn($this->relatedMap->getKeyName(), $keys);
119
        }
120
121
        $parentKey = $this->parentMap->getKeyName();
122
123
        $query->where($this->getPlainForeignKey(), '=', $this->parent->getEntityAttribute($parentKey))
124
            ->update([$this->getPlainForeignKey() => null]);
125
    }
126
127
    /**
128
     * @param array $entityHashes
129
     */
130
    public function detachMany(array $entityHashes)
131
    {
132
        $keys = [];
133
134
        foreach ($entityHashes as $hash) {
135
            $split = explode('.', $hash);
136
            $keys[] = $split[1];
137
        }
138
139
        $query = $this->query->getQuery()->from($this->relatedMap->getTable());
140
141
        $query->whereIn($this->relatedMap->getKeyName(), $keys)
142
            ->update([$this->getPlainForeignKey() => null]);
143
    }
144
145
    /**
146
     * Set the base constraints on the relation query.
147
     *
148
     * @return void
149
     */
150
    public function addConstraints()
151
    {
152
        if (static::$constraints) {
153
            $this->query->where($this->foreignKey, '=', $this->getParentKey());
154
        }
155
    }
156
157
    /**
158
     * Set the constraints for an eager load of the relation.
159
     *
160
     * @param  array $entities
161
     * @return void
162
     */
163
    public function addEagerConstraints(array $entities)
164
    {
165
        $this->query->whereIn($this->foreignKey, $this->getKeys($entities, $this->localKey));
0 ignored issues
show
Documentation Bug introduced by
The method whereIn does not exist on object<Analogue\ORM\System\Query>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
166
    }
167
168
    /**
169
     * Match the eagerly loaded results to their single parents.
170
     *
171
     * @param  array            $entities
172
     * @param  EntityCollection $results
173
     * @param  string           $relation
174
     * @return array
175
     */
176
    public function matchOne(array $entities, EntityCollection $results, $relation)
177
    {
178
        return $this->matchOneOrMany($entities, $results, $relation, 'one');
179
    }
180
181
    /**
182
     * Match the eagerly loaded results to their many parents.
183
     *
184
     * @param  array            $entities
185
     * @param  EntityCollection $results
186
     * @param  string           $relation
187
     * @return array
188
     */
189
    public function matchMany(array $entities, EntityCollection $results, $relation)
190
    {
191
        return $this->matchOneOrMany($entities, $results, $relation, 'many');
192
    }
193
194
    /**
195
     * Match the eagerly loaded results to their many parents.
196
     *
197
     * @param  array            $entities
198
     * @param  EntityCollection $results
199
     * @param  string           $relation
200
     * @param  string           $type
201
     * @return array
202
     */
203 View Code Duplication
    protected function matchOneOrMany(array $entities, EntityCollection $results, $relation, $type)
0 ignored issues
show
This method seems to be duplicated in your project.

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

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

Loading history...
204
    {
205
        $dictionary = $this->buildDictionary($results);
206
207
        $cache = $this->parentMapper->getEntityCache();
208
209
        // Once we have the dictionary we can simply spin through the parent models to
210
        // link them up with their children using the keyed dictionary to make the
211
        // matching very convenient and easy work. Then we'll just return them.
212
        foreach ($entities as $entity) {
213
            $entity = $this->factory->make($entity);
214
215
            $key = $entity->getEntityAttribute($this->localKey);
216
217
            if (isset($dictionary[$key])) {
218
                $value = $this->getRelationValue($dictionary, $key, $type);
219
220
                $entity->setEntityAttribute($relation, $value);
221
222
                $cache->cacheLoadedRelationResult($entity, $relation, $value, $this);
223
            }
224
        }
225
226
        return $entities;
227
    }
228
229
    /**
230
     * Get the value of a relationship by one or many type.
231
     *
232
     * @param  array  $dictionary
233
     * @param  string $key
234
     * @param  string $type
235
     * @return mixed
236
     */
237
    protected function getRelationValue(array $dictionary, $key, $type)
238
    {
239
        $value = $dictionary[$key];
240
241
        return $type == 'one' ? reset($value) : $this->relatedMap->newCollection($value);
242
    }
243
244
    /**
245
     * Build model dictionary keyed by the relation's foreign key.
246
     *
247
     * @param  EntityCollection $results
248
     * @return array
249
     */
250 View Code Duplication
    protected function buildDictionary(EntityCollection $results)
0 ignored issues
show
This method seems to be duplicated in your project.

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

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

Loading history...
251
    {
252
        $dictionary = [];
253
254
        $foreign = $this->getPlainForeignKey();
255
256
        // First we will create a dictionary of models keyed by the foreign key of the
257
        // relationship as this will allow us to quickly access all of the related
258
        // models without having to do nested looping which will be quite slow.
259
        foreach ($results as $result) {
260
            $dictionary[$result->{$foreign}][] = $result;
261
        }
262
263
        return $dictionary;
264
    }
265
266
    /**
267
     * Get the key for comparing against the parent key in "has" query.
268
     *
269
     * @return string
270
     */
271
    public function getHasCompareKey()
272
    {
273
        return $this->getForeignKey();
274
    }
275
276
    /**
277
     * Get the foreign key for the relationship.
278
     *
279
     * @return string
280
     */
281
    public function getForeignKey()
282
    {
283
        return $this->foreignKey;
284
    }
285
286
    /**
287
     * Get the plain foreign key.
288
     *
289
     * @return string
290
     */
291
    public function getPlainForeignKey()
292
    {
293
        $segments = explode('.', $this->getForeignKey());
294
295
        return $segments[count($segments) - 1];
296
    }
297
298
    /**
299
     * Get the key value of the parent's local key.
300
     *
301
     * @return mixed
302
     */
303
    public function getParentKey()
304
    {
305
        return $this->parent->getEntityAttribute($this->localKey);
306
    }
307
308
    /**
309
     * Get the fully qualified parent key name.
310
     *
311
     * @return string
312
     */
313
    public function getQualifiedParentKeyName()
314
    {
315
        return $this->parentMap->getTable() . '.' . $this->localKey;
316
    }
317
318
    /**
319
     * Get the foreign key as value pair for this relation
320
     *
321
     * @return array
322
     */
323
    public function getForeignKeyValuePair()
324
    {
325
        return [$this->getPlainForeignKey() => $this->getParentKey()];
326
    }
327
}
328