Completed
Push — master ( 6d6774...64f3ed )
by Jeroen
11:23 queued 05:13
created

Service/EntityVersionLockService.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\AdminListBundle\Service;
4
5
use Doctrine\Common\Persistence\ObjectManager;
6
use FOS\UserBundle\Model\User;
7
use Kunstmaan\AdminListBundle\Entity\LockableEntity;
8
use Kunstmaan\AdminListBundle\Entity\EntityVersionLock;
9
use Kunstmaan\AdminListBundle\Entity\LockableEntityInterface;
10
use Kunstmaan\AdminListBundle\Repository\EntityVersionLockRepository;
11
12
class EntityVersionLockService
13
{
14
    /**
15
     * @var ObjectManager
16
     */
17
    private $objectManager;
18
19
    /**
20
     * @var int
21
     */
22
    private $threshold;
23
24
    /**
25
     * @var bool
26
     */
27
    private $lockEnabled;
28
29 6
    public function __construct(ObjectManager $em, $threshold, $lockEnabled)
30
    {
31 6
        $this->setObjectManager($em);
32 6
        $this->setThreshold($threshold);
33 6
        $this->setLockEnabled($lockEnabled);
34 6
    }
35
36
    /**
37
     * @param LockableEntityInterface $entity
38
     *
39
     * @return bool
40
     */
41 3
    public function isEntityBelowThreshold(LockableEntityInterface $entity)
42
    {
43
        /** @var LockableEntity $lockable */
44 3
        $lockable = $this->getLockableEntity($entity, false);
45
46 3
        if ($this->lockEnabled && $lockable->getId() !== null) {
47 2
            $now = new \DateTime();
48 2
            $thresholdDate = clone $lockable->getUpdated();
49 2
            $thresholdDate->add(new \DateInterval('PT'.$this->threshold.'S'));
50
51 2
            return $thresholdDate > $now;
52
        }
53
54 1
        return false;
55
    }
56
57
    /**
58
     * @param User                    $user
59
     * @param LockableEntityInterface $entity
60
     *
61
     * @return bool
62
     */
63 2
    public function isEntityLocked(User $user, LockableEntityInterface $entity)
64
    {
65
        /** @var LockableEntity $lockable */
66 2
        $lockable = $this->getLockableEntity($entity);
67
68 2
        if ($this->lockEnabled) {
69 2
            $this->removeExpiredLocks($lockable);
70 2
            $locks = $this->getEntityVersionLocksByLockableEntity($lockable, $user);
71
72 2
            if ($locks === null || !\count($locks)) {
73 1
                $this->createEntityVersionLock($user, $lockable);
74
75 1
                $lockable->setUpdated(new \DateTime());
76 1
                $this->objectManager->flush();
77
78 1
                return false;
79
            }
80
81 1
            return true;
82
        }
83
84
        return false;
85
    }
86
87
    /**
88
     * When editing the entity, create a new entity translation lock.
89
     *
90
     * @param User           $user
91
     * @param LockableEntity $entity
92
     */
93 1
    protected function createEntityVersionLock(User $user, LockableEntity $entity)
94
    {
95
        /** @var EntityVersionLock $lock */
96 1
        $lock = $this->objectManager->getRepository('KunstmaanAdminListBundle:EntityVersionLock')->findOneBy([
97 1
            'owner' => $user->getUsername(),
98 1
            'lockableEntity' => $entity,
99
        ]);
100 1
        if (!$lock) {
101
            $lock = new EntityVersionLock();
102
        }
103 1
        $lock->setOwner($user->getUsername());
104 1
        $lock->setLockableEntity($entity);
105 1
        $lock->setCreatedAt(new \DateTime());
106 1
        $this->objectManager->persist($lock);
107 1
        $this->objectManager->flush();
108 1
    }
109
110
    /**
111
     * @param LockableEntityInterface $entity
112
     * @param User                    $userToExclude
113
     *
114
     * @return array
115
     */
116 1
    public function getUsersWithEntityVersionLock(LockableEntityInterface $entity, User $userToExclude = null)
117
    {
118
        /** @var LockableEntity $lockable */
119 1
        $lockable = $this->getLockableEntity($entity);
120
121 1
        return  array_reduce(
122 1
            $this->getEntityVersionLocksByLockableEntity($lockable, $userToExclude),
123
            function ($return, EntityVersionLock $item) {
124 1
                $return[] = $item->getOwner();
125
126 1
                return $return;
127 1
            },
128 1
            []
129
        );
130
    }
131
132
    /**
133
     * @param LockableEntity $entity
134
     */
135 2
    protected function removeExpiredLocks(LockableEntity $entity)
136
    {
137 2
        $locks = $this->objectManager->getRepository('KunstmaanAdminListBundle:EntityVersionLock')->getExpiredLocks($entity, $this->threshold);
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Doctrine\Persistence\ObjectRepository as the method getExpiredLocks() does only exist in the following implementations of said interface: Kunstmaan\AdminListBundl...tyVersionLockRepository, Kunstmaan\NodeBundle\Rep...deVersionLockRepository.

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...
138 2
        foreach ($locks as $lock) {
139 2
            $this->objectManager->remove($lock);
140
        }
141 2
    }
142
143
    /**
144
     * When editing an entity, check if there is a lock for this entity.
145
     *
146
     * @param LockableEntity $entity
147
     * @param User           $userToExclude
148
     *
149
     * @return EntityVersionLock[]
150
     */
151 3
    protected function getEntityVersionLocksByLockableEntity(LockableEntity $entity, User $userToExclude = null)
152
    {
153
        /** @var EntityVersionLockRepository $objectRepository */
154 3
        $objectRepository = $this->objectManager->getRepository('KunstmaanAdminListBundle:EntityVersionLock');
155
156 3
        return $objectRepository->getLocksForLockableEntity($entity, $this->threshold, $userToExclude);
157
    }
158
159
    /**
160
     * Get or create a LockableEntity for an entity with LockableEntityInterface
161
     *
162
     * @param LockableEntityInterface $entity
163
     *
164
     * @return LockableEntity
165
     */
166 6
    protected function getLockableEntity(LockableEntityInterface $entity, $create = true)
167
    {
168
        /** @var LockableEntity $lockable */
169 6
        $lockable = $this->objectManager->getRepository('KunstmaanAdminListBundle:LockableEntity')->getOrCreate($entity->getId(), \get_class($entity));
170
171 6
        if ($create === true && $lockable->getId() === null) {
172
            $this->objectManager->persist($lockable);
173
            $this->objectManager->flush();
174
        }
175
176 6
        return $lockable;
177
    }
178
179
    /**
180
     * @param ObjectManager $objectManager
181
     */
182 6
    public function setObjectManager($objectManager)
183
    {
184 6
        $this->objectManager = $objectManager;
185 6
    }
186
187
    /**
188
     * @param int $threshold
189
     */
190 6
    public function setThreshold($threshold)
191
    {
192 6
        $this->threshold = $threshold;
193 6
    }
194
195
    /**
196
     * @param bool lockEnabled
197
     */
198 6
    public function setLockEnabled($lockEnabled)
199
    {
200 6
        $this->lockEnabled = $lockEnabled;
201 6
    }
202
}
203