Completed
Push — master ( 4de43c...7a7d43 )
by Paul
06:26
created

WidgetManager   C

Complexity

Total Complexity 25

Size/Duplication

Total Lines 368
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 25
lcom 1
cbo 20
dl 0
loc 368
rs 6.4705
c 2
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 33 1
B newWidget() 0 31 1
B createWidget() 0 63 6
C editWidget() 0 77 7
B deleteWidget() 0 26 3
A overwriteWidget() 0 8 1
B cloneEntity() 0 35 6
1
<?php
2
3
namespace Victoire\Bundle\WidgetBundle\Model;
4
5
use Doctrine\ORM\EntityManager;
6
use Doctrine\ORM\Mapping\ClassMetadataInfo;
7
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
8
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
9
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
10
use Symfony\Component\HttpFoundation\Request;
11
use Symfony\Component\PropertyAccess\PropertyAccess;
12
use Victoire\Bundle\BusinessEntityBundle\Entity\BusinessEntity;
13
use Victoire\Bundle\BusinessEntityBundle\Helper\BusinessEntityHelper;
14
use Victoire\Bundle\BusinessPageBundle\Entity\VirtualBusinessPage;
15
use Victoire\Bundle\BusinessPageBundle\Transformer\VirtualToBusinessPageTransformer;
16
use Victoire\Bundle\CoreBundle\Entity\View;
17
use Victoire\Bundle\CoreBundle\Event\WidgetFlushedEvent;
18
use Victoire\Bundle\CoreBundle\VictoireCmsEvents;
19
use Victoire\Bundle\FormBundle\Helper\FormErrorHelper;
20
use Victoire\Bundle\PageBundle\Helper\PageHelper;
21
use Victoire\Bundle\WidgetBundle\Builder\WidgetFormBuilder;
22
use Victoire\Bundle\WidgetBundle\Helper\WidgetHelper;
23
use Victoire\Bundle\WidgetBundle\Renderer\WidgetRenderer;
24
use Victoire\Bundle\WidgetBundle\Resolver\WidgetContentResolver;
25
use Victoire\Bundle\WidgetMapBundle\Builder\WidgetMapBuilder;
26
use Victoire\Bundle\WidgetMapBundle\Entity\WidgetMap;
27
use Victoire\Bundle\WidgetMapBundle\Helper\WidgetMapHelper;
28
use Victoire\Bundle\WidgetMapBundle\Manager\WidgetMapManager;
29
30
/**
31
 * This manager handles crud operations on a Widget.
32
 */
33
class WidgetManager
34
{
35
    protected $widgetFormBuilder;
36
    protected $widgetHelper;
37
    protected $widgetContentResolver;
38
    protected $widgetRenderer;
39
    protected $eventDispatcher;
40
    protected $entityManager;
41
    protected $formErrorHelper; // @victoire_form.error_helper
42
    protected $request; // @request
43
    protected $widgetMapManager;
44
    protected $businessEntityHelper;
45
    protected $templating;
46
    protected $pageHelper;
47
    protected $slots; // %victoire_core.slots%
48
    protected $virtualToBpTransformer;
49
50
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$eventDispatcher" missing
Loading history...
introduced by
Doc comment for parameter "$businessEntityHelper" missing
Loading history...
introduced by
Doc comment for parameter "$virtualToBpTransformer" missing
Loading history...
51
     * construct.
52
     *
53
     * @param WidgetHelper              $widgetHelper
0 ignored issues
show
introduced by
Expected 13 spaces after parameter type; 14 found
Loading history...
54
     * @param WidgetFormBuilder         $widgetFormBuilder
0 ignored issues
show
introduced by
Expected 8 spaces after parameter type; 9 found
Loading history...
55
     * @param WidgetContentResolver     $widgetContentResolver
0 ignored issues
show
introduced by
Expected 4 spaces after parameter type; 5 found
Loading history...
56
     * @param WidgetRenderer            $widgetRenderer
0 ignored issues
show
introduced by
Expected 11 spaces after parameter type; 12 found
Loading history...
57
     * @param EventDispatcherInterface  $eventDispatcher,
0 ignored issues
show
Documentation introduced by
There is no parameter named $eventDispatcher,. Did you maybe mean $eventDispatcher?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
introduced by
Expected 1 spaces after parameter type; 2 found
Loading history...
introduced by
Doc comment for parameter $eventDispatcher, does not match actual variable name $eventDispatcher
Loading history...
58
     * @param EntityManager             $entityManager
0 ignored issues
show
introduced by
Expected 12 spaces after parameter type; 13 found
Loading history...
59
     * @param FormErrorHelper           $formErrorHelper
0 ignored issues
show
introduced by
Expected 10 spaces after parameter type; 11 found
Loading history...
60
     * @param Request                   $request
0 ignored issues
show
introduced by
Expected 18 spaces after parameter type; 19 found
Loading history...
61
     * @param WidgetMapManager          $widgetMapManager
0 ignored issues
show
introduced by
Expected 9 spaces after parameter type; 10 found
Loading history...
62
     * @param WidgetMapBuilder          $widgetMapBuilder
0 ignored issues
show
introduced by
Expected 9 spaces after parameter type; 10 found
Loading history...
63
     * @param EngineInterface           $templating
0 ignored issues
show
introduced by
Doc comment for parameter $templating does not match actual variable name $businessEntityHelper
Loading history...
introduced by
Expected 10 spaces after parameter type; 11 found
Loading history...
64
     * @param PageHelper                $pageHelper
0 ignored issues
show
introduced by
Doc comment for parameter $pageHelper does not match actual variable name $templating
Loading history...
introduced by
Expected 15 spaces after parameter type; 16 found
Loading history...
65
     * @param array                     $slots
0 ignored issues
show
introduced by
Doc comment for parameter $slots does not match actual variable name $pageHelper
Loading history...
introduced by
Expected 20 spaces after parameter type; 21 found
Loading history...
66
     */
67
    public function __construct(
68
        WidgetHelper $widgetHelper,
69
        WidgetFormBuilder $widgetFormBuilder,
70
        WidgetContentResolver $widgetContentResolver,
71
        WidgetRenderer $widgetRenderer,
72
        EventDispatcherInterface $eventDispatcher,
73
        EntityManager $entityManager,
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $entityManager. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

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:

function someFunction(ManagerRegistry $registry) {
    $em = $registry->getManager();
    $em->getConnection()->beginTransaction();
    try {
        // Do something.
        $em->getConnection()->commit();
    } catch (\Exception $ex) {
        $em->getConnection()->rollback();
        $em->close();

        throw $ex;
    }
}

If that code throws an exception and the EntityManager is closed. Any other code which depends on the same instance of the EntityManager during this request will fail.

On the other hand, if you instead inject the ManagerRegistry, the getManager() method guarantees that you will always get a usable manager instance.

Loading history...
74
        FormErrorHelper $formErrorHelper,
75
        Request $request,
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
76
        WidgetMapManager $widgetMapManager,
77
        WidgetMapBuilder $widgetMapBuilder,
78
        BusinessEntityHelper $businessEntityHelper,
79
        EngineInterface $templating,
80
        PageHelper $pageHelper,
81
        $slots,
82
        VirtualToBusinessPageTransformer $virtualToBpTransformer
83
    ) {
84
        $this->widgetFormBuilder = $widgetFormBuilder;
85
        $this->widgetHelper = $widgetHelper;
86
        $this->widgetContentResolver = $widgetContentResolver;
87
        $this->widgetRenderer = $widgetRenderer;
88
        $this->eventDispatcher = $eventDispatcher;
89
        $this->entityManager = $entityManager;
90
        $this->formErrorHelper = $formErrorHelper;
91
        $this->request = $request;
92
        $this->widgetMapManager = $widgetMapManager;
93
        $this->widgetMapBuilder = $widgetMapBuilder;
0 ignored issues
show
Bug introduced by
The property widgetMapBuilder does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
94
        $this->businessEntityHelper = $businessEntityHelper;
95
        $this->templating = $templating;
96
        $this->pageHelper = $pageHelper;
97
        $this->slots = $slots;
98
        $this->virtualToBpTransformer = $virtualToBpTransformer;
99
    }
100
101
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$mode" missing
Loading history...
introduced by
Doc comment for parameter "$parentWidgetMap" missing
Loading history...
introduced by
Doc comment for parameter "$quantum" missing
Loading history...
102
     * new widget.
103
     *
104
     * @param string $type
0 ignored issues
show
introduced by
Doc comment for parameter $type does not match actual variable name $mode
Loading history...
105
     * @param string $slot
0 ignored issues
show
introduced by
Doc comment for parameter $slot does not match actual variable name $type
Loading history...
106
     * @param View   $view
0 ignored issues
show
introduced by
Doc comment for parameter $view does not match actual variable name $slot
Loading history...
107
     * @param int    $position
0 ignored issues
show
introduced by
Doc comment for parameter $position does not match actual variable name $view
Loading history...
108
     *
109
     * @return array
110
     */
111
    public function newWidget($mode, $type, $slot, $view, $position, $parentWidgetMap, $quantum)
112
    {
113
        $quantum = $this->widgetFormBuilder->convertToString($quantum);
114
115
        $widget = $this->widgetHelper->newWidgetInstance($type, $mode);
116
        $widgets = [$widget];
117
118
        /** @var BusinessEntity[] $classes */
119
        $classes = $this->businessEntityHelper->getAvailableForWidget($this->widgetHelper->getWidgetName($widget));
120
121
        $forms = $this->widgetFormBuilder->renderNewQuantumForms($slot, $view, $widgets, $widget, $classes, $position, $parentWidgetMap, $quantum);
0 ignored issues
show
Documentation introduced by
$widgets is of type array<integer,object<Vic...dle\\Entity\\Widget>"}>, but the function expects a object<Victoire\Bundle\W...etBundle\Entity\Widget>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
122
123
        return [
124
            'widget' => $widget,
125
            'html'   => $this->templating->render(
126
                'VictoireCoreBundle:Widget:Form/new.html.twig',
127
                [
128
                    'id'                 => time(),
129
                    'view'               => $view,
130
                    'slot'               => $slot,
131
                    'position'           => $position,
132
                    'parentWidgetMap'    => $parentWidgetMap,
133
                    'classes'            => $classes,
134
                    'widgets'            => $widgets,
135
                    'widget'             => $widget,
136
                    'forms'              => $forms,
137
                    'quantum'            => $quantum,
138
                ]
139
            ),
140
        ];
141
    }
142
143
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$position" missing
Loading history...
introduced by
Doc comment for parameter "$widgetReference" missing
Loading history...
introduced by
Doc comment for parameter "$quantum" missing
Loading history...
144
     * Create a widget.
145
     *
146
     * @param string $mode
147
     * @param string $type
148
     * @param string $slotId
149
     * @param View   $view
150
     * @param string $entity
151
     * @param string $type
0 ignored issues
show
introduced by
Doc comment for parameter $type does not match actual variable name $position
Loading history...
152
     *
153
     * @throws \Exception
154
     *
155
     * @return Template
156
     */
157
    public function createWidget($mode, $type, $slotId, View $view, $entity, $position, $widgetReference, $quantum)
158
    {
159
        //services
160
        $formErrorHelper = $this->formErrorHelper;
161
        $request = $this->request;
162
163
        if ($view instanceof VirtualBusinessPage) {
164
            $this->virtualToBpTransformer->transform($view);
165
        }
166
        //create a new widget
167
        $widget = $this->widgetHelper->newWidgetInstance($type, $mode);
168
169
        $businessEntity = $this->entityManager->getRepository('VictoireBusinessEntityBundle:BusinessEntity')->findOneBy(['name' => $entity]);
170
        $form = $this->widgetFormBuilder->callBuildFormSwitchParameters($widget, $view, $businessEntity, $position, $widgetReference, $slotId, $quantum);
0 ignored issues
show
Documentation introduced by
$businessEntity is of type object|null, but the function expects a object<Victoire\Bundle\B...\Entity\BusinessEntity>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
171
172
        $noValidate = $request->query->get('novalidate', false);
173
174
        $form->handleRequest($request);
175
        if ($noValidate === false && $form->isValid()) {
176
            if (!$view->getId()) {
177
                //create a view for the business entity instance if we are currently on a virtual one
178
                $this->entityManager->persist($view);
179
            }
180
181
            //get the widget from the form
182
            $widget = $form->getData();
183
184
            //persist the widget
185
            $this->entityManager->persist($widget);
186
            $this->entityManager->flush();
187
188
            $this->widgetMapManager->insert($widget, $view, $slotId, $position, $widgetReference);
189
190
            $this->entityManager->persist($view);
191
            $this->entityManager->flush();
192
193
            $widget->setCurrentView($view);
194
195
            $event = new WidgetFlushedEvent($widget);
196
            $this->eventDispatcher->dispatch(VictoireCmsEvents::WIDGET_POST_FLUSH, $event);
197
            $this->eventDispatcher->dispatch(VictoireCmsEvents::WIDGET_POST_FLUSH.'_'.strtoupper($type), $event);
198
199
            $this->widgetMapBuilder->build($view);
200
201
            //get the html for the widget
202
            $htmlWidget = $this->widgetRenderer->renderContainer($widget, $view);
203
204
            $response = [
205
                'success'  => true,
206
                'widgetId' => $widget->getId(),
207
                'html'     => $htmlWidget,
208
            ];
209
        } else {
210
            //get the errors as a string
211
            $response = [
212
                'success' => false,
213
                'message' => $noValidate === false ? $formErrorHelper->getRecursiveReadableErrors($form) : null,
214
                'html'    => $this->widgetFormBuilder->renderNewForm($form, $widget, $slotId, $view, $quantum, $entity),
215
            ];
216
        }
217
218
        return $response;
219
    }
220
221
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$quantum" missing
Loading history...
introduced by
Doc comment for parameter "$widgetMode" missing
Loading history...
222
     * edit a widget.
223
     *
224
     * @param Request $request
225
     * @param Widget  $widget
226
     * @param View    $currentView
227
     * @param string  $businessEntityName The entity name is used to know which form to submit
0 ignored issues
show
introduced by
Doc comment for parameter $businessEntityName does not match actual variable name $quantum
Loading history...
228
     *
229
     * @return template
230
     */
231
    public function editWidget(Request $request, Widget $widget, View $currentView, $quantum = null, $businessEntityName = null, $widgetMode = Widget::MODE_STATIC)
232
    {
233
        /** @var BusinessEntity[] $classes */
234
        $classes = $this->businessEntityHelper->getAvailableForWidget($this->widgetHelper->getWidgetName($widget));
0 ignored issues
show
Compatibility introduced by
$widget of type object<Victoire\Bundle\WidgetBundle\Model\Widget> is not a sub-type of object<Victoire\Bundle\W...etBundle\Entity\Widget>. It seems like you assume a child class of the class Victoire\Bundle\WidgetBundle\Model\Widget to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
235
236
        //the id of the edited widget
237
        //a new widget might be created in the case of a legacy
238
        $initialWidgetId = $widget->getId();
239
240
        //the type of method used
241
        $requestMethod = $request->getMethod();
242
243
        //if the form is posted
244
        if ($requestMethod === 'POST') {
245
            //the widget view
246
            $widgetView = $widget->getWidgetMap()->getView();
247
248
            //we only copy the widget if the view of the widget is not the current view
249
            if ($widgetView !== $currentView) {
250
                $widget = $this->overwriteWidget($currentView, $widget);
251
            }
252
            $form = $this->widgetFormBuilder->buildForm($widget, $currentView, $businessEntityName, $widgetMode, null, null, null, $quantum);
0 ignored issues
show
Compatibility introduced by
$widget of type object<Victoire\Bundle\WidgetBundle\Model\Widget> is not a sub-type of object<Victoire\Bundle\W...etBundle\Entity\Widget>. It seems like you assume a child class of the class Victoire\Bundle\WidgetBundle\Model\Widget to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
253
254
            $noValidate = $request->query->get('novalidate', false);
255
            $form->handleRequest($request);
256
            if ($noValidate === false && $form->isValid()) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
257
258
                //force cache invalidation
259
                $widget->setUpdatedAt(new \DateTime());
260
                $this->entityManager->persist($widget);
261
262
                $this->entityManager->persist($currentView);
263
                $this->entityManager->flush();
264
265
                $response = [
266
                    'view'        => $currentView,
267
                    'success'     => true,
268
                    'html'        => $this->widgetRenderer->render($widget, $currentView),
0 ignored issues
show
Compatibility introduced by
$widget of type object<Victoire\Bundle\WidgetBundle\Model\Widget> is not a sub-type of object<Victoire\Bundle\W...etBundle\Entity\Widget>. It seems like you assume a child class of the class Victoire\Bundle\WidgetBundle\Model\Widget to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
269
                    'widgetId'    => $initialWidgetId,
270
                    'slot'        => $widget->getWidgetMap()->getSlot(),
271
                    'viewCssHash' => $currentView->getCssHash(),
272
                ];
273
            } else {
274
                $formErrorHelper = $this->formErrorHelper;
275
                //Return a message for developer in console and form view in order to refresh view and show form errors
276
                $response = [
277
                    'success'     => false,
278
                    'widgetId'    => $initialWidgetId,
279
                    'slot'        => $widget->getWidgetMap()->getSlot(),
280
                    'message'     => $noValidate === false ? $formErrorHelper->getRecursiveReadableErrors($form) : null,
281
                    'html'        => $this->widgetFormBuilder->renderForm($form, $widget, $businessEntityName),
0 ignored issues
show
Compatibility introduced by
$widget of type object<Victoire\Bundle\WidgetBundle\Model\Widget> is not a sub-type of object<Victoire\Bundle\W...etBundle\Entity\Widget>. It seems like you assume a child class of the class Victoire\Bundle\WidgetBundle\Model\Widget to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
Bug introduced by
It seems like $businessEntityName defined by parameter $businessEntityName on line 231 can also be of type string; however, Victoire\Bundle\WidgetBu...rmBuilder::renderForm() does only seem to accept object|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
282
                ];
283
            }
284
        } else {
285
            $widgets = $widget->getWidgetMap()->getWidgets();
286
            $forms = $this->widgetFormBuilder->renderNewQuantumForms($widget->getSlot(), $currentView, $widgets, $widget, $classes);
287
288
            $response = [
289
                'success'  => true,
290
                'html'     => $this->templating->render(
291
                    'VictoireCoreBundle:Widget:Form/edit.html.twig',
292
                    [
293
                        'view'               => $currentView,
294
                        'slot'               => $widget->getWidgetMap()->getSlot(),
295
                        'position'           => $widget->getWidgetMap()->getPosition(),
296
                        'parentWidgetMap'    => $widget->getWidgetMap()->getParent() ? $widget->getWidgetMap()->getParent()->getId() : null,
297
                        'classes'            => $classes,
298
                        'forms'              => $forms,
299
                        'widgets'            => $widgets,
300
                        'widget'             => $widget,
301
                    ]
302
                ),
303
            ];
304
        }
305
306
        return $response;
307
    }
308
309
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$view" missing
Loading history...
310
     * Remove a widget.
311
     *
312
     * @param Widget $widget
313
     *
314
     * @return array The parameter for the view
315
     */
316
    public function deleteWidget(Widget $widget, View $view)
317
    {
318
        //Used to update view in callback (we do it before delete it else it'll not exists anymore)
319
        $widgetId = $widget->getId();
320
        //we update the widget map of the view
321
        $this->widgetMapBuilder->build($view);
322
        $widgetMap = WidgetMapHelper::getWidgetMapByWidgetAndView($widget, $view);
0 ignored issues
show
Compatibility introduced by
$widget of type object<Victoire\Bundle\WidgetBundle\Model\Widget> is not a sub-type of object<Victoire\Bundle\W...etBundle\Entity\Widget>. It seems like you assume a child class of the class Victoire\Bundle\WidgetBundle\Model\Widget to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
323
        //the widget is removed only if the current view is the view of the widget
324
        if ($widgetMap->getView() == $view && $widgetMap->getAction() != WidgetMap::ACTION_DELETE) {
0 ignored issues
show
Bug introduced by
The method getView does only exist in Victoire\Bundle\WidgetMapBundle\Entity\WidgetMap, but not in Victoire\Bundle\WidgetMa...getMapNotFoundException.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Bug introduced by
The method getAction does only exist in Victoire\Bundle\WidgetMapBundle\Entity\WidgetMap, but not in Victoire\Bundle\WidgetMa...getMapNotFoundException.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
325
            //we remove the widget
326
            $this->entityManager->remove($widget);
327
        }
328
329
        //we update the view
330
        $this->entityManager->persist($view);
331
        //update the view deleting the widget
332
        $this->widgetMapManager->delete($view, $widget);
0 ignored issues
show
Compatibility introduced by
$widget of type object<Victoire\Bundle\WidgetBundle\Model\Widget> is not a sub-type of object<Victoire\Bundle\W...etBundle\Entity\Widget>. It seems like you assume a child class of the class Victoire\Bundle\WidgetBundle\Model\Widget to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
333
334
        $this->entityManager->flush();
335
336
        return [
337
            'success'     => true,
338
            'widgetId'    => $widgetId,
339
            'viewCssHash' => $view->getCssHash(),
340
        ];
341
    }
342
343
    /**
344
     * Overwrite the widget for the current view because the widget is not linked to the current view, a copy is created.
345
     *
346
     * @param View   $view
347
     * @param Widget $widget
348
     *
349
     * @throws \Exception The slot does not exists
350
     *
351
     * @return Widget The widget
352
     */
353
    public function overwriteWidget(View $view, Widget $widget)
354
    {
355
        $widgetCopy = $this->cloneEntity($widget);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->cloneEntity($widget); of type object|array adds the type array to the return on line 359 which is incompatible with the return type documented by Victoire\Bundle\WidgetBu...anager::overwriteWidget of type Victoire\Bundle\WidgetBundle\Model\Widget.
Loading history...
356
        $originalWidgetMap = $widget->getWidgetMap();
357
        $this->widgetMapManager->overwrite($view, $originalWidgetMap, $widgetCopy);
0 ignored issues
show
Documentation introduced by
$widgetCopy is of type object|array, but the function expects a object<Victoire\Bundle\W...etBundle\Entity\Widget>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
358
359
        return $widgetCopy;
360
    }
361
362
    /**
363
     * @param Widget $entity
364
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
365
    public function cloneEntity(Widget $entity)
366
    {
367
        $entityCopy = clone $entity;
368
        $entityCopy->setWidgetMap(null);
369
        //Look for on_to_many relations, if found, duplicate related entities.
370
        //It is necessary for 'list' widgets, this algo duplicates and persists list items.
371
        $associations = $this->entityManager->getClassMetadata(get_class($entityCopy))->getAssociationMappings();
372
        $accessor = PropertyAccess::createPropertyAccessor();
373
        foreach ($associations as $name => $values) {
374
            if ($values['type'] === ClassMetadataInfo::ONE_TO_MANY) {
375
                $relatedEntities = $accessor->getValue($entityCopy, $values['fieldName']);
376
                $relatedEntitiesCopies = [];
377
                foreach ($relatedEntities as $relatedEntity) {
378
                    $relatedEntityCopy = clone $relatedEntity;
379
                    $this->entityManager->persist($relatedEntity);
380
                    $relatedEntitiesCopies[] = $relatedEntityCopy;
381
                }
382
                $accessor->setValue($entityCopy, $name, $relatedEntitiesCopies);
383
            }
384
385
            //Clone OneToOne relation objects
386
            if ($values['type'] === ClassMetadataInfo::ONE_TO_ONE) {
387
                $relatedEntity = $accessor->getValue($entityCopy, $values['fieldName']);
388
                if ($relatedEntity) {
389
                    $relatedEntityCopy = clone $relatedEntity;
390
                    $this->entityManager->persist($relatedEntity);
391
                    $accessor->setValue($entityCopy, $name, $relatedEntityCopy);
392
                }
393
            }
394
        }
395
396
        $this->entityManager->persist($entityCopy);
0 ignored issues
show
Bug introduced by
It seems like $entityCopy can also be of type array; however, Doctrine\ORM\EntityManager::persist() does only seem to accept object, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
397
398
        return $entityCopy;
399
    }
400
}
401