PageHelper   F
last analyzed

Complexity

Total Complexity 67

Size/Duplication

Total Lines 471
Duplicated Lines 2.97 %

Coupling/Cohesion

Components 1
Dependencies 32

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 67
lcom 1
cbo 32
dl 14
loc 471
rs 1.3043
c 1
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 35 1
D findPageByParameters() 0 32 9
C renderPageByUrl() 0 23 7
B renderPage() 0 44 4
A updatePageWithEntity() 0 10 1
A findEntityByReference() 9 19 4
C findPageByReference() 5 58 9
C checkPageValidity() 0 53 19
B createPageInstanceFromBusinessTemplate() 0 25 1
A guessBestLayoutForView() 0 10 3
C getPageRoles() 0 24 7
A setPosition() 0 13 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PageHelper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PageHelper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Victoire\Bundle\PageBundle\Helper;
4
5
use Doctrine\Common\Util\ClassUtils;
6
use Doctrine\Orm\EntityManager;
7
use Symfony\Component\DependencyInjection\Container;
8
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
9
use Symfony\Component\HttpFoundation\RedirectResponse;
10
use Symfony\Component\HttpFoundation\Response;
11
use Symfony\Component\HttpFoundation\Session\Session;
12
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
13
use Symfony\Component\PropertyAccess\PropertyAccessor;
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\APIBusinessEntityBundle\Resolver\APIBusinessEntityResolver;
18
use Victoire\Bundle\BusinessEntityBundle\Entity\BusinessEntityInterface;
19
use Victoire\Bundle\BusinessEntityBundle\Helper\BusinessEntityHelper;
20
use Victoire\Bundle\BusinessPageBundle\Builder\BusinessPageBuilder;
21
use Victoire\Bundle\BusinessPageBundle\Entity\BusinessPage;
22
use Victoire\Bundle\BusinessPageBundle\Entity\BusinessTemplate;
23
use Victoire\Bundle\BusinessPageBundle\Helper\BusinessPageHelper;
24
use Victoire\Bundle\CoreBundle\Entity\EntityProxy;
25
use Victoire\Bundle\CoreBundle\Entity\Link;
26
use Victoire\Bundle\CoreBundle\Entity\View;
27
use Victoire\Bundle\CoreBundle\Event\PageRenderEvent;
28
use Victoire\Bundle\CoreBundle\Helper\CurrentViewHelper;
29
use Victoire\Bundle\ORMBusinessEntityBundle\Entity\ORMBusinessEntity;
30
use Victoire\Bundle\PageBundle\Entity\BasePage;
31
use Victoire\Bundle\PageBundle\Entity\Page;
32
use Victoire\Bundle\SeoBundle\Helper\PageSeoHelper;
33
use Victoire\Bundle\ViewReferenceBundle\Connector\ViewReferenceRepository;
34
use Victoire\Bundle\ViewReferenceBundle\Exception\ViewReferenceNotFoundException;
35
use Victoire\Bundle\ViewReferenceBundle\Helper\ViewReferenceHelper;
36
use Victoire\Bundle\ViewReferenceBundle\ViewReference\BusinessPageReference;
37
use Victoire\Bundle\ViewReferenceBundle\ViewReference\ViewReference;
38
use Victoire\Bundle\WidgetMapBundle\Builder\WidgetMapBuilder;
39
use Victoire\Bundle\WidgetMapBundle\Warmer\WidgetDataWarmer;
40
41
/**
42
 * Page helper
43
 * ref: victoire_page.page_helper.
44
 */
45
class PageHelper
46
{
47
    protected $businessEntityHelper;
48
    protected $entityManager;
49
    protected $viewReferenceHelper;
50
    protected $currentViewHelper;
51
    protected $eventDispatcher;
52
    protected $container;
53
    protected $pageSeoHelper;
54
    protected $session;
55
    protected $tokenStorage;
56
    protected $widgetMapBuilder;
57
    protected $businessPageBuilder;
58
    protected $businessPageHelper;
59
    protected $viewReferenceRepository;
60
    protected $widgetDataWarmer;
61
    protected $apiBusinessEntityResolver;
62
63
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$apiBusinessEntityResolver" missing
Loading history...
64
     * @param BusinessEntityHelper     $businessEntityHelper
65
     * @param EntityManager            $entityManager
66
     * @param ViewReferenceHelper      $viewReferenceHelper
67
     * @param CurrentViewHelper        $currentViewHelper
68
     * @param EventDispatcherInterface $eventDispatcher
69
     * @param Container                $container
70
     * @param PageSeoHelper            $pageSeoHelper
71
     * @param Session                  $session
72
     * @param TokenStorage             $tokenStorage
73
     * @param AuthorizationChecker     $authorizationChecker
74
     * @param WidgetMapBuilder         $widgetMapBuilder
75
     * @param BusinessPageBuilder      $businessPageBuilder
76
     * @param BusinessPageHelper       $businessPageHelper
77
     * @param WidgetDataWarmer         $widgetDataWarmer
78
     * @param ViewReferenceRepository  $viewReferenceRepository
79
     */
80
    public function __construct(
81
        BusinessEntityHelper $businessEntityHelper,
82
        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...
83
        ViewReferenceHelper $viewReferenceHelper,
84
        CurrentViewHelper $currentViewHelper,
85
        EventDispatcherInterface $eventDispatcher,
86
        Container $container,
87
        PageSeoHelper $pageSeoHelper,
88
        Session $session,
89
        TokenStorage $tokenStorage,
90
        AuthorizationChecker $authorizationChecker,
91
        WidgetMapBuilder $widgetMapBuilder,
92
        BusinessPageBuilder $businessPageBuilder,
93
        BusinessPageHelper $businessPageHelper,
94
        WidgetDataWarmer $widgetDataWarmer,
95
        ViewReferenceRepository $viewReferenceRepository,
96
        APIBusinessEntityResolver $apiBusinessEntityResolver
97
    ) {
98
        $this->businessEntityHelper = $businessEntityHelper;
99
        $this->entityManager = $entityManager;
100
        $this->viewReferenceHelper = $viewReferenceHelper;
101
        $this->currentViewHelper = $currentViewHelper;
102
        $this->eventDispatcher = $eventDispatcher;
103
        $this->container = $container;
104
        $this->pageSeoHelper = $pageSeoHelper;
105
        $this->session = $session;
106
        $this->tokenStorage = $tokenStorage;
107
        $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...
108
        $this->widgetMapBuilder = $widgetMapBuilder;
109
        $this->businessPageBuilder = $businessPageBuilder;
110
        $this->businessPageHelper = $businessPageHelper;
111
        $this->widgetDataWarmer = $widgetDataWarmer;
112
        $this->viewReferenceRepository = $viewReferenceRepository;
113
        $this->apiBusinessEntityResolver = $apiBusinessEntityResolver;
114
    }
115
116
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$parameters" missing
Loading history...
117
     * generates a response from parameters.
118
     *
119
     * @return View
120
     */
121
    public function findPageByParameters($parameters)
122
    {
123
        if (!empty($parameters['id']) && !preg_match('/^ref_/', $parameters['id'])) {
124
            $page = $this->entityManager->getRepository('VictoireCoreBundle:View')->findOneBy([
125
                'id' => $parameters['id'],
126
            ]);
127
128
            $this->checkPageValidity($page, $parameters);
129
        } else {
130
            if (isset($parameters['id']) && isset($parameters['locale'])) {
131
                //if locale is missing, we add append locale
132
                if (preg_match('/^ref_[0-9]*$/', $parameters['id'])) {
133
                    $parameters['id'] .= '_'.$parameters['locale'];
134
                }
135
            }
136
            $viewReference = $this->viewReferenceRepository->getOneReferenceByParameters($parameters);
137
            if ($viewReference === null && !empty($parameters['viewId'])) {
138
                $parameters['templateId'] = $parameters['viewId'];
139
                unset($parameters['viewId']);
140
                $viewReference = $this->viewReferenceRepository->getOneReferenceByParameters($parameters);
141
            }
142
143
            if ($viewReference instanceof ViewReference) {
144
                $page = $this->findPageByReference($viewReference);
145
            } else {
146
                throw new ViewReferenceNotFoundException($parameters);
147
            }
148
            $page->setReference($viewReference, $viewReference->getLocale());
149
        }
150
151
        return $page;
152
    }
153
154
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$locale" missing
Loading history...
155
     * generates a response from a page url.
156
     * if seo redirect, return target.
157
     *
158
     * @param string $url
159
     * @param        $locale
0 ignored issues
show
introduced by
Missing parameter name
Loading history...
160
     * @param null   $layout
161
     *
162
     * @return Response
163
     */
164
    public function renderPageByUrl($url, $locale, $layout = null)
165
    {
166
        $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...
167
        if ($viewReference = $this->viewReferenceRepository->getReferenceByUrl($url, $locale)) {
168
            $page = $this->findPageByReference($viewReference);
169
            $this->checkPageValidity($page, ['url' => $url, 'locale' => $locale]);
170
            $page->setReference($viewReference);
171
172
            if ($page instanceof BasePage
173
                && $page->getSeo()
174
                && $page->getSeo()->getRedirectTo()
175
                && $page->getSeo()->getRedirectTo()->getLinkType() != Link::TYPE_NONE
176
                && !$this->session->get('victoire.edit_mode', false)) {
177
                $link = $page->getSeo()->getRedirectTo();
178
179
                return new RedirectResponse($this->container->get('victoire_widget.twig.link_extension')->victoireLinkUrl($link->getParameters()));
180
            }
181
182
            return $this->renderPage($page, $layout);
0 ignored issues
show
Bug introduced by
It seems like $page defined by $this->findPageByReference($viewReference) on line 168 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...
183
        } else {
184
            throw new NotFoundHttpException(sprintf('Page not found (url: "%s", locale: "%s")', $url, $locale));
185
        }
186
    }
187
188
    /**
189
     * generates a response from a page.
190
     *
191
     * @param View $view
192
     * @param null $layout
193
     *
194
     * @return Response
195
     */
196
    public function renderPage($view, $layout = null)
197
    {
198
        $event = new \Victoire\Bundle\PageBundle\Event\Menu\PageMenuContextualEvent($view);
199
200
        //Set currentView and dispatch victoire.on_render_page event with this currentView
201
        $pageRenderEvent = new PageRenderEvent($view);
202
        $this->eventDispatcher->dispatch('victoire.on_render_page', $pageRenderEvent);
203
        $this->currentViewHelper->setCurrentView($view);
204
205
        //Build WidgetMap
206
        $this->widgetMapBuilder->build($view, true);
207
208
        //Populate widgets with their data
209
        $this->widgetDataWarmer->warm($this->entityManager, $view);
210
211
        //Dispatch contextual event regarding page type
212
        if (in_array($view->getType(), ['business_page', 'virtual_business_page'])) {
213
            //Dispatch also an event with the Business entity name
214
            $eventName = 'victoire_core.page_menu.contextual';
215
            if (!$view->getId()) {
216
                $eventName = 'victoire_core.business_template_menu.contextual';
217
                $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...
218
            }
219
            $this->eventDispatcher->dispatch($eventName, $event);
220
            $type = $view->getBusinessEntity()->getName();
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 getBusinessEntity() 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...
221
        } else {
222
            $type = $view->getType();
223
        }
224
225
        $eventName = 'victoire_core.'.strtolower($type).'_menu.contextual';
226
        $this->eventDispatcher->dispatch($eventName, $event);
227
228
        if (null === $layout) {
229
            //Determine which layout to use
230
            $layout = $this->guessBestLayoutForView($view);
231
        }
232
233
        //Create the response
234
        $response = $this->container->get('templating')->renderResponse('VictoireCoreBundle:Layout:'.$layout.'.html.twig', [
235
            'view' => $view,
236
        ]);
237
238
        return $response;
239
    }
240
241
    /**
242
     * populate the page with given entity.
243
     *
244
     * @param View                    $page
245
     * @param BusinessEntityInterface $entity
246
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
247
    public function updatePageWithEntity(BusinessTemplate $page, $entity)
248
    {
249
        $page = $this->businessPageBuilder->generateEntityPageFromTemplate($page, $entity, $this->entityManager);
250
        $this->pageSeoHelper->updateSeoByEntity($page, $entity);
0 ignored issues
show
Documentation introduced by
$entity is of type object<Victoire\Bundle\B...usinessEntityInterface>, but the function expects a object<Victoire\Bundle\SeoBundle\Helper\Entity>.

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...
251
252
        //update the parameters of the page
253
        $this->businessPageBuilder->updatePageParametersByEntity($page, $entity);
0 ignored issues
show
Documentation introduced by
$entity is of type object<Victoire\Bundle\B...usinessEntityInterface>, but the function expects a object<Victoire\Bundle\B...eBundle\Builder\Entity>.

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...
254
255
        return $page;
256
    }
257
258
    /**
259
     * @param ViewReference $viewReference
260
     *
261
     * @return BusinessEntityInterface
262
     *                                 read the cache to find entity according tu given url.
263
     */
264
    protected function findEntityByReference(ViewReference $viewReference)
265
    {
266
        $entity = null;
267
        if ($viewReference instanceof BusinessPageReference && !empty($viewReference->getEntityId())) {
268
            $businessEntity = $this->entityManager->getRepository('VictoireBusinessEntityBundle:BusinessEntity')
269
                ->findOneBy(['id' => $viewReference->getBusinessEntity()]);
270 View Code Duplication
            if ($businessEntity->getType() === ORMBusinessEntity::TYPE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
271
                $entity = $this->entityManager->getRepository($businessEntity->getClass())
272
                    ->findOneBy(['id' => $viewReference->getEntityId()]);
273
            } else {
274
                $entityProxy = new EntityProxy();
275
                $entityProxy->setBusinessEntity($businessEntity);
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...
276
                $entityProxy->setRessourceId($viewReference->getEntityId());
277
                $entity = $this->apiBusinessEntityResolver->getBusinessEntity($entityProxy);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->apiBusinessEntity...ssEntity($entityProxy); of type array adds the type array to the return on line 281 which is incompatible with the return type documented by Victoire\Bundle\PageBund...::findEntityByReference of type Victoire\Bundle\Business...essEntityInterface|null.
Loading history...
278
            }
279
        }
280
281
        return $entity;
282
    }
283
284
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$viewReference" missing
Loading history...
285
     * find the page according to given url.
286
     *
287
     * @return View
288
     */
289
    public function findPageByReference($viewReference)
0 ignored issues
show
introduced by
Declare public methods first,then protected ones and finally private ones
Loading history...
290
    {
291
        $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...
292
        if ($viewReference instanceof BusinessPageReference) {
293
            if ($viewReference->getViewId()) { //BusinessPage
294
                $page = $this->entityManager->getRepository('VictoireCoreBundle:View')
295
                    ->findOneBy([
296
                        'id' => $viewReference->getViewId(),
297
                    ]);
298
                $entity = $this->container->get('victoire_business_entity.resolver.business_entity_resolver')->getBusinessEntity($page->getEntityProxy());
299
                $page->getEntityProxy()->setEntity($entity);
300
301
                $page->setCurrentLocale($viewReference->getLocale());
302
            } else { //VirtualBusinessPage
303
                $page = $this->entityManager->getRepository('VictoireCoreBundle:View')
304
                    ->findOneBy([
305
                        'id' => $viewReference->getTemplateId(),
306
                    ]);
307
                if ($entity = $this->findEntityByReference($viewReference)) {
308
                    if ($page instanceof BusinessTemplate) {
309
                        $page = $this->updatePageWithEntity($page, $entity);
310
                    }
311
                    if ($page instanceof BusinessPage) {
312
                        if ($page->getSeo()) {
313
                            $page->getSeo()->setCurrentLocale($viewReference->getLocale());
314
                        }
315
                        $this->pageSeoHelper->updateSeoByEntity($page, $entity);
0 ignored issues
show
Documentation introduced by
$entity is of type object<Victoire\Bundle\B...usinessEntityInterface>, but the function expects a object<Victoire\Bundle\SeoBundle\Helper\Entity>.

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...
316
                    }
317
                    $entityProxy = new EntityProxy();
318
                    $accessor = new PropertyAccessor();
319
320 View Code Duplication
                    if (method_exists($entity, 'getId')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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.

Loading history...
321
                        $entityId = $entity->getId();
322
                    } else {
323
                        $entityId = $accessor->getValue($entity, $page->getBusinessEntity()->getBusinessIdentifiers()->first()->getName());
324
                    }
325
                    $entityProxy->setRessourceId($entityId);
326
                    $this->findEntityByReference($viewReference);
327
328
                    $entityProxy->setBusinessEntity($this->entityManager->getRepository('VictoireBusinessEntityBundle:BusinessEntity')
329
                        ->findOneById($viewReference->getBusinessEntity()));
0 ignored issues
show
Coding Style introduced by
This line of the multi-line function call does not seem to be indented correctly. Expected 20 spaces, but found 24.
Loading history...
330
                    $entityProxy->setEntity($entity);
331
332
                    $page->setEntityProxy($entityProxy);
333
                }
334
            }
335
        } elseif ($viewReference instanceof ViewReference) {
336
            $page = $this->entityManager->getRepository('VictoireCoreBundle:View')
337
                ->findOneBy([
338
                    'id' => $viewReference->getViewId(),
339
                ]);
340
            $page->setCurrentLocale($viewReference->getLocale());
341
        } else {
342
            throw new \Exception(sprintf('Oh no! Cannot find a page for this ViewReference (%s)', ClassUtils::getClass($viewReference)));
343
        }
344
345
        return $page;
346
    }
347
348
    /**
349
     * If the page is not valid, an exception is thrown.
350
     *
351
     * @param mixed $page
352
     * @param mixed $parameters
353
     *
354
     * @throws \Exception
355
     */
356
    public function checkPageValidity($page, $parameters = null)
357
    {
358
        $entity = null;
359
        $errorMessage = 'The page was not found';
360
        if ($parameters) {
361
            $errorMessage .= ' for parameters "'.implode('", "', $parameters).'"';
362
        }
363
        $isPageOwner = false;
364
365
        //there is no page
366
        if ($page === null) {
367
            throw new NotFoundHttpException($errorMessage);
368
        }
369
370
        if ($this->tokenStorage->getToken()) {
371
            $isPageOwner = $this->authorizationChecker->isGranted('PAGE_OWNER', $page);
372
        }
373
374
        //a page not published, not owned, nor granted throw an exception
375
        if (($page instanceof BasePage && !$page->isPublished()) && !$isPageOwner) {
376
            throw new NotFoundHttpException($errorMessage);
377
        }
378
379
        //if the page is a BusinessTemplate and the entity is not allowed for this page pattern
380
        if ($page instanceof BusinessTemplate) {
381
            //only victoire users are able to access a business page
382
            if (!$this->authorizationChecker->isGranted('ROLE_VICTOIRE')) {
383
                throw new AccessDeniedException('You are not allowed to see this page');
384
            }
385
        } elseif ($page instanceof BusinessPage) {
386
            if ($entity = $page->getEntity()) {
387
                if ($page->getBusinessEntity()->getType() === ORMBusinessEntity::TYPE && !$entity->isVisibleOnFront() && !$this->authorizationChecker->isGranted('ROLE_VICTOIRE')) {
388
                    throw new NotFoundHttpException('The BusinessPage for '.get_class($entity).'#'.$entity->getId().' is not visible on front.');
389
                }
390
            } else {
391
                throw new NotFoundHttpException('The BusinessPage has no related entity.');
392
            }
393
            if (!$page->getId()) {
394
                $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...
395
396
                if ($entityAllowed === false) {
397
                    throw new NotFoundHttpException('The entity ['.$entity->getId().'] is not allowed for the page pattern ['.$page->getTemplate()->getId().']');
398
                }
399
            }
400
        }
401
402
        if (!$this->authorizationChecker->isGranted('ROLE_VICTOIRE')) {
403
            $roles = $this->getPageRoles($page);
404
            if ($roles && !$this->authorizationChecker->isGranted($roles, $entity)) {
405
                throw new AccessDeniedException('You are not allowed to see this page, see the access roles defined in the view or its parents and templates');
406
            }
407
        }
408
    }
409
410
    /**
411
     * Create an instance of the business entity page.
412
     *
413
     * @param BusinessTemplate $BusinessTemplate The business entity page
414
     * @param entity           $entity           The entity
415
     * @param string           $url              The new url
416
     *
417
     * @return \Victoire\Bundle\PageBundle\Entity\Page
418
     */
419
    public function createPageInstanceFromBusinessTemplate(BusinessTemplate $BusinessTemplate, $entity, $url)
0 ignored issues
show
Coding Style introduced by
Variable "BusinessTemplate" is not in valid camel caps format
Loading history...
420
    {
421
        //create a new page
422
        $newPage = new Page();
423
424
        $parentPage = $BusinessTemplate->getParent();
0 ignored issues
show
Coding Style introduced by
Variable "BusinessTemplate" is not in valid camel caps format
Loading history...
425
426
        //set the page parameter by the business entity page
427
        $newPage->setParent($parentPage);
428
        $newPage->setTemplate($BusinessTemplate);
0 ignored issues
show
Coding Style introduced by
Variable "BusinessTemplate" is not in valid camel caps format
Loading history...
429
        $newPage->setUrl($url);
430
431
        $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...
Coding Style introduced by
Variable "BusinessTemplate" is not in valid camel caps format
Loading history...
432
433
        //update the parameters of the page
434
        $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...
435
436
        $businessEntity = $this->businessEntityHelper->findByEntityInstance($entity);
437
        $entityProxy = new EntityProxy();
438
        $entityProxy->setEntity($entity, $businessEntity->getName());
0 ignored issues
show
Unused Code introduced by
The call to EntityProxy::setEntity() has too many arguments starting with $businessEntity->getName().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
439
440
        $newPage->setEntityProxy($entityProxy);
441
442
        return $newPage;
443
    }
444
445
    /**
446
     * Guess which layout to use for a given View.
447
     *
448
     * @param View $view
449
     *
450
     * @return string
451
     */
452
    private function guessBestLayoutForView(View $view)
453
    {
454
        if (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...
455
            $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...
456
        } else {
457
            $viewLayout = $view->getTemplate()->getLayout();
458
        }
459
460
        return $viewLayout;
461
    }
462
463
    /**
464
     * Find page's ancestors (templates and parents) and flatted all their roles.
465
     *
466
     * @param View $view
467
     *
468
     * @return array
469
     */
470
    private function getPageRoles(View $view)
471
    {
472
        $insertAncestorRole = function (View $view = null) use (&$insertAncestorRole) {
473
            if ($view === null) {
474
                return;
475
            }
476
            $roles = $view->getRoles();
477
478
            if ($templateRoles = $insertAncestorRole($view->getTemplate(), $roles)) {
479
                $roles .= ($roles ? ',' : '').$templateRoles;
480
            }
481
            if ($parentRoles = $insertAncestorRole($view->getParent(), $roles)) {
482
                $roles .= ($roles ? ',' : '').$parentRoles;
483
            }
484
485
            return $roles;
486
        };
487
488
        $roles = $insertAncestorRole($view);
489
490
        if ($roles) {
491
            return array_unique(explode(',', $roles));
492
        }
493
    }
494
495
    /**
496
     * Set Page position.
497
     *
498
     * @param BasePage $page
499
     *
500
     * @return BasePage $page
501
     */
502
    public function setPosition(BasePage $page)
0 ignored issues
show
introduced by
Declare public methods first,then protected ones and finally private ones
Loading history...
503
    {
504
        if ($page->getParent()) {
505
            $pageNb = count($page->getParent()->getChildren());
506
        } else {
507
            $pageNb = count($this->entityManager->getRepository('VictoirePageBundle:BasePage')->findByParent(null));
508
        }
509
510
        // + 1 because position start at 1, not 0
511
        $page->setPosition($pageNb + 1);
512
513
        return $page;
514
    }
515
}
516