Completed
Push — master ( 17d2a9...43860c )
by Derek Stephen
06:07
created

UserService::hasProfile()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 5
cts 5
cp 1
rs 9.9
c 0
b 0
f 0
cc 3
nc 2
nop 1
crap 3
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 Laminas\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 26
    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 26
        $this->em = $entityManager;
35 26
        $this->personSvc = $personService;
36 26
        $this->setUserClass(User::class);
37 26
    }
38
39
    /**
40
     * @param array $data
41
     * @return UserInterface
42
     * @throws \Exception
43
     */
44 18
    public function createFromArray(array $data): UserInterface
45
    {
46
        /** @var UserInterface $user */
47 18
        $user = new $this->userClass();
48 18
        $person = isset($data['person']) ? $data['person'] : new Person();
49 18
        $user->setPerson($person);
50 18
        isset($data['id']) ? $user->setId($data['id']) : null;
51 18
        isset($data['email']) ? $user->setEmail($data['email']) : null;
52 18
        isset($data['password']) ? $user->setPassword($data['password']) : null;
53 18
        isset($data['state']) ? $user->setState(new State($data['state'])) : null;
54 18
        isset($data['registrationDate']) ? $user->setRegistrationDate(new DateTime($data['registrationDate'])) : null;
55 18
        isset($data['lastLogin']) ? $user->setLastLogin(new DateTime($data['lastLogin'])) : null;
56
57 18
        return $user;
58
    }
59
60
    /**
61
     * @return array
62
     */
63 1
    public function toArray(UserInterface $user): array
64
    {
65
        return array
66
        (
67 1
            'id' => $user->getID(),
68 1
            'email' => $user->getEmail(),
69 1
            'person' => $user->getPerson(),
70 1
            'password' => $user->getPassword(),
71 1
            'state' => $user->getState()->getValue(),
72 1
            'registrationDate' => $user->getRegistrationDate() === null ? null : $user->getRegistrationDate()->format('Y-m-d H:i:s'),
73 1
            '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 19
    public function saveUser(UserInterface $user): UserInterface
83
    {
84 19
        return $this->getUserRepository()->save($user);
85
    }
86
87
    /**
88
     * @param int $id
89
     * @return UserInterface|null
90
     */
91 1
    public function findUserById(int $id): ?UserInterface
92
    {
93 1
        $criteria = new UserCriteria();
94 1
        $criteria->setId($id);
95 1
        $results = $this->getUserRepository()->findByCriteria($criteria);
96 1
        return (count($results)) ? $results[0] : null;
97
    }
98
99
    /**
100
     * @param string $email
101
     * @return UserInterface|null
102
     */
103 1
    public function findUserByEmail(string $email): ?UserInterface
104
    {
105 1
        $criteria = new UserCriteria();
106 1
        $criteria->setEmail($email);
107 1
        $result = $this->getUserRepository()->findByCriteria($criteria);
108 1
        return count($result) ? $result[0] : null;
109
    }
110
111
    /**
112
     * @return \Doctrine\Common\Persistence\ObjectRepository|\Doctrine\ORM\EntityRepository
113
     */
114 21
    private function getUserRepository(): UserRepository
115
    {
116 21
        return $this->em->getRepository($this->userClass);
117
    }
118
119
    /**
120
     * @return \Doctrine\Common\Persistence\ObjectRepository|\Doctrine\ORM\EntityRepository
121
     */
122 5
    private function getEmailLinkRepository(): EmailLinkRepository
123
    {
124 5
        return $this->em->getRepository(EmailLink::class);
125
    }
126
127
    /**
128
     * @param User $user
129
     * @return bool
130
     */
131
    public function hasProfile(User $user): bool
132
    {
133 4
        $has = false;
134
        $person = $user->getPerson();
135 4
136 1
        if (!empty($person->getFirstname()) && !empty($person->getLastname())) {
137
            $has = true;
138 3
        }
139 1
140
        return $has;
141
    }
142 2
143 2
    /**
144 2
     * @param array $data
145 2
     * @return UserInterface
146 1
     * @throws UserException
147
     * @throws \Doctrine\ORM\OptimisticLockException
148
     */
149 2
    public function registerUser(array $data): UserInterface
150
    {
151 2
        if (!isset($data['email']) || !isset($data['password']) || !isset($data['confirm'])) {
152 2
            throw new InvalidArgumentException('Required fields missing', 400);
153 2
        }
154 2
        if ($data['password'] !== $data['confirm']) {
155 2
            throw new UserException(UserException::WRONG_PASSWORD);
156 2
        }
157
158 2
        $criteria = new UserCriteria();
159 2
        $criteria->setEmail($data['email']);
160 2
        $user = $this->getUserRepository()->findByCriteria($criteria);
161 2
        if(!empty($user)) {
162 2
            throw new UserException(UserException::USER_EXISTS);
163
        }
164 2
165
        $person = new Person();
166
        /** @var UserInterface $user */
167
        $user = new $this->userClass();
168
        $state = new State(State::STATE_UNACTIVATED);
169
        $user->setPerson($person);
170
        $user->setEmail($data['email']);
171
        $user->setRegistrationDate(new DateTime());
172
        $user->setState($state);
173 8
174
        $bcrypt = new Bcrypt();
175 8
        $bcrypt->setCost(14);
176 8
        $encryptedPassword = $bcrypt->create($data['password']);
177
        $user->setPassword($encryptedPassword);
178 8
        $this->saveUser($user);
179 8
180 8
        return $user;
181
    }
182 8
183
    /**
184
     * @param UserInterface $user
185
     * @param $password
186
     * @return UserInterface
187
     * @throws \Doctrine\ORM\OptimisticLockException
188
     */
189
    public function changePassword(UserInterface $user, string $password): UserInterface
190
    {
191 4
        $bcrypt = new Bcrypt();
192
        $bcrypt->setCost(14);
193 4
194 4
        $encryptedPassword = $bcrypt->create($password);
195 4
        $user->setPassword($encryptedPassword);
196 4
        $this->saveUser($user);
197 4
198 4
        return $user;
199 4
    }
200
201 4
    /**
202
     * @param UserInterface $user
203
     * @param int $expiry_days
204
     * @return EmailLink
205
     * @throws \Doctrine\ORM\OptimisticLockException
206
     */
207
    public function generateEmailLink(UserInterface $user, int $expiry_days = 7): EmailLink
208 4
    {
209
        $date = new DateTime();
210
        $date->modify('+'.$expiry_days.' days');
211 4
        $token = md5(uniqid(rand(), true));
212 4
        $link = new EmailLink();
213 4
        $link->setUser($user);
214
        $link->setToken($token);
215
        $link->setExpiryDate($date);
216
217
        return $this->getEmailLinkRepository()->save($link);
218
    }
219
220 19
    /**
221
     * @param EmailLink $link
222 19
     * @throws \Doctrine\ORM\ORMException
223 19
     */
224
    public function deleteEmailLink(EmailLink $link): void
225
    {
226
        /** @var EmailLink $link */
227
        $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...
228
        $this->getEmailLinkRepository()->delete($link);
229
    }
230
231 4
    /**
232
     * @param UserInterface $user
233 4
     * @param bool $deletePerson
234 4
     * @throws \Doctrine\ORM\OptimisticLockException
235 1
     */
236
    public function deleteUser(UserInterface $user, bool $deletePerson = false):  void
237 3
    {
238 1
        $this->getUserRepository()->delete($user,$deletePerson);
239
    }
240 2
241 1
    /**
242
     * @param string $email
243 1
     * @param string $token
244
     * @return EmailLink
245
     * @throws EmailLinkException
246
     */
247
    public function findEmailLink(string $email, string $token): EmailLink
248
    {
249
        $link = $this->getEmailLinkRepository()->findByToken($token);
250
        if(!$link) {
251
            throw new EmailLinkException(EmailLinkException::LINK_NOT_FOUND);
252 6
        }
253
        if($link->getUser()->getEmail() != $email) {
254 6
            throw new EmailLinkException(EmailLinkException::LINK_NO_MATCH);
255 6
        }
256
        if($link->getExpiryDate() < new DateTime()) {
257 6
            throw new EmailLinkException(EmailLinkException::LINK_EXPIRED);
258
        }
259 6
        return $link;
260 1
    }
261
262
    /**
263
     * @param string $email
264 5
     * @param string $password
265
     * @return int
266 5
     * @throws UserException
267 5
     */
268 1
    public function authenticate(string $email, string $password): int
269 4
    {
270 3
        $criteria = new UserCriteria();
271 1
        $criteria->setEmail($email);
272 3
273 1
        $user = $this->getUserRepository()->findByCriteria($criteria);
274
275
        if(empty($user)) {
276 2
            throw new UserException(UserException::USER_NOT_FOUND);
277 2
        }
278
279 2
        /** @var UserInterface $user  */
280
        $user = $user[0];
281 1
282
        switch($user->getState()->getValue()) {
283
            case State::STATE_UNACTIVATED:
284 1
                throw new UserException(UserException::USER_UNACTIVATED);
285
            case State::STATE_DISABLED:
286
            case State::STATE_SUSPENDED:
287
                throw new UserException(UserException::USER_DISABLED);
288
            case State::STATE_BANNED:
289
                throw new UserException(UserException::USER_BANNED);
290
        }
291 1
292
        $bcrypt = new Bcrypt();
293 1
        $bcrypt->setCost(14);
294
295
        if(!$bcrypt->verify($password, $user->getPassword()))
296
        {
297
            throw new UserException(UserException::WRONG_PASSWORD);
298
        }
299
300 3
        return $user->getID();
301
    }
302 3
303 3
    /**
304
     * @param UserCriteria $criteria
305
     * @return array
306
     */
307
    public function findByCriteria(UserCriteria $criteria): array
308
    {
309
        return $this->getUserRepository()->findByCriteria($criteria);
310
    }
311 1
312
    /**
313 1
     * @param UserCriteria $criteria
314 1
     * @return UserInterface|null
315
     */
316 1
    public function findOneByCriteria(UserCriteria $criteria): ?UserInterface
317
    {
318
        $results = $this->getUserRepository()->findByCriteria($criteria);
319
        return count($results) > 0 ? $results[0] : null;
320
    }
321
322 26
    /**
323
     * @param UserInterface $user
324 26
     * @param $password
325 26
     * @return bool
326
     */
327
    public function checkPassword(UserInterface $user, string $password): bool
328
    {
329
        $bcrypt = new Bcrypt();
330 1
        $bcrypt->setCost(14);
331
332 1
        return $bcrypt->verify($password, $user->getPassword());
333
    }
334
335
    /**
336
     * @param string $fullyQualifiedClassName
337
     */
338
    public function setUserClass(string $fullyQualifiedClassName): void
339
    {
340
        $this->userClass = $fullyQualifiedClassName;
341
    }
342
343
    /**
344
     * @return PersonService
345
     */
346
    public function getPersonSvc(): PersonService
347
    {
348
        return $this->personSvc;
349
    }
350
}