This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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
|
|||
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
![]() |
|||
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
![]() |
|||
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
![]() |
|||
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
![]() |
|||
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. ![]() |
|||
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. ![]() |
|||
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 |
If you access a property on an interface, you most likely code against a concrete implementation of the interface.
Available Fixes
Adding an additional type check:
Changing the type hint: