Completed
Push — master ( 6e39d0...b8063b )
by Paul
10s
created

PageHelper::renderPageByUrl()   C

Complexity

Conditions 7
Paths 3

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 24
rs 6.7272
cc 7
eloc 16
nc 3
nop 3
1
<?php
2
3
namespace Victoire\Bundle\PageBundle\Helper;
4
5
use Doctrine\Common\Util\ClassUtils;
6
use Doctrine\Orm\EntityManager;
7
use Doctrine\ORM\ORMInvalidArgumentException;
8
use Symfony\Component\DependencyInjection\Container;
9
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
10
use Symfony\Component\HttpFoundation\RedirectResponse;
11
use Symfony\Component\HttpFoundation\Response;
12
use Symfony\Component\HttpFoundation\Session\Session;
13
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
14
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
15
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
16
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
17
use Victoire\Bundle\BusinessEntityBundle\Helper\BusinessEntityHelper;
18
use Victoire\Bundle\BusinessPageBundle\Builder\BusinessPageBuilder;
19
use Victoire\Bundle\BusinessPageBundle\Entity\BusinessPage;
20
use Victoire\Bundle\BusinessPageBundle\Entity\BusinessTemplate;
21
use Victoire\Bundle\BusinessPageBundle\Helper\BusinessPageHelper;
22
use Victoire\Bundle\CoreBundle\Entity\EntityProxy;
23
use Victoire\Bundle\CoreBundle\Entity\Link;
24
use Victoire\Bundle\CoreBundle\Entity\View;
25
use Victoire\Bundle\CoreBundle\Event\PageRenderEvent;
26
use Victoire\Bundle\CoreBundle\Helper\CurrentViewHelper;
27
use Victoire\Bundle\PageBundle\Entity\BasePage;
28
use Victoire\Bundle\PageBundle\Entity\Page;
29
use Victoire\Bundle\SeoBundle\Helper\PageSeoHelper;
30
use Victoire\Bundle\ViewReferenceBundle\Connector\ViewReferenceRepository;
31
use Victoire\Bundle\ViewReferenceBundle\Helper\ViewReferenceHelper;
32
use Victoire\Bundle\ViewReferenceBundle\ViewReference\BusinessPageReference;
33
use Victoire\Bundle\ViewReferenceBundle\ViewReference\ViewReference;
34
use Victoire\Bundle\WidgetMapBundle\Builder\WidgetMapBuilder;
35
use Victoire\Bundle\WidgetMapBundle\Warmer\WidgetDataWarmer;
36
37
/**
38
 * Page helper
39
 * ref: victoire_page.page_helper.
40
 */
41
class PageHelper
42
{
43
    protected $businessEntityHelper;
44
    protected $entityManager;
45
    protected $viewReferenceHelper;
46
    protected $currentViewHelper;
47
    protected $eventDispatcher;
48
    protected $container;
49
    protected $pageSeoHelper;
50
    protected $session;
51
    protected $tokenStorage;
52
    protected $widgetMapBuilder;
53
    protected $businessPageBuilder;
54
    protected $businessPageHelper;
55
    protected $viewReferenceRepository;
56
    protected $widgetDataWarmer;
57
58
    /**
59
     * @param BusinessEntityHelper     $businessEntityHelper
60
     * @param EntityManager            $entityManager
61
     * @param ViewReferenceHelper      $viewReferenceHelper
62
     * @param CurrentViewHelper        $currentViewHelper
63
     * @param EventDispatcherInterface $eventDispatcher
64
     * @param Container                $container
65
     * @param PageSeoHelper            $pageSeoHelper
66
     * @param Session                  $session
67
     * @param TokenStorage             $tokenStorage
68
     * @param AuthorizationChecker     $authorizationChecker
69
     * @param WidgetMapBuilder         $widgetMapBuilder
70
     * @param BusinessPageBuilder      $businessPageBuilder
71
     * @param BusinessPageHelper       $businessPageHelper
72
     * @param WidgetDataWarmer         $widgetDataWarmer
73
     * @param ViewReferenceRepository  $viewReferenceRepository
74
     */
75
    public function __construct(
76
        BusinessEntityHelper $businessEntityHelper,
77
        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...
78
        ViewReferenceHelper $viewReferenceHelper,
79
        CurrentViewHelper $currentViewHelper,
80
        EventDispatcherInterface $eventDispatcher,
81
        Container $container,
82
        PageSeoHelper $pageSeoHelper,
83
        Session $session,
84
        TokenStorage $tokenStorage,
85
        AuthorizationChecker $authorizationChecker,
86
        WidgetMapBuilder $widgetMapBuilder,
87
        BusinessPageBuilder $businessPageBuilder,
88
        BusinessPageHelper $businessPageHelper,
89
        WidgetDataWarmer $widgetDataWarmer,
90
        ViewReferenceRepository $viewReferenceRepository
91
    ) {
92
        $this->businessEntityHelper = $businessEntityHelper;
93
        $this->entityManager = $entityManager;
94
        $this->viewReferenceHelper = $viewReferenceHelper;
95
        $this->currentViewHelper = $currentViewHelper;
96
        $this->eventDispatcher = $eventDispatcher;
97
        $this->container = $container;
98
        $this->pageSeoHelper = $pageSeoHelper;
99
        $this->session = $session;
100
        $this->tokenStorage = $tokenStorage;
101
        $this->authorizationChecker = $authorizationChecker;
0 ignored issues
show
Bug introduced by
The property authorizationChecker 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...
102
        $this->widgetMapBuilder = $widgetMapBuilder;
103
        $this->businessPageBuilder = $businessPageBuilder;
104
        $this->businessPageHelper = $businessPageHelper;
105
        $this->widgetDataWarmer = $widgetDataWarmer;
106
        $this->viewReferenceRepository = $viewReferenceRepository;
107
    }
108
109
    /**
110
     * generates a response from parameters.
111
     *
112
     * @return View
113
     */
114
    public function findPageByParameters($parameters)
115
    {
116
        if (!empty($parameters['id']) && !preg_match('/^ref_/', $parameters['id'])) {
117
            $page = $this->entityManager->getRepository('VictoireCoreBundle:View')->findOneBy([
118
                'id' => $parameters['id'],
119
            ]);
120
121
            $entity = null;
122
            if (method_exists($page, 'getBusinessEntity')) {
123
                $entity = $page->getBusinessEntity();
124
            }
125
            $this->checkPageValidity($page, $entity, $parameters);
126
        } else {
127
            $viewReference = $this->viewReferenceRepository->getOneReferenceByParameters($parameters);
128
            if ($viewReference === null && !empty($parameters['viewId'])) {
129
                $parameters['templateId'] = $parameters['viewId'];
130
                unset($parameters['viewId']);
131
                $viewReference = $this->viewReferenceRepository->getOneReferenceByParameters($parameters);
132
            }
133
134
            if ($viewReference instanceof ViewReference) {
135
                $page = $this->findPageByReference($viewReference, $this->findEntityByReference($viewReference));
136
            } else {
137
                $parametersAsString = [];
138
                foreach ($parameters as $key => $value) {
139
                    $parametersAsString[] = $key.': '.$value;
140
                }
141
142
                throw new \Exception(sprintf('Oh no! Cannot find a viewReference for the given parameters %s', implode(',', $parametersAsString)));
143
            }
144
            $page->setReference($viewReference, $viewReference->getLocale());
145
        }
146
147
        return $page;
148
    }
149
150
    /**
151
     * generates a response from a page url.
152
     * if seo redirect, return target.
153
     *
154
     * @param string $url
155
     *
156
     * @return Response
157
     */
158
    public function renderPageByUrl($url, $locale, $isAjax = false)
159
    {
160
        $page = null;
0 ignored issues
show
Unused Code introduced by
$page is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
161
        if ($viewReference = $this->viewReferenceRepository->getReferenceByUrl($url, $locale)) {
162
            $page = $this->findPageByReference($viewReference, $entity = $this->findEntityByReference($viewReference));
163
            $this->checkPageValidity($page, $entity, ['url' => $url, 'locale' => $locale]);
164
            $page->setReference($viewReference);
165
166
            if ($page instanceof BasePage
167
                && $page->getSeo()
168
                && $page->getSeo()->getRedirectTo()
169
                && $page->getSeo()->getRedirectTo()->getLinkType() != Link::TYPE_NONE
0 ignored issues
show
Bug introduced by
The method getLinkType cannot be called on $page->getSeo()->getRedirectTo() (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
170
                && !$this->session->get('victoire.edit_mode', false)) {
171
                $link = $page->getSeo()->getRedirectTo();
172
173
                return new RedirectResponse($this->container->get('victoire_widget.twig.link_extension')->victoireLinkUrl($link->getParameters()));
0 ignored issues
show
Bug introduced by
The method getParameters cannot be called on $link (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
174
            }
175
176
177
            return $this->renderPage($page, $isAjax);
0 ignored issues
show
Bug introduced by
It seems like $page defined by $this->findPageByReferen...erence($viewReference)) on line 162 can be null; however, Victoire\Bundle\PageBund...ageHelper::renderPage() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
178
        } else {
179
            throw new NotFoundHttpException(sprintf('Page not found (url: "%s", locale: "%s")', $url, $locale));
180
        }
181
    }
182
183
    /**
184
     * generates a response from a page.
185
     *
186
     * @param View $view
187
     *
188
     * @return Response
189
     */
190
    public function renderPage($view, $isAjax = false)
191
    {
192
        $event = new \Victoire\Bundle\PageBundle\Event\Menu\PageMenuContextualEvent($view);
193
194
        //Set currentView and dispatch victoire.on_render_page event with this currentView
195
        $this->currentViewHelper->setCurrentView($view);
196
        $pageRenderEvent = new PageRenderEvent($view);
197
        $this->eventDispatcher->dispatch('victoire.on_render_page', $pageRenderEvent);
198
199
        //Build WidgetMap
200
        $this->widgetMapBuilder->build($view, $this->entityManager, true);
201
202
        //Populate widgets with their data
203
        $this->widgetDataWarmer->warm($this->entityManager, $view);
204
205
        //Dispatch contextual event regarding page type
206
        if ($view->getType() == 'business_page') {
207
            //Dispatch also an event with the Business entity name
208
            $eventName = 'victoire_core.page_menu.contextual';
209
            if (!$view->getId()) {
210
                $eventName = 'victoire_core.business_template_menu.contextual';
211
                $event = new \Victoire\Bundle\PageBundle\Event\Menu\PageMenuContextualEvent($view->getTemplate());
0 ignored issues
show
Documentation introduced by
$view->getTemplate() is of type string, but the function expects a object<Victoire\Bundle\CoreBundle\Entity\View>.

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...
212
            }
213
            $this->eventDispatcher->dispatch($eventName, $event);
214
            $type = $view->getBusinessEntityId();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Victoire\Bundle\CoreBundle\Entity\View as the method getBusinessEntityId() does only exist in the following sub-classes of Victoire\Bundle\CoreBundle\Entity\View: Victoire\Bundle\BlogBundle\Entity\ArticleTemplate, Victoire\Bundle\Business...dle\Entity\BusinessPage, Victoire\Bundle\Business...Entity\BusinessTemplate, Victoire\Bundle\Business...ity\VirtualBusinessPage. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
215
        } else {
216
            $type = $view->getType();
217
        }
218
219
        $eventName = 'victoire_core.'.$type.'_menu.contextual';
220
        $this->eventDispatcher->dispatch($eventName, $event);
221
222
        //Determine which layout to use
223
        $layout = $this->guessBestLayoutForView($view, $isAjax);
224
225
        //Create the response
226
        $response = $this->container->get('templating')->renderResponse('VictoireCoreBundle:Layout:'.$layout, [
227
            'view' => $view,
228
        ]);
229
230
        return $response;
231
    }
232
233
    /**
234
     * populate the page with given entity.
235
     *
236
     * @param View           $page
237
     * @param BusinessEntity $entity
238
     */
239
    public function updatePageWithEntity(BusinessTemplate $page, $entity)
240
    {
241
        $page = $this->businessPageBuilder->generateEntityPageFromTemplate($page, $entity, $this->entityManager);
242
        $this->pageSeoHelper->updateSeoByEntity($page, $entity);
243
244
        //update the parameters of the page
245
        $this->businessPageBuilder->updatePageParametersByEntity($page, $entity);
246
247
        return $page;
248
    }
249
250
    /**
251
     * @param BusinessPageReference $viewReference
252
     *
253
     * @return BusinessPage
254
     *                      read the cache to find entity according tu given url.
255
     * @return object|null
256
     */
257
    protected function findEntityByReference(ViewReference $viewReference)
258
    {
259
        if ($viewReference instanceof BusinessPageReference && !empty($viewReference->getEntityId())) {
260
            return $this->entityManager->getRepository($viewReference->getEntityNamespace())
261
                ->findOneById($viewReference->getEntityId());
262
        }
263
    }
264
265
    /**
266
     * find the page according to given url.
267
     *
268
     * @return View
269
     */
270
    public function findPageByReference($viewReference, $entity = null)
271
    {
272
        $page = null;
0 ignored issues
show
Unused Code introduced by
$page is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
273
        if ($viewReference instanceof BusinessPageReference) {
274
            if ($viewReference->getViewId()) { //BusinessPage
275
                $page = $this->entityManager->getRepository('VictoireCoreBundle:View')
276
                    ->findOneBy([
277
                        'id'     => $viewReference->getViewId(),
278
                    ]);
279
                $page->setCurrentLocale($viewReference->getLocale());
280
            } else { //VirtualBusinessPage
281
                $page = $this->entityManager->getRepository('VictoireCoreBundle:View')
282
                    ->findOneBy([
283
                        'id'     => $viewReference->getTemplateId(),
284
                    ]);
285
                $page->setCurrentLocale($viewReference->getLocale());
286
                if ($entity) {
287
                    if ($page instanceof BusinessTemplate) {
288
                        $page = $this->updatePageWithEntity($page, $entity);
289
                    }
290
                    if ($page instanceof BusinessPage) {
291
                        if ($page->getSeo()) {
292
                            $page->getSeo()->setCurrentLocale($viewReference->getLocale());
293
                        }
294
                        $this->pageSeoHelper->updateSeoByEntity($page, $entity);
295
                    }
296
                }
297
            }
298
        } elseif ($viewReference instanceof ViewReference) {
299
            $page = $this->entityManager->getRepository('VictoireCoreBundle:View')
300
                ->findOneBy([
301
                    'id'     => $viewReference->getViewId(),
302
                ]);
303
            $page->setCurrentLocale($viewReference->getLocale());
304
        } else {
305
            throw new \Exception(sprintf('Oh no! Cannot find a page for this ViewReference (%s)', ClassUtils::getClass($viewReference)));
306
        }
307
308
        return $page;
309
    }
310
311
    /**
312
     * @param View $page
313
     * @param $locale
314
     */
315
    private function refreshPage($page, $locale)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
316
    {
317
        if ($page && $page instanceof View) {
318
            try {
319
                $this->entityManager->refresh($page->setTranslatableLocale($locale));
0 ignored issues
show
Bug introduced by
The method setTranslatableLocale() does not seem to exist on object<Victoire\Bundle\CoreBundle\Entity\View>.

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.

Loading history...
320
            } catch (ORMInvalidArgumentException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
321
            }
322
        }
323
    }
324
325
    /**
326
     * If the page is not valid, an exception is thrown.
327
     *
328
     * @param mixed $page
329
     * @param mixed $entity
330
     * @param mixed $parameters
331
     *
332
     * @throws \Exception
333
     */
334
    protected function checkPageValidity($page, $entity = null, $parameters = null)
335
    {
336
        $errorMessage = 'The page was not found';
337
        if ($parameters) {
338
            $errorMessage .= ' for parameters "'.implode('", "', $parameters).'"';
339
        }
340
        $isPageOwner = false;
341
342
        //there is no page
343
        if ($page === null) {
344
            throw new NotFoundHttpException($errorMessage);
345
        }
346
347
        if ($this->tokenStorage->getToken()) {
348
            $isPageOwner = $this->authorizationChecker->isGranted('PAGE_OWNER', $page);
349
        }
350
351
        //a page not published, not owned, nor granted throw an exception
352
        if (($page instanceof BasePage && !$page->isPublished()) && !$isPageOwner) {
353
            throw new NotFoundHttpException($errorMessage);
354
        }
355
356
        //if the page is a BusinessTemplate and the entity is not allowed for this page pattern
357
        if ($page instanceof BusinessTemplate) {
358
            //only victoire users are able to access a business page
359
            if (!$this->authorizationChecker->isGranted('ROLE_VICTOIRE')) {
360
                throw new AccessDeniedException('You are not allowed to see this page');
361
            }
362
        } elseif ($page instanceof BusinessPage) {
363
            if ($page->getTemplate()->isAuthorRestricted() && !$this->authorizationChecker->isGranted('BUSINESS_ENTITY_OWNER', $page->getBusinessEntity())) {
0 ignored issues
show
Bug introduced by
The method isAuthorRestricted cannot be called on $page->getTemplate() (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
364
                throw new AccessDeniedException('You are not allowed to see this page');
365
            }
366
367
            if (!$entity->isVisibleOnFront() && !$this->authorizationChecker->isGranted('ROLE_VICTOIRE')) {
368
                throw new NotFoundHttpException('The BusinessPage for '.get_class($entity).'#'.$entity->getId().' is not visible on front.');
369
            }
370
            if (!$page->getId()) {
371
                $entityAllowed = $this->businessPageHelper->isEntityAllowed($page->getTemplate(), $entity, $this->entityManager);
0 ignored issues
show
Documentation introduced by
$page->getTemplate() is of type string, but the function expects a object<Victoire\Bundle\B...ntity\BusinessTemplate>.

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...
372
373
                if ($entityAllowed === false) {
374
                    throw new NotFoundHttpException('The entity ['.$entity->getId().'] is not allowed for the page pattern ['.$page->getTemplate()->getId().']');
0 ignored issues
show
Bug introduced by
The method getId cannot be called on $page->getTemplate() (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
375
                }
376
            }
377
        }
378
    }
379
380
    /**
381
     * Create an instance of the business entity page.
382
     *
383
     * @param BusinessTemplate $BusinessTemplate The business entity page
384
     * @param entity           $entity           The entity
385
     * @param string           $url              The new url
386
     *
387
     * @return \Victoire\Bundle\PageBundle\Entity\Page
388
     */
389
    public function createPageInstanceFromBusinessTemplate(BusinessTemplate $BusinessTemplate, $entity, $url)
390
    {
391
        //create a new page
392
        $newPage = new Page();
393
394
        $parentPage = $BusinessTemplate->getParent();
395
396
        //set the page parameter by the business entity page
397
        $newPage->setParent($parentPage);
398
        $newPage->setTemplate($BusinessTemplate);
399
        $newPage->setUrl($url);
400
401
        $newPage->setTitle($BusinessTemplate->getTitle());
0 ignored issues
show
Bug introduced by
The method getTitle() does not seem to exist on object<Victoire\Bundle\B...ntity\BusinessTemplate>.

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.

Loading history...
Bug introduced by
The method setTitle() does not seem to exist on object<Victoire\Bundle\PageBundle\Entity\Page>.

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.

Loading history...
402
403
        //update the parameters of the page
404
        $this->businessPageBuilder->updatePageParametersByEntity($newPage, $entity);
0 ignored issues
show
Compatibility introduced by
$newPage of type object<Victoire\Bundle\PageBundle\Entity\Page> is not a sub-type of object<Victoire\Bundle\B...le\Entity\BusinessPage>. It seems like you assume a child class of the class Victoire\Bundle\PageBundle\Entity\Page 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...
405
406
        $businessEntity = $this->businessEntityHelper->findByEntityInstance($entity);
407
        $entityProxy = new EntityProxy();
408
        $entityProxy->setEntity($entity, $businessEntity->getName());
409
410
        $newPage->setEntityProxy($entityProxy);
411
412
        return $newPage;
413
    }
414
415
    /**
416
     * Guess which layout to use for a given View.
417
     *
418
     * @param View $view
419
     * @param bool $isAjax
420
     *
421
     * @return string
422
     */
423
    private function guessBestLayoutForView(View $view, $isAjax)
424
    {
425
        if ($isAjax) {
426
            $viewLayout = 'modal';
427
        } elseif (method_exists($view, 'getLayout') && $view->getLayout()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Victoire\Bundle\CoreBundle\Entity\View as the method getLayout() does only exist in the following sub-classes of Victoire\Bundle\CoreBundle\Entity\View: Victoire\Bundle\BlogBundle\Entity\ArticleTemplate, Victoire\Bundle\Business...Entity\BusinessTemplate, Victoire\Bundle\TemplateBundle\Entity\Template. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
428
            $viewLayout = $view->getLayout();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Victoire\Bundle\CoreBundle\Entity\View as the method getLayout() does only exist in the following sub-classes of Victoire\Bundle\CoreBundle\Entity\View: Victoire\Bundle\BlogBundle\Entity\ArticleTemplate, Victoire\Bundle\Business...Entity\BusinessTemplate, Victoire\Bundle\TemplateBundle\Entity\Template. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
429
        } else {
430
            $viewLayout = $view->getTemplate()->getLayout();
0 ignored issues
show
Bug introduced by
The method getLayout cannot be called on $view->getTemplate() (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
431
        }
432
433
        return $viewLayout.'.html.twig';
434
    }
435
}
436