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 | |||
3 | namespace Respect\Structural; |
||
4 | |||
5 | use Exception; |
||
6 | use Respect\Data\AbstractMapper; |
||
7 | use Respect\Data\CollectionIterator; |
||
8 | use Respect\Data\Collections as c; |
||
9 | use Respect\Data\Collections\Collection; |
||
10 | use SplObjectStorage; |
||
11 | |||
12 | /** Maps objects to nosql operations */ |
||
13 | class Mapper extends AbstractMapper implements |
||
14 | c\Filterable, |
||
15 | c\Mixable, |
||
16 | c\Typable |
||
17 | { |
||
18 | /** @var \Respect\Structural\Driver Holds our connector* */ |
||
19 | protected $driver; |
||
20 | |||
21 | /** @var string Namespace to look for entities * */ |
||
22 | public $entityNamespace = '\\'; |
||
23 | |||
24 | /** |
||
25 | * @param Driver $driver |
||
26 | */ |
||
27 | 3 | public function __construct(Driver $driver) |
|
28 | { |
||
29 | 3 | parent::__construct(); |
|
30 | |||
31 | 3 | $this->driver = $driver; |
|
32 | 3 | } |
|
33 | |||
34 | /** |
||
35 | * @return Mapper |
||
36 | */ |
||
37 | 3 | public function __get($name) |
|
38 | { |
||
39 | 3 | return parent::__get($name); |
|
40 | } |
||
41 | |||
42 | /** |
||
43 | * Flushes a single instance into the database. This method supports |
||
44 | * mixing, so flushing a mixed instance will flush distinct tables on the |
||
45 | * database |
||
46 | * |
||
47 | * @param object $entity Entity instance to be flushed |
||
48 | * |
||
49 | * @return null |
||
50 | */ |
||
51 | 3 | protected function flushSingle($entity) |
|
52 | { |
||
53 | 3 | $coll = $this->tracked[$entity]; |
|
54 | 3 | $cols = $this->extractColumns($entity, $coll); |
|
55 | |||
56 | 3 | if ($this->removed->contains($entity)) { |
|
57 | 1 | $this->rawDelete($coll, $entity); |
|
58 | 3 | } elseif ($this->new->contains($entity)) { |
|
59 | 1 | $this->rawInsert($coll, $entity); |
|
60 | 1 | } else { |
|
61 | 1 | $this->rawUpdate($cols, $coll); |
|
62 | } |
||
63 | 3 | } |
|
64 | |||
65 | 2 | public function persist($object, Collection $onCollection) |
|
66 | { |
||
67 | 2 | $next = $onCollection->getNext(); |
|
68 | |||
69 | 2 | if ($this->filterable($onCollection)) { |
|
70 | $next->setMapper($this); |
||
71 | $next->persist($object); |
||
72 | |||
73 | return; |
||
74 | } |
||
75 | |||
76 | 2 | if ($next) { |
|
77 | $remote = $this->getStyle()->remoteIdentifier($next->getName()); |
||
78 | $next->setMapper($this); |
||
79 | $next->persist($object->$remote); |
||
80 | } |
||
81 | |||
82 | 2 | foreach ($onCollection->getChildren() as $child) { |
|
83 | $remote = $this->getStyle()->remoteIdentifier($child->getName()); |
||
84 | $child->persist($object->$remote); |
||
85 | 2 | } |
|
86 | |||
87 | 2 | return parent::persist($object, $onCollection); |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * Receives columns from an entity and her collection. Returns the columns |
||
92 | * that belong only to the main entity. This method supports mixing, so |
||
93 | * extracting mixins will also persist them on their respective |
||
94 | * tables |
||
95 | * |
||
96 | * @param \Respect\Data\Collections\Collection $collection Target collection |
||
97 | * @param array $cols Entity columns |
||
98 | * |
||
99 | * @return array Columns left for the main collection |
||
100 | */ |
||
101 | 1 | protected function extractAndOperateMixins(Collection $collection, $cols) |
|
102 | { |
||
103 | 1 | if (!$this->mixable($collection)) { |
|
104 | 1 | return $cols; |
|
105 | } |
||
106 | |||
107 | foreach ($this->getMixins($collection) as $mix => $spec) { |
||
108 | //Extract from $cols only the columns from the mixin |
||
109 | $mixCols = array_intersect_key( |
||
110 | $cols, |
||
111 | array_combine(//create array with keys only |
||
112 | $spec, |
||
113 | array_fill(0, count($spec), '') |
||
114 | ) |
||
115 | ); |
||
116 | if (isset($cols["{$mix}_id"])) { |
||
117 | $mixCols['id'] = $cols["{$mix}_id"]; |
||
118 | $cols = array_diff($cols, $mixCols); //Remove mixin columns |
||
119 | $this->rawUpdate($mixCols, $this->__get($mix)); |
||
120 | } else { |
||
121 | $mixCols['id'] = null; |
||
122 | $cols = array_diff($cols, $mixCols); //Remove mixin columns |
||
123 | $this->rawinsert($mixCols, $this->__get($mix)); |
||
124 | } |
||
125 | } |
||
126 | |||
127 | return $cols; |
||
128 | } |
||
129 | |||
130 | 2 | protected function guessCondition(&$columns, Collection $collection) |
|
131 | { |
||
132 | 2 | $primaryName = $this->getStyle()->identifier($collection->getName()); |
|
133 | 2 | $condition = [$primaryName => $columns[$primaryName]]; |
|
134 | 2 | unset($columns[$primaryName]); |
|
135 | |||
136 | 2 | return $condition; |
|
137 | } |
||
138 | |||
139 | 1 | protected function rawDelete(Collection $collection, $entity) |
|
140 | { |
||
141 | 1 | $name = $collection->getName(); |
|
142 | 1 | $columns = $this->extractColumns($entity, $collection); |
|
143 | 1 | $condition = $this->guessCondition($columns, $collection); |
|
144 | |||
145 | 1 | return $this->driver->remove($name, $condition); |
|
146 | } |
||
147 | |||
148 | 1 | protected function rawUpdate(array $columns, Collection $collection) |
|
149 | { |
||
150 | 1 | $columns = $this->extractAndOperateMixins($collection, $columns); |
|
151 | 1 | $name = $collection->getName(); |
|
152 | 1 | $condition = $this->guessCondition($columns, $collection); |
|
153 | |||
154 | 1 | $this->driver->update($name, $condition, $columns); |
|
155 | 1 | } |
|
156 | |||
157 | 1 | protected function rawInsert(Collection $collection, $entity = null) |
|
158 | { |
||
159 | 1 | $name = $collection->getName(); |
|
160 | 1 | $this->driver->insert($name, $entity); |
|
161 | 1 | } |
|
162 | |||
163 | 3 | public function flush() |
|
164 | { |
||
165 | try { |
||
166 | 3 | foreach ($this->changed as $entity) { |
|
167 | 3 | $this->flushSingle($entity); |
|
168 | 3 | } |
|
169 | 3 | } catch (Exception $e) { |
|
170 | throw $e; |
||
171 | } |
||
172 | |||
173 | 3 | $this->reset(); |
|
174 | 3 | } |
|
175 | |||
176 | 2 | protected function createStatement(Collection $collection, $withExtra = null) |
|
177 | { |
||
178 | 2 | $query = $this->generateQuery($collection); |
|
179 | |||
180 | 2 | $withExtraList = (array)$withExtra; |
|
181 | |||
182 | 2 | $withExtraList = array_merge($withExtraList, $query); |
|
183 | |||
184 | 2 | return $this->driver->find($collection->getName(), $withExtraList); |
|
185 | } |
||
186 | |||
187 | 2 | protected function generateQuery(Collection $collection) |
|
188 | { |
||
189 | 2 | return $this->driver->generateQuery($collection); |
|
190 | } |
||
191 | |||
192 | 3 | protected function extractColumns($entity, Collection $collection) |
|
193 | { |
||
194 | 3 | $cols = get_object_vars($entity); |
|
195 | |||
196 | 3 | return $cols; |
|
197 | } |
||
198 | |||
199 | protected function hasComposition($entity, $next, $parent) |
||
200 | { |
||
201 | $style = $this->getStyle(); |
||
202 | |||
203 | return $entity === $style->composed($parent, $next) || $entity === $style->composed($next, $parent); |
||
204 | } |
||
205 | |||
206 | 2 | protected function fetchSingle(Collection $collection, $statement) |
|
207 | { |
||
208 | 2 | $name = $collection->getName(); |
|
209 | 2 | $entityName = $name; |
|
210 | 2 | $row = $this->driver->fetch($statement); |
|
211 | |||
212 | 2 | if (!$row) { |
|
0 ignored issues
–
show
|
|||
213 | return false; |
||
214 | } |
||
215 | |||
216 | 2 | if ($this->typable($collection)) { |
|
217 | $entityName = $row->{$this->getType($collection)}; |
||
218 | } |
||
219 | |||
220 | 2 | $entities = new SplObjectStorage(); |
|
221 | 2 | $entities[$this->transformSingleRow($row, $entityName)] = $collection; |
|
222 | |||
223 | 2 | return $entities; |
|
224 | } |
||
225 | |||
226 | 2 | protected function getNewEntityByName($entityName) |
|
227 | { |
||
228 | 2 | $entityName = $this->getStyle()->styledName($entityName); |
|
229 | 2 | $entityClass = $this->entityNamespace . $entityName; |
|
230 | 2 | $entityClass = class_exists($entityClass) ? $entityClass : '\stdClass'; |
|
231 | |||
232 | 2 | return new $entityClass; |
|
233 | } |
||
234 | |||
235 | 2 | protected function transformSingleRow($row, $entityName) |
|
236 | { |
||
237 | 2 | $newRow = $this->getNewEntityByName($entityName); |
|
238 | |||
239 | 2 | foreach ($row as $prop => $value) { |
|
240 | 2 | $this->inferSet($newRow, $prop, $value); |
|
241 | 2 | } |
|
242 | |||
243 | 2 | return $newRow; |
|
244 | } |
||
245 | |||
246 | 2 | protected function inferSet(&$entity, $prop, $value) |
|
247 | { |
||
248 | try { |
||
249 | 2 | $mirror = new \ReflectionProperty($entity, $prop); |
|
250 | $mirror->setAccessible(true); |
||
251 | $mirror->setValue($entity, $value); |
||
252 | 2 | } catch (\ReflectionException $e) { |
|
253 | 2 | $entity->$prop = $value; |
|
254 | } |
||
255 | 2 | } |
|
256 | |||
257 | protected function fetchMulti(Collection $collection, $statement) |
||
258 | { |
||
259 | $row = $this->driver->fetch($statement); |
||
260 | |||
261 | if (!$row) { |
||
0 ignored issues
–
show
The expression
$row of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
262 | return false; |
||
263 | } |
||
264 | |||
265 | $this->postHydrate( |
||
266 | $entities = $this->createEntities($row, $statement, $collection) |
||
267 | ); |
||
268 | |||
269 | return $entities; |
||
270 | } |
||
271 | |||
272 | protected function createEntities($row, $statement, Collection $collection) |
||
273 | { |
||
274 | $entities = new SplObjectStorage(); |
||
275 | $entitiesInstances = $this->buildEntitiesInstances($collection, $entities); |
||
276 | $entityInstance = array_pop($entitiesInstances); |
||
277 | |||
278 | //Reversely traverses the columns to avoid conflicting foreign key names |
||
279 | foreach (array_reverse($row, true) as $col => $value) { |
||
280 | $columnMeta = $statement->getColumnMeta($col); |
||
281 | $columnName = $columnMeta['name']; |
||
282 | $primaryName = $this->getStyle()->identifier( |
||
283 | $entities[$entityInstance]->getName() |
||
284 | ); |
||
285 | |||
286 | $this->inferSet($entityInstance, $columnName, $value); |
||
287 | |||
288 | if ($primaryName == $columnName) { |
||
289 | $entityInstance = array_pop($entitiesInstances); |
||
290 | } |
||
291 | } |
||
292 | |||
293 | return $entities; |
||
294 | } |
||
295 | |||
296 | protected function buildEntitiesInstances(Collection $collection, SplObjectStorage $entities) |
||
297 | { |
||
298 | $entitiesInstances = []; |
||
299 | |||
300 | foreach (CollectionIterator::recursive($collection) as $c) { |
||
301 | if ($this->filterable($c) && !$this->getFilters($c)) { |
||
302 | continue; |
||
303 | } |
||
304 | |||
305 | $entityInstance = $this->getNewEntityByName($c->getName()); |
||
306 | |||
307 | if ($this->mixable($c)) { |
||
308 | $mixins = $this->getMixins($c); |
||
309 | foreach ($mixins as $mix) { |
||
310 | $entitiesInstances[] = $entityInstance; |
||
311 | } |
||
312 | } |
||
313 | |||
314 | $entities[$entityInstance] = $c; |
||
315 | $entitiesInstances[] = $entityInstance; |
||
316 | } |
||
317 | |||
318 | return $entitiesInstances; |
||
319 | } |
||
320 | |||
321 | protected function postHydrate(SplObjectStorage $entities) |
||
322 | { |
||
323 | $entitiesClone = clone $entities; |
||
324 | |||
325 | foreach ($entities as $instance) { |
||
326 | foreach ($instance as $field => &$value) { |
||
327 | if ($this->getStyle()->isRemoteIdentifier($field)) { |
||
328 | foreach ($entitiesClone as $sub) { |
||
329 | $this->tryHydration($entities, $sub, $field, $value); |
||
330 | } |
||
331 | } |
||
332 | } |
||
333 | } |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * @param \SplObjectStorage $entities |
||
338 | * @param $sub |
||
339 | * @param $field |
||
340 | * @param $value |
||
341 | */ |
||
342 | protected function tryHydration($entities, $sub, $field, &$value) |
||
343 | { |
||
344 | $tableName = $entities[$sub]->getName(); |
||
345 | $primaryName = $this->getStyle()->identifier($tableName); |
||
346 | |||
347 | if ($tableName === $this->getStyle()->remoteFromIdentifier($field) |
||
348 | && $sub->{$primaryName} === $value |
||
349 | ) { |
||
350 | $value = $sub; |
||
351 | } |
||
352 | } |
||
353 | |||
354 | protected function getSetterStyle($name) |
||
355 | { |
||
356 | $name = str_replace('_', '', $this->getStyle()->styledProperty($name)); |
||
357 | |||
358 | return "set{$name}"; |
||
359 | } |
||
360 | |||
361 | public function getFilters(Collection $collection) |
||
362 | { |
||
363 | return $collection->getExtra('filters'); |
||
364 | } |
||
365 | |||
366 | public function getMixins(Collection $collection) |
||
367 | { |
||
368 | return $collection->getExtra('mixins'); |
||
369 | } |
||
370 | |||
371 | public function getType(Collection $collection) |
||
372 | { |
||
373 | return $collection->getExtra('type'); |
||
374 | } |
||
375 | |||
376 | 1 | public function mixable(Collection $collection) |
|
377 | { |
||
378 | 1 | return $collection->have('mixins'); |
|
379 | } |
||
380 | |||
381 | 2 | public function typable(Collection $collection) |
|
382 | { |
||
383 | 2 | return $collection->have('type'); |
|
384 | } |
||
385 | |||
386 | 2 | public function filterable(Collection $collection) |
|
387 | { |
||
388 | 2 | return $collection->have('filters'); |
|
389 | } |
||
390 | } |
||
391 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.