Issues (94)

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/Data/ModelSchemaInfo.php (6 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 declare(strict_types=1);
2
3
namespace Limoncello\Application\Data;
4
5
/**
6
 * Copyright 2015-2020 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use InvalidArgumentException;
22
use Limoncello\Contracts\Data\ModelSchemaInfoInterface;
23
use Limoncello\Contracts\Data\RelationshipTypes;
24
use ReflectionClass;
25
use ReflectionException;
26
use function array_change_key_case;
27
use function array_key_exists;
28
use function array_keys;
29
use function assert;
30
use function strtolower;
31
32
/**
33
 * @package Limoncello\Application
34
 *
35
 * @SuppressWarnings(PHPMD.LongVariable)
36
 */
37
class ModelSchemaInfo implements ModelSchemaInfoInterface
38
{
39
    /**
40
     * @var array
41
     */
42
    private $relationshipTypes = [];
43
44
    /**
45
     * @var array
46
     */
47
    private $reversedRelationships = [];
48
49
    /**
50
     * @var array
51
     */
52
    private $reversedClasses = [];
53
54
    /**
55
     * @var array
56
     */
57
    private $foreignKeys = [];
58
59
    /**
60
     * @var array
61
     */
62
    private $belongsToMany = [];
63
64
    /**
65
     * @var array
66
     */
67
    private $tableNames = [];
68
69
    /**
70
     * @var array
71
     */
72
    private $primaryKeys = [];
73
74
    /**
75
     * @var array
76
     */
77
    private $attributeTypes = [];
78
79
    /**
80
     * @var array
81
     */
82
    private $attributeLengths = [];
83
84
    /**
85
     * @var array
86
     */
87
    private $attributes = [];
88 8
89
    /**
90
     * @var array
91 8
     */
92 8
    private $rawAttributes = [];
93 8
94 8
    /**
95 8
     * @return array
96 8
     */
97 8
    public function getData(): array
98 8
    {
99 8
        $result = [
100 8
            $this->foreignKeys,
101 8
            $this->belongsToMany,
102
            $this->relationshipTypes,
103
            $this->reversedRelationships,
104 8
            $this->tableNames,
105
            $this->primaryKeys,
106
            $this->attributeTypes,
107
            $this->attributeLengths,
108
            $this->attributes,
109
            $this->rawAttributes,
110
            $this->reversedClasses,
111
        ];
112 7
113
        return $result;
114 7
    }
115 7
116 7
    /**
117 7
     * @param array $data
118
     *
119 7
     * @return self
120
     */
121
    public function setData(array $data): self
122
    {
123
        list($this->foreignKeys, $this->belongsToMany, $this->relationshipTypes,
124
            $this->reversedRelationships,$this->tableNames, $this->primaryKeys,
125
            $this->attributeTypes, $this->attributeLengths, $this->attributes, $this->rawAttributes,
126
            $this->reversedClasses) = $data;
127 8
128
        return $this;
129
    }
130
131
    /** @noinspection PhpTooManyParametersInspection
132
     * @inheritdoc
133
     *
134
     * @throws ReflectionException
135 8
     */
136 1
    public function registerClass(
137
        string $class,
138
        string $tableName,
139 8
        string $primaryKey,
140 1
        array $attributeTypes,
141
        array $attributeLengths,
142
        array $rawAttributes = []
143 8
    ): ModelSchemaInfoInterface {
144 1
        if (empty($class) === true) {
145
            throw new InvalidArgumentException('class');
146
        }
147 8
148 8
        if (empty($tableName) === true) {
149 8
            throw new InvalidArgumentException('tableName');
150
        }
151
152 8
        if (empty($primaryKey) === true) {
153 8
            throw new InvalidArgumentException('primaryKey');
154 8
        }
155 8
156 8
        assert(
157 8
            (new ReflectionClass($class))->getName() === $class,
158
            "Please check name for class `$class`. It should be case sensitive."
159 8
        );
160
161
        $this->tableNames[$class]       = $tableName;
162
        $this->primaryKeys[$class]      = $primaryKey;
163
        $this->attributeTypes[$class]   = $attributeTypes;
164
        $this->attributeLengths[$class] = $attributeLengths;
165 2
        $this->attributes[$class]       = array_keys($attributeTypes);
166
        $this->rawAttributes[$class]    = $rawAttributes;
167 2
168
        return $this;
169
    }
170 2
171 2
    /**
172 2
     * @inheritdoc
173
     */
174
    public function hasClass(string $class): bool
175 2
    {
176
        $result = array_key_exists($class, $this->tableNames);
177
178
        // check if not found it cannot be found case insensitive (protection from case insensitive values)
179
        assert(
180
            $result === true ||
181 2
            in_array(strtolower($class), array_change_key_case($this->tableNames, CASE_LOWER)) === false
182
        );
183 2
184
        return $result;
185 2
    }
186
187 2
    /**
188
     * @inheritdoc
189
     */
190
    public function getTable(string $class): string
191
    {
192
        assert($this->hasClass($class));
193 2
194
        $result = $this->tableNames[$class];
195 2
196
        return $result;
197 2
    }
198
199 2
    /**
200
     * @inheritdoc
201
     */
202
    public function getPrimaryKey(string $class): string
203
    {
204
        assert($this->hasClass($class));
205 1
206
        $result = $this->primaryKeys[$class];
207 1
208
        return $result;
209 1
    }
210
211 1
    /**
212
     * @inheritdoc
213
     */
214
    public function getAttributeTypes(string $class): array
215
    {
216
        assert($this->hasClass($class));
217 1
218
        $result = $this->attributeTypes[$class];
219 1
220 1
        return $result;
221 1
    }
222
223
    /**
224 1
     * @inheritdoc
225
     */
226 1
    public function getAttributeType(string $class, string $name): string
227
    {
228
        assert(
229
            $this->hasAttributeType($class, $name),
230
            "Type is not defined for attribute `$name` in class `$class`."
231
        );
232 1
233
        $result = $this->attributeTypes[$class][$name];
234 1
235
        return $result;
236 1
    }
237
238 1
    /**
239
     * @inheritdoc
240
     */
241 View Code Duplication
    public function hasAttributeType(string $class, string $name): bool
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...
242
    {
243
        assert($this->hasClass($class));
244 1
245
        $result = isset($this->attributeTypes[$class][$name]);
246 1
247
        return $result;
248 1
    }
249
250 1
    /**
251
     * @inheritdoc
252
     */
253
    public function getAttributeLengths(string $class): array
254
    {
255
        assert($this->hasClass($class));
256 1
257
        $result = $this->attributeLengths[$class];
258 1
259
        return $result;
260 1
    }
261
262 1
    /**
263
     * @inheritdoc
264
     */
265 View Code Duplication
    public function hasAttributeLength(string $class, string $name): bool
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...
266
    {
267
        assert($this->hasClass($class));
268 1
269
        $result = isset($this->attributeLengths[$class][$name]);
270 1
271 1
        return $result;
272 1
    }
273
274
    /**
275 1
     * @inheritdoc
276
     */
277 1 View Code Duplication
    public function getAttributeLength(string $class, string $name): int
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...
278
    {
279
        assert(
280
            $this->hasAttributeLength($class, $name) === true,
281
            "Length not found for column `$name` in class `$class`."
282
        );
283 1
284
        $result = $this->attributeLengths[$class][$name];
285 1
286
        return $result;
287 1
    }
288
289 1
    /**
290
     * @inheritdoc
291
     */
292
    public function getAttributes(string $class): array
293
    {
294
        assert($this->hasClass($class));
295 1
296
        $result = $this->attributes[$class];
297 1
298
        return $result;
299 1
    }
300
301 1
    /**
302
     * @inheritdoc
303
     */
304
    public function getRawAttributes(string $class): array
305
    {
306
        assert($this->hasClass($class));
307 2
308
        $result = $this->rawAttributes[$class];
309 2
310
        return $result;
311 2
    }
312
313
    /**
314
     * @inheritdoc
315
     */
316
    public function hasRelationship(string $class, string $name): bool
317 2
    {
318
        $result = isset($this->relationshipTypes[$class][$name]);
319 2
320 2
        return $result;
321 2
    }
322
323
    /**
324 2
     * @inheritdoc
325
     */
326 2 View Code Duplication
    public function getRelationshipType(string $class, string $name): int
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...
327
    {
328
        assert(
329
            $this->hasRelationship($class, $name) === true,
330
            "Relationship `$name` not found in class `$class`."
331
        );
332 2
333
        $result = $this->relationshipTypes[$class][$name];
334 2
335
        return $result;
336 2
    }
337
338
    /**
339
     * @inheritdoc
340
     */
341
    public function getReverseRelationship(string $class, string $name): array
342 1
    {
343
        $result = $this->reversedRelationships[$class][$name];
344 1
345
        return $result;
346 1
    }
347 1
348
    /**
349 1
     * @inheritdoc
350
     */
351 View Code Duplication
    public function getReversePrimaryKey(string $class, string $name): array
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...
352
    {
353
        $reverseClass = $this->getReverseModelClass($class, $name);
354
355 1
        $table = $this->getTable($reverseClass);
356
        $key   = $this->getPrimaryKey($reverseClass);
357 1
358
        return [$key, $table];
359 1
    }
360
361 1
    /**
362
     * @inheritdoc
363 1
     */
364 View Code Duplication
    public function getReverseForeignKey(string $class, string $name): array
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...
365
    {
366
        list ($reverseClass, $reverseName) = $this->getReverseRelationship($class, $name);
367
368
        $table = $this->getTable($reverseClass);
369 1
        // would work only if $name is hasMany relationship
370
        $key   = $this->getForeignKey($reverseClass, $reverseName);
371 1
372
        return [$key, $table];
373 1
    }
374
375
    /**
376
     * @inheritdoc
377
     */
378
    public function getReverseModelClass(string $class, string $name): string
379 1
    {
380
        $reverseClass = $this->reversedClasses[$class][$name];
381 1
382
        return $reverseClass;
383 1
    }
384
385
    /**
386
     * @inheritdoc
387
     */
388
    public function getForeignKey(string $class, string $name): string
389 1
    {
390
        $result = $this->foreignKeys[$class][$name];
391 1
392
        return $result;
393 1
    }
394
395
    /**
396
     * @inheritdoc
397
     */
398
    public function getBelongsToManyRelationship(string $class, string $name): array
399 8
    {
400
        $result = $this->belongsToMany[$class][$name];
401
402
        return $result;
403
    }
404
405
    /**
406 8
     * @inheritdoc
407 8
     */
408
    public function registerBelongsToOneRelationship(
409 8
        string $class,
410 8
        string $name,
411
        string $foreignKey,
412 8
        string $reverseClass,
413
        string $reverseName
414 8
    ): ModelSchemaInfoInterface {
415
        $this->registerRelationshipType(RelationshipTypes::BELONGS_TO, $class, $name);
416
        $this->registerRelationshipType(RelationshipTypes::HAS_MANY, $reverseClass, $reverseName);
417
418
        $this->registerReversedRelationship($class, $name, $reverseClass, $reverseName);
419
        $this->registerReversedRelationship($reverseClass, $reverseName, $class, $name);
420 8
421
        $this->foreignKeys[$class][$name] = $foreignKey;
422
423
        return $this;
424
    }
425
426
    /** @noinspection PhpTooManyParametersInspection
427
     * @inheritdoc
428
     */
429 8
    public function registerBelongsToManyRelationship(
430 8
        string $class,
431
        string $name,
432
        string $table,
433
        string $foreignKey,
434
        string $reverseForeignKey,
435 8
        string $reverseClass,
436 8
        string $reverseName
437
    ): ModelSchemaInfoInterface {
438 8
        $this->registerRelationshipType(RelationshipTypes::BELONGS_TO_MANY, $class, $name);
439 8
        $this->registerRelationshipType(RelationshipTypes::BELONGS_TO_MANY, $reverseClass, $reverseName);
440
441 8
        // NOTE:
442
        // `registerReversedRelationship` relies on duplicate registration check in `registerRelationshipType`
443
        // so it must be called afterwards
444
        $this->registerReversedRelationship($class, $name, $reverseClass, $reverseName);
445
        $this->registerReversedRelationship($reverseClass, $reverseName, $class, $name);
446
447
        $this->belongsToMany[$class][$name]               = [$table, $foreignKey, $reverseForeignKey];
448
        $this->belongsToMany[$reverseClass][$reverseName] = [$table, $reverseForeignKey, $foreignKey];
449
450
        return $this;
451 8
    }
452
453 8
    /**
454 8
     * @param int    $type
455 8
     * @param string $class
456 8
     * @param string $name
457
     *
458
     * @return void
459 8
     */
460
    private function registerRelationshipType(int $type, string $class, string $name): void
461
    {
462
        assert(empty($class) === false && empty($name) === false);
463
        assert(
464
            isset($this->relationshipTypes[$class][$name]) === false,
465
            "Relationship `$name` for class `$class` was already used."
466
        );
467
468
        $this->relationshipTypes[$class][$name] = $type;
469
    }
470 8
471
    /**
472
     * @param string $class
473
     * @param string $name
474
     * @param string $reverseClass
475
     * @param string $reverseName
476 8
     *
477 8
     * @return void
478 8
     */
479 8
    private function registerReversedRelationship(
480 8
        string $class,
481
        string $name,
482
        string $reverseClass,
483
        string $reverseName
484
    ): void {
485
        assert(
486
            empty($class) === false &&
487 8
            empty($name) === false &&
488 8
            empty($reverseClass) === false &&
489
            empty($reverseName) === false
490
        );
491
492
        // NOTE:
493
        // this function relies it would be called after
494
        // `registerRelationshipType` which prevents duplicate registrations
495
496
        $this->reversedRelationships[$class][$name] = [$reverseClass, $reverseName];
497
        $this->reversedClasses[$class][$name]       = $reverseClass;
498
    }
499
}
500