Completed
Push — master ( 06c1ce...67d37c )
by Jeroen
06:20
created

NodeAdminController   F

Complexity

Total Complexity 75

Size/Duplication

Total Lines 1259
Duplicated Lines 4.69 %

Coupling/Cohesion

Components 1
Dependencies 31

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 75
lcom 1
cbo 31
dl 59
loc 1259
ccs 0
cts 750
cp 0
rs 1.2
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 12 1
A indexAction() 0 34 3
A copyFromOtherLanguageAction() 0 36 1
A recopyFromOtherLanguageAction() 0 35 1
A createEmptyPageAction() 0 27 1
A publishAction() 12 12 1
A unPublishAction() 12 12 1
A unSchedulePublishAction() 17 17 1
A deleteAction() 0 49 3
B duplicateAction() 0 57 4
A duplicateWithChildrenAction() 0 15 2
B revertAction() 0 68 4
A addAction() 0 53 2
A addHomepageAction() 0 40 1
B reorderAction() 0 56 6
F editAction() 18 210 24
A checkNodeVersionLockAction() 0 29 4
A isNodeVersionLocked() 0 12 2
A createDraftVersion() 0 41 1
A checkPermission() 0 6 2
A deleteNodeChildren() 0 43 2
A createNewPage() 0 16 3
A validatePageType() 0 10 2
A renderNodeNotTranslatedPage() 0 38 3

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 NodeAdminController 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 NodeAdminController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kunstmaan\NodeBundle\Controller;
4
5
use DateTime;
6
use Doctrine\Common\Collections\ArrayCollection;
7
use Doctrine\ORM\EntityManager;
8
use InvalidArgumentException;
9
use Kunstmaan\AdminBundle\Entity\BaseUser;
10
use Kunstmaan\AdminBundle\Entity\EntityInterface;
11
use Kunstmaan\AdminBundle\FlashMessages\FlashTypes;
12
use Kunstmaan\AdminBundle\Helper\FormWidgets\FormWidget;
13
use Kunstmaan\AdminBundle\Helper\FormWidgets\Tabs\Tab;
14
use Kunstmaan\AdminBundle\Helper\FormWidgets\Tabs\TabPane;
15
use Kunstmaan\AdminBundle\Helper\Security\Acl\AclHelper;
16
use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\PermissionMap;
17
use Kunstmaan\AdminBundle\Service\AclManager;
18
use Kunstmaan\AdminListBundle\AdminList\AdminList;
19
use Kunstmaan\NodeBundle\AdminList\NodeAdminListConfigurator;
20
use Kunstmaan\NodeBundle\Entity\HasNodeInterface;
21
use Kunstmaan\NodeBundle\Entity\Node;
22
use Kunstmaan\NodeBundle\Entity\NodeTranslation;
23
use Kunstmaan\NodeBundle\Entity\NodeVersion;
24
use Kunstmaan\NodeBundle\Event\AdaptFormEvent;
25
use Kunstmaan\NodeBundle\Event\CopyPageTranslationNodeEvent;
26
use Kunstmaan\NodeBundle\Event\Events;
27
use Kunstmaan\NodeBundle\Event\NodeEvent;
28
use Kunstmaan\NodeBundle\Event\RecopyPageTranslationNodeEvent;
29
use Kunstmaan\NodeBundle\Event\RevertNodeAction;
30
use Kunstmaan\NodeBundle\Form\NodeMenuTabAdminType;
31
use Kunstmaan\NodeBundle\Form\NodeMenuTabTranslationAdminType;
32
use Kunstmaan\NodeBundle\Helper\NodeAdmin\NodeAdminPublisher;
33
use Kunstmaan\NodeBundle\Helper\NodeAdmin\NodeVersionLockHelper;
34
use Kunstmaan\NodeBundle\Helper\PageCloningHelper;
35
use Kunstmaan\NodeBundle\Repository\NodeVersionRepository;
36
use Kunstmaan\UtilitiesBundle\Helper\ClassLookup;
37
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
38
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
39
use Symfony\Component\HttpFoundation\JsonResponse;
40
use Symfony\Component\HttpFoundation\RedirectResponse;
41
use Symfony\Component\HttpFoundation\Request;
42
use Symfony\Component\Routing\Annotation\Route;
43
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
44
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
45
use Symfony\Component\Translation\TranslatorInterface;
46
47
/**
48
 * NodeAdminController
49
 */
50
class NodeAdminController extends Controller
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Bundle\Framework...e\Controller\Controller has been deprecated with message: since Symfony 4.2, use "Symfony\Bundle\FrameworkBundle\Controller\AbstractController" instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
51
{
52
    /**
53
     * @var EntityManager
54
     */
55
    protected $em;
56
57
    /**
58
     * @var string
59
     */
60
    protected $locale;
61
62
    /**
63
     * @var AuthorizationCheckerInterface
64
     */
65
    protected $authorizationChecker;
66
67
    /**
68
     * @var BaseUser
69
     */
70
    protected $user;
71
72
    /**
73
     * @var AclHelper
74
     */
75
    protected $aclHelper;
76
77
    /**
78
     * @var AclManager
79
     */
80
    protected $aclManager;
81
82
    /**
83
     * @var NodeAdminPublisher
84
     */
85
    protected $nodePublisher;
86
87
    /**
88
     * @var TranslatorInterface
89
     */
90
    protected $translator;
91
92
    /** @var PageCloningHelper */
93
    private $pageCloningHelper;
94
95
    /**
96
     * init
97
     *
98
     * @param Request $request
99
     */
100
    protected function init(Request $request)
101
    {
102
        $this->em = $this->getDoctrine()->getManager();
103
        $this->locale = $request->getLocale();
104
        $this->authorizationChecker = $this->container->get('security.authorization_checker');
105
        $this->user = $this->getUser();
106
        $this->aclHelper = $this->container->get('kunstmaan_admin.acl.helper');
107
        $this->aclManager = $this->container->get('kunstmaan_admin.acl.manager');
108
        $this->nodePublisher = $this->container->get('kunstmaan_node.admin_node.publisher');
109
        $this->translator = $this->container->get('translator');
110
        $this->pageCloningHelper = $this->container->get(PageCloningHelper::class);
111
    }
112
113
    /**
114
     * @Route("/", name="KunstmaanNodeBundle_nodes")
115
     * @Template("@KunstmaanNode/Admin/list.html.twig")
116
     *
117
     * @param Request $request
118
     *
119
     * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,AdminList>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
120
     */
121
    public function indexAction(Request $request)
122
    {
123
        $this->init($request);
124
125
        $nodeAdminListConfigurator = new NodeAdminListConfigurator(
126
            $this->em,
127
            $this->aclHelper,
128
            $this->locale,
129
            PermissionMap::PERMISSION_VIEW,
130
            $this->authorizationChecker
131
        );
132
133
        $locale = $this->locale;
134
        $acl = $this->authorizationChecker;
135
        $itemRoute = function (EntityInterface $item) use ($locale, $acl) {
136
            if ($acl->isGranted(PermissionMap::PERMISSION_VIEW, $item->getNode())) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Kunstmaan\AdminBundle\Entity\EntityInterface as the method getNode() does only exist in the following implementations of said interface: Kunstmaan\FormBundle\Entity\FormSubmission, Kunstmaan\FormBundle\Tes...List\FakeFormSubmission, Kunstmaan\NodeBundle\Entity\NodeTranslation, Kunstmaan\NodeSearchBundle\Entity\NodeSearch.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
137
                return array(
138
                    'path' => '_slug_preview',
139
                    'params' => ['_locale' => $locale, 'url' => $item->getUrl()],
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Kunstmaan\AdminBundle\Entity\EntityInterface as the method getUrl() does only exist in the following implementations of said interface: Kunstmaan\AdminBundle\Entity\Exception, Kunstmaan\MediaBundle\Entity\Media, Kunstmaan\MenuBundle\Entity\BaseMenuItem, Kunstmaan\MenuBundle\Entity\MenuItem, Kunstmaan\NodeBundle\Entity\NodeTranslation, Kunstmaan\PagePartBundle\Entity\LinkPagePart.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
140
                );
141
            }
142
        };
143
        $nodeAdminListConfigurator->addSimpleItemAction('action.preview', $itemRoute, 'eye');
144
        $nodeAdminListConfigurator->setDomainConfiguration($this->get('kunstmaan_admin.domain_configuration'));
145
        $nodeAdminListConfigurator->setShowAddHomepage($this->getParameter('kunstmaan_node.show_add_homepage') && $this->isGranted('ROLE_SUPER_ADMIN'));
146
147
        /** @var AdminList $adminlist */
148
        $adminlist = $this->get('kunstmaan_adminlist.factory')->createList($nodeAdminListConfigurator);
149
        $adminlist->bindRequest($request);
150
151
        return array(
152
            'adminlist' => $adminlist,
153
        );
154
    }
155
156
    /**
157
     * @Route(
158
     *      "/{id}/copyfromotherlanguage",
159
     *      requirements={"id" = "\d+"},
160
     *      name="KunstmaanNodeBundle_nodes_copyfromotherlanguage",
161
     *      methods={"GET"}
162
     * )
163
     *
164
     * @param Request $request
165
     * @param int     $id      The node id
166
     *
167
     * @return RedirectResponse
168
     *
169
     * @throws AccessDeniedException
170
     */
171
    public function copyFromOtherLanguageAction(Request $request, $id)
172
    {
173
        $this->init($request);
174
        /* @var Node $node */
175
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
176
177
        $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_EDIT, $node);
178
179
        $originalLanguage = $request->get('originallanguage');
180
        $otherLanguageNodeTranslation = $node->getNodeTranslation($originalLanguage, true);
181
        $otherLanguageNodeNodeVersion = $otherLanguageNodeTranslation->getPublicNodeVersion();
182
        $otherLanguagePage = $otherLanguageNodeNodeVersion->getRef($this->em);
183
        $myLanguagePage = $this->get('kunstmaan_admin.clone.helper')
184
            ->deepCloneAndSave($otherLanguagePage);
185
186
        /* @var NodeTranslation $nodeTranslation */
187
        $nodeTranslation = $this->em->getRepository('KunstmaanNodeBundle:NodeTranslation')
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createNodeTranslationFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Rep...deTranslationRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
188
            ->createNodeTranslationFor($myLanguagePage, $this->locale, $node, $this->user);
189
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
190
191
        $this->get('event_dispatcher')->dispatch(
192
            Events::COPY_PAGE_TRANSLATION,
193
            new CopyPageTranslationNodeEvent(
194
                $node,
195
                $nodeTranslation,
196
                $nodeVersion,
197
                $myLanguagePage,
198
                $otherLanguageNodeTranslation,
0 ignored issues
show
Bug introduced by
It seems like $otherLanguageNodeTranslation defined by $node->getNodeTranslatio...originalLanguage, true) on line 180 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() 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...
199
                $otherLanguageNodeNodeVersion,
200
                $otherLanguagePage,
201
                $originalLanguage
202
            )
203
        );
204
205
        return $this->redirect($this->generateUrl('KunstmaanNodeBundle_nodes_edit', array('id' => $id)));
206
    }
207
208
    /**
209
     * @Route(
210
     *      "/{id}/recopyfromotherlanguage",
211
     *      requirements={"id" = "\d+"},
212
     *      name="KunstmaanNodeBundle_nodes_recopyfromotherlanguage",
213
     *      methods={"POST"}
214
     * )
215
     *
216
     * @param Request $request
217
     * @param int     $id      The node id
218
     *
219
     * @return RedirectResponse
220
     *
221
     * @throws AccessDeniedException
222
     */
223
    public function recopyFromOtherLanguageAction(Request $request, $id)
224
    {
225
        $this->init($request);
226
        /* @var Node $node */
227
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
228
229
        $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_EDIT, $node);
230
231
        $otherLanguageNodeTranslation = $this->em->getRepository('KunstmaanNodeBundle:NodeTranslation')->find($request->get('source'));
232
        $otherLanguageNodeNodeVersion = $otherLanguageNodeTranslation->getPublicNodeVersion();
233
        $otherLanguagePage = $otherLanguageNodeNodeVersion->getRef($this->em);
234
        $myLanguagePage = $this->get('kunstmaan_admin.clone.helper')
235
            ->deepCloneAndSave($otherLanguagePage);
236
237
        /* @var NodeTranslation $nodeTranslation */
238
        $nodeTranslation = $this->em->getRepository('KunstmaanNodeBundle:NodeTranslation')
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method addDraftNodeVersionFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Rep...deTranslationRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
239
            ->addDraftNodeVersionFor($myLanguagePage, $this->locale, $node, $this->user);
240
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
241
242
        $this->get('event_dispatcher')->dispatch(
243
            Events::RECOPY_PAGE_TRANSLATION,
244
            new RecopyPageTranslationNodeEvent(
245
                $node,
246
                $nodeTranslation,
247
                $nodeVersion,
248
                $myLanguagePage,
249
                $otherLanguageNodeTranslation,
0 ignored issues
show
Documentation introduced by
$otherLanguageNodeTranslation is of type object|null, but the function expects a object<Kunstmaan\NodeBun...Entity\NodeTranslation>.

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...
250
                $otherLanguageNodeNodeVersion,
251
                $otherLanguagePage,
252
                $otherLanguageNodeTranslation->getLang()
253
            )
254
        );
255
256
        return $this->redirect($this->generateUrl('KunstmaanNodeBundle_nodes_edit', array('id' => $id, 'subaction' => NodeVersion::DRAFT_VERSION)));
257
    }
258
259
    /**
260
     * @Route(
261
     *      "/{id}/createemptypage",
262
     *      requirements={"id" = "\d+"},
263
     *      name="KunstmaanNodeBundle_nodes_createemptypage",
264
     *      methods={"GET"}
265
     * )
266
     *
267
     * @param Request $request
268
     * @param int     $id
269
     *
270
     * @return RedirectResponse
271
     *
272
     * @throws AccessDeniedException
273
     */
274
    public function createEmptyPageAction(Request $request, $id)
275
    {
276
        $this->init($request);
277
        /* @var Node $node */
278
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
279
280
        $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_EDIT, $node);
281
282
        $entityName = $node->getRefEntityName();
283
        /* @var HasNodeInterface $myLanguagePage */
284
        $myLanguagePage = new $entityName();
285
        $myLanguagePage->setTitle('New page');
286
287
        $this->em->persist($myLanguagePage);
288
        $this->em->flush();
289
        /* @var NodeTranslation $nodeTranslation */
290
        $nodeTranslation = $this->em->getRepository('KunstmaanNodeBundle:NodeTranslation')
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createNodeTranslationFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Rep...deTranslationRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
291
            ->createNodeTranslationFor($myLanguagePage, $this->locale, $node, $this->user);
292
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
293
294
        $this->get('event_dispatcher')->dispatch(
295
            Events::ADD_EMPTY_PAGE_TRANSLATION,
296
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $myLanguagePage)
297
        );
298
299
        return $this->redirect($this->generateUrl('KunstmaanNodeBundle_nodes_edit', array('id' => $id)));
300
    }
301
302
    /**
303
     * @Route("/{id}/publish", requirements={"id" =
304
     *                         "\d+"},
305
     *                         name="KunstmaanNodeBundle_nodes_publish", methods={"GET", "POST"})
306
     *
307
     * @param Request $request
308
     * @param int     $id
309
     *
310
     * @return RedirectResponse
311
     *
312
     * @throws AccessDeniedException
313
     */
314 View Code Duplication
    public function publishAction(Request $request, $id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
315
    {
316
        $this->init($request);
317
        /* @var Node $node */
318
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
319
320
        $nodeTranslation = $node->getNodeTranslation($this->locale, true);
321
        $request = $this->get('request_stack')->getCurrentRequest();
322
        $this->nodePublisher->chooseHowToPublish($request, $nodeTranslation, $this->translator);
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $node->getNodeTranslation($this->locale, true) on line 320 can be null; however, Kunstmaan\NodeBundle\Hel...r::chooseHowToPublish() 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...
323
324
        return $this->redirect($this->generateUrl('KunstmaanNodeBundle_nodes_edit', array('id' => $node->getId())));
325
    }
326
327
    /**
328
     * @Route(
329
     *      "/{id}/unpublish",
330
     *      requirements={"id" = "\d+"},
331
     *      name="KunstmaanNodeBundle_nodes_unpublish",
332
     *      methods={"GET", "POST"}
333
     * )
334
     *
335
     * @param Request $request
336
     * @param int     $id
337
     *
338
     * @return RedirectResponse
339
     *
340
     * @throws AccessDeniedException
341
     */
342 View Code Duplication
    public function unPublishAction(Request $request, $id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
343
    {
344
        $this->init($request);
345
        /* @var Node $node */
346
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
347
348
        $nodeTranslation = $node->getNodeTranslation($this->locale, true);
349
        $request = $this->get('request_stack')->getCurrentRequest();
350
        $this->nodePublisher->chooseHowToUnpublish($request, $nodeTranslation, $this->translator);
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $node->getNodeTranslation($this->locale, true) on line 348 can be null; however, Kunstmaan\NodeBundle\Hel...:chooseHowToUnpublish() 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...
351
352
        return $this->redirect($this->generateUrl('KunstmaanNodeBundle_nodes_edit', array('id' => $node->getId())));
353
    }
354
355
    /**
356
     * @Route(
357
     *      "/{id}/unschedulepublish",
358
     *      requirements={"id" = "\d+"},
359
     *      name="KunstmaanNodeBundle_nodes_unschedule_publish",
360
     *      methods={"GET", "POST"}
361
     * )
362
     *
363
     * @param Request $request
364
     * @param int     $id
365
     *
366
     * @return RedirectResponse
367
     *
368
     * @throws AccessDeniedException
369
     */
370 View Code Duplication
    public function unSchedulePublishAction(Request $request, $id)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
    {
372
        $this->init($request);
373
374
        /* @var Node $node */
375
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
376
377
        $nodeTranslation = $node->getNodeTranslation($this->locale, true);
378
        $this->nodePublisher->unSchedulePublish($nodeTranslation);
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $node->getNodeTranslation($this->locale, true) on line 377 can be null; however, Kunstmaan\NodeBundle\Hel...er::unSchedulePublish() 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...
379
380
        $this->addFlash(
381
            FlashTypes::SUCCESS,
382
            $this->get('translator')->trans('kuma_node.admin.unschedule.flash.success')
383
        );
384
385
        return $this->redirect($this->generateUrl('KunstmaanNodeBundle_nodes_edit', array('id' => $id)));
386
    }
387
388
    /**
389
     * @Route(
390
     *      "/{id}/delete",
391
     *      requirements={"id" = "\d+"},
392
     *      name="KunstmaanNodeBundle_nodes_delete",
393
     *      methods={"POST"}
394
     * )
395
     *
396
     * @param Request $request
397
     * @param int     $id
398
     *
399
     * @return RedirectResponse
400
     *
401
     * @throws AccessDeniedException
402
     */
403
    public function deleteAction(Request $request, $id)
404
    {
405
        $this->init($request);
406
        /* @var Node $node */
407
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
408
409
        $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_DELETE, $node);
410
411
        $nodeTranslation = $node->getNodeTranslation($this->locale, true);
412
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
413
        $page = $nodeVersion->getRef($this->em);
414
415
        $this->get('event_dispatcher')->dispatch(
416
            Events::PRE_DELETE,
417
            new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $node->getNodeTranslation($this->locale, true) on line 411 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() 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...
418
        );
419
420
        $node->setDeleted(true);
421
        $this->em->persist($node);
422
423
        $children = $node->getChildren();
424
        $this->deleteNodeChildren($this->em, $this->user, $this->locale, $children);
0 ignored issues
show
Bug introduced by
It seems like $children defined by $node->getChildren() on line 423 can also be of type array<integer,object<Kun...odeBundle\Entity\Node>>; however, Kunstmaan\NodeBundle\Con...r::deleteNodeChildren() does only seem to accept object<Doctrine\Common\C...ctions\ArrayCollection>, 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...
425
        $this->em->flush();
426
427
        $event = new NodeEvent($node, $nodeTranslation, $nodeVersion, $page);
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $node->getNodeTranslation($this->locale, true) on line 411 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() 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...
428
        $this->get('event_dispatcher')->dispatch(Events::POST_DELETE, $event);
429
        if (null === $response = $event->getResponse()) {
430
            $nodeParent = $node->getParent();
431
            // Check if we have a parent. Otherwise redirect to pages overview.
432
            if ($nodeParent) {
433
                $url = $this->get('router')->generate(
434
                    'KunstmaanNodeBundle_nodes_edit',
435
                    array('id' => $nodeParent->getId())
436
                );
437
            } else {
438
                $url = $this->get('router')->generate(
439
                    'KunstmaanNodeBundle_nodes'
440
                );
441
            }
442
            $response = new RedirectResponse($url);
443
        }
444
445
        $this->addFlash(
446
            FlashTypes::SUCCESS,
447
            $this->get('translator')->trans('kuma_node.admin.delete.flash.success')
448
        );
449
450
        return $response;
451
    }
452
453
    /**
454
     * @Route(
455
     *      "/{id}/duplicate",
456
     *      requirements={"id" = "\d+"},
457
     *      name="KunstmaanNodeBundle_nodes_duplicate",
458
     *      methods={"POST"}
459
     * )
460
     *
461
     * @param Request $request
462
     * @param int     $id
463
     *
464
     * @return RedirectResponse
465
     *
466
     * @throws AccessDeniedException
467
     */
468
    public function duplicateAction(Request $request, $id)
469
    {
470
        $this->init($request);
471
        /* @var Node $parentNode */
472
        $originalNode = $this->em->getRepository('KunstmaanNodeBundle:Node')
473
            ->find($id);
474
475
        // Check with Acl
476
        $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_EDIT, $originalNode);
477
478
        $request = $this->get('request_stack')->getCurrentRequest();
479
480
        $originalNodeTranslations = $originalNode->getNodeTranslation($this->locale, true);
481
        $originalRef = $originalNodeTranslations->getPublicNodeVersion()->getRef($this->em);
482
        $newPage = $this->get('kunstmaan_admin.clone.helper')
483
            ->deepCloneAndSave($originalRef);
484
485
        //set the title
486
        $title = $request->get('title');
487
        if (\is_string($title) && !empty($title)) {
488
            $newPage->setTitle($title);
489
        } else {
490
            $newPage->setTitle('New page');
491
        }
492
493
        //set the parent
494
        $parentNodeTranslation = $originalNode->getParent()->getNodeTranslation($this->locale, true);
495
        $parent = $parentNodeTranslation->getPublicNodeVersion()->getRef($this->em);
496
        $newPage->setParent($parent);
497
        $this->em->persist($newPage);
498
        $this->em->flush();
499
500
        /* @var Node $nodeNewPage */
501
        $nodeNewPage = $this->em->getRepository('KunstmaanNodeBundle:Node')->createNodeFor(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createNodeFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Repository\NodeRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
502
            $newPage,
503
            $this->locale,
504
            $this->user
505
        );
506
507
        $nodeTranslation = $nodeNewPage->getNodeTranslation($this->locale, true);
508
        if ($newPage->isStructureNode()) {
509
            $nodeTranslation->setSlug('');
510
            $this->em->persist($nodeTranslation);
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $nodeNewPage->getNodeTra...on($this->locale, true) on line 507 can be null; however, Doctrine\ORM\EntityManager::persist() 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...
511
        }
512
        $this->em->flush();
513
514
        $this->aclManager->updateNodeAcl($originalNode, $nodeNewPage);
0 ignored issues
show
Documentation introduced by
$originalNode is of type object|null, but the function expects a object<Kunstmaan\NodeBundle\Entity\Node>.

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...
515
516
        $this->addFlash(
517
            FlashTypes::SUCCESS,
518
            $this->get('translator')->trans('kuma_node.admin.duplicate.flash.success')
519
        );
520
521
        return $this->redirect(
522
            $this->generateUrl('KunstmaanNodeBundle_nodes_edit', array('id' => $nodeNewPage->getId()))
523
        );
524
    }
525
526
    /**
527
     * @Route(
528
     *      "/{id}/duplicate-with-children",
529
     *      requirements={"id" = "\d+"},
530
     *      name="KunstmaanNodeBundle_nodes_duplicate_with_children",
531
     *      methods={"POST"}
532
     * )
533
     *
534
     * @throws AccessDeniedException
535
     */
536
    public function duplicateWithChildrenAction(Request $request, $id)
537
    {
538
        if (!$this->getParameter('kunstmaan_node.show_duplicate_with_children')) {
539
            return $this->redirectToRoute('KunstmaanNodeBundle_nodes_edit', ['id' => $id]);
540
        }
541
542
        $this->init($request);
543
        $title = $request->get('title', null);
544
545
        $nodeNewPage = $this->pageCloningHelper->duplicateWithChildren($id, $this->locale, $this->user, $title);
546
547
        $this->addFlash(FlashTypes::SUCCESS, $this->get('translator')->trans('kuma_node.admin.duplicate.flash.success'));
548
549
        return $this->redirectToRoute('KunstmaanNodeBundle_nodes_edit', ['id' => $nodeNewPage->getId()]);
550
    }
551
552
    /**
553
     * @Route(
554
     *      "/{id}/revert",
555
     *      requirements={"id" = "\d+"},
556
     *      defaults={"subaction" = "public"},
557
     *      name="KunstmaanNodeBundle_nodes_revert",
558
     *      methods={"GET"}
559
     * )
560
     *
561
     * @param Request $request
562
     * @param int     $id      The node id
563
     *
564
     * @return RedirectResponse
565
     *
566
     * @throws AccessDeniedException
567
     * @throws InvalidArgumentException
568
     */
569
    public function revertAction(Request $request, $id)
570
    {
571
        $this->init($request);
572
        /* @var Node $node */
573
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
574
575
        $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_EDIT, $node);
576
577
        $version = $request->get('version');
578
579
        if (empty($version) || !is_numeric($version)) {
580
            throw new InvalidArgumentException('No version was specified');
581
        }
582
583
        /* @var NodeVersionRepository $nodeVersionRepo */
584
        $nodeVersionRepo = $this->em->getRepository('KunstmaanNodeBundle:NodeVersion');
585
        /* @var NodeVersion $nodeVersion */
586
        $nodeVersion = $nodeVersionRepo->find($version);
587
588
        if (\is_null($nodeVersion)) {
589
            throw new InvalidArgumentException('Version does not exist');
590
        }
591
592
        /* @var NodeTranslation $nodeTranslation */
593
        $nodeTranslation = $node->getNodeTranslation($this->locale, true);
594
        $page = $nodeVersion->getRef($this->em);
595
        /* @var HasNodeInterface $clonedPage */
596
        $clonedPage = $this->get('kunstmaan_admin.clone.helper')
597
            ->deepCloneAndSave($page);
598
        $newNodeVersion = $nodeVersionRepo->createNodeVersionFor(
599
            $clonedPage,
600
            $nodeTranslation,
601
            $this->user,
602
            $nodeVersion,
603
            'draft'
604
        );
605
606
        $nodeTranslation->setTitle($clonedPage->getTitle());
607
        $this->em->persist($nodeTranslation);
608
        $this->em->flush();
609
610
        $this->get('event_dispatcher')->dispatch(
611
            Events::REVERT,
612
            new RevertNodeAction(
613
                $node,
614
                $nodeTranslation,
615
                $newNodeVersion,
616
                $clonedPage,
617
                $nodeVersion,
618
                $page
0 ignored issues
show
Bug introduced by
It seems like $page defined by $nodeVersion->getRef($this->em) on line 594 can be null; however, Kunstmaan\NodeBundle\Eve...deAction::__construct() 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...
619
            )
620
        );
621
622
        $this->addFlash(
623
            FlashTypes::SUCCESS,
624
            $this->get('translator')->trans('kuma_node.admin.revert.flash.success')
625
        );
626
627
        return $this->redirect(
628
            $this->generateUrl(
629
                'KunstmaanNodeBundle_nodes_edit',
630
                array(
631
                    'id' => $id,
632
                    'subaction' => 'draft',
633
                )
634
            )
635
        );
636
    }
637
638
    /**
639
     * @Route(
640
     *      "/{id}/add",
641
     *      requirements={"id" = "\d+"},
642
     *      name="KunstmaanNodeBundle_nodes_add",
643
     *      methods={"POST"}
644
     * )
645
     *
646
     * @param Request $request
647
     * @param int     $id
648
     *
649
     * @return RedirectResponse
650
     *
651
     * @throws AccessDeniedException
652
     * @throws InvalidArgumentException
653
     */
654
    public function addAction(Request $request, $id)
655
    {
656
        $this->init($request);
657
        /* @var Node $parentNode */
658
        $parentNode = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
659
660
        // Check with Acl
661
        $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_EDIT, $parentNode);
662
663
        $parentNodeTranslation = $parentNode->getNodeTranslation($this->locale, true);
664
        $parentNodeVersion = $parentNodeTranslation->getPublicNodeVersion();
665
        $parentPage = $parentNodeVersion->getRef($this->em);
666
667
        $type = $this->validatePageType($request);
668
        $newPage = $this->createNewPage($request, $type);
669
        $newPage->setParent($parentPage);
670
671
        /* @var Node $nodeNewPage */
672
        $nodeNewPage = $this->em->getRepository('KunstmaanNodeBundle:Node')
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createNodeFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Repository\NodeRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
673
            ->createNodeFor($newPage, $this->locale, $this->user);
674
        $nodeTranslation = $nodeNewPage->getNodeTranslation(
675
            $this->locale,
676
            true
677
        );
678
        $weight = $this->em->getRepository('KunstmaanNodeBundle:NodeTranslation')
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method getMaxChildrenWeight() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Rep...deTranslationRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
679
                ->getMaxChildrenWeight($parentNode, $this->locale) + 1;
680
        $nodeTranslation->setWeight($weight);
681
682
        if ($newPage->isStructureNode()) {
683
            $nodeTranslation->setSlug('');
684
        }
685
686
        $this->em->persist($nodeTranslation);
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $nodeNewPage->getNodeTra...on($this->locale, true) on line 674 can be null; however, Doctrine\ORM\EntityManager::persist() 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...
687
        $this->em->flush();
688
689
        $this->aclManager->updateNodeAcl($parentNode, $nodeNewPage);
690
691
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
692
693
        $this->get('event_dispatcher')->dispatch(
694
            Events::ADD_NODE,
695
            new NodeEvent(
696
                $nodeNewPage, $nodeTranslation, $nodeVersion, $newPage
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $nodeNewPage->getNodeTra...on($this->locale, true) on line 674 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() 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...
697
            )
698
        );
699
700
        return $this->redirect(
701
            $this->generateUrl(
702
                'KunstmaanNodeBundle_nodes_edit',
703
                array('id' => $nodeNewPage->getId())
704
            )
705
        );
706
    }
707
708
    /**
709
     * @Route("/add-homepage", name="KunstmaanNodeBundle_nodes_add_homepage", methods={"POST"})
710
     *
711
     * @return RedirectResponse
712
     *
713
     * @throws AccessDeniedException
714
     * @throws InvalidArgumentException
715
     */
716
    public function addHomepageAction(Request $request)
717
    {
718
        $this->init($request);
719
720
        // Check with Acl
721
        $this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
722
723
        $type = $this->validatePageType($request);
724
725
        $newPage = $this->createNewPage($request, $type);
726
727
        /* @var Node $nodeNewPage */
728
        $nodeNewPage = $this->em->getRepository('KunstmaanNodeBundle:Node')
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createNodeFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Repository\NodeRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
729
            ->createNodeFor($newPage, $this->locale, $this->user);
730
        $nodeTranslation = $nodeNewPage->getNodeTranslation(
731
            $this->locale,
732
            true
733
        );
734
        $this->em->flush();
735
736
        // Set default permissions
737
        $this->container->get('kunstmaan_node.acl_permission_creator_service')
738
            ->createPermission($nodeNewPage);
739
740
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
741
742
        $this->get('event_dispatcher')->dispatch(
743
            Events::ADD_NODE,
744
            new NodeEvent(
745
                $nodeNewPage, $nodeTranslation, $nodeVersion, $newPage
0 ignored issues
show
Bug introduced by
It seems like $nodeTranslation defined by $nodeNewPage->getNodeTra...on($this->locale, true) on line 730 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() 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...
746
            )
747
        );
748
749
        return $this->redirect(
750
            $this->generateUrl(
751
                'KunstmaanNodeBundle_nodes_edit',
752
                array('id' => $nodeNewPage->getId())
753
            )
754
        );
755
    }
756
757
    /**
758
     * @Route("/reorder", name="KunstmaanNodeBundle_nodes_reorder", methods={"POST"})
759
     *
760
     * @param Request $request
761
     *
762
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be JsonResponse?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
763
     *
764
     * @throws AccessDeniedException
765
     */
766
    public function reorderAction(Request $request)
767
    {
768
        $this->init($request);
769
        $nodes = array();
770
        $nodeIds = $request->get('nodes');
771
        $changeParents = $request->get('parent');
772
773
        foreach ($nodeIds as $id) {
774
            /* @var Node $node */
775
            $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
776
            $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_EDIT, $node);
777
            $nodes[] = $node;
778
        }
779
780
        $weight = 1;
781
        foreach ($nodes as $node) {
782
            $newParentId = isset($changeParents[$node->getId()]) ? $changeParents[$node->getId()] : null;
783
            if ($newParentId) {
784
                $parent = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($newParentId);
785
                $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_EDIT, $parent);
786
                $node->setParent($parent);
787
                $this->em->persist($node);
788
                $this->em->flush($node);
789
            }
790
791
            /* @var NodeTranslation $nodeTranslation */
792
            $nodeTranslation = $node->getNodeTranslation($this->locale, true);
793
794
            if ($nodeTranslation) {
795
                $nodeVersion = $nodeTranslation->getPublicNodeVersion();
796
                $page = $nodeVersion->getRef($this->em);
797
798
                $this->get('event_dispatcher')->dispatch(
799
                    Events::PRE_PERSIST,
800
                    new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
0 ignored issues
show
Bug introduced by
It seems like $page defined by $nodeVersion->getRef($this->em) on line 796 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() 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...
801
                );
802
803
                $nodeTranslation->setWeight($weight);
804
                $this->em->persist($nodeTranslation);
805
                $this->em->flush($nodeTranslation);
806
807
                $this->get('event_dispatcher')->dispatch(
808
                    Events::POST_PERSIST,
809
                    new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
0 ignored issues
show
Bug introduced by
It seems like $page defined by $nodeVersion->getRef($this->em) on line 796 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() 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...
810
                );
811
812
                ++$weight;
813
            }
814
        }
815
816
        return new JsonResponse(
817
            array(
818
                'Success' => 'The node-translations for [' . $this->locale . '] have got new weight values',
819
            )
820
        );
821
    }
822
823
    /**
824
     * @Route(
825
     *      "/{id}/{subaction}",
826
     *      requirements={"id" = "\d+"},
827
     *      defaults={"subaction" = "public"},
828
     *      name="KunstmaanNodeBundle_nodes_edit",
829
     *      methods={"GET", "POST"}
830
     * )
831
     * @Template("@KunstmaanNode/NodeAdmin/edit.html.twig")
832
     *
833
     * @param Request $request
834
     * @param int     $id        The node id
835
     * @param string  $subaction The subaction (draft|public)
836
     *
837
     * @return RedirectResponse|array
0 ignored issues
show
Documentation introduced by
Should the return type not be \Symfony\Component\HttpFoundation\Response|array?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
838
     *
839
     * @throws AccessDeniedException
840
     */
841
    public function editAction(Request $request, $id, $subaction)
842
    {
843
        $this->init($request);
844
        /* @var Node $node */
845
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
846
847
        $this->denyAccessUnlessGranted(PermissionMap::PERMISSION_EDIT, $node);
848
849
        $tabPane = new TabPane(
850
            'todo',
851
            $request,
852
            $this->container->get('form.factory')
0 ignored issues
show
Documentation introduced by
$this->container->get('form.factory') is of type object|null, but the function expects a object<Symfony\Component...m\FormFactoryInterface>.

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...
853
        );
854
855
        $nodeTranslation = $node->getNodeTranslation($this->locale, true);
856
        if (!$nodeTranslation) {
857
            return $this->renderNodeNotTranslatedPage($node);
858
        }
859
860
        $nodeVersion = $nodeTranslation->getPublicNodeVersion();
861
        $draftNodeVersion = $nodeTranslation->getDraftNodeVersion();
862
        $nodeVersionIsLocked = false;
863
864
        /* @var HasNodeInterface $page */
865
        $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...
866
        $draft = ($subaction == 'draft');
867
        $saveAsDraft = $request->get('saveasdraft');
868
        if ((!$draft && !empty($saveAsDraft)) || ($draft && \is_null($draftNodeVersion))) {
869
            // Create a new draft version
870
            $draft = true;
871
            $subaction = 'draft';
872
            $page = $nodeVersion->getRef($this->em);
873
            $nodeVersion = $this->createDraftVersion(
874
                $page,
0 ignored issues
show
Bug introduced by
It seems like $page defined by $nodeVersion->getRef($this->em) on line 872 can be null; however, Kunstmaan\NodeBundle\Con...r::createDraftVersion() 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...
875
                $nodeTranslation,
876
                $nodeVersion
877
            );
878
            $draftNodeVersion = $nodeVersion;
879
        } elseif ($draft) {
880
            $nodeVersion = $draftNodeVersion;
881
            $page = $nodeVersion->getRef($this->em);
882
        } else {
883
            if ($request->getMethod() == 'POST') {
884
                $nodeVersionIsLocked = $this->isNodeVersionLocked($nodeTranslation, true);
885
886
                //Check the version timeout and make a new nodeversion if the timeout is passed
887
                $thresholdDate = date(
888
                    'Y-m-d H:i:s',
889
                    time() - $this->getParameter(
890
                        'kunstmaan_node.version_timeout'
891
                    )
892
                );
893
                $updatedDate = date(
894
                    'Y-m-d H:i:s',
895
                    strtotime($nodeVersion->getUpdated()->format('Y-m-d H:i:s'))
896
                );
897 View Code Duplication
                if ($thresholdDate >= $updatedDate || $nodeVersionIsLocked) {
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...
898
                    $page = $nodeVersion->getRef($this->em);
899
                    if ($nodeVersion === $nodeTranslation->getPublicNodeVersion()) {
900
                        $this->nodePublisher
901
                            ->createPublicVersion(
902
                                $page,
0 ignored issues
show
Bug introduced by
It seems like $page defined by $nodeVersion->getRef($this->em) on line 898 can be null; however, Kunstmaan\NodeBundle\Hel...::createPublicVersion() 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...
903
                                $nodeTranslation,
904
                                $nodeVersion,
905
                                $this->user
906
                            );
907
                    } else {
908
                        $this->createDraftVersion(
909
                            $page,
0 ignored issues
show
Bug introduced by
It seems like $page defined by $nodeVersion->getRef($this->em) on line 898 can be null; however, Kunstmaan\NodeBundle\Con...r::createDraftVersion() 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...
910
                            $nodeTranslation,
911
                            $nodeVersion
912
                        );
913
                    }
914
                }
915
            }
916
            $page = $nodeVersion->getRef($this->em);
917
        }
918
        $isStructureNode = $page->isStructureNode();
919
920
        $menubuilder = $this->get('kunstmaan_node.actions_menu_builder');
921
        $menubuilder->setActiveNodeVersion($nodeVersion);
922
        $menubuilder->setEditableNode(!$isStructureNode);
923
924
        // Building the form
925
        $propertiesWidget = new FormWidget();
926
        $propertiesWidget->addType('main', $page->getDefaultAdminType(), $page);
927
        $propertiesWidget->addType('node', $node->getDefaultAdminType(), $node);
928
        $tabPane->addTab(new Tab('kuma_node.tab.properties.title', $propertiesWidget));
929
930
        // Menu tab
931
        $menuWidget = new FormWidget();
932
        $menuWidget->addType(
933
            'menunodetranslation',
934
            NodeMenuTabTranslationAdminType::class,
935
            $nodeTranslation,
936
            ['slugable' => !$isStructureNode]
937
        );
938
        $menuWidget->addType('menunode', NodeMenuTabAdminType::class, $node, ['available_in_nav' => !$isStructureNode]);
939
        $tabPane->addTab(new Tab('kuma_node.tab.menu.title', $menuWidget));
940
941
        $this->get('event_dispatcher')->dispatch(
942
            Events::ADAPT_FORM,
943
            new AdaptFormEvent(
944
                $request,
945
                $tabPane,
946
                $page,
947
                $node,
948
                $nodeTranslation,
949
                $nodeVersion
950
            )
951
        );
952
953
        $tabPane->buildForm();
954
955
        if ($request->getMethod() == 'POST') {
956
            $tabPane->bindRequest($request);
957
958
            // Don't redirect to listing when coming from ajax request, needed for url chooser.
959
            if ($tabPane->isValid() && !$request->isXmlHttpRequest()) {
960
                $this->get('event_dispatcher')->dispatch(
961
                    Events::PRE_PERSIST,
962
                    new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
0 ignored issues
show
Bug introduced by
It seems like $nodeVersion defined by $draftNodeVersion on line 880 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() 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...
963
                );
964
965
                $nodeTranslation->setTitle($page->getTitle());
966
                if ($isStructureNode) {
967
                    $nodeTranslation->setSlug('');
968
                }
969
                $nodeVersion->setUpdated(new DateTime());
970
                if ($nodeVersion->getType() == 'public') {
971
                    $nodeTranslation->setUpdated($nodeVersion->getUpdated());
972
                }
973
                $this->em->persist($nodeTranslation);
974
                $this->em->persist($nodeVersion);
0 ignored issues
show
Bug introduced by
It seems like $nodeVersion defined by $draftNodeVersion on line 880 can be null; however, Doctrine\ORM\EntityManager::persist() 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...
975
                $tabPane->persist($this->em);
976
                $this->em->flush();
977
978
                $this->get('event_dispatcher')->dispatch(
979
                    Events::POST_PERSIST,
980
                    new NodeEvent($node, $nodeTranslation, $nodeVersion, $page)
0 ignored issues
show
Bug introduced by
It seems like $nodeVersion defined by $draftNodeVersion on line 880 can be null; however, Kunstmaan\NodeBundle\Eve...odeEvent::__construct() 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...
981
                );
982
983
                if ($nodeVersionIsLocked) {
984
                    $this->addFlash(
985
                        FlashTypes::SUCCESS,
986
                        $this->get('translator')->trans('kuma_node.admin.edit.flash.locked_success')
987
                    );
988
                } elseif ($request->request->has('publishing') || $request->request->has('publish_later')) {
989
                    $this->nodePublisher->chooseHowToPublish($request, $nodeTranslation, $this->translator);
990
                } elseif ($request->request->has('unpublishing') || $request->request->has('unpublish_later')) {
991
                    $this->nodePublisher->chooseHowToUnpublish($request, $nodeTranslation, $this->translator);
992
                } else {
993
                    $this->addFlash(
994
                        FlashTypes::SUCCESS,
995
                        $this->get('translator')->trans('kuma_node.admin.edit.flash.success')
996
                    );
997
                }
998
999
                $params = [
1000
                    'id' => $node->getId(),
1001
                    'subaction' => $subaction,
1002
                    'currenttab' => $tabPane->getActiveTab(),
1003
                ];
1004
                $params = array_merge(
1005
                    $params,
1006
                    $tabPane->getExtraParams($request)
1007
                );
1008
1009
                if ($subaction === 'draft' && ($request->request->has('publishing') || $request->request->has('publish_later'))) {
1010
                    unset($params['subaction']);
1011
                }
1012
1013
                return $this->redirect(
1014
                    $this->generateUrl(
1015
                        'KunstmaanNodeBundle_nodes_edit',
1016
                        $params
1017
                    )
1018
                );
1019
            }
1020
        }
1021
1022
        $nodeVersions = $this->em->getRepository(
1023
            'KunstmaanNodeBundle:NodeVersion'
1024
        )->findBy(
1025
            ['nodeTranslation' => $nodeTranslation],
1026
            ['updated' => 'ASC']
1027
        );
1028
        $queuedNodeTranslationAction = $this->em->getRepository(
1029
            'KunstmaanNodeBundle:QueuedNodeTranslationAction'
1030
        )->findOneBy(['nodeTranslation' => $nodeTranslation]);
1031
1032
        return [
1033
            'page' => $page,
1034
            'entityname' => ClassLookup::getClass($page),
1035
            'nodeVersions' => $nodeVersions,
1036
            'node' => $node,
1037
            'nodeTranslation' => $nodeTranslation,
1038
            'draft' => $draft,
1039
            'draftNodeVersion' => $draftNodeVersion,
1040
            'showDuplicateWithChildren' => $this->getParameter('kunstmaan_node.show_duplicate_with_children'),
1041
            'nodeVersion' => $nodeVersion,
1042
            'subaction' => $subaction,
1043
            'tabPane' => $tabPane,
1044
            'editmode' => true,
1045
            'childCount' => $this->em->getRepository('KunstmaanNodeBundle:Node')->getChildCount($node),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method getChildCount() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Repository\NodeRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1046
            'queuedNodeTranslationAction' => $queuedNodeTranslationAction,
1047
            'nodeVersionLockCheck' => $this->container->getParameter('kunstmaan_node.lock_enabled'),
1048
            'nodeVersionLockInterval' => $this->container->getParameter('kunstmaan_node.lock_check_interval'),
1049
        ];
1050
    }
1051
1052
    /**
1053
     * @Route(
1054
     *      "checkNodeVersionLock/{id}/{public}",
1055
     *      requirements={"id" = "\d+", "public" = "(0|1)"},
1056
     *      name="KunstmaanNodeBundle_nodes_versionlock_check"
1057
     * )
1058
     *
1059
     * @param Request $request
1060
     * @param $id
1061
     *
1062
     * @return JsonResponse
1063
     */
1064
    public function checkNodeVersionLockAction(Request $request, $id, $public)
1065
    {
1066
        $nodeVersionIsLocked = false;
1067
        $message = '';
1068
        $this->init($request);
1069
1070
        /* @var Node $node */
1071
        $node = $this->em->getRepository('KunstmaanNodeBundle:Node')->find($id);
1072
1073
        try {
1074
            $this->checkPermission($node, PermissionMap::PERMISSION_EDIT);
1075
1076
            /** @var NodeVersionLockHelper $nodeVersionLockHelper */
1077
            $nodeVersionLockHelper = $this->get('kunstmaan_node.admin_node.node_version_lock_helper');
1078
            $nodeTranslation = $node->getNodeTranslation($this->locale, true);
1079
1080
            if ($nodeTranslation) {
1081
                $nodeVersionIsLocked = $nodeVersionLockHelper->isNodeVersionLocked($this->getUser(), $nodeTranslation, $public);
0 ignored issues
show
Documentation introduced by
$this->getUser() is of type null|object, but the function expects a object<Kunstmaan\AdminBundle\Entity\BaseUser>.

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...
1082
1083
                if ($nodeVersionIsLocked) {
1084
                    $users = $nodeVersionLockHelper->getUsersWithNodeVersionLock($nodeTranslation, $public, $this->getUser());
1085
                    $message = $this->get('translator')->trans('kuma_node.admin.edit.flash.locked', array('%users%' => implode(', ', $users)));
1086
                }
1087
            }
1088
        } catch (AccessDeniedException $ade) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1089
        }
1090
1091
        return new JsonResponse(['lock' => $nodeVersionIsLocked, 'message' => $message]);
1092
    }
1093
1094
    /**
1095
     * @param NodeTranslation $nodeTranslation
1096
     * @param bool            $isPublic
1097
     *
1098
     * @return bool
1099
     */
1100
    private function isNodeVersionLocked(NodeTranslation $nodeTranslation, $isPublic)
1101
    {
1102
        if ($this->container->getParameter('kunstmaan_node.lock_enabled')) {
1103
            /** @var NodeVersionLockHelper $nodeVersionLockHelper */
1104
            $nodeVersionLockHelper = $this->get('kunstmaan_node.admin_node.node_version_lock_helper');
1105
            $nodeVersionIsLocked = $nodeVersionLockHelper->isNodeVersionLocked($this->getUser(), $nodeTranslation, $isPublic);
0 ignored issues
show
Documentation introduced by
$this->getUser() is of type null|object, but the function expects a object<Kunstmaan\AdminBundle\Entity\BaseUser>.

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...
1106
1107
            return $nodeVersionIsLocked;
1108
        }
1109
1110
        return false;
1111
    }
1112
1113
    /**
1114
     * @param HasNodeInterface $page            The page
1115
     * @param NodeTranslation  $nodeTranslation The node translation
1116
     * @param NodeVersion      $nodeVersion     The node version
1117
     *
1118
     * @return NodeVersion
1119
     */
1120
    private function createDraftVersion(
1121
        HasNodeInterface $page,
1122
        NodeTranslation $nodeTranslation,
1123
        NodeVersion $nodeVersion
1124
    ) {
1125
        $publicPage = $this->get('kunstmaan_admin.clone.helper')
1126
            ->deepCloneAndSave($page);
1127
        /* @var NodeVersion $publicNodeVersion */
1128
1129
        $publicNodeVersion = $this->em->getRepository(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method createNodeVersionFor() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Rep...y\NodeVersionRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1130
            'KunstmaanNodeBundle:NodeVersion'
1131
        )->createNodeVersionFor(
1132
            $publicPage,
1133
            $nodeTranslation,
1134
            $this->user,
1135
            $nodeVersion->getOrigin(),
1136
            'public',
1137
            $nodeVersion->getCreated()
1138
        );
1139
1140
        $nodeTranslation->setPublicNodeVersion($publicNodeVersion);
1141
        $nodeVersion->setType('draft');
1142
        $nodeVersion->setOrigin($publicNodeVersion);
1143
        $nodeVersion->setCreated(new DateTime());
1144
1145
        $this->em->persist($nodeTranslation);
1146
        $this->em->persist($nodeVersion);
1147
        $this->em->flush();
1148
1149
        $this->get('event_dispatcher')->dispatch(
1150
            Events::CREATE_DRAFT_VERSION,
1151
            new NodeEvent(
1152
                $nodeTranslation->getNode(),
1153
                $nodeTranslation,
1154
                $nodeVersion,
1155
                $page
1156
            )
1157
        );
1158
1159
        return $nodeVersion;
1160
    }
1161
1162
    /**
1163
     * @param Node   $node       The node
1164
     * @param string $permission The permission to check for
1165
     *
1166
     * @throws AccessDeniedException
1167
     */
1168
    private function checkPermission(Node $node, $permission)
1169
    {
1170
        if (false === $this->authorizationChecker->isGranted($permission, $node)) {
1171
            throw new AccessDeniedException();
1172
        }
1173
    }
1174
1175
    /**
1176
     * @param EntityManager   $em       The Entity Manager
1177
     * @param BaseUser        $user     The user who deletes the children
1178
     * @param string          $locale   The locale that was used
1179
     * @param ArrayCollection $children The children array
1180
     */
1181
    private function deleteNodeChildren(
1182
        EntityManager $em,
1183
        BaseUser $user,
1184
        $locale,
1185
        ArrayCollection $children
1186
    ) {
1187
        /* @var Node $childNode */
1188
        foreach ($children as $childNode) {
1189
            $childNodeTranslation = $childNode->getNodeTranslation(
1190
                $this->locale,
1191
                true
1192
            );
1193
1194
            $childNodeVersion = $childNodeTranslation->getPublicNodeVersion();
1195
            $childNodePage = $childNodeVersion->getRef($this->em);
1196
1197
            $this->get('event_dispatcher')->dispatch(
1198
                Events::PRE_DELETE,
1199
                new NodeEvent(
1200
                    $childNode,
1201
                    $childNodeTranslation,
1202
                    $childNodeVersion,
1203
                    $childNodePage
1204
                )
1205
            );
1206
1207
            $childNode->setDeleted(true);
1208
            $this->em->persist($childNode);
1209
1210
            $children2 = $childNode->getChildren();
1211
            $this->deleteNodeChildren($em, $user, $locale, $children2);
1212
1213
            $this->get('event_dispatcher')->dispatch(
1214
                Events::POST_DELETE,
1215
                new NodeEvent(
1216
                    $childNode,
1217
                    $childNodeTranslation,
1218
                    $childNodeVersion,
1219
                    $childNodePage
1220
                )
1221
            );
1222
        }
1223
    }
1224
1225
    /**
1226
     * @param Request $request
1227
     * @param string  $type
1228
     *
1229
     * @return HasNodeInterface
1230
     */
1231
    private function createNewPage(Request $request, $type)
1232
    {
1233
        /* @var HasNodeInterface $newPage */
1234
        $newPage = new $type();
1235
1236
        $title = $request->get('title');
1237
        if (\is_string($title) && !empty($title)) {
1238
            $newPage->setTitle($title);
1239
        } else {
1240
            $newPage->setTitle($this->get('translator')->trans('kuma_node.admin.new_page.title.default'));
1241
        }
1242
        $this->em->persist($newPage);
1243
        $this->em->flush();
1244
1245
        return $newPage;
1246
    }
1247
1248
    /**
1249
     * @param Request $request
1250
     *
1251
     * @return string
1252
     * @throw InvalidArgumentException
1253
     */
1254
    private function validatePageType($request)
1255
    {
1256
        $type = $request->get('type');
1257
1258
        if (empty($type)) {
1259
            throw new InvalidArgumentException('Please specify a type of page you want to create');
1260
        }
1261
1262
        return $type;
1263
    }
1264
1265
    /**
1266
     * @param Node $node
1267
     *
1268
     * @return \Symfony\Component\HttpFoundation\Response
1269
     */
1270
    private function renderNodeNotTranslatedPage(Node $node)
1271
    {
1272
        //try to find a parent node with the correct translation, if there is none allow copy.
1273
        //if there is a parent but it doesn't have the language to copy to don't allow it
1274
        $parentNode = $node->getParent();
1275
        if ($parentNode) {
1276
            $parentNodeTranslation = $parentNode->getNodeTranslation(
1277
                $this->locale,
1278
                true
1279
            );
1280
            $parentsAreOk = false;
1281
1282
            if ($parentNodeTranslation) {
1283
                $parentsAreOk = $this->em->getRepository(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method hasParentNodeTranslationsForLanguage() does only exist in the following implementations of said interface: Kunstmaan\NodeBundle\Rep...deTranslationRepository.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
1284
                    'KunstmaanNodeBundle:NodeTranslation'
1285
                )->hasParentNodeTranslationsForLanguage(
1286
                    $node->getParent()->getNodeTranslation(
1287
                        $this->locale,
1288
                        true
1289
                    ),
1290
                    $this->locale
1291
                );
1292
            }
1293
        } else {
1294
            $parentsAreOk = true;
1295
        }
1296
1297
        return $this->render(
1298
            '@KunstmaanNode/NodeAdmin/pagenottranslated.html.twig',
1299
            array(
1300
                'node' => $node,
1301
                'nodeTranslations' => $node->getNodeTranslations(
1302
                    true
1303
                ),
1304
                'copyfromotherlanguages' => $parentsAreOk,
1305
            )
1306
        );
1307
    }
1308
}
1309