Completed
Push — master ( e7d3f8...bbd5c2 )
by Derek Stephen
01:35
created

UserService::saveUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Del\Service;
4
5
use DateTime;
6
use Del\Criteria\UserCriteria;
7
use Del\Entity\EmailLink;
8
use Del\Entity\User;
9
use Del\Person\Entity\Person;
10
use Del\Entity\UserInterface;
11
use Del\Exception\EmailLinkException;
12
use Del\Exception\UserException;
13
use Del\Repository\EmailLink as EmailLinkRepository;
14
use Del\Repository\UserRepository;
15
use Del\Person\Service\PersonService;
16
use Del\Value\User\State;
17
use Doctrine\ORM\EntityManager;
18
use InvalidArgumentException;
19
use Zend\Crypt\Password\Bcrypt;
20
21
class UserService
22
{
23
    /** @var EntityManager $em */
24
    protected $em;
25
26
    /** @var  PersonService */
27
    private $personSvc;
28
29
    /** @var string $userClass */
30
    private $userClass;
31
32
    public function __construct(EntityManager $entityManager,  PersonService $personService)
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $entityManager. This is generally not recommended as it might get closed and become unusable. Instead, it is recommended to inject the ManagerRegistry and retrieve the EntityManager via getManager() each time you need it.

The EntityManager might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:

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

        throw $ex;
    }
}

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

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

Loading history...
33
    {
34
        $this->em = $entityManager;
35
        $this->personSvc = $personService;
36
        $this->setUserClass(User::class);
37
    }
38
39
    /**
40
     * @param array $data
41
     * @return UserInterface
42
     * @throws \Exception
43
     */
44
    public function createFromArray(array $data): UserInterface
45
    {
46
        /** @var UserInterface $user */
47
        $user = new $this->userClass();
48
        $person = isset($data['person']) ? $data['person'] : new Person();
49
        $user->setPerson($person);
50
        isset($data['id']) ? $user->setId($data['id']) : null;
51
        isset($data['email']) ? $user->setEmail($data['email']) : null;
52
        isset($data['password']) ? $user->setPassword($data['password']) : null;
53
        isset($data['state']) ? $user->setState(new State($data['state'])) : null;
54
        isset($data['registrationDate']) ? $user->setRegistrationDate(new DateTime($data['registrationDate'])) : null;
55
        isset($data['lastLogin']) ? $user->setLastLogin(new DateTime($data['lastLogin'])) : null;
56
57
        return $user;
58
    }
59
60
    /**
61
     * @return array
62
     */
63
    public function toArray(UserInterface $user): array
64
    {
65
        return array
66
        (
67
            'id' => $user->getID(),
68
            'email' => $user->getEmail(),
69
            'person' => $user->getPerson(),
70
            'password' => $user->getPassword(),
71
            'state' => $user->getState()->getValue(),
72
            'registrationDate' => $user->getRegistrationDate() === null ? null : $user->getRegistrationDate()->format('Y-m-d H:i:s'),
73
            'lastLoginDate' => $user->getLastLoginDate() === null ? null : $user->getLastLoginDate()->format('Y-m-d H:i:s'),
74
        );
75
    }
76
77
    /**
78
     * @param UserInterface $user
79
     * @return UserInterface
80
     * @throws \Doctrine\ORM\OptimisticLockException
81
     */
82
    public function saveUser(UserInterface $user): UserInterface
83
    {
84
        return $this->getUserRepository()->save($user);
85
    }
86
87
    /**
88
     * @param int $id
89
     * @return UserInterface|null
90
     */
91
    public function findUserById(int $id): ?UserInterface
92
    {
93
        $criteria = new UserCriteria();
94
        $criteria->setId($id);
95
        $results = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
96
        return (count($results)) ? $results[0] : null;
97
    }
98
99
    /**
100
     * @param string $email
101
     * @return UserInterface|null
102
     */
103
    public function findUserByEmail(string $email): ?UserInterface
104
    {
105
        $criteria = new UserCriteria();
106
        $criteria->setEmail($email);
107
        $result = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
108
        return count($result) ? $result[0] : null;
109
    }
110
111
    /**
112
     * @return \Doctrine\Common\Persistence\ObjectRepository|\Doctrine\ORM\EntityRepository
113
     */
114
    private function getUserRepository(): UserRepository
115
    {
116
        return $this->em->getRepository($this->userClass);
117
    }
118
119
    /**
120
     * @return \Doctrine\Common\Persistence\ObjectRepository|\Doctrine\ORM\EntityRepository
121
     */
122
    private function getEmailLinkRepository(): EmailLinkRepository
123
    {
124
        return $this->em->getRepository(EmailLink::class);
125
    }
126
127
    /**
128
     * @param array $data
129
     * @return UserInterface
130
     * @throws UserException
131
     * @throws \Doctrine\ORM\OptimisticLockException
132
     */
133
    public function registerUser(array $data): UserInterface
134
    {
135
        if (!isset($data['email']) || !isset($data['password']) || !isset($data['confirm'])) {
136
            throw new InvalidArgumentException('Required fields missing', 400);
137
        }
138
        if ($data['password'] !== $data['confirm']) {
139
            throw new UserException(UserException::WRONG_PASSWORD);
140
        }
141
142
        $criteria = new UserCriteria();
143
        $criteria->setEmail($data['email']);
144
        $user = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
145
        if(!empty($user)) {
146
            throw new UserException(UserException::USER_EXISTS);
147
        }
148
149
        $person = new Person();
150
        /** @var UserInterface $user */
151
        $user = new $this->userClass();
152
        $state = new State(State::STATE_UNACTIVATED);
153
        $user->setPerson($person);
154
        $user->setEmail($data['email']);
155
        $user->setRegistrationDate(new DateTime());
156
        $user->setState($state);
157
158
        $bcrypt = new Bcrypt();
159
        $bcrypt->setCost(14);
160
        $encryptedPassword = $bcrypt->create($data['password']);
161
        $user->setPassword($encryptedPassword);
162
        $this->saveUser($user);
163
164
        return $user;
165
    }
166
167
    /**
168
     * @param UserInterface $user
169
     * @param $password
170
     * @return UserInterface
171
     * @throws \Doctrine\ORM\OptimisticLockException
172
     */
173
    public function changePassword(UserInterface $user, string $password): UserInterface
174
    {
175
        $bcrypt = new Bcrypt();
176
        $bcrypt->setCost(14);
177
178
        $encryptedPassword = $bcrypt->create($password);
179
        $user->setPassword($encryptedPassword);
180
        $this->saveUser($user);
181
182
        return $user;
183
    }
184
185
    /**
186
     * @param UserInterface $user
187
     * @param int $expiry_days
188
     * @return EmailLink
189
     * @throws \Doctrine\ORM\OptimisticLockException
190
     */
191
    public function generateEmailLink(UserInterface $user, int $expiry_days = 7): EmailLink
192
    {
193
        $date = new DateTime();
194
        $date->modify('+'.$expiry_days.' days');
195
        $token = md5(uniqid(rand(), true));
196
        $link = new EmailLink();
197
        $link->setUser($user);
198
        $link->setToken($token);
199
        $link->setExpiryDate($date);
200
201
        return $this->getEmailLinkRepository()->save($link);
202
    }
203
204
    /**
205
     * @param EmailLink $link
206
     * @throws \Doctrine\ORM\ORMException
207
     */
208
    public function deleteEmailLink(EmailLink $link): void
209
    {
210
        /** @var EmailLink $link */
211
        $link = $this->em->merge($link);
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ORM\EntityManager::merge() has been deprecated with message: 2.7 This method is being removed from the ORM and won't have any replacement

This method has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
212
        $this->getEmailLinkRepository()->delete($link);
213
    }
214
215
    /**
216
     * @param UserInterface $user
217
     * @param bool $deletePerson
218
     * @throws \Doctrine\ORM\OptimisticLockException
219
     */
220
    public function deleteUser(UserInterface $user, bool $deletePerson = false):  void
221
    {
222
        $this->getUserRepository()->delete($user,$deletePerson);
223
    }
224
225
    /**
226
     * @param string $email
227
     * @param string $token
228
     * @return EmailLink
229
     * @throws EmailLinkException
230
     */
231
    public function findEmailLink(string $email, string $token): EmailLink
232
    {
233
        $link = $this->getEmailLinkRepository()->findByToken($token);
0 ignored issues
show
Bug introduced by
The method findByToken() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
234
        if(!$link) {
235
            throw new EmailLinkException(EmailLinkException::LINK_NOT_FOUND);
236
        }
237
        if($link->getUser()->getEmail() != $email) {
238
            throw new EmailLinkException(EmailLinkException::LINK_NO_MATCH);
239
        }
240
        if($link->getExpiryDate() < new DateTime()) {
241
            throw new EmailLinkException(EmailLinkException::LINK_EXPIRED);
242
        }
243
        return $link;
244
    }
245
246
    /**
247
     * @param string $email
248
     * @param string $password
249
     * @return int
250
     * @throws UserException
251
     */
252
    public function authenticate(string $email, string $password): int
253
    {
254
        $criteria = new UserCriteria();
255
        $criteria->setEmail($email);
256
257
        $user = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
258
259
        if(empty($user)) {
260
            throw new UserException(UserException::USER_NOT_FOUND);
261
        }
262
263
        /** @var UserInterface $user  */
264
        $user = $user[0];
265
266
        switch($user->getState()->getValue()) {
267
            case State::STATE_UNACTIVATED:
268
                throw new UserException(UserException::USER_UNACTIVATED);
269
            case State::STATE_DISABLED:
270
            case State::STATE_SUSPENDED:
271
                throw new UserException(UserException::USER_DISABLED);
272
            case State::STATE_BANNED:
273
                throw new UserException(UserException::USER_BANNED);
274
        }
275
276
        $bcrypt = new Bcrypt();
277
        $bcrypt->setCost(14);
278
279
        if(!$bcrypt->verify($password, $user->getPassword()))
280
        {
281
            throw new UserException(UserException::WRONG_PASSWORD);
282
        }
283
284
        return $user->getID();
285
    }
286
287
    /**
288
     * @param UserCriteria $criteria
289
     * @return array
290
     */
291
    public function findByCriteria(UserCriteria $criteria): array
292
    {
293
        return $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
294
    }
295
296
    /**
297
     * @param UserCriteria $criteria
298
     * @return UserInterface|null
299
     */
300
    public function findOneByCriteria(UserCriteria $criteria): ?UserInterface
301
    {
302
        $results = $this->getUserRepository()->findByCriteria($criteria);
0 ignored issues
show
Bug introduced by
The method findByCriteria() does not exist on Doctrine\Common\Persistence\ObjectRepository. Did you maybe mean findBy()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
303
        return count($results) > 0 ? $results[0] : null;
304
    }
305
306
    /**
307
     * @param UserInterface $user
308
     * @param $password
309
     * @return bool
310
     */
311
    public function checkPassword(UserInterface $user, string $password): bool
312
    {
313
        $bcrypt = new Bcrypt();
314
        $bcrypt->setCost(14);
315
316
        return $bcrypt->verify($password, $user->getPassword());
317
    }
318
319
    /**
320
     * @param string $fullyQualifiedClassName
321
     */
322
    public function setUserClass(string $fullyQualifiedClassName): void
323
    {
324
        $this->userClass = $fullyQualifiedClassName;
325
    }
326
327
    /**
328
     * @return PersonService
329
     */
330
    public function getPersonSvc(): PersonService
331
    {
332
        return $this->personSvc;
333
    }
334
}