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 Victoire\Bundle\WidgetMapBundle\Warmer; |
||
4 | |||
5 | use Doctrine\ORM\EntityManager; |
||
6 | use Symfony\Component\PropertyAccess\PropertyAccess; |
||
7 | use Victoire\Bundle\BusinessEntityBundle\Resolver\BusinessEntityResolver; |
||
8 | use Victoire\Bundle\BusinessPageBundle\Entity\BusinessTemplate; |
||
9 | use Victoire\Bundle\CoreBundle\Entity\Link; |
||
10 | use Victoire\Bundle\CoreBundle\Entity\View; |
||
11 | use Victoire\Bundle\CriteriaBundle\Entity\Criteria; |
||
12 | use Victoire\Bundle\PageBundle\Entity\Page; |
||
13 | use Victoire\Bundle\ViewReferenceBundle\Connector\ViewReferenceRepository; |
||
14 | use Victoire\Bundle\ViewReferenceBundle\ViewReference\ViewReference; |
||
15 | use Victoire\Bundle\WidgetBundle\Entity\Traits\LinkTrait; |
||
16 | use Victoire\Bundle\WidgetBundle\Entity\Widget; |
||
17 | use Victoire\Bundle\WidgetBundle\Helper\WidgetHelper; |
||
18 | use Victoire\Bundle\WidgetBundle\Repository\WidgetRepository; |
||
19 | use Victoire\Bundle\WidgetMapBundle\Entity\WidgetMap; |
||
20 | |||
21 | /** |
||
22 | * WidgetDataWarmer. |
||
23 | * |
||
24 | * This class prepare all widgets with their associated entities for the current View |
||
25 | * to reduce queries during page rendering. |
||
26 | * Only OneToMany and ManyToOne associations are handled because no OneToOne or ManyToMany |
||
27 | * associations have been used in Widgets. |
||
28 | * |
||
29 | * Ref: victoire_widget_map.widget_data_warmer |
||
30 | */ |
||
31 | class WidgetDataWarmer |
||
32 | { |
||
33 | /* @var $em EntityManager */ |
||
34 | protected $em; |
||
35 | protected $viewReferenceRepository; |
||
36 | protected $widgetHelper; |
||
37 | protected $accessor; |
||
38 | protected $manyToOneAssociations; |
||
39 | /** |
||
40 | * @var BusinessEntityResolver |
||
41 | */ |
||
42 | private $businessEntityResolver; |
||
43 | |||
44 | /** |
||
45 | * Constructor. |
||
46 | * |
||
47 | * @param ViewReferenceRepository $viewReferenceRepository |
||
48 | * @param WidgetHelper $widgetHelper |
||
49 | * @param BusinessEntityResolver $businessEntityResolver |
||
50 | */ |
||
51 | public function __construct( |
||
52 | ViewReferenceRepository $viewReferenceRepository, |
||
53 | WidgetHelper $widgetHelper, |
||
54 | BusinessEntityResolver $businessEntityResolver |
||
55 | ) { |
||
56 | $this->viewReferenceRepository = $viewReferenceRepository; |
||
57 | $this->widgetHelper = $widgetHelper; |
||
58 | $this->accessor = PropertyAccess::createPropertyAccessor(); |
||
59 | $this->businessEntityResolver = $businessEntityResolver; |
||
60 | } |
||
61 | |||
62 | /** |
||
63 | * Find all Widgets for current View, inject them in WidgetMap and warm associated entities. |
||
64 | * |
||
65 | * @param EntityManager $em |
||
66 | * @param View $view |
||
67 | */ |
||
68 | public function warm(EntityManager $em, View $view) |
||
0 ignored issues
–
show
|
|||
69 | { |
||
70 | $this->em = $em; |
||
71 | |||
72 | /* @var WidgetRepository $widgetRepo */ |
||
73 | $widgetRepo = $this->em->getRepository('Victoire\Bundle\WidgetBundle\Entity\Widget'); |
||
74 | $viewWidgets = $widgetRepo->findAllWidgetsForView($view); |
||
75 | |||
76 | $this->injectWidgets($view, $viewWidgets); |
||
77 | |||
78 | $this->extractAssociatedEntities($viewWidgets); |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Inject Widgets in View's builtWidgetMap. |
||
83 | * |
||
84 | * @param View $view |
||
85 | * @param $viewWidgets |
||
86 | */ |
||
87 | private function injectWidgets(View $view, $viewWidgets) |
||
88 | { |
||
89 | $builtWidgetMap = $view->getBuiltWidgetMap(); |
||
90 | |||
91 | foreach ($builtWidgetMap as $slot => $widgetMaps) { |
||
92 | foreach ($widgetMaps as $i => $widgetMap) { |
||
93 | foreach ($viewWidgets as $widget) { |
||
94 | if ($widget->getWidgetMap() == $widgetMap) { |
||
95 | $builtWidgetMap[$slot][$i]->addWidget($widget); |
||
96 | |||
97 | //Override Collection default behaviour to avoid useless query |
||
98 | $builtWidgetMap[$slot][$i]->getWidgets()->setDirty(false); |
||
99 | $builtWidgetMap[$slot][$i]->getWidgets()->setInitialized(true); |
||
100 | continue; |
||
101 | } |
||
102 | } |
||
103 | } |
||
104 | } |
||
105 | |||
106 | $view->setBuiltWidgetMap($builtWidgetMap); |
||
107 | } |
||
108 | |||
109 | /** |
||
110 | * Pass through all widgets and associated entities to extract all missing associations, |
||
111 | * store it by repository to group queries by entity type. |
||
112 | * |
||
113 | * @param Widget[] $entities Widgets and associated entities |
||
114 | */ |
||
115 | private function extractAssociatedEntities(array $entities) |
||
116 | { |
||
117 | $linkIds = $associatedEntities = []; |
||
118 | |||
119 | foreach ($entities as $entity) { |
||
120 | $reflect = new \ReflectionClass($entity); |
||
121 | |||
122 | //If Widget is already in cache, extract only its Criterias (used outside Widget rendering) |
||
123 | $widgetCached = ($entity instanceof Widget && $this->widgetHelper->isCacheEnabled($entity)); |
||
124 | |||
125 | //If Widget has LinkTrait, store the entity link id |
||
126 | if (!$widgetCached && $this->hasLinkTrait($reflect) && $entity->getLink()) { |
||
0 ignored issues
–
show
The method
getLink() does not seem to exist on object<Victoire\Bundle\W...etBundle\Entity\Widget> .
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||
127 | $linkIds[] = $entity->getLink()->getId(); |
||
0 ignored issues
–
show
The method
getLink() does not seem to exist on object<Victoire\Bundle\W...etBundle\Entity\Widget> .
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() |
|||
128 | } |
||
129 | |||
130 | //Pass through all entity associations |
||
131 | $metaData = $this->em->getClassMetadata(get_class($entity)); |
||
132 | foreach ($metaData->getAssociationMappings() as $association) { |
||
133 | $targetClass = $association['targetEntity']; |
||
134 | |||
135 | //Skip already set WidgetMap association |
||
136 | if ($targetClass == WidgetMap::class) { |
||
137 | continue; |
||
138 | } |
||
139 | |||
140 | //If Widget has OneToOne or ManyToOne association, store target entity id to construct |
||
141 | //a single query for this entity type |
||
142 | if ($metaData->isSingleValuedAssociation($association['fieldName']) |
||
143 | && !$widgetCached |
||
144 | ) { |
||
145 | //If target Entity is not null, treat it |
||
146 | if ($targetEntity = $this->accessor->getValue($entity, $association['fieldName'])) { |
||
147 | $associatedEntities[$targetClass]['id'][] = new AssociatedEntityToWarm( |
||
148 | AssociatedEntityToWarm::TYPE_MANY_TO_ONE, |
||
149 | $entity, |
||
150 | $association['fieldName'], |
||
151 | $targetEntity->getId() |
||
152 | ); |
||
153 | } |
||
154 | } |
||
155 | |||
156 | //If Widget has OneToMany association, store owner entity id and mappedBy value |
||
157 | //to construct a single query for this entity type |
||
158 | elseif ($metaData->isCollectionValuedAssociation($association['fieldName'])) { |
||
0 ignored issues
–
show
|
|||
159 | |||
160 | //Even if Widget is cached, we need its Criterias used before cache call |
||
161 | if (!$widgetCached || $targetClass === Criteria::class) { |
||
0 ignored issues
–
show
|
|||
162 | |||
163 | //If Collection is not null, treat it |
||
164 | if ($this->accessor->getValue($entity, $association['fieldName'])) { |
||
0 ignored issues
–
show
|
|||
165 | |||
166 | //Don't use Collection getter directly and override Collection |
||
167 | //default behaviour to avoid useless query |
||
168 | $getter = 'get'.ucwords($association['fieldName']); |
||
169 | $entity->$getter()->setDirty(false); |
||
170 | $entity->$getter()->setInitialized(true); |
||
171 | |||
172 | $associatedEntities[$targetClass][$association['mappedBy']][] = new AssociatedEntityToWarm( |
||
173 | AssociatedEntityToWarm::TYPE_ONE_TO_MANY, |
||
174 | $entity, |
||
175 | $association['fieldName'], |
||
176 | $entity->getId() |
||
177 | ); |
||
178 | } |
||
179 | } |
||
180 | } |
||
181 | } |
||
182 | } |
||
183 | |||
184 | $newEntities = $this->setAssociatedEntities($associatedEntities); |
||
185 | $this->setPagesForLinks($linkIds); |
||
186 | |||
187 | //Recursive call if previous has return new entities to warm |
||
188 | if ($newEntities) { |
||
0 ignored issues
–
show
The expression
$newEntities 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 ![]() |
|||
189 | $this->extractAssociatedEntities($newEntities); |
||
190 | } |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * Set all missing associated entities. |
||
195 | * |
||
196 | * @param array $repositories |
||
197 | * |
||
198 | * @throws \Throwable |
||
199 | * @throws \TypeError |
||
200 | * |
||
201 | * @return array |
||
202 | */ |
||
203 | private function setAssociatedEntities(array $repositories) |
||
204 | { |
||
205 | $newEntities = []; |
||
206 | |||
207 | foreach ($repositories as $repositoryName => $findMethods) { |
||
208 | foreach ($findMethods as $findMethod => $associatedEntitiesToWarm) { |
||
0 ignored issues
–
show
|
|||
209 | |||
210 | //Extract ids to search |
||
211 | $idsToSearch = array_map(function ($associatedEntityToWarm) { |
||
212 | return $associatedEntityToWarm->getEntityId(); |
||
213 | }, $associatedEntitiesToWarm); |
||
214 | |||
215 | //Find by id for ManyToOne associations based on target entity id |
||
216 | //Find by mappedBy value for OneToMany associations based on owner entity id |
||
217 | $foundEntities = $this->em->getRepository($repositoryName)->findBy([ |
||
218 | $findMethod => array_values($idsToSearch), |
||
219 | ]); |
||
220 | |||
221 | /* @var AssociatedEntityToWarm[] $associatedEntitiesToWarm */ |
||
222 | foreach ($associatedEntitiesToWarm as $associatedEntityToWarm) { |
||
223 | foreach ($foundEntities as $foundEntity) { |
||
224 | if ($associatedEntityToWarm->getType() === AssociatedEntityToWarm::TYPE_MANY_TO_ONE |
||
225 | && $foundEntity->getId() === $associatedEntityToWarm->getEntityId() |
||
226 | ) { |
||
227 | $inheritorEntity = $associatedEntityToWarm->getInheritorEntity(); |
||
228 | $inheritorPropertyName = $associatedEntityToWarm->getInheritorPropertyName(); |
||
229 | $this->accessor->setValue($inheritorEntity, $inheritorPropertyName, $foundEntity); |
||
230 | continue; |
||
231 | } elseif ($associatedEntityToWarm->getType() === AssociatedEntityToWarm::TYPE_ONE_TO_MANY |
||
232 | && $this->accessor->getValue($foundEntity, $findMethod) === $associatedEntityToWarm->getInheritorEntity() |
||
233 | ) { |
||
234 | $inheritorEntity = $associatedEntityToWarm->getInheritorEntity(); |
||
235 | $inheritorPropertyName = $associatedEntityToWarm->getInheritorPropertyName(); |
||
236 | |||
237 | //Don't use Collection getter directly and override Collection |
||
238 | //default behaviour to avoid useless query |
||
239 | $getter = 'get'.ucwords($inheritorPropertyName); |
||
240 | $inheritorEntity->$getter()->add($foundEntity); |
||
241 | $inheritorEntity->$getter()->setDirty(false); |
||
242 | $inheritorEntity->$getter()->setInitialized(true); |
||
243 | |||
244 | //Store new entities to warm if necessary |
||
245 | $newEntities[] = $foundEntity; |
||
246 | continue; |
||
247 | } |
||
248 | } |
||
249 | } |
||
250 | } |
||
251 | } |
||
252 | |||
253 | return $newEntities; |
||
254 | } |
||
255 | |||
256 | /** |
||
257 | * Set viewReferencePage for each link. |
||
258 | * |
||
259 | * @param array $linkIds |
||
260 | */ |
||
261 | private function setPagesForLinks(array $linkIds) |
||
262 | { |
||
263 | $viewReferences = []; |
||
264 | |||
265 | /* @var Link[] $links */ |
||
266 | $links = $this->em->getRepository('VictoireCoreBundle:Link')->findById($linkIds); |
||
267 | |||
268 | foreach ($links as $link) { |
||
269 | if ($link->getParameters()['linkType'] == 'viewReference') { |
||
270 | $viewReference = $this->viewReferenceRepository->getOneReferenceByParameters([ |
||
271 | 'id' => $link->getParameters()['viewReference'], |
||
272 | 'locale' => $link->getParameters()['locale'], |
||
273 | ]); |
||
274 | |||
275 | if ($viewReference instanceof ViewReference) { |
||
276 | $viewReferences[$link->getId()] = $viewReference; |
||
277 | } |
||
278 | } |
||
279 | } |
||
280 | |||
281 | /* @var Page[] $pages */ |
||
282 | $pages = $this->em->getRepository('VictoireCoreBundle:View')->findByViewReferences($viewReferences); |
||
283 | |||
284 | foreach ($links as $link) { |
||
285 | foreach ($pages as $page) { |
||
286 | if (!($page instanceof BusinessTemplate) && $page->getReference() && $link->getViewReference() == $page->getReference()->getId()) { |
||
287 | $link->setViewReferencePage($page); |
||
288 | } |
||
289 | } |
||
290 | } |
||
291 | } |
||
292 | |||
293 | /** |
||
294 | * Check if reflection class has LinkTrait. |
||
295 | * |
||
296 | * @param \ReflectionClass $reflect |
||
297 | * |
||
298 | * @return bool |
||
299 | */ |
||
300 | private function hasLinkTrait(\ReflectionClass $reflect) |
||
301 | { |
||
302 | $traits = $reflect->getTraits(); |
||
303 | foreach ($traits as $trait) { |
||
304 | if ($trait->getName() == LinkTrait::class) { |
||
0 ignored issues
–
show
![]() |
|||
305 | return true; |
||
306 | } |
||
307 | } |
||
308 | |||
309 | if ($parentClass = $reflect->getParentClass()) { |
||
310 | if ($this->hasLinkTrait($parentClass)) { |
||
311 | return true; |
||
312 | } |
||
313 | } |
||
314 | |||
315 | return false; |
||
316 | } |
||
317 | } |
||
318 |
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.