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 Admingenerator\GeneratorBundle\Guesser; |
||
4 | |||
5 | use Admingenerator\GeneratorBundle\Exception\NotImplementedException; |
||
6 | use Doctrine\Common\Persistence\ManagerRegistry; |
||
7 | use Doctrine\ORM\Mapping\ClassMetadataInfo; |
||
8 | use Symfony\Component\HttpKernel\Kernel; |
||
9 | |||
10 | abstract class DoctrineFieldGuesser |
||
11 | { |
||
12 | /** |
||
13 | * @var ManagerRegistry |
||
14 | */ |
||
15 | private $registry; |
||
16 | |||
17 | /** |
||
18 | * @var boolean |
||
19 | */ |
||
20 | private $guessRequired; |
||
21 | |||
22 | /** |
||
23 | * @var boolean |
||
24 | */ |
||
25 | private $defaultRequired; |
||
26 | |||
27 | /** |
||
28 | * @var array |
||
29 | */ |
||
30 | private $formTypes; |
||
31 | |||
32 | /** |
||
33 | * @var array |
||
34 | */ |
||
35 | private $filterTypes; |
||
36 | |||
37 | public function __construct(ManagerRegistry $registry, $objectModel, array $formTypes, array $filterTypes, $guessRequired, $defaultRequired) |
||
38 | { |
||
39 | if (!in_array($objectModel = strtolower($objectModel), array('document', 'entity'))) { |
||
40 | throw new \InvalidArgumentException('$objectModel must be Document or Entity'); |
||
41 | } |
||
42 | |||
43 | $this->registry = $registry; |
||
44 | $this->objectModel = strtolower($objectModel); |
||
0 ignored issues
–
show
|
|||
45 | $this->formTypes = $formTypes; |
||
46 | $this->filterTypes = $filterTypes; |
||
47 | $this->guessRequired = $guessRequired; |
||
48 | $this->defaultRequired = $defaultRequired; |
||
49 | } |
||
50 | |||
51 | protected function getMetadatas($class) |
||
52 | { |
||
53 | // Cache is implemented by Doctrine itself |
||
54 | return $this->registry->getManagerForClass($class)->getClassMetadata($class); |
||
55 | } |
||
56 | |||
57 | public function getAllFields($class) |
||
58 | { |
||
59 | return array_merge($this->getMetadatas($class)->getFieldNames(), $this->getMetadatas($class)->getAssociationNames()); |
||
60 | } |
||
61 | |||
62 | public function getManyToMany($model, $fieldPath) |
||
63 | { |
||
64 | $resolved = $this->resolveRelatedField($model, $fieldPath); |
||
65 | $metadata = $this->getMetadatas($resolved['class']); |
||
66 | return $metadata->hasAssociation($resolved['field']) && !$metadata->isAssociationWithSingleJoinColumn($resolved['field']); |
||
67 | } |
||
68 | |||
69 | /** |
||
70 | * Find out the database type for given model field path. |
||
71 | * |
||
72 | * @param string $model The starting model. |
||
73 | * @param string $fieldPath The field path. |
||
74 | * @return string The DB type for given model field path. |
||
75 | */ |
||
76 | View Code Duplication | public function getDbType($model, $fieldPath) |
|
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. ![]() |
|||
77 | { |
||
78 | $resolved = $this->resolveRelatedField($model, $fieldPath); |
||
79 | $class = $resolved['class']; |
||
80 | $field = $resolved['field']; |
||
81 | |||
82 | $metadata = $this->getMetadatas($class); |
||
83 | |||
84 | if ($metadata->hasAssociation($field)) { |
||
85 | if ($metadata->isSingleValuedAssociation($field)) { |
||
86 | return $this->objectModel; |
||
87 | } |
||
88 | |||
89 | return 'collection'; |
||
90 | } |
||
91 | |||
92 | if ($metadata->hasField($field)) { |
||
93 | return $metadata->getTypeOfField($field); |
||
94 | } |
||
95 | |||
96 | return 'virtual'; |
||
97 | } |
||
98 | |||
99 | View Code Duplication | public function getModelType($model, $fieldPath) |
|
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. ![]() |
|||
100 | { |
||
101 | $resolved = $this->resolveRelatedField($model, $fieldPath); |
||
102 | |||
103 | $class = $resolved['class']; |
||
104 | $fieldName = $resolved['field']; |
||
105 | |||
106 | $metadata = $this->getMetadatas($class); |
||
107 | |||
108 | if ($metadata->hasAssociation($fieldName)) { |
||
109 | return $metadata->getAssociationTargetClass($fieldName); |
||
110 | } |
||
111 | |||
112 | if ($metadata->hasField($fieldName)) { |
||
113 | return $metadata->getTypeOfField($fieldName); |
||
114 | } |
||
115 | |||
116 | return 'virtual'; |
||
117 | } |
||
118 | |||
119 | public function getSortType($dbType) |
||
120 | { |
||
121 | $alphabeticTypes = array( |
||
122 | 'string', |
||
123 | 'text', |
||
124 | 'id', |
||
125 | 'custom_id', |
||
126 | ); |
||
127 | |||
128 | $numericTypes = array( |
||
129 | 'decimal', |
||
130 | 'float', |
||
131 | 'int', |
||
132 | 'integer', |
||
133 | 'int_id', |
||
134 | 'bigint', |
||
135 | 'smallint', |
||
136 | ); |
||
137 | |||
138 | if (in_array($dbType, $alphabeticTypes)) { |
||
139 | return 'alphabetic'; |
||
140 | } |
||
141 | |||
142 | if (in_array($dbType, $numericTypes)) { |
||
143 | return 'numeric'; |
||
144 | } |
||
145 | |||
146 | return 'default'; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * @param $dbType |
||
151 | * @param $class: for debug only |
||
152 | * @param $columnName: for debug only |
||
153 | * @return string |
||
154 | */ |
||
155 | View Code Duplication | public function getFormType($dbType, $class, $columnName) |
|
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. ![]() |
|||
156 | { |
||
157 | if (array_key_exists($dbType, $this->formTypes)) { |
||
158 | return $this->formTypes[$dbType]; |
||
159 | } |
||
160 | |||
161 | if ('virtual' === $dbType) { |
||
162 | return 'virtual_form'; |
||
163 | } |
||
164 | |||
165 | throw new NotImplementedException( |
||
166 | 'The dbType "'.$dbType.'" is not yet implemented ' |
||
167 | .'(column "'.$columnName.'" in "'.$class.'")' |
||
168 | ); |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * @param $dbType |
||
173 | * @param $class: for debug only |
||
174 | * @param $columnName: for debug only |
||
175 | * @return string |
||
176 | */ |
||
177 | View Code Duplication | public function getFilterType($dbType, $class, $columnName) |
|
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. ![]() |
|||
178 | { |
||
179 | if (array_key_exists($dbType, $this->filterTypes)) { |
||
180 | return $this->filterTypes[$dbType]; |
||
181 | } |
||
182 | |||
183 | if ('virtual' === $dbType) { |
||
184 | return 'virtual_filter'; |
||
185 | } |
||
186 | |||
187 | throw new NotImplementedException( |
||
188 | 'The dbType "'.$dbType.'" is not yet implemented ' |
||
189 | .'(column "'.$columnName.'" in "'.$class.'")' |
||
190 | ); |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * @param $formType |
||
195 | * @param $dbType |
||
196 | * @param $model |
||
197 | * @param $fieldPath |
||
198 | * @return array |
||
199 | * @throws \Exception |
||
200 | */ |
||
201 | public function getFormOptions($formType, $dbType, $model, $fieldPath) |
||
202 | { |
||
203 | return $this->getOptions($formType, $dbType, $model, $fieldPath, false); |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * @param $filterType |
||
208 | * @param $dbType |
||
209 | * @param $model |
||
210 | * @param $fieldPath |
||
211 | * @return array |
||
212 | * @throws \Exception |
||
213 | */ |
||
214 | public function getFilterOptions($filterType, $dbType, $model, $fieldPath) |
||
215 | { |
||
216 | return $this->getOptions($filterType, $dbType, $model, $fieldPath, true); |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * @param $type |
||
221 | * @param $dbType |
||
222 | * @param $model |
||
223 | * @param $fieldPath |
||
224 | * @param bool $filter |
||
225 | * @return array |
||
226 | * @throws \Exception |
||
227 | */ |
||
228 | protected function getOptions($type, $dbType, $model, $fieldPath, $filter = false) |
||
229 | { |
||
230 | if ('virtual' === $dbType) { |
||
231 | return array(); |
||
232 | } |
||
233 | |||
234 | $resolved = $this->resolveRelatedField($model, $fieldPath); |
||
235 | $class = $resolved['class']; |
||
236 | $columnName = $resolved['field']; |
||
237 | |||
238 | if ('boolean' == $dbType && preg_match("/ChoiceType$/i", $type)) { |
||
239 | $options = array( |
||
240 | 'choices' => array( |
||
241 | 'boolean.no' => 0, |
||
242 | 'boolean.yes' => 1 |
||
243 | ), |
||
244 | 'placeholder' => 'boolean.yes_or_no', |
||
245 | 'translation_domain' => 'Admingenerator', |
||
246 | 'choice_translation_domain' => 'Admingenerator' |
||
247 | ); |
||
248 | |||
249 | if (Kernel::MAJOR_VERSION < 3) { |
||
250 | $options['choices_as_values'] = true; |
||
251 | } |
||
252 | |||
253 | return $options; |
||
254 | } |
||
255 | |||
256 | if ('boolean' == $dbType && preg_match("/CheckboxType/i", $type)) { |
||
257 | return array( |
||
258 | 'required' => false, |
||
259 | ); |
||
260 | } |
||
261 | |||
262 | if (preg_match("/NumberType/i", $type)) { |
||
263 | $mapping = $this->getMetadatas($class)->getFieldMapping($columnName); |
||
264 | |||
265 | if (isset($mapping['scale'])) { |
||
266 | $scale = $mapping['scale']; |
||
267 | } |
||
268 | |||
269 | if (isset($mapping['precision'])) { |
||
270 | $scale = $mapping['precision']; |
||
271 | } |
||
272 | |||
273 | return array( |
||
274 | 'scale' => isset($scale) ? $scale : null, |
||
275 | 'required' => $filter ? false : $this->isRequired($class, $columnName) |
||
276 | ); |
||
277 | } |
||
278 | |||
279 | if (preg_match(sprintf('/%sType$/i', ucfirst($this->objectModel)), $type)) { |
||
280 | $mapping = $this->getMetadatas($class)->getAssociationMapping($columnName); |
||
281 | |||
282 | return array( |
||
283 | 'multiple' => ($mapping['type'] === ClassMetadataInfo::MANY_TO_MANY || $mapping['type'] === ClassMetadataInfo::ONE_TO_MANY), |
||
284 | 'em' => $this->getObjectManagerName($mapping['target'.ucfirst($this->objectModel)]), |
||
285 | 'class' => $mapping['target'.ucfirst($this->objectModel)], |
||
286 | 'required' => $filter ? false : $this->isRequired($class, $columnName), |
||
287 | ); |
||
288 | } |
||
289 | |||
290 | if (preg_match("/CollectionType$/i", $type)) { |
||
291 | $options = array( |
||
292 | 'allow_add' => true, |
||
293 | 'allow_delete' => true, |
||
294 | 'by_reference' => false, |
||
295 | 'entry_type' => $filter ? $this->filterTypes[$this->objectModel] : $this->formTypes[$this->objectModel], |
||
296 | ); |
||
297 | |||
298 | if ($this->getMetadatas($class)->hasAssociation($columnName)) { |
||
299 | $mapping = $this->getMetadatas($class)->getAssociationMapping($columnName); |
||
300 | $options['entry_options'] = array( |
||
301 | 'class' => $mapping['target'.ucfirst($this->objectModel)] |
||
302 | ); |
||
303 | } |
||
304 | |||
305 | return $options; |
||
306 | } |
||
307 | |||
308 | return array( |
||
309 | 'required' => $filter ? false : $this->isRequired($class, $columnName) |
||
310 | ); |
||
311 | } |
||
312 | |||
313 | protected function isRequired($class, $fieldName) |
||
314 | { |
||
315 | if (!$this->guessRequired) { |
||
316 | return $this->defaultRequired; |
||
317 | } |
||
318 | |||
319 | $metadata = $this->getMetadatas($class); |
||
320 | |||
321 | $hasField = $metadata->hasField($fieldName); |
||
322 | $hasAssociation = $metadata->hasAssociation($fieldName); |
||
323 | $isSingleValAssoc = $metadata->isSingleValuedAssociation($fieldName); |
||
324 | |||
325 | if ($hasField && (!$hasAssociation || $isSingleValAssoc)) { |
||
326 | return !$metadata->isNullable($fieldName); |
||
327 | } |
||
328 | |||
329 | return false; |
||
330 | } |
||
331 | |||
332 | /** |
||
333 | * Find the pk name for given class |
||
334 | * |
||
335 | * @param string $class The class name. |
||
336 | * @return string Primary key field name. |
||
337 | */ |
||
338 | public function getModelPrimaryKeyName($class) |
||
339 | { |
||
340 | $identifierFieldNames = $this->getMetadatas($class)->getIdentifierFieldNames(); |
||
341 | |||
342 | return !empty($identifierFieldNames) ? $identifierFieldNames[0] : null; |
||
343 | } |
||
344 | |||
345 | /** |
||
346 | * Find out the primary key for given model field path. |
||
347 | * |
||
348 | * @param string $model The starting model. |
||
349 | * @param string $fieldPath The field path. |
||
350 | * @return string The leaf field's primary key. |
||
351 | */ |
||
352 | public function getPrimaryKeyFor($model, $fieldPath) |
||
353 | { |
||
354 | $resolved = $this->resolveRelatedField($model, $fieldPath); |
||
355 | $class = $resolved['class']; |
||
356 | $field = $resolved['field']; |
||
357 | |||
358 | $metadata = $this->getMetadatas($class); |
||
359 | |||
360 | if ($metadata->hasAssociation($field)) { |
||
361 | $class = $metadata->getAssociationTargetClass($field); |
||
362 | $classIdentifiers = $this->getMetadatas($class)->getIdentifierFieldNames(); |
||
363 | |||
364 | // Short workaround for https://github.com/symfony2admingenerator/GeneratorBundle/issues/161 |
||
365 | // TODO: throw an exception to correctly handle that situation? |
||
366 | return 1 == count($classIdentifiers) ? $classIdentifiers[0] : null; |
||
367 | } |
||
368 | |||
369 | // if the leaf node is not an association |
||
370 | return null; |
||
371 | } |
||
372 | |||
373 | /** |
||
374 | * Resolve field path for given model to class and field name. |
||
375 | * |
||
376 | * @param string $model The starting model. |
||
377 | * @param string $fieldPath The field path. |
||
378 | * @return array An array containing field and class information. |
||
379 | */ |
||
380 | protected function resolveRelatedField($model, $fieldPath) |
||
381 | { |
||
382 | $path = explode('.', $fieldPath); |
||
383 | $field = array_pop($path); |
||
384 | $class = $model; |
||
385 | |||
386 | foreach ($path as $part) { |
||
387 | $metadata = $this->getMetadatas($class); |
||
388 | |||
389 | if (!$metadata->hasAssociation($part)) { |
||
390 | throw new \LogicException('Field "'.$part.'" for class "'.$class.'" is not an association.'); |
||
391 | } |
||
392 | |||
393 | $class = $metadata->getAssociationTargetClass($part); |
||
394 | } |
||
395 | |||
396 | return array( |
||
397 | 'field' => $field, |
||
398 | 'class' => $class |
||
399 | ); |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * Retrieve Doctrine EntityManager name for class $className |
||
404 | * |
||
405 | * @param $className |
||
406 | * @return int|string |
||
407 | * @throws \Exception |
||
408 | */ |
||
409 | protected function getObjectManagerName($className) |
||
410 | { |
||
411 | $om = $this->registry->getManagerForClass($className); |
||
412 | foreach ($this->registry->getManagerNames() as $emName=>$omName) |
||
413 | { |
||
414 | $instance = $this->registry->getManager($emName); |
||
415 | if ($instance == $om) |
||
416 | { |
||
417 | return $emName; |
||
418 | } |
||
419 | } |
||
420 | |||
421 | throw new \Exception("Object manager for class: $className not found."); |
||
422 | } |
||
423 | } |
||
424 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: