These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php namespace App\Services; |
||
2 | |||
3 | use App\Contracts\Registrar as RegistrarContract; |
||
4 | use App\Events\Users\LoggedIn; |
||
5 | use App\Events\Users\LoggedOut; |
||
6 | use App\Events\Users\Registered; |
||
7 | use App\Events\Users\RequestedResetPasswordLink; |
||
8 | use App\Events\Users\ResetPassword; |
||
9 | use App\Events\Users\Updated; |
||
10 | use App\Exceptions\Common\ValidationException; |
||
11 | use App\Exceptions\Users\LoginNotValidException; |
||
12 | use App\Exceptions\Users\PasswordNotValidException; |
||
13 | use App\Exceptions\Users\TokenNotValidException; |
||
14 | use App\Models\User; |
||
15 | use App\Models\UserActivation; |
||
16 | use App\Models\UserOAuth; |
||
17 | use App\Traits\Users\Activates; |
||
18 | use Illuminate\Database\Eloquent\ModelNotFoundException; |
||
19 | use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers; |
||
20 | use Illuminate\Foundation\Auth\ThrottlesLogins; |
||
21 | use Illuminate\Foundation\Validation\ValidatesRequests; |
||
22 | use Laravel\Socialite\AbstractUser as SocialiteUser; |
||
23 | |||
24 | class Registrar implements RegistrarContract |
||
25 | { |
||
26 | use AuthenticatesAndRegistersUsers, Activates, ThrottlesLogins, ValidatesRequests; |
||
27 | |||
28 | /** @var \Illuminate\Http\Request */ |
||
29 | protected $request; |
||
30 | |||
31 | 20 | public function __construct() |
|
32 | { |
||
33 | 20 | $this->request = app('router')->getCurrentRequest(); |
|
34 | 20 | } |
|
35 | |||
36 | /** |
||
37 | * Create a new user instance after a valid registration. |
||
38 | * |
||
39 | * @return \Illuminate\Contracts\Auth\Authenticatable |
||
40 | * @throws \App\Exceptions\Common\ValidationException |
||
41 | */ |
||
42 | 3 | public function register() |
|
43 | { |
||
44 | 3 | $validator = app('validator')->make($this->request->all(), [ |
|
45 | 3 | 'name' => 'sometimes|required|max:255', |
|
46 | 3 | 'email' => 'required|email|max:255|unique:users', |
|
47 | 3 | 'password' => 'required|confirmed|min:' . config('auth.passwords.users.min_length'), |
|
48 | 3 | ]); |
|
49 | 3 | if ($validator->fails()) { |
|
50 | 2 | throw new ValidationException($validator); |
|
51 | } |
||
52 | |||
53 | 1 | $user = new User(); |
|
54 | 1 | $this->request->has('name') && $user->name = $this->request->input('name'); |
|
55 | 1 | $user->email = $this->request->input('email'); |
|
56 | 1 | $user->password = app('hash')->make($this->request->input('password')); |
|
57 | 1 | $user->save() && event(new Registered($user)); |
|
58 | |||
59 | 1 | return $user; |
|
0 ignored issues
–
show
|
|||
60 | } |
||
61 | |||
62 | /** |
||
63 | * @param SocialiteUser $oauthUserData |
||
64 | * @param string $provider |
||
65 | * |
||
66 | * @return \Illuminate\Contracts\Auth\Authenticatable|bool |
||
67 | */ |
||
68 | 1 | public function registerViaOAuth(SocialiteUser $oauthUserData, $provider) |
|
69 | { |
||
70 | /** @var \App\Models\User $ownerAccount */ |
||
71 | if (!($ownerAccount = User::withTrashed()->whereEmail($oauthUserData->email)->first())) { |
||
72 | $ownerAccount = User::create([ |
||
73 | 'name' => $oauthUserData->name, |
||
74 | 'email' => $oauthUserData->email, |
||
75 | 1 | 'password' => app('hash')->make(uniqid("", true)) |
|
76 | ]); |
||
77 | event(new Registered($ownerAccount, $provider)); |
||
78 | } |
||
79 | |||
80 | # If user account is soft-deleted, restore it. |
||
81 | $ownerAccount->trashed() && $ownerAccount->restore(); |
||
82 | |||
83 | # Update missing user name. |
||
84 | if (!$ownerAccount->name) { |
||
85 | $ownerAccount->name = $oauthUserData->name; |
||
86 | $ownerAccount->save(); |
||
87 | } |
||
88 | |||
89 | ($doLinkOAuthAccount = $this->linkOAuthAccount($oauthUserData, $provider, $ownerAccount)) && app('auth.driver')->login($ownerAccount, true); |
||
90 | |||
91 | event(new LoggedIn($ownerAccount, $provider)); |
||
92 | |||
93 | return $doLinkOAuthAccount; |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * @param string $token |
||
98 | * |
||
99 | * @return bool |
||
100 | * @throws \App\Exceptions\Common\ValidationException |
||
101 | * @throws \App\Exceptions\Users\TokenNotValidException |
||
102 | */ |
||
103 | 2 | public function activate($token = null) |
|
104 | { |
||
105 | 2 | $data = !is_null($token) ? ['token' => $token] : $this->request->all(); |
|
106 | 2 | $validator = app('validator')->make($data, [ |
|
107 | 2 | 'token' => 'required|string', |
|
108 | 2 | ]); |
|
109 | 2 | if ($validator->fails()) { |
|
110 | throw new ValidationException($validator); |
||
111 | } |
||
112 | |||
113 | 2 | $activation = UserActivation::whereCode($data['token'])->first(); |
|
0 ignored issues
–
show
The method
first does only exist in Illuminate\Database\Query\Builder , but not in App\Models\UserActivation .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
114 | 2 | if (!$activation) { |
|
115 | 1 | throw new TokenNotValidException; |
|
116 | } |
||
117 | /** @var \App\Models\User $user */ |
||
118 | 1 | $user = User::findOrFail($activation->user_id); |
|
119 | |||
120 | 1 | return $this->complete($user, $data['token']); |
|
121 | } |
||
122 | |||
123 | /** |
||
124 | * @param integer $id |
||
125 | * |
||
126 | * @return boolean |
||
127 | * @throws \App\Exceptions\Common\ValidationException |
||
128 | * @throws \Illuminate\Database\Eloquent\ModelNotFoundException |
||
129 | */ |
||
130 | 2 | public function delete($id) |
|
131 | { |
||
132 | 2 | $validator = app('validator')->make($this->request->all(), [ |
|
133 | 2 | 'password' => 'required|min:' . config('auth.passwords.users.min_length'), |
|
134 | 2 | ]); |
|
135 | 2 | if ($validator->fails()) { |
|
136 | throw new ValidationException($validator); |
||
137 | } |
||
138 | |||
139 | 2 | $user = $this->get($id); |
|
140 | 2 | if (!app('hash')->check($this->request->input("password"), $user->password)) { |
|
141 | 1 | throw new PasswordNotValidException; |
|
142 | } |
||
143 | |||
144 | 1 | return (bool)User::destroy($id); |
|
145 | } |
||
146 | |||
147 | /** |
||
148 | * @param integer $id |
||
149 | * |
||
150 | * @return \App\Models\User |
||
151 | * @throws \Illuminate\Database\Eloquent\ModelNotFoundException |
||
152 | */ |
||
153 | 7 | public function get($id) |
|
154 | { |
||
155 | 7 | if (!empty($user = User::findOrFail($id))) { |
|
156 | 6 | return $user; |
|
157 | } |
||
158 | |||
159 | throw new ModelNotFoundException; |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * @param integer $id |
||
164 | * |
||
165 | * @return boolean |
||
166 | * @throws \App\Exceptions\Common\ValidationException |
||
167 | */ |
||
168 | 2 | public function update($id) |
|
169 | { |
||
170 | 2 | $user = $this->get($id); |
|
171 | |||
172 | 2 | $validator = app('validator')->make($this->request->all(), [ |
|
173 | 2 | 'name' => 'sometimes|required|max:255', |
|
174 | 'email' => 'required|email|max:255|unique:users,email,' . $id |
||
175 | 2 | ]); |
|
176 | 2 | if ($validator->fails()) { |
|
177 | 1 | throw new ValidationException($validator); |
|
178 | } |
||
179 | |||
180 | 1 | $userBefore = clone $user; |
|
181 | |||
182 | 1 | $this->request->has('name') && $user->name = $this->request->input("name"); |
|
183 | 1 | $user->email = $this->request->input("email"); |
|
184 | |||
185 | 1 | return $user->save() && event(new Updated($userBefore, $user)); // Fire the event on success only! |
|
186 | } |
||
187 | |||
188 | /** |
||
189 | * @return bool|\App\Models\User |
||
190 | * @throws \App\Exceptions\Common\ValidationException |
||
191 | * @throws \App\Exceptions\Users\LoginNotValidException |
||
192 | */ |
||
193 | 2 | public function login() |
|
194 | { |
||
195 | 2 | $validator = app('validator')->make($this->request->all(), [ |
|
196 | 2 | 'email' => 'required|email', |
|
197 | 2 | 'password' => 'required', |
|
198 | 2 | ]); |
|
199 | 2 | if ($validator->fails()) { |
|
200 | 1 | throw new ValidationException($validator); |
|
201 | } |
||
202 | |||
203 | 2 | $credentials = $this->request->only('email', 'password'); |
|
204 | |||
205 | 2 | if (app('auth.driver')->attempt($credentials, $this->request->has('remember'))) { |
|
206 | 1 | $user = app('auth.driver')->user(); |
|
207 | |||
208 | 1 | event(new LoggedIn($user)); |
|
209 | |||
210 | 1 | return $user; |
|
211 | } |
||
212 | |||
213 | 1 | throw new LoginNotValidException($this->getFailedLoginMessage()); |
|
214 | } |
||
215 | |||
216 | /** |
||
217 | * @param SocialiteUser $oauthUserData |
||
218 | * @param string $provider |
||
219 | * |
||
220 | * @return bool |
||
221 | */ |
||
222 | public function loginViaOAuth(SocialiteUser $oauthUserData, $provider) |
||
223 | { |
||
224 | /** @var UserOAuth $owningOAuthAccount */ |
||
225 | if ($owningOAuthAccount = UserOAuth::whereRemoteProvider($provider)->whereRemoteId($oauthUserData->id)->first()) { |
||
226 | $ownerAccount = $owningOAuthAccount->owner; |
||
227 | app('auth.driver')->login($ownerAccount, true); |
||
228 | |||
229 | event(new LoggedIn($ownerAccount, $provider)); |
||
230 | |||
231 | return true; |
||
232 | } |
||
233 | |||
234 | return !$this->registerViaOAuth($oauthUserData, $provider) ? false : true; |
||
235 | } |
||
236 | |||
237 | /** |
||
238 | * @return void |
||
239 | */ |
||
240 | 1 | public function logout() |
|
241 | { |
||
242 | 1 | $user = app('auth.driver')->user(); |
|
243 | 1 | if (!empty($user)) { |
|
244 | 1 | app('auth.driver')->logout(); |
|
245 | 1 | app('events')->fire(new LoggedOut($user)); |
|
246 | 1 | } |
|
247 | 1 | } |
|
248 | |||
249 | /** |
||
250 | * @return boolean |
||
251 | * @throws \App\Exceptions\Common\ValidationException |
||
252 | * @throws \Illuminate\Database\Eloquent\ModelNotFoundException |
||
253 | */ |
||
254 | 2 | public function sendResetPasswordLinkViaEmail() |
|
255 | { |
||
256 | 2 | $validator = app('validator')->make($this->request->all(), [ |
|
257 | 'email' => 'required|email|max:255' |
||
258 | 2 | ]); |
|
259 | 2 | if ($validator->fails()) { |
|
260 | 1 | throw new ValidationException($validator); |
|
261 | } |
||
262 | |||
263 | 2 | $user = User::whereEmail($this->request->only('email'))->first(); |
|
264 | 2 | if (is_null($user)) { |
|
265 | 1 | throw new ModelNotFoundException(trans('passwords.user')); |
|
266 | } |
||
267 | |||
268 | 1 | event(new RequestedResetPasswordLink($user)); |
|
269 | |||
270 | 1 | return true; |
|
271 | } |
||
272 | |||
273 | /** |
||
274 | * @return boolean |
||
275 | * @throws \App\Exceptions\Common\ValidationException |
||
276 | * @throws \App\Exceptions\Users\TokenNotValidException |
||
277 | * @throws \Illuminate\Database\Eloquent\ModelNotFoundException |
||
278 | */ |
||
279 | 2 | public function resetPassword() |
|
280 | { |
||
281 | 2 | $validator = app('validator')->make($this->request->all(), [ |
|
282 | 2 | 'token' => 'required|string', |
|
283 | 2 | 'email' => 'required|email|max:255', |
|
284 | 2 | 'password' => 'required|confirmed|min:' . app('config')->get('auth.passwords.users.min_length') |
|
285 | 2 | ]); |
|
286 | 2 | if ($validator->fails()) { |
|
287 | 1 | throw new ValidationException($validator); |
|
288 | } |
||
289 | |||
290 | 2 | $credentials = $this->request->only('email', 'password', 'password_confirmation', 'token'); |
|
291 | |||
292 | 2 | $passwordBroker = app('auth.password.broker'); |
|
293 | 2 | $response = $passwordBroker->reset( |
|
294 | 2 | $credentials, function (User $user, $password) { |
|
295 | 1 | $user->password = app('hash')->make($password); |
|
296 | 1 | $user->save(); |
|
297 | 1 | app('auth.driver')->login($user); |
|
298 | 2 | }); |
|
299 | |||
300 | switch ($response) { |
||
301 | 2 | case $passwordBroker::INVALID_USER: |
|
302 | 1 | throw new ModelNotFoundException(trans($response)); |
|
303 | break; |
||
0 ignored issues
–
show
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 function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last
Loading history...
|
|||
304 | 2 | case $passwordBroker::INVALID_TOKEN: |
|
305 | 1 | throw new TokenNotValidException(trans($response)); |
|
306 | break; |
||
0 ignored issues
–
show
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 function fx() {
try {
doSomething();
return true;
}
catch (\Exception $e) {
return false;
}
return false;
}
In the above example, the last
Loading history...
|
|||
307 | } |
||
308 | |||
309 | 1 | event(new ResetPassword(app('auth.driver')->user())); |
|
310 | |||
311 | 1 | return true; |
|
312 | } |
||
313 | |||
314 | /** |
||
315 | * @param SocialiteUser $oauthUserData |
||
316 | * @param string $provider |
||
317 | * @param User $ownerAccount |
||
318 | * |
||
319 | * @return \App\Models\User|bool |
||
320 | */ |
||
321 | private function linkOAuthAccount(SocialiteUser $oauthUserData, $provider, $ownerAccount) |
||
322 | { |
||
323 | /** @var UserOAuth[] $linkedAccounts */ |
||
324 | $linkedAccounts = $ownerAccount->linkedAccounts()->ofProvider($provider)->get(); |
||
325 | |||
326 | foreach ($linkedAccounts as $linkedAccount) { |
||
327 | if ($linkedAccount->remote_id === $oauthUserData->id || $linkedAccount->email === $oauthUserData->email) { |
||
328 | $linkedAccount->remote_id = $oauthUserData->id; |
||
329 | $linkedAccount->nickname = $oauthUserData->nickname; |
||
330 | $linkedAccount->name = $oauthUserData->name; |
||
331 | $linkedAccount->email = $oauthUserData->email; |
||
332 | $linkedAccount->avatar = $oauthUserData->avatar; |
||
333 | |||
334 | return $linkedAccount->save() ? $ownerAccount : false; |
||
335 | } |
||
336 | } |
||
337 | |||
338 | $linkedAccount = new UserOAuth(); |
||
339 | $linkedAccount->remote_provider = $provider; |
||
340 | $linkedAccount->remote_id = $oauthUserData->id; |
||
341 | $linkedAccount->nickname = $oauthUserData->nickname; |
||
342 | $linkedAccount->name = $oauthUserData->name; |
||
343 | $linkedAccount->email = $oauthUserData->email; |
||
344 | $linkedAccount->avatar = $oauthUserData->avatar; |
||
345 | |||
346 | return $ownerAccount->linkedAccounts()->save($linkedAccount) ? $ownerAccount : false; |
||
347 | } |
||
348 | } |
||
349 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.