Issues (9)

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/ArrayHydrator.php (7 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
namespace pmill\Doctrine\Hydrator;
3
4
use Doctrine\DBAL\Types\Type;
5
use Doctrine\ORM\EntityManagerInterface;
6
use Doctrine\ORM\Mapping\ClassMetadataInfo;
7
use Exception;
8
9
class ArrayHydrator
10
{
11
    /**
12
     * The keys in the data array are entity field names
13
     */
14
    const HYDRATE_BY_FIELD = 1;
15
16
    /**
17
     * The keys in the data array are database column names
18
     */
19
    const HYDRATE_BY_COLUMN = 2;
20
21
    /**
22
     * @var EntityManagerInterface
23
     */
24
    protected $entityManager;
25
26
    /**
27
     * If true, then associations are filled only with reference proxies. This is faster than querying them from
28
     * database, but if the associated entity does not really exist, it will cause:
29
     * * The insert/update to fail, if there is a foreign key defined in database
30
     * * The record ind database also pointing to a non-existing record
31
     *
32
     * @var bool
33
     */
34
    protected $hydrateAssociationReferences = true;
35
36
    /**
37
     * Tells whether the input data array keys are entity field names or database column names
38
     *
39
     * @var int one of ArrayHydrator::HIDRATE_BY_* constants
40
     */
41
    protected $hydrateBy = self::HYDRATE_BY_FIELD;
42
43
    /**
44
     * If true, hydrate the primary key too. Useful if the primary key is not automatically generated by the database
45
     *
46
     * @var bool
47
     */
48
    protected $hydrateId = false;
49
50
    /**
51
     * @param EntityManagerInterface $entityManager
52
     */
53
    public function __construct(EntityManagerInterface $entityManager)
54
    {
55
        $this->entityManager = $entityManager;
56
    }
57
58
    /**
59
     * @param $entity
60
     * @param array $data
61
     * @return mixed|object
62
     * @throws Exception
63
     */
64
    public function hydrate($entity, array $data)
65
    {
66
        if (is_string($entity) && class_exists($entity)) {
67
            $entity = new $entity;
68
        }
69
        elseif (!is_object($entity)) {
70
            throw new Exception('Entity passed to ArrayHydrator::hydrate() must be a class name or entity object');
71
        }
72
73
        $entity = $this->hydrateProperties($entity, $data);
74
        $entity = $this->hydrateAssociations($entity, $data);
75
        return $entity;
76
    }
77
78
    /**
79
     * @param boolean $hydrateAssociationReferences
80
     */
81
    public function setHydrateAssociationReferences($hydrateAssociationReferences)
82
    {
83
        $this->hydrateAssociationReferences = $hydrateAssociationReferences;
84
    }
85
86
    /**
87
     * @param bool $hydrateId
88
     */
89
    public function setHydrateId($hydrateId)
90
    {
91
        $this->hydrateId = $hydrateId;
92
    }
93
94
    /**
95
     * @param int $hydrateBy
96
     */
97
    public function setHydrateBy($hydrateBy)
98
    {
99
        $this->hydrateBy = $hydrateBy;
100
    }
101
102
    /**
103
     * @param object $entity the doctrine entity
104
     * @param array $data
105
     * @return object
106
     */
107
    protected function hydrateProperties($entity, $data)
108
    {
109
        $reflectionObject = new \ReflectionObject($entity);
110
111
        $metaData = $this->entityManager->getClassMetadata(get_class($entity));
112
        
113
        $platform = $this->entityManager->getConnection()
114
                                        ->getDatabasePlatform();
115
116
        $skipFields = $this->hydrateId ? [] : $metaData->identifier;
0 ignored issues
show
Accessing identifier on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
117
118
        foreach ($metaData->fieldNames as $fieldName) {
0 ignored issues
show
Accessing fieldNames on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
119
            $dataKey = $this->hydrateBy === self::HYDRATE_BY_FIELD ? $fieldName : $metaData->getColumnName($fieldName);
120
121
            if (array_key_exists($dataKey, $data) && !in_array($fieldName, $skipFields, true)) {
122
                $value = $data[$dataKey];
123
124
                if (array_key_exists('type', $metaData->fieldMappings[$fieldName])) {
0 ignored issues
show
Accessing fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
125
                    $fieldType = $metaData->fieldMappings[$fieldName]['type'];
0 ignored issues
show
Accessing fieldMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
126
127
                    $type = Type::getType($fieldType);
128
129
                    $value = $type->convertToPHPValue($value, $platform);
130
                }
131
132
                $entity = $this->setProperty($entity, $fieldName, $value, $reflectionObject);
133
            }
134
        }
135
136
        return $entity;
137
    }
138
139
    /**
140
     * @param $entity
141
     * @param $data
142
     * @return mixed
143
     */
144
    protected function hydrateAssociations($entity, $data)
145
    {
146
        $metaData = $this->entityManager->getClassMetadata(get_class($entity));
147
        foreach ($metaData->associationMappings as $fieldName => $mapping) {
0 ignored issues
show
Accessing associationMappings on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
148
            $associationData = $this->getAssociatedId($fieldName, $mapping, $data);
149
            if (!empty($associationData)) {
150 View Code Duplication
                if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_ONE, ClassMetadataInfo::MANY_TO_ONE])) {
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...
151
                    $entity = $this->hydrateToOneAssociation($entity, $fieldName, $mapping, $associationData);
152
                }
153
154 View Code Duplication
                if (in_array($mapping['type'], [ClassMetadataInfo::ONE_TO_MANY, ClassMetadataInfo::MANY_TO_MANY])) {
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...
155
                    $entity = $this->hydrateToManyAssociation($entity, $fieldName, $mapping, $associationData);
156
                }
157
            }
158
        }
159
160
        return $entity;
161
    }
162
163
    /**
164
     * Retrieves the associated entity's id from $data
165
     *
166
     * @param string $fieldName name of field that stores the associated entity
167
     * @param array $mapping doctrine's association mapping array for the field
168
     * @param array $data the hydration data
169
     *
170
     * @return mixed null, if the association is not found
171
     */
172
    protected function getAssociatedId($fieldName, $mapping, $data)
173
    {
174
        if ($this->hydrateBy === self::HYDRATE_BY_FIELD) {
175
176
            return isset($data[$fieldName]) ? $data[$fieldName] : null;
177
        }
178
179
        // from this point it is self::HYDRATE_BY_COLUMN
180
        // we do not support compound foreign keys (yet)
181
        if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) === 1) {
182
            $columnName = $mapping['joinColumns'][0]['name'];
183
184
            return isset($data[$columnName]) ? $data[$columnName] : null;
185
        }
186
187
        // If joinColumns does not exist, then this is not the owning side of an association
188
        // This should not happen with column based hydration
189
        return null;
190
    }
191
192
    /**
193
     * @param $entity
194
     * @param $propertyName
195
     * @param $mapping
196
     * @param $value
197
     * @return mixed
198
     */
199
    protected function hydrateToOneAssociation($entity, $propertyName, $mapping, $value)
200
    {
201
        $reflectionObject = new \ReflectionObject($entity);
202
203
        $toOneAssociationObject = $this->fetchAssociationEntity($mapping['targetEntity'], $value);
204
        if (!is_null($toOneAssociationObject)) {
205
            $entity = $this->setProperty($entity, $propertyName, $toOneAssociationObject, $reflectionObject);
206
        }
207
208
        return $entity;
209
    }
210
211
    /**
212
     * @param $entity
213
     * @param $propertyName
214
     * @param $mapping
215
     * @param $value
216
     * @return mixed
217
     */
218
    protected function hydrateToManyAssociation($entity, $propertyName, $mapping, $value)
219
    {
220
        $reflectionObject = new \ReflectionObject($entity);
221
        $values = is_array($value) ? $value : [$value];
222
223
        $assocationObjects = [];
224
        foreach ($values as $value) {
225
            if (is_array($value)) {
226
                $assocationObjects[] = $this->hydrate($mapping['targetEntity'], $value);
227
            }
228
            elseif ($associationObject = $this->fetchAssociationEntity($mapping['targetEntity'], $value)) {
229
                $assocationObjects[] = $associationObject;
230
            }
231
        }
232
233
        $entity = $this->setProperty($entity, $propertyName, $assocationObjects, $reflectionObject);
234
235
        return $entity;
236
    }
237
238
    /**
239
     * @param $entity
240
     * @param $propertyName
241
     * @param $value
242
     * @param null $reflectionObject
243
     * @return mixed
244
     */
245
    protected function setProperty($entity, $propertyName, $value, $reflectionObject = null)
246
    {
247
        $reflectionObject = is_null($reflectionObject) ? new \ReflectionObject($entity) : $reflectionObject;
248
        $property = $reflectionObject->getProperty($propertyName);
249
        $property->setAccessible(true);
250
        $property->setValue($entity, $value);
251
        return $entity;
252
    }
253
254
    /**
255
     * @param $className
256
     * @param $id
257
     * @return bool|\Doctrine\Common\Proxy\Proxy|null|object
258
     * @throws \Doctrine\ORM\ORMException
259
     * @throws \Doctrine\ORM\OptimisticLockException
260
     * @throws \Doctrine\ORM\TransactionRequiredException
261
     */
262
    protected function fetchAssociationEntity($className, $id)
263
    {
264
        if ($this->hydrateAssociationReferences) {
265
            return $this->entityManager->getReference($className, $id);
266
        }
267
268
        return $this->entityManager->find($className, $id);
269
    }
270
}
271