Issues (104)

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/Metadata/Driver/YamlFileDriver.php (11 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 As3\Modlr\Metadata\Driver;
4
5
use As3\Modlr\Metadata;
6
use As3\Modlr\Exception\RuntimeException;
7
use As3\Modlr\Exception\MetadataException;
8
use Symfony\Component\Yaml\Yaml;
9
10
/**
11
 * The YAML metadata file driver.
12
 *
13
 * @author Jacob Bare <[email protected]>
14
 */
15
final class YamlFileDriver extends AbstractFileDriver
16
{
17
    /**
18
     * An in-memory cache of parsed metadata mappings (from file).
19
     *
20
     * @var array
21
     */
22
    private $mappings = [
23
        'model' => [],
24
        'mixin' => [],
25
        'embed' => [],
26
    ];
27
28
    /**
29
     * {@inheritDoc}
30
     */
31
    protected function loadMetadataFromFile($type, $file)
32
    {
33
        $mapping = $this->getMapping('model', $type, $file);
34
35
        $metadata = new Metadata\EntityMetadata($type);
36
37
        if (isset($mapping['entity']['abstract'])) {
38
            $metadata->setAbstract(true);
39
        }
40
41 View Code Duplication
        if (isset($mapping['entity']['polymorphic'])) {
0 ignored issues
show
This code seems to be duplicated across 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...
42
            $metadata->setPolymorphic(true);
43
            $metadata->ownedTypes = $this->getOwnedTypes($metadata->type);
44
        }
45
46
        if (isset($mapping['entity']['extends'])) {
47
            $metadata->extends = $mapping['entity']['extends'];
48
        }
49
50 View Code Duplication
        if (isset($mapping['entity']['defaultValues']) && is_array($mapping['entity']['defaultValues'])) {
0 ignored issues
show
This code seems to be duplicated across 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...
51
            $metadata->defaultValues = $mapping['entity']['defaultValues'];
52
        }
53
54
        $this->setPersistence($metadata, $mapping['entity']['persistence']);
55
        $this->setSearch($metadata, $mapping['entity']['search']);
56
        $this->setAttributes($metadata, $mapping['attributes']);
57
        $this->setRelationships($metadata, $mapping['relationships']);
58
        $this->setEmbeds($metadata, $mapping['embeds']);
59
        $this->setMixins($metadata, $mapping['mixins']);
60
        return $metadata;
61
    }
62
63
    /**
64
     * {@inheritDoc}
65
     */
66 View Code Duplication
    protected function loadEmbedFromFile($embedName, $file)
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...
67
    {
68
        $mapping = $this->getMapping('embed', $embedName, $file);
69
70
        $embed = new Metadata\EmbedMetadata($embedName);
71
        $this->setAttributes($embed, $mapping['attributes']);
72
        $this->setEmbeds($embed, $mapping['embeds']);
73
        $this->setMixins($embed, $mapping['mixins']);
74
        return $embed;
75
    }
76
77
    /**
78
     * {@inheritDoc}
79
     */
80 View Code Duplication
    protected function loadMixinFromFile($mixinName, $file)
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...
81
    {
82
        $mapping = $this->getMapping('mixin', $mixinName, $file);
83
84
        $mixin = new Metadata\MixinMetadata($mixinName);
85
86
        $this->setAttributes($mixin, $mapping['attributes']);
87
        $this->setEmbeds($mixin, $mapping['embeds']);
88
        $this->setRelationships($mixin, $mapping['relationships']);
89
        return $mixin;
90
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function getTypeHierarchy($type, array $types = [])
96
    {
97
        $path = $this->getFilePath('model', $type);
98
        $mapping = $this->getMapping('model', $type, $path);
99
100
        $types[] = $type;
101
        if (isset($mapping['entity']['extends']) && $mapping['entity']['extends'] !== $type) {
102
            return $this->getTypeHierarchy($mapping['entity']['extends'], $types);
103
        }
104
        return array_reverse($types);
105
    }
106
107
    /**
108
     * {@inheritDoc}
109
     */
110
    public function getOwnedTypes($type, array $types = [])
111
    {
112
        $path = $this->getFilePath('model', $type);
113
        $superMapping = $this->getMapping('model', $type, $path);
114
115
        $abstract = isset($superMapping['entity']['abstract']) && true === $superMapping['entity']['abstract'];
116
117
        foreach ($this->getAllTypeNames() as $searchType) {
118
119
            if ($type === $searchType && false === $abstract) {
120
                $types[] = $type;
121
                continue;
122
            }
123
            if (0 !== strpos($searchType, $type)) {
124
                continue;
125
            }
126
127
            $path = $this->getFilePath('model', $searchType);
128
            $mapping = $this->getMapping('model', $searchType, $path);
129
130
            if (!isset($mapping['entity']['extends']) || $mapping['entity']['extends'] !== $type) {
131
                continue;
132
            }
133
            $types[] = $searchType;
134
        }
135
        return $types;
136
    }
137
138
    /**
139
     * Gets the metadata mapping information from the YAML file.
140
     *
141
     * @param   string  $metaType   The metadata type, either mixin or model.
142
     * @param   string  $key        The metadata key name, either the mixin name or model type.
143
     * @param   string  $file       The YAML file location.
144
     * @return  array
145
     * @throws  MetadataException
146
     */
147
    private function getMapping($metaType, $key, $file)
148
    {
149
        if (isset($this->mappings[$metaType][$key])) {
150
            // Set to array cache to prevent multiple gets/parses.
151
            return $this->mappings[$metaType][$key];
152
        }
153
154
        $contents = Yaml::parse(file_get_contents($file));
155
        if (!isset($contents[$key])) {
156
            throw MetadataException::fatalDriverError($key, sprintf('No mapping key was found at the beginning of the YAML file. Expected "%s"', $key));
157
        }
158
        return $this->mappings[$metaType][$key] = $this->setDefaults($metaType, $contents[$key]);
159
    }
160
161
    /**
162
     * Sets the entity persistence metadata from the metadata mapping.
163
     *
164
     * @param   Metadata\EntityMetadata     $metadata
165
     * @param   array                       $mapping
166
     * @return  Metadata\EntityMetadata
167
     */
168 View Code Duplication
    protected function setPersistence(Metadata\EntityMetadata $metadata, array $mapping)
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...
169
    {
170
        $persisterKey = isset($mapping['key']) ? $mapping['key'] : null;
171
        $factory = $this->getPersistenceMetadataFactory($persisterKey);
172
173
        $persistence = $factory->createInstance($mapping);
174
175
        $metadata->setPersistence($persistence);
176
        return $metadata;
177
    }
178
179
    /**
180
     * Sets the entity search metadata from the metadata mapping.
181
     *
182
     * @param   Metadata\EntityMetadata     $metadata
183
     * @param   array                       $mapping
184
     * @return  Metadata\EntityMetadata
185
     */
186 View Code Duplication
    protected function setSearch(Metadata\EntityMetadata $metadata, array $mapping)
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...
187
    {
188
        $clientKey = isset($mapping['key']) ? $mapping['key'] : null;
189
        if (null === $clientKey) {
190
            // Search is not enabled for this model.
191
            return $metadata;
192
        }
193
194
        $factory = $this->getSearchMetadataFactory($clientKey);
195
196
        $search = $factory->createInstance($mapping);
197
198
        $metadata->setSearch($search);
199
        return $metadata;
200
    }
201
202
    /**
203
     * Sets the entity attribute metadata from the metadata mapping.
204
     *
205
     * @param   Metadata\Interfaces\AttributeInterface  $metadata
206
     * @param   array                                   $attrMapping
207
     * @return  Metadata\EntityMetadata
208
     */
209
    protected function setAttributes(Metadata\Interfaces\AttributeInterface $metadata, array $attrMapping)
210
    {
211
        foreach ($attrMapping as $key => $mapping) {
212
            if (!is_array($mapping)) {
213
                $mapping = ['type' => null];
214
            }
215
216
            if (!isset($mapping['type'])) {
217
                $mapping['type'] = null;
218
            }
219
220
            if (!isset($mapping['search'])) {
221
                $mapping['search'] = [];
222
            }
223
224
            $attribute = new Metadata\AttributeMetadata($key, $mapping['type'], $this->isMixin($metadata));
225
226
            if (isset($mapping['description'])) {
227
                $attribute->description = $mapping['description'];
228
            }
229
230
            if (isset($mapping['defaultValue'])) {
231
                $attribute->defaultValue = $mapping['defaultValue'];
232
            }
233
234
            if (isset($mapping['calculated']) && is_array($mapping['calculated'])) {
235
                $calculated = $mapping['calculated'];
236
                if (isset($calculated['class']) && isset($calculated['method'])) {
237
                    $attribute->calculated['class']  =  $calculated['class'];
238
                    $attribute->calculated['method'] =  $calculated['method'];
239
                }
240
            }
241
242
            if (isset($mapping['search']['autocomplete'])) {
243
                $attribute->setAutocomplete(true);
244
            } else if (isset($mapping['search']['store'])) {
245
                $attribute->setSearchProperty(true);
246
            }
247
248
            if (isset($mapping['save'])) {
249
                $attribute->enableSave($mapping['save']);
250
            }
251
252
            if (isset($mapping['serialize'])) {
253
                $attribute->enableSerialize($mapping['serialize']);
254
            }
255
256
            $metadata->addAttribute($attribute);
257
        }
258
        return $metadata;
259
    }
260
261
    /**
262
     * Sets the entity embed metadata from the metadata mapping.
263
     *
264
     * @param   Metadata\Interfaces\EmbedInterface  $metadata
265
     * @param   array                               $embedMapping
266
     * @return  Metadata\EntityMetadata
267
     */
268
    protected function setEmbeds(Metadata\Interfaces\EmbedInterface $metadata, array $embedMapping)
269
    {
270
        foreach ($embedMapping as $key => $mapping) {
271 View Code Duplication
            if (!is_array($mapping)) {
0 ignored issues
show
This code seems to be duplicated across 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...
272
                $mapping = ['type' => null, 'entity' => null];
273
            }
274
275
            if (!isset($mapping['type'])) {
276
                $mapping['type'] = null;
277
            }
278
279
            if (!isset($mapping['entity'])) {
280
                $mapping['entity'] = null;
281
            }
282
283
            if ($metadata instanceof Metadata\EmbedMetadata && $mapping['entity'] === $metadata->name) {
284
                // This embed meta is referencing itself as an embed property. As such, do not recreate.
285
                // This prevents infinite recursion.
286
                $embedMeta = $metadata;
287
            } else {
288
                $embedMeta = $this->loadMetadataForEmbed($mapping['entity']);
289
            }
290
            if (null === $embedMeta) {
291
                continue;
292
            }
293
            $property = new Metadata\EmbeddedPropMetadata($key, $mapping['type'], $embedMeta, $this->isMixin($metadata));
294
295
            if (isset($mapping['serialize'])) {
296
                $property->enableSerialize($mapping['serialize']);
297
            }
298
299
            $metadata->addEmbed($property);
300
        }
301
        return $metadata;
302
    }
303
304
    /**
305
     * Sets creates mixin metadata instances from a set of mixin mappings ands sets them to the entity metadata instance.
306
     *
307
     * @param   Metadata\Interfaces\MixinInterface  $metadata
308
     * @param   array                   $mixins
309
     * @return  Metadata\Interfaces\MixinInterface
310
     */
311
    protected function setMixins(Metadata\Interfaces\MixinInterface $metadata, array $mixins)
312
    {
313
        foreach ($mixins as $mixinName) {
314
            $mixinMeta = $this->loadMetadataForMixin($mixinName);
315
            if (null === $mixinMeta) {
316
                continue;
317
            }
318
            $metadata->addMixin($mixinMeta);
319
        }
320
        return $metadata;
321
    }
322
323
    /**
324
     * Sets the entity relationship metadata from the metadata mapping.
325
     *
326
     * @param   Metadata\Interfaces\RelationshipInterface   $metadata
327
     * @param   array                                       $relMapping
328
     * @return  Metadata\Interfaces\RelationshipInterface
329
     * @throws  RuntimeException If the related entity type was not found.
330
     */
331
    protected function setRelationships(Metadata\Interfaces\RelationshipInterface $metadata, array $relMapping)
332
    {
333
        foreach ($relMapping as $key => $mapping) {
334 View Code Duplication
            if (!is_array($mapping)) {
0 ignored issues
show
This code seems to be duplicated across 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...
335
                $mapping = ['type' => null, 'entity' => null];
336
            }
337
338
            if (!isset($mapping['type'])) {
339
                $mapping['type'] = null;
340
            }
341
342
            if (!isset($mapping['entity'])) {
343
                $mapping['entity'] = null;
344
            }
345
346
            if (!isset($mapping['search'])) {
347
                $mapping['search'] = [];
348
            }
349
350
            $relationship = new Metadata\RelationshipMetadata($key, $mapping['type'], $mapping['entity'], $this->isMixin($metadata));
351
352
            if (isset($mapping['description'])) {
353
                $relationship->description = $mapping['description'];
354
            }
355
356
            if (isset($mapping['inverse'])) {
357
                $relationship->isInverse = true;
358
                if (isset($mapping['field'])) {
359
                    $relationship->inverseField = $mapping['field'];
360
                }
361
            }
362
363
            $path = $this->getFilePath('model', $mapping['entity']);
364
            $relatedEntityMapping = $this->getMapping('model', $mapping['entity'], $path);
365
366 View Code Duplication
            if (isset($relatedEntityMapping['entity']['polymorphic'])) {
0 ignored issues
show
This code seems to be duplicated across 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...
367
                $relationship->setPolymorphic(true);
368
                $relationship->ownedTypes = $this->getOwnedTypes($mapping['entity']);
369
            }
370
371
            if (isset($mapping['search']['store'])) {
372
                $relationship->setSearchProperty(true);
373
            }
374
375
            if (isset($mapping['save'])) {
376
                $relationship->enableSave($mapping['save']);
377
            }
378
379
            if (isset($mapping['serialize'])) {
380
                $relationship->enableSerialize($mapping['serialize']);
381
            }
382
383
            $metadata->addRelationship($relationship);
384
        }
385
        return $metadata;
386
    }
387
388
    /**
389
     * Determines if a metadata instance is a mixin.
390
     *
391
     * @param   Metadata\Interfaces\PropertyInterface   $metadata
392
     * @return  bool
393
     */
394
    protected function isMixin(Metadata\Interfaces\PropertyInterface $metadata)
395
    {
396
        return $metadata instanceof Metadata\MixinMetadata;
397
    }
398
399
    /**
400
     * Sets default values to the metadata mapping array.
401
     *
402
     * @param   string  $metaType   The metadata type, either model or mixin.
403
     * @param   mixed   $mapping    The parsed mapping data.
404
     * @return  array
405
     */
406
    protected function setDefaults($metaType, $mapping)
407
    {
408
        if (!is_array($mapping)) {
409
            $mapping = [];
410
        }
411
412
        $mapping = $this->setRootDefault('attributes', $mapping);
413
        $mapping = $this->setRootDefault('embeds', $mapping);
414
        $mapping = $this->setRootDefault('mixins', $mapping);
415
        if ('embed' === $metaType) {
416
            return $mapping;
417
        }
418
419
        $mapping = $this->setRootDefault('relationships', $mapping);
420
        if ('mixin' === $metaType) {
421
            return $mapping;
422
        }
423
        $this->setRootDefault('entity', $mapping);
424
425 View Code Duplication
        if (!isset($mapping['entity']['persistence']) || !is_array($mapping['entity']['persistence'])) {
0 ignored issues
show
This code seems to be duplicated across 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...
426
            $mapping['entity']['persistence'] = [];
427
        }
428
429 View Code Duplication
        if (!isset($mapping['entity']['search']) || !is_array($mapping['entity']['search'])) {
0 ignored issues
show
This code seems to be duplicated across 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...
430
            $mapping['entity']['search'] = [];
431
        }
432
        return $mapping;
433
    }
434
435
    /**
436
     * Sets a root level default value to a metadata mapping array.
437
     *
438
     * @param   string  $key
439
     * @param   array   $mapping
440
     * @return  array
441
     */
442
    private function setRootDefault($key, array $mapping)
443
    {
444
        if (!isset($mapping[$key]) || !is_array($mapping[$key])) {
445
            $mapping[$key] = [];
446
        }
447
        return $mapping;
448
    }
449
450
    /**
451
     * {@inheritDoc}
452
     */
453
    protected function getExtension()
454
    {
455
        return 'yml';
456
    }
457
}
458