Completed
Push — master ( 0d8778...4693cf )
by Derek Stephen
05:55
created

User::deleteUser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 1
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\Person;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Del\Service\Person.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
use Del\Entity\User as UserEntity;
10
use Del\Exception\EmailLinkException;
11
use Del\Exception\UserException;
12
use Del\Repository\User as UserRepository;
13
use Del\Service\Person as PersonService;
14
use Del\Value\User\State;
15
use Doctrine\ORM\EntityManager;
16
use InvalidArgumentException;
17
use Zend\Crypt\Password\Bcrypt;
18
19
class User
20
{
21
    /** @var EntityManager $em */
22
    protected $em;
23
24
    /** @var  PersonService */
25
    private $personSvc;
26
27 22
    public function __construct(EntityManager $em, PersonService $personSvc)
0 ignored issues
show
Bug introduced by
You have injected the EntityManager via parameter $em. 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...
28
    {
29 22
        $this->em = $em;
30 22
        $this->personSvc = $personSvc;
31 22
    }
32
33
   /** 
34
    * @param array $data
35
    * @return UserEntity
36
    */
37 16
    public function createFromArray(array $data)
38
    {
39 16
        $user = new UserEntity();
40 16
        $user->setPerson(new Person());
41 16
        isset($data['id']) ? $user->setId($data['id']) : null;
42 16
        isset($data['email']) ? $user->setEmail($data['email']) : null;
43 16
        isset($data['password']) ? $user->setPassword($data['password']) : null;
44 16
        isset($data['state']) ? $user->setState(new State($data['state'])) : null;
45 16
        isset($data['registrationDate']) ? $user->setRegistrationDate(new DateTime($data['registrationDate'])) : null;
46 16
        isset($data['lastLogin']) ? $user->setLastLogin(new DateTime($data['lastLogin'])) : null;
47 16
        return $user;
48
    }
49
50
51
52
53
    /**
54
     * @return array
55
     */
56 1
    public function toArray(UserEntity $user)
57
    {
58
        return array
59
        (
60 1
            'id' => $user->getID(),
61 1
            'email' => $user->getEmail(),
62 1
            'person' => $user->getPerson(),
63 1
            'password' => $user->getPassword(),
64 1
            'state' => $user->getState()->getValue(),
65 1
            'registrationDate' => is_null($user->getRegistrationDate()) ? null : $user->getRegistrationDate()->format('Y-m-d H:i:s'),
66 1
            'lastLoginDate' => is_null($user->getLastLoginDate()) ? null : $user->getLastLoginDate()->format('Y-m-d H:i:s'),
67 1
        );
68
    }
69
70
    /**
71
     * @param UserEntity $user
72
     * @return UserEntity
73
     */
74 16
    public function saveUser(UserEntity $user)
75
    {
76 16
        return $this->getUserRepository()->save($user);
77 1
    }
78
79
    /**
80
     * @param int $id
81
     * @return UserEntity|null
82
     */
83 1 View Code Duplication
    public function findUserById($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...
84
    {
85 1
        $criteria = new UserCriteria();
86 1
        $criteria->setId($id);
87 1
        $results = $this->getUserRepository()->findByCriteria($criteria);
88 1
        return (count($results)) ? $results[0] : null;
89
    }
90
91
    /**
92
     * @param string $email
93
     * @return UserEntity|null
94
     */
95 1 View Code Duplication
    public function findUserByEmail($email)
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...
96
    {
97 1
        $criteria = new UserCriteria();
98 1
        $criteria->setEmail($email);
99 1
        $result = $this->getUserRepository()->findByCriteria($criteria);
100 1
        return count($result) ? $result[0] : null;
101
    }
102
103
   /**
104
    * @return UserRepository
105
    */
106 17
    private function getUserRepository()
107
    {
108 17
        return $this->em->getRepository('Del\Entity\User');
109
    }
110
111
    /**
112
     * @return \Del\Repository\EmailLink
113
     */
114 5
    private function getEmailLinkRepository()
115
    {
116 5
        return $this->em->getRepository('Del\Entity\EmailLink');
117
    }
118
119 4
    public function registerUser(array $data)
120
    {
121 4
        if (!isset($data['email']) || !isset($data['password']) || !isset($data['confirm'])) {
122 1
            throw new InvalidArgumentException();
123
        }
124 3
        if ($data['password'] !== $data['confirm']) {
125 1
            throw new UserException(UserException::WRONG_PASSWORD);
126
        }
127
128 2
        $criteria = new UserCriteria();
129 2
        $criteria->setEmail($data['email']);
130 2
        $user = $this->getUserRepository()->findByCriteria($criteria);
131 2
        if(!empty($user)) {
132 1
            throw new UserException(UserException::USER_EXISTS);
133
        }
134
135 2
        $person = new Person();
136 2
        $user = new UserEntity();
137 2
        $state = new State(State::STATE_UNACTIVATED);
138 2
        $user->setPerson($person)
139 2
             ->setEmail($data['email'])
140 2
             ->setRegistrationDate(new DateTime())
141 2
             ->setState($state);
142
143 2
        $bcrypt = new Bcrypt();
144 2
        $bcrypt->setCost(14);
145
146 2
        $encryptedPassword = $bcrypt->create($data['password']);
147 2
        $user->setPassword($encryptedPassword);
148
149 2
        $this->saveUser($user);
150 2
        return $user;
151
    }
152
153
    /**
154
     * @param UserEntity $user
155
     * @param $password
156
     * @return UserEntity
157
     */
158 6
    public function changePassword(UserEntity $user, $password)
159
    {
160 6
        $bcrypt = new Bcrypt();
161 6
        $bcrypt->setCost(14);
162
163 6
        $encryptedPassword = $bcrypt->create($password);
164 6
        $user->setPassword($encryptedPassword);
165
166 6
        $this->saveUser($user);
167 6
        return $user;
168
    }
169
170
    /**
171
     * @param UserEntity $user
172
     * @param int $expiry_days
173
     */
174 4
    public function generateEmailLink(UserEntity $user, $expiry_days = 7)
175
    {
176 4
        $date = new DateTime();
177 4
        $date->modify('+'.$expiry_days.' days');
178 4
        $token = md5(uniqid(rand(), true));
179 4
        $link = new EmailLink();
180 4
        $link->setUser($user);
181 4
        $link->setToken($token);
182 4
        $link->setExpiryDate($date);
183 4
        return $this->getEmailLinkRepository()->save($link);
184
    }
185
186
    /**
187
     * @param EmailLink $link
188
     */
189 4
    public function deleteEmailLink(EmailLink $link)
190
    {
191
        /** @var EmailLink $link */
192 4
        $link = $this->em->merge($link);
193 4
        $this->getEmailLinkRepository()->delete($link);
194 4
    }
195
196
    /**
197
     * @param UserEntity $user
198
     */
199 16
    public function deleteUser(UserEntity $user)
200
    {
201
        /** @var User $user */
202 16
        $user = $this->em->merge($user);
203 16
        $this->getUserRepository()->delete($user);
0 ignored issues
show
Documentation introduced by
$user is of type object<Del\Service\User>, but the function expects a object<Del\Entity\User>.

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...
204 16
    }
205
206
    /**
207
     * @param $email
208
     * @param $token
209
     * @throws EmailLinkException
210
     */
211 4
    public function findEmailLink($email, $token)
212
    {
213 4
        $link = $this->getEmailLinkRepository()->findByToken($token);
214 4
        if(!$link) {
215 1
            throw new EmailLinkException(EmailLinkException::LINK_NOT_FOUND);
216
        }
217 3
        if($link->getUser()->getEmail() != $email) {
218 1
            throw new EmailLinkException(EmailLinkException::LINK_NO_MATCH);
219
        }
220 2
        if($link->getExpiryDate() < new DateTime()) {
221 1
            throw new EmailLinkException(EmailLinkException::LINK_EXPIRED);
222
        }
223 1
        return $link;
224
    }
225
226
    /**
227
     * @param string $email
228
     * @param string $password
229
     * @return int
230
     * @throws UserException
231
     */
232 6
    function authenticate($email, $password)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
233
    {
234 6
        $criteria = new UserCriteria();
235 6
        $criteria->setEmail($email);
236
237 6
        $user = $this->getUserRepository()->findByCriteria($criteria);
238
239 6
        if(empty($user)) {
240 1
            throw new UserException(UserException::USER_NOT_FOUND);
241
        }
242
243
        /** @var UserEntity $user  */
244 5
        $user = $user[0];
245
246 5
        switch($user->getState()->getValue()) {
247 5
            case State::STATE_UNACTIVATED :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
248 1
                throw new UserException(UserException::USER_UNACTIVATED);
249
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
250 4
            case State::STATE_DISABLED :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
251 4
            case State::STATE_SUSPENDED :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
252 1
                throw new UserException(UserException::USER_DISABLED);
253
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
254 3
            case State::STATE_BANNED :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
255 1
                throw new UserException(UserException::USER_BANNED);
256
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
257
258 2
        }
259
260 2
        $bcrypt = new Bcrypt();
261 2
        $bcrypt->setCost(14);
262
263 2
        if(!$bcrypt->verify($password, $user->getPassword()))
264 2
        {
265 1
            throw new UserException(UserException::WRONG_PASSWORD);
266
        }
267
268 1
        return $user->getID();
269
    }
270
271
    /**
272
     * @param UserCriteria $criteria
273
     * @return array
274
     */
275 1
    public function findByCriteria(UserCriteria $criteria)
276
    {
277 1
        return $this->getUserRepository()->findByCriteria($criteria);
278
    }
279
280
    /**
281
     * @param UserEntity $user
282
     * @param $password
283
     * @return bool
284
     */
285 1
    public function checkPassword(UserEntity $user, $password)
286
    {
287 1
        $bcrypt = new Bcrypt();
288 1
        $bcrypt->setCost(14);
289
290 1
        return $bcrypt->verify($password, $user->getPassword());
291
    }
292
293
}
294